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

feat(snapshots): write Headers to snapshots during HeaderStage #6273

Merged
merged 51 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
24b9a1e
write headers to static files during HeaderStage
joshieDo Jan 29, 2024
a7132ea
get sync gap from the snapshot provider
joshieDo Jan 29, 2024
fd32124
insert_genesis_header writes to static files
joshieDo Jan 29, 2024
5f0d4c6
init_genesis takes a ProviderFactory with snapshots instead
joshieDo Jan 29, 2024
3ed71ee
append_header uses CompactU256 for td
joshieDo Jan 29, 2024
290f5da
make sure to commit static file writer on write_headers
joshieDo Jan 29, 2024
eee811f
add some docs related to append_X and increment block
joshieDo Jan 29, 2024
6c50aff
unwind HeaderStage
joshieDo Jan 29, 2024
ad6bb26
clippy
joshieDo Jan 29, 2024
debaca4
calculate_gas_used_from_headers uses static files
joshieDo Jan 30, 2024
489b582
change stages test_db to seed to Header static file instead
joshieDo Jan 30, 2024
c1f5704
Merge remote-tracking branch 'origin/feat/static-files' into feat/hea…
joshieDo Jan 30, 2024
f8cdc93
add warning if mdbx::begin_rw_txn gets stuck waiting rw unlock
joshieDo Jan 31, 2024
3d2514e
fix deadlock
joshieDo Jan 31, 2024
8673d82
fix header test seed_execution and validation
joshieDo Jan 31, 2024
155ce6e
add static file provider to StateProviders
joshieDo Jan 31, 2024
b442fee
get block_hash instead of the whole header
joshieDo Jan 31, 2024
c04e3dc
Merge remote-tracking branch 'origin/feat/static-files' into feat/hea…
joshieDo Jan 31, 2024
c988685
rm totaldifficulty stage file
joshieDo Jan 31, 2024
8873287
clippy
joshieDo Jan 31, 2024
5b20c26
change header_sync_gap_lookup test
joshieDo Jan 31, 2024
cc2adfa
fix auto mine by adding static headers to fetch_latest_canonical_hashes
joshieDo Jan 31, 2024
c186a5e
remove dbg statement
joshieDo Jan 31, 2024
7ddad97
fix range end
joshieDo Jan 31, 2024
78e0ab2
clippy
joshieDo Jan 31, 2024
8890dc9
fix remaining calc
joshieDo Jan 31, 2024
4778948
only check header hashes if static files has any
joshieDo Jan 31, 2024
3fda062
add temporary snapshots path to beacon engine tests
joshieDo Jan 31, 2024
9dcf073
use tempdir from tempfile
joshieDo Feb 1, 2024
e1647ac
enable test-utils for reth-providers on ef-tests
joshieDo Feb 1, 2024
b74b726
fix fail_init_inconsistent_db test
joshieDo Feb 1, 2024
d0a14f1
fix prune test
joshieDo Feb 1, 2024
4b6eb0a
fix prune tests
joshieDo Feb 1, 2024
a201af2
fmt
joshieDo Feb 1, 2024
874ae1c
fix bodies stage tests
joshieDo Feb 1, 2024
4a36762
fix merkle stage tests
joshieDo Feb 1, 2024
ced4432
fix update_index when called on the block start of a range without txs
joshieDo Feb 1, 2024
9b0ff57
fix number of headers to unwind
joshieDo Feb 1, 2024
0088ff1
remove dbg statement
joshieDo Feb 1, 2024
e0668ef
Update crates/stages/src/stages/headers.rs
joshieDo Feb 2, 2024
13382ce
Merge remote-tracking branch 'origin/feat/static-files' into feat/hea…
shekhirin Feb 2, 2024
94c6d16
fix snapshotter test
shekhirin Feb 2, 2024
132bd00
make sure we delete the data when pruning all blocks from the first b…
joshieDo Feb 5, 2024
483ae7f
no need for &mut self on rows
joshieDo Feb 5, 2024
c29c100
prune len from user header not num_rows
joshieDo Feb 5, 2024
ed4d768
Headers segment should not call increment_block before or after appen…
joshieDo Feb 5, 2024
0210eb9
fix debug_assert on increment block
joshieDo Feb 5, 2024
f6a8b5e
temporary/ci: comment fsync
joshieDo Feb 5, 2024
05ad35e
Revert "temporary/ci: comment fsync"
joshieDo Feb 5, 2024
b2dfb31
add insert_historical_block
joshieDo Feb 5, 2024
6c515f5
clippy
joshieDo Feb 5, 2024
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
5 changes: 3 additions & 2 deletions bin/reth/src/commands/debug_cmd/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,11 @@ impl Command {
fs::create_dir_all(&db_path)?;
let db =
Arc::new(init_db(db_path, DatabaseArguments::default().log_level(self.db.log_level))?);
let provider_factory = ProviderFactory::new(db.clone(), self.chain.clone());
let provider_factory = ProviderFactory::new(db.clone(), self.chain.clone())
.with_snapshots(data_dir.snapshots_path())?;

debug!(target: "reth::cli", chain=%self.chain.chain, genesis=?self.chain.genesis_hash(), "Initializing genesis");
init_genesis(db.clone(), self.chain.clone())?;
init_genesis(provider_factory.clone())?;

let consensus: Arc<dyn Consensus> = Arc::new(BeaconConsensus::new(Arc::clone(&self.chain)));

Expand Down
5 changes: 3 additions & 2 deletions bin/reth/src/commands/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,12 @@ impl ImportCommand {
let db =
Arc::new(init_db(db_path, DatabaseArguments::default().log_level(self.db.log_level))?);
info!(target: "reth::cli", "Database opened");
let provider_factory = ProviderFactory::new(db.clone(), self.chain.clone());
let provider_factory = ProviderFactory::new(db.clone(), self.chain.clone())
.with_snapshots(data_dir.snapshots_path())?;

debug!(target: "reth::cli", chain=%self.chain.chain, genesis=?self.chain.genesis_hash(), "Initializing genesis");

init_genesis(db.clone(), self.chain.clone())?;
init_genesis(provider_factory.clone())?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is better!!


let consensus = Arc::new(BeaconConsensus::new(self.chain.clone()));
info!(target: "reth::cli", "Consensus engine initialized");
Expand Down
6 changes: 5 additions & 1 deletion bin/reth/src/commands/init_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
use clap::Parser;
use reth_db::{init_db, mdbx::DatabaseArguments};
use reth_primitives::ChainSpec;
use reth_provider::ProviderFactory;
use std::sync::Arc;
use tracing::info;

Expand Down Expand Up @@ -57,7 +58,10 @@ impl InitCommand {
info!(target: "reth::cli", "Database opened");

info!(target: "reth::cli", "Writing genesis block");
let hash = init_genesis(db, self.chain)?;
let provider_factory = ProviderFactory::new(db.clone(), self.chain.clone())
.with_snapshots(data_dir.snapshots_path())?;

let hash = init_genesis(provider_factory)?;

info!(target: "reth::cli", hash = ?hash, "Genesis block written");
Ok(())
Expand Down
4 changes: 3 additions & 1 deletion bin/reth/src/commands/recover/storage_tries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ impl Command {
let db = Arc::new(init_db(db_path, Default::default())?);

debug!(target: "reth::cli", chain=%self.chain.chain, genesis=?self.chain.genesis_hash(), "Initializing genesis");
init_genesis(db.clone(), self.chain.clone())?;
let provider_factory = ProviderFactory::new(db.clone(), self.chain.clone())
.with_snapshots(data_dir.snapshots_path())?;
init_genesis(provider_factory.clone())?;

let factory = ProviderFactory::new(&db, self.chain);
let mut provider = factory.provider_rw()?;
Expand Down
8 changes: 5 additions & 3 deletions bin/reth/src/commands/stage/drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use reth_db::{
database::Database, mdbx::DatabaseArguments, open_db, tables, transaction::DbTxMut, DatabaseEnv,
};
use reth_primitives::{fs, stage::StageId, ChainSpec};
use reth_provider::providers::SnapshotProvider;
use std::sync::Arc;
use tracing::info;

Expand Down Expand Up @@ -60,6 +61,7 @@ impl Command {
open_db(db_path.as_ref(), DatabaseArguments::default().log_level(self.db.log_level))?;

let tool = DbTool::new(&db, self.chain.clone())?;
let snapshot_provider = Arc::new(SnapshotProvider::new(data_dir.snapshots_path())?);

tool.db.update(|tx| {
match &self.stage {
Expand All @@ -70,7 +72,7 @@ impl Command {
tx.clear::<tables::BlockOmmers>()?;
tx.clear::<tables::BlockWithdrawals>()?;
tx.put::<tables::SyncStage>(StageId::Bodies.to_string(), Default::default())?;
insert_genesis_header::<DatabaseEnv>(tx, self.chain)?;
insert_genesis_header::<DatabaseEnv>(tx, snapshot_provider, self.chain)?;
}
StageEnum::Senders => {
tx.clear::<tables::TxSenders>()?;
Expand Down Expand Up @@ -155,15 +157,15 @@ impl Command {
StageId::TotalDifficulty.to_string(),
Default::default(),
)?;
insert_genesis_header::<DatabaseEnv>(tx, self.chain)?;
insert_genesis_header::<DatabaseEnv>(tx, snapshot_provider, self.chain)?;
}
StageEnum::TxLookup => {
tx.clear::<tables::TxHashNumber>()?;
tx.put::<tables::SyncStage>(
StageId::TransactionLookup.to_string(),
Default::default(),
)?;
insert_genesis_header::<DatabaseEnv>(tx, self.chain)?;
insert_genesis_header::<DatabaseEnv>(tx, snapshot_provider, self.chain)?;
}
_ => {
info!("Nothing to do for stage {:?}", self.stage);
Expand Down
98 changes: 57 additions & 41 deletions crates/node-core/src/init.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
//! Reth genesis initialization utility functions.

use reth_db::{
cursor::DbCursorRO,
database::Database,
tables,
transaction::{DbTx, DbTxMut},
};
use reth_interfaces::{db::DatabaseError, provider::ProviderResult};
use reth_primitives::{
stage::StageId, Account, Bytecode, ChainSpec, Receipts, StorageEntry, B256, U256,
stage::StageId, Account, Bytecode, ChainSpec, Receipts, SnapshotSegment, StorageEntry, B256,
U256,
};
use reth_provider::{
bundle_state::{BundleStateInit, RevertsInit},
BundleStateWithReceipts, DatabaseProviderRW, HashingWriter, HistoryWriter, OriginalValuesKnown,
ProviderError, ProviderFactory,
providers::{SnapshotProvider, SnapshotWriter},
BundleStateWithReceipts, ChainSpecProvider, DatabaseProviderRW, HashingWriter, HeaderProvider,
HistoryWriter, OriginalValuesKnown, ProviderError, ProviderFactory,
};
use std::{
collections::{BTreeMap, HashMap},
Expand Down Expand Up @@ -46,40 +47,44 @@ impl From<DatabaseError> for InitDatabaseError {
}

/// Write the genesis block if it has not already been written
pub fn init_genesis<DB: Database>(
db: Arc<DB>,
chain: Arc<ChainSpec>,
) -> Result<B256, InitDatabaseError> {
pub fn init_genesis<DB: Database>(factory: ProviderFactory<DB>) -> Result<B256, InitDatabaseError> {
let chain = factory.chain_spec();
let genesis = chain.genesis();

let hash = chain.genesis_hash();

let tx = db.tx()?;
if let Some((_, db_hash)) = tx.cursor_read::<tables::CanonicalHeaders>()?.first()? {
if db_hash == hash {
debug!("Genesis already written, skipping.");
return Ok(hash)
}
// Check if we already have the genesis header or if we have the wrong one.
match factory.sealed_header(0) {
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 use factory.block_hash() here? should be easier because we just need the hash

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

hm, im not sure i understand your question. here we check if what's in static files exists OR if it's the right one

Copy link
Collaborator

Choose a reason for hiding this comment

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

we use only hash from the retrieved header, can we use factory.block_hash(0) instead, and match on Ok(Some(hash)) => { ... }? saves us retrieving the actual header

Ok(None) | Err(ProviderError::MissingSnapshotBlock(SnapshotSegment::Headers, 0)) => {}
Ok(Some(sealed_header)) => {
if sealed_header.hash() == hash {
debug!("Genesis already written, skipping.");
return Ok(hash)
}

return Err(InitDatabaseError::GenesisHashMismatch {
chainspec_hash: hash,
database_hash: db_hash,
})
return Err(InitDatabaseError::GenesisHashMismatch {
chainspec_hash: hash,
database_hash: sealed_header.hash(),
})
}
Err(e) => return Err(dbg!(e).into()),
}

drop(tx);
debug!("Writing genesis block.");

// use transaction to insert genesis header
let factory = ProviderFactory::new(&db, chain.clone());
let provider_rw = factory.provider_rw()?;
insert_genesis_hashes(&provider_rw, genesis)?;
insert_genesis_history(&provider_rw, genesis)?;
provider_rw.commit()?;

// Insert header
let tx = db.tx_mut()?;
insert_genesis_header::<DB>(&tx, chain.clone())?;
let tx = factory.provider_rw()?.into_tx();
insert_genesis_header::<DB>(
&tx,
factory.snapshot_provider().expect("should exist"),
chain.clone(),
)?;

insert_genesis_state::<DB>(&tx, genesis)?;

Expand Down Expand Up @@ -160,7 +165,7 @@ pub fn insert_genesis_state<DB: Database>(

/// Inserts hashes for the genesis state.
pub fn insert_genesis_hashes<DB: Database>(
provider: &DatabaseProviderRW<&DB>,
provider: &DatabaseProviderRW<DB>,
genesis: &reth_primitives::Genesis,
) -> ProviderResult<()> {
// insert and hash accounts to hashing table
Expand All @@ -187,7 +192,7 @@ pub fn insert_genesis_hashes<DB: Database>(

/// Inserts history indices for genesis accounts and storage.
pub fn insert_genesis_history<DB: Database>(
provider: &DatabaseProviderRW<&DB>,
provider: &DatabaseProviderRW<DB>,
genesis: &reth_primitives::Genesis,
) -> ProviderResult<()> {
let account_transitions =
Expand All @@ -208,22 +213,32 @@ pub fn insert_genesis_history<DB: Database>(
/// Inserts header for the genesis state.
pub fn insert_genesis_header<DB: Database>(
tx: &<DB as Database>::TXMut,
snapshot_provider: Arc<SnapshotProvider>,
chain: Arc<ChainSpec>,
) -> ProviderResult<()> {
let header = chain.sealed_genesis_header();

tx.put::<tables::CanonicalHeaders>(0, header.hash)?;
match snapshot_provider.header_by_number(0) {
Ok(None) | Err(ProviderError::MissingSnapshotBlock(SnapshotSegment::Headers, 0)) => {
let (difficulty, hash) = (header.difficulty, header.hash);
let mut writer = snapshot_provider.latest_writer(SnapshotSegment::Headers)?;
writer.append_header(header.header, difficulty, hash)?;
writer.commit()?;
}
Ok(Some(_)) => {}
Err(e) => return Err(e),
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

we're sure at this point that no header for block 0 is written just as before, right? so we can just write to snapshot using the provider

Copy link
Collaborator Author

@joshieDo joshieDo Jan 31, 2024

Choose a reason for hiding this comment

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

not quite, the stage drop command calls this function on a drop bodystage for example

tx.put::<tables::HeaderNumbers>(header.hash, 0)?;
tx.put::<tables::BlockBodyIndices>(0, Default::default())?;
tx.put::<tables::HeaderTD>(0, header.difficulty.into())?;
tx.put::<tables::Headers>(0, header.header)?;

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
use reth_db::cursor::DbCursorRO;

use reth_db::{
models::{storage_sharded_key::StorageShardedKey, ShardedKey},
Expand All @@ -249,38 +264,37 @@ mod tests {

#[test]
fn success_init_genesis_mainnet() {
let db = create_test_rw_db();
let genesis_hash = init_genesis(db, MAINNET.clone()).unwrap();
let genesis_hash =
init_genesis(ProviderFactory::new(create_test_rw_db(), MAINNET.clone())).unwrap();

// actual, expected
assert_eq!(genesis_hash, MAINNET_GENESIS_HASH);
}

#[test]
fn success_init_genesis_goerli() {
let db = create_test_rw_db();
let genesis_hash = init_genesis(db, GOERLI.clone()).unwrap();
let genesis_hash =
init_genesis(ProviderFactory::new(create_test_rw_db(), GOERLI.clone())).unwrap();

// actual, expected
assert_eq!(genesis_hash, GOERLI_GENESIS_HASH);
}

#[test]
fn success_init_genesis_sepolia() {
let db = create_test_rw_db();
let genesis_hash = init_genesis(db, SEPOLIA.clone()).unwrap();
let genesis_hash =
init_genesis(ProviderFactory::new(create_test_rw_db(), SEPOLIA.clone())).unwrap();

// actual, expected
assert_eq!(genesis_hash, SEPOLIA_GENESIS_HASH);
}

#[test]
fn fail_init_inconsistent_db() {
let db = create_test_rw_db();
init_genesis(db.clone(), SEPOLIA.clone()).unwrap();
init_genesis(ProviderFactory::new(create_test_rw_db(), SEPOLIA.clone())).unwrap();

// Try to init db with a different genesis block
let genesis_hash = init_genesis(db, MAINNET.clone());
let genesis_hash = init_genesis(ProviderFactory::new(create_test_rw_db(), MAINNET.clone()));

assert_eq!(
genesis_hash.unwrap_err(),
Expand Down Expand Up @@ -322,13 +336,15 @@ mod tests {
..Default::default()
});

let db = create_test_rw_db();
init_genesis(db.clone(), chain_spec).unwrap();
let factory = ProviderFactory::new(create_test_rw_db(), chain_spec);
init_genesis(factory.clone()).unwrap();

let provider = factory.provider().unwrap();

let tx = db.tx().expect("failed to init tx");
let tx = provider.tx_ref();

assert_eq!(
collect_table_entries::<Arc<DatabaseEnv>, tables::AccountHistory>(&tx)
collect_table_entries::<Arc<DatabaseEnv>, tables::AccountHistory>(tx)
.expect("failed to collect"),
vec![
(ShardedKey::new(address_with_balance, u64::MAX), IntegerList::new([0]).unwrap()),
Expand All @@ -337,7 +353,7 @@ mod tests {
);

assert_eq!(
collect_table_entries::<Arc<DatabaseEnv>, tables::StorageHistory>(&tx)
collect_table_entries::<Arc<DatabaseEnv>, tables::StorageHistory>(tx)
.expect("failed to collect"),
vec![(
StorageShardedKey::new(address_with_storage, storage_key, u64::MAX),
Expand Down
2 changes: 1 addition & 1 deletion crates/node-core/src/node_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1019,7 +1019,7 @@ impl<DB: Database + DatabaseMetrics + DatabaseMetadata + 'static> NodeBuilderWit

debug!(target: "reth::cli", chain=%self.config.chain.chain, genesis=?self.config.chain.genesis_hash(), "Initializing genesis");

let genesis_hash = init_genesis(Arc::clone(&self.db), self.config.chain.clone())?;
let genesis_hash = init_genesis(provider_factory.clone())?;

info!(target: "reth::cli", "{}", DisplayHardforks::new(self.config.chain.hardforks()));

Expand Down
Loading
Loading