How to Configure Block Production Timings in Fuel Core's PoA Module
Fuel Core's Proof-of-Authority (PoA) module uses a Trigger enum with four variants—Instant, Never, Interval, and Open—to control when blocks are produced, configured via CLI flags in bin/fuel-core/src/cli/run/consensus.rs and processed in crates/services/consensus_module/poa/src/service.rs.
Block production timings in the FuelLabs/fuel-core repository are governed by the consensus module's configuration system. The PoA service relies on a Trigger enum defined in crates/services/consensus_module/poa/src/config.rs to determine exactly when to seal new blocks. Understanding these triggers allows node operators to optimize their block production strategy for different network conditions.
Understanding the PoA Trigger Enum
The Trigger enum in crates/services/consensus_module/poa/src/config.rs defines four distinct block production strategies:
Instant– Produces a block immediately when the transaction pool receives new transactions. This minimizes latency but may result in smaller blocks.Never– Disables block production entirely. The node operates as a passive listener, syncing blocks from other producers without creating its own.Interval { block_time }– Produces blocks at fixed time intervals regardless of transaction activity. Theblock_timeparameter specifies the duration between blocks.Open { period }– Opens a block production window immediately and keeps it open for the specifiedperiod. The block is sealed when the period expires, allowing transaction accumulation during the window.
CLI Configuration for Block Production Timings
Node operators configure block production timings through command-line arguments defined in bin/fuel-core/src/cli/run/consensus.rs. The PoATriggerArgs struct parses these flags and converts them into the Trigger enum.
The available CLI flags map to trigger variants as follows:
--poa-instant– When set totrue(default), enablesTrigger::Instant. When set tofalse, falls through toTrigger::Never.--poa-interval-period <duration>– ActivatesTrigger::Intervalwith the specified block time (e.g.,5sfor 5 seconds).--poa-open-period <duration>– ActivatesTrigger::Openwith the specified window duration.
Clap's ArgGroup enforcement in bin/fuel-core/src/cli/run/consensus.rs ensures that only one mode (instant, interval, or open) can be active simultaneously.
How the PoA Service Schedules Block Production
The PoA service implementation in crates/services/consensus_module/poa/src/service.rs consumes the Trigger configuration to schedule block production through two primary functions: next_time and run.
Calculating Next Block Time
The next_time function calculates the timestamp for the next block based on the trigger variant:
// From crates/services/consensus_module/poa/src/service.rs
match self.trigger {
Trigger::Interval { block_time } => {
increase_time(self.last_timestamp, block_time)
}
Trigger::Open { period } => {
increase_time(self.last_timestamp, period)
}
// Instant and Never variants handled separately
}
For Interval triggers, it adds the configured block_time to the last timestamp. For Open triggers, it adds the period to determine when the window closes.
Scheduling Production Futures
The run function builds a next_block_production future that determines when to attempt block creation:
// From crates/services/consensus_module/poa/src/service.rs
let next_block_production: NextBlockProduction = match self.trigger {
Trigger::Instant => Box::pin(async {
let _ = self.new_txs_watcher.changed().await;
Instant::now()
}),
Trigger::Interval { block_time } => {
// Sleep until last_block_created + block_time
Box::pin(async move {
sleep_until(next_block_time).await;
Instant::now()
})
}
Trigger::Open { period } => {
// Deadline is last_block_created + period
Box::pin(async move { deadline })
}
Trigger::Never => Box::pin(std::future::pending()),
};
- Instant: Waits for transaction pool changes via
new_txs_watcher.changed(). - Interval: Sleeps until the next interval boundary.
- Open: Returns the deadline immediately; the block is produced when the period expires.
- Never: Returns a pending future that never resolves.
Additional Timing Configuration Parameters
Beyond the trigger mechanism, the PoA Config struct in crates/services/consensus_module/poa/src/config.rs includes several timeout parameters that affect block production:
production_timeout– Maximum duration allowed for the block producer to finish before the task aborts. Defaults to 20 seconds (Duration::from_secs(20)).time_until_synced– Duration the node waits for the network to be considered "synced" before starting production. Defaults toDuration::ZERO.min_connected_reserved_peers– Minimum number of reserved peers required before the PoA task can start. Defaults to0.
These values propagate from the global service configuration in crates/fuel-core/src/service/config.rs when the node initializes.
Practical Configuration Examples
Command-Line Examples
Configure instant mode (default behavior):
fuel-core run
Configure fixed 5-second block intervals:
fuel-core run --poa-interval-period=5s
Configure a 30-second open production window:
fuel-core run --poa-open-period=30s
Disable block production (listener mode):
fuel-core run --poa-instant=false
Programmatic Configuration
Create a custom PoA configuration in Rust:
use fuel_core_services::consensus_module::poa::{Config, Trigger};
use fuel_core_types::signer::SignMode;
use tokio::time::Duration;
// Configure interval mode with 10-second blocks
let poa_cfg = Config {
trigger: Trigger::Interval {
block_time: Duration::from_secs(10)
},
signer: SignMode::Unavailable, // Use a real key for production
metrics: false,
min_connected_reserved_peers: 0,
time_until_synced: Duration::ZERO,
production_timeout: Duration::from_secs(30),
chain_id: Default::default(),
};
Inspect the active trigger at runtime:
match self.trigger {
Trigger::Instant => tracing::info!("Instant mode – producing on Tx arrival"),
Trigger::Never => tracing::info!("Never mode – not producing blocks"),
Trigger::Interval { block_time } => {
tracing::info!("Interval mode: {} s", block_time.as_secs())
}
Trigger::Open { period } => {
tracing::info!("Open mode: {} s", period.as_secs())
}
}
Summary
- Fuel Core's PoA module uses a
Triggerenum with four variants (Instant,Never,Interval,Open) to control block production timings. - Configuration originates in
bin/fuel-core/src/cli/run/consensus.rsand is defined structurally incrates/services/consensus_module/poa/src/config.rs. - The PoA service in
crates/services/consensus_module/poa/src/service.rsimplements the scheduling logic vianext_time()for timestamp calculation andrun()for future-based production triggers. - Additional timing parameters include
production_timeout(default 20s),time_until_synced, andmin_connected_reserved_peers. - Only one trigger mode can be active at a time, enforced by CLI argument groups.
Frequently Asked Questions
What is the default block production mode in Fuel Core?
By default, Fuel Core runs in Instant mode (--poa-instant=true), meaning the node produces a new block immediately whenever the transaction pool receives new transactions. This minimizes latency but may result in smaller, more frequent blocks compared to fixed-interval production.
How do I configure Fuel Core to produce blocks at fixed time intervals?
Use the --poa-interval-period flag with a duration string (e.g., 5s, 1m). This activates Trigger::Interval, causing the PoA service to sleep until last_block_created + block_time before producing each new block, regardless of transaction activity. Note that you cannot use this flag simultaneously with --poa-instant or --poa-open-period due to CLI group constraints.
What happens if block production takes longer than the configured timeout?
If block production exceeds the production_timeout duration (default 20 seconds), the PoA task aborts the current production attempt. This timeout is defined in crates/services/consensus_module/poa/src/config.rs and propagated from the global service configuration. Operators can adjust this value programmatically via the Config struct if running Fuel Core as a library.
Can I disable block production entirely while still syncing the network?
Yes. Set --poa-instant=false to activate Trigger::Never, or simply omit any production flags and set --poa-instant to false. In this mode, the node operates as a passive listener, syncing blocks from peers without producing its own. The run() function in crates/services/consensus_module/poa/src/service.rs returns a pending future that never resolves, effectively disabling the production loop.
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 →