三匙奴、java簡(jiǎn)單實(shí)現(xiàn)區(qū)塊鏈

前面兩章說(shuō)了許多關(guān)于區(qū)塊鏈的理論知識(shí),這一章我們將會(huì)將前面的所有的理論知識(shí)結(jié)合起來(lái)妄荔,用java簡(jiǎn)答實(shí)現(xiàn)一套區(qū)塊鏈泼菌。

以下內(nèi)容轉(zhuǎn)自:https://github.com/longfeizheng/blockchain-java

基于面向?qū)ο蟮乃枷耄紫?/p>

1啦租、定義區(qū)塊鏈的類(lèi)塊

import java.util.Date;

public class Block {

public String hash;//區(qū)塊Hash

public String previousHash;//前導(dǎo)Hash

private String data; //可以理解為前面講到的區(qū)塊結(jié)構(gòu)里的區(qū)塊體里打包的交易數(shù)據(jù)

private long timeStamp; //時(shí)間戳

//Block Constructor.

public Block(String data,String previousHash ) {

this.data = data;

this.previousHash = previousHash;

this.timeStamp = new Date().getTime();

}

}

2哗伯、創(chuàng)建數(shù)字簽名

創(chuàng)建一個(gè)StringUtil,封裝了sha256方法篷角,方便后面調(diào)用焊刹,因?yàn)檫@是一個(gè)工具類(lèi),所以我們可以不關(guān)心它的具體實(shí)現(xiàn)方式恳蹲,只要知道它是sha256的java實(shí)現(xiàn)即可虐块。

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

}

}

}

接下來(lái)讓我們?cè)贐lock類(lèi)中應(yīng)用

方法 applySha256

方法,其主要的目的就是計(jì)算hash值阱缓,我們計(jì)算的hash值應(yīng)該包括區(qū)塊中所有我們不希望被惡意篡改的數(shù)據(jù)非凌,再加上preHash,data和timeStamp荆针,這在上面已經(jīng)演示過(guò)了敞嗡,是一套固定公式颁糟,所以這里沒(méi)有什么爭(zhēng)議。

//簡(jiǎn)化了計(jì)算方式

public String calculateHash() {

String calculatedhash = StringUtil.applySha256(

previousHash +

Long.toString(timeStamp) +

data

);

return calculatedhash;

}

然后把這個(gè)方法加入到Block的構(gòu)造函數(shù)中去

public Block(String data,String previousHash ) {

this.data = data;

this.previousHash = previousHash;

this.timeStamp = new Date().getTime();

this.hash = calculateHash();

}

3喉悴、測(cè)試

在主方法中讓我們創(chuàng)建一些區(qū)塊棱貌,并把其hash值打印出來(lái),來(lái)看看是否一切都在我們的掌控中箕肃。

第一個(gè)塊稱為創(chuàng)世紀(jì)區(qū)塊婚脱,因?yàn)樗穷^區(qū)塊,所以我們只需輸入“0”作為前一個(gè)塊的previous hash勺像。

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

}

}

打诱厦场:

Hash for block 1: f6d1bc5f7b0016eab53ec022db9a5d9e1873ee78513b1c666696e66777fe55fb

Hash for block 2: 6936612b3380660840f22ee6cb8b72ffc01dbca5369f305b92018321d883f4a3

Hash for block 3: f3e58f74b5adbd59a7a1fc68c97055d42e94d33f6c322d87b29ab20d3c959b8f

每一個(gè)區(qū)塊都必須要有自己的數(shù)據(jù)簽名即hash值,這個(gè)hash值依賴于自身的信息(data)和上一個(gè)區(qū)塊的數(shù)字簽名(previousHash)吟宦,但這個(gè)還不是區(qū)塊鏈篮洁,下面讓我們存儲(chǔ)區(qū)塊到數(shù)組中,這里我會(huì)引入gson包殃姓,目的是可以用json方式查看整個(gè)一條區(qū)塊鏈結(jié)構(gòu)袁波。

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

}

}這樣的輸出結(jié)構(gòu)就更類(lèi)似于我們所期待的區(qū)塊鏈的樣子。

4蜗侈、檢查區(qū)塊鏈的完整性

