Skip to content

Commit 165bca1

Browse files
committed
Release v3.1.0: Remove Clone constraint from TraceInspector
- **BREAKING CHANGE**: Remove Clone trait bound from TraceInspector and related generics - Eliminates unnecessary inspector cloning constraints in batch processing - Enables custom wrapper development with REVM's built-in GasInspector and TracerEip3155 - Improves memory usage and performance by avoiding unnecessary clones - Simplifies trait bounds for better API ergonomics Changes: - Updated trace_internal to use set_tx + inspect_replay_commit pattern - Removed clone_inspector method and related documentation - Added inspector_reset_test.rs example for batch processing verification - Updated documentation in lib.rs with multi-threading support - Enhanced CHANGELOG.md with detailed breaking change description - Updated version to 3.1.0 in Cargo.toml and documentation
1 parent 194507b commit 165bca1

File tree

8 files changed

+173
-70
lines changed

8 files changed

+173
-70
lines changed

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [3.1.0] - 2025-06-22
9+
10+
### 🚀 Performance Improvements
11+
12+
#### Breaking Changes
13+
- **Removed `Clone` constraint from `TraceInspector`**
14+
- Eliminates unnecessary inspector cloning constraints in batch processing
15+
- Enables users to use REVM's built-in `GasInspector` and `TracerEip3155` for custom wrapper development
16+
- Improves memory usage and performance
17+
- Simplifies trait bounds for better ergonomics
18+
19+
#### Internal Optimizations
20+
- **Optimized `trace_internal` function**
21+
- Direct use of `set_tx()` + `inspect_replay_commit()` pattern
22+
- Eliminates intermediate inspector cloning
23+
- Better utilizes the `InspectCommitEvm` trait design
24+
25+
#### Documentation Updates
26+
- Updated trait documentation to reflect removed `Clone` requirement
27+
- Simplified examples without unnecessary derives
28+
- Improved performance characteristics documentation
29+
830
## [3.0.0] - 2025-06-22
931

1032
### 🚀 Major Architecture Overhaul

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "revm-trace"
3-
version = "3.0.0"
3+
version = "3.1.0"
44
edition = "2021"
55
authors = ["REVM-Trace Contributors"]
66
description = "High-performance multi-threaded EVM transaction simulator and analyzer with comprehensive tracing capabilities"

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ Perfect for:
8383
Add this to your `Cargo.toml`:
8484
```toml
8585
[dependencies]
86-
revm-trace = "3.0.0"
86+
revm-trace = "3.1.0"
8787
```
8888

8989
### TLS Backend Selection
@@ -93,10 +93,10 @@ revm-trace = "3.0.0"
9393
```toml
9494
[dependencies]
9595
# Option 1: Default - uses native-tls (OpenSSL) for maximum compatibility
96-
revm-trace = "3.0.0"
96+
revm-trace = "3.1.0"
9797

9898
# Option 2: Pure Rust TLS with rustls for system-dependency-free builds
99-
revm-trace = { version = "3.0.0", default-features = false, features = ["rustls-tls"] }
99+
revm-trace = { version = "3.1.0", default-features = false, features = ["rustls-tls"] }
100100
```
101101

102102
Do not specify both features simultaneously, as this will include both TLS implementations and increase binary size unnecessarily.

