Skip to content

Commit dee35be

Browse files
committed
fix(inspector): ignore stale selfdestruct journal entries
1 parent 5199506 commit dee35be

2 files changed

Lines changed: 50 additions & 6 deletions

File tree

crates/inspector/src/handler.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ where
234234
CTX: ContextTr<Journal: JournalExt> + Host,
235235
IT: InterpreterTypes,
236236
{
237+
let mut instruction_journal_i = None;
237238
loop {
238239
inspector.step(interpreter, context);
239240
if interpreter.bytecode.is_end() {
@@ -242,6 +243,7 @@ where
242243
}
243244

244245
let opcode = interpreter.bytecode.opcode();
246+
instruction_journal_i = Some(context.journal().journal().len());
245247
if let Err(e) = interpreter.step(instructions, gas_table, context) {
246248
cold_path();
247249
if interpreter.bytecode.action().is_none() {
@@ -266,7 +268,9 @@ where
266268
// Handle selfdestruct.
267269
if let InterpreterAction::Return(result) = &next_action {
268270
if result.result == InstructionResult::SelfDestruct {
269-
inspect_selfdestruct(context, &mut inspector);
271+
if let Some(journal_i) = instruction_journal_i {
272+
inspect_selfdestruct(context, &mut inspector, journal_i);
273+
}
270274
}
271275
}
272276

@@ -299,11 +303,20 @@ fn inspect_log<CTX, IT>(
299303

300304
#[inline(never)]
301305
#[cold]
302-
fn inspect_selfdestruct<CTX, IT>(context: &mut CTX, inspector: &mut impl Inspector<CTX, IT>)
303-
where
306+
fn inspect_selfdestruct<CTX, IT>(
307+
context: &mut CTX,
308+
inspector: &mut impl Inspector<CTX, IT>,
309+
journal_i: usize,
310+
) where
304311
CTX: ContextTr<Journal: JournalExt> + Host,
305312
IT: InterpreterTypes,
306313
{
314+
let entry = context
315+
.journal_mut()
316+
.journal()
317+
.get(journal_i..)
318+
.and_then(|entries| entries.last());
319+
307320
if let Some(
308321
JournalEntry::AccountDestroyed {
309322
address: contract,
@@ -317,7 +330,7 @@ where
317330
balance,
318331
..
319332
},
320-
) = context.journal_mut().journal().last()
333+
) = entry
321334
{
322335
inspector.selfdestruct(*contract, *to, *balance);
323336
}

crates/inspector/src/inspector_tests.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ mod tests {
33
use crate::{
44
InspectCommitEvm, InspectEvm, InspectSystemCallEvm, InspectorEvent, TestInspector,
55
};
6-
use context::{Context, TxEnv};
6+
use context::{CfgEnv, Context, TxEnv};
77
use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET};
88
use handler::{ExecuteEvm, MainBuilder, MainContext};
9-
use primitives::{address, Address, Bytes, TxKind, U256};
9+
use primitives::{address, hardfork::SpecId, Address, Bytes, TxKind, U256};
1010
use state::{bytecode::opcode, AccountInfo, Bytecode};
1111

1212
#[test]
@@ -450,6 +450,37 @@ mod tests {
450450
assert_eq!(*event_beneficiary, beneficiary);
451451
}
452452

453+
#[test]
454+
fn cancun_selfdestruct_to_self_does_not_reuse_prior_journal_entry() {
455+
let code = Bytes::from(vec![opcode::ADDRESS, opcode::SELFDESTRUCT]);
456+
let ctx = Context::mainnet()
457+
.with_cfg(CfgEnv::new_with_spec(SpecId::CANCUN))
458+
.with_db(BenchmarkDB::new_bytecode(Bytecode::new_legacy(code)));
459+
let mut evm = ctx.build_mainnet_with_inspector(TestInspector::new());
460+
461+
let result = evm
462+
.inspect_one_tx(
463+
TxEnv::builder()
464+
.caller(BENCH_CALLER)
465+
.kind(TxKind::Call(BENCH_TARGET))
466+
.value(U256::from(459))
467+
.gas_limit(100_000)
468+
.gas_price(0)
469+
.build()
470+
.unwrap(),
471+
)
472+
.unwrap();
473+
assert!(result.is_success());
474+
475+
assert!(
476+
!evm.inspector
477+
.get_events()
478+
.iter()
479+
.any(|event| matches!(event, InspectorEvent::Selfdestruct { .. })),
480+
"Cancun selfdestruct-to-self must not reuse the transaction value-transfer journal entry"
481+
);
482+
}
483+
453484
#[test]
454485
fn test_comprehensive_inspector_integration() {
455486
// Complex contract with multiple operations:

0 commit comments

Comments
 (0)