mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 10:50:44 +00:00
core/vm: implement eip-8279: Block Access List Byte Floor
This commit is contained in:
parent
9e04883a85
commit
56408777f0
9 changed files with 207 additions and 17 deletions
|
|
@ -147,7 +147,7 @@ func Transaction(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
// For Prague txs, validate the floor data gas.
|
// For Prague txs, validate the floor data gas.
|
||||||
if rules.IsPrague {
|
if rules.IsPrague {
|
||||||
floorDataGas, err := core.FloorDataGas(rules, tx.Data(), tx.AccessList())
|
floorDataGas, err := core.FloorDataGas(rules, tx.Data(), tx.AccessList(), uint64(len(tx.SetCodeAuthorizations())))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Error = err
|
r.Error = err
|
||||||
results = append(results, r)
|
results = append(results, r)
|
||||||
|
|
|
||||||
|
|
@ -151,8 +151,11 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FloorDataGas computes the minimum gas required for a transaction based on its data tokens (EIP-7623).
|
// FloorDataGas computes the minimum gas required for a transaction based on its
|
||||||
func FloorDataGas(rules params.Rules, data []byte, accessList types.AccessList) (uint64, error) {
|
// data tokens (EIP-7623). On Amsterdam it also includes the EIP-8131 per-auth
|
||||||
|
// tx-content floor and the EIP-8279 per-auth Block Access List floor, which
|
||||||
|
// together form the static floor seed extended at runtime by EIP-8279.
|
||||||
|
func FloorDataGas(rules params.Rules, data []byte, accessList types.AccessList, numAuths uint64) (uint64, error) {
|
||||||
var (
|
var (
|
||||||
tokens uint64
|
tokens uint64
|
||||||
tokenCost uint64
|
tokenCost uint64
|
||||||
|
|
@ -203,7 +206,23 @@ func FloorDataGas(rules params.Rules, data []byte, accessList types.AccessList)
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
// Minimum gas required for a transaction based on its data tokens (EIP-7623).
|
// Minimum gas required for a transaction based on its data tokens (EIP-7623).
|
||||||
return params.TxGas + tokens*tokenCost, nil
|
floor := params.TxGas + tokens*tokenCost
|
||||||
|
|
||||||
|
// EIP-8131 / EIP-8279: each EIP-7702 authorization contributes a static
|
||||||
|
// per-auth floor. EIP-8131 prices the 101-byte authorization tuple
|
||||||
|
// (FloorCostPerAuth) and EIP-8279 adds the worst-case BAL bytes the auth
|
||||||
|
// writes when applied (BALBytesPerAuthorization at FloorGasPerByte). The
|
||||||
|
// per-auth BAL term is folded into the static floor because set_delegation
|
||||||
|
// runs outside the EVM's out-of-gas handler and cannot extend the floor at
|
||||||
|
// runtime.
|
||||||
|
if rules.IsAmsterdam && numAuths > 0 {
|
||||||
|
const perAuth = params.FloorCostPerAuth + params.BALBytesPerAuthorization*params.FloorGasPerByte
|
||||||
|
if (math.MaxUint64-floor)/perAuth < numAuths {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
floor += numAuths * perAuth
|
||||||
|
}
|
||||||
|
return floor, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// toWordSize returns the ceiled word size required for init code payment calculation.
|
// toWordSize returns the ceiled word size required for init code payment calculation.
|
||||||
|
|
@ -355,6 +374,11 @@ type stateTransition struct {
|
||||||
initReservoir uint64 // initial state-gas reservoir carved out of GasLimit (EIP-8037)
|
initReservoir uint64 // initial state-gas reservoir carved out of GasLimit (EIP-8037)
|
||||||
state vm.StateDB
|
state vm.StateDB
|
||||||
evm *vm.EVM
|
evm *vm.EVM
|
||||||
|
|
||||||
|
// floorGas is the EIP-8279 Block Access List floor accumulator, seeded with
|
||||||
|
// the static floor and extended at runtime via the EVM. It is nil before
|
||||||
|
// Amsterdam. settleGas reads its final value to apply the receipt floor.
|
||||||
|
floorGas *vm.FloorGasAccumulator
|
||||||
}
|
}
|
||||||
|
|
||||||
// newStateTransition initialises and returns a new state transition object.
|
// newStateTransition initialises and returns a new state transition object.
|
||||||
|
|
@ -645,7 +669,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
// Validate the EIP-7623 calldata floor against the gas limit. The floor inflates
|
// Validate the EIP-7623 calldata floor against the gas limit. The floor inflates
|
||||||
// the total gas usage at tx end, so the gas limit must be sufficient to cover that.
|
// the total gas usage at tx end, so the gas limit must be sufficient to cover that.
|
||||||
if rules.IsPrague {
|
if rules.IsPrague {
|
||||||
floorDataGas, err = FloorDataGas(rules, msg.Data, msg.AccessList)
|
floorDataGas, err = FloorDataGas(rules, msg.Data, msg.AccessList, uint64(len(msg.SetCodeAuthorizations)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -661,6 +685,15 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EIP-8279: seed the per-transaction Block Access List floor accumulator
|
||||||
|
// with the static floor and bound it by the transaction gas limit. The
|
||||||
|
// accumulator is extended at runtime as opcodes contribute BAL bytes; at
|
||||||
|
// settlement the receipt gas becomes max(execution_gas, floor_gas_used).
|
||||||
|
if rules.IsAmsterdam {
|
||||||
|
st.floorGas = vm.NewFloorGasAccumulator(floorDataGas, msg.GasLimit)
|
||||||
|
st.evm.SetFloorGas(st.floorGas)
|
||||||
|
}
|
||||||
|
|
||||||
if rules.IsEIP4762 {
|
if rules.IsEIP4762 {
|
||||||
st.evm.AccessEvents.AddTxOrigin(msg.From)
|
st.evm.AccessEvents.AddTxOrigin(msg.From)
|
||||||
|
|
||||||
|
|
@ -837,25 +870,34 @@ func (st *stateTransition) settleGas(rules params.Rules, floorDataGas uint64) (g
|
||||||
gasLeft += refund
|
gasLeft += refund
|
||||||
gasUsed = gasUsedBeforeRefund - refund
|
gasUsed = gasUsedBeforeRefund - refund
|
||||||
|
|
||||||
// EIP-7623: tx_gas_used = max(tx_gas_used_after_refund, calldata_floor).
|
// EIP-8279: the effective floor is the runtime accumulator (seeded with the
|
||||||
|
// static floorDataGas and extended by the BAL bytes opcodes contributed). It
|
||||||
|
// is always >= floorDataGas when the accumulator is active (Amsterdam); the
|
||||||
|
// max keeps the pre-Amsterdam / accumulator-less path on the static floor.
|
||||||
|
floorGas := floorDataGas
|
||||||
|
if st.floorGas != nil {
|
||||||
|
floorGas = max(floorGas, st.floorGas.FloorGasUsed())
|
||||||
|
}
|
||||||
|
|
||||||
|
// EIP-7623: tx_gas_used = max(tx_gas_used_after_refund, floor).
|
||||||
peakUsed = gasUsedBeforeRefund
|
peakUsed = gasUsedBeforeRefund
|
||||||
if rules.IsPrague && gasUsed < floorDataGas {
|
if rules.IsPrague && gasUsed < floorGas {
|
||||||
diff := floorDataGas - gasUsed
|
diff := floorGas - gasUsed
|
||||||
if st.evm.Config.Tracer.HasGasHook() {
|
if st.evm.Config.Tracer.HasGasHook() {
|
||||||
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{Regular: gasLeft}, tracing.Gas{Regular: gasLeft - diff}, tracing.GasChangeTxDataFloor)
|
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{Regular: gasLeft}, tracing.Gas{Regular: gasLeft - diff}, tracing.GasChangeTxDataFloor)
|
||||||
}
|
}
|
||||||
gasLeft -= diff
|
gasLeft -= diff
|
||||||
gasUsed = floorDataGas
|
gasUsed = floorGas
|
||||||
peakUsed = max(peakUsed, floorDataGas)
|
peakUsed = max(peakUsed, floorGas)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rules.IsAmsterdam {
|
if rules.IsAmsterdam {
|
||||||
// EIP-7623/7976: the calldata floor applies to the block-level regular
|
// EIP-7623/7976: the calldata floor applies to the block-level regular
|
||||||
// gas dimension as well, mirroring its effect on the receipt gas. The
|
// gas dimension as well, mirroring its effect on the receipt gas. The
|
||||||
// spec accumulates max(tx_regular_gas, calldata_floor) into
|
// spec accumulates max(tx_regular_gas, floor) into block_gas_used, so the
|
||||||
// block_gas_used, so the block must never count fewer regular units
|
// block must never count fewer regular units than the floor the sender
|
||||||
// than the floor the sender was charged.
|
// was charged. EIP-8279 widens the floor to include BAL byte costs.
|
||||||
blockRegularGas := max(txRegularGas, floorDataGas)
|
blockRegularGas := max(txRegularGas, floorGas)
|
||||||
if err = st.gp.ChargeGasAmsterdam(blockRegularGas, txStateGas, gasUsed); err != nil {
|
if err = st.gp.ChargeGasAmsterdam(blockRegularGas, txStateGas, gasUsed); err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ func TestFloorDataGas(t *testing.T) {
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
rules := params.Rules{IsAmsterdam: tt.amsterdam}
|
rules := params.Rules{IsAmsterdam: tt.amsterdam}
|
||||||
got, err := FloorDataGas(rules, tt.data, tt.accessList)
|
got, err := FloorDataGas(rules, tt.data, tt.accessList, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
||||||
}
|
}
|
||||||
// Ensure the transaction can cover floor data gas.
|
// Ensure the transaction can cover floor data gas.
|
||||||
if rules.IsPrague {
|
if rules.IsPrague {
|
||||||
floorDataGas, err := core.FloorDataGas(rules, tx.Data(), tx.AccessList())
|
floorDataGas, err := core.FloorDataGas(rules, tx.Data(), tx.AccessList(), uint64(len(tx.SetCodeAuthorizations())))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,13 @@ type EVM struct {
|
||||||
returnData []byte // Last CALL's return data for subsequent reuse
|
returnData []byte // Last CALL's return data for subsequent reuse
|
||||||
|
|
||||||
arena *stackArena
|
arena *stackArena
|
||||||
|
|
||||||
|
// floorGas is the per-transaction EIP-8279 Block Access List byte-floor
|
||||||
|
// accumulator. It is set by the state transition at the start of each
|
||||||
|
// transaction and extended at runtime as opcodes contribute BAL bytes. It
|
||||||
|
// is nil before EIP-8279 (Amsterdam) or in contexts without BAL
|
||||||
|
// construction, in which case the runtime extensions are no-ops.
|
||||||
|
floorGas *FloorGasAccumulator
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEVM constructs an EVM instance with the supplied block context, state
|
// NewEVM constructs an EVM instance with the supplied block context, state
|
||||||
|
|
@ -222,6 +229,26 @@ func (evm *EVM) SetTxContext(txCtx TxContext) {
|
||||||
evm.TxContext = txCtx
|
evm.TxContext = txCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetFloorGas installs the per-transaction EIP-8279 floor accumulator. It is
|
||||||
|
// called by the state transition once the static floor seed and gas limit are
|
||||||
|
// known. Passing nil disables runtime floor extensions for the transaction.
|
||||||
|
func (evm *EVM) SetFloorGas(acc *FloorGasAccumulator) {
|
||||||
|
evm.floorGas = acc
|
||||||
|
}
|
||||||
|
|
||||||
|
// FloorGas returns the active EIP-8279 floor accumulator, or nil if none is set.
|
||||||
|
func (evm *EVM) FloorGas() *FloorGasAccumulator {
|
||||||
|
return evm.floorGas
|
||||||
|
}
|
||||||
|
|
||||||
|
// extendFloor extends the EIP-8279 floor accumulator by numBytes BAL bytes. It
|
||||||
|
// is a no-op when no accumulator is installed (pre-Amsterdam or BAL-less
|
||||||
|
// contexts). The returned error, when non-nil, is ErrOutOfGas and MUST abort
|
||||||
|
// the operation before the matching BAL byte is inserted.
|
||||||
|
func (evm *EVM) extendFloor(numBytes uint64) error {
|
||||||
|
return evm.floorGas.extendFloor(numBytes)
|
||||||
|
}
|
||||||
|
|
||||||
// Cancel cancels any running EVM operation. This may be called concurrently and
|
// Cancel cancels any running EVM operation. This may be called concurrently and
|
||||||
// it's safe to be called multiple times.
|
// it's safe to be called multiple times.
|
||||||
func (evm *EVM) Cancel() {
|
func (evm *EVM) Cancel() {
|
||||||
|
|
@ -524,6 +551,15 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EIP-8279: an opcode-level CREATE/CREATE2 records the deployed address in
|
||||||
|
// the BAL. The top-level creation transaction's contract address is part of
|
||||||
|
// the implicit per-tx bytes covered by TX_BASE headroom, so only nested
|
||||||
|
// creations extend the floor here.
|
||||||
|
if evm.depth > 0 {
|
||||||
|
if err = evm.extendFloor(params.BALBytesPerAddress); err != nil {
|
||||||
|
return nil, common.Address{}, gas.ExitHalt(), err
|
||||||
|
}
|
||||||
|
}
|
||||||
// We add this to the access list _before_ taking a snapshot. Even if the
|
// We add this to the access list _before_ taking a snapshot. Even if the
|
||||||
// creation fails, the access-list change should not be rolled back.
|
// creation fails, the access-list change should not be rolled back.
|
||||||
if evm.chainRules.IsEIP2929 {
|
if evm.chainRules.IsEIP2929 {
|
||||||
|
|
@ -562,6 +598,21 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
// acts inside that account.
|
// acts inside that account.
|
||||||
evm.StateDB.CreateContract(address)
|
evm.StateDB.CreateContract(address)
|
||||||
|
|
||||||
|
// EIP-8279: an opcode-level CREATE/CREATE2 sets the new contract's nonce
|
||||||
|
// (and, with a non-zero endowment, its balance), both recorded in the BAL.
|
||||||
|
// The floor is extended after the collision check, before the state
|
||||||
|
// mutation. The top-level creation transaction's nonce is covered by
|
||||||
|
// TX_BASE headroom, so only nested creations extend the floor here.
|
||||||
|
if evm.depth > 0 {
|
||||||
|
if err = evm.extendFloor(params.BALBytesPerNonce); err != nil {
|
||||||
|
return nil, common.Address{}, gas.ExitHalt(), err
|
||||||
|
}
|
||||||
|
if !value.IsZero() {
|
||||||
|
if err = evm.extendFloor(params.BALBytesPerBalance); err != nil {
|
||||||
|
return nil, common.Address{}, gas.ExitHalt(), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if evm.chainRules.IsEIP158 {
|
if evm.chainRules.IsEIP158 {
|
||||||
evm.StateDB.SetNonce(address, 1, tracing.NonceChangeNewContract)
|
evm.StateDB.SetNonce(address, 1, tracing.NonceChangeNewContract)
|
||||||
}
|
}
|
||||||
|
|
@ -668,6 +719,16 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) (ret
|
||||||
return ret, true, err
|
return ret, true, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// EIP-8279: a successful opcode-level CREATE/CREATE2 deploy records the
|
||||||
|
// deployed code in the BAL. Extend the floor by the code length before
|
||||||
|
// set_code. A top-level creation transaction's deployed code is bounded by
|
||||||
|
// the calldata floor on its init code, so only nested creations extend the
|
||||||
|
// floor here.
|
||||||
|
if evm.depth > 0 && len(ret) > 0 {
|
||||||
|
if err := evm.extendFloor(uint64(len(ret))); err != nil {
|
||||||
|
return ret, true, err
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(ret) > 0 {
|
if len(ret) > 0 {
|
||||||
evm.StateDB.SetCode(address, ret, tracing.CodeChangeContractCreation)
|
evm.StateDB.SetCode(address, ret, tracing.CodeChangeContractCreation)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -504,6 +504,15 @@ func gasCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
||||||
}
|
}
|
||||||
// Stateful check
|
// Stateful check
|
||||||
if evm.chainRules.IsAmsterdam {
|
if evm.chainRules.IsAmsterdam {
|
||||||
|
// EIP-8279: a CALL transferring non-zero value to a different account
|
||||||
|
// records a balance change for the recipient in the BAL. Extend the
|
||||||
|
// floor by the balance bytes before the transfer. A self-call moves no
|
||||||
|
// value out of the executing account and adds no balance bytes.
|
||||||
|
if transfersValue && address != contract.Address() {
|
||||||
|
if err := evm.extendFloor(params.BALBytesPerBalance); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
// EIP-8037: the cost of creating a new account via a value-bearing
|
// EIP-8037: the cost of creating a new account via a value-bearing
|
||||||
// CALL is metered as state gas (NEW_ACCOUNT * CostPerStateByte),
|
// CALL is metered as state gas (NEW_ACCOUNT * CostPerStateByte),
|
||||||
// not the legacy regular CallNewAccountGas. It drains the state
|
// not the legacy regular CallNewAccountGas. It drains the state
|
||||||
|
|
@ -604,6 +613,10 @@ func gasSelfdestruct8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory
|
||||||
address = common.Address(stack.peek().Bytes20())
|
address = common.Address(stack.peek().Bytes20())
|
||||||
)
|
)
|
||||||
if !evm.StateDB.AddressInAccessList(address) {
|
if !evm.StateDB.AddressInAccessList(address) {
|
||||||
|
// EIP-8279: a cold beneficiary access adds the address to the BAL.
|
||||||
|
if err := evm.extendFloor(params.BALBytesPerAddress); err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
// If the caller cannot afford the cost, this change will be rolled back
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
evm.StateDB.AddAddressToAccessList(address)
|
evm.StateDB.AddAddressToAccessList(address)
|
||||||
gas.RegularGas = params.ColdAccountAccessCostEIP2929
|
gas.RegularGas = params.ColdAccountAccessCostEIP2929
|
||||||
|
|
@ -612,6 +625,14 @@ func gasSelfdestruct8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory
|
||||||
if contract.Gas.RegularGas < gas.RegularGas {
|
if contract.Gas.RegularGas < gas.RegularGas {
|
||||||
return gas, ErrOutOfGas
|
return gas, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
// EIP-8279: SELFDESTRUCT moving a non-zero balance to a different beneficiary
|
||||||
|
// records the beneficiary's balance change in the BAL. A self-targeted
|
||||||
|
// SELFDESTRUCT moves no value out and adds no balance bytes.
|
||||||
|
if address != contract.Address() && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||||||
|
if err := evm.extendFloor(params.BALBytesPerBalance); err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
// Important: use StateDB.Empty instead of !StateDB.Exist. An account may exist
|
// Important: use StateDB.Empty instead of !StateDB.Exist. An account may exist
|
||||||
// in the current state yet still be considered non-existent by EIP-161 if its
|
// in the current state yet still be considered non-existent by EIP-161 if its
|
||||||
// nonce, balance, and code are all zero. Such accounts can appear temporarily
|
// nonce, balance, and code are all zero. Such accounts can appear temporarily
|
||||||
|
|
@ -641,12 +662,29 @@ func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
||||||
)
|
)
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||||
|
// EIP-8279: a cold SSTORE adds the storage key to the BAL. Extend the
|
||||||
|
// floor before the slot is recorded; an out-of-gas here aborts the
|
||||||
|
// opcode before the unpaid BAL byte exists.
|
||||||
|
if err := evm.extendFloor(params.BALBytesPerStorageKey); err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
cost = GasCosts{RegularGas: params.ColdSloadCostEIP2929}
|
cost = GasCosts{RegularGas: params.ColdSloadCostEIP2929}
|
||||||
// If the caller cannot afford the cost, this change will be rolled back
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||||
}
|
}
|
||||||
value := common.Hash(y.Bytes32())
|
value := common.Hash(y.Bytes32())
|
||||||
|
|
||||||
|
// EIP-8279: an SSTORE that changes the slot's current value contributes a
|
||||||
|
// post-value to the BAL. Charge the value bytes whenever the value differs,
|
||||||
|
// mirroring the BAL StorageWrite. This may over-charge when the same slot is
|
||||||
|
// written more than once in a transaction (the BAL records only one final
|
||||||
|
// post-value per slot), which is safe: it never under-charges.
|
||||||
|
if current != value {
|
||||||
|
if err := evm.extendFloor(params.BALBytesPerStorageValue); err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if current == value { // noop (1)
|
if current == value { // noop (1)
|
||||||
// EIP 2200 original clause:
|
// EIP 2200 original clause:
|
||||||
// return params.SloadGasEIP2200, nil
|
// return params.SloadGasEIP2200, nil
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,12 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
||||||
slot := common.Hash(loc.Bytes32())
|
slot := common.Hash(loc.Bytes32())
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||||
|
// EIP-8279: a cold SLOAD adds the storage key to the BAL. Extend the
|
||||||
|
// floor before the slot is recorded; an out-of-gas here aborts the
|
||||||
|
// opcode before the unpaid BAL byte exists.
|
||||||
|
if err := evm.extendFloor(params.BALBytesPerStorageKey); err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
// If the caller cannot afford the cost, this change will be rolled back
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
// If he does afford it, we can skip checking the same thing later on, during execution
|
// If he does afford it, we can skip checking the same thing later on, during execution
|
||||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||||
|
|
@ -126,6 +132,10 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
||||||
addr := common.Address(stack.peek().Bytes20())
|
addr := common.Address(stack.peek().Bytes20())
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
if !evm.StateDB.AddressInAccessList(addr) {
|
if !evm.StateDB.AddressInAccessList(addr) {
|
||||||
|
// EIP-8279: a cold account access adds the address to the BAL.
|
||||||
|
if err := evm.extendFloor(params.BALBytesPerAddress); err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
var overflow bool
|
var overflow bool
|
||||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||||||
|
|
@ -148,6 +158,10 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
||||||
addr := common.Address(stack.peek().Bytes20())
|
addr := common.Address(stack.peek().Bytes20())
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
if !evm.StateDB.AddressInAccessList(addr) {
|
if !evm.StateDB.AddressInAccessList(addr) {
|
||||||
|
// EIP-8279: a cold account access adds the address to the BAL.
|
||||||
|
if err := evm.extendFloor(params.BALBytesPerAddress); err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
// If the caller cannot afford the cost, this change will be rolled back
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
// The warm storage read cost is already charged as constantGas
|
// The warm storage read cost is already charged as constantGas
|
||||||
|
|
@ -165,6 +179,10 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
|
||||||
// the cost to charge for cold access, if any, is Cold - Warm
|
// the cost to charge for cold access, if any, is Cold - Warm
|
||||||
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||||
if !warmAccess {
|
if !warmAccess {
|
||||||
|
// EIP-8279: a cold account access adds the address to the BAL.
|
||||||
|
if err := evm.extendFloor(params.BALBytesPerAddress); err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
// Charge the remaining difference here already, to correctly calculate available
|
// Charge the remaining difference here already, to correctly calculate available
|
||||||
// gas for call
|
// gas for call
|
||||||
|
|
@ -286,6 +304,10 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
|
||||||
// Perform EIP-2929 checks (stateless), checking address presence
|
// Perform EIP-2929 checks (stateless), checking address presence
|
||||||
// in the accessList and charge the cold access accordingly.
|
// in the accessList and charge the cold access accordingly.
|
||||||
if !evm.StateDB.AddressInAccessList(addr) {
|
if !evm.StateDB.AddressInAccessList(addr) {
|
||||||
|
// EIP-8279: a cold account access adds the address to the BAL.
|
||||||
|
if err := evm.extendFloor(params.BALBytesPerAddress); err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
|
|
||||||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form
|
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form
|
||||||
|
|
@ -321,6 +343,11 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
|
||||||
if evm.StateDB.AddressInAccessList(target) {
|
if evm.StateDB.AddressInAccessList(target) {
|
||||||
eip7702Cost = params.WarmStorageReadCostEIP2929
|
eip7702Cost = params.WarmStorageReadCostEIP2929
|
||||||
} else {
|
} else {
|
||||||
|
// EIP-8279: resolving a cold delegation target adds its address
|
||||||
|
// to the BAL.
|
||||||
|
if err := evm.extendFloor(params.BALBytesPerAddress); err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
evm.StateDB.AddAddressToAccessList(target)
|
evm.StateDB.AddAddressToAccessList(target)
|
||||||
eip7702Cost = params.ColdAccountAccessCostEIP2929
|
eip7702Cost = params.ColdAccountAccessCostEIP2929
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,28 @@ const (
|
||||||
TxAuthTupleGas uint64 = 12500 // Per auth tuple code specified in EIP-7702
|
TxAuthTupleGas uint64 = 12500 // Per auth tuple code specified in EIP-7702
|
||||||
TxAuthTupleRegularGas uint64 = 7500 // Per auth tuple regular gas specified in EIP-8037
|
TxAuthTupleRegularGas uint64 = 7500 // Per auth tuple regular gas specified in EIP-8037
|
||||||
|
|
||||||
|
// FloorCostPerAuth is the per-authorization tx-content floor contribution
|
||||||
|
// defined by EIP-8131: an EIP-7702 authorization tuple is 101 bytes, charged
|
||||||
|
// at the floor rate (101 * TxCostFloorPerToken7976 * TxTokenPerNonZeroByte).
|
||||||
|
FloorCostPerAuth uint64 = 101 * TxCostFloorPerToken7976 * TxTokenPerNonZeroByte // 6464
|
||||||
|
|
||||||
|
// EIP-8279: Block Access List Byte Floor. Each byte an opcode adds to the
|
||||||
|
// EIP-7928 Block Access List extends the transaction's floor accumulator by
|
||||||
|
// FloorGasPerByte gas, charged at runtime before the BAL grows.
|
||||||
|
FloorGasPerByte uint64 = TxCostFloorPerToken7976 * TxTokenPerNonZeroByte // 64: per-byte floor rate (EIP-7976)
|
||||||
|
BALBytesPerAddress uint64 = 20 // BAL bytes for an account address
|
||||||
|
BALBytesPerStorageKey uint64 = 32 // BAL bytes for a storage key
|
||||||
|
BALBytesPerStorageValue uint64 = 32 // BAL bytes for a storage post-value
|
||||||
|
BALBytesPerBalance uint64 = 32 // BAL bytes for a balance change
|
||||||
|
BALBytesPerNonce uint64 = 8 // BAL bytes for a nonce change
|
||||||
|
BALDelegationCodeBytes uint64 = 23 // EIP-7702 delegation marker length
|
||||||
|
// BALBytesPerAuthorization is the worst-case BAL contribution an EIP-7702
|
||||||
|
// authorization adds when it is applied: the authority address, the
|
||||||
|
// delegation marker written to its code, and its nonce change. It is folded
|
||||||
|
// into the static floor seed since set_delegation runs outside the EVM's
|
||||||
|
// out-of-gas handler.
|
||||||
|
BALBytesPerAuthorization uint64 = BALBytesPerAddress + BALDelegationCodeBytes + BALBytesPerNonce // 51
|
||||||
|
|
||||||
// These have been changed during the course of the chain
|
// These have been changed during the course of the chain
|
||||||
CallGasFrontier uint64 = 40 // Once per CALL operation & message call transaction.
|
CallGasFrontier uint64 = 40 // Once per CALL operation & message call transaction.
|
||||||
CallGasEIP150 uint64 = 700 // Static portion of gas for CALL-derivates after EIP 150 (Tangerine)
|
CallGasEIP150 uint64 = 700 // Static portion of gas for CALL-derivates after EIP 150 (Tangerine)
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ func (tt *TransactionTest) Run() error {
|
||||||
|
|
||||||
if rules.IsPrague {
|
if rules.IsPrague {
|
||||||
var floorDataGas uint64
|
var floorDataGas uint64
|
||||||
floorDataGas, err = core.FloorDataGas(rules, tx.Data(), tx.AccessList())
|
floorDataGas, err = core.FloorDataGas(rules, tx.Data(), tx.AccessList(), uint64(len(tx.SetCodeAuthorizations())))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue