OSDN Git Service

check vote output amount (#171)
[bytom/vapor.git] / protocol / state / vote_result.go
1 package state
2
3 import (
4         "encoding/hex"
5         "sort"
6
7         "github.com/vapor/config"
8         "github.com/vapor/consensus"
9         "github.com/vapor/crypto/ed25519/chainkd"
10         "github.com/vapor/errors"
11         "github.com/vapor/math/checked"
12         "github.com/vapor/protocol/bc"
13         "github.com/vapor/protocol/bc/types"
14 )
15
16 var errVotingOperationOverFlow = errors.New("voting operation result overflow")
17
18 type ConsensusNode struct {
19         XPub    chainkd.XPub
20         VoteNum uint64
21         Order   uint64
22 }
23
24 type byVote []*ConsensusNode
25
26 func (c byVote) Len() int { return len(c) }
27 func (c byVote) Less(i, j int) bool {
28         return c[i].VoteNum > c[j].VoteNum || (c[i].VoteNum == c[j].VoteNum && c[i].XPub.String() > c[j].XPub.String())
29 }
30 func (c byVote) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
31
32 // seq 0 is the genesis block
33 // seq 1 is the the block height 1, to block height RoundVoteBlockNums
34 // seq 2 is the block height RoundVoteBlockNums + 1 to block height 2 * RoundVoteBlockNums
35 // consensus node of the current round is the final result of previous round
36 func CalcVoteSeq(blockHeight uint64) uint64 {
37         if blockHeight == 0 {
38                 return 0
39         }
40         return (blockHeight-1)/consensus.RoundVoteBlockNums + 1
41 }
42
43 // VoteResult represents a snapshot of each round of DPOS voting
44 // Seq indicates the sequence of current votes, which start from zero
45 // NumOfVote indicates the number of votes each consensus node receives, the key of map represent public key
46 // Finalized indicates whether this vote is finalized
47 type VoteResult struct {
48         Seq         uint64
49         NumOfVote   map[string]uint64
50         BlockHash   bc.Hash
51         BlockHeight uint64
52 }
53
54 func (v *VoteResult) ApplyBlock(block *types.Block) error {
55         if v.BlockHash != block.PreviousBlockHash {
56                 return errors.New("block parent hash is not equals last block hash of vote result")
57         }
58
59         for _, tx := range block.Transactions {
60                 for _, input := range tx.Inputs {
61                         unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
62                         if !ok {
63                                 continue
64                         }
65
66                         pubkey := hex.EncodeToString(unVoteInput.Vote)
67                         v.NumOfVote[pubkey], ok = checked.SubUint64(v.NumOfVote[pubkey], unVoteInput.Amount)
68                         if !ok {
69                                 return errVotingOperationOverFlow
70                         }
71
72                         if v.NumOfVote[pubkey] == 0 {
73                                 delete(v.NumOfVote, pubkey)
74                         }
75                 }
76
77                 for _, output := range tx.Outputs {
78                         voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
79                         if !ok {
80                                 continue
81                         }
82
83                         pubkey := hex.EncodeToString(voteOutput.Vote)
84                         if v.NumOfVote[pubkey], ok = checked.AddUint64(v.NumOfVote[pubkey], voteOutput.Amount); !ok {
85                                 return errVotingOperationOverFlow
86                         }
87                 }
88         }
89
90         v.BlockHash = block.Hash()
91         v.BlockHeight = block.Height
92         v.Seq = CalcVoteSeq(block.Height)
93         return nil
94 }
95
96 func (v *VoteResult) ConsensusNodes() (map[string]*ConsensusNode, error) {
97         var nodes []*ConsensusNode
98         for pubkey, voteNum := range v.NumOfVote {
99                 if voteNum >= consensus.MinConsensusNodeVoteNum {
100                         var xpub chainkd.XPub
101                         if err := xpub.UnmarshalText([]byte(pubkey)); err != nil {
102                                 return nil, err
103                         }
104
105                         nodes = append(nodes, &ConsensusNode{XPub: xpub, VoteNum: voteNum})
106                 }
107         }
108         // In principle, there is no need to sort all voting nodes.
109         // if there is a performance problem, consider the optimization later.
110         sort.Sort(byVote(nodes))
111         result := make(map[string]*ConsensusNode)
112         for i := 0; i < len(nodes) && i < consensus.NumOfConsensusNode; i++ {
113                 nodes[i].Order = uint64(i)
114                 result[nodes[i].XPub.String()] = nodes[i]
115         }
116
117         if len(result) != 0 {
118                 return result, nil
119         }
120         return federationNodes(), nil
121 }
122
123 func federationNodes() map[string]*ConsensusNode {
124         voteResult := map[string]*ConsensusNode{}
125         for i, xpub := range config.CommonConfig.Federation.Xpubs {
126                 voteResult[xpub.String()] = &ConsensusNode{XPub: xpub, VoteNum: 0, Order: uint64(i)}
127         }
128         return voteResult
129 }
130
131 func (v *VoteResult) DetachBlock(block *types.Block) error {
132         if v.BlockHash != block.Hash() {
133                 return errors.New("block hash is not equals last block hash of vote result")
134         }
135
136         for i := len(block.Transactions) - 1; i >= 0; i-- {
137                 tx := block.Transactions[i]
138                 for _, input := range tx.Inputs {
139                         unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
140                         if !ok {
141                                 continue
142                         }
143
144                         pubkey := hex.EncodeToString(unVoteInput.Vote)
145                         if v.NumOfVote[pubkey], ok = checked.AddUint64(v.NumOfVote[pubkey], unVoteInput.Amount); !ok {
146                                 return errVotingOperationOverFlow
147                         }
148                 }
149
150                 for _, output := range tx.Outputs {
151                         voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
152                         if !ok {
153                                 continue
154                         }
155
156                         pubkey := hex.EncodeToString(voteOutput.Vote)
157                         v.NumOfVote[pubkey], ok = checked.SubUint64(v.NumOfVote[pubkey], voteOutput.Amount)
158                         if !ok {
159                                 return errVotingOperationOverFlow
160                         }
161
162                         if v.NumOfVote[pubkey] == 0 {
163                                 delete(v.NumOfVote, pubkey)
164                         }
165                 }
166         }
167
168         v.BlockHash = block.PreviousBlockHash
169         v.BlockHeight = block.Height - 1
170         v.Seq = CalcVoteSeq(block.Height - 1)
171         return nil
172 }
173
174 func (v *VoteResult) Fork() *VoteResult {
175         f := &VoteResult{
176                 Seq:         v.Seq,
177                 NumOfVote:   map[string]uint64{},
178                 BlockHash:   v.BlockHash,
179                 BlockHeight: v.BlockHeight,
180         }
181
182         for key, value := range v.NumOfVote {
183                 f.NumOfVote[key] = value
184         }
185         return f
186 }
187
188 func (v *VoteResult) IsFinalize() bool {
189         return v.BlockHeight%consensus.RoundVoteBlockNums == 0
190 }