aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorPéter Szilágyi <peterke@gmail.com>2017-02-15 00:59:00 +0800
committerGitHub <noreply@github.com>2017-02-15 00:59:00 +0800
commita973d1d5233bbd36cbaed0b7235311d4cd529580 (patch)
tree61ed1bd31a9c2a2e6a052a122a737deaee70c6cb /core
parent15a609d5d6613e37e819975ceba01cb5ba735242 (diff)
parentc12f4df910e2da1cc5dd28c5c4bbe2d8721e1057 (diff)
downloaddexon-a973d1d5233bbd36cbaed0b7235311d4cd529580.tar.gz
dexon-a973d1d5233bbd36cbaed0b7235311d4cd529580.tar.zst
dexon-a973d1d5233bbd36cbaed0b7235311d4cd529580.zip
Merge pull request #3674 from obscuren/gaz64
params: core, core/vm, miner: 64bit gas instructions
Diffstat (limited to 'core')
-rw-r--r--core/bench_test.go7
-rw-r--r--core/block_validator.go2
-rw-r--r--core/blockchain_test.go16
-rw-r--r--core/chain_makers_test.go6
-rw-r--r--core/state_transition.go88
-rw-r--r--core/vm/common.go78
-rw-r--r--core/vm/contract.go33
-rw-r--r--core/vm/contracts.go39
-rw-r--r--core/vm/evm.go (renamed from core/vm/environment.go)83
-rw-r--r--core/vm/gas.go153
-rw-r--r--core/vm/gas_table.go418
-rw-r--r--core/vm/gas_table_test.go24
-rw-r--r--core/vm/instructions.go338
-rw-r--r--core/vm/int_pool_verifier.go15
-rw-r--r--core/vm/int_pool_verifier_empty.go7
-rw-r--r--core/vm/interpreter.go (renamed from core/vm/vm.go)40
-rw-r--r--core/vm/intpool.go (renamed from core/vm/virtual_machine.go)35
-rw-r--r--core/vm/jump_table.go105
-rw-r--r--core/vm/logger_test.go4
-rw-r--r--core/vm/memory.go5
-rw-r--r--core/vm/runtime/env.go2
-rw-r--r--core/vm/runtime/runtime.go14
-rw-r--r--core/vm/runtime/runtime_test.go4
-rw-r--r--core/vm/stack_table.go8
24 files changed, 843 insertions, 681 deletions
diff --git a/core/bench_test.go b/core/bench_test.go
index 353d217fd..59b5ad758 100644
--- a/core/bench_test.go
+++ b/core/bench_test.go
@@ -92,6 +92,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
var (
ringKeys = make([]*ecdsa.PrivateKey, 1000)
ringAddrs = make([]common.Address, len(ringKeys))
+ bigTxGas = new(big.Int).SetUint64(params.TxGas)
)
func init() {
@@ -111,8 +112,8 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
return func(i int, gen *BlockGen) {
gas := CalcGasLimit(gen.PrevBlock(i - 1))
for {
- gas.Sub(gas, params.TxGas)
- if gas.Cmp(params.TxGas) < 0 {
+ gas.Sub(gas, bigTxGas)
+ if gas.Cmp(bigTxGas) < 0 {
break
}
to := (from + 1) % naccounts
@@ -120,7 +121,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
gen.TxNonce(ringAddrs[from]),
ringAddrs[to],
benchRootFunds,
- params.TxGas,
+ bigTxGas,
nil,
nil,
)
diff --git a/core/block_validator.go b/core/block_validator.go
index 65f975345..ee524b61f 100644
--- a/core/block_validator.go
+++ b/core/block_validator.go
@@ -204,7 +204,7 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b
//
// See YP section 4.3.4. "Block Header Validity"
func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
- if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
+ if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
}
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 16bea9d7c..99b155620 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -720,7 +720,7 @@ func TestFastVsFullChains(t *testing.T) {
// If the block number is multiple of 3, send a few bonus transactions to the miner
if i%3 == 2 {
for j := 0; j < i%4+1; j++ {
- tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil), signer, key)
+ tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), bigTxGas, nil, nil), signer, key)
if err != nil {
panic(err)
}
@@ -884,8 +884,8 @@ func TestChainTxReorgs(t *testing.T) {
// Create two transactions shared between the chains:
// - postponed: transaction included at a later block in the forked chain
// - swapped: transaction included at the same block number in the forked chain
- postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
- swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
+ postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
+ swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
// Create two transactions that will be dropped by the forked chain:
// - pastDrop: transaction dropped retroactively from a past block
@@ -901,13 +901,13 @@ func TestChainTxReorgs(t *testing.T) {
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {
switch i {
case 0:
- pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
+ pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point
gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork
case 2:
- freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
+ freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point
gen.AddTx(swapped) // This transaction will be swapped out at the exact height
@@ -926,18 +926,18 @@ func TestChainTxReorgs(t *testing.T) {
chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 5, func(i int, gen *BlockGen) {
switch i {
case 0:
- pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
+ pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
gen.AddTx(pastAdd) // This transaction needs to be injected during reorg
case 2:
gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain
gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain
- freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
+ freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time
case 3:
- futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
+ futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
}
})
diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go
index 2796817c0..5c563de46 100644
--- a/core/chain_makers_test.go
+++ b/core/chain_makers_test.go
@@ -56,13 +56,13 @@ func ExampleGenerateChain() {
switch i {
case 0:
// In block 1, addr1 sends addr2 some ether.
- tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
+ tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), bigTxGas, nil, nil), signer, key1)
gen.AddTx(tx)
case 1:
// In block 2, addr1 sends some more ether to addr2.
// addr2 passes it on to addr3.
- tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
- tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
+ tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
+ tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
gen.AddTx(tx1)
gen.AddTx(tx2)
case 2:
diff --git a/core/state_transition.go b/core/state_transition.go
index 3cb7e500c..98dc8d995 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -49,15 +49,16 @@ The state transitioning model does all all the necessary work to work out a vali
6) Derive new state root
*/
type StateTransition struct {
- gp *GasPool
- msg Message
- gas, gasPrice *big.Int
- initialGas *big.Int
- value *big.Int
- data []byte
- state vm.StateDB
-
- env *vm.EVM
+ gp *GasPool
+ msg Message
+ gas uint64
+ gasPrice *big.Int
+ initialGas *big.Int
+ value *big.Int
+ data []byte
+ state vm.StateDB
+
+ evm *vm.EVM
}
// Message represents a message sent to a contract.
@@ -81,12 +82,14 @@ func MessageCreatesContract(msg Message) bool {
// IntrinsicGas computes the 'intrinsic gas' for a message
// with the given data.
+//
+// TODO convert to uint64
func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
igas := new(big.Int)
if contractCreation && homestead {
- igas.Set(params.TxGasContractCreation)
+ igas.SetUint64(params.TxGasContractCreation)
} else {
- igas.Set(params.TxGas)
+ igas.SetUint64(params.TxGas)
}
if len(data) > 0 {
var nz int64
@@ -96,27 +99,26 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
}
}
m := big.NewInt(nz)
- m.Mul(m, params.TxDataNonZeroGas)
+ m.Mul(m, new(big.Int).SetUint64(params.TxDataNonZeroGas))
igas.Add(igas, m)
m.SetInt64(int64(len(data)) - nz)
- m.Mul(m, params.TxDataZeroGas)
+ m.Mul(m, new(big.Int).SetUint64(params.TxDataZeroGas))
igas.Add(igas, m)
}
return igas
}
// NewStateTransition initialises and returns a new state transition object.
-func NewStateTransition(env *vm.EVM, msg Message, gp *GasPool) *StateTransition {
+func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
return &StateTransition{
gp: gp,
- env: env,
+ evm: evm,
msg: msg,
- gas: new(big.Int),
gasPrice: msg.GasPrice(),
initialGas: new(big.Int),
value: msg.Value(),
data: msg.Data(),
- state: env.StateDB,
+ state: evm.StateDB,
}
}
@@ -127,8 +129,8 @@ func NewStateTransition(env *vm.EVM, msg Message, gp *GasPool) *StateTransition
// the gas used (which includes gas refunds) and an error if it failed. An error always
// indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block.
-func ApplyMessage(env *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
- st := NewStateTransition(env, msg, gp)
+func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
+ st := NewStateTransition(evm, msg, gp)
ret, _, gasUsed, err := st.TransitionDb()
return ret, gasUsed, err
@@ -157,21 +159,21 @@ func (self *StateTransition) to() vm.Account {
return self.state.GetAccount(*to)
}
-func (self *StateTransition) useGas(amount *big.Int) error {
- if self.gas.Cmp(amount) < 0 {
+func (self *StateTransition) useGas(amount uint64) error {
+ if self.gas < amount {
return vm.ErrOutOfGas
}
- self.gas.Sub(self.gas, amount)
+ self.gas -= amount
return nil
}
-func (self *StateTransition) addGas(amount *big.Int) {
- self.gas.Add(self.gas, amount)
-}
-
func (self *StateTransition) buyGas() error {
mgas := self.msg.Gas()
+ if mgas.BitLen() > 64 {
+ return vm.ErrOutOfGas
+ }
+
mgval := new(big.Int).Mul(mgas, self.gasPrice)
sender := self.from()
@@ -181,7 +183,8 @@ func (self *StateTransition) buyGas() error {
if err := self.gp.SubGas(mgas); err != nil {
return err
}
- self.addGas(mgas)
+ self.gas += mgas.Uint64()
+
self.initialGas.Set(mgas)
sender.SubBalance(mgval)
return nil
@@ -209,7 +212,9 @@ func (self *StateTransition) preCheck() (err error) {
return nil
}
-// TransitionDb will move the state by applying the message against the given environment.
+// TransitionDb will transition the state by applying the current message and returning the result
+// including the required gas for the operation as well as the used gas. It returns an error if it
+// failed. An error indicates a consensus issue.
func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, err error) {
if err = self.preCheck(); err != nil {
return
@@ -217,26 +222,32 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
msg := self.msg
sender := self.from() // err checked in preCheck
- homestead := self.env.ChainConfig().IsHomestead(self.env.BlockNumber)
+ homestead := self.evm.ChainConfig().IsHomestead(self.evm.BlockNumber)
contractCreation := MessageCreatesContract(msg)
// Pay intrinsic gas
- if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil {
+ // TODO convert to uint64
+ intrinsicGas := IntrinsicGas(self.data, contractCreation, homestead)
+ if intrinsicGas.BitLen() > 64 {
+ return nil, nil, nil, InvalidTxError(vm.ErrOutOfGas)
+ }
+
+ if err = self.useGas(intrinsicGas.Uint64()); err != nil {
return nil, nil, nil, InvalidTxError(err)
}
var (
- vmenv = self.env
+ evm = self.evm
// vm errors do not effect consensus and are therefor
// not assigned to err, except for insufficient balance
// error.
vmerr error
)
if contractCreation {
- ret, _, vmerr = vmenv.Create(sender, self.data, self.gas, self.value)
+ ret, _, self.gas, vmerr = evm.Create(sender, self.data, self.gas, self.value)
} else {
// Increment the nonce for the next transaction
self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1)
- ret, vmerr = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.value)
+ ret, self.gas, vmerr = evm.Call(sender, self.to().Address(), self.data, self.gas, self.value)
}
if vmerr != nil {
glog.V(logger.Core).Infoln("vm returned with error:", err)
@@ -251,7 +262,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
requiredGas = new(big.Int).Set(self.gasUsed())
self.refundGas()
- self.state.AddBalance(self.env.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice))
+ self.state.AddBalance(self.evm.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice))
return ret, requiredGas, self.gasUsed(), err
}
@@ -260,20 +271,21 @@ func (self *StateTransition) refundGas() {
// Return eth for remaining gas to the sender account,
// exchanged at the original rate.
sender := self.from() // err already checked
- remaining := new(big.Int).Mul(self.gas, self.gasPrice)
+ remaining := new(big.Int).Mul(new(big.Int).SetUint64(self.gas), self.gasPrice)
sender.AddBalance(remaining)
// Apply refund counter, capped to half of the used gas.
uhalf := remaining.Div(self.gasUsed(), common.Big2)
refund := common.BigMin(uhalf, self.state.GetRefund())
- self.gas.Add(self.gas, refund)
+ self.gas += refund.Uint64()
+
self.state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice))
// Also return remaining gas to the block gas counter so it is
// available for the next transaction.
- self.gp.AddGas(self.gas)
+ self.gp.AddGas(new(big.Int).SetUint64(self.gas))
}
func (self *StateTransition) gasUsed() *big.Int {
- return new(big.Int).Sub(self.initialGas, self.gas)
+ return new(big.Int).Sub(self.initialGas, new(big.Int).SetUint64(self.gas))
}
diff --git a/core/vm/common.go b/core/vm/common.go
index 2878b92d2..b7b9a6a9c 100644
--- a/core/vm/common.go
+++ b/core/vm/common.go
@@ -21,28 +21,11 @@ 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
@@ -54,48 +37,6 @@ 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 {
@@ -106,14 +47,17 @@ func getData(data []byte, start, size *big.Int) []byte {
return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64()))
}
-// 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
+// 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
}
- // Sub the amount of gas from the remaining
- gas.Sub(gas, amount)
- return true
+ return (size + 31) / 32
}
diff --git a/core/vm/contract.go b/core/vm/contract.go
index dfa93ab18..091106d84 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -24,7 +24,6 @@ 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)
@@ -48,7 +47,8 @@ type Contract struct {
CodeAddr *common.Address
Input []byte
- value, Gas, UsedGas *big.Int
+ Gas uint64
+ value *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, gas *big.Int) *Contract {
+func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract {
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}
if parent, ok := caller.(*Contract); ok {
@@ -68,9 +68,8 @@ func NewContract(caller ContractRef, object ContractRef, value, gas *big.Int) *C
// Gas should be a pointer so it can safely be reduced through the run
// This pointer will be off the state transition
- c.Gas = gas //new(big.Int).Set(gas)
+ c.Gas = gas
c.value = new(big.Int).Set(value)
- c.UsedGas = new(big.Int)
return c
}
@@ -107,27 +106,13 @@ 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 *big.Int) (ok bool) {
- ok = useGas(c.Gas, gas)
- if ok {
- c.UsedGas.Add(c.UsedGas, gas)
+func (c *Contract) UseGas(gas uint64) (ok bool) {
+ if c.Gas < gas {
+ return false
}
- 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)
+ c.Gas -= gas
+ return true
}
// Address returns the contracts address
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 9645d268f..593b6ca55 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -17,8 +17,6 @@
package vm
import (
- "math/big"
-
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
@@ -30,8 +28,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) *big.Int // RequiredPrice calculates the contract gas use
- Run(input []byte) []byte // Run runs the precompiled contract
+ RequiredGas(inputSize int) uint64 // RequiredPrice calculates the contract gas use
+ Run(input []byte) []byte // Run runs the precompiled contract
}
// Precompiled contains the default set of ethereum contracts
@@ -57,7 +55,7 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contr
// ECRECOVER implemented as a native contract
type ecrecover struct{}
-func (c *ecrecover) RequiredGas(inputSize int) *big.Int {
+func (c *ecrecover) RequiredGas(inputSize int) uint64 {
return params.EcrecoverGas
}
@@ -92,10 +90,12 @@ func (c *ecrecover) Run(in []byte) []byte {
// SHA256 implemented as a native contract
type sha256 struct{}
-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)
+// 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) Run(in []byte) []byte {
return crypto.Sha256(in)
@@ -104,10 +104,12 @@ func (c *sha256) Run(in []byte) []byte {
// RIPMED160 implemented as a native contract
type ripemd160 struct{}
-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)
+// 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) Run(in []byte) []byte {
return common.LeftPadBytes(crypto.Ripemd160(in), 32)
@@ -116,11 +118,12 @@ func (c *ripemd160) Run(in []byte) []byte {
// data copy implemented as a native contract
type dataCopy struct{}
-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)
+// 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) Run(in []byte) []byte {
return in
diff --git a/core/vm/environment.go b/core/vm/evm.go
index c19ef464b..0c5d998c2 100644
--- a/core/vm/environment.go
+++ b/core/vm/evm.go
@@ -17,7 +17,6 @@
package vm
import (
- "fmt"
"math/big"
"sync/atomic"
@@ -102,24 +101,18 @@ 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, value *big.Int) (ret []byte, err error) {
+func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 {
- caller.ReturnGas(gas)
-
- return nil, nil
+ return nil, gas, nil
}
// Depth check execution. Fail if we're trying to execute above the
// limit.
- if evm.depth > int(params.CallCreateDepth.Int64()) {
- caller.ReturnGas(gas)
-
- return nil, ErrDepth
+ if evm.depth > int(params.CallCreateDepth) {
+ return nil, gas, ErrDepth
}
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
- caller.ReturnGas(gas)
-
- return nil, ErrInsufficientBalance
+ return nil, gas, ErrInsufficientBalance
}
var (
@@ -128,8 +121,7 @@ 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 {
- caller.ReturnGas(gas)
- return nil, nil
+ return nil, gas, nil
}
to = evm.StateDB.CreateAccount(addr)
@@ -143,7 +135,6 @@ 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
@@ -154,7 +145,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas,
evm.StateDB.RevertToSnapshot(snapshot)
}
- return ret, err
+ return ret, contract.Gas, err
}
// CallCode executes the contract associated with the addr with the given input as parameters. It also handles any
@@ -162,24 +153,18 @@ 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, value *big.Int) (ret []byte, err error) {
+func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 {
- caller.ReturnGas(gas)
-
- return nil, nil
+ return nil, gas, nil
}
// Depth check execution. Fail if we're trying to execute above the
// limit.
- if evm.depth > int(params.CallCreateDepth.Int64()) {
- caller.ReturnGas(gas)
-
- return nil, ErrDepth
+ if evm.depth > int(params.CallCreateDepth) {
+ return nil, gas, ErrDepth
}
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
- caller.ReturnGas(gas)
-
- return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", value, evm.StateDB.GetBalance(caller.Address()))
+ return nil, gas, ErrInsufficientBalance
}
var (
@@ -191,7 +176,6 @@ 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 {
@@ -200,7 +184,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
evm.StateDB.RevertToSnapshot(snapshot)
}
- return ret, err
+ return ret, contract.Gas, err
}
// DelegateCall executes the contract associated with the addr with the given input as parameters.
@@ -208,18 +192,15 @@ 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 *big.Int) (ret []byte, err error) {
+func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 {
- caller.ReturnGas(gas)
-
- return nil, nil
+ return nil, gas, nil
}
// Depth check execution. Fail if we're trying to execute above the
// limit.
- if evm.depth > int(params.CallCreateDepth.Int64()) {
- caller.ReturnGas(gas)
- return nil, ErrDepth
+ if evm.depth > int(params.CallCreateDepth) {
+ return nil, gas, ErrDepth
}
var (
@@ -230,7 +211,6 @@ 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 {
@@ -239,28 +219,22 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
evm.StateDB.RevertToSnapshot(snapshot)
}
- return ret, err
+ return ret, contract.Gas, err
}
// Create creates a new contract using code as deployment code.
-func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (ret []byte, contractAddr common.Address, err error) {
+func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 {
- caller.ReturnGas(gas)
-
- return nil, common.Address{}, nil
+ return nil, common.Address{}, gas, nil
}
// Depth check execution. Fail if we're trying to execute above the
// limit.
- if evm.depth > int(params.CallCreateDepth.Int64()) {
- caller.ReturnGas(gas)
-
- return nil, common.Address{}, ErrDepth
+ if evm.depth > int(params.CallCreateDepth) {
+ return nil, common.Address{}, gas, ErrDepth
}
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
- caller.ReturnGas(gas)
-
- return nil, common.Address{}, ErrInsufficientBalance
+ return nil, common.Address{}, gas, ErrInsufficientBalance
}
// Create a new account on the state
@@ -280,7 +254,6 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (re
// only.
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code)
- defer contract.Finalise()
ret, err = evm.interpreter.Run(contract, nil)
@@ -291,9 +264,8 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (re
// 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 {
- dataGas := big.NewInt(int64(len(ret)))
- dataGas.Mul(dataGas, params.CreateDataGas)
- if contract.UseGas(dataGas) {
+ createDataGas := uint64(len(ret)) * params.CreateDataGas
+ if contract.UseGas(createDataGas) {
evm.StateDB.SetCode(contractAddr, ret)
} else {
err = ErrCodeStoreOutOfGas
@@ -305,11 +277,10 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (re
// 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, err
+ return nil, contractAddr, 0, 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
@@ -318,7 +289,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (re
ret = nil
}
- return ret, contractAddr, err
+ return ret, contractAddr, contract.Gas, err
}
// ChainConfig returns the evmironment's chain configuration
diff --git a/core/vm/gas.go b/core/vm/gas.go
index cb225b6ca..dd64d5f17 100644
--- a/core/vm/gas.go
+++ b/core/vm/gas.go
@@ -17,149 +17,42 @@
package vm
import (
- "fmt"
"math/big"
"github.com/ethereum/go-ethereum/params"
)
-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)
+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
)
// 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, 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
+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
}
}
- 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)
+ if callCost.BitLen() > 64 {
+ return 0, errGasUintOverflow
}
- 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
-}
-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},
+ return callCost.Uint64(), nil
}
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index 4d2c4e94a..fba1eb066 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -1,56 +1,80 @@
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"
)
-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)
- }
+// 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
}
- return gas
+
+ 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
+ }
+ return 0, 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 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 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))
+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
+ }
- return gas.Add(gas, words.Mul(words, params.CopyGas))
+ 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
}
-func gasSStore(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
y, x = stack.Back(1), stack.Back(0)
- val = env.StateDB.GetState(contract.Address(), common.BigToHash(x))
+ val = evm.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)
@@ -58,189 +82,335 @@ func gasSStore(gt params.GasTable, env *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 new(big.Int).Set(params.SstoreSetGas)
+ return params.SstoreSetGas, nil
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
- env.StateDB.AddRefund(params.SstoreRefundGas)
+ evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SstoreRefundGas))
- return new(big.Int).Set(params.SstoreClearGas)
+ return params.SstoreClearGas, nil
} else {
// non 0 => non 0 (or 0 => 0)
- return new(big.Int).Set(params.SstoreResetGas)
+ return params.SstoreResetGas, nil
}
}
-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)
+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
+ }
- 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
+ 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
}
}
-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 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
+ }
-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))
+ if gas, overflow = math.SafeAdd(gas, params.Sha3Gas); overflow {
+ return 0, errGasUintOverflow
+ }
- return gas.Add(gas, words.Mul(words, params.CopyGas))
+ 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 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))
+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
+ }
- return gas.Add(gas, words.Mul(words, params.CopyGas))
+ 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
}
-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 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
+ }
+
+ if gas, overflow = math.SafeAdd(gas, wordGas); 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 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 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 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 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 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 gasBalance(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
- return gt.Balance
+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 gasExtCodeSize(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
- return gt.ExtcodeSize
+func gasBalance(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ return gt.Balance, nil
}
-func gasSLoad(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
- return gt.SLoad
+func gasExtCodeSize(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ return gt.ExtcodeSize, 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 gasSLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ return gt.SLoad, nil
}
-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)
+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)
- transfersValue := stack.Back(2).BitLen() > 0
var (
- address = common.BigToAddress(stack.Back(1))
- eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
+ 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 gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ var (
+ gas = gt.Calls
+ transfersValue = stack.Back(2).BitLen() > 0
+ address = common.BigToAddress(stack.Back(1))
+ eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber)
)
if eip158 {
- if env.StateDB.Empty(address) && transfersValue {
- gas.Add(gas, params.CallNewAccountGas)
+ if evm.StateDB.Empty(address) && transfersValue {
+ gas += params.CallNewAccountGas
}
- } else if !env.StateDB.Exist(address) {
- gas.Add(gas, params.CallNewAccountGas)
+ } else if !evm.StateDB.Exist(address) {
+ gas += params.CallNewAccountGas
}
if transfersValue {
- gas.Add(gas, params.CallValueTransferGas)
+ 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, memoryGasCost(mem, memorySize))
- cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1])
+ cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
+ if err != nil {
+ return 0, err
+ }
// 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] = cg
+ stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
- return gas.Add(gas, cg)
+ if gas, overflow = math.SafeAdd(gas, cg); overflow {
+ return 0, errGasUintOverflow
+ }
+ return gas, nil
}
-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)
+func gasCallCode(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ gas := gt.Calls
if stack.Back(2).BitLen() > 0 {
- gas.Add(gas, params.CallValueTransferGas)
+ 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, memoryGasCost(mem, memorySize))
- cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1])
+ cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
+ if err != nil {
+ return 0, err
+ }
// 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] = cg
+ stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
- return gas.Add(gas, cg)
+ if gas, overflow = math.SafeAdd(gas, cg); overflow {
+ return 0, errGasUintOverflow
+ }
+ return gas, nil
}
-func gasReturn(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
+func gasReturn(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return memoryGasCost(mem, memorySize)
}
-func gasSuicide(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
- gas := new(big.Int)
+func gasSuicide(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ var gas uint64
// EIP150 homestead gas reprice fork:
- if env.ChainConfig().IsEIP150(env.BlockNumber) {
- gas.Set(gt.Suicide)
+ if evm.ChainConfig().IsEIP150(evm.BlockNumber) {
+ gas = gt.Suicide
var (
address = common.BigToAddress(stack.Back(0))
- eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
+ eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber)
)
if eip158 {
// if empty and transfers value
- if env.StateDB.Empty(address) && env.StateDB.GetBalance(contract.Address()).BitLen() > 0 {
- gas.Add(gas, gt.CreateBySuicide)
+ if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).BitLen() > 0 {
+ gas += gt.CreateBySuicide
}
- } else if !env.StateDB.Exist(address) {
- gas.Add(gas, gt.CreateBySuicide)
+ } else if !evm.StateDB.Exist(address) {
+ gas += gt.CreateBySuicide
}
}
- if !env.StateDB.HasSuicided(contract.Address()) {
- env.StateDB.AddRefund(params.SuicideRefundGas)
+ if !evm.StateDB.HasSuicided(contract.Address()) {
+ evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SuicideRefundGas))
}
- return gas
+ return gas, nil
}
-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))
+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
+ }
- cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1])
+ cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
+ if err != nil {
+ return 0, err
+ }
// 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] = cg
+ stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
- return gas.Add(gas, cg)
+ if gas, overflow = math.SafeAdd(gas, cg); overflow {
+ return 0, errGasUintOverflow
+ }
+ return gas, nil
}
-func gasPush(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
- return GasFastestStep
+func gasPush(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 gasSwap(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
+func gasDup(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ return GasFastestStep, nil
}
diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go
new file mode 100644
index 000000000..cceb89285
--- /dev/null
+++ b/core/vm/gas_table_test.go
@@ -0,0 +1,24 @@
+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 3b1b06cca..39e5c0587 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -27,42 +27,56 @@ import (
"github.com/ethereum/go-ethereum/params"
)
-func opAdd(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+var bigZero = new(big.Int)
+
+func opAdd(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opSub(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opMul(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opDiv(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opSdiv(pc *uint64, evm *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 new(big.Int).Mul(x, y).Cmp(common.Big0) < 0 {
+ if evm.interpreter.intPool.get().Mul(x, y).Cmp(common.Big0) < 0 {
n.SetInt64(-1)
} else {
n.SetInt64(1)
@@ -73,20 +87,22 @@ func opSdiv(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Sta
stack.push(U256(res))
}
+ evm.interpreter.intPool.put(y)
return nil, nil
}
-func opMod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opMod(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opSmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := S256(stack.pop()), S256(stack.pop())
if y.Cmp(common.Big0) == 0 {
@@ -104,16 +120,20 @@ func opSmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Sta
stack.push(U256(res))
}
+ evm.interpreter.intPool.put(y)
return nil, nil
}
-func opExp(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opExp(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opSignExtend(pc *uint64, evm *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)
@@ -128,198 +148,231 @@ func opSignExtend(pc *uint64, env *EVM, contract *Contract, memory *Memory, stac
stack.push(U256(num))
}
+
+ evm.interpreter.intPool.put(back)
return nil, nil
}
-func opNot(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opNot(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opLt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop()
if x.Cmp(y) < 0 {
- stack.push(big.NewInt(1))
+ stack.push(evm.interpreter.intPool.get().SetUint64(1))
} else {
stack.push(new(big.Int))
}
+
+ evm.interpreter.intPool.put(x, y)
return nil, nil
}
-func opGt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opGt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop()
if x.Cmp(y) > 0 {
- stack.push(big.NewInt(1))
+ stack.push(evm.interpreter.intPool.get().SetUint64(1))
} else {
stack.push(new(big.Int))
}
+
+ evm.interpreter.intPool.put(x, y)
return nil, nil
}
-func opSlt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opSlt(pc *uint64, evm *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(big.NewInt(1))
+ stack.push(evm.interpreter.intPool.get().SetUint64(1))
} else {
stack.push(new(big.Int))
}
+
+ evm.interpreter.intPool.put(x, y)
return nil, nil
}
-func opSgt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opSgt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := S256(stack.pop()), S256(stack.pop())
if x.Cmp(y) > 0 {
- stack.push(big.NewInt(1))
+ stack.push(evm.interpreter.intPool.get().SetUint64(1))
} else {
stack.push(new(big.Int))
}
+
+ evm.interpreter.intPool.put(x, y)
return nil, nil
}
-func opEq(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opEq(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop()
if x.Cmp(y) == 0 {
- stack.push(big.NewInt(1))
+ stack.push(evm.interpreter.intPool.get().SetUint64(1))
} else {
stack.push(new(big.Int))
}
+
+ evm.interpreter.intPool.put(x, y)
return nil, nil
}
-func opIszero(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opIszero(pc *uint64, evm *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(big.NewInt(1))
+ stack.push(evm.interpreter.intPool.get().SetUint64(1))
}
+
+ evm.interpreter.intPool.put(x)
return nil, nil
}
-func opAnd(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opAnd(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opOr(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opXor(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opByte(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
th, val := stack.pop(), stack.pop()
if th.Cmp(big.NewInt(32)) < 0 {
- byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
+ byte := evm.interpreter.intPool.get().SetInt64(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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opAddmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y, z := stack.pop(), stack.pop(), stack.pop()
- if z.Cmp(Zero) > 0 {
+ if z.Cmp(bigZero) > 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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opMulmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y, z := stack.pop(), stack.pop(), stack.pop()
- if z.Cmp(Zero) > 0 {
+ if z.Cmp(bigZero) > 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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opSha3(pc *uint64, evm *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 env.vmConfig.EnablePreimageRecording {
- env.StateDB.AddPreimage(common.BytesToHash(hash), data)
+ if evm.vmConfig.EnablePreimageRecording {
+ evm.StateDB.AddPreimage(common.BytesToHash(hash), data)
}
stack.push(common.BytesToBig(hash))
+
+ evm.interpreter.intPool.put(offset, size)
return nil, nil
}
-func opAddress(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opAddress(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(common.Bytes2Big(contract.Address().Bytes()))
return nil, nil
}
-func opBalance(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opBalance(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
addr := common.BigToAddress(stack.pop())
- balance := env.StateDB.GetBalance(addr)
+ balance := evm.StateDB.GetBalance(addr)
stack.push(new(big.Int).Set(balance))
return nil, nil
}
-func opOrigin(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(env.Origin.Big())
+func opOrigin(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(evm.Origin.Big())
return nil, nil
}
-func opCaller(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCaller(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(contract.Caller().Big())
return nil, nil
}
-func opCallValue(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(new(big.Int).Set(contract.value))
+func opCallValue(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(evm.interpreter.intPool.get().Set(contract.value))
return nil, nil
}
-func opCalldataLoad(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCalldataLoad(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(big.NewInt(int64(len(contract.Input))))
+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))))
return nil, nil
}
-func opCalldataCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCalldataCopy(pc *uint64, evm *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, 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)
+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)
+
return nil, nil
}
-func opCodeSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- l := big.NewInt(int64(len(contract.Code)))
+func opCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ l := evm.interpreter.intPool.get().SetInt64(int64(len(contract.Code)))
stack.push(l)
return nil, nil
}
-func opCodeCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
var (
mOff = stack.pop()
cOff = stack.pop()
@@ -328,113 +381,129 @@ func opCodeCopy(pc *uint64, env *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opExtCodeCopy(pc *uint64, evm *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(env.StateDB.GetCode(addr), cOff, l)
+ codeCopy := getData(evm.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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(new(big.Int).Set(env.GasPrice))
+func opGasprice(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(evm.interpreter.intPool.get().Set(evm.GasPrice))
return nil, nil
}
-func opBlockhash(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opBlockhash(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
num := stack.pop()
- 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())
+ 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())
} else {
stack.push(new(big.Int))
}
+
+ evm.interpreter.intPool.put(num, n)
return nil, nil
}
-func opCoinbase(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(env.Coinbase.Big())
+func opCoinbase(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(evm.Coinbase.Big())
return nil, nil
}
-func opTimestamp(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(U256(new(big.Int).Set(env.Time)))
+func opTimestamp(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(U256(new(big.Int).Set(evm.Time)))
return nil, nil
}
-func opNumber(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(U256(new(big.Int).Set(env.BlockNumber)))
+func opNumber(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(U256(new(big.Int).Set(evm.BlockNumber)))
return nil, nil
}
-func opDifficulty(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(U256(new(big.Int).Set(env.Difficulty)))
+func opDifficulty(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(U256(new(big.Int).Set(evm.Difficulty)))
return nil, nil
}
-func opGasLimit(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(U256(new(big.Int).Set(env.GasLimit)))
+func opGasLimit(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(U256(new(big.Int).Set(evm.GasLimit)))
return nil, nil
}
-func opPop(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.pop()
+func opPop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ evm.interpreter.intPool.put(stack.pop())
return nil, nil
}
-func opMload(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opMload(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opMstore(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opMstore8(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opSload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
loc := common.BigToHash(stack.pop())
- val := env.StateDB.GetState(contract.Address(), loc).Big()
+ val := evm.StateDB.GetState(contract.Address(), loc).Big()
stack.push(val)
return nil, nil
}
-func opSstore(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opSstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
loc := common.BigToHash(stack.pop())
val := stack.pop()
- env.StateDB.SetState(contract.Address(), loc, common.BigToHash(val))
+ evm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val))
+
+ evm.interpreter.intPool.put(val)
return nil, nil
}
-func opJump(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opJump(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opJumpi(pc *uint64, evm *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) {
@@ -445,57 +514,62 @@ func opJumpi(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *St
} else {
*pc++
}
+
+ evm.interpreter.intPool.put(pos, cond)
return nil, nil
}
-func opJumpdest(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opJumpdest(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
return nil, nil
}
-func opPc(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(new(big.Int).SetUint64(*pc))
+func opPc(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(evm.interpreter.intPool.get().SetUint64(*pc))
return nil, nil
}
-func opMsize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(big.NewInt(int64(memory.Len())))
+func opMsize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(evm.interpreter.intPool.get().SetInt64(int64(memory.Len())))
return nil, nil
}
-func opGas(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(new(big.Int).Set(contract.Gas))
+func opGas(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ stack.push(evm.interpreter.intPool.get().SetUint64(contract.Gas))
return nil, nil
}
-func opCreate(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCreate(pc *uint64, evm *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 = new(big.Int).Set(contract.Gas)
+ gas = contract.Gas
)
- if env.ChainConfig().IsEIP150(env.BlockNumber) {
- gas.Div(gas, n64)
- gas = gas.Sub(contract.Gas, gas)
+ if evm.ChainConfig().IsEIP150(evm.BlockNumber) {
+ gas -= gas / 64
}
contract.UseGas(gas)
- _, addr, suberr := env.Create(contract, input, gas, value)
+ _, addr, returnGas, suberr := evm.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 env.ChainConfig().IsHomestead(env.BlockNumber) && suberr == ErrCodeStoreOutOfGas {
+ if evm.ChainConfig().IsHomestead(evm.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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- gas := stack.pop()
+func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ gas := stack.pop().Uint64()
// pop gas and value of the stack.
addr, value := stack.pop(), stack.pop()
value = U256(value)
@@ -509,25 +583,26 @@ func opCall(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Sta
// Get the arguments from the memory
args := memory.Get(inOffset.Int64(), inSize.Int64())
- if len(value.Bytes()) > 0 {
- gas.Add(gas, params.CallStipend)
+ if value.BitLen() > 0 {
+ gas += params.CallStipend
}
- ret, err := env.Call(contract, address, args, gas, value)
-
+ ret, returnGas, err := evm.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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- gas := stack.pop()
+func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ gas := stack.pop().Uint64()
// pop gas and value of the stack.
addr, value := stack.pop(), stack.pop()
value = U256(value)
@@ -541,12 +616,11 @@ func opCallCode(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack
// Get the arguments from the memory
args := memory.Get(inOffset.Int64(), inSize.Int64())
- if len(value.Bytes()) > 0 {
- gas.Add(gas, params.CallStipend)
+ if value.BitLen() > 0 {
+ gas += params.CallStipend
}
- ret, err := env.CallCode(contract, address, args, gas, value)
-
+ ret, returnGas, err := evm.CallCode(contract, address, args, gas, value)
if err != nil {
stack.push(new(big.Int))
@@ -555,46 +629,54 @@ func opCallCode(pc *uint64, env *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
// if not homestead return an error. DELEGATECALL is not supported
// during pre-homestead.
- if !env.ChainConfig().IsHomestead(env.BlockNumber) {
+ if !evm.ChainConfig().IsHomestead(evm.BlockNumber) {
return nil, fmt.Errorf("invalid opcode %x", DELEGATECALL)
}
- gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ gas, to, inOffset, inSize, outOffset, outSize := stack.pop().Uint64(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.BigToAddress(to)
args := memory.Get(inOffset.Int64(), inSize.Int64())
- ret, err := env.DelegateCall(contract, toAddr, args, gas)
+
+ ret, returnGas, err := evm.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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opReturn(pc *uint64, evm *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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opStop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
return nil, nil
}
-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)
+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)
- env.StateDB.Suicide(contract.Address())
+ evm.StateDB.Suicide(contract.Address())
return nil, nil
}
@@ -603,7 +685,7 @@ func opSuicide(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *
// make log instruction function
func makeLog(size int) executionFunc {
- return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ return func(pc *uint64, evm *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++ {
@@ -611,22 +693,24 @@ func makeLog(size int) executionFunc {
}
d := memory.Get(mStart.Int64(), mSize.Int64())
- env.StateDB.AddLog(&types.Log{
+ evm.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: env.BlockNumber.Uint64(),
+ BlockNumber: evm.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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- byts := getData(contract.Code, new(big.Int).SetUint64(*pc+1), bsize)
+ 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)
stack.push(common.Bytes2Big(byts))
*pc += size
return nil, nil
@@ -635,7 +719,7 @@ func makePush(size uint64, bsize *big.Int) executionFunc {
// make push instruction function
func makeDup(size int64) executionFunc {
- return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.dup(int(size))
return nil, nil
}
@@ -645,7 +729,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, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ return func(pc *uint64, evm *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
new file mode 100644
index 000000000..61c83ba7e
--- /dev/null
+++ b/core/vm/int_pool_verifier.go
@@ -0,0 +1,15 @@
+// +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
new file mode 100644
index 000000000..982f8c6dd
--- /dev/null
+++ b/core/vm/int_pool_verifier_empty.go
@@ -0,0 +1,7 @@
+// +build !VERIFY_EVM_INTEGER_POOL
+
+package vm
+
+const verifyPool = false
+
+func verifyIntegerPool(ip *intPool) {}
diff --git a/core/vm/vm.go b/core/vm/interpreter.go
index 05886a863..46c6befef 100644
--- a/core/vm/vm.go
+++ b/core/vm/interpreter.go
@@ -23,6 +23,7 @@ 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"
@@ -60,6 +61,7 @@ type Interpreter struct {
env *EVM
cfg Config
gasTable params.GasTable
+ intPool *intPool
}
// NewInterpreter returns a new instance of the Interpreter.
@@ -75,6 +77,7 @@ func NewInterpreter(env *EVM, cfg Config) *Interpreter {
env: env,
cfg: cfg,
gasTable: env.ChainConfig().GasTable(env.BlockNumber),
+ intPool: newIntPool(),
}
}
@@ -106,14 +109,18 @@ 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 *big.Int
+ cost uint64
)
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 {
- 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 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)
}
}()
@@ -147,34 +154,47 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e
return nil, err
}
- var memorySize *big.Int
+ var memorySize uint64
// calculate the new memory size and expand the memory to fit
// the operation
if operation.memorySize != nil {
- memorySize = operation.memorySize(stack)
+ memSize, overflow := bigUint64(operation.memorySize(stack))
+ if overflow {
+ return nil, errGasUintOverflow
+ }
// memory is expanded in words of 32 bytes. Gas
// is also calculated in words.
- memorySize.Mul(toWordSize(memorySize), big.NewInt(32))
+ if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
+ return nil, errGasUintOverflow
+ }
}
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 = operation.gasCost(evm.gasTable, evm.env, contract, stack, mem, memorySize)
- if !contract.UseGas(cost) {
+ cost, err = operation.gasCost(evm.gasTable, evm.env, contract, stack, mem, memorySize)
+ if err != nil || !contract.UseGas(cost) {
return nil, ErrOutOfGas
}
}
- if memorySize != nil {
- mem.Resize(memorySize.Uint64())
+ if memorySize > 0 {
+ mem.Resize(memorySize)
}
if evm.cfg.Debug {
- evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err)
+ 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)
}
+ // 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
diff --git a/core/vm/virtual_machine.go b/core/vm/intpool.go
index 629108884..4f1228e14 100644
--- a/core/vm/virtual_machine.go
+++ b/core/vm/intpool.go
@@ -1,4 +1,4 @@
-// Copyright 2014 The go-ethereum Authors
+// Copyright 2017 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,7 +16,34 @@
package vm
-// VirtualMachine is an EVM interface
-type VirtualMachine interface {
- Run(*Contract, []byte) ([]byte, error)
+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)
+ }
}
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 073798274..ed30100ac 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -17,6 +17,7 @@
package vm
import (
+ "errors"
"math/big"
"github.com/ethereum/go-ethereum/params"
@@ -24,11 +25,13 @@ import (
type (
executionFunc func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
- gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, *big.Int) *big.Int
+ gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
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
@@ -54,7 +57,7 @@ func NewJumpTable() [256]operation {
return [256]operation{
STOP: {
execute: opStop,
- gasCost: constGasFunc(new(big.Int)),
+ gasCost: constGasFunc(0),
validateStack: makeStackFunc(0, 0),
halts: true,
valid: true,
@@ -62,139 +65,139 @@ func NewJumpTable() [256]operation {
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),
+ 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),
+ validateStack: makeStackFunc(3, 1),
valid: true,
},
MULMOD: {
execute: opMulmod,
gasCost: constGasFunc(GasMidStep),
- validateStack: makeStackFunc(3, -2),
+ validateStack: makeStackFunc(3, 1),
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),
+ 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, 0),
+ validateStack: makeStackFunc(1, 1),
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),
+ validateStack: makeStackFunc(2, 1),
valid: true,
},
OR: {
execute: opOr,
gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(2, -1),
+ validateStack: makeStackFunc(2, 1),
valid: true,
},
NOT: {
execute: opNot,
gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(1, 0),
+ validateStack: makeStackFunc(1, 1),
valid: true,
},
BYTE: {
execute: opByte,
gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(2, -1),
+ validateStack: makeStackFunc(2, 1),
valid: true,
},
SHA3: {
execute: opSha3,
gasCost: gasSha3,
- validateStack: makeStackFunc(2, -1),
+ validateStack: makeStackFunc(2, 1),
memorySize: memorySha3,
valid: true,
},
@@ -207,7 +210,7 @@ func NewJumpTable() [256]operation {
BALANCE: {
execute: opBalance,
gasCost: gasBalance,
- validateStack: makeStackFunc(1, 0),
+ validateStack: makeStackFunc(1, 1),
valid: true,
},
ORIGIN: {
@@ -231,7 +234,7 @@ func NewJumpTable() [256]operation {
CALLDATALOAD: {
execute: opCalldataLoad,
gasCost: constGasFunc(GasFastestStep),
- validateStack: makeStackFunc(1, 0),
+ validateStack: makeStackFunc(1, 1),
valid: true,
},
CALLDATASIZE: {
@@ -243,7 +246,7 @@ func NewJumpTable() [256]operation {
CALLDATACOPY: {
execute: opCalldataCopy,
gasCost: gasCalldataCopy,
- validateStack: makeStackFunc(3, -3),
+ validateStack: makeStackFunc(3, 0),
memorySize: memoryCalldataCopy,
valid: true,
},
@@ -256,7 +259,7 @@ func NewJumpTable() [256]operation {
CODECOPY: {
execute: opCodeCopy,
gasCost: gasCodeCopy,
- validateStack: makeStackFunc(3, -3),
+ validateStack: makeStackFunc(3, 0),
memorySize: memoryCodeCopy,
valid: true,
},
@@ -269,20 +272,20 @@ func NewJumpTable() [256]operation {
EXTCODESIZE: {
execute: opExtCodeSize,
gasCost: gasExtCodeSize,
- validateStack: makeStackFunc(1, 0),
+ validateStack: makeStackFunc(1, 1),
valid: true,
},
EXTCODECOPY: {
execute: opExtCodeCopy,
gasCost: gasExtCodeCopy,
- validateStack: makeStackFunc(4, -4),
+ validateStack: makeStackFunc(4, 0),
memorySize: memoryExtCodeCopy,
valid: true,
},
BLOCKHASH: {
execute: opBlockhash,
gasCost: constGasFunc(GasExtStep),
- validateStack: makeStackFunc(1, 0),
+ validateStack: makeStackFunc(1, 1),
valid: true,
},
COINBASE: {
@@ -318,20 +321,20 @@ func NewJumpTable() [256]operation {
POP: {
execute: opPop,
gasCost: constGasFunc(GasQuickStep),
- validateStack: makeStackFunc(1, -1),
+ validateStack: makeStackFunc(1, 0),
valid: true,
},
MLOAD: {
execute: opMload,
gasCost: gasMLoad,
- validateStack: makeStackFunc(1, 0),
+ validateStack: makeStackFunc(1, 1),
memorySize: memoryMLoad,
valid: true,
},
MSTORE: {
execute: opMstore,
gasCost: gasMStore,
- validateStack: makeStackFunc(2, -2),
+ validateStack: makeStackFunc(2, 0),
memorySize: memoryMStore,
valid: true,
},
@@ -339,33 +342,33 @@ func NewJumpTable() [256]operation {
execute: opMstore8,
gasCost: gasMStore8,
memorySize: memoryMStore8,
- validateStack: makeStackFunc(2, -2),
+ validateStack: makeStackFunc(2, 0),
valid: true,
},
SLOAD: {
execute: opSload,
gasCost: gasSLoad,
- validateStack: makeStackFunc(1, 0),
+ validateStack: makeStackFunc(1, 1),
valid: true,
},
SSTORE: {
execute: opSstore,
gasCost: gasSStore,
- validateStack: makeStackFunc(2, -2),
+ validateStack: makeStackFunc(2, 0),
valid: true,
},
JUMP: {
execute: opJump,
gasCost: constGasFunc(GasMidStep),
- validateStack: makeStackFunc(1, -1),
+ validateStack: makeStackFunc(1, 0),
jumps: true,
valid: true,
},
JUMPI: {
execute: opJumpi,
gasCost: constGasFunc(GasSlowStep),
- validateStack: makeStackFunc(2, -2),
+ validateStack: makeStackFunc(2, 0),
jumps: true,
valid: true,
},
@@ -780,63 +783,63 @@ func NewJumpTable() [256]operation {
LOG0: {
execute: makeLog(0),
gasCost: makeGasLog(0),
- validateStack: makeStackFunc(2, -2),
+ validateStack: makeStackFunc(2, 0),
memorySize: memoryLog,
valid: true,
},
LOG1: {
execute: makeLog(1),
gasCost: makeGasLog(1),
- validateStack: makeStackFunc(3, -3),
+ validateStack: makeStackFunc(3, 0),
memorySize: memoryLog,
valid: true,
},
LOG2: {
execute: makeLog(2),
gasCost: makeGasLog(2),
- validateStack: makeStackFunc(4, -4),
+ validateStack: makeStackFunc(4, 0),
memorySize: memoryLog,
valid: true,
},
LOG3: {
execute: makeLog(3),
gasCost: makeGasLog(3),
- validateStack: makeStackFunc(5, -5),
+ validateStack: makeStackFunc(5, 0),
memorySize: memoryLog,
valid: true,
},
LOG4: {
execute: makeLog(4),
gasCost: makeGasLog(4),
- validateStack: makeStackFunc(6, -6),
+ validateStack: makeStackFunc(6, 0),
memorySize: memoryLog,
valid: true,
},
CREATE: {
execute: opCreate,
gasCost: gasCreate,
- validateStack: makeStackFunc(3, -2),
+ validateStack: makeStackFunc(3, 1),
memorySize: memoryCreate,
valid: true,
},
CALL: {
execute: opCall,
gasCost: gasCall,
- validateStack: makeStackFunc(7, -6),
+ validateStack: makeStackFunc(7, 1),
memorySize: memoryCall,
valid: true,
},
CALLCODE: {
execute: opCallCode,
gasCost: gasCallCode,
- validateStack: makeStackFunc(7, -6),
+ validateStack: makeStackFunc(7, 1),
memorySize: memoryCall,
valid: true,
},
RETURN: {
execute: opReturn,
gasCost: gasReturn,
- validateStack: makeStackFunc(2, -2),
+ validateStack: makeStackFunc(2, 0),
memorySize: memoryReturn,
halts: true,
valid: true,
@@ -844,14 +847,14 @@ func NewJumpTable() [256]operation {
DELEGATECALL: {
execute: opDelegateCall,
gasCost: gasDelegateCall,
- validateStack: makeStackFunc(6, -5),
+ validateStack: makeStackFunc(6, 1),
memorySize: memoryDelegateCall,
valid: true,
},
SELFDESTRUCT: {
execute: opSuicide,
gasCost: gasSuicide,
- validateStack: makeStackFunc(1, -1),
+ validateStack: makeStackFunc(1, 0),
halts: true,
valid: true,
},
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index 1d0bd96fa..ca60cba43 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), new(big.Int))
+ contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
)
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), new(big.Int))
+ contract = NewContract(ref, ref, new(big.Int), 0)
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 d01188417..99a84d227 100644
--- a/core/vm/memory.go
+++ b/core/vm/memory.go
@@ -20,11 +20,12 @@ import "fmt"
// Memory implements a simple memory model for the ethereum virtual machine.
type Memory struct {
- store []byte
+ store []byte
+ lastGasCost uint64
}
func NewMemory() *Memory {
- return &Memory{nil}
+ return &Memory{}
}
// Set sets offset + size to value
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
index a25c6d71c..9aa88e669 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: cfg.GasLimit,
+ GasLimit: new(big.Int).SetUint64(cfg.GasLimit),
GasPrice: new(big.Int),
}
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index b5adb982c..cf46603db 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -17,6 +17,7 @@
package runtime
import (
+ "math"
"math/big"
"time"
@@ -37,7 +38,7 @@ type Config struct {
Coinbase common.Address
BlockNumber *big.Int
Time *big.Int
- GasLimit *big.Int
+ GasLimit uint64
GasPrice *big.Int
Value *big.Int
DisableJit bool // "disable" so it's enabled by default
@@ -68,8 +69,8 @@ func setDefaults(cfg *Config) {
if cfg.Time == nil {
cfg.Time = big.NewInt(time.Now().Unix())
}
- if cfg.GasLimit == nil {
- cfg.GasLimit = new(big.Int).Set(common.MaxBig)
+ if cfg.GasLimit == 0 {
+ cfg.GasLimit = math.MaxUint64
}
if cfg.GasPrice == nil {
cfg.GasPrice = new(big.Int)
@@ -112,7 +113,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,
@@ -140,12 +141,13 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, error) {
)
// Call the code with the given configuration.
- return vmenv.Create(
+ code, address, _, err := 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
@@ -160,7 +162,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 1e618b688..8ad74a89a 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 == nil {
- t.Error("expected time to be non nil")
+ if cfg.GasLimit == 0 {
+ t.Error("didn't expect gaslimit to be zero")
}
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 eed8805f2..ddc41fed2 100644
--- a/core/vm/stack_table.go
+++ b/core/vm/stack_table.go
@@ -6,13 +6,13 @@ import (
"github.com/ethereum/go-ethereum/params"
)
-func makeStackFunc(pop, diff int) stackValidationFunc {
+func makeStackFunc(pop, push int) stackValidationFunc {
return func(stack *Stack) error {
if err := stack.require(pop); err != nil {
return err
}
- if int64(stack.len()+diff) > params.StackLimit.Int64() {
+ if stack.len()+push-pop > int(params.StackLimit) {
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit)
}
return nil
@@ -20,9 +20,9 @@ func makeStackFunc(pop, diff int) stackValidationFunc {
}
func makeDupStackFunc(n int) stackValidationFunc {
- return makeStackFunc(n, 1)
+ return makeStackFunc(n, n+1)
}
func makeSwapStackFunc(n int) stackValidationFunc {
- return makeStackFunc(n, 0)
+ return makeStackFunc(n, n)
}