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"
15 var errVotingOperationOverFlow = errors.New("voting operation result overflow")
17 type ConsensusNode struct {
23 type byVote []*ConsensusNode
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())
29 func (c byVote) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
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 {
39 return (blockHeight-1)/consensus.RoundVoteBlockNums + 1
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 {
48 NumOfVote map[string]uint64
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")
58 for _, tx := range block.Transactions {
59 for _, input := range tx.Inputs {
60 unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
65 pubkey := hex.EncodeToString(unVoteInput.Vote)
66 v.NumOfVote[pubkey], ok = checked.SubUint64(v.NumOfVote[pubkey], unVoteInput.Amount)
68 return errVotingOperationOverFlow
71 if v.NumOfVote[pubkey] == 0 {
72 delete(v.NumOfVote, pubkey)
76 for _, output := range tx.Outputs {
77 voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
82 pubkey := hex.EncodeToString(voteOutput.Vote)
83 if v.NumOfVote[pubkey], ok = checked.AddUint64(v.NumOfVote[pubkey], voteOutput.Amount); !ok {
84 return errVotingOperationOverFlow
89 v.BlockHash = block.Hash()
90 v.BlockHeight = block.Height
91 v.Seq = CalcVoteSeq(block.Height)
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 {
100 if err := xpub.UnmarshalText([]byte(pubkey)); err != nil {
104 nodes = append(nodes, &ConsensusNode{XPub: xpub, VoteNum: voteNum})
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]
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")
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)
131 pubkey := hex.EncodeToString(unVoteInput.Vote)
132 if v.NumOfVote[pubkey], ok = checked.AddUint64(v.NumOfVote[pubkey], unVoteInput.Amount); !ok {
133 return errVotingOperationOverFlow
137 for _, output := range tx.Outputs {
138 voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
143 pubkey := hex.EncodeToString(voteOutput.Vote)
144 v.NumOfVote[pubkey], ok = checked.SubUint64(v.NumOfVote[pubkey], voteOutput.Amount)
146 return errVotingOperationOverFlow
149 if v.NumOfVote[pubkey] == 0 {
150 delete(v.NumOfVote, pubkey)
155 v.BlockHash = block.PreviousBlockHash
156 v.BlockHeight = block.Height - 1
157 v.Seq = CalcVoteSeq(block.Height - 1)
161 func (v *VoteResult) Fork() *VoteResult {
164 NumOfVote: map[string]uint64{},
165 BlockHash: v.BlockHash,
166 BlockHeight: v.BlockHeight,
169 for key, value := range v.NumOfVote {
170 f.NumOfVote[key] = value
175 func (v *VoteResult) IsFinalize() bool {
176 return v.BlockHeight%consensus.RoundVoteBlockNums == 0