如何使用Go構建權益區塊鏈證明 | 區塊鏈研究實驗室

火幣網(huobi.com)最新可用網址(點擊下圖直達註冊!)


火必交易所,曾经的火币交易所!

欧易OKX三大交易所,稳定好用!

币安全球第一大交易所!安全!


隨著區塊鏈給世界市場帶來的革命,在作出預測之前瞭解基礎知識至關重要。在本文中,我們將探討權益證明的基礎知識,該證明是一種區塊鏈協議,類似於一種在區塊鏈中偽造新區塊的彩票方法。


本文的主要目標如下:
瞭解區塊鏈領域的當前性能趨勢。
通過GoLang中的一個工作示例學習權益證明。
升級您的計算機科學和Go編程技能。


這將是一個有趣的過程,讓我們開始編寫代碼。

瞭解權益證明



股權證明(PoS)的基礎實際上很簡單。當然,該系統的核心組件是區塊鏈本身。簡而言之,區塊鏈是一個不變的分類賬,每個單獨的區塊都是從之前的區塊以密碼方式構建的。您永遠都無法更改區塊鏈的任何部分,因為連接到網絡的每個人都可以輕松看到更改並駁斥您的區塊鏈版本。


在過程中創造新的塊是由您的區塊鏈協議定義,比特幣是基於工作量證明(PoW)協議構建的,當中需要越來越多的計算能力才能通過數學過程驗證以前的交易,所以每次您驗證區塊中包含的交易列表時,都會以比特幣的形式獲得獎勵。


因此,交易歷史的證明在於您所從事的工作量,而完成這項工作的人稱為“礦工”。PoW的一個日益嚴重的問題是,隨著時間的流逝,解決這些數學難題所需的巨大計算能力。


權益證明在根本上是不同的,所以您無需在核算和擴展區塊鏈上具有計算能力,而可以在區塊鏈網絡上“占用”一定數量的令牌(不一定是加密貨幣)。通常情況可以通過創建自己的“節點”來完成的,以方便您參與區塊鏈生態系統。




如果節點采取勤懇的工作態度,您將有更大的機會在區塊鏈中創造一個新的區塊,並獲得原始返還的獎勵。被選擇偽造下一個區塊的可能性也與您在網絡上投入的令牌數量成比例增加;反之如果采取散惰的工作態度,您的賭註可能會受到處罰甚至被完全撤回。這種獎勵和懲罰方法旨在促進區塊鏈中的誠實工作,而沒有與工作量證明相關的計算可擴展性瓶頸。


現在我們已經有瞭PoS與PoW的概念概念,讓我們繼續在Go中編寫一個有效的PoS示例。



Go中的權益證明

導入“區塊鏈”


首先,除瞭定義自定義對象類型外,我們還需要在項目中包含一些Go包。這是您需要的軟件包-我們將使用math/rand,crypto/sha256以及encoding/hex用於加密區塊鏈方法。當然,這errors是Go的標準!
package main
import ( "crypto/sha256" "encoding/hex" "errors" "fmt" "log" math "math/rand" "time")


接下來是我們的自定義數據類型。使用Go可以使此超級簡單structs。在這裡我們有3個習慣types,第一個是PoSNetwork我們有一個Blockchain字段,該字段是對實例的引用的數組Block struct。我們將通過BlockchainHead字段跟蹤最近添加的塊,並且還將有一系列對的引用以Node struct用作Validators。


type PoSNetwork struct { Blockchain []*Block BlockchainHead *Block Validators []*Node}
type Node struct { Stake int Address string}
type Block struct { Timestamp string PrevHash string Hash string ValidatorAddr string}


該Node結構將具有一個Stake字段,該字段代表它添加到網絡中的令牌數量。該地址將是一個隨機生成的字符串,以便我們可以跟蹤哪個節點成功驗證瞭下一個塊。
最後,Block struct將包含跟蹤區塊鏈所需的信息。Timestamp創建該塊時,我們將有一個for,來自上一個塊的前一個哈希,Hash表示自身以及Node驗證此塊的的地址-所有type string。


逐塊建造區塊鏈磚



