github.com/ethereum/go-ethereum@v1.14.4-0.20240516095835-473ee8fc07a3/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 "testing" 28 "time" 29 30 "github.com/ethereum/go-ethereum/cmd/evm/internal/compiler" 31 "github.com/ethereum/go-ethereum/cmd/utils" 32 "github.com/ethereum/go-ethereum/common" 33 "github.com/ethereum/go-ethereum/core" 34 "github.com/ethereum/go-ethereum/core/rawdb" 35 "github.com/ethereum/go-ethereum/core/state" 36 "github.com/ethereum/go-ethereum/core/tracing" 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/params" 42 "github.com/ethereum/go-ethereum/triedb" 43 "github.com/ethereum/go-ethereum/triedb/hashdb" 44 "github.com/urfave/cli/v2" 45 ) 46 47 var runCommand = &cli.Command{ 48 Action: runCmd, 49 Name: "run", 50 Usage: "Run arbitrary evm binary", 51 ArgsUsage: "<code>", 52 Description: `The run command runs arbitrary EVM code.`, 53 Flags: flags.Merge(vmFlags, traceFlags), 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 logconfig := &logger.Config{ 112 EnableMemory: !ctx.Bool(DisableMemoryFlag.Name), 113 DisableStack: ctx.Bool(DisableStackFlag.Name), 114 DisableStorage: ctx.Bool(DisableStorageFlag.Name), 115 EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), 116 Debug: ctx.Bool(DebugFlag.Name), 117 } 118 119 var ( 120 tracer *tracing.Hooks 121 debugLogger *logger.StructLogger 122 statedb *state.StateDB 123 chainConfig *params.ChainConfig 124 sender = common.BytesToAddress([]byte("sender")) 125 receiver = common.BytesToAddress([]byte("receiver")) 126 preimages = ctx.Bool(DumpFlag.Name) 127 blobHashes []common.Hash // TODO (MariusVanDerWijden) implement blob hashes in state tests 128 blobBaseFee = new(big.Int) // TODO (MariusVanDerWijden) implement blob fee in state tests 129 ) 130 if ctx.Bool(MachineFlag.Name) { 131 tracer = logger.NewJSONLogger(logconfig, os.Stdout) 132 } else if ctx.Bool(DebugFlag.Name) { 133 debugLogger = logger.NewStructLogger(logconfig) 134 tracer = debugLogger.Hooks() 135 } else { 136 debugLogger = logger.NewStructLogger(logconfig) 137 } 138 139 initialGas := ctx.Uint64(GasFlag.Name) 140 genesisConfig := new(core.Genesis) 141 genesisConfig.GasLimit = initialGas 142 if ctx.String(GenesisFlag.Name) != "" { 143 genesisConfig = readGenesis(ctx.String(GenesisFlag.Name)) 144 if genesisConfig.GasLimit != 0 { 145 initialGas = genesisConfig.GasLimit 146 } 147 } else { 148 genesisConfig.Config = params.AllDevChainProtocolChanges 149 } 150 151 db := rawdb.NewMemoryDatabase() 152 triedb := triedb.NewDatabase(db, &triedb.Config{ 153 Preimages: preimages, 154 HashDB: hashdb.Defaults, 155 }) 156 defer triedb.Close() 157 genesis := genesisConfig.MustCommit(db, triedb) 158 sdb := state.NewDatabaseWithNodeDB(db, triedb) 159 statedb, _ = state.New(genesis.Root(), sdb, nil) 160 chainConfig = genesisConfig.Config 161 162 if ctx.String(SenderFlag.Name) != "" { 163 sender = common.HexToAddress(ctx.String(SenderFlag.Name)) 164 } 165 statedb.CreateAccount(sender) 166 167 if ctx.String(ReceiverFlag.Name) != "" { 168 receiver = common.HexToAddress(ctx.String(ReceiverFlag.Name)) 169 } 170 171 var code []byte 172 codeFileFlag := ctx.String(CodeFileFlag.Name) 173 codeFlag := ctx.String(CodeFlag.Name) 174 175 // The '--code' or '--codefile' flag overrides code in state 176 if codeFileFlag != "" || codeFlag != "" { 177 var hexcode []byte 178 if codeFileFlag != "" { 179 var err error 180 // If - is specified, it means that code comes from stdin 181 if codeFileFlag == "-" { 182 //Try reading from stdin 183 if hexcode, err = io.ReadAll(os.Stdin); err != nil { 184 fmt.Printf("Could not load code from stdin: %v\n", err) 185 os.Exit(1) 186 } 187 } else { 188 // Codefile with hex assembly 189 if hexcode, err = os.ReadFile(codeFileFlag); err != nil { 190 fmt.Printf("Could not load code from file: %v\n", err) 191 os.Exit(1) 192 } 193 } 194 } else { 195 hexcode = []byte(codeFlag) 196 } 197 hexcode = bytes.TrimSpace(hexcode) 198 if len(hexcode)%2 != 0 { 199 fmt.Printf("Invalid input length for hex data (%d)\n", len(hexcode)) 200 os.Exit(1) 201 } 202 code = common.FromHex(string(hexcode)) 203 } else if fn := ctx.Args().First(); len(fn) > 0 { 204 // EASM-file to compile 205 src, err := os.ReadFile(fn) 206 if err != nil { 207 return err 208 } 209 bin, err := compiler.Compile(fn, src, false) 210 if err != nil { 211 return err 212 } 213 code = common.Hex2Bytes(bin) 214 } 215 runtimeConfig := runtime.Config{ 216 Origin: sender, 217 State: statedb, 218 GasLimit: initialGas, 219 GasPrice: flags.GlobalBig(ctx, PriceFlag.Name), 220 Value: flags.GlobalBig(ctx, ValueFlag.Name), 221 Difficulty: genesisConfig.Difficulty, 222 Time: genesisConfig.Timestamp, 223 Coinbase: genesisConfig.Coinbase, 224 BlockNumber: new(big.Int).SetUint64(genesisConfig.Number), 225 BlobHashes: blobHashes, 226 BlobBaseFee: blobBaseFee, 227 EVMConfig: vm.Config{ 228 Tracer: tracer, 229 }, 230 } 231 232 if chainConfig != nil { 233 runtimeConfig.ChainConfig = chainConfig 234 } else { 235 runtimeConfig.ChainConfig = params.AllEthashProtocolChanges 236 } 237 238 var hexInput []byte 239 if inputFileFlag := ctx.String(InputFileFlag.Name); inputFileFlag != "" { 240 var err error 241 if hexInput, err = os.ReadFile(inputFileFlag); err != nil { 242 fmt.Printf("could not load input from file: %v\n", err) 243 os.Exit(1) 244 } 245 } else { 246 hexInput = []byte(ctx.String(InputFlag.Name)) 247 } 248 hexInput = bytes.TrimSpace(hexInput) 249 if len(hexInput)%2 != 0 { 250 fmt.Println("input length must be even") 251 os.Exit(1) 252 } 253 input := common.FromHex(string(hexInput)) 254 255 var execFunc func() ([]byte, uint64, error) 256 if ctx.Bool(CreateFlag.Name) { 257 input = append(code, input...) 258 execFunc = func() ([]byte, uint64, error) { 259 output, _, gasLeft, err := runtime.Create(input, &runtimeConfig) 260 return output, gasLeft, err 261 } 262 } else { 263 if len(code) > 0 { 264 statedb.SetCode(receiver, code) 265 } 266 execFunc = func() ([]byte, uint64, error) { 267 return runtime.Call(receiver, input, &runtimeConfig) 268 } 269 } 270 271 bench := ctx.Bool(BenchFlag.Name) 272 output, leftOverGas, stats, err := timedExec(bench, execFunc) 273 274 if ctx.Bool(DumpFlag.Name) { 275 root, err := statedb.Commit(genesisConfig.Number, true) 276 if err != nil { 277 fmt.Printf("Failed to commit changes %v\n", err) 278 return err 279 } 280 dumpdb, err := state.New(root, sdb, nil) 281 if err != nil { 282 fmt.Printf("Failed to open statedb %v\n", err) 283 return err 284 } 285 fmt.Println(string(dumpdb.Dump(nil))) 286 } 287 288 if ctx.Bool(DebugFlag.Name) { 289 if debugLogger != nil { 290 fmt.Fprintln(os.Stderr, "#### TRACE ####") 291 logger.WriteTrace(os.Stderr, debugLogger.StructLogs()) 292 } 293 fmt.Fprintln(os.Stderr, "#### LOGS ####") 294 logger.WriteLogs(os.Stderr, statedb.Logs()) 295 } 296 297 if bench || ctx.Bool(StatDumpFlag.Name) { 298 fmt.Fprintf(os.Stderr, `EVM gas used: %d 299 execution time: %v 300 allocations: %d 301 allocated bytes: %d 302 `, initialGas-leftOverGas, stats.time, stats.allocs, stats.bytesAllocated) 303 } 304 if tracer == nil { 305 fmt.Printf("%#x\n", output) 306 if err != nil { 307 fmt.Printf(" error: %v\n", err) 308 } 309 } 310 311 return nil 312 }