Skip to content

execution: implement EIP-8037 updates for glamsterdam-devnet-6 - #22122

Merged
taratorio merged 96 commits into
mainfrom
worktree-gd6-eip-8037
Jul 13, 2026
Merged

taratorio merged 96 commits into
mainfrom
worktree-gd6-eip-8037

Conversation

@taratorio

@taratorio taratorio commented Jul 1, 2026

Copy link
Copy Markdown
Member

taratorio and others added 30 commits June 25, 2026 18:53
Base automatically changed from worktree-gd6-eip-8282 to main July 10, 2026 13:11

@yperbasis yperbasis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed against the devnet-6 pin (EELS tests-glamsterdam-devnet@v6.1.1 = d0338f56, fork amsterdam). The refill/spill model, block accounting, and 7702 delegation rules match the pinned spec, including the handleFrameRevert entry-reservoir restore. Remaining nits, none blocking:

  • verifyAuthorities has no explicit auth.nonce == 2^64-1 skip (EELS validate_authorization has one). The nonce-mismatch check yields the identical skip+refund for every reachable state, so this is spec-parity hygiene; it also avoids the theoretical SetNonce wrap in step 8.
  • chargeTopLevelFrameGas: on the OOG return, gasRemaining.State has already been zeroed by the failed useMdGas. Benign since handleFrameRevert restores the entry reservoir unconditionally, but returning the pre-charge value would keep the helper self-consistent.
  • Two boundary paths have no unit pin: (a) depth-0 NEW_ACCOUNT charge OOG with a non-zero reservoir — the receipt must exclude the restored reservoir; (b) value CALL to a dead account with zero reservoir — the spill must be applied before the 63/64 child allowance. I verified (b) on this branch with exact leftover-gas assertions; drop-in test below if useful.
spill-before-63/64 test (drop-in next to TestEIP8038SStore)
// TestCallNewAccountSpillBefore63of64 pins the EIP-8037 charge order for a
// value CALL to a dead account: the NEW_ACCOUNT state charge (spilling into
// regular gas when the reservoir can't cover it) is applied BEFORE the 63/64
// child allowance is computed, per EELS amsterdam call(). With the reverse
// order, a caller forwarding ~all gas would OOG the whole frame on the spill.
//
// Caller bytecode: CALL(gas(), 0xdeadbeef, value=1, no args/ret), store the
// success flag at mem[0], return 32 bytes.
func TestCallNewAccountSpillBefore63of64(t *testing.T) {
	callerCode := "0x60006000600060006001" + // PUSH1 0 x4 (retSize retOffset inSize inOffset), PUSH1 1 (value)
		"7300000000000000000000000000000000deadbeef" + // PUSH20 callee
		"5af1" + // GAS, CALL
		"600052" + // PUSH1 0, MSTORE (store success flag)
		"60206000f3" // PUSH1 32, PUSH1 0, RETURN

	callee := accounts.InternAddress(common.HexToAddress("0x00000000000000000000000000000000deadbeef"))

	// Hand-computed (Amsterdam jump table, EELS amsterdam pin):
	//   pre-CALL opcodes: 5*PUSH1 + PUSH20 + GAS               = 20
	//   CALL constant (warm base)                              = 100
	//   cold account access (3000-100)                         = 2900
	//   CALL_VALUE (EIP-8038: ACCOUNT_WRITE 8000 + stipend)    = 10300
	//   NEW_ACCOUNT state gas                                  = 183600
	//   tail: PUSH1, MSTORE(+32B expansion), PUSH1, PUSH1, RET = 15
	for _, tt := range []struct {
		name            string
		pool            mdgas.MdGas
		leftoverRegular uint64
		leftoverState   uint64
	}{
		{
			// Zero reservoir: the full NEW_ACCOUNT spills into regular gas.
			// base for 63/64 = 500000-20-100-2900-10300-183600 = 303080
			// callGas = 303080 - 303080/64 = 298345
			// child (empty code) returns callGas + 2300 stipend in full;
			// leftover = 4735 + 298345 + 2300 - 15 = 305365
			name:            "zero reservoir, full spill",
			pool:            mdgas.MdGas{Regular: 500_000, State: 0},
			leftoverRegular: 305_365,
			leftoverState:   0,
		},
		{
			// Funded reservoir: no spill; 63/64 base = 500000-20-100-13200 = 486680
			// callGas = 486680 - 7604 = 479076
			// leftover = 7604 + 479076 + 2300 - 15 = 488965
			name:            "funded reservoir, no spill",
			pool:            mdgas.MdGas{Regular: 500_000, State: 200_000},
			leftoverRegular: 488_965,
			leftoverState:   200_000 - 183_600,
		},
	} {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			tx, sd := testTemporalTxSD(t)
			txNum, _, err := sd.SeekCommitment(t.Context(), tx)
			require.NoError(t, err)
			r, w := state.NewReaderV3(sd.AsGetter(tx)), state.NewWriter(sd.AsPutDel(tx), nil, txNum)
			s := state.New(r)
			caller := accounts.InternAddress(common.BytesToAddress([]byte("contract")))
			s.CreateAccount(caller, true)
			s.SetCode(caller, hexutil.MustDecode(callerCode), tracing.CodeChangeUnspecified)
			vmctx := evmtypes.BlockContext{
				CanTransfer: func(evmtypes.IntraBlockState, accounts.Address, uint256.Int) (bool, error) { return true, nil },
				Transfer: func(evmtypes.IntraBlockState, accounts.Address, accounts.Address, uint256.Int, bool, *chain.Rules) error {
					return nil
				},
			}
			_ = s.CommitBlock(vmctx.Rules(chain.AllProtocolChanges), w)
			vmenv := vm.NewEVM(vmctx, evmtypes.TxContext{}, s, chain.AllProtocolChanges, vm.Config{})

			ret, gas, _, err := vmenv.Call(accounts.ZeroAddress, caller, nil, tt.pool, uint256.Int{}, false /* bailout */)
			require.NoError(t, err, "outer frame must not OOG: NEW_ACCOUNT spill must precede the 63/64 computation")
			require.Len(t, ret, 32)
			require.Equal(t, byte(1), ret[31], "inner CALL must succeed")
			require.Equal(t, tt.leftoverRegular, gas.Regular, "leftover regular gas")
			require.Equal(t, tt.leftoverState, gas.State, "leftover state gas")
			exists, err := vmenv.IntraBlockState().Exist(callee)
			require.NoError(t, err)
			require.True(t, exists, "callee account must have been created")
		})
	}
}