這是我們構建區塊鏈的第一種方法。首先,在函數簽名中,將此方法附加到,PoSNetwork struct並將該結構引用為n。然後,我們Node將對a的引用作為唯一參數。我們將返回一個新的Block引用數組來表示new Blockchain,對a的引用Block將是new BlockchainHead,以及可能出現error毛病的可能。
您會看到,ValidateBlockchain()我們甚至在嘗試添加任何內容之前就立即調用瞭該方法。稍後我們將進行驗證,但是隻要知道我們發現要更改的區塊鏈,就會知道有邏輯要懲罰a Node。
func (n PoSNetwork) GenerateNewBlock(Validator *Node) ([]*Block, *Block, error) { if err := n.ValidateBlockchain(); err != nil { Validator.Stake -= 10 return n.Blockchain, n.BlockchainHead, err }
currentTime := time.Now().String()
newBlock := &Block { Timestamp: currentTime, PrevHash: n.BlockchainHead.Hash, Hash: NewBlockHash(n.BlockchainHead), ValidatorAddr: Validator.Address, }
if err := n.ValidateBlockCandidate(newBlock); err != nil { Validator.Stake -= 10 return n.Blockchain, n.BlockchainHead, err } else { n.Blockchain = append(n.Blockchain, newBlock) } return n.Blockchain, newBlock, nil}


在檢查Blockchain是否完好無損之後,我們獲得瞭系統的當前時間,將其存儲為Timestamp實例化new時的時間Block。我們還附上瞭Hash以前值,您可以輕松訪問BlockchainHead。之後我們將在NewBlockHash()上調用方法BlockchainHead,並將輸入的地址分配Node為我們的驗證器地址。


一旦新塊的字段被填滿,我們調用ValidateBlockCandidate()上新Block,看看有沒有錯誤。如果有,我們返回一層。如果沒有,我們將把新塊附加到區塊鏈上。


但是,NewBlockHash()是如何工作的呢?我們有兩個函數來完成為每個塊創建唯一Hash的任務。函數 New Block Hash ()隻是獲取 Block 的所有信息,並將其連接成一個字符串傳遞給 new Hash ()。
func NewBlockHash(block *Block) string { blockInfo := block.Timestamp + block.PrevHash + block.Hash + block.ValidatorAddr return newHash(blockInfo)}
func newHash(s string) string { h := sha256.New() h.Write([]byte(s)) hashed := h.Sum(nil) return hex.EncodeToString(hashed)}


接下來,newHash()將利用該crypto/sha256包創建一個存儲為的新SHA256對象h。然後,我們將輸入字符串s轉換為字節數組,並將其寫入h。最後,我們使用h.Sum()讓h進入的格式,我們可以調用hex.EncodeToString(),使我們有一個string為我們的最終輸出。


驗證我們的區塊鏈



如果你不能驗證,則區塊鏈就沒有用。在這裡,我們將ValidateBlockchain()方法附加到PoSNetwork結構,並返回一個可能的錯誤。如果區塊鏈是空的或者隻有一個區塊,我們無法確保它是正確的所以我們隻返回nil。


接下來,我們評估區塊鏈中每對塊之間的三個獨立條件。第一個檢查是前一個塊的哈希值是否等於當前塊為它的前一哈希值存儲的值。
func (n PoSNetwork) ValidateBlockchain() error { if len(n.Blockchain) <= 1 { return nil }
currBlockIdx := len(n.Blockchain)-1 prevBlockIdx := len(n.Blockchain)-2
for prevBlockIdx >= 0 { currBlock := n.Blockchain[currBlockIdx] prevBlock := n.Blockchain[prevBlockIdx] if currBlock.PrevHash != prevBlock.Hash { return errors.New("blockchain has inconsistent hashes") }
if currBlock.Timestamp <= prevBlock.Timestamp { return errors.New("blockchain has inconsistent timestamps") }
if NewBlockHash(prevBlock) != currBlock.Hash { return errors.New("blockchain has inconsistent hash generation") } currBlockIdx-- prevBlockIdx-- } return nil}
我們還要檢查是否在任何點上一個塊的時間戳比當前塊新。如果當前的Block是在2020年制造的,但之前的Blot是在2021年制造,此時就知道出問題瞭。


