参考地址:Creating Your First Blockchain with Java
顾名思义,区块链就是很多“区块”形成的“链”。
每个“区块”上包含的数据有:
数字指纹一般是一个哈希值
当前区块的数字指纹计算方式:会根据上一个区块的指纹,以及当前区块的信息来计算
故当前区块的信息发生改变,影响这条链上之后的所有区块的数字指纹
所以,区块链的一般结构为:
import java.util.Date; public class Block { public String hash; public String previousHash; private String data; private long timeStamp; public Block(String data,String previousHash ) { this.data = data; this.previousHash = previousHash; this.timeStamp = new Date().getTime(); } }
采用sha256哈希算法生成哈希值(String表示)
import java.security.MessageDigest; public class StringUtil { /** * 对输入input使用sha256算法进行哈希, * 返回哈希值的16进制字符串 * @param input * @return */ public static String applySha256(String input) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hash = digest.digest(input.getBytes("UTF-8")); StringBuffer hexString = new StringBuffer(); for (int i = 0; i < hash.length; i++) { String hex = Integer.toHexString(0xff & hash[i]); if (hex.length() == 1) hexString.append('0'); hexString.append(hex); } return hexString.toString(); } catch (Exception e) { throw new RuntimeException(e); } } }
接下来可以修改Block类,增加一个生成数字指纹的方法
在Block类的构造函数中,给变量hash赋初值
public class Block { // ... 省略成员变量 public Block(String data, String previousHash) { this.data = data; this.previousHash = previousHash; this.timeStamp = new Date().getTime(); this.hash = calculateHash(); // 给hash赋初值 } public String calculateHash() { String calculatedhash = StringUtil.applySha256( previousHash + // 上一区块的数字指纹 Long.toString(timeStamp) + // 当前区块的时间戳 data); // 当前区块的额外信息 return calculatedhash; } }
Noob是“菜鸟,新手”的意思。
要是去英文编程论坛里问问题,
别人这么标记这个问题,
不要误会为不可能回答的意思。
先把主函数放入这个类中,进行测试:
创世区块是指区块链的第一个区块
创世区块没有上一个区块,所以把该区块的“上一个区块数字指纹”设为0
有了创世区块,就可以在这条区块链上继续新增区块
public class NoobChain { // 测试 public static void main(String[] args) { // 创世区块 Block genesisBlock = new Block("Hi im the first block", "0"); System.out.println("Hash for block 1 : " + genesisBlock.hash); Block secondBlock = new Block("Yo im the second block", genesisBlock.hash); System.out.println("Hash for block 2 : " + secondBlock.hash); Block thirdBlock = new Block("Hey im the third block", secondBlock.hash); System.out.println("Hash for block 3 : " + thirdBlock.hash); } }
输出为:
修改pom.xml文件,添加Gson依赖
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.9.0</version> </dependency>
修改NoobChain类,用链表ArrayList保存区块
public class NoobChain { public static ArrayList<Block> blockchain = new ArrayList<Block>(); //测试 public static void main(String[] args) { // 往区块链上添加区块: blockchain.add(new Block("Hi im the first block", "0")); blockchain.add(new Block("Yo im the second block", blockchain.get(blockchain.size() - 1).hash)); blockchain.add(new Block("Hey im the third block", blockchain.get(blockchain.size() - 1).hash)); // 按json格式输出 String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain); System.out.println(blockchainJson); } }
之前提到,在一个区块链上,
如果当前区块的信息发生变化,
后序所有区块的数字指纹都要重新计算,
所以需要校验是否所有区块都满足前后依赖关系。
这里在NoobChain类中设计一个实例函数isChainValid()
public class NoobChain { // 区块链 public static ArrayList<Block> blockchain = new ArrayList<Block>(); // 区块链完整性校验 public static Boolean isChainValid() { Block currentBlock; Block previousBlock; // 从前往后遍历所有区块 for (int i = 1; i < blockchain.size(); i++) { currentBlock = blockchain.get(i); previousBlock = blockchain.get(i - 1); // 重新计算当前区块的数字指纹(实例变量hash) if (!currentBlock.hash.equals(currentBlock.calculateHash())) { System.out.println("Current Hashes not equal"); return false; } // 判断当前区块的中上一区块的数字指纹是否正确 if (!previousBlock.hash.equals(currentBlock.previousHash)) { System.out.println("Previous Hashes not equal"); return false; } } return true; } }
区块链系统是典型的分布式系统
每个机器(节点)都能运行区块链程序
每个节点都能运行上述NoobChain类中的主函数
在主函数中,主要执行的任务是什么呢?就是往区块链后添加区块
例如,比特币(bitcoin)系统的每个节点是共享区块链的,
而所有节点中的最长区块链会被整个系统接受。
为了防止某个节点随意创建“最长的区块链”,比特币有工作量证明机制(Proof of work),
也就是在“添加区块至区块链”时会消耗很多时间和算力
攻击者的攻击需要有超过整个系统中所有剩余节点的算力之和
挖矿:简单理解,挖矿等价于“添加区块至区块链”;
由于存在工作量证明机制,添加区块至区块链时会很难;
下面模拟一种工作量证明机制(使得“添加区块至区块链”变难):
在区块Block类中新增一个nonce属性,nonce的作用是:
public class Block { public String hash; public String previousHash; private String data; private long timeStamp; private int nonce; public Block(String data, String previousHash) { this.data = data; this.previousHash = previousHash; this.timeStamp = new Date().getTime(); this.hash = calculateHash(); // 给hash赋初值 } public String calculateHash() { String calculatedhash = StringUtil.applySha256( previousHash + // 上一区块的数字指纹 Long.toString(timeStamp) + // 当前区块的时间戳 Integer.toString(nonce) + // 随机值 data); // 当前区块的额外信息 return calculatedhash; } /** * 模拟:只有当hash数字指纹 以difficulty个0开头, * 才能结束while循环, * 然后去把已经创建好的区块添加至区块链 * * @param difficulty */ public void mineBlock(int difficulty) { String target = new String(new char[difficulty]).replace('\0', '0'); while (!hash.substring(0, difficulty).equals(target)) { nonce++; hash = calculateHash(); } System.out.println("Block Mined!!! : " + hash); } }
更新NoobChain类中的main函数,实现:只有满足工作量证明后,才创建区块
public class NoobChain { // 区块链 public static ArrayList<Block> blockchain = new ArrayList<Block>(); public static int difficulty = 1; // 模拟挖矿 public static void main(String[] args) { // add our blocks to the blockchain ArrayList: blockchain.add(new Block("Hi im the first block", "0")); System.out.println("Trying to Mine block 1... "); // 得完成工作量证明,才能算成功把新建区块1添加区块链 blockchain.get(0).mineBlock(difficulty); blockchain.add(new Block("Yo im the second block", blockchain.get(blockchain.size() - 1).hash)); System.out.println("Trying to Mine block 2... "); // 得完成工作量证明,才能算成功把新建区块2添加区块链 blockchain.get(1).mineBlock(difficulty); blockchain.add(new Block("Hey im the third block", blockchain.get(blockchain.size() - 1).hash)); System.out.println("Trying to Mine block 3... "); // 得完成工作量证明,才能算成功把新建区块3添加区块链 blockchain.get(2).mineBlock(difficulty); System.out.println("\nBlockchain is Valid: " + isChainValid()); // 检验区块链的完整性 String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain); System.out.println("\nThe block chain: "); System.out.println(blockchainJson); } // ... 其他代码省略 }
如果某人想篡改区块链中已经确定好的区块,那么这个人就必须得赶在其他所有人的前头,把他自己节点上的那条区块链变得更长,他所需要的算力必须比其他人加起来还要多,不然其他人中就会出现至少一个人把未经篡改的区块链变长,继而把他那条篡改过的区块链淘汰。