examples/inspector_reset_test.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use revm_trace::{create_evm_with_tracer,TransactionTrace, TxInspector, SimulationBatch, SimulationTx};
2+
use alloy::primitives::{address, U256, TxKind};
3+
4+
/// Test to verify that inspector state is properly reset between transactions in batch processing
5+
///
6+
/// This test creates a SINGLE BATCH containing two ETH transfers and verifies that:
7+
/// 1. Both transactions are processed in the same batch call
8+
/// 2. Inspector state is properly reset between transactions within the batch
9+
/// 3. Simple check: if asset_transfers lengths are equal but contents differ, reset worked
10+
#[tokio::main]
11+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
12+
println!("🧪 Testing Inspector Reset in Single Batch Processing");
13+
println!("{}", "=".repeat(60));
14+
15+
// Create EVM with tracer
16+
let trace_inspector = TxInspector::new();
17+
let mut evm = create_evm_with_tracer("https://eth.llamarpc.com", trace_inspector).await?;
18+
19+
// Define test addresses
20+
let from = address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); // Vitalik's address
21+
let to1 = address!("0000000000000000000000000000000000000001"); // First recipient
22+
let to2 = address!("0000000000000000000000000000000000000002"); // Second recipient
23+
let value = U256::from(1000000000000000u64); // 0.001 ETH
24+
25+
// Create single batch containing both transactions using SimulationTx
26+
let tx1 = SimulationTx {
27+
caller: from,
28+
transact_to: TxKind::Call(to1),
29+
value,
30+
data: vec![].into(),
31+
};
32+
33+
let tx2 = SimulationTx {
34+
caller: from,
35+
transact_to: TxKind::Call(to2),
36+
value,
37+
data: vec![].into(),
38+
};
39+
40+
let single_batch = SimulationBatch {
41+
block_env: None,
42+
transactions: vec![tx1, tx2],
43+
is_stateful: false, // stateless mode
44+
};
45+
46+
println!("📦 Processing SINGLE BATCH containing {} transactions:", single_batch.transactions.len());
47+
println!(" 📤 TX1: {} -> {} (value: {})", from, to1, value);
48+
println!(" 📤 TX2: {} -> {} (value: {})", from, to2, value);
49+
println!(" 🎯 Key Test: Inspector should reset between TX1 and TX2 within this batch");
50+
51+
// Execute the single batch containing both transactions
52+
let results: Vec<_> = evm.trace_transactions(single_batch)
53+
.into_iter()
54+
.map(|r| r.unwrap())
55+
.collect();
56+
57+
println!("\n✅ Single batch execution completed with {} results", results.len());
58+
59+
// Extract outputs from each transaction
60+
if results.len() != 2 {
61+
return Err(format!("Expected 2 results, got {}", results.len()).into());
62+
}
63+
64+
let (_result1, output1) = &results[0];
65+
let (_result2, output2) = &results[1];
66+
67+
println!("\n📊 Transaction 1 Results:");
68+
println!(" 💰 Asset transfers: {}", output1.asset_transfers.len());
69+
if let Some(transfer) = output1.asset_transfers.first() {
70+
println!(" 🎯 First transfer to: {:?}", transfer.to);
71+
}
72+
73+
println!("\n📊 Transaction 2 Results:");
74+
println!(" 💰 Asset transfers: {}", output2.asset_transfers.len());
75+
if let Some(transfer) = output2.asset_transfers.first() {
76+
println!(" 🎯 First transfer to: {:?}", transfer.to);
77+
}
78+
79+
// Simplified verification: lengths equal but contents different = reset worked
80+
println!("\n🔍 Verification Results:");
81+
println!("{}", "=".repeat(60));
82+
83+
if output1.asset_transfers.len() == output2.asset_transfers.len() {
84+
println!("✅ PASS: Asset transfer lengths are equal ({} each)", output1.asset_transfers.len());
85+
86+
// Check if contents are different (which proves reset worked)
87+
let contents_different = if let (Some(transfer1), Some(transfer2)) = (
88+
output1.asset_transfers.first(),
89+
output2.asset_transfers.first()
90+
) {
91+
transfer1.to != transfer2.to
92+
} else {
93+
false
94+
};
95+
96+
if contents_different {
97+
println!("✅ PASS: Asset transfer contents are different");
98+
println!(" 🎉 Inspector was properly reset between transactions!");
99+
println!(" ✨ TX1 and TX2 have independent outputs despite being in same batch");
100+
} else {
101+
println!("❌ FAIL: Asset transfer contents are the same");
102+
println!(" 🚨 Inspector was NOT reset between transactions!");
103+
return Err("Inspector reset test failed - contents identical".into());
104+
}
105+
} else {
106+
println!("❌ FAIL: Asset transfer lengths differ ({} vs {})",
107+
output1.asset_transfers.len(), output2.asset_transfers.len());
108+
println!(" 🚨 This suggests state accumulation or other issues");
109+
return Err("Inspector reset test failed - length mismatch".into());
110+
}
111+
112+
println!("\n🎉 All tests PASSED! Inspector reset is working correctly within single batch!");
113+
println!(" ✨ Both transactions were processed in the same batch call");
114+
println!(" ✨ Equal lengths + different contents = proper reset");
115+
println!(" ✨ No state accumulation between TX1 and TX2 within the batch");
116+
117+
Ok(())
118+
}