在主方法中增加一個(gè)isChainValid()方法篷牌,目的是循環(huán)區(qū)塊鏈中的所有區(qū)塊并且比較hash值,這個(gè)方法用來(lái)檢查hash值是否是于計(jì)算出來(lái)的hash值相等踏幻,同時(shí)previousHash值是否和前一個(gè)區(qū)塊的hash值相等枷颊。(第二章講到過(guò),驗(yàn)證區(qū)塊的有效性即重新計(jì)算區(qū)塊的Hash值并與鏈上記錄的區(qū)塊Hash值是否一致该面,而上一個(gè)區(qū)塊的Hash值—preHash偷卧,又影響了當(dāng)前Hash的計(jì)算)

所以我會(huì)的驗(yàn)證方法就會(huì)這么寫(xiě):

public static Boolean isChainValid() {

Block currentBlock;

Block previousBlock;

//遍歷整個(gè)區(qū)塊鏈

for(int i=1; i < blockchain.size(); i++) {

currentBlock = blockchain.get(i);

previousBlock = blockchain.get(i-1);

//當(dāng)重新計(jì)算區(qū)塊Hash發(fā)現(xiàn)和鏈上記錄的不相等時(shí)

if(!currentBlock.hash.equals(currentBlock.calculateHash()) ){

System.out.println("Current Hashes not equal");

return false;

}

//當(dāng)上一個(gè)區(qū)塊上的Hash和當(dāng)前區(qū)塊記錄的preHash不相等時(shí)

if(!previousBlock.hash.equals(currentBlock.previousHash) ) {

System.out.println("Previous Hashes not equal");

return false;

}

}

return true;

}

任何區(qū)塊鏈中區(qū)塊的一絲一毫改變都會(huì)導(dǎo)致這個(gè)函數(shù)返回false,也就證明了區(qū)塊鏈無(wú)效了吆倦。

5、挖礦

這里我們要求挖礦者做工作量證明坐求,具體的方式是在區(qū)塊中嘗試不同的參數(shù)值直到它的hash值是從一系列的0開(kāi)始的蚕泽。讓我們添加一個(gè)名為nonce的int類(lèi)型以包含在我們的calculatehash()方法中,以及需要的mineblock()方法

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;

//區(qū)塊的構(gòu)造?

public Block(String data,String previousHash ) {

this.data = data;

this.previousHash = previousHash;

this.timeStamp = new Date().getTime();

this.hash = calculateHash();

}

//基于交易數(shù)據(jù)信息和其他數(shù)據(jù)生成當(dāng)前區(qū)塊的hash

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');? ? ? ? //計(jì)算出的Hash要求前面多少個(gè)0(理論上前面的0要求越多代表挖礦難度越大)

while(!hash.substring( 0, difficulty).equals(target)) {

nonce ++;

hash = calculateHash();

}

System.out.println("Block Mined!!! : " + hash);

}

}

mineBlock()方法中引入了一個(gè)int值稱為difficulty難度桥嗤,低的難度比如1和2须妻,普通的電腦基本都可以馬上計(jì)算出來(lái),我的建議是在4-6之間進(jìn)行測(cè)試泛领,普通電腦大概會(huì)花費(fèi)3秒時(shí)間荒吏,在萊特幣中難度大概圍繞在442592左右,而在比特幣中每一次挖礦都要求大概在10分鐘左右渊鞋,當(dāng)然根據(jù)所有網(wǎng)絡(luò)中的計(jì)算能力绰更,難度也會(huì)不斷的進(jìn)行修改瞧挤。

我們?cè)贜oobChain類(lèi) 中增加difficulty這個(gè)靜態(tài)變量。

public static int difficulty = 5;

這樣我們必須修改主方法中讓創(chuàng)建每個(gè)新區(qū)塊時(shí)必須觸發(fā)mineBlock()方法儡湾,而isChainValid()方法用來(lái)檢查每個(gè)區(qū)塊的hash值是否正確特恬,整個(gè)區(qū)塊鏈?zhǔn)欠袷怯行У摹?/p>

