mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-24 08:49:29 +00:00
core: improve EIP-8037 implementations
This commit is contained in:
parent
b86cca23a8
commit
5963fc8408
22 changed files with 333 additions and 288 deletions
|
|
@ -133,8 +133,7 @@ func Transaction(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
// Check intrinsic gas
|
// Check intrinsic gas
|
||||||
rules := chainConfig.Rules(common.Big0, true, 0)
|
rules := chainConfig.Rules(common.Big0, true, 0)
|
||||||
gasCostPerStateByte := core.CostPerStateByte(&types.Header{}, chainConfig)
|
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, params.CostPerStateByte)
|
||||||
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, gasCostPerStateByte)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Error = err
|
r.Error = err
|
||||||
results = append(results, r)
|
results = append(results, r)
|
||||||
|
|
|
||||||
|
|
@ -89,8 +89,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
||||||
data := make([]byte, nbytes)
|
data := make([]byte, nbytes)
|
||||||
return func(i int, gen *BlockGen) {
|
return func(i int, gen *BlockGen) {
|
||||||
toaddr := common.Address{}
|
toaddr := common.Address{}
|
||||||
gasCostPerStateByte := CostPerStateByte(gen.header, gen.cm.config)
|
cost, _ := IntrinsicGas(data, nil, nil, false, params.Rules{}, params.CostPerStateByte)
|
||||||
cost, _ := IntrinsicGas(data, nil, nil, false, params.Rules{}, gasCostPerStateByte)
|
|
||||||
signer := gen.Signer()
|
signer := gen.Signer()
|
||||||
gasPrice := big.NewInt(0)
|
gasPrice := big.NewInt(0)
|
||||||
if gen.header.BaseFee != nil {
|
if gen.header.BaseFee != nil {
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
|
||||||
// been set, the block's coinbase is set to the zero address.
|
// been set, the block's coinbase is set to the zero address.
|
||||||
// The evm interpreter can be customized with the provided vm config.
|
// The evm interpreter can be customized with the provided vm config.
|
||||||
func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) {
|
func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) {
|
||||||
b.addTx(&BlockChain{chainConfig: b.cm.config}, config, tx)
|
b.addTx(nil, config, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBalance returns the balance of the given address at the generated block.
|
// GetBalance returns the balance of the given address at the generated block.
|
||||||
|
|
|
||||||
11
core/evm.go
11
core/evm.go
|
|
@ -80,19 +80,10 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
|
||||||
GasLimit: header.GasLimit,
|
GasLimit: header.GasLimit,
|
||||||
Random: random,
|
Random: random,
|
||||||
SlotNum: slotNum,
|
SlotNum: slotNum,
|
||||||
CostPerStateByte: CostPerStateByte(header, chain.Config()),
|
CostPerStateByte: params.CostPerStateByte,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 0
|
|
||||||
}
|
|
||||||
return params.CostPerStateByte
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEVMTxContext creates a new transaction context for a single transaction.
|
// NewEVMTxContext creates a new transaction context for a single transaction.
|
||||||
func NewEVMTxContext(msg *Message) vm.TxContext {
|
func NewEVMTxContext(msg *Message) vm.TxContext {
|
||||||
ctx := vm.TxContext{
|
ctx := vm.TxContext{
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,9 @@ func NewGasPool(amount uint64) *GasPool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubGas deducts the given amount from the pool if enough gas is
|
// CheckGasLegacy deducts the given amount from the pool if enough gas is
|
||||||
// available and returns an error otherwise.
|
// available and returns an error otherwise.
|
||||||
func (gp *GasPool) SubGas(amount uint64) error {
|
func (gp *GasPool) CheckGasLegacy(amount uint64) error {
|
||||||
if gp.remaining < amount {
|
if gp.remaining < amount {
|
||||||
return ErrGasLimitReached
|
return ErrGasLimitReached
|
||||||
}
|
}
|
||||||
|
|
@ -65,31 +65,24 @@ func (gp *GasPool) CheckGasAmsterdam(regularReservation, stateReservation uint64
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReturnGas adds the refunded gas back to the pool and updates
|
// ChargeGasLegacy adds the refunded gas back to the pool and updates
|
||||||
// the cumulative gas usage accordingly.
|
// the cumulative gas usage accordingly.
|
||||||
func (gp *GasPool) ReturnGas(returned uint64, gasUsed uint64) error {
|
func (gp *GasPool) ChargeGasLegacy(returned uint64, gasUsed uint64) error {
|
||||||
if gp.remaining > math.MaxUint64-returned {
|
if gp.remaining > math.MaxUint64-returned {
|
||||||
return fmt.Errorf("%w: remaining: %d, returned: %d", ErrGasLimitOverflow, gp.remaining, returned)
|
return fmt.Errorf("%w: remaining: %d, returned: %d", ErrGasLimitOverflow, gp.remaining, returned)
|
||||||
}
|
}
|
||||||
// The returned gas calculation differs across forks.
|
// returned = purchased - remaining (refund included)
|
||||||
//
|
|
||||||
// - Pre-Amsterdam:
|
|
||||||
// returned = purchased - remaining (refund included)
|
|
||||||
//
|
|
||||||
// - Post-Amsterdam:
|
|
||||||
// returned = purchased - gasUsed (refund excluded)
|
|
||||||
gp.remaining += returned
|
gp.remaining += returned
|
||||||
|
|
||||||
// gasUsed = max(txGasUsed - gasRefund, calldataFloorGasCost)
|
// gasUsed = max(txGasUsed - gasRefund, calldataFloorGasCost)
|
||||||
// regardless of Amsterdam is activated or not.
|
|
||||||
gp.cumulativeUsed += gasUsed
|
gp.cumulativeUsed += gasUsed
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChargeGasAmsterdam calculates the new remaining gas in the pool after the
|
// ChargeGasAmsterdam calculates the new remaining gas in the pool after the
|
||||||
// execution of a message. Previously we subtracted and re-added gas to the
|
// execution of a message. Previously we subtracted and re-added gas to the
|
||||||
// gaspool. After Amsterdam we only check if we can include the transaction and charge the
|
// gaspool. After Amsterdam we only check if we can include the transaction
|
||||||
// gaspool at the end.
|
// and charge the gaspool at the end.
|
||||||
func (gp *GasPool) ChargeGasAmsterdam(txRegular, txState, receiptGasUsed uint64) error {
|
func (gp *GasPool) ChargeGasAmsterdam(txRegular, txState, receiptGasUsed uint64) error {
|
||||||
gp.cumulativeRegular += txRegular
|
gp.cumulativeRegular += txRegular
|
||||||
gp.cumulativeState += txState
|
gp.cumulativeState += txState
|
||||||
|
|
|
||||||
|
|
@ -683,34 +683,6 @@ func (s *StateDB) IsNewContract(addr common.Address) bool {
|
||||||
return obj.newContract
|
return obj.newContract
|
||||||
}
|
}
|
||||||
|
|
||||||
// SameTxSelfDestructs returns the addresses that were both created and
|
|
||||||
// self-destructed in the current transaction (EIP-6780).
|
|
||||||
func (s *StateDB) SameTxSelfDestructs() []common.Address {
|
|
||||||
var out []common.Address
|
|
||||||
for addr, obj := range s.stateObjects {
|
|
||||||
if obj.newContract && obj.selfDestructed {
|
|
||||||
out = append(out, addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStorageSlotCount returns the number of storage slots that were written
|
|
||||||
// to a non-zero value in the current transaction on the given account.
|
|
||||||
func (s *StateDB) NewStorageSlotCount(addr common.Address) int {
|
|
||||||
obj, ok := s.stateObjects[addr]
|
|
||||||
if !ok {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var count int
|
|
||||||
for _, v := range obj.dirtyStorage {
|
|
||||||
if v != (common.Hash{}) {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy creates a deep, independent copy of the state.
|
// Copy creates a deep, independent copy of the state.
|
||||||
// Snapshots of the copied state cannot be applied to the copy.
|
// Snapshots of the copied state cannot be applied to the copy.
|
||||||
func (s *StateDB) Copy() *StateDB {
|
func (s *StateDB) Copy() *StateDB {
|
||||||
|
|
|
||||||
|
|
@ -59,14 +59,6 @@ func (s *hookedStateDB) IsNewContract(addr common.Address) bool {
|
||||||
return s.inner.IsNewContract(addr)
|
return s.inner.IsNewContract(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *hookedStateDB) SameTxSelfDestructs() []common.Address {
|
|
||||||
return s.inner.SameTxSelfDestructs()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *hookedStateDB) NewStorageSlotCount(addr common.Address) int {
|
|
||||||
return s.inner.NewStorageSlotCount(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *hookedStateDB) GetBalance(addr common.Address) *uint256.Int {
|
func (s *hookedStateDB) GetBalance(addr common.Address) *uint256.Int {
|
||||||
return s.inner.GetBalance(addr)
|
return s.inner.GetBalance(addr)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -277,9 +277,15 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM, blockAccessList
|
||||||
defer tracer.OnSystemCallEnd()
|
defer tracer.OnSystemCallEnd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// SYSTEM_MAX_SSTORES_PER_CALL = 16 is the upper bound on the number of
|
||||||
|
// new storage slots a single system call is expected to write.
|
||||||
|
//
|
||||||
|
// This value matches MAX_WITHDRAWAL_REQUESTS_PER_BLOCK (EIP-7002), the
|
||||||
|
// largest per-block bound across the existing system contracts.
|
||||||
|
stateBudget := params.SystemMaxSStoresPerCall * evm.Context.CostPerStateByte * params.StorageCreationSize
|
||||||
msg := &Message{
|
msg := &Message{
|
||||||
From: params.SystemAddress,
|
From: params.SystemAddress,
|
||||||
GasLimit: 30_000_000,
|
GasLimit: 30_000_000 + stateBudget,
|
||||||
GasPrice: uint256.NewInt(0),
|
GasPrice: uint256.NewInt(0),
|
||||||
GasFeeCap: uint256.NewInt(0),
|
GasFeeCap: uint256.NewInt(0),
|
||||||
GasTipCap: uint256.NewInt(0),
|
GasTipCap: uint256.NewInt(0),
|
||||||
|
|
@ -290,7 +296,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM, blockAccessList
|
||||||
evm.StateDB.Prepare(evm.GetRules(), common.Address{}, common.Address{}, nil, nil, nil)
|
evm.StateDB.Prepare(evm.GetRules(), common.Address{}, common.Address{}, nil, nil, nil)
|
||||||
evm.StateDB.SetTxContext(common.Hash{}, 0, 0)
|
evm.StateDB.SetTxContext(common.Hash{}, 0, 0)
|
||||||
evm.StateDB.AddAddressToAccessList(params.BeaconRootsAddress)
|
evm.StateDB.AddAddressToAccessList(params.BeaconRootsAddress)
|
||||||
_, _, _ = evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, 0), common.U2560)
|
_, _, _ = evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, stateBudget), common.U2560)
|
||||||
if evm.StateDB.AccessEvents() != nil {
|
if evm.StateDB.AccessEvents() != nil {
|
||||||
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
||||||
}
|
}
|
||||||
|
|
@ -306,9 +312,15 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM, blockAccessList *
|
||||||
defer tracer.OnSystemCallEnd()
|
defer tracer.OnSystemCallEnd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// SYSTEM_MAX_SSTORES_PER_CALL = 16 is the upper bound on the number of
|
||||||
|
// new storage slots a single system call is expected to write.
|
||||||
|
//
|
||||||
|
// This value matches MAX_WITHDRAWAL_REQUESTS_PER_BLOCK (EIP-7002), the
|
||||||
|
// largest per-block bound across the existing system contracts.
|
||||||
|
stateBudget := params.SystemMaxSStoresPerCall * evm.Context.CostPerStateByte * params.StorageCreationSize
|
||||||
msg := &Message{
|
msg := &Message{
|
||||||
From: params.SystemAddress,
|
From: params.SystemAddress,
|
||||||
GasLimit: 30_000_000,
|
GasLimit: 30_000_000 + stateBudget,
|
||||||
GasPrice: uint256.NewInt(0),
|
GasPrice: uint256.NewInt(0),
|
||||||
GasFeeCap: uint256.NewInt(0),
|
GasFeeCap: uint256.NewInt(0),
|
||||||
GasTipCap: uint256.NewInt(0),
|
GasTipCap: uint256.NewInt(0),
|
||||||
|
|
@ -319,7 +331,7 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM, blockAccessList *
|
||||||
evm.StateDB.Prepare(evm.GetRules(), common.Address{}, common.Address{}, nil, nil, nil)
|
evm.StateDB.Prepare(evm.GetRules(), common.Address{}, common.Address{}, nil, nil, nil)
|
||||||
evm.StateDB.SetTxContext(common.Hash{}, 0, 0)
|
evm.StateDB.SetTxContext(common.Hash{}, 0, 0)
|
||||||
evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress)
|
evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress)
|
||||||
_, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, 0), common.U2560)
|
_, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, stateBudget), common.U2560)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -348,9 +360,15 @@ func processRequestsSystemCall(requests *[][]byte, rules params.Rules, evm *vm.E
|
||||||
defer tracer.OnSystemCallEnd()
|
defer tracer.OnSystemCallEnd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// SYSTEM_MAX_SSTORES_PER_CALL = 16 is the upper bound on the number of
|
||||||
|
// new storage slots a single system call is expected to write.
|
||||||
|
//
|
||||||
|
// This value matches MAX_WITHDRAWAL_REQUESTS_PER_BLOCK (EIP-7002), the
|
||||||
|
// largest per-block bound across the existing system contracts.
|
||||||
|
stateBudget := params.SystemMaxSStoresPerCall * evm.Context.CostPerStateByte * params.StorageCreationSize
|
||||||
msg := &Message{
|
msg := &Message{
|
||||||
From: params.SystemAddress,
|
From: params.SystemAddress,
|
||||||
GasLimit: 30_000_000,
|
GasLimit: 30_000_000 + stateBudget,
|
||||||
GasPrice: uint256.NewInt(0),
|
GasPrice: uint256.NewInt(0),
|
||||||
GasFeeCap: uint256.NewInt(0),
|
GasFeeCap: uint256.NewInt(0),
|
||||||
GasTipCap: uint256.NewInt(0),
|
GasTipCap: uint256.NewInt(0),
|
||||||
|
|
@ -360,7 +378,7 @@ func processRequestsSystemCall(requests *[][]byte, rules params.Rules, evm *vm.E
|
||||||
evm.StateDB.Prepare(rules, common.Address{}, common.Address{}, nil, nil, nil)
|
evm.StateDB.Prepare(rules, common.Address{}, common.Address{}, nil, nil, nil)
|
||||||
evm.StateDB.SetTxContext(common.Hash{}, 0, blockAccessIndex)
|
evm.StateDB.SetTxContext(common.Hash{}, 0, blockAccessIndex)
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, 0), common.U2560)
|
ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, stateBudget), common.U2560)
|
||||||
if evm.StateDB.AccessEvents() != nil {
|
if evm.StateDB.AccessEvents() != nil {
|
||||||
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
@ -375,6 +376,24 @@ func (st *stateTransition) to() common.Address {
|
||||||
return *st.msg.To
|
return *st.msg.To
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buyGas pre-pays gas from the sender's balance and initializes the
|
||||||
|
// transaction's gas budget. It is invoked at the tail of preCheck.
|
||||||
|
//
|
||||||
|
// The balance requirement is the worst-case ETH the tx may need to lock
|
||||||
|
// up: `msg.GasLimit × max(msg.GasPrice, msg.GasFeeCap) + msg.Value`,
|
||||||
|
// plus `blobGas × msg.BlobGasFeeCap` under Cancun. Insufficient balance
|
||||||
|
// returns ErrInsufficientFunds. After the check, the sender is actually
|
||||||
|
// debited `msg.GasLimit × msg.GasPrice` (plus `blobGas × blobBaseFee`
|
||||||
|
// under Cancun), the cap-vs-tip differential is settled at tx end.
|
||||||
|
//
|
||||||
|
// The gas budget is seeded into both `initialBudget` (frozen snapshot
|
||||||
|
// for tx-end accounting) and `gasRemaining` (live running balance):
|
||||||
|
//
|
||||||
|
// - Pre-Amsterdam: one-dimensional regular budget equal to
|
||||||
|
// `msg.GasLimit`; the state-gas reservoir is zero.
|
||||||
|
// - Amsterdam+ (EIP-8037): two-dimensional budget. Regular gas is
|
||||||
|
// capped at `MaxTxGas` (EIP-7825, 16_777_216); any excess from
|
||||||
|
// `msg.GasLimit` above that cap becomes the state-gas reservoir.
|
||||||
func (st *stateTransition) buyGas() error {
|
func (st *stateTransition) buyGas() error {
|
||||||
mgval := new(uint256.Int).SetUint64(st.msg.GasLimit)
|
mgval := new(uint256.Int).SetUint64(st.msg.GasLimit)
|
||||||
_, overflow := mgval.MulOverflow(mgval, st.msg.GasPrice)
|
_, overflow := mgval.MulOverflow(mgval, st.msg.GasPrice)
|
||||||
|
|
@ -428,7 +447,7 @@ func (st *stateTransition) buyGas() error {
|
||||||
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
|
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// After Amsterdam we limit the regular gas to 16k, the data gas to the transaction limit
|
// After Amsterdam we limit the regular gas to 16M, the data gas to the transaction limit
|
||||||
limit := st.msg.GasLimit
|
limit := st.msg.GasLimit
|
||||||
if st.evm.ChainConfig().IsAmsterdam(st.evm.Context.BlockNumber, st.evm.Context.Time) {
|
if st.evm.ChainConfig().IsAmsterdam(st.evm.Context.BlockNumber, st.evm.Context.Time) {
|
||||||
limit = min(st.msg.GasLimit, params.MaxTxGas)
|
limit = min(st.msg.GasLimit, params.MaxTxGas)
|
||||||
|
|
@ -437,13 +456,32 @@ func (st *stateTransition) buyGas() error {
|
||||||
st.gasRemaining = st.initialBudget.Copy()
|
st.gasRemaining = st.initialBudget.Copy()
|
||||||
|
|
||||||
if st.evm.Config.Tracer.HasGasHook() {
|
if st.evm.Config.Tracer.HasGasHook() {
|
||||||
empty := vm.GasBudget{}
|
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{}, st.gasRemaining.AsTracing(), tracing.GasChangeTxInitialBalance)
|
||||||
st.evm.Config.Tracer.EmitGasChange(empty.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxInitialBalance)
|
|
||||||
}
|
}
|
||||||
|
// Deduct the gas cost from the sender's balance
|
||||||
st.state.SubBalance(st.msg.From, mgval, tracing.BalanceDecreaseGasBuy)
|
st.state.SubBalance(st.msg.From, mgval, tracing.BalanceDecreaseGasBuy)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// preCheck performs all pre-execution validation that does not require
|
||||||
|
// the EVM to run, then ends by calling buyGas to lock in the gas budget.
|
||||||
|
// It returns a consensus error if any of the following fail:
|
||||||
|
//
|
||||||
|
// - Sender nonce matches state and is not at 2^64-1 (EIP-2681).
|
||||||
|
// - EIP-7825 per-tx gas-limit cap on Osaka chains pre-Amsterdam
|
||||||
|
// (the cap also bounds the regular dimension after Amsterdam, but
|
||||||
|
// it is enforced there via the two-dimensional budget in buyGas).
|
||||||
|
// - EIP-3607 sender-is-EOA, allowing accounts whose only code is an
|
||||||
|
// EIP-7702 delegation designator.
|
||||||
|
// - EIP-1559 fee-cap, tip-cap and base-fee constraints (London+).
|
||||||
|
// - Blob-tx structural checks: non-nil `To`, non-empty hash list,
|
||||||
|
// valid KZG versioned hashes, count below `BlobTxMaxBlobs` (Osaka+).
|
||||||
|
// - Blob fee-cap not below the current blob base fee (Cancun+).
|
||||||
|
// - EIP-7702 set-code-tx shape: non-nil `To` and non-empty
|
||||||
|
// authorization list.
|
||||||
|
//
|
||||||
|
// The SkipNonceChecks / SkipTransactionChecks / NoBaseFee flags bypass
|
||||||
|
// subsets of these checks for simulation paths (eth_call, eth_estimateGas).
|
||||||
func (st *stateTransition) preCheck() error {
|
func (st *stateTransition) preCheck() error {
|
||||||
// Only check transactions that are not fake
|
// Only check transactions that are not fake
|
||||||
msg := st.msg
|
msg := st.msg
|
||||||
|
|
@ -461,8 +499,10 @@ func (st *stateTransition) preCheck() error {
|
||||||
msg.From.Hex(), stNonce)
|
msg.From.Hex(), stNonce)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isOsaka := st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time)
|
var (
|
||||||
isAmsterdam := st.evm.ChainConfig().IsAmsterdam(st.evm.Context.BlockNumber, st.evm.Context.Time)
|
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 {
|
if !msg.SkipTransactionChecks {
|
||||||
// Verify tx gas limit does not exceed EIP-7825 cap.
|
// Verify tx gas limit does not exceed EIP-7825 cap.
|
||||||
if !isAmsterdam && isOsaka && msg.GasLimit > params.MaxTxGas {
|
if !isAmsterdam && isOsaka && msg.GasLimit > params.MaxTxGas {
|
||||||
|
|
@ -539,28 +579,72 @@ func (st *stateTransition) preCheck() error {
|
||||||
return st.buyGas()
|
return st.buyGas()
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute will transition the state by applying the current message and
|
// reserveBlockGasBudget checks if the remaining gas budget in the block pool is
|
||||||
// returning the evm execution result with following fields.
|
// sufficient for including this transaction.
|
||||||
//
|
func (st *stateTransition) reserveBlockGasBudget(rules params.Rules, gasLimit uint64, intrinsicCost vm.GasCosts) error {
|
||||||
// - used gas: total gas used (including gas being refunded)
|
var err error
|
||||||
// - returndata: the returned data from evm
|
if rules.IsAmsterdam {
|
||||||
// - concrete execution error: various EVM errors which abort the execution, e.g.
|
// EIP-8037 per-tx 2D block-inclusion check. For each dimension,
|
||||||
// ErrOutOfGas, ErrExecutionReverted
|
// the worst-case contribution is tx.gas minus the other
|
||||||
//
|
// dimension's intrinsic (capped at MaxTxGas for the regular
|
||||||
// However if any consensus issue encountered, return the error directly with
|
// dimension).
|
||||||
// nil evm execution result.
|
regularReservation := gasLimit
|
||||||
func (st *stateTransition) execute() (*ExecutionResult, error) {
|
if regularReservation > intrinsicCost.StateGas {
|
||||||
// First check this message satisfies all consensus rules before
|
regularReservation -= intrinsicCost.StateGas
|
||||||
// applying the message. The rules include these clauses
|
} else {
|
||||||
//
|
regularReservation = 0
|
||||||
// 1. the nonce of the message caller is correct
|
}
|
||||||
// 2. caller has enough balance to cover transaction fee(gaslimit * gasprice)
|
regularReservation = min(regularReservation, params.MaxTxGas)
|
||||||
// 3. the amount of gas required is available in the block
|
|
||||||
// 4. the purchased gas is enough to cover intrinsic usage
|
|
||||||
// 5. there is no overflow when calculating intrinsic gas
|
|
||||||
// 6. caller has enough balance to cover asset transfer for **topmost** call
|
|
||||||
|
|
||||||
// Check clauses 1-3, buy gas if everything is correct
|
stateReservation := gasLimit
|
||||||
|
if stateReservation > intrinsicCost.RegularGas {
|
||||||
|
stateReservation -= intrinsicCost.RegularGas
|
||||||
|
} else {
|
||||||
|
stateReservation = 0
|
||||||
|
}
|
||||||
|
err = st.gp.CheckGasAmsterdam(regularReservation, stateReservation)
|
||||||
|
} else {
|
||||||
|
err = st.gp.CheckGasLegacy(gasLimit)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute transitions the state by applying the current message and
|
||||||
|
// returns the EVM execution result with the following fields:
|
||||||
|
//
|
||||||
|
// - used gas: total gas used, including gas refunded
|
||||||
|
// - peak used gas: maximum gas used before applying refunds
|
||||||
|
// - returndata: data returned by the EVM
|
||||||
|
// - execution error: EVM-level errors that abort execution, such as
|
||||||
|
// ErrOutOfGas or ErrExecutionReverted
|
||||||
|
//
|
||||||
|
// If a consensus error is encountered, it is returned directly with a
|
||||||
|
// nil EVM execution result.
|
||||||
|
func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
|
// The state-transition pipeline below runs in stages. Each stage may
|
||||||
|
// abort with a consensus error before the EVM is invoked:
|
||||||
|
//
|
||||||
|
// 1. preCheck: nonce, fee-cap, blob and EIP-7702 structural
|
||||||
|
// checks; ends by calling buyGas to debit the
|
||||||
|
// sender and seed the two-dimensional gas budget
|
||||||
|
// (EIP-8037).
|
||||||
|
// 2. Intrinsic: charges the intrinsic regular + state cost from
|
||||||
|
// the running budget with overflow detection.
|
||||||
|
// 3. Block pool: per-dimension inclusion reservation against the
|
||||||
|
// block gas pool (two-dimensional after Amsterdam,
|
||||||
|
// EIP-8037).
|
||||||
|
// 4. Floor pre: EIP-7623 calldata floor must fit in the gas allowance.
|
||||||
|
// 5. Top-call: run the top-most call, ensuring sender can cover
|
||||||
|
// the value transfer of the top call frame; init-code
|
||||||
|
// size respects the cap.
|
||||||
|
//
|
||||||
|
// After the EVM has run, the result path applies EIP-8037 state-gas
|
||||||
|
// refunds, the EIP-3529 regular-refund cap, and the EIP-7623 scalar
|
||||||
|
// floor (`tx_gas_used = max(tx_gas_used_after_refund, floor)`),
|
||||||
|
// returns leftover gas to the sender, settles the block pool and
|
||||||
|
// pays the coinbase tip.
|
||||||
|
|
||||||
|
// Stage 1: validate the message and pre-pay gas.
|
||||||
if err := st.preCheck(); err != nil {
|
if err := st.preCheck(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -572,7 +656,9 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
floorDataGas uint64
|
floorDataGas uint64
|
||||||
)
|
)
|
||||||
|
|
||||||
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
// Stage 2: charge intrinsic gas (with overflow detection inside
|
||||||
|
// IntrinsicGas). Under Amsterdam the cost is two-dimensional and
|
||||||
|
// Charge debits both regular and state in one step.
|
||||||
cost, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules, st.evm.Context.CostPerStateByte)
|
cost, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules, st.evm.Context.CostPerStateByte)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -585,62 +671,33 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
st.evm.Config.Tracer.EmitGasChange(prior.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxIntrinsicGas)
|
st.evm.Config.Tracer.EmitGasChange(prior.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxIntrinsicGas)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we have enough gas in the block
|
// Stage 3: reserve this tx's share of the block gas pool. Under
|
||||||
if rules.IsAmsterdam {
|
// Amsterdam this is a two-dimensional per-tx inclusion check; before
|
||||||
// EIP-8037 per-tx 2D block-inclusion check. For each dimension,
|
// Amsterdam it is a single scalar subtraction.
|
||||||
// the worst-case contribution is tx.gas minus the other
|
if err := st.reserveBlockGasBudget(rules, msg.GasLimit, cost); err != nil {
|
||||||
// dimension's intrinsic (capped at MaxTxGas for the regular
|
return nil, err
|
||||||
// dimension).
|
|
||||||
regularReservation := msg.GasLimit
|
|
||||||
if regularReservation > cost.StateGas {
|
|
||||||
regularReservation -= cost.StateGas
|
|
||||||
} else {
|
|
||||||
regularReservation = 0
|
|
||||||
}
|
|
||||||
regularReservation = min(regularReservation, params.MaxTxGas)
|
|
||||||
|
|
||||||
stateReservation := msg.GasLimit
|
|
||||||
if stateReservation > cost.RegularGas {
|
|
||||||
stateReservation -= cost.RegularGas
|
|
||||||
} else {
|
|
||||||
stateReservation = 0
|
|
||||||
}
|
|
||||||
if err := st.gp.CheckGasAmsterdam(regularReservation, stateReservation); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := st.gp.SubGas(msg.GasLimit); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the floor data cost (EIP-7623), needed for both Prague and Amsterdam validation.
|
// Stage 4: validate the EIP-7623 calldata floor against the gas limit.
|
||||||
|
// The floor inflates the total gas usage at tx end, so the gas limit
|
||||||
|
// must be sufficient to cover that.
|
||||||
if rules.IsPrague {
|
if rules.IsPrague {
|
||||||
floorDataGas, err = FloorDataGas(rules, msg.Data, msg.AccessList)
|
floorDataGas, err = FloorDataGas(rules, msg.Data, msg.AccessList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Make sure the transaction has sufficient gas allowance to
|
||||||
|
// pay the floor cost.
|
||||||
if msg.GasLimit < floorDataGas {
|
if msg.GasLimit < floorDataGas {
|
||||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, msg.GasLimit, floorDataGas)
|
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, msg.GasLimit, floorDataGas)
|
||||||
}
|
}
|
||||||
}
|
// In Amsterdam, the transaction gas limit is allowed to exceed
|
||||||
|
// params.MaxTxGas, but the calldata floor cost is capped by it.
|
||||||
if rules.IsAmsterdam {
|
if rules.IsAmsterdam {
|
||||||
if cost.Sum() > msg.GasLimit {
|
if max(cost.RegularGas, floorDataGas) > params.MaxTxGas {
|
||||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, msg.GasLimit, cost.Sum())
|
return nil, fmt.Errorf("%w: regular intrisic cost %v, floor: %v", ErrFloorDataGas, cost.RegularGas, floorDataGas)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// RegularGas must by < 16M
|
|
||||||
maxRegularGas := max(cost.RegularGas, floorDataGas)
|
|
||||||
if maxRegularGas > params.MaxTxGas {
|
|
||||||
return nil, fmt.Errorf("%w: max regular gas %d exceeds limit %d", ErrIntrinsicGas, maxRegularGas, params.MaxTxGas)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
before, ok := st.gasRemaining.Charge(cost)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining.RegularGas, cost.RegularGas)
|
|
||||||
}
|
|
||||||
if st.evm.Config.Tracer.HasGasHook() {
|
|
||||||
st.evm.Config.Tracer.EmitGasChange(before.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxIntrinsicGas)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if rules.IsEIP4762 {
|
if rules.IsEIP4762 {
|
||||||
|
|
@ -651,7 +708,8 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check clause 6
|
// Stage 5: top-call affordability, the sender must still be able
|
||||||
|
// to cover the value transfer of the top frame after gas pre-pay.
|
||||||
value := msg.Value
|
value := msg.Value
|
||||||
if value == nil {
|
if value == nil {
|
||||||
value = new(uint256.Int)
|
value = new(uint256.Int)
|
||||||
|
|
@ -689,7 +747,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
if msg.SetCodeAuthorizations != nil {
|
if msg.SetCodeAuthorizations != nil {
|
||||||
for _, auth := range msg.SetCodeAuthorizations {
|
for _, auth := range msg.SetCodeAuthorizations {
|
||||||
// Note errors are ignored, we simply skip invalid authorizations here.
|
// Note errors are ignored, we simply skip invalid authorizations here.
|
||||||
_ = st.applyAuthorization(rules, &auth)
|
st.applyAuthorization(rules, &auth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -706,58 +764,44 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
ret, result, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining.ForwardAll(), value)
|
ret, result, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining.ForwardAll(), value)
|
||||||
st.gasRemaining.Absorb(result)
|
st.gasRemaining.Absorb(result)
|
||||||
}
|
}
|
||||||
|
// If this was a failed contract creation, refund the account creation costs.
|
||||||
if rules.IsAmsterdam {
|
if rules.IsAmsterdam {
|
||||||
if vmerr != nil {
|
if vmerr != nil && contractCreation {
|
||||||
// If this was a contract creation, refund the account creation costs.
|
refund := params.AccountCreationSize * st.evm.Context.CostPerStateByte
|
||||||
if contractCreation {
|
st.gasRemaining.RefundState(refund)
|
||||||
refund := params.AccountCreationSize * st.evm.Context.CostPerStateByte
|
|
||||||
st.gasRemaining.RefundState(refund)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Compute refunds for selfdestructed slots
|
|
||||||
cpsb := st.evm.Context.CostPerStateByte
|
|
||||||
var sdRefund uint64
|
|
||||||
for _, addr := range st.state.SameTxSelfDestructs() {
|
|
||||||
r := params.AccountCreationSize * cpsb
|
|
||||||
r += uint64(st.state.NewStorageSlotCount(addr)) * params.StorageCreationSize * cpsb
|
|
||||||
r += uint64(st.state.GetCodeSize(addr)) * cpsb
|
|
||||||
sdRefund += r
|
|
||||||
}
|
|
||||||
if sdRefund > 0 {
|
|
||||||
st.gasRemaining.RefundState(sdRefund)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record the gas used excluding gas refunds. This value represents the actual
|
// Record the gas used excluding gas refunds. This value represents the actual
|
||||||
// gas allowance required to complete execution.
|
// gas allowance required to complete execution.
|
||||||
peakGasUsed := st.gasUsed()
|
peakGasUsed := st.gasUsed()
|
||||||
|
peakRegular := st.gasRemaining.UsedRegularGas
|
||||||
|
|
||||||
// Compute refund counter, capped to a refund quotient.
|
// Compute refund counter, capped to a refund quotient.
|
||||||
st.gasRemaining.RefundRegular(st.calcRefund())
|
st.gasRemaining.RefundRegular(st.calcRefund())
|
||||||
|
|
||||||
if rules.IsPrague {
|
if rules.IsPrague {
|
||||||
// After EIP-7623: Data-heavy transactions pay the floor gas.
|
// We can always guarantee that the initial regular gas allowance
|
||||||
|
// is sufficient to cover the floor cost.
|
||||||
|
//
|
||||||
|
// Pre-Amsterdam, there is a single dimension and gas limit is greater
|
||||||
|
// than the floor cost.
|
||||||
|
//
|
||||||
|
// Since Amsterdam:
|
||||||
|
// - If GasLimit <= 16M, the state reservoir is initialized to 0,
|
||||||
|
// and regular_gas_budget >= floor_cost always holds.
|
||||||
|
// - If GasLimit > 16M, the state reservoir is non-zero, while
|
||||||
|
// regular_gas_budget == 16M, which is still guaranteed to be
|
||||||
|
// greater than the floor cost. The extra cost should be deducted
|
||||||
|
// from the regular even the state reservoir is non-zero.
|
||||||
if used := st.gasUsed(); used < floorDataGas {
|
if used := st.gasUsed(); used < floorDataGas {
|
||||||
/*
|
prior, _ := st.gasRemaining.ChargeRegular(floorDataGas - used)
|
||||||
prior, _ := st.gasRemaining.Charge(vm.GasCosts{RegularGas: floorDataGas - used})
|
if st.evm.Config.Tracer.HasGasHook() {
|
||||||
if st.evm.Config.Tracer.HasGasHook() {
|
st.evm.Config.Tracer.EmitGasChange(prior.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxDataFloor)
|
||||||
st.evm.Config.Tracer.EmitGasChange(prior.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxDataFloor)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
prev := st.gasRemaining.RegularGas
|
|
||||||
// When the calldata floor exceeds actual gas used, any
|
|
||||||
// remaining state gas must also be consumed.
|
|
||||||
targetRemaining := (st.initialBudget.RegularGas + st.initialBudget.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.RegularGas, tracing.GasChangeTxDataFloor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if peakGasUsed < floorDataGas {
|
peakGasUsed = max(peakGasUsed, floorDataGas)
|
||||||
peakGasUsed = floorDataGas
|
peakRegular = max(peakRegular, floorDataGas)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
returned := st.returnGas()
|
returned := st.returnGas()
|
||||||
|
|
@ -766,19 +810,21 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
// st.gasRemaining.UsedRegularGas / UsedStateGas already include both
|
// st.gasRemaining.UsedRegularGas / UsedStateGas already include both
|
||||||
// the intrinsic charge (from st.gasRemaining.Charge(cost) above) and
|
// the intrinsic charge (from st.gasRemaining.Charge(cost) above) and
|
||||||
// the per-frame exec contributions absorbed from evm.Call / evm.Create.
|
// the per-frame exec contributions absorbed from evm.Call / evm.Create.
|
||||||
// UsedStateGas may be negative when inline refunds (SSTORE 0→x→0,
|
//
|
||||||
// CREATE-failure refund, 7702 auth refund, same-tx-SD refund) exceed
|
// UsedStateGas should never become negative in the top-most frame, since
|
||||||
// intrinsic + exec state charges. Clamp at 0.
|
// state gas refunds only occur when state creation is reverted within the
|
||||||
|
// same transaction, while clearing pre-existing state is never refunded.
|
||||||
var txState uint64
|
var txState uint64
|
||||||
if st.gasRemaining.UsedStateGas > 0 {
|
if st.gasRemaining.UsedStateGas >= 0 {
|
||||||
txState = uint64(st.gasRemaining.UsedStateGas)
|
txState = uint64(st.gasRemaining.UsedStateGas)
|
||||||
|
} else {
|
||||||
|
log.Error("Negative top-most frame state gas usage", "amount", st.gasRemaining.UsedStateGas)
|
||||||
}
|
}
|
||||||
txRegular := max(st.gasRemaining.UsedRegularGas, floorDataGas)
|
if err := st.gp.ChargeGasAmsterdam(peakRegular, txState, st.gasUsed()); err != nil {
|
||||||
if err := st.gp.ChargeGasAmsterdam(txRegular, txState, st.gasUsed()); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err = st.gp.ReturnGas(returned, st.gasUsed()); err != nil {
|
if err = st.gp.ChargeGasLegacy(returned, st.gasUsed()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -909,18 +955,15 @@ func (st *stateTransition) calcRefund() uint64 {
|
||||||
return refund
|
return refund
|
||||||
}
|
}
|
||||||
|
|
||||||
// returnGas returns ETH for remaining gas,
|
// returnGas returns ETH for remaining gas, exchanged at the original rate.
|
||||||
// exchanged at the original rate.
|
|
||||||
func (st *stateTransition) returnGas() uint64 {
|
func (st *stateTransition) returnGas() uint64 {
|
||||||
gas := st.gasRemaining.RegularGas + st.gasRemaining.StateGas
|
gas := st.gasRemaining.RegularGas + st.gasRemaining.StateGas
|
||||||
remaining := uint256.NewInt(st.gasRemaining.RegularGas)
|
remaining := uint256.NewInt(st.gasRemaining.RegularGas)
|
||||||
remaining.Mul(remaining, st.msg.GasPrice)
|
remaining.Mul(remaining, st.msg.GasPrice)
|
||||||
st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn)
|
st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn)
|
||||||
|
|
||||||
if st.gasRemaining.RegularGas > 0 && st.evm.Config.Tracer.HasGasHook() {
|
if !st.gasRemaining.IsZero() && st.evm.Config.Tracer.HasGasHook() {
|
||||||
after := st.gasRemaining
|
st.evm.Config.Tracer.EmitGasChange(st.gasRemaining.AsTracing(), tracing.Gas{}, tracing.GasChangeTxLeftOverReturned)
|
||||||
after.RegularGas = 0
|
|
||||||
st.evm.Config.Tracer.EmitGasChange(st.gasRemaining.AsTracing(), after.AsTracing(), tracing.GasChangeTxLeftOverReturned)
|
|
||||||
}
|
}
|
||||||
return gas
|
return gas
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -472,6 +472,10 @@ const (
|
||||||
// transaction data. This change will always be a negative change.
|
// transaction data. This change will always be a negative change.
|
||||||
GasChangeTxDataFloor GasChangeReason = 19
|
GasChangeTxDataFloor GasChangeReason = 19
|
||||||
|
|
||||||
|
// GasChangeStateGasRefund represents the amount of pre-charged state gas
|
||||||
|
// refunded back to the state reservoir.
|
||||||
|
GasChangeStateGasRefund GasChangeReason = 20
|
||||||
|
|
||||||
// GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as
|
// GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as
|
||||||
// it will be "manually" tracked by a direct emit of the gas change event.
|
// it will be "manually" tracked by a direct emit of the gas change event.
|
||||||
GasChangeIgnored GasChangeReason = 0xFF
|
GasChangeIgnored GasChangeReason = 0xFF
|
||||||
|
|
|
||||||
|
|
@ -125,8 +125,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
||||||
}
|
}
|
||||||
// Ensure the transaction has more gas than the bare minimum needed to cover
|
// Ensure the transaction has more gas than the bare minimum needed to cover
|
||||||
// the transaction metadata
|
// the transaction metadata
|
||||||
gasCostPerStateByte := core.CostPerStateByte(head, opts.Config)
|
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, params.CostPerStateByte)
|
||||||
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, gasCostPerStateByte)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -139,9 +138,18 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Make sure the transaction has sufficient gas allowance to
|
||||||
|
// pay the floor cost.
|
||||||
if tx.Gas() < floorDataGas {
|
if tx.Gas() < floorDataGas {
|
||||||
return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrFloorDataGas, tx.Gas(), floorDataGas)
|
return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrFloorDataGas, tx.Gas(), floorDataGas)
|
||||||
}
|
}
|
||||||
|
// In Amsterdam, the transaction gas limit is allowed to exceed
|
||||||
|
// params.MaxTxGas, but the calldata floor cost is capped by it.
|
||||||
|
if rules.IsAmsterdam {
|
||||||
|
if max(intrGas.RegularGas, floorDataGas) > params.MaxTxGas {
|
||||||
|
return fmt.Errorf("%w: regular intrisic cost %v, floor: %v", core.ErrFloorDataGas, intrGas.RegularGas, floorDataGas)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Ensure the gasprice is high enough to cover the requirement of the calling pool
|
// Ensure the gasprice is high enough to cover the requirement of the calling pool
|
||||||
if tx.GasTipCapIntCmp(opts.MinTip) < 0 {
|
if tx.GasTipCapIntCmp(opts.MinTip) < 0 {
|
||||||
|
|
|
||||||
|
|
@ -152,9 +152,19 @@ func (c *Contract) chargeState(s uint64, logger *tracing.Hooks, reason tracing.G
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefundGas absorbs a sub-call's leftover GasBudget into this contract's gas
|
// refundState refunds the pre-charged state gas back to state reservoir.
|
||||||
|
func (c *Contract) refundState(s uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
||||||
|
prior := c.Gas
|
||||||
|
c.Gas.RefundState(s)
|
||||||
|
|
||||||
|
if s != 0 && logger.HasGasHook() && reason != tracing.GasChangeIgnored {
|
||||||
|
logger.EmitGasChange(prior.AsTracing(), c.Gas.AsTracing(), reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// refundGas absorbs a sub-call's leftover GasBudget into this contract's gas
|
||||||
// state. Thin wrapper around GasBudget.Absorb with tracer integration.
|
// state. Thin wrapper around GasBudget.Absorb with tracer integration.
|
||||||
func (c *Contract) RefundGas(child GasBudget, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
func (c *Contract) refundGas(child GasBudget, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
||||||
prior := c.Gas
|
prior := c.Gas
|
||||||
c.Gas.Absorb(child)
|
c.Gas.Absorb(child)
|
||||||
if logger.HasGasHook() && reason != tracing.GasChangeIgnored {
|
if logger.HasGasHook() && reason != tracing.GasChangeIgnored {
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,7 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
|
||||||
// - any error that occurred
|
// - any error that occurred
|
||||||
func RunPrecompiledContract(stateDB StateDB, p PrecompiledContract, address common.Address, input []byte, gas GasBudget, logger *tracing.Hooks, rules params.Rules) (ret []byte, remaining GasBudget, err error) {
|
func RunPrecompiledContract(stateDB StateDB, p PrecompiledContract, address common.Address, input []byte, gas GasBudget, logger *tracing.Hooks, rules params.Rules) (ret []byte, remaining GasBudget, err error) {
|
||||||
gasCost := p.RequiredGas(input)
|
gasCost := p.RequiredGas(input)
|
||||||
prior, ok := gas.Charge(GasCosts{RegularGas: gasCost})
|
prior, ok := gas.ChargeRegular(gasCost)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, gas, ErrOutOfGas
|
return nil, gas, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -299,11 +299,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
}
|
}
|
||||||
|
|
||||||
if isPrecompile {
|
if isPrecompile {
|
||||||
var stateDB StateDB
|
ret, gas, err = RunPrecompiledContract(evm.StateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
|
||||||
if evm.chainRules.IsAmsterdam {
|
|
||||||
stateDB = evm.StateDB
|
|
||||||
}
|
|
||||||
ret, gas, err = RunPrecompiledContract(stateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
|
|
||||||
} else {
|
} else {
|
||||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||||
code := evm.resolveCode(addr)
|
code := evm.resolveCode(addr)
|
||||||
|
|
@ -318,18 +314,20 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// When an error was returned by the EVM or when setting the creation code
|
|
||||||
// above we revert to the snapshot. gasFromExec below handles the
|
// Calculate the remaining gas at the end of frame
|
||||||
// regular-gas burn on halt.
|
exitGas := gas.Exit(err, initialStateGas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
|
// Drain the leftover regular gas if unexceptional halt occurs
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer.HasGasHook() {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), tracing.Gas{}, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), exitGas.AsTracing(), tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret, gas.ExitFromErr(err, initialStateGas), err
|
return ret, exitGas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallCode executes the contract associated with the addr with the given input
|
// CallCode executes the contract associated with the addr with the given input
|
||||||
|
|
@ -361,11 +359,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
||||||
|
|
||||||
// It is allowed to call precompiles, even via delegatecall
|
// It is allowed to call precompiles, even via delegatecall
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
var stateDB StateDB
|
ret, gas, err = RunPrecompiledContract(evm.StateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
|
||||||
if evm.chainRules.IsAmsterdam {
|
|
||||||
stateDB = evm.StateDB
|
|
||||||
}
|
|
||||||
ret, gas, err = RunPrecompiledContract(stateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
|
|
||||||
} else {
|
} else {
|
||||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
// 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.
|
// The contract is a scoped environment for this execution context only.
|
||||||
|
|
@ -374,15 +368,20 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
||||||
ret, err = evm.Run(contract, input, false)
|
ret, err = evm.Run(contract, input, false)
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate the remaining gas at the end of frame
|
||||||
|
exitGas := gas.Exit(err, initialStateGas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
|
// Drain the leftover regular gas if unexceptional halt occurs
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer.HasGasHook() {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), tracing.Gas{}, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), exitGas.AsTracing(), tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret, gas.ExitFromErr(err, initialStateGas), err
|
return ret, exitGas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DelegateCall executes the contract associated with the addr with the given input
|
// DelegateCall executes the contract associated with the addr with the given input
|
||||||
|
|
@ -409,26 +408,27 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
||||||
|
|
||||||
// It is allowed to call precompiles, even via delegatecall
|
// It is allowed to call precompiles, even via delegatecall
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
var stateDB StateDB
|
ret, gas, err = RunPrecompiledContract(evm.StateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
|
||||||
if evm.chainRules.IsAmsterdam {
|
|
||||||
stateDB = evm.StateDB
|
|
||||||
}
|
|
||||||
ret, gas, err = RunPrecompiledContract(stateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
|
|
||||||
} else {
|
} else {
|
||||||
contract := NewContract(originCaller, caller, value, gas, evm.jumpDests)
|
contract := NewContract(originCaller, caller, value, gas, evm.jumpDests)
|
||||||
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
||||||
ret, err = evm.Run(contract, input, false)
|
ret, err = evm.Run(contract, input, false)
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate the remaining gas at the end of frame
|
||||||
|
exitGas := gas.Exit(err, initialStateGas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
|
// Drain the leftover regular gas if unexceptional halt occurs
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer.HasGasHook() {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), tracing.Gas{}, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), exitGas.AsTracing(), tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret, gas.ExitFromErr(err, initialStateGas), err
|
return ret, exitGas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// StaticCall executes the contract associated with the addr with the given input
|
// StaticCall executes the contract associated with the addr with the given input
|
||||||
|
|
@ -463,26 +463,25 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
||||||
evm.StateDB.AddBalance(addr, new(uint256.Int), tracing.BalanceChangeTouchAccount)
|
evm.StateDB.AddBalance(addr, new(uint256.Int), tracing.BalanceChangeTouchAccount)
|
||||||
|
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
var stateDB StateDB
|
ret, gas, err = RunPrecompiledContract(evm.StateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
|
||||||
if evm.chainRules.IsAmsterdam {
|
|
||||||
stateDB = evm.StateDB
|
|
||||||
}
|
|
||||||
ret, gas, err = RunPrecompiledContract(stateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
|
|
||||||
} else {
|
} else {
|
||||||
contract := NewContract(caller, addr, new(uint256.Int), gas, evm.jumpDests)
|
contract := NewContract(caller, addr, new(uint256.Int), gas, evm.jumpDests)
|
||||||
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
||||||
ret, err = evm.Run(contract, input, true)
|
ret, err = evm.Run(contract, input, true)
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate the remaining gas at the end of frame
|
||||||
|
exitGas := gas.Exit(err, initialStateGas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer.HasGasHook() {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), tracing.Gas{}, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), exitGas.AsTracing(), tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret, gas.ExitFromErr(err, initialStateGas), err
|
return ret, exitGas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// create creates a new contract using code as deployment code.
|
// create creates a new contract using code as deployment code.
|
||||||
|
|
@ -593,12 +592,14 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
// state and gas is preserved (i.e., treated as success).
|
// state and gas is preserved (i.e., treated as success).
|
||||||
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
|
exit := contract.Gas.Exit(err, initialStateGas)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.OnGasChange(contract.Gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.EmitGasChange(contract.Gas.AsTracing(), exit.AsTracing(), tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret, address, contract.Gas.ExitFromErr(err, initialStateGas), err
|
return ret, address, exit, err
|
||||||
}
|
}
|
||||||
// Either success, or pre-Homestead ErrCodeStoreOutOfGas (gas preserved).
|
// Either success, or pre-Homestead ErrCodeStoreOutOfGas (gas preserved).
|
||||||
// Both packaged as a success-form GasBudget.
|
// Both packaged as a success-form GasBudget.
|
||||||
|
|
|
||||||
|
|
@ -368,7 +368,7 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
expByteLen := uint64(((*stack.back(1)).BitLen() + 7) / 8)
|
expByteLen := uint64((stack.back(1).BitLen() + 7) / 8)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
gas = expByteLen * params.ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas
|
gas = expByteLen * params.ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas
|
||||||
|
|
@ -381,7 +381,7 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
expByteLen := uint64(((*stack.back(1)).BitLen() + 7) / 8)
|
expByteLen := uint64((stack.back(1).BitLen() + 7) / 8)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
gas = expByteLen * params.ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas
|
gas = expByteLen * params.ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas
|
||||||
|
|
@ -460,10 +460,10 @@ func gasCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// gasCallIntrinsic8037 is the intrinsic gas calculator for CALL in Amsterdam.
|
// regularGasCall8037 is the intrinsic gas calculator for CALL in Amsterdam.
|
||||||
// It computes memory expansion + value transfer gas but excludes new account
|
// It computes memory expansion + value transfer gas but excludes new account
|
||||||
// creation, which is handled as state gas by the wrapper.
|
// creation, which is handled as state gas by the wrapper.
|
||||||
func gasCallIntrinsic8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func regularGasCall8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
var (
|
var (
|
||||||
gas uint64
|
gas uint64
|
||||||
transfersValue = !stack.back(2).IsZero()
|
transfersValue = !stack.back(2).IsZero()
|
||||||
|
|
@ -571,7 +571,10 @@ func gasCreateEip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
||||||
words := (size + 31) / 32
|
words := (size + 31) / 32
|
||||||
wordGas := params.InitCodeWordGas * words
|
wordGas := params.InitCodeWordGas * words
|
||||||
stateGas := params.AccountCreationSize * evm.Context.CostPerStateByte
|
stateGas := params.AccountCreationSize * evm.Context.CostPerStateByte
|
||||||
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil
|
return GasCosts{
|
||||||
|
RegularGas: gas + wordGas,
|
||||||
|
StateGas: stateGas,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
|
|
@ -591,28 +594,35 @@ func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
}
|
}
|
||||||
// Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow
|
// Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow
|
||||||
words := (size + 31) / 32
|
words := (size + 31) / 32
|
||||||
// CREATE2 charges both InitCodeWordGas (EIP-3860) and Keccak256WordGas (for address hashing).
|
|
||||||
|
// CREATE2 charges both InitCodeWordGas (EIP-3860) and Keccak256WordGas
|
||||||
|
// (for address hashing).
|
||||||
wordGas := (params.InitCodeWordGas + params.Keccak256WordGas) * words
|
wordGas := (params.InitCodeWordGas + params.Keccak256WordGas) * words
|
||||||
stateGas := params.AccountCreationSize * evm.Context.CostPerStateByte
|
stateGas := params.AccountCreationSize * evm.Context.CostPerStateByte
|
||||||
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil
|
return GasCosts{
|
||||||
|
RegularGas: gas + wordGas,
|
||||||
|
StateGas: stateGas,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// gasCall8037 is the stateful gas calculator for CALL in Amsterdam (EIP-8037).
|
// stateGasCall8037 is the stateful gas calculator for CALL in Amsterdam (EIP-8037).
|
||||||
// It only returns the state-dependent gas (account creation as state gas).
|
// It only returns the state-dependent gas (account creation as state gas).
|
||||||
// Memory gas, transfer gas, and callGas are handled by gasCallStateless and
|
// Memory gas, transfer gas, and callGas are handled by gasCallStateless and
|
||||||
// makeCallVariantGasCall.
|
// makeCallVariantGasCall.
|
||||||
func gasCall8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func stateGasCall8037(evm *EVM, contract *Contract, stack *Stack) (uint64, error) {
|
||||||
var (
|
var (
|
||||||
gas GasCosts
|
gas uint64
|
||||||
transfersValue = !stack.back(2).IsZero()
|
transfersValue = !stack.back(2).IsZero()
|
||||||
address = common.Address(stack.back(1).Bytes20())
|
address = common.Address(stack.back(1).Bytes20())
|
||||||
)
|
)
|
||||||
|
// TODO(rjl, marius), can EIP8037 implicitly means the EIP158 is also activated?
|
||||||
|
// It's technically possible to skip the EIP158 but very unlikely in practice.
|
||||||
if evm.chainRules.IsEIP158 {
|
if evm.chainRules.IsEIP158 {
|
||||||
if transfersValue && evm.StateDB.Empty(address) {
|
if transfersValue && evm.StateDB.Empty(address) {
|
||||||
gas.StateGas += params.AccountCreationSize * evm.Context.CostPerStateByte
|
gas += params.AccountCreationSize * evm.Context.CostPerStateByte
|
||||||
}
|
}
|
||||||
} else if !evm.StateDB.Exist(address) {
|
} else if !evm.StateDB.Exist(address) {
|
||||||
gas.StateGas += params.AccountCreationSize * evm.Context.CostPerStateByte
|
gas += params.AccountCreationSize * evm.Context.CostPerStateByte
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
@ -671,14 +681,9 @@ func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
||||||
}
|
}
|
||||||
if original == current {
|
if original == current {
|
||||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||||
// EIP-8037: Return both regular and state gas. System calls do not charge state gas.
|
|
||||||
var stateGas uint64
|
|
||||||
if !contract.IsSystemCall {
|
|
||||||
stateGas = params.StorageCreationSize * evm.Context.CostPerStateByte
|
|
||||||
}
|
|
||||||
return GasCosts{
|
return GasCosts{
|
||||||
RegularGas: cost.RegularGas + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929,
|
RegularGas: cost.RegularGas + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929,
|
||||||
StateGas: stateGas,
|
StateGas: params.StorageCreationSize * evm.Context.CostPerStateByte,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ func (g GasCosts) String() string {
|
||||||
// in lockstep.
|
// in lockstep.
|
||||||
//
|
//
|
||||||
// - At frame exit: Preserved / ExitSuccess / ExitRevert / ExitHalt /
|
// - At frame exit: Preserved / ExitSuccess / ExitRevert / ExitHalt /
|
||||||
// ExitFromErr produce a new GasBudget in "leftover" form that packages
|
// Exit produce a new GasBudget in "leftover" form that packages
|
||||||
// the result for the caller.
|
// the result for the caller.
|
||||||
//
|
//
|
||||||
// - At absorption: the caller's Absorb method merges the child's leftover
|
// - At absorption: the caller's Absorb method merges the child's leftover
|
||||||
|
|
@ -263,7 +263,7 @@ func (g GasBudget) ExitHalt(initialStateGas uint64) GasBudget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExitFromErr dispatches on err to the appropriate exit-form constructor
|
// Exit dispatches on err to the appropriate exit-form constructor
|
||||||
// for the post-evm.Run path:
|
// for the post-evm.Run path:
|
||||||
//
|
//
|
||||||
// - err == nil → ExitSuccess
|
// - err == nil → ExitSuccess
|
||||||
|
|
@ -272,7 +272,7 @@ func (g GasBudget) ExitHalt(initialStateGas uint64) GasBudget {
|
||||||
//
|
//
|
||||||
// Soft validation failures (occurring BEFORE evm.Run) should call Preserved
|
// Soft validation failures (occurring BEFORE evm.Run) should call Preserved
|
||||||
// directly instead of going through this dispatcher.
|
// directly instead of going through this dispatcher.
|
||||||
func (g GasBudget) ExitFromErr(err error, initialStateGas uint64) GasBudget {
|
func (g GasBudget) Exit(err error, initialStateGas uint64) GasBudget {
|
||||||
switch {
|
switch {
|
||||||
case err == nil:
|
case err == nil:
|
||||||
return g.ExitSuccess()
|
return g.ExitSuccess()
|
||||||
|
|
|
||||||
|
|
@ -675,7 +675,7 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
}
|
}
|
||||||
scope.Stack.push(&stackvalue)
|
scope.Stack.push(&stackvalue)
|
||||||
|
|
||||||
scope.Contract.RefundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
if evm.chainRules.IsAmsterdam && suberr != nil {
|
if evm.chainRules.IsAmsterdam && suberr != nil {
|
||||||
scope.Contract.Gas.RefundState(params.AccountCreationSize * evm.Context.CostPerStateByte)
|
scope.Contract.Gas.RefundState(params.AccountCreationSize * evm.Context.CostPerStateByte)
|
||||||
}
|
}
|
||||||
|
|
@ -710,11 +710,13 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
stackvalue.SetBytes(addr.Bytes())
|
stackvalue.SetBytes(addr.Bytes())
|
||||||
}
|
}
|
||||||
scope.Stack.push(&stackvalue)
|
scope.Stack.push(&stackvalue)
|
||||||
scope.Contract.RefundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
if evm.chainRules.IsAmsterdam && suberr != nil {
|
|
||||||
scope.Contract.Gas.RefundState(params.AccountCreationSize * evm.Context.CostPerStateByte)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// If the creation frame reverts or halts exceptionally, the charged state-gas
|
||||||
|
// is refilled back to the state reservoir in Amsterdam.
|
||||||
|
if evm.chainRules.IsAmsterdam && suberr != nil {
|
||||||
|
scope.Contract.refundState(params.AccountCreationSize*evm.Context.CostPerStateByte, evm.Config.Tracer, tracing.GasChangeStateGasRefund)
|
||||||
|
}
|
||||||
if suberr == ErrExecutionReverted {
|
if suberr == ErrExecutionReverted {
|
||||||
evm.returnData = res // set REVERT data to return data buffer
|
evm.returnData = res // set REVERT data to return data buffer
|
||||||
return res, nil
|
return res, nil
|
||||||
|
|
@ -754,11 +756,24 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
temp.SetOne()
|
temp.SetOne()
|
||||||
}
|
}
|
||||||
stack.push(&temp)
|
stack.push(&temp)
|
||||||
|
|
||||||
if err == nil || err == ErrExecutionReverted {
|
if err == nil || err == ErrExecutionReverted {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
|
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
scope.Contract.RefundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
// If the call frame reverts or halts exceptionally, the charged state-gas
|
||||||
|
// is refilled back to the state reservoir in Amsterdam.
|
||||||
|
//
|
||||||
|
// The state-gas should only be refunded if the state creation doesn't
|
||||||
|
// happens, such as ErrDepth, ErrInsufficientBalance.
|
||||||
|
//
|
||||||
|
// TODO(rjl) it's so ugly, please rework it.
|
||||||
|
if evm.chainRules.IsAmsterdam && err != nil {
|
||||||
|
if (err == ErrDepth || err == ErrInsufficientBalance) && !value.IsZero() && evm.StateDB.Empty(toAddr) {
|
||||||
|
scope.Contract.refundState(params.AccountCreationSize*evm.Context.CostPerStateByte, evm.Config.Tracer, tracing.GasChangeStateGasRefund)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
evm.returnData = ret
|
evm.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
|
@ -792,7 +807,7 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.Contract.RefundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
evm.returnData = ret
|
evm.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
|
@ -821,7 +836,7 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
if err == nil || err == ErrExecutionReverted {
|
if err == nil || err == ErrExecutionReverted {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
scope.Contract.RefundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
evm.returnData = ret
|
evm.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
|
@ -851,7 +866,7 @@ func opStaticCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.Contract.RefundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
evm.returnData = ret
|
evm.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
|
|
||||||
|
|
@ -71,14 +71,6 @@ type StateDB interface {
|
||||||
// during the current transaction.
|
// during the current transaction.
|
||||||
IsNewContract(addr common.Address) bool
|
IsNewContract(addr common.Address) bool
|
||||||
|
|
||||||
// SameTxSelfDestructs returns addresses that were created and then
|
|
||||||
// self-destructed in the current transaction.
|
|
||||||
SameTxSelfDestructs() []common.Address
|
|
||||||
|
|
||||||
// NewStorageSlotCount returns the number of storage slots written to a
|
|
||||||
// non-zero value in the current transaction on the given address.
|
|
||||||
NewStorageSlotCount(addr common.Address) int
|
|
||||||
|
|
||||||
// Empty returns whether the given account is empty. Empty
|
// Empty returns whether the given account is empty. Empty
|
||||||
// is defined according to EIP161 (balance = nonce = code = 0).
|
// is defined according to EIP161 (balance = nonce = code = 0).
|
||||||
Empty(common.Address) bool
|
Empty(common.Address) bool
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@ type (
|
||||||
intrinsicGasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, 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 returns the required size, and whether the operation overflowed a uint64
|
||||||
memorySizeFunc func(*Stack) (size uint64, overflow bool)
|
memorySizeFunc func(*Stack) (size uint64, overflow bool)
|
||||||
|
|
||||||
|
regularGasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error)
|
||||||
|
stateGasFunc func(*EVM, *Contract, *Stack) (uint64, error)
|
||||||
)
|
)
|
||||||
|
|
||||||
type operation struct {
|
type operation struct {
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,7 @@ var (
|
||||||
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCallIntrinsic)
|
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCallIntrinsic)
|
||||||
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCodeIntrinsic)
|
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCodeIntrinsic)
|
||||||
|
|
||||||
innerGasCallEIP8037 = makeCallVariantGasCallEIP8037(gasCallIntrinsic8037, gasCall8037)
|
innerGasCallEIP8037 = makeCallVariantGasCallEIP8037(regularGasCall8037, stateGasCall8037)
|
||||||
)
|
)
|
||||||
|
|
||||||
func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
|
|
@ -381,7 +381,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
|
||||||
// It extends the EIP-7702 pattern with state gas handling and GasUsed tracking.
|
// It extends the EIP-7702 pattern with state gas handling and GasUsed tracking.
|
||||||
// intrinsicFunc computes the regular gas (memory + transfer, no new account creation).
|
// intrinsicFunc computes the regular gas (memory + transfer, no new account creation).
|
||||||
// stateGasFunc computes the state gas (new account creation as state gas).
|
// stateGasFunc computes the state gas (new account creation as state gas).
|
||||||
func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc gasFunc) gasFunc {
|
func makeCallVariantGasCallEIP8037(regularFunc regularGasFunc, stateGasFunc stateGasFunc) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
var (
|
var (
|
||||||
eip2929Cost uint64
|
eip2929Cost uint64
|
||||||
|
|
@ -397,8 +397,8 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute intrinsic cost (memory + transfer, no new account creation).
|
// Compute regular cost (memory + transfer, no new account creation).
|
||||||
intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
|
regularCost, err := regularFunc(evm, contract, stack, mem, memorySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -406,7 +406,7 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
|
||||||
// Charge intrinsic cost directly (regular gas). This must happen
|
// Charge intrinsic cost directly (regular gas). This must happen
|
||||||
// BEFORE state gas to prevent reservoir inflation, and also serves
|
// BEFORE state gas to prevent reservoir inflation, and also serves
|
||||||
// as the OOG guard before stateful operations.
|
// as the OOG guard before stateful operations.
|
||||||
if !contract.chargeRegular(intrinsicCost, evm.Config.Tracer, tracing.GasChangeCallOpCode) {
|
if !contract.chargeRegular(regularCost, evm.Config.Tracer, tracing.GasChangeCallOpCode) {
|
||||||
return GasCosts{}, ErrOutOfGas
|
return GasCosts{}, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -424,13 +424,12 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute and charge state gas (new account creation) AFTER regular gas.
|
// Compute and charge state gas (new account creation) AFTER regular gas.
|
||||||
stateGas, err := stateGasFunc(evm, contract, stack, mem, memorySize)
|
stateGas, err := stateGasFunc(evm, contract, stack)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
}
|
}
|
||||||
if stateGas.StateGas > 0 {
|
if stateGas > 0 {
|
||||||
// Charge updates contract.Gas.UsedStateGas in lockstep.
|
if _, ok := contract.Gas.ChargeState(stateGas); !ok {
|
||||||
if _, ok := contract.Gas.Charge(GasCosts{StateGas: stateGas.StateGas}); !ok {
|
|
||||||
return GasCosts{}, ErrOutOfGas
|
return GasCosts{}, ErrOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -443,8 +442,8 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
|
||||||
|
|
||||||
// Temporarily undo direct regular charges for tracer reporting.
|
// Temporarily undo direct regular charges for tracer reporting.
|
||||||
// The interpreter will charge the returned totalCost.
|
// The interpreter will charge the returned totalCost.
|
||||||
contract.Gas.RegularGas += eip2929Cost + eip7702Cost + intrinsicCost
|
contract.Gas.RegularGas += eip2929Cost + eip7702Cost + regularCost
|
||||||
contract.Gas.UsedRegularGas -= eip2929Cost + eip7702Cost + intrinsicCost
|
contract.Gas.UsedRegularGas -= eip2929Cost + eip7702Cost + regularCost
|
||||||
|
|
||||||
// Aggregate total cost.
|
// Aggregate total cost.
|
||||||
var (
|
var (
|
||||||
|
|
@ -454,7 +453,7 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
|
||||||
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
|
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost); overflow {
|
if totalCost, overflow = math.SafeAdd(totalCost, regularCost); overflow {
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {
|
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {
|
||||||
|
|
|
||||||
|
|
@ -199,10 +199,11 @@ const (
|
||||||
// don't consume block gas.
|
// don't consume block gas.
|
||||||
BALItemCost uint64 = 2000
|
BALItemCost uint64 = 2000
|
||||||
|
|
||||||
AccountCreationSize = 112
|
AccountCreationSize = 120
|
||||||
StorageCreationSize = 32
|
StorageCreationSize = 64
|
||||||
AuthorizationCreationSize = 23
|
AuthorizationCreationSize = 23
|
||||||
CostPerStateByte = 1174
|
CostPerStateByte = 1530
|
||||||
|
SystemMaxSStoresPerCall = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bls12381G1MultiExpDiscountTable is the gas discount table for BLS12-381 G1 multi exponentiation operation
|
// Bls12381G1MultiExpDiscountTable is the gas discount table for BLS12-381 G1 multi exponentiation operation
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ func (tt *TransactionTest) Run() error {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Intrinsic cost
|
// Intrinsic cost
|
||||||
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, 0)
|
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, params.CostPerStateByte)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue