The following figure shows the components that make up the consensus architecture. The components within blue shapes are algorithm specific, which is to say that they are added to a Full Node build depending on the consensus algorithm/s the build needs to support.
The Consensus Manager, which is a
ConsensusManager singleton, is the central part of the consensus architecture and implements the
IConsensusManager interface. It interacts closely with the Chained Header Tree (a
ChainedHeaderTree singleton). The Consensus Manager receives headers via the Consensus Behavior Manager and these are forwarded to the Chained Header Tree. The Chained Header Tree decides which of the headers are interesting and requests the Consensus Manager retrieves the headers’ blocks. The Block Puller is responsible for handling the block retrieval from the network.
The decisions on what level block validation needs to go to are taken by the Consensus Manager, which may rely on feedback from the Chained Header Tree. When looking at the routes taken by blocks on their way to validation, be aware that blocks undergoing full validation will have passed at least partial validation first. Blocks that are required to undergo only partial validation will have passed integrity verification.
ConsensusRulesEngine is an abstract class that the algorithm-specific
PoAConsensusRulesEngine classes inherit from. Each
ConsensusRulesEngine object contains a list of rules for the four types of validation. The Consensus Manager does not interact directly with the instances of
PoAConsensusRulesEngine which are created. Instead, the Consensus Manager calls into three wrapper class singletons:
Partial Validator, and the
FullValidator. These lean objects provide, internally, a logging facility and implement the
IFullValidator interfaces respectively. Except for
IPartialValidator, each of these singletons implements a single method, and it is these methods the Consensus Manager calls.
The Chained Header Tree follows the same pattern and interacts with the required header validation rules list via a
HeaderValidator wrapper singleton, which implements the
The table below provides links to the source files in which the C# classes and related interfaces (were relevant) for these components can be found:
|Component||C# Class||C# Interface|
|Chained Header Tree||ChainedHeaderTree||IChainedHeaderTree|
|Consensus Manager Behavior||Consensus Manager Behavior||N/A|
|Integrity Validator||IntegrityValidator||IIntegrity Validator|
|Consensus Rule Engine||ConsensusRuleEngine||IConsensusRuleEngine|
|PoW Consensus Rule Engine||PowConsensusRuleEngine||N/A|
|PoS Consensus Rule Engine||PosConsensusRuleEngine||N/A|
|PoA Consensus Rule Engine||PoAConsensusRuleEngine||N/A|
|Block Puller Behavior||BlockPullerBehavior||N/A|
When does the Consensus Manager request block validation?¶
Let’s look in the code at the situations which require that the Consensus Manager performs validation:
- PoW Mining, PoS Minting, and PoA Miner singletons call into the Consensus Manager, via the
ConsensusManager.BlockMinedAsync()method, when a new block is created. Calling
BlockMinedAsync()results in the new block undergoing both partial and full validation. As older blocks mined prior to the last checkpoint do not require full validation, partial validation includes a check to see if a block requires full validation. However, if a supposedly new block does not require full validation, something has gone wrong, and an error is raised.
- The Consensus Manager holds a reference to the Block Puller component. When the Consensus Manager is initialized, it supplies
ConsensusManager.BlockDownloaded()to the Block Puller as a callback. The
ConsensusManager.HeadersPresented()method is used to request that the Consensus Manager initiates block downloading for a set of block headers. The request is forwarded to the Block Puller. Each block retrieved from the network by the Block Puller is pushed into the ConsensusManager via the supplied
BlockDownloaded()callback. In this callback, the integrity of the downloaded block is verified with a call to
IntegrityValidator.VerifyBlockIntegrity(). All requests to the Block Puller are made via the ConsensusManager.
ConsensusManager.HeadersPresented()is a call to the private function
ConsensusManager.DownloadBlocks(), which takes a callback function as its second parameter. In the call to
ConsensusManager.ProcessDownloadedBlock()is supplied as the callback and is held in the
ProcessDownloadedBlock()ultimately gets retrieved from the dictionary and called in
ConsensusManager.BlockDownloaded(), and here it matches up a downloaded block with the block’s chained header in the Chained Header Tree. This is achieved via a call to
ChainedHeaderTree.BlockDataDownloaded(), which returns
trueif the downloaded block requires partial validation and false if it does not. If partial validation is required, a call is made to
PartialValidator.StartPartialValidation(). If partial validation succeeds at this point, full validation may be required.
- The decision to proceed with full validation is made by the Chained Header Tree. Specifically, this occurs when
ChainedHeaderTree.PartialValidationSucceeded()is called at two points in the Consensus Manager code: within
ConsensusManager.OnPartialValidationSucceededAsync(), which is called from the callback passed to
outparameter which returns whether full validation is required. It decides this by checking if the block has more chainwork than the current consensus tip. If this is the case, the new block will become the consensus tip if it passes full validation.
The Chained Header Tree component does not hold a reference to the Consensus Manager. Communication with the Consensus Manager occurs when it calls the
IChainedHeaderTree interface, and the Chain Header Tree’s response is either by return values or out parameters. Therefore, the communication can be thought of as two-way.
When does the Chained Header Tree request header validation?¶
The Chained Header Tree tries to create a new chained header (an instance of
ChainedHeader) each time it receives a header (an instance of
BlockHeader). Straight after the new chained header is created, it is validated with a call to
HeaderValidator.ValidateHeader(). The creation of the new chained header takes place in
ChainedHeaderTree.CreateAndValidateNewChainedHeader(), and this private function is ultimately only invoked in response to two public functions:
ChainedHeaderTree.CreateChainedHeaderOfMinedBlock(). These public functions are only ever called by