-
Notifications
You must be signed in to change notification settings - Fork 177
/
voting_status.go
94 lines (80 loc) · 2.83 KB
/
voting_status.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package voteaggregator
import (
"errors"
"fmt"
"github.com/onflow/flow-go/consensus/hotstuff"
"github.com/onflow/flow-go/consensus/hotstuff/model"
"github.com/onflow/flow-go/model/flow"
"github.com/onflow/flow-go/module/signature"
)
// VotingStatus keeps track of incorporated votes for the same block
type VotingStatus struct {
signer hotstuff.SignerVerifier
block *model.Block
stakeThreshold uint64
accumulatedStake uint64
// assume votes are all valid to build QC
votes map[flow.Identifier]*model.Vote
}
// NewVotingStatus creates a new Voting Status instance
func NewVotingStatus(block *model.Block, stakeThreshold uint64, signer hotstuff.SignerVerifier) *VotingStatus {
return &VotingStatus{
signer: signer,
block: block,
stakeThreshold: stakeThreshold,
accumulatedStake: 0,
votes: make(map[flow.Identifier]*model.Vote),
}
}
// AddVote add votes to the list, and accumulates the stake
// assume votes are valid.
// duplicate votes will not be accumulated again
func (vs *VotingStatus) AddVote(vote *model.Vote, voter *flow.Identity) {
_, exists := vs.votes[vote.ID()]
if exists {
return
}
vs.votes[vote.ID()] = vote
// - We assume different votes with different vote ID must belong to different signers.
// This requires the signature-scheme to be deterministic.
// - Deterministic means that signing on the same data twice with the same private key
// will generate the same signature.
// - The assumption currently holds because we are using BLS signatures, and
// BLS signature is deterministic.
// - If the signature-scheme is non-deterministic, for instance: ECDSA, then we need to
// check votes are from unique signers here, only accumulate stakes if the vote is
// from a new signer that never seen
vs.accumulatedStake += voter.Stake
}
// CanBuildQC check whether the
func (vs *VotingStatus) CanBuildQC() bool {
return vs.hasEnoughStake()
}
// TryBuildQC returns a QC if the existing votes are enought to build a QC, otherwise
// an error will be returned.
func (vs *VotingStatus) TryBuildQC() (*flow.QuorumCertificate, bool, error) {
// check if there are enough votes to build QC
if !vs.CanBuildQC() {
return nil, false, nil
}
// build the aggregated signature
votes := getSliceForVotes(vs.votes)
qc, err := vs.signer.CreateQC(votes)
if errors.Is(err, signature.ErrInsufficientShares) {
return nil, false, nil
}
if err != nil {
return nil, false, fmt.Errorf("could not create QC from votes: %w", err)
}
return qc, true, nil
}
func (vs *VotingStatus) hasEnoughStake() bool {
return vs.accumulatedStake >= vs.stakeThreshold
}
func getSliceForVotes(votes map[flow.Identifier]*model.Vote) []*model.Vote {
var voteSlice = make([]*model.Vote, 0, len(votes))
for _, vote := range votes {
voteSlice = append(voteSlice, vote)
}
return voteSlice
}