@@ -278,4 +278,69 @@ public long getGasLimit() {
278278
279279 assertThat (result .getHaltReason ()).isEqualTo (ExceptionalHaltReason .INSUFFICIENT_GAS );
280280 }
281+
282+ @ Test
283+ void oversizedInitcodeHaltsBeforeChargingStateGas () {
284+ // EIP-8037: A CREATE with initcode exceeding maxInitcodeSize must halt with CODE_TOO_LARGE
285+ // BEFORE any state gas is charged, so the state gas reservoir remains unchanged.
286+ final long blockGasLimit = 36_000_000L ;
287+ final GasCalculator amsterdamCalc = new AmsterdamGasCalculator ();
288+ final FakeCreateOperation amsterdamOp = new FakeCreateOperation (amsterdamCalc );
289+
290+ final EVM evm = MainnetEVMs .amsterdam (EvmConfiguration .DEFAULT );
291+ final int maxInitcodeSize = evm .getMaxInitcodeSize ();
292+ // Size just over the limit
293+ final int oversizedLength = maxInitcodeSize + 1 ;
294+
295+ final UInt256 memoryOffset = UInt256 .ZERO ;
296+ final MessageFrame frame =
297+ MessageFrame .builder ()
298+ .type (MessageFrame .Type .CONTRACT_CREATION )
299+ .contract (Address .ZERO )
300+ .inputData (Bytes .EMPTY )
301+ .sender (Address .fromHexString (SENDER ))
302+ .value (Wei .ZERO )
303+ .apparentValue (Wei .ZERO )
304+ .code (new Code (SIMPLE_CREATE ))
305+ .completer (__ -> {})
306+ .address (Address .fromHexString (SENDER ))
307+ .blockHashLookup ((__ , ___ ) -> Hash .ZERO )
308+ .blockValues (
309+ new FakeBlockValues (1337 ) {
310+ @ Override
311+ public long getGasLimit () {
312+ return blockGasLimit ;
313+ }
314+ })
315+ .gasPrice (Wei .ZERO )
316+ .miningBeneficiary (Address .ZERO )
317+ .originator (Address .ZERO )
318+ .initialGas (10_000_000L )
319+ .worldUpdater (worldUpdater )
320+ .build ();
321+
322+ // Push CREATE args: value=0, offset=0, size=oversizedLength
323+ frame .pushStackItem (Bytes .ofUnsignedLong (oversizedLength ));
324+ frame .pushStackItem (memoryOffset );
325+ frame .pushStackItem (Bytes .EMPTY ); // value = 0
326+
327+ when (account .getNonce ()).thenReturn (55L );
328+ when (account .getBalance ()).thenReturn (Wei .ZERO );
329+ when (worldUpdater .getAccount (any ())).thenReturn (account );
330+ when (worldUpdater .get (any ())).thenReturn (account );
331+ when (worldUpdater .getSenderAccount (any ())).thenReturn (account );
332+ when (worldUpdater .getOrCreate (any ())).thenReturn (newAccount );
333+ when (newAccount .getCode ()).thenReturn (Bytes .EMPTY );
334+ when (newAccount .isStorageEmpty ()).thenReturn (true );
335+ when (worldUpdater .updater ()).thenReturn (worldUpdater );
336+
337+ final long stateGasBefore = frame .getStateGasReservoir ();
338+
339+ final Operation .OperationResult result = amsterdamOp .execute (frame , evm );
340+
341+ assertThat (result .getHaltReason ()).isEqualTo (ExceptionalHaltReason .CODE_TOO_LARGE );
342+ assertThat (frame .getStateGasReservoir ())
343+ .as ("State gas reservoir must be unchanged — no state gas charged for oversized initcode" )
344+ .isEqualTo (stateGasBefore );
345+ }
281346}
0 commit comments