From e20e56b75ec961731428b4193c31ac1065e2fd9f Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Thu, 6 Nov 2025 00:39:49 +0000 Subject: [PATCH] feat: add `Metadata` table and `StorageSettings` to `ProviderFactory` (#19384) --- Cargo.lock | 1 + crates/cli/commands/src/common.rs | 2 +- .../cli/commands/src/stage/dump/execution.rs | 2 +- .../src/stage/dump/hashing_account.rs | 2 +- .../src/stage/dump/hashing_storage.rs | 2 +- crates/cli/commands/src/stage/dump/merkle.rs | 2 +- crates/e2e-test-utils/src/setup_import.rs | 11 ++- crates/exex/test-utils/src/lib.rs | 2 +- crates/node/builder/src/launch/common.rs | 2 +- .../stages/stages/src/test_utils/test_db.rs | 6 +- crates/storage/db-api/src/models/metadata.rs | 39 ++++++++++ crates/storage/db-api/src/models/mod.rs | 2 + crates/storage/db-api/src/tables/mod.rs | 7 ++ crates/storage/db-common/src/init.rs | 28 ++++--- crates/storage/provider/src/lib.rs | 5 +- .../src/providers/database/builder.rs | 37 +++++----- .../provider/src/providers/database/mod.rs | 73 ++++++++++++++----- .../src/providers/database/provider.rs | 38 ++++++++-- crates/storage/provider/src/test_utils/mod.rs | 7 +- crates/storage/storage-api/Cargo.toml | 3 + crates/storage/storage-api/src/lib.rs | 7 ++ crates/storage/storage-api/src/metadata.rs | 53 ++++++++++++++ examples/rpc-db/src/main.rs | 2 +- 23 files changed, 265 insertions(+), 68 deletions(-) create mode 100644 crates/storage/db-api/src/models/metadata.rs create mode 100644 crates/storage/storage-api/src/metadata.rs diff --git a/Cargo.lock b/Cargo.lock index b7c8618f42..21d98573bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10567,6 +10567,7 @@ dependencies = [ "reth-storage-errors", "reth-trie-common", "revm-database", + "serde_json", ] [[package]] diff --git a/crates/cli/commands/src/common.rs b/crates/cli/commands/src/common.rs index 5b8cfce771..4d18d81184 100644 --- a/crates/cli/commands/src/common.rs +++ b/crates/cli/commands/src/common.rs @@ -132,7 +132,7 @@ impl EnvironmentArgs { db, self.chain.clone(), static_file_provider, - ) + )? .with_prune_modes(prune_modes.clone()); // Check for consistency between database and static files. diff --git a/crates/cli/commands/src/stage/dump/execution.rs b/crates/cli/commands/src/stage/dump/execution.rs index 9e8e68e980..887f97dddd 100644 --- a/crates/cli/commands/src/stage/dump/execution.rs +++ b/crates/cli/commands/src/stage/dump/execution.rs @@ -42,7 +42,7 @@ where Arc::new(output_db), db_tool.chain(), StaticFileProvider::read_write(output_datadir.static_files())?, - ), + )?, to, from, evm_config, diff --git a/crates/cli/commands/src/stage/dump/hashing_account.rs b/crates/cli/commands/src/stage/dump/hashing_account.rs index 8b9ba5e937..0e976d4235 100644 --- a/crates/cli/commands/src/stage/dump/hashing_account.rs +++ b/crates/cli/commands/src/stage/dump/hashing_account.rs @@ -39,7 +39,7 @@ pub(crate) async fn dump_hashing_account_stage + pub receipts_in_static_files: bool, +} + +impl StorageSettings { + /// Creates a new `StorageSettings` with default values. + pub const fn new() -> Self { + Self { receipts_in_static_files: false } + } + + /// Creates `StorageSettings` for legacy nodes. + /// + /// This explicitly sets `receipts_in_static_files` to `false`, ensuring older nodes + /// continue writing receipts to the database when receipt pruning is enabled. + pub const fn legacy() -> Self { + Self { receipts_in_static_files: false } + } + + /// Sets the `receipts_static_files` flag to true. + pub const fn with_receipts_in_static_files(mut self) -> Self { + self.receipts_in_static_files = true; + self + } +} diff --git a/crates/storage/db-api/src/models/mod.rs b/crates/storage/db-api/src/models/mod.rs index 31d9b301f8..ebc3625250 100644 --- a/crates/storage/db-api/src/models/mod.rs +++ b/crates/storage/db-api/src/models/mod.rs @@ -20,12 +20,14 @@ use serde::{Deserialize, Serialize}; pub mod accounts; pub mod blocks; pub mod integer_list; +pub mod metadata; pub mod sharded_key; pub mod storage_sharded_key; pub use accounts::*; pub use blocks::*; pub use integer_list::IntegerList; +pub use metadata::*; pub use reth_db_models::{ AccountBeforeTx, ClientVersion, StaticFileBlockWithdrawals, StoredBlockBodyIndices, StoredBlockWithdrawals, diff --git a/crates/storage/db-api/src/tables/mod.rs b/crates/storage/db-api/src/tables/mod.rs index cf2a20fff0..483048383a 100644 --- a/crates/storage/db-api/src/tables/mod.rs +++ b/crates/storage/db-api/src/tables/mod.rs @@ -540,6 +540,13 @@ tables! { type Key = ChainStateKey; type Value = BlockNumber; } + + /// Stores generic node metadata as key-value pairs. + /// Can store feature flags, configuration markers, and other node-specific data. + table Metadata { + type Key = String; + type Value = Vec; + } } /// Keys for the `ChainState` table. diff --git a/crates/storage/db-common/src/init.rs b/crates/storage/db-common/src/init.rs index de55cea3c9..3579d5360d 100644 --- a/crates/storage/db-common/src/init.rs +++ b/crates/storage/db-common/src/init.rs @@ -15,9 +15,9 @@ use reth_primitives_traits::{ use reth_provider::{ errors::provider::ProviderResult, providers::StaticFileWriter, BlockHashReader, BlockNumReader, BundleStateInit, ChainSpecProvider, DBProvider, DatabaseProviderFactory, ExecutionOutcome, - HashingWriter, HeaderProvider, HistoryWriter, OriginalValuesKnown, ProviderError, RevertsInit, - StageCheckpointReader, StageCheckpointWriter, StateWriter, StaticFileProviderFactory, - TrieWriter, + HashingWriter, HeaderProvider, HistoryWriter, MetadataWriter, OriginalValuesKnown, + ProviderError, RevertsInit, StageCheckpointReader, StageCheckpointWriter, StateWriter, + StaticFileProviderFactory, StorageSettings, StorageSettingsCache, TrieWriter, }; use reth_stages_types::{StageCheckpoint, StageId}; use reth_static_file_types::StaticFileSegment; @@ -90,7 +90,8 @@ where + StaticFileProviderFactory> + ChainSpecProvider + StageCheckpointReader - + BlockHashReader, + + BlockHashReader + + StorageSettingsCache, PF::ProviderRW: StaticFileProviderFactory + StageCheckpointWriter + HistoryWriter @@ -98,6 +99,7 @@ where + HashingWriter + StateWriter + TrieWriter + + MetadataWriter + AsRef, PF::ChainSpec: EthChainSpec
::BlockHeader>, { @@ -161,9 +163,14 @@ where static_file_provider.latest_writer(StaticFileSegment::Receipts)?.increment_block(0)?; static_file_provider.latest_writer(StaticFileSegment::Transactions)?.increment_block(0)?; + // Behaviour reserved only for new nodes should be set here. + let storage_settings = StorageSettings::new(); + provider_rw.write_storage_settings(storage_settings)?; + // `commit_unwind`` will first commit the DB and then the static file provider, which is // necessary on `init_genesis`. provider_rw.commit()?; + factory.set_storage_settings_cache(storage_settings); Ok(hash) } @@ -726,11 +733,14 @@ mod tests { init_genesis(&factory).unwrap(); // Try to init db with a different genesis block - let genesis_hash = init_genesis(&ProviderFactory::::new( - factory.into_db(), - MAINNET.clone(), - static_file_provider, - )); + let genesis_hash = init_genesis( + &ProviderFactory::::new( + factory.into_db(), + MAINNET.clone(), + static_file_provider, + ) + .unwrap(), + ); assert!(matches!( genesis_hash.unwrap_err(), diff --git a/crates/storage/provider/src/lib.rs b/crates/storage/provider/src/lib.rs index 70822c604b..5cd598aa46 100644 --- a/crates/storage/provider/src/lib.rs +++ b/crates/storage/provider/src/lib.rs @@ -49,7 +49,10 @@ pub use reth_chain_state::{ }; // reexport traits to avoid breaking changes -pub use reth_storage_api::{HistoryWriter, StatsReader}; +pub use reth_storage_api::{ + HistoryWriter, MetadataProvider, MetadataWriter, StatsReader, StorageSettings, + StorageSettingsCache, +}; pub(crate) fn to_range>(bounds: R) -> std::ops::Range { let start = match bounds.start_bound() { diff --git a/crates/storage/provider/src/providers/database/builder.rs b/crates/storage/provider/src/providers/database/builder.rs index 4bc8569432..bcd61f188f 100644 --- a/crates/storage/provider/src/providers/database/builder.rs +++ b/crates/storage/provider/src/providers/database/builder.rs @@ -3,13 +3,17 @@ //! This also includes general purpose staging types that provide builder style functions that lead //! up to the intended build target. -use crate::{providers::StaticFileProvider, ProviderFactory}; +use crate::{ + providers::{NodeTypesForProvider, StaticFileProvider}, + ProviderFactory, +}; use reth_db::{ mdbx::{DatabaseArguments, MaxReadTransactionDuration}, open_db_read_only, DatabaseEnv, }; use reth_db_api::{database_metrics::DatabaseMetrics, Database}; use reth_node_types::{NodeTypes, NodeTypesWithDBAdapter}; +use reth_storage_errors::provider::ProviderResult; use std::{ marker::PhantomData, path::{Path, PathBuf}, @@ -48,10 +52,9 @@ impl ProviderFactoryBuilder { /// /// ```no_run /// use reth_chainspec::MAINNET; - /// use reth_node_types::NodeTypes; - /// use reth_provider::providers::ProviderFactoryBuilder; + /// use reth_provider::providers::{NodeTypesForProvider, ProviderFactoryBuilder}; /// - /// fn demo>() { + /// fn demo>() { /// let provider_factory = ProviderFactoryBuilder::::default() /// .open_read_only(MAINNET.clone(), "datadir") /// .unwrap(); @@ -64,11 +67,9 @@ impl ProviderFactoryBuilder { /// /// ```no_run /// use reth_chainspec::MAINNET; - /// use reth_node_types::NodeTypes; + /// use reth_provider::providers::{NodeTypesForProvider, ProviderFactoryBuilder, ReadOnlyConfig}; /// - /// use reth_provider::providers::{ProviderFactoryBuilder, ReadOnlyConfig}; - /// - /// fn demo>() { + /// fn demo>() { /// let provider_factory = ProviderFactoryBuilder::::default() /// .open_read_only(MAINNET.clone(), ReadOnlyConfig::from_datadir("datadir").no_watch()) /// .unwrap(); @@ -84,11 +85,9 @@ impl ProviderFactoryBuilder { /// /// ```no_run /// use reth_chainspec::MAINNET; - /// use reth_node_types::NodeTypes; + /// use reth_provider::providers::{NodeTypesForProvider, ProviderFactoryBuilder, ReadOnlyConfig}; /// - /// use reth_provider::providers::{ProviderFactoryBuilder, ReadOnlyConfig}; - /// - /// fn demo>() { + /// fn demo>() { /// let provider_factory = ProviderFactoryBuilder::::default() /// .open_read_only( /// MAINNET.clone(), @@ -103,15 +102,15 @@ impl ProviderFactoryBuilder { config: impl Into, ) -> eyre::Result>>> where - N: NodeTypes, + N: NodeTypesForProvider, { let ReadOnlyConfig { db_dir, db_args, static_files_dir, watch_static_files } = config.into(); - Ok(self - .db(Arc::new(open_db_read_only(db_dir, db_args)?)) + self.db(Arc::new(open_db_read_only(db_dir, db_args)?)) .chainspec(chainspec) .static_file(StaticFileProvider::read_only(static_files_dir, watch_static_files)?) - .build_provider_factory()) + .build_provider_factory() + .map_err(Into::into) } } @@ -320,11 +319,13 @@ impl TypesAnd3 { impl TypesAnd3, StaticFileProvider> where - N: NodeTypes, + N: NodeTypesForProvider, DB: Database + DatabaseMetrics + Clone + Unpin + 'static, { /// Creates the [`ProviderFactory`]. - pub fn build_provider_factory(self) -> ProviderFactory> { + pub fn build_provider_factory( + self, + ) -> ProviderResult>> { let Self { _types, val_1, val_2, val_3 } = self; ProviderFactory::new(val_1, val_2, val_3) } diff --git a/crates/storage/provider/src/providers/database/mod.rs b/crates/storage/provider/src/providers/database/mod.rs index 5d3b5280cd..a0de2f9e74 100644 --- a/crates/storage/provider/src/providers/database/mod.rs +++ b/crates/storage/provider/src/providers/database/mod.rs @@ -1,29 +1,31 @@ use crate::{ - providers::{state::latest::LatestStateProvider, StaticFileProvider}, + providers::{state::latest::LatestStateProvider, NodeTypesForProvider, StaticFileProvider}, to_range, traits::{BlockSource, ReceiptProvider}, BlockHashReader, BlockNumReader, BlockReader, ChainSpecProvider, DatabaseProviderFactory, - HashedPostStateProvider, HeaderProvider, HeaderSyncGapProvider, ProviderError, - PruneCheckpointReader, StageCheckpointReader, StateProviderBox, StaticFileProviderFactory, - TransactionVariant, TransactionsProvider, + HashedPostStateProvider, HeaderProvider, HeaderSyncGapProvider, MetadataProvider, + ProviderError, PruneCheckpointReader, StageCheckpointReader, StateProviderBox, + StaticFileProviderFactory, TransactionVariant, TransactionsProvider, }; use alloy_consensus::transaction::TransactionMeta; use alloy_eips::BlockHashOrNumber; use alloy_primitives::{Address, BlockHash, BlockNumber, TxHash, TxNumber, B256}; use core::fmt; +use parking_lot::RwLock; use reth_chainspec::ChainInfo; use reth_db::{init_db, mdbx::DatabaseArguments, DatabaseEnv}; use reth_db_api::{database::Database, models::StoredBlockBodyIndices}; use reth_errors::{RethError, RethResult}; use reth_node_types::{ - BlockTy, HeaderTy, NodeTypes, NodeTypesWithDB, NodeTypesWithDBAdapter, ReceiptTy, TxTy, + BlockTy, HeaderTy, NodeTypesWithDB, NodeTypesWithDBAdapter, ReceiptTy, TxTy, }; use reth_primitives_traits::{RecoveredBlock, SealedHeader}; use reth_prune_types::{PruneCheckpoint, PruneModes, PruneSegment}; use reth_stages_types::{StageCheckpoint, StageId}; use reth_static_file_types::StaticFileSegment; use reth_storage_api::{ - BlockBodyIndicesProvider, NodePrimitivesProvider, TryIntoHistoricalStateProvider, + BlockBodyIndicesProvider, NodePrimitivesProvider, StorageSettings, StorageSettingsCache, + TryIntoHistoricalStateProvider, }; use reth_storage_errors::provider::ProviderResult; use reth_trie::HashedPostState; @@ -64,31 +66,52 @@ pub struct ProviderFactory { prune_modes: PruneModes, /// The node storage handler. storage: Arc, + /// Storage configuration settings for this node + storage_settings: Arc>, } -impl ProviderFactory>> { +impl ProviderFactory>> { /// Instantiates the builder for this type pub fn builder() -> ProviderFactoryBuilder { ProviderFactoryBuilder::default() } } -impl ProviderFactory { +impl ProviderFactory { /// Create new database provider factory. pub fn new( db: N::DB, chain_spec: Arc, static_file_provider: StaticFileProvider, - ) -> Self { - Self { + ) -> ProviderResult { + // Load storage settings from database at init time. Creates a temporary provider + // to read persisted settings, falling back to legacy defaults if none exist. + // + // Both factory and all providers it creates should share these cached settings. + let legacy_settings = StorageSettings::legacy(); + let storage_settings = DatabaseProvider::<_, N>::new( + db.tx()?, + chain_spec.clone(), + static_file_provider.clone(), + Default::default(), + Default::default(), + Arc::new(RwLock::new(legacy_settings)), + ) + .storage_settings()? + .unwrap_or(legacy_settings); + + Ok(Self { db, chain_spec, static_file_provider, prune_modes: PruneModes::default(), storage: Default::default(), - } + storage_settings: Arc::new(RwLock::new(storage_settings)), + }) } +} +impl ProviderFactory { /// Enables metrics on the static file provider. pub fn with_static_files_metrics(mut self) -> Self { self.static_file_provider = self.static_file_provider.with_metrics(); @@ -113,7 +136,17 @@ impl ProviderFactory { } } -impl>> ProviderFactory { +impl StorageSettingsCache for ProviderFactory { + fn cached_storage_settings(&self) -> StorageSettings { + *self.storage_settings.read() + } + + fn set_storage_settings_cache(&self, settings: StorageSettings) { + *self.storage_settings.write() = settings; + } +} + +impl>> ProviderFactory { /// Create new database provider by passing a path. [`ProviderFactory`] will own the database /// instance. pub fn new_with_database_path>( @@ -122,13 +155,12 @@ impl>> ProviderFactory { args: DatabaseArguments, static_file_provider: StaticFileProvider, ) -> RethResult { - Ok(Self { - db: Arc::new(init_db(path, args).map_err(RethError::msg)?), + Self::new( + Arc::new(init_db(path, args).map_err(RethError::msg)?), chain_spec, static_file_provider, - prune_modes: PruneModes::default(), - storage: Default::default(), - }) + ) + .map_err(RethError::Provider) } } @@ -147,6 +179,7 @@ impl ProviderFactory { self.static_file_provider.clone(), self.prune_modes.clone(), self.storage.clone(), + self.storage_settings.clone(), )) } @@ -162,6 +195,7 @@ impl ProviderFactory { self.static_file_provider.clone(), self.prune_modes.clone(), self.storage.clone(), + self.storage_settings.clone(), ))) } @@ -545,13 +579,15 @@ where N: NodeTypesWithDB, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { db, chain_spec, static_file_provider, prune_modes, storage } = self; + let Self { db, chain_spec, static_file_provider, prune_modes, storage, storage_settings } = + self; f.debug_struct("ProviderFactory") .field("db", &db) .field("chain_spec", &chain_spec) .field("static_file_provider", &static_file_provider) .field("prune_modes", &prune_modes) .field("storage", &storage) + .field("storage_settings", &*storage_settings.read()) .finish() } } @@ -564,6 +600,7 @@ impl Clone for ProviderFactory { static_file_provider: self.static_file_provider.clone(), prune_modes: self.prune_modes.clone(), storage: self.storage.clone(), + storage_settings: self.storage_settings.clone(), } } } diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index a90b2c2e64..b46ccd9a63 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -31,6 +31,7 @@ use alloy_primitives::{ Address, BlockHash, BlockNumber, TxHash, TxNumber, B256, }; use itertools::Itertools; +use parking_lot::RwLock; use rayon::slice::ParallelSliceMut; use reth_chain_state::ExecutedBlock; use reth_chainspec::{ChainInfo, ChainSpecProvider, EthChainSpec}; @@ -39,7 +40,7 @@ use reth_db_api::{ database::Database, models::{ sharded_key, storage_sharded_key::StorageShardedKey, AccountBeforeTx, BlockNumberAddress, - BlockNumberHashedAddress, ShardedKey, StoredBlockBodyIndices, + BlockNumberHashedAddress, ShardedKey, StorageSettings, StoredBlockBodyIndices, }, table::Table, tables, @@ -57,8 +58,9 @@ use reth_prune_types::{ use reth_stages_types::{StageCheckpoint, StageId}; use reth_static_file_types::StaticFileSegment; use reth_storage_api::{ - BlockBodyIndicesProvider, BlockBodyReader, NodePrimitivesProvider, StateProvider, - StorageChangeSetReader, TryIntoHistoricalStateProvider, + BlockBodyIndicesProvider, BlockBodyReader, MetadataProvider, MetadataWriter, + NodePrimitivesProvider, StateProvider, StorageChangeSetReader, StorageSettingsCache, + TryIntoHistoricalStateProvider, }; use reth_storage_errors::provider::ProviderResult; use reth_trie::{ @@ -153,6 +155,8 @@ pub struct DatabaseProvider { prune_modes: PruneModes, /// Node storage handler. storage: Arc, + /// Storage configuration settings for this node + storage_settings: Arc>, } impl DatabaseProvider { @@ -248,8 +252,9 @@ impl DatabaseProvider { static_file_provider: StaticFileProvider, prune_modes: PruneModes, storage: Arc, + storage_settings: Arc>, ) -> Self { - Self { tx, chain_spec, static_file_provider, prune_modes, storage } + Self { tx, chain_spec, static_file_provider, prune_modes, storage, storage_settings } } } @@ -494,8 +499,9 @@ impl DatabaseProvider { static_file_provider: StaticFileProvider, prune_modes: PruneModes, storage: Arc, + storage_settings: Arc>, ) -> Self { - Self { tx, chain_spec, static_file_provider, prune_modes, storage } + Self { tx, chain_spec, static_file_provider, prune_modes, storage, storage_settings } } /// Consume `DbTx` or `DbTxMut`. @@ -3133,6 +3139,28 @@ impl DBProvider for DatabaseProvider } } +impl MetadataProvider for DatabaseProvider { + fn get_metadata(&self, key: &str) -> ProviderResult>> { + self.tx.get::(key.to_string()).map_err(Into::into) + } +} + +impl MetadataWriter for DatabaseProvider { + fn write_metadata(&self, key: &str, value: Vec) -> ProviderResult<()> { + self.tx.put::(key.to_string(), value).map_err(Into::into) + } +} + +impl StorageSettingsCache for DatabaseProvider { + fn cached_storage_settings(&self) -> StorageSettings { + *self.storage_settings.read() + } + + fn set_storage_settings_cache(&self, settings: StorageSettings) { + *self.storage_settings.write() = settings; + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/storage/provider/src/test_utils/mod.rs b/crates/storage/provider/src/test_utils/mod.rs index ccda2d60e8..5530c7411c 100644 --- a/crates/storage/provider/src/test_utils/mod.rs +++ b/crates/storage/provider/src/test_utils/mod.rs @@ -1,5 +1,5 @@ use crate::{ - providers::{ProviderNodeTypes, StaticFileProvider}, + providers::{NodeTypesForProvider, ProviderNodeTypes, StaticFileProvider}, HashingWriter, ProviderFactory, TrieWriter, }; use alloy_primitives::B256; @@ -10,7 +10,7 @@ use reth_db::{ }; use reth_errors::ProviderResult; use reth_ethereum_engine_primitives::EthEngineTypes; -use reth_node_types::{NodeTypes, NodeTypesWithDBAdapter}; +use reth_node_types::NodeTypesWithDBAdapter; use reth_primitives_traits::{Account, StorageEntry}; use reth_trie::StateRoot; use reth_trie_db::DatabaseStateRoot; @@ -50,7 +50,7 @@ pub fn create_test_provider_factory_with_chain_spec( } /// Creates test provider factory with provided chain spec. -pub fn create_test_provider_factory_with_node_types( +pub fn create_test_provider_factory_with_node_types( chain_spec: Arc, ) -> ProviderFactory>>> { let (static_dir, _) = create_test_static_files_dir(); @@ -60,6 +60,7 @@ pub fn create_test_provider_factory_with_node_types( chain_spec, StaticFileProvider::read_write(static_dir.keep()).expect("static file provider"), ) + .expect("failed to create test provider factory") } /// Inserts the genesis alloc from the provided chain spec into the trie. diff --git a/crates/storage/storage-api/Cargo.toml b/crates/storage/storage-api/Cargo.toml index a62193a5dd..83cbbbd714 100644 --- a/crates/storage/storage-api/Cargo.toml +++ b/crates/storage/storage-api/Cargo.toml @@ -32,6 +32,7 @@ alloy-consensus.workspace = true alloy-rpc-types-engine.workspace = true auto_impl.workspace = true +serde_json = { workspace = true, optional = true } [features] default = ["std"] @@ -50,10 +51,12 @@ std = [ "reth-storage-errors/std", "reth-db-models/std", "reth-trie-common/std", + "serde_json?/std", ] db-api = [ "dep:reth-db-api", + "dep:serde_json", ] serde = [ diff --git a/crates/storage/storage-api/src/lib.rs b/crates/storage/storage-api/src/lib.rs index 897802da98..5a191f3750 100644 --- a/crates/storage/storage-api/src/lib.rs +++ b/crates/storage/storage-api/src/lib.rs @@ -94,5 +94,12 @@ pub use state_writer::*; mod header_sync_gap; pub use header_sync_gap::HeaderSyncGapProvider; +#[cfg(feature = "db-api")] +pub mod metadata; +#[cfg(feature = "db-api")] +pub use metadata::{MetadataProvider, MetadataWriter, StorageSettingsCache}; +#[cfg(feature = "db-api")] +pub use reth_db_api::models::StorageSettings; + mod full; pub use full::*; diff --git a/crates/storage/storage-api/src/metadata.rs b/crates/storage/storage-api/src/metadata.rs new file mode 100644 index 0000000000..2ff48f7338 --- /dev/null +++ b/crates/storage/storage-api/src/metadata.rs @@ -0,0 +1,53 @@ +//! Metadata provider trait for reading and writing node metadata. + +use reth_db_api::models::StorageSettings; +use reth_storage_errors::provider::{ProviderError, ProviderResult}; + +/// Metadata keys. +pub mod keys { + /// Storage configuration settings for this node. + pub const STORAGE_SETTINGS: &str = "storage_settings"; +} + +/// Client trait for reading node metadata from the database. +#[auto_impl::auto_impl(&, Arc)] +pub trait MetadataProvider: Send + Sync { + /// Get a metadata value by key + fn get_metadata(&self, key: &str) -> ProviderResult>>; + + /// Get storage settings for this node + fn storage_settings(&self) -> ProviderResult> { + self.get_metadata(keys::STORAGE_SETTINGS)? + .map(|bytes| serde_json::from_slice(&bytes).map_err(ProviderError::other)) + .transpose() + } +} + +/// Client trait for writing node metadata to the database. +pub trait MetadataWriter: Send + Sync { + /// Write a metadata value + fn write_metadata(&self, key: &str, value: Vec) -> ProviderResult<()>; + + /// Write storage settings for this node + /// + /// Be sure to update provider factory cache with + /// [`StorageSettingsCache::set_storage_settings_cache`]. + fn write_storage_settings(&self, settings: StorageSettings) -> ProviderResult<()> { + self.write_metadata( + keys::STORAGE_SETTINGS, + serde_json::to_vec(&settings).map_err(ProviderError::other)?, + ) + } +} + +/// Trait for caching storage settings on a provider factory. +pub trait StorageSettingsCache: Send + Sync { + /// Gets the cached storage settings. + fn cached_storage_settings(&self) -> StorageSettings; + + /// Sets the storage settings of this `ProviderFactory`. + /// + /// IMPORTANT: It does not save settings in storage, that should be done by + /// [`MetadataWriter::write_storage_settings`] + fn set_storage_settings_cache(&self, settings: StorageSettings); +} diff --git a/examples/rpc-db/src/main.rs b/examples/rpc-db/src/main.rs index 97bd1debdc..b19d99776a 100644 --- a/examples/rpc-db/src/main.rs +++ b/examples/rpc-db/src/main.rs @@ -53,7 +53,7 @@ async fn main() -> eyre::Result<()> { db.clone(), spec.clone(), StaticFileProvider::read_only(db_path.join("static_files"), true)?, - ); + )?; // 2. Set up the blockchain provider using only the database provider and a noop for the tree to // satisfy trait bounds. Tree is not used in this example since we are only operating on the