-
Notifications
You must be signed in to change notification settings - Fork 178
/
vote_collector.go
120 lines (100 loc) · 5.41 KB
/
vote_collector.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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package hotstuff
import (
"github.com/rs/zerolog"
"github.com/onflow/flow-go/consensus/hotstuff/model"
"github.com/onflow/flow-go/model/flow"
)
// VoteConsumer consumes all votes for one specific view. It is registered with
// the `VoteCollector` for the respective view. Upon registration, the
// `VoteCollector` feeds votes into the consumer in the order they are received
// (already cached votes as well as votes received in the future). Only votes
// that pass de-duplication and equivocation detection are passed on. CAUTION,
// VoteConsumer implementations must be
// - NON-BLOCKING and consume the votes without noteworthy delay, and
// - CONCURRENCY SAFE
type VoteConsumer func(vote *model.Vote)
// OnQCCreated is a callback which will be used by VoteCollector to submit a QC when it's able to create it
type OnQCCreated func(*flow.QuorumCertificate)
// VoteCollectorStatus indicates the VoteCollector's status
// It has three different status.
type VoteCollectorStatus int
const (
// VoteCollectorStatusCaching is for the status when the block has not been received.
// The vote collector in this status will cache all the votes without verifying them
VoteCollectorStatusCaching VoteCollectorStatus = iota
// VoteCollectorStatusVerifying is for the status when the block has been received,
// and is able to process all votes for it.
VoteCollectorStatusVerifying
// VoteCollectorStatusInvalid is for the status when the block has been verified and
// is invalid. All votes to this block will be collected to slash the voter.
VoteCollectorStatusInvalid
)
// VoteCollector collects votes for the same block, produces QC when enough votes are collected
// VoteCollector takes a callback function to report the event that a QC has been produced.
var collectorStatusNames = [...]string{"VoteCollectorStatusCaching",
"VoteCollectorStatusVerifying",
"VoteCollectorStatusInvalid"}
func (ps VoteCollectorStatus) String() string {
if ps < 0 || int(ps) > len(collectorStatusNames) {
return "UNKNOWN"
}
return collectorStatusNames[ps]
}
// VoteCollector collects all votes for a specified view. On the happy path, it
// generates a QC when enough votes have been collected.
// The VoteCollector internally delegates the vote-format specific processing
// to the VoteProcessor.
type VoteCollector interface {
// ProcessBlock performs validation of block signature and processes block with respected collector.
// Calling this function will mark conflicting collector as stale and change state of valid collectors
// It returns nil if the block is valid.
// It returns model.InvalidBlockError if block is invalid.
// It returns other error if there is exception processing the block.
ProcessBlock(block *model.Proposal) error
// AddVote adds a vote to the collector
// return error if the signature is invalid
// When enough votes have been added to produce a QC, the QC will be created asynchronously, and
// passed to EventLoop through a callback.
AddVote(vote *model.Vote) error
// RegisterVoteConsumer registers a VoteConsumer. Upon registration, the collector
// feeds all cached votes into the consumer in the order they arrived.
// CAUTION, VoteConsumer implementations must be
// * NON-BLOCKING and consume the votes without noteworthy delay, and
// * CONCURRENCY SAFE
RegisterVoteConsumer(consumer VoteConsumer)
// View returns the view that this instance is collecting votes for.
// This method is useful when adding the newly created vote collector to vote collectors map.
View() uint64
// Status returns the status of the vote collector
Status() VoteCollectorStatus
}
// VoteProcessor processes votes. It implements the vote-format specific processing logic.
// Depending on their implementation, a VoteProcessor might drop votes or attempt to construct a QC.
type VoteProcessor interface {
// Process performs processing of single vote. This function is safe to call from multiple goroutines.
// Expected error returns during normal operations:
// * VoteForIncompatibleBlockError - submitted vote for incompatible block
// * VoteForIncompatibleViewError - submitted vote for incompatible view
// * model.InvalidVoteError - submitted vote with invalid signature
// * model.DuplicatedSignerError - vote from a signer whose vote was previously already processed
// All other errors should be treated as exceptions.
Process(vote *model.Vote) error
// Status returns the status of the vote processor
Status() VoteCollectorStatus
}
// VerifyingVoteProcessor is a VoteProcessor that attempts to construct a QC for the given block.
type VerifyingVoteProcessor interface {
VoteProcessor
// Block returns which block that will be used to collector votes for. Transition to VerifyingVoteCollector can occur only
// when we have received block proposal so this information has to be available.
Block() *model.Block
}
// VoteProcessorFactory is a factory that can be used to create a verifying vote processors for a specific proposal.
// Depending on factory implementation it will return processors for consensus or collection clusters
type VoteProcessorFactory interface {
// Create instantiates a VerifyingVoteProcessor for processing votes for a specific proposal.
// Caller can be sure that proposal vote was successfully verified and processed.
// Expected error returns during normal operations:
// * model.InvalidBlockError - proposal has invalid proposer vote
Create(log zerolog.Logger, proposal *model.Proposal) (VerifyingVoteProcessor, error)
}