@taratorio

Copy link
Copy Markdown
Member Author

Reviewed against the devnet-6 pin (EELS tests-glamsterdam-devnet@v6.1.1 = d0338f56, fork amsterdam). The refill/spill model, block accounting, and 7702 delegation rules match the pinned spec, including the handleFrameRevert entry-reservoir restore. Remaining nits, none blocking:

  • verifyAuthorities has no explicit auth.nonce == 2^64-1 skip (EELS validate_authorization has one). The nonce-mismatch check yields the identical skip+refund for every reachable state, so this is spec-parity hygiene; it also avoids the theoretical SetNonce wrap in step 8.
  • chargeTopLevelFrameGas: on the OOG return, gasRemaining.State has already been zeroed by the failed useMdGas. Benign since handleFrameRevert restores the entry reservoir unconditionally, but returning the pre-charge value would keep the helper self-consistent.
  • Two boundary paths have no unit pin: (a) depth-0 NEW_ACCOUNT charge OOG with a non-zero reservoir — the receipt must exclude the restored reservoir; (b) value CALL to a dead account with zero reservoir — the spill must be applied before the 63/64 child allowance. I verified (b) on this branch with exact leftover-gas assertions; drop-in test below if useful.

spill-before-63/64 test (drop-in next to TestEIP8038SStore)

thanks, addressed:

  1. N/A; already covered - the auth.nonce == 2^64-1 skip is already enforced: Authorization.RecoverSigner returns failed assertion: auth.nonce < 2**64 - 1, and verifyAuthorities treats a RecoverSigner error as skip+refund (refundSkippedAuth(); continue) before the nonce-match check and the SetNonce.
  2. done in e0367ce
  3. done in e0367ce

@taratorio
taratorio enabled auto-merge July 13, 2026 06:30
@taratorio
taratorio added this pull request to the merge queue Jul 13, 2026
@taratorio
taratorio removed this pull request from the merge queue due to a manual request Jul 13, 2026
@taratorio taratorio changed the title [DO-NOT-MERGE] execution: implement EIP-8037 updates for glamsterdam-devnet-6 execution: implement EIP-8037 updates for glamsterdam-devnet-6 Jul 13, 2026
@taratorio
taratorio added this pull request to the merge queue Jul 13, 2026
Merged via the queue into main with commit b887c6b Jul 13, 2026
91 of 92 checks passed
@taratorio
taratorio deleted the worktree-gd6-eip-8037 branch July 13, 2026 08:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Glamsterdam https://eips.ethereum.org/EIPS/eip-7773

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[gd6] implement EIP-8037 changes

3 participants