Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(anvil, evm): gas accounting in debug_traceTransaction #3230

Merged
merged 10 commits into from
Sep 18, 2022
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

105 changes: 79 additions & 26 deletions anvil/src/eth/backend/mem/inspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ use crate::{
use bytes::Bytes;
use ethers::types::{Address, Log, H256};
use foundry_evm::{
call_inspectors,
decode::decode_console_logs,
executor::inspector::{LogCollector, Tracer},
revm,
revm::{CallInputs, EVMData, Gas, Return},
revm::{CallInputs, EVMData, Gas, GasInspector, Return},
};
use std::{cell::RefCell, rc::Rc};

/// The [`revm::Inspector`] used when transacting in the evm
#[derive(Debug, Clone, Default)]
pub struct Inspector {
pub gas: Option<Rc<RefCell<GasInspector>>>,
pub tracer: Option<Tracer>,
/// collects all `console.sol` logs
pub logs: LogCollector,
Expand All @@ -37,28 +40,48 @@ impl Inspector {
self
}

/// Enables steps recording for `Tracer`
/// Enables steps recording for `Tracer` and attaches `GasInspector` to it
/// If `Tracer` wasn't configured before, configures it automatically
pub fn with_steps_tracing(mut self) -> Self {
if self.tracer.is_none() {
self = self.with_tracing()
}
self.tracer = self.tracer.map(|tracer| tracer.with_steps_recording());
let gas_inspector = Rc::new(RefCell::new(GasInspector::default()));
self.gas = Some(gas_inspector.clone());
self.tracer = self.tracer.map(|tracer| tracer.with_steps_recording(gas_inspector));

self
}
}

impl<DB: Database> revm::Inspector<DB> for Inspector {
fn initialize_interp(
&mut self,
interp: &mut Interpreter,
data: &mut EVMData<'_, DB>,
is_static: bool,
) -> Return {
call_inspectors!(
inspector,
[&mut self.gas.as_deref().map(|gas| gas.borrow_mut()), &mut self.tracer],
{ inspector.initialize_interp(interp, data, is_static) }
);
Return::Continue
}

fn step(
&mut self,
interp: &mut Interpreter,
data: &mut EVMData<'_, DB>,
is_static: bool,
) -> Return {
if let Some(tracer) = self.tracer.as_mut() {
tracer.step(interp, data, is_static);
}
call_inspectors!(
inspector,
[&mut self.gas.as_deref().map(|gas| gas.borrow_mut()), &mut self.tracer],
{
inspector.step(interp, data, is_static);
}
);
Return::Continue
}

Expand All @@ -69,10 +92,17 @@ impl<DB: Database> revm::Inspector<DB> for Inspector {
topics: &[H256],
data: &Bytes,
) {
if let Some(tracer) = self.tracer.as_mut() {
tracer.log(evm_data, address, topics, data);
}
self.logs.log(evm_data, address, topics, data);
call_inspectors!(
inspector,
[
&mut self.gas.as_deref().map(|gas| gas.borrow_mut()),
&mut self.tracer,
Some(&mut self.logs)
],
{
inspector.log(evm_data, address, topics, data);
}
);
}

fn step_end(
Expand All @@ -82,9 +112,13 @@ impl<DB: Database> revm::Inspector<DB> for Inspector {
is_static: bool,
eval: Return,
) -> Return {
if let Some(tracer) = self.tracer.as_mut() {
tracer.step_end(interp, data, is_static, eval);
}
call_inspectors!(
inspector,
[&mut self.gas.as_deref().map(|gas| gas.borrow_mut()), &mut self.tracer],
{
inspector.step_end(interp, data, is_static, eval);
}
);
eval
}

Expand All @@ -94,10 +128,17 @@ impl<DB: Database> revm::Inspector<DB> for Inspector {
call: &mut CallInputs,
is_static: bool,
) -> (Return, Gas, Bytes) {
if let Some(tracer) = self.tracer.as_mut() {
tracer.call(data, call, is_static);
}
self.logs.call(data, call, is_static);
call_inspectors!(
inspector,
[
&mut self.gas.as_deref().map(|gas| gas.borrow_mut()),
&mut self.tracer,
Some(&mut self.logs)
],
{
inspector.call(data, call, is_static);
}
);

(Return::Continue, Gas::new(call.gas_limit), Bytes::new())
}
Expand All @@ -111,9 +152,13 @@ impl<DB: Database> revm::Inspector<DB> for Inspector {
out: Bytes,
is_static: bool,
) -> (Return, Gas, Bytes) {
if let Some(tracer) = self.tracer.as_mut() {
tracer.call_end(data, inputs, remaining_gas, ret, out.clone(), is_static);
}
call_inspectors!(
inspector,
[&mut self.gas.as_deref().map(|gas| gas.borrow_mut()), &mut self.tracer],
{
inspector.call_end(data, inputs, remaining_gas, ret, out.clone(), is_static);
}
);
(ret, remaining_gas, out)
}

Expand All @@ -122,9 +167,13 @@ impl<DB: Database> revm::Inspector<DB> for Inspector {
data: &mut EVMData<'_, DB>,
call: &mut CreateInputs,
) -> (Return, Option<Address>, Gas, Bytes) {
if let Some(tracer) = self.tracer.as_mut() {
tracer.create(data, call);
}
call_inspectors!(
inspector,
[&mut self.gas.as_deref().map(|gas| gas.borrow_mut()), &mut self.tracer],
{
inspector.create(data, call);
}
);

(Return::Continue, None, Gas::new(call.gas_limit), Bytes::new())
}
Expand All @@ -138,9 +187,13 @@ impl<DB: Database> revm::Inspector<DB> for Inspector {
gas: Gas,
retdata: Bytes,
) -> (Return, Option<Address>, Gas, Bytes) {
if let Some(tracer) = self.tracer.as_mut() {
tracer.create_end(data, inputs, status, address, gas, retdata.clone());
}
call_inspectors!(
inspector,
[&mut self.gas.as_deref().map(|gas| gas.borrow_mut()), &mut self.tracer],
{
inspector.create_end(data, inputs, status, address, gas, retdata.clone());
}
);
(status, address, gas, retdata)
}
}
Expand Down
2 changes: 1 addition & 1 deletion anvil/src/eth/backend/mem/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ impl MinedTransaction {
}

pub fn geth_trace(&self, opts: GethDebugTracingOptions) -> GethTrace {
self.info.traces.geth_trace(opts)
self.info.traces.geth_trace(self.receipt.gas_used(), opts)
}
}

Expand Down
1 change: 1 addition & 0 deletions evm/src/executor/inspector/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use std::collections::BTreeMap;

/// 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) => {
$({
Expand Down
Loading