最後,我們還要直接計算Hash前一個的Block,我們仍然會取回Hash當前的Block。如果滿足這些條件中的任何一個,則error由於我們的區塊鏈處於篡改狀態,我們將返回。


現在我們已經驗證瞭整個區塊鏈,我們需要確保要添加的下一個Block也是有效的。遵循與上述相同的條件檢查,僅適用於要添加的單個新塊。
func (n PoSNetwork) ValidateBlockCandidate(newBlock *Block) error { if n.BlockchainHead.Hash != newBlock.PrevHash { return errors.New("blockchain HEAD hash is not equal to new block previous hash") }
if n.BlockchainHead.Timestamp >= newBlock.Timestamp { return errors.New("blockchain HEAD timestamp is greater than or equal to new block timestamp") }
if NewBlockHash(n.BlockchainHead) != newBlock.Hash { return errors.New("new block hash of blockchain HEAD does not equal new block hash") } return nil}


現在我們已經完成瞭向區塊鏈添加新區塊以及驗證其正確性的步驟。
那麼,我們如何決定何時添加新塊?


這就是我們的驗證器起作用的地方,對於每個與網絡有利益關系的節點,我們將通過抽獎方法隨機選擇一個節點,以偽造區塊鏈中的下一個區塊並獲得獎勵。




首先,我們首先需要一個Node。要添加新的Node給我們的PoSNetwork,我們稱之為NewNode()它接受的初始股份Node,並返回一個新的數組Node引用。這裡沒有什麼幻想,我們隻是追加到n.Validators數組並調用randAddress()以為new生成唯一地址Node。
func (n PoSNetwork) NewNode(stake int) []*Node { newNode := &Node{ Stake: stake, Address: randAddress(), } n.Validators = append(n.Validators, newNode) return n.Validators}
func randAddress() string { b := make([]byte, 16) _, _ = math.Read(b) return fmt.Sprintf("%x", b)}


那麼,我們如何實際選擇獲勝者呢?有一點概率和統計信息!在該SelectWinner()方法中,我們首先通過覆蓋范圍找到網絡內持有的全部股份n.Validators。我們還將所有權益大於零的節點添加到數組中winnerPool以進行可能的選擇。


如果發現winnerPool為空,則返回error。。然後,我們使用該Intn()方法選擇一個中獎號碼,該方法將從0到我們的總投註額之間選擇一個隨機數。
func (n PoSNetwork) SelectWinner() (*Node, error) { var winnerPool []*Node totalStake := 0 for _, node := range n.Validators { if node.Stake > 0 { winnerPool = append(winnerPool, node) totalStake += node.Stake } } if winnerPool == nil { return nil, errors.New("there are no nodes with stake in the network") } winnerNumber := math.Intn(totalStake) tmp := 0 for _, node := range n.Validators { tmp += node.Stake if winnerNumber < tmp { return node, nil } } return nil, errors.New("a winner should have been picked but wasn't")}


最後一部分是概率發揮作用的地方。為瞭使每個節點都有Stake與網絡中的總數成正比的獲勝機會,我們將Stake電流的增量累加Node到tmp變量中。如果在任何時候獲勝的數字小於tmp,Node則被選為我們的獲勝者。


匯集全部


我們現在所需要的就是將我們的函數和數據類型綁定在一起。我們在main()函數中做所有的事情,第一步是設置一個隨機種子與當前時間作為我們的輸入。不要使用時間作為隨機種子的輸入,因為它實際上會在解碼哈希輸出時引入安全漏洞。


在這個示例中,我們需要實例化一個新的ProofStake網絡,其中有一個被稱為Genesis塊,也就是我們所知的塊。區塊鏈中的第一個區塊。一旦我們這樣做,我們也設置網絡的BlockchainHead等於第一個塊。

