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