日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

使用 go 实现 Proof of Stake 共识机制

發布時間:2025/3/21 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用 go 实现 Proof of Stake 共识机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

使用 go 實現 Proof of Stake 共識機制

什么是 Proof of Stake

在PoW中,節點之間通過hash的計算力來競賽以獲取下一個區塊的記賬權,而在PoS中,塊是已經鑄造好的,鑄造的過程是基于每個節點(Node)愿意作為抵押的令牌(Token)數量。如果驗證者愿意提供更多的令牌作為抵押品,他們就有更大的機會記賬下一個區塊并獲得獎勵。

實現 Proof of Stake 主要功能點

  • 我們將有一個中心化的TCP服務節點,其他節點可以連接該服務器
  • 最新的區塊鏈狀態將定期廣播到每個節點
  • 每個節點都能提議建立新的區塊
  • 基于每個節點的令牌數量,其中一個節點將隨機地(以令牌數作為加權值)作為獲勝者,并且將該區塊添加到區塊鏈中

實現 Proof of Stake

設置 TCP 服務器的端口

新建 .env,添加如下內容 PORT=9000

安裝依賴軟件

$ go get github.com/davecgh/go-spew/spew$ go get github.com/joho/godotenv
  • spew 在控制臺中格式化輸出相應的結果。

  • godotenv 可以從我們項目的根目錄的 .env 文件中讀取數據。

引入相應的包

新建 main.go,引入相應的包

package mainimport ("bufio""crypto/sha256""encoding/hex""encoding/json""fmt""io""log""math/rand""net""os""strconv""sync""time""github.com/davecgh/go-spew/spew""github.com/joho/godotenv" )

全局變量

// Block represents each 'item' in the blockchain type Block struct {Index intTimestamp stringBPM intHash stringPrevHash stringValidator string }// Blockchain is a series of validated Blocks var Blockchain []Block var tempBlocks []Block// candidateBlocks handles incoming blocks for validation var candidateBlocks = make(chan Block)// announcements broadcasts winning validator to all nodes var announcements = make(chan string)var mutex = &sync.Mutex{}// validators keeps track of open validators and balances var validators = make(map[string]int)
  • Block 是每個區塊的內容
  • Blockchain 是我們的官方區塊鏈,它只是一串經過驗證的區塊集合。每個區塊中的 PrevHash 與前面塊的 Hash 相比較,以確保我們的鏈是正確的。 tempBlocks 是臨時存儲單元,在區塊被選出來并添加到 BlockChain 之前,臨時存儲在這里
  • candidateBlocks 是 Block 的通道,任何一個節點在提出一個新塊時都將它發送到這個通道
  • announcements 也是一個通道,我們的主Go TCP服務器將向所有節點廣播最新的區塊鏈
  • mutex是一個標準變量,允許我們控制讀/寫和防止數據競爭
  • validators 是節點的存儲map,同時也會保存每個節點持有的令牌數

生成區塊

func generateBlock(oldBlock Block, BPM int, address string) (Block, error) {var newBlock Blockt := time.Now()newBlock.Index = oldBlock.Index + 1newBlock.Timestamp = t.String()newBlock.BPM = BPMnewBlock.PrevHash = oldBlock.HashnewBlock.Hash = calculateBlockHash(newBlock)newBlock.Validator = addressreturn newBlock, nil }

generateBlock 是用來創建新塊的。
newBlock.PrevHash 存儲的是上一個區塊的 Hash
newBlock.Hash 是通過 calculateBlockHash(newBlock) 生成的 Hash 。
newBlock.Validator 存儲的是獲取記賬權的節點地址

// SHA256 hasing // calculateHash is a simple SHA256 hashing function func calculateHash(s string) string {h := sha256.New()h.Write([]byte(s))hashed := h.Sum(nil)return hex.EncodeToString(hashed) }//calculateBlockHash returns the hash of all block information func calculateBlockHash(block Block) string {record := string(block.Index) + block.Timestamp + string(block.BPM) + block.PrevHashreturn calculateHash(record) }

calculateHash 函數會接受一個 string ,并且返回一個SHA256 hash 。

calculateBlockHash 是對一個 block 進行 hash,將一個 block 的所有字段連接到一起后,再調用 calculateHash 將字符串轉為 SHA256 hash 。

驗證區塊

我們通過檢查 Index 來確保它們按預期遞增。我們也檢查以確保我們 PrevHash 的確與 Hash 前一個區塊相同。最后,我們希望通過在當前塊上 calculateBlockHash 再次運行該函數來檢查當前塊的散列。

// isBlockValid makes sure block is valid by checking index // and comparing the hash of the previous block func isBlockValid(newBlock, oldBlock Block) bool {if oldBlock.Index+1 != newBlock.Index {return false}if oldBlock.Hash != newBlock.PrevHash {return false}if calculateBlockHash(newBlock) != newBlock.Hash {return false}return true }

驗證者

當一個驗證者連接到我們的TCP服務,我們需要提供一些函數達到以下目標:

  • 輸入令牌的余額(之前提到過,我們不做錢包等邏輯)
  • 接收區塊鏈的最新廣播
  • 接收驗證者贏得區塊的廣播信息
  • 將自身節點添加到全局的驗證者列表中(validators)
  • 輸入Block的BPM數據- BPM是每個驗證者的人體脈搏值
  • 提議創建一個新的區塊
func handleConn(conn net.Conn) {defer conn.Close()go func() {for {msg := <-announcementsio.WriteString(conn, msg)}}()// 驗證者地址var address string// 驗證者輸入他所擁有的 tokens,tokens 的值越大,越容易獲得新區塊的記賬權io.WriteString(conn, "Enter token balance:")scanBalance := bufio.NewScanner(conn)for scanBalance.Scan() {// 獲取輸入的數據,并將輸入的值轉為 intbalance, err := strconv.Atoi(scanBalance.Text())if err != nil {log.Printf("%v not a number: %v", scanBalance.Text(), err)return}t := time.Now()// 生成驗證者的地址address = calculateHash(t.String())// 將驗證者的地址和token 存儲到 validatorsvalidators[address] = balancefmt.Println(validators)break}io.WriteString(conn, "\nEnter a new BPM:")scanBPM := bufio.NewScanner(conn)go func() {for {// take in BPM from stdin and add it to blockchain after conducting necessary validationfor scanBPM.Scan() {bpm, err := strconv.Atoi(scanBPM.Text())// 如果驗證者試圖提議一個被污染(例如偽造)的block,例如包含一個不是整數的BPM,那么程序會拋出一個錯誤,我們會立即從我們的驗證器列表validators中刪除該驗證者,他們將不再有資格參與到新塊的鑄造過程同時丟失相應的抵押令牌。if err != nil {log.Printf("%v not a number: %v", scanBPM.Text(), err)delete(validators, address)conn.Close()}mutex.Lock()oldLastIndex := Blockchain[len(Blockchain)-1]mutex.Unlock()// 創建新的區塊,然后將其發送到 candidateBlocks 通道newBlock, err := generateBlock(oldLastIndex, bpm, address)if err != nil {log.Println(err)continue}if isBlockValid(newBlock, oldLastIndex) {candidateBlocks <- newBlock}io.WriteString(conn, "\nEnter a new BPM:")}}}()// 循環會周期性的打印出最新的區塊鏈信息for {time.Sleep(time.Minute)mutex.Lock()output, err := json.Marshal(Blockchain)mutex.Unlock()if err != nil {log.Fatal(err)}io.WriteString(conn, string(output)+"\n")}}
  • io.WriteString(conn, "Enter token balance:")允許驗證者輸入他持有的令牌數量,然后,該驗證者被分配一個 SHA256地址,隨后該驗證者地址和驗證者的令牌數被添加到驗證者列表validators 中。

  • 接著我們輸入BPM,驗證者的脈搏值,并創建一個單獨的Go協程來處理這塊兒邏輯

  • delete(validators, address) 如果驗證者試圖提議一個被污染(例如偽造)的 block,例如包含一個不是整數的BPM,那么程序會拋出一個錯誤,我們會立即從我們的驗證器列表 validators 中刪除該驗證者,他們將不再有資格參與到新塊的鑄造過程同時丟失相應的抵押令牌。

  • 正是因為這種抵押令牌的機制,使得PoS協議是一種更加可靠的機制。如果一個人試圖偽造和破壞,那么他將被抓住,并且失去所有抵押和未來的權益,因此對于惡意者來說,是非常大的威懾。

  • 接著,我們用 generateBlock 函數創建一個新的 block,然后將其發送到 candidateBlocks 通道進行進一步處理。將Block 發送到通道使用的語法: candidateBlocks <- newBlock

  • 最后會循環打印出最新的區塊鏈,這樣每個驗證者都能獲知最新的狀態。

選擇獲取記賬權的節點

下面是PoS的主要邏輯。我們需要編寫代碼以實現獲勝驗證者的選擇;他們所持有的令牌數量越高,他們就越有可能被選為勝利者。

// pickWinner creates a lottery pool of validators and chooses the validator who gets to forge a block to the blockchain // by random selecting from the pool, weighted by amount of tokens staked func pickWinner() {time.Sleep(30 * time.Second)mutex.Lock()temp := tempBlocksmutex.Unlock()lotteryPool := []string{}if len(temp) > 0 { // slightly modified traditional proof of stake algorithm // from all validators who submitted a block, weight them by the number of staked tokens // in traditional proof of stake, validators can participate without submitting a block to be forgedOUTER:for _, block := range temp { // if already in lottery pool, skipfor _, node := range lotteryPool {if block.Validator == node {continue OUTER}} // lock list of validators to prevent data racemutex.Lock()setValidators := validatorsmutex.Unlock() // 獲取驗證者的tokensk, ok := setValidators[block.Validator]if ok { // 向 lotteryPool 追加 k 條數據,k 代表的是當前驗證者的tokensfor i := 0; i < k; i++ {lotteryPool = append(lotteryPool, block.Validator)}}} // 通過隨機獲得獲勝節點的地址s := rand.NewSource(time.Now().Unix())r := rand.New(s)lotteryWinner := lotteryPool[r.Intn(len(lotteryPool))] // 把獲勝者的區塊添加到整條區塊鏈上,然后通知所有節點關于勝利者的消息for _, block := range temp {if block.Validator == lotteryWinner {mutex.Lock()Blockchain = append(Blockchain, block)mutex.Unlock()for _ = range validators {announcements <- "\nwinning validator: " + lotteryWinner + "\n"}break}}}mutex.Lock()tempBlocks = []Block{}mutex.Unlock() }
  • 每隔30秒,我們選出一個勝利者,這樣對于每個驗證者來說,都有時間提議新的區塊,參與到競爭中來。接著創建一個lotteryPool,它會持有所有驗證者的地址,這些驗證者都有機會成為一個勝利者。然后,對于提議塊的暫存區域,我們會通過if len(temp) > 0來判斷是否已經有了被提議的區塊。

  • 在OUTER FOR循環中,要檢查暫存區域是否和 lotteryPool 中存在同樣的驗證者,如果存在,則跳過。

  • 在以 k, ok := setValidators[block.Validator]開始的代碼塊中,我們確保了從temp中取出來的驗證者都是合法的,即這些驗證者在驗證者列表validators已存在。若合法,則把該驗證者加入到lotteryPool中。

  • 那么我們怎么根據這些驗證者持有的令牌數來給予他們合適的隨機權重呢?

    • 首先,用驗證者的令牌填充lotteryPool數組,例如一個驗證者有100個令牌,那么在lotteryPool中就將有100個元素填充;如果有1個令牌,那么將僅填充1個元素。

    • 然后,從lotteryPool中隨機選擇一個元素,元素所屬的驗證者即是勝利者,把勝利驗證者的地址賦值給lotteryWinner。這里能夠看出來,如果驗證者持有的令牌越多,那么他在數組中的元素也越多,他獲勝的概率就越大;同時,持有令牌很少的驗證者,也是有概率獲勝的。

  • 接著我們把獲勝者的區塊添加到整條區塊鏈上,然后通知所有節點關于勝利者的消息:announcements <- "\nwinning validator: " + lotteryWinner + "\n"。

  • 最后,清空tempBlocks,以便下次提議的進行。

主函數

func main() {err := godotenv.Load()if err != nil {log.Fatal(err)}// 創建初始區塊t := time.Now()genesisBlock := Block{}genesisBlock = Block{0, t.String(), 0, calculateBlockHash(genesisBlock), "", ""}spew.Dump(genesisBlock)Blockchain = append(Blockchain, genesisBlock)httpPort := os.Getenv("PORT")// 啟動 TCP 服務server, err := net.Listen("tcp", ":"+httpPort)if err != nil {log.Fatal(err)}log.Println("HTTP Server Listening on port :", httpPort)defer server.Close()// 啟動了一個Go routine 從 candidateBlocks 通道中獲取提議的區塊,然后填充到臨時緩沖區 tempBlocks 中go func() {for candidate := range candidateBlocks {mutex.Lock()tempBlocks = append(tempBlocks, candidate)mutex.Unlock()}}()// 啟動了一個Go routine 完成 pickWinner 函數go func() {for {pickWinner()}}()// 接收驗證者節點的連接for {conn, err := server.Accept()if err != nil {log.Fatal(err)}go handleConn(conn)} }
  • godotenv.Load() 會解析 .env 文件并將相應的Key/Value對都放到環境變量中,通過 os.Getenv 獲取
  • 然后創建一個創世區塊genesisBlock,形成了區塊鏈。
  • 接著啟動了Tcp服務,等待所有驗證者的連接。
  • 啟動了一個Go協程從 candidateBlocks 通道中獲取提議的區塊,然后填充到臨時緩沖區 tempBlocks 中,最后啟動了另外一個Go協程來完成 pickWinner 函數。
  • 最后的for循環,用來接收驗證者節點的連接。

運行

go run main.go 啟動您的Go程序和TCP服務器,并會打印出初始區塊的信息。

$ go run main.go (main.Block) {Index: (int) 0,Timestamp: (string) (len=50) "2018-05-08 16:45:27.14287 +0800 CST m=+0.000956793",BPM: (int) 0,Hash: (string) (len=64) "96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7",PrevHash: (string) "",Validator: (string) "" } 2018/05/08 16:45:27 HTTP Server Listening on port : 9000

打開新的終端,運行 nc localhost 9000,
輸入 tokens , 然后輸入 BPM

可以打開多個終端,輸入不同的 tokens ,來檢驗 PoS 算法

總結

以上是生活随笔為你收集整理的使用 go 实现 Proof of Stake 共识机制的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 日本免费小视频 | 久久久久亚洲av无码a片 | 亚洲国产精华液网站w | 免费看毛片的网站 | 啪啪av | 欧美成人三级在线观看 | 日本sm调教—视频|vk | 日本毛片在线看 | 亚洲av色区一区二区三区 | 国产情侣在线播放 | 欧美人狂配大交3d | 奇米影视999| 曰韩av | 秋霞国产午夜精品免费视频 | 国内自拍2020 | 免费毛片在线 | 青青草视频免费播放 | 亚洲精品久久久久久国 | jizz欧美大全 | www在线观看国产 | 午夜精品av | 30一40一50女人毛片 | 国产精品美女一区 | 你懂的在线观看网站 | 黄色片免费 | 毛茸茸多毛bbb毛多视频 | 亚洲激情五月 | 狠狠爱亚洲 | 在线免费av网 | 午夜三区 | 免费网站av | 日日热| 男人添女人下部高潮视频 | 97干干 | 脱美女衣服亲摸揉视频 | 超碰96在线 | 国产sm调教一区二区 | 青青免费在线视频 | 国产成人一区二区在线 | 玖玖色在线 | 欧美日韩在线视频免费 | 不卡一区二区在线观看 | 国产九一精品 | 午夜视频免费在线 | 免费在线看黄色 | 手机看片福利视频 | 波多野结衣亚洲视频 | 久久麻豆av| 欧美一卡| 女人喂男人奶水做爰视频 | 国产又粗又黄视频 | 日本美女a级片 | 亚洲精品一区二区三区蜜桃久 | 免费国产视频在线观看 | 少妇激情一区二区三区 | 中国一级特黄毛片 | 久久久久久免费 | jlzzzjlzzz国产免费观看 | 免费成人高清在线视频 | 天天爽夜夜爽 | 亚洲性xxx | 精品少妇一区二区三区密爱 | 国产熟妇一区二区三区四区 | 日本不卡视频一区二区 | 久久久久在线 | 欧美精品高清 | 七仙女欲春2一级裸体片 | 91蜜桃在线 | 亚洲你我色 | 爱涩av| 三级三级久久三级久久18 | 国产麻豆乱码精品一区二区三区 | gay男互凵gay男同偷精 | 久久白浆 | 亚洲午夜久久 | 亚洲成人av免费 | 美女看片| 精品久久久精品 | 福利在线看 | 色爽视频| 一级片在线 | 午夜福利视频合集1000 | 久久久久久久久久一区 | 懂色av| 国产女主播自拍 | 中文字幕视频在线观看 | 婷婷在线视频观看 | 欧美精品免费一区二区 | 91精品婷婷国产综合久久 | 日韩视频免费在线 | 在线免费观看黄色小视频 | 国产成人麻豆精品午夜在线 | 亚洲s码欧洲m码国产av | 日韩三级一区二区 | 日批视频免费播放 | 午夜国产小视频 | 国产在线第二页 | 丰满岳乱妇一区二区 | 黄色avv|