Fuel Core Database Schema: A Comprehensive Guide to the Storage Layer

The Fuel Core database schema is a key-value storage layer built on the fuel-core-storage crate, where each logical entity (blocks, contracts, coins) is defined as a type-safe table implementing the Mappable trait with specific Key and Value types defined in crates/storage/src/tables.rs.

The Fuel Core storage layer, maintained in the FuelLabs/fuel-core repository, persists all on-chain state using a structured key-value approach rather than a relational model. Understanding the Fuel Core database schema is essential for developers building indexers, block explorers, or custom tooling that needs direct access to the chain's historical and current state.

Core Architecture of the Fuel Core Storage Layer

Fuel Core organizes its persistent storage into discrete tables that map directly to blockchain primitives. Unlike traditional SQL databases, this schema uses a strongly-typed key-value design where each table is a Rust struct implementing the Mappable trait.

The Mappable Trait Pattern

Every storage table in crates/storage/src/tables.rs defines four associated types that enforce type safety at the database boundary:

  • Key: The primary identifier type used for lookups
  • OwnedKey: The concrete Rust type serialized as the database key (e.g., BlockHeight, ContractId)
  • Value: The type returned by read operations, usually a reference
  • OwnedValue: The concrete Rust type persisted as the serialized value (e.g., CompressedBlock, ContractUtxoInfo)

This pattern ensures that operations like db.get::<FuelBlocks>(height) are compile-time checked against the actual schema definitions.

Complete Database Schema Reference

The canonical table definitions reside in crates/storage/src/tables.rs. The following tables comprise the core Fuel Core database schema:

Table OwnedKey OwnedValue Description
FuelBlocks BlockHeight CompressedBlock Serialized block headers and transactions indexed by height
ContractsLatestUtxo ContractId ContractUtxoInfo Current state/UTXO information for deployed contracts
SealedBlockConsensus BlockHeight Consensus Finalized consensus metadata for sealed blocks
Coins UtxoId CompressedCoin Individual UTXO entries representing spendable coins
Messages Nonce Message Bridged messages originating from Ethereum
Transactions TxId Transaction Full transaction objects indexed by transaction ID
ProcessedTransactions TxId () Deduplication marker table preventing replay
ConsensusParametersVersions ConsensusParametersVersion ConsensusParameters Versioned consensus configuration storage
StateTransitionBytecodeVersions StateTransitionBytecodeVersion Bytes32 Merkle roots of state transition bytecode uploads

Merkle Tree Storage Tables

The schema includes auxiliary tables under the merkle module for maintaining cryptographic proofs. These include FuelBlockMerkleData (mapping u64 indices to binary primitives) and metadata tables like DenseMerkleMetadata keyed by DenseMetadataKey<BlockHeight>. These support sparse and dense Merkle tree operations required for state root calculations.

Database Implementation and Wiring

The GenericDatabase struct in crates/fuel-core/src/database/storage.rs wraps the underlying key-value engine (typically RocksDB) and implements the StorageMutate, StorageWrite, and StorageBatchMutate traits for any type implementing Mappable.

This architecture enables:

  • Atomic transactions: Batched writes through StorageTransaction ensure all-or-nothing commits across multiple tables
  • Read-only views: The ReadView type exposes per-table iterators without blocking writers
  • Merkle proof generation: MerkleizedTableColumn implementations allow cryptographic verification of any table column

The generic design means adding a new table only requires defining a new Mappable type in tables.rs and implementing the trait for the database handle.

Practical Examples: Querying the Fuel Core Database

Inserting a Block Atomically

use fuel_core_storage::structured_storage::StructuredStorage;
use fuel_core_storage::StorageBatchMutate;
use fuel_core_types::blockchain::block::CompressedBlock;
use fuel_core_types::fuel_types::BlockHeight;

// db is a GenericDatabase instance wrapping RocksDB
let block_height: BlockHeight = 42.into();
let block: CompressedBlock = /* block construction */;

// Atomic batch insert into FuelBlocks table
db.insert_batch::<FuelBlocks>(std::iter::once((&block_height, &block)))?;

Querying Contract State

use fuel_core_storage::ReadView;
use fuel_core_types::fuel_types::ContractId;

// Obtained from ReadDatabase via GraphQL resolver or direct access
let contract_id: ContractId = /* contract identifier */;
let utxo_info = read_view
    .contract_latest_utxo(contract_id)?
    .map(|(_, info)| info); // Returns ContractUtxoInfo

Streaming the UTXO Set for Snapshots

use fuel_core_storage::ReadView;
use futures::StreamExt;

// Efficient iteration over all coins without loading into memory
let mut stream = read_view.table::<Coins>()?.iter();
while let Some(Ok((utxo_id, coin))) = stream.next().await {
    // Process each CompressedCoin entry
}

These examples demonstrate how the StructuredStorage API abstracts the underlying key-value operations while maintaining the type guarantees defined in the schema.

Summary

  • The Fuel Core database schema is defined in crates/storage/src/tables.rs using the Mappable trait pattern
  • Each table specifies concrete OwnedKey and OwnedValue types for type-safe storage
  • Core entities include FuelBlocks, Coins, ContractsLatestUtxo, and Messages
  • The GenericDatabase wrapper in crates/fuel-core/src/database/storage.rs implements atomic batch operations and Merkle proof generation
  • Read operations use ReadView for non-blocking access, while writes support transactional batches via StorageBatchMutate

Frequently Asked Questions

What database backend does Fuel Core use?

Fuel Core uses RocksDB as its default storage backend, wrapped by the GenericDatabase type. The storage layer abstracts the backend through the Mappable trait system, allowing the same schema definitions to work across different key-value implementations if needed.

How does Fuel Core handle atomic transactions?

Atomicity is achieved through the StorageBatchMutate trait implemented by GenericDatabase. Operations like insert_batch group multiple table modifications into a single atomic unit. If any part of the batch fails, the entire transaction is rolled back, ensuring consistency across related tables like FuelBlocks and Transactions.

What is the difference between the Transactions and ProcessedTransactions tables?

The Transactions table stores the full Transaction objects indexed by TxId, allowing retrieval of transaction data for blocks. The ProcessedTransactions table uses the same TxId as a key but stores only a unit type () as the value, serving as a lightweight deduplication marker to prevent transaction replay without storing duplicate data.

Where are Merkle roots stored in the schema?

Merkle metadata is stored in dedicated tables within the merkle module, including DenseMerkleMetadata keyed by DenseMetadataKey<BlockHeight>. The actual Merkle tree data resides in FuelBlockMerkleData (mapping u64 to binary values), while state transition bytecode roots are stored in StateTransitionBytecodeVersions as Bytes32 values.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →