# How Block Production Is Managed in Fuel Core by the Producer Service

> Discover how the Fuel Core producer service manages block production. Learn about transaction sourcing, header construction, and state transition execution for efficient block creation.

- Repository: [Fuel Labs/fuel-core](https://github.com/FuelLabs/fuel-core)
- Tags: internals
- Published: 2026-03-06

---

**The producer service in Fuel Core manages block production through a generic `Producer` struct that coordinates transaction sourcing, header construction, DA-height selection, and state transition execution while ensuring only one block is produced at a time via a mutex lock.**

The `fuel-core` repository implements its block production logic in the producer service located at `crates/services/producer`. This service orchestrates the creation of new blocks by interfacing with the transaction pool, database views, and the executor to generate uncommitted results ready for finalization and propagation across the network.

## Core Components of the Producer Service

### The Producer Struct and Its Dependencies

At the heart of block production in Fuel Core lies the `Producer` struct defined in [`crates/services/producer/src/block_producer.rs`](https://github.com/FuelLabs/fuel-core/blob/main/crates/services/producer/src/block_producer.rs). This generic struct aggregates several critical components that enable a node to create valid blocks:

- **`Config`** – Holds node-wide settings including the coinbase recipient address.
- **`view_provider`** – Provides read-only access to the latest database view via `AtomicView`.
- **`txpool`** – Supplies the set of transactions available for inclusion in the block.
- **`executor`** – Executes the block's state transition and returns an *uncommitted* result.
- **`relayer`** – Supplies the latest DA-layer height and gas-cost information for pending DA-blocks.
- **`lock: Mutex<()>`** – Guarantees that only one block is being produced at any given time.
- **`gas_price_provider`** – Returns the gas price for the block's mint transaction.
- **`chain_state_info_provider`** – Provides consensus parameters including the block gas limit.

### The Production Lock

Before any block production begins, the service acquires a mutual exclusion lock to prevent concurrent generation attempts. This is implemented as a standard `Mutex<()>` that must be held throughout the production lifecycle.

```rust
let _production_guard = self.lock.try_lock().map_err(|_| {
    anyhow!("Failed to acquire the production lock, block production is already in progress")
})?;

```

## Block Production Flow in Fuel Core

The production flow follows a strict sequence encapsulated in the `produce_and_execute` internal method. Here is how the producer service manages the end-to-end creation of a block:

### Step 1: Acquiring the Production Lock

The process begins by attempting to acquire the production mutex. If another block is currently being produced, the call returns an error immediately, ensuring serial block production.

### Step 2: Gas Price Determination and View Loading

The producer determines the gas price via `self.production_gas_price()` and loads the latest database view using `self.view_provider.latest_view()`. It then validates that the requested block height equals exactly one greater than the current tip height.

### Step 3: Header Construction with DA-Height Selection

The `new_header_with_new_da_height` method queries the **relayer** for the highest finalized DA-height, then selects a suitable new DA-height that respects both the block gas limit and the `u16::MAX - 1` transaction cap. The header also incorporates the latest consensus parameter version and state-transition bytecode version.

```rust
async fn new_header_with_new_da_height(
    &self,
    block_time: Tai64,
    view: &ViewProvider::LatestView,
) -> anyhow::Result<PartialBlockHeader> {
    let mut block_header = self.new_header(block_time, view)?;
    let previous_da_height = block_header.da_height;
    let gas_limit = self.chain_state_info_provider
        .consensus_params_at_version(&block_header.consensus_parameters_version)?
        .block_gas_limit();

    let new_da_height = self
        .select_new_da_height(gas_limit, previous_da_height, u16::MAX - 1)
        .await?;
    block_header.application.da_height = new_da_height;
    Ok(block_header)
}

```

### Step 4: Transaction Sourcing and Block Execution

After building a `Components` struct that bundles the header, transactions, coinbase recipient, and gas price, the producer executes the block via the executor. The call to `self.executor.produce_without_commit(component, deadline)` runs the state transition without persisting changes, returning an `UncommittedResult` that the caller can then commit to the database.

```rust
let result = self.executor
    .produce_without_commit(component, deadline)
    .await
    .map_err(Into::<anyhow::Error>::into)
    .context(format!("Failed to produce block {next_height:?} due to execution failure"))?;

```

## Public Methods for Block Production

The producer service exposes three primary async methods for block creation, depending on the transaction source:

### produce_and_execute_block_txpool

This is the standard entry point for validator and producer nodes. It pulls transactions from the transaction pool based on the current gas price and target block height.

```rust
self.produce_and_execute::<TxSource, _, Deadline>(
    height,
    block_time,
    |gas_price, height| self.txpool.get_source(gas_price, height),
    deadline,
)
.await

```

### produce_and_execute_block_transactions

Useful for testing scenarios or when a specific vector of transactions must be included. This method accepts a pre-defined list of transactions rather than querying the pool.

### produce_and_execute_predefined

This advanced method allows the caller to hand-craft a full `Block` instance, used primarily in integration tests that require precise control over block contents.

## Auxiliary Operations: Dry-Run and Storage Replay

The service provides **dry-run** (`dry_run`) and **storage-read-replay** (`storage_read_replay`) utilities that reuse the same header-building logic but intentionally skip the production lock, as these are read-only operations that do not modify state. These methods allow users to simulate transaction execution against historical or hypothetical states without impacting the canonical chain.

## Code Examples

### Produce a Block from the TxPool

This example demonstrates the typical node behavior for validators producing new blocks:

```rust
use fuel_core::producer::Producer;
use fuel_core::config::Config;

let height = /* next block height */;
let block_time = /* Tai64 timestamp */;
let deadline = std::time::Duration::from_secs(5);

let result = producer
    .produce_and_execute_block_txpool(height, block_time, deadline)
    .await?;

println!("Produced block {} with {} txs", height, result.result().transactions.len());

```

*Source:* [`produce_and_execute_block_txpool`](https://github.com/FuelLabs/fuel-core/blob/master/crates/services/producer/src/block_producer.rs#L305-L319)

### Produce a Block with Custom Transactions

When you need to include specific transactions rather than pool-selected ones:

```rust
let txs: Vec<Transaction> = vec![/* … */];
let result = producer
    .produce_and_execute_block_transactions(
        BlockHeight::new(42),
        Tai64::now(),
        txs,
    )
    .await?;

```

*Source:* [`produce_and_execute_block_transactions`](https://github.com/FuelLabs/fuel-core/blob/master/crates/services/producer/src/block_producer.rs#L330-L344)

### Dry-Run Transactions Without Committing

Simulate execution without acquiring the production lock or modifying the database:

```rust
let dry = producer
    .dry_run(
        txs,
        Some(BlockHeight::new(42)),
        None,
        Some(true),   // validate UTXO
        None,         // use producer's gas price
        false,        // don't record storage reads
    )
    .await?;
println!("Dry-run succeeded, receipts: {}", dry.receipts.len());

```

*Source:* [`dry_run`](https://github.com/FuelLabs/fuel-core/blob/master/crates/services/producer/src/block_producer.rs#L360-L424)

## Key Files in the Producer Service

| File | Contents |
|------|----------|
| [`crates/services/producer/src/block_producer.rs`](https://github.com/FuelLabs/fuel-core/blob/main/crates/services/producer/src/block_producer.rs) | The `Producer` implementation, header creation, DA-height selection, and block execution flow. |
| [`crates/services/producer/src/ports.rs`](https://github.com/FuelLabs/fuel-core/blob/main/crates/services/producer/src/ports.rs) | Trait definitions for database views, transaction pools, relayers, executors, and dry-run interfaces. |
| [`crates/services/producer/src/config.rs`](https://github.com/FuelLabs/fuel-core/blob/main/crates/services/producer/src/config.rs) | Configuration structs including coinbase address settings. |
| [`crates/services/producer/src/gas_price.rs`](https://github.com/FuelLabs/fuel-core/blob/main/crates/services/producer/src/gas_price.rs) | Gas-price provider abstractions for production and simulation contexts. |
| [`crates/services/producer/src/lib.rs`](https://github.com/FuelLabs/fuel-core/blob/main/crates/services/producer/src/lib.rs) | Public re-exports and crate entry points. |

## Summary

- **The `Producer` struct** in [`crates/services/producer/src/block_producer.rs`](https://github.com/FuelLabs/fuel-core/blob/main/crates/services/producer/src/block_producer.rs) coordinates all block production activities in Fuel Core by managing dependencies including the transaction pool, executor, and relayer.
- **A mutex lock** ensures atomic block production, preventing race conditions when multiple production requests arrive simultaneously.
- **Header construction** involves complex DA-height selection that respects gas limits and transaction caps while querying the relayer for finalized layer-1 state.
- **Three public methods** provide flexibility for production nodes (`produce_and_execute_block_txpool`), custom transaction lists (`produce_and_execute_block_transactions`), and predefined blocks (`produce_and_execute_predefined`).
- **Dry-run capabilities** allow transaction simulation without state commitment, reusing production logic but bypassing the lock mechanism.

## Frequently Asked Questions

### How does the producer service prevent concurrent block production?

The producer service uses a `Mutex<()>` field named `lock` within the `Producer` struct. Before beginning production, the code attempts to acquire this lock via `self.lock.try_lock()`. If the lock is already held by another production task, the method returns an error immediately, ensuring that only one block can be produced at a time per node instance.

### What determines the DA-height included in a new block?

The DA-height is determined by the `new_header_with_new_da_height` method, which queries the relayer for the highest finalized DA-layer height. The service then selects a new DA-height that satisfies two constraints: remaining under the block gas limit defined in consensus parameters, and respecting the `u16::MAX - 1` limit on transaction count. This ensures the block remains valid according to both Fuel Core and DA-layer rules.

### How does block production differ between validators and test scenarios?

Validators typically invoke `produce_and_execute_block_txpool`, which automatically sources transactions from the mempool based on current gas prices and block height. In contrast, test scenarios often use `produce_and_execute_block_transactions` to inject specific transaction vectors, or `produce_and_execute_predefined` to submit fully constructed `Block` instances for integration testing. Both test methods bypass the transaction pool selection logic.

### What happens if block execution fails during production?

If the executor's `produce_without_commit` method returns an error, the producer service wraps this error with context indicating the target block height and returns it to the caller. The uncommitted result is not persisted to the database, and the production lock is released when the guard drops out of scope, allowing subsequent production attempts without requiring manual intervention.