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