diff --git a/crates/core/src/driver/mod.rs b/crates/core/src/driver/mod.rs index 99bab18..fb13c29 100644 --- a/crates/core/src/driver/mod.rs +++ b/crates/core/src/driver/mod.rs @@ -5,8 +5,11 @@ use std::marker::PhantomData; use alloy::json_abi::JsonAbi; use alloy::network::{Ethereum, TransactionBuilder}; +use alloy::primitives::Bytes; use alloy::rpc::types::TransactionReceipt; -use alloy::rpc::types::trace::geth::GethTrace; +use alloy::rpc::types::trace::geth::{ + DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace, PreStateConfig, +}; use alloy::{ primitives::Address, rpc::types::{ @@ -19,7 +22,7 @@ use indexmap::IndexMap; use revive_dt_compiler::{Compiler, SolidityCompiler}; use revive_dt_config::Arguments; use revive_dt_format::case::CaseIdx; -use revive_dt_format::input::Method; +use revive_dt_format::input::{Calldata, Expected, ExpectedOutput, Method}; use revive_dt_format::metadata::{ContractInstance, ContractPathAndIdentifier}; use revive_dt_format::{input::Input, metadata::Metadata, mode::SolcMode}; use revive_dt_node_interaction::EthereumNode; @@ -145,7 +148,10 @@ where ) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> { let deployment_receipts = self.handle_contract_deployment(metadata, case_idx, input, node)?; - self.handle_input_execution(case_idx, input, deployment_receipts, node) + let execution_receipt = + self.handle_input_execution(case_idx, input, deployment_receipts, node)?; + self.handle_input_expectations(case_idx, input, &execution_receipt, node)?; + self.handle_input_diff(case_idx, execution_receipt, node) } /// Handles the contract deployment for a given input performing it if it needs to be performed. @@ -308,18 +314,15 @@ where &mut self, case_idx: CaseIdx, input: &Input, - deployment_receipts: HashMap, + mut deployment_receipts: HashMap, node: &T::Blockchain, - ) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> { - tracing::trace!("Calling execute_input for input: {input:?}"); - - let receipt = match input.method { + ) -> anyhow::Result { + match input.method { // This input was already executed when `handle_input` was called. We just need to // lookup the transaction receipt in this case and continue on. Method::Deployer => deployment_receipts - .get(&input.instance) - .context("Failed to find deployment receipt")? - .clone(), + .remove(&input.instance) + .context("Failed to find deployment receipt"), Method::Fallback | Method::FunctionName(_) => { let tx = match input .legacy_transaction(self.deployed_contracts.entry(case_idx).or_default(), node) @@ -337,35 +340,225 @@ where tracing::trace!("Executing transaction for input: {input:?}"); match node.execute_transaction(tx) { - Ok(receipt) => receipt, + Ok(receipt) => Ok(receipt), Err(err) => { tracing::error!( "Failed to execute transaction when executing the contract: {}, {:?}", &*input.instance, err ); - return Err(err); + Err(err) } } } + } + } + + fn handle_input_expectations( + &mut self, + case_idx: CaseIdx, + input: &Input, + execution_receipt: &TransactionReceipt, + node: &T::Blockchain, + ) -> anyhow::Result<()> { + let span = tracing::info_span!("Handling input expectations"); + let _guard = span.enter(); + + // Resolving the `input.expected` into a series of expectations that we can then assert on. + let expectations = match input { + // This is a bit of a special case and we have to support it separately on it's own. If + // it's a call to the deployer method, then the tests will assert that it "returns" the + // address of the contract. Deployments do not return the address of the contract but + // the runtime code of the contracts. Therefore, this assertion would always fail. So, + // we replace it with an assertion of "check if it succeeded" + Input { + expected: Some(Expected::Calldata(Calldata::Compound(compound))), + method: Method::Deployer, + .. + } if compound.len() == 1 + && compound + .first() + .is_some_and(|first| first.contains(".address")) => + { + vec![ExpectedOutput::new().with_success()] + } + Input { + expected: Some(Expected::Calldata(calldata)), + .. + } => vec![ExpectedOutput::new().with_calldata(calldata.clone())], + Input { + expected: Some(Expected::Expected(expected)), + .. + } => vec![expected.clone()], + Input { + expected: Some(Expected::ExpectedMany(expected)), + .. + } => expected.clone(), + Input { expected: None, .. } => vec![ExpectedOutput::new().with_success()], }; - tracing::trace!( - "Transaction receipt for executed contract: {} - {:?}", - &*input.instance, - receipt, - ); + // Note: we need to do assertions and checks on the output of the last call and this isn't + // available in the receipt. The only way to get this information is through tracing on the + // node. + let tracing_result = node + .trace_transaction( + execution_receipt, + GethDebugTracingOptions { + config: GethDefaultTracingOptions::default().with_enable_return_data(true), + ..Default::default() + }, + )? + .try_into_default_frame() + .expect("Impossible. We can't request default tracing and get some other type back"); - let trace = node.trace_transaction(receipt.clone())?; - tracing::trace!( - "Trace result for contract: {} - {:?}", - &*input.instance, - trace - ); + for expectation in expectations.iter() { + self.handle_input_expectation_item( + case_idx, + execution_receipt, + node, + expectation, + &tracing_result, + )?; + } - let diff = node.state_diff(receipt.clone())?; + Ok(()) + } - Ok((receipt, trace, diff)) + fn handle_input_expectation_item( + &mut self, + case_idx: CaseIdx, + execution_receipt: &TransactionReceipt, + node: &T::Blockchain, + expectation: &ExpectedOutput, + tracing_result: &DefaultFrame, + ) -> anyhow::Result<()> { + // TODO: We want to respect the compiler version filter on the expected output but would + // require some changes to the interfaces of the compiler and such. So, we add it later. + // Additionally, what happens if the compiler filter doesn't match? Do we consider that the + // transaction should succeed? Do we just ignore the expectation? + + // Handling the receipt state assertion. + let expected = !expectation.exception; + let actual = execution_receipt.status(); + if actual != expected { + tracing::error!( + ?execution_receipt, + expected, + actual, + "Transaction status assertion failed", + ); + anyhow::bail!( + "Transaction status assertion failed - Expected {expected} but got {actual}", + ); + } + + // Handling the calldata assertion + if let Some(ref expected_calldata) = expectation.return_data { + let expected = expected_calldata + .calldata(self.deployed_contracts.entry(case_idx).or_default(), node) + .map(Bytes::from)?; + let actual = tracing_result.return_value.clone(); + if !expected.starts_with(&actual) { + tracing::error!(?execution_receipt, %expected, %actual, "Calldata assertion failed"); + anyhow::bail!("Calldata assertion failed - Expected {expected} but got {actual}",); + } + } + + // Handling the events assertion + if let Some(ref expected_events) = expectation.events { + // Handling the events length assertion. + let expected = expected_events.len(); + let actual = execution_receipt.logs().len(); + if actual != expected { + tracing::error!( + ?execution_receipt, + expected, + actual, + "Event count assertion failed", + ); + anyhow::bail!( + "Event count assertion failed - Expected {expected} but got {actual}", + ); + } + + // Handling the events assertion. + for (expected_event, actual_event) in + expected_events.iter().zip(execution_receipt.logs()) + { + // Handling the emitter assertion. + if let Some(expected_address) = expected_event.address { + let expected = expected_address; + let actual = actual_event.address(); + if actual != expected { + tracing::error!( + ?execution_receipt, + %expected, + %actual, + "Event emitter assertion failed", + ); + anyhow::bail!( + "Event emitter assertion failed - Expected {expected} but got {actual}", + ); + } + } + + // Handling the topics assertion. + let expected = expected_event.topics.as_slice(); + let actual = actual_event.topics(); + if actual != expected { + tracing::error!( + ?execution_receipt, + ?expected, + ?actual, + "Event topics assertion failed", + ); + anyhow::bail!( + "Event topics assertion failed - Expected {expected:?} but got {actual:?}", + ); + } + + // Handling the values assertion. + let expected = &expected_event + .values + .calldata(self.deployed_contracts.entry(case_idx).or_default(), node) + .map(Bytes::from)?; + let actual = &actual_event.data().data; + if !expected.starts_with(actual) { + tracing::error!( + ?execution_receipt, + ?expected, + ?actual, + "Event value assertion failed", + ); + anyhow::bail!( + "Event value assertion failed - Expected {expected:?} but got {actual:?}", + ); + } + } + } + + Ok(()) + } + + fn handle_input_diff( + &mut self, + _: CaseIdx, + execution_receipt: TransactionReceipt, + node: &T::Blockchain, + ) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> { + let span = tracing::info_span!("Handling input diff"); + let _guard = span.enter(); + + let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig { + diff_mode: Some(true), + disable_code: None, + disable_storage: None, + }); + + let trace = node.trace_transaction(&execution_receipt, trace_options)?; + let diff = node.state_diff(&execution_receipt)?; + + Ok((execution_receipt, trace, diff)) } } @@ -505,7 +698,7 @@ where // For inputs if one of the inputs fail we move on to the next case (we do not move // on to the next input as it doesn't make sense. It depends on the previous one). - for (input_idx, input) in case.inputs.iter().enumerate() { + for (input_idx, input) in case.inputs_iterator().enumerate() { let tracing_span = tracing::info_span!("Handling input", input_idx); let _guard = tracing_span.enter(); @@ -513,7 +706,7 @@ where tracing::info_span!("Executing input", contract_name = ?input.instance) .in_scope(|| { let (leader_receipt, _, leader_diff) = match leader_state - .handle_input(self.metadata, case_idx, input, self.leader_node) + .handle_input(self.metadata, case_idx, &input, self.leader_node) { Ok(result) => result, Err(error) => { @@ -541,7 +734,7 @@ where .handle_input( self.metadata, case_idx, - input, + &input, self.follower_node, ) { Ok(result) => result, @@ -589,14 +782,6 @@ where tracing::trace!("Leader logs: {:?}", leader_receipt.logs()); tracing::trace!("Follower logs: {:?}", follower_receipt.logs()); } - - if leader_receipt.status() != follower_receipt.status() { - tracing::debug!( - "Mismatch in status: leader = {}, follower = {}", - leader_receipt.status(), - follower_receipt.status() - ); - } } // Note: Only consider the case as having been successful after we have processed diff --git a/crates/format/src/case.rs b/crates/format/src/case.rs index 21b620b..0013ac6 100644 --- a/crates/format/src/case.rs +++ b/crates/format/src/case.rs @@ -1,6 +1,10 @@ use serde::Deserialize; -use crate::{define_wrapper_type, input::Input, mode::Mode}; +use crate::{ + define_wrapper_type, + input::{Expected, Input}, + mode::Mode, +}; #[derive(Debug, Default, Deserialize, Clone, Eq, PartialEq)] pub struct Case { @@ -9,6 +13,7 @@ pub struct Case { pub modes: Option>, pub inputs: Vec, pub group: Option, + pub expected: Option, } define_wrapper_type!( @@ -16,3 +21,21 @@ define_wrapper_type!( #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] CaseIdx(usize); ); + +impl Case { + pub fn inputs_iterator(&self) -> impl Iterator { + let inputs_len = self.inputs.len(); + self.inputs + .clone() + .into_iter() + .enumerate() + .map(move |(idx, mut input)| { + if idx + 1 == inputs_len { + input.expected = self.expected.clone(); + input + } else { + input + } + }) + } +} diff --git a/crates/format/src/input.rs b/crates/format/src/input.rs index ed8379d..6a6f277 100644 --- a/crates/format/src/input.rs +++ b/crates/format/src/input.rs @@ -7,9 +7,9 @@ use alloy::{ primitives::{Address, Bytes, U256}, rpc::types::TransactionRequest, }; +use alloy_primitives::B256; use semver::VersionReq; use serde::Deserialize; -use serde_json::Value; use revive_dt_node_interaction::EthereumNode; @@ -40,10 +40,18 @@ pub enum Expected { #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] pub struct ExpectedOutput { - compiler_version: Option, - return_data: Option, - events: Option, - exception: Option, + pub compiler_version: Option, + pub return_data: Option, + pub events: Option>, + #[serde(default)] + pub exception: bool, +} + +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] +pub struct Event { + pub address: Option
, + pub topics: Vec, + pub values: Calldata, } #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] @@ -74,6 +82,27 @@ pub enum Method { FunctionName(String), } +impl ExpectedOutput { + pub fn new() -> Self { + Default::default() + } + + pub fn with_success(mut self) -> Self { + self.exception = false; + self + } + + pub fn with_failure(mut self) -> Self { + self.exception = true; + self + } + + pub fn with_calldata(mut self, calldata: Calldata) -> Self { + self.return_data = Some(calldata); + self + } +} + impl Default for Calldata { fn default() -> Self { Self::Compound(Default::default()) @@ -91,7 +120,17 @@ impl Calldata { } } - pub fn construct_call_data( + pub fn calldata( + &self, + deployed_contracts: &HashMap, + chain_state_provider: &impl EthereumNode, + ) -> anyhow::Result> { + let mut buffer = Vec::::with_capacity(self.size_requirement()); + self.calldata_into_slice(&mut buffer, deployed_contracts, chain_state_provider)?; + Ok(buffer) + } + + pub fn calldata_into_slice( &self, buffer: &mut Vec, deployed_contracts: &HashMap, @@ -114,8 +153,8 @@ impl Calldata { }; } } - } - todo!() + }; + Ok(()) } pub fn size_requirement(&self) -> usize { @@ -126,14 +165,6 @@ impl Calldata { } } -impl ExpectedOutput { - pub fn find_all_contract_instances(&self, vec: &mut Vec) { - if let Some(ref cd) = self.return_data { - cd.find_all_contract_instances(vec); - } - } -} - impl Input { fn instance_to_address( &self, @@ -153,12 +184,9 @@ impl Input { ) -> anyhow::Result { match self.method { Method::Deployer | Method::Fallback => { - let mut calldata = Vec::::with_capacity(self.calldata.size_requirement()); - self.calldata.construct_call_data( - &mut calldata, - deployed_contracts, - chain_state_provider, - )?; + let calldata = self + .calldata + .calldata(deployed_contracts, chain_state_provider)?; Ok(calldata.into()) } @@ -204,7 +232,7 @@ impl Input { // a new buffer for each one of the resolved arguments. let mut calldata = Vec::::with_capacity(4 + self.calldata.size_requirement()); calldata.extend(function.selector().0); - self.calldata.construct_call_data( + self.calldata.calldata_into_slice( &mut calldata, deployed_contracts, chain_state_provider, @@ -236,20 +264,6 @@ impl Input { vec.push(self.instance.clone()); self.calldata.find_all_contract_instances(&mut vec); - match &self.expected { - Some(Expected::Calldata(cd)) => { - cd.find_all_contract_instances(&mut vec); - } - Some(Expected::Expected(expected)) => { - expected.find_all_contract_instances(&mut vec); - } - Some(Expected::ExpectedMany(expected)) => { - for expected in expected { - expected.find_all_contract_instances(&mut vec); - } - } - None => {} - } vec } @@ -355,14 +369,15 @@ mod tests { fn trace_transaction( &self, - _: alloy::rpc::types::TransactionReceipt, + _: &alloy::rpc::types::TransactionReceipt, + _: alloy::rpc::types::trace::geth::GethDebugTracingOptions, ) -> anyhow::Result { unimplemented!() } fn state_diff( &self, - _: alloy::rpc::types::TransactionReceipt, + _: &alloy::rpc::types::TransactionReceipt, ) -> anyhow::Result { unimplemented!() } diff --git a/crates/node-interaction/src/lib.rs b/crates/node-interaction/src/lib.rs index afba76a..081490c 100644 --- a/crates/node-interaction/src/lib.rs +++ b/crates/node-interaction/src/lib.rs @@ -2,7 +2,7 @@ use alloy::eips::BlockNumberOrTag; use alloy::primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, ChainId, U256}; -use alloy::rpc::types::trace::geth::{DiffMode, GethTrace}; +use alloy::rpc::types::trace::geth::{DiffMode, GethDebugTracingOptions, GethTrace}; use alloy::rpc::types::{TransactionReceipt, TransactionRequest}; use anyhow::Result; @@ -15,10 +15,14 @@ pub trait EthereumNode { fn execute_transaction(&self, transaction: TransactionRequest) -> Result; /// Trace the transaction in the [TransactionReceipt] and return a [GethTrace]. - fn trace_transaction(&self, transaction: TransactionReceipt) -> Result; + fn trace_transaction( + &self, + receipt: &TransactionReceipt, + trace_options: GethDebugTracingOptions, + ) -> Result; /// Returns the state diff of the transaction hash in the [TransactionReceipt]. - fn state_diff(&self, transaction: TransactionReceipt) -> Result; + fn state_diff(&self, receipt: &TransactionReceipt) -> Result; /// Returns the next available nonce for the given [Address]. fn fetch_add_nonce(&self, address: Address) -> Result; diff --git a/crates/node/src/common.rs b/crates/node/src/common.rs new file mode 100644 index 0000000..f260062 --- /dev/null +++ b/crates/node/src/common.rs @@ -0,0 +1,78 @@ +use alloy::{ + network::{Network, TransactionBuilder}, + providers::{ + Provider, SendableTx, + fillers::{GasFiller, TxFiller}, + }, + transports::TransportResult, +}; + +#[derive(Clone, Debug)] +pub struct FallbackGasFiller { + inner: GasFiller, + default_gas_limit: u64, + default_max_fee_per_gas: u128, + default_priority_fee: u128, +} + +impl FallbackGasFiller { + pub fn new( + default_gas_limit: u64, + default_max_fee_per_gas: u128, + default_priority_fee: u128, + ) -> Self { + Self { + inner: GasFiller, + default_gas_limit, + default_max_fee_per_gas, + default_priority_fee, + } + } +} + +impl TxFiller for FallbackGasFiller +where + N: Network, +{ + type Fillable = Option<>::Fillable>; + + fn status( + &self, + tx: &::TransactionRequest, + ) -> alloy::providers::fillers::FillerControlFlow { + >::status(&self.inner, tx) + } + + fn fill_sync(&self, _: &mut alloy::providers::SendableTx) {} + + async fn prepare>( + &self, + provider: &P, + tx: &::TransactionRequest, + ) -> TransportResult { + // Try to fetch GasFiller’s “fillable” (gas_price, base_fee, estimate_gas, …) + // If it errors (i.e. tx would revert under eth_estimateGas), swallow it. + match self.inner.prepare(provider, tx).await { + Ok(fill) => Ok(Some(fill)), + Err(_) => Ok(None), + } + } + + async fn fill( + &self, + fillable: Self::Fillable, + mut tx: alloy::providers::SendableTx, + ) -> TransportResult> { + if let Some(fill) = fillable { + // our inner GasFiller succeeded — use it + self.inner.fill(fill, tx).await + } else { + if let Some(builder) = tx.as_mut_builder() { + builder.set_gas_limit(self.default_gas_limit); + builder.set_max_fee_per_gas(self.default_max_fee_per_gas); + builder.set_max_priority_fee_per_gas(self.default_priority_fee); + } + Ok(tx) + } + } +} diff --git a/crates/node/src/geth.rs b/crates/node/src/geth.rs index 4efd626..da9e2ee 100644 --- a/crates/node/src/geth.rs +++ b/crates/node/src/geth.rs @@ -20,7 +20,7 @@ use alloy::{ providers::{ Provider, ProviderBuilder, ext::DebugApi, - fillers::{FillProvider, TxFiller}, + fillers::{ChainIdFiller, FillProvider, NonceFiller, SimpleNonceManager, TxFiller}, }, rpc::types::{ TransactionReceipt, TransactionRequest, @@ -31,7 +31,7 @@ use revive_dt_config::Arguments; use revive_dt_node_interaction::{BlockingExecutor, EthereumNode}; use tracing::Level; -use crate::Node; +use crate::{Node, common::FallbackGasFiller}; static NODE_COUNT: AtomicU32 = AtomicU32::new(0); @@ -208,6 +208,10 @@ impl Instance { let wallet = self.wallet.clone(); Box::pin(async move { ProviderBuilder::new() + .disable_recommended_fillers() + .filler(FallbackGasFiller::new(500_000_000, 500_000_000, 1)) + .filler(ChainIdFiller::default()) + .filler(NonceFiller::::default()) .wallet(wallet) .connect(&connection_string) .await @@ -224,7 +228,7 @@ impl EthereumNode for Instance { ) -> anyhow::Result { let provider = self.provider(); BlockingExecutor::execute(async move { - let outer_span = tracing::debug_span!("Submitting transaction", ?transaction,); + let outer_span = tracing::debug_span!("Submitting transaction", ?transaction); let _outer_guard = outer_span.enter(); let provider = provider.await?; @@ -305,30 +309,28 @@ impl EthereumNode for Instance { #[tracing::instrument(skip_all, fields(geth_node_id = self.id))] fn trace_transaction( &self, - transaction: TransactionReceipt, + transaction: &TransactionReceipt, + trace_options: GethDebugTracingOptions, ) -> anyhow::Result { - let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig { - diff_mode: Some(true), - disable_code: None, - disable_storage: None, - }); + let tx_hash = transaction.transaction_hash; let provider = self.provider(); - BlockingExecutor::execute(async move { Ok(provider .await? - .debug_trace_transaction(transaction.transaction_hash, trace_options) + .debug_trace_transaction(tx_hash, trace_options) .await?) })? } #[tracing::instrument(skip_all, fields(geth_node_id = self.id))] - fn state_diff( - &self, - transaction: alloy::rpc::types::TransactionReceipt, - ) -> anyhow::Result { + fn state_diff(&self, transaction: &TransactionReceipt) -> anyhow::Result { + let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig { + diff_mode: Some(true), + disable_code: None, + disable_storage: None, + }); match self - .trace_transaction(transaction)? + .trace_transaction(transaction, trace_options)? .try_into_pre_state_frame()? { PreStateFrame::Diff(diff) => Ok(diff), diff --git a/crates/node/src/kitchensink.rs b/crates/node/src/kitchensink.rs index 6ffe83e..cdb94b0 100644 --- a/crates/node/src/kitchensink.rs +++ b/crates/node/src/kitchensink.rs @@ -23,7 +23,7 @@ use alloy::{ providers::{ Provider, ProviderBuilder, ext::DebugApi, - fillers::{FillProvider, TxFiller}, + fillers::{ChainIdFiller, FillProvider, NonceFiller, SimpleNonceManager, TxFiller}, }, rpc::types::{ TransactionReceipt, @@ -40,7 +40,7 @@ use tracing::Level; use revive_dt_config::Arguments; use revive_dt_node_interaction::{BlockingExecutor, EthereumNode}; -use crate::Node; +use crate::{Node, common::FallbackGasFiller}; static NODE_COUNT: AtomicU32 = AtomicU32::new(0); @@ -352,6 +352,14 @@ impl KitchensinkNode { let wallet = self.wallet.clone(); Box::pin(async move { ProviderBuilder::new() + .disable_recommended_fillers() + .filler(FallbackGasFiller::new( + 30_000_000, + 200_000_000_000, + 3_000_000_000, + )) + .filler(ChainIdFiller::default()) + .filler(NonceFiller::::default()) .network::() .wallet(wallet) .connect(&connection_string) @@ -384,27 +392,28 @@ impl EthereumNode for KitchensinkNode { #[tracing::instrument(skip_all, fields(kitchensink_node_id = self.id))] fn trace_transaction( &self, - transaction: TransactionReceipt, + transaction: &TransactionReceipt, + trace_options: GethDebugTracingOptions, ) -> anyhow::Result { - let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig { - diff_mode: Some(true), - disable_code: None, - disable_storage: None, - }); + let tx_hash = transaction.transaction_hash; let provider = self.provider(); - BlockingExecutor::execute(async move { Ok(provider .await? - .debug_trace_transaction(transaction.transaction_hash, trace_options) + .debug_trace_transaction(tx_hash, trace_options) .await?) })? } #[tracing::instrument(skip_all, fields(kitchensink_node_id = self.id))] - fn state_diff(&self, transaction: TransactionReceipt) -> anyhow::Result { + fn state_diff(&self, transaction: &TransactionReceipt) -> anyhow::Result { + let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig { + diff_mode: Some(true), + disable_code: None, + disable_storage: None, + }); match self - .trace_transaction(transaction)? + .trace_transaction(transaction, trace_options)? .try_into_pre_state_frame()? { PreStateFrame::Diff(diff) => Ok(diff), diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index 7552ae6..44eaf19 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -3,6 +3,7 @@ use revive_dt_config::Arguments; use revive_dt_node_interaction::EthereumNode; +pub mod common; pub mod geth; pub mod kitchensink; pub mod pool;