votecollector

package
v0.30.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 6, 2023 License: AGPL-3.0 Imports: 18 Imported by: 4

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	VoteForIncompatibleViewError  = errors.New("vote for incompatible view")
	VoteForIncompatibleBlockError = errors.New("vote for incompatible block")
)
View Source
var (
	ErrDifferentCollectorState = errors.New("different state")
)
View Source
var (
	// RepeatedVoteErr is emitted, when we receive a vote for the same block
	// from the same voter multiple times. This error does _not_ indicate
	// equivocation.
	RepeatedVoteErr = errors.New("duplicated vote")
)

Functions

func EnsureVoteForBlock

func EnsureVoteForBlock(vote *model.Vote, block *model.Block) error

EnsureVoteForBlock verifies that the vote is for the given block. Returns nil on success and sentinel errors:

  • model.VoteForIncompatibleViewError if the vote is from a different view than block
  • model.VoteForIncompatibleBlockError if the vote is from the same view as block but for a different blockID

func NewBootstrapCombinedVoteProcessor

func NewBootstrapCombinedVoteProcessor(log zerolog.Logger, committee hotstuff.DynamicCommittee, block *model.Block, onQCCreated hotstuff.OnQCCreated) (hotstuff.VerifyingVoteProcessor, error)

NewBootstrapCombinedVoteProcessor directly creates a CombinedVoteProcessorV2, suitable for the collector's local cluster consensus. Intended use: only for bootstrapping. UNSAFE: the proposer vote for `block` is _not_ validated or included

func NewBootstrapStakingVoteProcessor

func NewBootstrapStakingVoteProcessor(log zerolog.Logger, committee hotstuff.DynamicCommittee, block *model.Block, onQCCreated hotstuff.OnQCCreated) (hotstuff.VerifyingVoteProcessor, error)

NewBootstrapStakingVoteProcessor directly creates a `StakingVoteProcessor`, suitable for the collector's local cluster consensus. Intended use: only for bootstrapping. UNSAFE: the proposer vote for `block` is _not_ validated or included

func NewStateMachineFactory

func NewStateMachineFactory(
	log zerolog.Logger,
	notifier hotstuff.Consumer,
	verifyingVoteProcessorFactory VerifyingVoteProcessorFactory,
) voteaggregator.NewCollectorFactoryMethod

Types

type CombinedVoteProcessorV2

type CombinedVoteProcessorV2 struct {
	// contains filtered or unexported fields
}

CombinedVoteProcessorV2 implements the hotstuff.VerifyingVoteProcessor interface. It processes votes from the main consensus committee, where participants must _always_ provide the staking signature as part of their vote and can _optionally_ also provide a random beacon signature. Through their staking signature, a participant always contributes to HotStuff's progress. Participation in the random beacon is optional (but encouraged). This allows nodes that failed the DKG to still contribute only to consensus (as fallback). CombinedVoteProcessorV2 is Concurrency safe.

func NewCombinedVoteProcessor

func NewCombinedVoteProcessor(log zerolog.Logger,
	block *model.Block,
	stakingSigAggtor hotstuff.WeightedSignatureAggregator,
	rbRector hotstuff.RandomBeaconReconstructor,
	onQCCreated hotstuff.OnQCCreated,
	packer hotstuff.Packer,
	minRequiredWeight uint64,
) *CombinedVoteProcessorV2

func (*CombinedVoteProcessorV2) Block

func (p *CombinedVoteProcessorV2) Block() *model.Block

Block returns block that is part of proposal that we are processing votes for.

func (*CombinedVoteProcessorV2) Process

func (p *CombinedVoteProcessorV2) Process(vote *model.Vote) error

Process performs processing of single vote in concurrent safe way. This function is implemented to be called by multiple goroutines at the same time. Supports processing of both staking and random beacon signatures. Design of this function is event driven: as soon as we collect enough signatures to create a QC we will immediately do so and submit it via callback for further processing. 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 if the signer has been already added All other errors should be treated as exceptions.

Impossibility of vote double-counting: Our signature scheme requires _every_ vote to supply a staking signature. Therefore, the `stakingSigAggtor` has the set of _all_ signerIDs that have provided a valid vote. Hence, the `stakingSigAggtor` guarantees that only a single vote can be successfully added for each `signerID`, i.e. double-counting votes is impossible.

func (*CombinedVoteProcessorV2) Status

Status returns status of this vote processor, it's always verifying.

type CombinedVoteProcessorV3

type CombinedVoteProcessorV3 struct {
	// contains filtered or unexported fields
}

CombinedVoteProcessorV3 implements the hotstuff.VerifyingVoteProcessor interface. It processes votes from the main consensus committee, where participants vote in favour of a block by proving either their staking key signature or their random beacon signature. In the former case, the participant only contributes to HotStuff progress; while in the latter case, the voter also contributes to running the random beacon. Concurrency safe.

func (*CombinedVoteProcessorV3) Block

