cmd/evm: make t8ntool handle transaction decoding errors better (#28397)
This change closes https://github.com/ethereum/go-ethereum/issues/27730 . By using an iterator instead of a slice of transactions, we can better handle the case when an individual transaction (within an otherwise well-formed RLP-list) cannot be decoded.
This commit is contained in:
parent
a8617c6d4d
commit
300df874d7
@ -116,8 +116,8 @@ type rejectedTx struct {
|
|||||||
|
|
||||||
// Apply applies a set of transactions to a pre-state
|
// Apply applies a set of transactions to a pre-state
|
||||||
func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
txs types.Transactions, miningReward int64,
|
txIt txIterator, miningReward int64,
|
||||||
getTracerFn func(txIndex int, txHash common.Hash) (tracer vm.EVMLogger, err error)) (*state.StateDB, *ExecutionResult, error) {
|
getTracerFn func(txIndex int, txHash common.Hash) (tracer vm.EVMLogger, err error)) (*state.StateDB, *ExecutionResult, []byte, error) {
|
||||||
// Capture errors for BLOCKHASH operation, if we haven't been supplied the
|
// Capture errors for BLOCKHASH operation, if we haven't been supplied the
|
||||||
// required blockhashes
|
// required blockhashes
|
||||||
var hashError error
|
var hashError error
|
||||||
@ -190,25 +190,39 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
|
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
|
||||||
}
|
}
|
||||||
var blobGasUsed uint64
|
var blobGasUsed uint64
|
||||||
for i, tx := range txs {
|
|
||||||
|
for i := 0; txIt.Next(); i++ {
|
||||||
|
tx, err := txIt.Tx()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("rejected tx", "index", i, "error", err)
|
||||||
|
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
||||||
|
continue
|
||||||
|
}
|
||||||
if tx.Type() == types.BlobTxType && vmContext.BlobBaseFee == nil {
|
if tx.Type() == types.BlobTxType && vmContext.BlobBaseFee == nil {
|
||||||
errMsg := "blob tx used but field env.ExcessBlobGas missing"
|
errMsg := "blob tx used but field env.ExcessBlobGas missing"
|
||||||
log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", errMsg)
|
log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", errMsg)
|
||||||
rejectedTxs = append(rejectedTxs, &rejectedTx{i, errMsg})
|
rejectedTxs = append(rejectedTxs, &rejectedTx{i, errMsg})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if tx.Type() == types.BlobTxType {
|
|
||||||
blobGasUsed += uint64(params.BlobTxBlobGasPerBlob * len(tx.BlobHashes()))
|
|
||||||
}
|
|
||||||
msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee)
|
msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", err)
|
log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", err)
|
||||||
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if tx.Type() == types.BlobTxType {
|
||||||
|
txBlobGas := uint64(params.BlobTxBlobGasPerBlob * len(tx.BlobHashes()))
|
||||||
|
if used, max := blobGasUsed+txBlobGas, uint64(params.MaxBlobGasPerBlock); used > max {
|
||||||
|
err := fmt.Errorf("blob gas (%d) would exceed maximum allowance %d", used, max)
|
||||||
|
log.Warn("rejected tx", "index", i, "err", err)
|
||||||
|
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
blobGasUsed += txBlobGas
|
||||||
|
}
|
||||||
tracer, err := getTracerFn(txIndex, tx.Hash())
|
tracer, err := getTracerFn(txIndex, tx.Hash())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
vmConfig.Tracer = tracer
|
vmConfig.Tracer = tracer
|
||||||
statedb.SetTxContext(tx.Hash(), txIndex)
|
statedb.SetTxContext(tx.Hash(), txIndex)
|
||||||
@ -231,7 +245,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||||||
}
|
}
|
||||||
includedTxs = append(includedTxs, tx)
|
includedTxs = append(includedTxs, tx)
|
||||||
if hashError != nil {
|
if hashError != nil {
|
||||||
return nil, nil, NewError(ErrorMissingBlockhash, hashError)
|
return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError)
|
||||||
}
|
}
|
||||||
gasUsed += msgResult.UsedGas
|
gasUsed += msgResult.UsedGas
|
||||||
|
|
||||||
@ -306,7 +320,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||||||
// Commit block
|
// Commit block
|
||||||
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber))
|
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err))
|
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err))
|
||||||
}
|
}
|
||||||
execRs := &ExecutionResult{
|
execRs := &ExecutionResult{
|
||||||
StateRoot: root,
|
StateRoot: root,
|
||||||
@ -332,9 +346,10 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||||||
// for accessing latest states.
|
// for accessing latest states.
|
||||||
statedb, err = state.New(root, statedb.Database(), nil)
|
statedb, err = state.New(root, statedb.Database(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not reopen state: %v", err))
|
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not reopen state: %v", err))
|
||||||
}
|
}
|
||||||
return statedb, execRs, nil
|
body, _ := rlp.EncodeToBytes(includedTxs)
|
||||||
|
return statedb, execRs, body, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
|
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
|
||||||
|
@ -17,14 +17,12 @@
|
|||||||
package t8ntool
|
package t8ntool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
@ -33,11 +31,9 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"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"
|
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
@ -147,7 +143,7 @@ func Transition(ctx *cli.Context) error {
|
|||||||
// Check if anything needs to be read from stdin
|
// Check if anything needs to be read from stdin
|
||||||
var (
|
var (
|
||||||
prestate Prestate
|
prestate Prestate
|
||||||
txs types.Transactions // txs to apply
|
txIt txIterator // txs to apply
|
||||||
allocStr = ctx.String(InputAllocFlag.Name)
|
allocStr = ctx.String(InputAllocFlag.Name)
|
||||||
|
|
||||||
envStr = ctx.String(InputEnvFlag.Name)
|
envStr = ctx.String(InputEnvFlag.Name)
|
||||||
@ -192,7 +188,7 @@ func Transition(ctx *cli.Context) error {
|
|||||||
// Set the chain id
|
// Set the chain id
|
||||||
chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
|
chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
|
||||||
|
|
||||||
if txs, err = loadTransactions(txStr, inputData, prestate.Env, chainConfig); err != nil {
|
if txIt, err = loadTransactions(txStr, inputData, prestate.Env, chainConfig); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := applyLondonChecks(&prestate.Env, chainConfig); err != nil {
|
if err := applyLondonChecks(&prestate.Env, chainConfig); err != nil {
|
||||||
@ -208,136 +204,16 @@ func Transition(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Run the test and aggregate the result
|
// Run the test and aggregate the result
|
||||||
s, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer)
|
s, result, body, err := prestate.Apply(vmConfig, chainConfig, txIt, ctx.Int64(RewardFlag.Name), getTracer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
body, _ := rlp.EncodeToBytes(txs)
|
|
||||||
// Dump the excution result
|
// Dump the excution result
|
||||||
collector := make(Alloc)
|
collector := make(Alloc)
|
||||||
s.DumpToCollector(collector, nil)
|
s.DumpToCollector(collector, nil)
|
||||||
return dispatchOutput(ctx, baseDir, result, collector, body)
|
return dispatchOutput(ctx, baseDir, result, collector, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
// txWithKey is a helper-struct, to allow us to use the types.Transaction along with
|
|
||||||
// a `secretKey`-field, for input
|
|
||||||
type txWithKey struct {
|
|
||||||
key *ecdsa.PrivateKey
|
|
||||||
tx *types.Transaction
|
|
||||||
protected bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *txWithKey) UnmarshalJSON(input []byte) error {
|
|
||||||
// Read the metadata, if present
|
|
||||||
type txMetadata struct {
|
|
||||||
Key *common.Hash `json:"secretKey"`
|
|
||||||
Protected *bool `json:"protected"`
|
|
||||||
}
|
|
||||||
var data txMetadata
|
|
||||||
if err := json.Unmarshal(input, &data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if data.Key != nil {
|
|
||||||
k := data.Key.Hex()[2:]
|
|
||||||
if ecdsaKey, err := crypto.HexToECDSA(k); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
t.key = ecdsaKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if data.Protected != nil {
|
|
||||||
t.protected = *data.Protected
|
|
||||||
} else {
|
|
||||||
t.protected = true
|
|
||||||
}
|
|
||||||
// Now, read the transaction itself
|
|
||||||
var tx types.Transaction
|
|
||||||
if err := json.Unmarshal(input, &tx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
t.tx = &tx
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// signUnsignedTransactions converts the input txs to canonical transactions.
|
|
||||||
//
|
|
||||||
// The transactions can have two forms, either
|
|
||||||
// 1. unsigned or
|
|
||||||
// 2. signed
|
|
||||||
//
|
|
||||||
// For (1), r, s, v, need so be zero, and the `secretKey` needs to be set.
|
|
||||||
// If so, we sign it here and now, with the given `secretKey`
|
|
||||||
// If the condition above is not met, then it's considered a signed transaction.
|
|
||||||
//
|
|
||||||
// To manage this, we read the transactions twice, first trying to read the secretKeys,
|
|
||||||
// and secondly to read them with the standard tx json format
|
|
||||||
func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Transactions, error) {
|
|
||||||
var signedTxs []*types.Transaction
|
|
||||||
for i, tx := range txs {
|
|
||||||
var (
|
|
||||||
v, r, s = tx.tx.RawSignatureValues()
|
|
||||||
signed *types.Transaction
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
if tx.key == nil || v.BitLen()+r.BitLen()+s.BitLen() != 0 {
|
|
||||||
// Already signed
|
|
||||||
signedTxs = append(signedTxs, tx.tx)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// This transaction needs to be signed
|
|
||||||
if tx.protected {
|
|
||||||
signed, err = types.SignTx(tx.tx, signer, tx.key)
|
|
||||||
} else {
|
|
||||||
signed, err = types.SignTx(tx.tx, types.FrontierSigner{}, tx.key)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, NewError(ErrorJson, fmt.Errorf("tx %d: failed to sign tx: %v", i, err))
|
|
||||||
}
|
|
||||||
signedTxs = append(signedTxs, signed)
|
|
||||||
}
|
|
||||||
return signedTxs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadTransactions(txStr string, inputData *input, env stEnv, chainConfig *params.ChainConfig) (types.Transactions, error) {
|
|
||||||
var txsWithKeys []*txWithKey
|
|
||||||
var signed types.Transactions
|
|
||||||
if txStr != stdinSelector {
|
|
||||||
data, err := os.ReadFile(txStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err))
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(txStr, ".rlp") { // A file containing an rlp list
|
|
||||||
var body hexutil.Bytes
|
|
||||||
if err := json.Unmarshal(data, &body); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Already signed transactions
|
|
||||||
if err := rlp.DecodeBytes(body, &signed); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return signed, nil
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(data, &txsWithKeys); err != nil {
|
|
||||||
return nil, NewError(ErrorJson, fmt.Errorf("failed unmarshaling txs-file: %v", err))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if len(inputData.TxRlp) > 0 {
|
|
||||||
// Decode the body of already signed transactions
|
|
||||||
body := common.FromHex(inputData.TxRlp)
|
|
||||||
// Already signed transactions
|
|
||||||
if err := rlp.DecodeBytes(body, &signed); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return signed, nil
|
|
||||||
}
|
|
||||||
// JSON encoded transactions
|
|
||||||
txsWithKeys = inputData.Txs
|
|
||||||
}
|
|
||||||
// We may have to sign the transactions.
|
|
||||||
signer := types.LatestSignerForChainID(chainConfig.ChainID)
|
|
||||||
return signUnsignedTransactions(txsWithKeys, signer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error {
|
func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error {
|
||||||
if !chainConfig.IsLondon(big.NewInt(int64(env.Number))) {
|
if !chainConfig.IsLondon(big.NewInt(int64(env.Number))) {
|
||||||
return nil
|
return nil
|
||||||
|
194
cmd/evm/internal/t8ntool/tx_iterator.go
Normal file
194
cmd/evm/internal/t8ntool/tx_iterator.go
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
// Copyright 2023 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 (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// txWithKey is a helper-struct, to allow us to use the types.Transaction along with
|
||||||
|
// a `secretKey`-field, for input
|
||||||
|
type txWithKey struct {
|
||||||
|
key *ecdsa.PrivateKey
|
||||||
|
tx *types.Transaction
|
||||||
|
protected bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *txWithKey) UnmarshalJSON(input []byte) error {
|
||||||
|
// Read the metadata, if present
|
||||||
|
type txMetadata struct {
|
||||||
|
Key *common.Hash `json:"secretKey"`
|
||||||
|
Protected *bool `json:"protected"`
|
||||||
|
}
|
||||||
|
var data txMetadata
|
||||||
|
if err := json.Unmarshal(input, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if data.Key != nil {
|
||||||
|
k := data.Key.Hex()[2:]
|
||||||
|
if ecdsaKey, err := crypto.HexToECDSA(k); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
t.key = ecdsaKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.Protected != nil {
|
||||||
|
t.protected = *data.Protected
|
||||||
|
} else {
|
||||||
|
t.protected = true
|
||||||
|
}
|
||||||
|
// Now, read the transaction itself
|
||||||
|
var tx types.Transaction
|
||||||
|
if err := json.Unmarshal(input, &tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.tx = &tx
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// signUnsignedTransactions converts the input txs to canonical transactions.
|
||||||
|
//
|
||||||
|
// The transactions can have two forms, either
|
||||||
|
// 1. unsigned or
|
||||||
|
// 2. signed
|
||||||
|
//
|
||||||
|
// For (1), r, s, v, need so be zero, and the `secretKey` needs to be set.
|
||||||
|
// If so, we sign it here and now, with the given `secretKey`
|
||||||
|
// If the condition above is not met, then it's considered a signed transaction.
|
||||||
|
//
|
||||||
|
// To manage this, we read the transactions twice, first trying to read the secretKeys,
|
||||||
|
// and secondly to read them with the standard tx json format
|
||||||
|
func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Transactions, error) {
|
||||||
|
var signedTxs []*types.Transaction
|
||||||
|
for i, tx := range txs {
|
||||||
|
var (
|
||||||
|
v, r, s = tx.tx.RawSignatureValues()
|
||||||
|
signed *types.Transaction
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if tx.key == nil || v.BitLen()+r.BitLen()+s.BitLen() != 0 {
|
||||||
|
// Already signed
|
||||||
|
signedTxs = append(signedTxs, tx.tx)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// This transaction needs to be signed
|
||||||
|
if tx.protected {
|
||||||
|
signed, err = types.SignTx(tx.tx, signer, tx.key)
|
||||||
|
} else {
|
||||||
|
signed, err = types.SignTx(tx.tx, types.FrontierSigner{}, tx.key)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewError(ErrorJson, fmt.Errorf("tx %d: failed to sign tx: %v", i, err))
|
||||||
|
}
|
||||||
|
signedTxs = append(signedTxs, signed)
|
||||||
|
}
|
||||||
|
return signedTxs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadTransactions(txStr string, inputData *input, env stEnv, chainConfig *params.ChainConfig) (txIterator, error) {
|
||||||
|
var txsWithKeys []*txWithKey
|
||||||
|
if txStr != stdinSelector {
|
||||||
|
data, err := os.ReadFile(txStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err))
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(txStr, ".rlp") { // A file containing an rlp list
|
||||||
|
var body hexutil.Bytes
|
||||||
|
if err := json.Unmarshal(data, &body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newRlpTxIterator(body), nil
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(data, &txsWithKeys); err != nil {
|
||||||
|
return nil, NewError(ErrorJson, fmt.Errorf("failed unmarshaling txs-file: %v", err))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(inputData.TxRlp) > 0 {
|
||||||
|
// Decode the body of already signed transactions
|
||||||
|
return newRlpTxIterator(common.FromHex(inputData.TxRlp)), nil
|
||||||
|
}
|
||||||
|
// JSON encoded transactions
|
||||||
|
txsWithKeys = inputData.Txs
|
||||||
|
}
|
||||||
|
// We may have to sign the transactions.
|
||||||
|
signer := types.LatestSignerForChainID(chainConfig.ChainID)
|
||||||
|
txs, err := signUnsignedTransactions(txsWithKeys, signer)
|
||||||
|
return newSliceTxIterator(txs), err
|
||||||
|
}
|
||||||
|
|
||||||
|
type txIterator interface {
|
||||||
|
// Next returns true until EOF
|
||||||
|
Next() bool
|
||||||
|
// Tx returns the next transaction, OR an error.
|
||||||
|
Tx() (*types.Transaction, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceTxIterator struct {
|
||||||
|
idx int
|
||||||
|
txs []*types.Transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSliceTxIterator(transactions types.Transactions) txIterator {
|
||||||
|
return &sliceTxIterator{0, transactions}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ait *sliceTxIterator) Next() bool {
|
||||||
|
return ait.idx < len(ait.txs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ait *sliceTxIterator) Tx() (*types.Transaction, error) {
|
||||||
|
if ait.idx < len(ait.txs) {
|
||||||
|
ait.idx++
|
||||||
|
return ait.txs[ait.idx-1], nil
|
||||||
|
}
|
||||||
|
return nil, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
type rlpTxIterator struct {
|
||||||
|
in *rlp.Stream
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRlpTxIterator(rlpData []byte) txIterator {
|
||||||
|
in := rlp.NewStream(bytes.NewBuffer(rlpData), 1024*1024)
|
||||||
|
in.List()
|
||||||
|
return &rlpTxIterator{in}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *rlpTxIterator) Next() bool {
|
||||||
|
return it.in.MoreDataInList()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *rlpTxIterator) Tx() (*types.Transaction, error) {
|
||||||
|
var a types.Transaction
|
||||||
|
if err := it.in.Decode(&a); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &a, nil
|
||||||
|
}
|
@ -275,6 +275,14 @@ func TestT8n(t *testing.T) {
|
|||||||
output: t8nOutput{alloc: true, result: true},
|
output: t8nOutput{alloc: true, result: true},
|
||||||
expOut: "exp.json",
|
expOut: "exp.json",
|
||||||
},
|
},
|
||||||
|
{ // More cancun test, plus example of rlp-transaction that cannot be decoded properly
|
||||||
|
base: "./testdata/30",
|
||||||
|
input: t8nInput{
|
||||||
|
"alloc.json", "txs_more.rlp", "env.json", "Cancun", "",
|
||||||
|
},
|
||||||
|
output: t8nOutput{alloc: true, result: true},
|
||||||
|
expOut: "exp.json",
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
args := []string{"t8n"}
|
args := []string{"t8n"}
|
||||||
args = append(args, tc.output.get()...)
|
args = append(args, tc.output.get()...)
|
||||||
|
77
cmd/evm/testdata/30/README.txt
vendored
Normal file
77
cmd/evm/testdata/30/README.txt
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
This example comes from https://github.com/ethereum/go-ethereum/issues/27730.
|
||||||
|
The input transactions contain three transactions, number `0` and `2` are taken from
|
||||||
|
`testdata/13`, whereas number `1` is taken from #27730.
|
||||||
|
|
||||||
|
The problematic second transaction cannot be RLP-decoded, and the expectation is
|
||||||
|
that that particular transaction should be rejected, but number `0` and `1` should
|
||||||
|
still be accepted.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ go run . t8n --input.alloc=./testdata/30/alloc.json --input.txs=./testdata/30/txs_more.rlp --input.env=./testdata/30/env.json --output.result=stdout --output.alloc=stdout --state.fork=Cancun
|
||||||
|
WARN [10-22|15:38:03.283] rejected tx index=1 error="rlp: input string too short for common.Address, decoding into (types.Transaction)(types.BlobTx).To"
|
||||||
|
INFO [10-22|15:38:03.284] Trie dumping started root=348312..915c93
|
||||||
|
INFO [10-22|15:38:03.284] Trie dumping complete accounts=3 elapsed="160.831µs"
|
||||||
|
{
|
||||||
|
"alloc": {
|
||||||
|
"0x095e7baea6a6c7c4c2dfeb977efac326af552d87": {
|
||||||
|
"code": "0x60004960005500",
|
||||||
|
"balance": "0xde0b6b3a7640000"
|
||||||
|
},
|
||||||
|
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||||
|
"balance": "0xde0b6b3a7640000"
|
||||||
|
},
|
||||||
|
"0xd02d72e067e77158444ef2020ff2d325f929b363": {
|
||||||
|
"balance": "0xfffffffb8390",
|
||||||
|
"nonce": "0x3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"stateRoot": "0x3483124b6710486c9fb3e07975669c66924697c88cccdcc166af5e1218915c93",
|
||||||
|
"txRoot": "0x013509c8563d41c0ae4bf38f2d6d19fc6512a1d0d6be045079c8c9f68bf45f9d",
|
||||||
|
"receiptsRoot": "0x75308898d571eafb5cd8cde8278bf5b3d13c5f6ec074926de3bb895b519264e1",
|
||||||
|
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"receipts": [
|
||||||
|
{
|
||||||
|
"type": "0x2",
|
||||||
|
"root": "0x",
|
||||||
|
"status": "0x1",
|
||||||
|
"cumulativeGasUsed": "0x5208",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"logs": null,
|
||||||
|
"transactionHash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476",
|
||||||
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
|
"gasUsed": "0x5208",
|
||||||
|
"effectiveGasPrice": null,
|
||||||
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"transactionIndex": "0x0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "0x2",
|
||||||
|
"root": "0x",
|
||||||
|
"status": "0x1",
|
||||||
|
"cumulativeGasUsed": "0xa410",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"logs": null,
|
||||||
|
"transactionHash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a",
|
||||||
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
|
"gasUsed": "0x5208",
|
||||||
|
"effectiveGasPrice": null,
|
||||||
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"transactionIndex": "0x1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rejected": [
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"error": "rlp: input string too short for common.Address, decoding into (types.Transaction)(types.BlobTx).To"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"currentDifficulty": null,
|
||||||
|
"gasUsed": "0xa410",
|
||||||
|
"currentBaseFee": "0x7",
|
||||||
|
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
23
cmd/evm/testdata/30/alloc.json
vendored
Normal file
23
cmd/evm/testdata/30/alloc.json
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"0x095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
|
||||||
|
"balance" : "0x0de0b6b3a7640000",
|
||||||
|
"code" : "0x60004960005500",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0xd02d72e067e77158444ef2020ff2d325f929b363" : {
|
||||||
|
"balance": "0x01000000000000",
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "0x01",
|
||||||
|
"storage": {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "0x0de0b6b3a7640000",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
cmd/evm/testdata/30/env.json
vendored
Normal file
23
cmd/evm/testdata/30/env.json
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
|
||||||
|
"currentNumber" : "0x01",
|
||||||
|
"currentTimestamp" : "0x03e8",
|
||||||
|
"currentGasLimit" : "0x1000000000",
|
||||||
|
"previousHash" : "0xe4e2a30b340bec696242b67584264f878600dce98354ae0b6328740fd4ff18da",
|
||||||
|
"currentDataGasUsed" : "0x2000",
|
||||||
|
"parentTimestamp" : "0x00",
|
||||||
|
"parentDifficulty" : "0x00",
|
||||||
|
"parentUncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"parentBeaconBlockRoot" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"currentRandom" : "0x0000000000000000000000000000000000000000000000000000000000020000",
|
||||||
|
"withdrawals" : [
|
||||||
|
],
|
||||||
|
"parentBaseFee" : "0x08",
|
||||||
|
"parentGasUsed" : "0x00",
|
||||||
|
"parentGasLimit" : "0x1000000000",
|
||||||
|
"parentExcessBlobGas" : "0x1000",
|
||||||
|
"parentBlobGasUsed" : "0x2000",
|
||||||
|
"blockHashes" : {
|
||||||
|
"0" : "0xe4e2a30b340bec696242b67584264f878600dce98354ae0b6328740fd4ff18da"
|
||||||
|
}
|
||||||
|
}
|
64
cmd/evm/testdata/30/exp.json
vendored
Normal file
64
cmd/evm/testdata/30/exp.json
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"alloc": {
|
||||||
|
"0x095e7baea6a6c7c4c2dfeb977efac326af552d87": {
|
||||||
|
"code": "0x60004960005500",
|
||||||
|
"balance": "0xde0b6b3a7640000"
|
||||||
|
},
|
||||||
|
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||||
|
"balance": "0xde0b6b3a7640000"
|
||||||
|
},
|
||||||
|
"0xd02d72e067e77158444ef2020ff2d325f929b363": {
|
||||||
|
"balance": "0xfffffffb8390",
|
||||||
|
"nonce": "0x3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"stateRoot": "0x3483124b6710486c9fb3e07975669c66924697c88cccdcc166af5e1218915c93",
|
||||||
|
"txRoot": "0x013509c8563d41c0ae4bf38f2d6d19fc6512a1d0d6be045079c8c9f68bf45f9d",
|
||||||
|
"receiptsRoot": "0x75308898d571eafb5cd8cde8278bf5b3d13c5f6ec074926de3bb895b519264e1",
|
||||||
|
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"receipts": [
|
||||||
|
{
|
||||||
|
"type": "0x2",
|
||||||
|
"root": "0x",
|
||||||
|
"status": "0x1",
|
||||||
|
"cumulativeGasUsed": "0x5208",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"logs": null,
|
||||||
|
"transactionHash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476",
|
||||||
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
|
"gasUsed": "0x5208",
|
||||||
|
"effectiveGasPrice": null,
|
||||||
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"transactionIndex": "0x0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "0x2",
|
||||||
|
"root": "0x",
|
||||||
|
"status": "0x1",
|
||||||
|
"cumulativeGasUsed": "0xa410",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"logs": null,
|
||||||
|
"transactionHash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a",
|
||||||
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
|
"gasUsed": "0x5208",
|
||||||
|
"effectiveGasPrice": null,
|
||||||
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"transactionIndex": "0x1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rejected": [
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"error": "rlp: input string too short for common.Address, decoding into (types.Transaction)(types.BlobTx).To"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"currentDifficulty": null,
|
||||||
|
"gasUsed": "0xa410",
|
||||||
|
"currentBaseFee": "0x7",
|
||||||
|
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"currentExcessBlobGas": "0x0",
|
||||||
|
"blobGasUsed": "0x0"
|
||||||
|
}
|
||||||
|
}
|
1
cmd/evm/testdata/30/txs.rlp
vendored
Normal file
1
cmd/evm/testdata/30/txs.rlp
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
"0xf8dbb8d903f8d601800285012a05f200833d090080830186a000f85bf85994095e7baea6a6c7c4c2dfeb977efac326af552d87f842a00000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010ae1a001a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d880a0fc12b67159a3567f8bdbc49e0be369a2e20e09d57a51c41310543a4128409464a02de0cfe5495c4f58ff60645ceda0afd67a4c90a70bc89fe207269435b35e5b67"
|
1
cmd/evm/testdata/30/txs_more.rlp
vendored
Normal file
1
cmd/evm/testdata/30/txs_more.rlp
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
"0xf901adb86702f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904b8d903f8d601800285012a05f200833d090080830186a000f85bf85994095e7baea6a6c7c4c2dfeb977efac326af552d87f842a00000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010ae1a001a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d880a0fc12b67159a3567f8bdbc49e0be369a2e20e09d57a51c41310543a4128409464a02de0cfe5495c4f58ff60645ceda0afd67a4c90a70bc89fe207269435b35e5b67b86702f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9"
|
Loading…
Reference in New Issue
Block a user