使用 Go 語(yǔ)言打造區(qū)塊鏈(二)

關(guān)于 第一篇: 參見(jiàn)笑來(lái)老師翻譯的版本:
lixiaolai.com/2017/09/28/building-blockchain-in-go-part-1/

  1. 介紹
  2. 工作證明
  3. 哈希算法
  4. 如何實(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è)步驟:

  1. 獲取公開(kāi)的數(shù)據(jù)(在電子郵件的情況下柠逞,它是接收者的電子郵件地址;在比特幣的情況下倦蚪,它是塊標(biāo)題)。
  2. 添加一個(gè)計(jì)數(shù)器边苹。計(jì)數(shù)器從0開(kāi)始陵且。
  3. 獲取數(shù)據(jù)+計(jì)數(shù)器組合的哈希值
  4. 檢查哈希值是否符合要求个束。
    1. 如果滿足要求慕购,結(jié)束過(guò)程。
    2. 如果不滿足要求茬底,增加計(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连躏。

big: https://golang.org/pkg/math/big/

在新的工作證明的函數(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:

  1. 產(chǎn)生新區(qū)塊
  2. 工作證明
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:

  1. Full source codes
  2. Blockchain hashing algorithm
  3. Proof of work
  4. Hashcash

番外

不同branches中保存著各個(gè)階段的代碼


學(xué)習(xí)更多golang的知識(shí): https://golang.org/
關(guān)于如何Compile一個(gè)golang project

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市分苇,隨后出現(xiàn)的幾起案子添诉,更是在濱河造成了極大的恐慌,老刑警劉巖医寿,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件栏赴,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡靖秩,警方通過(guò)查閱死者的電腦和手機(jī)须眷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)沟突,“玉大人花颗,你說(shuō)我怎么就攤上這事』菔茫” “怎么了扩劝?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)职辅。 經(jīng)常有香客問(wèn)我今野,道長(zhǎng),這世上最難降的妖魔是什么罐农? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任条霜,我火速辦了婚禮,結(jié)果婚禮上涵亏,老公的妹妹穿的比我還像新娘宰睡。我一直安慰自己蒲凶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布拆内。 她就那樣靜靜地躺著旋圆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪麸恍。 梳的紋絲不亂的頭發(fā)上灵巧,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音抹沪,去河邊找鬼刻肄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛融欧,可吹牛的內(nèi)容都是我干的敏弃。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼噪馏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼麦到!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起欠肾,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤瓶颠,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后刺桃,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體粹淋,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年虏肾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了廓啊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡封豪,死狀恐怖谴轮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吹埠,我是刑警寧澤第步,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站缘琅,受9級(jí)特大地震影響粘都,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜刷袍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一翩隧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧呻纹,春花似錦堆生、人聲如沸专缠。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)涝婉。三九已至,卻和暖如春蔗怠,著一層夾襖步出監(jiān)牢的瞬間墩弯,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工寞射, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渔工,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓怠惶,卻偏偏與公主長(zhǎng)得像涨缚,于是被迫代替她去往敵國(guó)和親轧粟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子策治,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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

  • 老媽最近常跟我說(shuō),她跟一起跳廣場(chǎng)舞的阿姨兰吟,不能愉快地玩耍了通惫。我問(wèn)她是因?yàn)槭裁础K艺f(shuō)了很多細(xì)節(jié)混蔼,讓我不禁對(duì)這樣的...
    陽(yáng)光鶩舞閱讀 298評(píng)論 0 0
  • 龐邊的Adrian閱讀 111評(píng)論 0 0
  • 忘了誰(shuí)說(shuō)過(guò)類似這樣一句話履腋,原話忘記了大概意思就是無(wú)論和誰(shuí)生活在一起最幸福的事就是可以在午夜夢(mèng)醒時(shí)分仍可以陪你寬寬心...
    無(wú)敵之盾閱讀 262評(píng)論 1 1
  • 《想象》 想象,可以彌補(bǔ)一切 你 很丑 但要想的美 《還有你》 我不僅有詩(shī)和遠(yuǎn)方 還有眼前的茍且 更重要的是惭嚣,還...
    石石頭閱讀 382評(píng)論 0 7