Skip to content

Commit fd4cf14

Browse files
0xKarl98mattsse
andauthored
feat: Fix geth prestate filtering for prefunded creations (#383)
This aims at resolving issues mentioned in #382 , typically this one : <img width="1818" height="694" alt="image" src="https://github.com/user-attachments/assets/51b82944-c711-421b-8479-05095ae9bbce" /> To fix this , what i did was : - Keep track of addresses that are marked as Create and were truly empty in the database snapshot. Only these addresses are filtered from the prestate diff so prefunded contract deployments remain visible. - Introduce account_was_empty helper to avoid re-computing the emptiness check and add a regression test (prestate_diff_keeps_prefunded_created_accounts) that covers a prefunded creation vs. a true empty-address creation. cc @mattsse Please check it out if it resolves should close this one paradigmxyz/reth#19703 --------- Co-authored-by: Matthias Seitz <[email protected]>
1 parent 036116c commit fd4cf14

File tree

1 file changed

+63
-2
lines changed

1 file changed

+63
-2
lines changed

src/tracing/builder/geth.rs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use revm::{
2222
bytecode::opcode,
2323
context_interface::result::{HaltReasonTr, ResultAndState},
2424
primitives::KECCAK_EMPTY,
25-
state::EvmState,
25+
state::{AccountInfo, EvmState},
2626
DatabaseRef,
2727
};
2828

@@ -318,7 +318,11 @@ impl<'a> GethTraceBuilder<'a> {
318318
state_diff.pre.insert(addr, pre_state);
319319

320320
// determine the change type
321-
let pre_change = if changed_acc.is_created() {
321+
// if the account was created _and_ not empty we need to treat it as modified,
322+
// so that it is retained later in `diff_traces`
323+
// See <https://etherscan.io/tx/0x391f4b6a382d3bcc3120adc2ea8c62003e604e487d97281129156fd284a1a89d>
324+
// <https://github.com/paradigmxyz/reth/issues/19703#issuecomment-3527067849>
325+
let pre_change = if changed_acc.is_created() && account_was_empty(&db_acc) {
322326
AccountChangeKind::Create
323327
} else {
324328
AccountChangeKind::Modify
@@ -567,3 +571,60 @@ impl<'a> GethTraceBuilder<'a> {
567571
}
568572
}
569573
}
574+
575+
fn account_was_empty(account: &AccountInfo) -> bool {
576+
account.balance.is_zero() && account.nonce == 0 && account.code_hash == KECCAK_EMPTY
577+
}
578+
579+
#[cfg(test)]
580+
mod tests {
581+
use super::*;
582+
use alloy_primitives::{address, U256};
583+
use revm::{
584+
database::CacheDB,
585+
database_interface::EmptyDB,
586+
state::{Account, AccountInfo},
587+
};
588+
589+
#[test]
590+
fn prestate_diff_keeps_prefunded_created_accounts() {
591+
let mut state = EvmState::default();
592+
let prefunded_addr = address!("1000000000000000000000000000000000000001");
593+
let empty_addr = address!("2000000000000000000000000000000000000002");
594+
595+
let mut prefunded_account = Account::default();
596+
prefunded_account.mark_created();
597+
prefunded_account.info.balance = U256::from(1);
598+
prefunded_account.info.nonce = 1;
599+
state.insert(prefunded_addr, prefunded_account);
600+
601+
let mut empty_account = Account::default();
602+
empty_account.mark_created();
603+
empty_account.info.nonce = 1;
604+
state.insert(empty_addr, empty_account);
605+
606+
let mut db = CacheDB::new(EmptyDB::default());
607+
db.insert_account_info(
608+
prefunded_addr,
609+
AccountInfo { balance: U256::from(10), ..Default::default() },
610+
);
611+
612+
let builder = GethTraceBuilder::new(Vec::new());
613+
let frame =
614+
builder.geth_prestate_diff_traces(&state, db, false, false).expect("diff frame");
615+
616+
match frame {
617+
PreStateFrame::Diff(diff) => {
618+
assert!(
619+
diff.pre.contains_key(&prefunded_addr),
620+
"prefunded contract must remain in prestate diff"
621+
);
622+
assert!(
623+
!diff.pre.contains_key(&empty_addr),
624+
"contracts created on empty addresses are still filtered out"
625+
);
626+
}
627+
_ => panic!("expected diff prestate frame"),
628+
}
629+
}
630+
}

0 commit comments

Comments
 (0)