mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-24 23:46:17 +00:00
core: implement EIP-2780
This commit is contained in:
parent
c6a4fd8ca1
commit
7d5b36f86c
11 changed files with 347 additions and 31 deletions
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/tests"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
|
@ -133,7 +134,7 @@ func Transaction(ctx *cli.Context) error {
|
|||
}
|
||||
// Check intrinsic gas
|
||||
rules := chainConfig.Rules(common.Big0, true, 0)
|
||||
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, params.CostPerStateByte)
|
||||
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), r.Address, tx.To(), uint256.MustFromBig(tx.Value()), rules, params.CostPerStateByte)
|
||||
if err != nil {
|
||||
r.Error = err
|
||||
results = append(results, r)
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
|||
data := make([]byte, nbytes)
|
||||
return func(i int, gen *BlockGen) {
|
||||
toaddr := common.Address{}
|
||||
cost, _ := IntrinsicGas(data, nil, nil, false, params.Rules{}, params.CostPerStateByte)
|
||||
cost, _ := IntrinsicGas(data, nil, nil, common.Address{}, &toaddr, nil, params.Rules{}, params.CostPerStateByte)
|
||||
signer := gen.Signer()
|
||||
gasPrice := big.NewInt(0)
|
||||
if gen.header.BaseFee != nil {
|
||||
|
|
|
|||
|
|
@ -64,12 +64,12 @@ var (
|
|||
func TestProcessUBT(t *testing.T) {
|
||||
var (
|
||||
code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`)
|
||||
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, true, params.Rules{IsHomestead: true, IsIstanbul: true, IsShanghai: true}, 0)
|
||||
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, common.Address{}, nil, nil, params.Rules{IsHomestead: true, IsIstanbul: true, IsShanghai: true}, 0)
|
||||
// A contract creation that calls EXTCODECOPY in the constructor. Used to ensure that the witness
|
||||
// will not contain that copied data.
|
||||
// Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985
|
||||
codeWithExtCodeCopy = common.FromHex(`0x60806040526040516100109061017b565b604051809103906000f08015801561002c573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561007857600080fd5b5060008067ffffffffffffffff8111156100955761009461024a565b5b6040519080825280601f01601f1916602001820160405280156100c75781602001600182028036833780820191505090505b50905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506020600083833c81610101906101e3565b60405161010d90610187565b61011791906101a3565b604051809103906000f080158015610133573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061029b565b60d58061046783390190565b6102068061053c83390190565b61019d816101d9565b82525050565b60006020820190506101b86000830184610194565b92915050565b6000819050602082019050919050565b600081519050919050565b6000819050919050565b60006101ee826101ce565b826101f8846101be565b905061020381610279565b925060208210156102435761023e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8360200360080261028e565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600061028582516101d9565b80915050919050565b600082821b905092915050565b6101bd806102aa6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f566852414610030575b600080fd5b61003861004e565b6040516100459190610146565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166381ca91d36040518163ffffffff1660e01b815260040160206040518083038186803b1580156100b857600080fd5b505afa1580156100cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f0919061010a565b905090565b60008151905061010481610170565b92915050565b6000602082840312156101205761011f61016b565b5b600061012e848285016100f5565b91505092915050565b61014081610161565b82525050565b600060208201905061015b6000830184610137565b92915050565b6000819050919050565b600080fd5b61017981610161565b811461018457600080fd5b5056fea2646970667358221220a6a0e11af79f176f9c421b7b12f441356b25f6489b83d38cc828a701720b41f164736f6c63430008070033608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ab5ed15014602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212203a14eb0d5cd07c277d3e24912f110ddda3e553245a99afc4eeefb2fbae5327aa64736f6c63430008070033608060405234801561001057600080fd5b5060405161020638038061020683398181016040528101906100329190610063565b60018160001c6100429190610090565b60008190555050610145565b60008151905061005d8161012e565b92915050565b60006020828403121561007957610078610129565b5b60006100878482850161004e565b91505092915050565b600061009b826100f0565b91506100a6836100f0565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100db576100da6100fa565b5b828201905092915050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610137816100e6565b811461014257600080fd5b50565b60b3806101536000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806381ca91d314602d575b600080fd5b60336047565b604051603e9190605a565b60405180910390f35b60005481565b6054816073565b82525050565b6000602082019050606d6000830184604d565b92915050565b600081905091905056fea26469706673582212209bff7098a2f526de1ad499866f27d6d0d6f17b74a413036d6063ca6a0998ca4264736f6c63430008070033`)
|
||||
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, true, params.Rules{IsHomestead: true, IsIstanbul: true, IsShanghai: true}, 0)
|
||||
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, common.Address{}, nil, nil, params.Rules{IsHomestead: true, IsIstanbul: true, IsShanghai: true}, 0)
|
||||
signer = types.LatestSigner(testUBTChainConfig)
|
||||
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain
|
||||
|
|
|
|||
208
core/eip2780_test.go
Normal file
208
core/eip2780_test.go
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// TestEIP2780Intrinsic checks the intrinsic-gas decomposition.
|
||||
func TestEIP2780Intrinsic(t *testing.T) {
|
||||
var (
|
||||
from = common.HexToAddress("0x1111111111111111111111111111111111111111")
|
||||
to = common.HexToAddress("0x2222222222222222222222222222222222222222")
|
||||
)
|
||||
cases := []struct {
|
||||
name string
|
||||
to *common.Address
|
||||
value *uint256.Int
|
||||
want vm.GasCosts
|
||||
}{
|
||||
{
|
||||
name: "self-transfer",
|
||||
to: &from,
|
||||
value: uint256.NewInt(1),
|
||||
want: vm.GasCosts{RegularGas: params.TxBaseCost2780}, // 12,000
|
||||
},
|
||||
{
|
||||
name: "self-transfer/zero-value",
|
||||
to: &from,
|
||||
value: uint256.NewInt(0),
|
||||
want: vm.GasCosts{RegularGas: params.TxBaseCost2780}, // 12,000
|
||||
},
|
||||
{
|
||||
name: "zero-value call",
|
||||
to: &to,
|
||||
value: uint256.NewInt(0),
|
||||
// TxBaseCost + ColdAccountAccess = 15,000
|
||||
want: vm.GasCosts{RegularGas: params.TxBaseCost2780 + params.ColdAccountAccess2780},
|
||||
},
|
||||
{
|
||||
name: "value transfer to existing EOA",
|
||||
to: &to,
|
||||
value: uint256.NewInt(1),
|
||||
// TxBaseCost + ColdAccountAccess + TxValueCost + TransferLogCost = 21,000
|
||||
want: vm.GasCosts{RegularGas: params.TxBaseCost2780 + params.ColdAccountAccess2780 +
|
||||
params.TxValueCost2780 + params.TransferLogCost2780},
|
||||
},
|
||||
{
|
||||
name: "contract creation, value = 0",
|
||||
to: nil,
|
||||
value: uint256.NewInt(0),
|
||||
// TxBaseCost + CreateAccess = 23,000 regular, plus one account creation in state.
|
||||
want: vm.GasCosts{
|
||||
RegularGas: params.TxBaseCost2780 + params.CreateAccess2780,
|
||||
StateGas: params.AccountCreationSize * params.CostPerStateByte,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "contract creation, value > 0",
|
||||
to: nil,
|
||||
value: uint256.NewInt(1),
|
||||
// TxBaseCost + CreateAccess + TransferLogCost = 24,756 regular, plus account creation.
|
||||
want: vm.GasCosts{
|
||||
RegularGas: params.TxBaseCost2780 + params.CreateAccess2780 + params.TransferLogCost2780,
|
||||
StateGas: params.AccountCreationSize * params.CostPerStateByte,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := IntrinsicGas(nil, nil, nil, from, tc.to, tc.value, rules8037, params.CostPerStateByte)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got != tc.want {
|
||||
t.Fatalf("gas mismatch: got %+v, want %+v", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestEIP2780Gas checks every "Transaction reference case" in
|
||||
// the EIP-2780 specification end-to-end, asserting the two-dimensional charge
|
||||
// (intrinsic + top-level + execution) recorded in the block gas pool.
|
||||
func TestEIP2780Gas(t *testing.T) {
|
||||
const (
|
||||
cold = params.ColdAccountAccess2780
|
||||
base = params.TxBaseCost2780
|
||||
valueCst = params.TxValueCost2780 + params.TransferLogCost2780
|
||||
)
|
||||
var (
|
||||
existingEOA = common.HexToAddress("0xe0a0000000000000000000000000000000000001")
|
||||
stopContract = common.HexToAddress("0xc0de000000000000000000000000000000000001")
|
||||
delegated = common.HexToAddress("0xde1e000000000000000000000000000000000001")
|
||||
emptyTarget = common.HexToAddress("0x7a76000000000000000000000000000000000001") // never allocated
|
||||
freshEOA = common.HexToAddress("0xbeef000000000000000000000000000000000001") // never allocated
|
||||
)
|
||||
// Shared world: a funded EOA, a STOP contract and an account delegated to a
|
||||
// non-existent (codeless) target. The delegation target is intentionally
|
||||
// absent so resolving it executes no code.
|
||||
base7702 := types.GenesisAlloc{
|
||||
existingEOA: {Balance: big.NewInt(1)},
|
||||
stopContract: {Code: []byte{0x00}}, // STOP
|
||||
delegated: {Code: types.AddressToDelegation(emptyTarget)},
|
||||
}
|
||||
// valueCreateTx builds a contract-creation transaction carrying value.
|
||||
valueCreateTx := func(value int64) *types.Transaction {
|
||||
return types.MustSignNewTx(senderKey, signer8037, &types.DynamicFeeTx{
|
||||
ChainID: cfg8037.ChainID, Nonce: 0, To: nil, Value: big.NewInt(value),
|
||||
Gas: 300_000, GasFeeCap: big.NewInt(0), GasTipCap: big.NewInt(0),
|
||||
})
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
tx *types.Transaction
|
||||
wantRegular, wantState uint64
|
||||
}{
|
||||
// case 1: ETH transfer to self.
|
||||
{"self-transfer", callTx(0, senderAddr, 1, 100_000, nil), base, 0},
|
||||
// case 2: no-transfer to an existing EOA.
|
||||
{"zero-value/eoa", callTx(0, existingEOA, 0, 100_000, nil), base + cold, 0},
|
||||
// case 3: no-transfer to a contract.
|
||||
{"zero-value/contract", callTx(0, stopContract, 0, 100_000, nil), base + cold, 0},
|
||||
// case 4: ETH transfer to an existing EOA.
|
||||
{"value/eoa", callTx(0, existingEOA, 1, 100_000, nil), base + cold + valueCst, 0},
|
||||
// case 5: ETH transfer to a contract.
|
||||
{"value/contract", callTx(0, stopContract, 1, 100_000, nil), base + cold + valueCst, 0},
|
||||
// case 6: no-transfer to a 7702-delegated account.
|
||||
{"zero-value/delegated", callTx(0, delegated, 0, 100_000, nil), base + 2*cold, 0},
|
||||
// case 7: ETH transfer to a 7702-delegated account (no new-account charge).
|
||||
{"value/delegated", callTx(0, delegated, 1, 100_000, nil), base + 2*cold + valueCst, 0},
|
||||
// case 8: ETH transfer creating a new account.
|
||||
{"value/new-account", callTx(0, freshEOA, 1, 300_000, nil), base + cold + valueCst, newAccountState},
|
||||
// case 9: contract-creation transaction, value = 0.
|
||||
{"create/zero-value", createTx(0, 300_000, nil), base + params.CreateAccess2780, newAccountState},
|
||||
// case 10: contract-creation transaction, value > 0.
|
||||
{"create/value", valueCreateTx(1), base + params.CreateAccess2780 + params.TransferLogCost2780, newAccountState},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
res, gp, err := applyMsg(t, mkState(senderAlloc(base7702)), tc.tx)
|
||||
if err != nil {
|
||||
t.Fatalf("consensus error: %v", err)
|
||||
}
|
||||
if res.Err != nil {
|
||||
t.Fatalf("execution failed: %v", res.Err)
|
||||
}
|
||||
if gp.cumulativeRegular != tc.wantRegular {
|
||||
t.Errorf("regular gas = %d, want %d", gp.cumulativeRegular, tc.wantRegular)
|
||||
}
|
||||
if gp.cumulativeState != tc.wantState {
|
||||
t.Errorf("state gas = %d, want %d", gp.cumulativeState, tc.wantState)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestEIP2780NewAccountFunded verifies that a value transfer creating a new
|
||||
// account both materializes and funds the recipient.
|
||||
func TestEIP2780NewAccountFunded(t *testing.T) {
|
||||
fresh := common.HexToAddress("0xbeef000000000000000000000000000000000002")
|
||||
sdb := mkState(senderAlloc(nil))
|
||||
if _, _, err := applyMsg(t, sdb, callTx(0, fresh, 1, 300_000, nil)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !sdb.Exist(fresh) || sdb.GetBalance(fresh).Cmp(uint256.NewInt(1)) != 0 {
|
||||
t.Fatalf("recipient not funded: exist=%v balance=%v", sdb.Exist(fresh), sdb.GetBalance(fresh))
|
||||
}
|
||||
}
|
||||
|
||||
// TestEIP2780InsufficientGasForCallCharge verifies that a value transfer
|
||||
// creating a new account is rejected when the gas limit only covers the 21,000
|
||||
// intrinsic base and not the additional new-account state gas charged before the
|
||||
// call executes.
|
||||
func TestEIP2780InsufficientGasForCallCharge(t *testing.T) {
|
||||
fresh := common.HexToAddress("0xbeef000000000000000000000000000000000003")
|
||||
sdb := mkState(senderAlloc(nil))
|
||||
_, _, err := applyMsg(t, sdb, callTx(0, fresh, 1, 21_000, nil))
|
||||
if !errors.Is(err, ErrEIP2780CallCharge) {
|
||||
t.Fatalf("expected ErrEIP2780CallCharge, got %v", err)
|
||||
}
|
||||
if sdb.Exist(fresh) {
|
||||
t.Fatal("recipient should not be created when the call charge cannot be paid")
|
||||
}
|
||||
}
|
||||
|
|
@ -66,11 +66,6 @@ var (
|
|||
// have enough funds for transfer(topmost call only).
|
||||
ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer")
|
||||
|
||||
// ErrInsufficientBalanceWitness is returned if the transaction sender has enough
|
||||
// funds to cover the transfer, but not enough to pay for witness access/modification
|
||||
// costs for the transaction
|
||||
ErrInsufficientBalanceWitness = errors.New("insufficient funds to cover witness access costs for transaction")
|
||||
|
||||
// ErrInsufficientFunds is returned if the total cost of executing a transaction
|
||||
// is higher than the balance of the user's account.
|
||||
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
||||
|
|
@ -86,6 +81,10 @@ var (
|
|||
// than required for the data floor cost.
|
||||
ErrFloorDataGas = errors.New("insufficient gas for floor data gas cost")
|
||||
|
||||
// ErrEIP2780CallCharge is returned if the transaction doesn't have sufficient
|
||||
// gas to cover the cost of EIP-2780 call charge.
|
||||
ErrEIP2780CallCharge = errors.New("insufficient gas for EIP-2780 call charge")
|
||||
|
||||
// ErrTxTypeNotSupported is returned if a transaction is not supported in the
|
||||
// current network configuration.
|
||||
ErrTxTypeNotSupported = types.ErrTxTypeNotSupported
|
||||
|
|
|
|||
|
|
@ -88,10 +88,10 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated
|
|||
blockAccessList = bal.NewConstructionBlockAccessList()
|
||||
)
|
||||
defer evm.Release()
|
||||
|
||||
if jumpDestCache != nil {
|
||||
evm.SetJumpDestCache(jumpDestCache)
|
||||
}
|
||||
|
||||
// Run the pre-execution system calls
|
||||
blockAccessList.Merge(PreExecution(ctx, block.BeaconRoot(), block.ParentHash(), config, evm, block.Number(), block.Time()))
|
||||
|
||||
|
|
|
|||
|
|
@ -68,16 +68,15 @@ func (result *ExecutionResult) Revert() []byte {
|
|||
}
|
||||
|
||||
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
||||
func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.SetCodeAuthorization, isContractCreation bool, rules params.Rules, costPerStateByte uint64) (vm.GasCosts, error) {
|
||||
func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.SetCodeAuthorization, from common.Address, to *common.Address, value *uint256.Int, rules params.Rules, costPerStateByte uint64) (vm.GasCosts, error) {
|
||||
isContractCreation := to == nil
|
||||
|
||||
// Set the starting gas for the raw transaction
|
||||
var gas vm.GasCosts
|
||||
if isContractCreation && rules.IsHomestead {
|
||||
if rules.IsAmsterdam {
|
||||
gas.RegularGas = params.TxGas + params.CreateGasAmsterdam
|
||||
gas.StateGas = params.AccountCreationSize * costPerStateByte
|
||||
} else {
|
||||
gas.RegularGas = params.TxGasContractCreation
|
||||
}
|
||||
if rules.IsAmsterdam {
|
||||
gas = intrinsicBaseGasEIP2780(from, to, value, costPerStateByte)
|
||||
} else if isContractCreation && rules.IsHomestead {
|
||||
gas.RegularGas = params.TxGasContractCreation
|
||||
} else {
|
||||
gas.RegularGas = params.TxGas
|
||||
}
|
||||
|
|
@ -151,6 +150,39 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
|
|||
return gas, nil
|
||||
}
|
||||
|
||||
// intrinsicBaseGasEIP2780 computes the EIP-2780 intrinsic base cost.
|
||||
func intrinsicBaseGasEIP2780(from common.Address, to *common.Address, value *uint256.Int, costPerStateByte uint64) vm.GasCosts {
|
||||
var (
|
||||
isContractCreation = to == nil
|
||||
isSelfTransfer = to != nil && *to == from
|
||||
hasValue = value != nil && !value.IsZero()
|
||||
)
|
||||
// tx.sender: signature recovery plus the sender account access and write.
|
||||
gas := vm.GasCosts{RegularGas: params.TxBaseCost2780}
|
||||
|
||||
// tx.to charge.
|
||||
switch {
|
||||
case isSelfTransfer:
|
||||
// The recipient account is already accessed and written as the sender.
|
||||
case isContractCreation:
|
||||
gas.RegularGas += params.CreateAccess2780
|
||||
gas.StateGas += params.AccountCreationSize * costPerStateByte
|
||||
default:
|
||||
gas.RegularGas += params.ColdAccountAccess2780
|
||||
}
|
||||
|
||||
// tx.value charge.
|
||||
switch {
|
||||
case !hasValue || isSelfTransfer:
|
||||
// No transfer log and no recipient balance write.
|
||||
case isContractCreation:
|
||||
gas.RegularGas += params.TransferLogCost2780
|
||||
default:
|
||||
gas.RegularGas += params.TransferLogCost2780 + params.TxValueCost2780
|
||||
}
|
||||
return gas
|
||||
}
|
||||
|
||||
// FloorDataGas computes the minimum gas required for a transaction based on its data tokens (EIP-7623).
|
||||
func FloorDataGas(rules params.Rules, data []byte, accessList types.AccessList) (uint64, error) {
|
||||
var (
|
||||
|
|
@ -622,7 +654,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
contractCreation = msg.To == nil
|
||||
floorDataGas uint64
|
||||
)
|
||||
cost, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules, st.evm.Context.CostPerStateByte)
|
||||
cost, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, msg.From, msg.To, msg.Value, rules, st.evm.Context.CostPerStateByte)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -714,6 +746,10 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
if addr, ok := types.ParseDelegation(st.state.GetCode(*msg.To)); ok {
|
||||
st.state.AddAddressToAccessList(addr)
|
||||
}
|
||||
// EIP-2780: charge the transaction's top-level recipient costs.
|
||||
if rules.IsAmsterdam && !st.chargeCallRecipientEIP2780(value) {
|
||||
return nil, fmt.Errorf("%w: address %v", ErrEIP2780CallCharge, msg.To.Hex())
|
||||
}
|
||||
// Execute the transaction's call.
|
||||
ret, result, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining.ForwardAll(), value)
|
||||
st.gasRemaining.Absorb(result)
|
||||
|
|
@ -786,6 +822,39 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// chargeCallRecipientEIP2780 applies the EIP-2780 transaction top-level gas costs for
|
||||
// a message-call transaction, charged before any opcode executes:
|
||||
//
|
||||
// - if the recipient is EIP-161 empty and the transaction carries value, charge
|
||||
// for account creation.
|
||||
//
|
||||
// - if the recipient is an EIP-7702 delegated account, resolving the delegation
|
||||
// loads the target's code, charged an additional cold account access in
|
||||
// regular gas.
|
||||
func (st *stateTransition) chargeCallRecipientEIP2780(value *uint256.Int) bool {
|
||||
var (
|
||||
cost vm.GasCosts
|
||||
to = *st.msg.To
|
||||
)
|
||||
if !value.IsZero() && st.state.Empty(to) {
|
||||
cost.StateGas += params.AccountCreationSize * st.evm.Context.CostPerStateByte
|
||||
}
|
||||
if _, ok := types.ParseDelegation(st.state.GetCode(to)); ok {
|
||||
cost.RegularGas += params.ColdAccountAccess2780
|
||||
}
|
||||
if cost == (vm.GasCosts{}) {
|
||||
return true
|
||||
}
|
||||
prior, ok := st.gasRemaining.Charge(cost)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if st.evm.Config.Tracer.HasGasHook() {
|
||||
st.evm.Config.Tracer.EmitGasChange(prior.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxIntrinsicGas)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// settleGas finalizes the per-tx gas accounting after EVM execution:
|
||||
//
|
||||
// - Snapshots the EIP-8037 block-level 2D figures (tx_regular_gas,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
func TestFloorDataGas(t *testing.T) {
|
||||
|
|
@ -155,6 +156,7 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
isEIP2028 bool
|
||||
isEIP3860 bool
|
||||
isAmsterdam bool
|
||||
value *uint256.Int
|
||||
want vm.GasCosts
|
||||
}{
|
||||
{
|
||||
|
|
@ -237,8 +239,9 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
},
|
||||
isEIP2028: true,
|
||||
isAmsterdam: true,
|
||||
// base access-list charge + EIP-7981 extra
|
||||
want: vm.GasCosts{RegularGas: params.TxGas +
|
||||
// EIP-2780: zero-value call base is TxBaseCost + ColdAccountAccess
|
||||
// (15,000). Plus base access-list charge + EIP-7981 extra.
|
||||
want: vm.GasCosts{RegularGas: params.TxBaseCost2780 + params.ColdAccountAccess2780 +
|
||||
2*params.TxAccessListAddressGas + 3*params.TxAccessListStorageKeyGas +
|
||||
2*amsterdamAddressCost + 3*amsterdamStorageKeyCost},
|
||||
},
|
||||
|
|
@ -259,10 +262,10 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
isHomestead: true,
|
||||
isEIP2028: true,
|
||||
isAmsterdam: true,
|
||||
// EIP-8037: creation regular gas is TxGas + CreateGasAmsterdam (not TxGasContractCreation),
|
||||
// and account-creation cost is moved to state gas.
|
||||
// EIP-2780: creation regular gas is TxBaseCost + CreateAccess (23,000),
|
||||
// and account-creation cost is charged as state gas.
|
||||
want: vm.GasCosts{
|
||||
RegularGas: params.TxGas + params.CreateGasAmsterdam,
|
||||
RegularGas: params.TxBaseCost2780 + params.CreateAccess2780,
|
||||
StateGas: params.AccountCreationSize * params.CostPerStateByte,
|
||||
},
|
||||
},
|
||||
|
|
@ -275,7 +278,7 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
isEIP3860: true, // Shanghai gates init-code word gas
|
||||
isAmsterdam: true,
|
||||
want: vm.GasCosts{
|
||||
RegularGas: params.TxGas + params.CreateGasAmsterdam +
|
||||
RegularGas: params.TxBaseCost2780 + params.CreateAccess2780 +
|
||||
64*params.TxDataZeroGas + 2*params.InitCodeWordGas,
|
||||
StateGas: params.AccountCreationSize * params.CostPerStateByte,
|
||||
},
|
||||
|
|
@ -292,7 +295,7 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
isEIP3860: true,
|
||||
isAmsterdam: true,
|
||||
want: vm.GasCosts{
|
||||
RegularGas: params.TxGas + params.CreateGasAmsterdam +
|
||||
RegularGas: params.TxBaseCost2780 + params.CreateAccess2780 +
|
||||
32*params.TxDataNonZeroGasEIP2028 + 1*params.InitCodeWordGas +
|
||||
1*params.TxAccessListAddressGas + 1*params.TxAccessListStorageKeyGas +
|
||||
1*amsterdamAddressCost + 1*amsterdamStorageKeyCost,
|
||||
|
|
@ -314,7 +317,7 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
// regular: TxAuthTupleRegularGas (7500) per auth
|
||||
// state: (AuthorizationCreationSize + AccountCreationSize) * CostPerStateByte per auth
|
||||
want: vm.GasCosts{
|
||||
RegularGas: params.TxGas +
|
||||
RegularGas: params.TxBaseCost2780 + params.ColdAccountAccess2780 +
|
||||
100*params.TxDataNonZeroGasEIP2028 +
|
||||
1*params.TxAccessListAddressGas + 1*params.TxAccessListStorageKeyGas +
|
||||
1*amsterdamAddressCost + 1*amsterdamStorageKeyCost +
|
||||
|
|
@ -322,6 +325,28 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
StateGas: 1 * (params.AuthorizationCreationSize + params.AccountCreationSize) * params.CostPerStateByte,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "amsterdam/value-transfer-call",
|
||||
isEIP2028: true,
|
||||
isAmsterdam: true,
|
||||
value: uint256.NewInt(1),
|
||||
// EIP-2780: TxBaseCost + ColdAccountAccess + TransferLogCost + TxValueCost = 21,000.
|
||||
want: vm.GasCosts{RegularGas: params.TxBaseCost2780 + params.ColdAccountAccess2780 +
|
||||
params.TransferLogCost2780 + params.TxValueCost2780},
|
||||
},
|
||||
{
|
||||
name: "amsterdam/value-bearing-contract-creation",
|
||||
creation: true,
|
||||
isHomestead: true,
|
||||
isEIP2028: true,
|
||||
isAmsterdam: true,
|
||||
value: uint256.NewInt(1),
|
||||
// EIP-2780: TxBaseCost + CreateAccess + TransferLogCost = 24,756, plus account-creation state gas.
|
||||
want: vm.GasCosts{
|
||||
RegularGas: params.TxBaseCost2780 + params.CreateAccess2780 + params.TransferLogCost2780,
|
||||
StateGas: params.AccountCreationSize * params.CostPerStateByte,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
@ -331,8 +356,12 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
IsShanghai: tt.isEIP3860,
|
||||
IsAmsterdam: tt.isAmsterdam,
|
||||
}
|
||||
var to *common.Address
|
||||
if !tt.creation {
|
||||
to = &addr1
|
||||
}
|
||||
got, err := IntrinsicGas(tt.data, tt.accessList, tt.authList,
|
||||
tt.creation, rules, params.CostPerStateByte)
|
||||
common.Address{}, to, tt.value, rules, params.CostPerStateByte)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -116,7 +117,8 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
|||
return core.ErrTipAboveFeeCap
|
||||
}
|
||||
// Make sure the transaction is signed properly
|
||||
if _, err := types.Sender(signer, tx); err != nil {
|
||||
from, err := types.Sender(signer, tx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrInvalidSender, err)
|
||||
}
|
||||
// Limit nonce to 2^64-1 per EIP-2681
|
||||
|
|
@ -125,7 +127,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
|||
}
|
||||
// Ensure the transaction has more gas than the bare minimum needed to cover
|
||||
// the transaction metadata
|
||||
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, params.CostPerStateByte)
|
||||
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), from, tx.To(), uint256.MustFromBig(tx.Value()), rules, params.CostPerStateByte)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,13 @@ const (
|
|||
TxAuthTupleGas uint64 = 12500 // Per auth tuple code specified in EIP-7702
|
||||
TxAuthTupleRegularGas uint64 = 7500 // Per auth tuple regular gas specified in EIP-8037
|
||||
|
||||
// EIP-2780: resource-based intrinsic transaction gas.
|
||||
TxBaseCost2780 uint64 = 12000
|
||||
ColdAccountAccess2780 uint64 = 3000
|
||||
CreateAccess2780 uint64 = 11000
|
||||
TxValueCost2780 uint64 = 4244
|
||||
TransferLogCost2780 uint64 = 1756
|
||||
|
||||
// These have been changed during the course of the chain
|
||||
CallGasFrontier uint64 = 40 // Once per CALL operation & message call transaction.
|
||||
CallGasEIP150 uint64 = 700 // Static portion of gas for CALL-derivates after EIP 150 (Tangerine)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// TransactionTest checks RLP decoding and sender derivation of transactions.
|
||||
|
|
@ -81,7 +82,7 @@ func (tt *TransactionTest) Run() error {
|
|||
return
|
||||
}
|
||||
// Intrinsic cost
|
||||
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, params.CostPerStateByte)
|
||||
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), sender, tx.To(), uint256.MustFromBig(tx.Value()), rules, params.CostPerStateByte)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue