【go共識演算法】-DPOS
DPOS介紹
概念
Delegated Proof of Stake,委任權益證明。
中文名叫做股份授權證明機制(又稱受託人機制),它的原理是讓每一個持有位元股的人進行投票,由此產生101位代表 , 我們可以將其理解為101個超級節點或者礦池,而這101個超級節點彼此的權利是完全相等的。從某種角度來看,DPOS有點像是議會制度或人民代表大會制度。如果代表不能履行他們的職責(當輪到他們時,沒能生成區塊),他們會被除名,網路會選出新的超級節點來取代他們。DPOS的出現最主要還是因為礦機的產生,大量的算力在不瞭解也不關心比特幣的人身上,類似演唱會的黃牛,大量囤票而絲毫不關心演唱會的內容。
位元股引入了見證人這個概念,見證人可以生成區塊,每一個持有位元股的人都可以投票選舉見證人。得到總同意票數中的前N個(N通常定義為101)候選者可以當選為見證人,當選見證人的個數(N)需滿足:至少一半的參與投票者相信N已經充分地去中心化。
見證人的候選名單每個維護週期(1天)更新一次。見證人然後隨機排列,每個見證人按序有2秒的許可權時間生成區塊,若見證人在給定的時間片不能生成區塊,區塊生成許可權交給下一個時間片對應的見證人。DPoS的這種設計使得區塊的生成更為快速,也更加節能。
DPoS充分利用了持股人的投票,以公平民主的方式達成共識,他們投票選出的N個見證人,可以視為N個礦池,而這N個礦池彼此的權利是完全相等的。持股人可以隨時通過投票更換這些見證人(礦池),只要他們提供的算力不穩定,計算機宕機,或者試圖利用手中的權力作惡。
應用
- 位元股 Bitshares(提出這個概念)
- EOS
共識演算法我DPoS + BFT
- Asch
共識演算法為DPoS + PBFT, 有101個受託人, 目前正在開放競選
go實現DPOS
package main import ( "time" "encoding/hex" "math/rand" "log" "sort" "crypto/sha256" ) //定義區塊結構體 type Block struct { Index int Timestamp string BPM int Hash string PrevHash string Delegate string } // 建立區塊函式 func generateBlock(oldBlock Block, _BMP int, address string) (Block, error) { var newBlock Block t := time.Now() newBlock.Index = oldBlock.Index + 1 newBlock.Timestamp = t.String() newBlock.BPM = _BMP newBlock.PrevHash = oldBlock.Hash newBlock.Hash = createBlockHash(newBlock) newBlock.Delegate = address return newBlock, nil } //生成區塊hash func createBlockHash(block Block) string { record := string(block.Index) + block.Timestamp + string(block.BPM) + block.PrevHash sha3 := sha256.New() sha3.Write([] byte(record)) hash := sha3.Sum(nil) return hex.EncodeToString(hash) } // 簡單的檢驗區塊函式 func isBlockValid(newBlock, oldBlock Block) bool{ if oldBlock.Index + 1 != newBlock.Index { return false } if newBlock.PrevHash != oldBlock.Hash { return false } return true } // 區塊集合 var blockChain []Block // dpos裡的超級節點結構體(受託人) type Trustee struct { name string votes int } type trusteeList [] Trustee // 下面的三個函式是為了排序使用,大家可以查下go的排序還是很強大的 func (_trusteeList trusteeList) Len() int { return len(_trusteeList) } func (_trusteeList trusteeList) Swap(i, j int){ _trusteeList[i], _trusteeList[j] = _trusteeList[j], _trusteeList[i] } func (_trusteeList trusteeList) Less(i, j int) bool { return _trusteeList[j].votes < _trusteeList[i].votes } // 選舉獲得投票數最高的前5節點作為超級節點,並打亂其順序 func selectTrustee() ([]Trustee){ _trusteeList := [] Trustee { {"node1", rand.Intn(100)}, {"node2", rand.Intn(100)}, {"node3", rand.Intn(100)}, {"node4", rand.Intn(100)}, {"node5", rand.Intn(100)}, {"node6", rand.Intn(100)}, {"node7", rand.Intn(100)}, {"node8", rand.Intn(100)}, {"node9", rand.Intn(100)}, {"node10", rand.Intn(100)}, {"node11", rand.Intn(100)}, {"node12", rand.Intn(100)}, } sort.Sort(trusteeList(_trusteeList)) result := _trusteeList[:5] _trusteeList = result[1:] _trusteeList = append(_trusteeList, result[0]) log.Println("當前超級節點列表是", _trusteeList) return _trusteeList } func main() { t := time.Now() // init gensis block(建立創世塊,真正的可不是這麼簡單的,這裡只做流程實現) genesisBlock := Block{0, t.String(), 0, createBlockHash(Block{}), "", ""} blockChain = append(blockChain, genesisBlock) // 這裡只是完成了一次dpos的寫區塊操作,eos真正的是每個節點實現6個區塊,並且所有超級節點(21個)輪完,之後再做選舉 var trustee Trustee for _, trustee = range selectTrustee() { _BPM := rand.Intn(100) blockHeight := len(blockChain) oldBlock := blockChain[blockHeight - 1] newBlock, err := generateBlock(oldBlock, _BPM, trustee.name) if err != nil { log.Println(err) continue } if isBlockValid(newBlock, oldBlock) { blockChain = append(blockChain, newBlock) log.Println("當前操作區塊的節點是: ", trustee.name) log.Println("當前區塊數量是: ", len(blockChain) - 1) log.Println("當前區塊資訊: ", blockChain[len(blockChain) - 1]) } } }