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 }