github.com/theQRL/go-zond@v0.2.1/cmd/zvm/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/eip1559"
    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/crypto/pqcrypto"
    37  	"github.com/theQRL/go-zond/log"
    38  	"github.com/theQRL/go-zond/params"
    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  	ErrorZVM              = 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.ZVMLogger
    95  	)
    96  	var getTracer func(txIndex int, txHash common.Hash) (vm.ZVMLogger, 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  		// Configure the ZVM logger
   104  		logConfig := &logger.Config{
   105  			DisableStack:     ctx.Bool(TraceDisableStackFlag.Name),
   106  			EnableMemory:     ctx.Bool(TraceEnableMemoryFlag.Name),
   107  			EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.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.ZVMLogger, 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 logger.NewJSONLogger(logConfig, traceFile), nil
   127  		}
   128  	} else {
   129  		getTracer = func(txIndex int, txHash common.Hash) (tracer vm.ZVMLogger, 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  		if err := readFile(allocStr, "alloc", &inputData.Alloc); err != nil {
   154  			return err
   155  		}
   156  	}
   157  	prestate.Pre = inputData.Alloc
   158  
   159  	// Set the block environment
   160  	if envStr != stdinSelector {
   161  		var env stEnv
   162  		if err := readFile(envStr, "env", &env); err != nil {
   163  			return err
   164  		}
   165  		inputData.Env = &env
   166  	}
   167  	prestate.Env = *inputData.Env
   168  
   169  	vmConfig := vm.Config{
   170  		Tracer: tracer,
   171  	}
   172  	// Construct the chainconfig
   173  	var chainConfig *params.ChainConfig
   174  	if cConf, extraEips, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil {
   175  		return NewError(ErrorConfig, fmt.Errorf("failed constructing chain configuration: %v", err))
   176  	} else {
   177  		chainConfig = cConf
   178  		vmConfig.ExtraEips = extraEips
   179  	}
   180  	// Set the chain id
   181  	chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
   182  
   183  	if txs, err = loadTransactions(txStr, inputData, chainConfig); err != nil {
   184  		return err
   185  	}
   186  	if err := applyLondonChecks(&prestate.Env, chainConfig); err != nil {
   187  		return err
   188  	}
   189  	if err := applyShanghaiChecks(&prestate.Env, chainConfig); err != nil {
   190  		return err
   191  	}
   192  	if err := applyMergeChecks(&prestate.Env, chainConfig); err != nil {
   193  		return err
   194  	}
   195  	// Run the test and aggregate the result
   196  	s, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer)
   197  	if err != nil {
   198  		return err
   199  	}
   200  	body, _ := rlp.EncodeToBytes(txs)
   201  	// Dump the excution result
   202  	collector := make(Alloc)
   203  	s.DumpToCollector(collector, nil)
   204  	return dispatchOutput(ctx, baseDir, result, collector, body)
   205  }
   206  
   207  // txWithKey is a helper-struct, to allow us to use the types.Transaction along with
   208  // a `seed`-field, for input
   209  type txWithKey struct {
   210  	key *dilithium.Dilithium
   211  	tx  *types.Transaction
   212  }
   213  
   214  func (t *txWithKey) UnmarshalJSON(input []byte) error {
   215  	// Read the metadata, if present
   216  	type txMetadata struct {
   217  		Seed *string `json:"seed"`
   218  	}
   219  	var data txMetadata
   220  	if err := json.Unmarshal(input, &data); err != nil {
   221  		return err
   222  	}
   223  	if data.Seed != nil {
   224  		sd := *data.Seed
   225  		if dilithiumKey, err := pqcrypto.HexToDilithium(sd[2:]); err != nil {
   226  			return err
   227  		} else {
   228  			t.key = dilithiumKey
   229  		}
   230  	}
   231  	// Now, read the transaction itself
   232  	var tx types.Transaction
   233  	if err := json.Unmarshal(input, &tx); err != nil {
   234  		return err
   235  	}
   236  	t.tx = &tx
   237  	return nil
   238  }
   239  
   240  // signUnsignedTransactions converts the input txs to canonical transactions.
   241  //
   242  // The transactions can have two forms, either
   243  //  1. unsigned or
   244  //  2. signed
   245  //
   246  // For (1), signature, need so be zero, and the `seed` needs to be set.
   247  // If so, we sign it here and now, with the given `seed`
   248  // If the condition above is not met, then it's considered a signed transaction.
   249  //
   250  // To manage this, we read the transactions twice, first trying to read the seeds,
   251  // and secondly to read them with the standard tx json format
   252  
   253  func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Transactions, error) {
   254  	var signedTxs []*types.Transaction
   255  	for i, tx := range txs {
   256  		var (
   257  			signature = tx.tx.RawSignatureValue()
   258  			signed    *types.Transaction
   259  			err       error
   260  		)
   261  		if tx.key == nil || len(signature) != 0 {
   262  			// Already signed
   263  			signedTxs = append(signedTxs, tx.tx)
   264  			continue
   265  		}
   266  		signed, err = types.SignTx(tx.tx, signer, tx.key)
   267  		if err != nil {
   268  			return nil, NewError(ErrorJson, fmt.Errorf("tx %d: failed to sign tx: %v", i, err))
   269  		}
   270  		signedTxs = append(signedTxs, signed)
   271  	}
   272  	return signedTxs, nil
   273  }
   274  
   275  func loadTransactions(txStr string, inputData *input, chainConfig *params.ChainConfig) (types.Transactions, error) {
   276  	var txsWithKeys []*txWithKey
   277  	var signed types.Transactions
   278  	if txStr != stdinSelector {
   279  		data, err := os.ReadFile(txStr)
   280  		if err != nil {
   281  			return nil, NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err))
   282  		}
   283  		if strings.HasSuffix(txStr, ".rlp") { // A file containing an rlp list
   284  			var body hexutil.Bytes
   285  			if err := json.Unmarshal(data, &body); err != nil {
   286  				return nil, err
   287  			}
   288  			// Already signed transactions
   289  			if err := rlp.DecodeBytes(body, &signed); err != nil {
   290  				return nil, err
   291  			}
   292  			return signed, nil
   293  		}
   294  		if err := json.Unmarshal(data, &txsWithKeys); err != nil {
   295  			return nil, NewError(ErrorJson, fmt.Errorf("failed unmarshaling txs-file: %v", err))
   296  		}
   297  	} else {
   298  		if len(inputData.TxRlp) > 0 {
   299  			// Decode the body of already signed transactions
   300  			body := common.FromHex(inputData.TxRlp)
   301  			// Already signed transactions
   302  			if err := rlp.DecodeBytes(body, &signed); err != nil {
   303  				return nil, err
   304  			}
   305  			return signed, nil
   306  		}
   307  		// JSON encoded transactions
   308  		txsWithKeys = inputData.Txs
   309  	}
   310  	// We may have to sign the transactions.
   311  	signer := types.MakeSigner(chainConfig)
   312  	return signUnsignedTransactions(txsWithKeys, signer)
   313  }
   314  
   315  func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error {
   316  	// Sanity check, to not `panic` in state_transition
   317  	if env.BaseFee != nil {
   318  		// Already set, base fee has precedent over parent base fee.
   319  		return nil
   320  	}
   321  	if env.ParentBaseFee == nil || env.Number == 0 {
   322  		return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'parentBaseFee' in env section"))
   323  	}
   324  	env.BaseFee = eip1559.CalcBaseFee(chainConfig, &types.Header{
   325  		Number:   new(big.Int).SetUint64(env.Number - 1),
   326  		BaseFee:  env.ParentBaseFee,
   327  		GasUsed:  env.ParentGasUsed,
   328  		GasLimit: env.ParentGasLimit,
   329  	})
   330  	return nil
   331  }
   332  
   333  func applyShanghaiChecks(env *stEnv, chainConfig *params.ChainConfig) error {
   334  	if env.Withdrawals == nil {
   335  		return NewError(ErrorConfig, errors.New("shanghai config but missing 'withdrawals' in env section"))
   336  	}
   337  	return nil
   338  }
   339  
   340  func applyMergeChecks(env *stEnv, chainConfig *params.ChainConfig) error {
   341  	if env.Random == nil {
   342  		return NewError(ErrorConfig, errors.New("post-merge requires currentRandom to be defined in env"))
   343  	}
   344  	return nil
   345  }
   346  
   347  type Alloc map[common.Address]core.GenesisAccount
   348  
   349  func (g Alloc) OnRoot(common.Hash) {}
   350  
   351  func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
   352  	if addr == nil {
   353  		return
   354  	}
   355  	balance, _ := new(big.Int).SetString(dumpAccount.Balance, 10)
   356  	var storage map[common.Hash]common.Hash
   357  	if dumpAccount.Storage != nil {
   358  		storage = make(map[common.Hash]common.Hash)
   359  		for k, v := range dumpAccount.Storage {
   360  			storage[k] = common.HexToHash(v)
   361  		}
   362  	}
   363  	genesisAccount := core.GenesisAccount{
   364  		Code:    dumpAccount.Code,
   365  		Storage: storage,
   366  		Balance: balance,
   367  		Nonce:   dumpAccount.Nonce,
   368  	}
   369  	g[*addr] = genesisAccount
   370  }
   371  
   372  // saveFile marshals the object to the given file
   373  func saveFile(baseDir, filename string, data interface{}) error {
   374  	b, err := json.MarshalIndent(data, "", " ")
   375  	if err != nil {
   376  		return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   377  	}
   378  	location := path.Join(baseDir, filename)
   379  	if err = os.WriteFile(location, b, 0644); err != nil {
   380  		return NewError(ErrorIO, fmt.Errorf("failed writing output: %v", err))
   381  	}
   382  	log.Info("Wrote file", "file", location)
   383  	return nil
   384  }
   385  
   386  // dispatchOutput writes the output data to either stderr or stdout, or to the specified
   387  // files
   388  func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes) error {
   389  	stdOutObject := make(map[string]interface{})
   390  	stdErrObject := make(map[string]interface{})
   391  	dispatch := func(baseDir, fName, name string, obj interface{}) error {
   392  		switch fName {
   393  		case "stdout":
   394  			stdOutObject[name] = obj
   395  		case "stderr":
   396  			stdErrObject[name] = obj
   397  		case "":
   398  			// don't save
   399  		default: // save to file
   400  			if err := saveFile(baseDir, fName, obj); err != nil {
   401  				return err
   402  			}
   403  		}
   404  		return nil
   405  	}
   406  	if err := dispatch(baseDir, ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil {
   407  		return err
   408  	}
   409  	if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil {
   410  		return err
   411  	}
   412  	if err := dispatch(baseDir, ctx.String(OutputBodyFlag.Name), "body", body); err != nil {
   413  		return err
   414  	}
   415  	if len(stdOutObject) > 0 {
   416  		b, err := json.MarshalIndent(stdOutObject, "", "  ")
   417  		if err != nil {
   418  			return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   419  		}
   420  		os.Stdout.Write(b)
   421  		os.Stdout.WriteString("\n")
   422  	}
   423  	if len(stdErrObject) > 0 {
   424  		b, err := json.MarshalIndent(stdErrObject, "", "  ")
   425  		if err != nil {
   426  			return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   427  		}
   428  		os.Stderr.Write(b)
   429  		os.Stderr.WriteString("\n")
   430  	}
   431  	return nil
   432  }