OSDN Git Service

fix detach vote (#164)
[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 // seq 0 is the genesis block
32 // seq 1 is the the block height 1, to block height RoundVoteBlockNums
33 // seq 2 is the block height RoundVoteBlockNums + 1 to block height 2 * RoundVoteBlockNums
34 // consensus node of the current round is the final result of previous round
35 func CalcVoteSeq(blockHeight uint64) uint64 {
36         if blockHeight == 0 {
37                 return 0
38         }
39         return (blockHeight-1)/consensus.RoundVoteBlockNums + 1
40 }
41
42 // VoteResult represents a snapshot of each round of DPOS voting
43 // Seq indicates the sequence of current votes, which start from zero
44 // NumOfVote indicates the number of votes each consensus node receives, the key of map represent public key
45 // Finalized indicates whether this vote is finalized
46 type VoteResult struct {
47         Seq         uint64
48         NumOfVote   map[string]uint64
49         BlockHash   bc.Hash
50         BlockHeight uint64
51 }
52
53 func (v *VoteResult) ApplyBlock(block *types.Block) error {
54         if v.BlockHash != block.PreviousBlockHash {
55                 return errors.New("block parent hash is not equals last block hash of vote result")
56         }
57
58         for _, tx := range block.Transactions {
59                 for _, input := range tx.Inputs {
60                         unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
61                         if !ok {
62                                 continue
63                         }
64
65                         pubkey := hex.EncodeToString(unVoteInput.Vote)
66                         v.NumOfVote[pubkey], ok = checked.SubUint64(v.NumOfVote[pubkey], unVoteInput.Amount)
67                         if !ok {
68                                 return errVotingOperationOverFlow
69                         }
70
71                         if v.NumOfVote[pubkey] == 0 {
72                                 delete(v.NumOfVote, pubkey)
73                         }
74                 }
75
76                 for _, output := range tx.Outputs {
77                         voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
78                         if !ok {
79                                 continue
80                         }
81
82                         pubkey := hex.EncodeToString(voteOutput.Vote)
83                         if v.NumOfVote[pubkey], ok = checked.AddUint64(v.NumOfVote[pubkey], voteOutput.Amount); !ok {
84                                 return errVotingOperationOverFlow
85                         }
86                 }
87         }
88
89         v.BlockHash = block.Hash()
90         v.BlockHeight = block.Height
91         v.Seq = CalcVoteSeq(block.Height)
92         return nil
93 }
94
95 func (v *VoteResult) ConsensusNodes() (map[string]*ConsensusNode, error) {
96         var nodes []*ConsensusNode
97         for pubkey, voteNum := range v.NumOfVote {
98                 if voteNum >= consensus.MinVoteNum {
99                         var xpub chainkd.XPub
100                         if err := xpub.UnmarshalText([]byte(pubkey)); err != nil {
101                                 return nil, err
102                         }
103
104                         nodes = append(nodes, &ConsensusNode{XPub: xpub, VoteNum: voteNum})
105                 }
106         }
107         // In principle, there is no need to sort all voting nodes.
108         // if there is a performance problem, consider the optimization later.
109         sort.Sort(byVote(nodes))
110         result := make(map[string]*ConsensusNode)
111         for i := 0; i < len(nodes) && i < consensus.NumOfConsensusNode; i++ {
112                 nodes[i].Order = uint64(i)
113                 result[nodes[i].XPub.String()] = nodes[i]
114         }
115         return result, nil
116 }
117
118 func (v *VoteResult) DetachBlock(block *types.Block) error {
119         if v.BlockHash != block.Hash() {
120                 return errors.New("block hash is not equals last block hash of vote result")
121         }
122
123         for i := len(block.Transactions) - 1; i >= 0; i-- {
124                 tx := block.Transactions[i]
125                 for _, input := range tx.Inputs {
126                         unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
127                         if !ok {
128                                 continue
129                         }
130
131                         pubkey := hex.EncodeToString(unVoteInput.Vote)
132                         if v.NumOfVote[pubkey], ok = checked.AddUint64(v.NumOfVote[pubkey], unVoteInput.Amount); !ok {
133                                 return errVotingOperationOverFlow
134                         }
135                 }
136
137                 for _, output := range tx.Outputs {
138                         voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
139                         if !ok {
140                                 continue
141                         }
142
143                         pubkey := hex.EncodeToString(voteOutput.Vote)
144                         v.NumOfVote[pubkey], ok = checked.SubUint64(v.NumOfVote[pubkey], voteOutput.Amount)
145                         if !ok {
146                                 return errVotingOperationOverFlow
147                         }
148
149                         if v.NumOfVote[pubkey] == 0 {
150                                 delete(v.NumOfVote, pubkey)
151                         }
152                 }
153         }
154
155         v.BlockHash = block.PreviousBlockHash
156         v.BlockHeight = block.Height - 1
157         v.Seq = CalcVoteSeq(block.Height - 1)
158         return nil
159 }
160
161 func (v *VoteResult) Fork() *VoteResult {
162         f := &VoteResult{
163                 Seq:         v.Seq,
164                 NumOfVote:   map[string]uint64{},
165                 BlockHash:   v.BlockHash,
166                 BlockHeight: v.BlockHeight,
167         }
168
169         for key, value := range v.NumOfVote {
170                 f.NumOfVote[key] = value
171         }
172         return f
173 }
174
175 func (v *VoteResult) IsFinalize() bool {
176         return v.BlockHeight%consensus.RoundVoteBlockNums == 0
177 }