logo资料库

java实现区块链入门.pdf

第1页 / 共9页
第2页 / 共9页
第3页 / 共9页
第4页 / 共9页
第5页 / 共9页
第6页 / 共9页
第7页 / 共9页
第8页 / 共9页
资料共9页,剩余部分请下载后查看
用 java 创建你的第一个区块链(第一部分) 作者:Kass 翻译:Green 奇 本系列教程的目的,是帮助你学习怎样开发区块链技术。 在本教程中,我们将:  创建你的第一个基础“区块链”;  实现一个简单的工作量证明(采矿)系统;  惊叹于可能性; (学习本教程之前,需要对面向对象编程有基本了解) 需要注意的是,本教程并没有生产区块链的完整功能。相反,这是一个概念实现的证明, 以帮助您理解区块链,为以后的教程打基础。 安装 教程中使用 Java,当然你可以使用其他的面向对象编程语言。开发工具是 Eclipse,同 样的你可以使用其他的文本编辑器(虽然你可能会错过很多好用的功能)。 你需要:  安装 Java 和 JDK;  Eclipse(或者其他 IDE/文本编辑器)。 你还可以使用谷歌发布的 GSON 类库,实现 Java 对象和 JSON 字符串之间的转换。这是个 非常有用的类库,会在下文的点对点代码中继续使用,同样的,有其他方法能替换该类库。 在 Eclipse 中创建项目(文件>新建>),命名为“noobchain”。新建一个同名的类。 创建区块链 区块链即为一个由区块组成的链表或列表。其中的每个区块都包含自己的数字签名、前 一区块的数字签名和一些数据(例如:事物)。
图中的 Hash(哈希)即为数字签名。 每个区块不仅包含前一区块的哈希,还包含自己的哈希,根据前一区块的哈希计算得 到。如果前一区块的数据变化,则其哈希也会变化(因为哈希的计算也与区块数据有关), 继而影响其后面所有区块的哈希。计算和比较哈希可以检查区块链是否无效。 这意味着什么呢?这意味着如果改变了区块链中的任意数据,将改变签名并破坏区块链。 所以首先,创建类 Block 来构造区块链: import java.util.Date; public class Block { } public String hash; public String previousHash; private String data; //our data will be a simple message. private long timeStamp; //as number of milliseconds since 1/1/1970. //Block Constructor. public Block(String data,String previousHash ) { this.data = data; this.previousHash = previousHash; this.timeStamp = new Date().getTime(); } 可以看到 Block 类包含 String 类型的 hash 属性,用来表示数字签名。变量 previousHash 表示前一区块的哈希,变量 data 表示区块数据。 接着需要一种方法来生成数字签名: 有 很 多 种 密 码 学 算 法 可 供 选 择 , 然 而 SHA256 算 法 最 为 适 合 。 导 入 java.security.MessageDigest 来访问 SHA256 算法。 下文中还需要使用 SHA256 算法,所以创建一个 StringUtil 类并定义一个方法以方便使 用: import java.security.MessageDigest; public class StringUtil { //Applies Sha256 to a string and returns the result. public static String applySha256(String input){
try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); //Applies sha256 to our input, byte[] hash = digest.digest(input.getBytes("UTF-8")); StringBuffer hexString = new StringBuffer(); // This will contain hash as hexidecimal 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); } } } 如果你看不懂这个方法,不要担心,你只需要知道输入一个字符串,调用 SHA256 算 法进行处理,将生成的签名作为字符串返回。 现在 Block 类中创建一个新方法,调用 applySha256 方法来计算哈希。必须使用所有不 希望被篡改的区块中的哈希值进行计算,所以要用到变量 previousHash、data 和 timeStamp (时间戳)。 public String calculateHash() { String calculatedhash = StringUtil.applySha256( previousHash + Long.toString(timeStamp) + data ); return calculatedhash; } 将这些方法加到 Block 构造函数中: public Block(String data,String previousHash ) { this.data = data; this.previousHash = previousHash; this.timeStamp = new Date().getTime(); this.hash = calculateHash(); //Making sure we do this after we set the other values. }
是时候做一些测试了。 在 NoobChain 类中创建一些区块并输出其哈希到屏幕上,确保一切都运转正常。 第一个区块叫初始区块,因其前面没有区块,所以将 previousHash 的值设为 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); } } 输出如下图所示: (你的哈希值会和图上不一样,因为我们的时间戳是不一样的) 这样,每个区块都根据自己的信息和前一区块的签名,拥有了自己的数字签名。 目前还不是区块链,将所有区块保存为动态数组(ArrayList),并导入 gson 将其转化成 JSon 字符串 来查看。 import java.util.ArrayList; import com.google.gson.GsonBuilder; public class NoobChain { public static ArrayList blockchain = new ArrayList(); public static void main(String[] args) { //add our blocks to the blockchain ArrayList: 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)); String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain); System.out.println(blockchainJson); } } 现在的输出看起来像我们期望的区块链了。 现在需要一种方法来验证区块链的完整性 在 NoobChain 类中创建一个 isChainValid()方法,遍历链中所有区块并比较其哈希。该方法需要检查当 前区块的哈希值是否等于计算得到的哈希值,以及前一区块的哈希是否等于当前区块 previousHash 变量的 值。 public static Boolean isChainValid() { Block currentBlock; Block previousBlock; //loop through blockchain to check hashes: for(int i=1; i < blockchain.size(); i++) { currentBlock = blockchain.get(i); previousBlock = blockchain.get(i-1); //compare registered hash and calculated hash: if(!currentBlock.hash.equals(currentBlock.calculateHash()) ){ System.out.println("Current Hashes not equal"); return false; } //compare previous hash and registered previous hash if(!previousBlock.hash.equals(currentBlock.previousHash) ) { System.out.println("Previous Hashes not equal"); return false; } } return true; } 链中区块发生任意的改变都会使该方法返回 false。 当人们将自己的区块链分享到比特币网络节点后,网络会接收其最长有效链。是什么阻止攻击者篡改 旧区块上的数据,然后创建更长的新区块链并放在网络上呢?工作量证明机制。哈希现金工作量证明机制
意味着创建新的区块需要相当长的时间和计算能力。因此攻击者需要掌握巨大的计算能力,比其他所有同 行加起来的计算能力还要多。 开始采矿!!! 我们要让矿机完成工作量证明机制,即在区块中尝试不同的变量值,直到其哈希值以一定数量的 0 开 始。 添加一个整数类型的 nonce 向量用于 calculateHash()方法中调用,并添加一个 mineBlock()方法: import java.util.Date; public class Block { public String hash; public String previousHash; private String data; //our data will be a simple message. private long timeStamp; //as number of milliseconds since 1/1/1970. private int nonce; //Block Constructor. public Block(String data,String previousHash ) { this.data = data; this.previousHash = previousHash; this.timeStamp = new Date().getTime(); this.hash = calculateHash(); //Making sure we do this after we set the other values. } //Calculate new hash based on blocks contents public String calculateHash() { String calculatedhash = StringUtil.applySha256( previousHash + Long.toString(timeStamp) + Integer.toString(nonce) + data ); return calculatedhash; } public void mineBlock(int difficulty) { String target = new String(new char[difficulty]).replace('\0', '0'); //Create a string with difficulty * "0" while(!hash.substring( 0, difficulty).equals(target)) { nonce ++; hash = calculateHash();
} System.out.println("Block Mined!!! : " + hash); } } 实际上,每个矿机都从一个随机点开始迭代。有的矿机甚至可以尝试随机的数字。同样值得注意的是, 在更困难的情况下可能需要的不仅仅是整数。MAX_VALUE 的情况下,矿机可以尝试更改时间戳。 mineBlock()方法以整数变量 difficulty 作为输入,该变量(难度)表示需要处理的 0 的数量。大多数计 算机可以快速的处理 1 或 2 个 0 的情况,我建议在测试时处理 4 到 6 个 0。莱特币的难度大约为 442592。 将 difficulty 作为一个静态变量添加到 NoobChain 类中: public static int difficulty = 5; 我们需要更新 NoobChain 类来触发每个新区块的 mineBlock()方法。同样需要 isChainValid()方法检查 每个区块是否通过采矿得到了哈希值。 public class NoobChain { public static ArrayList blockchain = new ArrayList(); public static int difficulty = 5; 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... "); 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... "); 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... "); 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);
} public static Boolean isChainValid() { Block currentBlock; Block previousBlock; String hashTarget = new String(new char[difficulty]).replace('\0', '0'); //loop through blockchain to check hashes: for(int i=1; i < blockchain.size(); i++) { currentBlock = blockchain.get(i); previousBlock = blockchain.get(i-1); //compare registered hash and calculated hash: if(!currentBlock.hash.equals(currentBlock.calculateHash()) ){ System.out.println("Current Hashes not equal"); return false; } //compare previous hash and registered previous hash if(!previousBlock.hash.equals(currentBlock.previousHash) ) { System.out.println("Previous Hashes not equal"); return false; } //check if hash is solved if(!currentBlock.hash.substring( 0, difficulty).equals(hashTarget)) { System.out.println("This block hasn't been mined"); return false; } } return true; } } 运行后的结果如图:
分享到:
收藏