func (p *CombinedVoteProcessorV3) Block() *model.Block

Block returns block that is part of proposal that we are processing votes for.

func (*CombinedVoteProcessorV3) Process

func (p *CombinedVoteProcessorV3) Process(vote *model.Vote) error

Process performs processing of single vote in concurrent safe way. This function is implemented to be called by multiple goroutines at the same time. Supports processing of both staking and random beacon signatures. Design of this function is event driven: as soon as we collect enough signatures to create a QC we will immediately do so and submit it via callback for further processing. 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.

CAUTION: implementation is NOT (yet) BFT Explanation: for correctness, we require that no voter can be counted repeatedly. However, CombinedVoteProcessorV3 relies on the `VoteCollector`'s `votesCache` filter out all votes but the first for every signerID. However, we have the edge case, where we still feed the proposers vote twice into the `VerifyingVoteProcessor` (once as part of a cached vote, once as an individual vote). This can be exploited by a byzantine proposer to be erroneously counted twice, which would lead to a safety fault.

 TODO (suggestion): I think it would be worth-while to include a second `votesCache` into the `CombinedVoteProcessorV3`.
		Thereby, `CombinedVoteProcessorV3` inherently guarantees correctness of the QCs it produces without relying on
		external conditions (making the code more modular, less interdependent and thereby easier to maintain). The
		runtime overhead is marginal: For `votesCache` to add 500 votes (concurrently with 20 threads) takes about
		0.25ms. This runtime overhead is neglectable and a good tradeoff for the gain in maintainability and code clarity.

func (*CombinedVoteProcessorV3) Status

Status returns status of this vote processor, it's always verifying.

type NoopProcessor

type NoopProcessor struct {
	// contains filtered or unexported fields
}

NoopProcessor implements hotstuff.VoteProcessor. It drops all votes.

func NewNoopCollector

func NewNoopCollector(status hotstuff.VoteCollectorStatus) *NoopProcessor

func (*NoopProcessor) Process

func (c *NoopProcessor) Process(*model.Vote) error

func (*NoopProcessor) Status

type StakingVoteProcessor

type StakingVoteProcessor struct {
	// contains filtered or unexported fields
}

StakingVoteProcessor implements the hotstuff.VerifyingVoteProcessor interface. It processes hotstuff votes from a collector cluster, where participants vote in favour of a block by proving their staking key signature. Concurrency safe.

func (*StakingVoteProcessor) Block

func (p *StakingVoteProcessor) Block() *model.Block

Block returns block that is part of proposal that we are processing votes for.

func (*StakingVoteProcessor) Process

func (p *StakingVoteProcessor) Process(vote *model.Vote) error

Process performs processing of single vote in concurrent safe way. This function is implemented to be called by multiple goroutines at the same time. Supports processing of both staking and threshold signatures. Design of this function is event driven, as soon as we collect enough weight to create a QC we will immediately do this and submit it via callback for further processing. 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 All other errors should be treated as exceptions.

func (*StakingVoteProcessor) Status

Status returns status of this vote processor, it's always verifying.

type VerifyingVoteProcessorFactory

type VerifyingVoteProcessorFactory = func(log zerolog.Logger, proposal *model.Proposal) (hotstuff.VerifyingVoteProcessor, error)

VerifyingVoteProcessorFactory generates hotstuff.VerifyingVoteCollector instances

type VoteCollector

type VoteCollector struct {
	sync.Mutex
	// contains filtered or unexported fields
}

VoteCollector implements a state machine for transition between different states of vote collector

func NewStateMachine

func NewStateMachine(
	view uint64,
	log zerolog.Logger,
	workers hotstuff.Workers,
	notifier hotstuff.Consumer,
	verifyingVoteProcessorFactory VerifyingVoteProcessorFactory,
) *VoteCollector

func (*VoteCollector) AddVote

func (m *VoteCollector) AddVote(vote *model.Vote) error

AddVote adds a vote to current vote collector All expected errors are handled via callbacks to notifier. Under normal execution only exceptions are propagated to caller.

func (*VoteCollector) ProcessBlock

func (m *VoteCollector) ProcessBlock(proposal *model.Proposal) error

ProcessBlock performs validation of block signature and processes block with respected collector. In case we have received double proposal, we will stop attempting to build a QC for this view, because we don't want to build on any proposal from an equivocating primary. Note: slashing challenges for proposal equivocation are triggered by hotstuff.Forks, so we don't have to do anything else here.

The internal state change is implemented as an atomic compare-and-swap, i.e. the state transition is only executed if VoteCollector's internal state is equal to `expectedValue`. The implementation only allows the transitions

CachingVotes   -> VerifyingVotes
CachingVotes   -> Invalid
VerifyingVotes -> Invalid

func (*VoteCollector) RegisterVoteConsumer

func (m *VoteCollector) RegisterVoteConsumer(consumer hotstuff.VoteConsumer)

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

func (*VoteCollector) Status

