@@ -135,6 +135,47 @@ fn test_call_with_transfer_db_error_on_inspect_account() {
135135 }
136136}
137137
138+ /// Regression: `inspect_storage` must not query `DB::storage` for accounts created in the
139+ /// current transaction. Their post-CREATE storage is guaranteed empty (EIP-161 / EIP-6780),
140+ /// so any DB read returns ZERO at best — and fails outright under stateless replay, where
141+ /// no witness exists for these slots when CREATE lands on a pre-funded address (its
142+ /// `Loaded` cache status bypasses revm's `State::storage` short-circuit).
143+ ///
144+ /// The injected DB is configured to error on every `storage()` read of slot 0 at the
145+ /// to-be-created address. With the fix, `inspect_storage` short-circuits to ZERO for
146+ /// newly-created accounts and the error is never triggered, so the CREATE succeeds.
147+ /// Without the fix, the constructor's SSTORE pre-read would surface `EVMError::Custom`
148+ /// (mini-rex routes SSTORE through `additional_limit_ext::sstore`, which calls
149+ /// `inspect_storage` to compute the original/present values before writing).
150+ #[ test]
151+ fn test_inspect_storage_skips_db_for_newly_created_account ( ) {
152+ // Initcode: SSTORE slot 0 = 0x42; STOP. The SSTORE pre-read goes through
153+ // `inspect_storage` and is the path the fix targets.
154+ let initcode = BytecodeBuilder :: default ( ) . sstore ( U256 :: ZERO , U256 :: from ( 0x42 ) ) . stop ( ) . build ( ) ;
155+
156+ // CALLER's nonce starts at 0, so the top-level CREATE deploys to `CALLER.create(0)`.
157+ let created = CALLER . create ( 0 ) ;
158+
159+ let mut inner_db = MemoryDatabase :: default ( ) ;
160+ inner_db. set_account_balance ( CALLER , U256 :: from ( 100_000_000_000u64 ) ) ;
161+ // Pre-fund the future contract address so its DB cache status is `Loaded` rather than
162+ // `Vacant` — this is the scenario that exposed the original bug.
163+ inner_db. set_account_balance ( created, U256 :: from ( 1 ) ) ;
164+
165+ let mut db = ErrorInjectingDatabase :: new ( inner_db) ;
166+ // The fix means this DB call must never happen. If it does, the test fails.
167+ db. fail_on_storage = Some ( ( created, U256 :: ZERO ) ) ;
168+
169+ let result = transact ( MegaSpecId :: MINI_REX , db, CALLER , None , initcode, U256 :: ZERO , 10_000_000 ) ;
170+
171+ let res = result. expect ( "CREATE should not surface a DB error" ) ;
172+ assert ! (
173+ res. result. is_success( ) ,
174+ "CREATE should succeed without DB::storage being queried, got: {:?}" ,
175+ res. result
176+ ) ;
177+ }
178+
138179/// When `inspect_account_delegated` fails during STATICCALL (in `wrap_call_with_storage_gas!`),
139180/// the EVM should halt with `FatalExternalError`.
140181/// This tests a different code path from CALL-with-transfer: STATICCALL has no value parameter
0 commit comments