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