7 "github.com/vapor/consensus"
8 "github.com/vapor/config"
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"
16 var errVotingOperationOverFlow = errors.New("voting operation result overflow")
18 type ConsensusNode struct {
24 type byVote []*ConsensusNode
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())
30 func (c byVote) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
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 {
40 return (blockHeight-1)/consensus.RoundVoteBlockNums + 1
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 {
49 NumOfVote map[string]uint64
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")
59 for _, tx := range block.Transactions {
60 for _, input := range tx.Inputs {
61 unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
66 pubkey := hex.EncodeToString(unVoteInput.Vote)
67 v.NumOfVote[pubkey], ok = checked.SubUint64(v.NumOfVote[pubkey], unVoteInput.Amount)
69 return errVotingOperationOverFlow
72 if v.NumOfVote[pubkey] == 0 {
73 delete(v.NumOfVote, pubkey)
77 for _, output := range tx.Outputs {
78 voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
83 pubkey := hex.EncodeToString(voteOutput.Vote)
84 if v.NumOfVote[pubkey], ok = checked.AddUint64(v.NumOfVote[pubkey], voteOutput.Amount); !ok {
85 return errVotingOperationOverFlow
90 v.BlockHash = block.Hash()
91 v.BlockHeight = block.Height
92 v.Seq = CalcVoteSeq(block.Height)
96 func (v *VoteResult) ConsensusNodes() (map[string]*ConsensusNode, error) {
97 var nodes []*ConsensusNode
98 for pubkey, voteNum := range v.NumOfVote {
99 if voteNum >= consensus.MinVoteNum {
100 var xpub chainkd.XPub
101 if err := xpub.UnmarshalText([]byte(pubkey)); err != nil {
105 nodes = append(nodes, &ConsensusNode{XPub: xpub, VoteNum: voteNum})
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]
117 if len(result) != 0 {
120 return federationNodes(), nil
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)}
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")
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)
144 pubkey := hex.EncodeToString(unVoteInput.Vote)
145 if v.NumOfVote[pubkey], ok = checked.AddUint64(v.NumOfVote[pubkey], unVoteInput.Amount); !ok {
146 return errVotingOperationOverFlow
150 for _, output := range tx.Outputs {
151 voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
156 pubkey := hex.EncodeToString(voteOutput.Vote)
157 v.NumOfVote[pubkey], ok = checked.SubUint64(v.NumOfVote[pubkey], voteOutput.Amount)
159 return errVotingOperationOverFlow
162 if v.NumOfVote[pubkey] == 0 {
163 delete(v.NumOfVote, pubkey)
168 v.BlockHash = block.PreviousBlockHash
169 v.BlockHeight = block.Height - 1
170 v.Seq = CalcVoteSeq(block.Height - 1)
174 func (v *VoteResult) Fork() *VoteResult {
177 NumOfVote: map[string]uint64{},
178 BlockHash: v.BlockHash,
179 BlockHeight: v.BlockHeight,
182 for key, value := range v.NumOfVote {
183 f.NumOfVote[key] = value
188 func (v *VoteResult) IsFinalize() bool {
189 return v.BlockHeight%consensus.RoundVoteBlockNums == 0