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