-
Notifications
You must be signed in to change notification settings - Fork 1.3k
QuorumChain Consensus [Quorum v1.x ONLY]
*Note: QuorumChain was removed in Quorum 2.0. The information on this page only applies to Quorum v1.x
Whilst the overall goal is to ensure pluggability of consensus mechanisms, the initial consensus mechanism that Quorum supports is a new mechanism dubbed QuorumChain, a time-based, majority-voting algorithm that utilises:
- A Smart Contract to govern consensus and manage who can partake in consensus
- Ethereum Transactions to propagate
votes
through the network - Ethereum's signature validation to validate signatures received from
Maker
andVoter
nodes
Nodes within a Quorum network can be given the Voter
role which allows them to vote
on which block should be the canonical head at a particular height. The most recent block with the most votes is considered the canonical head of the chain. A block is only considered valid once a given threshold of votes
has been received from valid Voters
.
Block creation is only allowed by nodes that have been given the Maker
role. A node with this role can create a block and sign it by setting their signature in the ExtraData
field of the block. On block import, as part of the block header validation, nodes verify that the block was signed by one of the nodes that have the Maker
role by looking up the signer's address in the list of valid Makers
in the voting contract.
Nodes can be given no role, one of the roles or both roles through command line arguments.
QuorumChain is implemented in the BlockVoting
contract (found here) which is set at address 0x0000000000000000000000000000000000000020
within the Genesis block. That address, the pre-compiled byte code for the BlockVoting
contract and its associated ABI are hard coded into the Quorum client. If the consensus rules within the BlockVoting
contract need to be updated then the new code needs to be compiled and the Quorum client needs to be updated to reflect the new code.
Through predefined functions on the contract, Voters
and Makers
can be added or removed, and the minimum number of votes
before a block is selected as the winner can be configured.
The BlockVoting
contract tracks whether the votes
received are from valid Voters
, and whether the number of votes
received for a particular block is greater than the voteThreshold
that is defined within the contract.
As part of block validation, the contract is called to determine the latest block which meets the required number of votes
(the canonical height of the chain) - it is this block that the proposed block should be attempting to build upon (i.e. should be the parent of the proposed block).
Maker
Nodes are responsible for making blocks and their Ethereum addresses are registered in the BlockVoting
contract. There must be at least 1 Maker
Node configured in the contract. The initial set of Maker
Nodes is pre-configured in the genesis block via the genesis.json
file, however once the network is established, Maker
Nodes can add and remove other Maker
Nodes by sending a Transaction with the appropriate function call to the BlockVoting
contract.
Maker
Nodes can also be setup as Voter
Nodes.
Voter
nodes are also registered in the BlockVoting
contract and are responsible for voting on the validity of blocks. The voting role allows a node to vote
on which block should be the canonical hash at a particular height. The block with the most votes
will win and is considered the canonical head of the chain. Like Makers
, the initial set of Voter
Nodes is pre-configured in the genesis block via the genesis.json
file, however once the network is established, Voter
Nodes can add and remove other Voter
Nodes by sending a Transaction with the appropriate function call to the BlockVoting
contract. Voter
Nodes can also set the voteThreshold
that must be met before a block will be accepted into the chain.
Note: The current implementation does not dynamically update the voteThreshold
when the number of Voter
Nodes on the network changes, and also does not specifically track or limit the number of votes
a Voter
has made in a given Period
. These items are on the Product Roadmap.
If a Node is neither a Maker
nor Voter
then it is simply considered to be an Observer and will naturally not take part in block making nor voting but instead will simply receive and validate blocks.
Multiple Nodes on a Quorum network can be configured as Maker
Nodes, however to reduce the likelihood of 2 (or more) Makers
creating a block at the same time, each Maker
generates a random duration (a timeout) that it has to wait before it can create a block. If a Maker
reaches its timeout before any other Maker
does it will create a block, after which it will generate a new random timeout for itself and wait for that to elapse before attempting to create another block. Note that once a Maker
begins the block creation process, the other Maker
Nodes will reset their current timeout, generate a new random timeout, and wait until that expires before attempting to creating a block.
The timeout duration randomly falls within a min
and max
time (in seconds) that are defined in the BlockMakerStrategy
and that can be set at Node start up via the CLI flags --minblocktime
and --maxblocktime
. If these are not explicitly set at startup then default values will be used.
After a block is successfully imported as chain head a new pending block is prepared on top of that block. All processable transactions are selected and applied to the pending state in the new block. If the node is configured with the Maker
role and is ordered to create a block, it will first validate that the parent block hash is a valid canonical hash (latest block with required number of votes). If this block differs from the current local head the block creation fails. If the block was built on top of the correct block then the new block is inserted into the block chain and broadcast to other nodes.
Note: in the current implementation, in order to avoid chain-halting due to limited online Voter
Nodes, the Maker
node will cast votes
(if it is configured to be a Voter
) for the parent block in order to meet the voteThreshold
and allow the chain to progress. This naturally has control implications and should be managed by ensuring Voter
Nodes meet their obligations to the network by remaining up.
Block voting occurs within a Period
, the duration of which is linked to the block creation duration described above. After successfully validating a block, Voters
will cast a vote
for the block by calling the BlockVoting
contract.
Although block creation is governed by the BlockMakerStrategy
described in the Block Creation section above, it is possible (although unlikely) that two Maker
nodes simultaneously reach their timeouts and both create a block. Voters
will vote on both blocks, but the one that has the most votes
at the end of the voting Period
will be the one that is selected to be the canonical head of the chain.
Within a Period:
- The
Maker
Node that reaches the timeout first creates a block and signs it. The block includes thevotes
for its parent block, which were cast in the previousPeriod
. - The block is published to the network using the standard Ethereum P2P protocol - all nodes, regardless of role, receive the block.
-
Voter
Nodes validate the block. This includes: - Calling the
BlockVoting
contract to check whether theMaker
is allowed to create blocks. - Calling the
BlockVoting
contract to check whether the block's parent block received enoughvotes
. - Executing all processable Transactions in the block, i.e. the Public Transactions and the Private Transactions the node is party to (after retrieving the Transaction payload from its Transaction Manager, as defined in the Transaction Processing & Privacy page).
- Validate the public state by comparing the public state root hash with the state root within the block.
- Hashing all Transactions in the block (Public & Private) and comparing that hash to the Transaction Hash on the block. This is not a state check but ensures that all
Voter
Nodes agree on the list of Transactions in the block. - Once successfully validated,
Voter
Nodes send theirvote
to theBlockVoting
contract using a standard Ethereum Transaction that is distributed to all Nodes. Sincevotes
for a given block are cast via standard Transactions, they can only be processed when the next block is created. -
Maker
Node reaches its timeout, determines if the minimum number of votes have been received for the previous block, and then the block creation - > validation -> voting process is repeated.
Whilst consensus on private state is implicit through a combination of provably synchronized contract inputs (global Transaction Hash validation check), a provably deterministic EVM (public state validation check), and provable chain synchronization (new blocks only added to the canonical chain), private state consensus can be further validated via a new rpc command, eth_storageRoot
. This command returns the private state root hash of a contract account at a given block number. This can then be validated against a counterparty's storageRoot
result off-chain or at the application layer.
Quorum blocks include:
- A Global Transaction Hash, which is the hash of all Transactions in a block (both private and public)
- The Public State root hash (as opposed to a Global State root hash in standard Ethereum)
- Block
Maker
's signature in theExtraData
field