github.com/JFJun/bsc@v1.0.0/cmd/evm/runner.go (about) 1 // Copyright 2017 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 main 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "fmt" 23 "io/ioutil" 24 "math/big" 25 "os" 26 goruntime "runtime" 27 "runtime/pprof" 28 "testing" 29 "time" 30 31 "github.com/JFJun/bsc/cmd/evm/internal/compiler" 32 "github.com/JFJun/bsc/cmd/utils" 33 "github.com/JFJun/bsc/common" 34 "github.com/JFJun/bsc/core" 35 "github.com/JFJun/bsc/core/rawdb" 36 "github.com/JFJun/bsc/core/state" 37 "github.com/JFJun/bsc/core/vm" 38 "github.com/JFJun/bsc/core/vm/runtime" 39 "github.com/JFJun/bsc/log" 40 "github.com/JFJun/bsc/params" 41 cli "gopkg.in/urfave/cli.v1" 42 ) 43 44 var runCommand = cli.Command{ 45 Action: runCmd, 46 Name: "run", 47 Usage: "run arbitrary evm binary", 48 ArgsUsage: "<code>", 49 Description: `The run command runs arbitrary EVM code.`, 50 } 51 52 // readGenesis will read the given JSON format genesis file and return 53 // the initialized Genesis structure 54 func readGenesis(genesisPath string) *core.Genesis { 55 // Make sure we have a valid genesis JSON 56 //genesisPath := ctx.Args().First() 57 if len(genesisPath) == 0 { 58 utils.Fatalf("Must supply path to genesis JSON file") 59 } 60 file, err := os.Open(genesisPath) 61 if err != nil { 62 utils.Fatalf("Failed to read genesis file: %v", err) 63 } 64 defer file.Close() 65 66 genesis := new(core.Genesis) 67 if err := json.NewDecoder(file).Decode(genesis); err != nil { 68 utils.Fatalf("invalid genesis file: %v", err) 69 } 70 return genesis 71 } 72 73 type execStats struct { 74 time time.Duration // The execution time. 75 allocs int64 // The number of heap allocations during execution. 76 bytesAllocated int64 // The cumulative number of bytes allocated during execution. 77 } 78 79 func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) (output []byte, gasLeft uint64, stats execStats, err error) { 80 if bench { 81 result := testing.Benchmark(func(b *testing.B) { 82 for i := 0; i < b.N; i++ { 83 output, gasLeft, err = execFunc() 84 } 85 }) 86 87 // Get the average execution time from the benchmarking result. 88 // There are other useful stats here that could be reported. 89 stats.time = time.Duration(result.NsPerOp()) 90 stats.allocs = result.AllocsPerOp() 91 stats.bytesAllocated = result.AllocedBytesPerOp() 92 } else { 93 var memStatsBefore, memStatsAfter goruntime.MemStats 94 goruntime.ReadMemStats(&memStatsBefore) 95 startTime := time.Now() 96 output, gasLeft, err = execFunc() 97 stats.time = time.Since(startTime) 98 goruntime.ReadMemStats(&memStatsAfter) 99 stats.allocs = int64(memStatsAfter.Mallocs - memStatsBefore.Mallocs) 100 stats.bytesAllocated = int64(memStatsAfter.TotalAlloc - memStatsBefore.TotalAlloc) 101 } 102 103 return output, gasLeft, stats, err 104 } 105 106 func runCmd(ctx *cli.Context) error { 107 glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) 108 glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name))) 109 log.Root().SetHandler(glogger) 110 logconfig := &vm.LogConfig{ 111 DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name), 112 DisableStack: ctx.GlobalBool(DisableStackFlag.Name), 113 Debug: ctx.GlobalBool(DebugFlag.Name), 114 } 115 116 var ( 117 tracer vm.Tracer 118 debugLogger *vm.StructLogger 119 statedb *state.StateDB 120 chainConfig *params.ChainConfig 121 sender = common.BytesToAddress([]byte("sender")) 122 receiver = common.BytesToAddress([]byte("receiver")) 123 genesisConfig *core.Genesis 124 ) 125 if ctx.GlobalBool(MachineFlag.Name) { 126 tracer = vm.NewJSONLogger(logconfig, os.Stdout) 127 } else if ctx.GlobalBool(DebugFlag.Name) { 128 debugLogger = vm.NewStructLogger(logconfig) 129 tracer = debugLogger 130 } else { 131 debugLogger = vm.NewStructLogger(logconfig) 132 } 133 if ctx.GlobalString(GenesisFlag.Name) != "" { 134 gen := readGenesis(ctx.GlobalString(GenesisFlag.Name)) 135 genesisConfig = gen 136 db := rawdb.NewMemoryDatabase() 137 genesis := gen.ToBlock(db) 138 statedb, _ = state.New(genesis.Root(), state.NewDatabase(db), nil) 139 chainConfig = gen.Config 140 } else { 141 statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 142 genesisConfig = new(core.Genesis) 143 } 144 if ctx.GlobalString(SenderFlag.Name) != "" { 145 sender = common.HexToAddress(ctx.GlobalString(SenderFlag.Name)) 146 } 147 statedb.CreateAccount(sender) 148 149 if ctx.GlobalString(ReceiverFlag.Name) != "" { 150 receiver = common.HexToAddress(ctx.GlobalString(ReceiverFlag.Name)) 151 } 152 153 var code []byte 154 codeFileFlag := ctx.GlobalString(CodeFileFlag.Name) 155 codeFlag := ctx.GlobalString(CodeFlag.Name) 156 157 // The '--code' or '--codefile' flag overrides code in state 158 if codeFileFlag != "" || codeFlag != "" { 159 var hexcode []byte 160 if codeFileFlag != "" { 161 var err error 162 // If - is specified, it means that code comes from stdin 163 if codeFileFlag == "-" { 164 //Try reading from stdin 165 if hexcode, err = ioutil.ReadAll(os.Stdin); err != nil { 166 fmt.Printf("Could not load code from stdin: %v\n", err) 167 os.Exit(1) 168 } 169 } else { 170 // Codefile with hex assembly 171 if hexcode, err = ioutil.ReadFile(codeFileFlag); err != nil { 172 fmt.Printf("Could not load code from file: %v\n", err) 173 os.Exit(1) 174 } 175 } 176 } else { 177 hexcode = []byte(codeFlag) 178 } 179 hexcode = bytes.TrimSpace(hexcode) 180 if len(hexcode)%2 != 0 { 181 fmt.Printf("Invalid input length for hex data (%d)\n", len(hexcode)) 182 os.Exit(1) 183 } 184 code = common.FromHex(string(hexcode)) 185 } else if fn := ctx.Args().First(); len(fn) > 0 { 186 // EASM-file to compile 187 src, err := ioutil.ReadFile(fn) 188 if err != nil { 189 return err 190 } 191 bin, err := compiler.Compile(fn, src, false) 192 if err != nil { 193 return err 194 } 195 code = common.Hex2Bytes(bin) 196 } 197 initialGas := ctx.GlobalUint64(GasFlag.Name) 198 if genesisConfig.GasLimit != 0 { 199 initialGas = genesisConfig.GasLimit 200 } 201 runtimeConfig := runtime.Config{ 202 Origin: sender, 203 State: statedb, 204 GasLimit: initialGas, 205 GasPrice: utils.GlobalBig(ctx, PriceFlag.Name), 206 Value: utils.GlobalBig(ctx, ValueFlag.Name), 207 Difficulty: genesisConfig.Difficulty, 208 Time: new(big.Int).SetUint64(genesisConfig.Timestamp), 209 Coinbase: genesisConfig.Coinbase, 210 BlockNumber: new(big.Int).SetUint64(genesisConfig.Number), 211 EVMConfig: vm.Config{ 212 Tracer: tracer, 213 Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name), 214 EVMInterpreter: ctx.GlobalString(EVMInterpreterFlag.Name), 215 }, 216 } 217 218 if cpuProfilePath := ctx.GlobalString(CPUProfileFlag.Name); cpuProfilePath != "" { 219 f, err := os.Create(cpuProfilePath) 220 if err != nil { 221 fmt.Println("could not create CPU profile: ", err) 222 os.Exit(1) 223 } 224 if err := pprof.StartCPUProfile(f); err != nil { 225 fmt.Println("could not start CPU profile: ", err) 226 os.Exit(1) 227 } 228 defer pprof.StopCPUProfile() 229 } 230 231 if chainConfig != nil { 232 runtimeConfig.ChainConfig = chainConfig 233 } else { 234 runtimeConfig.ChainConfig = params.AllEthashProtocolChanges 235 } 236 237 var hexInput []byte 238 if inputFileFlag := ctx.GlobalString(InputFileFlag.Name); inputFileFlag != "" { 239 var err error 240 if hexInput, err = ioutil.ReadFile(inputFileFlag); err != nil { 241 fmt.Printf("could not load input from file: %v\n", err) 242 os.Exit(1) 243 } 244 } else { 245 hexInput = []byte(ctx.GlobalString(InputFlag.Name)) 246 } 247 input := common.FromHex(string(bytes.TrimSpace(hexInput))) 248 249 var execFunc func() ([]byte, uint64, error) 250 if ctx.GlobalBool(CreateFlag.Name) { 251 input = append(code, input...) 252 execFunc = func() ([]byte, uint64, error) { 253 output, _, gasLeft, err := runtime.Create(input, &runtimeConfig) 254 return output, gasLeft, err 255 } 256 } else { 257 if len(code) > 0 { 258 statedb.SetCode(receiver, code) 259 } 260 execFunc = func() ([]byte, uint64, error) { 261 return runtime.Call(receiver, input, &runtimeConfig) 262 } 263 } 264 265 bench := ctx.GlobalBool(BenchFlag.Name) 266 output, leftOverGas, stats, err := timedExec(bench, execFunc) 267 268 if ctx.GlobalBool(DumpFlag.Name) { 269 statedb.Commit(true) 270 statedb.IntermediateRoot(true) 271 fmt.Println(string(statedb.Dump(false, false, true))) 272 } 273 274 if memProfilePath := ctx.GlobalString(MemProfileFlag.Name); memProfilePath != "" { 275 f, err := os.Create(memProfilePath) 276 if err != nil { 277 fmt.Println("could not create memory profile: ", err) 278 os.Exit(1) 279 } 280 if err := pprof.WriteHeapProfile(f); err != nil { 281 fmt.Println("could not write memory profile: ", err) 282 os.Exit(1) 283 } 284 f.Close() 285 } 286 287 if ctx.GlobalBool(DebugFlag.Name) { 288 if debugLogger != nil { 289 fmt.Fprintln(os.Stderr, "#### TRACE ####") 290 vm.WriteTrace(os.Stderr, debugLogger.StructLogs()) 291 } 292 fmt.Fprintln(os.Stderr, "#### LOGS ####") 293 vm.WriteLogs(os.Stderr, statedb.Logs()) 294 } 295 296 if bench || ctx.GlobalBool(StatDumpFlag.Name) { 297 fmt.Fprintf(os.Stderr, `EVM gas used: %d 298 execution time: %v 299 allocations: %d 300 allocated bytes: %d 301 `, initialGas-leftOverGas, stats.time, stats.allocs, stats.bytesAllocated) 302 } 303 if tracer == nil { 304 fmt.Printf("0x%x\n", output) 305 if err != nil { 306 fmt.Printf(" error: %v\n", err) 307 } 308 } 309 310 return nil 311 }