Skip to content

QuorumChain Consensus [Quorum v1.x ONLY]

fixanoid edited this page Jun 13, 2018 · 1 revision

*Note: QuorumChain was removed in Quorum 2.0. The information on this page only applies to Quorum v1.x


Overview

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 and Voter 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.

Voting Smart Contract

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

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

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.

Observer Nodes

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.

Block Creation

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

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.

Consensus Process Flow

Within a Period:

  1. The Maker Node that reaches the timeout first creates a block and signs it. The block includes the votes for its parent block, which were cast in the previous Period.
  2. The block is published to the network using the standard Ethereum P2P protocol - all nodes, regardless of role, receive the block.
  3. Voter Nodes validate the block. This includes:
  4. Calling the BlockVoting contract to check whether the Maker is allowed to create blocks.
  5. Calling the BlockVoting contract to check whether the block's parent block received enough votes.
  6. 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).
  7. Validate the public state by comparing the public state root hash with the state root within the block.
  8. 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.
  9. Once successfully validated, Voter Nodes send their vote to the BlockVoting contract using a standard Ethereum Transaction that is distributed to all Nodes. Since votes for a given block are cast via standard Transactions, they can only be processed when the next block is created.
  10. 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.

A Note on Consensus on Private Transactions

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 Block Structure

Quorum blocks include:

  1. A Global Transaction Hash, which is the hash of all Transactions in a block (both private and public)
  2. The Public State root hash (as opposed to a Global State root hash in standard Ethereum)
  3. Block Maker's signature in the ExtraData field