Status returns the status of underlying vote processor

func (*VoteCollector) View

func (m *VoteCollector) View() uint64

View returns view associated with this collector

type VoteProcessorFactory

type VoteProcessorFactory struct {
	// contains filtered or unexported fields
}

VoteProcessorFactory implements `hotstuff.VoteProcessorFactory`. Its main purpose is to construct instances of VerifyingVoteProcessors for a given block proposal. VoteProcessorFactory * delegates the creation of the actual instances to baseFactory * adds the logic to verify the proposer's vote for its own block Thereby, VoteProcessorFactory guarantees that only proposals with valid proposer vote are accepted (as per API specification). Otherwise, an `model.InvalidBlockError` is returned.

func NewCombinedVoteProcessorFactory

func NewCombinedVoteProcessorFactory(committee hotstuff.DynamicCommittee, onQCCreated hotstuff.OnQCCreated) *VoteProcessorFactory

NewCombinedVoteProcessorFactory implements hotstuff.VoteProcessorFactory fo participants of the Main Consensus committee.

With their vote, members of the main consensus committee can contribute to hotstuff and the random beacon. When a consensus participant signs with its random beacon key, it contributes to HotStuff consensus _and_ the Random Beacon. As a fallback, a consensus participant can sign with its staking key; thereby it contributes only to consensus but not the random beacon. There should be an economic incentive for the nodes to preferably sign with their random beacon key.

func NewStakingVoteProcessorFactory

func NewStakingVoteProcessorFactory(committee hotstuff.DynamicCommittee, onQCCreated hotstuff.OnQCCreated) *VoteProcessorFactory

NewStakingVoteProcessorFactory implements hotstuff.VoteProcessorFactory for members of a collector cluster. For their cluster-local hotstuff, collectors only sign with their staking key.

func (*VoteProcessorFactory) Create

Create instantiates a VerifyingVoteProcessor for the given block proposal. A VerifyingVoteProcessor are only created for proposals with valid proposer votes. Expected error returns during normal operations: * model.InvalidBlockError - proposal has invalid proposer vote

type VoteProcessorTestSuiteBase

type VoteProcessorTestSuiteBase struct {
	suite.Suite
	// contains filtered or unexported fields
}

VoteProcessorTestSuiteBase is a helper structure which implements common logic between staking and combined vote processor test suites.

func (*VoteProcessorTestSuiteBase) SetupTest

func (s *VoteProcessorTestSuiteBase) SetupTest()

type VotesCache

type VotesCache struct {
	// contains filtered or unexported fields
}

VotesCache maintains a _concurrency safe_ cache of votes for one particular view. The cache memorizes the order in which the votes were received. Votes are de-duplicated based on the following rules:

  • Vor each voter (i.e. SignerID), we store the _first_ vote v0.
  • For any subsequent vote v, we check whether v.BlockID == v0.BlockID. If this is the case, we consider the vote a duplicate and drop it. If v and v0 have different BlockIDs, the voter is equivocating and we return a model.DoubleVoteError

func NewVotesCache

func NewVotesCache(view uint64) *VotesCache

NewVotesCache instantiates a VotesCache for the given view

func (*VotesCache) AddVote

func (vc *VotesCache) AddVote(vote *model.Vote) error

AddVote stores a vote in the cache. The following errors are expected during normal operations:

  • nil: if the vote was successfully added
  • model.DoubleVoteError is returned if the voter is equivocating (i.e. voting in the same view for different blocks).
  • RepeatedVoteErr is returned when adding a vote for the same block from the same voter multiple times.
  • IncompatibleViewErr is returned if the vote is for a different view.

When AddVote returns an error, the vote is _not_ stored.

func (*VotesCache) All

func (vc *VotesCache) All() []*model.Vote

All returns all currently cached votes. Concurrency safe.

func (*VotesCache) GetVote

func (vc *VotesCache) GetVote(signerID flow.Identifier) (*model.Vote, bool)

GetVote returns the stored vote for the given `signerID`. Returns:

  • (vote, true) if a vote from signerID is known
  • (false, nil) no vote from signerID is known

func (*VotesCache) RegisterVoteConsumer

func (vc *VotesCache) RegisterVoteConsumer(consumer hotstuff.VoteConsumer)

RegisterVoteConsumer registers a VoteConsumer. Upon registration, the cache feeds all cached votes into the consumer in the order they arrived. CAUTION: a consumer _must_ be non-blocking and consume the votes without noteworthy delay. Otherwise, consensus speed is impacted.

Expected usage patter: During happy-path operations, the block arrives in a timely manner. Hence, we expect that only a few votes are cached when a consumer is registered. For the purpose of forensics, we might register a consumer later, when already lots of votes are cached. However, this should be a rare occurrence (we except moderate performance overhead in this case).

func (*VotesCache) Size

func (vc *VotesCache) Size() int

Size returns the number of cached votes

func (*VotesCache) View

func (vc *VotesCache) View() uint64

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL