gitee.com/liu-zhao234568/cntest@v1.0.0/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  
    29  	"gitee.com/liu-zhao234568/cntest/common"
    30  	"gitee.com/liu-zhao234568/cntest/common/hexutil"
    31  	"gitee.com/liu-zhao234568/cntest/core"
    32  	"gitee.com/liu-zhao234568/cntest/core/state"
    33  	"gitee.com/liu-zhao234568/cntest/core/types"
    34  	"gitee.com/liu-zhao234568/cntest/core/vm"
    35  	"gitee.com/liu-zhao234568/cntest/crypto"
    36  	"gitee.com/liu-zhao234568/cntest/log"
    37  	"gitee.com/liu-zhao234568/cntest/params"
    38  	"gitee.com/liu-zhao234568/cntest/rlp"
    39  	"gitee.com/liu-zhao234568/cntest/tests"
    40  	"gopkg.in/urfave/cli.v1"
    41  )
    42  
    43  const (
    44  	ErrorEVM              = 2
    45  	ErrorVMConfig         = 3
    46  	ErrorMissingBlockhash = 4
    47  
    48  	ErrorJson = 10
    49  	ErrorIO   = 11
    50  
    51  	stdinSelector = "stdin"
    52  )
    53  
    54  type NumberedError struct {
    55  	errorCode int
    56  	err       error
    57  }
    58  
    59  func NewError(errorCode int, err error) *NumberedError {
    60  	return &NumberedError{errorCode, err}
    61  }
    62  
    63  func (n *NumberedError) Error() string {
    64  	return fmt.Sprintf("ERROR(%d): %v", n.errorCode, n.err.Error())
    65  }
    66  
    67  func (n *NumberedError) Code() int {
    68  	return n.errorCode
    69  }
    70  
    71  type input struct {
    72  	Alloc core.GenesisAlloc `json:"alloc,omitempty"`
    73  	Env   *stEnv            `json:"env,omitempty"`
    74  	Txs   []*txWithKey      `json:"txs,omitempty"`
    75  }
    76  
    77  func Main(ctx *cli.Context) error {
    78  	// Configure the go-ethereum logger
    79  	glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
    80  	glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
    81  	log.Root().SetHandler(glogger)
    82  
    83  	var (
    84  		err     error
    85  		tracer  vm.Tracer
    86  		baseDir = ""
    87  	)
    88  	var getTracer func(txIndex int, txHash common.Hash) (vm.Tracer, error)
    89  
    90  	// If user specified a basedir, make sure it exists
    91  	if ctx.IsSet(OutputBasedir.Name) {
    92  		if base := ctx.String(OutputBasedir.Name); len(base) > 0 {
    93  			err := os.MkdirAll(base, 0755) // //rw-r--r--
    94  			if err != nil {
    95  				return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err))
    96  			}
    97  			baseDir = base
    98  		}
    99  	}
   100  	if ctx.Bool(TraceFlag.Name) {
   101  		// Configure the EVM logger
   102  		logConfig := &vm.LogConfig{
   103  			DisableStack:      ctx.Bool(TraceDisableStackFlag.Name),
   104  			DisableMemory:     ctx.Bool(TraceDisableMemoryFlag.Name),
   105  			DisableReturnData: ctx.Bool(TraceDisableReturnDataFlag.Name),
   106  			Debug:             true,
   107  		}
   108  		var prevFile *os.File
   109  		// This one closes the last file
   110  		defer func() {
   111  			if prevFile != nil {
   112  				prevFile.Close()
   113  			}
   114  		}()
   115  		getTracer = func(txIndex int, txHash common.Hash) (vm.Tracer, error) {
   116  			if prevFile != nil {
   117  				prevFile.Close()
   118  			}
   119  			traceFile, err := os.Create(path.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String())))
   120  			if err != nil {
   121  				return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
   122  			}
   123  			prevFile = traceFile
   124  			return vm.NewJSONLogger(logConfig, traceFile), nil
   125  		}
   126  	} else {
   127  		getTracer = func(txIndex int, txHash common.Hash) (tracer vm.Tracer, err error) {
   128  			return nil, nil
   129  		}
   130  	}
   131  	// We need to load three things: alloc, env and transactions. May be either in
   132  	// stdin input or in files.
   133  	// Check if anything needs to be read from stdin
   134  	var (
   135  		prestate Prestate
   136  		txs      types.Transactions // txs to apply
   137  		allocStr = ctx.String(InputAllocFlag.Name)
   138  
   139  		envStr    = ctx.String(InputEnvFlag.Name)
   140  		txStr     = ctx.String(InputTxsFlag.Name)
   141  		inputData = &input{}
   142  	)
   143  	// Figure out the prestate alloc
   144  	if allocStr == stdinSelector || envStr == stdinSelector || txStr == stdinSelector {
   145  		decoder := json.NewDecoder(os.Stdin)
   146  		if err := decoder.Decode(inputData); err != nil {
   147  			return NewError(ErrorJson, fmt.Errorf("failed unmarshaling stdin: %v", err))
   148  		}
   149  	}
   150  	if allocStr != stdinSelector {
   151  		inFile, err := os.Open(allocStr)
   152  		if err != nil {
   153  			return NewError(ErrorIO, fmt.Errorf("failed reading alloc file: %v", err))
   154  		}
   155  		defer inFile.Close()
   156  		decoder := json.NewDecoder(inFile)
   157  		if err := decoder.Decode(&inputData.Alloc); err != nil {
   158  			return NewError(ErrorJson, fmt.Errorf("failed unmarshaling alloc-file: %v", err))
   159  		}
   160  	}
   161  	prestate.Pre = inputData.Alloc
   162  
   163  	// Set the block environment
   164  	if envStr != stdinSelector {
   165  		inFile, err := os.Open(envStr)
   166  		if err != nil {
   167  			return NewError(ErrorIO, fmt.Errorf("failed reading env file: %v", err))
   168  		}
   169  		defer inFile.Close()
   170  		decoder := json.NewDecoder(inFile)
   171  		var env stEnv
   172  		if err := decoder.Decode(&env); err != nil {
   173  			return NewError(ErrorJson, fmt.Errorf("failed unmarshaling env-file: %v", 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(ErrorVMConfig, 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 err := decoder.Decode(&txsWithKeys); err != nil {
   203  			return NewError(ErrorJson, fmt.Errorf("failed unmarshaling txs-file: %v", err))
   204  		}
   205  	} else {
   206  		txsWithKeys = inputData.Txs
   207  	}
   208  	// We may have to sign the transactions.
   209  	signer := types.MakeSigner(chainConfig, big.NewInt(int64(prestate.Env.Number)))
   210  
   211  	if txs, err = signUnsignedTransactions(txsWithKeys, signer); err != nil {
   212  		return NewError(ErrorJson, fmt.Errorf("failed signing transactions: %v", err))
   213  	}
   214  	// Sanity check, to not `panic` in state_transition
   215  	if chainConfig.IsLondon(big.NewInt(int64(prestate.Env.Number))) {
   216  		if prestate.Env.BaseFee == nil {
   217  			return NewError(ErrorVMConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
   218  		}
   219  	}
   220  	// Run the test and aggregate the result
   221  	s, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer)
   222  	if err != nil {
   223  		return err
   224  	}
   225  	body, _ := rlp.EncodeToBytes(txs)
   226  	// Dump the excution result
   227  	collector := make(Alloc)
   228  	s.DumpToCollector(collector, nil)
   229  	return dispatchOutput(ctx, baseDir, result, collector, body)
   230  }
   231  
   232  // txWithKey is a helper-struct, to allow us to use the types.Transaction along with
   233  // a `secretKey`-field, for input
   234  type txWithKey struct {
   235  	key *ecdsa.PrivateKey
   236  	tx  *types.Transaction
   237  }
   238  
   239  func (t *txWithKey) UnmarshalJSON(input []byte) error {
   240  	// Read the secretKey, if present
   241  	type sKey struct {
   242  		Key *common.Hash `json:"secretKey"`
   243  	}
   244  	var key sKey
   245  	if err := json.Unmarshal(input, &key); err != nil {
   246  		return err
   247  	}
   248  	if key.Key != nil {
   249  		k := key.Key.Hex()[2:]
   250  		if ecdsaKey, err := crypto.HexToECDSA(k); err != nil {
   251  			return err
   252  		} else {
   253  			t.key = ecdsaKey
   254  		}
   255  	}
   256  	// Now, read the transaction itself
   257  	var tx types.Transaction
   258  	if err := json.Unmarshal(input, &tx); err != nil {
   259  		return err
   260  	}
   261  	t.tx = &tx
   262  	return nil
   263  }
   264  
   265  // signUnsignedTransactions converts the input txs to canonical transactions.
   266  //
   267  // The transactions can have two forms, either
   268  //   1. unsigned or
   269  //   2. signed
   270  // For (1), r, s, v, need so be zero, and the `secretKey` needs to be set.
   271  // If so, we sign it here and now, with the given `secretKey`
   272  // If the condition above is not met, then it's considered a signed transaction.
   273  //
   274  // To manage this, we read the transactions twice, first trying to read the secretKeys,
   275  // and secondly to read them with the standard tx json format
   276  func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Transactions, error) {
   277  	var signedTxs []*types.Transaction
   278  	for i, txWithKey := range txs {
   279  		tx := txWithKey.tx
   280  		key := txWithKey.key
   281  		v, r, s := tx.RawSignatureValues()
   282  		if key != nil && v.BitLen()+r.BitLen()+s.BitLen() == 0 {
   283  			// This transaction needs to be signed
   284  			signed, err := types.SignTx(tx, signer, key)
   285  			if err != nil {
   286  				return nil, NewError(ErrorJson, fmt.Errorf("tx %d: failed to sign tx: %v", i, err))
   287  			}
   288  			signedTxs = append(signedTxs, signed)
   289  		} else {
   290  			// Already signed
   291  			signedTxs = append(signedTxs, tx)
   292  		}
   293  	}
   294  	return signedTxs, nil
   295  }
   296  
   297  type Alloc map[common.Address]core.GenesisAccount
   298  
   299  func (g Alloc) OnRoot(common.Hash) {}
   300  
   301  func (g Alloc) OnAccount(addr common.Address, dumpAccount state.DumpAccount) {
   302  	balance, _ := new(big.Int).SetString(dumpAccount.Balance, 10)
   303  	var storage map[common.Hash]common.Hash
   304  	if dumpAccount.Storage != nil {
   305  		storage = make(map[common.Hash]common.Hash)
   306  		for k, v := range dumpAccount.Storage {
   307  			storage[k] = common.HexToHash(v)
   308  		}
   309  	}
   310  	genesisAccount := core.GenesisAccount{
   311  		Code:    dumpAccount.Code,
   312  		Storage: storage,
   313  		Balance: balance,
   314  		Nonce:   dumpAccount.Nonce,
   315  	}
   316  	g[addr] = genesisAccount
   317  }
   318  
   319  // saveFile marshalls the object to the given file
   320  func saveFile(baseDir, filename string, data interface{}) error {
   321  	b, err := json.MarshalIndent(data, "", " ")
   322  	if err != nil {
   323  		return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   324  	}
   325  	location := path.Join(baseDir, filename)
   326  	if err = ioutil.WriteFile(location, b, 0644); err != nil {
   327  		return NewError(ErrorIO, fmt.Errorf("failed writing output: %v", err))
   328  	}
   329  	log.Info("Wrote file", "file", location)
   330  	return nil
   331  }
   332  
   333  // dispatchOutput writes the output data to either stderr or stdout, or to the specified
   334  // files
   335  func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes) error {
   336  	stdOutObject := make(map[string]interface{})
   337  	stdErrObject := make(map[string]interface{})
   338  	dispatch := func(baseDir, fName, name string, obj interface{}) error {
   339  		switch fName {
   340  		case "stdout":
   341  			stdOutObject[name] = obj
   342  		case "stderr":
   343  			stdErrObject[name] = obj
   344  		case "":
   345  			// don't save
   346  		default: // save to file
   347  			if err := saveFile(baseDir, fName, obj); err != nil {
   348  				return err
   349  			}
   350  		}
   351  		return nil
   352  	}
   353  	if err := dispatch(baseDir, ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil {
   354  		return err
   355  	}
   356  	if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil {
   357  		return err
   358  	}
   359  	if err := dispatch(baseDir, ctx.String(OutputBodyFlag.Name), "body", body); err != nil {
   360  		return err
   361  	}
   362  	if len(stdOutObject) > 0 {
   363  		b, err := json.MarshalIndent(stdOutObject, "", " ")
   364  		if err != nil {
   365  			return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   366  		}
   367  		os.Stdout.Write(b)
   368  	}
   369  	if len(stdErrObject) > 0 {
   370  		b, err := json.MarshalIndent(stdErrObject, "", " ")
   371  		if err != nil {
   372  			return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   373  		}
   374  		os.Stderr.Write(b)
   375  	}
   376  	return nil
   377  }