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 // 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 {
37 NumOfVote map[string]uint64
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")
46 for _, tx := range block.Transactions {
47 for _, input := range tx.Inputs {
48 unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
53 pubkey := hex.EncodeToString(unVoteInput.Vote)
54 v.NumOfVote[pubkey], ok = checked.SubUint64(v.NumOfVote[pubkey], unVoteInput.Amount)
56 return errVotingOperationOverFlow
59 if v.NumOfVote[pubkey] == 0 {
60 delete(v.NumOfVote, pubkey)
64 for _, output := range tx.Outputs {
65 voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
70 pubkey := hex.EncodeToString(voteOutput.Vote)
71 if v.NumOfVote[pubkey], ok = checked.AddUint64(v.NumOfVote[pubkey], voteOutput.Amount); !ok {
72 return errVotingOperationOverFlow
77 v.LastBlockHash = block.Hash()
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 {
86 if err := xpub.UnmarshalText([]byte(pubkey)); err != nil {
90 nodes = append(nodes, &ConsensusNode{XPub: xpub, VoteNum: voteNum})
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]
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")
109 for _, tx := range block.Transactions {
110 for _, input := range tx.Inputs {
111 unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
116 pubkey := hex.EncodeToString(unVoteInput.Vote)
117 if v.NumOfVote[pubkey], ok = checked.AddUint64(v.NumOfVote[pubkey], unVoteInput.Amount); !ok {
118 return errVotingOperationOverFlow
122 for _, output := range tx.Outputs {
123 voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
128 pubkey := hex.EncodeToString(voteOutput.Vote)
129 v.NumOfVote[pubkey], ok = checked.SubUint64(v.NumOfVote[pubkey], voteOutput.Amount)
131 return errVotingOperationOverFlow
134 if v.NumOfVote[pubkey] == 0 {
135 delete(v.NumOfVote, pubkey)
140 v.LastBlockHash = block.PreviousBlockHash