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
StorageTransactionensure all-or-nothing commits across multiple tables - Read-only views: The
ReadViewtype exposes per-table iterators without blocking writers - Merkle proof generation:
MerkleizedTableColumnimplementations 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.rsusing theMappabletrait pattern - Each table specifies concrete
OwnedKeyandOwnedValuetypes for type-safe storage - Core entities include
FuelBlocks,Coins,ContractsLatestUtxo, andMessages - The
GenericDatabasewrapper incrates/fuel-core/src/database/storage.rsimplements atomic batch operations and Merkle proof generation - Read operations use
ReadViewfor non-blocking access, while writes support transactional batches viaStorageBatchMutate
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →