引入:
上一篇文章中,雖然我們只設(shè)計(jì)了一個(gè)簡單的區(qū)塊鏈原型,但它體現(xiàn)了區(qū)塊鏈數(shù)據(jù)庫的本質(zhì)。我們可以向它里面添加區(qū)塊,這些區(qū)塊呈鏈狀的,后一個(gè)區(qū)塊指向前一個(gè)區(qū)塊。然而,我們實(shí)現(xiàn)的區(qū)塊鏈有一個(gè)非常明顯的瑕疵:添加區(qū)塊到這個(gè)鏈上特別容易。比特幣或者其他區(qū)塊鏈的一個(gè)要旨是向鏈中添加區(qū)塊具有一定的困難性。今天我們就來修復(fù)這個(gè)瑕疵。
PoW(工作量證明):
區(qū)塊鏈中一個(gè)關(guān)鍵的理念是你要想往里面添加數(shù)據(jù)需要完成一些艱難的工作(如解難題,或者叫挖礦)才行。這些艱難的工作保證著區(qū)塊鏈安全和一致性。所以區(qū)塊鏈會(huì)獎(jiǎng)勵(lì)這些完成艱難工作的人(這就是為什么人們挖礦會(huì)獎(jiǎng)勵(lì)比特幣)。
這種機(jī)制與生活中的一個(gè)現(xiàn)象非常相似:一個(gè)人需要努力工作從而獲得報(bào)酬去維持他的生活。在區(qū)塊鏈中,一些網(wǎng)絡(luò)參與者(礦工)努力工作從而維持此網(wǎng)絡(luò)的運(yùn)行,他們向鏈中添加區(qū)塊,并且從中獲得獎(jiǎng)勵(lì)(如比特幣)。 他們的工作使得一個(gè)區(qū)塊被安全的正確無誤的添加到區(qū)塊鏈中,這也維持著整個(gè)區(qū)塊鏈數(shù)據(jù)庫的穩(wěn)定。值得注意的是,誰完成這件工作的必須有一個(gè)證明。
整個(gè)“解難題和證明”的機(jī)制稱作“工作量證明”。說他難是因?yàn)椤敖怆y題”需要大量的計(jì)算:即使是性能很好的計(jì)算機(jī)也不能快速的完成。并且這個(gè)工作的難度會(huì)隨著時(shí)間持續(xù)增大以保證大約6塊/h的出塊速度。在區(qū)塊鏈中這種工作(挖礦工作)的目標(biāo)是為一個(gè)區(qū)塊找到一個(gè)滿足一些要求的的hash值。并且這個(gè)hash值也起到證明的作用。因此,挖礦實(shí)際上就是尋找這種證明。
最需要強(qiáng)調(diào)的是,“工作量證明算法”必需滿足一個(gè)需求:求解這個(gè)難題很困難,但是驗(yàn)證它很簡單。
哈希:
在這一小節(jié)中,我們將討論哈希。如果你對(duì)這一塊很熟悉,請(qǐng)?zhí)^這一部分。
哈希的過程就是給某一數(shù)據(jù)找到對(duì)應(yīng)的一個(gè)hash值的過程。哈希函數(shù)可以接受任意大小的數(shù)據(jù)并生成固定大小的hash值。哈希有如下幾個(gè)特性:
1.給定一個(gè)hash值,不能由此反推出原始數(shù)據(jù)。因此,哈希不是加密。
2.確定的數(shù)據(jù)只有一個(gè)hash值,并且這個(gè)hash值是唯一的。
3.僅僅改動(dòng)數(shù)據(jù)中的一個(gè)字節(jié),得到的hash值就完全不同。
哈希函數(shù)在數(shù)據(jù)一致性方面有著廣泛的應(yīng)用。一些軟件提供商將軟件的驗(yàn)證碼印在軟件的包裝上面。
用戶下載文件后可以用一個(gè)給定的哈希函數(shù)生成一個(gè)哈希值與軟件提供商提供的hash對(duì)比一下,看是否一致。
在區(qū)塊鏈中,哈希用來保證區(qū)塊的(添加到區(qū)塊鏈前后的)一致性。區(qū)塊鏈中哈希算法的入?yún)耙粋€(gè)區(qū)塊的哈希值,這使得惡意修改一個(gè)區(qū)塊變的不可能,或者至少非常困難。因?yàn)檫@需要重新計(jì)算這個(gè)區(qū)塊的哈希值以及所有之后的區(qū)塊的哈希值。
哈?,F(xiàn)金:
比特幣中使用了Hashcash算法,這是一個(gè)PoW算法,設(shè)計(jì)的初衷是用來防止垃圾郵件。該算法可分解為以下幾個(gè)步驟:
1.準(zhǔn)備公開的數(shù)據(jù)(data):在郵件系統(tǒng)中為接收方的地址;在比特幣中為區(qū)塊頭
2.添加一個(gè)計(jì)數(shù)器(counter)。從0開始計(jì)數(shù)。
3.計(jì)算data+counter組合的hash值
4.校驗(yàn)hash值是否滿足特定的要求
1).是,恭喜你成功了
2).否,counter++ 并且重復(fù)步驟3和4
所以,這是一個(gè)強(qiáng)制暴力計(jì)算的算法:改變counter,計(jì)算一個(gè)新的哈希值,檢驗(yàn)它,不符合繼續(xù)增加counter,繼續(xù)計(jì)算一個(gè)新的哈希,等等,所以這是一個(gè)計(jì)算困難型的算法。
現(xiàn)在我們來看看目標(biāo)哈希值需要滿足的條件。在最早的Hashcash實(shí)現(xiàn)中,目標(biāo)哈希值需要滿足前20個(gè)比特都是0這樣的要求。在比特幣中,目標(biāo)哈希滿足的條件是隨著時(shí)間變化的,因?yàn)樵诒忍貛诺脑O(shè)計(jì)中,不管是計(jì)算能力隨著時(shí)間推移的增長還是越來越多的員工加入到網(wǎng)絡(luò)當(dāng)中,出塊的速度需要保持在每10分鐘一個(gè)。
為了演示這一算法過程,我們使用前面例子中的數(shù)據(jù)("I like donuts") ,目標(biāo)哈希滿足前三個(gè)字節(jié)全部為0:
圖中 ca07ca是十六進(jìn)制表示的計(jì)數(shù)器(counter)的值,相當(dāng)于十進(jìn)制中的13240266。
實(shí)現(xiàn):
上面我們分析了理論基礎(chǔ),現(xiàn)在我們來編寫代碼。首先,我們定義挖礦的難度:
比特幣中,是在被挖出的區(qū)塊的區(qū)塊頭中存儲(chǔ)挖礦難度的。我們不打算實(shí)現(xiàn)一個(gè)目標(biāo)哈希調(diào)整(挖礦難度的調(diào)整)的算法,現(xiàn)在我們僅僅定義一個(gè)全局的常量表示挖礦難度。
24是隨意選取的,我們的目標(biāo)是目標(biāo)值占用內(nèi)存空間在256個(gè)比特以內(nèi)。并且我們希望這個(gè)"難度"足夠有意義但是不能太大,因?yàn)?難度"越大就越難找到一個(gè)合適的哈希值。
創(chuàng)建一個(gè)ProofOfWork結(jié)構(gòu),它持有兩個(gè)指針,一個(gè)指向區(qū)塊,一個(gè)指向“目標(biāo)值”?!澳繕?biāo)值”就是算出的哈希滿足的需求。我們之所以使用big.Int類型是因?yàn)槲覀冃枰容^哈希值和“目標(biāo)值”:我們將哈希值轉(zhuǎn)換為一個(gè)bigInt類型然后檢驗(yàn)它是否比目標(biāo)值小。
在NewProofOfWork函數(shù)中,我們初始化一個(gè)big.Int類型的變量,設(shè)置其值為1,然后左移256-targetBits?個(gè)比特位。256指的是SHA-256的比特位數(shù),我的區(qū)塊鏈中將使用SHA-256哈希算法。十六進(jìn)制表示的“目標(biāo)值”如下圖所示:
它占用29個(gè)字節(jié)的內(nèi)存空間。下面是之前計(jì)算的哈希與“目標(biāo)值”的比較:
第一個(gè)哈希值("I like donuts"計(jì)算得出的哈希值)大于目標(biāo)值,因此它是無效的。第二個(gè)哈希值("I like donutsca07ca"計(jì)算得出的哈希值)小于目標(biāo)值,因此它是有效的。
你可以認(rèn)為“目標(biāo)值”是有效哈希的上邊界:如果一個(gè)哈希值小于這個(gè)邊界值,它是有效的,反之則反。降低這個(gè)邊界值意味著有效值的減少,因此,找到一個(gè)有效值需要付出更多的工作。
現(xiàn)在,我們準(zhǔn)備計(jì)算哈希的數(shù)據(jù)。
這段代碼很簡單:我們僅僅將區(qū)塊中的字段與目標(biāo)值及nonce合并。nonce就是上文Hashcash中描述的計(jì)數(shù)器,這是一個(gè)密碼學(xué)上的名詞。
好了,所有的準(zhǔn)備工作都完成了,我們可以開始實(shí)現(xiàn)PoW算法的核心了。
首先,我們定義幾個(gè)變量,hashInt:hash轉(zhuǎn)換為整型后的值;nonce:計(jì)數(shù)器。接下來是一個(gè)(偽)"無限循環(huán)",循環(huán)的最大次數(shù)為maxNonce,它與math.MaxInt64相等;這樣是為了防止nonce的溢出。盡管在我們的Pow算法實(shí)現(xiàn)中計(jì)數(shù)器的溢出很難,但是檢查一下更好,以防萬一。
在循環(huán)中執(zhí)行的步驟:
1.準(zhǔn)備數(shù)據(jù)
2.用SHA-256計(jì)算數(shù)據(jù)的hash
3.得到hash的big integer 類型
4.將hash的整型值與目標(biāo)值比較
現(xiàn)在我們可以Block的SetHash方法刪掉了并且我們將NewBlock方法修改如下:
如你所見nonce將作為Block的一個(gè)屬性保存在其中。這樣做是必需的,因?yàn)楣ぷ髁孔C明需要用到noce。Block結(jié)構(gòu)變?yōu)槿缦滤镜臉幼樱?/p>
讓我們啟動(dòng)程序看看我們的程序是否能夠很好的工作。
非常好!現(xiàn)在你可以看到所有區(qū)塊的hash都是以三個(gè)字節(jié)的0開頭,并且生成這些hash值需要一些時(shí)間。
事情還沒完,我們還有一件事要做:驗(yàn)證工作量。
這就是我們使用區(qū)塊中存儲(chǔ)的nonce的地方。
再看看我們的代碼是否能正常運(yùn)行:
結(jié)果:
結(jié)論:
我們的區(qū)塊鏈進(jìn)一步接近它的真實(shí)架構(gòu)了:添加區(qū)塊需要一定的工作量證明,因此可以挖礦了。但是它依然缺少一些至關(guān)重要的特征:區(qū)塊鏈數(shù)據(jù)沒有持久化,缺少錢包,地址,交易以及共識(shí)機(jī)制。所有的這些特性,我們會(huì)在以后的文章中一一介紹。
-盡力而為-