github.com/ethxdao/go-ethereum@v0.0.0-20221218102228-5ae34a9cc189/cmd/evm/internal/t8ntool/transition.go (about)

     1  // Copyright 2020 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  	"crypto/ecdsa"
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"math/big"
    25  	"os"
    26  	"path"
    27  	"strings"
    28  
    29  	"github.com/ethxdao/go-ethereum/common"
    30  	"github.com/ethxdao/go-ethereum/common/hexutil"
    31  	"github.com/ethxdao/go-ethereum/core"
    32  	"github.com/ethxdao/go-ethereum/core/state"
    33  	"github.com/ethxdao/go-ethereum/core/types"
    34  	"github.com/ethxdao/go-ethereum/core/vm"
    35  	"github.com/ethxdao/go-ethereum/crypto"
    36  	"github.com/ethxdao/go-ethereum/eth/tracers/logger"
    37  	"github.com/ethxdao/go-ethereum/log"
    38  	"github.com/ethxdao/go-ethereum/params"
    39  	"github.com/ethxdao/go-ethereum/rlp"
    40  	"github.com/ethxdao/go-ethereum/tests"
    41  )
    42  
    43  const (
    44  	ErrorEVM              = 2
    45  	ErrorConfig           = 3
    46  	ErrorMissingBlockhash = 4
    47  
    48  	ErrorJson = 10
    49  	ErrorIO   = 11
    50  	ErrorRlp  = 12
    51  
    52  	stdinSelector = "stdin"
    53  )
    54  
    55  type NumberedError struct {
    56  	errorCode int
    57  	err       error
    58  }
    59  
    60  func NewError(errorCode int, err error) *NumberedError {
    61  	return &NumberedError{errorCode, err}
    62  }
    63  
    64  func (n *NumberedError) Error() string {
    65  	return fmt.Sprintf("ERROR(%d): %v", n.errorCode, n.err.Error())
    66  }
    67  
    68  func (n *NumberedError) ExitCode() int {
    69  	return n.errorCode
    70  }
    71  
    72  // compile-time conformance test
    73  var (
    74  	_ cli.ExitCoder = (*NumberedError)(nil)
    75  )
    76  
    77  type input struct {
    78  	Alloc core.GenesisAlloc `json:"alloc,omitempty"`
    79  	Env   *stEnv            `json:"env,omitempty"`
    80  	Txs   []*txWithKey      `json:"txs,omitempty"`
    81  	TxRlp string            `json:"txsRlp,omitempty"`
    82  }
    83  
    84  func Transition(ctx *cli.Context) error {
    85  	// Configure the go-ethereum logger
    86  	glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
    87  	glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
    88  	log.Root().SetHandler(glogger)
    89  
    90  	var (
    91  		err    error
    92  		tracer vm.EVMLogger
    93  	)
    94  	var getTracer func(txIndex int, txHash common.Hash) (vm.EVMLogger, error)
    95  
    96  	baseDir, err := createBasedir(ctx)
    97  	if err != nil {
    98  		return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err))
    99  	}
   100  	if ctx.Bool(TraceFlag.Name) {
   101  		if ctx.IsSet(TraceDisableMemoryFlag.Name) && ctx.IsSet(TraceEnableMemoryFlag.Name) {
   102  			return NewError(ErrorConfig, fmt.Errorf("can't use both flags --%s and --%s", TraceDisableMemoryFlag.Name, TraceEnableMemoryFlag.Name))
   103  		}
   104  		if ctx.IsSet(TraceDisableReturnDataFlag.Name) && ctx.IsSet(TraceEnableReturnDataFlag.Name) {
   105  			return NewError(ErrorConfig, fmt.Errorf("can't use both flags --%s and --%s", TraceDisableReturnDataFlag.Name, TraceEnableReturnDataFlag.Name))
   106  		}
   107  		if ctx.IsSet(TraceDisableMemoryFlag.Name) {
   108  			log.Warn(fmt.Sprintf("--%s has been deprecated in favour of --%s", TraceDisableMemoryFlag.Name, TraceEnableMemoryFlag.Name))
   109  		}
   110  		if ctx.IsSet(TraceDisableReturnDataFlag.Name) {
   111  			log.Warn(fmt.Sprintf("--%s has been deprecated in favour of --%s", TraceDisableReturnDataFlag.Name, TraceEnableReturnDataFlag.Name))
   112  		}
   113  		// Configure the EVM logger
   114  		logConfig := &logger.Config{
   115  			DisableStack:     ctx.Bool(TraceDisableStackFlag.Name),
   116  			EnableMemory:     !ctx.Bool(TraceDisableMemoryFlag.Name) || ctx.Bool(TraceEnableMemoryFlag.Name),
   117  			EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name) || ctx.Bool(TraceEnableReturnDataFlag.Name),
   118  			Debug:            true,
   119  		}
   120  		var prevFile *os.File
   121  		// This one closes the last file
   122  		defer func() {
   123  			if prevFile != nil {
   124  				prevFile.Close()
   125  			}
   126  		}()
   127  		getTracer = func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) {
   128  			if prevFile != nil {
   129  				prevFile.Close()
   130  			}
   131  			traceFile, err := os.Create(path.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String())))
   132  			if err != nil {
   133  				return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
   134  			}
   135  			prevFile = traceFile
   136  			return logger.NewJSONLogger(logConfig, traceFile), nil
   137  		}
   138  	} else {
   139  		getTracer = func(txIndex int, txHash common.Hash) (tracer vm.EVMLogger, err error) {
   140  			return nil, nil
   141  		}
   142  	}
   143  	// We need to load three things: alloc, env and transactions. May be either in
   144  	// stdin input or in files.
   145  	// Check if anything needs to be read from stdin
   146  	var (
   147  		prestate Prestate
   148  		txs      types.Transactions // txs to apply
   149  		allocStr = ctx.String(InputAllocFlag.Name)
   150  
   151  		envStr    = ctx.String(InputEnvFlag.Name)
   152  		txStr     = ctx.String(InputTxsFlag.Name)
   153  		inputData = &input{}
   154  	)
   155  	// Figure out the prestate alloc
   156  	if allocStr == stdinSelector || envStr == stdinSelector || txStr == stdinSelector {
   157  		decoder := json.NewDecoder(os.Stdin)
   158  		if err := decoder.Decode(inputData); err != nil {
   159  			return NewError(ErrorJson, fmt.Errorf("failed unmarshaling stdin: %v", err))
   160  		}
   161  	}
   162  	if allocStr != stdinSelector {
   163  		if err := readFile(allocStr, "alloc", &inputData.Alloc); err != nil {
   164  			return err
   165  		}
   166  	}
   167  	prestate.Pre = inputData.Alloc
   168  
   169  	// Set the block environment
   170  	if envStr != stdinSelector {
   171  		var env stEnv
   172  		if err := readFile(envStr, "env", &env); err != nil {
   173  			return err
   174  		}
   175  		inputData.Env = &env
   176  	}
   177  	prestate.Env = *inputData.Env
   178  
   179  	vmConfig := vm.Config{
   180  		Tracer: tracer,
   181  		Debug:  (tracer != nil),
   182  	}
   183  	// Construct the chainconfig
   184  	var chainConfig *params.ChainConfig
   185  	if cConf, extraEips, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil {
   186  		return NewError(ErrorConfig, fmt.Errorf("failed constructing chain configuration: %v", err))
   187  	} else {
   188  		chainConfig = cConf
   189  		vmConfig.ExtraEips = extraEips
   190  	}
   191  	// Set the chain id
   192  	chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
   193  
   194  	var txsWithKeys []*txWithKey
   195  	if txStr != stdinSelector {
   196  		inFile, err := os.Open(txStr)
   197  		if err != nil {
   198  			return NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err))
   199  		}
   200  		defer inFile.Close()
   201  		decoder := json.NewDecoder(inFile)
   202  		if strings.HasSuffix(txStr, ".rlp") {
   203  			var body hexutil.Bytes
   204  			if err := decoder.Decode(&body); err != nil {
   205  				return err
   206  			}
   207  			var txs types.Transactions
   208  			if err := rlp.DecodeBytes(body, &txs); err != nil {
   209  				return err
   210  			}
   211  			for _, tx := range txs {
   212  				txsWithKeys = append(txsWithKeys, &txWithKey{
   213  					key: nil,
   214  					tx:  tx,
   215  				})
   216  			}
   217  		} else {
   218  			if err := decoder.Decode(&txsWithKeys); err != nil {
   219  				return NewError(ErrorJson, fmt.Errorf("failed unmarshaling txs-file: %v", err))
   220  			}
   221  		}
   222  	} else {
   223  		if len(inputData.TxRlp) > 0 {
   224  			// Decode the body of already signed transactions
   225  			body := common.FromHex(inputData.TxRlp)
   226  			var txs types.Transactions
   227  			if err := rlp.DecodeBytes(body, &txs); err != nil {
   228  				return err
   229  			}
   230  			for _, tx := range txs {
   231  				txsWithKeys = append(txsWithKeys, &txWithKey{
   232  					key: nil,
   233  					tx:  tx,
   234  				})
   235  			}
   236  		} else {
   237  			// JSON encoded transactions
   238  			txsWithKeys = inputData.Txs
   239  		}
   240  	}
   241  	// We may have to sign the transactions.
   242  	signer := types.MakeSigner(chainConfig, big.NewInt(int64(prestate.Env.Number)))
   243  
   244  	if txs, err = signUnsignedTransactions(txsWithKeys, signer); err != nil {
   245  		return NewError(ErrorJson, fmt.Errorf("failed signing transactions: %v", err))
   246  	}
   247  	// Sanity check, to not `panic` in state_transition
   248  	if chainConfig.IsLondon(big.NewInt(int64(prestate.Env.Number))) {
   249  		if prestate.Env.BaseFee == nil {
   250  			return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
   251  		}
   252  	}
   253  	isMerged := chainConfig.TerminalTotalDifficulty != nil && chainConfig.TerminalTotalDifficulty.BitLen() == 0
   254  	env := prestate.Env
   255  	if isMerged {
   256  		// post-merge:
   257  		// - random must be supplied
   258  		// - difficulty must be zero
   259  		switch {
   260  		case env.Random == nil:
   261  			return NewError(ErrorConfig, errors.New("post-merge requires currentRandom to be defined in env"))
   262  		case env.Difficulty != nil && env.Difficulty.BitLen() != 0:
   263  			return NewError(ErrorConfig, errors.New("post-merge difficulty must be zero (or omitted) in env"))
   264  		}
   265  		prestate.Env.Difficulty = nil
   266  	} else if env.Difficulty == nil {
   267  		// pre-merge:
   268  		// If difficulty was not provided by caller, we need to calculate it.
   269  		switch {
   270  		case env.ParentDifficulty == nil:
   271  			return NewError(ErrorConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty"))
   272  		case env.Number == 0:
   273  			return NewError(ErrorConfig, errors.New("currentDifficulty needs to be provided for block number 0"))
   274  		case env.Timestamp <= env.ParentTimestamp:
   275  			return NewError(ErrorConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)",
   276  				env.Timestamp, env.ParentTimestamp))
   277  		}
   278  		prestate.Env.Difficulty = calcDifficulty(chainConfig, env.Number, env.Timestamp,
   279  			env.ParentTimestamp, env.ParentDifficulty, env.ParentUncleHash)
   280  	}
   281  	// Run the test and aggregate the result
   282  	s, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer)
   283  	if err != nil {
   284  		return err
   285  	}
   286  	body, _ := rlp.EncodeToBytes(txs)
   287  	// Dump the excution result
   288  	collector := make(Alloc)
   289  	s.DumpToCollector(collector, nil)
   290  	return dispatchOutput(ctx, baseDir, result, collector, body)
   291  }
   292  
   293  // txWithKey is a helper-struct, to allow us to use the types.Transaction along with
   294  // a `secretKey`-field, for input
   295  type txWithKey struct {
   296  	key       *ecdsa.PrivateKey
   297  	tx        *types.Transaction
   298  	protected bool
   299  }
   300  
   301  func (t *txWithKey) UnmarshalJSON(input []byte) error {
   302  	// Read the metadata, if present
   303  	type txMetadata struct {
   304  		Key       *common.Hash `json:"secretKey"`
   305  		Protected *bool        `json:"protected"`
   306  	}
   307  	var data txMetadata
   308  	if err := json.Unmarshal(input, &data); err != nil {
   309  		return err
   310  	}
   311  	if data.Key != nil {
   312  		k := data.Key.Hex()[2:]
   313  		if ecdsaKey, err := crypto.HexToECDSA(k); err != nil {
   314  			return err
   315  		} else {
   316  			t.key = ecdsaKey
   317  		}
   318  	}
   319  	if data.Protected != nil {
   320  		t.protected = *data.Protected
   321  	} else {
   322  		t.protected = true
   323  	}
   324  	// Now, read the transaction itself
   325  	var tx types.Transaction
   326  	if err := json.Unmarshal(input, &tx); err != nil {
   327  		return err
   328  	}
   329  	t.tx = &tx
   330  	return nil
   331  }
   332  
   333  // signUnsignedTransactions converts the input txs to canonical transactions.
   334  //
   335  // The transactions can have two forms, either
   336  //   1. unsigned or
   337  //   2. signed
   338  // For (1), r, s, v, need so be zero, and the `secretKey` needs to be set.
   339  // If so, we sign it here and now, with the given `secretKey`
   340  // If the condition above is not met, then it's considered a signed transaction.
   341  //
   342  // To manage this, we read the transactions twice, first trying to read the secretKeys,
   343  // and secondly to read them with the standard tx json format
   344  func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Transactions, error) {
   345  	var signedTxs []*types.Transaction
   346  	for i, txWithKey := range txs {
   347  		tx := txWithKey.tx
   348  		key := txWithKey.key
   349  		v, r, s := tx.RawSignatureValues()
   350  		if key != nil && v.BitLen()+r.BitLen()+s.BitLen() == 0 {
   351  			// This transaction needs to be signed
   352  			var (
   353  				signed *types.Transaction
   354  				err    error
   355  			)
   356  			if txWithKey.protected {
   357  				signed, err = types.SignTx(tx, signer, key)
   358  			} else {
   359  				signed, err = types.SignTx(tx, types.FrontierSigner{}, key)
   360  			}
   361  			if err != nil {
   362  				return nil, NewError(ErrorJson, fmt.Errorf("tx %d: failed to sign tx: %v", i, err))
   363  			}
   364  			signedTxs = append(signedTxs, signed)
   365  		} else {
   366  			// Already signed
   367  			signedTxs = append(signedTxs, tx)
   368  		}
   369  	}
   370  	return signedTxs, nil
   371  }
   372  
   373  type Alloc map[common.Address]core.GenesisAccount
   374  
   375  func (g Alloc) OnRoot(common.Hash) {}
   376  
   377  func (g Alloc) OnAccount(addr common.Address, dumpAccount state.DumpAccount) {
   378  	balance, _ := new(big.Int).SetString(dumpAccount.Balance, 10)
   379  	var storage map[common.Hash]common.Hash
   380  	if dumpAccount.Storage != nil {
   381  		storage = make(map[common.Hash]common.Hash)
   382  		for k, v := range dumpAccount.Storage {
   383  			storage[k] = common.HexToHash(v)
   384  		}
   385  	}
   386  	genesisAccount := core.GenesisAccount{
   387  		Code:    dumpAccount.Code,
   388  		Storage: storage,
   389  		Balance: balance,
   390  		Nonce:   dumpAccount.Nonce,
   391  	}
   392  	g[addr] = genesisAccount
   393  }
   394  
   395  // saveFile marshalls the object to the given file
   396  func saveFile(baseDir, filename string, data interface{}) error {
   397  	b, err := json.MarshalIndent(data, "", " ")
   398  	if err != nil {
   399  		return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   400  	}
   401  	location := path.Join(baseDir, filename)
   402  	if err = os.WriteFile(location, b, 0644); err != nil {
   403  		return NewError(ErrorIO, fmt.Errorf("failed writing output: %v", err))
   404  	}
   405  	log.Info("Wrote file", "file", location)
   406  	return nil
   407  }
   408  
   409  // dispatchOutput writes the output data to either stderr or stdout, or to the specified
   410  // files
   411  func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes) error {
   412  	stdOutObject := make(map[string]interface{})
   413  	stdErrObject := make(map[string]interface{})
   414  	dispatch := func(baseDir, fName, name string, obj interface{}) error {
   415  		switch fName {
   416  		case "stdout":
   417  			stdOutObject[name] = obj
   418  		case "stderr":
   419  			stdErrObject[name] = obj
   420  		case "":
   421  			// don't save
   422  		default: // save to file
   423  			if err := saveFile(baseDir, fName, obj); err != nil {
   424  				return err
   425  			}
   426  		}
   427  		return nil
   428  	}
   429  	if err := dispatch(baseDir, ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil {
   430  		return err
   431  	}
   432  	if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil {
   433  		return err
   434  	}
   435  	if err := dispatch(baseDir, ctx.String(OutputBodyFlag.Name), "body", body); err != nil {
   436  		return err
   437  	}
   438  	if len(stdOutObject) > 0 {
   439  		b, err := json.MarshalIndent(stdOutObject, "", "  ")
   440  		if err != nil {
   441  			return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   442  		}
   443  		os.Stdout.Write(b)
   444  		os.Stdout.WriteString("\n")
   445  	}
   446  	if len(stdErrObject) > 0 {
   447  		b, err := json.MarshalIndent(stdErrObject, "", "  ")
   448  		if err != nil {
   449  			return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   450  		}
   451  		os.Stderr.Write(b)
   452  		os.Stderr.WriteString("\n")
   453  	}
   454  	return nil
   455  }