func main() { // set random seed math.Seed(time.Now().UnixNano())
// generate an initial PoS network including a blockchain with a genesis block. genesisTime := time.Now().String() pos := &PoSNetwork{ Blockchain: []*Block{ { Timestamp: genesisTime, PrevHash: "", Hash: newHash(genesisTime), ValidatorAddr: "", }, }, } pos.BlockchainHead = pos.Blockchain[0]
// instantiate nodes to act as validators in our network pos.Validators = pos.NewNode(60) pos.Validators = pos.NewNode(40)
// build 5 additions to the blockchain for i := 0; i < 5; i++ { winner, err := pos.SelectWinner() if err != nil { log.Fatal(err) } winner.Stake += 10 pos.Blockchain, pos.BlockchainHead, err = pos.GenerateNewBlock(winner) if err != nil { log.Fatal(err) } fmt.Println("Round ", i) fmt.Println("\tAddress:", pos.Validators[0].Address, "-Stake:", pos.Validators[0].Stake) fmt.Println("\tAddress:", pos.Validators[1].Address, "-Stake:", pos.Validators[1].Stake) }
pos.PrintBlockchainInfo()}



然後,我們添加兩個節點的網絡作為驗證器與60和40令牌作為他們的初始股份。在五次迭代中,我們將為區塊鏈選擇一個新的贏傢,如果有任何錯誤,我們的程序將崩潰-因為做原型我們通過新選擇的贏傢產生一個新的塊,並打印出每一輪的每個節點的總樁。

最後,我們將打印出我們新制作的區塊鏈:

$ go run main.go Round 0 Address: f8d44bb083078de97b8428f4f9548130 -Stake: 70 Address: de6ae18584f02b3388569191a04a4b4a -Stake: 40Round 1 Address: f8d44bb083078de97b8428f4f9548130 -Stake: 70 Address: de6ae18584f02b3388569191a04a4b4a -Stake: 50Round 2 Address: f8d44bb083078de97b8428f4f9548130 -Stake: 80 Address: de6ae18584f02b3388569191a04a4b4a -Stake: 50Round 3 Address: f8d44bb083078de97b8428f4f9548130 -Stake: 90 Address: de6ae18584f02b3388569191a04a4b4a -Stake: 50Round 4 Address: f8d44bb083078de97b8428f4f9548130 -Stake: 100 Address: de6ae18584f02b3388569191a04a4b4a -Stake: 50Block 0 Info: Timestamp: 2021-04-12 MDT m=+0.000120025 Previous Hash:  Hash: c5d04de14efed52ce84889c6382f9d307d5b98093d93a84b419478 Validator Address: Block 1 Info: Timestamp: 2021-04-12 MDT m=+0.000277288 Previous Hash: c5d04de14efed52ce84889c6382f9d307d5b98093d93a Hash: d58e90a75b71ac62ef938fc0148314a7f864ad50bd702f959e2d27 Validator Address: f8d44bb083078de97b8428f4f9548130Block 2 Info: Timestamp: 2021-04-12 MDT m=+0.000306562 Previous Hash: d58e90a75b71ac62ef938fc0148314a7f864ad50bd702 Hash: e6bfdd6c2c869607e2d9a81b84ddf4478756fedff78a03746cde11 Validator Address: de6ae18584f02b3388569191a04a4b4aBlock 3 Info: Timestamp: 2021-04-12 MDT m=+0.000321755 Previous Hash: e6bfdd6c2c869607e2d9a81b84ddf4478756fedff78a0 Hash: 8e3dbacc4a610b1665658bc9e7238963eda0d5bbbf3ce809e8fa6e Validator Address: f8d44bb083078de97b8428f4f9548130Block 4 Info: Timestamp: 2021-04-12 MDT m=+0.000333024 Previous Hash: 8e3dbacc4a610b1665658bc9e7238963eda0d5bbbf3ce Hash: 22760f8deb96c354a4050a3c48741be062bccfa9c51571c170065a Validator Address: f8d44bb083078de97b8428f4f9548130Block 5 Info: Timestamp: 2021-04-12 MDT m=+0.000347521 Previous Hash: 22760f8deb96c354a4050a3c48741be062bccfa9c5157 Hash: d2a5047f7d8a7696c1d0fb9ec49b56d2e71bbcedaaebc83a18b7a5 Validator Address: f8d44bb083078de97b8428f4f9548130


作者:鏈三豐,來源:區塊鏈研究實驗室



返回列表页>>> 比特幣最新新聞