Hash算法是區(qū)塊鏈中最核心的算法,在了解區(qū)塊鏈前我們必須先了解關(guān)于Hash算法的一些基本概念。
2.1 Hash的種類:
Hash算法有很多種,其中有MD5、SHA,而SHA算法又分為SHA-1、SHA-224、SHA-256、SHA-384和SHA-512五種變體,區(qū)塊鏈中用到的是SHA256,,所以我們?cè)谶@里會(huì)重點(diǎn)關(guān)注,后面會(huì)講到。
2.2、Hash算法的特點(diǎn):
1、輸入任意長(zhǎng)度的字符串(x)可以得到長(zhǎng)度固定的結(jié)果(H(x)),例如使用MD5算法:
MD5("version1") = "966634ebf2fc135707d6753692bf4b1e";
MD5("version2") = "2e0e95285f08a07dea17e7ee111b21c8";
2、輸入敏感,輸入數(shù)據(jù)的稍微改變就會(huì)引起Hash運(yùn)算結(jié)果的面目全非,上面的例子中“version1”和“version2”僅僅是最后一位“1”和“2”的區(qū)別,得出來(lái)的結(jié)果卻截然不同。
3、免碰撞,即不會(huì)出現(xiàn)輸入x≠y但是H(x) = H(y)的情況,也就是強(qiáng)抗沖突性。
4、原像不可逆,通俗地說(shuō),指的是知道輸入值,很容易通過哈希函數(shù)計(jì)算出哈希值;但知道哈希值,沒有辦法計(jì)算出原來(lái)的輸入值。也就是說(shuō),對(duì)于一個(gè)給定的輸出哈希結(jié)果H(x),想要逆推出輸入x,在計(jì)算上是不可能的,比如上面將“version1”的字符串經(jīng)過MD5算法得出"966634ebf2fc135707d6753692bf4b1e",但是沒有方法在知道輸出值"966634ebf2fc135707d6753692bf4b1e"的情況下,反推出原始值是“version1”。
4)難題友好性,不存在比窮舉更好的方法,以使哈希結(jié)果H(x)落在特定的范圍,換一種說(shuō)法就是,沒有便捷的方法去產(chǎn)生一滿足特殊要求的哈希值。
哈希函數(shù)的難題友好性構(gòu)成了基于工作量證明的共識(shí)算法的基礎(chǔ),例如,給定字符串“blockchain”,并在這個(gè)字符串后面連接一個(gè)整數(shù)值串x,對(duì)連接后的字符串進(jìn)行SHA256哈希運(yùn)算,要求得到的哈希結(jié)果(以十六進(jìn)制的形式表示)以若干個(gè)0開頭的。按照這個(gè)規(guī)則,由x=1出發(fā),遞增x的值,我們需要經(jīng)過2688次哈希計(jì)算才能找到前3位均為0的哈希值,而要找到前6位均為0的哈希值,則需進(jìn)行620969次哈希計(jì)算。也就是說(shuō),沒有更快捷的方法來(lái)產(chǎn)生一個(gè)滿足要求的哈希結(jié)果。
2.3、SHA-256算法
SHA-256屬于Hash算法中的一種,由美國(guó)國(guó)家安全局研發(fā),又屬于SHA算法的變種之一,是SHA-1的后繼者。
SHA256相對(duì)于其他的Hash算法來(lái)說(shuō),特點(diǎn)是對(duì)于任意長(zhǎng)度的消息,SHA256都會(huì)產(chǎn)生一個(gè)256bit長(zhǎng)的哈希值,稱作消息摘要。這個(gè)摘要相當(dāng)于是個(gè)長(zhǎng)度為32個(gè)字節(jié)的數(shù)組,通常用一個(gè)長(zhǎng)度為64的十六進(jìn)制字符串來(lái)表示。
Hash算法作為區(qū)塊鏈中的主要算法對(duì)于區(qū)塊鏈來(lái)說(shuō)有著怎樣的意義,和區(qū)塊鏈的工作原理又有什么關(guān)系?
別急,慢慢往下看。
現(xiàn)在假設(shè)一種場(chǎng)景:
網(wǎng)絡(luò)傳輸數(shù)據(jù)的時(shí)候,A收到B的傳過來(lái)的文件,需要確認(rèn)收到的文件有沒有損壞。如何解決?
最簡(jiǎn)單的方法是對(duì)整個(gè)原始數(shù)據(jù)做Hash運(yùn)算得到固定長(zhǎng)度的Hash值,然后把得到的Hash值公布在網(wǎng)上,這樣用戶從可信的渠道下載到公布的Hash值之后,對(duì)數(shù)據(jù)再次進(jìn)行Hash運(yùn)算,比較運(yùn)算結(jié)果和網(wǎng)上公布的Hash值進(jìn)行比較,如果兩個(gè)Hash值相等,說(shuō)明數(shù)據(jù)在傳輸過程沒有損壞(篡改),反之說(shuō)明數(shù)據(jù)經(jīng)過篡改或損壞。
如果從一個(gè)穩(wěn)定的服務(wù)器(可信的渠道)進(jìn)行下載,采用單一Hash是可取的。但如果數(shù)據(jù)源不穩(wěn)定,一旦數(shù)據(jù)損壞,就需要整個(gè)數(shù)據(jù)重新下載,這種下載的效率是很低的。
為了解決這個(gè)問題,在P2P網(wǎng)絡(luò)中做數(shù)據(jù)傳輸?shù)臅r(shí)候,往往需要把文件拆分很多小的數(shù)據(jù)塊各自傳輸,同時(shí)從多個(gè)機(jī)器上下載拆分過后不同的小數(shù)據(jù)塊,這樣的好處是,如果小數(shù)據(jù)塊在傳輸過程中損壞了,那么只要重新下載這一塊數(shù)據(jù)就行了,不用重新下載整個(gè)文件。
但是這么做的話,新的問題又來(lái)了,怎么去驗(yàn)證小的數(shù)據(jù)塊有沒有損壞?
答案是,下載之前我們會(huì)對(duì)每個(gè)小的數(shù)據(jù)塊分別做Hash運(yùn)算最終得到一個(gè)Hash List。下載的時(shí)候,在下載到真正數(shù)據(jù)之前,我們會(huì)先下載這個(gè)Hash List。
同樣的問題,怎么確定這個(gè)Hash List是正確的呢?
有一種辦法是對(duì)所有的小數(shù)據(jù)塊進(jìn)行Hash運(yùn)算得到每個(gè)小數(shù)據(jù)塊的Hash值然后再用遍歷的辦法與原數(shù)據(jù)塊的Hash值做一一對(duì)比,只有hash list中的每一個(gè)hash都是正確的,我們才會(huì)認(rèn)為這個(gè)hash list是有效的,嗯,好像是可以,但這種方法代價(jià)是不是太大呢,正常場(chǎng)景下數(shù)據(jù)量可是相當(dāng)巨大的,這種方法的效率明顯比較低下。
解決辦法是最開始在生成HashList的時(shí)候把每個(gè)小塊數(shù)據(jù)的Hash值拼到一起,然后對(duì)這個(gè)長(zhǎng)字符串再做一次Hash運(yùn)算,這樣就能得到一個(gè)最終的hash,這個(gè)最終的Hash值我們稱之為Hash Root(Top Hash)。
這就是Hash算法的作用,利用了Hash算法中的敏感性,某一條數(shù)據(jù)中只要有任何一點(diǎn)小的改動(dòng),會(huì)導(dǎo)致計(jì)算出來(lái)的Hash List中的某一條Hash值的不同,而Hash List中的某一條Hash值的不同又會(huì)導(dǎo)致最后算出來(lái)Hash root與原數(shù)據(jù)的Hash Root有著天差地別,簡(jiǎn)單來(lái)說(shuō),我們只需要一個(gè)長(zhǎng)度固定的字符串(Hash Root)就能輕易識(shí)別任意大小的數(shù)據(jù)是否損壞或者經(jīng)過篡改,識(shí)別成本很低。
所以,最終處理就是,下載數(shù)據(jù)的時(shí)候,首先確保從可信的數(shù)據(jù)源得到正確的根Hash,用它來(lái)校驗(yàn)整個(gè)Hash List,然后通過校驗(yàn)后的Hash List校驗(yàn)整個(gè)數(shù)據(jù)塊。
如圖,收件人可以在收到信息后,先比對(duì)節(jié)點(diǎn)1的Hash值,若節(jié)點(diǎn)1沒有錯(cuò)誤則無(wú)需進(jìn)行進(jìn)一步的比對(duì)。若發(fā)現(xiàn)節(jié)點(diǎn)1錯(cuò)誤可以繼續(xù)比對(duì)節(jié)點(diǎn)2和3的值。發(fā)現(xiàn)節(jié)點(diǎn)3無(wú)誤則可以放棄比對(duì)節(jié)點(diǎn)6和7. 找到錯(cuò)誤來(lái)自節(jié)點(diǎn)2,再依次對(duì)節(jié)點(diǎn)4和5運(yùn)算Hash值并發(fā)現(xiàn)錯(cuò)誤源于自節(jié)點(diǎn)4.然后收件人可以重新要求發(fā)信人發(fā)送節(jié)點(diǎn)4的數(shù)據(jù)從而使整個(gè)數(shù)據(jù)和發(fā)件人的保持一致。
現(xiàn)實(shí)生活中我們常用的BT(BitTorrent,一種分布式文件下載協(xié)議)下載就是采用這一套技術(shù)流程,代表性的有迅雷下載,所以大家在用迅雷下載時(shí),有沒有想過,為什么用迅雷下載的時(shí)候有時(shí)候需要先下載一個(gè)“種子”文件呢,這個(gè)“種子文件到底是什么?
其實(shí)這個(gè)種子文件就包含了我們前面所說(shuō)的Hash List和Hash Root,用法自然也是前面的所說(shuō)的——驗(yàn)證數(shù)據(jù)塊是否損壞或者被篡改,保證下載原始數(shù)據(jù)的的完整性和正確性。
這里講了這么多,好像沒有感受到與區(qū)塊鏈有任何聯(lián)系,實(shí)際上這些只是對(duì)基本概念的普及,利用這些基礎(chǔ)信息我們才能開展后面的工作。
OK,進(jìn)入重點(diǎn)。
三、區(qū)塊鏈工作原理—區(qū)塊結(jié)構(gòu)篇
在了解區(qū)塊鏈之前,我們需要知道很重要的一點(diǎn)就是,區(qū)塊鏈?zhǔn)怯伞皡^(qū)塊”這個(gè)基本單元所構(gòu)成。
字面意思就可以看出來(lái),區(qū)塊鏈?zhǔn)怯伞皡^(qū)塊”組成的鏈條,從技術(shù)的角度來(lái)說(shuō),區(qū)塊鏈?zhǔn)且环N分布式數(shù)據(jù)庫(kù)。
如果把區(qū)塊鏈比作一支賬本,那么區(qū)塊就是這個(gè)賬本的一頁(yè)。
“區(qū)塊”的概念如此重要,以至于我們只有真正理解了區(qū)塊,才能真正理解區(qū)塊鏈。
所以,下面我們將會(huì)詳細(xì)“解剖”區(qū)塊,看看區(qū)塊的“五臟六腑”,由內(nèi)往外窺探整個(gè)區(qū)塊鏈的原理。
3.1、先來(lái)區(qū)塊基本組成,如圖
首先,一個(gè)完整的區(qū)塊分為區(qū)塊頭(Block Header)和區(qū)塊體(Block Body)。
什么是區(qū)塊體?區(qū)塊體內(nèi)包含的是一條條交易數(shù)據(jù),列如張三給李四轉(zhuǎn)了一筆賬,轉(zhuǎn)賬者,接收者,金額,時(shí)間等等,這些信息構(gòu)成一條交易數(shù)據(jù),每一個(gè)區(qū)塊的區(qū)塊體都包含著無(wú)數(shù)條數(shù)據(jù),可以理解為,區(qū)塊體才是數(shù)據(jù)真正存儲(chǔ)的地方。
了解了區(qū)塊體,那什么是區(qū)塊頭?區(qū)塊頭是整個(gè)區(qū)塊的核心也是區(qū)塊的元數(shù)據(jù),它的成員變量全都是公共的,這使得它可以很方便的向調(diào)用者提供關(guān)于Block屬性的操作。區(qū)塊頭的組成相對(duì)復(fù)雜,我們一一來(lái)看。
1、Hash
由SHA256算法計(jì)算得出得當(dāng)前區(qū)塊的哈希值,也是代表當(dāng)前區(qū)塊的唯一值,可以理解成我們通常開發(fā)中Id的概念。每個(gè)區(qū)塊都有自己的唯一Hash。
2、父哈希(Pre Hash)
上一個(gè)區(qū)塊的哈希值。每個(gè)一區(qū)塊當(dāng)中都保存著上一個(gè)區(qū)塊的Hash,可以理解為父子關(guān)系,因?yàn)槌说谝粋€(gè)區(qū)塊(創(chuàng)世區(qū)塊),所有區(qū)塊的產(chǎn)生都是基于前一個(gè)區(qū)塊。
3、子哈希(Next Hash)
基于當(dāng)前區(qū)塊所產(chǎn)生的下一個(gè)區(qū)塊的哈希值,當(dāng)前區(qū)塊也就是子區(qū)塊的父區(qū)塊。
看到1、2、3這里可以解決我們一直以來(lái)的一個(gè)疑惑,區(qū)塊鏈里面的“鏈”到底是什么?其實(shí)就是由于每個(gè)區(qū)塊有當(dāng)前Hash 、父Hash、子Hash的概念,這些所謂的“父”,“子”形成了一套完整的順序鏈條,換句話說(shuō),區(qū)塊與區(qū)塊之間并非雜亂無(wú)章,而是存在著嚴(yán)格的順序關(guān)系。而這種順序關(guān)系像極了鏈表的概念,二者的邏輯順序均是通過鏈表中的指針鏈接次序?qū)崿F(xiàn)的。
鏈表:
區(qū)塊鏈:
注:有一點(diǎn)點(diǎn)小的區(qū)別是區(qū)塊鏈?zhǔn)欠茄h(huán)的,而鏈表是可以有循環(huán)的。
4、版本號(hào)(Version)
系統(tǒng)版本號(hào),可以理解成日常開發(fā)中開發(fā)的軟件版本。
5、區(qū)塊高度(Height)
區(qū)塊鏈網(wǎng)絡(luò)中第幾個(gè)誕生的區(qū)塊序號(hào),例如第一個(gè)區(qū)塊通常也被稱為創(chuàng)世區(qū)塊,高度為0(這很程序員),第十個(gè)產(chǎn)生的區(qū)塊高度為9(區(qū)塊并不是一下子就全有的,例如比特幣區(qū)塊就是每10分鐘產(chǎn)生一個(gè),也就說(shuō)區(qū)塊的“出生”都是有著先后順序的,這個(gè)順序我們會(huì)打上標(biāo)簽,這個(gè)標(biāo)簽就是”高度“)。
6、時(shí)間戳(TimeStamp)
區(qū)塊創(chuàng)建的時(shí)間,同時(shí)也交易數(shù)據(jù)打包進(jìn)區(qū)塊的時(shí)間,區(qū)塊的誕生是伴隨著交易數(shù)據(jù)的打包記錄,保存的地方就是我們前面提到的區(qū)塊體。
7、隨機(jī)數(shù)(Nonce)
一個(gè)從0開始,最大長(zhǎng)度為32位的隨機(jī)數(shù),這里就涉及到挖礦了,所謂的挖礦,就是不停地變更區(qū)塊頭中的隨機(jī)數(shù),并對(duì)每次變更后的區(qū)塊頭(block_header,)做雙重SHA-256運(yùn)算(即SHA256 (SHA 256(Block_Header))),將結(jié)果值哈希反轉(zhuǎn)并與當(dāng)前網(wǎng)絡(luò)的目標(biāo)值對(duì)應(yīng)的十進(jìn)制字符串做對(duì)比,如果小于目標(biāo)值,則解題成功,工作量證明完成。(這里先簡(jiǎn)單了解即可,后面會(huì)重點(diǎn)講到)。
9、難度目標(biāo)(Target Bits)
難度值是礦工們挖礦的重要參考指標(biāo),它決定了礦工大約需要經(jīng)過多少次Hash運(yùn)算才能產(chǎn)生一個(gè)合法的區(qū)塊。比特幣的區(qū)塊大約每10分鐘生成一個(gè),如果要在不同的全網(wǎng)算力條件下,新區(qū)塊的產(chǎn)生都基本保持這個(gè)速率,難度值必須根據(jù)全網(wǎng)算力的變化進(jìn)行調(diào)整。簡(jiǎn)單地說(shuō),難度值被設(shè)定在無(wú)論挖礦能力如何,新區(qū)塊產(chǎn)生速率都保持在10分鐘一個(gè)。
難度值的調(diào)整是在每個(gè)完整節(jié)點(diǎn)中獨(dú)立自動(dòng)發(fā)生的。每隔2016個(gè)區(qū)塊,所有節(jié)點(diǎn)都會(huì)按統(tǒng)一的公式自動(dòng)調(diào)整難度值
公式如下:
新難度值 = 舊難度值*(過去2016個(gè)區(qū)塊花費(fèi)時(shí)長(zhǎng)/20160分鐘)
難度目標(biāo)值= 最大目標(biāo)值/新難度值
而最大目標(biāo)值為一個(gè)恒定值(常量):0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
解題(挖礦)成功的依據(jù)是:
SHA256(SHA256(block_header)) < F(nBits)
? ? ? ? ? ? ? |? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? (挖礦結(jié)果)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (目標(biāo)值)
10、、Merkle Root(默克爾樹根)
Merkle Root的計(jì)算過程相對(duì)復(fù)雜,一起放到最后的算法環(huán)節(jié)進(jìn)行統(tǒng)一詳細(xì)講解。
區(qū)塊鏈的工作原理—核心算法篇
在這一篇中我們將介紹幾點(diǎn)核心算法,同時(shí)也是整個(gè)區(qū)塊鏈的核心原理。
我們將會(huì)講到:
1、上一篇中提到的Merkle樹是什么?Merkle樹的樹根 —Merkle Root 的算法。
2、區(qū)塊Hash值的算法。
3、挖礦合法性算法驗(yàn)證。
一、Merkle Tree
1、Merkle Tree是一種樹,大多數(shù)是二叉樹,也可以多叉樹,無(wú)論是幾叉樹,它都具有樹結(jié)構(gòu)的所有特點(diǎn);
2、Merkle Tree的葉子節(jié)點(diǎn)的value是數(shù)據(jù)集合(區(qū)塊體中的數(shù)據(jù))的單元數(shù)據(jù)或者單元數(shù)據(jù)HASH。
3、非葉子節(jié)點(diǎn)的value是根據(jù)它下面所有的葉子節(jié)點(diǎn)值,然后按照Hash算法計(jì)算而得出的。
Merkle Tree可以看做前面所說(shuō)Hash List的泛化,Hash List可以看作一種特殊的Merkle Tree,即樹高為2的多叉Merkle Tree,(前面Hash List沒理解清楚的可以往回翻重新理解一下)。
在最底層,和哈希列表一樣,我們把數(shù)據(jù)分成小的數(shù)據(jù)塊,有相應(yīng)地哈希和它對(duì)應(yīng)。但是往上走,注意,這里并不是和Hash List一樣直接去運(yùn)算根哈希,而是把相鄰的兩個(gè)數(shù)據(jù)哈希合并成一個(gè)字符串,然后將這個(gè)由兩個(gè)Hash值拼成的字符串計(jì)算出新的Hash,,這樣每?jī)蓚€(gè)哈希就結(jié)婚生子,得到了一個(gè)”子哈?!?。如果最底層的哈??倲?shù)是單數(shù),那到最后必然出現(xiàn)一個(gè)單身哈希,這種情況就直接對(duì)它進(jìn)行哈希運(yùn)算,所以也能得到它的子哈希。于是往上推,依然是一樣的方式,可以得到數(shù)目更少的新一級(jí)哈希,最終必然形成一棵倒掛的樹,到了樹根的這個(gè)位置,這一代就剩下一個(gè)根哈希了,我們把它叫做 Merkle Root,而這就是所謂的Merkle Root的生成過程。
在p2p網(wǎng)絡(luò)下載網(wǎng)絡(luò)之前,先從可信的源獲得文件的Merkle Tree樹根。一旦獲得了樹根,就可以從其他從不可信的源獲取Merkle tree。通過可信的樹根來(lái)檢查接受到的Merkle Tree。如果Merkle Tree是損壞的或者虛假的,就從其他源獲得另一個(gè)Merkle Tree,直到獲得一個(gè)與可信樹根匹配的Merkle Tree。
Merkle Tree和Hash List的主要區(qū)別是,Merkle Tree可以直接下載并立即驗(yàn)證Merkle Tree的一個(gè)分支。因?yàn)榭梢詫⑽募蟹殖尚〉臄?shù)據(jù)塊,這樣如果有一塊數(shù)據(jù)損壞,僅僅重新下載這個(gè)數(shù)據(jù)塊就行了。如果文件非常大,那么Merkle tree和Hash list都很難做到,但是Merkle tree可以一次下載一個(gè)分支,然后立即驗(yàn)證這個(gè)分支,如果分支驗(yàn)證通過,就可以下載數(shù)據(jù)了。而Hash list只有下載整個(gè)hash list才能驗(yàn)證。(前面花了比較大的篇幅介紹了Hash算法,就是為了讓大家提前了解,如何利用Hash去保證數(shù)據(jù)的一致性,這里的merkle root也是這個(gè)作用,只不過算法上有區(qū)別)
說(shuō)了這么多, Hash Root也好、Merkle Root也罷,不管算法如何,剝開層層外殼,對(duì)于我們而言,它們的核心價(jià)值就是——確保數(shù)據(jù)的真?zhèn)涡裕覀兂Uf(shuō)區(qū)塊鏈的數(shù)據(jù)不可篡改性也就是基于這一點(diǎn)。
區(qū)塊的工作原理—區(qū)塊Hash的生成。
區(qū)塊Hash是代表區(qū)塊的唯一值,這個(gè)值并不是憑空創(chuàng)造,而是和merkle root 、timestamp、nonce息息相關(guān)。
我們采用驗(yàn)證法來(lái)看看這個(gè)值是如何產(chǎn)生的,以比特幣區(qū)塊277316為例。
其信息來(lái)自網(wǎng)站http://blockchain.info
以上是區(qū)塊高度為277316的區(qū)塊的區(qū)塊頭的所有數(shù)據(jù)。
現(xiàn)在開始對(duì)區(qū)塊277316的Hash值進(jìn)行驗(yàn)證。
第一步,準(zhǔn)備數(shù)據(jù)
1、版本號(hào)的十進(jìn)制:2
2、merkle root+pre_hash的16進(jìn)制:
0000000000000002a7bbd25a417c0374cc55261021e8a9ca74442b01284f0569 c91c008c26e50763e9f548bb8b2fc323735f73577effbc55502c51eb4cc7cf2e
3、2013-12-27 23:11:54 的時(shí)間戳:1388185914
4、難度目標(biāo)(Bits)的十進(jìn)制:419668748
5、隨機(jī)數(shù)(nonce)的十進(jìn)制:924591752
第二步,全部轉(zhuǎn)換為16進(jìn)制
1、00000002
2、0000000000000002a7bbd25a417c0374cc55261021e8a9ca74442b01284f0569
? ? ? c91c008c26e50763e9f548bb8b2fc323735f73577effbc55502c51eb4cc7cf2e
3、52be093a
4、1903a30c
5、371c2688
第三步,從big-endian轉(zhuǎn)化為little-endian
先解釋一下什么是endian?簡(jiǎn)單理解就是Big-endian是高字節(jié)在低地址,Litter-endian則高字節(jié)在高地址。
比如 ,int a = 0x05060708
在big-endian的情況下存放為:
字節(jié)號(hào) 0 1 2 3
數(shù)據(jù) 05 06 07 08
在little-endian的情況下存放為:
字節(jié)號(hào) 0 1 2 3
數(shù)據(jù) 08 07 06 05
發(fā)明人中本聰可能為了讓機(jī)器計(jì)算更快,而變?yōu)榱烁咏鼨C(jī)器的編碼方式little-endian.
好,回到正題,所以第二步的數(shù)據(jù) 我們?cè)龠M(jìn)行一次轉(zhuǎn)化,得到了
1、02000000
2、69054f28012b4474caa9e821102655cc74037c415ad2bba70200000000000000
? 2ecfc74ceb512c5055bcff7e57735f7323c32f8bbb48f5e96307e5268c001cc9(到這里的時(shí)候已經(jīng)拼成一個(gè)字符串了)
3、3a09be52
4、0ca30319
5、88261c37
第四步,拼接字符串,開始驗(yàn)證
1、我們將上面準(zhǔn)備的字符數(shù)據(jù)重新拼接成一個(gè)新的字符串,這個(gè)字符串我們稱之為“block_header”:
02000000
+
69054f28012b4474caa9e821102655cc74037c415ad2bba702000000000000002ecfc74ceb512c5055bcff7e57735f7323c32f8bbb48f5e96307e5268c001cc9
+
3a09be52
+
0ca30319
+
88261c37
最終拼接得到的字符串block_header為:
0200000069054f28012b4474caa9e821102655cc74037c415ad2bba702000000000000002ecfc74ceb512c5055bcff7e57735f7323c32f8bbb48f5e96307e5268c001cc93a09be520ca3031988261c37
2、將上面的 block_header轉(zhuǎn)成hex形式得到:
b'\x02\x00\x00\x00i\x05O(\x01+Dt\xca\xa9\xe8!\x10&U\xcct\x03|AZ\xd2\xbb\xa7\x02\x00\x00\x00\x00\x00\x00\x00.\xcf\xc7L\xebQ,PU\xbc\xff~Ws_s#\xc3/\x8b\xbbH\xf5\xe9c\x07\xe5&\x8c\x00\x1c\xc9:\t\xbeR\x0c\xa3\x03\x19\x88&\x1c7'
3、對(duì)這個(gè)值進(jìn)行兩次SHA256運(yùn)算,為什么要兩次,這是因?yàn)镾HA1在2017年被攻破,采用的方法是birthday collision attack。社區(qū)覺得SHA2被攻破也是時(shí)間的問題,而抵御birthday collision attack的有效方法為雙重哈希算法,當(dāng)然這種說(shuō)法只是網(wǎng)上傳言,沒有得到具體確認(rèn)。
偽代碼:SHA256(SHA256(Hex(block_header))),最終得到:
b'\xc4\xbd\xc7,\x1b\xb3\xa9D\xd9\xf2~\xb9(\xa9\xc4A\xdb\x96^\t;\xa1\xb9\xb6\x01\x00\x00\x00\x00\x00\x00\x00'
3、重新編碼得到:
c4bdc72c1bb3a944d9f27eb928a9c441db965e093ba1b9b60100000000000000
看到后面有一堆0,感覺勝利的曙光快來(lái)了。其實(shí)這里實(shí)際上已經(jīng)完成了,但是得到這個(gè)值是little-endian小端模式的,比特幣區(qū)塊上所記錄的值也確實(shí)是這個(gè)值,但是我們?cè)谄匠J褂煤途W(wǎng)頁(yè)瀏覽展示時(shí),都是使用Big-endian大端順序
所以這里我們?cè)龠M(jìn)行一次從little-endian轉(zhuǎn)化為big-endian,就能得到:
0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4
大功告成,看看http://blockchain.info所查詢到的,完全一致,說(shuō)明我們的方法是正確的,區(qū)塊Hash的算法就是我們前面的驗(yàn)算步驟。
總的來(lái)說(shuō),算法不難,最難的地方就在于親自驗(yàn)算的過程中,要把所有的隱藏知識(shí)都挖掘出來(lái)。中文資料中,極少有人做詳細(xì)的通篇驗(yàn)算,而一旦真正理解了驗(yàn)算的過程,我們會(huì)發(fā)現(xiàn)比特幣的算法真的不難。
附:
整個(gè)第四步的Python代碼如下:
有興趣的可以將這一段代碼copy到python開發(fā)工具中運(yùn)行試試,看看得到的值是否如上所說(shuō)。也可以依照上面的步驟驗(yàn)證其他區(qū)塊加強(qiáng)理解。
區(qū)塊的合法性驗(yàn)證
如何確認(rèn)區(qū)塊的合法性,前面在介紹區(qū)塊頭時(shí)有提及過,簡(jiǎn)而言之就是區(qū)塊Hash必須要小于Target Bits (目標(biāo)難度值),這里我們繼續(xù)采用驗(yàn)算法。
區(qū)塊Hash值在上一節(jié)已經(jīng)得到驗(yàn)證,我們算出的是
0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4
Target Bits在上面也已經(jīng)記錄了,是
419668748
數(shù)據(jù)都準(zhǔn)備好了,要驗(yàn)證區(qū)塊是否合法還不簡(jiǎn)單嗎,直接對(duì)比兩個(gè)值的大小不就完了?
真有這么簡(jiǎn)單嗎?
事實(shí)上所謂的“對(duì)比大小”也是有一套完整的換算流程的。
具體怎么換算,這里還們還是按照老套路,采用驗(yàn)證法一步步走。
1、準(zhǔn)備數(shù)據(jù)
Bits :419668748?
最大目標(biāo)值:0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
Hash:0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4
2、將Bits 轉(zhuǎn)成 16進(jìn)制得到:
1903a30c
3、將1903a30c進(jìn)行拆分,前2位我們稱為冪者指數(shù),后面6位稱為系數(shù),拆分后得到
19? 03a30c
3、將拆分的值代入固定公式:
目標(biāo)值=系數(shù)*2^(8*(冪者指數(shù)-3))次方
即:目標(biāo)值 = 0x03a30c*2^(8*(0x19-3))
最終計(jì)算出來(lái)的就是真正意義上的目標(biāo)值了。
0000000000000003a30c00000000000000000000000000000000000000000000
4、驗(yàn)算結(jié)果
target bits:
0000000000000003a30c00000000000000000000000000000000000000000000
target:
0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
>
0000000000000003a30c00000000000000000000000000000000000000000000
計(jì)算結(jié)果小于難度目標(biāo),符合要求,那這個(gè)結(jié)果就一定是網(wǎng)站上公布的數(shù)字了。
正確的hash值
在挖礦時(shí),nonce隨機(jī)數(shù)是未知的,要從0試到2^32,但是這個(gè)數(shù)字其實(shí)不大,只有4294967296次。以現(xiàn)在的一臺(tái)礦機(jī)動(dòng)輒14T每秒的算力,全部算完到上限也不需要一秒。所以需要使用創(chuàng)幣交易中的附帶信息,額外的字符串成為extra nonce,例如中本聰在比特幣的創(chuàng)世區(qū)塊中寫道“The Times 03/Jan/2009 Chancellor on brink of second ballout for banks”(英國(guó)泰晤士報(bào)2009年1月3日文,財(cái)政大臣正處于實(shí)施第二輪銀行緊急援助的邊緣)嘲諷中心化的銀行。
在2013年年底時(shí),區(qū)塊產(chǎn)生,這需要算力達(dá)到8T/s的設(shè)備,即每秒8*10^15次暴力驗(yàn)證,連續(xù)工作10分鐘。這對(duì)于2018年的現(xiàn)在來(lái)說(shuō)的確不算什么,一臺(tái)礦機(jī),不比兩三塊磚頭大多少,就擁有14T/s的算力,只需要6,7分鐘單獨(dú)就可以挖到。但在當(dāng)時(shí),8T也是全網(wǎng)千分之一的算力了,需要當(dāng)時(shí)最好的礦機(jī)上百臺(tái)一起工作。而如果這個(gè)計(jì)算使用一臺(tái)普通的桌面電腦,需要26年。如果使用一臺(tái)iphoneX的話,每秒可以做70次計(jì)算,那么將需要四百萬(wàn)年。
通過上面的算法我們完整地回顧了比特幣區(qū)塊鏈的工作量證明算法,如果各位完全理清了其中的思路,也就可以手動(dòng)實(shí)現(xiàn)自己的挖礦程序,或者另外嘗試設(shè)計(jì)一些新的區(qū)塊鏈產(chǎn)品了。最艱深的技術(shù),我們希望能夠在底層去了解,然而撥開云霧,其實(shí)底層的邏輯并不難。
不過比特幣里面的技術(shù)遠(yuǎn)不止挖礦算法,加密算法,Script智能合約,各種協(xié)議,各種網(wǎng)絡(luò),交易的驗(yàn)證,每一個(gè)都充滿了魔性,進(jìn)出之間,不由得讓人驚嘆發(fā)明人知識(shí)的深度與廣度。無(wú)論比特幣是什么,將會(huì)怎樣,但是以比特幣為第一個(gè)大規(guī)模應(yīng)用的區(qū)塊鏈技術(shù),已經(jīng)擴(kuò)散了開來(lái),整個(gè)系統(tǒng)的嚴(yán)密與邏輯的復(fù)雜,的確讓人著迷。
更多
對(duì)于創(chuàng)世區(qū)塊,版本號(hào)是1,高度為0(這很程序員),。
前一區(qū)塊的hash值,猜想會(huì)是什么呢?對(duì)于創(chuàng)世區(qū)塊,是沒有前一區(qū)塊,中本聰默認(rèn)寫成了0000000000000000000000000000000000000000000000000000000000000000。
難度目標(biāo)是1,這是定義為一個(gè)sha256結(jié)果的前32位是0,也就是對(duì)應(yīng)的16進(jìn)制字符串要有8個(gè)0,那么難度bits此時(shí)是0x1d00ffff。這個(gè)數(shù)字的得出需要概率論的知識(shí),同時(shí)也是中本聰?shù)囊粋€(gè)規(guī)定。
更多區(qū)塊信息可以前往http://blockchain.info查詢,或許會(huì)有更多發(fā)現(xiàn)哦。
其他工具
https://hash.online-convert.com/sha256-generatorSHA-256在線驗(yàn)證
https://webbtc.com/? 獲取區(qū)塊的json數(shù)據(jù)
http://blockexplorer.com/q/getdifficulty查詢比特幣的當(dāng)前難度
http://bitcoin.sipa.be/查看難度的變化情況
http://blockexplorer.com/q/getblockcount查詢比特幣的區(qū)塊數(shù)
https://bitcoin.org/en/developer-reference比特幣區(qū)塊鏈更詳細(xì)的講解(英文)
參考文章:
https://blog.csdn.net/wo541075754/article/details/54632929
http://www.itdecent.cn/p/c6ef67755105
http://www.cnblogs.com/zhaoweiwei/p/difficulty.html
https://blog.csdn.net/jason_cuijiahui/article/details/79011118
https://blog.csdn.net/chaiyu2002/article/details/81237971
https://www.8btc.com/article/9688
第三章—java簡(jiǎn)單實(shí)現(xiàn)區(qū)塊鏈
前面兩章說(shuō)了許多關(guān)于區(qū)塊鏈的理論知識(shí),這一章我們將會(huì)將前面的所有的理論知識(shí)結(jié)合起來(lái),用java簡(jiǎn)答實(shí)現(xiàn)一套區(qū)塊鏈。
基于面向?qū)ο蟮乃枷?,首?/p>
1、定義區(qū)塊鏈的類塊
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è)工具類,所以我們可以不關(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類中應(yīng)用 方法 applySha256 方法,其主要的目的就是計(jì)算hash值,我們計(jì)算的hash值應(yīng)該包括區(qū)塊中所有我們不希望被惡意篡改的數(shù)據(jù),再加上preHash,data和timeStamp,這在上面已經(jīng)演示過了,是一套固定公式,所以這里沒有什么爭(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);
}
}
打?。?/p>
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)就更類似于我們所期待的區(qū)塊鏈的樣子。
4、檢查區(qū)塊鏈的完整性
在主方法中增加一個(gè)isChainValid()方法,目的是循環(huán)區(qū)塊鏈中的所有區(qū)塊并且比較hash值,這個(gè)方法用來(lái)檢查hash值是否是于計(jì)算出來(lái)的hash值相等,同時(shí)previousHash值是否和前一個(gè)區(qū)塊的hash值相等。(第二章講到過,驗(yàn)證區(qū)塊的有效性即重新計(jì)算區(qū)塊的Hash值并與鏈上記錄的區(qū)塊Hash值是否一致,而上一個(gè)區(qū)塊的Hash值—preHash,又影響了當(dāng)前Hash的計(jì)算)
所以我會(huì)的驗(yàn)證方法就會(huì)這么寫:
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開始的。讓我們添加一個(gè)名為nonce的int類型以包含在我們的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類 中增加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í)就挖礦算法,通過不斷變化的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;
}
}
打?。?/p>
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)過測(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ì)再講到。