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