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