A case analysis of blockchain based on DPoS consensus algorithm
1, Foreword
Earlier, we introduced the cases of PoW and PoS, and we will find that both of them have some disadvantages. For example, PoW consumes more energy, and the more coins PoS holds, the greater the probability of successful mining, which will lead to a widening gap between the rich and the poor, and people are reluctant to consume their own coins.
The full name of our DPoS is delegated proof of stack, that is, the share authorization certificate, which solves these deficiencies.
DPoS is that you vote to select a certain number of nodes to mine. The number of votes a user has is related to the number of coins he holds. This is very similar to a joint-stock company. Everyone votes to elect the members of the board of directors.
These selected power nodes as like as two peas have the same power to dig.
If a node digs a mine, he will share some of the coins to the people who vote for him.
1, Define blocks and blockchains
type Node struct { Name string Votes int } type Block struct { Index int Timestamp string Prehash string Hash string Data []byte delegate *Node }
I believe that old friends who have followed the previous articles of this column should know what the information in the block represents. Here, briefly, Index is the block height, TimeStamp is the TimeStamp, Data is some Data saved by the block, Hash is the Hash value of the current block, PrevHash is the Hash value of the previous block, and delegate is the miner of the block.
The node information here is not introduced before. Name is the node name and Votes is the number of Votes cast.
2, Generate Genesis block
func firstBlock() Block { gene := Block{0, time.Now().String(), "", "", []byte("first block"), nil} gene.Hash = string(blockHash(gene)) return gene }
The genesis block is the first block. We need to write one here. The calculation of hash value is described below.
3, Calculate hash value
func blockHash(block Block) []byte { hash := strconv.Itoa(block.Index) + block.Timestamp + block.Prehash + hex.EncodeToString(block.Data) h := sha256.New() h.Write([]byte(hash)) hashed := h.Sum(nil) return hashed }
Here is to splice all data together, and then calculate the hash value of the spliced data.
4, Generate new module
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 return newBlock }
According to the logic mentioned above, a new block is generated by putting these data into the block.
5, Create node
var NodeAddr = make([]Node, 10) func CreateNode() { for i := 0; i < 10; i++ { name := fmt.Sprintf("node %d Number of votes", i) //The number of votes during initialization is 0 NodeAddr[i] = Node{name, 0} } }
Suppose that our blockchain project has 10 nodes, then initialize the nodes, set the node name to node 0 to node 9, then initialize the votes to 0, and put the initialized nodes into the node list.
6, Simulated voting
func Vote() { for i := 0; i < 10; i++ { rand.Seed(time.Now().UnixNano()) time.Sleep(100000) vote := rand.Intn(10000) NodeAddr[i].Votes = vote fmt.Printf("node [%d] Number of votes [%d]\n", i, vote) } }
Here, we use random numbers to allocate the number of votes cast by nodes. Because we want to vote for 10 nodes, we traverse 10 times and vote for nodes in the range of 0-9999 each time.
7, Select mining node
func SortNodes() []Node { n := NodeAddr for i := 0; i < len(n); i++ { for j := 0; j < len(n)-1; j++ { if n[j].Votes < n[j+1].Votes { n[j], n[j+1] = n[j+1], n[j] } } } return n[:3] }
Then we select the voting nodes according to the number of votes. Here, we use bubble sorting to sort the nodes according to the number of votes. Finally, we select the first three nodes with the largest number of votes from the sorted list as mining nodes.
8, Main logic
func main() { CreateNode() fmt.Printf("List of nodes created: \n") fmt.Println(NodeAddr) fmt.Print("Node votes: \n") Vote() nodes := SortNodes() fmt.Print("winner: \n") fmt.Println(nodes) first := firstBlock() lastBlock := first fmt.Print("Start generating blocks: \n") for i := 0; i < len(nodes); i++ { fmt.Printf("[%s %d] Generate a new block\n", nodes[i].Name, nodes[i].Votes) lastBlock = nodes[i].GenerateNewBlock(lastBlock, []byte(fmt.Sprintf("new Block %d", i))) } }
The main logic is also relatively simple. First initialize 10 nodes, then vote, and then select the top three according to the number of votes. The first three are then used to generate new blocks.
Is it very simple? If you are interested, you can also take a look at the simple examples of PoW and PoS described above.