1. 程式人生 > 其它 >Java實現簡單區塊鏈

Java實現簡單區塊鏈

目錄

參考地址:Creating Your First Blockchain with Java

準備

開發環境

  1. java1.8~
  2. maven
  3. 任選IDE

區塊鏈概述

顧名思義,區塊鏈就是很多“區塊”形成的“鏈”。
每個“區塊”上包含的資料有:

  • 它自身的數字指紋(digital fingerprint)
  • 上一個區塊的數字指紋
  • 一些額外資訊,如交易資訊(tansaction infomation)等等

數字指紋一般是一個雜湊值
當前區塊的數字指紋計算方式:會根據上一個區塊的指紋,以及當前區塊的資訊來計算
故當前區塊的資訊發生改變,影響這條鏈上之後的所有區塊的數字指紋

所以,區塊鏈的一般結構為:

編碼

區塊Block

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;
    }
}

區塊鏈NoobChain

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的作用是:

  • 在計算數字指紋(hash例項變數)時也需加上這個nonce值;
  • 只有當新建區塊的數字指紋以一定數量的0開頭,才能把新建區塊新增入區塊鏈
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);
    }

    // ... 其他程式碼省略
}

個人想法

如果某人想篡改區塊鏈中已經確定好的區塊,那麼這個人就必須得趕在其他所有人的前頭,把他自己節點上的那條區塊鏈變得更長,他所需要的算力必須比其他人加起來還要多,不然其他人中就會出現至少一個人把未經篡改的區塊鏈變長,繼而把他那條篡改過的區塊鏈淘汰。