aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--accounts/abi/type_test.go4
-rw-r--r--cmd/evm/main.go4
-rw-r--r--cmd/geth/main.go2
-rw-r--r--common/math/integer.go25
-rw-r--r--common/math/integer_test.go50
-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/genesis.go2
-rw-r--r--core/state_transition.go88
-rw-r--r--core/tx_pool.go2
-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)42
-rw-r--r--core/vm/intpool.go (renamed from core/vm/virtual_machine.go)35
-rw-r--r--core/vm/jump_table.go7
-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.go4
-rw-r--r--eth/api_backend.go4
-rw-r--r--eth/bind.go4
-rw-r--r--eth/downloader/downloader.go12
-rw-r--r--eth/downloader/downloader_test.go2
-rw-r--r--eth/fetcher/fetcher_test.go2
-rw-r--r--eth/filters/filter_system.go4
-rw-r--r--eth/handler.go4
-rw-r--r--eth/handler_test.go14
-rw-r--r--event/event.go60
-rw-r--r--event/event_test.go30
-rw-r--r--event/example_feed_test.go73
-rw-r--r--event/example_scope_test.go128
-rw-r--r--event/example_subscription_test.go56
-rw-r--r--event/feed.go243
-rw-r--r--event/feed_test.go294
-rw-r--r--event/subscription.go275
-rw-r--r--event/subscription_test.go121
-rw-r--r--internal/ethapi/api.go74
-rw-r--r--internal/ethapi/backend.go2
-rw-r--r--internal/ethapi/tracer_test.go4
-rw-r--r--internal/jsre/deps/bindata.go4
-rw-r--r--les/api_backend.go4
-rw-r--r--les/helper_test.go8
-rw-r--r--light/odr_test.go8
-rw-r--r--light/txpool.go2
-rw-r--r--light/txpool_test.go2
-rw-r--r--miner/miner.go2
-rw-r--r--miner/worker.go2
-rw-r--r--params/gas_table.go68
-rw-r--r--params/protocol_params.go100
-rw-r--r--rpc/client.go5
-rw-r--r--swarm/storage/dbstore.go2
-rw-r--r--swarm/storage/dbstore_test.go6
-rw-r--r--swarm/storage/dpa.go5
-rw-r--r--swarm/storage/localstore.go5
-rw-r--r--swarm/storage/memstore.go5
-rw-r--r--swarm/storage/netstore.go5
-rw-r--r--swarm/storage/types.go1
-rw-r--r--swarm/swarm.go16
-rw-r--r--tests/block_test_util.go2
-rw-r--r--tests/state_test_util.go2
-rw-r--r--tests/vm_test_util.go6
73 files changed, 2334 insertions, 845 deletions
diff --git a/accounts/abi/type_test.go b/accounts/abi/type_test.go
index 1557c2a41..155806459 100644
--- a/accounts/abi/type_test.go
+++ b/accounts/abi/type_test.go
@@ -55,8 +55,8 @@ func TestTypeRegexp(t *testing.T) {
{"string[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.String, T: StringTy, Size: -1, Elem: &Type{Kind: reflect.String, T: StringTy, Size: -1, stringKind: "string"}, stringKind: "string[]"}},
{"string[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.String, T: StringTy, Size: -1, Elem: &Type{Kind: reflect.String, T: StringTy, Size: -1, stringKind: "string"}, stringKind: "string[2]"}},
{"address", Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}},
- {"address[]", Type{IsSlice: true, SliceSize: -1,Kind: reflect.Array, Type:address_t, T: AddressTy, Size:20, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
- {"address[2]", Type{IsArray: true, SliceSize: 2,Kind: reflect.Array, Type:address_t, T: AddressTy, Size:20, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
+ {"address[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Array, Type: address_t, T: AddressTy, Size: 20, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
+ {"address[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Array, Type: address_t, T: AddressTy, Size: 20, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
// TODO when fixed types are implemented properly
// {"fixed", Type{}},
diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index 9f67e6628..8af0cebbb 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -156,7 +156,7 @@ func run(ctx *cli.Context) error {
ret, _, err = runtime.Create(input, &runtime.Config{
Origin: sender.Address(),
State: statedb,
- GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)),
+ GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)).Uint64(),
GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
EVMConfig: vm.Config{
@@ -172,7 +172,7 @@ func run(ctx *cli.Context) error {
ret, err = runtime.Call(receiver.Address(), common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtime.Config{
Origin: sender.Address(),
State: statedb,
- GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)),
+ GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)).Uint64(),
GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
EVMConfig: vm.Config{
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index ff9d34b3c..d7e4cc7b5 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -202,7 +202,7 @@ func makeFullNode(ctx *cli.Context) *node.Node {
if err != nil {
glog.V(logger.Warn).Infoln("error setting canonical miner information:", err)
}
- if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
+ if uint64(len(extra)) > params.MaximumExtraDataSize {
glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize)
glog.V(logger.Debug).Infof("extra: %x\n", extra)
extra = nil
diff --git a/common/math/integer.go b/common/math/integer.go
new file mode 100644
index 000000000..8162b1985
--- /dev/null
+++ b/common/math/integer.go
@@ -0,0 +1,25 @@
+package math
+
+import gmath "math"
+
+/*
+ * NOTE: The following methods need to be optimised using either bit checking or asm
+ */
+
+// SafeSub returns subtraction result and whether overflow occurred.
+func SafeSub(x, y uint64) (uint64, bool) {
+ return x - y, x < y
+}
+
+// SafeAdd returns the result and whether overflow occurred.
+func SafeAdd(x, y uint64) (uint64, bool) {
+ return x + y, y > gmath.MaxUint64-x
+}
+
+// SafeMul returns multiplication result and whether overflow occurred.
+func SafeMul(x, y uint64) (uint64, bool) {
+ if x == 0 {
+ return 0, false
+ }
+ return x * y, x != 0 && y != 0 && y > gmath.MaxUint64/x
+}
diff --git a/common/math/integer_test.go b/common/math/integer_test.go
new file mode 100644
index 000000000..198114e5e
--- /dev/null
+++ b/common/math/integer_test.go
@@ -0,0 +1,50 @@
+package math
+
+import (
+ gmath "math"
+ "testing"
+)
+
+type operation byte
+
+const (
+ sub operation = iota
+ add
+ mul
+)
+
+func TestOverflow(t *testing.T) {
+ for i, test := range []struct {
+ x uint64
+ y uint64
+ overflow bool
+ op operation
+ }{
+ // add operations
+ {gmath.MaxUint64, 1, true, add},
+ {gmath.MaxUint64 - 1, 1, false, add},
+
+ // sub operations
+ {0, 1, true, sub},
+ {0, 0, false, sub},
+
+ // mul operations
+ {10, 10, false, mul},
+ {gmath.MaxUint64, 2, true, mul},
+ {gmath.MaxUint64, 1, false, mul},
+ } {
+ var overflows bool
+ switch test.op {
+ case sub:
+ _, overflows = SafeSub(test.x, test.y)
+ case add:
+ _, overflows = SafeAdd(test.x, test.y)
+ case mul:
+ _, overflows = SafeMul(test.x, test.y)
+ }
+
+ if test.overflow != overflows {
+ t.Errorf("%d failed. Expected test to be %v, got %v", i, test.overflow, overflows)
+ }
+ }
+}
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 8f1383acd..cdf9b5cc6 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -719,7 +719,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)
}
@@ -883,8 +883,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
@@ -900,13 +900,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
@@ -925,18 +925,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/genesis.go b/core/genesis.go
index 4a440c267..a06c40408 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -57,6 +57,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
Code string
Storage map[string]string
Balance string
+ Nonce string
}
}
@@ -70,6 +71,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
address := common.HexToAddress(addr)
statedb.AddBalance(address, common.String2Big(account.Balance))
statedb.SetCode(address, common.Hex2Bytes(account.Code))
+ statedb.SetNonce(address, common.String2Big(account.Nonce).Uint64())
for key, value := range account.Storage {
statedb.SetState(address, common.HexToHash(key), common.HexToHash(value))
}
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/tx_pool.go b/core/tx_pool.go
index 58922f12f..ca16c1ba3 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -89,7 +89,7 @@ type TxPool struct {
gasLimit func() *big.Int // The current gas limit function callback
minGasPrice *big.Int
eventMux *event.TypeMux
- events event.Subscription
+ events *event.TypeMuxSubscription
localTx *txSet
signer types.Signer
mu sync.RWMutex
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 fdbc0df7f..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},
- SUICIDE: {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 a5f48750d..ad41e3602 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)
}
}()
@@ -126,7 +133,7 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e
}
// The Interpreter main run loop (contextual). This loop runs until either an
- // explicit STOP, RETURN or SUICIDE is executed, an error accured during
+ // explicit STOP, RETURN or SUICIDE is executed, an error occurred during
// the execution of one of the operations or until the evm.done is set by
// the parent context.Context.
for atomic.LoadInt32(&evm.env.abort) == 0 {
@@ -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 f4ce81883..80e12c10b 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
@@ -431,7 +434,7 @@ func NewJumpTable() [256]operation {
},
STOP: {
execute: opStop,
- gasCost: constGasFunc(Zero),
+ gasCost: constGasFunc(0),
validateStack: makeStackFunc(0, 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 ce4727a71..0936ef06f 100644
--- a/core/vm/stack_table.go
+++ b/core/vm/stack_table.go
@@ -12,8 +12,8 @@ func makeStackFunc(pop, push int) stackValidationFunc {
return err
}
- if push > 0 && int64(stack.len()-pop+push) > params.StackLimit.Int64() {
- return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64())
+ if push > 0 && stack.len()-pop+push > int(params.StackLimit) {
+ return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit)
}
return nil
}
diff --git a/eth/api_backend.go b/eth/api_backend.go
index 1174588ea..72ed76cc4 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -106,14 +106,14 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int {
return b.eth.blockchain.GetTdByHash(blockHash)
}
-func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.EVM, func() error, error) {
+func (b *EthApiBackend) GetEVM(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
statedb := state.(EthApiState).state
from := statedb.GetOrNewStateObject(msg.From())
from.SetBalance(common.MaxBig)
vmError := func() error { return nil }
context := core.NewEVMContext(msg, header, b.eth.BlockChain())
- return vm.NewEVM(context, statedb, b.eth.chainConfig, vm.Config{}), vmError, nil
+ return vm.NewEVM(context, statedb, b.eth.chainConfig, vmCfg), vmError, nil
}
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
diff --git a/eth/bind.go b/eth/bind.go
index a9864b367..2ee9f2bf7 100644
--- a/eth/bind.go
+++ b/eth/bind.go
@@ -69,7 +69,7 @@ func (b *ContractBackend) PendingCodeAt(ctx context.Context, contract common.Add
// against the pending block, not the stable head of the chain.
func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNum *big.Int) ([]byte, error) {
out, err := b.bcapi.Call(ctx, toCallArgs(msg), toBlockNumber(blockNum))
- return common.FromHex(out), err
+ return out, err
}
// ContractCall implements bind.ContractCaller executing an Ethereum contract
@@ -77,7 +77,7 @@ func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg
// against the pending block, not the stable head of the chain.
func (b *ContractBackend) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) {
out, err := b.bcapi.Call(ctx, toCallArgs(msg), rpc.PendingBlockNumber)
- return common.FromHex(out), err
+ return out, err
}
func toCallArgs(msg ethereum.CallMsg) ethapi.CallArgs {
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index 9be4bd87d..7e2952439 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -49,12 +49,12 @@ var (
MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request
MaxStateFetch = 384 // Amount of node state values to allow fetching per request
- MaxForkAncestry = 3 * params.EpochDuration.Uint64() // Maximum chain reorganisation
- rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests
- rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests
- rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value
- ttlScaling = 3 // Constant scaling factor for RTT -> TTL conversion
- ttlLimit = time.Minute // Maximum TTL allowance to prevent reaching crazy timeouts
+ MaxForkAncestry = 3 * params.EpochDuration // Maximum chain reorganisation
+ rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests
+ rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests
+ rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value
+ ttlScaling = 3 // Constant scaling factor for RTT -> TTL conversion
+ ttlLimit = time.Minute // Maximum TTL allowance to prevent reaching crazy timeouts
qosTuningPeers = 5 // Number of peers to tune based on (best peers)
qosConfidenceCap = 10 // Number of peers above which not to modify RTT confidence
diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go
index b156c471d..a9ea797ea 100644
--- a/eth/downloader/downloader_test.go
+++ b/eth/downloader/downloader_test.go
@@ -119,7 +119,7 @@ func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, paren
// If the block number is multiple of 3, send a bonus transaction to the miner
if parent == dl.genesis && i%3 == 0 {
signer := types.MakeSigner(params.TestChainConfig, block.Number())
- tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil), signer, testKey)
+ tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), new(big.Int).SetUint64(params.TxGas), nil, nil), signer, testKey)
if err != nil {
panic(err)
}
diff --git a/eth/fetcher/fetcher_test.go b/eth/fetcher/fetcher_test.go
index 2e28541ab..7a94241c6 100644
--- a/eth/fetcher/fetcher_test.go
+++ b/eth/fetcher/fetcher_test.go
@@ -51,7 +51,7 @@ func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common
// If the block number is multiple of 3, send a bonus transaction to the miner
if parent == genesis && i%3 == 0 {
signer := types.MakeSigner(params.TestChainConfig, block.Number())
- tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil), signer, testKey)
+ tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), new(big.Int).SetUint64(params.TxGas), nil, nil), signer, testKey)
if err != nil {
panic(err)
}
diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go
index e0ee2ff51..3adf8111a 100644
--- a/eth/filters/filter_system.go
+++ b/eth/filters/filter_system.go
@@ -74,7 +74,7 @@ type subscription struct {
// subscription which match the subscription criteria.
type EventSystem struct {
mux *event.TypeMux
- sub event.Subscription
+ sub *event.TypeMuxSubscription
backend Backend
lightMode bool
lastHead *types.Header
@@ -277,7 +277,7 @@ func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscr
type filterIndex map[Type]map[rpc.ID]*subscription
// broadcast event to filters that match criteria.
-func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) {
+func (es *EventSystem) broadcast(filters filterIndex, ev *event.TypeMuxEvent) {
if ev == nil {
return
}
diff --git a/eth/handler.go b/eth/handler.go
index e03c89149..0e7eed352 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -78,8 +78,8 @@ type ProtocolManager struct {
SubProtocols []p2p.Protocol
eventMux *event.TypeMux
- txSub event.Subscription
- minedBlockSub event.Subscription
+ txSub *event.TypeMuxSubscription
+ minedBlockSub *event.TypeMuxSubscription
// channels for fetcher, syncer, txsyncLoop
newPeerCh chan *peer
diff --git a/eth/handler_test.go b/eth/handler_test.go
index 8a5d7173b..03d5404a4 100644
--- a/eth/handler_test.go
+++ b/eth/handler_test.go
@@ -36,6 +36,8 @@ import (
"github.com/ethereum/go-ethereum/params"
)
+var bigTxGas = new(big.Int).SetUint64(params.TxGas)
+
// Tests that protocol versions and modes of operations are matched up properly.
func TestProtocolCompatibility(t *testing.T) {
// Define the compatibility chart
@@ -312,13 +314,13 @@ func testGetNodeData(t *testing.T, protocol int) {
switch i {
case 0:
// In block 1, the test bank sends account #1 some ether.
- tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
+ tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey)
block.AddTx(tx)
case 1:
// In block 2, the test bank sends some more ether to account #1.
// acc1Addr passes it on to account #2.
- tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
- tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
+ tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey)
+ tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key)
block.AddTx(tx1)
block.AddTx(tx2)
case 2:
@@ -404,13 +406,13 @@ func testGetReceipt(t *testing.T, protocol int) {
switch i {
case 0:
// In block 1, the test bank sends account #1 some ether.
- tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
+ tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey)
block.AddTx(tx)
case 1:
// In block 2, the test bank sends some more ether to account #1.
// acc1Addr passes it on to account #2.
- tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
- tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
+ tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey)
+ tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key)
block.AddTx(tx1)
block.AddTx(tx2)
case 2:
diff --git a/event/event.go b/event/event.go
index fd0bcfbd4..d3e84f0f7 100644
--- a/event/event.go
+++ b/event/event.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-// Package event implements an event multiplexer.
+// Package event deals with subscriptions to real-time events.
package event
import (
@@ -25,33 +25,22 @@ import (
"time"
)
-// Event is a time-tagged notification pushed to subscribers.
-type Event struct {
+// TypeMuxEvent is a time-tagged notification pushed to subscribers.
+type TypeMuxEvent struct {
Time time.Time
Data interface{}
}
-// Subscription is implemented by event subscriptions.
-type Subscription interface {
- // Chan returns a channel that carries events.
- // Implementations should return the same channel
- // for any subsequent calls to Chan.
- Chan() <-chan *Event
-
- // Unsubscribe stops delivery of events to a subscription.
- // The event channel is closed.
- // Unsubscribe can be called more than once.
- Unsubscribe()
-}
-
// A TypeMux dispatches events to registered receivers. Receivers can be
// registered to handle events of certain type. Any operation
// called after mux is stopped will return ErrMuxClosed.
//
// The zero value is ready to use.
+//
+// Deprecated: use Feed
type TypeMux struct {
mutex sync.RWMutex
- subm map[reflect.Type][]*muxsub
+ subm map[reflect.Type][]*TypeMuxSubscription
stopped bool
}
@@ -61,7 +50,7 @@ var ErrMuxClosed = errors.New("event: mux closed")
// Subscribe creates a subscription for events of the given types. The
// subscription's channel is closed when it is unsubscribed
// or the mux is closed.
-func (mux *TypeMux) Subscribe(types ...interface{}) Subscription {
+func (mux *TypeMux) Subscribe(types ...interface{}) *TypeMuxSubscription {
sub := newsub(mux)
mux.mutex.Lock()
defer mux.mutex.Unlock()
@@ -72,7 +61,7 @@ func (mux *TypeMux) Subscribe(types ...interface{}) Subscription {
close(sub.postC)
} else {
if mux.subm == nil {
- mux.subm = make(map[reflect.Type][]*muxsub)
+ mux.subm = make(map[reflect.Type][]*TypeMuxSubscription)
}
for _, t := range types {
rtyp := reflect.TypeOf(t)
@@ -80,7 +69,7 @@ func (mux *TypeMux) Subscribe(types ...interface{}) Subscription {
if find(oldsubs, sub) != -1 {
panic(fmt.Sprintf("event: duplicate type %s in Subscribe", rtyp))
}
- subs := make([]*muxsub, len(oldsubs)+1)
+ subs := make([]*TypeMuxSubscription, len(oldsubs)+1)
copy(subs, oldsubs)
subs[len(oldsubs)] = sub
mux.subm[rtyp] = subs
@@ -92,7 +81,7 @@ func (mux *TypeMux) Subscribe(types ...interface{}) Subscription {
// Post sends an event to all receivers registered for the given type.
// It returns ErrMuxClosed if the mux has been stopped.
func (mux *TypeMux) Post(ev interface{}) error {
- event := &Event{
+ event := &TypeMuxEvent{
Time: time.Now(),
Data: ev,
}
@@ -125,7 +114,7 @@ func (mux *TypeMux) Stop() {
mux.mutex.Unlock()
}
-func (mux *TypeMux) del(s *muxsub) {
+func (mux *TypeMux) del(s *TypeMuxSubscription) {
mux.mutex.Lock()
for typ, subs := range mux.subm {
if pos := find(subs, s); pos >= 0 {
@@ -139,7 +128,7 @@ func (mux *TypeMux) del(s *muxsub) {
s.mux.mutex.Unlock()
}
-func find(slice []*muxsub, item *muxsub) int {
+func find(slice []*TypeMuxSubscription, item *TypeMuxSubscription) int {
for i, v := range slice {
if v == item {
return i
@@ -148,14 +137,15 @@ func find(slice []*muxsub, item *muxsub) int {
return -1
}
-func posdelete(slice []*muxsub, pos int) []*muxsub {
- news := make([]*muxsub, len(slice)-1)
+func posdelete(slice []*TypeMuxSubscription, pos int) []*TypeMuxSubscription {
+ news := make([]*TypeMuxSubscription, len(slice)-1)
copy(news[:pos], slice[:pos])
copy(news[pos:], slice[pos+1:])
return news
}
-type muxsub struct {
+// TypeMuxSubscription is a subscription established through TypeMux.
+type TypeMuxSubscription struct {
mux *TypeMux
created time.Time
closeMu sync.Mutex
@@ -166,13 +156,13 @@ type muxsub struct {
// postC can be set to nil without affecting the return value of
// Chan.
postMu sync.RWMutex
- readC <-chan *Event
- postC chan<- *Event
+ readC <-chan *TypeMuxEvent
+ postC chan<- *TypeMuxEvent
}
-func newsub(mux *TypeMux) *muxsub {
- c := make(chan *Event)
- return &muxsub{
+func newsub(mux *TypeMux) *TypeMuxSubscription {
+ c := make(chan *TypeMuxEvent)
+ return &TypeMuxSubscription{
mux: mux,
created: time.Now(),
readC: c,
@@ -181,16 +171,16 @@ func newsub(mux *TypeMux) *muxsub {
}
}
-func (s *muxsub) Chan() <-chan *Event {
+func (s *TypeMuxSubscription) Chan() <-chan *TypeMuxEvent {
return s.readC
}
-func (s *muxsub) Unsubscribe() {
+func (s *TypeMuxSubscription) Unsubscribe() {
s.mux.del(s)
s.closewait()
}
-func (s *muxsub) closewait() {
+func (s *TypeMuxSubscription) closewait() {
s.closeMu.Lock()
defer s.closeMu.Unlock()
if s.closed {
@@ -205,7 +195,7 @@ func (s *muxsub) closewait() {
s.postMu.Unlock()
}
-func (s *muxsub) deliver(event *Event) {
+func (s *TypeMuxSubscription) deliver(event *TypeMuxEvent) {
// Short circuit delivery if stale event
if s.created.After(event.Time) {
return
diff --git a/event/event_test.go b/event/event_test.go
index 2c56ecf29..a12945a47 100644
--- a/event/event_test.go
+++ b/event/event_test.go
@@ -149,16 +149,34 @@ func emptySubscriber(mux *TypeMux, types ...interface{}) {
}()
}
-func BenchmarkPost3(b *testing.B) {
- var mux = new(TypeMux)
- defer mux.Stop()
- emptySubscriber(mux, testEvent(0))
- emptySubscriber(mux, testEvent(0))
- emptySubscriber(mux, testEvent(0))
+func BenchmarkPost1000(b *testing.B) {
+ var (
+ mux = new(TypeMux)
+ subscribed, done sync.WaitGroup
+ nsubs = 1000
+ )
+ subscribed.Add(nsubs)
+ done.Add(nsubs)
+ for i := 0; i < nsubs; i++ {
+ go func() {
+ s := mux.Subscribe(testEvent(0))
+ subscribed.Done()
+ for range s.Chan() {
+ }
+ done.Done()
+ }()
+ }
+ subscribed.Wait()
+ // The actual benchmark.
+ b.ResetTimer()
for i := 0; i < b.N; i++ {
mux.Post(testEvent(0))
}
+
+ b.StopTimer()
+ mux.Stop()
+ done.Wait()
}
func BenchmarkPostConcurrent(b *testing.B) {
diff --git a/event/example_feed_test.go b/event/example_feed_test.go
new file mode 100644
index 000000000..63436b226
--- /dev/null
+++ b/event/example_feed_test.go
@@ -0,0 +1,73 @@
+// 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
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package event_test
+
+import (
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/event"
+)
+
+func ExampleFeed_acknowledgedEvents() {
+ // This example shows how the return value of Send can be used for request/reply
+ // interaction between event consumers and producers.
+ var feed event.Feed
+ type ackedEvent struct {
+ i int
+ ack chan<- struct{}
+ }
+
+ // Consumers wait for events on the feed and acknowledge processing.
+ done := make(chan struct{})
+ defer close(done)
+ for i := 0; i < 3; i++ {
+ ch := make(chan ackedEvent, 100)
+ sub := feed.Subscribe(ch)
+ go func() {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case ev := <-ch:
+ fmt.Println(ev.i) // "process" the event
+ ev.ack <- struct{}{}
+ case <-done:
+ return
+ }
+ }
+ }()
+ }
+
+ // The producer sends values of type ackedEvent with increasing values of i.
+ // It waits for all consumers to acknowledge before sending the next event.
+ for i := 0; i < 3; i++ {
+ acksignal := make(chan struct{})
+ n := feed.Send(ackedEvent{i, acksignal})
+ for ack := 0; ack < n; ack++ {
+ <-acksignal
+ }
+ }
+ // Output:
+ // 0
+ // 0
+ // 0
+ // 1
+ // 1
+ // 1
+ // 2
+ // 2
+ // 2
+}
diff --git a/event/example_scope_test.go b/event/example_scope_test.go
new file mode 100644
index 000000000..c517a8324
--- /dev/null
+++ b/event/example_scope_test.go
@@ -0,0 +1,128 @@
+// 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
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package event_test
+
+import (
+ "fmt"
+ "sync"
+
+ "github.com/ethereum/go-ethereum/event"
+)
+
+// This example demonstrates how SubscriptionScope can be used to control the lifetime of
+// subscriptions.
+//
+// Our example program consists of two servers, each of which performs a calculation when
+// requested. The servers also allow subscribing to results of all computations.
+type divServer struct{ results event.Feed }
+type mulServer struct{ results event.Feed }
+
+func (s *divServer) do(a, b int) int {
+ r := a / b
+ s.results.Send(r)
+ return r
+}
+
+func (s *mulServer) do(a, b int) int {
+ r := a * b
+ s.results.Send(r)
+ return r
+}
+
+// The servers are contained in an App. The app controls the servers and exposes them
+// through its API.
+type App struct {
+ divServer
+ mulServer
+ scope event.SubscriptionScope
+}
+
+func (s *App) Calc(op byte, a, b int) int {
+ switch op {
+ case '/':
+ return s.divServer.do(a, b)
+ case '*':
+ return s.mulServer.do(a, b)
+ default:
+ panic("invalid op")
+ }
+}
+
+// The app's SubscribeResults method starts sending calculation results to the given
+// channel. Subscriptions created through this method are tied to the lifetime of the App
+// because they are registered in the scope.
+func (s *App) SubscribeResults(op byte, ch chan<- int) event.Subscription {
+ switch op {
+ case '/':
+ return s.scope.Track(s.divServer.results.Subscribe(ch))
+ case '*':
+ return s.scope.Track(s.mulServer.results.Subscribe(ch))
+ default:
+ panic("invalid op")
+ }
+}
+
+// Stop stops the App, closing all subscriptions created through SubscribeResults.
+func (s *App) Stop() {
+ s.scope.Close()
+}
+
+func ExampleSubscriptionScope() {
+ // Create the app.
+ var (
+ app App
+ wg sync.WaitGroup
+ divs = make(chan int)
+ muls = make(chan int)
+ )
+
+ // Run a subscriber in the background.
+ divsub := app.SubscribeResults('/', divs)
+ mulsub := app.SubscribeResults('*', muls)
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ defer fmt.Println("subscriber exited")
+ defer divsub.Unsubscribe()
+ defer mulsub.Unsubscribe()
+ for {
+ select {
+ case result := <-divs:
+ fmt.Println("division happened:", result)
+ case result := <-muls:
+ fmt.Println("multiplication happened:", result)
+ case <-divsub.Err():
+ return
+ case <-mulsub.Err():
+ return
+ }
+ }
+ }()
+
+ // Interact with the app.
+ app.Calc('/', 22, 11)
+ app.Calc('*', 3, 4)
+
+ // Stop the app. This shuts down the subscriptions, causing the subscriber to exit.
+ app.Stop()
+ wg.Wait()
+
+ // Output:
+ // division happened: 2
+ // multiplication happened: 12
+ // subscriber exited
+}
diff --git a/event/example_subscription_test.go b/event/example_subscription_test.go
new file mode 100644
index 000000000..de1126689
--- /dev/null
+++ b/event/example_subscription_test.go
@@ -0,0 +1,56 @@
+// 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
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package event_test
+
+import (
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/event"
+)
+
+func ExampleNewSubscription() {
+ // Create a subscription that sends 10 integers on ch.
+ ch := make(chan int)
+ sub := event.NewSubscription(func(quit <-chan struct{}) error {
+ for i := 0; i < 10; i++ {
+ select {
+ case ch <- i:
+ case <-quit:
+ fmt.Println("unsubscribed")
+ return nil
+ }
+ }
+ return nil
+ })
+
+ // This is the consumer. It reads 5 integers, then aborts the subscription.
+ // Note that Unsubscribe waits until the producer has shut down.
+ for i := range ch {
+ fmt.Println(i)
+ if i == 4 {
+ sub.Unsubscribe()
+ break
+ }
+ }
+ // Output:
+ // 0
+ // 1
+ // 2
+ // 3
+ // 4
+ // unsubscribed
+}
diff --git a/event/feed.go b/event/feed.go
new file mode 100644
index 000000000..b1b597f17
--- /dev/null
+++ b/event/feed.go
@@ -0,0 +1,243 @@
+// Copyright 2016 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
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package event
+
+import (
+ "errors"
+ "reflect"
+ "sync"
+)
+
+var errBadChannel = errors.New("event: Subscribe argument does not have sendable channel type")
+
+// Feed implements one-to-many subscriptions where the carrier of events is a channel.
+// Values sent to a Feed are delivered to all subscribed channels simultaneously.
+//
+// Feeds can only be used with a single type. The type is determined by the first Send or
+// Subscribe operation. Subsequent calls to these methods panic if the type does not
+// match.
+//
+// The zero value is ready to use.
+type Feed struct {
+ once sync.Once // ensures that init only runs once
+ sendLock chan struct{} // sendLock has a one-element buffer and is empty when held.It protects sendCases.
+ removeSub chan interface{} // interrupts Send
+ sendCases caseList // the active set of select cases used by Send
+
+ // The inbox holds newly subscribed channels until they are added to sendCases.
+ mu sync.Mutex
+ inbox caseList
+ etype reflect.Type
+ closed bool
+}
+
+// This is the index of the first actual subscription channel in sendCases.
+// sendCases[0] is a SelectRecv case for the removeSub channel.
+const firstSubSendCase = 1
+
+type feedTypeError struct {
+ got, want reflect.Type
+ op string
+}
+
+func (e feedTypeError) Error() string {
+ return "event: wrong type in " + e.op + " got " + e.got.String() + ", want " + e.want.String()
+}
+
+func (f *Feed) init() {
+ f.removeSub = make(chan interface{})
+ f.sendLock = make(chan struct{}, 1)
+ f.sendLock <- struct{}{}
+ f.sendCases = caseList{{Chan: reflect.ValueOf(f.removeSub), Dir: reflect.SelectRecv}}
+}
+
+// Subscribe adds a channel to the feed. Future sends will be delivered on the channel
+// until the subscription is canceled. All channels added must have the same element type.
+//
+// The channel should have ample buffer space to avoid blocking other subscribers.
+// Slow subscribers are not dropped.
+func (f *Feed) Subscribe(channel interface{}) Subscription {
+ f.once.Do(f.init)
+
+ chanval := reflect.ValueOf(channel)
+ chantyp := chanval.Type()
+ if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.SendDir == 0 {
+ panic(errBadChannel)
+ }
+ sub := &feedSub{feed: f, channel: chanval, err: make(chan error, 1)}
+
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ if !f.typecheck(chantyp.Elem()) {
+ panic(feedTypeError{op: "Subscribe", got: chantyp, want: reflect.ChanOf(reflect.SendDir, f.etype)})
+ }
+ // Add the select case to the inbox.
+ // The next Send will add it to f.sendCases.
+ cas := reflect.SelectCase{Dir: reflect.SelectSend, Chan: chanval}
+ f.inbox = append(f.inbox, cas)
+ return sub
+}
+
+// note: callers must hold f.mu
+func (f *Feed) typecheck(typ reflect.Type) bool {
+ if f.etype == nil {
+ f.etype = typ
+ return true
+ }
+ return f.etype == typ
+}
+
+func (f *Feed) remove(sub *feedSub) {
+ // Delete from inbox first, which covers channels
+ // that have not been added to f.sendCases yet.
+ ch := sub.channel.Interface()
+ f.mu.Lock()
+ index := f.inbox.find(ch)
+ if index != -1 {
+ f.inbox = f.inbox.delete(index)
+ f.mu.Unlock()
+ return
+ }
+ f.mu.Unlock()
+
+ select {
+ case f.removeSub <- ch:
+ // Send will remove the channel from f.sendCases.
+ case <-f.sendLock:
+ // No Send is in progress, delete the channel now that we have the send lock.
+ f.sendCases = f.sendCases.delete(f.sendCases.find(ch))
+ f.sendLock <- struct{}{}
+ }
+}
+
+// Send delivers to all subscribed channels simultaneously.
+// It returns the number of subscribers that the value was sent to.
+func (f *Feed) Send(value interface{}) (nsent int) {
+ f.once.Do(f.init)
+ <-f.sendLock
+
+ // Add new cases from the inbox after taking the send lock.
+ f.mu.Lock()
+ f.sendCases = append(f.sendCases, f.inbox...)
+ f.inbox = nil
+ f.mu.Unlock()
+
+ // Set the sent value on all channels.
+ rvalue := reflect.ValueOf(value)
+ if !f.typecheck(rvalue.Type()) {
+ f.sendLock <- struct{}{}
+ panic(feedTypeError{op: "Send", got: rvalue.Type(), want: f.etype})
+ }
+ for i := firstSubSendCase; i < len(f.sendCases); i++ {
+ f.sendCases[i].Send = rvalue
+ }
+
+ // Send until all channels except removeSub have been chosen.
+ cases := f.sendCases
+ for {
+ // Fast path: try sending without blocking before adding to the select set.
+ // This should usually succeed if subscribers are fast enough and have free
+ // buffer space.
+ for i := firstSubSendCase; i < len(cases); i++ {
+ if cases[i].Chan.TrySend(rvalue) {
+ nsent++
+ cases = cases.deactivate(i)
+ i--
+ }
+ }
+ if len(cases) == firstSubSendCase {
+ break
+ }
+ // Select on all the receivers, waiting for them to unblock.
+ chosen, recv, _ := reflect.Select(cases)
+ if chosen == 0 /* <-f.removeSub */ {
+ index := f.sendCases.find(recv.Interface())
+ f.sendCases = f.sendCases.delete(index)
+ if index >= 0 && index < len(cases) {
+ cases = f.sendCases[:len(cases)-1]
+ }
+ } else {
+ cases = cases.deactivate(chosen)
+ nsent++
+ }
+ }
+
+ // Forget about the sent value and hand off the send lock.
+ for i := firstSubSendCase; i < len(f.sendCases); i++ {
+ f.sendCases[i].Send = reflect.Value{}
+ }
+ f.sendLock <- struct{}{}
+ return nsent
+}
+
+type feedSub struct {
+ feed *Feed
+ channel reflect.Value
+ errOnce sync.Once
+ err chan error
+}
+
+func (sub *feedSub) Unsubscribe() {
+ sub.errOnce.Do(func() {
+ sub.feed.remove(sub)
+ close(sub.err)
+ })
+}
+
+func (sub *feedSub) Err() <-chan error {
+ return sub.err
+}
+
+type caseList []reflect.SelectCase
+
+// find returns the index of a case containing the given channel.
+func (cs caseList) find(channel interface{}) int {
+ for i, cas := range cs {
+ if cas.Chan.Interface() == channel {
+ return i
+ }
+ }
+ return -1
+}
+
+// delete removes the given case from cs.
+func (cs caseList) delete(index int) caseList {
+ return append(cs[:index], cs[index+1:]...)
+}
+
+// deactivate moves the case at index into the non-accessible portion of the cs slice.
+func (cs caseList) deactivate(index int) caseList {
+ last := len(cs) - 1
+ cs[index], cs[last] = cs[last], cs[index]
+ return cs[:last]
+}
+
+// func (cs caseList) String() string {
+// s := "["
+// for i, cas := range cs {
+// if i != 0 {
+// s += ", "
+// }
+// switch cas.Dir {
+// case reflect.SelectSend:
+// s += fmt.Sprintf("%v<-", cas.Chan.Interface())
+// case reflect.SelectRecv:
+// s += fmt.Sprintf("<-%v", cas.Chan.Interface())
+// }
+// }
+// return s + "]"
+// }
diff --git a/event/feed_test.go b/event/feed_test.go
new file mode 100644
index 000000000..a82c10303
--- /dev/null
+++ b/event/feed_test.go
@@ -0,0 +1,294 @@
+// Copyright 2016 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
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package event
+
+import (
+ "fmt"
+ "reflect"
+ "sync"
+ "testing"
+ "time"
+)
+
+func TestFeedPanics(t *testing.T) {
+ {
+ var f Feed
+ f.Send(int(2))
+ want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(int(0))}
+ if err := checkPanic(want, func() { f.Send(uint64(2)) }); err != nil {
+ t.Error(err)
+ }
+ }
+ {
+ var f Feed
+ ch := make(chan int)
+ f.Subscribe(ch)
+ want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(int(0))}
+ if err := checkPanic(want, func() { f.Send(uint64(2)) }); err != nil {
+ t.Error(err)
+ }
+ }
+ {
+ var f Feed
+ f.Send(int(2))
+ want := feedTypeError{op: "Subscribe", got: reflect.TypeOf(make(chan uint64)), want: reflect.TypeOf(make(chan<- int))}
+ if err := checkPanic(want, func() { f.Subscribe(make(chan uint64)) }); err != nil {
+ t.Error(err)
+ }
+ }
+ {
+ var f Feed
+ if err := checkPanic(errBadChannel, func() { f.Subscribe(make(<-chan int)) }); err != nil {
+ t.Error(err)
+ }
+ }
+ {
+ var f Feed
+ if err := checkPanic(errBadChannel, func() { f.Subscribe(int(0)) }); err != nil {
+ t.Error(err)
+ }
+ }
+}
+
+func checkPanic(want error, fn func()) (err error) {
+ defer func() {
+ panic := recover()
+ if panic == nil {
+ err = fmt.Errorf("didn't panic")
+ } else if !reflect.DeepEqual(panic, want) {
+ err = fmt.Errorf("panicked with wrong error: got %q, want %q", panic, want)
+ }
+ }()
+ fn()
+ return nil
+}
+
+func TestFeed(t *testing.T) {
+ var feed Feed
+ var done, subscribed sync.WaitGroup
+ subscriber := func(i int) {
+ defer done.Done()
+
+ subchan := make(chan int)
+ sub := feed.Subscribe(subchan)
+ timeout := time.NewTimer(2 * time.Second)
+ subscribed.Done()
+
+ select {
+ case v := <-subchan:
+ if v != 1 {
+ t.Errorf("%d: received value %d, want 1", i, v)
+ }
+ case <-timeout.C:
+ t.Errorf("%d: receive timeout", i)
+ }
+
+ sub.Unsubscribe()
+ select {
+ case _, ok := <-sub.Err():
+ if ok {
+ t.Errorf("%d: error channel not closed after unsubscribe", i)
+ }
+ case <-timeout.C:
+ t.Errorf("%d: unsubscribe timeout", i)
+ }
+ }
+
+ const n = 1000
+ done.Add(n)
+ subscribed.Add(n)
+ for i := 0; i < n; i++ {
+ go subscriber(i)
+ }
+ subscribed.Wait()
+ if nsent := feed.Send(1); nsent != n {
+ t.Errorf("first send delivered %d times, want %d", nsent, n)
+ }
+ if nsent := feed.Send(2); nsent != 0 {
+ t.Errorf("second send delivered %d times, want 0", nsent)
+ }
+ done.Wait()
+}
+
+func TestFeedSubscribeSameChannel(t *testing.T) {
+ var (
+ feed Feed
+ done sync.WaitGroup
+ ch = make(chan int)
+ sub1 = feed.Subscribe(ch)
+ sub2 = feed.Subscribe(ch)
+ _ = feed.Subscribe(ch)
+ )
+ expectSends := func(value, n int) {
+ if nsent := feed.Send(value); nsent != n {
+ t.Errorf("send delivered %d times, want %d", nsent, n)
+ }
+ done.Done()
+ }
+ expectRecv := func(wantValue, n int) {
+ for i := 0; i < n; i++ {
+ if v := <-ch; v != wantValue {
+ t.Errorf("received %d, want %d", v, wantValue)
+ }
+ }
+ }
+
+ done.Add(1)
+ go expectSends(1, 3)
+ expectRecv(1, 3)
+ done.Wait()
+
+ sub1.Unsubscribe()
+
+ done.Add(1)
+ go expectSends(2, 2)
+ expectRecv(2, 2)
+ done.Wait()
+
+ sub2.Unsubscribe()
+
+ done.Add(1)
+ go expectSends(3, 1)
+ expectRecv(3, 1)
+ done.Wait()
+}
+
+func TestFeedSubscribeBlockedPost(t *testing.T) {
+ var (
+ feed Feed
+ nsends = 2000
+ ch1 = make(chan int)
+ ch2 = make(chan int)
+ wg sync.WaitGroup
+ )
+ defer wg.Wait()
+
+ feed.Subscribe(ch1)
+ wg.Add(nsends)
+ for i := 0; i < nsends; i++ {
+ go func() {
+ feed.Send(99)
+ wg.Done()
+ }()
+ }
+
+ sub2 := feed.Subscribe(ch2)
+ defer sub2.Unsubscribe()
+
+ // We're done when ch1 has received N times.
+ // The number of receives on ch2 depends on scheduling.
+ for i := 0; i < nsends; {
+ select {
+ case <-ch1:
+ i++
+ case <-ch2:
+ }
+ }
+}
+
+func TestFeedUnsubscribeBlockedPost(t *testing.T) {
+ var (
+ feed Feed
+ nsends = 200
+ chans = make([]chan int, 2000)
+ subs = make([]Subscription, len(chans))
+ bchan = make(chan int)
+ bsub = feed.Subscribe(bchan)
+ wg sync.WaitGroup
+ )
+ for i := range chans {
+ chans[i] = make(chan int, nsends)
+ }
+
+ // Queue up some Sends. None of these can make progress while bchan isn't read.
+ wg.Add(nsends)
+ for i := 0; i < nsends; i++ {
+ go func() {
+ feed.Send(99)
+ wg.Done()
+ }()
+ }
+ // Subscribe the other channels.
+ for i, ch := range chans {
+ subs[i] = feed.Subscribe(ch)
+ }
+ // Unsubscribe them again.
+ for _, sub := range subs {
+ sub.Unsubscribe()
+ }
+ // Unblock the Sends.
+ bsub.Unsubscribe()
+ wg.Wait()
+}
+
+func TestFeedUnsubscribeFromInbox(t *testing.T) {
+ var (
+ feed Feed
+ ch1 = make(chan int)
+ ch2 = make(chan int)
+ sub1 = feed.Subscribe(ch1)
+ sub2 = feed.Subscribe(ch1)
+ sub3 = feed.Subscribe(ch2)
+ )
+ if len(feed.inbox) != 3 {
+ t.Errorf("inbox length != 3 after subscribe")
+ }
+ if len(feed.sendCases) != 1 {
+ t.Errorf("sendCases is non-empty after unsubscribe")
+ }
+
+ sub1.Unsubscribe()
+ sub2.Unsubscribe()
+ sub3.Unsubscribe()
+ if len(feed.inbox) != 0 {
+ t.Errorf("inbox is non-empty after unsubscribe")
+ }
+ if len(feed.sendCases) != 1 {
+ t.Errorf("sendCases is non-empty after unsubscribe")
+ }
+}
+
+func BenchmarkFeedSend1000(b *testing.B) {
+ var (
+ done sync.WaitGroup
+ feed Feed
+ nsubs = 1000
+ )
+ subscriber := func(ch <-chan int) {
+ for i := 0; i < b.N; i++ {
+ <-ch
+ }
+ done.Done()
+ }
+ done.Add(nsubs)
+ for i := 0; i < nsubs; i++ {
+ ch := make(chan int, 200)
+ feed.Subscribe(ch)
+ go subscriber(ch)
+ }
+
+ // The actual benchmark.
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ if feed.Send(i) != nsubs {
+ panic("wrong number of sends")
+ }
+ }
+
+ b.StopTimer()
+ done.Wait()
+}
diff --git a/event/subscription.go b/event/subscription.go
new file mode 100644
index 000000000..83bd21213
--- /dev/null
+++ b/event/subscription.go
@@ -0,0 +1,275 @@
+// 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
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package event
+
+import (
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common/mclock"
+ "golang.org/x/net/context"
+)
+
+// Subscription represents a stream of events. The carrier of the events is typically a
+// channel, but isn't part of the interface.
+//
+// Subscriptions can fail while established. Failures are reported through an error
+// channel. It receives a value if there is an issue with the subscription (e.g. the
+// network connection delivering the events has been closed). Only one value will ever be
+// sent.
+//
+// The error channel is closed when the subscription ends successfully (i.e. when the
+// source of events is closed). It is also closed when Unsubscribe is called.
+//
+// The Unsubscribe method cancels the sending of events. You must call Unsubscribe in all
+// cases to ensure that resources related to the subscription are released. It can be
+// called any number of times.
+type Subscription interface {
+ Err() <-chan error // returns the error channel
+ Unsubscribe() // cancels sending of events, closing the error channel
+}
+
+// NewSubscription runs a producer function as a subscription in a new goroutine. The
+// channel given to the producer is closed when Unsubscribe is called. If fn returns an
+// error, it is sent on the subscription's error channel.
+func NewSubscription(producer func(<-chan struct{}) error) Subscription {
+ s := &funcSub{unsub: make(chan struct{}), err: make(chan error, 1)}
+ go func() {
+ defer close(s.err)
+ err := producer(s.unsub)
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if !s.unsubscribed {
+ if err != nil {
+ s.err <- err
+ }
+ s.unsubscribed = true
+ }
+ }()
+ return s
+}
+
+type funcSub struct {
+ unsub chan struct{}
+ err chan error
+ mu sync.Mutex
+ unsubscribed bool
+}
+
+func (s *funcSub) Unsubscribe() {
+ s.mu.Lock()
+ if s.unsubscribed {
+ s.mu.Unlock()
+ return
+ }
+ s.unsubscribed = true
+ close(s.unsub)
+ s.mu.Unlock()
+ // Wait for producer shutdown.
+ <-s.err
+}
+
+func (s *funcSub) Err() <-chan error {
+ return s.err
+}
+
+// Resubscribe calls fn repeatedly to keep a subscription established. When the
+// subscription is established, Resubscribe waits for it to fail and calls fn again. This
+// process repeats until Unsubscribe is called or the active subscription ends
+// successfully.
+//
+// Resubscribe applies backoff between calls to fn. The time between calls is adapted
+// based on the error rate, but will never exceed backoffMax.
+func Resubscribe(backoffMax time.Duration, fn ResubscribeFunc) Subscription {
+ s := &resubscribeSub{
+ waitTime: backoffMax / 10,
+ backoffMax: backoffMax,
+ fn: fn,
+ err: make(chan error),
+ unsub: make(chan struct{}),
+ }
+ go s.loop()
+ return s
+}
+
+// A ResubscribeFunc attempts to establish a subscription.
+type ResubscribeFunc func(context.Context) (Subscription, error)
+
+type resubscribeSub struct {
+ fn ResubscribeFunc
+ err chan error
+ unsub chan struct{}
+ unsubOnce sync.Once
+ lastTry mclock.AbsTime
+ waitTime, backoffMax time.Duration
+}
+
+func (s *resubscribeSub) Unsubscribe() {
+ s.unsubOnce.Do(func() {
+ s.unsub <- struct{}{}
+ <-s.err
+ })
+}
+
+func (s *resubscribeSub) Err() <-chan error {
+ return s.err
+}
+
+func (s *resubscribeSub) loop() {
+ defer close(s.err)
+ var done bool
+ for !done {
+ sub := s.subscribe()
+ if sub == nil {
+ break
+ }
+ done = s.waitForError(sub)
+ sub.Unsubscribe()
+ }
+}
+
+func (s *resubscribeSub) subscribe() Subscription {
+ subscribed := make(chan error)
+ var sub Subscription
+retry:
+ for {
+ s.lastTry = mclock.Now()
+ ctx, cancel := context.WithCancel(context.Background())
+ go func() {
+ rsub, err := s.fn(ctx)
+ sub = rsub
+ subscribed <- err
+ }()
+ select {
+ case err := <-subscribed:
+ cancel()
+ if err != nil {
+ // Subscribing failed, wait before launching the next try.
+ if s.backoffWait() {
+ return nil
+ }
+ continue retry
+ }
+ if sub == nil {
+ panic("event: ResubscribeFunc returned nil subscription and no error")
+ }
+ return sub
+ case <-s.unsub:
+ cancel()
+ return nil
+ }
+ }
+}
+
+func (s *resubscribeSub) waitForError(sub Subscription) bool {
+ defer sub.Unsubscribe()
+ select {
+ case err := <-sub.Err():
+ return err == nil
+ case <-s.unsub:
+ return true
+ }
+}
+
+func (s *resubscribeSub) backoffWait() bool {
+ if time.Duration(mclock.Now()-s.lastTry) > s.backoffMax {
+ s.waitTime = s.backoffMax / 10
+ } else {
+ s.waitTime *= 2
+ if s.waitTime > s.backoffMax {
+ s.waitTime = s.backoffMax
+ }
+ }
+
+ t := time.NewTimer(s.waitTime)
+ defer t.Stop()
+ select {
+ case <-t.C:
+ return false
+ case <-s.unsub:
+ return true
+ }
+}
+
+// SubscriptionScope provides a facility to unsubscribe multiple subscriptions at once.
+//
+// For code that handle more than one subscription, a scope can be used to conveniently
+// unsubscribe all of them with a single call. The example demonstrates a typical use in a
+// larger program.
+//
+// The zero value is ready to use.
+type SubscriptionScope struct {
+ mu sync.Mutex
+ subs map[*scopeSub]struct{}
+ closed bool
+}
+
+type scopeSub struct {
+ sc *SubscriptionScope
+ s Subscription
+}
+
+// Track starts tracking a subscription. If the scope is closed, Track returns nil. The
+// returned subscription is a wrapper. Unsubscribing the wrapper removes it from the
+// scope.
+func (sc *SubscriptionScope) Track(s Subscription) Subscription {
+ sc.mu.Lock()
+ defer sc.mu.Unlock()
+ if sc.closed {
+ return nil
+ }
+ if sc.subs == nil {
+ sc.subs = make(map[*scopeSub]struct{})
+ }
+ ss := &scopeSub{sc, s}
+ sc.subs[ss] = struct{}{}
+ return ss
+}
+
+// Close calls Unsubscribe on all tracked subscriptions and prevents further additions to
+// the tracked set. Calls to Track after Close return nil.
+func (sc *SubscriptionScope) Close() {
+ sc.mu.Lock()
+ defer sc.mu.Unlock()
+ if sc.closed {
+ return
+ }
+ sc.closed = true
+ for s := range sc.subs {
+ s.s.Unsubscribe()
+ }
+ sc.subs = nil
+}
+
+// Count returns the number of tracked subscriptions.
+// It is meant to be used for debugging.
+func (sc *SubscriptionScope) Count() int {
+ sc.mu.Lock()
+ defer sc.mu.Unlock()
+ return len(sc.subs)
+}
+
+func (s *scopeSub) Unsubscribe() {
+ s.s.Unsubscribe()
+ s.sc.mu.Lock()
+ defer s.sc.mu.Unlock()
+ delete(s.sc.subs, s)
+}
+
+func (s *scopeSub) Err() <-chan error {
+ return s.s.Err()
+}
diff --git a/event/subscription_test.go b/event/subscription_test.go
new file mode 100644
index 000000000..a4fe30298
--- /dev/null
+++ b/event/subscription_test.go
@@ -0,0 +1,121 @@
+// 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
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package event
+
+import (
+ "errors"
+ "testing"
+ "time"
+
+ "golang.org/x/net/context"
+)
+
+var errInts = errors.New("error in subscribeInts")
+
+func subscribeInts(max, fail int, c chan<- int) Subscription {
+ return NewSubscription(func(quit <-chan struct{}) error {
+ for i := 0; i < max; i++ {
+ if i >= fail {
+ return errInts
+ }
+ select {
+ case c <- i:
+ case <-quit:
+ return nil
+ }
+ }
+ return nil
+ })
+}
+
+func TestNewSubscriptionError(t *testing.T) {
+ t.Parallel()
+
+ channel := make(chan int)
+ sub := subscribeInts(10, 2, channel)
+loop:
+ for want := 0; want < 10; want++ {
+ select {
+ case got := <-channel:
+ if got != want {
+ t.Fatalf("wrong int %d, want %d", got, want)
+ }
+ case err := <-sub.Err():
+ if err != errInts {
+ t.Fatalf("wrong error: got %q, want %q", err, errInts)
+ }
+ if want != 2 {
+ t.Fatalf("got errInts at int %d, should be received at 2", want)
+ }
+ break loop
+ }
+ }
+ sub.Unsubscribe()
+
+ err, ok := <-sub.Err()
+ if err != nil {
+ t.Fatal("got non-nil error after Unsubscribe")
+ }
+ if ok {
+ t.Fatal("channel still open after Unsubscribe")
+ }
+}
+
+func TestResubscribe(t *testing.T) {
+ t.Parallel()
+
+ var i int
+ nfails := 6
+ sub := Resubscribe(100*time.Millisecond, func(ctx context.Context) (Subscription, error) {
+ // fmt.Printf("call #%d @ %v\n", i, time.Now())
+ i++
+ if i == 2 {
+ // Delay the second failure a bit to reset the resubscribe interval.
+ time.Sleep(200 * time.Millisecond)
+ }
+ if i < nfails {
+ return nil, errors.New("oops")
+ }
+ sub := NewSubscription(func(unsubscribed <-chan struct{}) error { return nil })
+ return sub, nil
+ })
+
+ <-sub.Err()
+ if i != nfails {
+ t.Fatalf("resubscribe function called %d times, want %d times", i, nfails)
+ }
+}
+
+func TestResubscribeAbort(t *testing.T) {
+ t.Parallel()
+
+ done := make(chan error)
+ sub := Resubscribe(0, func(ctx context.Context) (Subscription, error) {
+ select {
+ case <-ctx.Done():
+ done <- nil
+ case <-time.After(2 * time.Second):
+ done <- errors.New("context given to resubscribe function not canceled within 2s")
+ }
+ return nil, nil
+ })
+
+ sub.Unsubscribe()
+ if err := <-done; err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 925f547b6..4c8e784c5 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -47,6 +47,8 @@ import (
const defaultGas = 90000
+var emptyHex = "0x"
+
// PublicEthereumAPI provides an API to access Ethereum related information.
// It offers only methods that operate on public data that is freely available to anyone.
type PublicEthereumAPI struct {
@@ -503,58 +505,76 @@ type CallArgs struct {
Data hexutil.Bytes `json:"data"`
}
-func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) {
+func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, *big.Int, error) {
defer func(start time.Time) { glog.V(logger.Debug).Infof("call took %v", time.Since(start)) }(time.Now())
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
if state == nil || err != nil {
- return "0x", common.Big0, err
+ return nil, common.Big0, err
}
- // Set the account address to interact with
- var addr common.Address
- if args.From == (common.Address{}) {
+ // Set sender address or use a default if none specified
+ addr := args.From
+ if addr == (common.Address{}) {
accounts := s.b.AccountManager().Accounts()
- if len(accounts) == 0 {
- addr = common.Address{}
- } else {
+ if len(accounts) > 0 {
addr = accounts[0].Address
}
- } else {
- addr = args.From
}
- // Assemble the CALL invocation
+ // Set default gas & gas price if none were set
gas, gasPrice := args.Gas.ToInt(), args.GasPrice.ToInt()
- if gas.Cmp(common.Big0) == 0 {
+ if gas.BitLen() == 0 {
gas = big.NewInt(50000000)
}
- if gasPrice.Cmp(common.Big0) == 0 {
+ if gasPrice.BitLen() == 0 {
gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
}
+
+ // Create new call message
msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false)
- // Execute the call and return
- vmenv, vmError, err := s.b.GetVMEnv(ctx, msg, state, header)
- if err != nil {
- return "0x", common.Big0, err
+ // Setup context so it may be cancelled the call has completed
+ // or, in case of unmetered gas, setup a context with a timeout.
+ var cancel context.CancelFunc
+ if vmCfg.DisableGasMetering {
+ ctx, cancel = context.WithTimeout(ctx, time.Second*5)
+ } else {
+ ctx, cancel = context.WithCancel(ctx)
}
+ // Make sure the context is cancelled when the call has completed
+ // this makes sure resources are cleaned up.
+ defer func() { cancel() }()
+
+ // Get a new instance of the EVM.
+ evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg)
+ if err != nil {
+ return nil, common.Big0, err
+ }
+ // Wait for the context to be done and cancel the evm. Even if the
+ // EVM has finished, cancelling may be done (repeatedly)
+ go func() {
+ select {
+ case <-ctx.Done():
+ evm.Cancel()
+ }
+ }()
+
+ // Setup the gas pool (also for unmetered requests)
+ // and apply the message.
gp := new(core.GasPool).AddGas(common.MaxBig)
- res, gas, err := core.ApplyMessage(vmenv, msg, gp)
+ res, gas, err := core.ApplyMessage(evm, msg, gp)
if err := vmError(); err != nil {
- return "0x", common.Big0, err
- }
- if len(res) == 0 { // backwards compatibility
- return "0x", gas, err
+ return nil, common.Big0, err
}
- return common.ToHex(res), gas, err
+ return res, gas, err
}
// Call executes the given transaction on the state for the given block number.
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
-func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, error) {
- result, _, err := s.doCall(ctx, args, blockNr)
- return result, err
+func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
+ result, _, err := s.doCall(ctx, args, blockNr, vm.Config{DisableGasMetering: true})
+ return (hexutil.Bytes)(result), err
}
// EstimateGas returns an estimate of the amount of gas needed to execute the given transaction.
@@ -576,7 +596,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*
mid := (hi + lo) / 2
(*big.Int)(&args.Gas).SetUint64(mid)
- _, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber)
+ _, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{})
// If the transaction became invalid or used all the gas (failed), raise the gas limit
if err != nil || gas.Cmp((*big.Int)(&args.Gas)) == 0 {
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index ebb14a5b5..214214f51 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -51,7 +51,7 @@ type Backend interface {
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error)
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
GetTd(blockHash common.Hash) *big.Int
- GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (*vm.EVM, func() error, error)
+ GetEVM(ctx context.Context, msg core.Message, state State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error)
// TxPool API
SendTx(ctx context.Context, signedTx *types.Transaction) error
RemoveTx(txHash common.Hash)
diff --git a/internal/ethapi/tracer_test.go b/internal/ethapi/tracer_test.go
index 65a23f55e..693afe802 100644
--- a/internal/ethapi/tracer_test.go
+++ b/internal/ethapi/tracer_test.go
@@ -45,7 +45,7 @@ func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
func runTrace(tracer *JavascriptTracer) (interface{}, error) {
env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
- contract := vm.NewContract(account{}, account{}, big.NewInt(0), big.NewInt(10000))
+ contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
_, err := env.Interpreter().Run(contract, []byte{})
@@ -134,7 +134,7 @@ func TestHaltBetweenSteps(t *testing.T) {
}
env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
- contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), big.NewInt(0))
+ contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0)
tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil)
timeout := errors.New("stahp")
diff --git a/internal/jsre/deps/bindata.go b/internal/jsre/deps/bindata.go
index 73732a4eb..5f6a2b873 100644
--- a/internal/jsre/deps/bindata.go
+++ b/internal/jsre/deps/bindata.go
@@ -206,8 +206,8 @@ type bintree struct {
}
var _bintree = &bintree{nil, map[string]*bintree{
- "bignumber.js": &bintree{bignumberJs, map[string]*bintree{}},
- "web3.js": &bintree{web3Js, map[string]*bintree{}},
+ "bignumber.js": {bignumberJs, map[string]*bintree{}},
+ "web3.js": {web3Js, map[string]*bintree{}},
}}
// RestoreAsset restores an asset under the given directory
diff --git a/les/api_backend.go b/les/api_backend.go
index 3a71ac4e0..ed2a7cd13 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -88,7 +88,7 @@ func (b *LesApiBackend) GetTd(blockHash common.Hash) *big.Int {
return b.eth.blockchain.GetTdByHash(blockHash)
}
-func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.EVM, func() error, error) {
+func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
stateDb := state.(*light.LightState).Copy()
addr := msg.From()
from, err := stateDb.GetOrNewStateObject(ctx, addr)
@@ -99,7 +99,7 @@ func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state et
vmstate := light.NewVMState(ctx, stateDb)
context := core.NewEVMContext(msg, header, b.eth.blockchain)
- return vm.NewEVM(context, vmstate, b.eth.chainConfig, vm.Config{}), vmstate.Error, nil
+ return vm.NewEVM(context, vmstate, b.eth.chainConfig, vmCfg), vmstate.Error, nil
}
func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
diff --git a/les/helper_test.go b/les/helper_test.go
index e0b7558ee..2c6f34a92 100644
--- a/les/helper_test.go
+++ b/les/helper_test.go
@@ -57,6 +57,8 @@ var (
testContractDeployed = uint64(2)
testBufLimit = uint64(100)
+
+ bigTxGas = new(big.Int).SetUint64(params.TxGas)
)
/*
@@ -80,15 +82,15 @@ func testChainGen(i int, block *core.BlockGen) {
switch i {
case 0:
// In block 1, the test bank sends account #1 some ether.
- tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
+ tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey)
block.AddTx(tx)
case 1:
// In block 2, the test bank sends some more ether to account #1.
// acc1Addr passes it on to account #2.
// acc1Addr creates a test contract.
- tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
+ tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey)
nonce := block.TxNonce(acc1Addr)
- tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
+ tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key)
nonce++
tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), big.NewInt(200000), big.NewInt(0), testContractCode), signer, acc1Key)
testContractAddr = crypto.CreateAddress(acc1Addr, nonce)
diff --git a/light/odr_test.go b/light/odr_test.go
index a2f969acb..a76050a29 100644
--- a/light/odr_test.go
+++ b/light/odr_test.go
@@ -49,6 +49,8 @@ var (
testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056")
testContractAddr common.Address
+
+ bigTxGas = new(big.Int).SetUint64(params.TxGas)
)
type testOdr struct {
@@ -205,15 +207,15 @@ func testChainGen(i int, block *core.BlockGen) {
switch i {
case 0:
// In block 1, the test bank sends account #1 some ether.
- tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
+ tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey)
block.AddTx(tx)
case 1:
// In block 2, the test bank sends some more ether to account #1.
// acc1Addr passes it on to account #2.
// acc1Addr creates a test contract.
- tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
+ tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey)
nonce := block.TxNonce(acc1Addr)
- tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
+ tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key)
nonce++
tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), big.NewInt(1000000), big.NewInt(0), testContractCode), signer, acc1Key)
testContractAddr = crypto.CreateAddress(acc1Addr, nonce)
diff --git a/light/txpool.go b/light/txpool.go
index d0781593b..bcdb6123d 100644
--- a/light/txpool.go
+++ b/light/txpool.go
@@ -47,7 +47,7 @@ type TxPool struct {
signer types.Signer
quit chan bool
eventMux *event.TypeMux
- events event.Subscription
+ events *event.TypeMuxSubscription
mu sync.RWMutex
chain *LightChain
odr OdrBackend
diff --git a/light/txpool_test.go b/light/txpool_test.go
index d8f04e617..af2dcbbef 100644
--- a/light/txpool_test.go
+++ b/light/txpool_test.go
@@ -77,7 +77,7 @@ func txPoolTestChainGen(i int, block *core.BlockGen) {
func TestTxPool(t *testing.T) {
for i := range testTx {
- testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey)
+ testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), types.HomesteadSigner{}, testBankKey)
}
var (
diff --git a/miner/miner.go b/miner/miner.go
index 61cd3e049..83059f4b1 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -171,7 +171,7 @@ func (self *Miner) HashRate() (tot int64) {
}
func (self *Miner) SetExtra(extra []byte) error {
- if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
+ if uint64(len(extra)) > params.MaximumExtraDataSize {
return fmt.Errorf("Extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize)
}
self.worker.setExtra(extra)
diff --git a/miner/worker.go b/miner/worker.go
index 77e4e0205..49ac60253 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -90,7 +90,7 @@ type worker struct {
// update loop
mux *event.TypeMux
- events event.Subscription
+ events *event.TypeMuxSubscription
wg sync.WaitGroup
agents map[Agent]struct{}
diff --git a/params/gas_table.go b/params/gas_table.go
index 093dacc8b..a06053904 100644
--- a/params/gas_table.go
+++ b/params/gas_table.go
@@ -16,41 +16,35 @@
package params
-import "math/big"
-
type GasTable struct {
- ExtcodeSize *big.Int
- ExtcodeCopy *big.Int
- Balance *big.Int
- SLoad *big.Int
- Calls *big.Int
- Suicide *big.Int
+ ExtcodeSize uint64
+ ExtcodeCopy uint64
+ Balance uint64
+ SLoad uint64
+ Calls uint64
+ Suicide uint64
- ExpByte *big.Int
+ ExpByte uint64
// CreateBySuicide occurs when the
// refunded account is one that does
// not exist. This logic is similar
// to call. May be left nil. Nil means
// not charged.
- CreateBySuicide *big.Int
+ CreateBySuicide uint64
}
var (
// GasTableHomestead contain the gas prices for
// the homestead phase.
GasTableHomestead = GasTable{
- ExtcodeSize: big.NewInt(20),
- ExtcodeCopy: big.NewInt(20),
- Balance: big.NewInt(20),
- SLoad: big.NewInt(50),
- Calls: big.NewInt(40),
- Suicide: big.NewInt(0),
- ExpByte: big.NewInt(10),
-
- // explicitly set to nil to indicate
- // this rule does not apply to homestead.
- CreateBySuicide: nil,
+ ExtcodeSize: 20,
+ ExtcodeCopy: 20,
+ Balance: 20,
+ SLoad: 50,
+ Calls: 40,
+ Suicide: 0,
+ ExpByte: 10,
}
// GasTableHomestead contain the gas re-prices for
@@ -58,26 +52,26 @@ var (
//
// TODO rename to GasTableEIP150
GasTableHomesteadGasRepriceFork = GasTable{
- ExtcodeSize: big.NewInt(700),
- ExtcodeCopy: big.NewInt(700),
- Balance: big.NewInt(400),
- SLoad: big.NewInt(200),
- Calls: big.NewInt(700),
- Suicide: big.NewInt(5000),
- ExpByte: big.NewInt(10),
+ ExtcodeSize: 700,
+ ExtcodeCopy: 700,
+ Balance: 400,
+ SLoad: 200,
+ Calls: 700,
+ Suicide: 5000,
+ ExpByte: 10,
- CreateBySuicide: big.NewInt(25000),
+ CreateBySuicide: 25000,
}
GasTableEIP158 = GasTable{
- ExtcodeSize: big.NewInt(700),
- ExtcodeCopy: big.NewInt(700),
- Balance: big.NewInt(400),
- SLoad: big.NewInt(200),
- Calls: big.NewInt(700),
- Suicide: big.NewInt(5000),
- ExpByte: big.NewInt(50),
+ ExtcodeSize: 700,
+ ExtcodeCopy: 700,
+ Balance: 400,
+ SLoad: 200,
+ Calls: 700,
+ Suicide: 5000,
+ ExpByte: 50,
- CreateBySuicide: big.NewInt(25000),
+ CreateBySuicide: 25000,
}
)
diff --git a/params/protocol_params.go b/params/protocol_params.go
index f5b6bedeb..f48bf4992 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -18,56 +18,58 @@ package params
import "math/big"
-var (
- MaximumExtraDataSize = big.NewInt(32) // Maximum size extra data may be after Genesis.
- ExpByteGas = big.NewInt(10) // Times ceil(log256(exponent)) for the EXP instruction.
- SloadGas = big.NewInt(50) // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added.
- CallValueTransferGas = big.NewInt(9000) // Paid for CALL when the value transfer is non-zero.
- CallNewAccountGas = big.NewInt(25000) // Paid for CALL when the destination address didn't exist prior.
- TxGas = big.NewInt(21000) // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions.
- TxGasContractCreation = big.NewInt(53000) // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions.
- TxDataZeroGas = big.NewInt(4) // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions.
- DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations.
- QuadCoeffDiv = big.NewInt(512) // Divisor for the quadratic particle of the memory cost equation.
- GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block.
- DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not.
- SstoreSetGas = big.NewInt(20000) // Once per SLOAD operation.
- LogDataGas = big.NewInt(8) // Per byte in a LOG* operation's data.
- CallStipend = big.NewInt(2300) // Free gas given at beginning of call.
- EcrecoverGas = big.NewInt(3000) //
- Sha256WordGas = big.NewInt(12) //
-
- MinGasLimit = big.NewInt(5000) // Minimum the gas limit may ever be.
- GenesisGasLimit = big.NewInt(4712388) // Gas limit of the Genesis block.
- TargetGasLimit = new(big.Int).Set(GenesisGasLimit) // The artificial target
+const (
+ MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis.
+ ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction.
+ SloadGas uint64 = 50 // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added.
+ CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero.
+ CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior.
+ TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions.
+ TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions.
+ TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions.
+ QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation.
+ SstoreSetGas uint64 = 20000 // Once per SLOAD operation.
+ LogDataGas uint64 = 8 // Per byte in a LOG* operation's data.
+ CallStipend uint64 = 2300 // Free gas given at beginning of call.
+ EcrecoverGas uint64 = 3000 //
+ Sha256WordGas uint64 = 12 //
- Sha3Gas = big.NewInt(30) // Once per SHA3 operation.
- Sha256Gas = big.NewInt(60) //
- IdentityWordGas = big.NewInt(3) //
- Sha3WordGas = big.NewInt(6) // Once per word of the SHA3 operation's data.
- SstoreResetGas = big.NewInt(5000) // Once per SSTORE operation if the zeroness changes from zero.
- SstoreClearGas = big.NewInt(5000) // Once per SSTORE operation if the zeroness doesn't change.
- SstoreRefundGas = big.NewInt(15000) // Once per SSTORE operation if the zeroness changes to zero.
- JumpdestGas = big.NewInt(1) // Refunded gas, once per SSTORE operation if the zeroness changes to zero.
- IdentityGas = big.NewInt(15) //
- GasLimitBoundDivisor = big.NewInt(1024) // The bound divisor of the gas limit, used in update calculations.
- EpochDuration = big.NewInt(30000) // Duration between proof-of-work epochs.
- CallGas = big.NewInt(40) // Once per CALL operation & message call transaction.
- CreateDataGas = big.NewInt(200) //
- Ripemd160Gas = big.NewInt(600) //
- Ripemd160WordGas = big.NewInt(120) //
- MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be.
- CallCreateDepth = big.NewInt(1024) // Maximum depth of call/create stack.
- ExpGas = big.NewInt(10) // Once per EXP instruction.
- LogGas = big.NewInt(375) // Per LOG* operation.
- CopyGas = big.NewInt(3) //
- StackLimit = big.NewInt(1024) // Maximum size of VM stack allowed.
- TierStepGas = big.NewInt(0) // Once per operation, for a selection of them.
- LogTopicGas = big.NewInt(375) // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas.
- CreateGas = big.NewInt(32000) // Once per CREATE operation & contract-creation transaction.
- SuicideRefundGas = big.NewInt(24000) // Refunded following a suicide operation.
- MemoryGas = big.NewInt(3) // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
- TxDataNonZeroGas = big.NewInt(68) // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
+ Sha3Gas uint64 = 30 // Once per SHA3 operation.
+ Sha256Gas uint64 = 60 //
+ IdentityWordGas uint64 = 3 //
+ Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data.
+ SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero.
+ SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change.
+ SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero.
+ JumpdestGas uint64 = 1 // Refunded gas, once per SSTORE operation if the zeroness changes to zero.
+ IdentityGas uint64 = 15 //
+ EpochDuration uint64 = 30000 // Duration between proof-of-work epochs.
+ CallGas uint64 = 40 // Once per CALL operation & message call transaction.
+ CreateDataGas uint64 = 200 //
+ Ripemd160Gas uint64 = 600 //
+ Ripemd160WordGas uint64 = 120 //
+ CallCreateDepth uint64 = 1024 // Maximum depth of call/create stack.
+ ExpGas uint64 = 10 // Once per EXP instruction
+ LogGas uint64 = 375 // Per LOG* operation.
+ CopyGas uint64 = 3 //
+ StackLimit uint64 = 1024 // Maximum size of VM stack allowed.
+ TierStepGas uint64 = 0 // Once per operation, for a selection of them.
+ LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas.
+ CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction.
+ SuicideRefundGas uint64 = 24000 // Refunded following a suicide operation.
+ MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
+ TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
MaxCodeSize = 24576
)
+
+var (
+ GasLimitBoundDivisor = big.NewInt(1024) // The bound divisor of the gas limit, used in update calculations.
+ MinGasLimit = big.NewInt(5000) // Minimum the gas limit may ever be.
+ GenesisGasLimit = big.NewInt(4712388) // Gas limit of the Genesis block.
+ TargetGasLimit = new(big.Int).Set(GenesisGasLimit) // The artificial target
+ DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations.
+ GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block.
+ MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be.
+ DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not.
+)
diff --git a/rpc/client.go b/rpc/client.go
index 34a3b7831..269eb78c8 100644
--- a/rpc/client.go
+++ b/rpc/client.go
@@ -682,7 +682,7 @@ func newClientSubscription(c *Client, channel reflect.Value) *ClientSubscription
// resubscription when the client connection is closed unexpectedly.
//
// The error channel receives a value when the subscription has ended due
-// to an error. The received error is ErrClientQuit if Close has been called
+// to an error. The received error is nil if Close has been called
// on the underlying client and no other error has occurred.
//
// The error channel is closed when Unsubscribe is called on the subscription.
@@ -707,6 +707,9 @@ func (sub *ClientSubscription) quitWithError(err error, unsubscribeServer bool)
sub.requestUnsubscribe()
}
if err != nil {
+ if err == ErrClientQuit {
+ err = nil // Adhere to subscription semantics.
+ }
sub.err <- err
}
})
diff --git a/swarm/storage/dbstore.go b/swarm/storage/dbstore.go
index 4ddebb021..f5d124d29 100644
--- a/swarm/storage/dbstore.go
+++ b/swarm/storage/dbstore.go
@@ -408,7 +408,7 @@ func (s *DbStore) getEntryCnt() uint64 {
return s.entryCnt
}
-func (s *DbStore) close() {
+func (s *DbStore) Close() {
s.db.Close()
}
diff --git a/swarm/storage/dbstore_test.go b/swarm/storage/dbstore_test.go
index 3d2b5bc36..ddce7ccfe 100644
--- a/swarm/storage/dbstore_test.go
+++ b/swarm/storage/dbstore_test.go
@@ -38,7 +38,7 @@ func initDbStore(t *testing.T) *DbStore {
func testDbStore(l int64, branches int64, t *testing.T) {
m := initDbStore(t)
- defer m.close()
+ defer m.Close()
testStore(m, l, branches, t)
}
@@ -64,7 +64,7 @@ func TestDbStore2_100_(t *testing.T) {
func TestDbStoreNotFound(t *testing.T) {
m := initDbStore(t)
- defer m.close()
+ defer m.Close()
_, err := m.Get(ZeroKey)
if err != notFound {
t.Errorf("Expected notFound, got %v", err)
@@ -73,7 +73,7 @@ func TestDbStoreNotFound(t *testing.T) {
func TestDbStoreSyncIterator(t *testing.T) {
m := initDbStore(t)
- defer m.close()
+ defer m.Close()
keys := []Key{
Key(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")),
Key(common.Hex2Bytes("4000000000000000000000000000000000000000000000000000000000000000")),
diff --git a/swarm/storage/dpa.go b/swarm/storage/dpa.go
index 31b6c54ac..7b3e23cac 100644
--- a/swarm/storage/dpa.go
+++ b/swarm/storage/dpa.go
@@ -237,3 +237,8 @@ func (self *dpaChunkStore) Put(entry *Chunk) {
self.n++
self.netStore.Put(chunk)
}
+
+// Close chunk store
+func (self *dpaChunkStore) Close() {
+ return
+}
diff --git a/swarm/storage/localstore.go b/swarm/storage/localstore.go
index 541462f0c..14827e361 100644
--- a/swarm/storage/localstore.go
+++ b/swarm/storage/localstore.go
@@ -72,3 +72,8 @@ func (self *LocalStore) Get(key Key) (chunk *Chunk, err error) {
self.memStore.Put(chunk)
return
}
+
+// Close local store
+func (self *LocalStore) Close() {
+ return
+}
diff --git a/swarm/storage/memstore.go b/swarm/storage/memstore.go
index f133bd7d3..e55abb45f 100644
--- a/swarm/storage/memstore.go
+++ b/swarm/storage/memstore.go
@@ -314,3 +314,8 @@ func (s *MemStore) removeOldest() {
}
}
}
+
+// Close memstore
+func (s *MemStore) Close() {
+ return
+}
diff --git a/swarm/storage/netstore.go b/swarm/storage/netstore.go
index f97862bbb..46479b58a 100644
--- a/swarm/storage/netstore.go
+++ b/swarm/storage/netstore.go
@@ -132,3 +132,8 @@ func (self *NetStore) Get(key Key) (*Chunk, error) {
go self.cloud.Retrieve(chunk)
return chunk, nil
}
+
+// Close netstore
+func (self *NetStore) Close() {
+ return
+}
diff --git a/swarm/storage/types.go b/swarm/storage/types.go
index f3ab99c6c..cc5ded931 100644
--- a/swarm/storage/types.go
+++ b/swarm/storage/types.go
@@ -167,6 +167,7 @@ The ChunkStore interface is implemented by :
type ChunkStore interface {
Put(*Chunk) // effectively there is no error even if there is an error
Get(Key) (*Chunk, error)
+ Close()
}
/*
diff --git a/swarm/swarm.go b/swarm/swarm.go
index 4b3621aff..eab01f036 100644
--- a/swarm/swarm.go
+++ b/swarm/swarm.go
@@ -54,6 +54,7 @@ type Swarm struct {
privateKey *ecdsa.PrivateKey
corsString string
swapEnabled bool
+ lstore *storage.LocalStore // local store, needs to store for releasing resources after node stopped
}
type SwarmAPI struct {
@@ -90,7 +91,7 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.
glog.V(logger.Debug).Infof("Setting up Swarm service components")
hash := storage.MakeHashFunc(config.ChunkerParams.Hash)
- lstore, err := storage.NewLocalStore(hash, config.StoreParams)
+ self.lstore, err = storage.NewLocalStore(hash, config.StoreParams)
if err != nil {
return
}
@@ -98,7 +99,7 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.
// setup local store
glog.V(logger.Debug).Infof("Set up local storage")
- self.dbAccess = network.NewDbAccess(lstore)
+ self.dbAccess = network.NewDbAccess(self.lstore)
glog.V(logger.Debug).Infof("Set up local db access (iterator/counter)")
// set up the kademlia hive
@@ -115,15 +116,15 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.
glog.V(logger.Debug).Infof("-> set swarm forwarder as cloud storage backend")
// setup cloud storage internal access layer
- self.storage = storage.NewNetStore(hash, lstore, cloud, config.StoreParams)
+ self.storage = storage.NewNetStore(hash, self.lstore, cloud, config.StoreParams)
glog.V(logger.Debug).Infof("-> swarm net store shared access layer to Swarm Chunk Store")
// set up Depo (storage handler = cloud storage access layer for incoming remote requests)
- self.depo = network.NewDepo(hash, lstore, self.storage)
+ self.depo = network.NewDepo(hash, self.lstore, self.storage)
glog.V(logger.Debug).Infof("-> REmote Access to CHunks")
// set up DPA, the cloud storage local access layer
- dpaChunkStore := storage.NewDpaChunkStore(lstore, self.storage)
+ dpaChunkStore := storage.NewDpaChunkStore(self.lstore, self.storage)
glog.V(logger.Debug).Infof("-> Local Access to Swarm")
// Swarm Hash Merklised Chunking for Arbitrary-length Document/File storage
self.dpa = storage.NewDPA(dpaChunkStore, self.config.ChunkerParams)
@@ -212,6 +213,11 @@ func (self *Swarm) Stop() error {
ch.Stop()
ch.Save()
}
+
+ if self.lstore != nil {
+ self.lstore.DbStore.Close()
+ }
+
return self.config.Save()
}
diff --git a/tests/block_test_util.go b/tests/block_test_util.go
index 470eb7cb7..01539de03 100644
--- a/tests/block_test_util.go
+++ b/tests/block_test_util.go
@@ -146,7 +146,7 @@ func runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, bt map[s
}
for name, test := range bt {
- if skipTest[name] {
+ if skipTest[name] /*|| name != "CallingCanonicalContractFromFork_CALLCODE"*/ {
glog.Infoln("Skipping block test", name)
continue
}
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 5469a4c71..e8ab29d14 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -108,7 +108,7 @@ func runStateTests(chainConfig *params.ChainConfig, tests map[string]VmTest, ski
}
for name, test := range tests {
- if skipTest[name] {
+ if skipTest[name] /*|| name != "JUMPDEST_Attack"*/ {
glog.Infoln("Skipping state test", name)
continue
}
diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go
index 25e55886f..1edf0e425 100644
--- a/tests/vm_test_util.go
+++ b/tests/vm_test_util.go
@@ -129,7 +129,7 @@ func runVmTests(tests map[string]VmTest, skipTests []string) error {
}
for name, test := range tests {
- if skipTest[name] /*|| name != "loop_stacklimit_1021"*/ {
+ if skipTest[name] /*|| name != "exp0"*/ {
glog.Infoln("Skipping VM test", name)
continue
}
@@ -229,6 +229,6 @@ func RunVm(statedb *state.StateDB, env, exec map[string]string) ([]byte, []*type
vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract)
environment, _ := NewEVMEnvironment(true, chainConfig, statedb, env, exec)
- ret, err := environment.Call(caller, to, data, gas, value)
- return ret, statedb.Logs(), gas, err
+ ret, g, err := environment.Call(caller, to, data, gas.Uint64(), value)
+ return ret, statedb.Logs(), new(big.Int).SetUint64(g), err
}