src/evm/inspector.rs

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::evm::TraceEvm;
1010
impl<DB,INSP> TraceEvm<DB,INSP>
1111
where
1212
DB: Database,
13-
INSP: TraceOutput + Reset + Clone,
13+
INSP: TraceOutput + Reset,
1414
{
1515
/// Retrieve the current output from the inspector
1616
///
@@ -70,48 +70,4 @@ where
7070
pub fn reset_inspector(&mut self) {
7171
self.inspector.reset();
7272
}
73-
74-
/// Clone the inspector instance for trace analysis
75-
///
76-
/// Creates a fresh copy of the current inspector. This is primarily used
77-
/// internally by the tracing system when calling `inspect_commit` functions,
78-
/// which require ownership of an inspector instance.
79-
///
80-
/// # Design Rationale
81-
///
82-
/// In revm 24+, the new inspector architecture requires passing inspector
83-
/// instances by value to `inspect_commit` functions. This means:
84-
///
85-
/// - Each transaction trace needs its own inspector instance
86-
/// - The inspector must be cloned before being passed to `inspect_commit`
87-
/// - This ensures proper isolation between transaction traces
88-
///
89-
/// # Examples
90-
/// ```no_run
91-
/// use revm_trace::{create_evm_with_tracer, TxInspector};
92-
///
93-
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
94-
/// let trace_inspector = TxInspector::new();
95-
/// let mut evm = create_evm_with_tracer("https://eth.llamarpc.com", trace_inspector).await?;
96-
///
97-
/// // Clone inspector for external analysis
98-
/// let inspector_copy = evm.clone_inspector();
99-
///
100-
/// // The original inspector remains unchanged
101-
/// // while the copy can be used independently
102-
/// # Ok(())
103-
/// # }
104-
/// ```
105-
///
106-
/// # Returns
107-
/// A fresh copy of the inspector with the same configuration but
108-
/// independent state (reset to initial state).
109-
///
110-
/// # Note
111-
/// This method is called internally during transaction processing to
112-
/// provide fresh inspector instances to `inspect_commit` functions.
113-
/// The `Clone` trait requirement ensures this operation is always available.
114-
pub fn clone_inspector(&self) -> INSP {
115-
self.inspector.clone()
116-
}
11773
}

src/evm/processor.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use crate::traits::TraceInspector;
2323
impl<DB, INSP> TraceEvm<DB, INSP>
2424
where
2525
DB: Database + DatabaseCommit,
26-
INSP: TraceInspector<MainnetContext<DB>> + Clone,
26+
INSP: TraceInspector<MainnetContext<DB>>,
2727
{
2828
/// Process a single transaction with tracing
2929
///
@@ -70,11 +70,9 @@ where
7070
.nonce(nonce)
7171
.build_fill();
7272

73-
// Clone inspector for execution
74-
let inspector = self.clone_inspector();
75-
76-
// Execute transaction with inspector and commit changes
77-
let result = self.inspect_commit(tx, inspector)
73+
// Set transaction and execute with current inspector, committing changes
74+
self.set_tx(tx);
75+
let result = self.inspect_replay_commit()
7876
.map_err(|e| RuntimeError::ExecutionFailed(format!("Inspector execution failed: {}", e)))?;
7977

8078
// Collect inspector output
@@ -87,7 +85,7 @@ where
8785
impl<DB, INSP> TransactionTrace<MainnetContext<CacheDB<DB>>> for TraceEvm<CacheDB<DB>, INSP>
8886
where
8987
DB: DatabaseRef,
90-
INSP: TraceInspector<MainnetContext<CacheDB<DB>>> + Clone,
88+
INSP: TraceInspector<MainnetContext<CacheDB<DB>>>,
9189
{
9290
type Inspector = INSP;
9391

@@ -166,6 +164,12 @@ where
166164

167165
// 4. Clean up inspector state after batch completion
168166
self.reset_inspector();
167+
168+
// 5. Reset transaction environment to prevent interference with other uses
169+
self.set_tx(Default::default());
170+
// Note: We don't reset_db here because EVM state can be preserved for other scenarios,
171+
// such as querying ERC20 token balances
172+
169173
results
170174
}
171175
}

