OSDN Git Service

edit
[bytom/vapor.git] / protocol / state / vote_result.go
1 package state
2
3 import (
4         "encoding/hex"
5         "sort"
6
7         "github.com/vapor/consensus"
8         "github.com/vapor/crypto/ed25519/chainkd"
9         "github.com/vapor/errors"
10         "github.com/vapor/math/checked"
11         "github.com/vapor/protocol/bc"
12         "github.com/vapor/protocol/bc/types"
13 )
14
15 var errVotingOperationOverFlow = errors.New("voting operation result overflow")
16
17 type ConsensusNode struct {
18         XPub    chainkd.XPub
19         VoteNum uint64
20         Order   uint64
21 }
22
23 type byVote []*ConsensusNode
24
25 func (c byVote) Len() int { return len(c) }
26 func (c byVote) Less(i, j int) bool {
27         return c[i].VoteNum > c[j].VoteNum || (c[i].VoteNum == c[j].VoteNum && c[i].XPub.String() > c[j].XPub.String())
28 }
29 func (c byVote) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
30
31 // VoteResult represents a snapshot of each round of DPOS voting
32 // Seq indicates the sequence of current votes, which start from zero
33 // NumOfVote indicates the number of votes each consensus node receives, the key of map represent public key
34 // Finalized indicates whether this vote is finalized
35 type VoteResult struct {
36         Seq           uint64
37         NumOfVote     map[string]uint64
38         LastBlockHash bc.Hash
39 }
40
41 func (v *VoteResult) ApplyBlock(block *types.Block) error {
42         if v.LastBlockHash != block.PreviousBlockHash {
43                 return errors.New("block parent hash is not equals last block hash of vote result")
44         }
45
46         for _, tx := range block.Transactions {
47                 for _, input := range tx.Inputs {
48                         unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
49                         if !ok {
50                                 continue
51                         }
52
53                         pubkey := hex.EncodeToString(unVoteInput.Vote)
54                         v.NumOfVote[pubkey], ok = checked.SubUint64(v.NumOfVote[pubkey], unVoteInput.Amount)
55                         if !ok {
56                                 return errVotingOperationOverFlow
57                         }
58
59                         if v.NumOfVote[pubkey] == 0 {
60                                 delete(v.NumOfVote, pubkey)
61                         }
62                 }
63
64                 for _, output := range tx.Outputs {
65                         voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
66                         if !ok {
67                                 continue
68                         }
69
70                         pubkey := hex.EncodeToString(voteOutput.Vote)
71                         if v.NumOfVote[pubkey], ok = checked.AddUint64(v.NumOfVote[pubkey], voteOutput.Amount); !ok {
72                                 return errVotingOperationOverFlow
73                         }
74                 }
75         }
76
77         v.LastBlockHash = block.Hash()
78         return nil
79 }
80
81 func (v *VoteResult) ConsensusNodes() (map[string]*ConsensusNode, error) {
82         var nodes []*ConsensusNode
83         for pubkey, voteNum := range v.NumOfVote {
84                 if voteNum >= consensus.MinVoteNum {
85                         var xpub chainkd.XPub
86                         if err := xpub.UnmarshalText([]byte(pubkey)); err != nil {
87                                 return nil, err
88                         }
89
90                         nodes = append(nodes, &ConsensusNode{XPub: xpub, VoteNum: voteNum})
91                 }
92         }
93         // In principle, there is no need to sort all voting nodes.
94         // if there is a performance problem, consider the optimization later.
95         sort.Sort(byVote(nodes))
96         result := make(map[string]*ConsensusNode)
97         for i := 0; i < len(nodes) && i < consensus.NumOfConsensusNode; i++ {
98                 nodes[i].Order = uint64(i)
99                 result[nodes[i].XPub.String()] = nodes[i]
100         }
101         return result, nil
102 }
103
104 func (v *VoteResult) DetachBlock(block *types.Block) error {
105         if v.LastBlockHash != block.Hash() {
106                 return errors.New("block hash is not equals last block hash of vote result")
107         }
108
109         for _, tx := range block.Transactions {
110                 for _, input := range tx.Inputs {
111                         unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
112                         if !ok {
113                                 continue
114                         }
115
116                         pubkey := hex.EncodeToString(unVoteInput.Vote)
117                         if v.NumOfVote[pubkey], ok = checked.AddUint64(v.NumOfVote[pubkey], unVoteInput.Amount); !ok {
118                                 return errVotingOperationOverFlow
119                         }
120                 }
121
122                 for _, output := range tx.Outputs {
123                         voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
124                         if !ok {
125                                 continue
126                         }
127
128                         pubkey := hex.EncodeToString(voteOutput.Vote)
129                         v.NumOfVote[pubkey], ok = checked.SubUint64(v.NumOfVote[pubkey], voteOutput.Amount)
130                         if !ok {
131                                 return errVotingOperationOverFlow
132                         }
133
134                         if v.NumOfVote[pubkey] == 0 {
135                                 delete(v.NumOfVote, pubkey)
136                         }
137                 }
138         }
139
140         v.LastBlockHash = block.PreviousBlockHash
141         return nil
142 }