Skip to content

Commit 13ca215

Browse files
grandizzyDaniPopesmattssezerosnacks
authored
v1.3.0: backport perf improvements and anvil fixes (#11136)
* perf: box inspectors in InspectorStack (#11100) * fix: use existing functions for accountinfo (#11134) * fix(`anvil`): unwrap panic in `eth/backend/mem/mod.rs` (#11141) make typed request casting not panic and return useful error to user --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com>
1 parent e5519c6 commit 13ca215

File tree

8 files changed

+62
-50
lines changed

8 files changed

+62
-50
lines changed

crates/anvil/src/eth/api.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,10 @@ use revm::{
9696
primitives::eip7702::PER_EMPTY_ACCOUNT_COST,
9797
};
9898
use std::{sync::Arc, time::Duration};
99-
use tokio::sync::mpsc::{UnboundedReceiver, unbounded_channel};
99+
use tokio::{
100+
sync::mpsc::{UnboundedReceiver, unbounded_channel},
101+
try_join,
102+
};
100103

101104
/// The client version: `anvil/v{major}.{minor}.{patch}`
102105
pub const CLIENT_VERSION: &str = concat!("anvil/v", env!("CARGO_PKG_VERSION"));
@@ -762,12 +765,9 @@ impl EthApi {
762765
block_number: Option<BlockId>,
763766
) -> Result<alloy_rpc_types::eth::AccountInfo> {
764767
node_info!("eth_getAccountInfo");
765-
let account = self
766-
.backend
767-
.get_account_at_block(address, Some(self.block_request(block_number).await?))
768-
.await?;
769-
let code =
770-
self.backend.get_code(address, Some(self.block_request(block_number).await?)).await?;
768+
let account = self.get_account(address, block_number);
769+
let code = self.get_code(address, block_number);
770+
let (account, code) = try_join!(account, code)?;
771771
Ok(alloy_rpc_types::eth::AccountInfo {
772772
balance: account.balance,
773773
nonce: account.nonce,

crates/anvil/src/eth/backend/mem/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,11 +1722,10 @@ impl Backend {
17221722
cache_db.commit(state);
17231723
gas_used += result.gas_used();
17241724

1725-
// TODO: this is likely incomplete
17261725
// create the transaction from a request
17271726
let from = request.from.unwrap_or_default();
1728-
let request =
1729-
transaction_request_to_typed(WithOtherFields::new(request)).unwrap();
1727+
let request = transaction_request_to_typed(WithOtherFields::new(request))
1728+
.ok_or(BlockchainError::MissingRequiredFields)?;
17301729
let tx = build_typed_transaction(
17311730
request,
17321731
Signature::new(Default::default(), Default::default(), false),

crates/anvil/src/eth/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ pub enum BlockchainError {
115115
/// Duration that was waited before timing out
116116
duration: Duration,
117117
},
118+
#[error("Failed to parse transaction request: missing required fields")]
119+
MissingRequiredFields,
118120
}
119121

120122
impl From<eyre::Report> for BlockchainError {
@@ -563,6 +565,9 @@ impl<T: Serialize> ToRpcResponseResult for Result<T> {
563565
err @ BlockchainError::UnknownTransactionType => {
564566
RpcError::invalid_params(err.to_string())
565567
}
568+
err @ BlockchainError::MissingRequiredFields => {
569+
RpcError::invalid_params(err.to_string())
570+
}
566571
}
567572
.into(),
568573
}

crates/cheatcodes/src/inspector.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ pub trait CheatcodesExecutor {
111111
}
112112

113113
/// Returns a mutable reference to the tracing inspector if it is available.
114-
fn tracing_inspector(&mut self) -> Option<&mut Option<TracingInspector>> {
114+
fn tracing_inspector(&mut self) -> Option<&mut Option<Box<TracingInspector>>> {
115115
None
116116
}
117117
}

crates/evm/evm/src/executors/invariant/mod.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -609,8 +609,11 @@ impl<'a> InvariantExecutor<'a> {
609609
));
610610
}
611611

612-
self.executor.inspector_mut().fuzzer =
613-
Some(Fuzzer { call_generator, fuzz_state: fuzz_state.clone(), collect: true });
612+
self.executor.inspector_mut().set_fuzzer(Fuzzer {
613+
call_generator,
614+
fuzz_state: fuzz_state.clone(),
615+
collect: true,
616+
});
614617

615618
// Let's make sure the invariant is sound before actually starting the run:
616619
// We'll assert the invariant in its initial state, and if it fails, we'll

crates/evm/evm/src/executors/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ pub struct RawCallResult {
842842
/// The `revm::Env` after the call
843843
pub env: Env,
844844
/// The cheatcode states after execution
845-
pub cheatcodes: Option<Cheatcodes>,
845+
pub cheatcodes: Option<Box<Cheatcodes>>,
846846
/// The raw output of the execution
847847
pub out: Option<Output>,
848848
/// The chisel state

crates/evm/evm/src/inspectors/stack.rs

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ pub struct InspectorData {
262262
pub traces: Option<SparsedTraceArena>,
263263
pub line_coverage: Option<HitMaps>,
264264
pub edge_coverage: Option<Vec<u8>>,
265-
pub cheatcodes: Option<Cheatcodes>,
265+
pub cheatcodes: Option<Box<Cheatcodes>>,
266266
pub chisel_state: Option<(Vec<U256>, Vec<u8>, Option<InstructionResult>)>,
267267
pub reverter: Option<Address>,
268268
}
@@ -290,7 +290,7 @@ pub struct InnerContextData {
290290
/// collection, etc.
291291
#[derive(Clone, Debug, Default)]
292292
pub struct InspectorStack {
293-
pub cheatcodes: Option<Cheatcodes>,
293+
pub cheatcodes: Option<Box<Cheatcodes>>,
294294
pub inner: InspectorStackInner,
295295
}
296296

@@ -300,15 +300,17 @@ pub struct InspectorStack {
300300
#[derive(Default, Clone, Debug)]
301301
pub struct InspectorStackInner {
302302
// Inspectors.
303-
pub chisel_state: Option<ChiselState>,
304-
pub edge_coverage: Option<EdgeCovInspector>,
305-
pub fuzzer: Option<Fuzzer>,
306-
pub line_coverage: Option<LineCoverageCollector>,
307-
pub log_collector: Option<LogCollector>,
308-
pub printer: Option<CustomPrintTracer>,
309-
pub revert_diag: Option<RevertDiagnostic>,
310-
pub script_execution_inspector: Option<ScriptExecutionInspector>,
311-
pub tracer: Option<TracingInspector>,
303+
// These are boxed to reduce the size of the struct and slightly improve performance of the
304+
// `if let Some` checks.
305+
pub chisel_state: Option<Box<ChiselState>>,
306+
pub edge_coverage: Option<Box<EdgeCovInspector>>,
307+
pub fuzzer: Option<Box<Fuzzer>>,
308+
pub line_coverage: Option<Box<LineCoverageCollector>>,
309+
pub log_collector: Option<Box<LogCollector>>,
310+
pub printer: Option<Box<CustomPrintTracer>>,
311+
pub revert_diag: Option<Box<RevertDiagnostic>>,
312+
pub script_execution_inspector: Option<Box<ScriptExecutionInspector>>,
313+
pub tracer: Option<Box<TracingInspector>>,
312314

313315
// InspectorExt and other internal data.
314316
pub enable_isolation: bool,
@@ -335,7 +337,7 @@ impl CheatcodesExecutor for InspectorStackInner {
335337
Box::new(InspectorStackRefMut { cheatcodes: Some(cheats), inner: self })
336338
}
337339

338-
fn tracing_inspector(&mut self) -> Option<&mut Option<TracingInspector>> {
340+
fn tracing_inspector(&mut self) -> Option<&mut Option<Box<TracingInspector>>> {
339341
Some(&mut self.tracer)
340342
}
341343
}
@@ -398,19 +400,19 @@ impl InspectorStack {
398400
/// Set the cheatcodes inspector.
399401
#[inline]
400402
pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes) {
401-
self.cheatcodes = Some(cheatcodes);
403+
self.cheatcodes = Some(cheatcodes.into());
402404
}
403405

404406
/// Set the fuzzer inspector.
405407
#[inline]
406408
pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
407-
self.fuzzer = Some(fuzzer);
409+
self.fuzzer = Some(fuzzer.into());
408410
}
409411

410412
/// Set the Chisel inspector.
411413
#[inline]
412414
pub fn set_chisel(&mut self, final_pc: usize) {
413-
self.chisel_state = Some(ChiselState::new(final_pc));
415+
self.chisel_state = Some(ChiselState::new(final_pc).into());
414416
}
415417

416418
/// Set whether to enable the line coverage collector.
@@ -422,7 +424,8 @@ impl InspectorStack {
422424
/// Set whether to enable the edge coverage collector.
423425
#[inline]
424426
pub fn collect_edge_coverage(&mut self, yes: bool) {
425-
self.edge_coverage = yes.then(EdgeCovInspector::new); // TODO configurable edge size?
427+
// TODO: configurable edge size?
428+
self.edge_coverage = yes.then(EdgeCovInspector::new).map(Into::into);
426429
}
427430

428431
/// Set whether to enable call isolation.
@@ -459,11 +462,7 @@ impl InspectorStack {
459462
/// Revert diagnostic inspector is activated when `mode != TraceMode::None`
460463
#[inline]
461464
pub fn tracing(&mut self, mode: TraceMode) {
462-
if mode.is_none() {
463-
self.revert_diag = None;
464-
} else {
465-
self.revert_diag = Some(RevertDiagnostic::default());
466-
}
465+
self.revert_diag = (!mode.is_none()).then(RevertDiagnostic::default).map(Into::into);
467466

468467
if let Some(config) = mode.into_config() {
469468
*self.tracer.get_or_insert_with(Default::default).config_mut() = config;
@@ -480,7 +479,6 @@ impl InspectorStack {
480479
}
481480

482481
/// Collects all the data gathered during inspection into a single struct.
483-
#[inline]
484482
pub fn collect(self) -> InspectorData {
485483
let Self {
486484
mut cheatcodes,
@@ -531,7 +529,7 @@ impl InspectorStack {
531529

532530
#[inline(always)]
533531
fn as_mut(&mut self) -> InspectorStackRefMut<'_> {
534-
InspectorStackRefMut { cheatcodes: self.cheatcodes.as_mut(), inner: &mut self.inner }
532+
InspectorStackRefMut { cheatcodes: self.cheatcodes.as_deref_mut(), inner: &mut self.inner }
535533
}
536534
}
537535

@@ -740,17 +738,16 @@ impl InspectorStackRefMut<'_> {
740738
/// it.
741739
fn with_stack<O>(&mut self, f: impl FnOnce(&mut InspectorStack) -> O) -> O {
742740
let mut stack = InspectorStack {
743-
cheatcodes: self
744-
.cheatcodes
745-
.as_deref_mut()
746-
.map(|cheats| core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone()))),
741+
cheatcodes: self.cheatcodes.as_deref_mut().map(|cheats| {
742+
core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone())).into()
743+
}),
747744
inner: std::mem::take(self.inner),
748745
};
749746

750747
let out = f(&mut stack);
751748

752749
if let Some(cheats) = self.cheatcodes.as_deref_mut() {
753-
*cheats = stack.cheatcodes.take().unwrap();
750+
*cheats = *stack.cheatcodes.take().unwrap();
754751
}
755752

756753
*self.inner = stack.inner;
@@ -791,6 +788,11 @@ impl InspectorStackRefMut<'_> {
791788
}
792789
}
793790

791+
// We take extra care in optimizing `step` and `step_end`, as they're are likely the most
792+
// hot functions in all of Foundry.
793+
// We want to `#[inline(always)]` these functions so that `InspectorStack` does not
794+
// delegate to `InspectorStackRefMut` in this case.
795+
794796
#[inline(always)]
795797
fn step_inlined(
796798
&mut self,
@@ -799,17 +801,18 @@ impl InspectorStackRefMut<'_> {
799801
) {
800802
call_inspectors!(
801803
[
804+
// These are sorted in definition order.
805+
&mut self.edge_coverage,
802806
&mut self.fuzzer,
803-
&mut self.tracer,
804807
&mut self.line_coverage,
805-
&mut self.edge_coverage,
806-
&mut self.script_execution_inspector,
807808
&mut self.printer,
808809
&mut self.revert_diag,
810+
&mut self.script_execution_inspector,
811+
&mut self.tracer,
809812
// Keep `cheatcodes` last to make use of the tail call.
810813
&mut self.cheatcodes,
811814
],
812-
|inspector| (*inspector).step(interpreter, ecx),
815+
|inspector| (**inspector).step(interpreter, ecx),
813816
);
814817
}
815818

@@ -821,14 +824,15 @@ impl InspectorStackRefMut<'_> {
821824
) {
822825
call_inspectors!(
823826
[
824-
&mut self.tracer,
827+
// These are sorted in definition order.
825828
&mut self.chisel_state,
826829
&mut self.printer,
827830
&mut self.revert_diag,
831+
&mut self.tracer,
828832
// Keep `cheatcodes` last to make use of the tail call.
829833
&mut self.cheatcodes,
830834
],
831-
|inspector| (*inspector).step_end(interpreter, ecx),
835+
|inspector| (**inspector).step_end(interpreter, ecx),
832836
);
833837
}
834838
}

crates/evm/fuzz/src/inspector.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ use revm::{
99
/// An inspector that can fuzz and collect data for that effect.
1010
#[derive(Clone, Debug)]
1111
pub struct Fuzzer {
12-
/// Given a strategy, it generates a random call.
13-
pub call_generator: Option<RandomCallGenerator>,
1412
/// If set, it collects `stack` and `memory` values for fuzzing purposes.
1513
pub collect: bool,
14+
/// Given a strategy, it generates a random call.
15+
pub call_generator: Option<RandomCallGenerator>,
1616
/// If `collect` is set, we store the collected values in this fuzz dictionary.
1717
pub fuzz_state: EvmFuzzState,
1818
}
@@ -21,6 +21,7 @@ impl<CTX> Inspector<CTX> for Fuzzer
2121
where
2222
CTX: ContextTr<Journal: JournalExt>,
2323
{
24+
#[inline]
2425
fn step(&mut self, interp: &mut Interpreter, _context: &mut CTX) {
2526
// We only collect `stack` and `memory` data before and after calls.
2627
if self.collect {

0 commit comments

Comments
 (0)