From 32b5ce9a327bb4fffcf621e9a6b64196c28440d8 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Sat, 14 May 2016 17:08:38 +0200 Subject: [PATCH] common/math, core/vm, core/state: EVM gas calculations to 64bit This changes the internals of the EVM-JIT by dropping support for blocks (and therefor also transactions) that can exceed a 64^2-1 gas-limit. This means that the EVM-JIT can drop support for abritrary precision (*big.Int) and move to 64bit based gas calculations. For obvious reasons integer overflows have added to make sure that gas calculations such as gas for memory doesn't overflow 64bit. If the overflow check fails it throws an out-of-gas error and will halt the execution of the code. --- cmd/evm/main.go | 33 +++--- common/big.go | 6 +- common/math/integer.go | 22 ++++ common/math/integer_test.go | 50 ++++++++ core/execution.go | 36 +++--- core/state/state_object.go | 2 +- core/state_transition.go | 4 +- core/vm/common.go | 36 ------ core/vm/contract.go | 37 +++--- core/vm/environment.go | 12 +- core/vm/gas.go | 36 ++++++ core/vm/instructions.go | 24 ++-- core/vm/jit.go | 224 ++++++++++++++++++++++++------------ core/vm/jit_optimiser.go | 20 ++-- core/vm/jit_test.go | 39 +++---- core/vm/logger_test.go | 12 +- core/vm/memory.go | 3 +- core/vm/runtime/env.go | 19 +-- core/vm/runtime/runtime.go | 2 - core/vm/segments.go | 4 +- core/vm/uint64.go | 175 ++++++++++++++++++++++++++++ core/vm/vm.go | 23 ++-- core/vm_env.go | 17 +-- tests/state_test_util.go | 7 +- tests/util.go | 38 +++--- tests/vm_test_util.go | 7 +- 26 files changed, 604 insertions(+), 284 deletions(-) create mode 100644 common/math/integer.go create mode 100644 common/math/integer_test.go create mode 100644 core/vm/uint64.go diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 3f44e0f3c0..dc19989334 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -120,8 +120,7 @@ func run(ctx *cli.Context) error { sender := statedb.CreateAccount(common.StringToAddress("sender")) logger := vm.NewStructLogger(nil) - - vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), vm.Config{ + vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), common.Big(ctx.GlobalString(PriceFlag.Name)), vm.Config{ Debug: ctx.GlobalBool(DebugFlag.Name), ForceJit: ctx.GlobalBool(ForceJitFlag.Name), EnableJit: !ctx.GlobalBool(DisableJitFlag.Name), @@ -141,7 +140,6 @@ func run(ctx *cli.Context) error { sender, input, common.Big(ctx.GlobalString(GasFlag.Name)), - common.Big(ctx.GlobalString(PriceFlag.Name)), common.Big(ctx.GlobalString(ValueFlag.Name)), ) } else { @@ -152,7 +150,6 @@ func run(ctx *cli.Context) error { receiver.Address(), common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), common.Big(ctx.GlobalString(GasFlag.Name)), - common.Big(ctx.GlobalString(PriceFlag.Name)), common.Big(ctx.GlobalString(ValueFlag.Name)), ) } @@ -199,19 +196,20 @@ type VMEnv struct { transactor *common.Address value *big.Int - depth int - Gas *big.Int - time *big.Int - logs []vm.StructLog + depth int + Gas, price *big.Int + time *big.Int + logs []vm.StructLog evm *vm.EVM } -func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg vm.Config) *VMEnv { +func NewEnv(state *state.StateDB, transactor common.Address, value, price *big.Int, cfg vm.Config) *VMEnv { env := &VMEnv{ state: state, transactor: &transactor, value: value, + price: price, time: big.NewInt(time.Now().Unix()), } @@ -237,6 +235,7 @@ func (self *VMEnv) Difficulty() *big.Int { return common.Big1 } func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) } func (self *VMEnv) Value() *big.Int { return self.value } func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) } +func (self *VMEnv) GasPrice() *big.Int { return big.NewInt(1000000000) } func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy } func (self *VMEnv) Depth() int { return 0 } func (self *VMEnv) SetDepth(i int) { self.depth = i } @@ -256,19 +255,19 @@ func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) { core.Transfer(from, to, amount) } -func (self *VMEnv) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { +func (self *VMEnv) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { self.Gas = gas - return core.Call(self, caller, addr, data, gas, price, value) + return core.Call(self, caller, addr, data, gas, value) } -func (self *VMEnv) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - return core.CallCode(self, caller, addr, data, gas, price, value) +func (self *VMEnv) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { + return core.CallCode(self, caller, addr, data, gas, value) } -func (self *VMEnv) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) { - return core.DelegateCall(self, caller, addr, data, gas, price) +func (self *VMEnv) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { + return core.DelegateCall(self, caller, addr, data, gas) } -func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { - return core.Create(self, caller, data, gas, price, value) +func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) { + return core.Create(self, caller, data, gas, value) } diff --git a/common/big.go b/common/big.go index 4ce87ee0c6..0282db68cf 100644 --- a/common/big.go +++ b/common/big.go @@ -16,7 +16,10 @@ package common -import "math/big" +import ( + "math" + "math/big" +) // Common big integers often used var ( @@ -33,6 +36,7 @@ var ( Big256 = big.NewInt(0xff) Big257 = big.NewInt(257) MaxBig = String2Big("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + Max64Bit = new(big.Int).SetUint64(math.MaxUint64) ) // Big pow diff --git a/common/math/integer.go b/common/math/integer.go new file mode 100644 index 0000000000..041cf3c700 --- /dev/null +++ b/common/math/integer.go @@ -0,0 +1,22 @@ +package math + +import gmath "math" + +/* + * NOTE: The following methods need to be optimised using either bit checking or asm + */ + +// IsMinSafe returns whether the subtraction is safe and does not overflow. +func IsSubSafe(x, y uint64) bool { + return x >= y +} + +// IsAddSafe returns whether the addition is safe and does not overflow. +func IsAddSafe(x, y uint64) bool { + return y <= gmath.MaxUint64-x +} + +// IsAddSafe returns whether the multiplication is safe and does not overflow. +func IsMulSafe(x, y uint64) bool { + return x == 0 || y == 0 || y <= gmath.MaxUint64/x +} diff --git a/common/math/integer_test.go b/common/math/integer_test.go new file mode 100644 index 0000000000..4e61c8a140 --- /dev/null +++ b/common/math/integer_test.go @@ -0,0 +1,50 @@ +package math + +import ( + gmath "math" + "testing" +) + +type operation byte + +const ( + sub operation = iota + add + mul +) + +func TestIsAddSafe(t *testing.T) { + for i, test := range []struct { + x uint64 + y uint64 + safe bool + op operation + }{ + // add operations + {gmath.MaxUint64, 1, false, add}, + {gmath.MaxUint64 - 1, 1, true, add}, + + // sub operations + {0, 1, false, sub}, + {0, 0, true, sub}, + + // mul operations + {10, 10, true, mul}, + {gmath.MaxUint64, 2, false, mul}, + {gmath.MaxUint64, 1, true, mul}, + } { + var isSafe bool + switch test.op { + case sub: + isSafe = IsSubSafe(test.x, test.y) + case add: + isSafe = IsAddSafe(test.x, test.y) + case mul: + isSafe = IsMulSafe(test.x, test.y) + } + + if test.safe != isSafe { + t.Errorf("%d failed. Expected test to be %v, got %v", i, test.safe, isSafe) + } + } +} diff --git a/core/execution.go b/core/execution.go index 82143443c7..f1af40f50b 100644 --- a/core/execution.go +++ b/core/execution.go @@ -26,30 +26,30 @@ import ( ) // Call executes within the given contract -func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) { - ret, _, err = exec(env, caller, &addr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value) +func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) { + ret, _, err = exec(env, caller, &addr, &addr, input, env.Db().GetCode(addr), gas, value) return ret, err } // CallCode executes the given address' code as the given contract address -func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) { +func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) { callerAddr := caller.Address() - ret, _, err = exec(env, caller, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value) + ret, _, err = exec(env, caller, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, value) return ret, err } // DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope -func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) { +func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas *big.Int) (ret []byte, err error) { callerAddr := caller.Address() originAddr := env.Origin() callerValue := caller.Value() - ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, callerValue) + ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, callerValue) return ret, err } // Create creates a new contract with the given code -func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) { - ret, address, err = exec(env, caller, nil, nil, nil, code, gas, gasPrice, value) +func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, value *big.Int) (ret []byte, address common.Address, err error) { + ret, address, err = exec(env, caller, nil, nil, nil, code, gas, value) // Here we get an error if we run into maximum stack depth, // See: https://github.com/ethereum/yellowpaper/pull/131 // and YP definitions for CREATE instruction @@ -59,18 +59,18 @@ func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPric return ret, address, err } -func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) { +func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) { evm := env.Vm() // Depth check execution. Fail if we're trying to execute above the // limit. if env.Depth() > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(gas, gasPrice) + caller.ReturnGas(gas.Uint64()) return nil, common.Address{}, vm.DepthError } if !env.CanTransfer(caller.Address(), value) { - caller.ReturnGas(gas, gasPrice) + caller.ReturnGas(gas.Uint64()) return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) } @@ -104,7 +104,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A // initialise a new contract and set the code that is to be used by the // EVM. The contract is a scoped environment for this execution context // only. - contract := vm.NewContract(caller, to, value, gas, gasPrice) + contract := vm.NewContract(caller, to, value, gas) contract.SetCallCode(codeAddr, code) defer contract.Finalise() @@ -116,7 +116,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A if err == nil && createAccount { dataGas := big.NewInt(int64(len(ret))) dataGas.Mul(dataGas, params.CreateDataGas) - if contract.UseGas(dataGas) { + if contract.UseGas(dataGas.Uint64()) { env.Db().SetCode(*address, ret) } else { err = vm.CodeStoreOutOfGasError @@ -127,7 +127,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) { - contract.UseGas(contract.Gas) + contract.UseGas(contract.Gas()) env.SetSnapshot(snapshotPreTransfer) } @@ -135,12 +135,12 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A return ret, addr, err } -func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) { +func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) { evm := env.Vm() // Depth check execution. Fail if we're trying to execute above the // limit. if env.Depth() > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(gas, gasPrice) + caller.ReturnGas(gas.Uint64()) return nil, common.Address{}, vm.DepthError } @@ -154,13 +154,13 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA } // Iinitialise a new contract and make initialise the delegate values - contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate() + contract := vm.NewContract(caller, to, value, gas).AsDelegate() contract.SetCallCode(codeAddr, code) defer contract.Finalise() ret, err = evm.Run(contract, input) if err != nil { - contract.UseGas(contract.Gas) + contract.UseGas(contract.Gas()) env.SetSnapshot(snapshot) } diff --git a/core/state/state_object.go b/core/state/state_object.go index c5462316dd..305c66f9d8 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -177,7 +177,7 @@ func (c *StateObject) St() Storage { } // Return the gas back to the origin. Used by the Virtual machine or Closures -func (c *StateObject) ReturnGas(gas, price *big.Int) {} +func (c *StateObject) ReturnGas(gas uint64) {} func (self *StateObject) Copy() *StateObject { stateObject := NewStateObject(self.Address(), self.db) diff --git a/core/state_transition.go b/core/state_transition.go index 9e6b2f567e..70f1844838 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -244,7 +244,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b vmenv := self.env //var addr common.Address if contractCreation { - ret, _, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value) + ret, _, err = vmenv.Create(sender, self.data, self.gas, self.value) if homestead && err == vm.CodeStoreOutOfGasError { self.gas = Big0 } @@ -256,7 +256,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b } else { // Increment the nonce for the next transaction self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1) - ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.gasPrice, self.value) + ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.value) if err != nil { glog.V(logger.Core).Infoln("VM call err:", err) } diff --git a/core/vm/common.go b/core/vm/common.go index 2878b92d2a..a17a6496b5 100644 --- a/core/vm/common.go +++ b/core/vm/common.go @@ -21,7 +21,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/params" ) // Type is the VM type accepted by **NewVm** @@ -45,41 +44,6 @@ var ( max = big.NewInt(math.MaxInt64) // Maximum 64 bit integer ) -// calculates the memory size required for a step -func calcMemSize(off, l *big.Int) *big.Int { - if l.Cmp(common.Big0) == 0 { - return common.Big0 - } - - return new(big.Int).Add(off, l) -} - -// calculates the quadratic gas -func quadMemGas(mem *Memory, newMemSize, gas *big.Int) { - if newMemSize.Cmp(common.Big0) > 0 { - newMemSizeWords := toWordSize(newMemSize) - newMemSize.Mul(newMemSizeWords, u256(32)) - - if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 { - // be careful reusing variables here when changing. - // The order has been optimised to reduce allocation - oldSize := toWordSize(big.NewInt(int64(mem.Len()))) - pow := new(big.Int).Exp(oldSize, common.Big2, Zero) - linCoef := oldSize.Mul(oldSize, params.MemoryGas) - quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv) - oldTotalFee := new(big.Int).Add(linCoef, quadCoef) - - pow.Exp(newMemSizeWords, common.Big2, Zero) - linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas) - quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv) - newTotalFee := linCoef.Add(linCoef, quadCoef) - - fee := newTotalFee.Sub(newTotalFee, oldTotalFee) - gas.Add(gas, fee) - } - } -} - // Simple helper func u256(n int64) *big.Int { return big.NewInt(n) diff --git a/core/vm/contract.go b/core/vm/contract.go index 844d3f328e..372f2e1885 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -24,7 +24,7 @@ import ( // ContractRef is a reference to the contract's backing object type ContractRef interface { - ReturnGas(*big.Int, *big.Int) + ReturnGas(uint64) Address() common.Address Value() *big.Int SetCode([]byte) @@ -47,7 +47,8 @@ type Contract struct { Input []byte CodeAddr *common.Address - value, Gas, UsedGas, Price *big.Int + value, gas *big.Int + gas64 uint64 Args []byte @@ -55,7 +56,7 @@ type Contract struct { } // NewContract returns a new contract environment for the execution of EVM. -func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract { +func NewContract(caller ContractRef, object ContractRef, value, gas *big.Int) *Contract { c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil} if parent, ok := caller.(*Contract); ok { @@ -67,12 +68,9 @@ func NewContract(caller ContractRef, object ContractRef, value, gas, price *big. // Gas should be a pointer so it can safely be reduced through the run // This pointer will be off the state transition - c.Gas = gas //new(big.Int).Set(gas) + c.gas = gas //new(big.Int).Set(gas) + c.gas64 = gas.Uint64() c.value = new(big.Int).Set(value) - // In most cases price and value are pointers to transaction objects - // and we don't want the transaction's values to change. - c.Price = new(big.Int).Set(price) - c.UsedGas = new(big.Int) return c } @@ -101,6 +99,11 @@ func (c *Contract) GetByte(n uint64) byte { return 0 } +// Gas returns the current gas left +func (c *Contract) Gas() uint64 { + return c.gas64 +} + // Caller returns the caller of the contract. // // Caller will recursively call caller when the contract is a delegate @@ -112,24 +115,24 @@ func (c *Contract) Caller() common.Address { // Finalise finalises the contract and returning any remaining gas to the original // caller. func (c *Contract) Finalise() { + c.gas.SetUint64(c.gas64) // Return the remaining gas to the caller - c.caller.ReturnGas(c.Gas, c.Price) + c.caller.ReturnGas(c.gas64) } // UseGas attempts the use gas and subtracts it and returns true on success -func (c *Contract) UseGas(gas *big.Int) (ok bool) { - ok = useGas(c.Gas, gas) - if ok { - c.UsedGas.Add(c.UsedGas, gas) +func (c *Contract) UseGas(gas uint64) (ok bool) { + if c.gas64 < gas { + return false } - return + c.gas64 -= gas + return true } // ReturnGas adds the given gas back to itself. -func (c *Contract) ReturnGas(gas, price *big.Int) { +func (c *Contract) ReturnGas(gas uint64) { // Return the gas to the context - c.Gas.Add(c.Gas, gas) - c.UsedGas.Sub(c.UsedGas, gas) + c.gas64 += gas } // Address returns the contracts address diff --git a/core/vm/environment.go b/core/vm/environment.go index 747627565e..a918d97210 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -53,6 +53,8 @@ type Environment interface { Difficulty() *big.Int // The gas limit of the block GasLimit() *big.Int + // The gas price of the current call + GasPrice() *big.Int // Determines whether it's possible to transact CanTransfer(from common.Address, balance *big.Int) bool // Transfers amount from one account to the other @@ -66,13 +68,13 @@ type Environment interface { // Set the current calling depth SetDepth(i int) // Call another contract - Call(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) + Call(me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) // Take another's contract code and execute within our own context - CallCode(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) + CallCode(me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) // Same as CallCode except sender and value is propagated from parent to child scope - DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) + DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) // Create a new contract - Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) + Create(me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) } // Vm is the basic interface for an implementation of the EVM. @@ -116,7 +118,7 @@ type Account interface { SetNonce(uint64) Balance() *big.Int Address() common.Address - ReturnGas(*big.Int, *big.Int) + ReturnGas(uint64) SetCode([]byte) ForEachStorage(cb func(key, value common.Hash) bool) Value() *big.Int diff --git a/core/vm/gas.go b/core/vm/gas.go index eb2c163463..440ee864d7 100644 --- a/core/vm/gas.go +++ b/core/vm/gas.go @@ -20,6 +20,7 @@ import ( "fmt" "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/params" ) @@ -72,6 +73,41 @@ func toWordSize(size *big.Int) *big.Int { return tmp } +// calculates the memory size required for a step +func calcMemSize(off, l *big.Int) *big.Int { + if l.Cmp(common.Big0) == 0 { + return common.Big0 + } + + return new(big.Int).Add(off, l) +} + +// calculates the quadratic gas +func quadMemGas(mem *Memory, newMemSize, gas *big.Int) { + if newMemSize.Cmp(common.Big0) > 0 { + newMemSizeWords := toWordSize(newMemSize) + newMemSize.Mul(newMemSizeWords, u256(32)) + + if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 { + // be careful reusing variables here when changing. + // The order has been optimised to reduce allocation + oldSize := toWordSize(big.NewInt(int64(mem.Len()))) + pow := new(big.Int).Exp(oldSize, common.Big2, Zero) + linCoef := oldSize.Mul(oldSize, params.MemoryGas) + quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv) + oldTotalFee := new(big.Int).Add(linCoef, quadCoef) + + pow.Exp(newMemSizeWords, common.Big2, Zero) + linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas) + quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv) + newTotalFee := linCoef.Add(linCoef, quadCoef) + + fee := newTotalFee.Sub(newTotalFee, oldTotalFee) + gas.Add(gas, fee) + } + } +} + type req struct { stackPop int gas *big.Int diff --git a/core/vm/instructions.go b/core/vm/instructions.go index a95ba26c52..8b081770cb 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -42,7 +42,7 @@ type instruction struct { fn instrFn data *big.Int - gas *big.Int + gas uint64 spop int spush int @@ -71,7 +71,7 @@ func (instr instruction) do(program *Program, pc *uint64, env Environment, contr return nil, OutOfGasError } // Resize the memory calculated previously - memory.Resize(newMemSize.Uint64()) + memory.Resize(newMemSize) // These opcodes return an argument and are therefor handled // differently from the rest of the opcodes @@ -316,7 +316,7 @@ func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { offset, size := stack.pop(), stack.pop() - hash := crypto.Keccak256(memory.Get(offset.Int64(), size.Int64())) + hash := crypto.Keccak256(memory.GetPtr(offset.Int64(), size.Int64())) stack.push(common.BytesToBig(hash)) } @@ -396,7 +396,7 @@ func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Con } func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { - stack.push(new(big.Int).Set(contract.Price)) + stack.push(new(big.Int).Set(env.GasPrice())) } func opBlockhash(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { @@ -461,7 +461,7 @@ func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, m func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { offset := stack.pop() - val := common.BigD(memory.Get(offset.Int64(), 32)) + val := common.BigD(memory.GetPtr(offset.Int64(), 32)) stack.push(val) } @@ -504,7 +504,7 @@ func opMsize(instr instruction, pc *uint64, env Environment, contract *Contract, } func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { - stack.push(new(big.Int).Set(contract.Gas)) + stack.push(new(big.Int).SetUint64(contract.gas64)) } func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { @@ -512,10 +512,10 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract value = stack.pop() offset, size = stack.pop(), stack.pop() input = memory.Get(offset.Int64(), size.Int64()) - gas = new(big.Int).Set(contract.Gas) + gas = new(big.Int).SetUint64(contract.gas64) ) - contract.UseGas(contract.Gas) - _, addr, suberr := env.Create(contract, input, gas, contract.Price, value) + contract.UseGas(contract.gas64) + _, addr, suberr := env.Create(contract, input, gas, value) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must @@ -548,7 +548,7 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, gas.Add(gas, params.CallStipend) } - ret, err := env.Call(contract, address, args, gas, contract.Price, value) + ret, err := env.Call(contract, address, args, gas, value) if err != nil { stack.push(new(big.Int)) @@ -579,7 +579,7 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra gas.Add(gas, params.CallStipend) } - ret, err := env.CallCode(contract, address, args, gas, contract.Price, value) + ret, err := env.CallCode(contract, address, args, gas, value) if err != nil { stack.push(new(big.Int)) @@ -596,7 +596,7 @@ func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Co toAddr := common.BigToAddress(to) args := memory.Get(inOffset.Int64(), inSize.Int64()) - ret, err := env.DelegateCall(contract, toAddr, args, gas, contract.Price) + ret, err := env.DelegateCall(contract, toAddr, args, gas) if err != nil { stack.push(new(big.Int)) } else { diff --git a/core/vm/jit.go b/core/vm/jit.go index 460a68dddd..2b451a8656 100644 --- a/core/vm/jit.go +++ b/core/vm/jit.go @@ -18,11 +18,13 @@ package vm import ( "fmt" + gmath "math" "math/big" "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" @@ -114,7 +116,7 @@ func (p *Program) addInstr(op OpCode, pc uint64, fn instrFn, data *big.Int) { if op >= DUP1 && op <= DUP16 { baseOp = DUP1 } - base := _baseCheck[baseOp] + base := _baseCheck64[baseOp] returns := op == RETURN || op == SUICIDE || op == STOP instr := instruction{op, pc, fn, data, base.gas, base.stackPop, base.stackPush, returns} @@ -357,14 +359,15 @@ func validDest(dests map[uint64]struct{}, dest *big.Int) bool { // jitCalculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for // the operation. This does not reduce gas or resizes the memory. -func jitCalculateGasAndSize(env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { +func jitCalculateGasAndSize(env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (uint64, uint64, error) { var ( - gas = new(big.Int) - newMemSize *big.Int = new(big.Int) + newMemSize uint64 + sizeFault bool ) - err := jitBaseCheck(instr, stack, gas) + + gas, err := jitBaseCalc(instr, stack) if err != nil { - return nil, nil, err + return 0, 0, err } // stack Check, memory resize & gas phase @@ -373,40 +376,57 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi n := int(op - SWAP1 + 2) err := stack.require(n) if err != nil { - return nil, nil, err + return 0, 0, err } - gas.Set(GasFastestStep) + gas = GasFastestStep64 case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: n := int(op - DUP1 + 1) err := stack.require(n) if err != nil { - return nil, nil, err + return 0, 0, err } - gas.Set(GasFastestStep) + gas = GasFastestStep64 case LOG0, LOG1, LOG2, LOG3, LOG4: n := int(op - LOG0) err := stack.require(n + 2) if err != nil { - return nil, nil, err + return 0, 0, err } mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1] + if mSize.BitLen() > 64 { + return 0, 0, OutOfGasError + } + msize64 := mSize.Uint64() - add := new(big.Int) - gas.Add(gas, params.LogGas) - gas.Add(gas, add.Mul(big.NewInt(int64(n)), params.LogTopicGas)) - gas.Add(gas, add.Mul(mSize, params.LogDataGas)) + gas = (gas + LogGas64) + (uint64(n) * LogTopicGas64) + if !math.IsMulSafe(msize64, LogDataGas64) { + return 0, 0, OutOfGasError + } + gasLogData := msize64 * LogDataGas64 - newMemSize = calcMemSize(mStart, mSize) + if !math.IsAddSafe(gas, gasLogData) { + return 0, 0, OutOfGasError + } + gas += gasLogData + + newMemSize, sizeFault = calcMemSize64(mStart, mSize) case EXP: - gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas)) + x := uint64(len(stack.data[stack.len()-2].Bytes())) + if !math.IsMulSafe(x, ExpByteGas64) { + return 0, 0, OutOfGasError + } + x *= ExpByteGas64 + if !math.IsAddSafe(gas, x) { + return 0, 0, OutOfGasError + } + gas += x case SSTORE: err := stack.require(2) if err != nil { - return nil, nil, err + return 0, 0, err } - var g *big.Int y, x := stack.data[stack.len()-2], stack.data[stack.len()-1] val := statedb.GetState(contract.Address(), common.BigToHash(x)) @@ -415,98 +435,152 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi // 2. From a non-zero value address to a zero-value address (DELETE) // 3. From a non-zero to a non-zero (CHANGE) if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) { - g = params.SstoreSetGas + gas = SstoreSetGas64 } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) { statedb.AddRefund(params.SstoreRefundGas) - g = params.SstoreClearGas + gas = SstoreClearGas64 } else { - g = params.SstoreResetGas + gas = SstoreResetGas64 } - gas.Set(g) case SUICIDE: if !statedb.IsDeleted(contract.Address()) { statedb.AddRefund(params.SuicideRefundGas) } case MLOAD: - newMemSize = calcMemSize(stack.peek(), u256(32)) + newMemSize, sizeFault = calcMemSize64(stack.peek(), u256(32)) case MSTORE8: - newMemSize = calcMemSize(stack.peek(), u256(1)) + newMemSize, sizeFault = calcMemSize64(stack.peek(), u256(1)) case MSTORE: - newMemSize = calcMemSize(stack.peek(), u256(32)) + newMemSize, sizeFault = calcMemSize64(stack.peek(), u256(32)) case RETURN: - newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) + newMemSize, sizeFault = calcMemSize64(stack.peek(), stack.data[stack.len()-2]) case SHA3: - newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) + newMemSize, sizeFault = calcMemSize64(stack.peek(), stack.data[stack.len()-2]) + if sizeFault { + return 0, 0, OutOfGasError + } - words := toWordSize(stack.data[stack.len()-2]) - gas.Add(gas, words.Mul(words, params.Sha3WordGas)) + if stack.data[stack.len()-2].BitLen() > 64 { + return 0, 0, OutOfGasError + } + words := toWordSize64(stack.data[stack.len()-2].Uint64()) + if !math.IsMulSafe(words, KeccakWordGas64) { + return 0, 0, OutOfGasError + } + wordsGas := words * KeccakWordGas64 + if !math.IsAddSafe(gas, wordsGas) { + return 0, 0, OutOfGasError + } + gas += wordsGas case CALLDATACOPY: - newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) + newMemSize, sizeFault = calcMemSize64(stack.peek(), stack.data[stack.len()-3]) + if sizeFault { + return 0, 0, OutOfGasError + } - words := toWordSize(stack.data[stack.len()-3]) - gas.Add(gas, words.Mul(words, params.CopyGas)) + words := toWordSize64(stack.data[stack.len()-3].Uint64()) + if !math.IsMulSafe(words, CopyGas64) { + return 0, 0, OutOfGasError + } + wordsGas := words * CopyGas64 + if !math.IsAddSafe(gas, wordsGas) { + return 0, 0, OutOfGasError + } + gas += wordsGas case CODECOPY: - newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) + newMemSize, sizeFault = calcMemSize64(stack.peek(), stack.data[stack.len()-3]) + if sizeFault { + return 0, 0, OutOfGasError + } - words := toWordSize(stack.data[stack.len()-3]) - gas.Add(gas, words.Mul(words, params.CopyGas)) + words := toWordSize64(stack.data[stack.len()-3].Uint64()) + if !math.IsMulSafe(words, CopyGas64) { + return 0, 0, OutOfGasError + } + wordsGas := words * CopyGas64 + if !math.IsAddSafe(gas, wordsGas) { + return 0, 0, OutOfGasError + } + gas += wordsGas case EXTCODECOPY: - newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4]) - - words := toWordSize(stack.data[stack.len()-4]) - gas.Add(gas, words.Mul(words, params.CopyGas)) + newMemSize, sizeFault = calcMemSize64(stack.data[stack.len()-2], stack.data[stack.len()-4]) + if sizeFault { + return 0, 0, OutOfGasError + } + words := toWordSize64(stack.data[stack.len()-4].Uint64()) + if !math.IsMulSafe(words, CopyGas64) { + return 0, 0, OutOfGasError + } + wordsGas := words * CopyGas64 + if !math.IsAddSafe(gas, wordsGas) { + return 0, 0, OutOfGasError + } + gas += wordsGas case CREATE: - newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3]) + newMemSize, sizeFault = calcMemSize64(stack.data[stack.len()-2], stack.data[stack.len()-3]) case CALL, CALLCODE: - gas.Add(gas, stack.data[stack.len()-1]) + callGas := stack.data[stack.len()-1] + if callGas.BitLen() > 64 { + return 0, 0, OutOfGasError + } + gas += callGas.Uint64() if op == CALL { if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) { - gas.Add(gas, params.CallNewAccountGas) + if !math.IsAddSafe(gas, CallNewAccountGas64) { + return 0, 0, OutOfGasError + } + gas += CallNewAccountGas64 } } if len(stack.data[stack.len()-3].Bytes()) > 0 { - gas.Add(gas, params.CallValueTransferGas) + if !math.IsAddSafe(gas, CallValueTransferGas64) { + return 0, 0, OutOfGasError + } + gas += CallValueTransferGas64 } - x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7]) - y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5]) + x, xSizeFault := calcMemSize64(stack.data[stack.len()-6], stack.data[stack.len()-7]) + if xSizeFault { + return 0, 0, OutOfGasError + } + y, ySizeFault := calcMemSize64(stack.data[stack.len()-4], stack.data[stack.len()-5]) + if ySizeFault { + return 0, 0, OutOfGasError + } - newMemSize = common.BigMax(x, y) + newMemSize = x + if y > newMemSize { + newMemSize = y + } case DELEGATECALL: - gas.Add(gas, stack.data[stack.len()-1]) + callGas := stack.data[stack.len()-1] + if callGas.BitLen() > 64 { + return 0, 0, OutOfGasError + } + gas += callGas.Uint64() - x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6]) - y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4]) + x, xSizeFault := calcMemSize64(stack.data[stack.len()-5], stack.data[stack.len()-6]) + if xSizeFault { + return 0, 0, OutOfGasError + } + y, ySizeFault := calcMemSize64(stack.data[stack.len()-3], stack.data[stack.len()-4]) + if ySizeFault { + return 0, 0, OutOfGasError + } - newMemSize = common.BigMax(x, y) + newMemSize = uint64(gmath.Max(float64(x), float64(y))) + } + if sizeFault { + return 0, 0, OutOfGasError } - quadMemGas(mem, newMemSize, gas) - return newMemSize, gas, nil -} - -// jitBaseCheck is the same as baseCheck except it doesn't do the look up in the -// gas table. This is done during compilation instead. -func jitBaseCheck(instr instruction, stack *Stack, gas *big.Int) error { - err := stack.require(instr.spop) - if err != nil { - return err - } - - if instr.spush > 0 && stack.len()-instr.spop+instr.spush > int(params.StackLimit.Int64()) { - return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64()) - } - - // nil on gas means no base calculation - if instr.gas == nil { - return nil - } - - gas.Add(gas, instr.gas) - - return nil + memGas, _ := calcQuadMemGas64(mem, newMemSize) + if !math.IsAddSafe(gas, memGas) { + return 0, 0, OutOfGasError + } + return toWordSize64(newMemSize) * 32, gas + memGas, nil } diff --git a/core/vm/jit_optimiser.go b/core/vm/jit_optimiser.go index 31ad0c2e2b..831af1ed10 100644 --- a/core/vm/jit_optimiser.go +++ b/core/vm/jit_optimiser.go @@ -30,15 +30,16 @@ func optimiseProgram(program *Program) { var load []instruction var ( - statsJump = 0 - statsPush = 0 + statsJump = 0 + statsOldPush = 0 + statsNewPush = 0 ) if glog.V(logger.Debug) { glog.Infof("optimising %x\n", program.Id[:4]) tstart := time.Now() defer func() { - glog.Infof("optimised %x done in %v with JMP: %d PSH: %d\n", program.Id[:4], time.Since(tstart), statsJump, statsPush) + glog.Infof("optimised %x done in %v with JMP: %d PSH: %d/%d\n", program.Id[:4], time.Since(tstart), statsJump, statsNewPush, statsOldPush) }() } @@ -65,6 +66,7 @@ func optimiseProgram(program *Program) { switch { case instr.op.IsPush(): load = append(load, instr) + statsOldPush++ case instr.op.IsStaticJump(): if len(load) == 0 { continue @@ -74,7 +76,7 @@ func optimiseProgram(program *Program) { if len(load) > 2 { seg, size := makePushSeg(load[:len(load)-1]) program.instructions[i-size-1] = seg - statsPush++ + statsNewPush++ } // create a segment consisting of a pre determined // jump, destination and validity. @@ -88,7 +90,7 @@ func optimiseProgram(program *Program) { if len(load) > 1 { seg, size := makePushSeg(load) program.instructions[i-size] = seg - statsPush++ + statsNewPush++ } load = nil } @@ -99,12 +101,12 @@ func optimiseProgram(program *Program) { func makePushSeg(instrs []instruction) (pushSeg, int) { var ( data []*big.Int - gas = new(big.Int) + gas uint64 ) for _, instr := range instrs { data = append(data, instr.data) - gas.Add(gas, instr.gas) + gas += instr.gas } return pushSeg{data, gas}, len(instrs) @@ -113,9 +115,7 @@ func makePushSeg(instrs []instruction) (pushSeg, int) { // makeStaticJumpSeg creates a new static jump segment from a predefined // destination (PUSH, JUMP). func makeStaticJumpSeg(to *big.Int, program *Program) jumpSeg { - gas := new(big.Int) - gas.Add(gas, _baseCheck[PUSH1].gas) - gas.Add(gas, _baseCheck[JUMP].gas) + gas := _baseCheck64[PUSH1].gas + _baseCheck64[JUMP].gas contract := &Contract{Code: program.code} pos, err := jump(program.mapping, program.destinations, contract, to) diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go index 809abfea97..dc83fabf4e 100644 --- a/core/vm/jit_test.go +++ b/core/vm/jit_test.go @@ -29,10 +29,7 @@ const maxRun = 1000 func TestSegmenting(t *testing.T) { prog := NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, 0x0}) - err := CompileProgram(prog) - if err != nil { - t.Fatal(err) - } + CompileProgram(prog) if instr, ok := prog.instructions[0].(pushSeg); ok { if len(instr.data) != 2 { @@ -43,20 +40,16 @@ func TestSegmenting(t *testing.T) { } prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)}) - err = CompileProgram(prog) - if err != nil { - t.Fatal(err) - } + CompileProgram(prog) + if _, ok := prog.instructions[1].(jumpSeg); ok { } else { t.Errorf("expected instr[1] to be jumpSeg, got %T", prog.instructions[1]) } prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)}) - err = CompileProgram(prog) - if err != nil { - t.Fatal(err) - } + CompileProgram(prog) + if instr, ok := prog.instructions[0].(pushSeg); ok { if len(instr.data) != 2 { t.Error("expected 2 element width pushSegment, got", len(instr.data)) @@ -72,10 +65,7 @@ func TestSegmenting(t *testing.T) { func TestCompiling(t *testing.T) { prog := NewProgram([]byte{0x60, 0x10}) - err := CompileProgram(prog) - if err != nil { - t.Error("didn't expect compile error") - } + CompileProgram(prog) if len(prog.instructions) != 1 { t.Error("expected 1 compiled instruction, got", len(prog.instructions)) @@ -85,8 +75,8 @@ func TestCompiling(t *testing.T) { func TestResetInput(t *testing.T) { var sender account - env := NewEnv(&Config{EnableJit: true, ForceJit: true}) - contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0)) + env := NewEnv(true, true) + contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000)) contract.CodeAddr = &common.Address{} program := NewProgram([]byte{}) @@ -134,7 +124,7 @@ func (account) SetBalance(*big.Int) {} func (account) SetNonce(uint64) {} func (account) Balance() *big.Int { return nil } func (account) Address() common.Address { return common.Address{} } -func (account) ReturnGas(*big.Int, *big.Int) {} +func (account) ReturnGas(uint64) {} func (account) SetCode([]byte) {} func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} @@ -149,7 +139,7 @@ func runVmBench(test vmBench, b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0)) + context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000)) context.Code = test.code context.CodeAddr = &common.Address{} _, err := env.Vm().Run(context, test.input) @@ -174,6 +164,7 @@ func NewEnv(config *Config) *Env { func (self *Env) RuleSet() RuleSet { return ruleSet{new(big.Int)} } func (self *Env) Vm() Vm { return self.evm } +func (self *Env) GasPrice() *big.Int { return new(big.Int) } func (self *Env) Origin() common.Address { return common.Address{} } func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) } @@ -197,15 +188,15 @@ func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool { return true } func (self *Env) Transfer(from, to Account, amount *big.Int) {} -func (self *Env) Call(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { +func (self *Env) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { return nil, nil } -func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { +func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { return nil, nil } -func (self *Env) Create(caller ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { +func (self *Env) Create(caller ContractRef, data []byte, gas, price *big.Int) ([]byte, common.Address, error) { return nil, common.Address{}, nil } -func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) { +func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { return nil, nil } diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index e85bca2277..910532a55a 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -27,10 +27,10 @@ type dummyContractRef struct { calledForEach bool } -func (dummyContractRef) ReturnGas(*big.Int, *big.Int) {} -func (dummyContractRef) Address() common.Address { return common.Address{} } -func (dummyContractRef) Value() *big.Int { return new(big.Int) } -func (dummyContractRef) SetCode([]byte) {} +func (dummyContractRef) ReturnGas(uint64) {} +func (dummyContractRef) Address() common.Address { return common.Address{} } +func (dummyContractRef) Value() *big.Int { return new(big.Int) } +func (dummyContractRef) SetCode([]byte) {} func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) { d.calledForEach = true } @@ -61,7 +61,7 @@ func TestStoreCapture(t *testing.T) { logger = NewStructLogger(nil) mem = NewMemory() stack = newstack() - contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int), new(big.Int)) + contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int)) ) stack.push(big.NewInt(1)) stack.push(big.NewInt(0)) @@ -83,7 +83,7 @@ func TestStorageCapture(t *testing.T) { t.Skip("implementing this function is difficult. it requires all sort of interfaces to be implemented which isn't trivial. The value (the actual test) isn't worth it") var ( ref = &dummyContractRef{} - contract = NewContract(ref, ref, new(big.Int), new(big.Int), new(big.Int)) + contract = NewContract(ref, ref, new(big.Int), new(big.Int)) env = newDummyEnv(ref) logger = NewStructLogger(nil) mem = NewMemory() diff --git a/core/vm/memory.go b/core/vm/memory.go index d011884173..ddf3b605eb 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -21,10 +21,11 @@ import "fmt" // Memory implements a simple memory model for the ethereum virtual machine. type Memory struct { store []byte + cost uint64 } func NewMemory() *Memory { - return &Memory{nil} + return &Memory{} } // Set sets offset + size to value diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index a4793c98fc..e39d5a4386 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -38,6 +38,7 @@ type Env struct { time *big.Int difficulty *big.Int gasLimit *big.Int + gasPrice *big.Int getHashFn func(uint64) common.Hash @@ -55,6 +56,7 @@ func NewEnv(cfg *Config, state *state.StateDB) vm.Environment { time: cfg.Time, difficulty: cfg.Difficulty, gasLimit: cfg.GasLimit, + gasPrice: cfg.GasPrice, } env.evm = vm.New(env, vm.Config{ Debug: cfg.Debug, @@ -66,6 +68,7 @@ func NewEnv(cfg *Config, state *state.StateDB) vm.Environment { } func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet } +func (self *Env) GasPrice() *big.Int { return self.gasPrice } func (self *Env) Vm() vm.Vm { return self.evm } func (self *Env) Origin() common.Address { return self.origin } func (self *Env) BlockNumber() *big.Int { return self.number } @@ -97,17 +100,17 @@ func (self *Env) Transfer(from, to vm.Account, amount *big.Int) { core.Transfer(from, to, amount) } -func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - return core.Call(self, caller, addr, data, gas, price, value) +func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { + return core.Call(self, caller, addr, data, gas, value) } -func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - return core.CallCode(self, caller, addr, data, gas, price, value) +func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { + return core.CallCode(self, caller, addr, data, gas, value) } -func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) { - return core.DelegateCall(self, me, addr, data, gas, price) +func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { + return core.DelegateCall(self, me, addr, data, gas) } -func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { - return core.Create(self, caller, data, gas, price, value) +func (self *Env) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) { + return core.Create(self, caller, data, gas, value) } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 309d508c3d..33f325d962 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -112,7 +112,6 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { receiver.Address(), input, cfg.GasLimit, - cfg.GasPrice, cfg.Value, ) @@ -136,7 +135,6 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) { address, input, cfg.GasLimit, - cfg.GasPrice, cfg.Value, ) diff --git a/core/vm/segments.go b/core/vm/segments.go index 648d8a04a1..61d87367c6 100644 --- a/core/vm/segments.go +++ b/core/vm/segments.go @@ -21,7 +21,7 @@ import "math/big" type jumpSeg struct { pos uint64 err error - gas *big.Int + gas uint64 } func (j jumpSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { @@ -39,7 +39,7 @@ func (s jumpSeg) Op() OpCode { return 0 } type pushSeg struct { data []*big.Int - gas *big.Int + gas uint64 } func (s pushSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { diff --git a/core/vm/uint64.go b/core/vm/uint64.go new file mode 100644 index 0000000000..96d1e76364 --- /dev/null +++ b/core/vm/uint64.go @@ -0,0 +1,175 @@ +package vm + +import ( + "fmt" + gmath "math" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" +) + +var ( + maxInt63 = new(big.Int).Exp(big.NewInt(2), big.NewInt(63), big.NewInt(0)) + maxIntCap = new(big.Int).Sub(maxInt63, big.NewInt(1)) +) + +var ( + StackLimit64 = params.StackLimit.Uint64() + GasQuickStep64 uint64 = 2 + GasFastestStep64 uint64 = 3 + GasFastStep64 uint64 = 5 + GasMidStep64 uint64 = 8 + GasSlowStep64 uint64 = 10 + GasExtStep64 uint64 = 20 + + GasReturn64 uint64 = 0 + GasStop64 uint64 = 0 + + GasContractByte64 uint64 = 200 + LogGas64 = params.LogGas.Uint64() + LogTopicGas64 = params.LogTopicGas.Uint64() + LogDataGas64 = params.LogDataGas.Uint64() + ExpByteGas64 = params.ExpByteGas.Uint64() + SstoreSetGas64 = params.SstoreSetGas.Uint64() + SstoreClearGas64 = params.SstoreClearGas.Uint64() + SstoreResetGas64 = params.SstoreResetGas.Uint64() + KeccakWordGas64 = params.Sha3WordGas.Uint64() + CopyGas64 = params.CopyGas.Uint64() + CallNewAccountGas64 = params.CallNewAccountGas.Uint64() + CallValueTransferGas64 = params.CallValueTransferGas.Uint64() + MemoryGas64 = params.MemoryGas.Uint64() + QuadCoeffDiv64 = params.QuadCoeffDiv.Uint64() +) + +// casts a arbitrary number to the amount of words (sets of 32 bytes) +func toWordSize64(size uint64) uint64 { + return (size + 31) / 32 +} + +// calculates the memory size required for a step +func calcMemSize64(off, l *big.Int) (uint64, bool) { + if l.Cmp(common.Big0) == 0 { + return 0, false + } + size := new(big.Int).Add(off, l) + if size.Cmp(maxIntCap) > 0 { + return 0, true + } + return size.Uint64(), false +} + +// calculates the quadratic gas +// TODO this function requires guarding of overflows +func calcQuadMemGas64(mem *Memory, newMemSize uint64) (uint64, bool) { + oldTotalFee := mem.cost + if newMemSize > 0 { + newMemSizeWords := toWordSize64(newMemSize) + newMemSize = newMemSizeWords * 32 + + if newMemSize > uint64(mem.Len()) { + pow := uint64(gmath.Pow(float64(newMemSizeWords), 2)) + linCoef := newMemSizeWords * MemoryGas64 + quadCoef := pow / QuadCoeffDiv64 + newTotalFee := linCoef + quadCoef + + fee := newTotalFee - oldTotalFee + mem.cost = newTotalFee + + return fee, false + } + } + return 0, false +} + +// jitBaseCalc is the same as baseCheck except it doesn't do the look up in the +// gas table. This is done during compilation instead. +func jitBaseCalc(instr instruction, stack *Stack) (uint64, error) { + err := stack.require(instr.spop) + if err != nil { + return 0, err + } + + if instr.spush > 0 && stack.len()-instr.spop+instr.spush > int(StackLimit64) { + return 0, fmt.Errorf("stack limit reached %d (%d)", stack.len(), StackLimit64) + } + + // 0 on gas means no base calculation + if instr.gas == 0 { + return 0, nil + } + + return instr.gas, nil +} + +type req64 struct { + stackPop int + gas uint64 + stackPush int +} + +var _baseCheck64 = map[OpCode]req64{ + // opcode | stack pop | gas price | stack push + ADD: {2, GasFastestStep64, 1}, + LT: {2, GasFastestStep64, 1}, + GT: {2, GasFastestStep64, 1}, + SLT: {2, GasFastestStep64, 1}, + SGT: {2, GasFastestStep64, 1}, + EQ: {2, GasFastestStep64, 1}, + ISZERO: {1, GasFastestStep64, 1}, + SUB: {2, GasFastestStep64, 1}, + AND: {2, GasFastestStep64, 1}, + OR: {2, GasFastestStep64, 1}, + XOR: {2, GasFastestStep64, 1}, + NOT: {1, GasFastestStep64, 1}, + BYTE: {2, GasFastestStep64, 1}, + CALLDATALOAD: {1, GasFastestStep64, 1}, + CALLDATACOPY: {3, GasFastestStep64, 1}, + MLOAD: {1, GasFastestStep64, 1}, + MSTORE: {2, GasFastestStep64, 0}, + MSTORE8: {2, GasFastestStep64, 0}, + CODECOPY: {3, GasFastestStep64, 0}, + MUL: {2, GasFastStep64, 1}, + DIV: {2, GasFastStep64, 1}, + SDIV: {2, GasFastStep64, 1}, + MOD: {2, GasFastStep64, 1}, + SMOD: {2, GasFastStep64, 1}, + SIGNEXTEND: {2, GasFastStep64, 1}, + ADDMOD: {3, GasMidStep64, 1}, + MULMOD: {3, GasMidStep64, 1}, + JUMP: {1, GasMidStep64, 0}, + JUMPI: {2, GasSlowStep64, 0}, + EXP: {2, GasSlowStep64, 1}, + ADDRESS: {0, GasQuickStep64, 1}, + ORIGIN: {0, GasQuickStep64, 1}, + CALLER: {0, GasQuickStep64, 1}, + CALLVALUE: {0, GasQuickStep64, 1}, + CODESIZE: {0, GasQuickStep64, 1}, + GASPRICE: {0, GasQuickStep64, 1}, + COINBASE: {0, GasQuickStep64, 1}, + TIMESTAMP: {0, GasQuickStep64, 1}, + NUMBER: {0, GasQuickStep64, 1}, + CALLDATASIZE: {0, GasQuickStep64, 1}, + DIFFICULTY: {0, GasQuickStep64, 1}, + GASLIMIT: {0, GasQuickStep64, 1}, + POP: {1, GasQuickStep64, 0}, + PC: {0, GasQuickStep64, 1}, + MSIZE: {0, GasQuickStep64, 1}, + GAS: {0, GasQuickStep64, 1}, + BLOCKHASH: {1, GasExtStep64, 1}, + BALANCE: {1, GasExtStep64, 1}, + EXTCODESIZE: {1, GasExtStep64, 1}, + EXTCODECOPY: {4, GasExtStep64, 0}, + SLOAD: {1, params.SloadGas.Uint64(), 1}, + SSTORE: {2, 0, 0}, + SHA3: {2, params.Sha3Gas.Uint64(), 1}, + CREATE: {3, params.CreateGas.Uint64(), 1}, + CALL: {7, params.CallGas.Uint64(), 1}, + CALLCODE: {7, params.CallGas.Uint64(), 1}, + DELEGATECALL: {6, params.CallGas.Uint64(), 1}, + JUMPDEST: {0, params.JumpdestGas.Uint64(), 0}, + SUICIDE: {1, 0, 0}, + RETURN: {2, 0, 0}, + PUSH1: {0, GasFastestStep64, 1}, + DUP1: {0, 0, 1}, +} diff --git a/core/vm/vm.go b/core/vm/vm.go index 9d7b550587..8eb1916615 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -141,7 +141,8 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return. defer func() { if err != nil && evm.cfg.Debug { - evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), err) + gas := new(big.Int).SetUint64(contract.gas64) + evm.cfg.Tracer.CaptureState(evm.env, pc, op, gas, cost, mem, stack, contract, evm.env.Depth(), err) } }() @@ -149,22 +150,11 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { glog.Infof("running byte VM %x\n", codehash[:4]) tstart := time.Now() defer func() { - glog.Infof("byte VM %x done. time: %v instrc: %v\n", codehash[:4], time.Since(tstart), instrCount) + glog.Infof("VM %x done. time: %v instrc: %v\n", codehash[:4], time.Since(tstart), instrCount) }() } for ; ; instrCount++ { - /* - if EnableJit && it%100 == 0 { - if program != nil && progStatus(atomic.LoadInt32(&program.status)) == progReady { - // move execution - fmt.Println("moved", it) - glog.V(logger.Info).Infoln("Moved execution to JIT") - return runProgram(program, pc, mem, stack, evm.env, contract, input) - } - } - */ - // Get the memory location of pc op = contract.GetOp(pc) // calculate the new memory size and gas price for the current executing opcode @@ -175,7 +165,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { // Use the calculated gas. When insufficient gas is present, use all gas and return an // Out Of Gas error - if !contract.UseGas(cost) { + if !contract.UseGas(cost.Uint64()) { return nil, OutOfGasError } @@ -183,7 +173,8 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { mem.Resize(newMemSize.Uint64()) // Add a log message if evm.cfg.Debug { - evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil) + gas := new(big.Int).SetUint64(contract.gas64) + evm.cfg.Tracer.CaptureState(evm.env, pc, op, gas, cost, mem, stack, contract, evm.env.Depth(), nil) } if opPtr := evm.jumpTable[op]; opPtr.valid { @@ -369,7 +360,7 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef // RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go func (evm *EVM) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) { gas := p.Gas(len(input)) - if contract.UseGas(gas) { + if contract.UseGas(gas.Uint64()) { ret = p.Call(input) return ret, nil diff --git a/core/vm_env.go b/core/vm_env.go index e541eaef47..2a86ad9cc4 100644 --- a/core/vm_env.go +++ b/core/vm_env.go @@ -74,6 +74,7 @@ func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase } func (self *VMEnv) Time() *big.Int { return self.header.Time } func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty } func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit } +func (self *VMEnv) GasPrice() *big.Int { return self.msg.GasPrice() } func (self *VMEnv) Value() *big.Int { return self.msg.Value() } func (self *VMEnv) Db() vm.Database { return self.state } func (self *VMEnv) Depth() int { return self.depth } @@ -101,17 +102,17 @@ func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) { Transfer(from, to, amount) } -func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - return Call(self, me, addr, data, gas, price, value) +func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { + return Call(self, me, addr, data, gas, value) } -func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - return CallCode(self, me, addr, data, gas, price, value) +func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { + return CallCode(self, me, addr, data, gas, value) } -func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) { - return DelegateCall(self, me, addr, data, gas, price) +func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { + return DelegateCall(self, me, addr, data, gas) } -func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { - return Create(self, me, data, gas, price, value) +func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) { + return Create(self, me, data, gas, value) } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 36fa30881d..18512e9ae6 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -21,6 +21,7 @@ import ( "encoding/hex" "fmt" "io" + "math" "math/big" "strconv" "testing" @@ -115,7 +116,7 @@ func runStateTests(ruleSet RuleSet, tests map[string]VmTest, skipTests []string) } for name, test := range tests { - if skipTest[name] /*|| name != "callcodecallcode_11" */ { + if skipTest[name] /*|| name != "TestInputLimitsLight"*/ { glog.Infoln("Skipping state test", name) continue } @@ -162,6 +163,10 @@ func runStateTest(ruleSet RuleSet, test VmTest) error { // err error logs vm.Logs ) + if common.Big(test.Transaction["gasLimit"]).Cmp(new(big.Int).SetUint64(math.MaxUint64)) > 0 { + fmt.Println("skipping a test because of unsupported high gas") + return nil + } ret, logs, _, _ = RunState(ruleSet, statedb, env, test.Transaction) diff --git a/tests/util.go b/tests/util.go index 79c3bfad12..85e9500669 100644 --- a/tests/util.go +++ b/tests/util.go @@ -150,12 +150,12 @@ func (r RuleSet) IsHomestead(n *big.Int) bool { } type Env struct { - ruleSet RuleSet - depth int - state *state.StateDB - skipTransfer bool - initial bool - Gas *big.Int + ruleSet RuleSet + depth int + state *state.StateDB + skipTransfer bool + initial bool + Gas, gasPrice *big.Int origin common.Address parent common.Hash @@ -190,6 +190,7 @@ func NewEnvFromMap(ruleSet RuleSet, state *state.StateDB, envValues map[string]s env.difficulty = common.Big(envValues["currentDifficulty"]) env.gasLimit = common.Big(envValues["currentGasLimit"]) env.Gas = new(big.Int) + env.gasPrice = common.Big(exeValues["gasPrice"]) env.evm = vm.New(env, vm.Config{ EnableJit: EnableJit, @@ -208,6 +209,7 @@ func (self *Env) Time() *big.Int { return self.time } func (self *Env) Difficulty() *big.Int { return self.difficulty } func (self *Env) Db() vm.Database { return self.state } func (self *Env) GasLimit() *big.Int { return self.gasLimit } +func (self *Env) GasPrice() *big.Int { return self.gasPrice } func (self *Env) VmType() vm.Type { return vm.StdVmTy } func (self *Env) GetHash(n uint64) common.Hash { return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String()))) @@ -241,46 +243,46 @@ func (self *Env) Transfer(from, to vm.Account, amount *big.Int) { core.Transfer(from, to, amount) } -func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { +func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { if self.vmTest && self.depth > 0 { - caller.ReturnGas(gas, price) + caller.ReturnGas(gas.Uint64()) return nil, nil } - ret, err := core.Call(self, caller, addr, data, gas, price, value) + ret, err := core.Call(self, caller, addr, data, gas, value) self.Gas = gas return ret, err } -func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { +func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { if self.vmTest && self.depth > 0 { - caller.ReturnGas(gas, price) + caller.ReturnGas(gas.Uint64()) return nil, nil } - return core.CallCode(self, caller, addr, data, gas, price, value) + return core.CallCode(self, caller, addr, data, gas, value) } -func (self *Env) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) { +func (self *Env) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { if self.vmTest && self.depth > 0 { - caller.ReturnGas(gas, price) + caller.ReturnGas(gas.Uint64()) return nil, nil } - return core.DelegateCall(self, caller, addr, data, gas, price) + return core.DelegateCall(self, caller, addr, data, gas) } -func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { +func (self *Env) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) { if self.vmTest { - caller.ReturnGas(gas, price) + caller.ReturnGas(gas.Uint64()) nonce := self.state.GetNonce(caller.Address()) obj := self.state.GetOrNewStateObject(crypto.CreateAddress(caller.Address(), nonce)) return nil, obj.Address(), nil } else { - return core.Create(self, caller, data, gas, price, value) + return core.Create(self, caller, data, gas, value) } } diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index 37f0af33c5..289a8f35bf 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -135,9 +135,9 @@ func runVmTests(tests map[string]VmTest, skipTests []string) error { } for name, test := range tests { - if skipTest[name] { + if skipTest[name] /*|| name != "57a9a2fbfe21a848e8d83379562a8513417c73d127495cf4abc5a44a5fe08e92"*/ { glog.Infoln("Skipping VM test", name) - return nil + continue } if err := runVmTest(test); err != nil { @@ -233,7 +233,6 @@ func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, from = common.HexToAddress(exec["caller"]) data = common.FromHex(exec["data"]) gas = common.Big(exec["gas"]) - price = common.Big(exec["gasPrice"]) value = common.Big(exec["value"]) ) // Reset the pre-compiled contracts for VM tests. @@ -245,7 +244,7 @@ func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, vmenv.vmTest = true vmenv.skipTransfer = true vmenv.initial = true - ret, err := vmenv.Call(caller, to, data, gas, price, value) + ret, err := vmenv.Call(caller, to, data, gas, value) return ret, vmenv.state.Logs(), vmenv.Gas, err }