From caaefd4526b59f79f22d37f6dbfff277c7a7e743 Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:44:07 +0000 Subject: [PATCH 1/3] throw error if genesis header found on sf, but empty db --- crates/node/builder/src/launch/common.rs | 6 ++-- crates/storage/db-common/src/init.rs | 35 ++++++++++++++++-------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/crates/node/builder/src/launch/common.rs b/crates/node/builder/src/launch/common.rs index f4557bd2272e..648edc1938e5 100644 --- a/crates/node/builder/src/launch/common.rs +++ b/crates/node/builder/src/launch/common.rs @@ -14,7 +14,7 @@ use reth_beacon_consensus::EthBeaconConsensus; use reth_chainspec::{Chain, EthChainSpec, EthereumHardforks}; use reth_config::{config::EtlConfig, PruneConfig}; use reth_db_api::{database::Database, database_metrics::DatabaseMetrics}; -use reth_db_common::init::{init_genesis, InitDatabaseError}; +use reth_db_common::init::{init_genesis, InitStorageError}; use reth_downloaders::{bodies::noop::NoopBodiesDownloader, headers::noop::NoopHeaderDownloader}; use reth_engine_local::MiningMode; use reth_engine_tree::tree::{InvalidBlockHook, InvalidBlockHooks, NoopInvalidBlockHook}; @@ -542,13 +542,13 @@ where } /// Convenience function to [`Self::init_genesis`] - pub fn with_genesis(self) -> Result { + pub fn with_genesis(self) -> Result { init_genesis(self.provider_factory())?; Ok(self) } /// Write the genesis block and state if it has not already been written - pub fn init_genesis(&self) -> Result { + pub fn init_genesis(&self) -> Result { init_genesis(self.provider_factory()) } diff --git a/crates/storage/db-common/src/init.rs b/crates/storage/db-common/src/init.rs index 493b27be7808..425316f6eb7f 100644 --- a/crates/storage/db-common/src/init.rs +++ b/crates/storage/db-common/src/init.rs @@ -16,8 +16,8 @@ use reth_provider::{ errors::provider::ProviderResult, providers::StaticFileWriter, writer::UnifiedStorageWriter, BlockHashReader, BlockNumReader, BundleStateInit, ChainSpecProvider, DBProvider, DatabaseProviderFactory, ExecutionOutcome, HashingWriter, HeaderProvider, HistoryWriter, - OriginalValuesKnown, ProviderError, RevertsInit, StageCheckpointWriter, StateWriter, - StaticFileProviderFactory, StorageLocation, TrieWriter, + OriginalValuesKnown, ProviderError, RevertsInit, StageCheckpointReader, StageCheckpointWriter, + StateWriter, StaticFileProviderFactory, StorageLocation, TrieWriter, }; use reth_stages_types::{StageCheckpoint, StageId}; use reth_trie::{IntermediateStateRootState, StateRoot as StateRootComputer, StateRootProgress}; @@ -43,12 +43,15 @@ pub const AVERAGE_COUNT_ACCOUNTS_PER_GB_STATE_DUMP: usize = 285_228; /// Soft limit for the number of flushed updates after which to log progress summary. const SOFT_LIMIT_COUNT_FLUSHED_UPDATES: usize = 1_000_000; -/// Database initialization error type. +/// Storage initialization error type. #[derive(Debug, thiserror::Error, PartialEq, Eq, Clone)] -pub enum InitDatabaseError { +pub enum InitStorageError { + /// Genesis header found on static files but the database is empty. + #[error("static files found, but the database is uninitialized. If attempting to re-syncing, delete both.")] + UninitializedDatabase, /// An existing genesis block was found in the database, and its hash did not match the hash of /// the chainspec. - #[error("genesis hash in the database does not match the specified chainspec: chainspec is {chainspec_hash}, database is {database_hash}")] + #[error("genesis hash in the storage does not match the specified chainspec: chainspec is {chainspec_hash}, database is {database_hash}")] GenesisHashMismatch { /// Expected genesis hash. chainspec_hash: B256, @@ -63,18 +66,19 @@ pub enum InitDatabaseError { StateRootMismatch(GotExpected), } -impl From for InitDatabaseError { +impl From for InitStorageError { fn from(error: DatabaseError) -> Self { Self::Provider(ProviderError::Database(error)) } } /// Write the genesis block if it has not already been written -pub fn init_genesis(factory: &PF) -> Result +pub fn init_genesis(factory: &PF) -> Result where PF: DatabaseProviderFactory + StaticFileProviderFactory> + ChainSpecProvider + + StageCheckpointReader + BlockHashReader, PF::ProviderRW: StaticFileProviderFactory + StageCheckpointWriter @@ -96,11 +100,18 @@ where Ok(None) | Err(ProviderError::MissingStaticFileBlock(StaticFileSegment::Headers, 0)) => {} Ok(Some(block_hash)) => { if block_hash == hash { + // Some users will at times attempt to re-sync from scratch by just deleting the + // database. Since `factory.block_hash` will only query the static files, we need to + // make sure that our database has been written to, and throw error if it's empty. + if factory.get_stage_checkpoint(StageId::Headers)?.is_none() { + return Err(InitStorageError::UninitializedDatabase) + } + debug!("Genesis already written, skipping."); return Ok(hash) } - return Err(InitDatabaseError::GenesisHashMismatch { + return Err(InitStorageError::GenesisHashMismatch { chainspec_hash: hash, database_hash: block_hash, }) @@ -376,7 +387,7 @@ where ?expected_state_root, "State root from state dump does not match state root in current header." ); - return Err(InitDatabaseError::StateRootMismatch(GotExpected { + return Err(InitStorageError::StateRootMismatch(GotExpected { got: dump_state_root, expected: expected_state_root, }) @@ -409,7 +420,7 @@ where "Computed state root does not match state root in state dump" ); - return Err(InitDatabaseError::StateRootMismatch(GotExpected { + return Err(InitStorageError::StateRootMismatch(GotExpected { got: computed_state_root, expected: expected_state_root, }) @@ -622,7 +633,7 @@ mod tests { fn collect_table_entries( tx: &::TX, - ) -> Result>, InitDatabaseError> + ) -> Result>, InitStorageError> where DB: Database, T: Table, @@ -672,7 +683,7 @@ mod tests { assert_eq!( genesis_hash.unwrap_err(), - InitDatabaseError::GenesisHashMismatch { + InitStorageError::GenesisHashMismatch { chainspec_hash: MAINNET_GENESIS_HASH, database_hash: SEPOLIA_GENESIS_HASH } From 8cee240bc08e509fb6aad4c1f1e3b66711686e45 Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:50:12 +0000 Subject: [PATCH 2/3] rename to storage_hash --- crates/storage/db-common/src/init.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/storage/db-common/src/init.rs b/crates/storage/db-common/src/init.rs index 425316f6eb7f..437897716f08 100644 --- a/crates/storage/db-common/src/init.rs +++ b/crates/storage/db-common/src/init.rs @@ -51,12 +51,12 @@ pub enum InitStorageError { UninitializedDatabase, /// An existing genesis block was found in the database, and its hash did not match the hash of /// the chainspec. - #[error("genesis hash in the storage does not match the specified chainspec: chainspec is {chainspec_hash}, database is {database_hash}")] + #[error("genesis hash in the storage does not match the specified chainspec: chainspec is {chainspec_hash}, database is {storage_hash}")] GenesisHashMismatch { /// Expected genesis hash. chainspec_hash: B256, /// Actual genesis hash. - database_hash: B256, + storage_hash: B256, }, /// Provider error. #[error(transparent)] @@ -113,7 +113,7 @@ where return Err(InitStorageError::GenesisHashMismatch { chainspec_hash: hash, - database_hash: block_hash, + storage_hash: block_hash, }) } Err(e) => { @@ -685,7 +685,7 @@ mod tests { genesis_hash.unwrap_err(), InitStorageError::GenesisHashMismatch { chainspec_hash: MAINNET_GENESIS_HASH, - database_hash: SEPOLIA_GENESIS_HASH + storage_hash: SEPOLIA_GENESIS_HASH } ) } From ff820a4e449562017318588dd8bc82d5810ab01e Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Thu, 5 Dec 2024 14:21:33 +0000 Subject: [PATCH 3/3] add error log --- crates/storage/db-common/src/init.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/storage/db-common/src/init.rs b/crates/storage/db-common/src/init.rs index 437897716f08..95b2a5d5c4a9 100644 --- a/crates/storage/db-common/src/init.rs +++ b/crates/storage/db-common/src/init.rs @@ -104,6 +104,7 @@ where // database. Since `factory.block_hash` will only query the static files, we need to // make sure that our database has been written to, and throw error if it's empty. if factory.get_stage_checkpoint(StageId::Headers)?.is_none() { + error!(target: "reth::storage", "Genesis header found on static files, but database is uninitialized."); return Err(InitStorageError::UninitializedDatabase) }