使用 Go 語言打造區塊鏈(二)
關於 第一篇: 參見笑來老師翻譯的版本:
lixiaolai.com/2017/09/28/building-blockchain-in-go-part-1/
- 作者:Ivan Kuznetsov
- 中文翻譯:曉頓
- 原文連結:https://jeiwan.cc/posts/building-blockchain-in-go-part-2/
- 介紹
- 工作證明
- 雜湊演算法
- 如何實施
1. 介紹
在上一篇文章中,我們構建了一個非常簡單的資料結構,這是區塊鏈資料庫的本質。 而且我們可以用它們之間的連結向它新增區塊:每個區塊與前一個連結。 唉,然而在現實中新增一個區塊新增到鏈是需要高成本的工作。
2. 工作證明
區塊鏈的一個關鍵思想是,必須通過工作證明才能將資料放入其中。這是一個艱鉅的工作,使塊鏈安全和一致。此外,這筆辛苦的工作也得到了獎勵(這是人們獲得採礦硬幣的方式)。
這種機制與現實生活中的機制非常相似:人們必須工作獲酬勞勵並維持生命。在網路中,網路的一些參與者(礦工)努力維持網路,為其新增新的塊,併為他們的工作獲得獎勵。作為其工作的結果,塊以安全的方式併入到塊鏈中,這保持了整個塊鏈資料庫的穩定性。值得注意的是,完成工作的人必須證明這一點。
這個整體“努力工作和證明工作價值”機制被稱為工作證明。這很難因為它需要很多的計算能力:即使是高效能的計算機也不能很快的完成。此外,這項工作的難度不時增加,以保持新的塊率每小時大約6個塊。在比特幣,這樣的工作的目標是找到一個塊的雜湊,滿足一些要求。這是雜湊,作為證明。因此,找到證據是實際工作。
最後要注意的事情。工作證明演算法必須滿足要求:工作不易,證明容易。證明通常交給非工作者,所以對他們來說,驗證它不應該花太多的時間。
3. 雜湊演算法
在本文中,我們將討論雜湊演算法。 如果你熟悉這個概念,你可以跳過這個部分。
雜湊是獲取指定資料的雜湊值的過程。 雜湊值是對其計算的資料的唯一表示。 雜湊函式是一個獲取任意大小的資料併產生固定大小的雜湊的函式。 以下是雜湊的一些主要功能:
- 原始資料無法從雜湊值恢復。 因此,雜湊過程不是加密。
- 資料只能有一個與之對應的雜湊值,因此雜湊是唯一的。
- 更改輸入資料中的一個位元組將導致完全不同的雜湊。
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.
雜湊函式被廣泛用於檢查資料的一致性。在區塊鏈中,使用雜湊來保證塊的一致性。 雜湊演算法的輸入資料包含前一個塊的雜湊值,從而使得已經生成的鏈難以修改之前產生的區塊(或至少相當困難):篡改一個區塊必須重新計算其前的所有塊的雜湊值。
譯者注: genesis block的previous block是空
雜湊現金
比特幣使用Hashcash,雜湊現金的發明最初是為防止電子郵件垃圾郵件而開發的。它可以分為以下幾個步驟:
- 獲取公開的資料(在電子郵件的情況下,它是接收者的電子郵件地址;在比特幣的情況下,它是塊標題)。
- 新增一個計數器。計數器從0開始。
- 獲取
資料+計數器
組合的雜湊值。 - 檢查雜湊值是否符合要求。
- 如果滿足要求,結束過程。
- 如果不滿足要求,增加計數器並重復步驟3和4。
因此,這是一個強力brute force演算法:
1. 計算一個新的雜湊
2. 檢查該雜湊值
3. 增加計數器
現在讓我們看看一個雜湊必須滿足的要求。在原來的Hashcash實現中“雜湊的前20位必須是零”。然而在比特幣中,雜湊要求是不時進行調整的,因為儘管計算能力隨著時間的推移而增加,越來越多的礦工加入網路,因此設計必須每10分鐘生成一個塊。
4. 編寫程式碼
程式設計師小提醒:go和python都是不用加分號的語言
好的,我們完成了理論,讓我們編寫程式碼! 首先,我們來定義挖掘難度:
const targetBits = 24
4.1 目標位
在比特幣中,“目標位(target bit)”是儲存塊被挖掘的困難的頭部資料。 我們現在不會實現目標調整演算法,所以我們可以將難度定義為全域性常數。
24是一個任意數字,我們的目標是在記憶體中佔用少於256位的目標。 而且我們希望差異足夠大,但不要太大,因為差異越大,找到合適的雜湊越難。
// 工作證明
type ProofOfWork struct {
block *Block
target *big.Int //定義目標位
}
// 新的工作證明
func NewProofOfWork(b *Block) *ProofOfWork {
target := big.NewInt(1)
target.Lsh(target, uint(256-targetBits)) //用於隨機產生target,目標數值!!!這裡從數學上保證了
// Lsh: local sensitivity hashing
//左移256個 target bits位
pow := &ProofOfWork{b, target}
return pow
}
譯者注:這裡從數學上保證了miner必須使用brute force的方式最終發現target:https://github.com/ekzhu/lsh
這裡建立工作證明結構中儲存指向區塊的指標的和指向target的指標。 “target”是上一段所述要求的另一個名稱。 我們使用一個大整數,因為我們將雜湊與目標進行比較:我們將雜湊轉換為一個大整數,並檢查它是否小於target。
在新的工作證明的函式中,我們初始化一個值為1的big.Int,並將其左移256個 - targetBits位。 256是SHA-256雜湊的長度,以位元為單位,它是我們要使用的SHA-256雜湊演算法。 目標的十六進位制表示為:
0x10000000000000000000000000000000000000000000000000000000000
它在記憶體中佔用29個位元組。 這是與以前的例子中的雜湊的比較:
0fac49161af82ed938add1d8725835cc123a1a87b1b196488360e58d4bfb51e3
0000010000000000000000000000000000000000000000000000000000000000
0000008b0f41ec78bab747864db66bcb9fb89920ee75f43fdaaeb5544f7f76ca
第一個雜湊(以“我喜歡甜甜圈”計算)大於目標,因此它不是有效的工作證明。 第二個雜湊(以“我喜歡甜甜圈ca07ca”計算)小於目標,因此這是一個有效的證明。
您可以將目標視為範圍的上限:如果數字(雜湊)低於邊界,則它是有效的,反之亦然。 降低邊界將導致有效數量減少,因此找到有效數量所需的工作更加困難。
4.2 準備資料
//準備資料,加入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被設定成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 轉換成 big INT
//執行這個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:
- 產生新區塊
- 工作證明
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結構中
type Block struct {
Timestamp int64
Data []byte
PrevBlockHash []byte
Hash []byte
Nonce int // 用於驗證
}
4.5. 驗證工作證明 validate()
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 //檢查產生的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 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. 總結
我們的塊鏈是一個更接近其實際架構的一步:新增塊現在需要工作證明,因此mining是可能的。但是它仍然缺乏一些關鍵的特徵:塊鏈資料庫不是永續性資料,沒有錢包,地址,交易,沒有共識機制。所有這些我們將在以後的文章中實現。
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中儲存著各個階段的程式碼
學習更多golang的知識: https://golang.org/
關於如何Compile一個golang project
相關文章
- 使用Go語言從零編寫PoS區塊鏈(譯)Go區塊鏈
- go 語言與區塊鏈基礎講解Go區塊鏈
- 區塊鏈開發之Go語言—IO操作區塊鏈Go
- 實戰區塊鏈技術培訓之Go語言區塊鏈Go
- GO語言實現區塊鏈Part7 NetworkGo區塊鏈
- GO語言實現區塊鏈Part6 Transactions 2Go區塊鏈
- GO語言實現區塊鏈Part4 Transactions 1Go區塊鏈
- GO語言實現區塊鏈Part3 Persistence and CLIGo區塊鏈
- GO語言實現區塊鏈Part1 Basic PrototypeGo區塊鏈
- 區塊鏈,中心去,何曾著眼看君王?用Go語言實現區塊鏈技術,透過Golang秒懂區塊鏈區塊鏈Golang
- GO語言實現區塊鏈Part5 AddressesGo區塊鏈
- GO語言實現區塊鏈Part2 Proof-of-WorkGo區塊鏈
- 比原鏈CTO James | Go語言成為區塊鏈主流開發語言的四點理由Go區塊鏈
- Go 語言區塊鏈測試實踐指南(一):GO單元測試Go區塊鏈
- 打造一個最小區塊鏈區塊鏈
- 基於Java語言構建區塊鏈(二)—— 工作量證明Java區塊鏈
- 【Go區塊鏈開發】手把手教你匯入Go語言第三方庫Go區塊鏈
- 區塊鏈有哪些特徵,幣信打造安全好用的區塊鏈區塊鏈特徵
- 使用Go語言製作二維碼Go
- [譯] 用不到 200 行的 GO 語言編寫您自己的區塊鏈Go區塊鏈
- 區塊鏈特輯——solidity語言基礎(三)區塊鏈Solid
- Red 語言建立基金會,發力區塊鏈區塊鏈
- 區塊鏈特輯——solidity語言基礎(六)區塊鏈Solid
- 區塊鏈特輯——solidity語言基礎(七)區塊鏈Solid
- go語言使用Go
- 基於Java語言構建區塊鏈(四)—— 交易(UTXO)Java區塊鏈
- 區塊鏈開發入門如何選擇程式語言?區塊鏈
- Go 語言 := 的具體使用與區別Go
- 區塊鏈開發公司區塊鏈+遊戲”二者結合區塊鏈遊戲
- 區塊鏈程式設計(二)區塊鏈程式設計
- 區塊鏈教程、區塊鏈指南、區塊鏈中文手冊、區塊鏈原理區塊鏈
- 區塊鏈背後的資訊保安(1)AES加密演算法原理及其GO語言實現區塊鏈加密演算法Go
- 基於Java語言構建區塊鏈(五)—— 地址(錢包)Java區塊鏈
- 基於Java語言構建區塊鏈(一)—— 基本原型Java區塊鏈原型
- 用於加密貨幣和區塊鏈的語言Simplicity加密區塊鏈
- 上海/GO工作/區塊鏈領域公司Go區塊鏈
- 區塊鏈程式設計go(四)-交易區塊鏈程式設計Go
- 深知區塊鏈,可你知道區塊鏈的專業術語嗎?區塊鏈