feat: add helpers for testing rpc requests with prestate (#19790)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Karl Yu
2025-11-18 22:34:34 +08:00
committed by GitHub
parent 69c219eede
commit 4f94fa240f
7 changed files with 195 additions and 12 deletions

22
Cargo.lock generated
View File

@@ -3049,7 +3049,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users 0.5.2",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -3379,7 +3379,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -4728,7 +4728,7 @@ dependencies = [
"js-sys",
"log",
"wasm-bindgen",
"windows-core 0.62.2",
"windows-core 0.57.0",
]
[[package]]
@@ -5073,7 +5073,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -5982,7 +5982,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -7013,7 +7013,7 @@ dependencies = [
"once_cell",
"socket2 0.6.1",
"tracing",
"windows-sys 0.60.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -9185,6 +9185,7 @@ dependencies = [
"alloy-rpc-types-beacon",
"alloy-rpc-types-engine",
"alloy-rpc-types-eth",
"alloy-rpc-types-trace",
"alloy-signer",
"alloy-sol-types",
"eyre",
@@ -9217,10 +9218,13 @@ dependencies = [
"reth-rpc-eth-types",
"reth-rpc-server-types",
"reth-tasks",
"reth-testing-utils",
"reth-tracing",
"reth-transaction-pool",
"revm",
"serde",
"serde_json",
"similar-asserts",
"tokio",
]
@@ -11459,7 +11463,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.11.0",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -12350,7 +12354,7 @@ dependencies = [
"getrandom 0.3.4",
"once_cell",
"rustix 1.1.2",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -13565,7 +13569,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.48.0",
]
[[package]]

View File

@@ -496,7 +496,7 @@ alloy-genesis = { version = "1.0.41", default-features = false }
alloy-json-rpc = { version = "1.0.41", default-features = false }
alloy-network = { version = "1.0.41", default-features = false }
alloy-network-primitives = { version = "1.0.41", default-features = false }
alloy-provider = { version = "1.0.41", features = ["reqwest"], default-features = false }
alloy-provider = { version = "1.0.41", features = ["reqwest", "debug-api"], default-features = false }
alloy-pubsub = { version = "1.0.41", default-features = false }
alloy-rpc-client = { version = "1.0.41", default-features = false }
alloy-rpc-types = { version = "1.0.41", features = ["eth"], default-features = false }

View File

@@ -60,6 +60,7 @@ reth-exex.workspace = true
reth-node-core.workspace = true
reth-e2e-test-utils.workspace = true
reth-tasks.workspace = true
reth-testing-utils.workspace = true
alloy-primitives.workspace = true
alloy-provider.workspace = true
@@ -74,6 +75,9 @@ futures.workspace = true
tokio.workspace = true
serde_json.workspace = true
rand.workspace = true
serde.workspace = true
alloy-rpc-types-trace.workspace = true
similar-asserts.workspace = true
[features]
default = []

View File

@@ -5,6 +5,7 @@ mod dev;
mod eth;
mod p2p;
mod pool;
mod prestate;
mod rpc;
mod utils;

View File

@@ -0,0 +1,132 @@
use alloy_eips::BlockId;
use alloy_genesis::{Genesis, GenesisAccount};
use alloy_primitives::address;
use alloy_provider::ext::DebugApi;
use alloy_rpc_types_eth::{Transaction, TransactionRequest};
use alloy_rpc_types_trace::geth::{
AccountState, GethDebugTracingOptions, PreStateConfig, PreStateFrame,
};
use eyre::{eyre, Result};
use reth_chainspec::{ChainSpecBuilder, MAINNET};
use reth_node_builder::{NodeBuilder, NodeHandle};
use reth_node_core::{args::RpcServerArgs, node_config::NodeConfig};
use reth_node_ethereum::EthereumNode;
use reth_rpc_server_types::RpcModuleSelection;
use reth_tasks::TaskManager;
use serde::Deserialize;
use std::sync::Arc;
const PRESTATE_SNAPSHOT: &str =
include_str!("../../../../../testing/prestate/tx-selfdestruct-prestate.json");
/// Replays the selfdestruct transaction via `debug_traceCall` and ensures Reth's prestate matches
/// Geth's captured snapshot.
// <https://github.com/paradigmxyz/reth/issues/19703>
#[tokio::test]
async fn debug_trace_call_matches_geth_prestate_snapshot() -> Result<()> {
reth_tracing::init_test_tracing();
let mut genesis: Genesis = MAINNET.genesis().clone();
genesis.coinbase = address!("0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5");
let exec = TaskManager::current();
let exec = exec.executor();
let expected_frame = expected_snapshot_frame()?;
let prestate_mode = match &expected_frame {
PreStateFrame::Default(mode) => mode.clone(),
_ => return Err(eyre!("snapshot must contain default prestate frame")),
};
genesis.alloc.extend(
prestate_mode
.0
.clone()
.into_iter()
.map(|(addr, state)| (addr, account_state_to_genesis(state))),
);
let chain_spec = Arc::new(
ChainSpecBuilder::default()
.chain(MAINNET.chain)
.genesis(genesis)
.cancun_activated()
.prague_activated()
.build(),
);
let node_config = NodeConfig::test().with_chain(chain_spec).with_rpc(
RpcServerArgs::default()
.with_unused_ports()
.with_http()
.with_http_api(RpcModuleSelection::all_modules().into()),
);
let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config)
.testing_node(exec)
.node(EthereumNode::default())
.launch()
.await?;
let provider = node.rpc_server_handle().eth_http_provider().unwrap();
// <https://etherscan.io/tx/0x391f4b6a382d3bcc3120adc2ea8c62003e604e487d97281129156fd284a1a89d>
let tx = r#"{
"type": "0x2",
"chainId": "0x1",
"nonce": "0x39af8",
"gas": "0x249f0",
"maxFeePerGas": "0xc6432e2d7",
"maxPriorityFeePerGas": "0x68889c2b",
"to": "0xc77ad0a71008d7094a62cfbd250a2eb2afdf2776",
"value": "0x0",
"accessList": [],
"input": "0xf3fef3a3000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000000f6b64",
"r": "0x40ab901a8262d5e6fe9b6513996cd5df412526580cab7410c13acc9dd9f6ec93",
"s": "0x6b76354c8cb1c1d6dbebfd555be9053170f02a648c4b36740e3fd7c6e9499572",
"yParity": "0x1",
"v": "0x1",
"hash": "0x391f4b6a382d3bcc3120adc2ea8c62003e604e487d97281129156fd284a1a89d",
"blockHash": "0xf9b77bcf8c69544304dff34129f3bdc71f00fdf766c1522ed6ac1382726ead82",
"blockNumber": "0x1294fd2",
"transactionIndex": "0x3a",
"from": "0xa7fb5ca286fc3fd67525629048a4de3ba24cba2e",
"gasPrice": "0x7c5bcc0e0"
}"#;
let tx = serde_json::from_str::<Transaction>(tx).unwrap();
let request = TransactionRequest::from_recovered_transaction(tx.into_recovered());
let trace: PreStateFrame = provider
.debug_trace_call_prestate(
request,
BlockId::latest(),
GethDebugTracingOptions::prestate_tracer(PreStateConfig::default()).into(),
)
.await?;
similar_asserts::assert_eq!(trace, expected_frame);
Ok(())
}
fn expected_snapshot_frame() -> Result<PreStateFrame> {
#[derive(Deserialize)]
struct Snapshot {
result: serde_json::Value,
}
let snapshot: Snapshot = serde_json::from_str(PRESTATE_SNAPSHOT)?;
Ok(serde_json::from_value(snapshot.result)?)
}
fn account_state_to_genesis(value: AccountState) -> GenesisAccount {
let balance = value.balance.unwrap_or_default();
let code = value.code.filter(|code| !code.is_empty());
let storage = (!value.storage.is_empty()).then_some(value.storage);
GenesisAccount::default()
.with_balance(balance)
.with_nonce(value.nonce)
.with_code(code)
.with_storage(storage)
}

File diff suppressed because one or more lines are too long

View File

@@ -8,8 +8,7 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
pub mod generators;
pub mod genesis_allocator;
pub use genesis_allocator::GenesisAllocator;
pub mod generators;