1. 程式人生 > 實用技巧 >區塊鏈,工作證明(POW)程式碼+原理 golang版剖析

區塊鏈,工作證明(POW)程式碼+原理 golang版剖析

介紹

在之前的文章中,我們構建了一個非常簡單的資料結構,這是塊鏈資料庫的本質。 而且我們可以用它們之間的鏈式關係向它新增區塊:每個區塊與前一個連結。 唉,然而在現實中新增一個區塊新增到鏈是艱鉅的工作。

工作證明

塊鏈的一個關鍵思想是,必須通過工作證明才能將資料放入其中。這是一個艱鉅的工作,使塊鏈安全和一致。此外,這筆辛苦的工作也得到了獎勵(這是人們獲得採礦硬幣的方式)。

這種機制與現實生活中的機制非常相似:人們必須工作獲酬勞勵並維持生命。在網路中,網路的一些參與者(礦工)努力維持網路,為其新增新的塊,併為他們的工作獲得獎勵。作為其工作的結果,塊以安全的方式併入到塊鏈中,這保持了整個塊鏈資料庫的穩定性。值得注意的是,完成工作的人必須證明這一點。

這個整體“努力工作和證明工作價值”機制被稱為工作證明。這很難因為它需要很多的計算能力:即使是高效能的計算機也不能很快的完成。此外,這項工作的難度不時增加,以保持新的塊率每小時大約6個塊。在比特幣,這樣的工作的目標是找到一個塊的雜湊,滿足一些要求。這是雜湊,作為證明。因此,找到證據是實際工作。

最後要注意的事情。工作證明演算法必須滿足要求:做完工作不易完成,證明工作容易完成。證明通常交給非工作者,所以對他們來說,驗證它不應該花太多的時間。

雜湊演算法加密

在本文中,我們將討論雜湊值。 如果你熟悉這個概念,你可以跳過這個部分。

雜湊是獲取指定資料的雜湊值的過程。 雜湊值是對其計算的資料的唯一表示。 雜湊函式是一個獲取任意大小的資料併產生固定大小的雜湊的函式。 以下是雜湊的一些主要功能:

  • 原始資料無法從雜湊值恢復。 因此,雜湊不是加密。

  • 資料只能有一個雜湊值,雜湊是唯一的。

  • 更改輸入資料中的一個位元組將導致完全不同的雜湊。

1.png

雜湊函式被廣泛用於檢查資料的一致性。在區塊鏈中,使用雜湊來保證塊的一致性。 雜湊演算法的輸入資料包含前一個塊的雜湊值,從而使得已經生成的鏈難以修改之前產生的區塊(或至少相當困難):必須重新計算其後的所有塊的雜湊值。

雜湊現金 、 Hashcash

比特幣使用Hashcash,雜湊現金的發明最初是為防止電子郵件垃圾郵件而開發的。它可以分為以下幾個步驟:

  1. 獲取公開的資料(在電子郵件的情況下,它是接收者的電子郵件地址;在比特幣的情況下,它是塊標題)。

  2. 新增一個計數器。計數器從0開始。

  3. 獲取資料+計數器組合的雜湊。

  4. 檢查雜湊值是否符合要求。

    1. 如果滿足要求,結束過程。

    2. 如果不滿足要求,增加計數器並重復步驟3和4。

因此,這是一個強力演算法:您更改計數器,計算一個新的雜湊,檢查它,增加計數器,計算雜湊等。這就是為什麼它在計算上是昂貴的。

現在讓我們看看一個雜湊必須滿足的要求。在原來的Hashcash實現中“雜湊的前20位必須是零”。然而在比特幣中,雜湊要求是不時進行調整的,因為儘管計算能力隨著時間的推移而增加,越來越多的礦工加入網路,因此設計必須每10分鐘生成一個塊

為了演示這個演算法,我從前面的例子中獲取了資料(“我喜歡甜甜圈”),並發現一個以0個零位元組開頭的雜湊:

編寫程式碼

程式設計師小提醒:go和python都是不用加分號的語言

好的,我們完成了理論,讓我們編寫程式碼! 首先,我們來定義挖掘的難度:

consttargetBits=24

In Bitcoin, “target bits” is the block header storing the difficulty at which the block was mined. We won’t implement a target adjusting algorithm, for now, so we can just define the difficulty as a global constant.

24 is an arbitrary number, our goal is to have a target that takes less than 256 bits in memory. And we want the difference to be significant enough, but not too big, because the bigger the difference the more difficult it’s to find a proper hash.

在比特幣中,“目標位(target bit)”是儲存塊被挖掘的困難的塊頭。 我們現在不會實現目標調整演算法,所以我們可以將難度定義為全域性常數

24是一個任意數字,我們的目標是在記憶體中佔用少於256位的目標。 而且我們希望差異足夠大,但不要太大,因為差異越大,找到合適的雜湊越難。

typeProofOfWorkstruct{
block*Block
target*big.Int//定義目標位
}

funcNewProofOfWork(b*Block)*ProofOfWork{
target:=big.NewInt(1)
target.Lsh(target,uint(256-targetBits))//左移256個targetbits位

pow:=&ProofOfWork{b,target}

returnpow
}

這裡建立儲存指向塊的指標的工作證明結構和指向目標的指標。 “目標”是上一段所述要求的另一個名稱。 我們使用一個大整數,因為我們將雜湊與目標進行比較:我們將雜湊轉換為一個大整數,並檢查它是否小於目標。

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

在新的工作證明的函式中,我們初始化一個值為1的big.Int,並將其左移256個 - targetBits位。 256是SHA-256雜湊的長度,以位元為單位,它是我們要使用的SHA-256雜湊演算法。 目標的十六進位制表示為:

0x10000000000000000000000000000000000000000000000000000000000

它在記憶體中佔用29個位元組。 這是與以前的例子中的雜湊的比較:

0fac49161af82ed938add1d8725835cc123a1a87b1b196488360e58d4bfb51e3
0000010000000000000000000000000000000000000000000000000000000000
0000008b0f41ec78bab747864db66bcb9fb89920ee75f43fdaaeb5544f7f76ca

第一個雜湊(以“我喜歡甜甜圈”計算)大於目標,因此它不是有效的工作證明。 第二個雜湊(以“我喜歡甜甜圈ca07ca”計算)小於目標,因此這是一個有效的證明。

您可以將目標視為範圍的上限:如果數字(雜湊)低於邊界,則它是有效的,反之亦然。 降低邊界將導致有效數量減少,因此找到有效數量所需的工作更加困難。

現在,對資料進行雜湊處理。

func(pow*ProofOfWork)prepareData(nonceint)[]byte{
data:=bytes.Join(
[][]byte{
pow.block.PrevBlockHash,
pow.block.Data,
IntToHex(pow.block.Timestamp),
IntToHex(int64(targetBits)),
IntToHex(int64(nonce)),
},
[]byte{},
)

returndata
}

我們只是將塊區域與目標和隨機數合併。 nonce這裡是從上面的Hashcash描述的計數器,這是加密術語。

好的,所有的準備工作都完成了,我們來實現PoW演算法的核心:

func(pow*ProofOfWork)Run()(int,[]byte){
varhashIntbig.Int
varhash[32]byte
nonce:=0

fmt.Printf("Miningtheblockcontaining\"%s\"\n",pow.block.Data)
fornonce<maxNonce{
data:=pow.prepareData(nonce)//準備資料
hash=sha256.Sum256(data)//SHA-256加密
fmt.Printf("\r%x",hash)
hashInt.SetBytes(hash[:])//講hash轉換成BigInteger

ifhashInt.Cmp(pow.target)==-1{
break
}else{
nonce++
}
}
fmt.Print("\n\n")

returnnonce,hash[:]
}

首先,我們初始化變數:hashInt是雜湊的整數表示; nonce是櫃檯。 接下來,我們執行一個“無限”迴圈:它受限於maxNonce,它等於math.MaxInt64; 這樣做是為了避免可能的隨機數溢位。 雖然我們的PoW實施的難度太低,以至於防止溢位,但最好是進行此檢查,以防萬一。

在迴圈中我們:

  • 準備資料

  • 用SHA-256進行雜湊。

  • 將雜湊轉換為大整數。

  • 將整數與目標進行比較。

現在我們可以刪除BlockSetHash方法並修改NewBlock函式:

funcNewBlock(datastring,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

returnblock
}

Here you can see thatnonceis saved as aBlockproperty. This is necessary becausenonceis required to verify a proof. TheBlockstructure now looks so:

typeBlockstruct{
Timestampint64
Data[]byte
PrevBlockHash[]byte
Hash[]byte
Nonceint
}

驗證工作證明

func(pow*ProofOfWork)Validate()bool{
varhashIntbig.Int

data:=pow.prepareData(pow.block.Nonce)
hash:=sha256.Sum256(data)
hashInt.SetBytes(hash[:])

isValid:=hashInt.Cmp(pow.target)==-1

returnisValid
}

主函式代程式碼再次檢查

funcmain(){
...

for_,block:=rangebc.blocks{
...
pow:=NewProofOfWork(block)
fmt.Printf("PoW:%s\n",strconv.FormatBool(pow.Validate()))
fmt.Println()
}
}

結論

我們的塊鏈是一個更接近其實際架構的一步:新增塊現在需要努力工作,因此挖掘是可能的。 但是它仍然缺乏一些關鍵的特徵:塊鏈資料庫不是持久的,沒有錢包,地址,交易,沒有共識機制。 所有這些我們將在以後的文章中實現的,現在,開採開採!


轉載於:https://blog.51cto.com/iceman123/2089546