mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(cli): merkle debug cmd (#2314)
This commit is contained in:
44
Cargo.lock
generated
44
Cargo.lock
generated
@ -1056,6 +1056,16 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
|
||||
dependencies = [
|
||||
"quote 1.0.26",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctr"
|
||||
version = "0.8.0"
|
||||
@ -1308,6 +1318,12 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[package]]
|
||||
name = "difflib"
|
||||
version = "0.4.0"
|
||||
@ -3678,6 +3694,15 @@ version = "6.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||
|
||||
[[package]]
|
||||
name = "output_vt100"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
@ -4025,6 +4050,18 @@ dependencies = [
|
||||
"termtree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755"
|
||||
dependencies = [
|
||||
"ctor",
|
||||
"diff",
|
||||
"output_vt100",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.4"
|
||||
@ -4462,6 +4499,7 @@ dependencies = [
|
||||
"metrics-util",
|
||||
"num_cpus",
|
||||
"pin-project",
|
||||
"pretty_assertions",
|
||||
"proptest",
|
||||
"reth-auto-seal-consensus",
|
||||
"reth-basic-payload-builder",
|
||||
@ -7576,6 +7614,12 @@ dependencies = [
|
||||
"xml-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.6.0"
|
||||
|
||||
@ -84,3 +84,4 @@ tempfile = { version = "3.3.0" }
|
||||
backon = "0.4"
|
||||
hex = "0.4"
|
||||
thiserror = "1.0"
|
||||
pretty_assertions = "1.3.0"
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
use crate::{
|
||||
chain, config, db,
|
||||
dirs::{LogsDir, PlatformPath},
|
||||
drop_stage, dump_stage, node, p2p,
|
||||
drop_stage, dump_stage, merkle_debug, node, p2p,
|
||||
runner::CliRunner,
|
||||
stage, test_eth_chain, test_vectors,
|
||||
};
|
||||
@ -42,6 +42,7 @@ pub fn run() -> eyre::Result<()> {
|
||||
Commands::TestVectors(command) => runner.run_until_ctrl_c(command.execute()),
|
||||
Commands::TestEthChain(command) => runner.run_until_ctrl_c(command.execute()),
|
||||
Commands::Config(command) => runner.run_until_ctrl_c(command.execute()),
|
||||
Commands::MerkleDebug(command) => runner.run_until_ctrl_c(command.execute()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,6 +87,9 @@ pub enum Commands {
|
||||
/// Write config to stdout
|
||||
#[command(name = "config")]
|
||||
Config(config::Command),
|
||||
/// Debug state root calculation
|
||||
#[command(name = "merkle-debug")]
|
||||
MerkleDebug(merkle_debug::Command),
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
|
||||
@ -15,6 +15,7 @@ pub mod db;
|
||||
pub mod dirs;
|
||||
pub mod drop_stage;
|
||||
pub mod dump_stage;
|
||||
pub mod merkle_debug;
|
||||
pub mod node;
|
||||
pub mod p2p;
|
||||
pub mod prometheus_exporter;
|
||||
|
||||
254
bin/reth/src/merkle_debug.rs
Normal file
254
bin/reth/src/merkle_debug.rs
Normal file
@ -0,0 +1,254 @@
|
||||
//! Command for debugging merkle trie calculation.
|
||||
use crate::dirs::{DbPath, MaybePlatformPath};
|
||||
use clap::Parser;
|
||||
use reth_db::{cursor::DbCursorRO, tables, transaction::DbTx};
|
||||
use reth_primitives::ChainSpec;
|
||||
use reth_provider::Transaction;
|
||||
use reth_staged_sync::utils::{chainspec::genesis_value_parser, init::init_db};
|
||||
use reth_stages::{
|
||||
stages::{
|
||||
AccountHashingStage, ExecutionStage, MerkleStage, StorageHashingStage, ACCOUNT_HASHING,
|
||||
EXECUTION, MERKLE_EXECUTION, SENDER_RECOVERY, STORAGE_HASHING,
|
||||
},
|
||||
ExecInput, Stage,
|
||||
};
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
/// `reth merkle-debug` command
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Command {
|
||||
/// The path to the database folder.
|
||||
///
|
||||
/// Defaults to the OS-specific data directory:
|
||||
///
|
||||
/// - Linux: `$XDG_DATA_HOME/reth/db` or `$HOME/.local/share/reth/db`
|
||||
/// - Windows: `{FOLDERID_RoamingAppData}/reth/db`
|
||||
/// - macOS: `$HOME/Library/Application Support/reth/db`
|
||||
#[arg(global = true, long, value_name = "PATH", verbatim_doc_comment, default_value_t)]
|
||||
db: MaybePlatformPath<DbPath>,
|
||||
|
||||
/// The chain this node is running.
|
||||
///
|
||||
/// Possible values are either a built-in chain or the path to a chain specification file.
|
||||
///
|
||||
/// Built-in chains:
|
||||
/// - mainnet
|
||||
/// - goerli
|
||||
/// - sepolia
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "CHAIN_OR_PATH",
|
||||
verbatim_doc_comment,
|
||||
default_value = "mainnet",
|
||||
value_parser = genesis_value_parser
|
||||
)]
|
||||
chain: Arc<ChainSpec>,
|
||||
|
||||
/// The height to finish at
|
||||
#[arg(long)]
|
||||
to: u64,
|
||||
|
||||
/// The depth after which we should start comparing branch nodes
|
||||
#[arg(long)]
|
||||
skip_node_depth: Option<usize>,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
/// Execute `merkle-debug` command
|
||||
pub async fn execute(self) -> eyre::Result<()> {
|
||||
// add network name to db directory
|
||||
let db_path = self.db.unwrap_or_chain_default(self.chain.chain);
|
||||
|
||||
std::fs::create_dir_all(&db_path)?;
|
||||
|
||||
let db = Arc::new(init_db(db_path)?);
|
||||
let mut tx = Transaction::new(db.as_ref())?;
|
||||
|
||||
let execution_progress = EXECUTION.get_progress(tx.deref())?.unwrap_or_default();
|
||||
assert!(execution_progress < self.to, "Nothing to run");
|
||||
|
||||
let should_reset_stages = !(execution_progress ==
|
||||
ACCOUNT_HASHING.get_progress(tx.deref())?.unwrap_or_default() &&
|
||||
execution_progress == STORAGE_HASHING.get_progress(tx.deref())?.unwrap_or_default() &&
|
||||
execution_progress ==
|
||||
MERKLE_EXECUTION.get_progress(tx.deref())?.unwrap_or_default());
|
||||
|
||||
let factory = reth_revm::Factory::new(self.chain.clone());
|
||||
let mut execution_stage = ExecutionStage::new(factory, 1);
|
||||
|
||||
let mut account_hashing_stage = AccountHashingStage::default();
|
||||
let mut storage_hashing_stage = StorageHashingStage::default();
|
||||
let mut merkle_stage = MerkleStage::default_execution();
|
||||
|
||||
for block in execution_progress + 1..=self.to {
|
||||
tracing::trace!(target: "reth::cli", block, "Executing block");
|
||||
let progress = if (!should_reset_stages || block > execution_progress + 1) && block > 0
|
||||
{
|
||||
Some(block - 1)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
execution_stage
|
||||
.execute(
|
||||
&mut tx,
|
||||
ExecInput {
|
||||
previous_stage: Some((SENDER_RECOVERY, block)),
|
||||
stage_progress: block.checked_sub(1),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut account_hashing_done = false;
|
||||
while !account_hashing_done {
|
||||
let output = account_hashing_stage
|
||||
.execute(
|
||||
&mut tx,
|
||||
ExecInput {
|
||||
previous_stage: Some((EXECUTION, block)),
|
||||
stage_progress: progress,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
account_hashing_done = output.done;
|
||||
}
|
||||
|
||||
let mut storage_hashing_done = false;
|
||||
while !storage_hashing_done {
|
||||
let output = storage_hashing_stage
|
||||
.execute(
|
||||
&mut tx,
|
||||
ExecInput {
|
||||
previous_stage: Some((ACCOUNT_HASHING, block)),
|
||||
stage_progress: progress,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
storage_hashing_done = output.done;
|
||||
}
|
||||
|
||||
let incremental_result = merkle_stage
|
||||
.execute(
|
||||
&mut tx,
|
||||
ExecInput {
|
||||
previous_stage: Some((STORAGE_HASHING, block)),
|
||||
stage_progress: progress,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
if incremental_result.is_err() {
|
||||
tracing::warn!(target: "reth::cli", block, "Incremental calculation failed, retrying from scratch");
|
||||
let incremental_account_trie = tx
|
||||
.cursor_read::<tables::AccountsTrie>()?
|
||||
.walk_range(..)?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let incremental_storage_trie = tx
|
||||
.cursor_dup_read::<tables::StoragesTrie>()?
|
||||
.walk_range(..)?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let clean_result = merkle_stage
|
||||
.execute(
|
||||
&mut tx,
|
||||
ExecInput {
|
||||
previous_stage: Some((STORAGE_HASHING, block)),
|
||||
stage_progress: None,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
assert!(clean_result.is_ok(), "Clean state root calculation failed");
|
||||
|
||||
let clean_account_trie = tx
|
||||
.cursor_read::<tables::AccountsTrie>()?
|
||||
.walk_range(..)?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let clean_storage_trie = tx
|
||||
.cursor_dup_read::<tables::StoragesTrie>()?
|
||||
.walk_range(..)?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
tracing::info!(target: "reth::cli", block, "Comparing incremental trie vs clean trie");
|
||||
|
||||
// Account trie
|
||||
let mut incremental_account_mismatched = Vec::new();
|
||||
let mut clean_account_mismatched = Vec::new();
|
||||
let mut incremental_account_trie_iter =
|
||||
incremental_account_trie.into_iter().peekable();
|
||||
let mut clean_account_trie_iter = clean_account_trie.into_iter().peekable();
|
||||
while incremental_account_trie_iter.peek().is_some() ||
|
||||
clean_account_trie_iter.peek().is_some()
|
||||
{
|
||||
match (incremental_account_trie_iter.next(), clean_account_trie_iter.next()) {
|
||||
(Some(incremental), Some(clean)) => {
|
||||
pretty_assertions::assert_eq!(
|
||||
incremental.0,
|
||||
clean.0,
|
||||
"Nibbles don't match"
|
||||
);
|
||||
if incremental.1 != clean.1 &&
|
||||
clean.0.inner.len() > self.skip_node_depth.unwrap_or_default()
|
||||
{
|
||||
incremental_account_mismatched.push(incremental);
|
||||
clean_account_mismatched.push(clean);
|
||||
}
|
||||
}
|
||||
(Some(incremental), None) => {
|
||||
tracing::warn!(target: "reth::cli", next = ?incremental, "Incremental account trie has more entries");
|
||||
}
|
||||
(None, Some(clean)) => {
|
||||
tracing::warn!(target: "reth::cli", next = ?clean, "Clean account trie has more entries");
|
||||
}
|
||||
(None, None) => {
|
||||
tracing::info!(target: "reth::cli", "Exhausted all account trie entries");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stoarge trie
|
||||
let mut first_mismatched_storage = None;
|
||||
let mut incremental_storage_trie_iter =
|
||||
incremental_storage_trie.into_iter().peekable();
|
||||
let mut clean_storage_trie_iter = clean_storage_trie.into_iter().peekable();
|
||||
while incremental_storage_trie_iter.peek().is_some() ||
|
||||
clean_storage_trie_iter.peek().is_some()
|
||||
{
|
||||
match (incremental_storage_trie_iter.next(), clean_storage_trie_iter.next()) {
|
||||
(Some(incremental), Some(clean)) => {
|
||||
if incremental != clean &&
|
||||
clean.1.nibbles.inner.len() >
|
||||
self.skip_node_depth.unwrap_or_default()
|
||||
{
|
||||
first_mismatched_storage = Some((incremental, clean));
|
||||
break
|
||||
}
|
||||
}
|
||||
(Some(incremental), None) => {
|
||||
tracing::warn!(target: "reth::cli", next = ?incremental, "Incremental storage trie has more entries");
|
||||
}
|
||||
(None, Some(clean)) => {
|
||||
tracing::warn!(target: "reth::cli", next = ?clean, "Clean storage trie has more entries")
|
||||
}
|
||||
(None, None) => {
|
||||
tracing::info!(target: "reth::cli", "Exhausted all storage trie entries.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pretty_assertions::assert_eq!(
|
||||
(
|
||||
incremental_account_mismatched,
|
||||
first_mismatched_storage.as_ref().map(|(incremental, _)| incremental)
|
||||
),
|
||||
(
|
||||
clean_account_mismatched,
|
||||
first_mismatched_storage.as_ref().map(|(_, clean)| clean)
|
||||
),
|
||||
"Mismatched trie nodes"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user