区块链共识算法之DPOS(上)
简介
DPOS:Delegated Proof of Stake,委任权益证明。简而言之就是一群“有资格的群众”通过投票选举出少许“更有资格的大佬”来替群众“做事”。如果“更有资格的大佬”做的不好,一群“有资格的群众”通过投票也可以将这些大佬踢出“权力圈子”。一群“有资格的群众”怎么理解呢?就是你持有“某些东西”,通常又是“代币”。持有的越多,通常你的话语权就越大,也就意味着你的票“越牛批”
通常的实现一般分为2部分:
1、通过投票选举出少许“更有资格的大佬”
2、替群众“做事”
简单的代码模拟
下面用简单的代码来模拟上面所说的2部分, 代码实际比较简单,仅仅用来理解dpos的核心思想----选举+做事。另外代码是可以直接运行的,因为不熟悉go所以写的一般化。。。。
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"math/rand"
"sort"
"strconv"
"time"
)
const (
runForNodeNum = 10
superNodeMaxNum = 3
)
//节点类型
type Node struct {
Name string //参选节点名称
Votes int //获得的票数
}
type Block struct {
Index int
Timestamp string
Prehash string
Hash string
Data []byte
delegate *Node //哪个节点再出块
}
func GenerateGenesisBlock() *Block {
gene := Block{0, time.Now().String(), "0000000000000000000000000000000000000000000000000000000000000000", "", []byte("genesis block"), nil}
gene.Hash = hex.EncodeToString(blockHash(&gene))
return &gene
}
func blockHash(block *Block) []byte {
record := strconv.Itoa(block.Index) + block.Timestamp + block.Prehash + hex.EncodeToString(block.Data)
h := sha256.New()
h.Write([]byte(record))
hashed := h.Sum(nil)
return hashed
}
func (node *Node) GenerateNewBlock(lastBlock *Block, data []byte) *Block {
var newBlock = Block{lastBlock.Index + 1, time.Now().String(), lastBlock.Hash, "", data, nil}
newBlock.Hash = hex.EncodeToString(blockHash(&newBlock))
newBlock.delegate = node
fmt.Printf("%s(%d) produce ", node.Name, node.Votes)
fmt.Println(newBlock)
return &newBlock
}
var nodeArr = make([]Node, runForNodeNum)
func CreateNode() {
for i := 0; i < runForNodeNum; i++ {
name := fmt.Sprintf("runForNode%d", i+1)
nodeArr[i] = Node{name, 0}
}
}
func Vote(nodeChan chan<- []Node) {
for {
for i := 0; i < runForNodeNum; i++ {
rand.Seed(time.Now().UnixNano())
vote := rand.Intn(100) + 1
nodeArr[i].Votes = vote
time.Sleep(297005 * time.Nanosecond)
}
nodeChan <- nodeArr
}
}
func SortNodes(nodeArrAfterVote []Node) {
sort.Slice(nodeArrAfterVote, func(i int, j int) bool {
return nodeArrAfterVote[i].Votes > nodeArrAfterVote[j].Votes
})
}
func main() {
CreateNode()
nodeChan := make(chan []Node)
go Vote(nodeChan)
gene := GenerateGenesisBlock()
lastBlock := gene
nodeArrAfterVote := make([]Node, runForNodeNum)
for {
nodeArrAfterVoteTemp := <-nodeChan
//fmt.Println(nodeArrAfterVoteTemp)
copy(nodeArrAfterVote, nodeArrAfterVoteTemp)
SortNodes(nodeArrAfterVote)
fmt.Printf("\n")
fmt.Println(nodeArrAfterVote)
//fmt.Printf("%p\n", &nodeArrAfterVote[0]);
//fmt.Printf("%p\n", &nodeArr[0]);
//fmt.Printf("%p\n", &nodeArrAfterVoteTemp[0]);
for i := 0; i < superNodeMaxNum; i++ {
lastBlock = nodeArrAfterVote[i].GenerateNewBlock(lastBlock, []byte(fmt.Sprintf("new block id %d", lastBlock.Index)))
time.Sleep(1 * time.Second)
}
}
}
上面的代码简单的模拟了下 每轮出块完成后(3个节点轮流生产一个block/每轮),就去获取最新的投票结果(“有资格的群众”一直处于投票状态中,大佬也是要每轮竞选的,大佬压力也大啊),然后整个处于 出块->竞选->出块。。。。
最后
后面2节将详细分析著名公链EOS的源码-----dpos算法,一起探讨dpos算法不为人知的实现细节。