Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: throw error if genesis header found on static files, but empty db #13157

Merged
merged 3 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/node/builder/src/launch/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -542,13 +542,13 @@ where
}

/// Convenience function to [`Self::init_genesis`]
pub fn with_genesis(self) -> Result<Self, InitDatabaseError> {
pub fn with_genesis(self) -> Result<Self, InitStorageError> {
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<B256, InitDatabaseError> {
pub fn init_genesis(&self) -> Result<B256, InitStorageError> {
init_genesis(self.provider_factory())
}

Expand Down
42 changes: 27 additions & 15 deletions crates/storage/db-common/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -43,17 +43,20 @@ 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 {storage_hash}")]
GenesisHashMismatch {
/// Expected genesis hash.
chainspec_hash: B256,
/// Actual genesis hash.
database_hash: B256,
storage_hash: B256,
},
/// Provider error.
#[error(transparent)]
Expand All @@ -63,18 +66,19 @@ pub enum InitDatabaseError {
StateRootMismatch(GotExpected<B256>),
}

impl From<DatabaseError> for InitDatabaseError {
impl From<DatabaseError> 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<PF>(factory: &PF) -> Result<B256, InitDatabaseError>
pub fn init_genesis<PF>(factory: &PF) -> Result<B256, InitStorageError>
where
PF: DatabaseProviderFactory
+ StaticFileProviderFactory<Primitives: NodePrimitives<BlockHeader: Compact>>
+ ChainSpecProvider
+ StageCheckpointReader
+ BlockHashReader,
PF::ProviderRW: StaticFileProviderFactory<Primitives = PF::Primitives>
+ StageCheckpointWriter
Expand All @@ -96,13 +100,21 @@ 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() {
error!(target: "reth::storage", "Genesis header found on static files, but database is uninitialized.");
return Err(InitStorageError::UninitializedDatabase)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we log an error here so in case this happens we know where this is thrown?

}

debug!("Genesis already written, skipping.");
return Ok(hash)
}

return Err(InitDatabaseError::GenesisHashMismatch {
return Err(InitStorageError::GenesisHashMismatch {
chainspec_hash: hash,
database_hash: block_hash,
storage_hash: block_hash,
})
}
Err(e) => {
Expand Down Expand Up @@ -376,7 +388,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,
})
Expand Down Expand Up @@ -409,7 +421,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,
})
Expand Down Expand Up @@ -622,7 +634,7 @@ mod tests {

fn collect_table_entries<DB, T>(
tx: &<DB as Database>::TX,
) -> Result<Vec<TableRow<T>>, InitDatabaseError>
) -> Result<Vec<TableRow<T>>, InitStorageError>
where
DB: Database,
T: Table,
Expand Down Expand Up @@ -672,9 +684,9 @@ mod tests {

assert_eq!(
genesis_hash.unwrap_err(),
InitDatabaseError::GenesisHashMismatch {
InitStorageError::GenesisHashMismatch {
chainspec_hash: MAINNET_GENESIS_HASH,
database_hash: SEPOLIA_GENESIS_HASH
storage_hash: SEPOLIA_GENESIS_HASH
}
)
}
Expand Down
Loading