寫這篇隨筆主要是嘗試幫助自己了解如何學習區塊鏈技術開發。
【本文禁止任何形式的全文粘貼式轉載,本文來自 zacky31 的隨筆】
目標:
- 創建一個最基本的“區塊鏈”
- 實現一個簡單的挖礦系統
前提:
對面向對象編程有一定的基礎
注意:
值得注意的是,這不會是一個完整的功能,恰恰相反,這是一個概念證明的實例,可以幫助您進一步了解區塊鏈。
准備:
我將會使用Java來實現,當然你也可以使用任何面向對象的語言。
環境:
- JDK 8
- IDEA
- Maven
開始吧
區塊鏈就好比多個塊連接起來。其中每一塊都將擁有自己的簽名,簽名中包含其前面的塊信息和一些數據(例如交易信息)。

每個塊不僅僅包含它之前的塊信息,同時也包含自身。如果前面一塊內容改變了,其 hash 值也會改變,將會導致其后面所有的塊發生變化。通過計算和比較所得的 hash 值,我們可以判斷區塊鏈是否合法。換句話說,改變區塊鏈中的任意內容,將會改變整個區塊鏈的簽名。
根據上面的分析,我們先創建一個 Block 類。
import java.util.Date; public class Block { public String hash; //存放數字簽名 public String preHash; //前面塊的簽名 private String data; private long timeStamp; public Block(String data, String preHash) { this.data = data; this.preHash = preHash; this.timeStamp = new Date().getTime(); } }
接下來,我們需要一個生成簽名的方法。有很多加密算法可供選擇,這里使用 SHA256 剛剛好。
import java.security.MessageDigest; public class StringUtil { public static String applySha256(String input) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hash = digest.digest(input.getBytes("UTF-8")); StringBuilder hexString = new StringBuilder(); 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 類中添加計算數字簽名的方法,並修改一下其構造方法。
public Block(String data, String preHash) { this.data = data; this.preHash = preHash; this.timeStamp = new Date().getTime(); this.hash = calculateHash(); } public String calculateHash() { String calculatedhash = StringUtil.applySha256(preHash + Long.toString(timeStamp) + data); return calculatedhash; }
到這里,可以寫個 Main 方法看一下效果。
public class Main { public static void main(String[] args) { Block first = new Block("Hi i am the first block", "0"); System.out.println("Hash for block 1 : " + first.hash); Block second = new Block("Hi i am the second block", first.hash); System.out.println("Hash for block 2 : " + second.hash); Block third = new Block("Hi i am the third block", second.hash); System.out.println("Hash for block 3 : " + third.hash); } }

可以看見每個 Block 都有自己唯一的 數字簽名,當然,現在還沒有構成一個區塊鏈,將這些塊存放到一個 ArrayList 中吧。修改 Main 類后再次運行。
import com.google.gson.GsonBuilder; import java.util.ArrayList; public class Main { public static ArrayList<Block> blockchain = new ArrayList<Block>(); public static void main(String[] args) { blockchain.add(new Block("Hi i am the first block", "0")); blockchain.add(new Block("Hi i am the second block", blockchain.get(blockchain.size() - 1).hash)); blockchain.add(new Block("Hi i am the third block", blockchain.get(blockchain.size() - 1).hash)); String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain); System.out.println(blockchainJson); } }

現在,需要一種方法去驗證創建的區塊鏈。編寫一段 isChainValid() 方法。任何塊的改變將會導致這個方法失效。
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); if (!currentBlock.hash.equals(currentBlock.calculateHash())) { System.out.println("Current Hashes not equal!"); return false; } if (!previousBlock.hash.equals(currentBlock.preHash)) { System.out.println("Previous Hashes not equal!"); return false } } return true; }
接下來,嘗試一下挖礦!

在 Block 類中,新增一個變量 nonce,並且添加到 calculateHash() 這個方法中,同時需要 mineBlock() 這個方法。這個方法中的 difficulty 變量就是用來控制計算量的。當設置的值較低時,大部分計算機很快就能算出來。
import java.util.Date; public class Block { public String hash; public String preHash; private String data; private long timeStamp; private int nonce; public Block(String data, String preHash) { this.data = data; this.preHash = preHash; this.timeStamp = new Date().getTime(); this.hash = calculateHash(); } public String calculateHash() { String calculatedhash = StringUtil.applySha256(preHash + Long.toString(timeStamp) + Integer.toString(nonce) + data); return calculatedhash; } 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); } }
我們可以在 Main 類中定義個靜態變量。嘗試在每次創建新塊去調用 mineBlock() 方法。
import com.google.gson.GsonBuilder; import java.util.ArrayList; import java.util.Date; public class Main { public static ArrayList<Block> blockchain = new ArrayList<Block>(); public static int difficulty = 5; public static void main(String[] args) { long beginTime1 = new Date().getTime(); blockchain.add(new Block("Hi i am the first block", "0")); System.out.println("Trying to mine block 1..."); blockchain.get(0).mineBlock(difficulty); long endTime1 = new Date().getTime(); System.out.println("Mining block 1 cost " + (endTime1 - beginTime1)); long beginTime2 = new Date().getTime(); blockchain.add(new Block("Hi i am the second block", blockchain.get(blockchain.size() - 1).hash)); System.out.println("Trying to mine block 2..."); blockchain.get(1).mineBlock(difficulty); long endTime2 = new Date().getTime(); System.out.println("Mining block 1 cost " + (endTime2 - beginTime2)); long beginTime3 = new Date().getTime(); blockchain.add(new Block("Hi i am the third block", blockchain.get(blockchain.size() - 1).hash)); System.out.println("Trying to mine block 3..."); blockchain.get(2).mineBlock(difficulty); long endTime3 = new Date().getTime(); System.out.println("Mining block 1 cost " + (endTime3 - beginTime3)); System.out.println("\nBlockchain is Valid: " + isChainValid()); String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain); System.out.println(blockchainJson); } public static Boolean isChainValid() { Block currentBlock; Block previousBlock; String hashTarget = new String(new char[difficulty]).replace('\0', '0'); for (int i = 1; i < blockchain.size(); i++) { currentBlock = blockchain.get(i); previousBlock = blockchain.get(i - 1); if (!currentBlock.hash.equals(currentBlock.calculateHash())) { System.out.println("Current Hashes not equal!"); return false; } if (!previousBlock.hash.equals(currentBlock.preHash)) { System.out.println("Previous Hashes not equal!"); return false; } if (!currentBlock.hash.substring(0, difficulty).equals(hashTarget)) { System.out.println("This block hasn't been mined"); return false; } } return true; } }
運行發現,挖礦過程還是很費時間的。把計算量改成7,差不多每挖一個需要一分鍾。。。

如果在此過程中,有人篡改了數據,將會導致:
- 區塊鏈將會無效
- 不能夠創建一個更長的區塊鏈
- 網絡中的誠實鏈將會比較長的區塊鏈有時間上的優勢
不過如果篡改數據擁有更強的運算速度,可能成功篡改。
這樣,基本上簡單實現了一個區塊鏈了。


