Proof of Authority (PoA) Consensus Mechanism in Fuel Core: Architecture and Implementation
Fuel Core's Proof of Authority (PoA) consensus uses a pre-configured authority key to cryptographically sign block headers, where the PoA Service orchestrates production triggers and the PoAVerifier validates signatures against the known authority address to ensure only authorized entities generate the chain.
The Fuel Labs fuel-core repository implements a lightweight, permissioned Proof of Authority (PoA) consensus mechanism as an efficient alternative to Proof-of-Stake. This system relies on designated authority keys to secure the blockchain, making it ideal for testnets, private networks, and controlled environments where deterministic block production is required.
How PoA Consensus Works in Fuel Core
Authority Keys and Block Production
At the heart of the PoA consensus mechanism lies the authority key, a cryptographic key pair pre-configured in the node's consensus configuration. The node holding this key runs the PoA Service, which handles three critical operations:
- Triggering block production based on the configured
Triggerpolicy (instant, interval, never, or open) - Signing the block header with the authority's private key, producing a
PoAConsensus.signature - Publishing the sealed block to the network for import, gossip, and finalization
Block Validation and Signature Verification
When other nodes receive a block, they validate it using the PoAVerifier. The verification process, implemented in crates/services/consensus_module/poa/src/verifier.rs, checks that the signature in the block header matches the known authority address defined in the ConsensusConfig.
The core verification logic handles both PoA V1 (single key) and PoA V2 (rotating keys):
match consensus_config {
ConsensusConfig::PoA { signing_key } => {
let msg = header.id().as_message();
consensus.signature.recover(msg)
.is_ok_and(|k| Input::owner(&k) == *signing_key)
}
ConsensusConfig::PoAV2(poa) => {
let msg = header.id().as_message();
let signing_key = poa.address_for_height(*header.height());
consensus.signature.recover(msg)
.is_ok_and(|k| Input::owner(&k) == signing_key)
}
}
Core Architectural Components
The PoA consensus mechanism in Fuel Core is modularized across several key components:
| Component | Role | Source Location |
|---|---|---|
| PoAConsensus | Struct holding the block-header signature (fuel_crypto::Signature) |
crates/types/src/blockchain/consensus/poa.rs |
| PoAAdapter | Entry point for the consensus module; provides manually_produce_blocks and implements ConsensusModulePort |
crates/fuel-core/src/service/adapters/consensus_module/poa.rs |
| PoAVerifier | Validates signatures and block fields during import | crates/services/consensus_module/poa/src/verifier.rs |
| RedisLeaderLeaseAdapter | Optional leader-lock for multi-node setups using Redis | crates/fuel-core/src/service/adapters/consensus_module/poa.rs |
| Config & Trigger | Defines production policies (instant, interval, never, open) | crates/services/consensus_module/poa/src/config.rs |
Block Production Flow
The block production process follows a deterministic sequence orchestrated by the PoA Service. According to the flow documentation in docs/poa/flows.md, the sequence involves:
- Trigger: The PoA Service initiates production based on the configured trigger policy
- Transaction Selection: The Block Producer queries the transaction pool for pending transactions
- Execution: The block is executed and the header is finalized
- Signing: The PoA Service signs the block header with the authority key
- Commit: The sealed block is committed to the database via the Block Importer
- Broadcast: The signed header is gossiped to the network via P2P
Implementing PoA Operations
Constructing a PoA Consensus Object
To create a PoAConsensus instance programmatically, typically used during testing or custom block construction:
use fuel_core_types::blockchain::consensus::poa::PoAConsensus;
use fuel_core_fuel_crypto::Signature;
// Create a placeholder signature (in production, sign with authority key)
let dummy_sig = Signature::default();
let poa_consensus = PoAConsensus::new(dummy_sig);
Reference: PoAConsensus::new
Manually Triggering Block Production
For testing or operational purposes, you can manually trigger block production using the PoAAdapter:
use fuel_core_fuel_core::service::adapters::consensus_module::poa::PoAAdapter;
use fuel_core_poa::service::{Mode, SharedState};
use fuel_core_tai64::Tai64;
// Obtain shared state from the running service
let shared: Option<SharedState> = /* obtained from core service */;
let poa_adapter = PoAAdapter::new(shared);
// Produce a single block immediately
poa_adapter
.manually_produce_blocks(Some(Tai64::now()), Mode::Blocks { number_of_blocks: 1 })
.await
.expect("block production failed");
Implemented in: PoAAdapter::manually_produce_blocks (source)
Verifying PoA Signatures During Block Import
When implementing custom block import logic, use the verifier to validate PoA signatures:
use fuel_core_chain_config::ConsensusConfig;
use fuel_core_types::blockchain::{
block::Block,
header::BlockHeader,
consensus::poa::PoAConsensus,
};
use fuel_core_services::consensus_module::poa::verify_consensus;
// Validate block against consensus configuration
let valid = verify_consensus(&config, &header, &PoAConsensus { signature });
assert!(valid, "Invalid PoA block signature");
Verification logic: verify_consensus
Leader Election for Multi-Node Deployments
When running multiple nodes with the same authority key, Fuel Core provides the RedisLeaderLeaseAdapter to prevent split-brain scenarios. This optional component, located in crates/fuel-core/src/service/adapters/consensus_module/poa.rs, implements a distributed leader lock using Redis.
The adapter checks lease ownership before allowing block production:
async fn can_produce_block(&self, _height: BlockHeight) -> anyhow::Result<bool> {
if self.renew_lease_if_owner().await? {
return Ok(true);
}
self.acquire_lease_if_free().await
}
This ensures that even in clustered deployments, only one node produces blocks at any given time, maintaining the singleton authority property required by the PoA consensus mechanism.
Summary
- Proof of Authority (PoA) in Fuel Core uses a pre-configured authority key to sign block headers, providing a lightweight alternative to Proof-of-Stake for permissioned networks.
- The PoA Service orchestrates block production through configurable triggers (instant, interval, never) and seals blocks with cryptographic signatures stored in
PoAConsensus. - PoAVerifier validates incoming blocks by recovering the public key from the signature and comparing it against the configured authority address, supporting both static and rotating key configurations.
- The PoAAdapter bridges the core service graph with the consensus module, enabling manual block production via
manually_produce_blocksfor testing and operational control. - For high-availability deployments, the RedisLeaderLeaseAdapter implements distributed leader election to ensure only one node produces blocks at a time, preventing consensus forks.
Frequently Asked Questions
What is the difference between PoA and PoA V2 in Fuel Core?
PoA V1 utilizes a single static authority key defined in the genesis configuration, requiring all blocks to be signed by that specific key. PoA V2 introduces key rotation capabilities, allowing multiple authority addresses that change based on block height. The verifier handles both modes: V1 checks against a fixed signing_key, whereas V2 calls poa.address_for_height(*header.height()) to dynamically determine the valid signer for that specific block.
How does Fuel Core prevent multiple nodes from producing blocks simultaneously?
In single-node deployments, the authority key configuration naturally prevents conflicts since only one node possesses the key. For multi-node setups sharing the same authority key, Fuel Core provides the RedisLeaderLeaseAdapter. This component acquires a distributed lease in Redis before allowing block production, ensuring only one node holds the production lock at any time. If the lease expires or the node fails, another node can acquire the lease and seamlessly take over production responsibilities.
Can block production be triggered manually in a PoA network?
Yes. The PoAAdapter exposes the manually_produce_blocks method, which allows operators or automated test suites to trigger immediate block production outside the normal trigger schedule. This functionality requires access to the SharedState from the running PoA service and accepts parameters specifying the production mode, such as the number of blocks to produce and the target timestamp.
Where is the PoA signature stored in the block structure?
The PoA signature is stored within the PoAConsensus struct, which serves as the consensus data container for the block header. Defined in crates/types/src/blockchain/consensus/poa.rs, this struct contains a single field of type fuel_crypto::Signature. During the block import process, the PoAVerifier extracts this signature from the PoAConsensus instance and validates it against the expected authority address recovered from the signed message.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →