-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Changes from 9 commits
24b9a1e
a7132ea
fd32124
5f0d4c6
3ed71ee
290f5da
eee811f
6c50aff
ad6bb26
debaca4
489b582
c1f5704
f8cdc93
3d2514e
8673d82
155ce6e
b442fee
c04e3dc
c988685
8873287
5b20c26
cc2adfa
c186a5e
7ddad97
78e0ab2
8890dc9
4778948
3fda062
9dcf073
e1647ac
b74b726
d0a14f1
4b6eb0a
a201af2
874ae1c
4a36762
ced4432
9b0ff57
0088ff1
e0668ef
13382ce
94c6d16
132bd00
483ae7f
c29c100
ed4d768
0210eb9
f6a8b5e
05ad35e
b2dfb31
6c515f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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}, | ||
|
@@ -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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we use There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we use only hash from the retrieved header, can we use |
||
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)?; | ||
|
||
|
@@ -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 | ||
|
@@ -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 = | ||
|
@@ -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), | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we're sure at this point that no header for block There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not quite, the |
||
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}, | ||
|
@@ -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(), | ||
|
@@ -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()), | ||
|
@@ -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), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is better!!