From 817ebbcbec9d5060f2b7ca7f5999a37ca14c9f84 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 7 Nov 2025 11:28:24 +0100 Subject: [PATCH 01/17] Fix prestate tracer reports wrong address --- .../src/evm/tracing/prestate_tracing.rs | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs index e6bdf843be223..02ce463ff3579 100644 --- a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs +++ b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs @@ -28,8 +28,8 @@ pub struct PrestateTracer { /// The tracer configuration. config: PrestateTracerConfig, - /// The current address of the contract's which storage is being accessed. - current_addr: H160, + /// Stack of the current address of the contract's which storage is being accessed. + current_addrs: Vec, /// Whether the current call is a contract creation. is_create: Option, @@ -49,6 +49,10 @@ where Self { config, ..Default::default() } } + fn current_addr(&self) -> H160 { + self.current_addrs.last().copied().unwrap_or_default() + } + /// Returns an empty trace. pub fn empty_trace(&self) -> PrestateTrace { if self.config.diff_mode { @@ -205,16 +209,18 @@ where } if !is_delegate_call { - self.current_addr = to; + self.current_addrs.push(to); } } fn exit_child_span_with_error(&mut self, _error: crate::DispatchError, _gas_used: Weight) { + self.current_addrs.pop(); self.is_create = None; } fn exit_child_span(&mut self, output: &ExecReturnValue, _gas_used: Weight) { let create_code = self.is_create.take(); + let current_addr = self.current_addrs.pop().unwrap_or_default(); if output.did_revert() { return } @@ -228,12 +234,12 @@ where PristineCode::::get(&code_hash).map(|code| Bytes::from(code.to_vec())), } } else { - Self::bytecode(&self.current_addr) + Self::bytecode(¤t_addr) }; Self::update_prestate_info( - self.trace.1.entry(self.current_addr).or_default(), - &self.current_addr, + self.trace.1.entry(current_addr).or_default(), + ¤t_addr, code, ); } @@ -244,7 +250,7 @@ where let old_value = self .trace .0 - .entry(self.current_addr) + .entry(self.current_addr()) .or_default() .storage .entry(key.clone()) @@ -257,19 +263,19 @@ where if old_value.as_ref().map(|v| v.0.as_ref()) != new_value { self.trace .1 - .entry(self.current_addr) + .entry(self.current_addr()) .or_default() .storage .insert(key, new_value.map(|v| v.to_vec().into())); } else { - self.trace.1.entry(self.current_addr).or_default().storage.remove(&key); + self.trace.1.entry(self.current_addr()).or_default().storage.remove(&key); } } fn storage_read(&mut self, key: &Key, value: Option<&[u8]>) { self.trace .0 - .entry(self.current_addr) + .entry(self.current_addr()) .or_default() .storage .entry(key.unhashed().to_vec().into()) From d41fc0d7fc562b3bf91d6dfa24b8fb2db6c20155 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 7 Nov 2025 11:54:43 +0100 Subject: [PATCH 02/17] Fix --- .../src/evm/tracing/prestate_tracing.rs | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs index 02ce463ff3579..70dc337ff416a 100644 --- a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs +++ b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs @@ -22,17 +22,23 @@ use crate::{ use alloc::{collections::BTreeMap, vec::Vec}; use sp_core::{H160, U256}; +/// A call in the call stack. +#[derive(Debug, Default, Clone, PartialEq)] +struct Call { + /// The address of the call. + addr: H160, + /// The init code if this is a contract creation. + create_code: Option, +} + /// A tracer that traces the prestate. #[derive(frame_support::DefaultNoBound, Debug, Clone, PartialEq)] pub struct PrestateTracer { /// The tracer configuration. config: PrestateTracerConfig, - /// Stack of the current address of the contract's which storage is being accessed. - current_addrs: Vec, - - /// Whether the current call is a contract creation. - is_create: Option, + /// Stack of calls. + calls: Vec, // pre / post state trace: (BTreeMap, BTreeMap), @@ -50,7 +56,11 @@ where } fn current_addr(&self) -> H160 { - self.current_addrs.last().copied().unwrap_or_default() + self.calls.last().map(|c| c.addr.clone()).unwrap_or_default() + } + + fn current_is_create(&self) -> bool { + self.calls.last().map(|c| c.create_code.is_some()).unwrap_or_default() } /// Returns an empty trace. @@ -176,7 +186,7 @@ where } fn instantiate_code(&mut self, code: &crate::Code, _salt: Option<&[u8; 32]>) { - self.is_create = Some(code.clone()); + self.calls.last_mut().map(|c| c.create_code = Some(code.clone())); } fn enter_child_span( @@ -198,7 +208,7 @@ where ) }); - if self.is_create.is_none() { + if !self.current_is_create() { self.trace.0.entry(to).or_insert_with_key(|addr| { Self::prestate_info( addr, @@ -209,18 +219,16 @@ where } if !is_delegate_call { - self.current_addrs.push(to); + self.calls.push(Call { addr: to, create_code: None }); } } fn exit_child_span_with_error(&mut self, _error: crate::DispatchError, _gas_used: Weight) { - self.current_addrs.pop(); - self.is_create = None; + self.calls.pop(); } fn exit_child_span(&mut self, output: &ExecReturnValue, _gas_used: Weight) { - let create_code = self.is_create.take(); - let current_addr = self.current_addrs.pop().unwrap_or_default(); + let Call { addr: current_addr, create_code } = self.calls.pop().unwrap_or_default(); if output.did_revert() { return } From ac8ab225a6bc23f8f940a604811e9b5df8f377bc Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 7 Nov 2025 12:11:53 +0100 Subject: [PATCH 03/17] fix --- .../frame/revive/src/evm/tracing/prestate_tracing.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs index 70dc337ff416a..51714f0780063 100644 --- a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs +++ b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs @@ -40,6 +40,9 @@ pub struct PrestateTracer { /// Stack of calls. calls: Vec, + // The code used by create transaction + create_code: Option, + // pre / post state trace: (BTreeMap, BTreeMap), @@ -186,7 +189,11 @@ where } fn instantiate_code(&mut self, code: &crate::Code, _salt: Option<&[u8; 32]>) { - self.calls.last_mut().map(|c| c.create_code = Some(code.clone())); + if let Some(current) = self.calls.last_mut() { + current.create_code = Some(code.clone()); + } else { + self.create_code = Some(code.clone()); + } } fn enter_child_span( @@ -219,7 +226,7 @@ where } if !is_delegate_call { - self.calls.push(Call { addr: to, create_code: None }); + self.calls.push(Call { addr: to, create_code: self.create_code.take() }); } } From 5e1a5e95ba4658d163c576c52fd16eed48b9c9c9 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 7 Nov 2025 12:41:53 +0100 Subject: [PATCH 04/17] fix --- .../frame/revive/src/evm/tracing/prestate_tracing.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs index 51714f0780063..caa0583ad8278 100644 --- a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs +++ b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs @@ -215,6 +215,10 @@ where ) }); + if !is_delegate_call { + self.calls.push(Call { addr: to, create_code: self.create_code.take() }); + } + if !self.current_is_create() { self.trace.0.entry(to).or_insert_with_key(|addr| { Self::prestate_info( @@ -224,10 +228,6 @@ where ) }); } - - if !is_delegate_call { - self.calls.push(Call { addr: to, create_code: self.create_code.take() }); - } } fn exit_child_span_with_error(&mut self, _error: crate::DispatchError, _gas_used: Weight) { From 9ab36e0bb6ad4118e323404f513129678da340c5 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 7 Nov 2025 12:42:47 +0100 Subject: [PATCH 05/17] fix test --- substrate/frame/revive/src/tests/pvm.rs | 75 ++++++++++++++++--------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/substrate/frame/revive/src/tests/pvm.rs b/substrate/frame/revive/src/tests/pvm.rs index c2a765981489d..f157d5d0ac7ba 100644 --- a/substrate/frame/revive/src/tests/pvm.rs +++ b/substrate/frame/revive/src/tests/pvm.rs @@ -4240,19 +4240,31 @@ fn prestate_tracing_works() { // redact balance so that tests are resilient to weight changes let alice_redacted_balance = Some(U256::from(1)); - let test_cases: Vec<(Box, _, _)> = vec![ - ( - Box::new(|| { - builder::bare_call(addr) - .data((3u32, addr_callee).encode()) - .build_and_unwrap_result(); + struct TestCase { + description: &'static str, + exec_call: Box, + config: PrestateTracerConfig, + expected_trace: PrestateTrace, + } + + let test_cases: Vec = vec![ + TestCase { + description: "prestate mode with cross-contract call", + exec_call: Box::new({ + let addr = addr; + let addr_callee = addr_callee; + move || { + builder::bare_call(addr) + .data((3u32, addr_callee).encode()) + .build_and_unwrap_result(); + } }), - PrestateTracerConfig { + config: PrestateTracerConfig { diff_mode: false, disable_storage: false, disable_code: false, }, - PrestateTrace::Prestate(BTreeMap::from([ + expected_trace: PrestateTrace::Prestate(BTreeMap::from([ ( ALICE_ADDR, PrestateTraceInfo { @@ -4284,19 +4296,24 @@ fn prestate_tracing_works() { }, ), ])), - ), - ( - Box::new(|| { - builder::bare_call(addr) - .data((3u32, addr_callee).encode()) - .build_and_unwrap_result(); + }, + TestCase { + description: "diff mode with cross-contract call", + exec_call: Box::new({ + let addr = addr; + let addr_callee = addr_callee; + move || { + builder::bare_call(addr) + .data((3u32, addr_callee).encode()) + .build_and_unwrap_result(); + } }), - PrestateTracerConfig { + config: PrestateTracerConfig { diff_mode: true, disable_storage: false, disable_code: false, }, - PrestateTrace::DiffMode { + expected_trace: PrestateTrace::DiffMode { pre: BTreeMap::from([ ( BOB_ADDR, @@ -4332,19 +4349,23 @@ fn prestate_tracing_works() { ), ]), }, - ), - ( - Box::new(|| { - builder::bare_instantiate(Code::Upload(dummy_code.clone())) - .salt(None) - .build_and_unwrap_result(); + }, + TestCase { + description: "diff mode with contract instantiation", + exec_call: Box::new({ + let dummy_code = dummy_code.clone(); + move || { + builder::bare_instantiate(Code::Upload(dummy_code)) + .salt(None) + .build_and_unwrap_result(); + } }), - PrestateTracerConfig { + config: PrestateTracerConfig { diff_mode: true, disable_storage: false, disable_code: false, }, - PrestateTrace::DiffMode { + expected_trace: PrestateTrace::DiffMode { pre: BTreeMap::from([( ALICE_ADDR, PrestateTraceInfo { @@ -4373,10 +4394,10 @@ fn prestate_tracing_works() { ), ]), }, - ), + }, ]; - for (exec_call, config, expected_trace) in test_cases.into_iter() { + for TestCase { description, exec_call, config, expected_trace } in test_cases.into_iter() { let mut tracer = PrestateTracer::::new(config); trace(&mut tracer, || { exec_call(); @@ -4401,7 +4422,7 @@ fn prestate_tracing_works() { }, } - assert_eq!(trace, expected_trace); + assert_eq!(trace, expected_trace, "Trace mismatch for: {description}"); } }); } From f5b34f2b07d065ae87fceca6c8874298beffbf01 Mon Sep 17 00:00:00 2001 From: "cmd[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 7 Nov 2025 11:54:31 +0000 Subject: [PATCH 06/17] Update from github-actions[bot] running command 'prdoc --audience runtime_dev --bump patch' --- prdoc/pr_10239.prdoc | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 prdoc/pr_10239.prdoc diff --git a/prdoc/pr_10239.prdoc b/prdoc/pr_10239.prdoc new file mode 100644 index 0000000000000..b753fe23166aa --- /dev/null +++ b/prdoc/pr_10239.prdoc @@ -0,0 +1,7 @@ +title: '[pallet-revive] fix prestate tracer current address' +doc: +- audience: Runtime Dev + description: Fix prestate tracer not reporting the contract addresses properly. +crates: +- name: pallet-revive + bump: patch From 2b3e67caad47465e6e893e26e7be1ea587f06420 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 7 Nov 2025 14:31:56 +0100 Subject: [PATCH 07/17] fix clippy --- substrate/frame/revive/src/evm/tracing/prestate_tracing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs index caa0583ad8278..a88deca589e54 100644 --- a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs +++ b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs @@ -59,7 +59,7 @@ where } fn current_addr(&self) -> H160 { - self.calls.last().map(|c| c.addr.clone()).unwrap_or_default() + self.calls.last().map(|c| c.addr).unwrap_or_default() } fn current_is_create(&self) -> bool { From 214314fdb2f1b77129fcdc29122db8f6cc10dc6f Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 7 Nov 2025 18:11:52 +0100 Subject: [PATCH 08/17] Fixes --- .../revive/fixtures/contracts/Counter.sol | 35 +++++++++ .../src/evm/tracing/prestate_tracing.rs | 76 ++++++++++++------- substrate/frame/revive/src/tests/sol.rs | 36 +++++++++ 3 files changed, 119 insertions(+), 28 deletions(-) create mode 100644 substrate/frame/revive/fixtures/contracts/Counter.sol diff --git a/substrate/frame/revive/fixtures/contracts/Counter.sol b/substrate/frame/revive/fixtures/contracts/Counter.sol new file mode 100644 index 0000000000000..2be58e6e71cbb --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/Counter.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; +contract Counter { + uint256 public number; + + constructor() { + number = 3; + } + + function setNumber(uint256 newNumber) public returns (uint256) { + number = newNumber; + } + + function increment() public { + number++; + } +} + +contract NestedCounter { + Counter public counter; + uint256 public number; + + + constructor() { + counter = new Counter(); + counter.setNumber(10); + number = 7; + } + + function nestedNumber() public returns (uint256) { + uint256 currentNumber = counter.setNumber(number); + number++; /// This gets recorded as happening in the storage of count instead of the `NestedCounter` where it actually happens. + return currentNumber; + } +} diff --git a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs index a88deca589e54..8adf2253e2507 100644 --- a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs +++ b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs @@ -19,7 +19,10 @@ use crate::{ tracing::Tracing, AccountInfo, Code, Config, ExecReturnValue, Key, Pallet, PristineCode, Weight, }; -use alloc::{collections::BTreeMap, vec::Vec}; +use alloc::{ + collections::{BTreeMap, BTreeSet}, + vec::Vec, +}; use sp_core::{H160, U256}; /// A call in the call stack. @@ -40,9 +43,12 @@ pub struct PrestateTracer { /// Stack of calls. calls: Vec, - // The code used by create transaction + /// The code used by create transaction create_code: Option, + /// List of created contracts addresses. + created_addrs: BTreeSet, + // pre / post state trace: (BTreeMap, BTreeMap), @@ -171,6 +177,22 @@ where info.nonce = if nonce > 0 { Some(nonce) } else { None }; info } + + /// Record a read + fn account_read(&mut self, addr: H160) { + if self.created_addrs.contains(&addr) { + return + } + + let include_code = !self.config.disable_code; + self.trace.0.entry(addr).or_insert_with_key(|addr| { + Self::prestate_info( + addr, + Pallet::::evm_balance(addr), + include_code.then(|| Self::bytecode(addr)).flatten(), + ) + }); + } } impl Tracing for PrestateTracer @@ -189,11 +211,7 @@ where } fn instantiate_code(&mut self, code: &crate::Code, _salt: Option<&[u8; 32]>) { - if let Some(current) = self.calls.last_mut() { - current.create_code = Some(code.clone()); - } else { - self.create_code = Some(code.clone()); - } + self.create_code = Some(code.clone()); } fn enter_child_span( @@ -206,28 +224,17 @@ where _input: &[u8], _gas: Weight, ) { - let include_code = !self.config.disable_code; - self.trace.0.entry(from).or_insert_with_key(|addr| { - Self::prestate_info( - addr, - Pallet::::evm_balance(addr), - include_code.then(|| Self::bytecode(addr)).flatten(), - ) - }); - - if !is_delegate_call { + if is_delegate_call { + self.calls.push(Call { addr: self.current_addr(), create_code: None }); + } else { self.calls.push(Call { addr: to, create_code: self.create_code.take() }); } - if !self.current_is_create() { - self.trace.0.entry(to).or_insert_with_key(|addr| { - Self::prestate_info( - addr, - Pallet::::evm_balance(addr), - include_code.then(|| Self::bytecode(addr)).flatten(), - ) - }); + if self.current_is_create() { + self.created_addrs.insert(to); } + self.account_read(from); + self.account_read(to); } fn exit_child_span_with_error(&mut self, _error: crate::DispatchError, _gas_used: Weight) { @@ -260,12 +267,16 @@ where } fn storage_write(&mut self, key: &Key, old_value: Option>, new_value: Option<&[u8]>) { + let current_addr = self.current_addr(); + if self.created_addrs.contains(¤t_addr) { + return + } let key = Bytes::from(key.unhashed().to_vec()); let old_value = self .trace .0 - .entry(self.current_addr()) + .entry(current_addr) .or_default() .storage .entry(key.clone()) @@ -278,7 +289,7 @@ where if old_value.as_ref().map(|v| v.0.as_ref()) != new_value { self.trace .1 - .entry(self.current_addr()) + .entry(current_addr) .or_default() .storage .insert(key, new_value.map(|v| v.to_vec().into())); @@ -288,9 +299,14 @@ where } fn storage_read(&mut self, key: &Key, value: Option<&[u8]>) { + let current_addr = self.current_addr(); + if self.created_addrs.contains(¤t_addr) { + return + } + self.trace .0 - .entry(self.current_addr()) + .entry(current_addr) .or_default() .storage .entry(key.unhashed().to_vec().into()) @@ -298,6 +314,10 @@ where } fn balance_read(&mut self, addr: &H160, value: U256) { + if self.created_addrs.contains(&addr) { + return + } + let include_code = !self.config.disable_code; self.trace.0.entry(*addr).or_insert_with_key(|addr| { Self::prestate_info(addr, value, include_code.then(|| Self::bytecode(addr)).flatten()) diff --git a/substrate/frame/revive/src/tests/sol.rs b/substrate/frame/revive/src/tests/sol.rs index 96d3f91033b08..9a483d0e72054 100644 --- a/substrate/frame/revive/src/tests/sol.rs +++ b/substrate/frame/revive/src/tests/sol.rs @@ -302,3 +302,39 @@ fn dust_work_with_child_calls(fixture_type: FixtureType) { assert_eq!(crate::Pallet::::evm_balance(&addr), value); }); } + +#[test] +fn prestate_diff_mode_tracing_works() { + use crate::{ + evm::{PrestateTrace, PrestateTraceInfo, PrestateTracer, PrestateTracerConfig}, + tracing::trace, + }; + use alloc::collections::BTreeMap; + + let (counter_code, _) = compile_module_with_type("NestedCounter", FixtureType::Solc).unwrap(); + + ExtBuilder::default().build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000_000_000); + let initial_balance = Pallet::::evm_balance(&ALICE_ADDR); + + let mut tracer = PrestateTracer::::new(PrestateTracerConfig { + diff_mode: false, + disable_storage: false, + disable_code: false, + }); + + let Contract { addr: contract_addr, .. } = trace(&mut tracer, || { + builder::bare_instantiate(Code::Upload(counter_code.clone())) + .build_and_unwrap_contract() + }); + + let trace = tracer.collect_trace(); + + let expected_trace = PrestateTrace::Prestate(BTreeMap::from([( + ALICE_ADDR, + PrestateTraceInfo { balance: Some(initial_balance), ..Default::default() }, + )])); + + assert_eq!(trace, expected_trace); + }); +} From eb159854f58ce9adbf2a2cada669c9eeb77e979c Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 7 Nov 2025 18:46:43 +0100 Subject: [PATCH 09/17] add test --- substrate/frame/revive/src/tests/sol.rs | 116 ++++++++++++++++++------ 1 file changed, 87 insertions(+), 29 deletions(-) diff --git a/substrate/frame/revive/src/tests/sol.rs b/substrate/frame/revive/src/tests/sol.rs index 9a483d0e72054..f921500b7b0ea 100644 --- a/substrate/frame/revive/src/tests/sol.rs +++ b/substrate/frame/revive/src/tests/sol.rs @@ -19,14 +19,17 @@ use crate::{ assert_refcount, call_builder::VmBinaryModule, debug::DebugSettings, + evm::{PrestateTrace, PrestateTraceInfo, PrestateTracer, PrestateTracerConfig}, test_utils::{builder::Contract, ALICE, ALICE_ADDR}, tests::{ builder, test_utils::{contract_base_deposit, ensure_stored, get_contract}, AllowEvmBytecode, DebugFlag, ExtBuilder, RuntimeOrigin, Test, }, - Code, Config, Error, GenesisConfig, Pallet, PristineCode, + tracing::trace, + Code, Config, Error, GenesisConfig, Pallet, PristineCode, H160, }; +use alloc::collections::BTreeMap; use alloy_core::sol_types::{SolCall, SolInterface}; use frame_support::{assert_err, assert_ok, traits::fungible::Mutate}; use pallet_revive_fixtures::{compile_module_with_type, Fibonacci, FixtureType}; @@ -305,36 +308,91 @@ fn dust_work_with_child_calls(fixture_type: FixtureType) { #[test] fn prestate_diff_mode_tracing_works() { - use crate::{ - evm::{PrestateTrace, PrestateTraceInfo, PrestateTracer, PrestateTracerConfig}, - tracing::trace, - }; - use alloc::collections::BTreeMap; - - let (counter_code, _) = compile_module_with_type("NestedCounter", FixtureType::Solc).unwrap(); + struct TestCase { + config: PrestateTracerConfig, + build_expected_trace: Box PrestateTrace>, + } - ExtBuilder::default().build().execute_with(|| { - let _ = ::Currency::set_balance(&ALICE, 1_000_000_000_000); - let initial_balance = Pallet::::evm_balance(&ALICE_ADDR); + let test_cases = [ + TestCase { + config: PrestateTracerConfig { + diff_mode: false, + disable_storage: false, + disable_code: false, + }, + build_expected_trace: Box::new(|_contract_addr| { + let balance = Pallet::::convert_native_to_evm( + 1_000_000_000_000 - Pallet::::min_balance(), + ); + PrestateTrace::Prestate(BTreeMap::from([( + ALICE_ADDR, + PrestateTraceInfo { balance: Some(balance), ..Default::default() }, + )])) + }), + }, + TestCase { + config: PrestateTracerConfig { + diff_mode: true, + disable_storage: false, + disable_code: true, + }, + build_expected_trace: Box::new(|contract_addr| { + let balance = Pallet::::convert_native_to_evm( + 1_000_000_000_000 - Pallet::::min_balance(), + ); + let post_balance = Pallet::::evm_balance(&ALICE_ADDR); + let child_addr = crate::address::create1(&contract_addr, 1u64); + + PrestateTrace::DiffMode { + pre: BTreeMap::from([( + ALICE_ADDR, + PrestateTraceInfo { balance: Some(balance), ..Default::default() }, + )]), + post: BTreeMap::from([ + ( + ALICE_ADDR, + PrestateTraceInfo { + balance: Some(post_balance), + nonce: Some(1), + ..Default::default() + }, + ), + ( + contract_addr, + PrestateTraceInfo { + balance: Some(0.into()), + nonce: Some(2), + ..Default::default() + }, + ), + ( + child_addr, + PrestateTraceInfo { + balance: Some(0.into()), + nonce: Some(1), + ..Default::default() + }, + ), + ]), + } + }), + }, + ]; - let mut tracer = PrestateTracer::::new(PrestateTracerConfig { - diff_mode: false, - disable_storage: false, - disable_code: false, - }); + let (counter_code, _) = compile_module_with_type("NestedCounter", FixtureType::Solc).unwrap(); + for test_case in test_cases { + ExtBuilder::default().build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000_000_000); + let mut tracer = PrestateTracer::::new(test_case.config); + + let Contract { addr: contract_addr, .. } = trace(&mut tracer, || { + builder::bare_instantiate(Code::Upload(counter_code.clone())) + .build_and_unwrap_contract() + }); - let Contract { addr: contract_addr, .. } = trace(&mut tracer, || { - builder::bare_instantiate(Code::Upload(counter_code.clone())) - .build_and_unwrap_contract() + let trace = tracer.collect_trace(); + let expected_trace = (test_case.build_expected_trace)(contract_addr); + assert_eq!(trace, expected_trace); }); - - let trace = tracer.collect_trace(); - - let expected_trace = PrestateTrace::Prestate(BTreeMap::from([( - ALICE_ADDR, - PrestateTraceInfo { balance: Some(initial_balance), ..Default::default() }, - )])); - - assert_eq!(trace, expected_trace); - }); + } } From 5f1424435c84568927350cb3c551ddc21bd63bf7 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Sun, 9 Nov 2025 14:02:26 +0100 Subject: [PATCH 10/17] fix --- .../revive/src/evm/tracing/prestate_tracing.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs index 8adf2253e2507..c6937e0b35e1b 100644 --- a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs +++ b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs @@ -94,6 +94,14 @@ where }; if self.config.diff_mode { + if include_code { + for addr in &self.created_addrs { + if let Some(info) = post.get_mut(addr) { + info.code = Self::bytecode(addr); + } + } + } + // clean up the storage that are in pre but not in post these are just read pre.iter_mut().for_each(|(addr, info)| { if let Some(post_info) = post.get(addr) { @@ -179,7 +187,7 @@ where } /// Record a read - fn account_read(&mut self, addr: H160) { + fn read_account_prestate(&mut self, addr: H160) { if self.created_addrs.contains(&addr) { return } @@ -233,8 +241,8 @@ where if self.current_is_create() { self.created_addrs.insert(to); } - self.account_read(from); - self.account_read(to); + self.read_account_prestate(from); + self.read_account_prestate(to); } fn exit_child_span_with_error(&mut self, _error: crate::DispatchError, _gas_used: Weight) { From b1fd54152bf316daa016a10ee5f5f8abaa808df8 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Sun, 9 Nov 2025 21:50:57 +0100 Subject: [PATCH 11/17] fixup tests --- substrate/frame/revive/src/tests/sol.rs | 226 +++++++++++++++++------- 1 file changed, 164 insertions(+), 62 deletions(-) diff --git a/substrate/frame/revive/src/tests/sol.rs b/substrate/frame/revive/src/tests/sol.rs index f921500b7b0ea..93754c407437c 100644 --- a/substrate/frame/revive/src/tests/sol.rs +++ b/substrate/frame/revive/src/tests/sol.rs @@ -19,7 +19,7 @@ use crate::{ assert_refcount, call_builder::VmBinaryModule, debug::DebugSettings, - evm::{PrestateTrace, PrestateTraceInfo, PrestateTracer, PrestateTracerConfig}, + evm::{PrestateTracer, PrestateTracerConfig}, test_utils::{builder::Contract, ALICE, ALICE_ADDR}, tests::{ builder, @@ -27,9 +27,8 @@ use crate::{ AllowEvmBytecode, DebugFlag, ExtBuilder, RuntimeOrigin, Test, }, tracing::trace, - Code, Config, Error, GenesisConfig, Pallet, PristineCode, H160, + Code, Config, Error, GenesisConfig, Pallet, PristineCode, }; -use alloc::collections::BTreeMap; use alloy_core::sol_types::{SolCall, SolInterface}; use frame_support::{assert_err, assert_ok, traits::fungible::Mutate}; use pallet_revive_fixtures::{compile_module_with_type, Fibonacci, FixtureType}; @@ -117,7 +116,9 @@ fn basic_evm_flow_tracing_works() { let _ = ::Currency::set_balance(&ALICE, 100_000_000_000); let Contract { addr, .. } = trace(&mut tracer, || { - builder::bare_instantiate(Code::Upload(code.clone())).build_and_unwrap_contract() + builder::bare_instantiate(Code::Upload(code.clone())) + .salt(None) + .build_and_unwrap_contract() }); let contract = get_contract(&addr); @@ -127,7 +128,7 @@ fn basic_evm_flow_tracing_works() { tracer.collect_trace().unwrap(), CallTrace { from: ALICE_ADDR, - call_type: CallType::Create2, + call_type: CallType::Create, to: addr, input: code.into(), output: runtime_code.into(), @@ -308,11 +309,21 @@ fn dust_work_with_child_calls(fixture_type: FixtureType) { #[test] fn prestate_diff_mode_tracing_works() { + use alloy_core::hex; + use pallet_revive_fixtures::NestedCounter; + struct TestCase { config: PrestateTracerConfig, - build_expected_trace: Box PrestateTrace>, + expected_instantiate_trace_json: &'static str, + expected_call_trace_json: &'static str, } + let (counter_code, _) = compile_module_with_type("NestedCounter", FixtureType::Solc).unwrap(); + let (contract_runtime_code, _) = + compile_module_with_type("NestedCounter", FixtureType::SolcRuntime).unwrap(); + let (child_runtime_code, _) = + compile_module_with_type("Counter", FixtureType::SolcRuntime).unwrap(); + let test_cases = [ TestCase { config: PrestateTracerConfig { @@ -320,79 +331,170 @@ fn prestate_diff_mode_tracing_works() { disable_storage: false, disable_code: false, }, - build_expected_trace: Box::new(|_contract_addr| { - let balance = Pallet::::convert_native_to_evm( - 1_000_000_000_000 - Pallet::::min_balance(), - ); - PrestateTrace::Prestate(BTreeMap::from([( - ALICE_ADDR, - PrestateTraceInfo { balance: Some(balance), ..Default::default() }, - )])) - }), + expected_instantiate_trace_json: r#"{ + "{{ALICE_ADDR}}": { + "balance": "{{ALICE_BALANCE_PRE}}" + } +}"#, + expected_call_trace_json: r#"{ + "{{ALICE_ADDR}}": { + "balance": "{{ALICE_BALANCE_POST}}", + "nonce": 1 + }, + "{{CONTRACT_ADDR}}": { + "balance": "0x0", + "nonce": 2, + "code": "{{CONTRACT_CODE}}", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "{{CHILD_ADDR_PADDED}}", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000007" + } + }, + "{{CHILD_ADDR}}": { + "balance": "0x0", + "nonce": 1, + "code": "{{CHILD_CODE}}", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000000000000000000000000000000000000000000a" + } + } +}"#, }, TestCase { config: PrestateTracerConfig { diff_mode: true, disable_storage: false, - disable_code: true, + disable_code: false, }, - build_expected_trace: Box::new(|contract_addr| { - let balance = Pallet::::convert_native_to_evm( - 1_000_000_000_000 - Pallet::::min_balance(), - ); - let post_balance = Pallet::::evm_balance(&ALICE_ADDR); - let child_addr = crate::address::create1(&contract_addr, 1u64); - - PrestateTrace::DiffMode { - pre: BTreeMap::from([( - ALICE_ADDR, - PrestateTraceInfo { balance: Some(balance), ..Default::default() }, - )]), - post: BTreeMap::from([ - ( - ALICE_ADDR, - PrestateTraceInfo { - balance: Some(post_balance), - nonce: Some(1), - ..Default::default() - }, - ), - ( - contract_addr, - PrestateTraceInfo { - balance: Some(0.into()), - nonce: Some(2), - ..Default::default() - }, - ), - ( - child_addr, - PrestateTraceInfo { - balance: Some(0.into()), - nonce: Some(1), - ..Default::default() - }, - ), - ]), - } - }), + expected_instantiate_trace_json: r#"{ + "pre": { + "{{ALICE_ADDR}}": { + "balance": "{{ALICE_BALANCE_PRE}}" + } + }, + "post": { + "{{ALICE_ADDR}}": { + "balance": "{{ALICE_BALANCE_POST}}", + "nonce": 1 + }, + "{{CONTRACT_ADDR}}": { + "balance": "0x0", + "nonce": 2, + "code": "{{CONTRACT_CODE}}" + }, + "{{CHILD_ADDR}}": { + "balance": "0x0", + "nonce": 1, + "code": "{{CHILD_CODE}}" + } + } +}"#, + expected_call_trace_json: r#"{ + "pre": { + "{{CONTRACT_ADDR}}": { + "balance": "0x0", + "nonce": 2, + "code": "{{CONTRACT_CODE}}", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000007" + } + }, + "{{CHILD_ADDR}}": { + "balance": "0x0", + "nonce": 1, + "code": "{{CHILD_CODE}}", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000000000000000000000000000000000000000000a" + } + } + }, + "post": { + "{{CONTRACT_ADDR}}": { + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000008" + } + }, + "{{CHILD_ADDR}}": { + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000007" + } + } + } +}"#, }, ]; - let (counter_code, _) = compile_module_with_type("NestedCounter", FixtureType::Solc).unwrap(); for test_case in test_cases { ExtBuilder::default().build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000_000_000); - let mut tracer = PrestateTracer::::new(test_case.config); - let Contract { addr: contract_addr, .. } = trace(&mut tracer, || { + let contract_addr = crate::address::create1(&ALICE_ADDR, 0u64); + let child_addr = crate::address::create1(&contract_addr, 1u64); + + // Compute balances + let alice_balance_pre = Pallet::::convert_native_to_evm( + 1_000_000_000_000 - Pallet::::min_balance(), + ); + + let replace_placeholders = |json: &str| -> String { + let alice_balance_post = Pallet::::evm_balance(&ALICE_ADDR); + + let mut child_addr_bytes = [0u8; 32]; + child_addr_bytes[12..32].copy_from_slice(child_addr.as_bytes()); + + json.replace("{{ALICE_ADDR}}", &format!("{:#x}", ALICE_ADDR)) + .replace("{{CONTRACT_ADDR}}", &format!("{:#x}", contract_addr)) + .replace("{{CHILD_ADDR}}", &format!("{:#x}", child_addr)) + .replace("{{ALICE_BALANCE_PRE}}", &format!("{:#x}", alice_balance_pre)) + .replace("{{ALICE_BALANCE_POST}}", &format!("{:#x}", alice_balance_post)) + .replace( + "{{CONTRACT_CODE}}", + &format!("0x{}", hex::encode(&contract_runtime_code)), + ) + .replace("{{CHILD_CODE}}", &format!("0x{}", hex::encode(&child_runtime_code))) + .replace( + "{{CHILD_ADDR_PADDED}}", + &format!("0x{}", hex::encode(child_addr_bytes)), + ) + }; + + let mut tracer = PrestateTracer::::new(test_case.config.clone()); + let Contract { addr: contract_addr_actual, .. } = trace(&mut tracer, || { builder::bare_instantiate(Code::Upload(counter_code.clone())) + .salt(None) .build_and_unwrap_contract() }); + assert_eq!(contract_addr, contract_addr_actual, "contract address mismatch"); + + let instantiate_trace = tracer.collect_trace(); + let actual_json = serde_json::to_string_pretty(&instantiate_trace).unwrap(); + let expected_json = replace_placeholders(test_case.expected_instantiate_trace_json); + assert_eq!( + actual_json, expected_json, + "unexpected instantiate trace for {:?}", + test_case.config + ); + + let mut tracer = PrestateTracer::::new(test_case.config.clone()); + trace(&mut tracer, || { + builder::bare_call(contract_addr) + .data( + NestedCounter::NestedCounterCalls::nestedNumber( + NestedCounter::nestedNumberCall {}, + ) + .abi_encode(), + ) + .build_and_unwrap_result(); + }); - let trace = tracer.collect_trace(); - let expected_trace = (test_case.build_expected_trace)(contract_addr); - assert_eq!(trace, expected_trace); + let call_trace = tracer.collect_trace(); + let actual_json = serde_json::to_string_pretty(&call_trace).unwrap(); + let expected_json = replace_placeholders(test_case.expected_call_trace_json); + assert_eq!( + actual_json, expected_json, + "unexpected call trace for {:?}", + test_case.config + ); }); } } From 2095be1b4f28ef9761cac341cdeb298a5579b797 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Sun, 9 Nov 2025 21:57:14 +0100 Subject: [PATCH 12/17] simplify --- .../src/evm/tracing/prestate_tracing.rs | 37 ++++--------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs index c6937e0b35e1b..ec98539f470c5 100644 --- a/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs +++ b/substrate/frame/revive/src/evm/tracing/prestate_tracing.rs @@ -25,15 +25,6 @@ use alloc::{ }; use sp_core::{H160, U256}; -/// A call in the call stack. -#[derive(Debug, Default, Clone, PartialEq)] -struct Call { - /// The address of the call. - addr: H160, - /// The init code if this is a contract creation. - create_code: Option, -} - /// A tracer that traces the prestate. #[derive(frame_support::DefaultNoBound, Debug, Clone, PartialEq)] pub struct PrestateTracer { @@ -41,7 +32,7 @@ pub struct PrestateTracer { config: PrestateTracerConfig, /// Stack of calls. - calls: Vec, + calls: Vec, /// The code used by create transaction create_code: Option, @@ -65,11 +56,7 @@ where } fn current_addr(&self) -> H160 { - self.calls.last().map(|c| c.addr).unwrap_or_default() - } - - fn current_is_create(&self) -> bool { - self.calls.last().map(|c| c.create_code.is_some()).unwrap_or_default() + self.calls.last().copied().unwrap_or_default() } /// Returns an empty trace. @@ -233,12 +220,12 @@ where _gas: Weight, ) { if is_delegate_call { - self.calls.push(Call { addr: self.current_addr(), create_code: None }); + self.calls.push(self.current_addr()); } else { - self.calls.push(Call { addr: to, create_code: self.create_code.take() }); + self.calls.push(to); } - if self.current_is_create() { + if self.create_code.take().is_some() { self.created_addrs.insert(to); } self.read_account_prestate(from); @@ -250,22 +237,12 @@ where } fn exit_child_span(&mut self, output: &ExecReturnValue, _gas_used: Weight) { - let Call { addr: current_addr, create_code } = self.calls.pop().unwrap_or_default(); + let current_addr = self.calls.pop().unwrap_or_default(); if output.did_revert() { return } - let code = if self.config.disable_code { - None - } else if let Some(code) = create_code { - match code { - Code::Upload(code) => Some(code.into()), - Code::Existing(code_hash) => - PristineCode::::get(&code_hash).map(|code| Bytes::from(code.to_vec())), - } - } else { - Self::bytecode(¤t_addr) - }; + let code = if self.config.disable_code { None } else { Self::bytecode(¤t_addr) }; Self::update_prestate_info( self.trace.1.entry(current_addr).or_default(), From f45f0b0d4fbf5151e9a1fc3cb3a64af7af0ef5ef Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 10 Nov 2025 10:54:43 +0100 Subject: [PATCH 13/17] nit makes tests easier to read --- .../revive/src/evm/api/debug_rpc_types.rs | 70 ++++++- substrate/frame/revive/src/tests/sol.rs | 175 +++++++++--------- 2 files changed, 154 insertions(+), 91 deletions(-) diff --git a/substrate/frame/revive/src/evm/api/debug_rpc_types.rs b/substrate/frame/revive/src/evm/api/debug_rpc_types.rs index b3a547c59eaf4..df8f82fb6fb60 100644 --- a/substrate/frame/revive/src/evm/api/debug_rpc_types.rs +++ b/substrate/frame/revive/src/evm/api/debug_rpc_types.rs @@ -21,6 +21,7 @@ use codec::{Decode, Encode}; use derive_more::From; use scale_info::TypeInfo; use serde::{ + de::{Error, MapAccess, Visitor}, ser::{SerializeMap, Serializer}, Deserialize, Serialize, }; @@ -166,7 +167,8 @@ pub enum CallType { } /// A Trace -#[derive(TypeInfo, From, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[derive(TypeInfo, Serialize, From, Encode, Decode, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "std", derive(Deserialize))] #[serde(untagged)] pub enum Trace { /// A call trace. @@ -176,7 +178,7 @@ pub enum Trace { } /// A prestate Trace -#[derive(TypeInfo, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[derive(TypeInfo, Encode, Serialize, Decode, Clone, Debug, Eq, PartialEq)] #[serde(untagged)] pub enum PrestateTrace { /// The Prestate mode returns the accounts necessary to execute a given transaction @@ -196,6 +198,68 @@ pub enum PrestateTrace { }, } +impl<'de> Deserialize<'de> for PrestateTrace { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct PrestateTraceVisitor; + + impl<'de> Visitor<'de> for PrestateTraceVisitor { + type Value = PrestateTrace; + + fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { + formatter.write_str("a map representing either Prestate or DiffMode") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut pre_map = None; + let mut post_map = None; + let mut account_map = BTreeMap::new(); + + while let Some(key) = map.next_key::()? { + match key.as_str() { + "pre" => { + if pre_map.is_some() { + return Err(Error::duplicate_field("pre")); + } + pre_map = Some(map.next_value::>()?); + }, + "post" => { + if post_map.is_some() { + return Err(Error::duplicate_field("post")); + } + post_map = Some(map.next_value::>()?); + }, + _ => { + let addr: H160 = + key.parse().map_err(|_| Error::custom("Invalid address"))?; + let info = map.next_value::()?; + account_map.insert(addr, info); + }, + } + } + + match (pre_map, post_map) { + (Some(pre), Some(post)) => { + if !account_map.is_empty() { + return Err(Error::custom("Mixed diff and prestate mode")); + } + Ok(PrestateTrace::DiffMode { pre, post }) + }, + (None, None) => Ok(PrestateTrace::Prestate(account_map)), + _ => Err(Error::custom("diff mode: must have both 'pre' and 'post'")), + } + } + } + + deserializer.deserialize_map(PrestateTraceVisitor) + } +} + impl PrestateTrace { /// Returns the pre and post trace info. pub fn state_mut( @@ -223,7 +287,7 @@ pub struct PrestateTraceInfo { #[serde(skip_serializing_if = "Option::is_none")] pub code: Option, /// The storage of the contract account. - #[serde(skip_serializing_if = "is_empty", serialize_with = "serialize_map_skip_none")] + #[serde(default, skip_serializing_if = "is_empty", serialize_with = "serialize_map_skip_none")] pub storage: BTreeMap>, } diff --git a/substrate/frame/revive/src/tests/sol.rs b/substrate/frame/revive/src/tests/sol.rs index 93754c407437c..f855480609047 100644 --- a/substrate/frame/revive/src/tests/sol.rs +++ b/substrate/frame/revive/src/tests/sol.rs @@ -19,7 +19,7 @@ use crate::{ assert_refcount, call_builder::VmBinaryModule, debug::DebugSettings, - evm::{PrestateTracer, PrestateTracerConfig}, + evm::{PrestateTrace, PrestateTracer, PrestateTracerConfig}, test_utils::{builder::Contract, ALICE, ALICE_ADDR}, tests::{ builder, @@ -31,11 +31,10 @@ use crate::{ }; use alloy_core::sol_types::{SolCall, SolInterface}; use frame_support::{assert_err, assert_ok, traits::fungible::Mutate}; -use pallet_revive_fixtures::{compile_module_with_type, Fibonacci, FixtureType}; +use pallet_revive_fixtures::{compile_module_with_type, Fibonacci, FixtureType, NestedCounter}; use pretty_assertions::assert_eq; -use test_case::test_case; - use revm::bytecode::opcode::*; +use test_case::test_case; mod arithmetic; mod bitwise; @@ -310,7 +309,6 @@ fn dust_work_with_child_calls(fixture_type: FixtureType) { #[test] fn prestate_diff_mode_tracing_works() { use alloy_core::hex; - use pallet_revive_fixtures::NestedCounter; struct TestCase { config: PrestateTracerConfig, @@ -332,33 +330,33 @@ fn prestate_diff_mode_tracing_works() { disable_code: false, }, expected_instantiate_trace_json: r#"{ - "{{ALICE_ADDR}}": { - "balance": "{{ALICE_BALANCE_PRE}}" - } -}"#, + "{{ALICE_ADDR}}": { + "balance": "{{ALICE_BALANCE_PRE}}" + } + }"#, expected_call_trace_json: r#"{ - "{{ALICE_ADDR}}": { - "balance": "{{ALICE_BALANCE_POST}}", - "nonce": 1 - }, - "{{CONTRACT_ADDR}}": { - "balance": "0x0", - "nonce": 2, - "code": "{{CONTRACT_CODE}}", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "{{CHILD_ADDR_PADDED}}", - "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000007" - } - }, - "{{CHILD_ADDR}}": { - "balance": "0x0", - "nonce": 1, - "code": "{{CHILD_CODE}}", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000000000000000000000000000000000000000000a" - } - } -}"#, + "{{ALICE_ADDR}}": { + "balance": "{{ALICE_BALANCE_POST}}", + "nonce": 1 + }, + "{{CONTRACT_ADDR}}": { + "balance": "0x0", + "nonce": 2, + "code": "{{CONTRACT_CODE}}", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "{{CHILD_ADDR_PADDED}}", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000007" + } + }, + "{{CHILD_ADDR}}": { + "balance": "0x0", + "nonce": 1, + "code": "{{CHILD_CODE}}", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000000000000000000000000000000000000000000a" + } + } + }"#, }, TestCase { config: PrestateTracerConfig { @@ -367,60 +365,60 @@ fn prestate_diff_mode_tracing_works() { disable_code: false, }, expected_instantiate_trace_json: r#"{ - "pre": { - "{{ALICE_ADDR}}": { - "balance": "{{ALICE_BALANCE_PRE}}" - } - }, - "post": { - "{{ALICE_ADDR}}": { - "balance": "{{ALICE_BALANCE_POST}}", - "nonce": 1 - }, - "{{CONTRACT_ADDR}}": { - "balance": "0x0", - "nonce": 2, - "code": "{{CONTRACT_CODE}}" - }, - "{{CHILD_ADDR}}": { - "balance": "0x0", - "nonce": 1, - "code": "{{CHILD_CODE}}" - } - } -}"#, + "pre": { + "{{ALICE_ADDR}}": { + "balance": "{{ALICE_BALANCE_PRE}}" + } + }, + "post": { + "{{ALICE_ADDR}}": { + "balance": "{{ALICE_BALANCE_POST}}", + "nonce": 1 + }, + "{{CONTRACT_ADDR}}": { + "balance": "0x0", + "nonce": 2, + "code": "{{CONTRACT_CODE}}" + }, + "{{CHILD_ADDR}}": { + "balance": "0x0", + "nonce": 1, + "code": "{{CHILD_CODE}}" + } + } + }"#, expected_call_trace_json: r#"{ - "pre": { - "{{CONTRACT_ADDR}}": { - "balance": "0x0", - "nonce": 2, - "code": "{{CONTRACT_CODE}}", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000007" - } - }, - "{{CHILD_ADDR}}": { - "balance": "0x0", - "nonce": 1, - "code": "{{CHILD_CODE}}", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000000000000000000000000000000000000000000a" - } - } - }, - "post": { - "{{CONTRACT_ADDR}}": { - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000008" - } - }, - "{{CHILD_ADDR}}": { - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000007" - } - } - } -}"#, + "pre": { + "{{CONTRACT_ADDR}}": { + "balance": "0x0", + "nonce": 2, + "code": "{{CONTRACT_CODE}}", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000007" + } + }, + "{{CHILD_ADDR}}": { + "balance": "0x0", + "nonce": 1, + "code": "{{CHILD_CODE}}", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000000000000000000000000000000000000000000a" + } + } + }, + "post": { + "{{CONTRACT_ADDR}}": { + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000008" + } + }, + "{{CHILD_ADDR}}": { + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000007" + } + } + } + }"#, }, ]; @@ -467,10 +465,11 @@ fn prestate_diff_mode_tracing_works() { assert_eq!(contract_addr, contract_addr_actual, "contract address mismatch"); let instantiate_trace = tracer.collect_trace(); - let actual_json = serde_json::to_string_pretty(&instantiate_trace).unwrap(); + let expected_json = replace_placeholders(test_case.expected_instantiate_trace_json); + let expected_trace: PrestateTrace = serde_json::from_str(&expected_json).unwrap(); assert_eq!( - actual_json, expected_json, + instantiate_trace, expected_trace, "unexpected instantiate trace for {:?}", test_case.config ); @@ -488,10 +487,10 @@ fn prestate_diff_mode_tracing_works() { }); let call_trace = tracer.collect_trace(); - let actual_json = serde_json::to_string_pretty(&call_trace).unwrap(); let expected_json = replace_placeholders(test_case.expected_call_trace_json); + let expected_trace: PrestateTrace = serde_json::from_str(&expected_json).unwrap(); assert_eq!( - actual_json, expected_json, + call_trace, expected_trace, "unexpected call trace for {:?}", test_case.config ); From 11b9bdf4ec19dd304c2eb1d6a7fd93d5cfe46ff7 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 10 Nov 2025 11:35:40 +0100 Subject: [PATCH 14/17] nit rm comment --- substrate/frame/revive/fixtures/contracts/Counter.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/frame/revive/fixtures/contracts/Counter.sol b/substrate/frame/revive/fixtures/contracts/Counter.sol index 2be58e6e71cbb..e4bde49eaab0f 100644 --- a/substrate/frame/revive/fixtures/contracts/Counter.sol +++ b/substrate/frame/revive/fixtures/contracts/Counter.sol @@ -19,7 +19,7 @@ contract Counter { contract NestedCounter { Counter public counter; uint256 public number; - + constructor() { counter = new Counter(); @@ -29,7 +29,7 @@ contract NestedCounter { function nestedNumber() public returns (uint256) { uint256 currentNumber = counter.setNumber(number); - number++; /// This gets recorded as happening in the storage of count instead of the `NestedCounter` where it actually happens. + number++; return currentNumber; } } From 2511ae3e8f4fcfbc2e7fcefd05bab23b4aa223db Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 10 Nov 2025 11:37:43 +0100 Subject: [PATCH 15/17] spacing --- substrate/frame/revive/src/tests/sol.rs | 54 ++++++++++++------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/substrate/frame/revive/src/tests/sol.rs b/substrate/frame/revive/src/tests/sol.rs index f855480609047..6da97e2cc56f8 100644 --- a/substrate/frame/revive/src/tests/sol.rs +++ b/substrate/frame/revive/src/tests/sol.rs @@ -367,55 +367,55 @@ fn prestate_diff_mode_tracing_works() { expected_instantiate_trace_json: r#"{ "pre": { "{{ALICE_ADDR}}": { - "balance": "{{ALICE_BALANCE_PRE}}" + "balance": "{{ALICE_BALANCE_PRE}}" } }, "post": { "{{ALICE_ADDR}}": { - "balance": "{{ALICE_BALANCE_POST}}", - "nonce": 1 + "balance": "{{ALICE_BALANCE_POST}}", + "nonce": 1 }, "{{CONTRACT_ADDR}}": { - "balance": "0x0", - "nonce": 2, - "code": "{{CONTRACT_CODE}}" + "balance": "0x0", + "nonce": 2, + "code": "{{CONTRACT_CODE}}" }, "{{CHILD_ADDR}}": { - "balance": "0x0", - "nonce": 1, - "code": "{{CHILD_CODE}}" + "balance": "0x0", + "nonce": 1, + "code": "{{CHILD_CODE}}" } } }"#, expected_call_trace_json: r#"{ "pre": { "{{CONTRACT_ADDR}}": { - "balance": "0x0", - "nonce": 2, - "code": "{{CONTRACT_CODE}}", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000007" - } + "balance": "0x0", + "nonce": 2, + "code": "{{CONTRACT_CODE}}", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000007" + } }, "{{CHILD_ADDR}}": { - "balance": "0x0", - "nonce": 1, - "code": "{{CHILD_CODE}}", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000000000000000000000000000000000000000000a" - } + "balance": "0x0", + "nonce": 1, + "code": "{{CHILD_CODE}}", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000000000000000000000000000000000000000000a" + } } }, "post": { "{{CONTRACT_ADDR}}": { - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000008" - } + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000008" + } }, "{{CHILD_ADDR}}": { - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000007" - } + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000007" + } } } }"#, From 1a34946dc1da22e2960b25931df32f04db7a139b Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 10 Nov 2025 11:38:21 +0100 Subject: [PATCH 16/17] more spacing --- substrate/frame/revive/src/tests/sol.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/substrate/frame/revive/src/tests/sol.rs b/substrate/frame/revive/src/tests/sol.rs index 6da97e2cc56f8..e32058779a858 100644 --- a/substrate/frame/revive/src/tests/sol.rs +++ b/substrate/frame/revive/src/tests/sol.rs @@ -344,8 +344,8 @@ fn prestate_diff_mode_tracing_works() { "nonce": 2, "code": "{{CONTRACT_CODE}}", "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "{{CHILD_ADDR_PADDED}}", - "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000007" + "0x0000000000000000000000000000000000000000000000000000000000000000": "{{CHILD_ADDR_PADDED}}", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000007" } }, "{{CHILD_ADDR}}": { @@ -353,7 +353,7 @@ fn prestate_diff_mode_tracing_works() { "nonce": 1, "code": "{{CHILD_CODE}}", "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000000000000000000000000000000000000000000a" + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000000000000000000000000000000000000000000a" } } }"#, From 02b2282099f2e6a6fdeaf424d1becd906c0ff36d Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 10 Nov 2025 12:33:22 +0100 Subject: [PATCH 17/17] fix build --- substrate/frame/revive/src/evm/api/debug_rpc_types.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/substrate/frame/revive/src/evm/api/debug_rpc_types.rs b/substrate/frame/revive/src/evm/api/debug_rpc_types.rs index df8f82fb6fb60..008dd683bcea2 100644 --- a/substrate/frame/revive/src/evm/api/debug_rpc_types.rs +++ b/substrate/frame/revive/src/evm/api/debug_rpc_types.rs @@ -167,8 +167,7 @@ pub enum CallType { } /// A Trace -#[derive(TypeInfo, Serialize, From, Encode, Decode, Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "std", derive(Deserialize))] +#[derive(TypeInfo, Deserialize, Serialize, From, Encode, Decode, Clone, Debug, Eq, PartialEq)] #[serde(untagged)] pub enum Trace { /// A call trace.