public class NoobChain {

public static ArrayList<Block> blockchain = new ArrayList<Block>();

public static int difficulty = 5;

public static void main(String[] args) {

//手動(dòng)創(chuàng)建幾個(gè)區(qū)塊

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');

//遍歷區(qū)塊,驗(yàn)證有效性

for(int i=1; i < blockchain.size(); i++) {

currentBlock = blockchain.get(i);

previousBlock = blockchain.get(i-1);

//當(dāng)重新計(jì)算區(qū)塊Hash發(fā)現(xiàn)和鏈上記錄的不相等時(shí)

if(!currentBlock.hash.equals(currentBlock.calculateHash()) ){

System.out.println("Current Hashes not equal");

return false;

}

//當(dāng)上一個(gè)區(qū)塊上的Hash和當(dāng)前區(qū)塊記錄的preHash不相等時(shí)

if(!previousBlock.hash.equals(currentBlock.previousHash) ) {

System.out.println("Previous Hashes not equal");

return false;

}

//這里涉及到的其實(shí)就挖礦算法徐钠,通過(guò)不斷變化的nonce值來(lái)生成Hash值癌刽,如果生成的Hash值的前幾位的都是0且和target要求的位數(shù)一致,代表這個(gè)隨機(jī)數(shù)生成的區(qū)塊是有效的尝丐。

if(!currentBlock.hash.substring( 0, difficulty).equals(hashTarget)) {

System.out.println("This block hasn't been mined");

return false;

}

}

return true;

}

}

打酉园荨:

Trying to Mine block 1...

Block Mined!!! : 0000016667d4240e9c30f53015310b0ec6ce99032d7e1d66d670afc509cab082

Trying to Mine block 2...

Block Mined!!! : 000002ea55735bea4cac7e358c7b0d8d81e8ca24021f5f85211bf54fd4ac795a

Trying to Mine block 3...

Block Mined!!! : 000000576987e5e9afbdf19b512b2b7d0c56db0e6ca49b3a7e638177f617994b

Blockchain is Valid: true

[

? {

? ? "hash": "0000016667d4240e9c30f53015310b0ec6ce99032d7e1d66d670afc509cab082",

? ? "previousHash": "0",

? ? "data": "first",

? ? "timeStamp": 1520659506042,

? ? "nonce": 618139

? },

? {

? ? "hash": "000002ea55735bea4cac7e358c7b0d8d81e8ca24021f5f85211bf54fd4ac795a",

? ? "previousHash": "0000016667d4240e9c30f53015310b0ec6ce99032d7e1d66d670afc509cab082",

? ? "data": "second",

? ? "timeStamp": 1520659508825,

? ? "nonce": 1819877

? },

? {

? ? "hash": "000000576987e5e9afbdf19b512b2b7d0c56db0e6ca49b3a7e638177f617994b",

? ? "previousHash": "000002ea55735bea4cac7e358c7b0d8d81e8ca24021f5f85211bf54fd4ac795a",

? ? "data": "third",

? ? "timeStamp": 1520659515910,

? ? "nonce": 1404341

? }

]

經(jīng)過(guò)測(cè)試增加一個(gè)新的區(qū)塊即挖礦必須花費(fèi)一定時(shí)間,大概是3秒左右爹袁,你可以提高difficulty難度來(lái)看远荠,它是如何影響數(shù)據(jù)難題所花費(fèi)的時(shí)間的

如果有人在你的區(qū)塊鏈系統(tǒng)中惡意篡改數(shù)據(jù):

他們的區(qū)塊鏈?zhǔn)菬o(wú)效的。

他們無(wú)法創(chuàng)建更長(zhǎng)的區(qū)塊鏈

網(wǎng)絡(luò)中誠(chéng)實(shí)的區(qū)塊鏈會(huì)在長(zhǎng)鏈中更有時(shí)間的優(yōu)勢(shì)

因?yàn)榇鄹牡膮^(qū)塊鏈將無(wú)法趕上長(zhǎng)鏈和有效鏈呢簸,除非他們比你網(wǎng)絡(luò)中所有的節(jié)點(diǎn)擁有更大的計(jì)算速度矮台,可能是未來(lái)的量子計(jì)算機(jī)或者是其他什么。

如果你耐心的看到這里了根时,恭喜瘦赫,我們已經(jīng)基本創(chuàng)建了屬于自己的區(qū)塊鏈

它有:

有很多區(qū)塊組成用來(lái)存儲(chǔ)數(shù)據(jù)

