mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-10 01:56:37 +00:00
EIP-7825 caps the transaction gas limit at `MaxTxGas`, but after Amsterdam/EIP-8037 the transaction gas limit can include state gas reservoir in addition to the regular gas dimension. Applying the Osaka cap to the full `tx.Gas()` rejects otherwise valid Amsterdam transactions that need more than `MaxTxGas` total gas because of state gas, while their regular gas use remains within the intended limit. This changes geth to stop applying the full transaction gas cap once Amsterdam is active: - txpool stateless validation no longer rejects `tx.Gas() > MaxTxGas` under Amsterdam - legacy pool reorg cleanup does not purge high-total-gas transactions at the Osaka transition if Amsterdam is also active - execution precheck mirrors the txpool behavior and does not reject high-total-gas messages under Amsterdam The block gas limit check remains in place, so transactions still cannot request more total gas than the current block gas limit. Validation run: ``` go test ./core/txpool ./core/txpool/legacypool go test ./core -run TestStateProcessorErrors ``` --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com>
202 lines
6.2 KiB
Go
202 lines
6.2 KiB
Go
// Copyright 2021 The go-ethereum Authors
|
|
// This file is part of go-ethereum.
|
|
//
|
|
// go-ethereum is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// go-ethereum is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package t8ntool
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/ethereum/go-ethereum/core"
|
|
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/core/vm"
|
|
"github.com/ethereum/go-ethereum/params"
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
"github.com/ethereum/go-ethereum/tests"
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
type result struct {
|
|
Error error
|
|
Address common.Address
|
|
Hash common.Hash
|
|
IntrinsicGas uint64
|
|
}
|
|
|
|
// MarshalJSON marshals as JSON with a hash.
|
|
func (r *result) MarshalJSON() ([]byte, error) {
|
|
type xx struct {
|
|
Error string `json:"error,omitempty"`
|
|
Address *common.Address `json:"address,omitempty"`
|
|
Hash *common.Hash `json:"hash,omitempty"`
|
|
IntrinsicGas hexutil.Uint64 `json:"intrinsicGas,omitempty"`
|
|
}
|
|
var out xx
|
|
if r.Error != nil {
|
|
out.Error = r.Error.Error()
|
|
}
|
|
if r.Address != (common.Address{}) {
|
|
out.Address = &r.Address
|
|
}
|
|
if r.Hash != (common.Hash{}) {
|
|
out.Hash = &r.Hash
|
|
}
|
|
out.IntrinsicGas = hexutil.Uint64(r.IntrinsicGas)
|
|
return json.Marshal(out)
|
|
}
|
|
|
|
func Transaction(ctx *cli.Context) error {
|
|
// We need to load the transactions. May be either in stdin input or in files.
|
|
// Check if anything needs to be read from stdin
|
|
var (
|
|
err error
|
|
txStr = ctx.String(InputTxsFlag.Name)
|
|
inputData = &input{}
|
|
chainConfig *params.ChainConfig
|
|
)
|
|
// Construct the chainconfig
|
|
if cConf, _, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil {
|
|
return NewError(ErrorConfig, fmt.Errorf("failed constructing chain configuration: %v", err))
|
|
} else {
|
|
chainConfig = cConf
|
|
}
|
|
// Set the chain id
|
|
chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
|
|
|
|
var body hexutil.Bytes
|
|
if txStr == stdinSelector {
|
|
decoder := json.NewDecoder(os.Stdin)
|
|
if err := decoder.Decode(inputData); err != nil {
|
|
return NewError(ErrorJson, fmt.Errorf("failed unmarshalling stdin: %v", err))
|
|
}
|
|
// Decode the body of already signed transactions
|
|
body = common.FromHex(inputData.TxRlp)
|
|
} else {
|
|
// Read input from file
|
|
inFile, err := os.Open(txStr)
|
|
if err != nil {
|
|
return NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err))
|
|
}
|
|
defer inFile.Close()
|
|
decoder := json.NewDecoder(inFile)
|
|
if strings.HasSuffix(txStr, ".rlp") {
|
|
if err := decoder.Decode(&body); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
return NewError(ErrorIO, errors.New("only rlp supported"))
|
|
}
|
|
}
|
|
signer := types.MakeSigner(chainConfig, new(big.Int), 0)
|
|
|
|
// We now have the transactions in 'body', which is supposed to be an
|
|
// rlp list of transactions
|
|
it, err := rlp.NewListIterator([]byte(body))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var results []result
|
|
for it.Next() {
|
|
var tx types.Transaction
|
|
err := rlp.DecodeBytes(it.Value(), &tx)
|
|
if err != nil {
|
|
results = append(results, result{Error: err})
|
|
continue
|
|
}
|
|
r := result{Hash: tx.Hash()}
|
|
if sender, err := types.Sender(signer, &tx); err != nil {
|
|
r.Error = err
|
|
results = append(results, r)
|
|
continue
|
|
} else {
|
|
r.Address = sender
|
|
}
|
|
// Check intrinsic gas
|
|
rules := chainConfig.Rules(common.Big0, true, 0)
|
|
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
|
|
if err != nil {
|
|
r.Error = err
|
|
results = append(results, r)
|
|
continue
|
|
}
|
|
r.IntrinsicGas = cost.RegularGas
|
|
if tx.Gas() < cost.RegularGas {
|
|
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), cost.RegularGas)
|
|
results = append(results, r)
|
|
continue
|
|
}
|
|
// For Prague txs, validate the floor data gas.
|
|
if rules.IsPrague {
|
|
floorDataGas, err := core.FloorDataGas(rules, tx.Data())
|
|
if err != nil {
|
|
r.Error = err
|
|
results = append(results, r)
|
|
continue
|
|
}
|
|
if tx.Gas() < floorDataGas {
|
|
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrFloorDataGas, tx.Gas(), floorDataGas)
|
|
results = append(results, r)
|
|
continue
|
|
}
|
|
}
|
|
// Validate <256bit fields
|
|
switch {
|
|
case tx.Nonce()+1 < tx.Nonce():
|
|
r.Error = errors.New("nonce exceeds 2^64-1")
|
|
case tx.Value().BitLen() > 256:
|
|
r.Error = errors.New("value exceeds 256 bits")
|
|
case tx.GasPrice().BitLen() > 256:
|
|
r.Error = errors.New("gasPrice exceeds 256 bits")
|
|
case tx.GasTipCap().BitLen() > 256:
|
|
r.Error = errors.New("maxPriorityFeePerGas exceeds 256 bits")
|
|
case tx.GasFeeCap().BitLen() > 256:
|
|
r.Error = errors.New("maxFeePerGas exceeds 256 bits")
|
|
case tx.GasFeeCap().Cmp(tx.GasTipCap()) < 0:
|
|
r.Error = errors.New("maxFeePerGas < maxPriorityFeePerGas")
|
|
case new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas())).BitLen() > 256:
|
|
r.Error = errors.New("gas * gasPrice exceeds 256 bits")
|
|
case new(big.Int).Mul(tx.GasFeeCap(), new(big.Int).SetUint64(tx.Gas())).BitLen() > 256:
|
|
r.Error = errors.New("gas * maxFeePerGas exceeds 256 bits")
|
|
}
|
|
// Check whether the init code size has been exceeded.
|
|
if tx.To() == nil {
|
|
if err := vm.CheckMaxInitCodeSize(&rules, uint64(len(tx.Data()))); err != nil {
|
|
r.Error = err
|
|
}
|
|
}
|
|
|
|
isOsaka := chainConfig.IsOsaka(new(big.Int), 0)
|
|
isAmsterdam := chainConfig.IsAmsterdam(new(big.Int), 0)
|
|
if isOsaka && !isAmsterdam && tx.Gas() > params.MaxTxGas {
|
|
r.Error = errors.New("gas limit exceeds maximum")
|
|
}
|
|
results = append(results, r)
|
|
}
|
|
if err := it.Err(); err != nil {
|
|
return NewError(ErrorIO, err)
|
|
}
|
|
|
|
out, err := json.MarshalIndent(results, "", " ")
|
|
fmt.Println(string(out))
|
|
return err
|
|
}
|