mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-30 03:01:58 -04:00
Compare commits
1 Commits
klkvr/debu
...
klkvr/flus
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a18325e0e |
@@ -9,9 +9,7 @@ use crate::StaticFileProviderFactory;
|
||||
use alloy_eips::eip2718::Encodable2718;
|
||||
use alloy_primitives::BlockNumber;
|
||||
use rayon::prelude::*;
|
||||
use reth_chainspec::{ChainSpecProvider, EthChainSpec};
|
||||
use reth_db::models::{storage_sharded_key::StorageShardedKey, ShardedKey};
|
||||
use reth_db_api::tables;
|
||||
use reth_db_api::{table::Table, tables};
|
||||
use reth_stages_types::StageId;
|
||||
use reth_static_file_types::StaticFileSegment;
|
||||
use reth_storage_api::{
|
||||
@@ -59,8 +57,7 @@ impl RocksDBProvider {
|
||||
+ BlockBodyIndicesProvider
|
||||
+ StorageChangeSetReader
|
||||
+ ChangeSetReader
|
||||
+ TransactionsProvider<Transaction: Encodable2718>
|
||||
+ ChainSpecProvider,
|
||||
+ TransactionsProvider<Transaction: Encodable2718>,
|
||||
{
|
||||
let mut unwind_target: Option<BlockNumber> = None;
|
||||
|
||||
@@ -117,14 +114,11 @@ impl RocksDBProvider {
|
||||
|
||||
// Fast path: clear any stale data and return.
|
||||
if checkpoint == 0 {
|
||||
if self.first::<tables::TransactionHashNumbers>()?.is_some() {
|
||||
tracing::info!(
|
||||
target: "reth::providers::rocksdb",
|
||||
"TransactionHashNumbers: checkpoint is 0, clearing stale data"
|
||||
);
|
||||
self.clear::<tables::TransactionHashNumbers>()?;
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
target: "reth::providers::rocksdb",
|
||||
"TransactionHashNumbers: checkpoint is 0, clearing stale data"
|
||||
);
|
||||
self.clear::<tables::TransactionHashNumbers>()?;
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
@@ -262,39 +256,21 @@ impl RocksDBProvider {
|
||||
provider: &Provider,
|
||||
) -> ProviderResult<Option<BlockNumber>>
|
||||
where
|
||||
Provider: DBProvider
|
||||
+ StageCheckpointReader
|
||||
+ StaticFileProviderFactory
|
||||
+ StorageChangeSetReader
|
||||
+ ChainSpecProvider,
|
||||
Provider:
|
||||
DBProvider + StageCheckpointReader + StaticFileProviderFactory + StorageChangeSetReader,
|
||||
{
|
||||
let checkpoint = provider
|
||||
.get_stage_checkpoint(StageId::IndexStorageHistory)?
|
||||
.map(|cp| cp.block_number)
|
||||
.unwrap_or(0);
|
||||
|
||||
// Fast path: clear and re-insert genesis history.
|
||||
// Fast path: clear any stale data and return.
|
||||
if checkpoint == 0 {
|
||||
tracing::info!(
|
||||
target: "reth::providers::rocksdb",
|
||||
"StoragesHistory: checkpoint is 0, clearing stale data"
|
||||
);
|
||||
self.clear::<tables::StoragesHistory>()?;
|
||||
|
||||
let chain_spec = provider.chain_spec();
|
||||
let genesis = chain_spec.genesis();
|
||||
let list = tables::BlockNumberList::new([0]).expect("single block always fits");
|
||||
for (addr, account) in &genesis.alloc {
|
||||
if let Some(storage) = &account.storage {
|
||||
for key in storage.keys() {
|
||||
self.put::<tables::StoragesHistory>(
|
||||
StorageShardedKey::last(*addr, *key),
|
||||
&list,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
@@ -358,6 +334,7 @@ impl RocksDBProvider {
|
||||
|
||||
let batch = self.unwind_storage_history_indices(&indices)?;
|
||||
self.commit_batch(batch)?;
|
||||
self.flush(&[tables::StoragesHistory::NAME])?;
|
||||
}
|
||||
|
||||
batch_start = batch_end + 1;
|
||||
@@ -375,32 +352,20 @@ impl RocksDBProvider {
|
||||
provider: &Provider,
|
||||
) -> ProviderResult<Option<BlockNumber>>
|
||||
where
|
||||
Provider: DBProvider
|
||||
+ StageCheckpointReader
|
||||
+ StaticFileProviderFactory
|
||||
+ ChangeSetReader
|
||||
+ ChainSpecProvider,
|
||||
Provider: DBProvider + StageCheckpointReader + StaticFileProviderFactory + ChangeSetReader,
|
||||
{
|
||||
let checkpoint = provider
|
||||
.get_stage_checkpoint(StageId::IndexAccountHistory)?
|
||||
.map(|cp| cp.block_number)
|
||||
.unwrap_or(0);
|
||||
|
||||
// Fast path: clear and re-insert genesis history.
|
||||
// Fast path: clear any stale data and return.
|
||||
if checkpoint == 0 {
|
||||
tracing::info!(
|
||||
target: "reth::providers::rocksdb",
|
||||
"AccountsHistory: checkpoint is 0, clearing stale data"
|
||||
);
|
||||
self.clear::<tables::AccountsHistory>()?;
|
||||
|
||||
let chain_spec = provider.chain_spec();
|
||||
let genesis = chain_spec.genesis();
|
||||
let list = tables::BlockNumberList::new([0]).expect("single block always fits");
|
||||
for addr in genesis.alloc.keys() {
|
||||
self.put::<tables::AccountsHistory>(ShardedKey::last(*addr), &list)?;
|
||||
}
|
||||
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
@@ -474,18 +439,13 @@ impl RocksDBProvider {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
init::insert_genesis_history,
|
||||
providers::{rocksdb::RocksDBBuilder, static_file::StaticFileWriter},
|
||||
test_utils::{create_test_provider_factory, create_test_provider_factory_with_chain_spec},
|
||||
BlockWriter, DatabaseProviderFactory, RocksDBProviderFactory, StageCheckpointWriter,
|
||||
TransactionsProvider,
|
||||
test_utils::create_test_provider_factory,
|
||||
BlockWriter, DatabaseProviderFactory, StageCheckpointWriter, TransactionsProvider,
|
||||
};
|
||||
use alloy_primitives::{Address, B256};
|
||||
use reth_chainspec::MAINNET;
|
||||
use reth_db::cursor::{DbCursorRO, DbCursorRW};
|
||||
use reth_db_api::{
|
||||
models::{storage_sharded_key::StorageShardedKey, StorageSettings},
|
||||
@@ -569,14 +529,11 @@ mod tests {
|
||||
|
||||
let result = rocksdb.heal_storages_history(&provider).unwrap();
|
||||
assert_eq!(result, None, "StoragesHistory should return early at checkpoint 0");
|
||||
assert!(rocksdb.first::<tables::StoragesHistory>().unwrap().is_none());
|
||||
|
||||
let result = rocksdb.heal_accounts_history(&provider).unwrap();
|
||||
assert_eq!(result, None, "AccountsHistory should return early at checkpoint 0");
|
||||
// Genesis account history entries are re-inserted
|
||||
assert_eq!(
|
||||
rocksdb.iter::<tables::AccountsHistory>().unwrap().count(),
|
||||
factory.chain_spec().genesis().alloc.len()
|
||||
);
|
||||
assert!(rocksdb.first::<tables::AccountsHistory>().unwrap().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -703,38 +660,35 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_consistency_storages_history_preserves_genesis_entries_at_checkpoint_zero(
|
||||
) -> eyre::Result<()> {
|
||||
// Modify mainnet chainspec to include a single genesis storage slot
|
||||
let mut chain_spec = MAINNET.clone();
|
||||
Arc::make_mut(&mut chain_spec).genesis.alloc.first_entry().unwrap().get_mut().storage =
|
||||
Some(From::from([(B256::random(), B256::random())]));
|
||||
fn test_check_consistency_storages_history_has_data_no_checkpoint_prunes_data() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let rocksdb = RocksDBBuilder::new(temp_dir.path()).with_default_tables().build().unwrap();
|
||||
|
||||
// Insert data into RocksDB
|
||||
let key = StorageShardedKey::new(Address::ZERO, B256::ZERO, 50);
|
||||
let block_list = BlockNumberList::new_pre_sorted([10, 20, 30, 50]);
|
||||
rocksdb.put::<tables::StoragesHistory>(key, &block_list).unwrap();
|
||||
|
||||
// Verify data exists
|
||||
assert!(rocksdb.last::<tables::StoragesHistory>().unwrap().is_some());
|
||||
|
||||
// Create a test provider factory for MDBX with NO checkpoint
|
||||
let factory = create_test_provider_factory_with_chain_spec(chain_spec);
|
||||
let rocksdb = factory.rocksdb_provider();
|
||||
let factory = create_test_provider_factory();
|
||||
factory.set_storage_settings_cache(StorageSettings::v2());
|
||||
|
||||
// Insert genesis history into RocksDB
|
||||
let provider_rw = factory.database_provider_rw().unwrap();
|
||||
insert_genesis_history(&provider_rw, factory.chain_spec().genesis.alloc.iter())?;
|
||||
provider_rw.commit()?;
|
||||
|
||||
let provider = factory.database_provider_ro().unwrap();
|
||||
|
||||
// This should not prune anything because only genesis entries are present
|
||||
let result = rocksdb.heal_storages_history(&provider).unwrap();
|
||||
assert_eq!(result, None, "Should skip healing when only genesis entries present");
|
||||
// RocksDB has data but checkpoint is 0
|
||||
// This means RocksDB has stale data that should be pruned (healed)
|
||||
let result = rocksdb.check_consistency(&provider).unwrap();
|
||||
assert_eq!(result, None, "Should heal by pruning, no unwind needed");
|
||||
|
||||
// Verify data was NOT deleted
|
||||
// Verify data was pruned
|
||||
assert!(
|
||||
rocksdb.iter::<tables::StoragesHistory>().unwrap().count() > 0,
|
||||
"Genesis entries should be preserved"
|
||||
rocksdb.last::<tables::StoragesHistory>().unwrap().is_none(),
|
||||
"RocksDB should be empty after pruning"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_consistency_mdbx_behind_checkpoint_needs_unwind() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
@@ -1114,31 +1068,36 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_consistency_accounts_history_preserves_genesis_entries_at_checkpoint_zero(
|
||||
) -> eyre::Result<()> {
|
||||
fn test_check_consistency_accounts_history_has_data_no_checkpoint_prunes_data() {
|
||||
use reth_db_api::models::ShardedKey;
|
||||
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let rocksdb = RocksDBBuilder::new(temp_dir.path()).with_default_tables().build().unwrap();
|
||||
|
||||
// Insert data into RocksDB
|
||||
let key = ShardedKey::new(Address::ZERO, 50);
|
||||
let block_list = BlockNumberList::new_pre_sorted([10, 20, 30, 50]);
|
||||
rocksdb.put::<tables::AccountsHistory>(key, &block_list).unwrap();
|
||||
|
||||
// Verify data exists
|
||||
assert!(rocksdb.last::<tables::AccountsHistory>().unwrap().is_some());
|
||||
|
||||
// Create a test provider factory for MDBX with NO checkpoint
|
||||
let factory = create_test_provider_factory();
|
||||
factory.set_storage_settings_cache(StorageSettings::v2());
|
||||
let rocksdb = factory.rocksdb_provider();
|
||||
|
||||
// Insert genesis history into RocksDB
|
||||
let provider_rw = factory.database_provider_rw().unwrap();
|
||||
insert_genesis_history(&provider_rw, factory.chain_spec().genesis.alloc.iter())?;
|
||||
provider_rw.commit()?;
|
||||
|
||||
let provider = factory.database_provider_ro().unwrap();
|
||||
|
||||
// This should not prune anything because only genesis entries are present
|
||||
// RocksDB has data but checkpoint is 0
|
||||
// This means RocksDB has stale data that should be pruned (healed)
|
||||
let result = rocksdb.check_consistency(&provider).unwrap();
|
||||
assert_eq!(result, None, "Should heal by pruning, no unwind needed");
|
||||
|
||||
// Verify data was NOT deleted
|
||||
// Verify data was pruned
|
||||
assert!(
|
||||
rocksdb.iter::<tables::AccountsHistory>().unwrap().count() > 0,
|
||||
"Genesis entries should be preserved"
|
||||
rocksdb.last::<tables::AccountsHistory>().unwrap().is_none(),
|
||||
"RocksDB should be empty after pruning"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user