diff options
author | Jeffrey Wilcke <jeffrey@ethereum.org> | 2017-02-08 20:39:26 +0800 |
---|---|---|
committer | Jeffrey Wilcke <jeffrey@ethereum.org> | 2017-02-13 22:15:12 +0800 |
commit | 57f4e9025757254536a738bb4771712038f1e763 (patch) | |
tree | 5d2f140139f3763a7da3c20a88acff96b58ec8ad /core/vm | |
parent | f8f428cc18c5f70814d7b3937128781bac14bffd (diff) | |
download | dexon-57f4e9025757254536a738bb4771712038f1e763.tar dexon-57f4e9025757254536a738bb4771712038f1e763.tar.gz dexon-57f4e9025757254536a738bb4771712038f1e763.tar.bz2 dexon-57f4e9025757254536a738bb4771712038f1e763.tar.lz dexon-57f4e9025757254536a738bb4771712038f1e763.tar.xz dexon-57f4e9025757254536a738bb4771712038f1e763.tar.zst dexon-57f4e9025757254536a738bb4771712038f1e763.zip |
Revert "params: core, core/vm, miner: 64bit gas instructions (#3514)"
This reverts commit 8b57c494908637a5c0e74f8f7a13b3218e026757.
Diffstat (limited to 'core/vm')
-rw-r--r-- | core/vm/common.go | 78 | ||||
-rw-r--r-- | core/vm/contract.go | 33 | ||||
-rw-r--r-- | core/vm/contracts.go | 39 | ||||
-rw-r--r-- | core/vm/environment.go (renamed from core/vm/evm.go) | 83 | ||||
-rw-r--r-- | core/vm/gas.go | 153 | ||||
-rw-r--r-- | core/vm/gas_table.go | 418 | ||||
-rw-r--r-- | core/vm/gas_table_test.go | 24 | ||||
-rw-r--r-- | core/vm/instructions.go | 338 | ||||
-rw-r--r-- | core/vm/int_pool_verifier.go | 15 | ||||
-rw-r--r-- | core/vm/int_pool_verifier_empty.go | 7 | ||||
-rw-r--r-- | core/vm/jump_table.go | 567 | ||||
-rw-r--r-- | core/vm/logger_test.go | 4 | ||||
-rw-r--r-- | core/vm/memory.go | 5 | ||||
-rw-r--r-- | core/vm/opcodes.go | 6 | ||||
-rw-r--r-- | core/vm/runtime/env.go | 2 | ||||
-rw-r--r-- | core/vm/runtime/runtime.go | 14 | ||||
-rw-r--r-- | core/vm/runtime/runtime_test.go | 4 | ||||
-rw-r--r-- | core/vm/stack_table.go | 12 | ||||
-rw-r--r-- | core/vm/virtual_machine.go (renamed from core/vm/intpool.go) | 35 | ||||
-rw-r--r-- | core/vm/vm.go (renamed from core/vm/interpreter.go) | 42 |
20 files changed, 869 insertions, 1010 deletions
diff --git a/core/vm/common.go b/core/vm/common.go index b7b9a6a9c..2878b92d2 100644 --- a/core/vm/common.go +++ b/core/vm/common.go @@ -21,11 +21,28 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" +) + +// Type is the VM type accepted by **NewVm** +type Type byte + +const ( + StdVmTy Type = iota // Default standard VM + JitVmTy // LLVM JIT VM + MaxVmTy ) var ( + Pow256 = common.BigPow(2, 256) // Pow256 is 2**256 + U256 = common.U256 // Shortcut to common.U256 S256 = common.S256 // Shortcut to common.S256 + + Zero = common.Big0 // Shortcut to common.Big0 + One = common.Big1 // Shortcut to common.Big1 + + max = big.NewInt(math.MaxInt64) // Maximum 64 bit integer ) // calculates the memory size required for a step @@ -37,6 +54,48 @@ func calcMemSize(off, l *big.Int) *big.Int { 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) +} + +// Mainly used for print variables and passing to Print* +func toValue(val *big.Int) interface{} { + // Let's assume a string on right padded zero's + b := val.Bytes() + if b[0] != 0 && b[len(b)-1] == 0x0 && b[len(b)-2] == 0x0 { + return string(b) + } + + return val +} + // getData returns a slice from the data based on the start and size and pads // up to size with zero's. This function is overflow safe. func getData(data []byte, start, size *big.Int) []byte { @@ -47,17 +106,14 @@ func getData(data []byte, start, size *big.Int) []byte { return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64())) } -// bigUint64 returns the integer casted to a uint64 and returns whether it -// overflowed in the process. -func bigUint64(v *big.Int) (uint64, bool) { - return v.Uint64(), v.BitLen() > 64 -} - -// toWordSize returns the ceiled word size required for memory expansion. -func toWordSize(size uint64) uint64 { - if size > math.MaxUint64-31 { - return math.MaxUint64/32 + 1 +// useGas attempts to subtract the amount of gas and returns whether it was +// successful +func useGas(gas, amount *big.Int) bool { + if gas.Cmp(amount) < 0 { + return false } - return (size + 31) / 32 + // Sub the amount of gas from the remaining + gas.Sub(gas, amount) + return true } diff --git a/core/vm/contract.go b/core/vm/contract.go index 091106d84..dfa93ab18 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -24,6 +24,7 @@ import ( // ContractRef is a reference to the contract's backing object type ContractRef interface { + ReturnGas(*big.Int) Address() common.Address Value() *big.Int SetCode(common.Hash, []byte) @@ -47,8 +48,7 @@ type Contract struct { CodeAddr *common.Address Input []byte - Gas uint64 - value *big.Int + value, Gas, UsedGas *big.Int Args []byte @@ -56,7 +56,7 @@ type Contract struct { } // NewContract returns a new contract environment for the execution of EVM. -func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *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 { @@ -68,8 +68,9 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uin // 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 + c.Gas = gas //new(big.Int).Set(gas) c.value = new(big.Int).Set(value) + c.UsedGas = new(big.Int) return c } @@ -106,13 +107,27 @@ func (c *Contract) Caller() common.Address { return c.CallerAddress } +// Finalise finalises the contract and returning any remaining gas to the original +// caller. +func (c *Contract) Finalise() { + // Return the remaining gas to the caller + c.caller.ReturnGas(c.Gas) +} + // UseGas attempts the use gas and subtracts it and returns true on success -func (c *Contract) UseGas(gas uint64) (ok bool) { - if c.Gas < gas { - return false +func (c *Contract) UseGas(gas *big.Int) (ok bool) { + ok = useGas(c.Gas, gas) + if ok { + c.UsedGas.Add(c.UsedGas, gas) } - c.Gas -= gas - return true + return +} + +// ReturnGas adds the given gas back to itself. +func (c *Contract) ReturnGas(gas *big.Int) { + // Return the gas to the context + c.Gas.Add(c.Gas, gas) + c.UsedGas.Sub(c.UsedGas, gas) } // Address returns the contracts address diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 593b6ca55..9645d268f 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -17,6 +17,8 @@ package vm import ( + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/logger" @@ -28,8 +30,8 @@ import ( // requires a deterministic gas count based on the input size of the Run method of the // contract. type PrecompiledContract interface { - RequiredGas(inputSize int) uint64 // RequiredPrice calculates the contract gas use - Run(input []byte) []byte // Run runs the precompiled contract + RequiredGas(inputSize int) *big.Int // RequiredPrice calculates the contract gas use + Run(input []byte) []byte // Run runs the precompiled contract } // Precompiled contains the default set of ethereum contracts @@ -55,7 +57,7 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contr // ECRECOVER implemented as a native contract type ecrecover struct{} -func (c *ecrecover) RequiredGas(inputSize int) uint64 { +func (c *ecrecover) RequiredGas(inputSize int) *big.Int { return params.EcrecoverGas } @@ -90,12 +92,10 @@ func (c *ecrecover) Run(in []byte) []byte { // SHA256 implemented as a native contract type sha256 struct{} -// RequiredGas returns the gas required to execute the pre-compiled contract. -// -// This method does not require any overflow checking as the input size gas costs -// required for anything significant is so high it's impossible to pay for. -func (c *sha256) RequiredGas(inputSize int) uint64 { - return uint64(inputSize+31)/32*params.Sha256WordGas + params.Sha256Gas +func (c *sha256) RequiredGas(inputSize int) *big.Int { + n := big.NewInt(int64(inputSize+31) / 32) + n.Mul(n, params.Sha256WordGas) + return n.Add(n, params.Sha256Gas) } func (c *sha256) Run(in []byte) []byte { return crypto.Sha256(in) @@ -104,12 +104,10 @@ func (c *sha256) Run(in []byte) []byte { // RIPMED160 implemented as a native contract type ripemd160 struct{} -// RequiredGas returns the gas required to execute the pre-compiled contract. -// -// This method does not require any overflow checking as the input size gas costs -// required for anything significant is so high it's impossible to pay for. -func (c *ripemd160) RequiredGas(inputSize int) uint64 { - return uint64(inputSize+31)/32*params.Ripemd160WordGas + params.Ripemd160Gas +func (c *ripemd160) RequiredGas(inputSize int) *big.Int { + n := big.NewInt(int64(inputSize+31) / 32) + n.Mul(n, params.Ripemd160WordGas) + return n.Add(n, params.Ripemd160Gas) } func (c *ripemd160) Run(in []byte) []byte { return common.LeftPadBytes(crypto.Ripemd160(in), 32) @@ -118,12 +116,11 @@ func (c *ripemd160) Run(in []byte) []byte { // data copy implemented as a native contract type dataCopy struct{} -// RequiredGas returns the gas required to execute the pre-compiled contract. -// -// This method does not require any overflow checking as the input size gas costs -// required for anything significant is so high it's impossible to pay for. -func (c *dataCopy) RequiredGas(inputSize int) uint64 { - return uint64(inputSize+31)/32*params.IdentityWordGas + params.IdentityGas +func (c *dataCopy) RequiredGas(inputSize int) *big.Int { + n := big.NewInt(int64(inputSize+31) / 32) + n.Mul(n, params.IdentityWordGas) + + return n.Add(n, params.IdentityGas) } func (c *dataCopy) Run(in []byte) []byte { return in diff --git a/core/vm/evm.go b/core/vm/environment.go index 0c5d998c2..c19ef464b 100644 --- a/core/vm/evm.go +++ b/core/vm/environment.go @@ -17,6 +17,7 @@ package vm import ( + "fmt" "math/big" "sync/atomic" @@ -101,18 +102,24 @@ func (evm *EVM) Cancel() { // Call executes the contract associated with the addr with the given input as parameters. It also handles any // necessary value transfer required and takes the necessary steps to create accounts and reverses the state in // case of an execution error or failed value transfer. -func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { +func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, gas, nil + caller.ReturnGas(gas) + + return nil, nil } // Depth check execution. Fail if we're trying to execute above the // limit. - if evm.depth > int(params.CallCreateDepth) { - return nil, gas, ErrDepth + if evm.depth > int(params.CallCreateDepth.Int64()) { + caller.ReturnGas(gas) + + return nil, ErrDepth } if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { - return nil, gas, ErrInsufficientBalance + caller.ReturnGas(gas) + + return nil, ErrInsufficientBalance } var ( @@ -121,7 +128,8 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas ) if !evm.StateDB.Exist(addr) { if PrecompiledContracts[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.BitLen() == 0 { - return nil, gas, nil + caller.ReturnGas(gas) + return nil, nil } to = evm.StateDB.CreateAccount(addr) @@ -135,6 +143,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // only. contract := NewContract(caller, to, value, gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + defer contract.Finalise() ret, err = evm.interpreter.Run(contract, input) // When an error was returned by the EVM or when setting the creation code @@ -145,7 +154,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas evm.StateDB.RevertToSnapshot(snapshot) } - return ret, contract.Gas, err + return ret, err } // CallCode executes the contract associated with the addr with the given input as parameters. It also handles any @@ -153,18 +162,24 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // case of an execution error or failed value transfer. // // CallCode differs from Call in the sense that it executes the given address' code with the caller as context. -func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { +func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, gas, nil + caller.ReturnGas(gas) + + return nil, nil } // Depth check execution. Fail if we're trying to execute above the // limit. - if evm.depth > int(params.CallCreateDepth) { - return nil, gas, ErrDepth + if evm.depth > int(params.CallCreateDepth.Int64()) { + caller.ReturnGas(gas) + + return nil, ErrDepth } if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { - return nil, gas, ErrInsufficientBalance + caller.ReturnGas(gas) + + return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", value, evm.StateDB.GetBalance(caller.Address())) } var ( @@ -176,6 +191,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // only. contract := NewContract(caller, to, value, gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + defer contract.Finalise() ret, err = evm.interpreter.Run(contract, input) if err != nil { @@ -184,7 +200,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, evm.StateDB.RevertToSnapshot(snapshot) } - return ret, contract.Gas, err + return ret, err } // DelegateCall executes the contract associated with the addr with the given input as parameters. @@ -192,15 +208,18 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // // DelegateCall differs from CallCode in the sense that it executes the given address' code with the caller as context // and the caller is set to the caller of the caller. -func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { +func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas *big.Int) (ret []byte, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, gas, nil + caller.ReturnGas(gas) + + return nil, nil } // Depth check execution. Fail if we're trying to execute above the // limit. - if evm.depth > int(params.CallCreateDepth) { - return nil, gas, ErrDepth + if evm.depth > int(params.CallCreateDepth.Int64()) { + caller.ReturnGas(gas) + return nil, ErrDepth } var ( @@ -211,6 +230,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // Iinitialise a new contract and make initialise the delegate values contract := NewContract(caller, to, caller.Value(), gas).AsDelegate() contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + defer contract.Finalise() ret, err = evm.interpreter.Run(contract, input) if err != nil { @@ -219,22 +239,28 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by evm.StateDB.RevertToSnapshot(snapshot) } - return ret, contract.Gas, err + return ret, err } // Create creates a new contract using code as deployment code. -func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { +func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (ret []byte, contractAddr common.Address, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, common.Address{}, gas, nil + caller.ReturnGas(gas) + + return nil, common.Address{}, nil } // Depth check execution. Fail if we're trying to execute above the // limit. - if evm.depth > int(params.CallCreateDepth) { - return nil, common.Address{}, gas, ErrDepth + if evm.depth > int(params.CallCreateDepth.Int64()) { + caller.ReturnGas(gas) + + return nil, common.Address{}, ErrDepth } if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { - return nil, common.Address{}, gas, ErrInsufficientBalance + caller.ReturnGas(gas) + + return nil, common.Address{}, ErrInsufficientBalance } // Create a new account on the state @@ -254,6 +280,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I // only. contract := NewContract(caller, to, value, gas) contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code) + defer contract.Finalise() ret, err = evm.interpreter.Run(contract, nil) @@ -264,8 +291,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I // be stored due to not enough gas set an error and let it be handled // by the error checking condition below. if err == nil && !maxCodeSizeExceeded { - createDataGas := uint64(len(ret)) * params.CreateDataGas - if contract.UseGas(createDataGas) { + dataGas := big.NewInt(int64(len(ret))) + dataGas.Mul(dataGas, params.CreateDataGas) + if contract.UseGas(dataGas) { evm.StateDB.SetCode(contractAddr, ret) } else { err = ErrCodeStoreOutOfGas @@ -277,10 +305,11 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I // when we're in homestead this also counts for code storage gas errors. if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) { + contract.UseGas(contract.Gas) evm.StateDB.RevertToSnapshot(snapshot) // Nothing should be returned when an error is thrown. - return nil, contractAddr, 0, err + return nil, contractAddr, err } // If the vm returned with an error the return value should be set to nil. // This isn't consensus critical but merely to for behaviour reasons such as @@ -289,7 +318,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I ret = nil } - return ret, contractAddr, contract.Gas, err + return ret, contractAddr, err } // ChainConfig returns the evmironment's chain configuration diff --git a/core/vm/gas.go b/core/vm/gas.go index dd64d5f17..cb225b6ca 100644 --- a/core/vm/gas.go +++ b/core/vm/gas.go @@ -17,42 +17,149 @@ package vm import ( + "fmt" "math/big" "github.com/ethereum/go-ethereum/params" ) -const ( - GasQuickStep uint64 = 2 - GasFastestStep uint64 = 3 - GasFastStep uint64 = 5 - GasMidStep uint64 = 8 - GasSlowStep uint64 = 10 - GasExtStep uint64 = 20 - - GasReturn uint64 = 0 - GasStop uint64 = 0 - GasContractByte uint64 = 200 +var ( + GasQuickStep = big.NewInt(2) + GasFastestStep = big.NewInt(3) + GasFastStep = big.NewInt(5) + GasMidStep = big.NewInt(8) + GasSlowStep = big.NewInt(10) + GasExtStep = big.NewInt(20) + + GasReturn = big.NewInt(0) + GasStop = big.NewInt(0) + + GasContractByte = big.NewInt(200) + + n64 = big.NewInt(64) ) // calcGas returns the actual gas cost of the call. // // The cost of gas was changed during the homestead price change HF. To allow for EIP150 // to be implemented. The returned gas is gas - base * 63 / 64. -func callGas(gasTable params.GasTable, availableGas, base uint64, callCost *big.Int) (uint64, error) { - if gasTable.CreateBySuicide > 0 { - availableGas = availableGas - base - gas := availableGas - availableGas/64 - // If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150 - // is smaller than the requested amount. Therefor we return the new gas instead - // of returning an error. - if callCost.BitLen() > 64 || gas < callCost.Uint64() { - return gas, nil +func callGas(gasTable params.GasTable, availableGas, base, callCost *big.Int) *big.Int { + if gasTable.CreateBySuicide != nil { + availableGas = new(big.Int).Sub(availableGas, base) + g := new(big.Int).Div(availableGas, n64) + g.Sub(availableGas, g) + + if g.Cmp(callCost) < 0 { + return g } } - if callCost.BitLen() > 64 { - return 0, errGasUintOverflow + return callCost +} + +// baseCheck checks for any stack error underflows +func baseCheck(op OpCode, stack *Stack, gas *big.Int) error { + // PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit + // PUSH is also allowed to calculate the same price for all PUSHes + // DUP requirements are handled elsewhere (except for the stack limit check) + if op >= PUSH1 && op <= PUSH32 { + op = PUSH1 + } + if op >= DUP1 && op <= DUP16 { + op = DUP1 + } + + if r, ok := _baseCheck[op]; ok { + err := stack.require(r.stackPop) + if err != nil { + return err + } + + if r.stackPush > 0 && stack.len()-r.stackPop+r.stackPush > int(params.StackLimit.Int64()) { + return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64()) + } + + gas.Add(gas, r.gas) } + return nil +} + +// casts a arbitrary number to the amount of words (sets of 32 bytes) +func toWordSize(size *big.Int) *big.Int { + tmp := new(big.Int) + tmp.Add(size, u256(31)) + tmp.Div(tmp, u256(32)) + return tmp +} + +type req struct { + stackPop int + gas *big.Int + stackPush int +} - return callCost.Uint64(), nil +var _baseCheck = map[OpCode]req{ + // opcode | stack pop | gas price | stack push + ADD: {2, GasFastestStep, 1}, + LT: {2, GasFastestStep, 1}, + GT: {2, GasFastestStep, 1}, + SLT: {2, GasFastestStep, 1}, + SGT: {2, GasFastestStep, 1}, + EQ: {2, GasFastestStep, 1}, + ISZERO: {1, GasFastestStep, 1}, + SUB: {2, GasFastestStep, 1}, + AND: {2, GasFastestStep, 1}, + OR: {2, GasFastestStep, 1}, + XOR: {2, GasFastestStep, 1}, + NOT: {1, GasFastestStep, 1}, + BYTE: {2, GasFastestStep, 1}, + CALLDATALOAD: {1, GasFastestStep, 1}, + CALLDATACOPY: {3, GasFastestStep, 1}, + MLOAD: {1, GasFastestStep, 1}, + MSTORE: {2, GasFastestStep, 0}, + MSTORE8: {2, GasFastestStep, 0}, + CODECOPY: {3, GasFastestStep, 0}, + MUL: {2, GasFastStep, 1}, + DIV: {2, GasFastStep, 1}, + SDIV: {2, GasFastStep, 1}, + MOD: {2, GasFastStep, 1}, + SMOD: {2, GasFastStep, 1}, + SIGNEXTEND: {2, GasFastStep, 1}, + ADDMOD: {3, GasMidStep, 1}, + MULMOD: {3, GasMidStep, 1}, + JUMP: {1, GasMidStep, 0}, + JUMPI: {2, GasSlowStep, 0}, + EXP: {2, GasSlowStep, 1}, + ADDRESS: {0, GasQuickStep, 1}, + ORIGIN: {0, GasQuickStep, 1}, + CALLER: {0, GasQuickStep, 1}, + CALLVALUE: {0, GasQuickStep, 1}, + CODESIZE: {0, GasQuickStep, 1}, + GASPRICE: {0, GasQuickStep, 1}, + COINBASE: {0, GasQuickStep, 1}, + TIMESTAMP: {0, GasQuickStep, 1}, + NUMBER: {0, GasQuickStep, 1}, + CALLDATASIZE: {0, GasQuickStep, 1}, + DIFFICULTY: {0, GasQuickStep, 1}, + GASLIMIT: {0, GasQuickStep, 1}, + POP: {1, GasQuickStep, 0}, + PC: {0, GasQuickStep, 1}, + MSIZE: {0, GasQuickStep, 1}, + GAS: {0, GasQuickStep, 1}, + BLOCKHASH: {1, GasExtStep, 1}, + BALANCE: {1, Zero, 1}, + EXTCODESIZE: {1, Zero, 1}, + EXTCODECOPY: {4, Zero, 0}, + SLOAD: {1, params.SloadGas, 1}, + SSTORE: {2, Zero, 0}, + SHA3: {2, params.Sha3Gas, 1}, + CREATE: {3, params.CreateGas, 1}, + // Zero is calculated in the gasSwitch + CALL: {7, Zero, 1}, + CALLCODE: {7, Zero, 1}, + DELEGATECALL: {6, Zero, 1}, + SELFDESTRUCT: {1, Zero, 0}, + JUMPDEST: {0, params.JumpdestGas, 0}, + RETURN: {2, Zero, 0}, + PUSH1: {0, GasFastestStep, 1}, + DUP1: {0, Zero, 1}, } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index fba1eb066..4d2c4e94a 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -1,80 +1,56 @@ package vm import ( - gmath "math" "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/params" ) -// memoryGasCosts calculates the quadratic gas for memory expansion. It does so -// only for the memory region that is expanded, not the total memory. -func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { - // The maximum that will fit in a uint64 is max_word_count - 1 - // anything above that will result in an overflow. - if newMemSize > gmath.MaxUint64-32 { - return 0, errGasUintOverflow - } - - if newMemSize == 0 { - return 0, nil - } - - newMemSizeWords := toWordSize(newMemSize) - newMemSize = newMemSizeWords * 32 - - if newMemSize > uint64(mem.Len()) { - square := newMemSizeWords * newMemSizeWords - linCoef := newMemSizeWords * params.MemoryGas - quadCoef := square / params.QuadCoeffDiv - newTotalFee := linCoef + quadCoef - - fee := newTotalFee - mem.lastGasCost - mem.lastGasCost = newTotalFee - - return fee, nil +func memoryGasCost(mem *Memory, newMemSize *big.Int) *big.Int { + gas := new(big.Int) + if newMemSize.Cmp(common.Big0) > 0 { + newMemSizeWords := toWordSize(newMemSize) + + 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) + } } - return 0, nil + return gas } -func constGasFunc(gas uint64) gasFunc { - return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return gas, nil +func constGasFunc(gas *big.Int) gasFunc { + return func(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + return gas } } -func gasCalldataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, err - } - - var overflow bool - if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { - return 0, errGasUintOverflow - } +func gasCalldataCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + gas := memoryGasCost(mem, memorySize) + gas.Add(gas, GasFastestStep) + words := toWordSize(stack.Back(2)) - words, overflow := bigUint64(stack.Back(2)) - if overflow { - return 0, errGasUintOverflow - } - - if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow { - return 0, errGasUintOverflow - } - - if gas, overflow = math.SafeAdd(gas, words); overflow { - return 0, errGasUintOverflow - } - return gas, nil + return gas.Add(gas, words.Mul(words, params.CopyGas)) } -func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { +func gasSStore(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { var ( y, x = stack.Back(1), stack.Back(0) - val = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) + val = env.StateDB.GetState(contract.Address(), common.BigToHash(x)) ) // This checks for 3 scenario's and calculates gas accordingly // 1. From a zero-value address to a non-zero value (NEW VALUE) @@ -82,335 +58,189 @@ func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, m // 3. From a non-zero to a non-zero (CHANGE) if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) { // 0 => non 0 - return params.SstoreSetGas, nil + return new(big.Int).Set(params.SstoreSetGas) } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) { - evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SstoreRefundGas)) + env.StateDB.AddRefund(params.SstoreRefundGas) - return params.SstoreClearGas, nil + return new(big.Int).Set(params.SstoreClearGas) } else { // non 0 => non 0 (or 0 => 0) - return params.SstoreResetGas, nil + return new(big.Int).Set(params.SstoreResetGas) } } -func makeGasLog(n uint64) gasFunc { - return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - requestedSize, overflow := bigUint64(stack.Back(1)) - if overflow { - return 0, errGasUintOverflow - } +func makeGasLog(n uint) gasFunc { + return func(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + mSize := stack.Back(1) - gas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, err - } - - if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow { - return 0, errGasUintOverflow - } - if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow { - return 0, errGasUintOverflow - } - - var memorySizeGas uint64 - if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow { - return 0, errGasUintOverflow - } - if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow { - return 0, errGasUintOverflow - } - return gas, nil + gas := new(big.Int).Add(memoryGasCost(mem, memorySize), params.LogGas) + gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(n)), params.LogTopicGas)) + gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas)) + return gas } } -func gasSha3(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - var overflow bool - gas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, err - } - - if gas, overflow = math.SafeAdd(gas, params.Sha3Gas); overflow { - return 0, errGasUintOverflow - } - - wordGas, overflow := bigUint64(stack.Back(1)) - if overflow { - return 0, errGasUintOverflow - } - if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow { - return 0, errGasUintOverflow - } - if gas, overflow = math.SafeAdd(gas, wordGas); overflow { - return 0, errGasUintOverflow - } - return gas, nil +func gasSha3(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + gas := memoryGasCost(mem, memorySize) + gas.Add(gas, params.Sha3Gas) + words := toWordSize(stack.Back(1)) + return gas.Add(gas, words.Mul(words, params.Sha3WordGas)) } -func gasCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, err - } - - var overflow bool - if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { - return 0, errGasUintOverflow - } +func gasCodeCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + gas := memoryGasCost(mem, memorySize) + gas.Add(gas, GasFastestStep) + words := toWordSize(stack.Back(2)) - wordGas, overflow := bigUint64(stack.Back(2)) - if overflow { - return 0, errGasUintOverflow - } - if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.CopyGas); overflow { - return 0, errGasUintOverflow - } - if gas, overflow = math.SafeAdd(gas, wordGas); overflow { - return 0, errGasUintOverflow - } - return gas, nil + return gas.Add(gas, words.Mul(words, params.CopyGas)) } -func gasExtCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, err - } - - var overflow bool - if gas, overflow = math.SafeAdd(gas, gt.ExtcodeCopy); overflow { - return 0, errGasUintOverflow - } - - wordGas, overflow := bigUint64(stack.Back(3)) - if overflow { - return 0, errGasUintOverflow - } - - if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.CopyGas); overflow { - return 0, errGasUintOverflow - } +func gasExtCodeCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + gas := memoryGasCost(mem, memorySize) + gas.Add(gas, gt.ExtcodeCopy) + words := toWordSize(stack.Back(3)) - if gas, overflow = math.SafeAdd(gas, wordGas); overflow { - return 0, errGasUintOverflow - } - return gas, nil + return gas.Add(gas, words.Mul(words, params.CopyGas)) } -func gasMLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - var overflow bool - gas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, errGasUintOverflow - } - if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { - return 0, errGasUintOverflow - } - return gas, nil +func gasMLoad(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize)) } -func gasMStore8(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - var overflow bool - gas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, errGasUintOverflow - } - if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { - return 0, errGasUintOverflow - } - return gas, nil +func gasMStore8(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize)) } -func gasMStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - var overflow bool - gas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, errGasUintOverflow - } - if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { - return 0, errGasUintOverflow - } - return gas, nil +func gasMStore(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize)) } -func gasCreate(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - var overflow bool - gas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, err - } - if gas, overflow = math.SafeAdd(gas, params.CreateGas); overflow { - return 0, errGasUintOverflow - } - return gas, nil +func gasCreate(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + return new(big.Int).Add(params.CreateGas, memoryGasCost(mem, memorySize)) } -func gasBalance(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return gt.Balance, nil +func gasBalance(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + return gt.Balance } -func gasExtCodeSize(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return gt.ExtcodeSize, nil +func gasExtCodeSize(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + return gt.ExtcodeSize } -func gasSLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return gt.SLoad, nil +func gasSLoad(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + return gt.SLoad } -func gasExp(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8) - - var ( - gas = expByteLen * gt.ExpByte // no overflow check required. Max is 256 * ExpByte gas - overflow bool - ) - if gas, overflow = math.SafeAdd(gas, GasSlowStep); overflow { - return 0, errGasUintOverflow - } - return gas, nil +func gasExp(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + expByteLen := int64((stack.data[stack.len()-2].BitLen() + 7) / 8) + gas := big.NewInt(expByteLen) + gas.Mul(gas, gt.ExpByte) + return gas.Add(gas, GasSlowStep) } -func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { +func gasCall(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + gas := new(big.Int).Set(gt.Calls) + + transfersValue := stack.Back(2).BitLen() > 0 var ( - gas = gt.Calls - transfersValue = stack.Back(2).BitLen() > 0 - address = common.BigToAddress(stack.Back(1)) - eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber) + address = common.BigToAddress(stack.Back(1)) + eip158 = env.ChainConfig().IsEIP158(env.BlockNumber) ) if eip158 { - if evm.StateDB.Empty(address) && transfersValue { - gas += params.CallNewAccountGas + if env.StateDB.Empty(address) && transfersValue { + gas.Add(gas, params.CallNewAccountGas) } - } else if !evm.StateDB.Exist(address) { - gas += params.CallNewAccountGas + } else if !env.StateDB.Exist(address) { + gas.Add(gas, params.CallNewAccountGas) } if transfersValue { - gas += params.CallValueTransferGas - } - memoryGas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, err - } - var overflow bool - if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { - return 0, errGasUintOverflow + gas.Add(gas, params.CallValueTransferGas) } + gas.Add(gas, memoryGasCost(mem, memorySize)) - cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) - if err != nil { - return 0, err - } + cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1]) // Replace the stack item with the new gas calculation. This means that // either the original item is left on the stack or the item is replaced by: // (availableGas - gas) * 63 / 64 // We replace the stack item so that it's available when the opCall instruction is // called. This information is otherwise lost due to the dependency on *current* // available gas. - stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) + stack.data[stack.len()-1] = cg - if gas, overflow = math.SafeAdd(gas, cg); overflow { - return 0, errGasUintOverflow - } - return gas, nil + return gas.Add(gas, cg) } -func gasCallCode(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas := gt.Calls +func gasCallCode(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + gas := new(big.Int).Set(gt.Calls) if stack.Back(2).BitLen() > 0 { - gas += params.CallValueTransferGas - } - memoryGas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, err - } - var overflow bool - if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { - return 0, errGasUintOverflow + gas.Add(gas, params.CallValueTransferGas) } + gas.Add(gas, memoryGasCost(mem, memorySize)) - cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) - if err != nil { - return 0, err - } + cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1]) // Replace the stack item with the new gas calculation. This means that // either the original item is left on the stack or the item is replaced by: // (availableGas - gas) * 63 / 64 // We replace the stack item so that it's available when the opCall instruction is // called. This information is otherwise lost due to the dependency on *current* // available gas. - stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) + stack.data[stack.len()-1] = cg - if gas, overflow = math.SafeAdd(gas, cg); overflow { - return 0, errGasUintOverflow - } - return gas, nil + return gas.Add(gas, cg) } -func gasReturn(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { +func gasReturn(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { return memoryGasCost(mem, memorySize) } -func gasSuicide(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - var gas uint64 +func gasSuicide(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + gas := new(big.Int) // EIP150 homestead gas reprice fork: - if evm.ChainConfig().IsEIP150(evm.BlockNumber) { - gas = gt.Suicide + if env.ChainConfig().IsEIP150(env.BlockNumber) { + gas.Set(gt.Suicide) var ( address = common.BigToAddress(stack.Back(0)) - eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber) + eip158 = env.ChainConfig().IsEIP158(env.BlockNumber) ) if eip158 { // if empty and transfers value - if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).BitLen() > 0 { - gas += gt.CreateBySuicide + if env.StateDB.Empty(address) && env.StateDB.GetBalance(contract.Address()).BitLen() > 0 { + gas.Add(gas, gt.CreateBySuicide) } - } else if !evm.StateDB.Exist(address) { - gas += gt.CreateBySuicide + } else if !env.StateDB.Exist(address) { + gas.Add(gas, gt.CreateBySuicide) } } - if !evm.StateDB.HasSuicided(contract.Address()) { - evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SuicideRefundGas)) + if !env.StateDB.HasSuicided(contract.Address()) { + env.StateDB.AddRefund(params.SuicideRefundGas) } - return gas, nil + return gas } -func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, err - } - var overflow bool - if gas, overflow = math.SafeAdd(gas, gt.Calls); overflow { - return 0, errGasUintOverflow - } +func gasDelegateCall(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + gas := new(big.Int).Add(gt.Calls, memoryGasCost(mem, memorySize)) - cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) - if err != nil { - return 0, err - } + cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1]) // Replace the stack item with the new gas calculation. This means that // either the original item is left on the stack or the item is replaced by: // (availableGas - gas) * 63 / 64 // We replace the stack item so that it's available when the opCall instruction is // called. - stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) + stack.data[stack.len()-1] = cg - if gas, overflow = math.SafeAdd(gas, cg); overflow { - return 0, errGasUintOverflow - } - return gas, nil + return gas.Add(gas, cg) } -func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return GasFastestStep, nil +func gasPush(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + return GasFastestStep } -func gasSwap(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return GasFastestStep, nil +func gasSwap(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + return GasFastestStep } -func gasDup(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return GasFastestStep, nil +func gasDup(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int { + return GasFastestStep } diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go deleted file mode 100644 index cceb89285..000000000 --- a/core/vm/gas_table_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package vm - -import ( - "math" - "testing" -) - -func TestMemoryGasCost(t *testing.T) { - size := uint64(math.MaxUint64 - 64) - _, err := memoryGasCost(&Memory{}, size) - if err != nil { - t.Error("didn't expect error:", err) - } - - _, err = memoryGasCost(&Memory{}, size+32) - if err != nil { - t.Error("didn't expect error:", err) - } - - _, err = memoryGasCost(&Memory{}, size+33) - if err == nil { - t.Error("expected error") - } -} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 39e5c0587..3b1b06cca 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -27,56 +27,42 @@ import ( "github.com/ethereum/go-ethereum/params" ) -var bigZero = new(big.Int) - -func opAdd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opAdd(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Add(x, y))) - - evm.interpreter.intPool.put(y) - return nil, nil } -func opSub(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSub(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Sub(x, y))) - - evm.interpreter.intPool.put(y) - return nil, nil } -func opMul(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMul(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Mul(x, y))) - - evm.interpreter.intPool.put(y) - return nil, nil } -func opDiv(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opDiv(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() if y.Cmp(common.Big0) != 0 { stack.push(U256(x.Div(x, y))) } else { stack.push(new(big.Int)) } - - evm.interpreter.intPool.put(y) - return nil, nil } -func opSdiv(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSdiv(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := S256(stack.pop()), S256(stack.pop()) if y.Cmp(common.Big0) == 0 { stack.push(new(big.Int)) return nil, nil } else { n := new(big.Int) - if evm.interpreter.intPool.get().Mul(x, y).Cmp(common.Big0) < 0 { + if new(big.Int).Mul(x, y).Cmp(common.Big0) < 0 { n.SetInt64(-1) } else { n.SetInt64(1) @@ -87,22 +73,20 @@ func opSdiv(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta stack.push(U256(res)) } - evm.interpreter.intPool.put(y) return nil, nil } -func opMod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() if y.Cmp(common.Big0) == 0 { stack.push(new(big.Int)) } else { stack.push(U256(x.Mod(x, y))) } - evm.interpreter.intPool.put(y) return nil, nil } -func opSmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := S256(stack.pop()), S256(stack.pop()) if y.Cmp(common.Big0) == 0 { @@ -120,20 +104,16 @@ func opSmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta stack.push(U256(res)) } - evm.interpreter.intPool.put(y) return nil, nil } -func opExp(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opExp(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { base, exponent := stack.pop(), stack.pop() stack.push(math.Exp(base, exponent)) - - evm.interpreter.intPool.put(base, exponent) - return nil, nil } -func opSignExtend(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSignExtend(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { back := stack.pop() if back.Cmp(big.NewInt(31)) < 0 { bit := uint(back.Uint64()*8 + 7) @@ -148,231 +128,198 @@ func opSignExtend(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stac stack.push(U256(num)) } - - evm.interpreter.intPool.put(back) return nil, nil } -func opNot(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opNot(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x := stack.pop() stack.push(U256(x.Not(x))) return nil, nil } -func opLt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opLt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() if x.Cmp(y) < 0 { - stack.push(evm.interpreter.intPool.get().SetUint64(1)) + stack.push(big.NewInt(1)) } else { stack.push(new(big.Int)) } - - evm.interpreter.intPool.put(x, y) return nil, nil } -func opGt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opGt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() if x.Cmp(y) > 0 { - stack.push(evm.interpreter.intPool.get().SetUint64(1)) + stack.push(big.NewInt(1)) } else { stack.push(new(big.Int)) } - - evm.interpreter.intPool.put(x, y) return nil, nil } -func opSlt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSlt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := S256(stack.pop()), S256(stack.pop()) if x.Cmp(S256(y)) < 0 { - stack.push(evm.interpreter.intPool.get().SetUint64(1)) + stack.push(big.NewInt(1)) } else { stack.push(new(big.Int)) } - - evm.interpreter.intPool.put(x, y) return nil, nil } -func opSgt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSgt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := S256(stack.pop()), S256(stack.pop()) if x.Cmp(y) > 0 { - stack.push(evm.interpreter.intPool.get().SetUint64(1)) + stack.push(big.NewInt(1)) } else { stack.push(new(big.Int)) } - - evm.interpreter.intPool.put(x, y) return nil, nil } -func opEq(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opEq(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() if x.Cmp(y) == 0 { - stack.push(evm.interpreter.intPool.get().SetUint64(1)) + stack.push(big.NewInt(1)) } else { stack.push(new(big.Int)) } - - evm.interpreter.intPool.put(x, y) return nil, nil } -func opIszero(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opIszero(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x := stack.pop() if x.Cmp(common.Big0) > 0 { stack.push(new(big.Int)) } else { - stack.push(evm.interpreter.intPool.get().SetUint64(1)) + stack.push(big.NewInt(1)) } - - evm.interpreter.intPool.put(x) return nil, nil } -func opAnd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opAnd(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() stack.push(x.And(x, y)) - - evm.interpreter.intPool.put(y) return nil, nil } -func opOr(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opOr(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() stack.push(x.Or(x, y)) - - evm.interpreter.intPool.put(y) return nil, nil } -func opXor(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opXor(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() stack.push(x.Xor(x, y)) - - evm.interpreter.intPool.put(y) return nil, nil } -func opByte(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opByte(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { th, val := stack.pop(), stack.pop() if th.Cmp(big.NewInt(32)) < 0 { - byte := evm.interpreter.intPool.get().SetInt64(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) + byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) stack.push(byte) } else { stack.push(new(big.Int)) } - - evm.interpreter.intPool.put(th, val) return nil, nil } -func opAddmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opAddmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y, z := stack.pop(), stack.pop(), stack.pop() - if z.Cmp(bigZero) > 0 { + if z.Cmp(Zero) > 0 { add := x.Add(x, y) add.Mod(add, z) stack.push(U256(add)) } else { stack.push(new(big.Int)) } - - evm.interpreter.intPool.put(y, z) return nil, nil } -func opMulmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMulmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y, z := stack.pop(), stack.pop(), stack.pop() - if z.Cmp(bigZero) > 0 { + if z.Cmp(Zero) > 0 { mul := x.Mul(x, y) mul.Mod(mul, z) stack.push(U256(mul)) } else { stack.push(new(big.Int)) } - - evm.interpreter.intPool.put(y, z) return nil, nil } -func opSha3(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSha3(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { offset, size := stack.pop(), stack.pop() data := memory.Get(offset.Int64(), size.Int64()) hash := crypto.Keccak256(data) - if evm.vmConfig.EnablePreimageRecording { - evm.StateDB.AddPreimage(common.BytesToHash(hash), data) + if env.vmConfig.EnablePreimageRecording { + env.StateDB.AddPreimage(common.BytesToHash(hash), data) } stack.push(common.BytesToBig(hash)) - - evm.interpreter.intPool.put(offset, size) return nil, nil } -func opAddress(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opAddress(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { stack.push(common.Bytes2Big(contract.Address().Bytes())) return nil, nil } -func opBalance(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opBalance(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { addr := common.BigToAddress(stack.pop()) - balance := evm.StateDB.GetBalance(addr) + balance := env.StateDB.GetBalance(addr) stack.push(new(big.Int).Set(balance)) return nil, nil } -func opOrigin(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.Origin.Big()) +func opOrigin(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(env.Origin.Big()) return nil, nil } -func opCaller(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opCaller(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { stack.push(contract.Caller().Big()) return nil, nil } -func opCallValue(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.interpreter.intPool.get().Set(contract.value)) +func opCallValue(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(new(big.Int).Set(contract.value)) return nil, nil } -func opCalldataLoad(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opCalldataLoad(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { stack.push(common.Bytes2Big(getData(contract.Input, stack.pop(), common.Big32))) return nil, nil } -func opCalldataSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.interpreter.intPool.get().SetInt64(int64(len(contract.Input)))) +func opCalldataSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(big.NewInt(int64(len(contract.Input)))) return nil, nil } -func opCalldataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opCalldataCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( mOff = stack.pop() cOff = stack.pop() l = stack.pop() ) memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l)) - - evm.interpreter.intPool.put(mOff, cOff, l) return nil, nil } -func opExtCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - a := stack.pop() - - addr := common.BigToAddress(a) - a.SetInt64(int64(evm.StateDB.GetCodeSize(addr))) - stack.push(a) - +func opExtCodeSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + addr := common.BigToAddress(stack.pop()) + l := big.NewInt(int64(env.StateDB.GetCodeSize(addr))) + stack.push(l) return nil, nil } -func opCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - l := evm.interpreter.intPool.get().SetInt64(int64(len(contract.Code))) +func opCodeSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + l := big.NewInt(int64(len(contract.Code))) stack.push(l) return nil, nil } -func opCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opCodeCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( mOff = stack.pop() cOff = stack.pop() @@ -381,129 +328,113 @@ func opCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack codeCopy := getData(contract.Code, cOff, l) memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) - - evm.interpreter.intPool.put(mOff, cOff, l) return nil, nil } -func opExtCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opExtCodeCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( addr = common.BigToAddress(stack.pop()) mOff = stack.pop() cOff = stack.pop() l = stack.pop() ) - codeCopy := getData(evm.StateDB.GetCode(addr), cOff, l) + codeCopy := getData(env.StateDB.GetCode(addr), cOff, l) memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) - - evm.interpreter.intPool.put(mOff, cOff, l) - return nil, nil } -func opGasprice(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.interpreter.intPool.get().Set(evm.GasPrice)) +func opGasprice(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(new(big.Int).Set(env.GasPrice)) return nil, nil } -func opBlockhash(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opBlockhash(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { num := stack.pop() - n := evm.interpreter.intPool.get().Sub(evm.BlockNumber, common.Big257) - if num.Cmp(n) > 0 && num.Cmp(evm.BlockNumber) < 0 { - stack.push(evm.GetHash(num.Uint64()).Big()) + n := new(big.Int).Sub(env.BlockNumber, common.Big257) + if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber) < 0 { + stack.push(env.GetHash(num.Uint64()).Big()) } else { stack.push(new(big.Int)) } - - evm.interpreter.intPool.put(num, n) return nil, nil } -func opCoinbase(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.Coinbase.Big()) +func opCoinbase(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(env.Coinbase.Big()) return nil, nil } -func opTimestamp(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(U256(new(big.Int).Set(evm.Time))) +func opTimestamp(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(U256(new(big.Int).Set(env.Time))) return nil, nil } -func opNumber(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(U256(new(big.Int).Set(evm.BlockNumber))) +func opNumber(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(U256(new(big.Int).Set(env.BlockNumber))) return nil, nil } -func opDifficulty(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(U256(new(big.Int).Set(evm.Difficulty))) +func opDifficulty(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(U256(new(big.Int).Set(env.Difficulty))) return nil, nil } -func opGasLimit(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(U256(new(big.Int).Set(evm.GasLimit))) +func opGasLimit(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(U256(new(big.Int).Set(env.GasLimit))) return nil, nil } -func opPop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - evm.interpreter.intPool.put(stack.pop()) +func opPop(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.pop() return nil, nil } -func opMload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMload(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { offset := stack.pop() val := common.BigD(memory.Get(offset.Int64(), 32)) stack.push(val) - - evm.interpreter.intPool.put(offset) return nil, nil } -func opMstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMstore(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // pop value of the stack mStart, val := stack.pop(), stack.pop() memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256)) - - evm.interpreter.intPool.put(mStart, val) return nil, nil } -func opMstore8(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMstore8(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { off, val := stack.pop().Int64(), stack.pop().Int64() memory.store[off] = byte(val & 0xff) - return nil, nil } -func opSload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSload(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { loc := common.BigToHash(stack.pop()) - val := evm.StateDB.GetState(contract.Address(), loc).Big() + val := env.StateDB.GetState(contract.Address(), loc).Big() stack.push(val) return nil, nil } -func opSstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSstore(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { loc := common.BigToHash(stack.pop()) val := stack.pop() - evm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val)) - - evm.interpreter.intPool.put(val) + env.StateDB.SetState(contract.Address(), loc, common.BigToHash(val)) return nil, nil } -func opJump(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opJump(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { pos := stack.pop() if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) { nop := contract.GetOp(pos.Uint64()) return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos) } *pc = pos.Uint64() - - evm.interpreter.intPool.put(pos) return nil, nil } -func opJumpi(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opJumpi(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { pos, cond := stack.pop(), stack.pop() if cond.Cmp(common.BigTrue) >= 0 { if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) { @@ -514,62 +445,57 @@ func opJumpi(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *St } else { *pc++ } - - evm.interpreter.intPool.put(pos, cond) return nil, nil } -func opJumpdest(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opJumpdest(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { return nil, nil } -func opPc(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.interpreter.intPool.get().SetUint64(*pc)) +func opPc(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(new(big.Int).SetUint64(*pc)) return nil, nil } -func opMsize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.interpreter.intPool.get().SetInt64(int64(memory.Len()))) +func opMsize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(big.NewInt(int64(memory.Len()))) return nil, nil } -func opGas(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.interpreter.intPool.get().SetUint64(contract.Gas)) +func opGas(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(new(big.Int).Set(contract.Gas)) return nil, nil } -func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opCreate(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( value = stack.pop() offset, size = stack.pop(), stack.pop() input = memory.Get(offset.Int64(), size.Int64()) - gas = contract.Gas + gas = new(big.Int).Set(contract.Gas) ) - if evm.ChainConfig().IsEIP150(evm.BlockNumber) { - gas -= gas / 64 + if env.ChainConfig().IsEIP150(env.BlockNumber) { + gas.Div(gas, n64) + gas = gas.Sub(contract.Gas, gas) } contract.UseGas(gas) - _, addr, returnGas, suberr := evm.Create(contract, input, gas, value) + _, 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 // ignore this error and pretend the operation was successful. - if evm.ChainConfig().IsHomestead(evm.BlockNumber) && suberr == ErrCodeStoreOutOfGas { + if env.ChainConfig().IsHomestead(env.BlockNumber) && suberr == ErrCodeStoreOutOfGas { stack.push(new(big.Int)) } else if suberr != nil && suberr != ErrCodeStoreOutOfGas { stack.push(new(big.Int)) } else { stack.push(addr.Big()) } - contract.Gas += returnGas - - evm.interpreter.intPool.put(value, offset, size) - return nil, nil } -func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - gas := stack.pop().Uint64() +func opCall(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + gas := stack.pop() // pop gas and value of the stack. addr, value := stack.pop(), stack.pop() value = U256(value) @@ -583,26 +509,25 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta // Get the arguments from the memory args := memory.Get(inOffset.Int64(), inSize.Int64()) - if value.BitLen() > 0 { - gas += params.CallStipend + if len(value.Bytes()) > 0 { + gas.Add(gas, params.CallStipend) } - ret, returnGas, err := evm.Call(contract, address, args, gas, value) + ret, err := env.Call(contract, address, args, gas, value) + if err != nil { stack.push(new(big.Int)) + } else { stack.push(big.NewInt(1)) memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - contract.Gas += returnGas - - evm.interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) return nil, nil } -func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - gas := stack.pop().Uint64() +func opCallCode(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + gas := stack.pop() // pop gas and value of the stack. addr, value := stack.pop(), stack.pop() value = U256(value) @@ -616,11 +541,12 @@ func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack // Get the arguments from the memory args := memory.Get(inOffset.Int64(), inSize.Int64()) - if value.BitLen() > 0 { - gas += params.CallStipend + if len(value.Bytes()) > 0 { + gas.Add(gas, params.CallStipend) } - ret, returnGas, err := evm.CallCode(contract, address, args, gas, value) + ret, err := env.CallCode(contract, address, args, gas, value) + if err != nil { stack.push(new(big.Int)) @@ -629,54 +555,46 @@ func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - contract.Gas += returnGas - - evm.interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) return nil, nil } -func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opDelegateCall(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // if not homestead return an error. DELEGATECALL is not supported // during pre-homestead. - if !evm.ChainConfig().IsHomestead(evm.BlockNumber) { + if !env.ChainConfig().IsHomestead(env.BlockNumber) { return nil, fmt.Errorf("invalid opcode %x", DELEGATECALL) } - gas, to, inOffset, inSize, outOffset, outSize := stack.pop().Uint64(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.BigToAddress(to) args := memory.Get(inOffset.Int64(), inSize.Int64()) - - ret, returnGas, err := evm.DelegateCall(contract, toAddr, args, gas) + ret, err := env.DelegateCall(contract, toAddr, args, gas) if err != nil { stack.push(new(big.Int)) } else { stack.push(big.NewInt(1)) memory.Set(outOffset.Uint64(), outSize.Uint64(), ret) } - contract.Gas += returnGas - - evm.interpreter.intPool.put(to, inOffset, inSize, outOffset, outSize) return nil, nil } -func opReturn(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opReturn(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { offset, size := stack.pop(), stack.pop() ret := memory.GetPtr(offset.Int64(), size.Int64()) - evm.interpreter.intPool.put(offset, size) return ret, nil } -func opStop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opStop(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { return nil, nil } -func opSuicide(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - balance := evm.StateDB.GetBalance(contract.Address()) - evm.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance) +func opSuicide(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + balance := env.StateDB.GetBalance(contract.Address()) + env.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance) - evm.StateDB.Suicide(contract.Address()) + env.StateDB.Suicide(contract.Address()) return nil, nil } @@ -685,7 +603,7 @@ func opSuicide(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack * // make log instruction function func makeLog(size int) executionFunc { - return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { topics := make([]common.Hash, size) mStart, mSize := stack.pop(), stack.pop() for i := 0; i < size; i++ { @@ -693,24 +611,22 @@ func makeLog(size int) executionFunc { } d := memory.Get(mStart.Int64(), mSize.Int64()) - evm.StateDB.AddLog(&types.Log{ + env.StateDB.AddLog(&types.Log{ Address: contract.Address(), Topics: topics, Data: d, // This is a non-consensus field, but assigned here because // core/state doesn't know the current block number. - BlockNumber: evm.BlockNumber.Uint64(), + BlockNumber: env.BlockNumber.Uint64(), }) - - evm.interpreter.intPool.put(mStart, mSize) return nil, nil } } // make push instruction function func makePush(size uint64, bsize *big.Int) executionFunc { - return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - byts := getData(contract.Code, evm.interpreter.intPool.get().SetUint64(*pc+1), bsize) + return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + byts := getData(contract.Code, new(big.Int).SetUint64(*pc+1), bsize) stack.push(common.Bytes2Big(byts)) *pc += size return nil, nil @@ -719,7 +635,7 @@ func makePush(size uint64, bsize *big.Int) executionFunc { // make push instruction function func makeDup(size int64) executionFunc { - return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { stack.dup(int(size)) return nil, nil } @@ -729,7 +645,7 @@ func makeDup(size int64) executionFunc { func makeSwap(size int64) executionFunc { // switch n + 1 otherwise n would be swapped with n size += 1 - return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { stack.swap(int(size)) return nil, nil } diff --git a/core/vm/int_pool_verifier.go b/core/vm/int_pool_verifier.go deleted file mode 100644 index 61c83ba7e..000000000 --- a/core/vm/int_pool_verifier.go +++ /dev/null @@ -1,15 +0,0 @@ -// +build VERIFY_EVM_INTEGER_POOL - -package vm - -import "fmt" - -const verifyPool = true - -func verifyIntegerPool(ip *intPool) { - for i, item := range ip.pool.data { - if item.Cmp(checkVal) != 0 { - panic(fmt.Sprintf("%d'th item failed aggressive pool check. Value was modified", i)) - } - } -} diff --git a/core/vm/int_pool_verifier_empty.go b/core/vm/int_pool_verifier_empty.go deleted file mode 100644 index 982f8c6dd..000000000 --- a/core/vm/int_pool_verifier_empty.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build !VERIFY_EVM_INTEGER_POOL - -package vm - -const verifyPool = false - -func verifyIntegerPool(ip *intPool) {} diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 80e12c10b..073798274 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -17,7 +17,6 @@ package vm import ( - "errors" "math/big" "github.com/ethereum/go-ethereum/params" @@ -25,13 +24,11 @@ import ( type ( executionFunc func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) - gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64 + gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, *big.Int) *big.Int stackValidationFunc func(*Stack) error memorySizeFunc func(*Stack) *big.Int ) -var errGasUintOverflow = errors.New("gas uint64 overflow") - type operation struct { // op is the operation function execute executionFunc @@ -55,142 +52,149 @@ var defaultJumpTable = NewJumpTable() func NewJumpTable() [256]operation { return [256]operation{ - ADD: { - execute: opAdd, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), + STOP: { + execute: opStop, + gasCost: constGasFunc(new(big.Int)), + validateStack: makeStackFunc(0, 0), + halts: true, valid: true, }, - SUB: { - execute: opSub, + ADD: { + execute: opAdd, gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), valid: true, }, MUL: { execute: opMul, gasCost: constGasFunc(GasFastStep), - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), + valid: true, + }, + SUB: { + execute: opSub, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(2, -1), valid: true, }, DIV: { execute: opDiv, gasCost: constGasFunc(GasFastStep), - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), valid: true, }, SDIV: { execute: opSdiv, gasCost: constGasFunc(GasFastStep), - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), valid: true, }, MOD: { execute: opMod, gasCost: constGasFunc(GasFastStep), - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), valid: true, }, SMOD: { execute: opSmod, gasCost: constGasFunc(GasFastStep), - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), + valid: true, + }, + ADDMOD: { + execute: opAddmod, + gasCost: constGasFunc(GasMidStep), + validateStack: makeStackFunc(3, -2), + valid: true, + }, + MULMOD: { + execute: opMulmod, + gasCost: constGasFunc(GasMidStep), + validateStack: makeStackFunc(3, -2), valid: true, }, EXP: { execute: opExp, gasCost: gasExp, - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), valid: true, }, SIGNEXTEND: { execute: opSignExtend, gasCost: constGasFunc(GasFastStep), - validateStack: makeStackFunc(2, 1), - valid: true, - }, - NOT: { - execute: opNot, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(1, 1), + validateStack: makeStackFunc(2, -1), valid: true, }, LT: { execute: opLt, gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), valid: true, }, GT: { execute: opGt, gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), valid: true, }, SLT: { execute: opSlt, gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), valid: true, }, SGT: { execute: opSgt, gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), valid: true, }, EQ: { execute: opEq, gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), valid: true, }, ISZERO: { execute: opIszero, gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(1, 1), + validateStack: makeStackFunc(1, 0), valid: true, }, AND: { execute: opAnd, gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), + valid: true, + }, + XOR: { + execute: opXor, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(2, -1), valid: true, }, OR: { execute: opOr, gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), valid: true, }, - XOR: { - execute: opXor, + NOT: { + execute: opNot, gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(1, 0), valid: true, }, BYTE: { execute: opByte, gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, - }, - ADDMOD: { - execute: opAddmod, - gasCost: constGasFunc(GasMidStep), - validateStack: makeStackFunc(3, 1), - valid: true, - }, - MULMOD: { - execute: opMulmod, - gasCost: constGasFunc(GasMidStep), - validateStack: makeStackFunc(3, 1), + validateStack: makeStackFunc(2, -1), valid: true, }, SHA3: { execute: opSha3, gasCost: gasSha3, - validateStack: makeStackFunc(2, 1), + validateStack: makeStackFunc(2, -1), memorySize: memorySha3, valid: true, }, @@ -203,7 +207,7 @@ func NewJumpTable() [256]operation { BALANCE: { execute: opBalance, gasCost: gasBalance, - validateStack: makeStackFunc(0, 1), + validateStack: makeStackFunc(1, 0), valid: true, }, ORIGIN: { @@ -227,7 +231,7 @@ func NewJumpTable() [256]operation { CALLDATALOAD: { execute: opCalldataLoad, gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(1, 1), + validateStack: makeStackFunc(1, 0), valid: true, }, CALLDATASIZE: { @@ -239,7 +243,7 @@ func NewJumpTable() [256]operation { CALLDATACOPY: { execute: opCalldataCopy, gasCost: gasCalldataCopy, - validateStack: makeStackFunc(3, 1), + validateStack: makeStackFunc(3, -3), memorySize: memoryCalldataCopy, valid: true, }, @@ -249,36 +253,36 @@ func NewJumpTable() [256]operation { validateStack: makeStackFunc(0, 1), valid: true, }, - EXTCODESIZE: { - execute: opExtCodeSize, - gasCost: gasExtCodeSize, - validateStack: makeStackFunc(1, 1), - valid: true, - }, CODECOPY: { execute: opCodeCopy, gasCost: gasCodeCopy, - validateStack: makeStackFunc(3, 0), + validateStack: makeStackFunc(3, -3), memorySize: memoryCodeCopy, valid: true, }, - EXTCODECOPY: { - execute: opExtCodeCopy, - gasCost: gasExtCodeCopy, - validateStack: makeStackFunc(4, 0), - memorySize: memoryExtCodeCopy, - valid: true, - }, GASPRICE: { execute: opGasprice, gasCost: constGasFunc(GasQuickStep), validateStack: makeStackFunc(0, 1), valid: true, }, + EXTCODESIZE: { + execute: opExtCodeSize, + gasCost: gasExtCodeSize, + validateStack: makeStackFunc(1, 0), + valid: true, + }, + EXTCODECOPY: { + execute: opExtCodeCopy, + gasCost: gasExtCodeCopy, + validateStack: makeStackFunc(4, -4), + memorySize: memoryExtCodeCopy, + valid: true, + }, BLOCKHASH: { execute: opBlockhash, gasCost: constGasFunc(GasExtStep), - validateStack: makeStackFunc(1, 1), + validateStack: makeStackFunc(1, 0), valid: true, }, COINBASE: { @@ -314,20 +318,20 @@ func NewJumpTable() [256]operation { POP: { execute: opPop, gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(1, 0), + validateStack: makeStackFunc(1, -1), valid: true, }, MLOAD: { execute: opMload, gasCost: gasMLoad, - validateStack: makeStackFunc(1, 1), + validateStack: makeStackFunc(1, 0), memorySize: memoryMLoad, valid: true, }, MSTORE: { execute: opMstore, gasCost: gasMStore, - validateStack: makeStackFunc(2, 0), + validateStack: makeStackFunc(2, -2), memorySize: memoryMStore, valid: true, }, @@ -335,26 +339,34 @@ func NewJumpTable() [256]operation { execute: opMstore8, gasCost: gasMStore8, memorySize: memoryMStore8, - validateStack: makeStackFunc(2, 0), + validateStack: makeStackFunc(2, -2), valid: true, }, SLOAD: { execute: opSload, gasCost: gasSLoad, - validateStack: makeStackFunc(1, 1), + validateStack: makeStackFunc(1, 0), valid: true, }, SSTORE: { execute: opSstore, gasCost: gasSStore, - validateStack: makeStackFunc(2, 0), + validateStack: makeStackFunc(2, -2), valid: true, }, - JUMPDEST: { - execute: opJumpdest, - gasCost: constGasFunc(params.JumpdestGas), - validateStack: makeStackFunc(0, 0), + JUMP: { + execute: opJump, + gasCost: constGasFunc(GasMidStep), + validateStack: makeStackFunc(1, -1), + jumps: true, + valid: true, + }, + JUMPI: { + execute: opJumpi, + gasCost: constGasFunc(GasSlowStep), + validateStack: makeStackFunc(2, -2), + jumps: true, valid: true, }, PC: { @@ -375,199 +387,10 @@ func NewJumpTable() [256]operation { validateStack: makeStackFunc(0, 1), valid: true, }, - CREATE: { - execute: opCreate, - gasCost: gasCreate, - validateStack: makeStackFunc(3, 1), - memorySize: memoryCreate, - valid: true, - }, - CALL: { - execute: opCall, - gasCost: gasCall, - validateStack: makeStackFunc(7, 1), - memorySize: memoryCall, - valid: true, - }, - CALLCODE: { - execute: opCallCode, - gasCost: gasCallCode, - validateStack: makeStackFunc(7, 1), - memorySize: memoryCall, - valid: true, - }, - DELEGATECALL: { - execute: opDelegateCall, - gasCost: gasDelegateCall, - validateStack: makeStackFunc(6, 1), - memorySize: memoryDelegateCall, - valid: true, - }, - RETURN: { - execute: opReturn, - gasCost: gasReturn, - validateStack: makeStackFunc(2, 0), - memorySize: memoryReturn, - halts: true, - valid: true, - }, - SUICIDE: { - execute: opSuicide, - gasCost: gasSuicide, - validateStack: makeStackFunc(1, 0), - halts: true, - valid: true, - }, - JUMP: { - execute: opJump, - gasCost: constGasFunc(GasMidStep), - validateStack: makeStackFunc(1, 0), - jumps: true, - valid: true, - }, - JUMPI: { - execute: opJumpi, - gasCost: constGasFunc(GasSlowStep), - validateStack: makeStackFunc(2, 0), - jumps: true, - valid: true, - }, - STOP: { - execute: opStop, - gasCost: constGasFunc(0), + JUMPDEST: { + execute: opJumpdest, + gasCost: constGasFunc(params.JumpdestGas), validateStack: makeStackFunc(0, 0), - halts: true, - valid: true, - }, - LOG0: { - execute: makeLog(0), - gasCost: makeGasLog(0), - validateStack: makeStackFunc(2, 0), - memorySize: memoryLog, - valid: true, - }, - LOG1: { - execute: makeLog(1), - gasCost: makeGasLog(1), - validateStack: makeStackFunc(3, 0), - memorySize: memoryLog, - valid: true, - }, - LOG2: { - execute: makeLog(2), - gasCost: makeGasLog(2), - validateStack: makeStackFunc(4, 0), - memorySize: memoryLog, - valid: true, - }, - LOG3: { - execute: makeLog(3), - gasCost: makeGasLog(3), - validateStack: makeStackFunc(5, 0), - memorySize: memoryLog, - valid: true, - }, - LOG4: { - execute: makeLog(4), - gasCost: makeGasLog(4), - validateStack: makeStackFunc(6, 0), - memorySize: memoryLog, - valid: true, - }, - SWAP1: { - execute: makeSwap(1), - gasCost: gasSwap, - validateStack: makeStackFunc(2, 0), - valid: true, - }, - SWAP2: { - execute: makeSwap(2), - gasCost: gasSwap, - validateStack: makeStackFunc(3, 0), - valid: true, - }, - SWAP3: { - execute: makeSwap(3), - gasCost: gasSwap, - validateStack: makeStackFunc(4, 0), - valid: true, - }, - SWAP4: { - execute: makeSwap(4), - gasCost: gasSwap, - validateStack: makeStackFunc(5, 0), - valid: true, - }, - SWAP5: { - execute: makeSwap(5), - gasCost: gasSwap, - validateStack: makeStackFunc(6, 0), - valid: true, - }, - SWAP6: { - execute: makeSwap(6), - gasCost: gasSwap, - validateStack: makeStackFunc(7, 0), - valid: true, - }, - SWAP7: { - execute: makeSwap(7), - gasCost: gasSwap, - validateStack: makeStackFunc(8, 0), - valid: true, - }, - SWAP8: { - execute: makeSwap(8), - gasCost: gasSwap, - validateStack: makeStackFunc(9, 0), - valid: true, - }, - SWAP9: { - execute: makeSwap(9), - gasCost: gasSwap, - validateStack: makeStackFunc(10, 0), - valid: true, - }, - SWAP10: { - execute: makeSwap(10), - gasCost: gasSwap, - validateStack: makeStackFunc(11, 0), - valid: true, - }, - SWAP11: { - execute: makeSwap(11), - gasCost: gasSwap, - validateStack: makeStackFunc(12, 0), - valid: true, - }, - SWAP12: { - execute: makeSwap(12), - gasCost: gasSwap, - validateStack: makeStackFunc(13, 0), - valid: true, - }, - SWAP13: { - execute: makeSwap(13), - gasCost: gasSwap, - validateStack: makeStackFunc(14, 0), - valid: true, - }, - SWAP14: { - execute: makeSwap(14), - gasCost: gasSwap, - validateStack: makeStackFunc(15, 0), - valid: true, - }, - SWAP15: { - execute: makeSwap(15), - gasCost: gasSwap, - validateStack: makeStackFunc(16, 0), - valid: true, - }, - SWAP16: { - execute: makeSwap(16), - gasCost: gasSwap, - validateStack: makeStackFunc(17, 0), valid: true, }, PUSH1: { @@ -765,97 +588,271 @@ func NewJumpTable() [256]operation { DUP1: { execute: makeDup(1), gasCost: gasDup, - validateStack: makeStackFunc(1, 1), + validateStack: makeDupStackFunc(1), valid: true, }, DUP2: { execute: makeDup(2), gasCost: gasDup, - validateStack: makeStackFunc(2, 1), + validateStack: makeDupStackFunc(2), valid: true, }, DUP3: { execute: makeDup(3), gasCost: gasDup, - validateStack: makeStackFunc(3, 1), + validateStack: makeDupStackFunc(3), valid: true, }, DUP4: { execute: makeDup(4), gasCost: gasDup, - validateStack: makeStackFunc(4, 1), + validateStack: makeDupStackFunc(4), valid: true, }, DUP5: { execute: makeDup(5), gasCost: gasDup, - validateStack: makeStackFunc(5, 1), + validateStack: makeDupStackFunc(5), valid: true, }, DUP6: { execute: makeDup(6), gasCost: gasDup, - validateStack: makeStackFunc(6, 1), + validateStack: makeDupStackFunc(6), valid: true, }, DUP7: { execute: makeDup(7), gasCost: gasDup, - validateStack: makeStackFunc(7, 1), + validateStack: makeDupStackFunc(7), valid: true, }, DUP8: { execute: makeDup(8), gasCost: gasDup, - validateStack: makeStackFunc(8, 1), + validateStack: makeDupStackFunc(8), valid: true, }, DUP9: { execute: makeDup(9), gasCost: gasDup, - validateStack: makeStackFunc(9, 1), + validateStack: makeDupStackFunc(9), valid: true, }, DUP10: { execute: makeDup(10), gasCost: gasDup, - validateStack: makeStackFunc(10, 1), + validateStack: makeDupStackFunc(10), valid: true, }, DUP11: { execute: makeDup(11), gasCost: gasDup, - validateStack: makeStackFunc(11, 1), + validateStack: makeDupStackFunc(11), valid: true, }, DUP12: { execute: makeDup(12), gasCost: gasDup, - validateStack: makeStackFunc(12, 1), + validateStack: makeDupStackFunc(12), valid: true, }, DUP13: { execute: makeDup(13), gasCost: gasDup, - validateStack: makeStackFunc(13, 1), + validateStack: makeDupStackFunc(13), valid: true, }, DUP14: { execute: makeDup(14), gasCost: gasDup, - validateStack: makeStackFunc(14, 1), + validateStack: makeDupStackFunc(14), valid: true, }, DUP15: { execute: makeDup(15), gasCost: gasDup, - validateStack: makeStackFunc(15, 1), + validateStack: makeDupStackFunc(15), valid: true, }, DUP16: { execute: makeDup(16), gasCost: gasDup, - validateStack: makeStackFunc(16, 1), + validateStack: makeDupStackFunc(16), + valid: true, + }, + SWAP1: { + execute: makeSwap(1), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(2), + valid: true, + }, + SWAP2: { + execute: makeSwap(2), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(3), + valid: true, + }, + SWAP3: { + execute: makeSwap(3), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(4), + valid: true, + }, + SWAP4: { + execute: makeSwap(4), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(5), + valid: true, + }, + SWAP5: { + execute: makeSwap(5), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(6), + valid: true, + }, + SWAP6: { + execute: makeSwap(6), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(7), + valid: true, + }, + SWAP7: { + execute: makeSwap(7), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(8), + valid: true, + }, + SWAP8: { + execute: makeSwap(8), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(9), + valid: true, + }, + SWAP9: { + execute: makeSwap(9), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(10), + valid: true, + }, + SWAP10: { + execute: makeSwap(10), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(11), + valid: true, + }, + SWAP11: { + execute: makeSwap(11), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(12), + valid: true, + }, + SWAP12: { + execute: makeSwap(12), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(13), + valid: true, + }, + SWAP13: { + execute: makeSwap(13), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(14), + valid: true, + }, + SWAP14: { + execute: makeSwap(14), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(15), + valid: true, + }, + SWAP15: { + execute: makeSwap(15), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(16), + valid: true, + }, + SWAP16: { + execute: makeSwap(16), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(17), + valid: true, + }, + LOG0: { + execute: makeLog(0), + gasCost: makeGasLog(0), + validateStack: makeStackFunc(2, -2), + memorySize: memoryLog, + valid: true, + }, + LOG1: { + execute: makeLog(1), + gasCost: makeGasLog(1), + validateStack: makeStackFunc(3, -3), + memorySize: memoryLog, + valid: true, + }, + LOG2: { + execute: makeLog(2), + gasCost: makeGasLog(2), + validateStack: makeStackFunc(4, -4), + memorySize: memoryLog, + valid: true, + }, + LOG3: { + execute: makeLog(3), + gasCost: makeGasLog(3), + validateStack: makeStackFunc(5, -5), + memorySize: memoryLog, + valid: true, + }, + LOG4: { + execute: makeLog(4), + gasCost: makeGasLog(4), + validateStack: makeStackFunc(6, -6), + memorySize: memoryLog, + valid: true, + }, + CREATE: { + execute: opCreate, + gasCost: gasCreate, + validateStack: makeStackFunc(3, -2), + memorySize: memoryCreate, + valid: true, + }, + CALL: { + execute: opCall, + gasCost: gasCall, + validateStack: makeStackFunc(7, -6), + memorySize: memoryCall, + valid: true, + }, + CALLCODE: { + execute: opCallCode, + gasCost: gasCallCode, + validateStack: makeStackFunc(7, -6), + memorySize: memoryCall, + valid: true, + }, + RETURN: { + execute: opReturn, + gasCost: gasReturn, + validateStack: makeStackFunc(2, -2), + memorySize: memoryReturn, + halts: true, + valid: true, + }, + DELEGATECALL: { + execute: opDelegateCall, + gasCost: gasDelegateCall, + validateStack: makeStackFunc(6, -5), + memorySize: memoryDelegateCall, + valid: true, + }, + SELFDESTRUCT: { + execute: opSuicide, + gasCost: gasSuicide, + validateStack: makeStackFunc(1, -1), + halts: true, valid: true, }, } diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index ca60cba43..1d0bd96fa 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -56,7 +56,7 @@ func TestStoreCapture(t *testing.T) { logger = NewStructLogger(nil) mem = NewMemory() stack = newstack() - contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) + contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int)) ) stack.push(big.NewInt(1)) stack.push(big.NewInt(0)) @@ -78,7 +78,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), 0) + contract = NewContract(ref, ref, new(big.Int), new(big.Int)) env = NewEVM(Context{}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false}) logger = NewStructLogger(nil) mem = NewMemory() diff --git a/core/vm/memory.go b/core/vm/memory.go index 99a84d227..d01188417 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -20,12 +20,11 @@ import "fmt" // Memory implements a simple memory model for the ethereum virtual machine. type Memory struct { - store []byte - lastGasCost uint64 + store []byte } func NewMemory() *Memory { - return &Memory{} + return &Memory{nil} } // Set sets offset + size to value diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 9d2b037a5..d4ba7f156 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -202,7 +202,7 @@ const ( RETURN DELEGATECALL - SUICIDE = 0xff + SELFDESTRUCT = 0xff ) // Since the opcodes aren't all in order we can't use a regular slice @@ -355,7 +355,7 @@ var opCodeToString = map[OpCode]string{ RETURN: "RETURN", CALLCODE: "CALLCODE", DELEGATECALL: "DELEGATECALL", - SUICIDE: "SUICIDE", + SELFDESTRUCT: "SELFDESTRUCT", PUSH: "PUSH", DUP: "DUP", @@ -501,7 +501,7 @@ var stringToOp = map[string]OpCode{ "CALL": CALL, "RETURN": RETURN, "CALLCODE": CALLCODE, - "SUICIDE": SUICIDE, + "SELFDESTRUCT": SELFDESTRUCT, } func StringToOp(str string) OpCode { diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 9aa88e669..a25c6d71c 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -36,7 +36,7 @@ func NewEnv(cfg *Config, state *state.StateDB) *vm.EVM { BlockNumber: cfg.BlockNumber, Time: cfg.Time, Difficulty: cfg.Difficulty, - GasLimit: new(big.Int).SetUint64(cfg.GasLimit), + GasLimit: cfg.GasLimit, GasPrice: new(big.Int), } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index cf46603db..b5adb982c 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -17,7 +17,6 @@ package runtime import ( - "math" "math/big" "time" @@ -38,7 +37,7 @@ type Config struct { Coinbase common.Address BlockNumber *big.Int Time *big.Int - GasLimit uint64 + GasLimit *big.Int GasPrice *big.Int Value *big.Int DisableJit bool // "disable" so it's enabled by default @@ -69,8 +68,8 @@ func setDefaults(cfg *Config) { if cfg.Time == nil { cfg.Time = big.NewInt(time.Now().Unix()) } - if cfg.GasLimit == 0 { - cfg.GasLimit = math.MaxUint64 + if cfg.GasLimit == nil { + cfg.GasLimit = new(big.Int).Set(common.MaxBig) } if cfg.GasPrice == nil { cfg.GasPrice = new(big.Int) @@ -113,7 +112,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { receiver.SetCode(crypto.Keccak256Hash(code), code) // Call the code with the given configuration. - ret, _, err := vmenv.Call( + ret, err := vmenv.Call( sender, receiver.Address(), input, @@ -141,13 +140,12 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, error) { ) // Call the code with the given configuration. - code, address, _, err := vmenv.Create( + return vmenv.Create( sender, input, cfg.GasLimit, cfg.Value, ) - return code, address, err } // Call executes the code given by the contract's address. It will return the @@ -162,7 +160,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) { sender := cfg.State.GetOrNewStateObject(cfg.Origin) // Call the code with the given configuration. - ret, _, err := vmenv.Call( + ret, err := vmenv.Call( sender, address, input, diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 8ad74a89a..1e618b688 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -39,8 +39,8 @@ func TestDefaults(t *testing.T) { if cfg.Time == nil { t.Error("expected time to be non nil") } - if cfg.GasLimit == 0 { - t.Error("didn't expect gaslimit to be zero") + if cfg.GasLimit == nil { + t.Error("expected time to be non nil") } if cfg.GasPrice == nil { t.Error("expected time to be non nil") diff --git a/core/vm/stack_table.go b/core/vm/stack_table.go index 0936ef06f..eed8805f2 100644 --- a/core/vm/stack_table.go +++ b/core/vm/stack_table.go @@ -6,15 +6,23 @@ import ( "github.com/ethereum/go-ethereum/params" ) -func makeStackFunc(pop, push int) stackValidationFunc { +func makeStackFunc(pop, diff int) stackValidationFunc { return func(stack *Stack) error { if err := stack.require(pop); err != nil { return err } - if push > 0 && stack.len()-pop+push > int(params.StackLimit) { + if int64(stack.len()+diff) > params.StackLimit.Int64() { return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit) } return nil } } + +func makeDupStackFunc(n int) stackValidationFunc { + return makeStackFunc(n, 1) +} + +func makeSwapStackFunc(n int) stackValidationFunc { + return makeStackFunc(n, 0) +} diff --git a/core/vm/intpool.go b/core/vm/virtual_machine.go index 4f1228e14..629108884 100644 --- a/core/vm/intpool.go +++ b/core/vm/virtual_machine.go @@ -1,4 +1,4 @@ -// Copyright 2017 The go-ethereum Authors +// Copyright 2014 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -16,34 +16,7 @@ package vm -import "math/big" - -var checkVal = big.NewInt(-42) - -// intPool is a pool of big integers that -// can be reused for all big.Int operations. -type intPool struct { - pool *Stack -} - -func newIntPool() *intPool { - return &intPool{pool: newstack()} -} - -func (p *intPool) get() *big.Int { - if p.pool.len() > 0 { - return p.pool.pop() - } - return new(big.Int) -} -func (p *intPool) put(is ...*big.Int) { - for _, i := range is { - // verifyPool is a build flag. Pool verification makes sure the integrity - // of the integer pool by comparing values to a default value. - if verifyPool { - i.Set(checkVal) - } - - p.pool.push(i) - } +// VirtualMachine is an EVM interface +type VirtualMachine interface { + Run(*Contract, []byte) ([]byte, error) } diff --git a/core/vm/interpreter.go b/core/vm/vm.go index ad41e3602..05886a863 100644 --- a/core/vm/interpreter.go +++ b/core/vm/vm.go @@ -23,7 +23,6 @@ import ( "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" @@ -61,7 +60,6 @@ type Interpreter struct { env *EVM cfg Config gasTable params.GasTable - intPool *intPool } // NewInterpreter returns a new instance of the Interpreter. @@ -77,7 +75,6 @@ func NewInterpreter(env *EVM, cfg Config) *Interpreter { env: env, cfg: cfg, gasTable: env.ChainConfig().GasTable(env.BlockNumber), - intPool: newIntPool(), } } @@ -109,18 +106,14 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e // 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 - cost uint64 + cost *big.Int ) contract.Input = input // 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 { - // XXX For debugging - //fmt.Printf("%04d: %8v cost = %-8d stack = %-8d ERR = %v\n", pc, op, cost, stack.len(), err) - // TODO update the tracer - g, c := new(big.Int).SetUint64(contract.Gas), new(big.Int).SetUint64(cost) - evm.cfg.Tracer.CaptureState(evm.env, pc, op, g, c, mem, stack, contract, evm.env.depth, err) + evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err) } }() @@ -133,7 +126,7 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e } // The Interpreter main run loop (contextual). This loop runs until either an - // explicit STOP, RETURN or SUICIDE is executed, an error occurred during + // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during // the execution of one of the operations or until the evm.done is set by // the parent context.Context. for atomic.LoadInt32(&evm.env.abort) == 0 { @@ -154,47 +147,34 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e return nil, err } - var memorySize uint64 + var memorySize *big.Int // calculate the new memory size and expand the memory to fit // the operation if operation.memorySize != nil { - memSize, overflow := bigUint64(operation.memorySize(stack)) - if overflow { - return nil, errGasUintOverflow - } + memorySize = operation.memorySize(stack) // memory is expanded in words of 32 bytes. Gas // is also calculated in words. - if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { - return nil, errGasUintOverflow - } + memorySize.Mul(toWordSize(memorySize), big.NewInt(32)) } if !evm.cfg.DisableGasMetering { // consume the gas and return an error if not enough gas is available. // cost is explicitly set so that the capture state defer method cas get the proper cost - cost, err = operation.gasCost(evm.gasTable, evm.env, contract, stack, mem, memorySize) - if err != nil || !contract.UseGas(cost) { + cost = operation.gasCost(evm.gasTable, evm.env, contract, stack, mem, memorySize) + if !contract.UseGas(cost) { return nil, ErrOutOfGas } } - if memorySize > 0 { - mem.Resize(memorySize) + if memorySize != nil { + mem.Resize(memorySize.Uint64()) } if evm.cfg.Debug { - g, c := new(big.Int).SetUint64(contract.Gas), new(big.Int).SetUint64(cost) - evm.cfg.Tracer.CaptureState(evm.env, pc, op, g, c, mem, stack, contract, evm.env.depth, err) + evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err) } - // XXX For debugging - //fmt.Printf("%04d: %8v cost = %-8d stack = %-8d\n", pc, op, cost, stack.len()) // execute the operation res, err := operation.execute(&pc, evm.env, contract, mem, stack) - // verifyPool is a build flag. Pool verification makes sure the integrity - // of the integer pool by comparing values to a default value. - if verifyPool { - verifyIntegerPool(evm.intPool) - } switch { case err != nil: return nil, err |