mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-19 11:20:45 +00:00
core, cmd: 8037 changes and etc
This commit is contained in:
parent
0246297108
commit
359da7772f
17 changed files with 301 additions and 310 deletions
|
|
@ -140,15 +140,15 @@ func Transaction(ctx *cli.Context) error {
|
|||
value = uint256.NewInt(1)
|
||||
}
|
||||
rules := chainConfig.Rules(common.Big0, true, 0)
|
||||
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), r.Address, tx.To(), value, rules, params.CostPerStateByte)
|
||||
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), r.Address, tx.To(), value, rules)
|
||||
if err != nil {
|
||||
r.Error = err
|
||||
results = append(results, r)
|
||||
continue
|
||||
}
|
||||
r.IntrinsicGas = cost.RegularGas
|
||||
if tx.Gas() < cost.RegularGas {
|
||||
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), cost.RegularGas)
|
||||
r.IntrinsicGas = cost
|
||||
if tx.Gas() < cost {
|
||||
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), cost)
|
||||
results = append(results, r)
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, common.Address{}, &toaddr, nil, params.Rules{}, params.CostPerStateByte)
|
||||
cost, _ := IntrinsicGas(data, nil, nil, common.Address{}, &toaddr, nil, params.Rules{})
|
||||
signer := gen.Signer()
|
||||
gasPrice := big.NewInt(0)
|
||||
if gen.header.BaseFee != nil {
|
||||
|
|
@ -99,7 +99,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
|||
Nonce: gen.TxNonce(benchRootAddr),
|
||||
To: &toaddr,
|
||||
Value: big.NewInt(1),
|
||||
Gas: cost.RegularGas,
|
||||
Gas: cost,
|
||||
Data: data,
|
||||
GasPrice: gasPrice,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -65,12 +65,12 @@ var (
|
|||
func TestProcessUBT(t *testing.T) {
|
||||
var (
|
||||
code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`)
|
||||
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, common.Address{}, nil, nil, 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})
|
||||
// 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, common.Address{}, nil, nil, 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})
|
||||
signer = types.LatestSigner(testUBTChainConfig)
|
||||
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain
|
||||
|
|
@ -102,11 +102,11 @@ func TestProcessUBT(t *testing.T) {
|
|||
|
||||
txCost1 := params.TxGas
|
||||
txCost2 := params.TxGas
|
||||
contractCreationCost := intrinsicContractCreationGas.RegularGas +
|
||||
contractCreationCost := intrinsicContractCreationGas +
|
||||
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */
|
||||
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */
|
||||
739 /* execution costs */
|
||||
codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas.RegularGas +
|
||||
codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas +
|
||||
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (tx) */
|
||||
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at pc=0x20) */
|
||||
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */
|
||||
|
|
|
|||
|
|
@ -38,19 +38,19 @@ func TestEIP2780Intrinsic(t *testing.T) {
|
|||
to *common.Address
|
||||
value *uint256.Int
|
||||
auths []types.SetCodeAuthorization
|
||||
want vm.GasCosts
|
||||
want uint64
|
||||
}{
|
||||
{
|
||||
name: "self-transfer",
|
||||
to: &from,
|
||||
value: uint256.NewInt(1),
|
||||
want: vm.GasCosts{RegularGas: params.TxBaseCost2780}, // 12,000
|
||||
want: params.TxBaseCost2780, // 12,000
|
||||
},
|
||||
{
|
||||
name: "self-transfer/zero-value",
|
||||
to: &from,
|
||||
value: uint256.NewInt(0),
|
||||
want: vm.GasCosts{RegularGas: params.TxBaseCost2780}, // 12,000
|
||||
want: params.TxBaseCost2780, // 12,000
|
||||
},
|
||||
{
|
||||
name: "zero-value call",
|
||||
|
|
@ -58,15 +58,15 @@ func TestEIP2780Intrinsic(t *testing.T) {
|
|||
value: uint256.NewInt(0),
|
||||
// TxBaseCost + ColdAccountAccess = 15,000; the recipient touch is
|
||||
// charged at the cold rate unconditionally at the intrinsic phase.
|
||||
want: vm.GasCosts{RegularGas: params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam},
|
||||
want: params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam,
|
||||
},
|
||||
{
|
||||
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.ColdAccountAccessAmsterdam +
|
||||
params.TxValueCost2780 + params.TransferLogCost2780},
|
||||
want: params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam +
|
||||
params.TxValueCost2780 + params.TransferLogCost2780,
|
||||
},
|
||||
{
|
||||
name: "contract creation, value = 0",
|
||||
|
|
@ -75,18 +75,14 @@ func TestEIP2780Intrinsic(t *testing.T) {
|
|||
// TxBaseCost + CreateAccess = 23,000 regular. The new-account state
|
||||
// charge depends on whether the deployment target exists and is
|
||||
// charged at runtime, not intrinsically.
|
||||
want: vm.GasCosts{
|
||||
RegularGas: params.TxBaseCost2780 + params.CreateAccessAmsterdam,
|
||||
},
|
||||
want: params.TxBaseCost2780 + params.CreateAccessAmsterdam,
|
||||
},
|
||||
{
|
||||
name: "contract creation, value > 0",
|
||||
to: nil,
|
||||
value: uint256.NewInt(1),
|
||||
// TxBaseCost + CreateAccess + TransferLogCost = 24,756 regular.
|
||||
want: vm.GasCosts{
|
||||
RegularGas: params.TxBaseCost2780 + params.CreateAccessAmsterdam + params.TransferLogCost2780,
|
||||
},
|
||||
want: params.TxBaseCost2780 + params.CreateAccessAmsterdam + params.TransferLogCost2780,
|
||||
},
|
||||
{
|
||||
name: "value transfer with authorizations",
|
||||
|
|
@ -95,13 +91,13 @@ func TestEIP2780Intrinsic(t *testing.T) {
|
|||
auths: make([]types.SetCodeAuthorization, 3),
|
||||
// Each authorization adds the state-independent per-auth base
|
||||
// (cold authority access included).
|
||||
want: vm.GasCosts{RegularGas: params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam +
|
||||
params.TxValueCost2780 + params.TransferLogCost2780 + 3*params.RegularPerAuthBaseCost},
|
||||
want: params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam +
|
||||
params.TxValueCost2780 + params.TransferLogCost2780 + 3*params.RegularPerAuthBaseCost,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := IntrinsicGas(nil, nil, tc.auths, from, tc.to, tc.value, rules8037, params.CostPerStateByte)
|
||||
got, err := IntrinsicGas(nil, nil, tc.auths, from, tc.to, tc.value, rules8037)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
|
@ -426,9 +422,10 @@ func TestEIP2780HaltKeepsAuthStateGas(t *testing.T) {
|
|||
if code := sdb.GetCode(authority); len(code) == 0 {
|
||||
t.Fatal("delegation should persist through an in-frame halt")
|
||||
}
|
||||
// The regular dimension is burned in full by the halt; the state dimension
|
||||
// keeps the delegation's durable growth: a new account leaf plus the
|
||||
// 23-byte indicator.
|
||||
// The regular dimension is burned in full by the halt: the intrinsic cost
|
||||
// plus the entire regular execution budget, which together equal the
|
||||
// MaxTxGas cap. The state dimension keeps the delegation's durable
|
||||
// growth: a new account leaf plus the 23-byte indicator.
|
||||
if gp.cumulativeRegular != params.MaxTxGas {
|
||||
t.Errorf("regular gas = %d, want %d", gp.cumulativeRegular, params.MaxTxGas)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,8 +106,16 @@ func applyMsg(t *testing.T, sdb *state.StateDB, tx *types.Transaction) (*Executi
|
|||
res, err := st.execute()
|
||||
if err == nil && res != nil {
|
||||
assertPoolSane(t, res, gp)
|
||||
limit := min(msg.GasLimit, params.MaxTxGas)
|
||||
assertBudgetSane(t, vm.NewGasBudget(limit, msg.GasLimit-limit), st.gasRemaining)
|
||||
// The budget is seeded with the post-intrinsic remainder: the intrinsic
|
||||
// cost counts towards the MaxTxGas regular cap and the execution gas
|
||||
// exceeding the regular budget forms the reservoir.
|
||||
intrinsic, ierr := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, msg.From, msg.To, msg.Value, rules8037)
|
||||
if ierr != nil {
|
||||
t.Fatalf("intrinsic gas: %v", ierr)
|
||||
}
|
||||
executionGas := msg.GasLimit - intrinsic
|
||||
gasLeft := min(params.MaxTxGas-intrinsic, executionGas)
|
||||
assertBudgetSane(t, vm.NewGasBudget(gasLeft, executionGas-gasLeft), st.gasRemaining)
|
||||
}
|
||||
return res, gp, err
|
||||
}
|
||||
|
|
@ -193,15 +201,12 @@ var (
|
|||
// charge depends on whether the deployment target exists and is charged at
|
||||
// runtime (EIP-2780), not intrinsically.
|
||||
func TestCreateTxIntrinsicNoStateGas(t *testing.T) {
|
||||
cost, err := IntrinsicGas(nil, nil, nil, common.Address{}, nil, nil, rules8037, params.CostPerStateByte)
|
||||
cost, err := IntrinsicGas(nil, nil, nil, common.Address{}, nil, nil, rules8037)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cost.StateGas != 0 {
|
||||
t.Fatalf("intrinsic state gas = %d, want 0", cost.StateGas)
|
||||
}
|
||||
if want := params.TxBaseCost2780 + params.CreateAccessAmsterdam; cost.RegularGas != want {
|
||||
t.Fatalf("intrinsic regular gas = %d, want %d", cost.RegularGas, want)
|
||||
if want := params.TxBaseCost2780 + params.CreateAccessAmsterdam; cost != want {
|
||||
t.Fatalf("intrinsic gas = %d, want %d", cost, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -481,19 +486,16 @@ var delegate8037 = common.HexToAddress("0xde1e8a7e")
|
|||
// Intrinsic gas charges only the state-independent per-authorization base;
|
||||
// the state-dependent charges are applied at runtime (EIP-2780).
|
||||
func TestAuthIntrinsicBaseOnly(t *testing.T) {
|
||||
cost, err := IntrinsicGas(nil, nil, []types.SetCodeAuthorization{{}}, common.Address{}, &delegate8037, nil, rules8037, params.CostPerStateByte)
|
||||
cost, err := IntrinsicGas(nil, nil, []types.SetCodeAuthorization{{}}, common.Address{}, &delegate8037, nil, rules8037)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cost.StateGas != 0 {
|
||||
t.Fatalf("intrinsic state gas = %d, want 0", cost.StateGas)
|
||||
}
|
||||
// The recipient touch and the per-authorization authority access (priced
|
||||
// into RegularPerAuthBaseCost) are both charged at the cold rate
|
||||
// unconditionally at the intrinsic phase (EIP-2780).
|
||||
want := params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam + params.RegularPerAuthBaseCost
|
||||
if cost.RegularGas != want {
|
||||
t.Fatalf("intrinsic regular gas = %d, want %d", cost.RegularGas, want)
|
||||
if cost != want {
|
||||
t.Fatalf("intrinsic gas = %d, want %d", cost, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,24 +69,24 @@ 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, from common.Address, to *common.Address, value *uint256.Int, 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) (uint64, error) {
|
||||
isContractCreation := to == nil
|
||||
|
||||
// Set the starting gas for the raw transaction
|
||||
var gas vm.GasCosts
|
||||
var gas uint64
|
||||
if rules.IsAmsterdam {
|
||||
gas.RegularGas = intrinsicBaseGasEIP2780(from, to, value)
|
||||
gas = intrinsicBaseGasEIP2780(from, to, value)
|
||||
} else if isContractCreation && rules.IsHomestead {
|
||||
gas.RegularGas = params.TxGasContractCreation
|
||||
gas = params.TxGasContractCreation
|
||||
} else {
|
||||
gas.RegularGas = params.TxGas
|
||||
gas = params.TxGas
|
||||
}
|
||||
// Add gas for authorizations
|
||||
if authList != nil {
|
||||
if rules.IsAmsterdam {
|
||||
gas.RegularGas += uint64(len(authList)) * params.RegularPerAuthBaseCost
|
||||
gas += uint64(len(authList)) * params.RegularPerAuthBaseCost
|
||||
} else {
|
||||
gas.RegularGas += uint64(len(authList)) * params.CallNewAccountGas
|
||||
gas += uint64(len(authList)) * params.CallNewAccountGas
|
||||
}
|
||||
}
|
||||
// Bump the required gas by the amount of transactional data
|
||||
|
|
@ -101,22 +101,22 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
|
|||
if rules.IsIstanbul {
|
||||
nonZeroGas = params.TxDataNonZeroGasEIP2028
|
||||
}
|
||||
if (math.MaxUint64-gas.RegularGas)/nonZeroGas < nz {
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
if (math.MaxUint64-gas)/nonZeroGas < nz {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
gas.RegularGas += nz * nonZeroGas
|
||||
gas += nz * nonZeroGas
|
||||
|
||||
if (math.MaxUint64-gas.RegularGas)/params.TxDataZeroGas < z {
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
gas.RegularGas += z * params.TxDataZeroGas
|
||||
gas += z * params.TxDataZeroGas
|
||||
|
||||
if isContractCreation && rules.IsShanghai {
|
||||
lenWords := toWordSize(dataLen)
|
||||
if (math.MaxUint64-gas.RegularGas)/params.InitCodeWordGas < lenWords {
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
gas.RegularGas += lenWords * params.InitCodeWordGas
|
||||
gas += lenWords * params.InitCodeWordGas
|
||||
}
|
||||
}
|
||||
// Add the gas for accessList
|
||||
|
|
@ -131,14 +131,14 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
|
|||
addressCost = params.TxAccessListAddressGasAmsterdam
|
||||
storageKeyCost = params.TxAccessListStorageKeyGasAmsterdam
|
||||
}
|
||||
if (math.MaxUint64-gas.RegularGas)/addressCost < addresses {
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
if (math.MaxUint64-gas)/addressCost < addresses {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
gas.RegularGas += addresses * addressCost
|
||||
if (math.MaxUint64-gas.RegularGas)/storageKeyCost < storageKeys {
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
gas += addresses * addressCost
|
||||
if (math.MaxUint64-gas)/storageKeyCost < storageKeys {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
gas.RegularGas += storageKeys * storageKeyCost
|
||||
gas += storageKeys * storageKeyCost
|
||||
|
||||
// EIP-7981: access list data is charged in addition to the base charge.
|
||||
if rules.IsAmsterdam {
|
||||
|
|
@ -146,21 +146,20 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
|
|||
addressCost = common.AddressLength * params.TxCostFloorPerToken7976 * params.TxTokenPerNonZeroByte
|
||||
storageKeyCost = common.HashLength * params.TxCostFloorPerToken7976 * params.TxTokenPerNonZeroByte
|
||||
)
|
||||
if (math.MaxUint64-gas.RegularGas)/addressCost < addresses {
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
if (math.MaxUint64-gas)/addressCost < addresses {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
gas.RegularGas += addresses * addressCost
|
||||
if (math.MaxUint64-gas.RegularGas)/storageKeyCost < storageKeys {
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
gas += addresses * addressCost
|
||||
if (math.MaxUint64-gas)/storageKeyCost < storageKeys {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
gas.RegularGas += storageKeys * storageKeyCost
|
||||
gas += storageKeys * storageKeyCost
|
||||
}
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
// intrinsicBaseGasEIP2780 computes the regular-gas portion of the EIP-2780
|
||||
// intrinsic base cost: the per-resource decomposition of the legacy flat 21,000.
|
||||
// intrinsicBaseGasEIP2780 computes the intrinsic base cost of the transaction.
|
||||
func intrinsicBaseGasEIP2780(from common.Address, to *common.Address, value *uint256.Int) uint64 {
|
||||
var (
|
||||
isContractCreation = to == nil
|
||||
|
|
@ -425,24 +424,16 @@ func (st *stateTransition) to() common.Address {
|
|||
return *st.msg.To
|
||||
}
|
||||
|
||||
// buyGas pre-pays gas from the sender's balance and initializes the
|
||||
// transaction's gas budget. It is invoked at the tail of preCheck.
|
||||
// buyGas pre-pays gas from the sender's balance.
|
||||
//
|
||||
// The balance requirement is the worst-case ETH the tx may need to lock
|
||||
// up: `msg.GasLimit × max(msg.GasPrice, msg.GasFeeCap) + msg.Value`,
|
||||
// plus `blobGas × msg.BlobGasFeeCap` under Cancun. Insufficient balance
|
||||
// returns ErrInsufficientFunds. After the check, the sender is actually
|
||||
// debited `msg.GasLimit × msg.GasPrice` (plus `blobGas × blobBaseFee`
|
||||
// under Cancun), the cap-vs-tip differential is settled at tx end.
|
||||
// returns ErrInsufficientFunds.
|
||||
//
|
||||
// The gas budget is seeded into both `initialBudget` (frozen snapshot
|
||||
// for tx-end accounting) and `gasRemaining` (live running balance):
|
||||
//
|
||||
// - Pre-Amsterdam: one-dimensional regular budget equal to
|
||||
// `msg.GasLimit`; the state-gas reservoir is zero.
|
||||
// - Amsterdam+ (EIP-8037): two-dimensional budget. Regular gas is
|
||||
// capped at `MaxTxGas` (EIP-7825, 16_777_216); any excess from
|
||||
// `msg.GasLimit` above that cap becomes the state-gas reservoir.
|
||||
// After the check, the sender is actually debited `msg.GasLimit × msg.GasPrice`
|
||||
// (plus `blobGas × blobBaseFee` under Cancun), the cap-vs-tip differential
|
||||
// is settled at tx end.
|
||||
func (st *stateTransition) buyGas() error {
|
||||
mgval := new(uint256.Int).SetUint64(st.msg.GasLimit)
|
||||
_, overflow := mgval.MulOverflow(mgval, st.msg.GasPrice)
|
||||
|
|
@ -495,53 +486,60 @@ func (st *stateTransition) buyGas() error {
|
|||
if have, want := st.state.GetBalance(st.msg.From), balanceCheck; have.Cmp(want) < 0 {
|
||||
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
|
||||
}
|
||||
isAmsterdam := st.evm.ChainConfig().IsAmsterdam(st.evm.Context.BlockNumber, st.evm.Context.Time)
|
||||
|
||||
// Reserve the gas budget in the block gas pool
|
||||
var err error
|
||||
if isAmsterdam {
|
||||
err = st.gp.CheckGasAmsterdam(min(st.msg.GasLimit, params.MaxTxGas), st.msg.GasLimit)
|
||||
} else {
|
||||
err = st.gp.CheckGasLegacy(st.msg.GasLimit)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// After Amsterdam we limit the regular gas to 16M, the data gas to the transaction limit
|
||||
limit := st.msg.GasLimit
|
||||
if isAmsterdam {
|
||||
limit = min(st.msg.GasLimit, params.MaxTxGas)
|
||||
}
|
||||
st.gasRemaining = vm.NewGasBudget(limit, st.msg.GasLimit-limit)
|
||||
|
||||
if st.evm.Config.Tracer.HasGasHook() {
|
||||
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{}, st.gasRemaining.AsTracing(), tracing.GasChangeTxInitialBalance)
|
||||
}
|
||||
// Deduct the gas cost from the sender's balance
|
||||
st.state.SubBalance(st.msg.From, mgval, tracing.BalanceDecreaseGasBuy)
|
||||
return nil
|
||||
}
|
||||
|
||||
// initRuntimeGasBudget initializes the transaction's running gas budget with the
|
||||
// gas remaining after the intrinsic cost has been deducted.
|
||||
//
|
||||
// After Amsterdam (EIP-8037) the intrinsic cost counts towards the EIP-7825
|
||||
// regular-gas cap:
|
||||
//
|
||||
// execution_gas = tx.gas - intrinsic_gas
|
||||
// regular_gas_budget = TX_MAX_GAS_LIMIT - intrinsic_gas
|
||||
// gas_left = min(regular_gas_budget, execution_gas)
|
||||
// state_gas_reservoir = execution_gas - gas_left
|
||||
func (st *stateTransition) initRuntimeGasBudget(rules params.Rules, intrinsicGas uint64) {
|
||||
executionGas := st.msg.GasLimit - intrinsicGas
|
||||
gasLeft := executionGas
|
||||
if rules.IsAmsterdam {
|
||||
gasLeft = min(params.MaxTxGas-intrinsicGas, executionGas)
|
||||
}
|
||||
st.gasRemaining = vm.NewGasBudget(gasLeft, executionGas-gasLeft)
|
||||
|
||||
if st.evm.Config.Tracer.HasGasHook() {
|
||||
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{Regular: st.msg.GasLimit}, st.gasRemaining.AsTracing(), tracing.GasChangeTxIntrinsicGas)
|
||||
}
|
||||
}
|
||||
|
||||
// preCheck performs all pre-execution validation that does not require
|
||||
// the EVM to run, then ends by calling buyGas to lock in the gas budget.
|
||||
// the EVM to run, then ends by calling buyGas to lock ether for prepay.
|
||||
// It returns a consensus error if any of the following fail:
|
||||
//
|
||||
// - Sender nonce matches state and is not at 2^64-1 (EIP-2681).
|
||||
// - EIP-7825 per-tx gas-limit cap on Osaka chains pre-Amsterdam
|
||||
// (the cap also bounds the regular dimension after Amsterdam, but
|
||||
// it is enforced there via the two-dimensional budget in buyGas).
|
||||
//
|
||||
// - EIP-7825 per-tx gas-limit cap on Osaka chains pre-Amsterdam.
|
||||
//
|
||||
// - EIP-3607 sender-is-EOA, allowing accounts whose only code is an
|
||||
// EIP-7702 delegation designator.
|
||||
//
|
||||
// - EIP-1559 fee-cap, tip-cap and base-fee constraints (London+).
|
||||
//
|
||||
// - Blob-tx structural checks: non-nil `To`, non-empty hash list,
|
||||
// valid KZG versioned hashes, count below `BlobTxMaxBlobs` (Osaka+).
|
||||
//
|
||||
// - Blob fee-cap not below the current blob base fee (Cancun+).
|
||||
//
|
||||
// - EIP-7702 set-code-tx shape: non-nil `To` and non-empty
|
||||
// authorization list.
|
||||
//
|
||||
// - EIP-3860 init code size cap on create transactions (Shanghai+,
|
||||
// with the raised Amsterdam cap).
|
||||
//
|
||||
// - Insufficient block gas budget for including the transaction.
|
||||
//
|
||||
// The SkipNonceChecks / SkipTransactionChecks / NoBaseFee flags bypass
|
||||
// subsets of these checks for simulation paths (eth_call, eth_estimateGas).
|
||||
func (st *stateTransition) preCheck(rules params.Rules) error {
|
||||
|
|
@ -640,6 +638,16 @@ func (st *stateTransition) preCheck(rules params.Rules) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
// Reserve the gas budget in the block gas pool
|
||||
var err error
|
||||
if rules.IsAmsterdam {
|
||||
err = st.gp.CheckGasAmsterdam(min(st.msg.GasLimit, params.MaxTxGas), st.msg.GasLimit)
|
||||
} else {
|
||||
err = st.gp.CheckGasLegacy(st.msg.GasLimit)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return st.buyGas()
|
||||
}
|
||||
|
||||
|
|
@ -665,22 +673,15 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
if err := st.preCheck(rules); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Charge intrinsic gas (with overflow detection inside IntrinsicGas).
|
||||
// Under Amsterdam the cost is two-dimensional and Charge debits both
|
||||
// regular and state in one step.
|
||||
cost, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, msg.From, msg.To, msg.Value, rules, st.evm.Context.CostPerStateByte)
|
||||
// Calculate the intrinsic gas of this transaction and make sure the gas limit
|
||||
// is sufficient to cover that.
|
||||
intrinsicGas, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, msg.From, msg.To, msg.Value, rules)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prior, sufficient := st.gasRemaining.Charge(cost)
|
||||
if !sufficient {
|
||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining.RegularGas, cost.RegularGas)
|
||||
if msg.GasLimit < intrinsicGas {
|
||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, msg.GasLimit, intrinsicGas)
|
||||
}
|
||||
if st.evm.Config.Tracer.HasGasHook() {
|
||||
st.evm.Config.Tracer.EmitGasChange(prior.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxIntrinsicGas)
|
||||
}
|
||||
|
||||
// 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.
|
||||
if rules.IsPrague {
|
||||
|
|
@ -693,13 +694,15 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
if msg.GasLimit < floorDataGas {
|
||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrFloorDataGas, msg.GasLimit, floorDataGas)
|
||||
}
|
||||
// In Amsterdam, the transaction gas limit is allowed to exceed
|
||||
// params.MaxTxGas, but the calldata floor cost is capped by it.
|
||||
if rules.IsAmsterdam && max(cost.RegularGas, floorDataGas) > params.MaxTxGas {
|
||||
return nil, fmt.Errorf("%w: regular intrisic cost %v, floor: %v", ErrFloorDataGas, cost.RegularGas, floorDataGas)
|
||||
}
|
||||
// In Amsterdam, the transaction gas limit is allowed to exceed
|
||||
// params.MaxTxGas, but the intrinsic cost and calldata floor
|
||||
// cost is still capped by it.
|
||||
if rules.IsAmsterdam && max(intrinsicGas, floorDataGas) > params.MaxTxGas {
|
||||
return nil, fmt.Errorf("%w: intrinsic cost %v, floor: %v", ErrFloorDataGas, intrinsicGas, floorDataGas)
|
||||
}
|
||||
|
||||
// EIP-4762 setup
|
||||
if rules.IsEIP4762 {
|
||||
st.evm.AccessEvents.AddTxOrigin(msg.From)
|
||||
|
||||
|
|
@ -724,6 +727,9 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
// - enable block-level accessList construction (EIP-7928)
|
||||
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
|
||||
|
||||
// Initialize the running gas budget with the post-intrinsic remainder.
|
||||
st.initRuntimeGasBudget(rules, intrinsicGas)
|
||||
|
||||
// Execute the top-most frame
|
||||
var (
|
||||
ret []byte
|
||||
|
|
@ -879,15 +885,11 @@ func (st *stateTransition) chargeRuntimeGas(cost vm.GasCosts) bool {
|
|||
}
|
||||
|
||||
// chargeCallRecipientEIP2780 applies the EIP-2780 runtime charges for the
|
||||
// top-level recipient of a message-call transaction, as the first frame is
|
||||
// top-level recipient of a message-call transaction, before the first frame is
|
||||
// entered:
|
||||
//
|
||||
// - the recipient touch was already charged at the cold rate unconditionally
|
||||
// at the intrinsic phase (EIP-2780) and the account is warm from
|
||||
// statedb.Prepare (EIP-2929), so no access charge or warming is due here;
|
||||
//
|
||||
// - if the recipient is EIP-161 non-existent and the transaction carries
|
||||
// value, the durable state growth of the new account;
|
||||
// - if the recipient is EIP-161 empty and the transaction carries value,
|
||||
// the durable state growth of the new account;
|
||||
//
|
||||
// - if the recipient is an EIP-7702 delegated account, resolving the
|
||||
// delegation loads the target's code: a cold account access, or a warm
|
||||
|
|
@ -899,18 +901,17 @@ func (st *stateTransition) chargeRuntimeGas(cost vm.GasCosts) bool {
|
|||
func (st *stateTransition) chargeCallRecipientEIP2780(value *uint256.Int) bool {
|
||||
to := *st.msg.To
|
||||
|
||||
// This runs in the topmost frame before any bytecode executes, so unlike the
|
||||
// execution-level checks which must use StateDB.Empty because SELFDESTRUCT can
|
||||
// leave a transient EIP-161-empty account, no empty account can exist here, and
|
||||
// !Exist is equivalent to Empty.
|
||||
if !value.IsZero() && !st.state.Exist(to) {
|
||||
// This runs in the topmost frame before any bytecode executes, non-existence
|
||||
// is equivalent with EIP-161-empty, as no preceding operation can leave a
|
||||
// transient EIP-161-empty account (such as zero-value transfer).
|
||||
if !value.IsZero() && st.state.Empty(to) {
|
||||
if !st.chargeRuntimeGas(vm.GasCosts{StateGas: params.AccountCreationSize * st.evm.Context.CostPerStateByte}) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if target, delegated := types.ParseDelegation(st.state.GetCode(to)); delegated {
|
||||
// Pay the delegation-target access before the target is warmed and
|
||||
// its code resolved (loaded) on frame entry.
|
||||
// its code resolved (loaded).
|
||||
cost := vm.GasCosts{RegularGas: params.ColdAccountAccessAmsterdam}
|
||||
if st.state.AddressInAccessList(target) {
|
||||
cost.RegularGas = params.WarmAccountAccessAmsterdam
|
||||
|
|
@ -958,7 +959,7 @@ func (st *stateTransition) settleGas(rules params.Rules, floorDataGas uint64) (g
|
|||
|
||||
// EIP-8037:
|
||||
// tx_gas_used_before_refund = tx.gas - tx_output.gas_left - tx_output.state_gas_reservoir
|
||||
// tx_state_gas = intrinsic_state_gas + tx_output.execution_state_gas_used
|
||||
// tx_state_gas = tx_output.execution_state_gas_used
|
||||
// tx_regular_gas = tx_gas_used_before_refund - tx_state_gas
|
||||
gasLeft := st.gasRemaining.RegularGas + st.gasRemaining.StateGas
|
||||
gasUsedBeforeRefund := st.msg.GasLimit - gasLeft
|
||||
|
|
@ -988,6 +989,7 @@ func (st *stateTransition) settleGas(rules params.Rules, floorDataGas uint64) (g
|
|||
peakUsed = max(peakUsed, floorDataGas)
|
||||
}
|
||||
|
||||
// Settle down the final gas consumption in the block-level pool
|
||||
if rules.IsAmsterdam {
|
||||
if err = st.gp.ChargeGasAmsterdam(txRegularGas, txStateGas, gasUsed); err != nil {
|
||||
return 0, 0, err
|
||||
|
|
@ -998,7 +1000,7 @@ func (st *stateTransition) settleGas(rules params.Rules, floorDataGas uint64) (g
|
|||
}
|
||||
}
|
||||
|
||||
// Refund leftover gas to the sender as ETH.
|
||||
// Refund leftover gas to the sender
|
||||
if gasLeft > 0 {
|
||||
refund := new(uint256.Int).Mul(uint256.NewInt(gasLeft), st.msg.GasPrice)
|
||||
st.state.AddBalance(st.msg.From, refund, tracing.BalanceIncreaseGasReturn)
|
||||
|
|
@ -1086,7 +1088,7 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
|
|||
cost.RegularGas += params.AccountWriteAmsterdam
|
||||
}
|
||||
// Durable state growth of the new account
|
||||
if !st.state.Exist(authority) {
|
||||
if st.state.Empty(authority) {
|
||||
cost.StateGas += params.AccountCreationSize * st.evm.Context.CostPerStateByte
|
||||
}
|
||||
// Writing the 23-byte delegation indicator into a previously empty
|
||||
|
|
@ -1100,11 +1102,7 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
|
|||
// earlier state charge as it is no longer justified.
|
||||
//
|
||||
// Note that the refund and the charges above can never apply to the
|
||||
// same authorization. The refund requires the indicator to have been
|
||||
// created by a preceding authorization in this transaction, in which
|
||||
// case the authority already exists, has already been written, and
|
||||
// its indicator slot was empty at transaction start, so none of the
|
||||
// charges is due. The ordering of the refund and the charge is
|
||||
// same authorization. The ordering of the refund and the charge is
|
||||
// therefore irrelevant.
|
||||
if auth.Address == (common.Address{}) && curDelegated && !preDelegated {
|
||||
st.gasRemaining.RefundState(authBase)
|
||||
|
|
@ -1113,7 +1111,6 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
|
|||
return ErrOutOfGasRuntime
|
||||
}
|
||||
}
|
||||
|
||||
// Update nonce and account code.
|
||||
st.state.SetNonce(authority, auth.Nonce+1, tracing.NonceChangeAuthorization)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
|
||||
"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"
|
||||
)
|
||||
|
|
@ -158,50 +157,50 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
isEIP3860 bool
|
||||
isAmsterdam bool
|
||||
value *uint256.Int
|
||||
want vm.GasCosts
|
||||
want uint64
|
||||
}{
|
||||
{
|
||||
name: "frontier/empty-call",
|
||||
want: vm.GasCosts{RegularGas: params.TxGas},
|
||||
want: params.TxGas,
|
||||
},
|
||||
{
|
||||
name: "frontier/contract-creation-pre-homestead",
|
||||
creation: true,
|
||||
isHomestead: false,
|
||||
// pre-homestead, contract creation still uses TxGas
|
||||
want: vm.GasCosts{RegularGas: params.TxGas},
|
||||
want: params.TxGas,
|
||||
},
|
||||
{
|
||||
name: "homestead/contract-creation",
|
||||
creation: true,
|
||||
isHomestead: true,
|
||||
want: vm.GasCosts{RegularGas: params.TxGasContractCreation},
|
||||
want: params.TxGasContractCreation,
|
||||
},
|
||||
{
|
||||
name: "frontier/non-zero-data",
|
||||
data: bytes.Repeat([]byte{0xff}, 100),
|
||||
// 100 nz bytes * 68 (frontier)
|
||||
want: vm.GasCosts{RegularGas: params.TxGas + 100*params.TxDataNonZeroGasFrontier},
|
||||
want: params.TxGas + 100*params.TxDataNonZeroGasFrontier,
|
||||
},
|
||||
{
|
||||
name: "istanbul/non-zero-data",
|
||||
data: bytes.Repeat([]byte{0xff}, 100),
|
||||
isEIP2028: true,
|
||||
// 100 nz bytes * 16 (post-EIP2028)
|
||||
want: vm.GasCosts{RegularGas: params.TxGas + 100*params.TxDataNonZeroGasEIP2028},
|
||||
want: params.TxGas + 100*params.TxDataNonZeroGasEIP2028,
|
||||
},
|
||||
{
|
||||
name: "istanbul/zero-data",
|
||||
data: bytes.Repeat([]byte{0x00}, 100),
|
||||
isEIP2028: true,
|
||||
// 100 zero bytes * 4
|
||||
want: vm.GasCosts{RegularGas: params.TxGas + 100*params.TxDataZeroGas},
|
||||
want: params.TxGas + 100*params.TxDataZeroGas,
|
||||
},
|
||||
{
|
||||
name: "istanbul/mixed-data",
|
||||
data: append(bytes.Repeat([]byte{0x00}, 50), bytes.Repeat([]byte{0xff}, 50)...),
|
||||
isEIP2028: true,
|
||||
want: vm.GasCosts{RegularGas: params.TxGas + 50*params.TxDataZeroGas + 50*params.TxDataNonZeroGasEIP2028},
|
||||
want: params.TxGas + 50*params.TxDataZeroGas + 50*params.TxDataNonZeroGasEIP2028,
|
||||
},
|
||||
{
|
||||
name: "shanghai/init-code-word-gas",
|
||||
|
|
@ -211,7 +210,7 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
isEIP2028: true,
|
||||
isEIP3860: true,
|
||||
// TxGasContractCreation + 64 zero bytes * 4 + 2 words * 2
|
||||
want: vm.GasCosts{RegularGas: params.TxGasContractCreation + 64*params.TxDataZeroGas + 2*params.InitCodeWordGas},
|
||||
want: params.TxGasContractCreation + 64*params.TxDataZeroGas + 2*params.InitCodeWordGas,
|
||||
},
|
||||
{
|
||||
name: "shanghai/init-code-non-multiple-of-32",
|
||||
|
|
@ -220,7 +219,7 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
isHomestead: true,
|
||||
isEIP2028: true,
|
||||
isEIP3860: true,
|
||||
want: vm.GasCosts{RegularGas: params.TxGasContractCreation + 33*params.TxDataZeroGas + 2*params.InitCodeWordGas},
|
||||
want: params.TxGasContractCreation + 33*params.TxDataZeroGas + 2*params.InitCodeWordGas,
|
||||
},
|
||||
{
|
||||
name: "berlin/access-list",
|
||||
|
|
@ -230,7 +229,7 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
},
|
||||
isEIP2028: true,
|
||||
// 2 addrs * 2400 + 3 keys * 1900
|
||||
want: vm.GasCosts{RegularGas: params.TxGas + 2*params.TxAccessListAddressGas + 3*params.TxAccessListStorageKeyGas},
|
||||
want: params.TxGas + 2*params.TxAccessListAddressGas + 3*params.TxAccessListStorageKeyGas,
|
||||
},
|
||||
{
|
||||
name: "amsterdam/access-list-extra-cost",
|
||||
|
|
@ -244,9 +243,9 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
// (15,000); the recipient touch is charged at the cold rate
|
||||
// unconditionally at the intrinsic phase. Plus base access-list
|
||||
// charge + EIP-7981 extra.
|
||||
want: vm.GasCosts{RegularGas: params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam +
|
||||
want: params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam +
|
||||
2*params.TxAccessListAddressGasAmsterdam + 3*params.TxAccessListStorageKeyGasAmsterdam +
|
||||
2*amsterdamAddressCost + 3*amsterdamStorageKeyCost},
|
||||
2*amsterdamAddressCost + 3*amsterdamStorageKeyCost,
|
||||
},
|
||||
{
|
||||
name: "prague/auth-list",
|
||||
|
|
@ -257,7 +256,7 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
},
|
||||
isEIP2028: true,
|
||||
// 3 auths * 25000 (pre-Amsterdam: CallNewAccountGas per auth tuple)
|
||||
want: vm.GasCosts{RegularGas: params.TxGas + 3*params.CallNewAccountGas},
|
||||
want: params.TxGas + 3*params.CallNewAccountGas,
|
||||
},
|
||||
{
|
||||
name: "amsterdam/contract-creation-empty",
|
||||
|
|
@ -267,9 +266,7 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
isAmsterdam: true,
|
||||
// EIP-2780: creation regular gas is TxBaseCost + CreateAccess (23,000);
|
||||
// the new-account state charge is applied at runtime.
|
||||
want: vm.GasCosts{
|
||||
RegularGas: params.TxBaseCost2780 + params.CreateAccessAmsterdam,
|
||||
},
|
||||
want: params.TxBaseCost2780 + params.CreateAccessAmsterdam,
|
||||
},
|
||||
{
|
||||
name: "amsterdam/contract-creation-init-code",
|
||||
|
|
@ -279,11 +276,9 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
isEIP2028: true,
|
||||
isEIP3860: true, // Shanghai gates init-code word gas
|
||||
isAmsterdam: true,
|
||||
want: vm.GasCosts{
|
||||
RegularGas: params.TxBaseCost2780 + params.CreateAccessAmsterdam +
|
||||
want: params.TxBaseCost2780 + params.CreateAccessAmsterdam +
|
||||
64*params.TxDataZeroGas + 2*params.InitCodeWordGas,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "amsterdam/contract-creation-with-access-list",
|
||||
data: bytes.Repeat([]byte{0xff}, 32), // 1 word of non-zero init code
|
||||
|
|
@ -295,13 +290,11 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
isEIP2028: true,
|
||||
isEIP3860: true,
|
||||
isAmsterdam: true,
|
||||
want: vm.GasCosts{
|
||||
RegularGas: params.TxBaseCost2780 + params.CreateAccessAmsterdam +
|
||||
want: params.TxBaseCost2780 + params.CreateAccessAmsterdam +
|
||||
32*params.TxDataNonZeroGasEIP2028 + 1*params.InitCodeWordGas +
|
||||
1*params.TxAccessListAddressGasAmsterdam + 1*params.TxAccessListStorageKeyGasAmsterdam +
|
||||
1*amsterdamAddressCost + 1*amsterdamStorageKeyCost,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "amsterdam/combined",
|
||||
data: bytes.Repeat([]byte{0xff}, 100),
|
||||
|
|
@ -317,22 +310,20 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
// access (priced into RegularPerAuthBaseCost) are both charged at the
|
||||
// cold rate unconditionally at the intrinsic phase; the account leaf
|
||||
// and indicator bytes are charged at runtime.
|
||||
want: vm.GasCosts{
|
||||
RegularGas: params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam +
|
||||
want: params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam +
|
||||
100*params.TxDataNonZeroGasEIP2028 +
|
||||
1*params.TxAccessListAddressGasAmsterdam + 1*params.TxAccessListStorageKeyGasAmsterdam +
|
||||
1*amsterdamAddressCost + 1*amsterdamStorageKeyCost +
|
||||
1*params.RegularPerAuthBaseCost,
|
||||
},
|
||||
},
|
||||
{
|
||||
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.ColdAccountAccessAmsterdam +
|
||||
params.TransferLogCost2780 + params.TxValueCost2780},
|
||||
want: params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam +
|
||||
params.TransferLogCost2780 + params.TxValueCost2780,
|
||||
},
|
||||
{
|
||||
name: "amsterdam/value-bearing-contract-creation",
|
||||
|
|
@ -343,9 +334,7 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
value: uint256.NewInt(1),
|
||||
// EIP-2780: TxBaseCost + CreateAccess + TransferLogCost = 24,756;
|
||||
// the new-account state charge is applied at runtime.
|
||||
want: vm.GasCosts{
|
||||
RegularGas: params.TxBaseCost2780 + params.CreateAccessAmsterdam + params.TransferLogCost2780,
|
||||
},
|
||||
want: params.TxBaseCost2780 + params.CreateAccessAmsterdam + params.TransferLogCost2780,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
|
@ -361,7 +350,7 @@ func TestIntrinsicGas(t *testing.T) {
|
|||
to = &addr1
|
||||
}
|
||||
got, err := IntrinsicGas(tt.data, tt.accessList, tt.authList,
|
||||
common.Address{}, to, tt.value, rules, params.CostPerStateByte)
|
||||
common.Address{}, to, tt.value, rules)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,21 +30,22 @@ func _() {
|
|||
_ = x[GasChangeTxDataFloor-19]
|
||||
_ = x[GasChangeRefundAccountCreation-20]
|
||||
_ = x[GasChangeTxRuntimeGas-21]
|
||||
_ = x[GasChangeAccountCreation-22]
|
||||
_ = x[GasChangeIgnored-255]
|
||||
}
|
||||
|
||||
const (
|
||||
_GasChangeReason_name_0 = "UnspecifiedTxInitialBalanceTxIntrinsicGasTxRefundsTxLeftOverReturnedCallInitialBalanceCallLeftOverReturnedCallLeftOverRefundedCallContractCreationCallContractCreation2CallCodeStorageCallOpCodeCallPrecompiledContractCallStorageColdAccessCallFailedExecutionWitnessContractInitWitnessContractCreationWitnessCodeChunkWitnessContractCollisionCheckTxDataFloorRefundAccountCreationTxRuntimeGas"
|
||||
_GasChangeReason_name_0 = "UnspecifiedTxInitialBalanceTxIntrinsicGasTxRefundsTxLeftOverReturnedCallInitialBalanceCallLeftOverReturnedCallLeftOverRefundedCallContractCreationCallContractCreation2CallCodeStorageCallOpCodeCallPrecompiledContractCallStorageColdAccessCallFailedExecutionWitnessContractInitWitnessContractCreationWitnessCodeChunkWitnessContractCollisionCheckTxDataFloorRefundAccountCreationTxRuntimeGasAccountCreation"
|
||||
_GasChangeReason_name_1 = "Ignored"
|
||||
)
|
||||
|
||||
var (
|
||||
_GasChangeReason_index_0 = [...]uint16{0, 11, 27, 41, 50, 68, 86, 106, 126, 146, 167, 182, 192, 215, 236, 255, 274, 297, 313, 342, 353, 374, 386}
|
||||
_GasChangeReason_index_0 = [...]uint16{0, 11, 27, 41, 50, 68, 86, 106, 126, 146, 167, 182, 192, 215, 236, 255, 274, 297, 313, 342, 353, 374, 386, 401}
|
||||
)
|
||||
|
||||
func (i GasChangeReason) String() string {
|
||||
switch {
|
||||
case i <= 21:
|
||||
case i <= 22:
|
||||
return _GasChangeReason_name_0[_GasChangeReason_index_0[i]:_GasChangeReason_index_0[i+1]]
|
||||
case i == 255:
|
||||
return _GasChangeReason_name_1
|
||||
|
|
|
|||
|
|
@ -480,6 +480,11 @@ const (
|
|||
// costs of the transaction per EIP-2780.
|
||||
GasChangeTxRuntimeGas GasChangeReason = 21
|
||||
|
||||
// GasChangeAccountCreation represents the conditional account-creation
|
||||
// state cost charged in the creating frame when a CREATE/CREATE2 is about
|
||||
// to create a new account (EIP-8037).
|
||||
GasChangeAccountCreation GasChangeReason = 22
|
||||
|
||||
// GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as
|
||||
// it will be "manually" tracked by a direct emit of the gas change event.
|
||||
GasChangeIgnored GasChangeReason = 0xFF
|
||||
|
|
|
|||
|
|
@ -132,12 +132,12 @@ 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(), from, tx.To(), value, rules, params.CostPerStateByte)
|
||||
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), from, tx.To(), value, rules)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tx.Gas() < intrGas.RegularGas {
|
||||
return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrIntrinsicGas, tx.Gas(), intrGas.RegularGas)
|
||||
if tx.Gas() < intrGas {
|
||||
return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrIntrinsicGas, tx.Gas(), intrGas)
|
||||
}
|
||||
// Ensure the transaction can cover floor data gas.
|
||||
if rules.IsPrague {
|
||||
|
|
@ -152,8 +152,8 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
|||
}
|
||||
// In Amsterdam, the transaction gas limit is allowed to exceed
|
||||
// params.MaxTxGas, but the calldata floor cost is capped by it.
|
||||
if rules.IsAmsterdam && max(intrGas.RegularGas, floorDataGas) > params.MaxTxGas {
|
||||
return fmt.Errorf("%w: regular intrisic cost %v, floor: %v", core.ErrFloorDataGas, intrGas.RegularGas, floorDataGas)
|
||||
if rules.IsAmsterdam && max(intrGas, floorDataGas) > params.MaxTxGas {
|
||||
return fmt.Errorf("%w: intrinsic cost %v, floor: %v", core.ErrFloorDataGas, intrGas, floorDataGas)
|
||||
}
|
||||
}
|
||||
// Ensure the gasprice is high enough to cover the requirement of the calling pool
|
||||
|
|
|
|||
|
|
@ -473,20 +473,58 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
|||
return ret, exitGas, err
|
||||
}
|
||||
|
||||
// create creates a new contract using code as deployment code.
|
||||
func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, result GasBudget, creation bool, err error) {
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
// createFramePreCheck the precondition before executing the contract deployment,
|
||||
// halts the create frame if fails with any check below.
|
||||
func (evm *EVM) createFramePreCheck(caller common.Address, value *uint256.Int) error {
|
||||
var nonce uint64
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
err = ErrDepth
|
||||
} else if !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
||||
err = ErrInsufficientBalance
|
||||
} else {
|
||||
return ErrDepth
|
||||
}
|
||||
if !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
||||
return ErrInsufficientBalance
|
||||
}
|
||||
nonce = evm.StateDB.GetNonce(caller)
|
||||
if nonce+1 < nonce {
|
||||
err = ErrNonceUintOverflow
|
||||
return ErrNonceUintOverflow
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// chargeAccountCreation runs the create-frame precheck and charges the
|
||||
// account-creation state gas since Amsterdam, before the 63/64ths split.
|
||||
//
|
||||
// The charge only applies if the destination is empty, skipping pre-funded
|
||||
// deployment destinations. Note, a destination colliding on storage alone
|
||||
// (zero nonce, zero balance, empty code) is still empty and is charged.
|
||||
//
|
||||
// If halt is true, the caller must terminate with the returned error:
|
||||
// - a failed precheck halts the create frame only and parent frame continues,
|
||||
// - an insufficient charge halts the parent frame with ErrOutOfGas.
|
||||
func (evm *EVM) chargeAccountCreation(scope *ScopeContext, contractAddr common.Address, value *uint256.Int) (charged, halt bool, err error) {
|
||||
if !evm.chainRules.IsAmsterdam {
|
||||
return false, false, nil
|
||||
}
|
||||
if err := evm.createFramePreCheck(scope.Contract.Address(), value); err != nil {
|
||||
scope.Stack.get().Clear()
|
||||
evm.returnData = nil
|
||||
return false, true, nil
|
||||
}
|
||||
if !evm.StateDB.Empty(contractAddr) {
|
||||
return false, false, nil
|
||||
}
|
||||
cost := params.AccountCreationSize * evm.Context.CostPerStateByte
|
||||
if !scope.Contract.chargeState(cost, evm.Config.Tracer, tracing.GasChangeAccountCreation) {
|
||||
return false, true, ErrOutOfGas
|
||||
}
|
||||
return true, false, nil
|
||||
}
|
||||
|
||||
// create creates a new contract using code as deployment code.
|
||||
func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, result GasBudget, creation bool, err error) {
|
||||
// Since Amsterdam, the precheck has been folded into the parent frame
|
||||
// due to account-creation determination, so skip the duplicate check here.
|
||||
if !evm.chainRules.IsAmsterdam {
|
||||
evm.createFramePreCheck(caller, value)
|
||||
}
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.captureBegin(evm.depth, typ, caller, address, code, gas, value.ToBig())
|
||||
|
|
@ -498,7 +536,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
|||
return nil, common.Address{}, gas, false, err
|
||||
}
|
||||
// Increment the caller's nonce after passing all validations
|
||||
evm.StateDB.SetNonce(caller, nonce+1, tracing.NonceChangeContractCreator)
|
||||
evm.StateDB.SetNonce(caller, evm.StateDB.GetNonce(caller)+1, tracing.NonceChangeContractCreator)
|
||||
|
||||
// Charge the contract creation init gas in verkle mode
|
||||
if evm.chainRules.IsEIP4762 {
|
||||
|
|
@ -540,8 +578,11 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
|||
snapshot := evm.StateDB.Snapshot()
|
||||
if !evm.StateDB.Exist(address) {
|
||||
evm.StateDB.CreateAccount(address)
|
||||
creation = true
|
||||
}
|
||||
// Explicitly check the creation with EIP-161-emptiness, for pre-funded
|
||||
// destination (non-zero balance) the creation is skipped.
|
||||
creation = evm.StateDB.Empty(address)
|
||||
|
||||
// CreateContract means that regardless of whether the account previously existed
|
||||
// in the state trie or not, it _now_ becomes created as a _contract_ account.
|
||||
// This is performed _prior_ to executing the initcode, since the initcode
|
||||
|
|
|
|||
|
|
@ -560,12 +560,10 @@ func gasCreateEip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||
words := (size + 31) / 32
|
||||
wordGas := params.InitCodeWordGas * words
|
||||
|
||||
// Unconditionally pre-charge the account creation and refunds if the creation
|
||||
// doesn't happen after the create-frame.
|
||||
return GasCosts{
|
||||
RegularGas: gas + wordGas,
|
||||
StateGas: params.AccountCreationSize * evm.Context.CostPerStateByte,
|
||||
}, nil
|
||||
// The account-creation state gas is not part of the opcode cost: it is
|
||||
// charged conditionally at the destination access, in the creating frame,
|
||||
// right before the 63/64ths split (see opCreate).
|
||||
return GasCosts{RegularGas: gas + wordGas}, nil
|
||||
}
|
||||
|
||||
func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
|
|
@ -590,12 +588,10 @@ func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
|||
// (for address hashing).
|
||||
wordGas := (params.InitCodeWordGas + params.Keccak256WordGas) * words
|
||||
|
||||
// Unconditionally pre-charge the account creation and refunds if the creation
|
||||
// doesn't happen after the create-frame.
|
||||
return GasCosts{
|
||||
RegularGas: gas + wordGas,
|
||||
StateGas: params.AccountCreationSize * evm.Context.CostPerStateByte,
|
||||
}, nil
|
||||
// The account-creation state gas is not part of the opcode cost: it is
|
||||
// charged conditionally at the destination access, in the creating frame,
|
||||
// right before the 63/64ths split (see opCreate2).
|
||||
return GasCosts{RegularGas: gas + wordGas}, nil
|
||||
}
|
||||
|
||||
// regularGasCall8038 is the intrinsic regular-gas calculator for CALL in
|
||||
|
|
@ -635,9 +631,6 @@ func stateGasCall8037(evm *EVM, contract *Contract, stack *Stack) (uint64, error
|
|||
transfersValue = !stack.back(2).IsZero()
|
||||
address = common.Address(stack.back(1).Bytes20())
|
||||
)
|
||||
// TODO(rjl, marius), can EIP8037 implicitly means the EIP158 is also activated?
|
||||
// It's technically possible to skip the EIP158 but very unlikely in practice.
|
||||
if evm.chainRules.IsEIP158 {
|
||||
// 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
|
||||
// nonce, balance, and code are all zero. Such accounts can appear temporarily
|
||||
|
|
@ -647,9 +640,6 @@ func stateGasCall8037(evm *EVM, contract *Contract, stack *Stack) (uint64, error
|
|||
if transfersValue && evm.StateDB.Empty(address) {
|
||||
gas += params.AccountCreationSize * evm.Context.CostPerStateByte
|
||||
}
|
||||
} else if !evm.StateDB.Exist(address) {
|
||||
gas += params.AccountCreationSize * evm.Context.CostPerStateByte
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,18 +48,6 @@ func (g GasCosts) String() string {
|
|||
// - UsedRegularGas / UsedStateGas: per-frame accumulators tracking gross
|
||||
// consumption. UsedStateGas is signed so it can be decremented by inline
|
||||
// state-gas refunds (e.g., SSTORE 0->A->0).
|
||||
//
|
||||
// The same struct serves three roles:
|
||||
//
|
||||
// - During execution: Charge / ChargeRegular / ChargeState / RefundState
|
||||
// and RefundRegular mutate the running balance and the usage accumulators
|
||||
// in lockstep.
|
||||
//
|
||||
// - At frame exit: ExitSuccess / ExitRevert / ExitHalt produce a new
|
||||
// GasBudget in "leftover" form that packages the result for the caller.
|
||||
//
|
||||
// - At absorption: the caller's Absorb method merges the child's leftover
|
||||
// budget into its own running budget.
|
||||
type GasBudget struct {
|
||||
RegularGas uint64 // remaining regular-gas balance (or leftover for caller to absorb)
|
||||
StateGas uint64 // remaining state-gas reservoir (or leftover for caller to absorb)
|
||||
|
|
@ -78,9 +66,7 @@ func NewGasBudget(regular, state uint64) GasBudget {
|
|||
return GasBudget{RegularGas: regular, StateGas: state}
|
||||
}
|
||||
|
||||
// Used returns the total scalar gas consumed relative to an initial budget
|
||||
// (= (initial.regular + initial.state) − (current.regular + current.state)).
|
||||
// This is the payment scalar (EIP-8037's tx_gas_used_before_refund).
|
||||
// Used returns the total scalar gas consumed relative to an initial budget.
|
||||
func (g GasBudget) Used(initial GasBudget) uint64 {
|
||||
return (initial.RegularGas + initial.StateGas) - (g.RegularGas + g.StateGas)
|
||||
}
|
||||
|
|
@ -91,16 +77,16 @@ func (g GasBudget) String() string {
|
|||
}
|
||||
|
||||
// Charge deducts a combined regular+state cost from the running balance and
|
||||
// updates the usage accumulators. State-gas in excess of the reservoir spills
|
||||
// into regular_gas.
|
||||
// updates the usage accumulators.
|
||||
func (g *GasBudget) Charge(cost GasCosts) (GasBudget, bool) {
|
||||
prior := *g
|
||||
ok := g.charge(cost)
|
||||
return prior, ok
|
||||
}
|
||||
|
||||
// chargeRegularOnly deducts a regular-only cost.
|
||||
func (g *GasBudget) chargeRegularOnly(r uint64) bool {
|
||||
// ChargeRegularOnly deducts a regular-only cost. It's always preferred for
|
||||
// performance consideration if the opcode doesn't have any state cost.
|
||||
func (g *GasBudget) ChargeRegularOnly(r uint64) bool {
|
||||
if g.RegularGas < r {
|
||||
return false
|
||||
}
|
||||
|
|
@ -110,9 +96,7 @@ func (g *GasBudget) chargeRegularOnly(r uint64) bool {
|
|||
}
|
||||
|
||||
// CanAfford reports whether the running budget can cover the given cost vector
|
||||
// without going out of gas. The regular cost must fit in the regular balance,
|
||||
// and any state gas in excess of the reservoir must be coverable by the
|
||||
// remaining regular gas (the spillover), mirroring charge without mutating.
|
||||
// without going out of gas.
|
||||
func (g GasBudget) CanAfford(cost GasCosts) bool {
|
||||
if g.RegularGas < cost.RegularGas {
|
||||
return false
|
||||
|
|
@ -162,8 +146,7 @@ func (g *GasBudget) ChargeRegular(r uint64) (GasBudget, bool) {
|
|||
return g.Charge(GasCosts{RegularGas: r})
|
||||
}
|
||||
|
||||
// ChargeState is a convenience that deducts a state-only cost (spills to
|
||||
// regular when the reservoir is exhausted). Returns false on OOG.
|
||||
// ChargeState is a convenience that deducts a state-only cost.
|
||||
func (g *GasBudget) ChargeState(s uint64) (GasBudget, bool) {
|
||||
return g.Charge(GasCosts{StateGas: s})
|
||||
}
|
||||
|
|
@ -174,24 +157,10 @@ func (g *GasBudget) IsZero() bool {
|
|||
}
|
||||
|
||||
// RefundState applies an inline state-gas refund (e.g., SSTORE 0->A->0).
|
||||
//
|
||||
// Per EIP-8037, the refund repays the regular gas previously borrowed for
|
||||
// state-gas spillover (tracked by Spilled) before crediting the
|
||||
// reservoir: it is returned to RegularGas up to the outstanding borrowed
|
||||
// amount, and only the remainder tops up StateGas.
|
||||
//
|
||||
// The signed usage counter is decremented by the full refund regardless of the
|
||||
// split, preserving the per-frame invariant:
|
||||
//
|
||||
// StateGas + UsedStateGas == initialStateGas + Spilled
|
||||
//
|
||||
// which the revert and halt paths rely on for the correct gross refund.
|
||||
func (g *GasBudget) RefundState(s uint64) {
|
||||
repay := min(s, g.Spilled)
|
||||
g.RegularGas += repay
|
||||
g.Spilled -= repay
|
||||
|
||||
// Whatever is left tops up the reservoir.
|
||||
g.StateGas += s - repay
|
||||
g.UsedStateGas -= int64(s)
|
||||
}
|
||||
|
|
@ -206,14 +175,6 @@ func (g *GasBudget) DrainRegular() {
|
|||
// the parent's running budget and returns the initial GasBudget for a child
|
||||
// frame. The parent's UsedRegularGas is bumped by the forwarded amount so
|
||||
// that the absorb-on-return path correctly reclaims the unused portion.
|
||||
//
|
||||
// Used by frame boundaries where the regular forward has NOT been pre-
|
||||
// deducted: tx-level dispatch (state_transition) and CREATE / CREATE2. The
|
||||
// CALL family pre-deducts the forward via the dynamic gas table for tracer-
|
||||
// reporting reasons and therefore constructs its child budget directly.
|
||||
//
|
||||
// Caller must ensure `regular` does not exceed the running balance and
|
||||
// apply any EIP-150 1/64 retention before calling Forward.
|
||||
func (g *GasBudget) Forward(regular uint64) GasBudget {
|
||||
g.RegularGas -= regular
|
||||
g.UsedRegularGas += regular
|
||||
|
|
@ -239,19 +200,15 @@ func (g *GasBudget) ForwardAll() GasBudget {
|
|||
// absorb to update its own state.
|
||||
// ============================================================================
|
||||
|
||||
// ExitSuccess produces the leftover form for a successful frame. Inline
|
||||
// state-gas refunds have already been folded into StateGas / UsedStateGas
|
||||
// during execution; the running budget IS the exit budget on success.
|
||||
// ExitSuccess produces the leftover form for a successful frame.
|
||||
func (g GasBudget) ExitSuccess() GasBudget {
|
||||
return g
|
||||
}
|
||||
|
||||
// ExitRevert produces the leftover for a REVERT exit. The frame's state
|
||||
// changes are discarded, so all state gas it charged is refilled to its origin
|
||||
// (EIP-8037): up to Spilled is returned to RegularGas (the regular
|
||||
// gas it borrowed), and the remainder restores the reservoir. Because the
|
||||
// borrowed regular gas is repaid first, the reservoir is made whole back to its
|
||||
// start-of-frame value.
|
||||
// changes are discarded, so all state gas it charged is refilled with LIFO
|
||||
// mechanism: up to Spilled is returned to RegularGas (the regular gas it
|
||||
// borrowed), and the remainder restores the reservoir.
|
||||
func (g GasBudget) ExitRevert() GasBudget {
|
||||
reservoir := int64(g.StateGas) + g.UsedStateGas - int64(g.Spilled)
|
||||
if reservoir < 0 {
|
||||
|
|
@ -270,10 +227,10 @@ func (g GasBudget) ExitRevert() GasBudget {
|
|||
}
|
||||
|
||||
// ExitHalt produces the leftover for an exceptional halt. As with a revert, the
|
||||
// frame's state changes are rolled back and its state gas is refilled to origin
|
||||
// (EIP-8037); the difference is that the frame's gas_left is consumed rather
|
||||
// frame's state changes are rolled back and its state gas is refilled with LIFO
|
||||
// mechanism. The difference is that the frame's regular gas is consumed rather
|
||||
// than returned. The portion refilled to RegularGas is therefore burned along
|
||||
// with the rest of gas_left, leaving only the reservoir portion to survive,
|
||||
// with the rest of regular gas, leaving only the reservoir portion to survive,
|
||||
// which equals the reservoir's value at the start of the frame.
|
||||
func (g GasBudget) ExitHalt() GasBudget {
|
||||
reservoir := int64(g.StateGas) + g.UsedStateGas - int64(g.Spilled)
|
||||
|
|
|
|||
|
|
@ -635,7 +635,12 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
value = scope.Stack.pop()
|
||||
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
||||
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||
contractAddr = crypto.CreateAddress(scope.Contract.Address(), evm.StateDB.GetNonce(scope.Contract.Address()))
|
||||
)
|
||||
creationCharged, halt, err := evm.chargeAccountCreation(scope, contractAddr, &value)
|
||||
if halt {
|
||||
return nil, err
|
||||
}
|
||||
// Apply EIP-150 to the regular gas left after the state charge.
|
||||
forward := scope.Contract.Gas.RegularGas
|
||||
if evm.chainRules.IsEIP150 {
|
||||
|
|
@ -646,7 +651,8 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
stackvalue := size
|
||||
|
||||
child := scope.Contract.forwardGas(forward, evm.Config.Tracer, tracing.GasChangeCallContractCreation)
|
||||
res, addr, result, creation, suberr := evm.Create(scope.Contract.Address(), input, child, &value)
|
||||
res, addr, result, _, suberr := evm.create(scope.Contract.Address(), input, child, &value, contractAddr, CREATE)
|
||||
|
||||
// Push item on the stack based on the returned error. If the ruleset is
|
||||
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
||||
// rule) and treat as an error, if the ruleset is frontier we must
|
||||
|
|
@ -663,8 +669,9 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// Refund the leftover gas back to current frame
|
||||
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
// Refund the state gas of account-creation if creation doesn't happen
|
||||
if evm.GetRules().IsAmsterdam && !creation {
|
||||
// Refill the account-creation charge if the create frame failed (reverted,
|
||||
// halted exceptionally, or collided); a successful creation consumes it.
|
||||
if creationCharged && suberr != nil {
|
||||
scope.Contract.refundState(params.AccountCreationSize*evm.Context.CostPerStateByte, evm.Config.Tracer, tracing.GasChangeRefundAccountCreation)
|
||||
}
|
||||
if suberr == ErrExecutionReverted {
|
||||
|
|
@ -681,7 +688,13 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
||||
salt = scope.Stack.pop()
|
||||
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||
inithash = crypto.Keccak256Hash(input)
|
||||
contractAddr = crypto.CreateAddress2(scope.Contract.Address(), salt.Bytes32(), inithash[:])
|
||||
)
|
||||
creationCharged, halt, err := evm.chargeAccountCreation(scope, contractAddr, &endowment)
|
||||
if halt {
|
||||
return nil, err
|
||||
}
|
||||
// Apply EIP-150 to the regular gas left after the state charge.
|
||||
forward := scope.Contract.Gas.RegularGas
|
||||
forward -= forward / 64
|
||||
|
|
@ -689,7 +702,7 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// reuse size int for stackvalue
|
||||
stackvalue := size
|
||||
child := scope.Contract.forwardGas(forward, evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
|
||||
res, addr, result, creation, suberr := evm.Create2(scope.Contract.Address(), input, child, &endowment, &salt)
|
||||
res, addr, result, _, suberr := evm.create(scope.Contract.Address(), input, child, &endowment, contractAddr, CREATE2)
|
||||
// Push item on the stack based on the returned error.
|
||||
if suberr != nil {
|
||||
stackvalue.Clear()
|
||||
|
|
@ -701,8 +714,9 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// Refund the leftover gas back to current frame
|
||||
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
// Refund the state gas of account-creation if creation doesn't happen
|
||||
if evm.GetRules().IsAmsterdam && !creation {
|
||||
// Refill the account-creation charge if the create frame failed (reverted,
|
||||
// halted exceptionally, or collided); a successful creation consumes it.
|
||||
if creationCharged && suberr != nil {
|
||||
scope.Contract.refundState(params.AccountCreationSize*evm.Context.CostPerStateByte, evm.Config.Tracer, tracing.GasChangeRefundAccountCreation)
|
||||
}
|
||||
if suberr == ErrExecutionReverted {
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
|||
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
|
||||
}
|
||||
// for tracing: this gas consumption event is emitted below in the debug section.
|
||||
if !contract.Gas.chargeRegularOnly(cost) {
|
||||
if !contract.Gas.ChargeRegularOnly(cost) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
||||
|
|
@ -223,7 +223,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
|||
return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err)
|
||||
}
|
||||
if dynamicCost.StateGas == 0 {
|
||||
if !contract.Gas.chargeRegularOnly(dynamicCost.RegularGas) {
|
||||
if !contract.Gas.ChargeRegularOnly(dynamicCost.RegularGas) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
} else if !contract.Gas.charge(dynamicCost) {
|
||||
|
|
|
|||
|
|
@ -101,14 +101,12 @@ const (
|
|||
// defined in EIP-8037 as the sum of:
|
||||
//
|
||||
// - Calldata cost for the authorization tuple
|
||||
// - ECDSA recovery of the authority address (per EIP-7904)
|
||||
// - ECDSA recovery of the authority address
|
||||
// - Cold authority access (COLD_ACCOUNT_ACCESS)
|
||||
// - Warm writes to the authority account
|
||||
RegularPerAuthBaseCost uint64 = 7816
|
||||
|
||||
// EIP-2780: resource-based intrinsic transaction gas. The access primitives
|
||||
// it references (COLD_ACCOUNT_ACCESS, WARM_ACCESS, CREATE_ACCESS) are the
|
||||
// EIP-8038 parameters defined below.
|
||||
// EIP-2780: resource-based intrinsic transaction gas.
|
||||
TxBaseCost2780 uint64 = 12000
|
||||
TxValueCost2780 uint64 = 4244
|
||||
TransferLogCost2780 uint64 = 1756
|
||||
|
|
|
|||
|
|
@ -86,11 +86,11 @@ func (tt *TransactionTest) Run() error {
|
|||
if overflow {
|
||||
return sender, hash, 0, errors.New("value exceeds 256 bits")
|
||||
}
|
||||
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), sender, tx.To(), value, rules, params.CostPerStateByte)
|
||||
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), sender, tx.To(), value, rules)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
requiredGas = cost.RegularGas
|
||||
requiredGas = cost
|
||||
if requiredGas > tx.Gas() {
|
||||
return sender, hash, 0, fmt.Errorf("insufficient gas ( %d < %d )", tx.Gas(), requiredGas)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue