在上一篇文章中,我們構(gòu)建了一個非常簡單的數(shù)據(jù)結(jié)構(gòu),這是區(qū)塊鏈數(shù)據(jù)庫的本質(zhì)。 而且我們可以用它們之間的鏈接向它添加區(qū)塊:每個區(qū)塊與前一個鏈接。 唉,然而在現(xiàn)實中添加一個區(qū)塊添加到鏈是需要高成本的工作。
區(qū)塊鏈的一個關(guān)鍵思想是,必須通過工作證明才能將數(shù)據(jù)放入其中。這是一個艱巨的工作,使塊鏈安全和一致。此外,這筆辛苦的工作也得到了獎勵(這是人們獲得采礦硬幣的方式)。
這種機制與現(xiàn)實生活中的機制非常相似:人們必須工作獲酬勞勵并維持生命。在網(wǎng)絡(luò)中,網(wǎng)絡(luò)的一些參與者(礦工)努力維持網(wǎng)絡(luò),為其添加新的塊,并為他們的工作獲得獎勵。作為其工作的結(jié)果,塊以安全的方式并入到塊鏈中,這保持了整個塊鏈數(shù)據(jù)庫的穩(wěn)定性。值得注意的是,完成工作的人必須證明這一點。
這個整體“努力工作和證明工作價值”機制被稱為工作證明。這很難因為它需要很多的計算能力:即使是高性能的計算機也不能很快的完成。此外,這項工作的難度不時增加,以保持新的塊率每小時大約6個塊。在比特幣,這樣的工作的目標是找到一個塊的哈希,滿足一些要求。這是散列,作為證明。因此,找到證據(jù)是實際工作。
最后要注意的事情。工作證明算法必須滿足要求:工作不易,證明容易。證明通常交給非工作者,所以對他們來說,驗證它不應(yīng)該花太多的時間。
在本文中,我們將討論哈希算法。 如果你熟悉這個概念,你可以跳過這個部分。
哈希是獲取指定數(shù)據(jù)的哈希值的過程。 哈希值是對其計算的數(shù)據(jù)的唯一表示。 哈希函數(shù)是一個獲取任意大小的數(shù)據(jù)并產(chǎn)生固定大小的哈希的函數(shù)。 以下是哈希的一些主要功能:
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ū)塊鏈中,使用哈希來保證塊的一致性。 哈希算法的輸入數(shù)據(jù)包含前一個塊的哈希值,從而使得已經(jīng)生成的鏈難以修改之前產(chǎn)生的區(qū)塊(或至少相當困難):篡改一個區(qū)塊必須重新計算其前的所有塊的哈希值。
比特幣使用Hashcash,哈希現(xiàn)金的發(fā)明最初是為防止電子郵件垃圾郵件而開發(fā)的。它可以分為以下幾個步驟:
數(shù)據(jù)+計數(shù)器
組合的哈希值。因此,這是一個強力brute force算法:
1. 計算一個新的哈希2. 檢查該哈希值3. 增加計數(shù)器
現(xiàn)在讓我們看看一個哈希必須滿足的要求。在原來的Hashcash實現(xiàn)中“哈希的前20位必須是零”。然而在比特幣中,哈希要求是不時進行調(diào)整的,因為盡管計算能力隨著時間的推移而增加,越來越多的礦工加入網(wǎng)絡(luò),因此設(shè)計必須每10分鐘生成一個塊。
程序員小提醒:go和python都是不用加分號的語言
好的,我們完成了理論,讓我們編寫代碼! 首先,我們來定義挖掘難度:
const targetBits = 24
在比特幣中,“目標位(target bit)”是存儲塊被挖掘的困難的頭部數(shù)據(jù)。 我們現(xiàn)在不會實現(xiàn)目標調(diào)整算法,所以我們可以將難度定義為全局常數(shù)。
24是一個任意數(shù)字,我們的目標是在內(nèi)存中占用少于256位的目標。 而且我們希望差異足夠大,但不要太大,因為差異越大,找到合適的哈希越難。
// 工作證明type ProofOfWork struct { block *Block target *big.Int //定義目標位}// 新的工作證明func NewProofOfWork(b *Block) *ProofOfWork { target := big.NewInt(1) target.Lsh(target, uint(256-targetBits)) //用于隨機產(chǎn)生target,目標數(shù)值!!!這里從數(shù)學上保證了 // Lsh: local sensitivity hashing //左移256個 target bits位 pow := &ProofOfWork{b, target} return pow}
這里創(chuàng)建工作證明結(jié)構(gòu)中保存指向區(qū)塊的指針的和指向target的指針。 “target”是上一段所述要求的另一個名稱。 我們使用一個大整數(shù),因為我們將哈希與目標進行比較:我們將哈希轉(zhuǎn)換為一個大整數(shù),并檢查它是否小于target。
在新的工作證明的函數(shù)中,我們初始化一個值為1的big.Int,并將其左移256個 - targetBits位。 256是SHA-256哈希的長度,以比特為單位,它是我們要使用的SHA-256散列算法。 目標的十六進制表示為:
0x10000000000000000000000000000000000000000000000000000000000
它在內(nèi)存中占用29個字節(jié)。 這是與以前的例子中的哈希的比較:
0fac49161af82ed938add1d8725835cc123a1a87b1b196488360e58d4bfb51e300000100000000000000000000000000000000000000000000000000000000000000008b0f41ec78bab747864db66bcb9fb89920ee75f43fdaaeb5544f7f76ca
第一個哈希(以“我喜歡甜甜圈”計算)大于目標,因此它不是有效的工作證明。 第二個哈希(以“我喜歡甜甜圈ca07ca”計算)小于目標,因此這是一個有效的證明。
您可以將目標視為范圍的上限:如果數(shù)字(哈希)低于邊界,則它是有效的,反之亦然。 降低邊界將導致有效數(shù)量減少,因此找到有效數(shù)量所需的工作更加困難。
//準備數(shù)據(jù),加入targetBits和noncefunc (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}
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í)行這個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[:]}
NewBlock()
加入nounce和工作證明移除SetHash,并更改NewBlock:
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 // 用于驗證}
func (pow *ProofOfWork) Validate() bool { var hashInt big.Int data := pow.prepareData(pow.block.Nonce) // 驗證 hash := sha256.Sum256(data) hashInt.SetBytes(hash[:]) isValid := hashInt.Cmp(pow.target) == -1 //檢查產(chǎn)生的Big Int hashInt是否和target相當 return isValid}
func main() { ... for _, block := range bc.blocks { ... pow := NewProofOfWork(block) fmt.Printf("PoW: %s/n", strconv.FormatBool(pow.Validate())) //驗證工作證明 fmt.Println() }}
Output:
...Prev. hash:Data: Genesis BlockHash: 00000093253acb814afb942e652a84a8f245069a67b5eaa709df8ac612075038PoW: truePrev. hash: 00000093253acb814afb942e652a84a8f245069a67b5eaa709df8ac612075038Data: Send 1 BTC to IvanHash: 0000003eeb3743ee42020e4a15262fd110a72823d804ce8e49643b5fd9d1062bPoW: truePrev. hash: 0000003eeb3743ee42020e4a15262fd110a72823d804ce8e49643b5fd9d1062bData: Send 2 more BTC to IvanHash: 000000e42afddf57a3daa11b43b2e0923f23e894f96d1f24bfd9b8d2d494c57aPoW: true
我們的塊鏈是一個更接近其實際架構(gòu)的一步:添加塊現(xiàn)在需要工作證明,因此mining是可能的。但是它仍然缺乏一些關(guān)鍵的特征:塊鏈數(shù)據(jù)庫不是持久性數(shù)據(jù),沒有錢包,地址,交易,沒有共識機制。所有這些我們將在以后的文章中實現(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中保存著各個階段的代碼
新聞熱點
疑難解答
圖片精選