從零開始建立自己的區塊鏈應用(JAVA版)
本文閱讀物件,主要是希望和即將從事區塊鏈開發的專案架構師,開發工程師,專案設計或產品設計師。要求閱讀者具備一定的“區塊鏈”基礎知識、概念和以及相關的技術知識。
如果你只需要對區塊鏈應用做一個更深更直觀的瞭解,通過本文的例子更清晰瞭解區塊鏈是怎麼回事,大概是怎麼開發出來的,怎麼使用,那麼知道一些區塊鏈相關知識即可。
如果你是一個想從事這方面工作的人,尤其是從事開發和架構的技術人員,那麼需要的技術知識就相當的多了,無法一一列舉,大概一個高階網路開發工程師和中級以上的架構師水平,是需要的。
前言
很多新的概念,人們在剛接觸的時候,會讓人感到非常困惑,學了好多次都稀裡糊塗,並不僅僅是技術,很多地方都是如此。對於比較難理解的概念,用什麼辦法去學習最高效呢?我一直認為,就是找一個例子,或者寫一個
最近一段時間,AI人工智慧方興未艾,區塊鏈技術又迎面走來,對數字貨幣的崛起感到新奇的我們,估計很想知道其背後的技術--區塊鏈是怎樣的一個東西。但是完全搞懂區塊鏈並非易事,在實踐中學習方為上策。
我喜歡把程式當成詩歌來寫,理所當然要通過寫程式碼來實現並執行一個例子,來理解並學習這門技術。這裡,就通過用JAVA語言實現和構建一個區塊鏈來探討對區塊鏈的理解。
本文,我們要用區塊鏈來實現這麼一個例子,名字叫“區塊鏈成語接龍”。使用者通過這個例子,可以不斷的接龍前面一個使用者的成語,規則和普通遊戲一樣,前面一個使用者的成語的最後一個字,作為後面一個成語的第一個字。
單單作為一個應用,這個很簡單吶,問題是,我們要用區塊鏈的原理和相關技術實現它。
OK,let’s go… …
準備工作
這個時候其實我很想大喊一聲:“區塊鏈”其實不是某一種單純的技術,而是基於某一種思想的多種技術的結合。
需要的技術包括分散式儲存,分散式計算,P2P資料同步,加密解密,安全傳輸,一些語言,一些開發工具。。。還有一些新型的概念 – 分散式節點,工作量證明,共識演算法。。。等等
基於哪一種思想呢,大家都說是“去中心化”,我覺得就是“反壟斷”“反。。。”,敏感話題,不展開了。但是我事實上是一個喜歡民主的人,所以區塊鏈的“去中心化”對我吸引力頗大。。。all men are created equal…
本文裡面涉及到的一些例子,來源是網上,有些是Python版本,有些是Java版本,C版本,考慮到JAVA更為通用和易讀寫(其實我是認為JAVA語言更像詩歌了),就把他們改成了JAVA版,所以要求讀者對詩歌..哦,不是,是對JAVA非常瞭解,能讀寫基本的語法框架和邏輯,並且因為這個DEMO的特殊性,需要對網路框架和HTTP請求有基本的瞭解。
我們知道區塊鏈是資料塊和鏈的儲存方式的組合,是由N多個數據區塊按照鏈的組織和記錄構成的不可變、有序的鏈結構,記錄可以是交易、檔案或任何你想要的資料,同時它們是通過雜湊值(hashes)連結起來的。所以,在閱讀本文之前,一定要閱讀幾篇關於區塊鏈的文章,瞭解裡面塊,鏈,資料,工作量證明等等概念,最好也瞭解一些相關技術比如Hash,分散式儲存等等的概念。如果你還不是很瞭解這些,請找度娘。
如果在瞭解這些概念的過程中,一不小心接觸到了比特幣,請一定要明白,比特幣只是區塊鏈技術的一個產品實現。如果還接觸到其它一些諸如乙太網等等的名詞,那麼需要知道,這些都是區塊鏈技術下實現的一些框架和產品。
環境準備
理論上來說,可以用任何一種語言來建立任何一種技術的例子,包括區塊鏈的例子,我們這裡選用了JAVA,所以要在自己的電腦上,準備JAVA的相關開發環境。
確保你的電腦上已經安裝了較新版本的JDK,Tomcat和某一種你熟悉的最好能整合Tomcat的JAVA IDE,本例用的是Eclipse。Tomcat在我們這個例子中間,是一個WEB工具,因為我們的專案,需要基於WEB HTTP 釋出和執行。
如何安裝Java和各種工具,包括如何用Eclipse建立Dynamic web專案併發布到Tomcat等等步驟,既然你已經如此熟悉JAVA了,這裡就不再多講。
開始建立區塊鏈
區塊或稱資料塊Block類
區塊鏈中每個區塊包含以下基本內容:索引(index),Unix時間戳(timestamp),資料塊(data)(包括交易,文字,申明,獎勵等任何和合適的內容),證明或工作量證明(proof稍後解釋)以及前一個區塊的Hash值,Hash 用來連結資料塊,同時確保資料塊不被非法修改。
以下是一個區塊的結構:
public class Block {
int iIndex; //索引
String sProof; //工作量證明,在這個例子裡面,其實就是一個經過驗證的正確的成語
String sPreviousHash; //前一個區塊的Hash值
Timestamp tsCreateTime; //區塊建立時間戳
/*資料塊
*
* 使用者每接上一個成語,會得到系統10元錢的獎勵,同時會贏得前面一個使用者的2元錢
* 資料區同時需要記錄自己的使用者名稱和回答出上一個成語的使用者名稱
*
* */
String sSender; //回答出上一個成語的使用者名稱
String sRecipient; //回答出當前這個成語的使用者名稱
final int iMoneyAward=10; //系統獎勵,數額固定
final int iMoneyWin=2; //贏取獎勵,數額固定
public Block(){
}
}
區塊鏈實現Blockchain類
import java.util.*;
import blockchain.Block;
public class BlockChain {
//用來儲存區塊
private List<Block> lBlockchain=new ArrayList<>[];
public BlockChain(){
}
//建立新塊
public Block NewBlock(){
Block bRet=null;
//在這裡建立一個新塊
return bRet;
}
//Hash 一個塊
public String Hash(Block block){
String sHash=null;
//在這裡Hash 一個塊
return sHash;
}
//其他方法
//....
}
Blockchain類用來管理鏈條,它能儲存和更新鏈資料,加入新塊等等,下面我們來進一步增加和完善裡面的一些方法
建立新塊和創世塊
當一個使用者按照成語接龍的規則,對上上一個成語,並且系統驗證這成語正確(工作量被證明)。這個時候我們就可以建立一個新塊,並且加到鏈裡面。
一旦工作量證明確認,並且上一個塊hash 生成後,就可以簡單呼叫函式建立一個新塊了。
建立新塊方法如下:
//建立新塊
public Block NewBlock(int i,String proof,String hash,Timestamp c,String sender,String recipient){
Block bRet=null;
//在這裡建立一個新塊
bRet = NewBlock(i,proof,hash,c,sender,recipient);
return bRet;
}
這裡,我們需要提一下創世塊的概念,創世塊是區塊鏈的第一個區塊,它是沒有前區塊的。邏輯上只在第一個使用者第一次啟動系統的時候,才需要建立創世塊,後面的都是通過同步獲得。創世塊索引是0,同樣需要給它加上一個工作量證明,我們這裡就是初始程式“海闊天空”,因為沒有前面的塊,所以hash=””,同時給一個固定的建立時間。方法如下:
//創始塊的建立,創世塊是一個塊,必須是固定的資訊
//邏輯上來說,只有在區塊鏈產品的第一個使用者第一次啟動的時候,才會需要建立創世塊
public Block CreateFirstBlock(){
try{
Timestamp t=new Timestamp((new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).parse("2018-01-01 01:01:01").getTime());
return NewBlock(0,"海闊天空","",t,"","");
}catch(Exception e){
return null;
}
}
這裡順便貼上生成塊的Hash字串的方法:
//Hash 一個塊
public static String Hash(Block block){
String sHash=null;
//在這裡Hash 一個塊
String s=block.sPreviousHash+block.sProof+block.sRecipient+block.sSender+block.tsCreateTime.toString();
sHash = MD5(s);
return sHash;
}
public static String MD5(String key) {
char hexDigits[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B','C', 'D', 'E', 'F'
};
try {
byte[] btInput = key.getBytes();
// 獲得MD5摘要演算法的 MessageDigest 物件
java.security.MessageDigest mdInst = java.security.MessageDigest.getInstance("MD5");
// 使用指定的位元組更新摘要
mdInst.update(btInput);
// 獲得密文
byte[] md = mdInst.digest();
// 把密文