src/lib.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@
1414
//! - HTTP and WebSocket RPC connections
1515
//! - Automatic protocol detection
1616
//! - Any EVM-compatible chain support
17-
//! - Built-in rustls TLS support for cross-platform compatibility
17+
//! - Configurable TLS support (rustls/native-tls features)
18+
//!
19+
//! - **Multi-threading Support**
20+
//! - Thread-safe design for concurrent transaction processing
21+
//! - Suitable for HTTP API integration and high-throughput scenarios
22+
//! - Inspector state isolation between concurrent executions
1823
//!
1924
//! - **Flexible Inspector System**
2025
//! - Custom transaction tracers
@@ -27,7 +32,7 @@
2732
//!
2833
//! ```toml
2934
//! [dependencies]
30-
//! revm-trace = "3.0.0"
35+
//! revm-trace = "3.1.0"
3136
//! ```
3237
//!
3338
//! ### TLS Backend Selection
@@ -36,14 +41,11 @@
3641
//!
3742
//! ```toml
3843
//! # Option 1: Default - uses native-tls (OpenSSL) for maximum compatibility
39-
//! revm-trace = "3.0.0"
44+
//! revm-trace = "3.1.0"
4045
//!
4146
//! # Option 2: Pure Rust TLS with rustls for system-dependency-free builds
42-
//! revm-trace = { version = "3.0.0", default-features = false, features = ["rustls-tls"] }
47+
//! revm-trace = { version = "3.1.0", default-features = false, features = ["rustls-tls"] }
4348
//! ```
44-
//!
45-
//! The library uses rustls for TLS connections, providing excellent cross-platform
46-
//! compatibility without requiring OpenSSL or other system TLS libraries.
4749
//!
4850
//! ## Example Usage
4951
//!

src/traits.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ pub trait TraceInspector<CTX>: Inspector<CTX> + Reset + TraceOutput {}
152152
/// - **`Inspector<CTX>`**: Core REVM inspector interface for EVM execution callbacks
153153
/// - **`Reset`**: Ability to reset internal state between transactions
154154
/// - **`TraceOutput`**: Ability to extract structured output after execution
155-
/// - **`Clone`**: Required for batch processing where inspector instances need duplication
155+
///
156+
/// Note: The `Clone` requirement was removed in v3.1 for better performance.
156157
///
157158
/// # Design Rationale
158159
///
@@ -169,7 +170,7 @@ pub trait TraceInspector<CTX>: Inspector<CTX> + Reset + TraceOutput {}
169170
/// use revm_trace::traits::{Reset, TraceOutput, TraceInspector};
170171
/// use revm::Inspector;
171172
///
172-
/// #[derive(Clone)]
173+
/// #[derive(Default)]
173174
/// struct MyInspector {
174175
/// calls: Vec<String>,
175176
/// }
@@ -198,11 +199,11 @@ pub trait TraceInspector<CTX>: Inspector<CTX> + Reset + TraceOutput {}
198199
///
199200
/// # Trait Bounds
200201
///
201-
/// The `Clone` bound is specifically required for batch transaction processing,
202-
/// where multiple inspector instances may need to be created from a template.
202+
/// Previously required `Clone` bound for batch transaction processing,
203+
/// but this has been optimized away in v3.1 for better performance.
203204
impl<CTX, T> TraceInspector<CTX> for T
204205
where
205-
T: Inspector<CTX> + Reset + TraceOutput + Clone
206+
T: Inspector<CTX> + Reset + TraceOutput
206207
{}
207208

208209
impl Reset for () {
@@ -309,7 +310,7 @@ impl TraceOutput for NoOpInspector {
309310

310311
// Note: NoOpInspector automatically implements TraceInspector<CTX> through the blanket implementation:
311312
// impl<CTX, T> TraceInspector<CTX> for T
312-
// where T: Inspector<CTX> + Reset + TraceOutput + Clone
313+
// where T: Inspector<CTX> + Reset + TraceOutput
313314
//
314-
// Since NoOpInspector already implements Inspector<CTX> (from revm) and Clone (derive),
315+
// Since NoOpInspector already implements Inspector<CTX> (from revm),
315316
// and we've implemented Reset and TraceOutput above, it automatically gets TraceInspector.

0 commit comments

Comments
 (0)