mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-03-25 04:18:08 +00:00
core: implement EIP-8037: state creation gas cost increase
This commit is contained in:
parent
b87340a856
commit
da0d7b575a
31 changed files with 1160 additions and 481 deletions
|
|
@ -133,15 +133,16 @@ func Transaction(ctx *cli.Context) error {
|
|||
}
|
||||
// Check intrinsic gas
|
||||
rules := chainConfig.Rules(common.Big0, true, 0)
|
||||
gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
|
||||
gasCostPerStateByte := core.CostPerStateByte(&types.Header{}, chainConfig)
|
||||
gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, gasCostPerStateByte)
|
||||
if err != nil {
|
||||
r.Error = err
|
||||
results = append(results, r)
|
||||
continue
|
||||
}
|
||||
r.IntrinsicGas = gas
|
||||
if tx.Gas() < gas {
|
||||
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), gas)
|
||||
r.IntrinsicGas = gas.RegularGas
|
||||
if tx.Gas() < gas.RegularGas {
|
||||
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), gas.RegularGas)
|
||||
results = append(results, r)
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,8 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
|||
data := make([]byte, nbytes)
|
||||
return func(i int, gen *BlockGen) {
|
||||
toaddr := common.Address{}
|
||||
gas, _ := IntrinsicGas(data, nil, nil, false, false, false, false)
|
||||
gasCostPerStateByte := CostPerStateByte(gen.header, gen.cm.config)
|
||||
gas, _ := IntrinsicGas(data, nil, nil, false, params.Rules{}, gasCostPerStateByte)
|
||||
signer := gen.Signer()
|
||||
gasPrice := big.NewInt(0)
|
||||
if gen.header.BaseFee != nil {
|
||||
|
|
@ -99,7 +100,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
|||
Nonce: gen.TxNonce(benchRootAddr),
|
||||
To: &toaddr,
|
||||
Value: big.NewInt(1),
|
||||
Gas: gas,
|
||||
Gas: gas.RegularGas,
|
||||
Data: data,
|
||||
GasPrice: gasPrice,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -63,12 +63,12 @@ var (
|
|||
func TestProcessVerkle(t *testing.T) {
|
||||
var (
|
||||
code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`)
|
||||
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, true, true, true, true)
|
||||
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, true, 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, true, true, true)
|
||||
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, true, params.Rules{IsHomestead: true, IsIstanbul: true, IsShanghai: true}, 0)
|
||||
signer = types.LatestSigner(testVerkleChainConfig)
|
||||
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain
|
||||
|
|
@ -97,11 +97,11 @@ func TestProcessVerkle(t *testing.T) {
|
|||
|
||||
txCost1 := params.TxGas
|
||||
txCost2 := params.TxGas
|
||||
contractCreationCost := intrinsicContractCreationGas +
|
||||
contractCreationCost := intrinsicContractCreationGas.RegularGas +
|
||||
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */
|
||||
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */
|
||||
739 /* execution costs */
|
||||
codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas +
|
||||
codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas.RegularGas +
|
||||
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 */
|
||||
|
|
@ -199,7 +199,7 @@ func TestProcessParentBlockHash(t *testing.T) {
|
|||
if isVerkle {
|
||||
chainConfig = testVerkleChainConfig
|
||||
}
|
||||
vmContext := NewEVMBlockContext(header, nil, new(common.Address))
|
||||
vmContext := NewEVMBlockContext(header, &BlockChain{chainConfig: chainConfig}, new(common.Address))
|
||||
evm := vm.NewEVM(vmContext, statedb, chainConfig, vm.Config{})
|
||||
ProcessParentBlockHash(header.ParentHash, evm)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3865,7 +3865,8 @@ func TestTransientStorageReset(t *testing.T) {
|
|||
Data: initCode,
|
||||
})
|
||||
nonce++
|
||||
b.AddTxWithVMConfig(tx, vmConfig)
|
||||
bc := &BlockChain{chainConfig: gspec.Config}
|
||||
b.AddTxWithVMConfig(bc, tx, vmConfig)
|
||||
|
||||
tx, _ = types.SignNewTx(key, signer, &types.LegacyTx{
|
||||
Nonce: nonce,
|
||||
|
|
@ -3873,7 +3874,7 @@ func TestTransientStorageReset(t *testing.T) {
|
|||
Gas: 100000,
|
||||
To: &destAddress,
|
||||
})
|
||||
b.AddTxWithVMConfig(tx, vmConfig)
|
||||
b.AddTxWithVMConfig(bc, tx, vmConfig)
|
||||
nonce++
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -164,8 +164,8 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
|
|||
// AddTxWithVMConfig adds a transaction to the generated block. If no coinbase has
|
||||
// been set, the block's coinbase is set to the zero address.
|
||||
// The evm interpreter can be customized with the provided vm config.
|
||||
func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) {
|
||||
b.addTx(nil, config, tx)
|
||||
func (b *BlockGen) AddTxWithVMConfig(bc *BlockChain, tx *types.Transaction, config vm.Config) {
|
||||
b.addTx(bc, config, tx)
|
||||
}
|
||||
|
||||
// GetBalance returns the balance of the given address at the generated block.
|
||||
|
|
|
|||
36
core/evm.go
36
core/evm.go
|
|
@ -68,21 +68,33 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
|
|||
}
|
||||
|
||||
return vm.BlockContext{
|
||||
CanTransfer: CanTransfer,
|
||||
Transfer: Transfer,
|
||||
GetHash: GetHashFn(header, chain),
|
||||
Coinbase: beneficiary,
|
||||
BlockNumber: new(big.Int).Set(header.Number),
|
||||
Time: header.Time,
|
||||
Difficulty: new(big.Int).Set(header.Difficulty),
|
||||
BaseFee: baseFee,
|
||||
BlobBaseFee: blobBaseFee,
|
||||
GasLimit: header.GasLimit,
|
||||
Random: random,
|
||||
SlotNum: slotNum,
|
||||
CanTransfer: CanTransfer,
|
||||
Transfer: Transfer,
|
||||
GetHash: GetHashFn(header, chain),
|
||||
Coinbase: beneficiary,
|
||||
BlockNumber: new(big.Int).Set(header.Number),
|
||||
Time: header.Time,
|
||||
Difficulty: new(big.Int).Set(header.Difficulty),
|
||||
BaseFee: baseFee,
|
||||
BlobBaseFee: blobBaseFee,
|
||||
GasLimit: header.GasLimit,
|
||||
Random: random,
|
||||
SlotNum: slotNum,
|
||||
CostPerGasByte: CostPerStateByte(header, chain.Config()),
|
||||
}
|
||||
}
|
||||
|
||||
// CostPerStateByte computes the cost per one byte of state creation
|
||||
// after EIP-8037
|
||||
func CostPerStateByte(header *types.Header, config *params.ChainConfig) uint64 {
|
||||
if config.IsAmsterdam(header.Number, header.Time) {
|
||||
return 1174
|
||||
// TODO (MariusVanDerWijden): for devnet-3 we hardcode the costPerStateByte
|
||||
//return ((header.GasLimit / 2) * 7200 * 365) / params.TargetStateGrowthPerYear
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// NewEVMTxContext creates a new transaction context for a single transaction.
|
||||
func NewEVMTxContext(msg *Message) vm.TxContext {
|
||||
ctx := vm.TxContext{
|
||||
|
|
|
|||
|
|
@ -27,6 +27,11 @@ type GasPool struct {
|
|||
remaining uint64
|
||||
initial uint64
|
||||
cumulativeUsed uint64
|
||||
|
||||
// EIP-8037: per-dimension cumulative sums for Amsterdam.
|
||||
// Block gas used = max(cumulativeRegular, cumulativeState).
|
||||
cumulativeRegular uint64
|
||||
cumulativeState uint64
|
||||
}
|
||||
|
||||
// NewGasPool initializes the gasPool with the given amount.
|
||||
|
|
@ -68,20 +73,41 @@ func (gp *GasPool) ReturnGas(returned uint64, gasUsed uint64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ReturnGasAmsterdam handles 2D gas accounting for Amsterdam (EIP-8037).
|
||||
// It undoes the SubGas deduction fully and accumulates per-dimension block totals.
|
||||
func (gp *GasPool) ReturnGasAmsterdam(returned, txRegular, txState, receiptGasUsed uint64) error {
|
||||
if gp.remaining > math.MaxUint64-returned {
|
||||
return fmt.Errorf("%w: remaining: %d, returned: %d", ErrGasLimitOverflow, gp.remaining, returned)
|
||||
}
|
||||
// Undo SubGas deduction fully (Amsterdam uses cumulative tracking)
|
||||
gp.remaining += returned
|
||||
// Accumulate 2D block dimensions
|
||||
gp.cumulativeRegular += txRegular
|
||||
gp.cumulativeState += txState
|
||||
gp.cumulativeUsed += receiptGasUsed
|
||||
return nil
|
||||
}
|
||||
|
||||
// Gas returns the amount of gas remaining in the pool.
|
||||
func (gp *GasPool) Gas() uint64 {
|
||||
return gp.remaining
|
||||
}
|
||||
|
||||
// CumulativeUsed returns the amount of cumulative consumed gas (refunded included).
|
||||
// CumulativeUsed returns the cumulative gas consumed for receipt tracking.
|
||||
// For Amsterdam blocks, this is the sum of per-tx tx_gas_used_after_refund
|
||||
// (what users pay), not the 2D block-level metric.
|
||||
func (gp *GasPool) CumulativeUsed() uint64 {
|
||||
return gp.cumulativeUsed
|
||||
}
|
||||
|
||||
// Used returns the amount of consumed gas.
|
||||
// Used returns the amount of consumed gas. For Amsterdam blocks with
|
||||
// 2D gas accounting (EIP-8037), returns max(sum_regular, sum_state).
|
||||
func (gp *GasPool) Used() uint64 {
|
||||
if gp.cumulativeRegular > 0 || gp.cumulativeState > 0 {
|
||||
return max(gp.cumulativeRegular, gp.cumulativeState)
|
||||
}
|
||||
if gp.initial < gp.remaining {
|
||||
panic("gas used underflow")
|
||||
panic(fmt.Sprintf("gas used underflow: %v %v", gp.initial, gp.remaining))
|
||||
}
|
||||
return gp.initial - gp.remaining
|
||||
}
|
||||
|
|
@ -89,9 +115,11 @@ func (gp *GasPool) Used() uint64 {
|
|||
// Snapshot returns the deep-copied object as the snapshot.
|
||||
func (gp *GasPool) Snapshot() *GasPool {
|
||||
return &GasPool{
|
||||
initial: gp.initial,
|
||||
remaining: gp.remaining,
|
||||
cumulativeUsed: gp.cumulativeUsed,
|
||||
initial: gp.initial,
|
||||
remaining: gp.remaining,
|
||||
cumulativeUsed: gp.cumulativeUsed,
|
||||
cumulativeRegular: gp.cumulativeRegular,
|
||||
cumulativeState: gp.cumulativeState,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,6 +128,14 @@ func (gp *GasPool) Set(other *GasPool) {
|
|||
gp.initial = other.initial
|
||||
gp.remaining = other.remaining
|
||||
gp.cumulativeUsed = other.cumulativeUsed
|
||||
gp.cumulativeRegular = other.cumulativeRegular
|
||||
gp.cumulativeState = other.cumulativeState
|
||||
}
|
||||
|
||||
// AmsterdamDimensions returns the per-dimension cumulative gas values
|
||||
// for 2D gas accounting (EIP-8037).
|
||||
func (gp *GasPool) AmsterdamDimensions() (regular, state uint64) {
|
||||
return gp.cumulativeRegular, gp.cumulativeState
|
||||
}
|
||||
|
||||
func (gp *GasPool) String() string {
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
|
|||
}
|
||||
evm.SetTxContext(NewEVMTxContext(msg))
|
||||
evm.StateDB.AddAddressToAccessList(params.BeaconRootsAddress)
|
||||
_, _, _ = evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||
_, _, _, _ = evm.Call(msg.From, *msg.To, msg.Data, vm.GasCosts{RegularGas: 30_000_000}, common.U2560)
|
||||
evm.StateDB.Finalise(true)
|
||||
}
|
||||
|
||||
|
|
@ -283,7 +283,7 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
|
|||
}
|
||||
evm.SetTxContext(NewEVMTxContext(msg))
|
||||
evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress)
|
||||
_, _, err := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||
_, _, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.GasCosts{RegularGas: 30_000_000}, common.U2560)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -322,7 +322,7 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte
|
|||
}
|
||||
evm.SetTxContext(NewEVMTxContext(msg))
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||
ret, _, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.GasCosts{RegularGas: 30_000_000}, common.U2560)
|
||||
evm.StateDB.Finalise(true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("system call failed to execute: %v", err)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"crypto/ecdsa"
|
||||
"math"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -432,3 +433,105 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
|
|||
}
|
||||
return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil))
|
||||
}
|
||||
|
||||
// TestEIP8037MaxRegularGasValidation tests that transactions with floor data gas
|
||||
// exceeding MaxTxGas are rejected in Amsterdam.
|
||||
func TestEIP8037MaxRegularGasValidation(t *testing.T) {
|
||||
var (
|
||||
// Create an Amsterdam-enabled chain config
|
||||
config = ¶ms.ChainConfig{
|
||||
ChainID: big.NewInt(1),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
IstanbulBlock: big.NewInt(0),
|
||||
MuirGlacierBlock: big.NewInt(0),
|
||||
BerlinBlock: big.NewInt(0),
|
||||
LondonBlock: big.NewInt(0),
|
||||
ArrowGlacierBlock: big.NewInt(0),
|
||||
GrayGlacierBlock: big.NewInt(0),
|
||||
MergeNetsplitBlock: big.NewInt(0),
|
||||
ShanghaiTime: u64(0),
|
||||
CancunTime: u64(0),
|
||||
PragueTime: u64(0),
|
||||
OsakaTime: u64(0),
|
||||
AmsterdamTime: u64(0),
|
||||
TerminalTotalDifficulty: big.NewInt(0),
|
||||
Ethash: new(params.EthashConfig),
|
||||
BlobScheduleConfig: ¶ms.BlobScheduleConfig{
|
||||
Cancun: params.DefaultCancunBlobConfig,
|
||||
Prague: params.DefaultPragueBlobConfig,
|
||||
Osaka: params.DefaultOsakaBlobConfig,
|
||||
Amsterdam: params.DefaultOsakaBlobConfig,
|
||||
},
|
||||
}
|
||||
signer = types.LatestSigner(config)
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
)
|
||||
|
||||
// Calculate how much data is needed to exceed MaxTxGas with floor data gas.
|
||||
// FloorDataGas = TxGas + tokens * TxCostFloorPerToken
|
||||
// For non-zero bytes: tokens = 4 per byte, so cost = 40 gas per byte
|
||||
// MaxTxGas = 16,777,216
|
||||
// To exceed: tokens * 10 > MaxTxGas - TxGas = 16,756,216
|
||||
// For non-zero bytes: bytes * 4 * 10 > 16,756,216 → bytes > 418,905
|
||||
dataSize := 420000 // This should exceed MaxTxGas
|
||||
largeData := make([]byte, dataSize)
|
||||
for i := range largeData {
|
||||
largeData[i] = 0xFF // Non-zero bytes have higher token cost
|
||||
}
|
||||
|
||||
// Verify that floor data gas exceeds MaxTxGas
|
||||
floorGas, err := FloorDataGas(largeData)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate floor data gas: %v", err)
|
||||
}
|
||||
if floorGas <= params.MaxTxGas {
|
||||
t.Fatalf("Test setup error: floor data gas %d should exceed MaxTxGas %d", floorGas, params.MaxTxGas)
|
||||
}
|
||||
t.Logf("Floor data gas: %d, MaxTxGas: %d", floorGas, params.MaxTxGas)
|
||||
|
||||
// Create a transaction with large calldata. The gas limit is set high enough
|
||||
// to cover the floor data gas, but since floor data gas > MaxTxGas,
|
||||
// this should be rejected in Amsterdam.
|
||||
tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{
|
||||
Nonce: 0,
|
||||
GasTipCap: big.NewInt(params.InitialBaseFee),
|
||||
GasFeeCap: big.NewInt(params.InitialBaseFee),
|
||||
Gas: floorGas + 1000000, // Enough gas to cover floor + state gas
|
||||
To: &common.Address{},
|
||||
Value: big.NewInt(0),
|
||||
Data: largeData,
|
||||
}), signer, key1)
|
||||
|
||||
var (
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec = &Genesis{
|
||||
Config: config,
|
||||
GasLimit: 100_000_000, // High block gas limit to not interfere with the test
|
||||
Alloc: types.GenesisAlloc{
|
||||
common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): types.Account{
|
||||
Balance: new(big.Int).Mul(big.NewInt(1000000), big.NewInt(params.Ether)), // Lots of ether
|
||||
Nonce: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
blockchain, _ = NewBlockChain(db, gspec, beacon.New(ethash.NewFaker()), nil)
|
||||
)
|
||||
defer blockchain.Stop()
|
||||
|
||||
block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), types.Transactions{tx}, config, false)
|
||||
_, err = blockchain.InsertChain(types.Blocks{block})
|
||||
if err == nil {
|
||||
t.Fatal("block with floor data gas > MaxTxGas should have been rejected")
|
||||
}
|
||||
// The error should indicate that max regular gas exceeds the limit
|
||||
if !strings.Contains(err.Error(), "max regular gas") || !strings.Contains(err.Error(), "exceeds limit") {
|
||||
t.Errorf("unexpected error message: %v", err)
|
||||
}
|
||||
t.Logf("Got expected error: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,13 +68,20 @@ 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, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) {
|
||||
// costPerStateByte needs to be set post-Amsterdam.
|
||||
func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.SetCodeAuthorization, isContractCreation bool, rules params.Rules, costPerStateByte uint64) (vm.GasCosts, error) {
|
||||
// Set the starting gas for the raw transaction
|
||||
var gas uint64
|
||||
if isContractCreation && isHomestead {
|
||||
gas = params.TxGasContractCreation
|
||||
var gas vm.GasCosts
|
||||
if isContractCreation && rules.IsHomestead {
|
||||
if rules.IsAmsterdam {
|
||||
// EIP-8037: account creation is state gas; base tx + CREATE overhead is regular gas.
|
||||
gas.RegularGas = params.TxGas + params.CreateGasAmsterdam
|
||||
gas.StateGas = params.AccountCreationSize * costPerStateByte
|
||||
} else {
|
||||
gas.RegularGas = params.TxGasContractCreation
|
||||
}
|
||||
} else {
|
||||
gas = params.TxGas
|
||||
gas.RegularGas = params.TxGas
|
||||
}
|
||||
dataLen := uint64(len(data))
|
||||
// Bump the required gas by the amount of transactional data
|
||||
|
|
@ -85,33 +92,38 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
|
|||
|
||||
// Make sure we don't exceed uint64 for all data combinations
|
||||
nonZeroGas := params.TxDataNonZeroGasFrontier
|
||||
if isEIP2028 {
|
||||
if rules.IsIstanbul {
|
||||
nonZeroGas = params.TxDataNonZeroGasEIP2028
|
||||
}
|
||||
if (math.MaxUint64-gas)/nonZeroGas < nz {
|
||||
return 0, ErrGasUintOverflow
|
||||
if (math.MaxUint64-gas.RegularGas)/nonZeroGas < nz {
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
gas += nz * nonZeroGas
|
||||
gas.RegularGas += nz * nonZeroGas
|
||||
|
||||
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
|
||||
return 0, ErrGasUintOverflow
|
||||
if (math.MaxUint64-gas.RegularGas)/params.TxDataZeroGas < z {
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
gas += z * params.TxDataZeroGas
|
||||
gas.RegularGas += z * params.TxDataZeroGas
|
||||
|
||||
if isContractCreation && isEIP3860 {
|
||||
if isContractCreation && rules.IsShanghai {
|
||||
lenWords := toWordSize(dataLen)
|
||||
if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords {
|
||||
return 0, ErrGasUintOverflow
|
||||
if (math.MaxUint64-gas.RegularGas)/params.InitCodeWordGas < lenWords {
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
gas += lenWords * params.InitCodeWordGas
|
||||
gas.RegularGas += lenWords * params.InitCodeWordGas
|
||||
}
|
||||
}
|
||||
if accessList != nil {
|
||||
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
|
||||
gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
|
||||
gas.RegularGas += uint64(len(accessList)) * params.TxAccessListAddressGas
|
||||
gas.RegularGas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
|
||||
}
|
||||
if authList != nil {
|
||||
gas += uint64(len(authList)) * params.CallNewAccountGas
|
||||
if rules.IsAmsterdam {
|
||||
gas.RegularGas += uint64(len(authList)) * params.TxAuthTupleRegularGas
|
||||
gas.StateGas += uint64(len(authList)) * (params.AuthorizationCreationSize + params.AccountCreationSize) * costPerStateByte
|
||||
} else {
|
||||
gas.RegularGas += uint64(len(authList)) * params.CallNewAccountGas
|
||||
}
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
|
@ -244,8 +256,8 @@ func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, err
|
|||
type stateTransition struct {
|
||||
gp *GasPool
|
||||
msg *Message
|
||||
gasRemaining uint64
|
||||
initialGas uint64
|
||||
gasRemaining vm.GasCosts
|
||||
initialGas vm.GasCosts
|
||||
state vm.StateDB
|
||||
evm *vm.EVM
|
||||
}
|
||||
|
|
@ -304,9 +316,14 @@ func (st *stateTransition) buyGas() error {
|
|||
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil {
|
||||
st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, tracing.GasChangeTxInitialBalance)
|
||||
}
|
||||
st.gasRemaining = st.msg.GasLimit
|
||||
st.initialGas = st.msg.GasLimit
|
||||
st.gasRemaining.RegularGas = st.msg.GasLimit
|
||||
|
||||
// After Amsterdam we limit the regular gas to 16k, the data gas to the transaction limit
|
||||
limit := st.msg.GasLimit
|
||||
if st.evm.ChainConfig().IsAmsterdam(st.evm.Context.BlockNumber, st.evm.Context.Time) {
|
||||
limit = min(st.msg.GasLimit, params.MaxTxGas)
|
||||
}
|
||||
st.initialGas = vm.GasCosts{RegularGas: limit, StateGas: st.msg.GasLimit - limit}
|
||||
mgvalU256, _ := uint256.FromBig(mgval)
|
||||
st.state.SubBalance(st.msg.From, mgvalU256, tracing.BalanceDecreaseGasBuy)
|
||||
return nil
|
||||
|
|
@ -330,9 +347,10 @@ func (st *stateTransition) preCheck() error {
|
|||
}
|
||||
}
|
||||
isOsaka := st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time)
|
||||
isAmsterdam := st.evm.ChainConfig().IsAmsterdam(st.evm.Context.BlockNumber, st.evm.Context.Time)
|
||||
if !msg.SkipTransactionChecks {
|
||||
// Verify tx gas limit does not exceed EIP-7825 cap.
|
||||
if isOsaka && msg.GasLimit > params.MaxTxGas {
|
||||
if !isAmsterdam && isOsaka && msg.GasLimit > params.MaxTxGas {
|
||||
return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit)
|
||||
}
|
||||
// Make sure the sender is an EOA
|
||||
|
|
@ -448,14 +466,12 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
)
|
||||
|
||||
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
||||
gas, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
|
||||
gas, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules, st.evm.Context.CostPerGasByte)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if st.gasRemaining < gas {
|
||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas)
|
||||
}
|
||||
// Gas limit suffices for the floor data cost (EIP-7623)
|
||||
|
||||
// Compute the floor data cost (EIP-7623), needed for both Prague and Amsterdam validation.
|
||||
if rules.IsPrague {
|
||||
floorDataGas, err = FloorDataGas(msg.Data)
|
||||
if err != nil {
|
||||
|
|
@ -465,10 +481,35 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
return nil, fmt.Errorf("%w: have %d, want %d", ErrFloorDataGas, msg.GasLimit, floorDataGas)
|
||||
}
|
||||
}
|
||||
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
|
||||
t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, tracing.GasChangeTxIntrinsicGas)
|
||||
|
||||
if rules.IsAmsterdam {
|
||||
// EIP-8037: total intrinsic must fit within the transaction gas limit.
|
||||
if msg.GasLimit < gas.Sum() {
|
||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, msg.GasLimit, gas.Sum())
|
||||
}
|
||||
// EIP-8037: the regular gas consumption (intrinsic or floor) must fit within MaxTxGas.
|
||||
// The transaction gas limit is no longer statically capped, but regular gas usage is.
|
||||
maxRegularGas := max(gas.RegularGas, floorDataGas)
|
||||
if maxRegularGas > params.MaxTxGas {
|
||||
return nil, fmt.Errorf("%w: max regular gas %d exceeds limit %d", ErrIntrinsicGas, maxRegularGas, params.MaxTxGas)
|
||||
}
|
||||
// Split remaining execution gas into regular and state reservoir.
|
||||
executionGas := msg.GasLimit - gas.Sum()
|
||||
regularGas := min(params.MaxTxGas-gas.RegularGas, executionGas)
|
||||
st.gasRemaining = vm.GasCosts{RegularGas: regularGas, StateGas: executionGas - regularGas}
|
||||
} else {
|
||||
if st.gasRemaining.Underflow(gas) {
|
||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining.RegularGas, gas.RegularGas)
|
||||
}
|
||||
st.gasRemaining.Sub(gas)
|
||||
}
|
||||
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
|
||||
if rules.IsAmsterdam {
|
||||
t.OnGasChange(msg.GasLimit, st.gasRemaining.RegularGas+st.gasRemaining.StateGas, tracing.GasChangeTxIntrinsicGas)
|
||||
} else {
|
||||
t.OnGasChange(st.gasRemaining.RegularGas+gas.RegularGas, st.gasRemaining.RegularGas, tracing.GasChangeTxIntrinsicGas)
|
||||
}
|
||||
}
|
||||
st.gasRemaining -= gas
|
||||
|
||||
if rules.IsEIP4762 {
|
||||
st.evm.AccessEvents.AddTxOrigin(msg.From)
|
||||
|
|
@ -500,11 +541,13 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
|
||||
|
||||
var (
|
||||
ret []byte
|
||||
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||
ret []byte
|
||||
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||
authRefund uint64
|
||||
)
|
||||
var execGasUsed vm.GasUsed
|
||||
if contractCreation {
|
||||
ret, _, st.gasRemaining, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining, value)
|
||||
ret, _, st.gasRemaining, execGasUsed, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining, value)
|
||||
} else {
|
||||
// Increment the nonce for the next transaction.
|
||||
st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1, tracing.NonceChangeEoACall)
|
||||
|
|
@ -513,7 +556,8 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
if msg.SetCodeAuthorizations != nil {
|
||||
for _, auth := range msg.SetCodeAuthorizations {
|
||||
// Note errors are ignored, we simply skip invalid authorizations here.
|
||||
st.applyAuthorization(&auth)
|
||||
refund, _ := st.applyAuthorization(rules, &auth)
|
||||
authRefund += refund
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -527,7 +571,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
}
|
||||
|
||||
// Execute the transaction's call.
|
||||
ret, st.gasRemaining, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value)
|
||||
ret, st.gasRemaining, execGasUsed, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value)
|
||||
}
|
||||
|
||||
// Record the gas used excluding gas refunds. This value represents the actual
|
||||
|
|
@ -535,33 +579,42 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
peakGasUsed := st.gasUsed()
|
||||
|
||||
// Compute refund counter, capped to a refund quotient.
|
||||
st.gasRemaining += st.calcRefund()
|
||||
st.gasRemaining.RegularGas += st.calcRefund()
|
||||
if rules.IsPrague {
|
||||
// After EIP-7623: Data-heavy transactions pay the floor gas.
|
||||
if st.gasUsed() < floorDataGas {
|
||||
prev := st.gasRemaining
|
||||
st.gasRemaining = st.initialGas - floorDataGas
|
||||
prev := st.gasRemaining.RegularGas
|
||||
// When the calldata floor exceeds actual gas used, any
|
||||
// remaining state gas must also be consumed
|
||||
targetRemaining := (st.initialGas.RegularGas + st.initialGas.StateGas) - floorDataGas
|
||||
st.gasRemaining.StateGas = 0
|
||||
st.gasRemaining.RegularGas = targetRemaining
|
||||
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
|
||||
t.OnGasChange(prev, st.gasRemaining, tracing.GasChangeTxDataFloor)
|
||||
t.OnGasChange(prev, st.gasRemaining.RegularGas, tracing.GasChangeTxDataFloor)
|
||||
}
|
||||
}
|
||||
if peakGasUsed < floorDataGas {
|
||||
peakGasUsed = floorDataGas
|
||||
}
|
||||
}
|
||||
// Return gas to the user
|
||||
st.returnGas()
|
||||
|
||||
// Return gas to the gas pool
|
||||
returned := st.returnGas()
|
||||
|
||||
if rules.IsAmsterdam {
|
||||
// Refund is excluded for returning
|
||||
err = st.gp.ReturnGas(st.initialGas-peakGasUsed, st.gasUsed())
|
||||
// EIP-8037: 2D gas accounting for Amsterdam.
|
||||
// tx_regular = intrinsic_regular + exec_regular_gas_used
|
||||
// tx_state = intrinsic_state (adjusted) + exec_state_gas_used
|
||||
// These are tracked independently, not derived from remaining gas.
|
||||
txState := (gas.StateGas - authRefund) + execGasUsed.StateGasCharged
|
||||
txRegular := gas.RegularGas + execGasUsed.RegularGasUsed
|
||||
txRegular = max(txRegular, floorDataGas)
|
||||
if err := st.gp.ReturnGasAmsterdam(returned, txRegular, txState, st.gasUsed()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Refund is included for returning
|
||||
err = st.gp.ReturnGas(st.gasRemaining, st.gasUsed())
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err := st.gp.ReturnGas(returned, st.gasUsed()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
effectiveTip := msg.GasPrice
|
||||
if rules.IsLondon {
|
||||
|
|
@ -574,8 +627,14 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
// are 0. This avoids a negative effectiveTip being applied to
|
||||
// the coinbase when simulating calls.
|
||||
} else {
|
||||
fee := new(uint256.Int).SetUint64(st.gasUsed())
|
||||
// For Amsterdam, the fee is based on what the user pays (receipt gas used).
|
||||
feeGas := st.gasUsed()
|
||||
fee := new(uint256.Int).SetUint64(feeGas)
|
||||
fee.Mul(fee, effectiveTipU256)
|
||||
|
||||
// always read the coinbase account to include it in the BAL (TODO check this is actually part of the spec)
|
||||
st.state.GetBalance(st.evm.Context.Coinbase)
|
||||
|
||||
st.state.AddBalance(st.evm.Context.Coinbase, fee, tracing.BalanceIncreaseRewardTransactionFee)
|
||||
|
||||
// add the coinbase to the witness iff the fee is greater than 0
|
||||
|
|
@ -586,8 +645,9 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
if rules.IsAmsterdam {
|
||||
st.evm.StateDB.EmitLogsForBurnAccounts()
|
||||
}
|
||||
usedGas := st.gasUsed()
|
||||
return &ExecutionResult{
|
||||
UsedGas: st.gasUsed(),
|
||||
UsedGas: usedGas,
|
||||
MaxUsedGas: peakGasUsed,
|
||||
Err: vmerr,
|
||||
ReturnData: ret,
|
||||
|
|
@ -626,30 +686,43 @@ func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorizatio
|
|||
}
|
||||
|
||||
// applyAuthorization applies an EIP-7702 code delegation to the state.
|
||||
func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization) error {
|
||||
func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.SetCodeAuthorization) (uint64, error) {
|
||||
authority, err := st.validateAuthorization(auth)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// If the account already exists in state, refund the new account cost
|
||||
// charged in the intrinsic calculation.
|
||||
var refund uint64
|
||||
if st.state.Exist(authority) {
|
||||
st.state.AddRefund(params.CallNewAccountGas - params.TxAuthTupleGas)
|
||||
if rules.IsAmsterdam {
|
||||
// EIP-8037: refund account creation state gas to the reservoir
|
||||
refund = params.AccountCreationSize * st.evm.Context.CostPerGasByte
|
||||
st.gasRemaining.StateGas += refund
|
||||
} else {
|
||||
st.state.AddRefund(params.CallNewAccountGas - params.TxAuthTupleGas)
|
||||
}
|
||||
}
|
||||
|
||||
prevDelegation, isDelegated := types.ParseDelegation(st.state.GetCode(authority))
|
||||
|
||||
// Update nonce and account code.
|
||||
st.state.SetNonce(authority, auth.Nonce+1, tracing.NonceChangeAuthorization)
|
||||
if auth.Address == (common.Address{}) {
|
||||
// Delegation to zero address means clear.
|
||||
st.state.SetCode(authority, nil, tracing.CodeChangeAuthorizationClear)
|
||||
return nil
|
||||
if isDelegated {
|
||||
st.state.SetCode(authority, nil, tracing.CodeChangeAuthorizationClear)
|
||||
}
|
||||
return refund, nil
|
||||
}
|
||||
|
||||
// Otherwise install delegation to auth.Address.
|
||||
st.state.SetCode(authority, types.AddressToDelegation(auth.Address), tracing.CodeChangeAuthorization)
|
||||
// install delegation to auth.Address if the delegation changed
|
||||
if !isDelegated || auth.Address != prevDelegation {
|
||||
st.state.SetCode(authority, types.AddressToDelegation(auth.Address), tracing.CodeChangeAuthorization)
|
||||
}
|
||||
|
||||
return nil
|
||||
return refund, nil
|
||||
}
|
||||
|
||||
// calcRefund computes refund counter, capped to a refund quotient.
|
||||
|
|
@ -666,26 +739,30 @@ func (st *stateTransition) calcRefund() uint64 {
|
|||
refund = st.state.GetRefund()
|
||||
}
|
||||
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && refund > 0 {
|
||||
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, tracing.GasChangeTxRefunds)
|
||||
st.evm.Config.Tracer.OnGasChange(st.gasRemaining.RegularGas, st.gasRemaining.RegularGas+refund, tracing.GasChangeTxRefunds)
|
||||
}
|
||||
return refund
|
||||
}
|
||||
|
||||
// returnGas returns ETH for remaining gas,
|
||||
// exchanged at the original rate.
|
||||
func (st *stateTransition) returnGas() {
|
||||
remaining := uint256.NewInt(st.gasRemaining)
|
||||
func (st *stateTransition) returnGas() uint64 {
|
||||
gas := st.gasRemaining.RegularGas + st.gasRemaining.StateGas
|
||||
remaining := uint256.NewInt(gas)
|
||||
remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice))
|
||||
st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn)
|
||||
|
||||
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 {
|
||||
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, tracing.GasChangeTxLeftOverReturned)
|
||||
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && gas > 0 {
|
||||
st.evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeTxLeftOverReturned)
|
||||
}
|
||||
return gas
|
||||
}
|
||||
|
||||
// gasUsed returns the amount of gas used up by the state transition.
|
||||
// For Amsterdam (2D gas), this includes both regular and state gas consumed.
|
||||
func (st *stateTransition) gasUsed() uint64 {
|
||||
return st.initialGas - st.gasRemaining
|
||||
return (st.initialGas.RegularGas + st.initialGas.StateGas) -
|
||||
(st.gasRemaining.RegularGas + st.gasRemaining.StateGas)
|
||||
}
|
||||
|
||||
// blobGasUsed returns the amount of blob gas used by the message.
|
||||
|
|
|
|||
|
|
@ -125,13 +125,23 @@ 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, true, rules.IsIstanbul, rules.IsShanghai)
|
||||
gasCostPerStateByte := core.CostPerStateByte(head, opts.Config)
|
||||
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, gasCostPerStateByte)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tx.Gas() < intrGas {
|
||||
return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrIntrinsicGas, tx.Gas(), intrGas)
|
||||
if gasCostPerStateByte != 0 {
|
||||
// We require transactions to pay for 110% of intrinsic gas in order to
|
||||
// prevent situations where a change in gas limit invalidates a lot
|
||||
// of transactions in the txpool
|
||||
if tx.Gas() < (intrGas.RegularGas*10)/9 {
|
||||
return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrIntrinsicGas, tx.Gas(), intrGas.RegularGas)
|
||||
}
|
||||
}
|
||||
if tx.Gas() < intrGas.RegularGas {
|
||||
return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrIntrinsicGas, tx.Gas(), intrGas.RegularGas)
|
||||
}
|
||||
|
||||
// Ensure the transaction can cover floor data gas.
|
||||
if rules.IsPrague {
|
||||
floorDataGas, err := core.FloorDataGas(tx.Data())
|
||||
|
|
|
|||
|
|
@ -42,12 +42,13 @@ type Contract struct {
|
|||
IsDeployment bool
|
||||
IsSystemCall bool
|
||||
|
||||
Gas uint64
|
||||
value *uint256.Int
|
||||
Gas GasCosts
|
||||
GasUsed GasUsed // EIP-8037: canonical per-frame gas usage accumulator
|
||||
value *uint256.Int
|
||||
}
|
||||
|
||||
// NewContract returns a new contract environment for the execution of EVM.
|
||||
func NewContract(caller common.Address, address common.Address, value *uint256.Int, gas uint64, jumpDests JumpDestCache) *Contract {
|
||||
func NewContract(caller common.Address, address common.Address, value *uint256.Int, gas GasCosts, jumpDests JumpDestCache) *Contract {
|
||||
// Initialize the jump analysis cache if it's nil, mostly for tests
|
||||
if jumpDests == nil {
|
||||
jumpDests = newMapJumpDests()
|
||||
|
|
@ -126,26 +127,37 @@ func (c *Contract) Caller() common.Address {
|
|||
}
|
||||
|
||||
// UseGas attempts the use gas and subtracts it and returns true on success
|
||||
func (c *Contract) UseGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) (ok bool) {
|
||||
if c.Gas < gas {
|
||||
func (c *Contract) UseGas(gas GasCosts, logger *tracing.Hooks, reason tracing.GasChangeReason) (ok bool) {
|
||||
if c.Gas.Underflow(gas) {
|
||||
return false
|
||||
}
|
||||
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
|
||||
logger.OnGasChange(c.Gas, c.Gas-gas, reason)
|
||||
logger.OnGasChange(c.Gas.RegularGas, c.Gas.RegularGas-gas.RegularGas, reason)
|
||||
}
|
||||
c.Gas -= gas
|
||||
c.GasUsed.Add(gas)
|
||||
c.Gas.Sub(gas)
|
||||
return true
|
||||
}
|
||||
|
||||
// RefundGas refunds gas to the contract
|
||||
func (c *Contract) RefundGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
||||
if gas == 0 {
|
||||
// RefundGas refunds gas to the contract. gasUsed carries the child frame's
|
||||
// accumulated gas usage metrics (EIP-8037), incorporated on both success and error.
|
||||
func (c *Contract) RefundGas(err error, gas GasCosts, gasUsed GasUsed, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
||||
// If the preceding call errored, return the state gas
|
||||
// to the parent call
|
||||
if err != nil {
|
||||
gas.StateGas += gasUsed.StateGasCharged
|
||||
gasUsed.StateGasCharged = 0
|
||||
}
|
||||
if gas.RegularGas == 0 && gas.StateGas == 0 && gasUsed.StateGasCharged == 0 && gasUsed.RegularGasUsed == 0 {
|
||||
return
|
||||
}
|
||||
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
|
||||
logger.OnGasChange(c.Gas, c.Gas+gas, reason)
|
||||
logger.OnGasChange(c.Gas.RegularGas, c.Gas.RegularGas+gas.RegularGas, reason)
|
||||
}
|
||||
c.Gas += gas
|
||||
c.Gas.RegularGas += gas.RegularGas
|
||||
c.Gas.StateGas = gas.StateGas
|
||||
c.GasUsed.StateGasCharged += gasUsed.StateGasCharged
|
||||
c.GasUsed.RegularGasUsed += gasUsed.RegularGasUsed
|
||||
}
|
||||
|
||||
// Address returns the contracts address
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ var activators = map[int]func(*JumpTable){
|
|||
7939: enable7939,
|
||||
8024: enable8024,
|
||||
7843: enable7843,
|
||||
8037: enable8037,
|
||||
}
|
||||
|
||||
// EnableEIP enables the given EIP on the config.
|
||||
|
|
@ -381,8 +382,8 @@ func opExtCodeCopyEIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, er
|
|||
addr := common.Address(a.Bytes20())
|
||||
code := evm.StateDB.GetCode(addr)
|
||||
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas)
|
||||
scope.Contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas.RegularGas)
|
||||
scope.Contract.UseGas(GasCosts{RegularGas: consumed}, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||
if consumed < wanted {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
|
@ -407,8 +408,8 @@ func opPush1EIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
|
||||
// advanced past this boundary.
|
||||
contractAddr := scope.Contract.Address()
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
||||
scope.Contract.UseGas(wanted, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas.RegularGas)
|
||||
scope.Contract.UseGas(GasCosts{RegularGas: wanted}, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||
if consumed < wanted {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
|
@ -435,8 +436,8 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
|
|||
|
||||
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
||||
contractAddr := scope.Contract.Address()
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
||||
scope.Contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas.RegularGas)
|
||||
scope.Contract.UseGas(GasCosts{RegularGas: consumed}, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||
if consumed < wanted {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
|
@ -596,3 +597,16 @@ func enable7843(jt *JumpTable) {
|
|||
maxStack: maxStack(0, 1),
|
||||
}
|
||||
}
|
||||
|
||||
// enable8037 enables the multidimensional-metering as specified in EIP-8037.
|
||||
func enable8037(jt *JumpTable) {
|
||||
// EIP-8037: CREATE/CREATE2 constant gas changes from 32000 to 9000 regular;
|
||||
// the account creation cost moves to state gas (in dynamicGas).
|
||||
jt[CREATE].constantGas = params.CreateGasAmsterdam
|
||||
jt[CREATE].dynamicGas = gasCreateEip8037
|
||||
jt[CREATE2].constantGas = params.CreateGasAmsterdam
|
||||
jt[CREATE2].dynamicGas = gasCreate2Eip8037
|
||||
jt[CALL].dynamicGas = gasCallEIP8037
|
||||
jt[SELFDESTRUCT].dynamicGas = gasSelfdestruct8037
|
||||
jt[SSTORE].dynamicGas = gasSStore8037
|
||||
}
|
||||
|
|
|
|||
238
core/vm/evm.go
238
core/vm/evm.go
|
|
@ -67,6 +67,8 @@ type BlockContext struct {
|
|||
BlobBaseFee *big.Int // Provides information for BLOBBASEFEE (0 if vm runs with NoBaseFee flag and 0 blob gas price)
|
||||
Random *common.Hash // Provides information for PREVRANDAO
|
||||
SlotNum uint64 // Provides information for SLOTNUM
|
||||
|
||||
CostPerGasByte uint64 // EIP-8037
|
||||
}
|
||||
|
||||
// TxContext provides the EVM with information about a transaction.
|
||||
|
|
@ -236,46 +238,40 @@ func isSystemCall(caller common.Address) bool {
|
|||
// parameters. It also handles any necessary value transfer required and takse
|
||||
// the necessary steps to create accounts and reverses the state in case of an
|
||||
// execution error or failed value transfer.
|
||||
func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
|
||||
func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, gas GasCosts, value *uint256.Int) (ret []byte, leftOverGas GasCosts, gasUsed GasUsed, err error) {
|
||||
// Capture the tracer start/end events in debug mode
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.captureBegin(evm.depth, CALL, caller, addr, input, gas, value.ToBig())
|
||||
defer func(startGas uint64) {
|
||||
defer func(startGas GasCosts) {
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
return nil, gas, GasUsed{}, ErrDepth
|
||||
}
|
||||
syscall := isSystemCall(caller)
|
||||
|
||||
// Fail if we're trying to transfer more than the available balance.
|
||||
if !syscall && !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
||||
return nil, gas, ErrInsufficientBalance
|
||||
return nil, gas, GasUsed{}, ErrInsufficientBalance
|
||||
}
|
||||
snapshot := evm.StateDB.Snapshot()
|
||||
|
||||
p, isPrecompile := evm.precompile(addr)
|
||||
if !evm.StateDB.Exist(addr) {
|
||||
if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) {
|
||||
// Add proof of absence to witness
|
||||
// At this point, the read costs have already been charged, either because this
|
||||
// is a direct tx call, in which case it's covered by the intrinsic gas, or because
|
||||
// of a CALL instruction, in which case BASIC_DATA has been added to the access
|
||||
// list in write mode. If there is enough gas paying for the addition of the code
|
||||
// hash leaf to the access list, then account creation will proceed unimpaired.
|
||||
// Thus, only pay for the creation of the code hash leaf here.
|
||||
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas, false)
|
||||
if gas < wgas {
|
||||
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas.RegularGas, false)
|
||||
if gas.RegularGas < wgas {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
return nil, 0, ErrOutOfGas
|
||||
return nil, GasCosts{}, GasUsed{}, ErrOutOfGas
|
||||
}
|
||||
gas -= wgas
|
||||
gas.RegularGas -= wgas
|
||||
}
|
||||
|
||||
if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() {
|
||||
// Calling a non-existing account, don't do anything.
|
||||
return nil, gas, nil
|
||||
return nil, gas, GasUsed{}, nil
|
||||
}
|
||||
evm.StateDB.CreateAccount(addr)
|
||||
}
|
||||
|
|
@ -291,7 +287,9 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
if evm.chainRules.IsAmsterdam {
|
||||
stateDB = evm.StateDB
|
||||
}
|
||||
ret, gas, err = RunPrecompiledContract(stateDB, p, addr, input, gas, evm.Config.Tracer)
|
||||
gasBefore := gas.RegularGas
|
||||
ret, gas.RegularGas, err = RunPrecompiledContract(stateDB, p, addr, input, gas.RegularGas, evm.Config.Tracer)
|
||||
gasUsed.RegularGasUsed += gasBefore - gas.RegularGas
|
||||
} else {
|
||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||
code := evm.resolveCode(addr)
|
||||
|
|
@ -304,6 +302,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
contract.SetCallCode(evm.resolveCodeHash(addr), code)
|
||||
ret, err = evm.Run(contract, input, false)
|
||||
gas = contract.Gas
|
||||
gasUsed = contract.GasUsed
|
||||
}
|
||||
}
|
||||
// When an error was returned by the EVM or when setting the creation code
|
||||
|
|
@ -311,17 +310,16 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
// when we're in homestead this also counts for code storage gas errors.
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
isRevert := err == ErrExecutionReverted
|
||||
if !isRevert {
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
gas = 0
|
||||
gasUsed.RegularGasUsed += gas.RegularGas
|
||||
gas.RegularGas = 0
|
||||
}
|
||||
// TODO: consider clearing up unused snapshots:
|
||||
//} else {
|
||||
// evm.StateDB.DiscardSnapshot(snapshot)
|
||||
}
|
||||
return ret, gas, err
|
||||
return ret, gas, gasUsed, err
|
||||
}
|
||||
|
||||
// CallCode executes the contract associated with the addr with the given input
|
||||
|
|
@ -331,24 +329,21 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
//
|
||||
// CallCode differs from Call in the sense that it executes the given address'
|
||||
// code with the caller as context.
|
||||
func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
|
||||
func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byte, gas GasCosts, value *uint256.Int) (ret []byte, leftOverGas GasCosts, gasUsed GasUsed, err error) {
|
||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.captureBegin(evm.depth, CALLCODE, caller, addr, input, gas, value.ToBig())
|
||||
defer func(startGas uint64) {
|
||||
defer func(startGas GasCosts) {
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
return nil, gas, GasUsed{}, ErrDepth
|
||||
}
|
||||
// Fail if we're trying to transfer more than the available balance
|
||||
// Note although it's noop to transfer X ether to caller itself. But
|
||||
// if caller doesn't have enough balance, it would be an error to allow
|
||||
// over-charging itself. So the check here is necessary.
|
||||
if !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
||||
return nil, gas, ErrInsufficientBalance
|
||||
return nil, gas, GasUsed{}, ErrInsufficientBalance
|
||||
}
|
||||
var snapshot = evm.StateDB.Snapshot()
|
||||
|
||||
|
|
@ -358,7 +353,9 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
|||
if evm.chainRules.IsAmsterdam {
|
||||
stateDB = evm.StateDB
|
||||
}
|
||||
ret, gas, err = RunPrecompiledContract(stateDB, p, addr, input, gas, evm.Config.Tracer)
|
||||
gasBefore := gas.RegularGas
|
||||
ret, gas.RegularGas, err = RunPrecompiledContract(stateDB, p, addr, input, gas.RegularGas, evm.Config.Tracer)
|
||||
gasUsed.RegularGasUsed += gasBefore - gas.RegularGas
|
||||
} else {
|
||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||
// The contract is a scoped environment for this execution context only.
|
||||
|
|
@ -366,17 +363,20 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
|||
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
||||
ret, err = evm.Run(contract, input, false)
|
||||
gas = contract.Gas
|
||||
gasUsed = contract.GasUsed
|
||||
}
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
isRevert := err == ErrExecutionReverted
|
||||
if !isRevert {
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
gas = 0
|
||||
gasUsed.RegularGasUsed += gas.RegularGas
|
||||
gas.RegularGas = 0
|
||||
}
|
||||
}
|
||||
return ret, gas, err
|
||||
return ret, gas, gasUsed, err
|
||||
}
|
||||
|
||||
// DelegateCall executes the contract associated with the addr with the given input
|
||||
|
|
@ -384,18 +384,18 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
|||
//
|
||||
// DelegateCall differs from CallCode in the sense that it executes the given address'
|
||||
// code with the caller as context and the caller is set to the caller of the caller.
|
||||
func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
|
||||
func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, addr common.Address, input []byte, gas GasCosts, value *uint256.Int) (ret []byte, leftOverGas GasCosts, gasUsed GasUsed, err error) {
|
||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||
if evm.Config.Tracer != nil {
|
||||
// DELEGATECALL inherits value from parent call
|
||||
evm.captureBegin(evm.depth, DELEGATECALL, caller, addr, input, gas, value.ToBig())
|
||||
defer func(startGas uint64) {
|
||||
defer func(startGas GasCosts) {
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
return nil, gas, GasUsed{}, ErrDepth
|
||||
}
|
||||
var snapshot = evm.StateDB.Snapshot()
|
||||
|
||||
|
|
@ -405,55 +405,48 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
|||
if evm.chainRules.IsAmsterdam {
|
||||
stateDB = evm.StateDB
|
||||
}
|
||||
ret, gas, err = RunPrecompiledContract(stateDB, p, addr, input, gas, evm.Config.Tracer)
|
||||
gasBefore := gas.RegularGas
|
||||
ret, gas.RegularGas, err = RunPrecompiledContract(stateDB, p, addr, input, gas.RegularGas, evm.Config.Tracer)
|
||||
gasUsed.RegularGasUsed += gasBefore - gas.RegularGas
|
||||
} else {
|
||||
// Initialise a new contract and make initialise the delegate values
|
||||
//
|
||||
// Note: The value refers to the original value from the parent call.
|
||||
contract := NewContract(originCaller, caller, value, gas, evm.jumpDests)
|
||||
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
||||
ret, err = evm.Run(contract, input, false)
|
||||
gas = contract.Gas
|
||||
gasUsed = contract.GasUsed
|
||||
}
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
isRevert := err == ErrExecutionReverted
|
||||
if !isRevert {
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
gas = 0
|
||||
gasUsed.RegularGasUsed += gas.RegularGas
|
||||
gas.RegularGas = 0
|
||||
}
|
||||
}
|
||||
return ret, gas, err
|
||||
return ret, gas, gasUsed, err
|
||||
}
|
||||
|
||||
// StaticCall executes the contract associated with the addr with the given input
|
||||
// as parameters while disallowing any modifications to the state during the call.
|
||||
// Opcodes that attempt to perform such modifications will result in exceptions
|
||||
// instead of performing the modifications.
|
||||
func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
|
||||
func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []byte, gas GasCosts) (ret []byte, leftOverGas GasCosts, gasUsed GasUsed, err error) {
|
||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.captureBegin(evm.depth, STATICCALL, caller, addr, input, gas, nil)
|
||||
defer func(startGas uint64) {
|
||||
defer func(startGas GasCosts) {
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
return nil, gas, GasUsed{}, ErrDepth
|
||||
}
|
||||
// We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped.
|
||||
// However, even a staticcall is considered a 'touch'. On mainnet, static calls were introduced
|
||||
// after all empty accounts were deleted, so this is not required. However, if we omit this,
|
||||
// then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json.
|
||||
// We could change this, but for now it's left for legacy reasons
|
||||
var snapshot = evm.StateDB.Snapshot()
|
||||
|
||||
// We do an AddBalance of zero here, just in order to trigger a touch.
|
||||
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
|
||||
// but is the correct thing to do and matters on other networks, in tests, and potential
|
||||
// future scenarios
|
||||
evm.StateDB.AddBalance(addr, new(uint256.Int), tracing.BalanceChangeTouchAccount)
|
||||
|
||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||
|
|
@ -461,64 +454,66 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
|||
if evm.chainRules.IsAmsterdam {
|
||||
stateDB = evm.StateDB
|
||||
}
|
||||
ret, gas, err = RunPrecompiledContract(stateDB, p, addr, input, gas, evm.Config.Tracer)
|
||||
gasBefore := gas.RegularGas
|
||||
ret, gas.RegularGas, err = RunPrecompiledContract(stateDB, p, addr, input, gas.RegularGas, evm.Config.Tracer)
|
||||
gasUsed.RegularGasUsed += gasBefore - gas.RegularGas
|
||||
} else {
|
||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||
// The contract is a scoped environment for this execution context only.
|
||||
contract := NewContract(caller, addr, new(uint256.Int), gas, evm.jumpDests)
|
||||
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
||||
|
||||
// When an error was returned by the EVM or when setting the creation code
|
||||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
||||
// when we're in Homestead this also counts for code storage gas errors.
|
||||
ret, err = evm.Run(contract, input, true)
|
||||
gas = contract.Gas
|
||||
gasUsed = contract.GasUsed
|
||||
}
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
isRevert := err == ErrExecutionReverted
|
||||
if !isRevert {
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
|
||||
gas = 0
|
||||
gasUsed.RegularGasUsed += gas.RegularGas
|
||||
gas.RegularGas = 0
|
||||
}
|
||||
}
|
||||
return ret, gas, err
|
||||
return ret, gas, gasUsed, err
|
||||
}
|
||||
|
||||
// create creates a new contract using code as deployment code.
|
||||
func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) {
|
||||
func (evm *EVM) create(caller common.Address, code []byte, gas GasCosts, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas GasCosts, gasUsed GasUsed, err error) {
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.captureBegin(evm.depth, typ, caller, address, code, gas, value.ToBig())
|
||||
defer func(startGas uint64) {
|
||||
defer func(startGas GasCosts) {
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
}
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, common.Address{}, gas, ErrDepth
|
||||
err = ErrDepth
|
||||
} else if !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
||||
err = ErrInsufficientBalance
|
||||
} else {
|
||||
nonce := evm.StateDB.GetNonce(caller)
|
||||
if nonce+1 < nonce {
|
||||
err = ErrNonceUintOverflow
|
||||
} else {
|
||||
evm.StateDB.SetNonce(caller, nonce+1, tracing.NonceChangeContractCreator)
|
||||
}
|
||||
}
|
||||
if !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
||||
return nil, common.Address{}, gas, ErrInsufficientBalance
|
||||
if err != nil {
|
||||
return nil, common.Address{}, gas, GasUsed{}, err
|
||||
}
|
||||
nonce := evm.StateDB.GetNonce(caller)
|
||||
if nonce+1 < nonce {
|
||||
return nil, common.Address{}, gas, ErrNonceUintOverflow
|
||||
}
|
||||
evm.StateDB.SetNonce(caller, nonce+1, tracing.NonceChangeContractCreator)
|
||||
|
||||
// Charge the contract creation init gas in verkle mode
|
||||
if evm.chainRules.IsEIP4762 {
|
||||
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas)
|
||||
if statelessGas > gas {
|
||||
return nil, common.Address{}, 0, ErrOutOfGas
|
||||
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas.RegularGas)
|
||||
if statelessGas > gas.RegularGas {
|
||||
return nil, common.Address{}, GasCosts{}, GasUsed{}, ErrOutOfGas
|
||||
}
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, gas-statelessGas, tracing.GasChangeWitnessContractCollisionCheck)
|
||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, gas.RegularGas-statelessGas, tracing.GasChangeWitnessContractCollisionCheck)
|
||||
}
|
||||
gas = gas - statelessGas
|
||||
gas.RegularGas = gas.RegularGas - statelessGas
|
||||
}
|
||||
|
||||
// We add this to the access list _before_ taking a snapshot. Even if the
|
||||
|
|
@ -537,14 +532,16 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
|||
(contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code
|
||||
(storageRoot != (common.Hash{}) && storageRoot != types.EmptyRootHash) { // non-empty storage
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
return nil, common.Address{}, 0, ErrContractAddressCollision
|
||||
gas.RegularGas = 0
|
||||
return nil, common.Address{}, gas, GasUsed{}, ErrContractAddressCollision
|
||||
}
|
||||
// Create a new account on the state only if the object was not present.
|
||||
// It might be possible the contract code is deployed to a pre-existent
|
||||
// account with non-zero balance.
|
||||
snapshot := evm.StateDB.Snapshot()
|
||||
|
||||
if !evm.StateDB.Exist(address) {
|
||||
evm.StateDB.CreateAccount(address)
|
||||
}
|
||||
|
|
@ -559,14 +556,14 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
|||
}
|
||||
// Charge the contract creation init gas in verkle mode
|
||||
if evm.chainRules.IsEIP4762 {
|
||||
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas)
|
||||
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas.RegularGas)
|
||||
if consumed < wanted {
|
||||
return nil, common.Address{}, 0, ErrOutOfGas
|
||||
return nil, common.Address{}, GasCosts{}, GasUsed{}, ErrOutOfGas
|
||||
}
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, gas-consumed, tracing.GasChangeWitnessContractInit)
|
||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, gas.RegularGas-consumed, tracing.GasChangeWitnessContractInit)
|
||||
}
|
||||
gas = gas - consumed
|
||||
gas.RegularGas = gas.RegularGas - consumed
|
||||
}
|
||||
evm.Context.Transfer(evm.StateDB, caller, address, value, &evm.chainRules)
|
||||
|
||||
|
|
@ -583,10 +580,11 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
|||
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
|
||||
contract.GasUsed.RegularGasUsed += contract.Gas.RegularGas
|
||||
contract.Gas.RegularGas = 0
|
||||
}
|
||||
}
|
||||
return ret, address, contract.Gas, err
|
||||
return ret, address, contract.Gas, contract.GasUsed, err
|
||||
}
|
||||
|
||||
// initNewContract runs a new contract's creation code, performs checks on the
|
||||
|
|
@ -597,29 +595,43 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
|
|||
return ret, err
|
||||
}
|
||||
|
||||
// Check whether the max code size has been exceeded, assign err if the case.
|
||||
if err := CheckMaxCodeSize(&evm.chainRules, uint64(len(ret))); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// Check prefix before gas calculation.
|
||||
// Reject code starting with 0xEF if EIP-3541 is enabled.
|
||||
if len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon {
|
||||
return ret, ErrInvalidCode
|
||||
}
|
||||
|
||||
if !evm.chainRules.IsEIP4762 {
|
||||
createDataGas := uint64(len(ret)) * params.CreateDataGas
|
||||
if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
|
||||
return ret, ErrCodeStoreOutOfGas
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
// EIP-8037: Split code deposit into state gas (code storage) and
|
||||
// regular gas (keccak256 hashing).
|
||||
stateGas := GasCosts{StateGas: uint64(len(ret)) * evm.Context.CostPerGasByte}
|
||||
if !contract.UseGas(stateGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
|
||||
return ret, ErrCodeStoreOutOfGas
|
||||
}
|
||||
regularGas := GasCosts{RegularGas: toWordSize(uint64(len(ret))) * params.Keccak256WordGas}
|
||||
if !contract.UseGas(regularGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
|
||||
return ret, ErrCodeStoreOutOfGas
|
||||
}
|
||||
} else {
|
||||
createDataGas := GasCosts{RegularGas: uint64(len(ret)) * params.CreateDataGas}
|
||||
if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
|
||||
return ret, ErrCodeStoreOutOfGas
|
||||
}
|
||||
}
|
||||
} else {
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas)
|
||||
contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas.RegularGas)
|
||||
contract.UseGas(GasCosts{RegularGas: consumed}, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||
if len(ret) > 0 && (consumed < wanted) {
|
||||
return ret, ErrCodeStoreOutOfGas
|
||||
}
|
||||
}
|
||||
|
||||
// Verify max code size after gas calculation.
|
||||
if err := CheckMaxCodeSize(&evm.chainRules, uint64(len(ret))); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
if len(ret) > 0 {
|
||||
evm.StateDB.SetCode(address, ret, tracing.CodeChangeContractCreation)
|
||||
}
|
||||
|
|
@ -627,7 +639,7 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
|
|||
}
|
||||
|
||||
// Create creates a new contract using code as deployment code.
|
||||
func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||
func (evm *EVM) Create(caller common.Address, code []byte, gas GasCosts, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas GasCosts, gasUsed GasUsed, err error) {
|
||||
contractAddr = crypto.CreateAddress(caller, evm.StateDB.GetNonce(caller))
|
||||
return evm.create(caller, code, gas, value, contractAddr, CREATE)
|
||||
}
|
||||
|
|
@ -636,7 +648,7 @@ func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *ui
|
|||
//
|
||||
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
|
||||
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
|
||||
func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||
func (evm *EVM) Create2(caller common.Address, code []byte, gas GasCosts, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas GasCosts, gasUsed GasUsed, err error) {
|
||||
inithash := crypto.Keccak256Hash(code)
|
||||
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:])
|
||||
return evm.create(caller, code, gas, endowment, contractAddr, CREATE2)
|
||||
|
|
@ -674,20 +686,20 @@ func (evm *EVM) resolveCodeHash(addr common.Address) common.Hash {
|
|||
// ChainConfig returns the environment's chain configuration
|
||||
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
|
||||
|
||||
func (evm *EVM) captureBegin(depth int, typ OpCode, from common.Address, to common.Address, input []byte, startGas uint64, value *big.Int) {
|
||||
func (evm *EVM) captureBegin(depth int, typ OpCode, from common.Address, to common.Address, input []byte, startGas GasCosts, value *big.Int) {
|
||||
tracer := evm.Config.Tracer
|
||||
if tracer.OnEnter != nil {
|
||||
tracer.OnEnter(depth, byte(typ), from, to, input, startGas, value)
|
||||
tracer.OnEnter(depth, byte(typ), from, to, input, startGas.RegularGas, value)
|
||||
}
|
||||
if tracer.OnGasChange != nil {
|
||||
tracer.OnGasChange(0, startGas, tracing.GasChangeCallInitialBalance)
|
||||
tracer.OnGasChange(0, startGas.RegularGas, tracing.GasChangeCallInitialBalance)
|
||||
}
|
||||
}
|
||||
|
||||
func (evm *EVM) captureEnd(depth int, startGas uint64, leftOverGas uint64, ret []byte, err error) {
|
||||
func (evm *EVM) captureEnd(depth int, startGas GasCosts, leftOverGas GasCosts, ret []byte, err error) {
|
||||
tracer := evm.Config.Tracer
|
||||
if leftOverGas != 0 && tracer.OnGasChange != nil {
|
||||
tracer.OnGasChange(leftOverGas, 0, tracing.GasChangeCallLeftOverReturned)
|
||||
if leftOverGas.RegularGas != 0 && tracer.OnGasChange != nil {
|
||||
tracer.OnGasChange(leftOverGas.RegularGas, 0, tracing.GasChangeCallLeftOverReturned)
|
||||
}
|
||||
var reverted bool
|
||||
if err != nil {
|
||||
|
|
@ -697,7 +709,7 @@ func (evm *EVM) captureEnd(depth int, startGas uint64, leftOverGas uint64, ret [
|
|||
reverted = false
|
||||
}
|
||||
if tracer.OnExit != nil {
|
||||
tracer.OnExit(depth, ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
|
||||
tracer.OnExit(depth, ret, startGas.RegularGas-leftOverGas.RegularGas, VMErrorFromErr(err), reverted)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,26 +64,26 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
|||
// EXTCODECOPY (stack position 3)
|
||||
// RETURNDATACOPY (stack position 2)
|
||||
func memoryCopierGas(stackpos int) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
// Gas for expanding the memory
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
// And gas for copying data, charged per word at param.CopyGas
|
||||
words, overflow := stack.Back(stackpos).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
if gas, overflow = math.SafeAdd(gas, words); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,9 +95,9 @@ var (
|
|||
gasReturnDataCopy = memoryCopierGas(2)
|
||||
)
|
||||
|
||||
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
if evm.readOnly {
|
||||
return 0, ErrWriteProtection
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
var (
|
||||
y, x = stack.Back(1), stack.Back(0)
|
||||
|
|
@ -114,12 +114,12 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
// 3. From a non-zero to a non-zero (CHANGE)
|
||||
switch {
|
||||
case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0
|
||||
return params.SstoreSetGas, nil
|
||||
return GasCosts{RegularGas: params.SstoreSetGas}, nil
|
||||
case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
|
||||
evm.StateDB.AddRefund(params.SstoreRefundGas)
|
||||
return params.SstoreClearGas, nil
|
||||
return GasCosts{RegularGas: params.SstoreClearGas}, nil
|
||||
default: // non 0 => non 0 (or 0 => 0)
|
||||
return params.SstoreResetGas, nil
|
||||
return GasCosts{RegularGas: params.SstoreResetGas}, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,16 +139,16 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
// (2.2.2.2.) Otherwise, add 4800 gas to refund counter.
|
||||
value := common.Hash(y.Bytes32())
|
||||
if current == value { // noop (1)
|
||||
return params.NetSstoreNoopGas, nil
|
||||
return GasCosts{RegularGas: params.NetSstoreNoopGas}, nil
|
||||
}
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
return params.NetSstoreInitGas, nil
|
||||
return GasCosts{RegularGas: params.NetSstoreInitGas}, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
|
||||
}
|
||||
return params.NetSstoreCleanGas, nil // write existing slot (2.1.2)
|
||||
return GasCosts{RegularGas: params.NetSstoreCleanGas}, nil // write existing slot (2.1.2)
|
||||
}
|
||||
if original != (common.Hash{}) {
|
||||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||
|
|
@ -164,7 +164,7 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
evm.StateDB.AddRefund(params.NetSstoreResetRefund)
|
||||
}
|
||||
}
|
||||
return params.NetSstoreDirtyGas, nil
|
||||
return GasCosts{RegularGas: params.NetSstoreDirtyGas}, nil
|
||||
}
|
||||
|
||||
// Here come the EIP2200 rules:
|
||||
|
|
@ -182,13 +182,13 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
// (2.2.2.) If original value equals new value (this storage slot is reset):
|
||||
// (2.2.2.1.) If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
|
||||
// (2.2.2.2.) Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
|
||||
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
if evm.readOnly {
|
||||
return 0, ErrWriteProtection
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
// If we fail the minimum gas availability invariant, fail (0)
|
||||
if contract.Gas <= params.SstoreSentryGasEIP2200 {
|
||||
return 0, errors.New("not enough gas for reentrancy sentry")
|
||||
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 {
|
||||
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
|
||||
}
|
||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
var (
|
||||
|
|
@ -198,16 +198,16 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||
value := common.Hash(y.Bytes32())
|
||||
|
||||
if current == value { // noop (1)
|
||||
return params.SloadGasEIP2200, nil
|
||||
return GasCosts{RegularGas: params.SloadGasEIP2200}, nil
|
||||
}
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
return params.SstoreSetGasEIP2200, nil
|
||||
return GasCosts{RegularGas: params.SstoreSetGasEIP2200}, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
|
||||
}
|
||||
return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
||||
return GasCosts{RegularGas: params.SstoreResetGasEIP2200}, nil // write existing slot (2.1.2)
|
||||
}
|
||||
if original != (common.Hash{}) {
|
||||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||
|
|
@ -223,62 +223,66 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||
evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
|
||||
}
|
||||
}
|
||||
return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||
return GasCosts{RegularGas: params.SloadGasEIP2200}, nil // dirty update (2.2)
|
||||
}
|
||||
|
||||
func makeGasLog(n uint64) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
|
||||
if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
var memorySizeGas uint64
|
||||
if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
wordGas, overflow := stack.Back(1).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
// pureMemoryGascost is used by several operations, which aside from their
|
||||
// static cost have a dynamic cost which is solely based on the memory
|
||||
// expansion
|
||||
func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return memoryGasCost(mem, memorySize)
|
||||
func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -290,64 +294,65 @@ var (
|
|||
gasCreate = pureMemoryGascost
|
||||
)
|
||||
|
||||
func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
wordGas, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
// Since size <= the protocol-defined maximum initcode size limit, these multiplication cannot overflow
|
||||
moreGas := params.InitCodeWordGas * ((size + 31) / 32)
|
||||
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
|
||||
func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
// Since size <= the protocol-defined maximum initcode size limit, these multiplication cannot overflow
|
||||
moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32)
|
||||
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
|
||||
var (
|
||||
|
|
@ -355,12 +360,12 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
|
|||
overflow bool
|
||||
)
|
||||
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
|
||||
var (
|
||||
|
|
@ -368,9 +373,9 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
|
|||
overflow bool
|
||||
)
|
||||
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -380,21 +385,21 @@ var (
|
|||
gasStaticCall = makeCallVariantGasCost(gasStaticCallIntrinsic)
|
||||
)
|
||||
|
||||
func makeCallVariantGasCost(intrinsicFunc gasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func makeCallVariantGasCost(intrinsicFunc intrinsicGasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
intrinsic, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, intrinsic, stack.Back(0))
|
||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsic, stack.Back(0))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
gas, overflow := math.SafeAdd(intrinsic, evm.callGasTemp)
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -422,7 +427,7 @@ func gasCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||
}
|
||||
// Terminate the gas measurement if the leftover gas is not sufficient,
|
||||
// it can effectively prevent accessing the states in the following steps.
|
||||
if contract.Gas < gas {
|
||||
if contract.Gas.RegularGas < gas {
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
// Stateful check
|
||||
|
|
@ -440,6 +445,32 @@ func gasCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||
return gas, nil
|
||||
}
|
||||
|
||||
// gasCallIntrinsic8037 is the intrinsic gas calculator for CALL in Amsterdam.
|
||||
// It computes memory expansion + value transfer gas but excludes new account
|
||||
// creation, which is handled as state gas by the wrapper.
|
||||
func gasCallIntrinsic8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var (
|
||||
gas uint64
|
||||
transfersValue = !stack.Back(2).IsZero()
|
||||
)
|
||||
if evm.readOnly && transfersValue {
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
memoryGas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var transferGas uint64
|
||||
if transfersValue && !evm.chainRules.IsEIP4762 {
|
||||
transferGas = params.CallValueTransferGas
|
||||
}
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(memoryGas, transferGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasCallCodeIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
memoryGas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
|
|
@ -466,11 +497,10 @@ func gasStaticCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
|||
return memoryGasCost(mem, memorySize)
|
||||
}
|
||||
|
||||
func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
if evm.readOnly {
|
||||
return 0, ErrWriteProtection
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
|
||||
var gas uint64
|
||||
// EIP150 homestead gas reprice fork:
|
||||
if evm.chainRules.IsEIP150 {
|
||||
|
|
@ -490,5 +520,166 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
if !evm.StateDB.HasSelfDestructed(contract.Address()) {
|
||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
|
||||
func gasCreateEip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
// Cap word gas at MaxInitCodeSizeAmsterdam to avoid overflow.
|
||||
// The actual init code size check happens in create() for graceful failure.
|
||||
wordSize := min(size, params.MaxInitCodeSizeAmsterdam)
|
||||
words := (wordSize + 31) / 32
|
||||
// Account creation is a fixed state gas cost, not proportional to init code size.
|
||||
// Code storage state gas is charged separately in initNewContract.
|
||||
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte
|
||||
// CREATE uses InitCodeWordGas (EIP-3860); Keccak256WordGas is only for CREATE2.
|
||||
wordGas := params.InitCodeWordGas * words
|
||||
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil
|
||||
}
|
||||
|
||||
func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
// Cap word gas at MaxInitCodeSizeAmsterdam to avoid overflow.
|
||||
// The actual init code size check happens in create() for graceful failure.
|
||||
wordSize := min(size, params.MaxInitCodeSizeAmsterdam)
|
||||
words := (wordSize + 31) / 32
|
||||
// Account creation is a fixed state gas cost, not proportional to init code size.
|
||||
// Code storage state gas is charged separately in initNewContract.
|
||||
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte
|
||||
// CREATE2 charges both InitCodeWordGas (EIP-3860) and Keccak256WordGas (for address hashing).
|
||||
wordGas := (params.InitCodeWordGas + params.Keccak256WordGas) * words
|
||||
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil
|
||||
}
|
||||
|
||||
// gasCall8037 is the stateful gas calculator for CALL in Amsterdam (EIP-8037).
|
||||
// It only returns the state-dependent gas (account creation as state gas).
|
||||
// Memory gas, transfer gas, and callGas are handled by gasCallStateless and
|
||||
// makeCallVariantGasCall.
|
||||
func gasCall8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
var (
|
||||
gas GasCosts
|
||||
transfersValue = !stack.Back(2).IsZero()
|
||||
address = common.Address(stack.Back(1).Bytes20())
|
||||
)
|
||||
if evm.chainRules.IsEIP158 {
|
||||
if transfersValue && evm.StateDB.Empty(address) {
|
||||
gas.StateGas += params.AccountCreationSize * evm.Context.CostPerGasByte
|
||||
}
|
||||
} else if !evm.StateDB.Exist(address) {
|
||||
gas.StateGas += params.AccountCreationSize * evm.Context.CostPerGasByte
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasSelfdestruct8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
var (
|
||||
gas GasCosts
|
||||
address = common.Address(stack.peek().Bytes20())
|
||||
)
|
||||
if !evm.StateDB.AddressInAccessList(address) {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddAddressToAccessList(address)
|
||||
gas.RegularGas = params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
// Check we have enough regular gas before we add the address to the BAL
|
||||
if contract.Gas.RegularGas < gas.RegularGas {
|
||||
return gas, nil
|
||||
}
|
||||
// if empty and transfers value
|
||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||||
gas.StateGas += params.AccountCreationSize * evm.Context.CostPerGasByte
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
// If we fail the minimum gas availability invariant, fail (0)
|
||||
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 {
|
||||
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
|
||||
}
|
||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
var (
|
||||
y, x = stack.Back(1), stack.peek()
|
||||
slot = common.Hash(x.Bytes32())
|
||||
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
|
||||
cost GasCosts
|
||||
)
|
||||
// Check slot presence in the access list
|
||||
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||
cost = GasCosts{RegularGas: params.ColdSloadCostEIP2929}
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||
}
|
||||
value := common.Hash(y.Bytes32())
|
||||
|
||||
if current == value { // noop (1)
|
||||
// EIP 2200 original clause:
|
||||
// return params.SloadGasEIP2200, nil
|
||||
return GasCosts{RegularGas: cost.RegularGas + params.WarmStorageReadCostEIP2929}, nil // SLOAD_GAS
|
||||
}
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
// EIP-8037: Charge state gas first (before regular gas), matching the
|
||||
// spec's charge_state_gas → charge_gas ordering. This ensures that
|
||||
// state_gas_used is recorded even if the subsequent regular gas charge
|
||||
// fails with OOG.
|
||||
stateGas := GasCosts{StateGas: params.StorageCreationSize * evm.Context.CostPerGasByte}
|
||||
if contract.Gas.Underflow(stateGas) {
|
||||
return GasCosts{}, errors.New("out of gas for state gas")
|
||||
}
|
||||
contract.GasUsed.Add(stateGas)
|
||||
contract.Gas.Sub(stateGas)
|
||||
return GasCosts{RegularGas: cost.RegularGas + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929}, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP3529)
|
||||
}
|
||||
// EIP-2200 original clause:
|
||||
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
||||
return GasCosts{RegularGas: cost.RegularGas + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929}, nil // write existing slot (2.1.2)
|
||||
}
|
||||
if original != (common.Hash{}) {
|
||||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||
evm.StateDB.SubRefund(params.SstoreClearsScheduleRefundEIP3529)
|
||||
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
|
||||
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP3529)
|
||||
}
|
||||
}
|
||||
if original == value {
|
||||
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
|
||||
// EIP 2200 Original clause:
|
||||
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
|
||||
evm.StateDB.AddRefund(params.StorageCreationSize*evm.Context.CostPerGasByte + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929 - params.WarmStorageReadCostEIP2929)
|
||||
} else { // reset to original existing slot (2.2.2.2)
|
||||
// EIP 2200 Original clause:
|
||||
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
|
||||
// - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
|
||||
// - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
|
||||
// Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
|
||||
evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
|
||||
}
|
||||
}
|
||||
// EIP-2200 original clause:
|
||||
//return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||
return GasCosts{RegularGas: cost.RegularGas + params.WarmStorageReadCostEIP2929}, nil // dirty update (2.2)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,11 +98,11 @@ func TestEIP2200(t *testing.T) {
|
|||
}
|
||||
evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
|
||||
|
||||
_, gas, err := evm.Call(common.Address{}, address, nil, tt.gaspool, new(uint256.Int))
|
||||
_, gas, _, err := evm.Call(common.Address{}, address, nil, GasCosts{RegularGas: tt.gaspool}, new(uint256.Int))
|
||||
if !errors.Is(err, tt.failure) {
|
||||
t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
|
||||
}
|
||||
if used := tt.gaspool - gas; used != tt.used {
|
||||
if used := tt.gaspool - gas.RegularGas; used != tt.used {
|
||||
t.Errorf("test %d: gas used mismatch: have %v, want %v", i, used, tt.used)
|
||||
}
|
||||
if refund := evm.StateDB.GetRefund(); refund != tt.refund {
|
||||
|
|
@ -158,11 +158,11 @@ func TestCreateGas(t *testing.T) {
|
|||
|
||||
evm := NewEVM(vmctx, statedb, chainConfig, config)
|
||||
var startGas = uint64(testGas)
|
||||
ret, gas, err := evm.Call(common.Address{}, address, nil, startGas, new(uint256.Int))
|
||||
ret, gas, _, err := evm.Call(common.Address{}, address, nil, GasCosts{RegularGas: startGas}, new(uint256.Int))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
gasUsed = startGas - gas
|
||||
gasUsed = startGas - gas.RegularGas
|
||||
if len(ret) != 32 {
|
||||
t.Fatalf("test %d: expected 32 bytes returned, have %d", i, len(ret))
|
||||
}
|
||||
|
|
|
|||
67
core/vm/gascosts.go
Normal file
67
core/vm/gascosts.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package vm
|
||||
|
||||
import "fmt"
|
||||
|
||||
type GasCosts struct {
|
||||
RegularGas uint64
|
||||
StateGas uint64
|
||||
}
|
||||
|
||||
// GasUsed tracks how much gas has been consumed during execution.
|
||||
type GasUsed struct {
|
||||
RegularGasUsed uint64
|
||||
StateGasCharged uint64
|
||||
}
|
||||
|
||||
// Add increments gas used counters based on a GasCosts charge.
|
||||
func (g *GasUsed) Add(cost GasCosts) {
|
||||
g.RegularGasUsed += cost.RegularGas
|
||||
g.StateGasCharged += cost.StateGas
|
||||
}
|
||||
|
||||
func (g GasCosts) Max() uint64 {
|
||||
return max(g.RegularGas, g.StateGas)
|
||||
}
|
||||
|
||||
func (g GasCosts) Sum() uint64 {
|
||||
return g.RegularGas + g.StateGas
|
||||
}
|
||||
|
||||
// Underflow returns true if the operation would underflow.
|
||||
// When state gas exceeds the reservoir, the excess spills to regular gas.
|
||||
// The check accounts for regular gas already consumed by b.RegularGas.
|
||||
func (g GasCosts) Underflow(b GasCosts) bool {
|
||||
if b.RegularGas > g.RegularGas {
|
||||
return true
|
||||
}
|
||||
if b.StateGas > g.StateGas {
|
||||
spillover := b.StateGas - g.StateGas
|
||||
remainingRegular := g.RegularGas - b.RegularGas
|
||||
if spillover > remainingRegular {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Sub doesn't check for underflows
|
||||
func (g *GasCosts) Sub(b GasCosts) {
|
||||
g.RegularGas -= b.RegularGas
|
||||
if b.StateGas > g.StateGas {
|
||||
diff := b.StateGas - g.StateGas
|
||||
g.StateGas = 0
|
||||
g.RegularGas -= diff
|
||||
} else {
|
||||
g.StateGas -= b.StateGas
|
||||
}
|
||||
}
|
||||
|
||||
// Add doesn't check for overflows
|
||||
func (g *GasCosts) Add(b GasCosts) {
|
||||
g.RegularGas += b.RegularGas
|
||||
g.StateGas += b.StateGas
|
||||
}
|
||||
|
||||
func (g GasCosts) String() string {
|
||||
return fmt.Sprintf("<%v,%v>", g.RegularGas, g.StateGas)
|
||||
}
|
||||
|
|
@ -516,9 +516,6 @@ func opSload(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
}
|
||||
|
||||
func opSstore(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||
if evm.readOnly {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
loc := scope.Stack.pop()
|
||||
val := scope.Stack.pop()
|
||||
evm.StateDB.SetState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32())
|
||||
|
|
@ -566,7 +563,7 @@ func opMsize(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
}
|
||||
|
||||
func opGas(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(scope.Contract.Gas))
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(scope.Contract.Gas.RegularGas))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -661,15 +658,25 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
gas = scope.Contract.Gas
|
||||
)
|
||||
if evm.chainRules.IsEIP150 {
|
||||
gas -= gas / 64
|
||||
gas.RegularGas -= gas.RegularGas / 64
|
||||
}
|
||||
|
||||
// EIP-7954: check init code size after gas is charged (by the gas function)
|
||||
// but before execution. This aborts the caller's execution, ensuring all
|
||||
// regular gas is consumed while the state gas spill is tracked.
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
if err := CheckMaxInitCodeSize(&evm.chainRules, uint64(len(input))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// reuse size int for stackvalue
|
||||
stackvalue := size
|
||||
|
||||
scope.Contract.UseGas(gas, evm.Config.Tracer, tracing.GasChangeCallContractCreation)
|
||||
scope.Contract.UseGas(GasCosts{RegularGas: gas.RegularGas}, evm.Config.Tracer, tracing.GasChangeCallContractCreation)
|
||||
scope.Contract.GasUsed.RegularGasUsed -= gas.RegularGas // escrow: undo parent's RGU for forwarded gas
|
||||
|
||||
res, addr, returnGas, suberr := evm.Create(scope.Contract.Address(), input, gas, &value)
|
||||
res, addr, returnGas, childGasUsed, suberr := evm.Create(scope.Contract.Address(), input, GasCosts{RegularGas: gas.RegularGas, StateGas: scope.Contract.Gas.StateGas}, &value)
|
||||
// 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
|
||||
|
|
@ -683,7 +690,7 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
}
|
||||
scope.Stack.push(&stackvalue)
|
||||
|
||||
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
scope.Contract.RefundGas(suberr, returnGas, childGasUsed, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
if suberr == ErrExecutionReverted {
|
||||
evm.returnData = res // set REVERT data to return data buffer
|
||||
|
|
@ -706,11 +713,22 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
)
|
||||
|
||||
// Apply EIP150
|
||||
gas -= gas / 64
|
||||
scope.Contract.UseGas(gas, evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
|
||||
gas.RegularGas -= gas.RegularGas / 64
|
||||
|
||||
// EIP-7954: check init code size after gas is charged (by the gas function)
|
||||
// but before execution. This aborts the caller's execution, ensuring all
|
||||
// regular gas is consumed while the state gas spill is tracked.
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
if err := CheckMaxInitCodeSize(&evm.chainRules, uint64(len(input))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
scope.Contract.UseGas(GasCosts{RegularGas: gas.RegularGas}, evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
|
||||
scope.Contract.GasUsed.RegularGasUsed -= gas.RegularGas // escrow: undo parent's RGU for forwarded gas
|
||||
// reuse size int for stackvalue
|
||||
stackvalue := size
|
||||
res, addr, returnGas, suberr := evm.Create2(scope.Contract.Address(), input, gas,
|
||||
res, addr, returnGas, childGasUsed, suberr := evm.Create2(scope.Contract.Address(), input, GasCosts{RegularGas: gas.RegularGas, StateGas: scope.Contract.Gas.StateGas},
|
||||
&endowment, &salt)
|
||||
// Push item on the stack based on the returned error.
|
||||
if suberr != nil {
|
||||
|
|
@ -719,7 +737,8 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
stackvalue.SetBytes(addr.Bytes())
|
||||
}
|
||||
scope.Stack.push(&stackvalue)
|
||||
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
scope.Contract.RefundGas(suberr, returnGas, childGasUsed, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
if suberr == ErrExecutionReverted {
|
||||
evm.returnData = res // set REVERT data to return data buffer
|
||||
|
|
@ -747,7 +766,8 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
if !value.IsZero() {
|
||||
gas += params.CallStipend
|
||||
}
|
||||
ret, returnGas, err := evm.Call(scope.Contract.Address(), toAddr, args, gas, &value)
|
||||
scope.Contract.GasUsed.RegularGasUsed -= gas // escrow: undo parent's RGU for forwarded gas
|
||||
ret, returnGas, childGasUsed, err := evm.Call(scope.Contract.Address(), toAddr, args, GasCosts{RegularGas: gas, StateGas: scope.Contract.Gas.StateGas}, &value)
|
||||
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
|
|
@ -759,7 +779,7 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
|
||||
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
scope.Contract.RefundGas(err, returnGas, childGasUsed, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
evm.returnData = ret
|
||||
return ret, nil
|
||||
|
|
@ -781,7 +801,8 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
gas += params.CallStipend
|
||||
}
|
||||
|
||||
ret, returnGas, err := evm.CallCode(scope.Contract.Address(), toAddr, args, gas, &value)
|
||||
scope.Contract.GasUsed.RegularGasUsed -= gas // escrow
|
||||
ret, returnGas, childGasUsed, err := evm.CallCode(scope.Contract.Address(), toAddr, args, GasCosts{RegularGas: gas, StateGas: scope.Contract.Gas.StateGas}, &value)
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -792,7 +813,7 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
|
||||
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
scope.Contract.RefundGas(err, returnGas, childGasUsed, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
evm.returnData = ret
|
||||
return ret, nil
|
||||
|
|
@ -810,7 +831,8 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// Get arguments from the memory.
|
||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
|
||||
ret, returnGas, err := evm.DelegateCall(scope.Contract.Caller(), scope.Contract.Address(), toAddr, args, gas, scope.Contract.value)
|
||||
scope.Contract.GasUsed.RegularGasUsed -= gas // escrow
|
||||
ret, returnGas, childGasUsed, err := evm.DelegateCall(scope.Contract.Caller(), scope.Contract.Address(), toAddr, args, GasCosts{RegularGas: gas, StateGas: scope.Contract.Gas.StateGas}, scope.Contract.value)
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -821,7 +843,7 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
|
||||
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
scope.Contract.RefundGas(err, returnGas, childGasUsed, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
evm.returnData = ret
|
||||
return ret, nil
|
||||
|
|
@ -839,7 +861,8 @@ func opStaticCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// Get arguments from the memory.
|
||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
|
||||
ret, returnGas, err := evm.StaticCall(scope.Contract.Address(), toAddr, args, gas)
|
||||
scope.Contract.GasUsed.RegularGasUsed -= gas // escrow
|
||||
ret, returnGas, childGasUsed, err := evm.StaticCall(scope.Contract.Address(), toAddr, args, GasCosts{RegularGas: gas, StateGas: scope.Contract.Gas.StateGas})
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -850,7 +873,7 @@ func opStaticCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
|
||||
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
scope.Contract.RefundGas(err, returnGas, childGasUsed, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
evm.returnData = ret
|
||||
return ret, nil
|
||||
|
|
|
|||
|
|
@ -565,7 +565,7 @@ func TestOpTstore(t *testing.T) {
|
|||
mem = NewMemory()
|
||||
caller = common.Address{}
|
||||
to = common.Address{1}
|
||||
contract = NewContract(caller, to, new(uint256.Int), 0, nil)
|
||||
contract = NewContract(caller, to, new(uint256.Int), GasCosts{RegularGas: 0, StateGas: 0}, nil)
|
||||
scopeContext = ScopeContext{mem, stack, contract}
|
||||
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
|
||||
)
|
||||
|
|
@ -879,7 +879,7 @@ func TestOpMCopy(t *testing.T) {
|
|||
if dynamicCost, err := gasMcopy(evm, nil, stack, mem, memorySize); err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
haveGas = GasFastestStep + dynamicCost
|
||||
haveGas = GasFastestStep + dynamicCost.RegularGas
|
||||
}
|
||||
// Expand mem
|
||||
if memorySize > 0 {
|
||||
|
|
|
|||
|
|
@ -166,15 +166,15 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
|||
for {
|
||||
if debug {
|
||||
// Capture pre-execution values for tracing.
|
||||
logged, pcCopy, gasCopy = false, pc, contract.Gas
|
||||
logged, pcCopy, gasCopy = false, pc, contract.Gas.RegularGas
|
||||
}
|
||||
|
||||
if isEIP4762 && !contract.IsDeployment && !contract.IsSystemCall {
|
||||
// if the PC ends up in a new "chunk" of verkleized code, charge the
|
||||
// associated costs.
|
||||
contractAddr := contract.Address()
|
||||
consumed, wanted := evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas)
|
||||
contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||
consumed, wanted := evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas.RegularGas)
|
||||
contract.UseGas(GasCosts{RegularGas: consumed}, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||
if consumed < wanted {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
|
@ -192,10 +192,11 @@ 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 < cost {
|
||||
if contract.Gas.RegularGas < cost {
|
||||
return nil, ErrOutOfGas
|
||||
} else {
|
||||
contract.Gas -= cost
|
||||
contract.Gas.RegularGas -= cost
|
||||
contract.GasUsed.RegularGasUsed += cost // EIP-8037: track constant gas
|
||||
}
|
||||
|
||||
// All ops with a dynamic memory usage also has a dynamic gas cost.
|
||||
|
|
@ -218,17 +219,18 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
|||
}
|
||||
// Consume the gas and return an error if not enough gas is available.
|
||||
// cost is explicitly set so that the capture state defer method can get the proper cost
|
||||
var dynamicCost uint64
|
||||
var dynamicCost GasCosts
|
||||
dynamicCost, err = operation.dynamicGas(evm, contract, stack, mem, memorySize)
|
||||
cost += dynamicCost // for tracing
|
||||
cost += dynamicCost.RegularGas // for tracing
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err)
|
||||
}
|
||||
// for tracing: this gas consumption event is emitted below in the debug section.
|
||||
if contract.Gas < dynamicCost {
|
||||
if contract.Gas.Underflow(dynamicCost) {
|
||||
return nil, ErrOutOfGas
|
||||
} else {
|
||||
contract.Gas -= dynamicCost
|
||||
contract.GasUsed.Add(dynamicCost)
|
||||
contract.Gas.Sub(dynamicCost)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ func TestLoopInterrupt(t *testing.T) {
|
|||
timeout := make(chan bool)
|
||||
|
||||
go func(evm *EVM) {
|
||||
_, _, err := evm.Call(common.Address{}, address, nil, math.MaxUint64, new(uint256.Int))
|
||||
_, _, _, err := evm.Call(common.Address{}, address, nil, GasCosts{RegularGas: math.MaxUint64}, new(uint256.Int))
|
||||
errChannel <- err
|
||||
}(evm)
|
||||
|
||||
|
|
@ -85,7 +85,7 @@ func BenchmarkInterpreter(b *testing.B) {
|
|||
value = uint256.NewInt(0)
|
||||
stack = newstack()
|
||||
mem = NewMemory()
|
||||
contract = NewContract(common.Address{}, common.Address{}, value, startGas, nil)
|
||||
contract = NewContract(common.Address{}, common.Address{}, value, GasCosts{RegularGas: startGas, StateGas: 0}, nil)
|
||||
)
|
||||
stack.push(uint256.NewInt(123))
|
||||
stack.push(uint256.NewInt(123))
|
||||
|
|
|
|||
|
|
@ -23,8 +23,9 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
executionFunc func(pc *uint64, evm *EVM, callContext *ScopeContext) ([]byte, error)
|
||||
gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
|
||||
executionFunc func(pc *uint64, evm *EVM, callContext *ScopeContext) ([]byte, error)
|
||||
gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (GasCosts, error) // last parameter is the requested memory size as a uint64
|
||||
intrinsicGasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
|
||||
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
|
||||
memorySizeFunc func(*Stack) (size uint64, overflow bool)
|
||||
)
|
||||
|
|
@ -97,6 +98,7 @@ func newAmsterdamInstructionSet() JumpTable {
|
|||
instructionSet := newOsakaInstructionSet()
|
||||
enable7843(&instructionSet) // EIP-7843 (SLOTNUM opcode)
|
||||
enable8024(&instructionSet) // EIP-8024 (Backward compatible SWAPN, DUPN, EXCHANGE)
|
||||
enable8037(&instructionSet) // EIP-8037 (State creation gas cost increase)
|
||||
return validate(instructionSet)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,24 +27,24 @@ import (
|
|||
)
|
||||
|
||||
func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
if evm.readOnly {
|
||||
return 0, ErrWriteProtection
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
// If we fail the minimum gas availability invariant, fail (0)
|
||||
if contract.Gas <= params.SstoreSentryGasEIP2200 {
|
||||
return 0, errors.New("not enough gas for reentrancy sentry")
|
||||
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 {
|
||||
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
|
||||
}
|
||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
var (
|
||||
y, x = stack.Back(1), stack.peek()
|
||||
slot = common.Hash(x.Bytes32())
|
||||
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
|
||||
cost = uint64(0)
|
||||
cost = GasCosts{RegularGas: 0}
|
||||
)
|
||||
// Check slot presence in the access list
|
||||
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||
cost = params.ColdSloadCostEIP2929
|
||||
cost = GasCosts{RegularGas: params.ColdSloadCostEIP2929}
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||
}
|
||||
|
|
@ -53,18 +53,18 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
|||
if current == value { // noop (1)
|
||||
// EIP 2200 original clause:
|
||||
// return params.SloadGasEIP2200, nil
|
||||
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
|
||||
return GasCosts{RegularGas: cost.RegularGas + params.WarmStorageReadCostEIP2929}, nil // SLOAD_GAS
|
||||
}
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
return cost + params.SstoreSetGasEIP2200, nil
|
||||
return GasCosts{RegularGas: cost.RegularGas + params.SstoreSetGasEIP2200}, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(clearingRefund)
|
||||
}
|
||||
// EIP-2200 original clause:
|
||||
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
||||
return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
|
||||
return GasCosts{RegularGas: cost.RegularGas + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929)}, nil // write existing slot (2.1.2)
|
||||
}
|
||||
if original != (common.Hash{}) {
|
||||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||
|
|
@ -89,7 +89,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
|||
}
|
||||
// EIP-2200 original clause:
|
||||
//return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||
return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
|
||||
return GasCosts{RegularGas: cost.RegularGas + params.WarmStorageReadCostEIP2929}, nil // dirty update (2.2)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
|||
// whose storage is being read) is not yet in accessed_storage_keys,
|
||||
// charge 2100 gas and add the pair to accessed_storage_keys.
|
||||
// If the pair is already in accessed_storage_keys, charge 100 gas.
|
||||
func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
loc := stack.peek()
|
||||
slot := common.Hash(loc.Bytes32())
|
||||
// Check slot presence in the access list
|
||||
|
|
@ -106,9 +106,9 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
// If he does afford it, we can skip checking the same thing later on, during execution
|
||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||
return params.ColdSloadCostEIP2929, nil
|
||||
return GasCosts{RegularGas: params.ColdSloadCostEIP2929}, nil
|
||||
}
|
||||
return params.WarmStorageReadCostEIP2929, nil
|
||||
return GasCosts{RegularGas: params.WarmStorageReadCostEIP2929}, nil
|
||||
}
|
||||
|
||||
// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
|
||||
|
|
@ -116,11 +116,11 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
// > If the target is not in accessed_addresses,
|
||||
// > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses.
|
||||
// > Otherwise, charge WARM_STORAGE_READ_COST gas.
|
||||
func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
// memory expansion first (dynamic part of pre-2929 implementation)
|
||||
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
// Check slot presence in the access list
|
||||
|
|
@ -128,8 +128,8 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
|||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
var overflow bool
|
||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||||
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
|
@ -143,20 +143,20 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
|||
// - extcodehash,
|
||||
// - extcodesize,
|
||||
// - (ext) balance
|
||||
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
// Check slot presence in the access list
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// The warm storage read cost is already charged as constantGas
|
||||
return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil
|
||||
return GasCosts{RegularGas: params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929}, nil
|
||||
}
|
||||
return 0, nil
|
||||
return GasCosts{RegularGas: 0}, nil
|
||||
}
|
||||
|
||||
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
addr := common.Address(stack.Back(addressPosition).Bytes20())
|
||||
// Check slot presence in the access list
|
||||
warmAccess := evm.StateDB.AddressInAccessList(addr)
|
||||
|
|
@ -167,8 +167,8 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
|
|||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// Charge the remaining difference here already, to correctly calculate available
|
||||
// gas for call
|
||||
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return 0, ErrOutOfGas
|
||||
if !contract.UseGas(GasCosts{RegularGas: coldCost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
// Now call the old calculator, which takes into account
|
||||
|
|
@ -184,11 +184,11 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
|
|||
// add it to the returned gas. By adding it to the return, it will be charged
|
||||
// outside of this function, as part of the dynamic gas, and that will make it
|
||||
// also become correctly reported to tracers.
|
||||
contract.Gas += coldCost
|
||||
contract.Gas.RegularGas += coldCost
|
||||
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, coldCost); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
|
@ -224,13 +224,13 @@ var (
|
|||
|
||||
// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-3529
|
||||
func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
||||
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
var (
|
||||
gas uint64
|
||||
address = common.Address(stack.peek().Bytes20())
|
||||
)
|
||||
if evm.readOnly {
|
||||
return 0, ErrWriteProtection
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
if !evm.StateDB.AddressInAccessList(address) {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
|
|
@ -239,8 +239,8 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
|||
|
||||
// Terminate the gas measurement if the leftover gas is not sufficient,
|
||||
// it can effectively prevent accessing the states in the following steps
|
||||
if contract.Gas < gas {
|
||||
return 0, ErrOutOfGas
|
||||
if contract.Gas.RegularGas < gas {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
// if empty and transfers value
|
||||
|
|
@ -250,7 +250,7 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
|||
if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) {
|
||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
}
|
||||
return gasFunc
|
||||
}
|
||||
|
|
@ -260,22 +260,34 @@ var (
|
|||
gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCallIntrinsic)
|
||||
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCallIntrinsic)
|
||||
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCodeIntrinsic)
|
||||
|
||||
innerGasCallEIP8037 = makeCallVariantGasCallEIP8037(gasCallIntrinsic8037, gasCall8037)
|
||||
)
|
||||
|
||||
func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
// Return early if this call attempts to transfer value in a static context.
|
||||
// Although it's checked in `gasCall`, EIP-7702 loads the target's code before
|
||||
// to determine if it is resolving a delegation. This could incorrectly record
|
||||
// the target in the block access list (BAL) if the call later fails.
|
||||
transfersValue := !stack.Back(2).IsZero()
|
||||
if evm.readOnly && transfersValue {
|
||||
return 0, ErrWriteProtection
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
return innerGasCallEIP7702(evm, contract, stack, mem, memorySize)
|
||||
}
|
||||
|
||||
func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasCallEIP8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
// Same write-protection guard as gasCallEIP7702: check before any gas
|
||||
// charging to avoid incorrectly recording in the access list.
|
||||
transfersValue := !stack.Back(2).IsZero()
|
||||
if evm.readOnly && transfersValue {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
return innerGasCallEIP8037(evm, contract, stack, mem, memorySize)
|
||||
}
|
||||
|
||||
func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
var (
|
||||
eip2929Cost uint64
|
||||
eip7702Cost uint64
|
||||
|
|
@ -293,8 +305,8 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
|
|||
|
||||
// Charge the remaining difference here already, to correctly calculate
|
||||
// available gas for call
|
||||
if !contract.UseGas(eip2929Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return 0, ErrOutOfGas
|
||||
if !contract.UseGas(GasCosts{RegularGas: eip2929Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -305,13 +317,13 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
|
|||
// - create new account
|
||||
intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
// Terminate the gas measurement if the leftover gas is not sufficient,
|
||||
// it can effectively prevent accessing the states in the following steps.
|
||||
// It's an essential safeguard before any stateful check.
|
||||
if contract.Gas < intrinsicCost {
|
||||
return 0, ErrOutOfGas
|
||||
if contract.Gas.RegularGas < intrinsicCost {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
|
||||
// Check if code is a delegation and if so, charge for resolution.
|
||||
|
|
@ -322,21 +334,21 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
|
|||
evm.StateDB.AddAddressToAccessList(target)
|
||||
eip7702Cost = params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
if !contract.UseGas(eip7702Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return 0, ErrOutOfGas
|
||||
if !contract.UseGas(GasCosts{RegularGas: eip7702Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
// Calculate the gas budget for the nested call. The costs defined by
|
||||
// EIP-2929 and EIP-7702 have already been applied.
|
||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, intrinsicCost, stack.Back(0))
|
||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsicCost, stack.Back(0))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
// Temporarily add the gas charge back to the contract and return value. By
|
||||
// adding it to the return, it will be charged outside of this function, as
|
||||
// part of the dynamic gas. This will ensure it is correctly reported to
|
||||
// tracers.
|
||||
contract.Gas += eip2929Cost + eip7702Cost
|
||||
contract.Gas.RegularGas += eip2929Cost + eip7702Cost
|
||||
|
||||
// Aggregate the gas costs from all components, including EIP-2929, EIP-7702,
|
||||
// the CALL opcode itself, and the cost incurred by nested calls.
|
||||
|
|
@ -345,14 +357,104 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
|
|||
totalCost uint64
|
||||
)
|
||||
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return totalCost, nil
|
||||
return GasCosts{RegularGas: totalCost}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// makeCallVariantGasCallEIP8037 creates a call gas function for Amsterdam (EIP-8037).
|
||||
// It extends the EIP-7702 pattern with state gas handling and GasUsed tracking.
|
||||
// intrinsicFunc computes the regular gas (memory + transfer, no new account creation).
|
||||
// stateGasFunc computes the state gas (new account creation as state gas).
|
||||
func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc gasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
var (
|
||||
eip2929Cost uint64
|
||||
eip7702Cost uint64
|
||||
addr = common.Address(stack.Back(1).Bytes20())
|
||||
)
|
||||
// EIP-2929 cold access check.
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
eip2929Cost = params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||
if !contract.UseGas(GasCosts{RegularGas: eip2929Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
|
||||
// Compute intrinsic cost (memory + transfer, no new account creation).
|
||||
intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
// Early OOG check before stateful operations.
|
||||
if contract.Gas.RegularGas < intrinsicCost {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
|
||||
// Compute state gas (new account creation as state gas).
|
||||
stateGas, err := stateGasFunc(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
|
||||
// EIP-7702 delegation check.
|
||||
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
|
||||
if evm.StateDB.AddressInAccessList(target) {
|
||||
eip7702Cost = params.WarmStorageReadCostEIP2929
|
||||
} else {
|
||||
evm.StateDB.AddAddressToAccessList(target)
|
||||
eip7702Cost = params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
if !contract.UseGas(GasCosts{RegularGas: eip7702Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
|
||||
// Charge state gas directly before callGas computation. State gas that
|
||||
// spills to regular gas must reduce the gas available for callGasTemp.
|
||||
if stateGas.StateGas > 0 {
|
||||
stateGasCost := GasCosts{StateGas: stateGas.StateGas}
|
||||
if contract.Gas.Underflow(stateGasCost) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
contract.GasUsed.Add(stateGasCost)
|
||||
contract.Gas.Sub(stateGasCost)
|
||||
}
|
||||
|
||||
// Calculate the gas budget for the nested call (63/64 rule).
|
||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsicCost, stack.Back(0))
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
|
||||
// Temporarily add gas charges back for tracer reporting.
|
||||
contract.Gas.RegularGas += eip2929Cost + eip7702Cost
|
||||
// Undo GasUsed increments from direct UseGas charges.
|
||||
contract.GasUsed.RegularGasUsed -= eip2929Cost
|
||||
contract.GasUsed.RegularGasUsed -= eip7702Cost
|
||||
|
||||
// Aggregate total cost.
|
||||
var (
|
||||
overflow bool
|
||||
totalCost uint64
|
||||
)
|
||||
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: totalCost}, nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,40 +24,40 @@ import (
|
|||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas, true), nil
|
||||
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
return GasCosts{RegularGas: evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas.RegularGas, true)}, nil
|
||||
}
|
||||
|
||||
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas, true), nil
|
||||
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
return GasCosts{RegularGas: evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas.RegularGas, true)}, nil
|
||||
}
|
||||
|
||||
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
address := stack.peek().Bytes20()
|
||||
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil
|
||||
return GasCosts{RegularGas: evm.AccessEvents.BasicDataGas(address, false, contract.Gas.RegularGas, true)}, nil
|
||||
}
|
||||
|
||||
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
address := stack.peek().Bytes20()
|
||||
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||
return 0, nil
|
||||
return GasCosts{}, nil
|
||||
}
|
||||
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil
|
||||
return GasCosts{RegularGas: evm.AccessEvents.BasicDataGas(address, false, contract.Gas.RegularGas, true)}, nil
|
||||
}
|
||||
|
||||
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
address := stack.peek().Bytes20()
|
||||
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||
return 0, nil
|
||||
return GasCosts{}, nil
|
||||
}
|
||||
return evm.AccessEvents.CodeHashGas(address, false, contract.Gas, true), nil
|
||||
return GasCosts{RegularGas: evm.AccessEvents.CodeHashGas(address, false, contract.Gas.RegularGas, true)}, nil
|
||||
}
|
||||
|
||||
func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
var (
|
||||
target = common.Address(stack.Back(1).Bytes20())
|
||||
witnessGas uint64
|
||||
witnessGas GasCosts
|
||||
_, isPrecompile = evm.precompile(target)
|
||||
isSystemContract = target == params.HistoryStorageAddress
|
||||
)
|
||||
|
|
@ -65,38 +65,38 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) ga
|
|||
// If value is transferred, it is charged before 1/64th
|
||||
// is subtracted from the available gas pool.
|
||||
if withTransferCosts && !stack.Back(2).IsZero() {
|
||||
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas)
|
||||
if wantedValueTransferWitnessGas > contract.Gas {
|
||||
return wantedValueTransferWitnessGas, nil
|
||||
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas.RegularGas)
|
||||
if wantedValueTransferWitnessGas > contract.Gas.RegularGas {
|
||||
return GasCosts{RegularGas: wantedValueTransferWitnessGas}, nil
|
||||
}
|
||||
witnessGas = wantedValueTransferWitnessGas
|
||||
witnessGas = GasCosts{RegularGas: wantedValueTransferWitnessGas}
|
||||
} else if isPrecompile || isSystemContract {
|
||||
witnessGas = params.WarmStorageReadCostEIP2929
|
||||
witnessGas = GasCosts{RegularGas: params.WarmStorageReadCostEIP2929}
|
||||
} else {
|
||||
// The charging for the value transfer is done BEFORE subtracting
|
||||
// the 1/64th gas, as this is considered part of the CALL instruction.
|
||||
// (so before we get to this point)
|
||||
// But the message call is part of the subcall, for which only 63/64th
|
||||
// of the gas should be available.
|
||||
wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas-witnessGas)
|
||||
wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas.RegularGas-witnessGas.RegularGas)
|
||||
var overflow bool
|
||||
if witnessGas, overflow = math.SafeAdd(witnessGas, wantedMessageCallWitnessGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
if witnessGas.RegularGas, overflow = math.SafeAdd(witnessGas.RegularGas, wantedMessageCallWitnessGas); overflow {
|
||||
return GasCosts{RegularGas: 0}, ErrGasUintOverflow
|
||||
}
|
||||
if witnessGas > contract.Gas {
|
||||
if witnessGas.RegularGas > contract.Gas.RegularGas {
|
||||
return witnessGas, nil
|
||||
}
|
||||
}
|
||||
|
||||
contract.Gas -= witnessGas
|
||||
contract.Gas.Sub(witnessGas)
|
||||
// if the operation fails, adds witness gas to the gas before returning the error
|
||||
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||
contract.Gas += witnessGas // restore witness gas so that it can be charged at the callsite
|
||||
contract.Gas.Add(witnessGas) // restore witness gas so that it can be charged at the callsite
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, witnessGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, witnessGas.RegularGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, err
|
||||
return GasCosts{RegularGas: gas.RegularGas}, err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,20 +107,20 @@ var (
|
|||
gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall, false)
|
||||
)
|
||||
|
||||
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
beneficiaryAddr := common.Address(stack.peek().Bytes20())
|
||||
if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile {
|
||||
return 0, nil
|
||||
return GasCosts{}, nil
|
||||
}
|
||||
if contract.IsSystemCall {
|
||||
return 0, nil
|
||||
return GasCosts{}, nil
|
||||
}
|
||||
contractAddr := contract.Address()
|
||||
wanted := evm.AccessEvents.BasicDataGas(contractAddr, false, contract.Gas, false)
|
||||
if wanted > contract.Gas {
|
||||
return wanted, nil
|
||||
wanted := evm.AccessEvents.BasicDataGas(contractAddr, false, contract.Gas.RegularGas, false)
|
||||
if wanted > contract.Gas.RegularGas {
|
||||
return GasCosts{RegularGas: wanted}, nil
|
||||
}
|
||||
statelessGas := wanted
|
||||
statelessGas := GasCosts{RegularGas: wanted}
|
||||
balanceIsZero := evm.StateDB.GetBalance(contractAddr).Sign() == 0
|
||||
_, isPrecompile := evm.precompile(beneficiaryAddr)
|
||||
isSystemContract := beneficiaryAddr == params.HistoryStorageAddress
|
||||
|
|
@ -130,39 +130,39 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
|||
}
|
||||
|
||||
if contractAddr != beneficiaryAddr {
|
||||
wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas-statelessGas, false)
|
||||
if wanted > contract.Gas-statelessGas {
|
||||
return statelessGas + wanted, nil
|
||||
wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas.RegularGas-statelessGas.RegularGas, false)
|
||||
if wanted > contract.Gas.RegularGas-statelessGas.RegularGas {
|
||||
return GasCosts{RegularGas: statelessGas.RegularGas + wanted}, nil
|
||||
}
|
||||
statelessGas += wanted
|
||||
statelessGas.RegularGas += wanted
|
||||
}
|
||||
// Charge write costs if it transfers value
|
||||
if !balanceIsZero {
|
||||
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas-statelessGas, false)
|
||||
if wanted > contract.Gas-statelessGas {
|
||||
return statelessGas + wanted, nil
|
||||
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas.RegularGas-statelessGas.RegularGas, false)
|
||||
if wanted > contract.Gas.RegularGas-statelessGas.RegularGas {
|
||||
return GasCosts{RegularGas: statelessGas.RegularGas + wanted}, nil
|
||||
}
|
||||
statelessGas += wanted
|
||||
statelessGas.RegularGas += wanted
|
||||
|
||||
if contractAddr != beneficiaryAddr {
|
||||
if evm.StateDB.Exist(beneficiaryAddr) {
|
||||
wanted = evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, contract.Gas-statelessGas, false)
|
||||
wanted = evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, contract.Gas.RegularGas-statelessGas.RegularGas, false)
|
||||
} else {
|
||||
wanted = evm.AccessEvents.AddAccount(beneficiaryAddr, true, contract.Gas-statelessGas)
|
||||
wanted = evm.AccessEvents.AddAccount(beneficiaryAddr, true, contract.Gas.RegularGas-statelessGas.RegularGas)
|
||||
}
|
||||
if wanted > contract.Gas-statelessGas {
|
||||
return statelessGas + wanted, nil
|
||||
if wanted > contract.Gas.RegularGas-statelessGas.RegularGas {
|
||||
return GasCosts{RegularGas: statelessGas.RegularGas + wanted}, nil
|
||||
}
|
||||
statelessGas += wanted
|
||||
statelessGas.RegularGas += wanted
|
||||
}
|
||||
}
|
||||
return statelessGas, nil
|
||||
}
|
||||
|
||||
func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := gasCodeCopy(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
if !contract.IsDeployment && !contract.IsSystemCall {
|
||||
var (
|
||||
|
|
@ -175,31 +175,31 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
|||
}
|
||||
|
||||
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
|
||||
_, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas-gas)
|
||||
gas += wanted
|
||||
_, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas.RegularGas-gas.RegularGas)
|
||||
gas.RegularGas += wanted
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
// memory expansion first (dynamic part of pre-2929 implementation)
|
||||
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return GasCosts{}, err
|
||||
}
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
_, isPrecompile := evm.precompile(addr)
|
||||
if isPrecompile || addr == params.HistoryStorageAddress {
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, params.WarmStorageReadCostEIP2929); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, params.WarmStorageReadCostEIP2929); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas.RegularGas}, nil
|
||||
}
|
||||
wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas-gas, true)
|
||||
wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas.RegularGas-gas.RegularGas, true)
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, wgas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, wgas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return GasCosts{RegularGas: gas.RegularGas}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,15 +142,15 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
|||
// set the receiver's (the executing contract) code for execution.
|
||||
cfg.State.SetCode(address, code, tracing.CodeChangeUnspecified)
|
||||
// Call the code with the given configuration.
|
||||
ret, leftOverGas, err := vmenv.Call(
|
||||
ret, leftOverGas, _, err := vmenv.Call(
|
||||
cfg.Origin,
|
||||
common.BytesToAddress([]byte("contract")),
|
||||
input,
|
||||
cfg.GasLimit,
|
||||
vm.GasCosts{RegularGas: cfg.GasLimit},
|
||||
uint256.MustFromBig(cfg.Value),
|
||||
)
|
||||
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil {
|
||||
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err)
|
||||
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas.RegularGas}, err)
|
||||
}
|
||||
return ret, cfg.State, err
|
||||
}
|
||||
|
|
@ -177,16 +177,16 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
|||
// - reset transient storage(eip 1153)
|
||||
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil)
|
||||
// Call the code with the given configuration.
|
||||
code, address, leftOverGas, err := vmenv.Create(
|
||||
code, address, leftOverGas, _, err := vmenv.Create(
|
||||
cfg.Origin,
|
||||
input,
|
||||
cfg.GasLimit,
|
||||
vm.GasCosts{RegularGas: cfg.GasLimit},
|
||||
uint256.MustFromBig(cfg.Value),
|
||||
)
|
||||
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil {
|
||||
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err)
|
||||
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas.RegularGas}, err)
|
||||
}
|
||||
return code, address, leftOverGas, err
|
||||
return code, address, leftOverGas.RegularGas, err
|
||||
}
|
||||
|
||||
// Call executes the code given by the contract's address. It will return the
|
||||
|
|
@ -211,15 +211,15 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
|
|||
statedb.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
|
||||
|
||||
// Call the code with the given configuration.
|
||||
ret, leftOverGas, err := vmenv.Call(
|
||||
ret, leftOverGas, _, err := vmenv.Call(
|
||||
cfg.Origin,
|
||||
address,
|
||||
input,
|
||||
cfg.GasLimit,
|
||||
vm.GasCosts{RegularGas: cfg.GasLimit},
|
||||
uint256.MustFromBig(cfg.Value),
|
||||
)
|
||||
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil {
|
||||
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err)
|
||||
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas.RegularGas}, err)
|
||||
}
|
||||
return ret, leftOverGas, err
|
||||
return ret, leftOverGas.RegularGas, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo
|
|||
gasLimit uint64 = 31000
|
||||
startGas uint64 = 10000
|
||||
value = uint256.NewInt(0)
|
||||
contract = vm.NewContract(common.Address{}, common.Address{}, value, startGas, nil)
|
||||
contract = vm.NewContract(common.Address{}, common.Address{}, value, vm.GasCosts{RegularGas: startGas}, nil)
|
||||
)
|
||||
evm.SetTxContext(vmctx.txCtx)
|
||||
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
|
||||
|
|
@ -66,9 +66,9 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo
|
|||
tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit, GasPrice: vmctx.txCtx.GasPrice.ToBig()}), contract.Caller())
|
||||
tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig())
|
||||
ret, err := evm.Run(contract, []byte{}, false)
|
||||
tracer.OnExit(0, ret, startGas-contract.Gas, err, true)
|
||||
tracer.OnExit(0, ret, startGas-contract.Gas.RegularGas, err, true)
|
||||
// Rest gas assumes no refund
|
||||
tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil)
|
||||
tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas.RegularGas}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -183,7 +183,7 @@ func TestHaltBetweenSteps(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
scope := &vm.ScopeContext{
|
||||
Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil),
|
||||
Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), vm.GasCosts{}, nil),
|
||||
}
|
||||
evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks})
|
||||
evm.SetTxContext(vm.TxContext{GasPrice: uint256.NewInt(1)})
|
||||
|
|
@ -281,7 +281,7 @@ func TestEnterExit(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
scope := &vm.ScopeContext{
|
||||
Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil),
|
||||
Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), vm.GasCosts{}, nil),
|
||||
}
|
||||
tracer.OnEnter(1, byte(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
|
||||
tracer.OnExit(1, []byte{}, 400, nil, false)
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ func TestStoreCapture(t *testing.T) {
|
|||
var (
|
||||
logger = NewStructLogger(nil)
|
||||
evm = vm.NewEVM(vm.BlockContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger.Hooks()})
|
||||
contract = vm.NewContract(common.Address{}, common.Address{}, new(uint256.Int), 100000, nil)
|
||||
contract = vm.NewContract(common.Address{}, common.Address{}, new(uint256.Int), vm.GasCosts{RegularGas: 100000}, nil)
|
||||
)
|
||||
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)}
|
||||
var index common.Hash
|
||||
|
|
|
|||
|
|
@ -1315,6 +1315,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
|||
}
|
||||
|
||||
// Prevent redundant operations if args contain more authorizations than EVM may handle
|
||||
// TODO change with EIP-8037
|
||||
maxAuthorizations := uint64(*args.Gas) / params.CallNewAccountGas
|
||||
if uint64(len(args.AuthorizationList)) > maxAuthorizations {
|
||||
return nil, 0, nil, errors.New("insufficient gas to process all authorizations")
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ const (
|
|||
LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas.
|
||||
CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction.
|
||||
Create2Gas uint64 = 32000 // Once per CREATE2 operation
|
||||
CreateGasAmsterdam uint64 = 9000 // Regular gas portion of CREATE in Amsterdam (EIP-8037); state gas is charged separately.
|
||||
CreateNGasEip4762 uint64 = 1000 // Once per CREATEn operations post-verkle
|
||||
SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation.
|
||||
MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
|
||||
|
|
@ -99,6 +100,7 @@ const (
|
|||
TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list
|
||||
TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list
|
||||
TxAuthTupleGas uint64 = 12500 // Per auth tuple code specified in EIP-7702
|
||||
TxAuthTupleRegularGas uint64 = 7500 // Per auth tuple regular gas specified in EIP-8037
|
||||
|
||||
// These have been changed during the course of the chain
|
||||
CallGasFrontier uint64 = 40 // Once per CALL operation & message call transaction.
|
||||
|
|
@ -185,6 +187,14 @@ const (
|
|||
HistoryServeWindow = 8191 // Number of blocks to serve historical block hashes for, EIP-2935.
|
||||
|
||||
MaxBlockSize = 8_388_608 // maximum size of an RLP-encoded block
|
||||
|
||||
TargetStateGrowthPerYear = 100 * 1024 * 1024 * 1024 // 100GB
|
||||
AccountCreationSize = 112
|
||||
StorageCreationSize = 32
|
||||
AuthorizationCreationSize = 23
|
||||
|
||||
// TODO: Add when EIP-7928 is implemented
|
||||
// GasBlockAccessListItem = 2000 // EIP-7928: gas cost per BAL item for gas limit check
|
||||
)
|
||||
|
||||
// Bls12381G1MultiExpDiscountTable is the gas discount table for BLS12-381 G1 multi exponentiation operation
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ func runBenchmark(b *testing.B, t *StateTest) {
|
|||
evm.SetTxContext(txContext)
|
||||
|
||||
// Create "contract" for sender to cache code analysis.
|
||||
sender := vm.NewContract(msg.From, msg.From, nil, 0, nil)
|
||||
sender := vm.NewContract(msg.From, msg.From, nil, vm.GasCosts{}, nil)
|
||||
|
||||
var (
|
||||
gasUsed uint64
|
||||
|
|
@ -316,7 +316,7 @@ func runBenchmark(b *testing.B, t *StateTest) {
|
|||
start := time.Now()
|
||||
|
||||
// Execute the message.
|
||||
_, leftOverGas, err := evm.Call(sender.Address(), *msg.To, msg.Data, msg.GasLimit, uint256.MustFromBig(msg.Value))
|
||||
_, leftOverGas, _, err := evm.Call(sender.Address(), *msg.To, msg.Data, vm.GasCosts{RegularGas: msg.GasLimit}, uint256.MustFromBig(msg.Value))
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
|
|
@ -325,7 +325,7 @@ func runBenchmark(b *testing.B, t *StateTest) {
|
|||
b.StopTimer()
|
||||
elapsed += uint64(time.Since(start))
|
||||
refund += state.StateDB.GetRefund()
|
||||
gasUsed += msg.GasLimit - leftOverGas
|
||||
gasUsed += msg.GasLimit - leftOverGas.RegularGas
|
||||
|
||||
state.StateDB.RevertToSnapshot(snapshot)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,10 +81,12 @@ func (tt *TransactionTest) Run() error {
|
|||
return
|
||||
}
|
||||
// Intrinsic gas
|
||||
requiredGas, err = core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
|
||||
// TODO (MariusVanDerWijden): correctly set this for post-amsterdam tests.
|
||||
gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, *rules, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
requiredGas = gas.RegularGas
|
||||
if requiredGas > tx.Gas() {
|
||||
return sender, hash, 0, fmt.Errorf("insufficient gas ( %d < %d )", tx.Gas(), requiredGas)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue