From 19f481006b95013b54457c59b19703c8c2ced8a3 Mon Sep 17 00:00:00 2001
From: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Date: Tue, 9 Jan 2024 22:33:45 +0100
Subject: [PATCH] chore: move revm-inspectors to a separate repo (#5992)
---
Cargo.lock | 39 +-
Cargo.toml | 18 +-
bin/reth/Cargo.toml | 2 +-
bin/reth/src/builder/mod.rs | 4 +-
crates/revm/Cargo.toml | 3 +-
crates/revm/revm-inspectors/Cargo.toml | 35 -
.../revm/revm-inspectors/src/access_list.rs | 99 --
crates/revm/revm-inspectors/src/lib.rs | 24 -
.../revm-inspectors/src/stack/maybe_owned.rs | 178 ----
crates/revm/revm-inspectors/src/stack/mod.rs | 215 ----
.../revm/revm-inspectors/src/tracing/arena.rs | 92 --
.../src/tracing/builder/geth.rs | 325 ------
.../src/tracing/builder/mod.rs | 10 -
.../src/tracing/builder/parity.rs | 633 ------------
.../src/tracing/builder/walker.rs | 39 -
.../revm-inspectors/src/tracing/config.rs | 225 -----
.../revm-inspectors/src/tracing/fourbyte.rs | 78 --
.../revm-inspectors/src/tracing/js/bigint.js | 1 -
.../src/tracing/js/bindings.rs | 936 ------------------
.../src/tracing/js/builtins.rs | 258 -----
.../revm-inspectors/src/tracing/js/mod.rs | 583 -----------
.../revm/revm-inspectors/src/tracing/mod.rs | 563 -----------
.../revm-inspectors/src/tracing/opcount.rs | 29 -
.../revm/revm-inspectors/src/tracing/types.rs | 684 -------------
.../revm/revm-inspectors/src/tracing/utils.rs | 91 --
crates/revm/src/lib.rs | 2 +-
crates/rpc/rpc/Cargo.toml | 2 +-
deny.toml | 6 +-
28 files changed, 40 insertions(+), 5134 deletions(-)
delete mode 100644 crates/revm/revm-inspectors/Cargo.toml
delete mode 100644 crates/revm/revm-inspectors/src/access_list.rs
delete mode 100644 crates/revm/revm-inspectors/src/lib.rs
delete mode 100644 crates/revm/revm-inspectors/src/stack/maybe_owned.rs
delete mode 100644 crates/revm/revm-inspectors/src/stack/mod.rs
delete mode 100644 crates/revm/revm-inspectors/src/tracing/arena.rs
delete mode 100644 crates/revm/revm-inspectors/src/tracing/builder/geth.rs
delete mode 100644 crates/revm/revm-inspectors/src/tracing/builder/mod.rs
delete mode 100644 crates/revm/revm-inspectors/src/tracing/builder/parity.rs
delete mode 100644 crates/revm/revm-inspectors/src/tracing/builder/walker.rs
delete mode 100644 crates/revm/revm-inspectors/src/tracing/config.rs
delete mode 100644 crates/revm/revm-inspectors/src/tracing/fourbyte.rs
delete mode 100644 crates/revm/revm-inspectors/src/tracing/js/bigint.js
delete mode 100644 crates/revm/revm-inspectors/src/tracing/js/bindings.rs
delete mode 100644 crates/revm/revm-inspectors/src/tracing/js/builtins.rs
delete mode 100644 crates/revm/revm-inspectors/src/tracing/js/mod.rs
delete mode 100644 crates/revm/revm-inspectors/src/tracing/mod.rs
delete mode 100644 crates/revm/revm-inspectors/src/tracing/opcount.rs
delete mode 100644 crates/revm/revm-inspectors/src/tracing/types.rs
delete mode 100644 crates/revm/revm-inspectors/src/tracing/utils.rs
diff --git a/Cargo.lock b/Cargo.lock
index 06b6b987b..9a69f1a94 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5692,7 +5692,6 @@ dependencies = [
"reth-provider",
"reth-prune",
"reth-revm",
- "reth-revm-inspectors",
"reth-rpc",
"reth-rpc-api",
"reth-rpc-builder",
@@ -5705,6 +5704,7 @@ dependencies = [
"reth-tracing",
"reth-transaction-pool",
"reth-trie",
+ "revm-inspectors",
"secp256k1 0.27.0",
"serde",
"serde_json",
@@ -6446,29 +6446,12 @@ dependencies = [
"reth-interfaces",
"reth-primitives",
"reth-provider",
- "reth-revm-inspectors",
"reth-trie",
"revm",
+ "revm-inspectors",
"tracing",
]
-[[package]]
-name = "reth-revm-inspectors"
-version = "0.1.0-alpha.14"
-dependencies = [
- "alloy-primitives",
- "alloy-rpc-trace-types",
- "alloy-rpc-types",
- "alloy-sol-types",
- "boa_engine",
- "boa_gc",
- "revm",
- "serde",
- "serde_json",
- "thiserror",
- "tokio",
-]
-
[[package]]
name = "reth-rpc"
version = "0.1.0-alpha.14"
@@ -6805,6 +6788,24 @@ dependencies = [
"revm-precompile",
]
+[[package]]
+name = "revm-inspectors"
+version = "0.1.0"
+source = "git+https://github.com/paradigmxyz/evm-inspectors#4a3f82fa0d2010b3de69479b42fafe82339dae5e"
+dependencies = [
+ "alloy-primitives",
+ "alloy-rpc-trace-types",
+ "alloy-rpc-types",
+ "alloy-sol-types",
+ "boa_engine",
+ "boa_gc",
+ "revm",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tokio",
+]
+
[[package]]
name = "revm-interpreter"
version = "1.3.0"
diff --git a/Cargo.toml b/Cargo.toml
index b37e03d4f..c91c1c51d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,7 +28,6 @@ members = [
"crates/primitives/",
"crates/prune/",
"crates/revm/",
- "crates/revm/revm-inspectors/",
"crates/rpc/ipc/",
"crates/rpc/rpc/",
"crates/rpc/rpc-api/",
@@ -135,7 +134,6 @@ reth-primitives = { path = "crates/primitives" }
reth-provider = { path = "crates/storage/provider" }
reth-prune = { path = "crates/prune" }
reth-revm = { path = "crates/revm" }
-reth-revm-inspectors = { path = "crates/revm/revm-inspectors" }
reth-rpc = { path = "crates/rpc/rpc" }
reth-rpc-api = { path = "crates/rpc/rpc-api" }
reth-rpc-api-testing-util = { path = "crates/rpc/rpc-testing-util" }
@@ -152,8 +150,14 @@ reth-transaction-pool = { path = "crates/transaction-pool" }
reth-trie = { path = "crates/trie" }
# revm
-revm = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze", features = ["std", "secp256k1"], default-features = false }
-revm-primitives = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze", features = ["std"], default-features = false }
+revm = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze", features = [
+ "std",
+ "secp256k1",
+], default-features = false }
+revm-primitives = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze", features = [
+ "std",
+], default-features = false }
+revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors" }
# eth
alloy-primitives = "0.5"
@@ -161,7 +165,9 @@ alloy-dyn-abi = "0.5"
alloy-sol-types = "0.5"
alloy-rlp = "0.3"
alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", features = ["jsonrpsee-types"] }
-alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy", features = ["jsonrpsee-types"] }
+alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy", features = [
+ "jsonrpsee-types",
+] }
ethers-core = { version = "2.0", default-features = false }
ethers-providers = { version = "2.0", default-features = false }
ethers-signers = { version = "2.0", default-features = false }
@@ -196,7 +202,6 @@ once_cell = "1.17"
syn = "2.0"
ahash = "0.8.6"
-
# proc-macros
proc-macro2 = "1.0"
quote = "1.0"
@@ -241,7 +246,6 @@ proptest = "1.4"
proptest-derive = "0.4"
serial_test = "2"
-
[workspace.metadata.cargo-udeps.ignore]
# ignored because this is mutually exclusive with the optimism payload builder via feature flags
normal = ["reth-ethereum-payload-builder"]
diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml
index 0ac8e5303..39245bf48 100644
--- a/bin/reth/Cargo.toml
+++ b/bin/reth/Cargo.toml
@@ -26,7 +26,6 @@ reth-db = { workspace = true, features = ["mdbx", "test-utils"] }
# TODO: Temporary use of the test-utils feature
reth-provider = { workspace = true, features = ["test-utils"] }
reth-revm.workspace = true
-reth-revm-inspectors.workspace = true
reth-stages.workspace = true
reth-interfaces = { workspace = true, features = ["test-utils", "clap"] }
reth-transaction-pool.workspace = true
@@ -60,6 +59,7 @@ reth-nippy-jar.workspace = true
# crypto
alloy-rlp.workspace = true
secp256k1 = { workspace = true, features = ["global-context", "rand-std", "recovery"] }
+revm-inspectors.workspace = true
# tracing
tracing.workspace = true
diff --git a/bin/reth/src/builder/mod.rs b/bin/reth/src/builder/mod.rs
index 71361b83a..1163b9ebb 100644
--- a/bin/reth/src/builder/mod.rs
+++ b/bin/reth/src/builder/mod.rs
@@ -73,7 +73,6 @@ use reth_provider::{
};
use reth_prune::PrunerBuilder;
use reth_revm::EvmProcessorFactory;
-use reth_revm_inspectors::stack::Hook;
use reth_rpc_engine_api::EngineApi;
use reth_stages::{
prelude::*,
@@ -89,6 +88,7 @@ use reth_transaction_pool::{
blobstore::InMemoryBlobStore, EthTransactionPool, TransactionPool,
TransactionValidationTaskExecutor,
};
+use revm_inspectors::stack::Hook;
use secp256k1::SecretKey;
use std::{
net::{SocketAddr, SocketAddrV4},
@@ -862,7 +862,7 @@ impl NodeConfig {
}
let (tip_tx, tip_rx) = watch::channel(B256::ZERO);
- use reth_revm_inspectors::stack::InspectorStackConfig;
+ use revm_inspectors::stack::InspectorStackConfig;
let factory = reth_revm::EvmProcessorFactory::new(self.chain.clone());
let stack_config = InspectorStackConfig {
diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml
index 6d12f754a..e717ee22e 100644
--- a/crates/revm/Cargo.toml
+++ b/crates/revm/Cargo.toml
@@ -16,11 +16,11 @@ workspace = true
reth-primitives.workspace = true
reth-interfaces.workspace = true
reth-provider.workspace = true
-reth-revm-inspectors.workspace = true
reth-consensus-common.workspace = true
# revm
revm.workspace = true
+revm-inspectors.workspace = true
# common
tracing.workspace = true
@@ -35,3 +35,4 @@ optimism = [
"reth-consensus-common/optimism",
"reth-interfaces/optimism",
]
+js-tracer = ["revm-inspectors/js-tracer"]
diff --git a/crates/revm/revm-inspectors/Cargo.toml b/crates/revm/revm-inspectors/Cargo.toml
deleted file mode 100644
index a6fcbda0f..000000000
--- a/crates/revm/revm-inspectors/Cargo.toml
+++ /dev/null
@@ -1,35 +0,0 @@
-[package]
-name = "reth-revm-inspectors"
-version.workspace = true
-edition.workspace = true
-rust-version.workspace = true
-license.workspace = true
-homepage.workspace = true
-repository.workspace = true
-description = "revm inspector implementations used by reth"
-
-[lints]
-workspace = true
-
-[dependencies]
-# eth
-alloy-sol-types.workspace = true
-alloy-primitives.workspace = true
-alloy-rpc-types.workspace = true
-alloy-rpc-trace-types.workspace = true
-
-revm.workspace = true
-
-serde = { workspace = true, features = ["derive"] }
-thiserror = { workspace = true, optional = true }
-serde_json = { workspace = true, optional = true }
-
-# js-tracing-inspector
-boa_engine = { workspace = true, optional = true }
-boa_gc = { workspace = true, optional = true }
-
-tokio = { version = "1", features = ["sync"], optional = true }
-
-[features]
-default = ["js-tracer"]
-js-tracer = ["boa_engine", "boa_gc", "tokio", "thiserror", "serde_json"]
diff --git a/crates/revm/revm-inspectors/src/access_list.rs b/crates/revm/revm-inspectors/src/access_list.rs
deleted file mode 100644
index e591a4f1d..000000000
--- a/crates/revm/revm-inspectors/src/access_list.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-use alloy_primitives::{Address, B256};
-use alloy_rpc_types::{AccessList, AccessListItem};
-use revm::{
- interpreter::{opcode, Interpreter},
- Database, EVMData, Inspector,
-};
-use std::collections::{BTreeSet, HashMap, HashSet};
-
-/// An [Inspector] that collects touched accounts and storage slots.
-///
-/// This can be used to construct an [AccessList] for a transaction via `eth_createAccessList`
-#[derive(Default, Debug)]
-pub struct AccessListInspector {
- /// All addresses that should be excluded from the final accesslist
- excluded: HashSet
,
- /// All addresses and touched slots
- access_list: HashMap>,
-}
-
-impl AccessListInspector {
- /// Creates a new inspector instance
- ///
- /// The `access_list` is the provided access list from the call request
- pub fn new(
- access_list: AccessList,
- from: Address,
- to: Address,
- precompiles: impl IntoIterator- ,
- ) -> Self {
- AccessListInspector {
- excluded: [from, to].into_iter().chain(precompiles).collect(),
- access_list: access_list
- .0
- .into_iter()
- .map(|v| (v.address, v.storage_keys.into_iter().collect()))
- .collect(),
- }
- }
-
- /// Returns list of addresses and storage keys used by the transaction. It gives you the list of
- /// addresses and storage keys that were touched during execution.
- pub fn into_access_list(self) -> AccessList {
- let items = self.access_list.into_iter().map(|(address, slots)| AccessListItem {
- address,
- storage_keys: slots.into_iter().collect(),
- });
- AccessList(items.collect())
- }
-
- /// Returns list of addresses and storage keys used by the transaction. It gives you the list of
- /// addresses and storage keys that were touched during execution.
- pub fn access_list(&self) -> AccessList {
- let items = self.access_list.iter().map(|(address, slots)| AccessListItem {
- address: *address,
- storage_keys: slots.iter().copied().collect(),
- });
- AccessList(items.collect())
- }
-}
-
-impl Inspector for AccessListInspector
-where
- DB: Database,
-{
- fn step(&mut self, interpreter: &mut Interpreter<'_>, _data: &mut EVMData<'_, DB>) {
- match interpreter.current_opcode() {
- opcode::SLOAD | opcode::SSTORE => {
- if let Ok(slot) = interpreter.stack().peek(0) {
- let cur_contract = interpreter.contract.address;
- self.access_list
- .entry(cur_contract)
- .or_default()
- .insert(B256::from(slot.to_be_bytes()));
- }
- }
- opcode::EXTCODECOPY |
- opcode::EXTCODEHASH |
- opcode::EXTCODESIZE |
- opcode::BALANCE |
- opcode::SELFDESTRUCT => {
- if let Ok(slot) = interpreter.stack().peek(0) {
- let addr = Address::from_word(B256::from(slot.to_be_bytes()));
- if !self.excluded.contains(&addr) {
- self.access_list.entry(addr).or_default();
- }
- }
- }
- opcode::DELEGATECALL | opcode::CALL | opcode::STATICCALL | opcode::CALLCODE => {
- if let Ok(slot) = interpreter.stack().peek(1) {
- let addr = Address::from_word(B256::from(slot.to_be_bytes()));
- if !self.excluded.contains(&addr) {
- self.access_list.entry(addr).or_default();
- }
- }
- }
- _ => (),
- }
- }
-}
diff --git a/crates/revm/revm-inspectors/src/lib.rs b/crates/revm/revm-inspectors/src/lib.rs
deleted file mode 100644
index e524eb517..000000000
--- a/crates/revm/revm-inspectors/src/lib.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-//! revm [Inspector](revm::Inspector) implementations, such as call tracers
-//!
-//! ## Feature Flags
-//!
-//! - `js-tracer` (default): Enables a JavaScript tracer implementation. This pulls in extra
-//! dependencies (such as `boa`, `tokio` and `serde_json`).
-
-#![doc(
- html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
- html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
- issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
-)]
-#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
-
-/// An inspector implementation for an EIP2930 Accesslist
-pub mod access_list;
-
-/// An inspector stack abstracting the implementation details of
-/// each inspector and allowing to hook on block/transaction execution,
-/// used in the main RETH executor.
-pub mod stack;
-
-/// An inspector for recording traces
-pub mod tracing;
diff --git a/crates/revm/revm-inspectors/src/stack/maybe_owned.rs b/crates/revm/revm-inspectors/src/stack/maybe_owned.rs
deleted file mode 100644
index c02ce6e4b..000000000
--- a/crates/revm/revm-inspectors/src/stack/maybe_owned.rs
+++ /dev/null
@@ -1,178 +0,0 @@
-use alloy_primitives::U256;
-use revm::{
- interpreter::{CallInputs, CreateInputs, Gas, InstructionResult, Interpreter},
- primitives::{db::Database, Address, Bytes, B256},
- EVMData, Inspector,
-};
-use std::{
- cell::{Ref, RefCell},
- rc::Rc,
-};
-
-/// An [Inspector] that is either owned by an individual [Inspector] or is shared as part of a
-/// series of inspectors in a [InspectorStack](crate::stack::InspectorStack).
-///
-/// Caution: if the [Inspector] is _stacked_ then it _must_ be called first.
-#[derive(Debug)]
-pub enum MaybeOwnedInspector {
- /// Inspector is owned.
- Owned(Rc>),
- /// Inspector is shared and part of a stack
- Stacked(Rc>),
-}
-
-impl MaybeOwnedInspector {
- /// Create a new _owned_ instance
- pub fn new_owned(inspector: INSP) -> Self {
- MaybeOwnedInspector::Owned(Rc::new(RefCell::new(inspector)))
- }
-
- /// Creates a [MaybeOwnedInspector::Stacked] clone of this type.
- pub fn clone_stacked(&self) -> Self {
- match self {
- MaybeOwnedInspector::Owned(gas) | MaybeOwnedInspector::Stacked(gas) => {
- MaybeOwnedInspector::Stacked(Rc::clone(gas))
- }
- }
- }
-
- /// Returns a reference to the inspector.
- pub fn as_ref(&self) -> Ref<'_, INSP> {
- match self {
- MaybeOwnedInspector::Owned(insp) => insp.borrow(),
- MaybeOwnedInspector::Stacked(insp) => insp.borrow(),
- }
- }
-}
-
-impl MaybeOwnedInspector {
- /// Create a new _owned_ instance
- pub fn owned() -> Self {
- Self::new_owned(Default::default())
- }
-}
-
-impl Default for MaybeOwnedInspector {
- fn default() -> Self {
- Self::owned()
- }
-}
-
-impl Clone for MaybeOwnedInspector {
- fn clone(&self) -> Self {
- self.clone_stacked()
- }
-}
-
-impl Inspector for MaybeOwnedInspector
-where
- DB: Database,
- INSP: Inspector,
-{
- fn initialize_interp(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) {
- match self {
- MaybeOwnedInspector::Owned(insp) => insp.borrow_mut().initialize_interp(interp, data),
- MaybeOwnedInspector::Stacked(_) => {}
- }
- }
-
- fn step(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) {
- match self {
- MaybeOwnedInspector::Owned(insp) => insp.borrow_mut().step(interp, data),
- MaybeOwnedInspector::Stacked(_) => {}
- }
- }
-
- fn log(
- &mut self,
- evm_data: &mut EVMData<'_, DB>,
- address: &Address,
- topics: &[B256],
- data: &Bytes,
- ) {
- match self {
- MaybeOwnedInspector::Owned(insp) => {
- return insp.borrow_mut().log(evm_data, address, topics, data)
- }
- MaybeOwnedInspector::Stacked(_) => {}
- }
- }
-
- fn step_end(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) {
- match self {
- MaybeOwnedInspector::Owned(insp) => insp.borrow_mut().step_end(interp, data),
- MaybeOwnedInspector::Stacked(_) => {}
- }
- }
-
- fn call(
- &mut self,
- data: &mut EVMData<'_, DB>,
- inputs: &mut CallInputs,
- ) -> (InstructionResult, Gas, Bytes) {
- match self {
- MaybeOwnedInspector::Owned(insp) => return insp.borrow_mut().call(data, inputs),
- MaybeOwnedInspector::Stacked(_) => {}
- }
-
- (InstructionResult::Continue, Gas::new(0), Bytes::new())
- }
-
- fn call_end(
- &mut self,
- data: &mut EVMData<'_, DB>,
- inputs: &CallInputs,
- remaining_gas: Gas,
- ret: InstructionResult,
- out: Bytes,
- ) -> (InstructionResult, Gas, Bytes) {
- match self {
- MaybeOwnedInspector::Owned(insp) => {
- return insp.borrow_mut().call_end(data, inputs, remaining_gas, ret, out)
- }
- MaybeOwnedInspector::Stacked(_) => {}
- }
- (ret, remaining_gas, out)
- }
-
- fn create(
- &mut self,
- data: &mut EVMData<'_, DB>,
- inputs: &mut CreateInputs,
- ) -> (InstructionResult, Option, Gas, Bytes) {
- match self {
- MaybeOwnedInspector::Owned(insp) => return insp.borrow_mut().create(data, inputs),
- MaybeOwnedInspector::Stacked(_) => {}
- }
-
- (InstructionResult::Continue, None, Gas::new(0), Bytes::default())
- }
-
- fn create_end(
- &mut self,
- data: &mut EVMData<'_, DB>,
- inputs: &CreateInputs,
- ret: InstructionResult,
- address: Option,
- remaining_gas: Gas,
- out: Bytes,
- ) -> (InstructionResult, Option, Gas, Bytes) {
- match self {
- MaybeOwnedInspector::Owned(insp) => {
- return insp.borrow_mut().create_end(data, inputs, ret, address, remaining_gas, out)
- }
- MaybeOwnedInspector::Stacked(_) => {}
- }
-
- (ret, address, remaining_gas, out)
- }
-
- fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
- match self {
- MaybeOwnedInspector::Owned(insp) => {
- return insp.borrow_mut().selfdestruct(contract, target, value)
- }
- MaybeOwnedInspector::Stacked(_) => {}
- }
- }
-}
diff --git a/crates/revm/revm-inspectors/src/stack/mod.rs b/crates/revm/revm-inspectors/src/stack/mod.rs
deleted file mode 100644
index f8ea91794..000000000
--- a/crates/revm/revm-inspectors/src/stack/mod.rs
+++ /dev/null
@@ -1,215 +0,0 @@
-use alloy_primitives::{Address, Bytes, B256, U256};
-use revm::{
- inspectors::CustomPrintTracer,
- interpreter::{CallInputs, CreateInputs, Gas, InstructionResult, Interpreter},
- primitives::Env,
- Database, EVMData, Inspector,
-};
-use std::fmt::Debug;
-
-/// A wrapped [Inspector] that can be reused in the stack
-mod maybe_owned;
-pub use maybe_owned::MaybeOwnedInspector;
-
-/// One can hook on inspector execution in 3 ways:
-/// - Block: Hook on block execution
-/// - BlockWithIndex: Hook on block execution transaction index
-/// - Transaction: Hook on a specific transaction hash
-#[derive(Debug, Default, Clone)]
-pub enum Hook {
- #[default]
- /// No hook.
- None,
- /// Hook on a specific block.
- Block(u64),
- /// Hook on a specific transaction hash.
- Transaction(B256),
- /// Hooks on every transaction in a block.
- All,
-}
-
-/// An inspector that calls multiple inspectors in sequence.
-///
-/// If a call to an inspector returns a value other than [InstructionResult::Continue] (or
-/// equivalent) the remaining inspectors are not called.
-#[derive(Default, Clone)]
-pub struct InspectorStack {
- /// An inspector that prints the opcode traces to the console.
- pub custom_print_tracer: Option,
- /// The provided hook
- pub hook: Hook,
-}
-
-impl Debug for InspectorStack {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("InspectorStack")
- .field("custom_print_tracer", &self.custom_print_tracer.is_some())
- .field("hook", &self.hook)
- .finish()
- }
-}
-
-impl InspectorStack {
- /// Create a new inspector stack.
- pub fn new(config: InspectorStackConfig) -> Self {
- let mut stack = InspectorStack { hook: config.hook, ..Default::default() };
-
- if config.use_printer_tracer {
- stack.custom_print_tracer = Some(CustomPrintTracer::default());
- }
-
- stack
- }
-
- /// Check if the inspector should be used.
- pub fn should_inspect(&self, env: &Env, tx_hash: B256) -> bool {
- match self.hook {
- Hook::None => false,
- Hook::Block(block) => env.block.number.to::() == block,
- Hook::Transaction(hash) => hash == tx_hash,
- Hook::All => true,
- }
- }
-}
-
-/// Configuration for the inspectors.
-#[derive(Debug, Default)]
-pub struct InspectorStackConfig {
- /// Enable revm inspector printer.
- /// In execution this will print opcode level traces directly to console.
- pub use_printer_tracer: bool,
-
- /// Hook on a specific block or transaction.
- pub hook: Hook,
-}
-
-/// Helper macro to call the same method on multiple inspectors without resorting to dynamic
-/// dispatch
-#[macro_export]
-macro_rules! call_inspectors {
- ($id:ident, [ $($inspector:expr),+ ], $call:block) => {
- $({
- if let Some($id) = $inspector {
- $call;
- }
- })+
- }
-}
-
-impl Inspector for InspectorStack
-where
- DB: Database,
-{
- fn initialize_interp(&mut self, interpreter: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) {
- call_inspectors!(inspector, [&mut self.custom_print_tracer], {
- inspector.initialize_interp(interpreter, data);
- });
- }
-
- fn step(&mut self, interpreter: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) {
- call_inspectors!(inspector, [&mut self.custom_print_tracer], {
- inspector.step(interpreter, data);
- });
- }
-
- fn log(
- &mut self,
- evm_data: &mut EVMData<'_, DB>,
- address: &Address,
- topics: &[B256],
- data: &Bytes,
- ) {
- call_inspectors!(inspector, [&mut self.custom_print_tracer], {
- inspector.log(evm_data, address, topics, data);
- });
- }
-
- fn step_end(&mut self, interpreter: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) {
- call_inspectors!(inspector, [&mut self.custom_print_tracer], {
- inspector.step_end(interpreter, data);
- });
- }
-
- fn call(
- &mut self,
- data: &mut EVMData<'_, DB>,
- inputs: &mut CallInputs,
- ) -> (InstructionResult, Gas, Bytes) {
- call_inspectors!(inspector, [&mut self.custom_print_tracer], {
- let (status, gas, retdata) = inspector.call(data, inputs);
-
- // Allow inspectors to exit early
- if status != InstructionResult::Continue {
- return (status, gas, retdata)
- }
- });
-
- (InstructionResult::Continue, Gas::new(inputs.gas_limit), Bytes::new())
- }
-
- fn call_end(
- &mut self,
- data: &mut EVMData<'_, DB>,
- inputs: &CallInputs,
- remaining_gas: Gas,
- ret: InstructionResult,
- out: Bytes,
- ) -> (InstructionResult, Gas, Bytes) {
- call_inspectors!(inspector, [&mut self.custom_print_tracer], {
- let (new_ret, new_gas, new_out) =
- inspector.call_end(data, inputs, remaining_gas, ret, out.clone());
-
- // If the inspector returns a different ret or a revert with a non-empty message,
- // we assume it wants to tell us something
- if new_ret != ret || (new_ret == InstructionResult::Revert && new_out != out) {
- return (new_ret, new_gas, new_out)
- }
- });
-
- (ret, remaining_gas, out)
- }
-
- fn create(
- &mut self,
- data: &mut EVMData<'_, DB>,
- inputs: &mut CreateInputs,
- ) -> (InstructionResult, Option, Gas, Bytes) {
- call_inspectors!(inspector, [&mut self.custom_print_tracer], {
- let (status, addr, gas, retdata) = inspector.create(data, inputs);
-
- // Allow inspectors to exit early
- if status != InstructionResult::Continue {
- return (status, addr, gas, retdata)
- }
- });
-
- (InstructionResult::Continue, None, Gas::new(inputs.gas_limit), Bytes::new())
- }
-
- fn create_end(
- &mut self,
- data: &mut EVMData<'_, DB>,
- inputs: &CreateInputs,
- ret: InstructionResult,
- address: Option,
- remaining_gas: Gas,
- out: Bytes,
- ) -> (InstructionResult, Option, Gas, Bytes) {
- call_inspectors!(inspector, [&mut self.custom_print_tracer], {
- let (new_ret, new_address, new_gas, new_retdata) =
- inspector.create_end(data, inputs, ret, address, remaining_gas, out.clone());
-
- if new_ret != ret {
- return (new_ret, new_address, new_gas, new_retdata)
- }
- });
-
- (ret, address, remaining_gas, out)
- }
-
- fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
- call_inspectors!(inspector, [&mut self.custom_print_tracer], {
- Inspector::::selfdestruct(inspector, contract, target, value);
- });
- }
-}
diff --git a/crates/revm/revm-inspectors/src/tracing/arena.rs b/crates/revm/revm-inspectors/src/tracing/arena.rs
deleted file mode 100644
index cb7c6b518..000000000
--- a/crates/revm/revm-inspectors/src/tracing/arena.rs
+++ /dev/null
@@ -1,92 +0,0 @@
-use crate::tracing::types::{CallTrace, CallTraceNode, LogCallOrder};
-
-/// An arena of recorded traces.
-///
-/// This type will be populated via the [TracingInspector](crate::tracing::TracingInspector).
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct CallTraceArena {
- /// The arena of recorded trace nodes
- pub(crate) arena: Vec,
-}
-
-impl CallTraceArena {
- /// Pushes a new trace into the arena, returning the trace ID
- ///
- /// This appends a new trace to the arena, and also inserts a new entry in the node's parent
- /// node children set if `attach_to_parent` is `true`. E.g. if calls to precompiles should
- /// not be included in the call graph this should be called with [PushTraceKind::PushOnly].
- pub(crate) fn push_trace(
- &mut self,
- mut entry: usize,
- kind: PushTraceKind,
- new_trace: CallTrace,
- ) -> usize {
- loop {
- match new_trace.depth {
- // The entry node, just update it
- 0 => {
- self.arena[0].trace = new_trace;
- return 0
- }
- // We found the parent node, add the new trace as a child
- _ if self.arena[entry].trace.depth == new_trace.depth - 1 => {
- let id = self.arena.len();
- let node = CallTraceNode {
- parent: Some(entry),
- trace: new_trace,
- idx: id,
- ..Default::default()
- };
- self.arena.push(node);
-
- // also track the child in the parent node
- if kind.is_attach_to_parent() {
- let parent = &mut self.arena[entry];
- let trace_location = parent.children.len();
- parent.ordering.push(LogCallOrder::Call(trace_location));
- parent.children.push(id);
- }
-
- return id
- }
- _ => {
- // We haven't found the parent node, go deeper
- entry = *self.arena[entry].children.last().expect("Disconnected trace");
- }
- }
- }
- }
-
- /// Returns the nodes in the arena
- pub fn nodes(&self) -> &[CallTraceNode] {
- &self.arena
- }
-
- /// Consumes the arena and returns the nodes
- pub fn into_nodes(self) -> Vec {
- self.arena
- }
-}
-
-/// How to push a trace into the arena
-pub(crate) enum PushTraceKind {
- /// This will _only_ push the trace into the arena.
- PushOnly,
- /// This will push the trace into the arena, and also insert a new entry in the node's parent
- /// node children set.
- PushAndAttachToParent,
-}
-
-impl PushTraceKind {
- #[inline]
- fn is_attach_to_parent(&self) -> bool {
- matches!(self, PushTraceKind::PushAndAttachToParent)
- }
-}
-
-impl Default for CallTraceArena {
- fn default() -> Self {
- // The first node is the root node
- CallTraceArena { arena: vec![Default::default()] }
- }
-}
diff --git a/crates/revm/revm-inspectors/src/tracing/builder/geth.rs b/crates/revm/revm-inspectors/src/tracing/builder/geth.rs
deleted file mode 100644
index b4a6b6548..000000000
--- a/crates/revm/revm-inspectors/src/tracing/builder/geth.rs
+++ /dev/null
@@ -1,325 +0,0 @@
-//! Geth trace builder
-
-use crate::tracing::{
- types::{CallTraceNode, CallTraceStepStackItem},
- utils::load_account_code,
- TracingInspectorConfig,
-};
-use alloy_primitives::{Address, Bytes, B256, U256};
-use alloy_rpc_trace_types::geth::{
- AccountChangeKind, AccountState, CallConfig, CallFrame, DefaultFrame, DiffMode,
- GethDefaultTracingOptions, PreStateConfig, PreStateFrame, PreStateMode, StructLog,
-};
-use revm::{db::DatabaseRef, primitives::ResultAndState};
-use std::collections::{btree_map::Entry, BTreeMap, HashMap, VecDeque};
-
-/// A type for creating geth style traces
-#[derive(Clone, Debug)]
-pub struct GethTraceBuilder {
- /// Recorded trace nodes.
- nodes: Vec,
- /// How the traces were recorded
- _config: TracingInspectorConfig,
-}
-
-impl GethTraceBuilder {
- /// Returns a new instance of the builder
- pub fn new(nodes: Vec, _config: TracingInspectorConfig) -> Self {
- Self { nodes, _config }
- }
-
- /// Fill in the geth trace with all steps of the trace and its children traces in the order they
- /// appear in the transaction.
- fn fill_geth_trace(
- &self,
- main_trace_node: &CallTraceNode,
- opts: &GethDefaultTracingOptions,
- storage: &mut HashMap>,
- struct_logs: &mut Vec,
- ) {
- // A stack with all the steps of the trace and all its children's steps.
- // This is used to process the steps in the order they appear in the transactions.
- // Steps are grouped by their Call Trace Node, in order to process them all in the order
- // they appear in the transaction, we need to process steps of call nodes when they appear.
- // When we find a call step, we push all the steps of the child trace on the stack, so they
- // are processed next. The very next step is the last item on the stack
- let mut step_stack = VecDeque::with_capacity(main_trace_node.trace.steps.len());
-
- main_trace_node.push_steps_on_stack(&mut step_stack);
-
- // Iterate over the steps inside the given trace
- while let Some(CallTraceStepStackItem { trace_node, step, call_child_id }) =
- step_stack.pop_back()
- {
- let mut log = step.convert_to_geth_struct_log(opts);
-
- // Fill in memory and storage depending on the options
- if opts.is_storage_enabled() {
- let contract_storage = storage.entry(step.contract).or_default();
- if let Some(change) = step.storage_change {
- contract_storage.insert(change.key.into(), change.value.into());
- log.storage = Some(contract_storage.clone());
- }
- }
-
- if opts.is_return_data_enabled() {
- log.return_data = Some(trace_node.trace.output.clone());
- }
-
- // Add step to geth trace
- struct_logs.push(log);
-
- // If the step is a call, we first push all the steps of the child trace on the stack,
- // so they are processed next
- if let Some(call_child_id) = call_child_id {
- let child_trace = &self.nodes[call_child_id];
- child_trace.push_steps_on_stack(&mut step_stack);
- }
- }
- }
-
- /// Generate a geth-style trace e.g. for `debug_traceTransaction`
- ///
- /// This expects the gas used and return value for the
- /// [ExecutionResult](revm::primitives::ExecutionResult) of the executed transaction.
- pub fn geth_traces(
- &self,
- receipt_gas_used: u64,
- return_value: Bytes,
- opts: GethDefaultTracingOptions,
- ) -> DefaultFrame {
- if self.nodes.is_empty() {
- return Default::default()
- }
- // Fetch top-level trace
- let main_trace_node = &self.nodes[0];
- let main_trace = &main_trace_node.trace;
-
- let mut struct_logs = Vec::new();
- let mut storage = HashMap::new();
- self.fill_geth_trace(main_trace_node, &opts, &mut storage, &mut struct_logs);
-
- DefaultFrame {
- // If the top-level trace succeeded, then it was a success
- failed: !main_trace.success,
- gas: receipt_gas_used,
- return_value,
- struct_logs,
- }
- }
-
- /// Generate a geth-style traces for the call tracer.
- ///
- /// This decodes all call frames from the recorded traces.
- ///
- /// This expects the gas used and return value for the
- /// [ExecutionResult](revm::primitives::ExecutionResult) of the executed transaction.
- pub fn geth_call_traces(&self, opts: CallConfig, gas_used: u64) -> CallFrame {
- if self.nodes.is_empty() {
- return Default::default()
- }
-
- let include_logs = opts.with_log.unwrap_or_default();
- // first fill up the root
- let main_trace_node = &self.nodes[0];
- let mut root_call_frame = main_trace_node.geth_empty_call_frame(include_logs);
- root_call_frame.gas_used = U256::from(gas_used);
-
- // selfdestructs are not recorded as individual call traces but are derived from
- // the call trace and are added as additional `CallFrame` objects to the parent call
- if let Some(selfdestruct) = main_trace_node.geth_selfdestruct_call_trace() {
- root_call_frame.calls.push(selfdestruct);
- }
-
- if opts.only_top_call.unwrap_or_default() {
- return root_call_frame
- }
-
- // fill all the call frames in the root call frame with the recorded traces.
- // traces are identified by their index in the arena
- // so we can populate the call frame tree by walking up the call tree
- let mut call_frames = Vec::with_capacity(self.nodes.len());
- call_frames.push((0, root_call_frame));
-
- for (idx, trace) in self.nodes.iter().enumerate().skip(1) {
- // selfdestructs are not recorded as individual call traces but are derived from
- // the call trace and are added as additional `CallFrame` objects to the parent call
- if let Some(selfdestruct) = trace.geth_selfdestruct_call_trace() {
- call_frames.last_mut().expect("not empty").1.calls.push(selfdestruct);
- }
- call_frames.push((idx, trace.geth_empty_call_frame(include_logs)));
- }
-
- // pop the _children_ calls frame and move it to the parent
- // this will roll up the child frames to their parent; this works because `child idx >
- // parent idx`
- loop {
- let (idx, call) = call_frames.pop().expect("call frames not empty");
- let node = &self.nodes[idx];
- if let Some(parent) = node.parent {
- let parent_frame = &mut call_frames[parent];
- // we need to ensure that calls are in order they are called: the last child node is
- // the last call, but since we walk up the tree, we need to always
- // insert at position 0
- parent_frame.1.calls.insert(0, call);
- } else {
- debug_assert!(call_frames.is_empty(), "only one root node has no parent");
- return call
- }
- }
- }
-
- /// Returns the accounts necessary for transaction execution.
- ///
- /// The prestate mode returns the accounts necessary to execute a given transaction.
- /// diff_mode returns the differences between the transaction's pre and post-state.
- ///
- /// * `state` - The state post-transaction execution.
- /// * `diff_mode` - if prestate is in diff or prestate mode.
- /// * `db` - The database to fetch state pre-transaction execution.
- pub fn geth_prestate_traces(
- &self,
- ResultAndState { state, .. }: &ResultAndState,
- prestate_config: PreStateConfig,
- db: DB,
- ) -> Result {
- let account_diffs = state.into_iter().map(|(addr, acc)| (*addr, acc));
-
- if prestate_config.is_default_mode() {
- let mut prestate = PreStateMode::default();
- // in default mode we __only__ return the touched state
- for node in self.nodes.iter() {
- let addr = node.trace.address;
-
- let acc_state = match prestate.0.entry(addr) {
- Entry::Vacant(entry) => {
- let db_acc = db.basic_ref(addr)?.unwrap_or_default();
- let code = load_account_code(&db, &db_acc);
- let acc_state =
- AccountState::from_account_info(db_acc.nonce, db_acc.balance, code);
- entry.insert(acc_state)
- }
- Entry::Occupied(entry) => entry.into_mut(),
- };
-
- for (key, value) in node.touched_slots() {
- match acc_state.storage.entry(key.into()) {
- Entry::Vacant(entry) => {
- entry.insert(value.into());
- }
- Entry::Occupied(_) => {
- // we've already recorded this slot
- }
- }
- }
- }
-
- // also need to check changed accounts for things like balance changes etc
- for (addr, changed_acc) in account_diffs {
- let acc_state = match prestate.0.entry(addr) {
- Entry::Vacant(entry) => {
- let db_acc = db.basic_ref(addr)?.unwrap_or_default();
- let code = load_account_code(&db, &db_acc);
- let acc_state =
- AccountState::from_account_info(db_acc.nonce, db_acc.balance, code);
- entry.insert(acc_state)
- }
- Entry::Occupied(entry) => {
- // already recorded via touched accounts
- entry.into_mut()
- }
- };
-
- // in case we missed anything during the trace, we need to add the changed accounts
- // storage
- for (key, slot) in changed_acc.storage.iter() {
- match acc_state.storage.entry((*key).into()) {
- Entry::Vacant(entry) => {
- entry.insert(slot.previous_or_original_value.into());
- }
- Entry::Occupied(_) => {
- // we've already recorded this slot
- }
- }
- }
- }
-
- Ok(PreStateFrame::Default(prestate))
- } else {
- let mut state_diff = DiffMode::default();
- let mut account_change_kinds = HashMap::with_capacity(account_diffs.len());
- for (addr, changed_acc) in account_diffs {
- let db_acc = db.basic_ref(addr)?.unwrap_or_default();
-
- let pre_code = load_account_code(&db, &db_acc);
-
- let mut pre_state =
- AccountState::from_account_info(db_acc.nonce, db_acc.balance, pre_code);
-
- let mut post_state = AccountState::from_account_info(
- changed_acc.info.nonce,
- changed_acc.info.balance,
- changed_acc.info.code.as_ref().map(|code| code.original_bytes()),
- );
-
- // handle storage changes
- for (key, slot) in changed_acc.storage.iter().filter(|(_, slot)| slot.is_changed())
- {
- pre_state.storage.insert((*key).into(), slot.previous_or_original_value.into());
- post_state.storage.insert((*key).into(), slot.present_value.into());
- }
-
- state_diff.pre.insert(addr, pre_state);
- state_diff.post.insert(addr, post_state);
-
- // determine the change type
- let pre_change = if changed_acc.is_created() {
- AccountChangeKind::Create
- } else {
- AccountChangeKind::Modify
- };
- let post_change = if changed_acc.is_selfdestructed() {
- AccountChangeKind::SelfDestruct
- } else {
- AccountChangeKind::Modify
- };
-
- account_change_kinds.insert(addr, (pre_change, post_change));
- }
-
- // ensure we're only keeping changed entries
- state_diff.retain_changed().remove_zero_storage_values();
-
- self.diff_traces(&mut state_diff.pre, &mut state_diff.post, account_change_kinds);
- Ok(PreStateFrame::Diff(state_diff))
- }
- }
-
- /// Returns the difference between the pre and post state of the transaction depending on the
- /// kind of changes of that account (pre,post)
- fn diff_traces(
- &self,
- pre: &mut BTreeMap,
- post: &mut BTreeMap,
- change_type: HashMap,
- ) {
- post.retain(|addr, post_state| {
- // Don't keep destroyed accounts in the post state
- if change_type.get(addr).map(|ty| ty.1.is_selfdestruct()).unwrap_or(false) {
- return false
- }
- if let Some(pre_state) = pre.get(addr) {
- // remove any unchanged account info
- post_state.remove_matching_account_info(pre_state);
- }
-
- true
- });
-
- // Don't keep created accounts the pre state
- pre.retain(|addr, _pre_state| {
- // only keep accounts that are not created
- change_type.get(addr).map(|ty| !ty.0.is_created()).unwrap_or(true)
- });
- }
-}
diff --git a/crates/revm/revm-inspectors/src/tracing/builder/mod.rs b/crates/revm/revm-inspectors/src/tracing/builder/mod.rs
deleted file mode 100644
index e6e58d8c2..000000000
--- a/crates/revm/revm-inspectors/src/tracing/builder/mod.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-//! Builder types for building traces
-
-/// Geth style trace builders for `debug_` namespace
-pub mod geth;
-
-/// Parity style trace builders for `trace_` namespace
-pub mod parity;
-
-/// Walker types used for traversing various callgraphs
-mod walker;
diff --git a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs b/crates/revm/revm-inspectors/src/tracing/builder/parity.rs
deleted file mode 100644
index a36b155f1..000000000
--- a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs
+++ /dev/null
@@ -1,633 +0,0 @@
-use super::walker::CallTraceNodeWalkerBF;
-use crate::tracing::{
- types::{CallTraceNode, CallTraceStep},
- utils::load_account_code,
- TracingInspectorConfig,
-};
-use alloy_primitives::{Address, U64};
-use alloy_rpc_trace_types::parity::*;
-use alloy_rpc_types::TransactionInfo;
-use revm::{
- db::DatabaseRef,
- interpreter::{
- opcode::{self, spec_opcode_gas},
- OpCode,
- },
- primitives::{Account, ExecutionResult, ResultAndState, SpecId, KECCAK_EMPTY},
-};
-use std::collections::{HashSet, VecDeque};
-
-/// A type for creating parity style traces
-///
-/// Note: Parity style traces always ignore calls to precompiles.
-#[derive(Clone, Debug)]
-pub struct ParityTraceBuilder {
- /// Recorded trace nodes
- nodes: Vec,
- /// The spec id of the EVM.
- spec_id: Option,
-
- /// How the traces were recorded
- _config: TracingInspectorConfig,
-}
-
-impl ParityTraceBuilder {
- /// Returns a new instance of the builder
- pub fn new(
- nodes: Vec,
- spec_id: Option,
- _config: TracingInspectorConfig,
- ) -> Self {
- Self { nodes, spec_id, _config }
- }
-
- /// Returns a list of all addresses that appeared as callers.
- pub fn callers(&self) -> HashSet {
- self.nodes.iter().map(|node| node.trace.caller).collect()
- }
-
- /// Manually the gas used of the root trace.
- ///
- /// The root trace's gasUsed should mirror the actual gas used by the transaction.
- ///
- /// This allows setting it manually by consuming the execution result's gas for example.
- #[inline]
- pub fn set_transaction_gas_used(&mut self, gas_used: u64) {
- if let Some(node) = self.nodes.first_mut() {
- node.trace.gas_used = gas_used;
- }
- }
-
- /// Convenience function for [ParityTraceBuilder::set_transaction_gas_used] that consumes the
- /// type.
- #[inline]
- pub fn with_transaction_gas_used(mut self, gas_used: u64) -> Self {
- self.set_transaction_gas_used(gas_used);
- self
- }
-
- /// Returns the trace addresses of all call nodes in the set
- ///
- /// Each entry in the returned vector represents the [Self::trace_address] of the corresponding
- /// node in the nodes set.
- ///
- /// CAUTION: This also includes precompiles, which have an empty trace address.
- fn trace_addresses(&self) -> Vec> {
- let mut all_addresses = Vec::with_capacity(self.nodes.len());
- for idx in 0..self.nodes.len() {
- all_addresses.push(self.trace_address(idx));
- }
- all_addresses
- }
-
- /// Returns the `traceAddress` of the node in the arena
- ///
- /// The `traceAddress` field of all returned traces, gives the exact location in the call trace
- /// [index in root, index in first CALL, index in second CALL, …].
- ///
- /// # Panics
- ///
- /// if the `idx` does not belong to a node
- ///
- /// Note: if the call node of `idx` is a precompile, the returned trace address will be empty.
- fn trace_address(&self, idx: usize) -> Vec {
- if idx == 0 {
- // root call has empty traceAddress
- return vec![]
- }
- let mut graph = vec![];
- let mut node = &self.nodes[idx];
- if node.is_precompile() {
- return graph
- }
- while let Some(parent) = node.parent {
- // the index of the child call in the arena
- let child_idx = node.idx;
- node = &self.nodes[parent];
- // find the index of the child call in the parent node
- let call_idx = node
- .children
- .iter()
- .position(|child| *child == child_idx)
- .expect("non precompile child call exists in parent");
- graph.push(call_idx);
- }
- graph.reverse();
- graph
- }
-
- /// Returns an iterator over all nodes to trace
- ///
- /// This excludes nodes that represent calls to precompiles.
- fn iter_traceable_nodes(&self) -> impl Iterator
- {
- self.nodes.iter().filter(|node| !node.is_precompile())
- }
-
- /// Returns an iterator over all recorded traces for `trace_transaction`
- pub fn into_localized_transaction_traces_iter(
- self,
- info: TransactionInfo,
- ) -> impl Iterator
- {
- self.into_transaction_traces_iter().map(move |trace| {
- let TransactionInfo { hash, index, block_hash, block_number, .. } = info;
- LocalizedTransactionTrace {
- trace,
- transaction_position: index,
- transaction_hash: hash,
- block_number,
- block_hash,
- }
- })
- }
-
- /// Returns all recorded traces for `trace_transaction`
- pub fn into_localized_transaction_traces(
- self,
- info: TransactionInfo,
- ) -> Vec {
- self.into_localized_transaction_traces_iter(info).collect()
- }
-
- /// Consumes the inspector and returns the trace results according to the configured trace
- /// types.
- ///
- /// Warning: If `trace_types` contains [TraceType::StateDiff] the returned [StateDiff] will not
- /// be filled. Use [ParityTraceBuilder::into_trace_results_with_state] or
- /// [populate_state_diff] to populate the balance and nonce changes for the [StateDiff]
- /// using the [DatabaseRef].
- pub fn into_trace_results(
- self,
- res: &ExecutionResult,
- trace_types: &HashSet,
- ) -> TraceResults {
- let gas_used = res.gas_used();
- let output = res.output().cloned().unwrap_or_default();
-
- let (trace, vm_trace, state_diff) = self.into_trace_type_traces(trace_types);
-
- let mut trace =
- TraceResults { output, trace: trace.unwrap_or_default(), vm_trace, state_diff };
-
- // we're setting the gas used of the root trace explicitly to the gas used of the execution
- // result
- trace.set_root_trace_gas_used(gas_used);
-
- trace
- }
-
- /// Consumes the inspector and returns the trace results according to the configured trace
- /// types.
- ///
- /// This also takes the [DatabaseRef] to populate the balance and nonce changes for the
- /// [StateDiff].
- ///
- /// Note: this is considered a convenience method that takes the state map of
- /// [ResultAndState] after inspecting a transaction
- /// with the [TracingInspector](crate::tracing::TracingInspector).
- pub fn into_trace_results_with_state(
- self,
- res: &ResultAndState,
- trace_types: &HashSet,
- db: DB,
- ) -> Result {
- let ResultAndState { ref result, ref state } = res;
-
- let breadth_first_addresses = if trace_types.contains(&TraceType::VmTrace) {
- CallTraceNodeWalkerBF::new(&self.nodes)
- .map(|node| node.trace.address)
- .collect::>()
- } else {
- vec![]
- };
-
- let mut trace_res = self.into_trace_results(result, trace_types);
-
- // check the state diff case
- if let Some(ref mut state_diff) = trace_res.state_diff {
- populate_state_diff(state_diff, &db, state.iter())?;
- }
-
- // check the vm trace case
- if let Some(ref mut vm_trace) = trace_res.vm_trace {
- populate_vm_trace_bytecodes(&db, vm_trace, breadth_first_addresses)?;
- }
-
- Ok(trace_res)
- }
-
- /// Returns the tracing types that are configured in the set.
- ///
- /// Warning: if [TraceType::StateDiff] is provided this does __not__ fill the state diff, since
- /// this requires access to the account diffs.
- ///
- /// See [Self::into_trace_results_with_state] and [populate_state_diff].
- pub fn into_trace_type_traces(
- self,
- trace_types: &HashSet,
- ) -> (Option>, Option, Option) {
- if trace_types.is_empty() || self.nodes.is_empty() {
- return (None, None, None)
- }
-
- let with_traces = trace_types.contains(&TraceType::Trace);
- let with_diff = trace_types.contains(&TraceType::StateDiff);
-
- let vm_trace =
- if trace_types.contains(&TraceType::VmTrace) { Some(self.vm_trace()) } else { None };
-
- let mut traces = Vec::with_capacity(if with_traces { self.nodes.len() } else { 0 });
-
- for node in self.iter_traceable_nodes() {
- let trace_address = self.trace_address(node.idx);
-
- if with_traces {
- let trace = node.parity_transaction_trace(trace_address);
- traces.push(trace);
-
- // check if the trace node is a selfdestruct
- if node.is_selfdestruct() {
- // selfdestructs are not recorded as individual call traces but are derived from
- // the call trace and are added as additional `TransactionTrace` objects in the
- // trace array
- let addr = {
- let last = traces.last_mut().expect("exists");
- let mut addr = last.trace_address.clone();
- addr.push(last.subtraces);
- // need to account for the additional selfdestruct trace
- last.subtraces += 1;
- addr
- };
-
- if let Some(trace) = node.parity_selfdestruct_trace(addr) {
- traces.push(trace);
- }
- }
- }
- }
-
- let traces = with_traces.then_some(traces);
- let diff = with_diff.then_some(StateDiff::default());
-
- (traces, vm_trace, diff)
- }
-
- /// Returns an iterator over all recorded traces for `trace_transaction`
- pub fn into_transaction_traces_iter(self) -> impl Iterator
- {
- let trace_addresses = self.trace_addresses();
- TransactionTraceIter {
- next_selfdestruct: None,
- iter: self
- .nodes
- .into_iter()
- .zip(trace_addresses)
- .filter(|(node, _)| !node.is_precompile())
- .map(|(node, trace_address)| (node.parity_transaction_trace(trace_address), node)),
- }
- }
-
- /// Returns the raw traces of the transaction
- pub fn into_transaction_traces(self) -> Vec {
- self.into_transaction_traces_iter().collect()
- }
-
- /// Returns the last recorded step
- #[inline]
- fn last_step(&self) -> Option<&CallTraceStep> {
- self.nodes.last().and_then(|node| node.trace.steps.last())
- }
-
- /// Returns true if the last recorded step is a STOP
- #[inline]
- fn is_last_step_stop_op(&self) -> bool {
- self.last_step().map(|step| step.is_stop()).unwrap_or(false)
- }
-
- /// Creates a VM trace by walking over `CallTraceNode`s
- ///
- /// does not have the code fields filled in
- pub fn vm_trace(&self) -> VmTrace {
- self.nodes.first().map(|node| self.make_vm_trace(node)).unwrap_or_default()
- }
-
- /// Returns a VM trace without the code filled in
- ///
- /// Iteratively creates a VM trace by traversing the recorded nodes in the arena
- fn make_vm_trace(&self, start: &CallTraceNode) -> VmTrace {
- let mut child_idx_stack = Vec::with_capacity(self.nodes.len());
- let mut sub_stack = VecDeque::with_capacity(self.nodes.len());
-
- let mut current = start;
- let mut child_idx: usize = 0;
-
- // finds the deepest nested calls of each call frame and fills them up bottom to top
- let instructions = 'outer: loop {
- match current.children.get(child_idx) {
- Some(child) => {
- child_idx_stack.push(child_idx + 1);
-
- child_idx = 0;
- current = self.nodes.get(*child).expect("there should be a child");
- }
- None => {
- let mut instructions = Vec::with_capacity(current.trace.steps.len());
-
- for step in ¤t.trace.steps {
- let maybe_sub_call = if step.is_calllike_op() {
- sub_stack.pop_front().flatten()
- } else {
- None
- };
-
- if step.is_stop() && instructions.is_empty() && self.is_last_step_stop_op()
- {
- // This is a special case where there's a single STOP which is
- // "optimised away", transfers for example
- break 'outer instructions
- }
-
- instructions.push(self.make_instruction(step, maybe_sub_call));
- }
-
- match current.parent {
- Some(parent) => {
- sub_stack.push_back(Some(VmTrace {
- code: Default::default(),
- ops: instructions,
- }));
-
- child_idx = child_idx_stack.pop().expect("there should be a child idx");
-
- current = self.nodes.get(parent).expect("there should be a parent");
- }
- None => break instructions,
- }
- }
- }
- };
-
- VmTrace { code: Default::default(), ops: instructions }
- }
-
- /// Creates a VM instruction from a [CallTraceStep] and a [VmTrace] for the subcall if there is
- /// one
- fn make_instruction(
- &self,
- step: &CallTraceStep,
- maybe_sub_call: Option,
- ) -> VmInstruction {
- let maybe_storage = step.storage_change.map(|storage_change| StorageDelta {
- key: storage_change.key,
- val: storage_change.value,
- });
-
- let maybe_memory = if step.memory.is_empty() {
- None
- } else {
- Some(MemoryDelta {
- off: step.memory_size,
- data: step.memory.as_bytes().to_vec().into(),
- })
- };
-
- let maybe_execution = Some(VmExecutedOperation {
- used: step.gas_remaining,
- push: step.push_stack.clone().unwrap_or_default(),
- mem: maybe_memory,
- store: maybe_storage,
- });
-
- let cost = self
- .spec_id
- .and_then(|spec_id| {
- spec_opcode_gas(spec_id).get(step.op.get() as usize).map(|op| op.get_gas())
- })
- .unwrap_or_default();
-
- VmInstruction {
- pc: step.pc,
- cost: cost as u64,
- ex: maybe_execution,
- sub: maybe_sub_call,
- op: Some(step.op.to_string()),
- idx: None,
- }
- }
-}
-
-/// An iterator for [TransactionTrace]s
-struct TransactionTraceIter {
- iter: Iter,
- next_selfdestruct: Option,
-}
-
-impl Iterator for TransactionTraceIter
-where
- Iter: Iterator
- ,
-{
- type Item = TransactionTrace;
-
- fn next(&mut self) -> Option {
- if let Some(selfdestruct) = self.next_selfdestruct.take() {
- return Some(selfdestruct)
- }
- let (mut trace, node) = self.iter.next()?;
- if node.is_selfdestruct() {
- // since selfdestructs are emitted as additional trace, increase the trace count
- let mut addr = trace.trace_address.clone();
- addr.push(trace.subtraces);
- // need to account for the additional selfdestruct trace
- trace.subtraces += 1;
- self.next_selfdestruct = node.parity_selfdestruct_trace(addr);
- }
- Some(trace)
- }
-}
-
-/// addresses are presorted via breadth first walk thru [CallTraceNode]s, this can be done by a
-/// walker in [crate::tracing::builder::walker]
-///
-/// iteratively fill the [VmTrace] code fields
-pub(crate) fn populate_vm_trace_bytecodes(
- db: DB,
- trace: &mut VmTrace,
- breadth_first_addresses: I,
-) -> Result<(), DB::Error>
-where
- DB: DatabaseRef,
- I: IntoIterator
- ,
-{
- let mut stack: VecDeque<&mut VmTrace> = VecDeque::new();
- stack.push_back(trace);
-
- let mut addrs = breadth_first_addresses.into_iter();
-
- while let Some(curr_ref) = stack.pop_front() {
- for op in curr_ref.ops.iter_mut() {
- if let Some(sub) = op.sub.as_mut() {
- stack.push_back(sub);
- }
- }
-
- let addr = addrs.next().expect("there should be an address");
-
- let db_acc = db.basic_ref(addr)?.unwrap_or_default();
-
- let code_hash = if db_acc.code_hash != KECCAK_EMPTY { db_acc.code_hash } else { continue };
-
- curr_ref.code = db.code_by_hash_ref(code_hash)?.original_bytes();
- }
-
- Ok(())
-}
-
-/// Loops over all state accounts in the accounts diff that contains all accounts that are included
-/// in the [ExecutionResult] state map and compares the balance and nonce against what's in the
-/// `db`, which should point to the beginning of the transaction.
-///
-/// It's expected that `DB` is a revm [Database](revm::db::Database) which at this point already
-/// contains all the accounts that are in the state map and never has to fetch them from disk.
-pub fn populate_state_diff<'a, DB, I>(
- state_diff: &mut StateDiff,
- db: DB,
- account_diffs: I,
-) -> Result<(), DB::Error>
-where
- I: IntoIterator
- ,
- DB: DatabaseRef,
-{
- for (addr, changed_acc) in account_diffs.into_iter() {
- // if the account was selfdestructed and created during the transaction, we can ignore it
- if changed_acc.is_selfdestructed() && changed_acc.is_created() {
- continue
- }
-
- let addr = *addr;
- let entry = state_diff.entry(addr).or_default();
-
- // we check if this account was created during the transaction
- if changed_acc.is_created() || changed_acc.is_loaded_as_not_existing() {
- entry.balance = Delta::Added(changed_acc.info.balance);
- entry.nonce = Delta::Added(U64::from(changed_acc.info.nonce));
-
- // accounts without code are marked as added
- let account_code = load_account_code(&db, &changed_acc.info).unwrap_or_default();
- entry.code = Delta::Added(account_code);
-
- // new storage values are marked as added,
- // however we're filtering changed here to avoid adding entries for the zero value
- for (key, slot) in changed_acc.storage.iter().filter(|(_, slot)| slot.is_changed()) {
- entry.storage.insert((*key).into(), Delta::Added(slot.present_value.into()));
- }
- } else {
- // account already exists, we need to fetch the account from the db
- let db_acc = db.basic_ref(addr)?.unwrap_or_default();
-
- // update _changed_ storage values
- for (key, slot) in changed_acc.storage.iter().filter(|(_, slot)| slot.is_changed()) {
- entry.storage.insert(
- (*key).into(),
- Delta::changed(
- slot.previous_or_original_value.into(),
- slot.present_value.into(),
- ),
- );
- }
-
- // check if the account was changed at all
- if entry.storage.is_empty() &&
- db_acc == changed_acc.info &&
- !changed_acc.is_selfdestructed()
- {
- // clear the entry if the account was not changed
- state_diff.remove(&addr);
- continue
- }
-
- entry.balance = if db_acc.balance == changed_acc.info.balance {
- Delta::Unchanged
- } else {
- Delta::Changed(ChangedType { from: db_acc.balance, to: changed_acc.info.balance })
- };
-
- // this is relevant for the caller and contracts
- entry.nonce = if db_acc.nonce == changed_acc.info.nonce {
- Delta::Unchanged
- } else {
- Delta::Changed(ChangedType {
- from: U64::from(db_acc.nonce),
- to: U64::from(changed_acc.info.nonce),
- })
- };
- }
- }
-
- Ok(())
-}
-
-/// Returns the number of items pushed on the stack by a given opcode.
-/// This used to determine how many stack etries to put in the `push` element
-/// in a parity vmTrace.
-/// The value is obvious for most opcodes, but SWAP* and DUP* are a bit weird,
-/// and we handle those as they are handled in parity vmtraces.
-/// For reference:
-pub(crate) fn stack_push_count(step_op: OpCode) -> usize {
- let step_op = step_op.get();
- match step_op {
- opcode::PUSH0..=opcode::PUSH32 => 1,
- opcode::SWAP1..=opcode::SWAP16 => (step_op - opcode::SWAP1) as usize + 2,
- opcode::DUP1..=opcode::DUP16 => (step_op - opcode::DUP1) as usize + 2,
- opcode::CALLDATALOAD |
- opcode::SLOAD |
- opcode::MLOAD |
- opcode::CALLDATASIZE |
- opcode::LT |
- opcode::GT |
- opcode::DIV |
- opcode::SDIV |
- opcode::SAR |
- opcode::AND |
- opcode::EQ |
- opcode::CALLVALUE |
- opcode::ISZERO |
- opcode::ADD |
- opcode::EXP |
- opcode::CALLER |
- opcode::KECCAK256 |
- opcode::SUB |
- opcode::ADDRESS |
- opcode::GAS |
- opcode::MUL |
- opcode::RETURNDATASIZE |
- opcode::NOT |
- opcode::SHR |
- opcode::SHL |
- opcode::EXTCODESIZE |
- opcode::SLT |
- opcode::OR |
- opcode::NUMBER |
- opcode::PC |
- opcode::TIMESTAMP |
- opcode::BALANCE |
- opcode::SELFBALANCE |
- opcode::MULMOD |
- opcode::ADDMOD |
- opcode::BASEFEE |
- opcode::BLOCKHASH |
- opcode::BYTE |
- opcode::XOR |
- opcode::ORIGIN |
- opcode::CODESIZE |
- opcode::MOD |
- opcode::SIGNEXTEND |
- opcode::GASLIMIT |
- opcode::DIFFICULTY |
- opcode::SGT |
- opcode::GASPRICE |
- opcode::MSIZE |
- opcode::EXTCODEHASH |
- opcode::SMOD |
- opcode::CHAINID |
- opcode::COINBASE => 1,
- _ => 0,
- }
-}
diff --git a/crates/revm/revm-inspectors/src/tracing/builder/walker.rs b/crates/revm/revm-inspectors/src/tracing/builder/walker.rs
deleted file mode 100644
index 4d88a2af4..000000000
--- a/crates/revm/revm-inspectors/src/tracing/builder/walker.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-use crate::tracing::types::CallTraceNode;
-use std::collections::VecDeque;
-
-/// Traverses Reths internal tracing structure breadth-first
-///
-/// This is a lazy iterator
-pub(crate) struct CallTraceNodeWalkerBF<'trace> {
- /// the entire arena
- nodes: &'trace Vec,
-
- /// holds indexes of nodes to visit as we traverse
- queue: VecDeque,
-}
-
-impl<'trace> CallTraceNodeWalkerBF<'trace> {
- pub(crate) fn new(nodes: &'trace Vec) -> Self {
- let mut queue = VecDeque::with_capacity(nodes.len());
- queue.push_back(0);
-
- Self { nodes, queue }
- }
-}
-
-impl<'trace> Iterator for CallTraceNodeWalkerBF<'trace> {
- type Item = &'trace CallTraceNode;
-
- fn next(&mut self) -> Option {
- match self.queue.pop_front() {
- Some(idx) => {
- let curr = self.nodes.get(idx).expect("there should be a node");
-
- self.queue.extend(curr.children.iter());
-
- Some(curr)
- }
- None => None,
- }
- }
-}
diff --git a/crates/revm/revm-inspectors/src/tracing/config.rs b/crates/revm/revm-inspectors/src/tracing/config.rs
deleted file mode 100644
index e3940849f..000000000
--- a/crates/revm/revm-inspectors/src/tracing/config.rs
+++ /dev/null
@@ -1,225 +0,0 @@
-use alloy_rpc_trace_types::{geth::GethDefaultTracingOptions, parity::TraceType};
-use std::collections::HashSet;
-
-/// Gives guidance to the [TracingInspector](crate::tracing::TracingInspector).
-///
-/// Use [TracingInspectorConfig::default_parity] or [TracingInspectorConfig::default_geth] to get
-/// the default configs for specific styles of traces.
-#[derive(Debug, Clone, Copy, Eq, PartialEq)]
-pub struct TracingInspectorConfig {
- /// Whether to record every individual opcode level step.
- pub record_steps: bool,
- /// Whether to record individual memory snapshots.
- pub record_memory_snapshots: bool,
- /// Whether to record individual stack snapshots.
- pub record_stack_snapshots: StackSnapshotType,
- /// Whether to record state diffs.
- pub record_state_diff: bool,
- /// Whether to ignore precompile calls.
- pub exclude_precompile_calls: bool,
- /// Whether to record individual return data
- pub record_call_return_data: bool,
- /// Whether to record logs
- pub record_logs: bool,
-}
-
-impl TracingInspectorConfig {
- /// Returns a config with everything enabled.
- pub const fn all() -> Self {
- Self {
- record_steps: true,
- record_memory_snapshots: true,
- record_stack_snapshots: StackSnapshotType::Full,
- record_state_diff: false,
- exclude_precompile_calls: false,
- record_call_return_data: false,
- record_logs: true,
- }
- }
-
- /// Returns a config for parity style traces.
- ///
- /// This config does _not_ record opcode level traces and is suited for `trace_transaction`
- pub const fn default_parity() -> Self {
- Self {
- record_steps: false,
- record_memory_snapshots: false,
- record_stack_snapshots: StackSnapshotType::None,
- record_state_diff: false,
- exclude_precompile_calls: true,
- record_call_return_data: false,
- record_logs: false,
- }
- }
-
- /// Returns a config for geth style traces.
- ///
- /// This config does _not_ record opcode level traces and is suited for `debug_traceTransaction`
- pub const fn default_geth() -> Self {
- Self {
- record_steps: true,
- record_memory_snapshots: true,
- record_stack_snapshots: StackSnapshotType::Full,
- record_state_diff: true,
- exclude_precompile_calls: false,
- record_call_return_data: false,
- record_logs: false,
- }
- }
-
- /// Returns the [TracingInspectorConfig] depending on the enabled [TraceType]s
- ///
- /// Note: the parity statediffs can be populated entirely via the execution result, so we don't
- /// need statediff recording
- #[inline]
- pub fn from_parity_config(trace_types: &HashSet) -> Self {
- let needs_vm_trace = trace_types.contains(&TraceType::VmTrace);
- let snap_type =
- if needs_vm_trace { StackSnapshotType::Pushes } else { StackSnapshotType::None };
- TracingInspectorConfig::default_parity()
- .set_steps(needs_vm_trace)
- .set_stack_snapshots(snap_type)
- .set_memory_snapshots(needs_vm_trace)
- }
-
- /// Returns a config for geth style traces based on the given [GethDefaultTracingOptions].
- #[inline]
- pub fn from_geth_config(config: &GethDefaultTracingOptions) -> Self {
- Self {
- record_memory_snapshots: config.enable_memory.unwrap_or_default(),
- record_stack_snapshots: if config.disable_stack.unwrap_or_default() {
- StackSnapshotType::None
- } else {
- StackSnapshotType::Full
- },
- record_state_diff: !config.disable_storage.unwrap_or_default(),
- ..Self::default_geth()
- }
- }
-
- /// Configure whether calls to precompiles should be ignored.
- ///
- /// If set to `true`, calls to precompiles without value transfers will be ignored.
- pub fn set_exclude_precompile_calls(mut self, exclude_precompile_calls: bool) -> Self {
- self.exclude_precompile_calls = exclude_precompile_calls;
- self
- }
-
- /// Configure whether individual opcode level steps should be recorded
- pub fn set_steps(mut self, record_steps: bool) -> Self {
- self.record_steps = record_steps;
- self
- }
-
- /// Configure whether the tracer should record memory snapshots
- pub fn set_memory_snapshots(mut self, record_memory_snapshots: bool) -> Self {
- self.record_memory_snapshots = record_memory_snapshots;
- self
- }
-
- /// Configure how the tracer should record stack snapshots
- pub fn set_stack_snapshots(mut self, record_stack_snapshots: StackSnapshotType) -> Self {
- self.record_stack_snapshots = record_stack_snapshots;
- self
- }
-
- /// Sets state diff recording to true.
- pub fn with_state_diffs(self) -> Self {
- self.set_steps_and_state_diffs(true)
- }
-
- /// Configure whether the tracer should record state diffs
- pub fn set_state_diffs(mut self, record_state_diff: bool) -> Self {
- self.record_state_diff = record_state_diff;
- self
- }
-
- /// Configure whether the tracer should record steps and state diffs.
- ///
- /// This is a convenience method for setting both [TracingInspectorConfig::set_steps] and
- /// [TracingInspectorConfig::set_state_diffs] since tracking state diffs requires steps tracing.
- pub fn set_steps_and_state_diffs(mut self, steps_and_diffs: bool) -> Self {
- self.record_steps = steps_and_diffs;
- self.record_state_diff = steps_and_diffs;
- self
- }
-
- /// Configure whether the tracer should record logs
- pub fn set_record_logs(mut self, record_logs: bool) -> Self {
- self.record_logs = record_logs;
- self
- }
-}
-
-/// How much of the stack to record. Nothing, just the items pushed, or the full stack
-#[derive(Debug, Clone, Copy, Eq, PartialEq)]
-pub enum StackSnapshotType {
- /// Don't record stack snapshots
- None,
- /// Record only the items pushed to the stack
- Pushes,
- /// Record the full stack
- Full,
-}
-
-impl StackSnapshotType {
- /// Returns true if this is the [StackSnapshotType::Full] variant
- #[inline]
- pub fn is_full(self) -> bool {
- matches!(self, Self::Full)
- }
-
- /// Returns true if this is the [StackSnapshotType::Pushes] variant
- #[inline]
- pub fn is_pushes(self) -> bool {
- matches!(self, Self::Pushes)
- }
-}
-
-/// What kind of tracing style this is.
-///
-/// This affects things like error messages.
-#[derive(Debug, Clone, Copy, Eq, PartialEq)]
-pub(crate) enum TraceStyle {
- /// Parity style tracer
- Parity,
- /// Geth style tracer
- #[allow(dead_code)]
- Geth,
-}
-
-impl TraceStyle {
- /// Returns true if this is a parity style tracer.
- pub(crate) const fn is_parity(self) -> bool {
- matches!(self, Self::Parity)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_parity_config() {
- let mut s = HashSet::new();
- s.insert(TraceType::StateDiff);
- let config = TracingInspectorConfig::from_parity_config(&s);
- // not required
- assert!(!config.record_steps);
- assert!(!config.record_state_diff);
-
- let mut s = HashSet::new();
- s.insert(TraceType::VmTrace);
- let config = TracingInspectorConfig::from_parity_config(&s);
- assert!(config.record_steps);
- assert!(!config.record_state_diff);
-
- let mut s = HashSet::new();
- s.insert(TraceType::VmTrace);
- s.insert(TraceType::StateDiff);
- let config = TracingInspectorConfig::from_parity_config(&s);
- assert!(config.record_steps);
- // not required for StateDiff
- assert!(!config.record_state_diff);
- }
-}
diff --git a/crates/revm/revm-inspectors/src/tracing/fourbyte.rs b/crates/revm/revm-inspectors/src/tracing/fourbyte.rs
deleted file mode 100644
index 5abf378d5..000000000
--- a/crates/revm/revm-inspectors/src/tracing/fourbyte.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-//! Fourbyte tracing inspector
-//!
-//! Solidity contract functions are addressed using the first four byte of the Keccak-256 hash of
-//! their signature. Therefore when calling the function of a contract, the caller must send this
-//! function selector as well as the ABI-encoded arguments as call data.
-//!
-//! The 4byteTracer collects the function selectors of every function executed in the lifetime of a
-//! transaction, along with the size of the supplied call data. The result is a map of
-//! SELECTOR-CALLDATASIZE to number of occurrences entries, where the keys are SELECTOR-CALLDATASIZE
-//! and the values are number of occurrences of this key. For example:
-//!
-//! ```json
-//! {
-//! "0x27dc297e-128": 1,
-//! "0x38cc4831-0": 2,
-//! "0x524f3889-96": 1,
-//! "0xadf59f99-288": 1,
-//! "0xc281d19e-0": 1
-//! }
-//! ```
-//!
-//! See also
-
-use alloy_primitives::{hex, Bytes, Selector};
-use alloy_rpc_trace_types::geth::FourByteFrame;
-use revm::{
- interpreter::{CallInputs, Gas, InstructionResult},
- Database, EVMData, Inspector,
-};
-use std::collections::HashMap;
-
-/// Fourbyte tracing inspector that records all function selectors and their calldata sizes.
-#[derive(Debug, Clone, Default)]
-pub struct FourByteInspector {
- /// The map of SELECTOR to number of occurrences entries
- inner: HashMap<(Selector, usize), u64>,
-}
-
-impl FourByteInspector {
- /// Returns the map of SELECTOR to number of occurrences entries
- pub fn inner(&self) -> &HashMap<(Selector, usize), u64> {
- &self.inner
- }
-}
-
-impl Inspector for FourByteInspector
-where
- DB: Database,
-{
- fn call(
- &mut self,
- _data: &mut EVMData<'_, DB>,
- call: &mut CallInputs,
- ) -> (InstructionResult, Gas, Bytes) {
- if call.input.len() >= 4 {
- let selector = Selector::try_from(&call.input[..4]).expect("input is at least 4 bytes");
- let calldata_size = call.input[4..].len();
- *self.inner.entry((selector, calldata_size)).or_default() += 1;
- }
-
- (InstructionResult::Continue, Gas::new(0), Bytes::new())
- }
-}
-
-impl From for FourByteFrame {
- fn from(value: FourByteInspector) -> Self {
- FourByteFrame(
- value
- .inner
- .into_iter()
- .map(|((selector, calldata_size), count)| {
- let key = format!("0x{}-{}", hex::encode(&selector[..]), calldata_size);
- (key, count)
- })
- .collect(),
- )
- }
-}
diff --git a/crates/revm/revm-inspectors/src/tracing/js/bigint.js b/crates/revm/revm-inspectors/src/tracing/js/bigint.js
deleted file mode 100644
index d9a8411b2..000000000
--- a/crates/revm/revm-inspectors/src/tracing/js/bigint.js
+++ /dev/null
@@ -1 +0,0 @@
-var bigInt=function(undefined){"use strict";var BASE=1e7,LOG_BASE=7,MAX_INT=9007199254740992,MAX_INT_ARR=smallToArray(MAX_INT),LOG_MAX_INT=Math.log(MAX_INT);function Integer(v,radix){if(typeof v==="undefined")return Integer[0];if(typeof radix!=="undefined")return+radix===10?parseValue(v):parseBase(v,radix);return parseValue(v)}function BigInteger(value,sign){this.value=value;this.sign=sign;this.isSmall=false}BigInteger.prototype=Object.create(Integer.prototype);function SmallInteger(value){this.value=value;this.sign=value<0;this.isSmall=true}SmallInteger.prototype=Object.create(Integer.prototype);function isPrecise(n){return-MAX_INT0)return Math.floor(n);return Math.ceil(n)}function add(a,b){var l_a=a.length,l_b=b.length,r=new Array(l_a),carry=0,base=BASE,sum,i;for(i=0;i=base?1:0;r[i]=sum-carry*base}while(i0)r.push(carry);return r}function addAny(a,b){if(a.length>=b.length)return add(a,b);return add(b,a)}function addSmall(a,carry){var l=a.length,r=new Array(l),base=BASE,sum,i;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}BigInteger.prototype.add=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.subtract(n.negate())}var a=this.value,b=n.value;if(n.isSmall){return new BigInteger(addSmall(a,Math.abs(b)),this.sign)}return new BigInteger(addAny(a,b),this.sign)};BigInteger.prototype.plus=BigInteger.prototype.add;SmallInteger.prototype.add=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.subtract(n.negate())}var b=n.value;if(n.isSmall){if(isPrecise(a+b))return new SmallInteger(a+b);b=smallToArray(Math.abs(b))}return new BigInteger(addSmall(b,Math.abs(a)),a<0)};SmallInteger.prototype.plus=SmallInteger.prototype.add;function subtract(a,b){var a_l=a.length,b_l=b.length,r=new Array(a_l),borrow=0,base=BASE,i,difference;for(i=0;i=0){value=subtract(a,b)}else{value=subtract(b,a);sign=!sign}value=arrayToSmall(value);if(typeof value==="number"){if(sign)value=-value;return new SmallInteger(value)}return new BigInteger(value,sign)}function subtractSmall(a,b,sign){var l=a.length,r=new Array(l),carry=-b,base=BASE,i,difference;for(i=0;i=0)};SmallInteger.prototype.minus=SmallInteger.prototype.subtract;BigInteger.prototype.negate=function(){return new BigInteger(this.value,!this.sign)};SmallInteger.prototype.negate=function(){var sign=this.sign;var small=new SmallInteger(-this.value);small.sign=!sign;return small};BigInteger.prototype.abs=function(){return new BigInteger(this.value,false)};SmallInteger.prototype.abs=function(){return new SmallInteger(Math.abs(this.value))};function multiplyLong(a,b){var a_l=a.length,b_l=b.length,l=a_l+b_l,r=createArray(l),base=BASE,product,carry,i,a_i,b_j;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}function shiftLeft(x,n){var r=[];while(n-- >0)r.push(0);return r.concat(x)}function multiplyKaratsuba(x,y){var n=Math.max(x.length,y.length);if(n<=30)return multiplyLong(x,y);n=Math.ceil(n/2);var b=x.slice(n),a=x.slice(0,n),d=y.slice(n),c=y.slice(0,n);var ac=multiplyKaratsuba(a,c),bd=multiplyKaratsuba(b,d),abcd=multiplyKaratsuba(addAny(a,b),addAny(c,d));var product=addAny(addAny(ac,shiftLeft(subtract(subtract(abcd,ac),bd),n)),shiftLeft(bd,2*n));trim(product);return product}function useKaratsuba(l1,l2){return-.012*l1-.012*l2+15e-6*l1*l2>0}BigInteger.prototype.multiply=function(v){var n=parseValue(v),a=this.value,b=n.value,sign=this.sign!==n.sign,abs;if(n.isSmall){if(b===0)return Integer[0];if(b===1)return this;if(b===-1)return this.negate();abs=Math.abs(b);if(abs=0;shift--){quotientDigit=base-1;if(remainder[shift+b_l]!==divisorMostSignificantDigit){quotientDigit=Math.floor((remainder[shift+b_l]*base+remainder[shift+b_l-1])/divisorMostSignificantDigit)}carry=0;borrow=0;l=divisor.length;for(i=0;ib_l){highx=(highx+1)*base}guess=Math.ceil(highx/highy);do{check=multiplySmall(b,guess);if(compareAbs(check,part)<=0)break;guess--}while(guess);result.push(guess);part=subtract(part,check)}result.reverse();return[arrayToSmall(result),arrayToSmall(part)]}function divModSmall(value,lambda){var length=value.length,quotient=createArray(length),base=BASE,i,q,remainder,divisor;remainder=0;for(i=length-1;i>=0;--i){divisor=remainder*base+value[i];q=truncate(divisor/lambda);remainder=divisor-q*lambda;quotient[i]=q|0}return[quotient,remainder|0]}function divModAny(self,v){var value,n=parseValue(v);var a=self.value,b=n.value;var quotient;if(b===0)throw new Error("Cannot divide by zero");if(self.isSmall){if(n.isSmall){return[new SmallInteger(truncate(a/b)),new SmallInteger(a%b)]}return[Integer[0],self]}if(n.isSmall){if(b===1)return[self,Integer[0]];if(b==-1)return[self.negate(),Integer[0]];var abs=Math.abs(b);if(absb.length?1:-1}for(var i=a.length-1;i>=0;i--){if(a[i]!==b[i])return a[i]>b[i]?1:-1}return 0}BigInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall)return 1;return compareAbs(a,b)};SmallInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=Math.abs(this.value),b=n.value;if(n.isSmall){b=Math.abs(b);return a===b?0:a>b?1:-1}return-1};BigInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(this.sign!==n.sign){return n.sign?1:-1}if(n.isSmall){return this.sign?-1:1}return compareAbs(a,b)*(this.sign?-1:1)};BigInteger.prototype.compareTo=BigInteger.prototype.compare;SmallInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall){return a==b?0:a>b?1:-1}if(a<0!==n.sign){return a<0?-1:1}return a<0?1:-1};SmallInteger.prototype.compareTo=SmallInteger.prototype.compare;BigInteger.prototype.equals=function(v){return this.compare(v)===0};SmallInteger.prototype.eq=SmallInteger.prototype.equals=BigInteger.prototype.eq=BigInteger.prototype.equals;BigInteger.prototype.notEquals=function(v){return this.compare(v)!==0};SmallInteger.prototype.neq=SmallInteger.prototype.notEquals=BigInteger.prototype.neq=BigInteger.prototype.notEquals;BigInteger.prototype.greater=function(v){return this.compare(v)>0};SmallInteger.prototype.gt=SmallInteger.prototype.greater=BigInteger.prototype.gt=BigInteger.prototype.greater;BigInteger.prototype.lesser=function(v){return this.compare(v)<0};SmallInteger.prototype.lt=SmallInteger.prototype.lesser=BigInteger.prototype.lt=BigInteger.prototype.lesser;BigInteger.prototype.greaterOrEquals=function(v){return this.compare(v)>=0};SmallInteger.prototype.geq=SmallInteger.prototype.greaterOrEquals=BigInteger.prototype.geq=BigInteger.prototype.greaterOrEquals;BigInteger.prototype.lesserOrEquals=function(v){return this.compare(v)<=0};SmallInteger.prototype.leq=SmallInteger.prototype.lesserOrEquals=BigInteger.prototype.leq=BigInteger.prototype.lesserOrEquals;BigInteger.prototype.isEven=function(){return(this.value[0]&1)===0};SmallInteger.prototype.isEven=function(){return(this.value&1)===0};BigInteger.prototype.isOdd=function(){return(this.value[0]&1)===1};SmallInteger.prototype.isOdd=function(){return(this.value&1)===1};BigInteger.prototype.isPositive=function(){return!this.sign};SmallInteger.prototype.isPositive=function(){return this.value>0};BigInteger.prototype.isNegative=function(){return this.sign};SmallInteger.prototype.isNegative=function(){return this.value<0};BigInteger.prototype.isUnit=function(){return false};SmallInteger.prototype.isUnit=function(){return Math.abs(this.value)===1};BigInteger.prototype.isZero=function(){return false};SmallInteger.prototype.isZero=function(){return this.value===0};BigInteger.prototype.isDivisibleBy=function(v){var n=parseValue(v);var value=n.value;if(value===0)return false;if(value===1)return true;if(value===2)return this.isEven();return this.mod(n).equals(Integer[0])};SmallInteger.prototype.isDivisibleBy=BigInteger.prototype.isDivisibleBy;function isBasicPrime(v){var n=v.abs();if(n.isUnit())return false;if(n.equals(2)||n.equals(3)||n.equals(5))return true;if(n.isEven()||n.isDivisibleBy(3)||n.isDivisibleBy(5))return false;if(n.lesser(25))return true}BigInteger.prototype.isPrime=function(){var isPrime=isBasicPrime(this);if(isPrime!==undefined)return isPrime;var n=this.abs(),nPrev=n.prev();var a=[2,3,5,7,11,13,17,19],b=nPrev,d,t,i,x;while(b.isEven())b=b.divide(2);for(i=0;i-MAX_INT)return new SmallInteger(value-1);return new BigInteger(MAX_INT_ARR,true)};var powersOfTwo=[1];while(2*powersOfTwo[powersOfTwo.length-1]<=BASE)powersOfTwo.push(2*powersOfTwo[powersOfTwo.length-1]);var powers2Length=powersOfTwo.length,highestPower2=powersOfTwo[powers2Length-1];function shift_isSmall(n){return(typeof n==="number"||typeof n==="string")&&+Math.abs(n)<=BASE||n instanceof BigInteger&&n.value.length<=1}BigInteger.prototype.shiftLeft=function(n){if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftRight(-n);var result=this;while(n>=powers2Length){result=result.multiply(highestPower2);n-=powers2Length-1}return result.multiply(powersOfTwo[n])};SmallInteger.prototype.shiftLeft=BigInteger.prototype.shiftLeft;BigInteger.prototype.shiftRight=function(n){var remQuo;if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftLeft(-n);var result=this;while(n>=powers2Length){if(result.isZero())return result;remQuo=divModAny(result,highestPower2);result=remQuo[1].isNegative()?remQuo[0].prev():remQuo[0];n-=powers2Length-1}remQuo=divModAny(result,powersOfTwo[n]);return remQuo[1].isNegative()?remQuo[0].prev():remQuo[0]};SmallInteger.prototype.shiftRight=BigInteger.prototype.shiftRight;function bitwise(x,y,fn){y=parseValue(y);var xSign=x.isNegative(),ySign=y.isNegative();var xRem=xSign?x.not():x,yRem=ySign?y.not():y;var xDigit=0,yDigit=0;var xDivMod=null,yDivMod=null;var result=[];while(!xRem.isZero()||!yRem.isZero()){xDivMod=divModAny(xRem,highestPower2);xDigit=xDivMod[1].toJSNumber();if(xSign){xDigit=highestPower2-1-xDigit}yDivMod=divModAny(yRem,highestPower2);yDigit=yDivMod[1].toJSNumber();if(ySign){yDigit=highestPower2-1-yDigit}xRem=xDivMod[0];yRem=yDivMod[0];result.push(fn(xDigit,yDigit))}var sum=fn(xSign?1:0,ySign?1:0)!==0?bigInt(-1):bigInt(0);for(var i=result.length-1;i>=0;i-=1){sum=sum.multiply(highestPower2).add(bigInt(result[i]))}return sum}BigInteger.prototype.not=function(){return this.negate().prev()};SmallInteger.prototype.not=BigInteger.prototype.not;BigInteger.prototype.and=function(n){return bitwise(this,n,function(a,b){return a&b})};SmallInteger.prototype.and=BigInteger.prototype.and;BigInteger.prototype.or=function(n){return bitwise(this,n,function(a,b){return a|b})};SmallInteger.prototype.or=BigInteger.prototype.or;BigInteger.prototype.xor=function(n){return bitwise(this,n,function(a,b){return a^b})};SmallInteger.prototype.xor=BigInteger.prototype.xor;var LOBMASK_I=1<<30,LOBMASK_BI=(BASE&-BASE)*(BASE&-BASE)|LOBMASK_I;function roughLOB(n){var v=n.value,x=typeof v==="number"?v|LOBMASK_I:v[0]+v[1]*BASE|LOBMASK_BI;return x&-x}function max(a,b){a=parseValue(a);b=parseValue(b);return a.greater(b)?a:b}function min(a,b){a=parseValue(a);b=parseValue(b);return a.lesser(b)?a:b}function gcd(a,b){a=parseValue(a).abs();b=parseValue(b).abs();if(a.equals(b))return a;if(a.isZero())return b;if(b.isZero())return a;var c=Integer[1],d,t;while(a.isEven()&&b.isEven()){d=Math.min(roughLOB(a),roughLOB(b));a=a.divide(d);b=b.divide(d);c=c.multiply(d)}while(a.isEven()){a=a.divide(roughLOB(a))}do{while(b.isEven()){b=b.divide(roughLOB(b))}if(a.greater(b)){t=b;b=a;a=t}b=b.subtract(a)}while(!b.isZero());return c.isUnit()?a:a.multiply(c)}function lcm(a,b){a=parseValue(a).abs();b=parseValue(b).abs();return a.divide(gcd(a,b)).multiply(b)}function randBetween(a,b){a=parseValue(a);b=parseValue(b);var low=min(a,b),high=max(a,b);var range=high.subtract(low).add(1);if(range.isSmall)return low.add(Math.floor(Math.random()*range));var length=range.value.length-1;var result=[],restricted=true;for(var i=length;i>=0;i--){var top=restricted?range.value[i]:BASE;var digit=truncate(Math.random()*top);result.unshift(digit);if(digit=absBase){if(c==="1"&&absBase===1)continue;throw new Error(c+" is not a valid digit in base "+base+".")}else if(c.charCodeAt(0)-87>=absBase){throw new Error(c+" is not a valid digit in base "+base+".")}}}if(2<=base&&base<=36){if(length<=LOG_MAX_INT/Math.log(base)){var result=parseInt(text,base);if(isNaN(result)){throw new Error(c+" is not a valid digit in base "+base+".")}return new SmallInteger(parseInt(text,base))}}base=parseValue(base);var digits=[];var isNegative=text[0]==="-";for(i=isNegative?1:0;i");digits.push(parseValue(text.slice(start+1,i)))}else throw new Error(c+" is not a valid character")}return parseBaseFromArray(digits,base,isNegative)};function parseBaseFromArray(digits,base,isNegative){var val=Integer[0],pow=Integer[1],i;for(i=digits.length-1;i>=0;i--){val=val.add(digits[i].times(pow));pow=pow.times(base)}return isNegative?val.negate():val}function stringify(digit){var v=digit.value;if(typeof v==="number")v=[v];if(v.length===1&&v[0]<=35){return"0123456789abcdefghijklmnopqrstuvwxyz".charAt(v[0])}return"<"+v+">"}function toBase(n,base){base=bigInt(base);if(base.isZero()){if(n.isZero())return"0";throw new Error("Cannot convert nonzero numbers to base 0.")}if(base.equals(-1)){if(n.isZero())return"0";if(n.isNegative())return new Array(1-n).join("10");return"1"+new Array(+n).join("01")}var minusSign="";if(n.isNegative()&&base.isPositive()){minusSign="-";n=n.abs()}if(base.equals(1)){if(n.isZero())return"0";return minusSign+new Array(+n+1).join(1)}var out=[];var left=n,divmod;while(left.isNegative()||left.compareAbs(base)>=0){divmod=left.divmod(base);left=divmod.quotient;var digit=divmod.remainder;if(digit.isNegative()){digit=base.minus(digit).abs();left=left.next()}out.push(stringify(digit))}out.push(stringify(left));return minusSign+out.reverse().join("")}BigInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!==10)return toBase(this,radix);var v=this.value,l=v.length,str=String(v[--l]),zeros="0000000",digit;while(--l>=0){digit=String(v[l]);str+=zeros.slice(digit.length)+digit}var sign=this.sign?"-":"";return sign+str};SmallInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!=10)return toBase(this,radix);return String(this.value)};BigInteger.prototype.toJSON=SmallInteger.prototype.toJSON=function(){return this.toString()};BigInteger.prototype.valueOf=function(){return+this.toString()};BigInteger.prototype.toJSNumber=BigInteger.prototype.valueOf;SmallInteger.prototype.valueOf=function(){return this.value};SmallInteger.prototype.toJSNumber=SmallInteger.prototype.valueOf;function parseStringValue(v){if(isPrecise(+v)){var x=+v;if(x===truncate(x))return new SmallInteger(x);throw"Invalid integer: "+v}var sign=v[0]==="-";if(sign)v=v.slice(1);var split=v.split(/e/i);if(split.length>2)throw new Error("Invalid integer: "+split.join("e"));if(split.length===2){var exp=split[1];if(exp[0]==="+")exp=exp.slice(1);exp=+exp;if(exp!==truncate(exp)||!isPrecise(exp))throw new Error("Invalid integer: "+exp+" is not a valid exponent.");var text=split[0];var decimalPlace=text.indexOf(".");if(decimalPlace>=0){exp-=text.length-decimalPlace-1;text=text.slice(0,decimalPlace)+text.slice(decimalPlace+1)}if(exp<0)throw new Error("Cannot include negative exponent part for integers");text+=new Array(exp+1).join("0");v=text}var isValid=/^([0-9][0-9]*)$/.test(v);if(!isValid)throw new Error("Invalid integer: "+v);var r=[],max=v.length,l=LOG_BASE,min=max-l;while(max>0){r.push(+v.slice(min,max));min-=l;if(min<0)min=0;max-=l}trim(r);return new BigInteger(r,sign)}function parseNumberValue(v){if(isPrecise(v)){if(v!==truncate(v))throw new Error(v+" is not an integer.");return new SmallInteger(v)}return parseStringValue(v.toString())}function parseValue(v){if(typeof v==="number"){return parseNumberValue(v)}if(typeof v==="string"){return parseStringValue(v)}return v}for(var i=0;i<1e3;i++){Integer[i]=new SmallInteger(i);if(i>0)Integer[-i]=new SmallInteger(-i)}Integer.one=Integer[1];Integer.zero=Integer[0];Integer.minusOne=Integer[-1];Integer.max=max;Integer.min=min;Integer.gcd=gcd;Integer.lcm=lcm;Integer.isInstance=function(x){return x instanceof BigInteger||x instanceof SmallInteger};Integer.randBetween=randBetween;Integer.fromArray=function(digits,base,isNegative){return parseBaseFromArray(digits.map(parseValue),parseValue(base||10),isNegative)};return Integer}();if(typeof module!=="undefined"&&module.hasOwnProperty("exports")){module.exports=bigInt}if(typeof define==="function"&&define.amd){define("big-integer",[],function(){return bigInt})}; bigInt
\ No newline at end of file
diff --git a/crates/revm/revm-inspectors/src/tracing/js/bindings.rs b/crates/revm/revm-inspectors/src/tracing/js/bindings.rs
deleted file mode 100644
index 438571a5b..000000000
--- a/crates/revm/revm-inspectors/src/tracing/js/bindings.rs
+++ /dev/null
@@ -1,936 +0,0 @@
-//! Type bindings for js tracing inspector
-
-use crate::tracing::{
- js::{
- builtins::{
- address_to_buf, bytes_to_address, bytes_to_hash, from_buf, to_bigint, to_buf,
- to_buf_value,
- },
- JsDbRequest,
- },
- types::CallKind,
-};
-use alloy_primitives::{Address, Bytes, B256, U256};
-use boa_engine::{
- native_function::NativeFunction,
- object::{builtins::JsArrayBuffer, FunctionObjectBuilder},
- Context, JsArgs, JsError, JsNativeError, JsObject, JsResult, JsValue,
-};
-use boa_gc::{empty_trace, Finalize, Trace};
-use revm::{
- interpreter::{
- opcode::{PUSH0, PUSH32},
- OpCode, SharedMemory, Stack,
- },
- primitives::{AccountInfo, State, KECCAK_EMPTY},
-};
-use std::{cell::RefCell, rc::Rc, sync::mpsc::channel};
-use tokio::sync::mpsc;
-
-/// A macro that creates a native function that returns via [JsValue::from]
-macro_rules! js_value_getter {
- ($value:ident, $ctx:ident) => {
- FunctionObjectBuilder::new(
- $ctx,
- NativeFunction::from_copy_closure(move |_this, _args, _ctx| Ok(JsValue::from($value))),
- )
- .length(0)
- .build()
- };
-}
-
-/// A macro that creates a native function that returns a captured JsValue
-macro_rules! js_value_capture_getter {
- ($value:ident, $ctx:ident) => {
- FunctionObjectBuilder::new(
- $ctx,
- NativeFunction::from_copy_closure_with_captures(
- move |_this, _args, input, _ctx| Ok(JsValue::from(input.clone())),
- $value,
- ),
- )
- .length(0)
- .build()
- };
-}
-
-/// A reference to a value that can be garbagae collected, but will not give access to the value if
-/// it has been dropped.
-///
-/// This is used to allow the JS tracer functions to access values at a certain point during
-/// inspection by ref without having to clone them and capture them in the js object.
-///
-/// JS tracer functions get access to evm internals via objects or function arguments, for example
-/// `function step(log,evm)` where log has an object `stack` that has a function `peek(number)` that
-/// returns a value from the stack.
-///
-/// These functions could get garbage collected, however the data accessed by the function is
-/// supposed to be ephemeral and only valid for the duration of the function call.
-///
-/// This type supports garbage collection of (rust) references and prevents access to the value if
-/// it has been dropped.
-#[derive(Debug, Clone)]
-pub(crate) struct GuardedNullableGcRef {
- /// The lifetime is a lie to make it possible to use a reference in boa which requires 'static
- inner: Rc>>,
-}
-
-impl GuardedNullableGcRef {
- /// Creates a garbage collectible reference to the given reference.
- ///
- /// SAFETY; the caller must ensure that the guard is dropped before the value is dropped.
- pub(crate) fn new(val: &Val) -> (Self, RefGuard<'_, Val>) {
- let inner = Rc::new(RefCell::new(Some(val)));
- let guard = RefGuard { inner: Rc::clone(&inner) };
-
- // SAFETY: guard enforces that the value is removed from the refcell before it is dropped
- let this = Self { inner: unsafe { std::mem::transmute(inner) } };
-
- (this, guard)
- }
-
- /// Executes the given closure with a reference to the inner value if it is still present.
- pub(crate) fn with_inner(&self, f: F) -> Option
- where
- F: FnOnce(&Val) -> R,
- {
- self.inner.borrow().map(f)
- }
-}
-
-impl Finalize for GuardedNullableGcRef {}
-
-unsafe impl Trace for GuardedNullableGcRef {
- empty_trace!();
-}
-
-/// Guard the inner references, once this value is dropped the inner reference is also removed.
-///
-/// This type guarantees that it never outlives the wrapped reference.
-#[derive(Debug)]
-pub(crate) struct RefGuard<'a, Val> {
- inner: Rc>>,
-}
-
-impl<'a, Val> Drop for RefGuard<'a, Val> {
- fn drop(&mut self) {
- self.inner.borrow_mut().take();
- }
-}
-
-/// The Log object that is passed to the javascript inspector.
-#[derive(Debug)]
-pub(crate) struct StepLog {
- /// Stack before step execution
- pub(crate) stack: StackRef,
- /// Opcode to be executed
- pub(crate) op: OpObj,
- /// All allocated memory in a step
- pub(crate) memory: MemoryRef,
- /// Program counter before step execution
- pub(crate) pc: u64,
- /// Remaining gas before step execution
- pub(crate) gas_remaining: u64,
- /// Gas cost of step execution
- pub(crate) cost: u64,
- /// Call depth
- pub(crate) depth: u64,
- /// Gas refund counter before step execution
- pub(crate) refund: u64,
- /// returns information about the error if one occurred, otherwise returns undefined
- pub(crate) error: Option,
- /// The contract object available to the js inspector
- pub(crate) contract: Contract,
-}
-
-impl StepLog {
- /// Converts the contract object into a js object
- ///
- /// Caution: this expects a global property `bigint` to be present.
- pub(crate) fn into_js_object(self, context: &mut Context<'_>) -> JsResult {
- let Self {
- stack,
- op,
- memory,
- pc,
- gas_remaining: gas,
- cost,
- depth,
- refund,
- error,
- contract,
- } = self;
- let obj = JsObject::default();
-
- // fields
- let op = op.into_js_object(context)?;
- let memory = memory.into_js_object(context)?;
- let stack = stack.into_js_object(context)?;
- let contract = contract.into_js_object(context)?;
-
- obj.set("op", op, false, context)?;
- obj.set("memory", memory, false, context)?;
- obj.set("stack", stack, false, context)?;
- obj.set("contract", contract, false, context)?;
-
- // methods
- let error =
- if let Some(error) = error { JsValue::from(error) } else { JsValue::undefined() };
- let get_error = js_value_capture_getter!(error, context);
- let get_pc = js_value_getter!(pc, context);
- let get_gas = js_value_getter!(gas, context);
- let get_cost = js_value_getter!(cost, context);
- let get_refund = js_value_getter!(refund, context);
- let get_depth = js_value_getter!(depth, context);
-
- obj.set("getPc", get_pc, false, context)?;
- obj.set("getError", get_error, false, context)?;
- obj.set("getGas", get_gas, false, context)?;
- obj.set("getCost", get_cost, false, context)?;
- obj.set("getDepth", get_depth, false, context)?;
- obj.set("getRefund", get_refund, false, context)?;
-
- Ok(obj)
- }
-}
-
-/// Represents the memory object
-#[derive(Debug, Clone)]
-pub(crate) struct MemoryRef(pub(crate) GuardedNullableGcRef);
-
-impl MemoryRef {
- /// Creates a new stack reference
- pub(crate) fn new(mem: &SharedMemory) -> (Self, RefGuard<'_, SharedMemory>) {
- let (inner, guard) = GuardedNullableGcRef::new(mem);
- (MemoryRef(inner), guard)
- }
-
- fn len(&self) -> usize {
- self.0.with_inner(|mem| mem.len()).unwrap_or_default()
- }
-
- pub(crate) fn into_js_object(self, context: &mut Context<'_>) -> JsResult {
- let obj = JsObject::default();
- let len = self.len();
-
- let length = FunctionObjectBuilder::new(
- context,
- NativeFunction::from_copy_closure(move |_this, _args, _ctx| {
- Ok(JsValue::from(len as u64))
- }),
- )
- .length(0)
- .build();
-
- // slice returns the requested range of memory as a byte slice.
- let slice = FunctionObjectBuilder::new(
- context,
- NativeFunction::from_copy_closure_with_captures(
- move |_this, args, memory, ctx| {
- let start = args.get_or_undefined(0).to_number(ctx)?;
- let end = args.get_or_undefined(1).to_number(ctx)?;
- if end < start || start < 0. || (end as usize) < memory.len() {
- return Err(JsError::from_native(JsNativeError::typ().with_message(
- format!(
- "tracer accessed out of bound memory: offset {start}, end {end}"
- ),
- )))
- }
- let start = start as usize;
- let end = end as usize;
- let size = end - start;
- let slice = memory
- .0
- .with_inner(|mem| mem.slice(start, size).to_vec())
- .unwrap_or_default();
-
- to_buf_value(slice, ctx)
- },
- self.clone(),
- ),
- )
- .length(2)
- .build();
-
- let get_uint = FunctionObjectBuilder::new(
- context,
- NativeFunction::from_copy_closure_with_captures(
- move |_this, args, memory, ctx| {
- let offset_f64 = args.get_or_undefined(0).to_number(ctx)?;
- let len = memory.len();
- let offset = offset_f64 as usize;
- if len < offset+32 || offset_f64 < 0. {
- return Err(JsError::from_native(
- JsNativeError::typ().with_message(format!("tracer accessed out of bound memory: available {len}, offset {offset}, size 32"))
- ));
- }
- let slice = memory.0.with_inner(|mem| mem.slice(offset, 32).to_vec()).unwrap_or_default();
- to_buf_value(slice, ctx)
- },
- self
- ),
- )
- .length(1)
- .build();
-
- obj.set("slice", slice, false, context)?;
- obj.set("getUint", get_uint, false, context)?;
- obj.set("length", length, false, context)?;
- Ok(obj)
- }
-}
-
-impl Finalize for MemoryRef {}
-
-unsafe impl Trace for MemoryRef {
- empty_trace!();
-}
-
-/// Represents the state object
-#[derive(Debug, Clone)]
-pub(crate) struct StateRef(pub(crate) GuardedNullableGcRef);
-
-impl StateRef {
- /// Creates a new stack reference
- pub(crate) fn new(state: &State) -> (Self, RefGuard<'_, State>) {
- let (inner, guard) = GuardedNullableGcRef::new(state);
- (StateRef(inner), guard)
- }
-
- fn get_account(&self, address: &Address) -> Option {
- self.0.with_inner(|state| state.get(address).map(|acc| acc.info.clone()))?
- }
-}
-
-impl Finalize for StateRef {}
-
-unsafe impl Trace for StateRef {
- empty_trace!();
-}
-
-/// Represents the opcode object
-#[derive(Debug)]
-pub(crate) struct OpObj(pub(crate) u8);
-
-impl OpObj {
- pub(crate) fn into_js_object(self, context: &mut Context<'_>) -> JsResult {
- let obj = JsObject::default();
- let value = self.0;
- let is_push = (PUSH0..=PUSH32).contains(&value);
-
- let to_number = FunctionObjectBuilder::new(
- context,
- NativeFunction::from_copy_closure(move |_this, _args, _ctx| Ok(JsValue::from(value))),
- )
- .length(0)
- .build();
-
- let is_push = FunctionObjectBuilder::new(
- context,
- NativeFunction::from_copy_closure(move |_this, _args, _ctx| Ok(JsValue::from(is_push))),
- )
- .length(0)
- .build();
-
- let to_string = FunctionObjectBuilder::new(
- context,
- NativeFunction::from_copy_closure(move |_this, _args, _ctx| {
- let op = OpCode::new(value)
- .or_else(|| {
- // if the opcode is invalid, we'll use the invalid opcode to represent it
- // because this is invoked before the opcode is
- // executed, the evm will eventually return a `Halt`
- // with invalid/unknown opcode as result
- let invalid_opcode = 0xfe;
- OpCode::new(invalid_opcode)
- })
- .expect("is valid opcode;");
- let s = op.to_string();
- Ok(JsValue::from(s))
- }),
- )
- .length(0)
- .build();
-
- obj.set("toNumber", to_number, false, context)?;
- obj.set("toString", to_string, false, context)?;
- obj.set("isPush", is_push, false, context)?;
- Ok(obj)
- }
-}
-
-impl From for OpObj {
- fn from(op: u8) -> Self {
- Self(op)
- }
-}
-
-/// Represents the stack object
-#[derive(Debug)]
-pub(crate) struct StackRef(pub(crate) GuardedNullableGcRef);
-
-impl StackRef {
- /// Creates a new stack reference
- pub(crate) fn new(stack: &Stack) -> (Self, RefGuard<'_, Stack>) {
- let (inner, guard) = GuardedNullableGcRef::new(stack);
- (StackRef(inner), guard)
- }
-
- fn peek(&self, idx: usize, ctx: &mut Context<'_>) -> JsResult {
- self.0
- .with_inner(|stack| {
- let value = stack.peek(idx).map_err(|_| {
- JsError::from_native(JsNativeError::typ().with_message(format!(
- "tracer accessed out of bound stack: size {}, index {}",
- stack.len(),
- idx
- )))
- })?;
- to_bigint(value, ctx)
- })
- .ok_or_else(|| {
- JsError::from_native(JsNativeError::typ().with_message(format!(
- "tracer accessed out of bound stack: size 0, index {}",
- idx
- )))
- })?
- }
-
- pub(crate) fn into_js_object(self, context: &mut Context<'_>) -> JsResult {
- let obj = JsObject::default();
- let len = self.0.with_inner(|stack| stack.len()).unwrap_or_default();
- let length = FunctionObjectBuilder::new(
- context,
- NativeFunction::from_copy_closure(move |_this, _args, _ctx| Ok(JsValue::from(len))),
- )
- .length(0)
- .build();
-
- // peek returns the nth-from-the-top element of the stack.
- let peek = FunctionObjectBuilder::new(
- context,
- NativeFunction::from_copy_closure_with_captures(
- move |_this, args, stack, ctx| {
- let idx_f64 = args.get_or_undefined(0).to_number(ctx)?;
- let idx = idx_f64 as usize;
- if len <= idx || idx_f64 < 0. {
- return Err(JsError::from_native(JsNativeError::typ().with_message(
- format!(
- "tracer accessed out of bound stack: size {len}, index {idx_f64}"
- ),
- )))
- }
- stack.peek(idx, ctx)
- },
- self,
- ),
- )
- .length(1)
- .build();
-
- obj.set("length", length, false, context)?;
- obj.set("peek", peek, false, context)?;
- Ok(obj)
- }
-}
-
-impl Finalize for StackRef {}
-
-unsafe impl Trace for StackRef {
- empty_trace!();
-}
-
-/// Represents the contract object
-#[derive(Debug, Clone, Default)]
-pub(crate) struct Contract {
- pub(crate) caller: Address,
- pub(crate) contract: Address,
- pub(crate) value: U256,
- pub(crate) input: Bytes,
-}
-
-impl Contract {
- /// Converts the contract object into a js object
- ///
- /// Caution: this expects a global property `bigint` to be present.
- pub(crate) fn into_js_object(self, context: &mut Context<'_>) -> JsResult {
- let Contract { caller, contract, value, input } = self;
- let obj = JsObject::default();
-
- let get_caller = FunctionObjectBuilder::new(
- context,
- NativeFunction::from_copy_closure(move |_this, _args, ctx| {
- to_buf_value(caller.as_slice().to_vec(), ctx)
- }),
- )
- .length(0)
- .build();
-
- let get_address = FunctionObjectBuilder::new(
- context,
- NativeFunction::from_copy_closure(move |_this, _args, ctx| {
- to_buf_value(contract.as_slice().to_vec(), ctx)
- }),
- )
- .length(0)
- .build();
-
- let get_value = FunctionObjectBuilder::new(
- context,
- NativeFunction::from_copy_closure(move |_this, _args, ctx| to_bigint(value, ctx)),
- )
- .length(0)
- .build();
-
- let input = to_buf_value(input.to_vec(), context)?;
- let get_input = FunctionObjectBuilder::new(
- context,
- NativeFunction::from_copy_closure_with_captures(
- move |_this, _args, input, _ctx| Ok(input.clone()),
- input,
- ),
- )
- .length(0)
- .build();
-
- obj.set("getCaller", get_caller, false, context)?;
- obj.set("getAddress", get_address, false, context)?;
- obj.set("getValue", get_value, false, context)?;
- obj.set("getInput", get_input, false, context)?;
-
- Ok(obj)
- }
-}
-
-/// Represents the call frame object for exit functions
-pub(crate) struct FrameResult {
- pub(crate) gas_used: u64,
- pub(crate) output: Bytes,
- pub(crate) error: Option,
-}
-
-impl FrameResult {
- pub(crate) fn into_js_object(self, ctx: &mut Context<'_>) -> JsResult {
- let Self { gas_used, output, error } = self;
- let obj = JsObject::default();
-
- let output = to_buf_value(output.to_vec(), ctx)?;
- let get_output = FunctionObjectBuilder::new(
- ctx,
- NativeFunction::from_copy_closure_with_captures(
- move |_this, _args, output, _ctx| Ok(output.clone()),
- output,
- ),
- )
- .length(0)
- .build();
-
- let error = error.map(JsValue::from).unwrap_or_default();
- let get_error = js_value_capture_getter!(error, ctx);
- let get_gas_used = js_value_getter!(gas_used, ctx);
-
- obj.set("getGasUsed", get_gas_used, false, ctx)?;
- obj.set("getOutput", get_output, false, ctx)?;
- obj.set("getError", get_error, false, ctx)?;
-
- Ok(obj)
- }
-}
-
-/// Represents the call frame object for enter functions
-pub(crate) struct CallFrame {
- pub(crate) contract: Contract,
- pub(crate) kind: CallKind,
- pub(crate) gas: u64,
-}
-
-impl CallFrame {
- pub(crate) fn into_js_object(self, ctx: &mut Context<'_>) -> JsResult {
- let CallFrame { contract: Contract { caller, contract, value, input }, kind, gas } = self;
- let obj = JsObject::default();
-
- let get_from = FunctionObjectBuilder::new(
- ctx,
- NativeFunction::from_copy_closure(move |_this, _args, ctx| {
- to_buf_value(caller.as_slice().to_vec(), ctx)
- }),
- )
- .length(0)
- .build();
-
- let get_to = FunctionObjectBuilder::new(
- ctx,
- NativeFunction::from_copy_closure(move |_this, _args, ctx| {
- to_buf_value(contract.as_slice().to_vec(), ctx)
- }),
- )
- .length(0)
- .build();
-
- let get_value = FunctionObjectBuilder::new(
- ctx,
- NativeFunction::from_copy_closure(move |_this, _args, ctx| to_bigint(value, ctx)),
- )
- .length(0)
- .build();
-
- let input = to_buf_value(input.to_vec(), ctx)?;
- let get_input = FunctionObjectBuilder::new(
- ctx,
- NativeFunction::from_copy_closure_with_captures(
- move |_this, _args, input, _ctx| Ok(input.clone()),
- input,
- ),
- )
- .length(0)
- .build();
-
- let get_gas = js_value_getter!(gas, ctx);
- let ty = kind.to_string();
- let get_type = js_value_capture_getter!(ty, ctx);
-
- obj.set("getFrom", get_from, false, ctx)?;
- obj.set("getTo", get_to, false, ctx)?;
- obj.set("getValue", get_value, false, ctx)?;
- obj.set("getInput", get_input, false, ctx)?;
- obj.set("getGas", get_gas, false, ctx)?;
- obj.set("getType", get_type, false, ctx)?;
-
- Ok(obj)
- }
-}
-
-/// The `ctx` object that represents the context in which the transaction is executed.
-pub(crate) struct EvmContext {
- /// String, one of the two values CALL and CREATE
- pub(crate) r#type: String,
- /// Sender of the transaction
- pub(crate) from: Address,
- /// Target of the transaction
- pub(crate) to: Option,
- pub(crate) input: Bytes,
- /// Gas limit
- pub(crate) gas: u64,
- /// Number, amount of gas used in executing the transaction (excludes txdata costs)
- pub(crate) gas_used: u64,
- /// Number, gas price configured in the transaction being executed
- pub(crate) gas_price: u64,
- /// Number, intrinsic gas for the transaction being executed
- pub(crate) intrinsic_gas: u64,
- /// big.int Amount to be transferred in wei
- pub(crate) value: U256,
- /// Number, block number
- pub(crate) block: u64,
- pub(crate) output: Bytes,
- /// Number, block number
- pub(crate) time: String,
- pub(crate) block_hash: Option,
- pub(crate) tx_index: Option,
- pub(crate) tx_hash: Option,
-}
-
-impl EvmContext {
- pub(crate) fn into_js_object(self, ctx: &mut Context<'_>) -> JsResult {
- let Self {
- r#type,
- from,
- to,
- input,
- gas,
- gas_used,
- gas_price,
- intrinsic_gas,
- value,
- block,
- output,
- time,
- block_hash,
- tx_index,
- tx_hash,
- } = self;
- let obj = JsObject::default();
-
- // add properties
-
- obj.set("type", r#type, false, ctx)?;
- obj.set("from", address_to_buf(from, ctx)?, false, ctx)?;
- if let Some(to) = to {
- obj.set("to", address_to_buf(to, ctx)?, false, ctx)?;
- } else {
- obj.set("to", JsValue::null(), false, ctx)?;
- }
-
- obj.set("input", to_buf(input.to_vec(), ctx)?, false, ctx)?;
- obj.set("gas", gas, false, ctx)?;
- obj.set("gasUsed", gas_used, false, ctx)?;
- obj.set("gasPrice", gas_price, false, ctx)?;
- obj.set("intrinsicGas", intrinsic_gas, false, ctx)?;
- obj.set("value", to_bigint(value, ctx)?, false, ctx)?;
- obj.set("block", block, false, ctx)?;
- obj.set("output", to_buf(output.to_vec(), ctx)?, false, ctx)?;
- obj.set("time", time, false, ctx)?;
- if let Some(block_hash) = block_hash {
- obj.set("blockHash", to_buf(block_hash.as_slice().to_vec(), ctx)?, false, ctx)?;
- }
- if let Some(tx_index) = tx_index {
- obj.set("txIndex", tx_index as u64, false, ctx)?;
- }
- if let Some(tx_hash) = tx_hash {
- obj.set("txHash", to_buf(tx_hash.as_slice().to_vec(), ctx)?, false, ctx)?;
- }
-
- Ok(obj)
- }
-}
-
-/// DB is the object that allows the js inspector to interact with the database.
-#[derive(Debug, Clone)]
-pub(crate) struct EvmDbRef {
- state: StateRef,
- to_db: mpsc::Sender,
-}
-
-impl EvmDbRef {
- /// Creates a new DB reference
- pub(crate) fn new(
- state: &State,
- to_db: mpsc::Sender,
- ) -> (Self, RefGuard<'_, State>) {
- let (state, guard) = StateRef::new(state);
- let this = Self { state, to_db };
- (this, guard)
- }
-
- fn read_basic(&self, address: JsValue, ctx: &mut Context<'_>) -> JsResult