diff --git a/cmd/evm/json_logger.go b/cmd/evm/json_logger.go index d61981062c98..b7372b70b4fc 100644 --- a/cmd/evm/json_logger.go +++ b/cmd/evm/json_logger.go @@ -40,7 +40,7 @@ func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos log := vm.StructLog{ Pc: pc, Op: op, - Gas: gas + cost, + Gas: gas, GasCost: cost, MemorySize: memory.Len(), Storage: nil, diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 1892ae3d312e..c0e2e35a553c 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -102,6 +102,10 @@ var ( Name: "sender", Usage: "The transaction origin", } + ReceiverFlag = cli.StringFlag{ + Name: "receiver", + Usage: "The transaction receiver (execution context)", + } DisableMemoryFlag = cli.BoolFlag{ Name: "nomemory", Usage: "disable memory output", @@ -131,6 +135,7 @@ func init() { GenesisFlag, MachineFlag, SenderFlag, + ReceiverFlag, DisableMemoryFlag, DisableStackFlag, } diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 3f95a0c93a35..5a80dd7f844b 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -24,6 +24,7 @@ import ( "os" "runtime/pprof" "time" + "math/big" goruntime "runtime" @@ -83,7 +84,12 @@ func runCmd(ctx *cli.Context) error { debugLogger *vm.StructLogger statedb *state.StateDB chainConfig *params.ChainConfig + genCoinbase common.Address + genTimestamp uint64 + genGasLimit uint64 + genDifficulty *big.Int sender = common.StringToAddress("sender") + receiver = common.StringToAddress("receiver") ) if ctx.GlobalBool(MachineFlag.Name) { tracer = NewJSONLogger(logconfig, os.Stdout) @@ -97,6 +103,14 @@ func runCmd(ctx *cli.Context) error { gen := readGenesis(ctx.GlobalString(GenesisFlag.Name)) _, statedb = gen.ToBlock() chainConfig = gen.Config + genCoinbase = gen.Coinbase + genDifficulty = gen.Difficulty + genGasLimit = gen.GasLimit + genTimestamp = gen.Timestamp + fmt.Println("runner.go gen.Difficulty:", gen.Difficulty) + fmt.Println("runner.go gen.Coinbase:", gen.Coinbase) + fmt.Println("runner.go gen.Timestamp:", gen.Timestamp) + fmt.Println("runner.go chainConfig:", chainConfig) } else { db, _ := ethdb.NewMemDatabase() statedb, _ = state.New(common.Hash{}, state.NewDatabase(db)) @@ -104,52 +118,63 @@ func runCmd(ctx *cli.Context) error { if ctx.GlobalString(SenderFlag.Name) != "" { sender = common.HexToAddress(ctx.GlobalString(SenderFlag.Name)) } + // createAccount overwrites the nonce in the prestate. should only be used if no prestate provided + // statedb.CreateAccount(sender) - statedb.CreateAccount(sender) + if ctx.GlobalString(ReceiverFlag.Name) != "" { + receiver = common.HexToAddress(ctx.GlobalString(ReceiverFlag.Name)) + } var ( code []byte ret []byte err error ) - if fn := ctx.Args().First(); len(fn) > 0 { - src, err := ioutil.ReadFile(fn) - if err != nil { - return err - } + if statedb.GetCodeSize(receiver) == 0 { + if fn := ctx.Args().First(); len(fn) > 0 { + src, err := ioutil.ReadFile(fn) + if err != nil { + return err + } - bin, err := compiler.Compile(fn, src, false) - if err != nil { - return err - } - code = common.Hex2Bytes(bin) - } else if ctx.GlobalString(CodeFlag.Name) != "" { - code = common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)) - } else { - var hexcode []byte - if ctx.GlobalString(CodeFileFlag.Name) != "" { - var err error - hexcode, err = ioutil.ReadFile(ctx.GlobalString(CodeFileFlag.Name)) + bin, err := compiler.Compile(fn, src, false) if err != nil { - fmt.Printf("Could not load code from file: %v\n", err) - os.Exit(1) + return err } + code = common.Hex2Bytes(bin) + } else if ctx.GlobalString(CodeFlag.Name) != "" { + code = common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)) } else { - var err error - hexcode, err = ioutil.ReadAll(os.Stdin) - if err != nil { - fmt.Printf("Could not load code from stdin: %v\n", err) - os.Exit(1) + var hexcode []byte + if ctx.GlobalString(CodeFileFlag.Name) != "" { + var err error + hexcode, err = ioutil.ReadFile(ctx.GlobalString(CodeFileFlag.Name)) + if err != nil { + fmt.Printf("Could not load code from file: %v\n", err) + os.Exit(1) + } + } else { + var err error + hexcode, err = ioutil.ReadAll(os.Stdin) + if err != nil { + fmt.Printf("Could not load code from stdin: %v\n", err) + os.Exit(1) + } } + code = common.Hex2Bytes(string(bytes.TrimRight(hexcode, "\n"))) } - code = common.Hex2Bytes(string(bytes.TrimRight(hexcode, "\n"))) } + initialGas := ctx.GlobalUint64(GasFlag.Name) runtimeConfig := runtime.Config{ Origin: sender, + Time: new(big.Int).SetUint64(genTimestamp), + Coinbase: genCoinbase, + Difficulty: genDifficulty, State: statedb, - GasLimit: initialGas, + GasLimit: genGasLimit, GasPrice: utils.GlobalBig(ctx, PriceFlag.Name), + TxGasLimit: initialGas, Value: utils.GlobalBig(ctx, ValueFlag.Name), EVMConfig: vm.Config{ Tracer: tracer, @@ -176,15 +201,30 @@ func runCmd(ctx *cli.Context) error { } tstart := time.Now() var leftOverGas uint64 + + // sender prepays the gas fee + mgval := new(big.Int).Mul(new(big.Int).SetUint64(initialGas), runtimeConfig.GasPrice) + statedb.SubBalance(sender, mgval) + if ctx.GlobalBool(CreateFlag.Name) { input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...) + intrinsicGas := core.IntrinsicGas(input, ctx.GlobalBool(CreateFlag.Name), true) + initialGas -= intrinsicGas.Uint64() + runtimeConfig.TxGasLimit = initialGas ret, _, leftOverGas, err = runtime.Create(input, &runtimeConfig) } else { - receiver := common.StringToAddress("receiver") - statedb.SetCode(receiver, code) - + if statedb.GetCodeSize(receiver) == 0 { + statedb.SetCode(receiver, code) + } + intrinsicGas := core.IntrinsicGas(common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), false, true) + initialGas -= intrinsicGas.Uint64() + runtimeConfig.TxGasLimit = initialGas ret, leftOverGas, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig) } + + remainingEther := new(big.Int).Mul(new(big.Int).SetUint64(leftOverGas), runtimeConfig.GasPrice) + statedb.AddBalance(sender, remainingEther) + execTime := time.Since(tstart) if ctx.GlobalBool(DumpFlag.Name) { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 0a225bca4115..b8277bea03ae 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -112,17 +112,21 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret op OpCode // current opcode mem = NewMemory() // bound memory stack = newstack() // local stack + stackCopy = newstack() + logged = bool(false) // For optimisation reason we're using uint64 as the program counter. // It's theoretically possible to go above 2^64. The YP defines the PC // to be uint256. Practically much less so feasible. pc = uint64(0) // program counter + pcCopy = uint64(0) + gasCopy = uint64(0) cost uint64 ) contract.Input = input defer func() { - if err != nil && in.cfg.Debug { - in.cfg.Tracer.CaptureState(in.evm, pc, op, contract.Gas, cost, mem, stack, contract, in.evm.depth, err) + if err != nil && !logged && in.cfg.Debug { + in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stackCopy, contract, in.evm.depth, err) } }() @@ -133,6 +137,7 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret for atomic.LoadInt32(&in.evm.abort) == 0 { // Get the memory location of pc op = contract.GetOp(pc) + logged = false // get the operation from the jump table matching the opcode operation := in.cfg.JumpTable[op] @@ -140,6 +145,15 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret return nil, err } + if in.cfg.Debug { + pcCopy = uint64(pc) + gasCopy = uint64(contract.Gas) + stackCopy = newstack() + for _, val := range stack.data { + stackCopy.push(val) + } + } + // if the op is invalid abort the process and return an error if !operation.valid { return nil, fmt.Errorf("invalid opcode 0x%x", int(op)) @@ -179,7 +193,9 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret } if in.cfg.Debug { - in.cfg.Tracer.CaptureState(in.evm, pc, op, contract.Gas, cost, mem, stack, contract, in.evm.depth, err) + // trace needs to be called before operation.execute for CALLs etc + in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stackCopy, contract, in.evm.depth, err) + logged = true } // execute the operation diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 7b41fe85a7f9..b6e68fed7888 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -19,7 +19,6 @@ package runtime import ( "math/big" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" @@ -29,8 +28,7 @@ func NewEnv(cfg *Config, state *state.StateDB) *vm.EVM { context := vm.Context{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, - GetHash: func(uint64) common.Hash { return common.Hash{} }, - + GetHash: cfg.GetHashFn, Origin: cfg.Origin, Coinbase: cfg.Coinbase, BlockNumber: cfg.BlockNumber, diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 44cde4f70a93..5ee954d80e73 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -40,6 +40,7 @@ type Config struct { Time *big.Int GasLimit uint64 GasPrice *big.Int + TxGasLimit uint64 Value *big.Int DisableJit bool // "disable" so it's enabled by default Debug bool @@ -79,7 +80,7 @@ func setDefaults(cfg *Config) { cfg.Value = new(big.Int) } if cfg.BlockNumber == nil { - cfg.BlockNumber = new(big.Int) + cfg.BlockNumber = big.NewInt(1) } if cfg.GetHashFn == nil { cfg.GetHashFn = func(n uint64) common.Hash { @@ -144,7 +145,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { code, address, leftOverGas, err := vmenv.Create( sender, input, - cfg.GasLimit, + cfg.TxGasLimit, cfg.Value, ) return code, address, leftOverGas, err @@ -166,7 +167,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er sender, address, input, - cfg.GasLimit, + cfg.TxGasLimit, cfg.Value, )