本系列教程的目的是帮助您了解如何开发区块链技术。
在本教程中,我们将:
- 创建你的第一个(非常)基本的“区块链”。
- 实施简单的工作证明(采矿)系统。
- 惊叹于可能性。
(我假设您对面向对象编程有基本的了解)
需要注意的是,本教程并没有生产区块链的完整功能。相反,这是一个概念实现的证明,以帮助您理解区块链,为以后的教程打基础。
1,安装
教程中使用 Java,当然你可以使用其他的面向对象编程语言。 开发工具是 Eclipse,同样你可以使用其他的文本编辑器(虽然你可能会错过很多好用的功能 。
你需要:
- 安装 Java 和 JDK;
- Eclipse(或者其他 IDE/文本编辑器)。
你还可以使用谷歌发布的 GSON 类库,实现 Java 对象和 JSON 字符串之间的转换。这是个非常有用的类库,会在下文的点对点代码中继续使用,同样的,有其他方法能替换该类库。
在Eclipse中创建一个(file> new>)Java项目。我将把我的项目称为“ noobchain ”,并使用相同的名称创建一个新类。
2,制作区块链
区块链只是一个由区块组成的链表/列表。区块链中的每个区块都包括自己的数字签名、前一个区块的数字签名和一些数据(这些数据可能是交易)。
哈希=数字签名
每个区块不仅包含它前一个区块的哈希,也包含它自己的哈希(根据前一个区块的哈希和自己区块的数据计算得到)。如果前一区块的数据变化,则其哈希也变化(因为它的计算也与区块的数据有关),然后影响后面所有区块的哈希。计算和比较哈希可以检查区块链是否无效。
这意味着什么?更改区块链中的任何数据,将更改签名并破坏区块链。
所有我们先创建构造区块链的Block类:
1 import java.util.Date; 2 3 public class Block { 4 5 public String hash; 6 public String previousHash; 7 private String data; //our data will be a simple message. 8 private long timeStamp; //as number of milliseconds since 1/1/1970. 9 10 //Block Constructor. 11 public Block(String data,String previousHash ) { 12 this.data = data; 13 this.previousHash = previousHash; 14 this.timeStamp = new Date().getTime(); 15 } 16 }
可以看到,我们的区块Block包含一个String类型的hash作为数字签名。变量previousHash作为前一个区块的hash,String类型的data作为区块数据。
接下来我们需要一种方法生成数字签名:
有很多密码算法供你选择,然而SHA256最为合适,我们可以导入java.security.MessageDigest来使用该算法。
下面我们需要使用SHA256算法,所以我们创建一个StringUtil类并在其中定义了一个方法:
1 import java.security.MessageDigest; 2 3 public class StringUtil { 4 //Applies Sha256 to a string and returns the result. 5 public static String applySha256(String input){ 6 try { 7 MessageDigest digest = MessageDigest.getInstance("SHA-256"); 8 //Applies sha256 to our input, 9 byte[] hash = digest.digest(input.getBytes("UTF-8")); 10 StringBuffer hexString = new StringBuffer(); // This will contain hash as hexidecimal 11 for (int i = 0; i < hash.length; i++) { 12 String hex = Integer.toHexString(0xff & hash[i]); 13 if(hex.length() == 1) hexString.append('0'); 14 hexString.append(hex); 15 } 16 return hexString.toString(); 17 } 18 catch(Exception e) { 19 throw new RuntimeException(e); 20 } 21 } 22 }
不用担心看不懂,你只需要知道,它需要输入一个字符串,调用SHA256算法,然后就返回一个字符串签名。
现在让我们在Block类中创建一个新方法,调用SHA256算法来计算哈希。我们必须根据不希望被篡改的所有区块来计算hash,所以我们的区块包括previousHash,
data
,timeStamp(时间戳)。
1 public String calculateHash() { 2 String calculatedhash = StringUtil.applySha256( 3 previousHash + 4 Long.toString(timeStamp) + 5 data 6 ); 7 return calculatedhash; 8 }
把该方法添加到Block的构造函数中:
1 public Block(String data,String previousHash ) { 2 this.data = data; 3 this.previousHash = previousHash; 4 this.timeStamp = new Date().getTime(); 5 this.hash = calculateHash(); //Making sure we do this after we set the other values. 6 }
是时候做些测试了
在NoobChain类中创建些区块并打印它的哈希到屏幕上,确认它正常工作。
第一个区块叫做初始区块,因为它没有前驱区块,所以我们把它前一区块的哈希设为“0”.
1 public class NoobChain { 2 3 public static void main(String[] args) { 4 5 Block genesisBlock = new Block("Hi im the first block", "0"); 6 System.out.println("Hash for block 1 : " + genesisBlock.hash); 7 8 Block secondBlock = new Block("Yo im the second block",genesisBlock.hash); 9 System.out.println("Hash for block 2 : " + secondBlock.hash); 10 11 Block thirdBlock = new Block("Hey im the third block",secondBlock.hash); 12 System.out.println("Hash for block 3 : " + thirdBlock.hash); 13 14 } 15 }
输出如下:
(你的是不一样的,因为你的时间戳是不同的)
现在每一个区块有自己的基于它自己的信息和前一区块数字签名的数字签名。
现在它还不是区块链,所以我们存储我们的区块到ArrayList,然后导入gson包将其转化为Json来查看。
1 import java.util.ArrayList; 2 import com.google.gson.GsonBuilder; 3 4 public class NoobChain { 5 6 public static ArrayList<Block> blockchain = new ArrayList<Block>(); 7 8 public static void main(String[] args) { 9 //add our blocks to the blockchain ArrayList: 10 blockchain.add(new Block("Hi im the first block", "0")); 11 blockchain.add(new Block("Yo im the second block",blockchain.get(blockchain.size()-1).hash)); 12 blockchain.add(new Block("Hey im the third block",blockchain.get(blockchain.size()-1).hash)); 13 14 String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain); 15 System.out.println(blockchainJson); 16 } 17 18 }
现在我们的输出应该看起来接近我们所期望的区块链了。
现在我们需要一种方法来验证区块链的完整性。
在NoobChain类中创建一个isChainValid()方法,遍历所有的区块并比较它们的哈希。这个方法需要验证当前区块的哈希值是否等于计算得到的哈希值,前一区块的哈希值是否等于当前区块的哈希值。
1 public static Boolean isChainValid() { 2 Block currentBlock; 3 Block previousBlock; 4 5 //loop through blockchain to check hashes: 6 for(int i=1; i < blockchain.size(); i++) { 7 currentBlock = blockchain.get(i); 8 previousBlock = blockchain.get(i-1); 9 //compare registered hash and calculated hash: 10 if(!currentBlock.hash.equals(currentBlock.calculateHash()) ){ 11 System.out.println("Current Hashes not equal"); 12 return false; 13 } 14 //compare previous hash and registered previous hash 15 if(!previousBlock.hash.equals(currentBlock.previousHash) ) { 16 System.out.println("Previous Hashes not equal"); 17 return false; 18 } 19 } 20 return true; 21 }
区块链中的区块有任何改变将导致该方法返回false。
在比特币网络节点上共享其区块链,并且网络接受最长的有效链。什么阻止某人篡改旧块中的数据然后创建一个全新的更长的区块链并将其呈现给网络?工作量证明。哈希工作量证明系统意味着创建新的区块需要相当长的时间和计算能力。因此攻击者需要有比其他所有同行加起来还有多的算力。
3,开始挖矿
我们要让矿机完成工作量证明机制,通过在区块中尝试不同的变量值直到它的哈希值以某个固定数量的0开头。
在calculateHash()方法中添加一个整数类型变量nonce,添加一个mineBlock()方法。
1 import java.util.Date; 2 3 public class Block { 4 5 public String hash; 6 public String previousHash; 7 private String data; //our data will be a simple message. 8 private long timeStamp; //as number of milliseconds since 1/1/1970. 9 private int nonce; 10 11 //Block Constructor. 12 public Block(String data,String previousHash ) { 13 this.data = data; 14 this.previousHash = previousHash; 15 this.timeStamp = new Date().getTime(); 16 17 this.hash = calculateHash(); //Making sure we do this after we set the other values. 18 } 19 20 //Calculate new hash based on blocks contents 21 public String calculateHash() { 22 String calculatedhash = StringUtil.applySha256( 23 previousHash + 24 Long.toString(timeStamp) + 25 Integer.toString(nonce) + 26 data 27 ); 28 return calculatedhash; 29 } 30 31 public void mineBlock(int difficulty) { 32 String target = new String(new char[difficulty]).replace(' ', '0'); //Create a string with difficulty * "0" 33 while(!hash.substring( 0, difficulty).equals(target)) { 34 nonce ++; 35 hash = calculateHash(); 36 } 37 System.out.println("Block Mined!!! : " + hash); 38 } 39 }
实际上每个矿机都从一个随机点开始迭代。一些矿机甚至尝试随机的nonce的值。更困难的可能不仅仅是整数。MAX_VALUE情况下,矿机可以尝试改变时间戳。
mineBlock()方法输入一个整数变量difficulty,这是矿机必须找到的0的数量。在大多数计算机上可以立即处理1或2个0的情况,我建议用4到6个0进行测试。在写本文时,莱特币的难度大约是442592.
我们将difficulty作为静态变量添加到NoobChain类:
public static int difficulty = 5;
我们应该更新NoobChain类来触发每个新区块的mineBlock()方法。isChainValid()方法应该检查每个区块是否通过采矿得到哈希值。
1 import java.util.ArrayList; 2 import com.google.gson.GsonBuilder; 3 4 public class NoobChain { 5 6 public static ArrayList<Block> blockchain = new ArrayList<Block>(); 7 public static int difficulty = 5; 8 9 public static void main(String[] args) { 10 //add our blocks to the blockchain ArrayList: 11 12 blockchain.add(new Block("Hi im the first block", "0")); 13 System.out.println("Trying to Mine block 1... "); 14 blockchain.get(0).mineBlock(difficulty); 15 16 blockchain.add(new Block("Yo im the second block",blockchain.get(blockchain.size()-1).hash)); 17 System.out.println("Trying to Mine block 2... "); 18 blockchain.get(1).mineBlock(difficulty); 19 20 blockchain.add(new Block("Hey im the third block",blockchain.get(blockchain.size()-1).hash)); 21 System.out.println("Trying to Mine block 3... "); 22 blockchain.get(2).mineBlock(difficulty); 23 24 System.out.println(" Blockchain is Valid: " + isChainValid()); 25 26 String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain); 27 System.out.println(" The block chain: "); 28 System.out.println(blockchainJson); 29 } 30 31 public static Boolean isChainValid() { 32 Block currentBlock; 33 Block previousBlock; 34 String hashTarget = new String(new char[difficulty]).replace(' ', '0'); 35 36 //loop through blockchain to check hashes: 37 for(int i=1; i < blockchain.size(); i++) { 38 currentBlock = blockchain.get(i); 39 previousBlock = blockchain.get(i-1); 40 //compare registered hash and calculated hash: 41 if(!currentBlock.hash.equals(currentBlock.calculateHash()) ){ 42 System.out.println("Current Hashes not equal"); 43 return false; 44 } 45 //compare previous hash and registered previous hash 46 if(!previousBlock.hash.equals(currentBlock.previousHash) ) { 47 System.out.println("Previous Hashes not equal"); 48 return false; 49 } 50 //check if hash is solved 51 if(!currentBlock.hash.substring( 0, difficulty).equals(hashTarget)) { 52 System.out.println("This block hasn't been mined"); 53 return false; 54 } 55 } 56 return true; 57 } 58 }
运行结果如下
挖掘每个区块需要一些时间(大约3秒)。你应该更改difficulty的值(即挖矿难度)来看看对挖矿时间的影响。
如果有人篡改了你的区块链中的数据:
- 他们的区块链是无效的;
- 他们不能创建一个更长的区块链;
- 在你网络中诚实的区块链会在最长的链上有个时间优势。
一个被篡改的区块链不肯赶上一个更长且有效的链。
除非他们的算力比你网络中其他所有节点的算力总和还要多。
你已经完成了基本的区块链!
你的区块链:
- 由存储数据的块组成。
- 由数字签名把区块 链接起来。
- 需要工作量证明挖矿来验证新的区块。
- 可以验证其中的数据是否有效且未被篡改。
项目文件 : https://github.com/CryptoKass/NoobChain-Tutorial-Part-1
原文:https://medium.com/programmers-blockchain/create-simple-blockchain-java-tutorial-from-scratch-6eeed3cb03fa