aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/blockchain.go55
-rw-r--r--core/config.go14
-rw-r--r--core/state/state_object.go4
-rw-r--r--core/state/statedb.go11
-rw-r--r--core/tx_pool.go65
-rw-r--r--core/tx_pool_test.go90
-rw-r--r--core/vm/environment.go4
-rw-r--r--core/vm/gas.go34
-rw-r--r--core/vm/instructions.go7
-rw-r--r--core/vm/runtime/runtime.go4
-rw-r--r--core/vm/util_test.go9
-rw-r--r--core/vm/vm.go77
12 files changed, 331 insertions, 43 deletions
diff --git a/core/blockchain.go b/core/blockchain.go
index 1fbcdfc6f..7657fce78 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -269,7 +269,7 @@ func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error {
if block == nil {
return fmt.Errorf("non existent block [%x…]", hash[:4])
}
- if _, err := trie.NewSecure(block.Root(), self.chainDb); err != nil {
+ if _, err := trie.NewSecure(block.Root(), self.chainDb, 0); err != nil {
return err
}
// If all checks out, manually set the head block
@@ -834,19 +834,16 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
// faster than direct delivery and requires much less mutex
// acquiring.
var (
- stats struct{ queued, processed, ignored int }
+ stats = insertStats{startTime: time.Now()}
events = make([]interface{}, 0, len(chain))
coalescedLogs vm.Logs
- tstart = time.Now()
-
- nonceChecked = make([]bool, len(chain))
+ nonceChecked = make([]bool, len(chain))
)
// Start the parallel nonce verifier.
nonceAbort, nonceResults := verifyNoncesFromBlocks(self.pow, chain)
defer close(nonceAbort)
- txcount := 0
for i, block := range chain {
if atomic.LoadInt32(&self.procInterrupt) == 1 {
glog.V(logger.Debug).Infoln("Premature abort during block chain processing")
@@ -941,7 +938,6 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
return i, err
}
- txcount += len(block.Transactions())
// write the block to the chain and get the status
status, err := self.WriteBlock(block)
if err != nil {
@@ -976,19 +972,54 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
case SplitStatTy:
events = append(events, ChainSplitEvent{block, logs})
}
+
stats.processed++
+ if glog.V(logger.Info) {
+ stats.report(chain, i)
+ }
}
- if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) {
- tend := time.Since(tstart)
- start, end := chain[0], chain[len(chain)-1]
- glog.Infof("imported %d block(s) (%d queued %d ignored) including %d txs in %v. #%v [%x / %x]\n", stats.processed, stats.queued, stats.ignored, txcount, tend, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4])
- }
go self.postChainEvents(events, coalescedLogs)
return 0, nil
}
+// insertStats tracks and reports on block insertion.
+type insertStats struct {
+ queued, processed, ignored int
+ lastIndex int
+ startTime time.Time
+}
+
+const (
+ statsReportLimit = 1024
+ statsReportTimeLimit = 8 * time.Second
+)
+
+// report prints statistics if some number of blocks have been processed
+// or more than a few seconds have passed since the last message.
+func (st *insertStats) report(chain []*types.Block, index int) {
+ limit := statsReportLimit
+ if index == len(chain)-1 {
+ limit = 0 // Always print a message for the last block.
+ }
+ now := time.Now()
+ duration := now.Sub(st.startTime)
+ if duration > statsReportTimeLimit || st.queued > limit || st.processed > limit || st.ignored > limit {
+ start, end := chain[st.lastIndex], chain[index]
+ txcount := countTransactions(chain[st.lastIndex : index+1])
+ glog.Infof("imported %d block(s) (%d queued %d ignored) including %d txs in %v. #%v [%x / %x]\n", st.processed, st.queued, st.ignored, txcount, duration, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4])
+ *st = insertStats{startTime: now, lastIndex: index}
+ }
+}
+
+func countTransactions(chain []*types.Block) (c int) {
+ for _, b := range chain {
+ c += len(b.Transactions())
+ }
+ return c
+}
+
// reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
// to be part of the new canonical chain and accumulates potential missing transactions and post an
// event about them
diff --git a/core/config.go b/core/config.go
index c0d065a57..3ab04e520 100644
--- a/core/config.go
+++ b/core/config.go
@@ -21,6 +21,7 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/params"
)
var ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error
@@ -35,6 +36,8 @@ type ChainConfig struct {
DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork)
DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork
+ HomesteadGasRepriceBlock *big.Int `json:"homesteadGasRepriceBlock"` // Homestead gas reprice switch block (nil = no fork)
+
VmConfig vm.Config `json:"-"`
}
@@ -45,3 +48,14 @@ func (c *ChainConfig) IsHomestead(num *big.Int) bool {
}
return num.Cmp(c.HomesteadBlock) >= 0
}
+
+// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice).
+//
+// The returned GasTable's fields shouldn't, under any circumstances, be changed.
+func (c *ChainConfig) GasTable(num *big.Int) params.GasTable {
+ if c.HomesteadGasRepriceBlock == nil || num == nil || num.Cmp(c.HomesteadGasRepriceBlock) < 0 {
+ return params.GasTableHomestead
+ }
+
+ return params.GasTableHomesteadGasRepriceFork
+}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index 6eab27d9e..edb073173 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -137,9 +137,9 @@ func (self *StateObject) markSuicided() {
func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie {
if c.trie == nil {
var err error
- c.trie, err = trie.NewSecure(c.data.Root, db)
+ c.trie, err = trie.NewSecure(c.data.Root, db, 0)
if err != nil {
- c.trie, _ = trie.NewSecure(common.Hash{}, db)
+ c.trie, _ = trie.NewSecure(common.Hash{}, db, 0)
c.setError(fmt.Errorf("can't create storage trie: %v", err))
}
}
diff --git a/core/state/statedb.go b/core/state/statedb.go
index ec9e9392f..dcb897628 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -41,7 +41,10 @@ var StartingNonce uint64
const (
// Number of past tries to keep. The arbitrarily chosen value here
// is max uncle depth + 1.
- maxTrieCacheLength = 8
+ maxPastTries = 8
+
+ // Trie cache generation limit.
+ maxTrieCacheGen = 100
// Number of codehash->size associations to keep.
codeSizeCacheSize = 100000
@@ -86,7 +89,7 @@ type StateDB struct {
// Create a new state from a given trie
func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
- tr, err := trie.NewSecure(root, db)
+ tr, err := trie.NewSecure(root, db, maxTrieCacheGen)
if err != nil {
return nil, err
}
@@ -155,14 +158,14 @@ func (self *StateDB) openTrie(root common.Hash) (*trie.SecureTrie, error) {
return &tr, nil
}
}
- return trie.NewSecure(root, self.db)
+ return trie.NewSecure(root, self.db, maxTrieCacheGen)
}
func (self *StateDB) pushTrie(t *trie.SecureTrie) {
self.lock.Lock()
defer self.lock.Unlock()
- if len(self.pastTries) >= maxTrieCacheLength {
+ if len(self.pastTries) >= maxPastTries {
copy(self.pastTries, self.pastTries[1:])
self.pastTries[len(self.pastTries)-1] = t
} else {
diff --git a/core/tx_pool.go b/core/tx_pool.go
index 10a110e0b..2c8a5c396 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
+ "gopkg.in/karalabe/cookiejar.v2/collections/prque"
)
var (
@@ -46,10 +47,12 @@ var (
)
var (
- maxQueuedPerAccount = uint64(64) // Max limit of queued transactions per address
- maxQueuedInTotal = uint64(65536) // Max limit of queued transactions from all accounts
- maxQueuedLifetime = 3 * time.Hour // Max amount of time transactions from idle accounts are queued
- evictionInterval = time.Minute // Time interval to check for evictable transactions
+ minPendingPerAccount = uint64(16) // Min number of guaranteed transaction slots per address
+ maxPendingTotal = uint64(4096) // Max limit of pending transactions from all accounts (soft)
+ maxQueuedPerAccount = uint64(64) // Max limit of queued transactions per address
+ maxQueuedInTotal = uint64(1024) // Max limit of queued transactions from all accounts
+ maxQueuedLifetime = 3 * time.Hour // Max amount of time transactions from idle accounts are queued
+ evictionInterval = time.Minute // Time interval to check for evictable transactions
)
type stateFn func() (*state.StateDB, error)
@@ -481,7 +484,6 @@ func (pool *TxPool) promoteExecutables() {
}
// Iterate over all accounts and promote any executable transactions
queued := uint64(0)
-
for addr, list := range pool.queue {
// Drop all transactions that are deemed too old (low nonce)
for _, tx := range list.Forward(state.GetNonce(addr)) {
@@ -519,6 +521,59 @@ func (pool *TxPool) promoteExecutables() {
delete(pool.queue, addr)
}
}
+ // If the pending limit is overflown, start equalizing allowances
+ pending := uint64(0)
+ for _, list := range pool.pending {
+ pending += uint64(list.Len())
+ }
+ if pending > maxPendingTotal {
+ // Assemble a spam order to penalize large transactors first
+ spammers := prque.New()
+ for addr, list := range pool.pending {
+ // Only evict transactions from high rollers
+ if uint64(list.Len()) > minPendingPerAccount {
+ // Skip local accounts as pools should maintain backlogs for themselves
+ for _, tx := range list.txs.items {
+ if !pool.localTx.contains(tx.Hash()) {
+ spammers.Push(addr, float32(list.Len()))
+ }
+ break // Checking on transaction for locality is enough
+ }
+ }
+ }
+ // Gradually drop transactions from offenders
+ offenders := []common.Address{}
+ for pending > maxPendingTotal && !spammers.Empty() {
+ // Retrieve the next offender if not local address
+ offender, _ := spammers.Pop()
+ offenders = append(offenders, offender.(common.Address))
+
+ // Equalize balances until all the same or below threshold
+ if len(offenders) > 1 {
+ // Calculate the equalization threshold for all current offenders
+ threshold := pool.pending[offender.(common.Address)].Len()
+
+ // Iteratively reduce all offenders until below limit or threshold reached
+ for pending > maxPendingTotal && pool.pending[offenders[len(offenders)-2]].Len() > threshold {
+ for i := 0; i < len(offenders)-1; i++ {
+ list := pool.pending[offenders[i]]
+ list.Cap(list.Len() - 1)
+ pending--
+ }
+ }
+ }
+ }
+ // If still above threshold, reduce to limit or min allowance
+ if pending > maxPendingTotal && len(offenders) > 0 {
+ for pending > maxPendingTotal && uint64(pool.pending[offenders[len(offenders)-1]].Len()) > minPendingPerAccount {
+ for _, addr := range offenders {
+ list := pool.pending[addr]
+ list.Cap(list.Len() - 1)
+ pending--
+ }
+ }
+ }
+ }
// If we've queued more transactions than the hard limit, drop oldest ones
if queued > maxQueuedInTotal {
// Sort all accounts with queued transactions by heartbeat
diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go
index 4bc5aed38..dbe6fa635 100644
--- a/core/tx_pool_test.go
+++ b/core/tx_pool_test.go
@@ -618,6 +618,96 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
}
}
+// Tests that if the transaction count belonging to multiple accounts go above
+// some hard threshold, the higher transactions are dropped to prevent DOS
+// attacks.
+func TestTransactionPendingGlobalLimiting(t *testing.T) {
+ // Reduce the queue limits to shorten test time
+ defer func(old uint64) { maxPendingTotal = old }(maxPendingTotal)
+ maxPendingTotal = minPendingPerAccount * 10
+
+ // Create the pool to test the limit enforcement with
+ db, _ := ethdb.NewMemDatabase()
+ statedb, _ := state.New(common.Hash{}, db)
+
+ pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
+ pool.resetState()
+
+ // Create a number of test accounts and fund them
+ state, _ := pool.currentState()
+
+ keys := make([]*ecdsa.PrivateKey, 5)
+ for i := 0; i < len(keys); i++ {
+ keys[i], _ = crypto.GenerateKey()
+ state.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ }
+ // Generate and queue a batch of transactions
+ nonces := make(map[common.Address]uint64)
+
+ txs := types.Transactions{}
+ for _, key := range keys {
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ for j := 0; j < int(maxPendingTotal)/len(keys)*2; j++ {
+ txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key))
+ nonces[addr]++
+ }
+ }
+ // Import the batch and verify that limits have been enforced
+ pool.AddBatch(txs)
+
+ pending := 0
+ for _, list := range pool.pending {
+ pending += list.Len()
+ }
+ if pending > int(maxPendingTotal) {
+ t.Fatalf("total pending transactions overflow allowance: %d > %d", pending, maxPendingTotal)
+ }
+}
+
+// Tests that if the transaction count belonging to multiple accounts go above
+// some hard threshold, if they are under the minimum guaranteed slot count then
+// the transactions are still kept.
+func TestTransactionPendingMinimumAllowance(t *testing.T) {
+ // Reduce the queue limits to shorten test time
+ defer func(old uint64) { maxPendingTotal = old }(maxPendingTotal)
+ maxPendingTotal = 0
+
+ // Create the pool to test the limit enforcement with
+ db, _ := ethdb.NewMemDatabase()
+ statedb, _ := state.New(common.Hash{}, db)
+
+ pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
+ pool.resetState()
+
+ // Create a number of test accounts and fund them
+ state, _ := pool.currentState()
+
+ keys := make([]*ecdsa.PrivateKey, 5)
+ for i := 0; i < len(keys); i++ {
+ keys[i], _ = crypto.GenerateKey()
+ state.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ }
+ // Generate and queue a batch of transactions
+ nonces := make(map[common.Address]uint64)
+
+ txs := types.Transactions{}
+ for _, key := range keys {
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ for j := 0; j < int(minPendingPerAccount)*2; j++ {
+ txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key))
+ nonces[addr]++
+ }
+ }
+ // Import the batch and verify that limits have been enforced
+ pool.AddBatch(txs)
+
+ for addr, list := range pool.pending {
+ if list.Len() != int(minPendingPerAccount) {
+ t.Errorf("addr %x: total pending transactions mismatch: have %d, want %d", addr, list.Len(), minPendingPerAccount)
+ }
+ }
+}
+
// Benchmarks the speed of validating the contents of the pending queue of the
// transaction pool.
func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) }
diff --git a/core/vm/environment.go b/core/vm/environment.go
index a4b2ac196..f8996e648 100644
--- a/core/vm/environment.go
+++ b/core/vm/environment.go
@@ -20,12 +20,16 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/params"
)
// RuleSet is an interface that defines the current rule set during the
// execution of the EVM instructions (e.g. whether it's homestead)
type RuleSet interface {
IsHomestead(*big.Int) bool
+ // GasTable returns the gas prices for this phase, which is based on
+ // block number passed in.
+ GasTable(*big.Int) params.GasTable
}
// Environment is an EVM requirement and helper which allows access to outside
diff --git a/core/vm/gas.go b/core/vm/gas.go
index eb2c16346..fdbc0df7f 100644
--- a/core/vm/gas.go
+++ b/core/vm/gas.go
@@ -35,8 +35,27 @@ var (
GasStop = big.NewInt(0)
GasContractByte = big.NewInt(200)
+
+ n64 = big.NewInt(64)
)
+// calcGas returns the actual gas cost of the call.
+//
+// The cost of gas was changed during the homestead price change HF. To allow for EIP150
+// to be implemented. The returned gas is gas - base * 63 / 64.
+func callGas(gasTable params.GasTable, availableGas, base, 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
+ }
+ }
+ 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
@@ -127,18 +146,19 @@ var _baseCheck = map[OpCode]req{
MSIZE: {0, GasQuickStep, 1},
GAS: {0, GasQuickStep, 1},
BLOCKHASH: {1, GasExtStep, 1},
- BALANCE: {1, GasExtStep, 1},
- EXTCODESIZE: {1, GasExtStep, 1},
- EXTCODECOPY: {4, GasExtStep, 0},
+ 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},
- CALL: {7, params.CallGas, 1},
- CALLCODE: {7, params.CallGas, 1},
- DELEGATECALL: {6, params.CallGas, 1},
- JUMPDEST: {0, params.JumpdestGas, 0},
+ // 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},
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 79aee60d2..3352877c1 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -514,7 +514,12 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
input = memory.Get(offset.Int64(), size.Int64())
gas = new(big.Int).Set(contract.Gas)
)
- contract.UseGas(contract.Gas)
+ if env.RuleSet().GasTable(env.BlockNumber()).CreateBySuicide != nil {
+ gas.Div(gas, n64)
+ gas = gas.Sub(contract.Gas, gas)
+ }
+
+ contract.UseGas(gas)
_, addr, suberr := env.Create(contract, input, gas, contract.Price, value)
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index 6d980cb32..343aee514 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -25,12 +25,16 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/params"
)
// The default, always homestead, rule set for the vm env
type ruleSet struct{}
func (ruleSet) IsHomestead(*big.Int) bool { return true }
+func (ruleSet) GasTable(*big.Int) params.GasTable {
+ return params.GasTableHomesteadGasRepriceFork
+}
// Config is a basic type specifying certain configuration flags for running
// the EVM.
diff --git a/core/vm/util_test.go b/core/vm/util_test.go
index f0d825555..5783ee015 100644
--- a/core/vm/util_test.go
+++ b/core/vm/util_test.go
@@ -16,10 +16,17 @@
package vm
-import "math/big"
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/params"
+)
type ruleSet struct {
hs *big.Int
}
func (r ruleSet) IsHomestead(n *big.Int) bool { return n.Cmp(r.hs) >= 0 }
+func (r ruleSet) GasTable(*big.Int) params.GasTable {
+ return params.GasTableHomestead
+}
diff --git a/core/vm/vm.go b/core/vm/vm.go
index 033ada21c..fcffcf317 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -44,6 +44,7 @@ type EVM struct {
env Environment
jumpTable vmJumpTable
cfg Config
+ gasTable params.GasTable
}
// New returns a new instance of the EVM.
@@ -52,6 +53,7 @@ func New(env Environment, cfg Config) *EVM {
env: env,
jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()),
cfg: cfg,
+ gasTable: env.RuleSet().GasTable(env.BlockNumber()),
}
}
@@ -169,7 +171,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
// Get the memory location of pc
op = contract.GetOp(pc)
// calculate the new memory size and gas price for the current executing opcode
- newMemSize, cost, err = calculateGasAndSize(evm.env, contract, caller, op, statedb, mem, stack)
+ newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, statedb, mem, stack)
if err != nil {
return nil, err
}
@@ -234,7 +236,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
// calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
// the operation. This does not reduce gas or resizes the memory.
-func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
+func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
var (
gas = new(big.Int)
newMemSize *big.Int = new(big.Int)
@@ -246,6 +248,24 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
// stack Check, memory resize & gas phase
switch op {
+ case SUICIDE:
+ // if suicide is not nil: homestead gas fork
+ if gasTable.CreateBySuicide != nil {
+ gas.Set(gasTable.Suicide)
+ if !env.Db().Exist(common.BigToAddress(stack.data[len(stack.data)-1])) {
+ gas.Add(gas, gasTable.CreateBySuicide)
+ }
+ }
+
+ if !statedb.HasSuicided(contract.Address()) {
+ statedb.AddRefund(params.SuicideRefundGas)
+ }
+ case EXTCODESIZE:
+ gas.Set(gasTable.ExtcodeSize)
+ case BALANCE:
+ gas.Set(gasTable.Balance)
+ case SLOAD:
+ gas.Set(gasTable.SLoad)
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
n := int(op - SWAP1 + 2)
err := stack.require(n)
@@ -274,6 +294,8 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas))
newMemSize = calcMemSize(mStart, mSize)
+
+ quadMemGas(mem, newMemSize, gas)
case EXP:
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas))
case SSTORE:
@@ -302,67 +324,100 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
g = params.SstoreResetGas
}
gas.Set(g)
- case SUICIDE:
- if !statedb.HasSuicided(contract.Address()) {
- statedb.AddRefund(params.SuicideRefundGas)
- }
case MLOAD:
newMemSize = calcMemSize(stack.peek(), u256(32))
+ quadMemGas(mem, newMemSize, gas)
case MSTORE8:
newMemSize = calcMemSize(stack.peek(), u256(1))
+ quadMemGas(mem, newMemSize, gas)
case MSTORE:
newMemSize = calcMemSize(stack.peek(), u256(32))
+ quadMemGas(mem, newMemSize, gas)
case RETURN:
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
+ quadMemGas(mem, newMemSize, gas)
case SHA3:
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
words := toWordSize(stack.data[stack.len()-2])
gas.Add(gas, words.Mul(words, params.Sha3WordGas))
+
+ quadMemGas(mem, newMemSize, gas)
case CALLDATACOPY:
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
words := toWordSize(stack.data[stack.len()-3])
gas.Add(gas, words.Mul(words, params.CopyGas))
+
+ quadMemGas(mem, newMemSize, gas)
case CODECOPY:
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
words := toWordSize(stack.data[stack.len()-3])
gas.Add(gas, words.Mul(words, params.CopyGas))
+
+ quadMemGas(mem, newMemSize, gas)
case EXTCODECOPY:
+ gas.Set(gasTable.ExtcodeCopy)
+
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
words := toWordSize(stack.data[stack.len()-4])
gas.Add(gas, words.Mul(words, params.CopyGas))
+ quadMemGas(mem, newMemSize, gas)
case CREATE:
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
+
+ quadMemGas(mem, newMemSize, gas)
case CALL, CALLCODE:
- gas.Add(gas, stack.data[stack.len()-1])
+ gas.Set(gasTable.Calls)
if op == CALL {
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
gas.Add(gas, params.CallNewAccountGas)
}
}
-
if len(stack.data[stack.len()-3].Bytes()) > 0 {
gas.Add(gas, params.CallValueTransferGas)
}
-
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
newMemSize = common.BigMax(x, y)
+
+ quadMemGas(mem, newMemSize, gas)
+
+ cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1])
+ // Replace the stack item with the new gas calculation. This means that
+ // either the original item is left on the stack or the item is replaced by:
+ // (availableGas - gas) * 63 / 64
+ // We replace the stack item so that it's available when the opCall instruction is
+ // called. This information is otherwise lost due to the dependency on *current*
+ // available gas.
+ stack.data[stack.len()-1] = cg
+ gas.Add(gas, cg)
+
case DELEGATECALL:
- gas.Add(gas, stack.data[stack.len()-1])
+ gas.Set(gasTable.Calls)
x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6])
y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4])
newMemSize = common.BigMax(x, y)
+
+ quadMemGas(mem, newMemSize, gas)
+
+ cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1])
+ // Replace the stack item with the new gas calculation. This means that
+ // either the original item is left on the stack or the item is replaced by:
+ // (availableGas - gas) * 63 / 64
+ // We replace the stack item so that it's available when the opCall instruction is
+ // called.
+ stack.data[stack.len()-1] = cg
+ gas.Add(gas, cg)
+
}
- quadMemGas(mem, newMemSize, gas)
return newMemSize, gas, nil
}