feat: add StaticFileSegment::AccountChangeSets (#18882)

Co-authored-by: Alexey Shekhirin <github@shekhirin.com>
This commit is contained in:
Dan Cline
2026-01-08 07:05:05 -05:00
committed by GitHub
parent b38d37a1e1
commit eed34254f5
98 changed files with 3692 additions and 402 deletions

View File

@@ -107,13 +107,13 @@ impl<C: ChainSpecParser> EnvironmentArgs<C> {
let (db, sfp) = match access {
AccessRights::RW => (
Arc::new(init_db(db_path, self.db.database_args())?),
StaticFileProviderBuilder::read_write(sf_path)?
StaticFileProviderBuilder::read_write(sf_path)
.with_genesis_block_number(genesis_block_number)
.build()?,
),
AccessRights::RO | AccessRights::RoInconsistent => {
(Arc::new(open_db_read_only(&db_path, self.db.database_args())?), {
let provider = StaticFileProviderBuilder::read_only(sf_path)?
let provider = StaticFileProviderBuilder::read_only(sf_path)
.with_genesis_block_number(genesis_block_number)
.build()?;
provider.watch_directory();

View File

@@ -2,8 +2,8 @@ use alloy_primitives::{hex, BlockHash};
use clap::Parser;
use reth_db::{
static_file::{
ColumnSelectorOne, ColumnSelectorTwo, HeaderWithHashMask, ReceiptMask, TransactionMask,
TransactionSenderMask,
AccountChangesetMask, ColumnSelectorOne, ColumnSelectorTwo, HeaderWithHashMask,
ReceiptMask, TransactionMask, TransactionSenderMask,
},
RawDupSort,
};
@@ -19,7 +19,7 @@ use reth_db_common::DbTool;
use reth_node_api::{HeaderTy, ReceiptTy, TxTy};
use reth_node_builder::NodeTypesWithDB;
use reth_primitives_traits::ValueWithSubKey;
use reth_provider::{providers::ProviderNodeTypes, StaticFileProviderFactory};
use reth_provider::{providers::ProviderNodeTypes, ChangeSetReader, StaticFileProviderFactory};
use reth_static_file_types::StaticFileSegment;
use tracing::error;
@@ -64,6 +64,10 @@ enum Subcommand {
#[arg(value_parser = maybe_json_value_parser)]
key: String,
/// The subkey to get content for, for example address in changeset
#[arg(value_parser = maybe_json_value_parser)]
subkey: Option<String>,
/// Output bytes instead of human-readable decoded value
#[arg(long)]
raw: bool,
@@ -77,33 +81,77 @@ impl Command {
Subcommand::Mdbx { table, key, subkey, end_key, end_subkey, raw } => {
table.view(&GetValueViewer { tool, key, subkey, end_key, end_subkey, raw })?
}
Subcommand::StaticFile { segment, key, raw } => {
let (key, mask): (u64, _) = match segment {
Subcommand::StaticFile { segment, key, subkey, raw } => {
let (key, subkey, mask): (u64, _, _) = match segment {
StaticFileSegment::Headers => (
table_key::<tables::Headers>(&key)?,
None,
<HeaderWithHashMask<HeaderTy<N>>>::MASK,
),
StaticFileSegment::Transactions => {
(table_key::<tables::Transactions>(&key)?, <TransactionMask<TxTy<N>>>::MASK)
}
StaticFileSegment::Receipts => {
(table_key::<tables::Receipts>(&key)?, <ReceiptMask<ReceiptTy<N>>>::MASK)
}
StaticFileSegment::Transactions => (
table_key::<tables::Transactions>(&key)?,
None,
<TransactionMask<TxTy<N>>>::MASK,
),
StaticFileSegment::Receipts => (
table_key::<tables::Receipts>(&key)?,
None,
<ReceiptMask<ReceiptTy<N>>>::MASK,
),
StaticFileSegment::TransactionSenders => (
table_key::<tables::TransactionSenders>(&key)?,
<TransactionSenderMask>::MASK,
None,
TransactionSenderMask::MASK,
),
StaticFileSegment::AccountChangeSets => {
let subkey =
table_subkey::<tables::AccountChangeSets>(subkey.as_deref()).ok();
(
table_key::<tables::AccountChangeSets>(&key)?,
subkey,
AccountChangesetMask::MASK,
)
}
};
let content = tool
.provider_factory
.static_file_provider()
.get_segment_provider(segment, key)?
.cursor()?
.get(key.into(), mask)
.map(|result| {
result.map(|vec| vec.iter().map(|slice| slice.to_vec()).collect::<Vec<_>>())
})?;
// handle account changesets differently if a subkey is provided.
if let StaticFileSegment::AccountChangeSets = segment {
let Some(subkey) = subkey else {
// get all changesets for the block
let changesets = tool
.provider_factory
.static_file_provider()
.account_block_changeset(key)?;
println!("{}", serde_json::to_string_pretty(&changesets)?);
return Ok(())
};
let account = tool
.provider_factory
.static_file_provider()
.get_account_before_block(key, subkey)?;
if let Some(account) = account {
println!("{}", serde_json::to_string_pretty(&account)?);
} else {
error!(target: "reth::cli", "No content for the given table key.");
}
return Ok(())
}
let content = tool.provider_factory.static_file_provider().find_static_file(
segment,
|provider| {
let mut cursor = provider.cursor()?;
cursor.get(key.into(), mask).map(|result| {
result.map(|vec| {
vec.iter().map(|slice| slice.to_vec()).collect::<Vec<_>>()
})
})
},
)?;
match content {
Some(content) => {
@@ -139,6 +187,9 @@ impl Command {
)?;
println!("{}", serde_json::to_string_pretty(&sender)?);
}
StaticFileSegment::AccountChangeSets => {
unreachable!("account changeset static files are special cased before this match")
}
}
}
}

View File

@@ -40,12 +40,17 @@ enum Subcommands {
#[clap(rename_all = "snake_case")]
pub enum SetCommand {
/// Store receipts in static files instead of the database
ReceiptsInStaticFiles {
Receipts {
#[clap(action(ArgAction::Set))]
value: bool,
},
/// Store transaction senders in static files instead of the database
TransactionSendersInStaticFiles {
TransactionSenders {
#[clap(action(ArgAction::Set))]
value: bool,
},
/// Store account changesets in static files instead of the database
AccountChangesets {
#[clap(action(ArgAction::Set))]
value: bool,
},
@@ -94,11 +99,12 @@ impl Command {
storages_history_in_rocksdb: _,
transaction_hash_numbers_in_rocksdb: _,
account_history_in_rocksdb: _,
account_changesets_in_static_files: _,
} = settings.unwrap_or_else(StorageSettings::legacy);
// Update the setting based on the key
match cmd {
SetCommand::ReceiptsInStaticFiles { value } => {
SetCommand::Receipts { value } => {
if settings.receipts_in_static_files == value {
println!("receipts_in_static_files is already set to {}", value);
return Ok(());
@@ -106,7 +112,7 @@ impl Command {
settings.receipts_in_static_files = value;
println!("Set receipts_in_static_files = {}", value);
}
SetCommand::TransactionSendersInStaticFiles { value } => {
SetCommand::TransactionSenders { value } => {
if settings.transaction_senders_in_static_files == value {
println!("transaction_senders_in_static_files is already set to {}", value);
return Ok(());
@@ -114,6 +120,14 @@ impl Command {
settings.transaction_senders_in_static_files = value;
println!("Set transaction_senders_in_static_files = {}", value);
}
SetCommand::AccountChangesets { value } => {
if settings.account_changesets_in_static_files == value {
println!("account_changesets_in_static_files is already set to {}", value);
return Ok(());
}
settings.account_changesets_in_static_files = value;
println!("Set account_changesets_in_static_files = {}", value);
}
}
// Write updated settings

View File

@@ -87,6 +87,9 @@ impl<C: ChainSpecParser> Command<C> {
.unwrap_or_default();
writer.prune_transaction_senders(to_delete, 0)?;
}
StaticFileSegment::AccountChangeSets => {
writer.prune_account_changesets(highest_block)?;
}
}
}
}