使用Java建立第一個區塊鏈
本系列教程的目的是幫助你瞭解如何開發區塊鏈技術。在本教程中,我們將:
- 建立你的第一個非常基礎的區塊鏈。
- 實施簡單的工作量證明系統(採礦)。
- 探討任何的可能性。
我假設你對面向物件程式設計有基本的瞭解。值得注意的是,這不是一個功能齊全的生產區塊鏈。相反,這是一個概念驗證實現,可幫助你瞭解區塊鏈對於未來教程中區塊鏈的作用。
配置
我們將使用Java,但你應該能夠使用任何OOP語言。我將使用Eclipse,但你可以使用任何新的花哨的文字編輯器(雖然你會錯過很多好的擴充套件)。
你會需要:
- 安裝了Java和JDK。
- Eclipse或其他IDE。
或者你可以通過谷歌獲取GSON庫。這將允許我們將物件轉換為Json。這是一個非常有用的庫,我們也將在peer2peer中使用更多的東西,但是可以隨意使用替代方法。
在Eclipse中(file> new>)建立一個Java專案。我將把我的專案稱為noobchain
,並使用相同的名稱建立一個新類NoobChain
。
現在你就可以去試試:)
建立區塊鏈。
區塊鏈只是一個鏈/列表塊。區塊鏈中的每個區塊都有自己的數字簽名,包含前一個區塊的數字簽名,並且有一些資料(例如,這些資料可能是交易)。
雜湊=數字簽名。
每個塊不僅包含之前塊的雜湊值,而且它自己的雜湊部分是從前一個雜湊計算的。如果前一個塊的資料被改變,那麼前一個塊的雜湊將改變(因為它部分地由資料計算),進而影響其後的塊的所有雜湊。計算和比較雜湊值可以讓我們看到區塊鏈是否無效。
這是什麼意思?…更改此列表中的任何資料,將更改簽名並破壞區塊鏈。
所以先讓我們建立構成區塊鏈的類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(); } }
正如你所看到的,我們的基本塊包含一個String hash
,它將儲存我們的數字簽名。變數previousHash
用於儲存前一個塊的雜湊和String data
以儲存我們的塊資料。
接下來我們需要一種生成數字簽名的方法,你可以選擇許多加密演算法,但SHA256
適用於此示例。我們可以import java.security.MessageDigest;
訪問SHA256演算法。
我們需要稍後使用SHA256
,以便在新的StringUtil
utility
類中建立一個方便的helper方法:
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演算法,並將生成的簽名作為字串返回。
現在讓我們使用我們的applySha256
helper,在Block類的新方法中計算雜湊值。我們必須計算我們不想被篡改的塊的所有部分的雜湊值。因此,對於我們的塊,我們將包括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類中,我們可以建立一些塊並將雜湊值列印到螢幕上,以檢視所有內容是否正常工作。
讓我們測試一下…第一個塊稱為genesis
塊,因為沒有先前的塊,我們只需輸入“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
。(單擊此處瞭解如何匯入gson庫)
import java.util.ArrayList;
import com.google.gson.GsonBuilder;
public class NoobChain {
public static ArrayList<Block> blockchain = new ArrayList<Block>();
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
。
在比特幣網路節點上共享其區塊鏈,並且網路接受最長的有效鏈。什麼阻止某人篡改舊塊中的資料然後建立一個全新的更長的區塊鏈並將其呈現給網路?工作量證明。工作系統的hashcash
證明意味著建立新塊需要相當多的時間和計算能力。因此,攻擊者需要比其他對等組合更多的計算能力。
讓我們開始挖掘塊!
我們將要求礦工通過在塊中嘗試不同的變數值來進行工作量證明,直到其雜湊以一定數量的0開始。
讓我們在calculateHash()
方法中新增一個名為nonce
的int
,以及非常需要的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);
}
}
實際上,每個礦工將從隨機點開始迭代。一些礦工甚至可以嘗試隨機數來獲取隨機數。另外值得注意的是,在更難的解決方案可能需要超過integer.MAX_VALUE
,礦工可以嘗試更改時間戳。
mineBlock()
方法接受一個名為difficulty
的int
,這中間必須解決的數量為0的問題。在大多數計算機上幾乎可以立即解決像1或2這樣的低難度問題,我建議在4-6左右進行難度測試。在撰寫本文時,Litecoin的難度大約是442,592。
讓我們將難度作為靜態變數新增到NoobChain
類:
public static int difficulty = 5;
我們應該更新NoobChain
類以觸發每個新塊的mineBlock()
方法。isChainValid()
布林值還應檢查每個塊是否具有已解決(通過挖掘)雜湊。
import java.util.ArrayList;
import com.google.gson.GsonBuilder;
public class NoobChain {
public static ArrayList<Block> blockchain = new ArrayList<Block>();
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;
}
}
請注意,我們還檢查並列印isChainValid
。
執行此結果應如下所示:
挖掘每個區塊需要一些時間!(大約3秒)你應該弄亂難度值,看看這會影響挖掘每個區塊的時間。
如果有人要篡改區塊鏈系統中的資料:
- 他們的區塊鏈無效。
- 他們無法建立更長的區塊鏈。
- 你網路中的誠實區塊鏈將在最長的鏈條上具有時間優勢。
被篡改的區塊鏈將無法趕上更長且有效的鏈條。
除非它們的計算速度遠遠超過網路中所有其他節點的總和。未來的量子計算機或其他東西。
你已經完成了基本的區塊鏈!
你的區塊鏈:
- 由儲存資料的塊組成。
- 具有將你的塊連結在一起的數字簽名。
- 需要工作挖掘證明來驗證新塊。
- 可以檢查其中的資料是否有效且未更改。
你可以在Github上下載專案檔案。
======================================================================
分享一些以太坊、EOS、比特幣等區塊鏈相關的互動式線上程式設計實戰教程:
- java以太坊開發教程,主要是針對java和android程式設計師進行區塊鏈以太坊開發的web3j詳解。
- python以太坊,主要是針對python工程師使用web3.py進行區塊鏈以太坊開發的詳解。
- php以太坊,主要是介紹使用php進行智慧合約開發互動,進行賬號建立、交易、轉賬、代幣開發以及過濾器和交易等內容。
- 以太坊入門教程,主要介紹智慧合約與dapp應用開發,適合入門。
- 以太坊開發進階教程,主要是介紹使用node.js、mongodb、區塊鏈、ipfs實現去中心化電商DApp實戰,適合進階。
- C#以太坊,主要講解如何使用C#開發基於.Net的以太坊應用,包括賬戶管理、狀態與交易、智慧合約開發與互動、過濾器和交易等。
- EOS教程,本課程幫助你快速入門EOS區塊鏈去中心化應用的開發,內容涵蓋EOS工具鏈、賬戶與錢包、發行代幣、智慧合約開發與部署、使用程式碼與智慧合約互動等核心知識點,最後綜合運用各知識點完成一個便籤DApp的開發。
- java比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈儲存、去中心化共識機制、金鑰與指令碼、交易與UTXO等,同時也詳細講解如何在Java程式碼中整合比特幣支援功能,例如建立地址、管理錢包、構造裸交易等,是Java工程師不可多得的比特幣開發學習課程。
- php比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈儲存、去中心化共識機制、金鑰與指令碼、交易與UTXO等,同時也詳細講解如何在Php程式碼中整合比特幣支援功能,例如建立地址、管理錢包、構造裸交易等,是Php工程師不可多得的比特幣開發學習課程。
- tendermint區塊鏈開發詳解,本課程適合希望使用tendermint進行區塊鏈開發的工程師,課程內容即包括tendermint應用開發模型中的核心概念,例如ABCI介面、默克爾樹、多版本狀態庫等,也包括代幣發行等豐富的實操程式碼,是go語言工程師快速入門區塊鏈開發的最佳選擇。
匯智網原創翻譯,轉載請標明出處。這裡是原文使用Java建立第一個區塊鏈