github.com/ethereum/go-ethereum@v1.14.4-0.20240516095835-473ee8fc07a3/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  	"io"
    24  	"math/big"
    25  	"os"
    26  	"path/filepath"
    27  
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/common/hexutil"
    30  	"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
    31  	"github.com/ethereum/go-ethereum/core/state"
    32  	"github.com/ethereum/go-ethereum/core/tracing"
    33  	"github.com/ethereum/go-ethereum/core/types"
    34  	"github.com/ethereum/go-ethereum/core/vm"
    35  	"github.com/ethereum/go-ethereum/eth/tracers"
    36  	"github.com/ethereum/go-ethereum/eth/tracers/logger"
    37  	"github.com/ethereum/go-ethereum/log"
    38  	"github.com/ethereum/go-ethereum/params"
    39  	"github.com/ethereum/go-ethereum/tests"
    40  	"github.com/urfave/cli/v2"
    41  )
    42  
    43  const (
    44  	ErrorEVM              = 2
    45  	ErrorConfig           = 3
    46  	ErrorMissingBlockhash = 4
    47  
    48  	ErrorJson = 10
    49  	ErrorIO   = 11
    50  	ErrorRlp  = 12
    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 types.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  	var getTracer = func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error) { return nil, nil, nil }
    86  
    87  	baseDir, err := createBasedir(ctx)
    88  	if err != nil {
    89  		return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err))
    90  	}
    91  
    92  	if ctx.Bool(TraceFlag.Name) { // JSON opcode tracing
    93  		// Configure the EVM logger
    94  		logConfig := &logger.Config{
    95  			DisableStack:     ctx.Bool(TraceDisableStackFlag.Name),
    96  			EnableMemory:     ctx.Bool(TraceEnableMemoryFlag.Name),
    97  			EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
    98  			Debug:            true,
    99  		}
   100  		getTracer = func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error) {
   101  			traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String())))
   102  			if err != nil {
   103  				return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
   104  			}
   105  			var l *tracing.Hooks
   106  			if ctx.Bool(TraceEnableCallFramesFlag.Name) {
   107  				l = logger.NewJSONLoggerWithCallFrames(logConfig, traceFile)
   108  			} else {
   109  				l = logger.NewJSONLogger(logConfig, traceFile)
   110  			}
   111  			tracer := &tracers.Tracer{
   112  				Hooks: l,
   113  				// jsonLogger streams out result to file.
   114  				GetResult: func() (json.RawMessage, error) { return nil, nil },
   115  				Stop:      func(err error) {},
   116  			}
   117  			return tracer, traceFile, nil
   118  		}
   119  	} else if ctx.IsSet(TraceTracerFlag.Name) {
   120  		var config json.RawMessage
   121  		if ctx.IsSet(TraceTracerConfigFlag.Name) {
   122  			config = []byte(ctx.String(TraceTracerConfigFlag.Name))
   123  		}
   124  		getTracer = func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error) {
   125  			traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.json", txIndex, txHash.String())))
   126  			if err != nil {
   127  				return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
   128  			}
   129  			tracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name), nil, config)
   130  			if err != nil {
   131  				return nil, nil, NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %w", err))
   132  			}
   133  			return tracer, traceFile, nil
   134  		}
   135  	}
   136  	// We need to load three things: alloc, env and transactions. May be either in
   137  	// stdin input or in files.
   138  	// Check if anything needs to be read from stdin
   139  	var (
   140  		prestate Prestate
   141  		txIt     txIterator // txs to apply
   142  		allocStr = ctx.String(InputAllocFlag.Name)
   143  
   144  		envStr    = ctx.String(InputEnvFlag.Name)
   145  		txStr     = ctx.String(InputTxsFlag.Name)
   146  		inputData = &input{}
   147  	)
   148  	// Figure out the prestate alloc
   149  	if allocStr == stdinSelector || envStr == stdinSelector || txStr == stdinSelector {
   150  		decoder := json.NewDecoder(os.Stdin)
   151  		if err := decoder.Decode(inputData); err != nil {
   152  			return NewError(ErrorJson, fmt.Errorf("failed unmarshalling stdin: %v", err))
   153  		}
   154  	}
   155  	if allocStr != stdinSelector {
   156  		if err := readFile(allocStr, "alloc", &inputData.Alloc); err != nil {
   157  			return err
   158  		}
   159  	}
   160  	prestate.Pre = inputData.Alloc
   161  
   162  	// Set the block environment
   163  	if envStr != stdinSelector {
   164  		var env stEnv
   165  		if err := readFile(envStr, "env", &env); err != nil {
   166  			return err
   167  		}
   168  		inputData.Env = &env
   169  	}
   170  	prestate.Env = *inputData.Env
   171  
   172  	vmConfig := vm.Config{}
   173  	// Construct the chainconfig
   174  	var chainConfig *params.ChainConfig
   175  	if cConf, extraEips, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil {
   176  		return NewError(ErrorConfig, fmt.Errorf("failed constructing chain configuration: %v", err))
   177  	} else {
   178  		chainConfig = cConf
   179  		vmConfig.ExtraEips = extraEips
   180  	}
   181  	// Set the chain id
   182  	chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
   183  
   184  	if txIt, err = loadTransactions(txStr, inputData, prestate.Env, chainConfig); err != nil {
   185  		return err
   186  	}
   187  	if err := applyLondonChecks(&prestate.Env, chainConfig); err != nil {
   188  		return err
   189  	}
   190  	if err := applyShanghaiChecks(&prestate.Env, chainConfig); err != nil {
   191  		return err
   192  	}
   193  	if err := applyMergeChecks(&prestate.Env, chainConfig); err != nil {
   194  		return err
   195  	}
   196  	if err := applyCancunChecks(&prestate.Env, chainConfig); err != nil {
   197  		return err
   198  	}
   199  	// Run the test and aggregate the result
   200  	s, result, body, err := prestate.Apply(vmConfig, chainConfig, txIt, ctx.Int64(RewardFlag.Name), getTracer)
   201  	if err != nil {
   202  		return err
   203  	}
   204  	// Dump the execution result
   205  	collector := make(Alloc)
   206  	s.DumpToCollector(collector, nil)
   207  	return dispatchOutput(ctx, baseDir, result, collector, body)
   208  }
   209  
   210  func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error {
   211  	if !chainConfig.IsLondon(big.NewInt(int64(env.Number))) {
   212  		return nil
   213  	}
   214  	// Sanity check, to not `panic` in state_transition
   215  	if env.BaseFee != nil {
   216  		// Already set, base fee has precedent over parent base fee.
   217  		return nil
   218  	}
   219  	if env.ParentBaseFee == nil || env.Number == 0 {
   220  		return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
   221  	}
   222  	env.BaseFee = eip1559.CalcBaseFee(chainConfig, &types.Header{
   223  		Number:   new(big.Int).SetUint64(env.Number - 1),
   224  		BaseFee:  env.ParentBaseFee,
   225  		GasUsed:  env.ParentGasUsed,
   226  		GasLimit: env.ParentGasLimit,
   227  	})
   228  	return nil
   229  }
   230  
   231  func applyShanghaiChecks(env *stEnv, chainConfig *params.ChainConfig) error {
   232  	if !chainConfig.IsShanghai(big.NewInt(int64(env.Number)), env.Timestamp) {
   233  		return nil
   234  	}
   235  	if env.Withdrawals == nil {
   236  		return NewError(ErrorConfig, errors.New("Shanghai config but missing 'withdrawals' in env section"))
   237  	}
   238  	return nil
   239  }
   240  
   241  func applyMergeChecks(env *stEnv, chainConfig *params.ChainConfig) error {
   242  	isMerged := chainConfig.TerminalTotalDifficulty != nil && chainConfig.TerminalTotalDifficulty.BitLen() == 0
   243  	if !isMerged {
   244  		// pre-merge: If difficulty was not provided by caller, we need to calculate it.
   245  		if env.Difficulty != nil {
   246  			// already set
   247  			return nil
   248  		}
   249  		switch {
   250  		case env.ParentDifficulty == nil:
   251  			return NewError(ErrorConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty"))
   252  		case env.Number == 0:
   253  			return NewError(ErrorConfig, errors.New("currentDifficulty needs to be provided for block number 0"))
   254  		case env.Timestamp <= env.ParentTimestamp:
   255  			return NewError(ErrorConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)",
   256  				env.Timestamp, env.ParentTimestamp))
   257  		}
   258  		env.Difficulty = calcDifficulty(chainConfig, env.Number, env.Timestamp,
   259  			env.ParentTimestamp, env.ParentDifficulty, env.ParentUncleHash)
   260  		return nil
   261  	}
   262  	// post-merge:
   263  	// - random must be supplied
   264  	// - difficulty must be zero
   265  	switch {
   266  	case env.Random == nil:
   267  		return NewError(ErrorConfig, errors.New("post-merge requires currentRandom to be defined in env"))
   268  	case env.Difficulty != nil && env.Difficulty.BitLen() != 0:
   269  		return NewError(ErrorConfig, errors.New("post-merge difficulty must be zero (or omitted) in env"))
   270  	}
   271  	env.Difficulty = nil
   272  	return nil
   273  }
   274  
   275  func applyCancunChecks(env *stEnv, chainConfig *params.ChainConfig) error {
   276  	if !chainConfig.IsCancun(big.NewInt(int64(env.Number)), env.Timestamp) {
   277  		env.ParentBeaconBlockRoot = nil // un-set it if it has been set too early
   278  		return nil
   279  	}
   280  	// Post-cancun
   281  	// We require EIP-4788 beacon root to be set in the env
   282  	if env.ParentBeaconBlockRoot == nil {
   283  		return NewError(ErrorConfig, errors.New("post-cancun env requires parentBeaconBlockRoot to be set"))
   284  	}
   285  	return nil
   286  }
   287  
   288  type Alloc map[common.Address]types.Account
   289  
   290  func (g Alloc) OnRoot(common.Hash) {}
   291  
   292  func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
   293  	if addr == nil {
   294  		return
   295  	}
   296  	balance, _ := new(big.Int).SetString(dumpAccount.Balance, 0)
   297  	var storage map[common.Hash]common.Hash
   298  	if dumpAccount.Storage != nil {
   299  		storage = make(map[common.Hash]common.Hash, len(dumpAccount.Storage))
   300  		for k, v := range dumpAccount.Storage {
   301  			storage[k] = common.HexToHash(v)
   302  		}
   303  	}
   304  	genesisAccount := types.Account{
   305  		Code:    dumpAccount.Code,
   306  		Storage: storage,
   307  		Balance: balance,
   308  		Nonce:   dumpAccount.Nonce,
   309  	}
   310  	g[*addr] = genesisAccount
   311  }
   312  
   313  // saveFile marshals the object to the given file
   314  func saveFile(baseDir, filename string, data interface{}) error {
   315  	b, err := json.MarshalIndent(data, "", " ")
   316  	if err != nil {
   317  		return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   318  	}
   319  	location := filepath.Join(baseDir, filename)
   320  	if err = os.WriteFile(location, b, 0644); err != nil {
   321  		return NewError(ErrorIO, fmt.Errorf("failed writing output: %v", err))
   322  	}
   323  	log.Info("Wrote file", "file", location)
   324  	return nil
   325  }
   326  
   327  // dispatchOutput writes the output data to either stderr or stdout, or to the specified
   328  // files
   329  func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes) error {
   330  	stdOutObject := make(map[string]interface{})
   331  	stdErrObject := make(map[string]interface{})
   332  	dispatch := func(baseDir, fName, name string, obj interface{}) error {
   333  		switch fName {
   334  		case "stdout":
   335  			stdOutObject[name] = obj
   336  		case "stderr":
   337  			stdErrObject[name] = obj
   338  		case "":
   339  			// don't save
   340  		default: // save to file
   341  			if err := saveFile(baseDir, fName, obj); err != nil {
   342  				return err
   343  			}
   344  		}
   345  		return nil
   346  	}
   347  	if err := dispatch(baseDir, ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil {
   348  		return err
   349  	}
   350  	if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil {
   351  		return err
   352  	}
   353  	if err := dispatch(baseDir, ctx.String(OutputBodyFlag.Name), "body", body); err != nil {
   354  		return err
   355  	}
   356  	if len(stdOutObject) > 0 {
   357  		b, err := json.MarshalIndent(stdOutObject, "", "  ")
   358  		if err != nil {
   359  			return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   360  		}
   361  		os.Stdout.Write(b)
   362  		os.Stdout.WriteString("\n")
   363  	}
   364  	if len(stdErrObject) > 0 {
   365  		b, err := json.MarshalIndent(stdErrObject, "", "  ")
   366  		if err != nil {
   367  			return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   368  		}
   369  		os.Stderr.Write(b)
   370  		os.Stderr.WriteString("\n")
   371  	}
   372  	return nil
   373  }