github.com/core-coin/go-core/v2@v2.1.9/cmd/cvm/runner.go (about) 1 // Copyright 2017 by the Authors 2 // This file is part of go-core. 3 // 4 // go-core 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-core 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-core. 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 "gopkg.in/urfave/cli.v1" 32 33 "github.com/core-coin/go-core/v2/cmd/cvm/internal/compiler" 34 35 "github.com/core-coin/go-core/v2/cmd/utils" 36 "github.com/core-coin/go-core/v2/common" 37 "github.com/core-coin/go-core/v2/core" 38 "github.com/core-coin/go-core/v2/core/rawdb" 39 "github.com/core-coin/go-core/v2/core/state" 40 "github.com/core-coin/go-core/v2/core/vm" 41 "github.com/core-coin/go-core/v2/core/vm/runtime" 42 "github.com/core-coin/go-core/v2/log" 43 "github.com/core-coin/go-core/v2/params" 44 ) 45 46 var runCommand = cli.Command{ 47 Action: runCmd, 48 Name: "run", 49 Usage: "run arbitrary cvm binary", 50 ArgsUsage: "<code>", 51 Description: `The run command runs arbitrary CVM 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, energyLeft 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, energyLeft, 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, energyLeft, 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, energyLeft, 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.GlobalInt(VerbosityFlag.Name))) 111 log.Root().SetHandler(glogger) 112 logconfig := &vm.LogConfig{ 113 DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name), 114 DisableStack: ctx.GlobalBool(DisableStackFlag.Name), 115 DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name), 116 DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name), 117 Debug: ctx.GlobalBool(DebugFlag.Name), 118 } 119 120 if id := ctx.GlobalUint64(utils.NetworkIdFlag.Name); id != 0 { 121 common.DefaultNetworkID = common.NetworkID(id) 122 } 123 124 var ( 125 tracer vm.Tracer 126 debugLogger *vm.StructLogger 127 statedb *state.StateDB 128 chainConfig *params.ChainConfig 129 genesisConfig *core.Genesis 130 ) 131 if ctx.GlobalBool(MachineFlag.Name) { 132 tracer = vm.NewJSONLogger(logconfig, os.Stdout) 133 } else if ctx.GlobalBool(DebugFlag.Name) { 134 debugLogger = vm.NewStructLogger(logconfig) 135 tracer = debugLogger 136 } else { 137 debugLogger = vm.NewStructLogger(logconfig) 138 } 139 if ctx.GlobalString(GenesisFlag.Name) != "" { 140 gen := readGenesis(ctx.GlobalString(GenesisFlag.Name)) 141 genesisConfig = gen 142 db := rawdb.NewMemoryDatabase() 143 genesis := gen.ToBlock(db) 144 statedb, _ = state.New(genesis.Root(), state.NewDatabase(db), nil) 145 chainConfig = gen.Config 146 common.DefaultNetworkID = common.NetworkID(chainConfig.NetworkID.Int64()) 147 } else { 148 statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) 149 genesisConfig = new(core.Genesis) 150 } 151 152 sender := common.BytesToAddress([]byte("sender")) 153 receiver := common.BytesToAddress([]byte("receiver")) 154 155 var err error 156 if ctx.GlobalString(SenderFlag.Name) != "" { 157 sender, err = common.HexToAddress(ctx.GlobalString(SenderFlag.Name)) 158 if err != nil { 159 return err 160 } 161 } 162 statedb.CreateAccount(sender) 163 164 if ctx.GlobalString(ReceiverFlag.Name) != "" { 165 receiver, err = common.HexToAddress(ctx.GlobalString(ReceiverFlag.Name)) 166 if err != nil { 167 return err 168 } 169 } 170 171 var code []byte 172 codeFileFlag := ctx.GlobalString(CodeFileFlag.Name) 173 codeFlag := ctx.GlobalString(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 = ioutil.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 = ioutil.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 := ioutil.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 initialEnergy := ctx.GlobalUint64(EnergyFlag.Name) 216 if genesisConfig.EnergyLimit != 0 { 217 initialEnergy = genesisConfig.EnergyLimit 218 } 219 runtimeConfig := runtime.Config{ 220 Origin: sender, 221 State: statedb, 222 EnergyLimit: initialEnergy, 223 EnergyPrice: utils.GlobalBig(ctx, PriceFlag.Name), 224 Value: utils.GlobalBig(ctx, ValueFlag.Name), 225 Difficulty: genesisConfig.Difficulty, 226 Time: new(big.Int).SetUint64(genesisConfig.Timestamp), 227 Coinbase: genesisConfig.Coinbase, 228 BlockNumber: new(big.Int).SetUint64(genesisConfig.Number), 229 CVMConfig: vm.Config{ 230 Tracer: tracer, 231 Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name), 232 CVMInterpreter: ctx.GlobalString(CVMInterpreterFlag.Name), 233 }, 234 } 235 236 if cpuProfilePath := ctx.GlobalString(CPUProfileFlag.Name); cpuProfilePath != "" { 237 f, err := os.Create(cpuProfilePath) 238 if err != nil { 239 fmt.Println("could not create CPU profile: ", err) 240 os.Exit(1) 241 } 242 if err := pprof.StartCPUProfile(f); err != nil { 243 fmt.Println("could not start CPU profile: ", err) 244 os.Exit(1) 245 } 246 defer pprof.StopCPUProfile() 247 } 248 249 if chainConfig != nil { 250 runtimeConfig.ChainConfig = chainConfig 251 } else { 252 runtimeConfig.ChainConfig = params.MainnetChainConfig 253 } 254 255 var hexInput []byte 256 if inputFileFlag := ctx.GlobalString(InputFileFlag.Name); inputFileFlag != "" { 257 var err error 258 if hexInput, err = ioutil.ReadFile(inputFileFlag); err != nil { 259 fmt.Printf("could not load input from file: %v\n", err) 260 os.Exit(1) 261 } 262 } else { 263 hexInput = []byte(ctx.GlobalString(InputFlag.Name)) 264 } 265 input := common.FromHex(string(bytes.TrimSpace(hexInput))) 266 267 var execFunc func() ([]byte, uint64, error) 268 if ctx.GlobalBool(CreateFlag.Name) { 269 input = append(code, input...) 270 execFunc = func() ([]byte, uint64, error) { 271 output, _, energyLeft, err := runtime.Create(input, &runtimeConfig) 272 return output, energyLeft, err 273 } 274 } else { 275 if len(code) > 0 { 276 statedb.SetCode(receiver, code) 277 } 278 execFunc = func() ([]byte, uint64, error) { 279 return runtime.Call(receiver, input, &runtimeConfig) 280 } 281 } 282 283 bench := ctx.GlobalBool(BenchFlag.Name) 284 output, leftOverEnergy, stats, err := timedExec(bench, execFunc) 285 286 if ctx.GlobalBool(DumpFlag.Name) { 287 statedb.Commit(true) 288 statedb.IntermediateRoot(true) 289 fmt.Println(string(statedb.Dump(false, false, true))) 290 } 291 292 if memProfilePath := ctx.GlobalString(MemProfileFlag.Name); memProfilePath != "" { 293 f, err := os.Create(memProfilePath) 294 if err != nil { 295 fmt.Println("could not create memory profile: ", err) 296 os.Exit(1) 297 } 298 if err := pprof.WriteHeapProfile(f); err != nil { 299 fmt.Println("could not write memory profile: ", err) 300 os.Exit(1) 301 } 302 f.Close() 303 } 304 305 if ctx.GlobalBool(DebugFlag.Name) { 306 if debugLogger != nil { 307 fmt.Fprintln(os.Stderr, "#### TRACE ####") 308 vm.WriteTrace(os.Stderr, debugLogger.StructLogs()) 309 } 310 fmt.Fprintln(os.Stderr, "#### LOGS ####") 311 vm.WriteLogs(os.Stderr, statedb.Logs()) 312 } 313 314 if bench || ctx.GlobalBool(StatDumpFlag.Name) { 315 fmt.Fprintf(os.Stderr, `CVM energy used: %d 316 execution time: %v 317 allocations: %d 318 allocated bytes: %d 319 `, initialEnergy-leftOverEnergy, stats.time, stats.allocs, stats.bytesAllocated) 320 } 321 if tracer == nil { 322 fmt.Printf("0x%x\n", output) 323 if err != nil { 324 fmt.Printf(" error: %v\n", err) 325 } 326 } 327 328 return nil 329 }