github.com/ethereum/go-ethereum@v1.16.1/cmd/evm/internal/t8ntool/transaction.go (about)

     1  // Copyright 2021 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package t8ntool
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"math/big"
    24  	"os"
    25  	"strings"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/common/hexutil"
    29  	"github.com/ethereum/go-ethereum/core"
    30  	"github.com/ethereum/go-ethereum/core/types"
    31  	"github.com/ethereum/go-ethereum/params"
    32  	"github.com/ethereum/go-ethereum/rlp"
    33  	"github.com/ethereum/go-ethereum/tests"
    34  	"github.com/urfave/cli/v2"
    35  )
    36  
    37  type result struct {
    38  	Error        error
    39  	Address      common.Address
    40  	Hash         common.Hash
    41  	IntrinsicGas uint64
    42  }
    43  
    44  // MarshalJSON marshals as JSON with a hash.
    45  func (r *result) MarshalJSON() ([]byte, error) {
    46  	type xx struct {
    47  		Error        string          `json:"error,omitempty"`
    48  		Address      *common.Address `json:"address,omitempty"`
    49  		Hash         *common.Hash    `json:"hash,omitempty"`
    50  		IntrinsicGas hexutil.Uint64  `json:"intrinsicGas,omitempty"`
    51  	}
    52  	var out xx
    53  	if r.Error != nil {
    54  		out.Error = r.Error.Error()
    55  	}
    56  	if r.Address != (common.Address{}) {
    57  		out.Address = &r.Address
    58  	}
    59  	if r.Hash != (common.Hash{}) {
    60  		out.Hash = &r.Hash
    61  	}
    62  	out.IntrinsicGas = hexutil.Uint64(r.IntrinsicGas)
    63  	return json.Marshal(out)
    64  }
    65  
    66  func Transaction(ctx *cli.Context) error {
    67  	// We need to load the transactions. May be either in stdin input or in files.
    68  	// Check if anything needs to be read from stdin
    69  	var (
    70  		err         error
    71  		txStr       = ctx.String(InputTxsFlag.Name)
    72  		inputData   = &input{}
    73  		chainConfig *params.ChainConfig
    74  	)
    75  	// Construct the chainconfig
    76  	if cConf, _, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil {
    77  		return NewError(ErrorConfig, fmt.Errorf("failed constructing chain configuration: %v", err))
    78  	} else {
    79  		chainConfig = cConf
    80  	}
    81  	// Set the chain id
    82  	chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
    83  
    84  	var body hexutil.Bytes
    85  	if txStr == stdinSelector {
    86  		decoder := json.NewDecoder(os.Stdin)
    87  		if err := decoder.Decode(inputData); err != nil {
    88  			return NewError(ErrorJson, fmt.Errorf("failed unmarshalling stdin: %v", err))
    89  		}
    90  		// Decode the body of already signed transactions
    91  		body = common.FromHex(inputData.TxRlp)
    92  	} else {
    93  		// Read input from file
    94  		inFile, err := os.Open(txStr)
    95  		if err != nil {
    96  			return NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err))
    97  		}
    98  		defer inFile.Close()
    99  		decoder := json.NewDecoder(inFile)
   100  		if strings.HasSuffix(txStr, ".rlp") {
   101  			if err := decoder.Decode(&body); err != nil {
   102  				return err
   103  			}
   104  		} else {
   105  			return NewError(ErrorIO, errors.New("only rlp supported"))
   106  		}
   107  	}
   108  	signer := types.MakeSigner(chainConfig, new(big.Int), 0)
   109  
   110  	// We now have the transactions in 'body', which is supposed to be an
   111  	// rlp list of transactions
   112  	it, err := rlp.NewListIterator([]byte(body))
   113  	if err != nil {
   114  		return err
   115  	}
   116  	var results []result
   117  	for it.Next() {
   118  		if err := it.Err(); err != nil {
   119  			return NewError(ErrorIO, err)
   120  		}
   121  		var tx types.Transaction
   122  		err := rlp.DecodeBytes(it.Value(), &tx)
   123  		if err != nil {
   124  			results = append(results, result{Error: err})
   125  			continue
   126  		}
   127  		r := result{Hash: tx.Hash()}
   128  		if sender, err := types.Sender(signer, &tx); err != nil {
   129  			r.Error = err
   130  			results = append(results, r)
   131  			continue
   132  		} else {
   133  			r.Address = sender
   134  		}
   135  		// Check intrinsic gas
   136  		rules := chainConfig.Rules(common.Big0, true, 0)
   137  		gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
   138  		if err != nil {
   139  			r.Error = err
   140  			results = append(results, r)
   141  			continue
   142  		}
   143  		r.IntrinsicGas = gas
   144  		if tx.Gas() < gas {
   145  			r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), gas)
   146  			results = append(results, r)
   147  			continue
   148  		}
   149  		// For Prague txs, validate the floor data gas.
   150  		if rules.IsPrague {
   151  			floorDataGas, err := core.FloorDataGas(tx.Data())
   152  			if err != nil {
   153  				r.Error = err
   154  				results = append(results, r)
   155  				continue
   156  			}
   157  			if tx.Gas() < floorDataGas {
   158  				r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrFloorDataGas, tx.Gas(), floorDataGas)
   159  				results = append(results, r)
   160  				continue
   161  			}
   162  		}
   163  		// Validate <256bit fields
   164  		switch {
   165  		case tx.Nonce()+1 < tx.Nonce():
   166  			r.Error = errors.New("nonce exceeds 2^64-1")
   167  		case tx.Value().BitLen() > 256:
   168  			r.Error = errors.New("value exceeds 256 bits")
   169  		case tx.GasPrice().BitLen() > 256:
   170  			r.Error = errors.New("gasPrice exceeds 256 bits")
   171  		case tx.GasTipCap().BitLen() > 256:
   172  			r.Error = errors.New("maxPriorityFeePerGas exceeds 256 bits")
   173  		case tx.GasFeeCap().BitLen() > 256:
   174  			r.Error = errors.New("maxFeePerGas exceeds 256 bits")
   175  		case tx.GasFeeCap().Cmp(tx.GasTipCap()) < 0:
   176  			r.Error = errors.New("maxFeePerGas < maxPriorityFeePerGas")
   177  		case new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas())).BitLen() > 256:
   178  			r.Error = errors.New("gas * gasPrice exceeds 256 bits")
   179  		case new(big.Int).Mul(tx.GasFeeCap(), new(big.Int).SetUint64(tx.Gas())).BitLen() > 256:
   180  			r.Error = errors.New("gas * maxFeePerGas exceeds 256 bits")
   181  		}
   182  		// Check whether the init code size has been exceeded.
   183  		if chainConfig.IsShanghai(new(big.Int), 0) && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize {
   184  			r.Error = errors.New("max initcode size exceeded")
   185  		}
   186  		results = append(results, r)
   187  	}
   188  	out, err := json.MarshalIndent(results, "", "  ")
   189  	fmt.Println(string(out))
   190  	return err
   191  }