關(guān)于 第一篇: 參見(jiàn)笑來(lái)老師翻譯的版本:
lixiaolai.com/2017/09/28/building-blockchain-in-go-part-1/
- 作者:Ivan Kuznetsov
- 中文翻譯:曉頓
- 原文鏈接:https://jeiwan.cc/posts/building-blockchain-in-go-part-2/
- 介紹
- 工作證明
- 哈希算法
- 如何實(shí)施
1. 介紹
在上一篇文章中奈偏,我們構(gòu)建了一個(gè)非常簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu)钝鸽,這是區(qū)塊鏈數(shù)據(jù)庫(kù)的本質(zhì)妈踊。 而且我們可以用它們之間的鏈接向它添加區(qū)塊:每個(gè)區(qū)塊與前一個(gè)鏈接私沮。 唉矮男,然而在現(xiàn)實(shí)中添加一個(gè)區(qū)塊添加到鏈?zhǔn)切枰?strong>高成本的工作勋拟。
2. 工作證明
區(qū)塊鏈的一個(gè)關(guān)鍵思想是括荡,必須通過(guò)工作證明才能將數(shù)據(jù)放入其中疏遏。這是一個(gè)艱巨的工作揍障,使塊鏈安全和一致目养。此外,這筆辛苦的工作也得到了獎(jiǎng)勵(lì)(這是人們獲得采礦硬幣的方式)毒嫡。
這種機(jī)制與現(xiàn)實(shí)生活中的機(jī)制非常相似:人們必須工作獲酬勞勵(lì)并維持生命癌蚁。在網(wǎng)絡(luò)中,網(wǎng)絡(luò)的一些參與者(礦工)努力維持網(wǎng)絡(luò)兜畸,為其添加新的塊努释,并為他們的工作獲得獎(jiǎng)勵(lì)。作為其工作的結(jié)果咬摇,塊以安全的方式并入到塊鏈中伐蒂,這保持了整個(gè)塊鏈數(shù)據(jù)庫(kù)的穩(wěn)定性。值得注意的是肛鹏,完成工作的人必須證明這一點(diǎn)逸邦。
這個(gè)整體“努力工作和證明工作價(jià)值”機(jī)制被稱為工作證明。這很難因?yàn)樗枰芏嗟挠?jì)算能力:即使是高性能的計(jì)算機(jī)也不能很快的完成在扰。此外缕减,這項(xiàng)工作的難度不時(shí)增加,以保持新的塊率每小時(shí)大約6個(gè)塊芒珠。在比特幣桥狡,這樣的工作的目標(biāo)是找到一個(gè)塊的哈希,滿足一些要求皱卓。這是散列裹芝,作為證明。因此娜汁,找到證據(jù)是實(shí)際工作嫂易。
最后要注意的事情。工作證明算法必須滿足要求:工作不易存炮,證明容易炬搭。證明通常交給非工作者,所以對(duì)他們來(lái)說(shuō)穆桂,驗(yàn)證它不應(yīng)該花太多的時(shí)間宫盔。
3. 哈希算法
在本文中,我們將討論哈希算法享完。 如果你熟悉這個(gè)概念灼芭,你可以跳過(guò)這個(gè)部分。
哈希是獲取指定數(shù)據(jù)的哈希值的過(guò)程般又。 哈希值是對(duì)其計(jì)算的數(shù)據(jù)的唯一表示彼绷。 哈希函數(shù)是一個(gè)獲取任意大小的數(shù)據(jù)并產(chǎn)生固定大小的哈希的函數(shù)。 以下是哈希的一些主要功能:
- 原始數(shù)據(jù)無(wú)法從哈希值恢復(fù)茴迁。 因此寄悯,哈希過(guò)程不是加密。
- 數(shù)據(jù)只能有一個(gè)與之對(duì)應(yīng)的哈希值堕义,因此哈希是唯一的猜旬。
- 更改輸入數(shù)據(jù)中的一個(gè)字節(jié)將導(dǎo)致完全不同的散列。
Hashing functions are widely used to check the consistency of data. Some software providers publish checksums in addition to a software package. After downloading a file you can feed it to a hashing function and compare produced hash with the one provided by the software developer.
In blockchain, hashing is used to guarantee the consistency of a block. The input data for a hashing algorithm contains the hash of the previous block, thus making it impossible (or, at least, quite difficult) to modify a block in the chain: one has to recalculate its hash and hashes of all the blocks after it.
哈希函數(shù)被廣泛用于檢查數(shù)據(jù)的一致性倦卖。在區(qū)塊鏈中洒擦,使用哈希來(lái)保證塊的一致性。 哈希算法的輸入數(shù)據(jù)包含前一個(gè)塊的哈希值怕膛,從而使得已經(jīng)生成的鏈難以修改之前產(chǎn)生的區(qū)塊(或至少相當(dāng)困難):篡改一個(gè)區(qū)塊必須重新計(jì)算其前的所有塊的哈希值熟嫩。
譯者注: genesis block的previous block是空
哈希現(xiàn)金
比特幣使用Hashcash褐捻,哈系現(xiàn)金的發(fā)明最初是為防止電子郵件垃圾郵件而開(kāi)發(fā)的。它可以分為以下幾個(gè)步驟:
- 獲取公開(kāi)的數(shù)據(jù)(在電子郵件的情況下柠逞,它是接收者的電子郵件地址;在比特幣的情況下倦蚪,它是塊標(biāo)題)。
- 添加一個(gè)計(jì)數(shù)器边苹。計(jì)數(shù)器從0開(kāi)始陵且。
- 獲取
數(shù)據(jù)+計(jì)數(shù)器
組合的哈希值。 - 檢查哈希值是否符合要求个束。
- 如果滿足要求慕购,結(jié)束過(guò)程。
- 如果不滿足要求茬底,增加計(jì)數(shù)器并重復(fù)步驟3和4沪悲。
因此,這是一個(gè)強(qiáng)力brute force算法:
1. 計(jì)算一個(gè)新的哈希
2. 檢查該哈希值
3. 增加計(jì)數(shù)器
現(xiàn)在讓我們看看一個(gè)哈希必須滿足的要求阱表。在原來(lái)的Hashcash實(shí)現(xiàn)中“哈希的前20位必須是零”殿如。然而在比特幣中贡珊,哈希要求是不時(shí)進(jìn)行調(diào)整的,因?yàn)楸M管計(jì)算能力隨著時(shí)間的推移而增加涉馁,越來(lái)越多的礦工加入網(wǎng)絡(luò)门岔,因此設(shè)計(jì)必須每10分鐘生成一個(gè)塊。
4. 編寫(xiě)代碼
程序員小提醒:go和python都是不用加分號(hào)的語(yǔ)言
好的烤送,我們完成了理論寒随,讓我們編寫(xiě)代碼! 首先帮坚,我們來(lái)定義挖掘難度:
const targetBits = 24
4.1 目標(biāo)位
在比特幣中妻往,“目標(biāo)位(target bit)”是存儲(chǔ)塊被挖掘的困難的頭部數(shù)據(jù)。 我們現(xiàn)在不會(huì)實(shí)現(xiàn)目標(biāo)調(diào)整算法试和,所以我們可以將難度定義為全局常數(shù)讯泣。
24是一個(gè)任意數(shù)字,我們的目標(biāo)是在內(nèi)存中占用少于256位的目標(biāo)阅悍。 而且我們希望差異足夠大判帮,但不要太大,因?yàn)椴町愒酱蟾然业胶线m的哈希越難晦墙。
// 工作證明
type ProofOfWork struct {
block *Block
target *big.Int //定義目標(biāo)位
}
// 新的工作證明
func NewProofOfWork(b *Block) *ProofOfWork {
target := big.NewInt(1)
target.Lsh(target, uint(256-targetBits)) //用于隨機(jī)產(chǎn)生target,目標(biāo)數(shù)值k惹选I纬!這里從數(shù)學(xué)上保證了
// Lsh: local sensitivity hashing
//左移256個(gè) target bits位
pow := &ProofOfWork{b, target}
return pow
}
譯者注:這里從數(shù)學(xué)上保證了miner必須使用brute force的方式最終發(fā)現(xiàn)target:https://github.com/ekzhu/lsh
這里創(chuàng)建工作證明結(jié)構(gòu)中保存指向區(qū)塊的指針的和指向target的指針寡痰。 “target”是上一段所述要求的另一個(gè)名稱抗楔。 我們使用一個(gè)大整數(shù),因?yàn)槲覀儗?strong>哈希與目標(biāo)進(jìn)行比較:我們將哈希轉(zhuǎn)換為一個(gè)大整數(shù)拦坠,并檢查它是否小于target连躏。
在新的工作證明的函數(shù)中,我們初始化一個(gè)值為1的big.Int贞滨,并將其左移256個(gè) - targetBits位入热。 256是SHA-256哈希的長(zhǎng)度,以比特為單位晓铆,它是我們要使用的SHA-256散列算法勺良。 目標(biāo)的十六進(jìn)制表示為:
0x10000000000000000000000000000000000000000000000000000000000
它在內(nèi)存中占用29個(gè)字節(jié)。 這是與以前的例子中的哈希的比較:
0fac49161af82ed938add1d8725835cc123a1a87b1b196488360e58d4bfb51e3
0000010000000000000000000000000000000000000000000000000000000000
0000008b0f41ec78bab747864db66bcb9fb89920ee75f43fdaaeb5544f7f76ca
第一個(gè)哈希(以“我喜歡甜甜圈”計(jì)算)大于目標(biāo)骄噪,因此它不是有效的工作證明尚困。 第二個(gè)哈希(以“我喜歡甜甜圈ca07ca”計(jì)算)小于目標(biāo),因此這是一個(gè)有效的證明链蕊。
您可以將目標(biāo)視為范圍的上限:如果數(shù)字(哈希)低于邊界事甜,則它是有效的谬泌,反之亦然。 降低邊界將導(dǎo)致有效數(shù)量減少逻谦,因此找到有效數(shù)量所需的工作更加困難掌实。
4.2 準(zhǔn)備數(shù)據(jù)
//準(zhǔn)備數(shù)據(jù),加入targetBits和nonce
func (pow *ProofOfWork) prepareData(nonce int) []byte {
data := bytes.Join(
[][]byte{
pow.block.PrevBlockHash,
pow.block.Data,
IntToHex(pow.block.Timestamp),
IntToHex(int64(targetBits)),
IntToHex(int64(nonce)),
},
[]byte{},
)
return data
}
4.3 工作證明
func (pow *ProofOfWork) Run() (int, []byte) {
var hashInt big.Int
var hash [32]byte
nonce := 0
fmt.Printf("Mining the block containing \"%s\"\n", pow.block.Data)
// nounce 是counter
for nonce < maxNonce { // maxNounce被設(shè)置成math.MaxInt64跨跨,防止溢出
data := pow.prepareData(nonce) // 1. prepare data
hash = sha256.Sum256(data) // 2. sha256 hash:https://golang.org/pkg/crypto/sha256/#Sum256
fmt.Printf("\r%x", hash)
hashInt.SetBytes(hash[:]) // 3. 從hexidecimal 轉(zhuǎn)換成 big INT
//執(zhí)行這個(gè)for loop直到找到hashInt和target相等
if hashInt.Cmp(pow.target) == -1 { // 4. Compare integer with the target
break
} else {
nonce++
}
}
fmt.Print("\n\n")
return nonce, hash[:]
}
4.4. 給NewBlock()
加入nounce和工作證明
移除SetHash潮峦,并更改NewBlock:
- 產(chǎn)生新區(qū)塊
- 工作證明
func NewBlock(data string, prevBlockHash []byte) *Block {
block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}, 0}
// 工作證明
pow := NewProofOfWork(block)
nonce, hash := pow.Run()
block.Hash = hash[:]
block.Nonce = nonce
return block
}
nonce
被加入到Block結(jié)構(gòu)中
type Block struct {
Timestamp int64
Data []byte
PrevBlockHash []byte
Hash []byte
Nonce int // 用于驗(yàn)證
}
4.5. 驗(yàn)證工作證明 validate()
func (pow *ProofOfWork) Validate() bool {
var hashInt big.Int
data := pow.prepareData(pow.block.Nonce) // 驗(yàn)證
hash := sha256.Sum256(data)
hashInt.SetBytes(hash[:])
isValid := hashInt.Cmp(pow.target) == -1 //檢查產(chǎn)生的Big Int hashInt是否和target相當(dāng)
return isValid
}
func main() {
...
for _, block := range bc.blocks {
...
pow := NewProofOfWork(block)
fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate())) //驗(yàn)證工作證明
fmt.Println()
}
}
Output:
...
Prev. hash:
Data: Genesis Block
Hash: 00000093253acb814afb942e652a84a8f245069a67b5eaa709df8ac612075038
PoW: true
Prev. hash: 00000093253acb814afb942e652a84a8f245069a67b5eaa709df8ac612075038
Data: Send 1 BTC to Ivan
Hash: 0000003eeb3743ee42020e4a15262fd110a72823d804ce8e49643b5fd9d1062b
PoW: true
Prev. hash: 0000003eeb3743ee42020e4a15262fd110a72823d804ce8e49643b5fd9d1062b
Data: Send 2 more BTC to Ivan
Hash: 000000e42afddf57a3daa11b43b2e0923f23e894f96d1f24bfd9b8d2d494c57a
PoW: true
5. 總結(jié)
我們的塊鏈?zhǔn)且粋€(gè)更接近其實(shí)際架構(gòu)的一步:添加塊現(xiàn)在需要工作證明囱皿,因此mining是可能的勇婴。但是它仍然缺乏一些關(guān)鍵的特征:塊鏈數(shù)據(jù)庫(kù)不是持久性數(shù)據(jù),沒(méi)有錢(qián)包嘱腥,地址耕渴,交易,沒(méi)有共識(shí)機(jī)制齿兔。所有這些我們將在以后的文章中實(shí)現(xiàn)橱脸。
persistence refers to the characteristic of state that outlives the process that created it. This is achieved in practice by storing the state as data in computer data storage.
Links:
番外
不同branches中保存著各個(gè)階段的代碼
學(xué)習(xí)更多golang的知識(shí): https://golang.org/
關(guān)于如何Compile一個(gè)golang project