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