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