From a6024b73ff4e54f979a1304e5a86b058f7ddfd65 Mon Sep 17 00:00:00 2001 From: MariusVanDerWijden Date: Wed, 17 Jun 2026 11:09:07 +0200 Subject: [PATCH] core/vm: implement eip-8279: Block Access List Byte Floor --- core/vm/floor.go | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 core/vm/floor.go diff --git a/core/vm/floor.go b/core/vm/floor.go new file mode 100644 index 0000000000..b8a6388e97 --- /dev/null +++ b/core/vm/floor.go @@ -0,0 +1,76 @@ +// Copyright 2026 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import "github.com/ethereum/go-ethereum/params" + +// FloorGasAccumulator implements the per-transaction floor accumulator defined +// by EIP-8279 (Block Access List Byte Floor). It is an internal counter on the +// execution environment, seeded with the EIP-8131 static floor and extended at +// runtime by FloorGasPerByte for every byte an opcode adds to the EIP-7928 +// Block Access List. +// +// The accumulator is not part of the signed transaction, is not RLP-encoded, +// gossiped, or persisted; no gas is reserved or deducted from the execution +// budget. It is checked against the transaction's gas limit only to ensure the +// sender can pay the floor if it ends up binding, and at transaction end the +// receipt gas is settled as max(execution_gas_used, floor_gas_used). +type FloorGasAccumulator struct { + floorGasUsed uint64 // accumulated floor gas (static seed + runtime extensions) + gasLimit uint64 // tx.gas; the accumulator must never climb past this +} + +// NewFloorGasAccumulator returns an accumulator seeded with the static floor +// and bounded by the transaction gas limit. +func NewFloorGasAccumulator(staticFloor, gasLimit uint64) *FloorGasAccumulator { + return &FloorGasAccumulator{floorGasUsed: staticFloor, gasLimit: gasLimit} +} + +// FloorGasUsed returns the current value of the floor accumulator. +func (f *FloorGasAccumulator) FloorGasUsed() uint64 { + if f == nil { + return 0 + } + return f.floorGasUsed +} + +// extendFloor extends the floor accumulator by numBytes BAL bytes, each priced +// at params.FloorGasPerByte. It MUST be called BEFORE the matching BAL +// insertion or state mutation: if the new floor would exceed the transaction +// gas limit it returns ErrOutOfGas, which aborts the operation before any +// unpaid BAL byte exists. A nil accumulator (pre-EIP-8279, or contexts without +// BAL construction) is a no-op. +func (f *FloorGasAccumulator) extendFloor(numBytes uint64) error { + if f == nil { + return nil + } + // numBytes is bounded by deployed-code length in the worst case; guard the + // multiplication against overflow before checking against the gas limit. + if numBytes > (^uint64(0))/params.FloorGasPerByte { + return ErrOutOfGas + } + extension := numBytes * params.FloorGasPerByte + if f.floorGasUsed > f.gasLimit-min(f.gasLimit, extension) { + return ErrOutOfGas + } + newFloor := f.floorGasUsed + extension + if newFloor > f.gasLimit { + return ErrOutOfGas + } + f.floorGasUsed = newFloor + return nil +}