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