Implements a raw consensus algorithm. The system supports pluggable consensus, meaning multiple consensus algorithms running side by side on different virtual chains. Abstracts away the details of how the consensus algorithm works and provides the system with a single unified interface.
Controls and drives the timing of consensus, for example when new blocks are committed or when new blocks need to be proposed and populated with transaction from the pool.
Note: ConsensusAlgo
is not in charge of block sync (BlockStorage
is) and it does not deal with block content (ConsensusContext
does).
Currently a single instance per virtual chain per node (per supported algorithm).
ConsensusContext
- Calls it during consensus when system interface regarding the block content is required.BlockStorage
- Requests block headers from it in order to be on the consensus frontline and participate.Gossip
- Uses it to communicate with other nodes.
- Needs to be persistent.
last_committed_block
- The last valid committed block from the previous round (persistent).
- Initialize the configuration.
- Load persistent data.
- Subscribe to gossip messages in the relevant consensus topic by calling
Gossip.TopicSubscribe
. - Start the consensus algorithm.
Called internally when the consensus algorithm decides a new term (block height) starts, probably after the last block was committed.
- Assumes the block height for the upcoming round is known.
- Get the previously committed block pair by calling
BlockStorage.GetTransactionsBlockHeader
andBlockStorage.GetResultsBlockHeader
.- Store it in
last_committed_block
for the benefit of the next steps in this round. - It is recommended to cache the committed block from the previous round in order to prevent fetching this data.
- If fails or times out, skip this round and don't participate (the node is probably out of sync).
- Store it in
- Calculate the random seed from the previous block.
- Get the desired committee size from configuration.
- Get a sorted list of committee members for the upcoming Transactions block (ordering phase) by calling
ConsensusContext.RequestOrderingCommittee
. - Get a sorted list of committee members for the upcoming Results block (validation phase) by calling
ConsensusContext.RequestValidationCommittee
. - Note: If the consensus algorithm relies on a single committee for both, call
ConsensusContext.RequestValidationCommittee
only based on the Results block random seed.
- Note to self that participating in this round as leader.
- Get the desired block size from configuration.
- Request new Transactions block proposal (ordering phase) by calling
ConsensusContext.RequestNewTransactionsBlock
. - Request new Results block proposal (validation phase) by calling
ConsensusContext.RequestNewResultsBlock
. - Sign both proposals (according to the algo spec) - on hash of the header.
- Send signed proposals to the algo of all committee nodes (according to the algo spec) by calling
Gossip.SendMessage
.
- Note to self that participating in this round as non-leader committee member.
- Note to self that participating in this round as a non-committee member.
- Just waiting for the committed block, not actually taking part in the consensus itself.
Called internally when during consensus, a non-leader committee member receives a proposal for a new block by the leader.
- Wait until decided if participating in this round or not (
OnNewConsensusRound
) as a non-leader committee member.
- Perform algorithm related checks on the proposal:
- Signature, block height, previous block pair hash pointers.
- Get the desired block size from configuration and validate it.
- Validate the Transactions block (ordering phase) by calling
ConsensusContext.ValidateTransactionsBlock
. - Validate the Results block (validation phase) by calling
ConsensusContext.ValidateResultsBlock
.
- Sign both approvals (according to the algo spec) - on hash of the header.
- Send signed approvals to the algo of all committee nodes (according to the algo spec) by calling
Gossip.SendMessage
.
Called internally for committee members (leader or not) when the consensus algorithm has decided a block proposal can be committed.
- Wait until decided if participating in this round or not (
OnNewConsensusRound
) as a committee member (leader or not).
- Pass the committed block (including the block proofs) to block storage by calling
BlockStorage.CommitBlock
.
Called internally for non-committee members when they receive a new claimed committed block in order to validate that it was committed under consensus.
- Wait until decided if participating in this round or not (
OnNewConsensusRound
) as not a committee member.
- Correct block protocol version.
- Correct virtual chain.
- Check block height:
- If the block isn't the next of
last_commited_block
according to height, discard.
- If the block isn't the next of
- Check the block's
transactions_root_hash
:- Calculate the merkle root hash of the block's transactions and verify the hash in the header.
- Check the block's metadata hash:
- Calculate the hash of the block's metadata and verify the hash in the header.
- Correct block protocol version.
- Correct virtual chain.
- Check block height:
- If the block isn't the next of
last_commited_block
according to height, discard.
- If the block isn't the next of
- Check the block's
receipts_root_hash
:- Calculate the merkle root hash of the block's receipts and verify the hash in the header.
- Check the block's
state_diff_hash
:- Calculate the hash of the block's state diff and verify the hash in the header.
Note: The logic up to here also appears in BlockStorage
and should probably be extracted to avoid duplication.
- Check consensus of the Transactions block header by calling
AcknowledgeTransactionsBlockConsensus
. - Check consensus of the Results block header by calling
AcknowledgeResultsBlockConsensus
.
- Pass the committed block (including the block proofs) to block storage by calling
BlockStorage.CommitBlock
.
Validates the consensus for an untrusted block header. Ignores whether the block body (content) is valid and its relevant hash values match the ones in the header. Called internally and by block storage during block sync.
- Verify the previous block hash pointer matches the hash of the previous block (given).
- Calculate the random seed from the previous block (given).
- Get the desired committee size from configuration.
- Get a sorted list of committee members for the block by calling
ConsensusContext.RequestOrderingCommittee
. - Note: If the consensus algorithm relies on a single committee for both, call
ConsensusContext.RequestValidationCommittee
only based on the Results block random seed.
- Verify the block proof based on the committee members.
- If all valid, update the consensus algorithm about the block commit (with block height and consensus dependent data).
Validates the consensus for an untrusted block header. Ignores whether the block body (content) is valid and its relevant hash values match the ones in the header. Called internally and by block storage during block sync.
- Verify the previous block hash pointer matches the hash of the previous block (given).
- Calculate the random seed from the previous block (given).
- Get the desired committee size from configuration.
- Get a sorted list of committee members for the block by calling
ConsensusContext.RequestValidationCommittee
.
- Verify the block proof based on the committee members.
- If all valid, update the consensus algorithm about the block commit (with block height and consensus dependent data).
Handles a gossip message from another node. Relevant messages include algorithm-specific consensus messages.
- Depends on consensus algorithm.