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