Skip to content

Commit 678498d

Browse files
authored
Fix ExpectRevert family of cheats (#458)
* fix expect_revert
1 parent 217e905 commit 678498d

File tree

4 files changed

+150
-45
lines changed

4 files changed

+150
-45
lines changed

crates/forge/tests/it/revive/cheats_individual.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ macro_rules! revive_cheat_test_original {
4141

4242
revive_cheat_test!(test_custom_nonce, "Nonce");
4343
revive_cheat_test_original!(test_nonce, "Nonce");
44+
revive_cheat_test_original!(test_expect_revert, "ExpectRevert");
4445
revive_cheat_test!(test_coinbase, "CoinBase");
4546
revive_cheat_test!(test_set_custom_blockhash, "SetBlockhash");
4647
revive_cheat_test_original!(test_set_blockhash, "SetBlockhash");

crates/revive-strategy/src/cheatcodes/mod.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -875,7 +875,7 @@ impl foundry_cheatcodes::CheatcodeInspectorStrategyExt for PvmCheatcodeInspector
875875

876876
let gas_price_pvm =
877877
sp_core::U256::from_little_endian(&U256::from(ecx.tx.gas_price).as_le_bytes());
878-
let mut tracer = Tracer::new(true);
878+
let mut tracer = Tracer::new();
879879
let caller_h160 = H160::from_slice(input.caller().as_slice());
880880

881881
let res = ctx.externalities.execute_with(|| {
@@ -1042,7 +1042,7 @@ impl foundry_cheatcodes::CheatcodeInspectorStrategyExt for PvmCheatcodeInspector
10421042
let should_bump_nonce = !call.is_static;
10431043
let caller_h160 = H160::from_slice(call.caller.as_slice());
10441044

1045-
let mut tracer = Tracer::new(true);
1045+
let mut tracer = Tracer::new();
10461046
let res = ctx.externalities.execute_with(|| {
10471047
// Watch the caller's address so its nonce changes get tracked in prestate trace
10481048
tracer.watch_address(&caller_h160);
@@ -1238,8 +1238,11 @@ fn post_exec(
12381238
}
12391239

12401240
if let Some(expected_revert) = &mut state.expected_revert {
1241-
expected_revert.max_depth =
1242-
std::cmp::max(ecx.journaled_state.depth() + 1, expected_revert.max_depth);
1241+
expected_revert.max_depth = std::cmp::max(
1242+
ecx.journaled_state.depth() + tracer.revert_tracer.max_depth,
1243+
expected_revert.max_depth,
1244+
);
1245+
expected_revert.reverted_by = tracer.revert_tracer.has_reverted.map(|x| Address::from(x.0));
12431246
}
12441247
}
12451248

crates/revive-strategy/src/tracing/mod.rs

Lines changed: 59 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,22 @@ use polkadot_sdk::pallet_revive::{
1111
},
1212
tracing::{Tracing, trace as trace_revive},
1313
};
14+
use revert_tracer::RevertTracer;
1415
use revive_env::Runtime;
1516
use revm::{context::JournalTr, database::states::StorageSlot, state::Bytecode};
1617
use storage_tracer::{AccountAccess, StorageTracer};
18+
mod revert_tracer;
1719
pub mod storage_tracer;
1820

1921
pub struct Tracer {
2022
pub call_tracer: CallTracer<U256, fn(Weight) -> U256>,
2123
pub prestate_tracer: PrestateTracer<Runtime>,
22-
pub storage_accesses: Option<StorageTracer>,
24+
pub storage_accesses: StorageTracer,
25+
pub revert_tracer: RevertTracer,
2326
}
2427

2528
impl Tracer {
26-
pub fn new(is_recording: bool) -> Self {
29+
pub fn new() -> Self {
2730
let call_tracer =
2831
match Pallet::<revive_env::Runtime>::evm_tracer(TracerType::CallTracer(None)) {
2932
ReviveTracer::CallTracer(tracer) => tracer,
@@ -37,9 +40,12 @@ impl Tracer {
3740
disable_code: false,
3841
});
3942

40-
let storage_tracer = if is_recording { Some(Default::default()) } else { None };
41-
42-
Self { call_tracer, prestate_tracer, storage_accesses: storage_tracer }
43+
Self {
44+
call_tracer,
45+
prestate_tracer,
46+
storage_accesses: Default::default(),
47+
revert_tracer: RevertTracer::new(),
48+
}
4349
}
4450

4551
pub fn trace<R, F: FnOnce() -> R>(&mut self, f: F) -> R {
@@ -58,7 +64,7 @@ impl Tracer {
5864

5965
/// Collects recorded accesses
6066
pub fn get_recorded_accesses(&mut self) -> Vec<AccountAccess> {
61-
self.storage_accesses.take().unwrap_or_default().get_records()
67+
self.storage_accesses.get_records()
6268
}
6369

6470
/// Applies `PrestateTrace` diffs to the revm state
@@ -146,9 +152,21 @@ impl Tracing for Tracer {
146152
fn watch_address(&mut self, addr: &polkadot_sdk::sp_core::H160) {
147153
self.prestate_tracer.watch_address(addr);
148154
self.call_tracer.watch_address(addr);
149-
if let Some(storage_tracer) = &mut self.storage_accesses {
150-
storage_tracer.watch_address(addr);
151-
}
155+
self.storage_accesses.watch_address(addr);
156+
self.revert_tracer.watch_address(addr);
157+
}
158+
159+
fn terminate(
160+
&mut self,
161+
contract_address: polkadot_sdk::sp_core::H160,
162+
beneficiary_address: polkadot_sdk::sp_core::H160,
163+
gas_left: Weight,
164+
value: U256,
165+
) {
166+
self.prestate_tracer.terminate(contract_address, beneficiary_address, gas_left, value);
167+
self.call_tracer.terminate(contract_address, beneficiary_address, gas_left, value);
168+
self.storage_accesses.terminate(contract_address, beneficiary_address, gas_left, value);
169+
self.revert_tracer.terminate(contract_address, beneficiary_address, gas_left, value);
152170
}
153171

154172
fn enter_child_span(
@@ -179,17 +197,24 @@ impl Tracing for Tracer {
179197
input,
180198
gas,
181199
);
182-
if let Some(storage_tracer) = &mut self.storage_accesses {
183-
storage_tracer.enter_child_span(
184-
from,
185-
to,
186-
is_delegate_call,
187-
is_read_only,
188-
value,
189-
input,
190-
gas,
191-
)
192-
}
200+
self.storage_accesses.enter_child_span(
201+
from,
202+
to,
203+
is_delegate_call,
204+
is_read_only,
205+
value,
206+
input,
207+
gas,
208+
);
209+
self.revert_tracer.enter_child_span(
210+
from,
211+
to,
212+
is_delegate_call,
213+
is_read_only,
214+
value,
215+
input,
216+
gas,
217+
)
193218
}
194219

195220
fn instantiate_code(
@@ -199,25 +224,22 @@ impl Tracing for Tracer {
199224
) {
200225
self.prestate_tracer.instantiate_code(code, salt);
201226
self.call_tracer.instantiate_code(code, salt);
202-
if let Some(storage_tracer) = &mut self.storage_accesses {
203-
storage_tracer.instantiate_code(code, salt);
204-
}
227+
self.storage_accesses.instantiate_code(code, salt);
228+
self.revert_tracer.instantiate_code(code, salt);
205229
}
206230

207231
fn balance_read(&mut self, addr: &polkadot_sdk::sp_core::H160, value: U256) {
208232
self.prestate_tracer.balance_read(addr, value);
209233
self.call_tracer.balance_read(addr, value);
210-
if let Some(storage_tracer) = &mut self.storage_accesses {
211-
storage_tracer.balance_read(addr, value);
212-
}
234+
self.storage_accesses.balance_read(addr, value);
235+
self.revert_tracer.balance_read(addr, value);
213236
}
214237

215238
fn storage_read(&mut self, key: &polkadot_sdk::pallet_revive::Key, value: Option<&[u8]>) {
216239
self.prestate_tracer.storage_read(key, value);
217240
self.call_tracer.storage_read(key, value);
218-
if let Some(storage_tracer) = &mut self.storage_accesses {
219-
storage_tracer.storage_read(key, value);
220-
}
241+
self.storage_accesses.storage_read(key, value);
242+
self.revert_tracer.storage_read(key, value);
221243
}
222244

223245
fn storage_write(
@@ -228,9 +250,8 @@ impl Tracing for Tracer {
228250
) {
229251
self.prestate_tracer.storage_write(key, old_value.clone(), new_value);
230252
self.call_tracer.storage_write(key, old_value.clone(), new_value);
231-
if let Some(storage_tracer) = &mut self.storage_accesses {
232-
storage_tracer.storage_write(key, old_value, new_value);
233-
}
253+
self.storage_accesses.storage_write(key, old_value.clone(), new_value);
254+
self.revert_tracer.storage_write(key, old_value, new_value);
234255
}
235256

236257
fn log_event(
@@ -241,9 +262,8 @@ impl Tracing for Tracer {
241262
) {
242263
self.prestate_tracer.log_event(event, topics, data);
243264
self.call_tracer.log_event(event, topics, data);
244-
if let Some(storage_tracer) = &mut self.storage_accesses {
245-
storage_tracer.log_event(event, topics, data);
246-
}
265+
self.storage_accesses.log_event(event, topics, data);
266+
self.revert_tracer.log_event(event, topics, data);
247267
}
248268

249269
fn exit_child_span(
@@ -253,9 +273,8 @@ impl Tracing for Tracer {
253273
) {
254274
self.prestate_tracer.exit_child_span(output, gas_left);
255275
self.call_tracer.exit_child_span(output, gas_left);
256-
if let Some(storage_tracer) = &mut self.storage_accesses {
257-
storage_tracer.exit_child_span(output, gas_left);
258-
}
276+
self.storage_accesses.exit_child_span(output, gas_left);
277+
self.revert_tracer.exit_child_span(output, gas_left);
259278
}
260279

261280
fn exit_child_span_with_error(
@@ -265,8 +284,7 @@ impl Tracing for Tracer {
265284
) {
266285
self.prestate_tracer.exit_child_span_with_error(error, gas_left);
267286
self.call_tracer.exit_child_span_with_error(error, gas_left);
268-
if let Some(storage_tracer) = &mut self.storage_accesses {
269-
storage_tracer.exit_child_span_with_error(error, gas_left);
270-
}
287+
self.storage_accesses.exit_child_span_with_error(error, gas_left);
288+
self.revert_tracer.exit_child_span_with_error(error, gas_left);
271289
}
272290
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use polkadot_sdk::{
2+
pallet_revive::{Code, tracing::Tracing},
3+
sp_core::{H160, U256},
4+
sp_weights::Weight,
5+
};
6+
7+
#[derive(Debug)]
8+
pub(crate) struct RevertTracer {
9+
pub max_depth: usize,
10+
pub has_reverted: Option<polkadot_sdk::sp_core::H160>,
11+
pub calls: Vec<polkadot_sdk::sp_core::H160>,
12+
pub is_create: bool,
13+
pub call_types: Vec<Type>,
14+
}
15+
16+
#[derive(Debug)]
17+
pub enum Type {
18+
Create,
19+
Rest,
20+
}
21+
22+
impl RevertTracer {
23+
fn current_addr(&self) -> H160 {
24+
self.calls.last().copied().unwrap_or_default()
25+
}
26+
pub fn new() -> Self {
27+
Self {
28+
max_depth: 1,
29+
has_reverted: None,
30+
calls: vec![],
31+
is_create: false,
32+
call_types: vec![],
33+
}
34+
}
35+
}
36+
37+
impl Tracing for RevertTracer {
38+
fn instantiate_code(&mut self, _code: &Code, _salt: Option<&[u8; 32]>) {
39+
self.is_create = true;
40+
}
41+
42+
fn enter_child_span(
43+
&mut self,
44+
_from: H160,
45+
to: H160,
46+
_is_delegate_call: bool,
47+
_is_read_only: bool,
48+
_value: U256,
49+
_input: &[u8],
50+
_gas: Weight,
51+
) {
52+
self.call_types.push(if self.is_create { Type::Create } else { Type::Rest });
53+
54+
self.calls.push(if self.call_types.last().is_some_and(|x| matches!(x, Type::Create)) {
55+
self.current_addr()
56+
} else {
57+
to
58+
});
59+
60+
if self.has_reverted.is_none() {
61+
self.max_depth += 1;
62+
}
63+
}
64+
65+
fn exit_child_span(
66+
&mut self,
67+
output: &polkadot_sdk::pallet_revive::ExecReturnValue,
68+
_gas_left: Weight,
69+
) {
70+
let addr = self.calls.pop().unwrap_or_default();
71+
72+
if output.did_revert() && self.has_reverted.is_none() {
73+
self.has_reverted = Some(addr);
74+
}
75+
let typ = self.call_types.pop();
76+
if typ.is_some_and(|x| matches!(x, Type::Create)) {
77+
self.is_create = false;
78+
}
79+
if self.has_reverted.is_none() {
80+
self.max_depth -= 1;
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)