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

買賣虛擬貨幣

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


本文的主要目標如下:
  • 瞭解區塊鏈領域的當前效能趨勢。
  • 透過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


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

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读