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