mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 19:09:54 +00:00
Revm example (#6855)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -1816,6 +1816,16 @@ dependencies = [
|
|||||||
"syn 2.0.50",
|
"syn 2.0.50",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "custom-inspector"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"eyre",
|
||||||
|
"futures-util",
|
||||||
|
"reth",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "custom-node"
|
name = "custom-node"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
|||||||
@ -65,6 +65,7 @@ members = [
|
|||||||
"examples/rpc-db/",
|
"examples/rpc-db/",
|
||||||
"examples/trace-transaction-cli/",
|
"examples/trace-transaction-cli/",
|
||||||
"examples/polygon-p2p/",
|
"examples/polygon-p2p/",
|
||||||
|
"examples/custom-inspector/",
|
||||||
"testing/ef-tests/",
|
"testing/ef-tests/",
|
||||||
]
|
]
|
||||||
default-members = ["bin/reth"]
|
default-members = ["bin/reth"]
|
||||||
|
|||||||
12
examples/custom-inspector/Cargo.toml
Normal file
12
examples/custom-inspector/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "custom-inspector"
|
||||||
|
version = "0.0.0"
|
||||||
|
publish = false
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
reth.workspace = true
|
||||||
|
clap = { workspace = true, features = ["derive"] }
|
||||||
|
futures-util.workspace = true
|
||||||
|
eyre.workspace = true
|
||||||
149
examples/custom-inspector/src/main.rs
Normal file
149
examples/custom-inspector/src/main.rs
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
//! Example of how to use a custom inspector to trace new pending transactions
|
||||||
|
//!
|
||||||
|
//! Run with
|
||||||
|
//!
|
||||||
|
//! ```not_rust
|
||||||
|
//! cargo run --release -p custom-inspector --node --http --ws --recipients 0x....,0x....
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! If no recipients are specified, all transactions will be inspected.
|
||||||
|
|
||||||
|
#![warn(unused_crate_dependencies)]
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use futures_util::stream::StreamExt;
|
||||||
|
use reth::{
|
||||||
|
cli::{
|
||||||
|
components::{RethNodeComponents, RethRpcComponents, RethRpcServerHandles},
|
||||||
|
config::RethRpcConfig,
|
||||||
|
ext::{RethCliExt, RethNodeCommandConfig},
|
||||||
|
Cli,
|
||||||
|
},
|
||||||
|
primitives::{Address, BlockId, IntoRecoveredTransaction},
|
||||||
|
revm::{
|
||||||
|
inspector_handle_register,
|
||||||
|
interpreter::{Interpreter, OpCode},
|
||||||
|
Database, Evm, EvmContext, Inspector,
|
||||||
|
},
|
||||||
|
rpc::{
|
||||||
|
compat::transaction::transaction_to_call_request,
|
||||||
|
eth::{revm_utils::EvmOverrides, EthTransactions},
|
||||||
|
},
|
||||||
|
tasks::TaskSpawner,
|
||||||
|
transaction_pool::TransactionPool,
|
||||||
|
};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Cli::<MyRethCliExt>::parse().run().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type that tells the reth CLI what extensions to use
|
||||||
|
struct MyRethCliExt;
|
||||||
|
|
||||||
|
impl RethCliExt for MyRethCliExt {
|
||||||
|
/// This tells the reth CLI to trace addresses via `RethCliTxpoolExt`
|
||||||
|
type Node = RethCliTxpoolExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Our custom cli args extension that adds one flag to reth default CLI.
|
||||||
|
#[derive(Debug, Clone, Default, clap::Args)]
|
||||||
|
struct RethCliTxpoolExt {
|
||||||
|
/// The addresses of the recipients that we want to trace.
|
||||||
|
#[arg(long, value_delimiter = ',')]
|
||||||
|
pub recipients: Vec<Address>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A dummy inspector that logs the opcodes and their corresponding program counter for a
|
||||||
|
/// transaction
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
struct DummyInspector {
|
||||||
|
ret_val: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DB> Inspector<DB> for DummyInspector
|
||||||
|
where
|
||||||
|
DB: Database,
|
||||||
|
{
|
||||||
|
/// This method is called at each step of the EVM execution.
|
||||||
|
/// It checks if the current opcode is valid and if so, it stores the opcode and its
|
||||||
|
/// corresponding program counter in the `ret_val` vector.
|
||||||
|
fn step(&mut self, interp: &mut Interpreter, _context: &mut EvmContext<DB>) {
|
||||||
|
if let Some(opcode) = OpCode::new(interp.current_opcode()) {
|
||||||
|
self.ret_val.push(format!("{}: {}", interp.program_counter(), opcode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RethNodeCommandConfig for RethCliTxpoolExt {
|
||||||
|
/// Sets up a subscription to listen for new pending transactions and traces them.
|
||||||
|
/// If the transaction is from one of the specified recipients, it will be traced.
|
||||||
|
/// If no recipients are specified, all transactions will be traced.
|
||||||
|
fn on_rpc_server_started<Conf, Reth>(
|
||||||
|
&mut self,
|
||||||
|
_config: &Conf,
|
||||||
|
components: &Reth,
|
||||||
|
rpc_components: RethRpcComponents<'_, Reth>,
|
||||||
|
_handles: RethRpcServerHandles,
|
||||||
|
) -> eyre::Result<()>
|
||||||
|
where
|
||||||
|
Conf: RethRpcConfig,
|
||||||
|
Reth: RethNodeComponents,
|
||||||
|
{
|
||||||
|
let recipients = self.recipients.iter().copied().collect::<HashSet<_>>();
|
||||||
|
|
||||||
|
// create a new subscription to pending transactions
|
||||||
|
let mut pending_transactions = components.pool().new_pending_pool_transactions_listener();
|
||||||
|
|
||||||
|
let eth_api = rpc_components.registry.eth_api();
|
||||||
|
|
||||||
|
println!("Spawning trace task!");
|
||||||
|
// Spawn an async block to listen for transactions.
|
||||||
|
components.task_executor().spawn(Box::pin(async move {
|
||||||
|
// Waiting for new transactions
|
||||||
|
while let Some(event) = pending_transactions.next().await {
|
||||||
|
let tx = event.transaction;
|
||||||
|
println!("Transaction received: {tx:?}");
|
||||||
|
|
||||||
|
if recipients.is_empty() {
|
||||||
|
// convert the pool transaction
|
||||||
|
let call_request = transaction_to_call_request(tx.to_recovered_transaction());
|
||||||
|
|
||||||
|
let result = eth_api
|
||||||
|
.spawn_with_call_at(
|
||||||
|
call_request,
|
||||||
|
BlockId::default(),
|
||||||
|
EvmOverrides::default(),
|
||||||
|
move |db, env| {
|
||||||
|
let mut dummy_inspector = DummyInspector::default();
|
||||||
|
{
|
||||||
|
// configure the evm with the custom inspector
|
||||||
|
let mut evm = Evm::builder()
|
||||||
|
.with_db(db)
|
||||||
|
.with_external_context(&mut dummy_inspector)
|
||||||
|
.with_env_with_handler_cfg(env)
|
||||||
|
.append_handler_register(inspector_handle_register)
|
||||||
|
.build();
|
||||||
|
// execute the transaction on a blocking task and await the
|
||||||
|
// inspector result
|
||||||
|
let _ = evm.transact()?;
|
||||||
|
}
|
||||||
|
Ok(dummy_inspector)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if let Ok(ret_val) = result {
|
||||||
|
let hash = tx.hash();
|
||||||
|
println!(
|
||||||
|
"Inspector result for transaction {}: \n {}",
|
||||||
|
hash,
|
||||||
|
ret_val.ret_val.join("\n")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user