有數(shù)字簽名讓你的區(qū)塊鏈鏈接在一起

需要挖礦的工作量證明新的區(qū)塊

可以用來(lái)檢查數(shù)據(jù)是否是有效的同時(shí)是未經(jīng)篡改的

結(jié)語(yǔ):

坦白說(shuō),這篇文章更多的是從代碼講解的角度來(lái)剖析區(qū)塊鏈的工作原理蛤迎,為的是讓大家更好的吸收前面所講解的知識(shí)點(diǎn)确虱,雖然有一些偽代碼的成分,但是我們確實(shí)完成了區(qū)塊鏈所有應(yīng)該有的功能替裆。當(dāng)然校辩,真正的區(qū)塊鏈實(shí)現(xiàn)遠(yuǎn)不止這么一點(diǎn),大量的實(shí)現(xiàn)細(xì)節(jié)值得我們?nèi)パ芯苛就⑻接懞蛯?shí)現(xiàn)宜咒,更多的我們后續(xù)會(huì)再講到。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末把鉴,一起剝皮案震驚了整個(gè)濱河市故黑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌庭砍,老刑警劉巖场晶,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異怠缸,居然都是意外死亡诗轻,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)揭北,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)扳炬,“玉大人吏颖,你說(shuō)我怎么就攤上這事【媳” “怎么了侦高?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)厌杜。 經(jīng)常有香客問(wèn)我奉呛,道長(zhǎng),這世上最難降的妖魔是什么夯尽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任瞧壮,我火速辦了婚禮,結(jié)果婚禮上匙握,老公的妹妹穿的比我還像新娘咆槽。我一直安慰自己,他們只是感情好圈纺,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布秦忿。 她就那樣靜靜地躺著,像睡著了一般蛾娶。 火紅的嫁衣襯著肌膚如雪灯谣。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天蛔琅,我揣著相機(jī)與錄音胎许,去河邊找鬼。 笑死罗售,一個(gè)胖子當(dāng)著我的面吹牛辜窑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播寨躁,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼穆碎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了职恳?” 一聲冷哼從身側(cè)響起惨远,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎话肖,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體葡幸,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡最筒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蔚叨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片床蜘。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辙培,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出邢锯,到底是詐尸還是另有隱情扬蕊,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布丹擎,位于F島的核電站尾抑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蒂培。R本人自食惡果不足惜再愈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望护戳。 院中可真熱鬧翎冲,春花似錦、人聲如沸媳荒。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)钳枕。三九已至缴渊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間么伯,已是汗流浹背疟暖。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留田柔,地道東北人俐巴。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像硬爆,于是被迫代替她去往敵國(guó)和親欣舵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 區(qū)塊鏈?zhǔn)欠植际綌?shù)據(jù)存儲(chǔ)缀磕、點(diǎn)對(duì)點(diǎn)傳輸缘圈、共識(shí)機(jī)制、加密算法等計(jì)算機(jī)技術(shù)的新型應(yīng)用模式袜蚕。所謂共識(shí)機(jī)制是區(qū)塊鏈系統(tǒng)中實(shí)現(xiàn)不...
    dtdh閱讀 552評(píng)論 0 0
  • 區(qū)塊鏈?zhǔn)怯梢粋€(gè)個(gè)的區(qū)塊構(gòu)成糟把,區(qū)塊主要包含前一個(gè)區(qū)塊的hash值,加密的數(shù)據(jù)data牲剃,當(dāng)前區(qū)塊的hash值遣疯。 pac...
    Lavidateamadeve閱讀 783評(píng)論 0 1
  • 今天聽(tīng)林海的《遠(yuǎn)方的寂靜》,突然有一種告別的感覺(jué)凿傅,要告別這個(gè)階段的自己了缠犀,這樣的旋律也已經(jīng)不再是最?lèi)?ài)数苫。 前天去花樹(shù)...
    華英成秀閱讀 154評(píng)論 0 0
  • 我們 時(shí)間在走滔迈,我們也在走止吁。 大家已經(jīng)漸行漸遠(yuǎn),不在出現(xiàn)在彼此的生活中亡鼠,沒(méi)有交集赏殃。 有些人走著走著就再也沒(méi)了聯(lián)系,...
    扎到鐵了老心_閱讀 973評(píng)論 1 22