提交 ef9265d0 编写于 作者: J Jeffrey Wilcke 提交者: GitHub

Merge pull request #3140 from karalabe/release/1.4

Geth 1.4.18: Note 7
...@@ -74,7 +74,7 @@ func runTestWithReader(test string, r io.Reader) error { ...@@ -74,7 +74,7 @@ func runTestWithReader(test string, r io.Reader) error {
var err error var err error
switch strings.ToLower(test) { switch strings.ToLower(test) {
case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests": case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests":
err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, r, skipTests) err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, params.MainNetHomesteadGasRepriceBlock, r, skipTests)
case "st", "state", "statetest", "statetests": case "st", "state", "statetest", "statetests":
rs := tests.RuleSet{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true} rs := tests.RuleSet{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true}
err = tests.RunStateTestWithReader(rs, r, skipTests) err = tests.RunStateTestWithReader(rs, r, skipTests)
......
...@@ -33,6 +33,7 @@ import ( ...@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
) )
...@@ -222,6 +223,9 @@ func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg ...@@ -222,6 +223,9 @@ func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg
type ruleSet struct{} type ruleSet struct{}
func (ruleSet) IsHomestead(*big.Int) bool { return true } func (ruleSet) IsHomestead(*big.Int) bool { return true }
func (ruleSet) GasTable(*big.Int) params.GasTable {
return params.GasTableHomesteadGasRepriceFork
}
func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} } func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} }
func (self *VMEnv) Vm() vm.Vm { return self.evm } func (self *VMEnv) Vm() vm.Vm { return self.evm }
......
...@@ -50,7 +50,7 @@ const ( ...@@ -50,7 +50,7 @@ const (
clientIdentifier = "Geth" // Client identifier to advertise over the network clientIdentifier = "Geth" // Client identifier to advertise over the network
versionMajor = 1 // Major version component of the current release versionMajor = 1 // Major version component of the current release
versionMinor = 4 // Minor version component of the current release versionMinor = 4 // Minor version component of the current release
versionPatch = 17 // Patch version component of the current release versionPatch = 18 // Patch version component of the current release
versionMeta = "stable" // Version metadata to append to the version string versionMeta = "stable" // Version metadata to append to the version string
versionOracle = "0xfa7b9770ca4cb04296cac84f37736d4041251cdf" // Ethereum address of the Geth release oracle versionOracle = "0xfa7b9770ca4cb04296cac84f37736d4041251cdf" // Ethereum address of the Geth release oracle
......
...@@ -809,6 +809,13 @@ func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainC ...@@ -809,6 +809,13 @@ func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainC
} }
config.DAOForkSupport = true config.DAOForkSupport = true
} }
if config.HomesteadGasRepriceBlock == nil {
if ctx.GlobalBool(TestNetFlag.Name) {
config.HomesteadGasRepriceBlock = params.TestNetHomesteadGasRepriceBlock
} else {
config.HomesteadGasRepriceBlock = params.MainNetHomesteadGasRepriceBlock
}
}
// Force override any existing configs if explicitly requested // Force override any existing configs if explicitly requested
switch { switch {
case ctx.GlobalBool(SupportDAOFork.Name): case ctx.GlobalBool(SupportDAOFork.Name):
......
...@@ -269,7 +269,7 @@ func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error { ...@@ -269,7 +269,7 @@ func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error {
if block == nil { if block == nil {
return fmt.Errorf("non existent block [%x…]", hash[:4]) 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 return err
} }
// If all checks out, manually set the head block // If all checks out, manually set the head block
...@@ -824,19 +824,16 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { ...@@ -824,19 +824,16 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
// faster than direct delivery and requires much less mutex // faster than direct delivery and requires much less mutex
// acquiring. // acquiring.
var ( var (
stats struct{ queued, processed, ignored int } stats = insertStats{startTime: time.Now()}
events = make([]interface{}, 0, len(chain)) events = make([]interface{}, 0, len(chain))
coalescedLogs vm.Logs coalescedLogs vm.Logs
tstart = time.Now() nonceChecked = make([]bool, len(chain))
nonceChecked = make([]bool, len(chain))
) )
// Start the parallel nonce verifier. // Start the parallel nonce verifier.
nonceAbort, nonceResults := verifyNoncesFromBlocks(self.pow, chain) nonceAbort, nonceResults := verifyNoncesFromBlocks(self.pow, chain)
defer close(nonceAbort) defer close(nonceAbort)
txcount := 0
for i, block := range chain { for i, block := range chain {
if atomic.LoadInt32(&self.procInterrupt) == 1 { if atomic.LoadInt32(&self.procInterrupt) == 1 {
glog.V(logger.Debug).Infoln("Premature abort during block chain processing") glog.V(logger.Debug).Infoln("Premature abort during block chain processing")
...@@ -931,7 +928,6 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { ...@@ -931,7 +928,6 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
return i, err return i, err
} }
txcount += len(block.Transactions())
// write the block to the chain and get the status // write the block to the chain and get the status
status, err := self.WriteBlock(block) status, err := self.WriteBlock(block)
if err != nil { if err != nil {
...@@ -966,19 +962,54 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { ...@@ -966,19 +962,54 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
case SplitStatTy: case SplitStatTy:
events = append(events, ChainSplitEvent{block, logs}) events = append(events, ChainSplitEvent{block, logs})
} }
stats.processed++ 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) go self.postChainEvents(events, coalescedLogs)
return 0, nil 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 // 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 // to be part of the new canonical chain and accumulates potential missing transactions and post an
// event about them // event about them
......
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/core/vm" "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 var ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error
...@@ -35,6 +36,8 @@ type ChainConfig struct { ...@@ -35,6 +36,8 @@ type ChainConfig struct {
DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork) 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 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:"-"` VmConfig vm.Config `json:"-"`
} }
...@@ -45,3 +48,14 @@ func (c *ChainConfig) IsHomestead(num *big.Int) bool { ...@@ -45,3 +48,14 @@ func (c *ChainConfig) IsHomestead(num *big.Int) bool {
} }
return num.Cmp(c.HomesteadBlock) >= 0 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
}
...@@ -137,9 +137,9 @@ func (self *StateObject) markSuicided() { ...@@ -137,9 +137,9 @@ func (self *StateObject) markSuicided() {
func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie { func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie {
if c.trie == nil { if c.trie == nil {
var err error 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 { 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)) c.setError(fmt.Errorf("can't create storage trie: %v", err))
} }
} }
......
...@@ -41,7 +41,10 @@ var StartingNonce uint64 ...@@ -41,7 +41,10 @@ var StartingNonce uint64
const ( const (
// Number of past tries to keep. The arbitrarily chosen value here // Number of past tries to keep. The arbitrarily chosen value here
// is max uncle depth + 1. // is max uncle depth + 1.
maxTrieCacheLength = 8 maxPastTries = 8
// Trie cache generation limit.
maxTrieCacheGen = 100
// Number of codehash->size associations to keep. // Number of codehash->size associations to keep.
codeSizeCacheSize = 100000 codeSizeCacheSize = 100000
...@@ -86,7 +89,7 @@ type StateDB struct { ...@@ -86,7 +89,7 @@ type StateDB struct {
// Create a new state from a given trie // Create a new state from a given trie
func New(root common.Hash, db ethdb.Database) (*StateDB, error) { 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 { if err != nil {
return nil, err return nil, err
} }
...@@ -155,14 +158,14 @@ func (self *StateDB) openTrie(root common.Hash) (*trie.SecureTrie, error) { ...@@ -155,14 +158,14 @@ func (self *StateDB) openTrie(root common.Hash) (*trie.SecureTrie, error) {
return &tr, nil return &tr, nil
} }
} }
return trie.NewSecure(root, self.db) return trie.NewSecure(root, self.db, maxTrieCacheGen)
} }
func (self *StateDB) pushTrie(t *trie.SecureTrie) { func (self *StateDB) pushTrie(t *trie.SecureTrie) {
self.lock.Lock() self.lock.Lock()
defer self.lock.Unlock() defer self.lock.Unlock()
if len(self.pastTries) >= maxTrieCacheLength { if len(self.pastTries) >= maxPastTries {
copy(self.pastTries, self.pastTries[1:]) copy(self.pastTries, self.pastTries[1:])
self.pastTries[len(self.pastTries)-1] = t self.pastTries[len(self.pastTries)-1] = t
} else { } else {
......
...@@ -30,6 +30,7 @@ import ( ...@@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
) )
var ( var (
...@@ -46,10 +47,12 @@ var ( ...@@ -46,10 +47,12 @@ var (
) )
var ( var (
maxQueuedPerAccount = uint64(64) // Max limit of queued transactions per address minPendingPerAccount = uint64(16) // Min number of guaranteed transaction slots per address
maxQueuedInTotal = uint64(8192) // Max limit of queued transactions from all accounts maxPendingTotal = uint64(4096) // Max limit of pending transactions from all accounts (soft)
maxQueuedLifetime = 3 * time.Hour // Max amount of time transactions from idle accounts are queued maxQueuedPerAccount = uint64(64) // Max limit of queued transactions per address
evictionInterval = time.Minute // Time interval to check for evictable transactions 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) type stateFn func() (*state.StateDB, error)
...@@ -481,7 +484,6 @@ func (pool *TxPool) promoteExecutables() { ...@@ -481,7 +484,6 @@ func (pool *TxPool) promoteExecutables() {
} }
// Iterate over all accounts and promote any executable transactions // Iterate over all accounts and promote any executable transactions
queued := uint64(0) queued := uint64(0)
for addr, list := range pool.queue { for addr, list := range pool.queue {
// Drop all transactions that are deemed too old (low nonce) // Drop all transactions that are deemed too old (low nonce)
for _, tx := range list.Forward(state.GetNonce(addr)) { for _, tx := range list.Forward(state.GetNonce(addr)) {
...@@ -519,6 +521,59 @@ func (pool *TxPool) promoteExecutables() { ...@@ -519,6 +521,59 @@ func (pool *TxPool) promoteExecutables() {
delete(pool.queue, addr) 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 we've queued more transactions than the hard limit, drop oldest ones
if queued > maxQueuedInTotal { if queued > maxQueuedInTotal {
// Sort all accounts with queued transactions by heartbeat // Sort all accounts with queued transactions by heartbeat
......
...@@ -618,6 +618,96 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) { ...@@ -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 // Benchmarks the speed of validating the contents of the pending queue of the
// transaction pool. // transaction pool.
func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) } func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) }
......
...@@ -20,12 +20,16 @@ import ( ...@@ -20,12 +20,16 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
) )
// RuleSet is an interface that defines the current rule set during the // RuleSet is an interface that defines the current rule set during the
// execution of the EVM instructions (e.g. whether it's homestead) // execution of the EVM instructions (e.g. whether it's homestead)
type RuleSet interface { type RuleSet interface {
IsHomestead(*big.Int) bool 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 // Environment is an EVM requirement and helper which allows access to outside
......
...@@ -35,8 +35,27 @@ var ( ...@@ -35,8 +35,27 @@ var (
GasStop = big.NewInt(0) GasStop = big.NewInt(0)
GasContractByte = big.NewInt(200) 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 // baseCheck checks for any stack error underflows
func baseCheck(op OpCode, stack *stack, gas *big.Int) error { 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 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{ ...@@ -127,18 +146,19 @@ var _baseCheck = map[OpCode]req{
MSIZE: {0, GasQuickStep, 1}, MSIZE: {0, GasQuickStep, 1},
GAS: {0, GasQuickStep, 1}, GAS: {0, GasQuickStep, 1},
BLOCKHASH: {1, GasExtStep, 1}, BLOCKHASH: {1, GasExtStep, 1},
BALANCE: {1, GasExtStep, 1}, BALANCE: {1, Zero, 1},
EXTCODESIZE: {1, GasExtStep, 1}, EXTCODESIZE: {1, Zero, 1},
EXTCODECOPY: {4, GasExtStep, 0}, EXTCODECOPY: {4, Zero, 0},
SLOAD: {1, params.SloadGas, 1}, SLOAD: {1, params.SloadGas, 1},
SSTORE: {2, Zero, 0}, SSTORE: {2, Zero, 0},
SHA3: {2, params.Sha3Gas, 1}, SHA3: {2, params.Sha3Gas, 1},
CREATE: {3, params.CreateGas, 1}, CREATE: {3, params.CreateGas, 1},
CALL: {7, params.CallGas, 1}, // Zero is calculated in the gasSwitch
CALLCODE: {7, params.CallGas, 1}, CALL: {7, Zero, 1},
DELEGATECALL: {6, params.CallGas, 1}, CALLCODE: {7, Zero, 1},
JUMPDEST: {0, params.JumpdestGas, 0}, DELEGATECALL: {6, Zero, 1},
SUICIDE: {1, Zero, 0}, SUICIDE: {1, Zero, 0},
JUMPDEST: {0, params.JumpdestGas, 0},
RETURN: {2, Zero, 0}, RETURN: {2, Zero, 0},
PUSH1: {0, GasFastestStep, 1}, PUSH1: {0, GasFastestStep, 1},
DUP1: {0, Zero, 1}, DUP1: {0, Zero, 1},
......
...@@ -514,7 +514,12 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract ...@@ -514,7 +514,12 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
input = memory.Get(offset.Int64(), size.Int64()) input = memory.Get(offset.Int64(), size.Int64())
gas = new(big.Int).Set(contract.Gas) 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) _, addr, suberr := env.Create(contract, input, gas, contract.Price, value)
// Push item on the stack based on the returned error. If the ruleset is // Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only // homestead we must check for CodeStoreOutOfGasError (homestead only
......
...@@ -25,12 +25,16 @@ import ( ...@@ -25,12 +25,16 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
) )
// The default, always homestead, rule set for the vm env // The default, always homestead, rule set for the vm env
type ruleSet struct{} type ruleSet struct{}
func (ruleSet) IsHomestead(*big.Int) bool { return true } 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 // Config is a basic type specifying certain configuration flags for running
// the EVM. // the EVM.
......
...@@ -16,10 +16,17 @@ ...@@ -16,10 +16,17 @@
package vm package vm
import "math/big" import (
"math/big"
"github.com/ethereum/go-ethereum/params"
)
type ruleSet struct { type ruleSet struct {
hs *big.Int hs *big.Int
} }
func (r ruleSet) IsHomestead(n *big.Int) bool { return n.Cmp(r.hs) >= 0 } 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
}
...@@ -44,8 +44,8 @@ type EVM struct { ...@@ -44,8 +44,8 @@ type EVM struct {
env Environment env Environment
jumpTable vmJumpTable jumpTable vmJumpTable
cfg Config cfg Config
logger *Logger
logger *Logger gasTable params.GasTable
} }
// New returns a new instance of the EVM. // New returns a new instance of the EVM.
...@@ -60,6 +60,7 @@ func New(env Environment, cfg Config) *EVM { ...@@ -60,6 +60,7 @@ func New(env Environment, cfg Config) *EVM {
jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()), jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()),
cfg: cfg, cfg: cfg,
logger: logger, logger: logger,
gasTable: env.RuleSet().GasTable(env.BlockNumber()),
} }
} }
...@@ -177,7 +178,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { ...@@ -177,7 +178,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
// Get the memory location of pc // Get the memory location of pc
op = contract.GetOp(pc) op = contract.GetOp(pc)
// calculate the new memory size and gas price for the current executing opcode // 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 { if err != nil {
return nil, err return nil, err
} }
...@@ -242,7 +243,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { ...@@ -242,7 +243,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 // 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. // 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 ( var (
gas = new(big.Int) gas = new(big.Int)
newMemSize *big.Int = new(big.Int) newMemSize *big.Int = new(big.Int)
...@@ -254,6 +255,24 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef ...@@ -254,6 +255,24 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
// stack Check, memory resize & gas phase // stack Check, memory resize & gas phase
switch op { 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: case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
n := int(op - SWAP1 + 2) n := int(op - SWAP1 + 2)
err := stack.require(n) err := stack.require(n)
...@@ -282,6 +301,8 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef ...@@ -282,6 +301,8 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas)) gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas))
newMemSize = calcMemSize(mStart, mSize) newMemSize = calcMemSize(mStart, mSize)
quadMemGas(mem, newMemSize, gas)
case EXP: case EXP:
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas)) gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas))
case SSTORE: case SSTORE:
...@@ -310,67 +331,100 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef ...@@ -310,67 +331,100 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
g = params.SstoreClearGas g = params.SstoreClearGas
} }
gas.Set(g) gas.Set(g)
case SUICIDE:
if !statedb.HasSuicided(contract.Address()) {
statedb.AddRefund(params.SuicideRefundGas)
}
case MLOAD: case MLOAD:
newMemSize = calcMemSize(stack.peek(), u256(32)) newMemSize = calcMemSize(stack.peek(), u256(32))
quadMemGas(mem, newMemSize, gas)
case MSTORE8: case MSTORE8:
newMemSize = calcMemSize(stack.peek(), u256(1)) newMemSize = calcMemSize(stack.peek(), u256(1))
quadMemGas(mem, newMemSize, gas)
case MSTORE: case MSTORE:
newMemSize = calcMemSize(stack.peek(), u256(32)) newMemSize = calcMemSize(stack.peek(), u256(32))
quadMemGas(mem, newMemSize, gas)
case RETURN: case RETURN:
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
quadMemGas(mem, newMemSize, gas)
case SHA3: case SHA3:
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
words := toWordSize(stack.data[stack.len()-2]) words := toWordSize(stack.data[stack.len()-2])
gas.Add(gas, words.Mul(words, params.Sha3WordGas)) gas.Add(gas, words.Mul(words, params.Sha3WordGas))
quadMemGas(mem, newMemSize, gas)
case CALLDATACOPY: case CALLDATACOPY:
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
words := toWordSize(stack.data[stack.len()-3]) words := toWordSize(stack.data[stack.len()-3])
gas.Add(gas, words.Mul(words, params.CopyGas)) gas.Add(gas, words.Mul(words, params.CopyGas))
quadMemGas(mem, newMemSize, gas)
case CODECOPY: case CODECOPY:
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
words := toWordSize(stack.data[stack.len()-3]) words := toWordSize(stack.data[stack.len()-3])
gas.Add(gas, words.Mul(words, params.CopyGas)) gas.Add(gas, words.Mul(words, params.CopyGas))
quadMemGas(mem, newMemSize, gas)
case EXTCODECOPY: case EXTCODECOPY:
gas.Set(gasTable.ExtcodeCopy)
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4]) newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
words := toWordSize(stack.data[stack.len()-4]) words := toWordSize(stack.data[stack.len()-4])
gas.Add(gas, words.Mul(words, params.CopyGas)) gas.Add(gas, words.Mul(words, params.CopyGas))
quadMemGas(mem, newMemSize, gas)
case CREATE: case CREATE:
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3]) newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
quadMemGas(mem, newMemSize, gas)
case CALL, CALLCODE: case CALL, CALLCODE:
gas.Add(gas, stack.data[stack.len()-1]) gas.Set(gasTable.Calls)
if op == CALL { if op == CALL {
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) { if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
gas.Add(gas, params.CallNewAccountGas) gas.Add(gas, params.CallNewAccountGas)
} }
} }
if len(stack.data[stack.len()-3].Bytes()) > 0 { if len(stack.data[stack.len()-3].Bytes()) > 0 {
gas.Add(gas, params.CallValueTransferGas) gas.Add(gas, params.CallValueTransferGas)
} }
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7]) x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5]) y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
newMemSize = common.BigMax(x, y) 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: 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]) x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6])
y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4]) y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4])
newMemSize = common.BigMax(x, y) 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 return newMemSize, gas, nil
} }
......
...@@ -286,7 +286,7 @@ func (dl *downloadTester) headFastBlock() *types.Block { ...@@ -286,7 +286,7 @@ func (dl *downloadTester) headFastBlock() *types.Block {
func (dl *downloadTester) commitHeadBlock(hash common.Hash) error { func (dl *downloadTester) commitHeadBlock(hash common.Hash) error {
// For now only check that the state trie is correct // For now only check that the state trie is correct
if block := dl.getBlock(hash); block != nil { if block := dl.getBlock(hash); block != nil {
_, err := trie.NewSecure(block.Root(), dl.stateDb) _, err := trie.NewSecure(block.Root(), dl.stateDb, 0)
return err return err
} }
return fmt.Errorf("non existent block: %x", hash[:4]) return fmt.Errorf("non existent block: %x", hash[:4])
......
...@@ -288,7 +288,7 @@ func (pm *ProtocolManager) handle(p *peer) error { ...@@ -288,7 +288,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
} }
// Start a timer to disconnect if the peer doesn't reply in time // Start a timer to disconnect if the peer doesn't reply in time
p.forkDrop = time.AfterFunc(daoChallengeTimeout, func() { p.forkDrop = time.AfterFunc(daoChallengeTimeout, func() {
glog.V(logger.Warn).Infof("%v: timed out DAO fork-check, dropping", p) glog.V(logger.Debug).Infof("%v: timed out DAO fork-check, dropping", p)
pm.removePeer(p.id) pm.removePeer(p.id)
}) })
// Make sure it's cleaned up if the peer dies off // Make sure it's cleaned up if the peer dies off
......
...@@ -102,6 +102,11 @@ func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) { ...@@ -102,6 +102,11 @@ func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) {
}, nil }, nil
} }
// Path returns the path to the database directory.
func (db *LDBDatabase) Path() string {
return db.fn
}
// Put puts the given key / value to the queue // Put puts the given key / value to the queue
func (self *LDBDatabase) Put(key []byte, value []byte) error { func (self *LDBDatabase) Put(key []byte, value []byte) error {
// Measure the database put latency, if requested // Measure the database put latency, if requested
......
...@@ -52,6 +52,11 @@ var ( ...@@ -52,6 +52,11 @@ var (
Usage: "pprof HTTP server listening port", Usage: "pprof HTTP server listening port",
Value: 6060, Value: 6060,
} }
pprofAddrFlag = cli.StringFlag{
Name: "pprofaddr",
Usage: "pprof HTTP server listening interface",
Value: "127.0.0.1",
}
memprofilerateFlag = cli.IntFlag{ memprofilerateFlag = cli.IntFlag{
Name: "memprofilerate", Name: "memprofilerate",
Usage: "Turn on memory profiling with the given rate", Usage: "Turn on memory profiling with the given rate",
...@@ -74,7 +79,7 @@ var ( ...@@ -74,7 +79,7 @@ var (
// Flags holds all command-line flags required for debugging. // Flags holds all command-line flags required for debugging.
var Flags = []cli.Flag{ var Flags = []cli.Flag{
verbosityFlag, vmoduleFlag, backtraceAtFlag, verbosityFlag, vmoduleFlag, backtraceAtFlag,
pprofFlag, pprofPortFlag, pprofFlag, pprofAddrFlag, pprofPortFlag,
memprofilerateFlag, blockprofilerateFlag, cpuprofileFlag, traceFlag, memprofilerateFlag, blockprofilerateFlag, cpuprofileFlag, traceFlag,
} }
...@@ -101,7 +106,7 @@ func Setup(ctx *cli.Context) error { ...@@ -101,7 +106,7 @@ func Setup(ctx *cli.Context) error {
// pprof server // pprof server
if ctx.GlobalBool(pprofFlag.Name) { if ctx.GlobalBool(pprofFlag.Name) {
address := fmt.Sprintf("127.0.0.1:%d", ctx.GlobalInt(pprofPortFlag.Name)) address := fmt.Sprintf("%s:%d", ctx.GlobalString(pprofAddrFlag.Name), ctx.GlobalInt(pprofPortFlag.Name))
go func() { go func() {
glog.V(logger.Info).Infof("starting pprof server at http://%s/debug/pprof", address) glog.V(logger.Info).Infof("starting pprof server at http://%s/debug/pprof", address)
glog.Errorln(http.ListenAndServe(address, nil)) glog.Errorln(http.ListenAndServe(address, nil))
......
...@@ -79,7 +79,7 @@ func (t *LightTrie) do(ctx context.Context, fallbackKey []byte, fn func() error) ...@@ -79,7 +79,7 @@ func (t *LightTrie) do(ctx context.Context, fallbackKey []byte, fn func() error)
func (t *LightTrie) Get(ctx context.Context, key []byte) (res []byte, err error) { func (t *LightTrie) Get(ctx context.Context, key []byte) (res []byte, err error) {
err = t.do(ctx, key, func() (err error) { err = t.do(ctx, key, func() (err error) {
if t.trie == nil { if t.trie == nil {
t.trie, err = trie.NewSecure(t.originalRoot, t.db) t.trie, err = trie.NewSecure(t.originalRoot, t.db, 0)
} }
if err == nil { if err == nil {
res, err = t.trie.TryGet(key) res, err = t.trie.TryGet(key)
...@@ -98,7 +98,7 @@ func (t *LightTrie) Get(ctx context.Context, key []byte) (res []byte, err error) ...@@ -98,7 +98,7 @@ func (t *LightTrie) Get(ctx context.Context, key []byte) (res []byte, err error)
func (t *LightTrie) Update(ctx context.Context, key, value []byte) (err error) { func (t *LightTrie) Update(ctx context.Context, key, value []byte) (err error) {
err = t.do(ctx, key, func() (err error) { err = t.do(ctx, key, func() (err error) {
if t.trie == nil { if t.trie == nil {
t.trie, err = trie.NewSecure(t.originalRoot, t.db) t.trie, err = trie.NewSecure(t.originalRoot, t.db, 0)
} }
if err == nil { if err == nil {
err = t.trie.TryUpdate(key, value) err = t.trie.TryUpdate(key, value)
...@@ -112,7 +112,7 @@ func (t *LightTrie) Update(ctx context.Context, key, value []byte) (err error) { ...@@ -112,7 +112,7 @@ func (t *LightTrie) Update(ctx context.Context, key, value []byte) (err error) {
func (t *LightTrie) Delete(ctx context.Context, key []byte) (err error) { func (t *LightTrie) Delete(ctx context.Context, key []byte) (err error) {
err = t.do(ctx, key, func() (err error) { err = t.do(ctx, key, func() (err error) {
if t.trie == nil { if t.trie == nil {
t.trie, err = trie.NewSecure(t.originalRoot, t.db) t.trie, err = trie.NewSecure(t.originalRoot, t.db, 0)
} }
if err == nil { if err == nil {
err = t.trie.TryDelete(key) err = t.trie.TryDelete(key)
......
...@@ -26,6 +26,7 @@ import ( ...@@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/rcrowley/go-metrics" "github.com/rcrowley/go-metrics"
"github.com/rcrowley/go-metrics/exp"
) )
// MetricsEnabledFlag is the CLI flag name to use to enable metrics collections. // MetricsEnabledFlag is the CLI flag name to use to enable metrics collections.
...@@ -44,6 +45,7 @@ func init() { ...@@ -44,6 +45,7 @@ func init() {
Enabled = true Enabled = true
} }
} }
exp.Exp(metrics.DefaultRegistry)
} }
// NewMeter create a new metrics Meter, either a real one of a NOP stub depending // NewMeter create a new metrics Meter, either a real one of a NOP stub depending
......
// 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 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
// 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
}
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),
// explicitly set to nil to indicate
// this rule does not apply to homestead.
CreateBySuicide: nil,
}
// GasTableHomestead contain the gas re-prices for
// the homestead phase.
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),
CreateBySuicide: big.NewInt(25000),
}
)
...@@ -71,4 +71,5 @@ var ( ...@@ -71,4 +71,5 @@ var (
SuicideRefundGas = big.NewInt(24000) // Refunded following a suicide operation. 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. 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. 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.
) )
...@@ -19,6 +19,8 @@ package params ...@@ -19,6 +19,8 @@ package params
import "math/big" import "math/big"
var ( var (
TestNetHomesteadBlock = big.NewInt(494000) // Testnet homestead block TestNetHomesteadBlock = big.NewInt(494000) // Testnet homestead block
MainNetHomesteadBlock = big.NewInt(1150000) // Mainnet homestead block MainNetHomesteadBlock = big.NewInt(1150000) // Mainnet homestead block
TestNetHomesteadGasRepriceBlock = big.NewInt(1783000) // Test net gas reprice block
MainNetHomesteadGasRepriceBlock = big.NewInt(2463000) // Main net gas reprice block
) )
...@@ -23,63 +23,63 @@ import ( ...@@ -23,63 +23,63 @@ import (
) )
func TestBcValidBlockTests(t *testing.T) { func TestBcValidBlockTests(t *testing.T) {
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcValidBlockTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcValidBlockTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcUncleHeaderValidityTests(t *testing.T) { func TestBcUncleHeaderValidityTests(t *testing.T) {
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcUncleHeaderValiditiy.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcUncleHeaderValiditiy.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcUncleTests(t *testing.T) { func TestBcUncleTests(t *testing.T) {
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcUncleTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcUncleTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcForkUncleTests(t *testing.T) { func TestBcForkUncleTests(t *testing.T) {
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcForkUncle.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcForkUncle.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcInvalidHeaderTests(t *testing.T) { func TestBcInvalidHeaderTests(t *testing.T) {
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcInvalidHeaderTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcInvalidHeaderTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcInvalidRLPTests(t *testing.T) { func TestBcInvalidRLPTests(t *testing.T) {
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcInvalidRLPTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcInvalidRLPTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcRPCAPITests(t *testing.T) { func TestBcRPCAPITests(t *testing.T) {
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcRPC_API_Test.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcRPC_API_Test.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcForkBlockTests(t *testing.T) { func TestBcForkBlockTests(t *testing.T) {
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcForkBlockTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcForkBlockTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcForkStress(t *testing.T) { func TestBcForkStress(t *testing.T) {
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcForkStressTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcForkStressTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -89,21 +89,21 @@ func TestBcTotalDifficulty(t *testing.T) { ...@@ -89,21 +89,21 @@ func TestBcTotalDifficulty(t *testing.T) {
// skip because these will fail due to selfish mining fix // skip because these will fail due to selfish mining fix
t.Skip() t.Skip()
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcTotalDifficultyTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcTotalDifficultyTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcWallet(t *testing.T) { func TestBcWallet(t *testing.T) {
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcWalletTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcWalletTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcGasPricer(t *testing.T) { func TestBcGasPricer(t *testing.T) {
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcGasPricerTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcGasPricerTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -111,7 +111,7 @@ func TestBcGasPricer(t *testing.T) { ...@@ -111,7 +111,7 @@ func TestBcGasPricer(t *testing.T) {
// TODO: iterate over files once we got more than a few // TODO: iterate over files once we got more than a few
func TestBcRandom(t *testing.T) { func TestBcRandom(t *testing.T) {
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "RandomTests/bl201507071825GO.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, big.NewInt(10), filepath.Join(blockTestDir, "RandomTests/bl201507071825GO.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -121,14 +121,14 @@ func TestBcMultiChain(t *testing.T) { ...@@ -121,14 +121,14 @@ func TestBcMultiChain(t *testing.T) {
// skip due to selfish mining // skip due to selfish mining
t.Skip() t.Skip()
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcMultiChainTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, big.NewInt(10), filepath.Join(blockTestDir, "bcMultiChainTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcState(t *testing.T) { func TestBcState(t *testing.T) {
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcStateTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(1000000), nil, big.NewInt(10), filepath.Join(blockTestDir, "bcStateTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -136,77 +136,77 @@ func TestBcState(t *testing.T) { ...@@ -136,77 +136,77 @@ func TestBcState(t *testing.T) {
// Homestead tests // Homestead tests
func TestHomesteadBcValidBlockTests(t *testing.T) { func TestHomesteadBcValidBlockTests(t *testing.T) {
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcValidBlockTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcValidBlockTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcUncleHeaderValidityTests(t *testing.T) { func TestHomesteadBcUncleHeaderValidityTests(t *testing.T) {
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcUncleHeaderValiditiy.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcUncleHeaderValiditiy.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcUncleTests(t *testing.T) { func TestHomesteadBcUncleTests(t *testing.T) {
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcUncleTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcUncleTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcInvalidHeaderTests(t *testing.T) { func TestHomesteadBcInvalidHeaderTests(t *testing.T) {
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcInvalidHeaderTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcInvalidHeaderTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcRPCAPITests(t *testing.T) { func TestHomesteadBcRPCAPITests(t *testing.T) {
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcRPC_API_Test.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcRPC_API_Test.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcForkStress(t *testing.T) { func TestHomesteadBcForkStress(t *testing.T) {
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcForkStressTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcForkStressTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcTotalDifficulty(t *testing.T) { func TestHomesteadBcTotalDifficulty(t *testing.T) {
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcTotalDifficultyTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcTotalDifficultyTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcWallet(t *testing.T) { func TestHomesteadBcWallet(t *testing.T) {
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcWalletTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcWalletTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcGasPricer(t *testing.T) { func TestHomesteadBcGasPricer(t *testing.T) {
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcGasPricerTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcGasPricerTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcMultiChain(t *testing.T) { func TestHomesteadBcMultiChain(t *testing.T) {
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcMultiChainTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcMultiChainTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcState(t *testing.T) { func TestHomesteadBcState(t *testing.T) {
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcStateTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcStateTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -214,26 +214,33 @@ func TestHomesteadBcState(t *testing.T) { ...@@ -214,26 +214,33 @@ func TestHomesteadBcState(t *testing.T) {
// DAO hard-fork tests // DAO hard-fork tests
func TestDAOBcTheDao(t *testing.T) { func TestDAOBcTheDao(t *testing.T) {
err := RunBlockTest(big.NewInt(5), big.NewInt(8), filepath.Join(blockTestDir, "TestNetwork", "bcTheDaoTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(5), big.NewInt(8), nil, filepath.Join(blockTestDir, "TestNetwork", "bcTheDaoTest.json"), BlockSkipTests)
if err != nil {
t.Fatal(err)
}
}
func TestEIP150Bc(t *testing.T) {
err := RunBlockTest(big.NewInt(0), big.NewInt(8), big.NewInt(10), filepath.Join(blockTestDir, "TestNetwork", "bcEIP150Test.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcExploit(t *testing.T) { func TestHomesteadBcExploit(t *testing.T) {
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcExploitTest.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcExploitTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcShanghaiLove(t *testing.T) { func TestHomesteadBcShanghaiLove(t *testing.T) {
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcShanghaiLove.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcShanghaiLove.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcSuicideIssue(t *testing.T) { func TestHomesteadBcSuicideIssue(t *testing.T) {
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcSuicideIssue.json"), BlockSkipTests) err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcSuicideIssue.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
......
...@@ -104,7 +104,7 @@ type btTransaction struct { ...@@ -104,7 +104,7 @@ type btTransaction struct {
Value string Value string
} }
func RunBlockTestWithReader(homesteadBlock, daoForkBlock *big.Int, r io.Reader, skipTests []string) error { func RunBlockTestWithReader(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, r io.Reader, skipTests []string) error {
btjs := make(map[string]*btJSON) btjs := make(map[string]*btJSON)
if err := readJson(r, &btjs); err != nil { if err := readJson(r, &btjs); err != nil {
return err return err
...@@ -115,13 +115,13 @@ func RunBlockTestWithReader(homesteadBlock, daoForkBlock *big.Int, r io.Reader, ...@@ -115,13 +115,13 @@ func RunBlockTestWithReader(homesteadBlock, daoForkBlock *big.Int, r io.Reader,
return err return err
} }
if err := runBlockTests(homesteadBlock, daoForkBlock, bt, skipTests); err != nil { if err := runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork, bt, skipTests); err != nil {
return err return err
} }
return nil return nil
} }
func RunBlockTest(homesteadBlock, daoForkBlock *big.Int, file string, skipTests []string) error { func RunBlockTest(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, file string, skipTests []string) error {
btjs := make(map[string]*btJSON) btjs := make(map[string]*btJSON)
if err := readJsonFile(file, &btjs); err != nil { if err := readJsonFile(file, &btjs); err != nil {
return err return err
...@@ -131,13 +131,13 @@ func RunBlockTest(homesteadBlock, daoForkBlock *big.Int, file string, skipTests ...@@ -131,13 +131,13 @@ func RunBlockTest(homesteadBlock, daoForkBlock *big.Int, file string, skipTests
if err != nil { if err != nil {
return err return err
} }
if err := runBlockTests(homesteadBlock, daoForkBlock, bt, skipTests); err != nil { if err := runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork, bt, skipTests); err != nil {
return err return err
} }
return nil return nil
} }
func runBlockTests(homesteadBlock, daoForkBlock *big.Int, bt map[string]*BlockTest, skipTests []string) error { func runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, bt map[string]*BlockTest, skipTests []string) error {
skipTest := make(map[string]bool, len(skipTests)) skipTest := make(map[string]bool, len(skipTests))
for _, name := range skipTests { for _, name := range skipTests {
skipTest[name] = true skipTest[name] = true
...@@ -149,7 +149,7 @@ func runBlockTests(homesteadBlock, daoForkBlock *big.Int, bt map[string]*BlockTe ...@@ -149,7 +149,7 @@ func runBlockTests(homesteadBlock, daoForkBlock *big.Int, bt map[string]*BlockTe
continue continue
} }
// test the block // test the block
if err := runBlockTest(homesteadBlock, daoForkBlock, test); err != nil { if err := runBlockTest(homesteadBlock, daoForkBlock, gasPriceFork, test); err != nil {
return fmt.Errorf("%s: %v", name, err) return fmt.Errorf("%s: %v", name, err)
} }
glog.Infoln("Block test passed: ", name) glog.Infoln("Block test passed: ", name)
...@@ -158,7 +158,7 @@ func runBlockTests(homesteadBlock, daoForkBlock *big.Int, bt map[string]*BlockTe ...@@ -158,7 +158,7 @@ func runBlockTests(homesteadBlock, daoForkBlock *big.Int, bt map[string]*BlockTe
return nil return nil
} }
func runBlockTest(homesteadBlock, daoForkBlock *big.Int, test *BlockTest) error { func runBlockTest(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, test *BlockTest) error {
// import pre accounts & construct test genesis block & state root // import pre accounts & construct test genesis block & state root
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
if _, err := test.InsertPreState(db); err != nil { if _, err := test.InsertPreState(db); err != nil {
...@@ -170,7 +170,7 @@ func runBlockTest(homesteadBlock, daoForkBlock *big.Int, test *BlockTest) error ...@@ -170,7 +170,7 @@ func runBlockTest(homesteadBlock, daoForkBlock *big.Int, test *BlockTest) error
core.WriteCanonicalHash(db, test.Genesis.Hash(), test.Genesis.NumberU64()) core.WriteCanonicalHash(db, test.Genesis.Hash(), test.Genesis.NumberU64())
core.WriteHeadBlockHash(db, test.Genesis.Hash()) core.WriteHeadBlockHash(db, test.Genesis.Hash())
evmux := new(event.TypeMux) evmux := new(event.TypeMux)
config := &core.ChainConfig{HomesteadBlock: homesteadBlock, DAOForkBlock: daoForkBlock, DAOForkSupport: true} config := &core.ChainConfig{HomesteadBlock: homesteadBlock, DAOForkBlock: daoForkBlock, DAOForkSupport: true, HomesteadGasRepriceBlock: gasPriceFork}
chain, err := core.NewBlockChain(db, config, ethash.NewShared(), evmux) chain, err := core.NewBlockChain(db, config, ethash.NewShared(), evmux)
if err != nil { if err != nil {
return err return err
......
{
"contractCreationOOGdontLeaveEmptyContract" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x02b8feb0",
"currentGasLimit" : "0x0f4240",
"currentNumber" : "0x257da8",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"1000000000000000000000000000000000000001" : {
"balance" : "0x00",
"code" : "0x60106001557f6001600155601080600c6000396000f3006000355415600957005b6020356000600052602060006000f0",
"nonce" : "0x01",
"storage" : {
"0x01" : "0x10"
}
},
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
"balance" : "0x016a48",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x1c58",
"code" : "0x",
"nonce" : "0x01",
"storage" : {
}
}
},
"postStateRoot" : "35df33d2a146ff660bb837914781857715d1b8752371b2f3e0768f29dd484775",
"pre" : {
"1000000000000000000000000000000000000001" : {
"balance" : "0x00",
"code" : "0x60106001557f6001600155601080600c6000396000f3006000355415600957005b6020356000600052602060006000f0",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0186a0",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "0x016b80",
"gasPrice" : "0x01",
"nonce" : "0x00",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "1000000000000000000000000000000000000001",
"value" : "0x00"
}
},
"contractCreationOOGdontLeaveEmptyContractViaTransaction" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x02b8feb0",
"currentGasLimit" : "0x0f4240",
"currentNumber" : "0x257da8",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"1000000000000000000000000000000000000001" : {
"balance" : "0x0186a0",
"code" : "0x6001600155",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x10c8e0",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"b94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0186a0",
"code" : "0x6040600060406000600073100000000000000000000000000000000000000161c350f1",
"nonce" : "0x00",
"storage" : {
}
}
},
"postStateRoot" : "e83fc77d3037af4447bc5e67db8c646240473cd77125129d085d4ffeddbb5e4f",
"pre" : {
"1000000000000000000000000000000000000001" : {
"balance" : "0x0186a0",
"code" : "0x6001600155",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x10c8e0",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"b94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0186a0",
"code" : "0x6040600060406000600073100000000000000000000000000000000000000161c350f1",
"nonce" : "0x00",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x6040600060406000600073100000000000000000000000000000000000000161c350f1",
"gasLimit" : "0xcf08",
"gasPrice" : "0x01",
"nonce" : "0x00",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "",
"value" : "0x00"
}
},
"createContractViaContract" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x02b8feb0",
"currentGasLimit" : "0x0f4240",
"currentNumber" : "0x257da8",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"1000000000000000000000000000000000000001" : {
"balance" : "0x00",
"code" : "0x600060006000f0",
"nonce" : "0x01",
"storage" : {
}
},
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
"balance" : "0xcf11",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643" : {
"balance" : "0x00",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0xb78f",
"code" : "0x",
"nonce" : "0x01",
"storage" : {
}
}
},
"postStateRoot" : "373942284112628b5c8cfd0f36d8058934fe6861cd7224feb3ac2d03739da305",
"pre" : {
"1000000000000000000000000000000000000001" : {
"balance" : "0x00",
"code" : "0x600060006000f0",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0186a0",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "0x0186a0",
"gasPrice" : "0x01",
"nonce" : "0x00",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "1000000000000000000000000000000000000001",
"value" : "0x00"
}
},
"createContractViaContractOOGInitCode" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x02b8feb0",
"currentGasLimit" : "0x0f4240",
"currentNumber" : "0x257da8",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"1000000000000000000000000000000000000001" : {
"balance" : "0x00",
"code" : "0x6b602060406000f0600c600055600052600c60146000f0",
"nonce" : "0x01",
"storage" : {
}
},
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
"balance" : "0x019728",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0f31b8",
"code" : "0x",
"nonce" : "0x01",
"storage" : {
}
}
},
"postStateRoot" : "f079b93388a39a4c343c66a917a044fb0d91de079786366d0c6c3b01bee07e7b",
"pre" : {
"1000000000000000000000000000000000000001" : {
"balance" : "0x00",
"code" : "0x6b602060406000f0600c600055600052600c60146000f0",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x10c8e0",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "0x019a54",
"gasPrice" : "0x01",
"nonce" : "0x00",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "1000000000000000000000000000000000000001",
"value" : "0x00"
}
},
"createContractViaTransactionCost53000" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x02b8feb0",
"currentGasLimit" : "0x0f4240",
"currentNumber" : "0x257da8",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
"balance" : "0xcf08",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"6295ee1b4f6dd65047762f924ecd367c17eabf8f" : {
"balance" : "0x00",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0xb798",
"code" : "0x",
"nonce" : "0x01",
"storage" : {
}
}
},
"postStateRoot" : "53f5b84edd82703a225e53e9ae3639729eb8e337098531456998af602b0ded0a",
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0186a0",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "0x0186a0",
"gasPrice" : "0x01",
"nonce" : "0x00",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "",
"value" : "0x00"
}
}
}
\ No newline at end of file
此差异已折叠。
此差异已折叠。
...@@ -440,3 +440,238 @@ func TestHomesteadBounds(t *testing.T) { ...@@ -440,3 +440,238 @@ func TestHomesteadBounds(t *testing.T) {
t.Error(err) t.Error(err)
} }
} }
// EIP150 tests
func TestEIP150Specific(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "stEIPSpecificTest.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150SingleCodeGasPrice(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "stEIPSingleCodeGasPrices.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150MemExpandingCalls(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "stMemExpandingEIPCalls.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadStateSystemOperations(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stSystemOperationsTest.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadStatePreCompiledContracts(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stPreCompiledContracts.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadStateRecursiveCreate(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stSpecialTest.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadStateRefund(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stRefundTest.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadStateInitCode(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stInitCodeTest.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadStateLog(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stLogTests.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadStateTransaction(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stTransactionTest.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadCallCreateCallCode(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stCallCreateCallCodeTest.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadCallCodes(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stCallCodes.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadMemory(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stMemoryTest.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadMemoryStress(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
if os.Getenv("TEST_VM_COMPLEX") == "" {
t.Skip()
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stMemoryStressTest.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadQuadraticComplexity(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
if os.Getenv("TEST_VM_COMPLEX") == "" {
t.Skip()
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stQuadraticComplexityTest.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadWallet(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stWalletTest.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadDelegateCodes(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stCallDelegateCodes.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadDelegateCodesCallCode(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stCallDelegateCodesCallCode.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
func TestEIP150HomesteadBounds(t *testing.T) {
ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
HomesteadGasRepriceBlock: big.NewInt(2457000),
}
fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stBoundsTest.json")
if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err)
}
}
...@@ -30,6 +30,7 @@ import ( ...@@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
) )
var ( var (
...@@ -148,15 +149,24 @@ type VmTest struct { ...@@ -148,15 +149,24 @@ type VmTest struct {
} }
type RuleSet struct { type RuleSet struct {
HomesteadBlock *big.Int HomesteadBlock *big.Int
DAOForkBlock *big.Int DAOForkBlock *big.Int
DAOForkSupport bool DAOForkSupport bool
HomesteadGasRepriceBlock *big.Int
} }
func (r RuleSet) IsHomestead(n *big.Int) bool { func (r RuleSet) IsHomestead(n *big.Int) bool {
return n.Cmp(r.HomesteadBlock) >= 0 return n.Cmp(r.HomesteadBlock) >= 0
} }
func (r RuleSet) GasTable(num *big.Int) params.GasTable {
if r.HomesteadGasRepriceBlock == nil || num == nil || num.Cmp(r.HomesteadGasRepriceBlock) < 0 {
return params.GasTableHomestead
}
return params.GasTableHomesteadGasRepriceFork
}
type Env struct { type Env struct {
ruleSet RuleSet ruleSet RuleSet
depth int depth int
......
...@@ -225,7 +225,7 @@ func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, ...@@ -225,7 +225,7 @@ func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs,
caller := state.GetOrNewStateObject(from) caller := state.GetOrNewStateObject(from)
vmenv := NewEnvFromMap(RuleSet{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true}, state, env, exec) vmenv := NewEnvFromMap(RuleSet{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true, nil}, state, env, exec)
vmenv.vmTest = true vmenv.vmTest = true
vmenv.skipTransfer = true vmenv.skipTransfer = true
vmenv.initial = true vmenv.initial = true
......
此差异已折叠。
...@@ -56,11 +56,11 @@ func (it *Iterator) makeKey() []byte { ...@@ -56,11 +56,11 @@ func (it *Iterator) makeKey() []byte {
key := it.keyBuf[:0] key := it.keyBuf[:0]
for _, se := range it.nodeIt.stack { for _, se := range it.nodeIt.stack {
switch node := se.node.(type) { switch node := se.node.(type) {
case fullNode: case *fullNode:
if se.child <= 16 { if se.child <= 16 {
key = append(key, byte(se.child)) key = append(key, byte(se.child))
} }
case shortNode: case *shortNode:
if hasTerm(node.Key) { if hasTerm(node.Key) {
key = append(key, node.Key[:len(node.Key)-1]...) key = append(key, node.Key[:len(node.Key)-1]...)
} else { } else {
...@@ -148,7 +148,7 @@ func (it *NodeIterator) step() error { ...@@ -148,7 +148,7 @@ func (it *NodeIterator) step() error {
if (ancestor == common.Hash{}) { if (ancestor == common.Hash{}) {
ancestor = parent.parent ancestor = parent.parent
} }
if node, ok := parent.node.(fullNode); ok { if node, ok := parent.node.(*fullNode); ok {
// Full node, traverse all children, then the node itself // Full node, traverse all children, then the node itself
if parent.child >= len(node.Children) { if parent.child >= len(node.Children) {
break break
...@@ -156,7 +156,7 @@ func (it *NodeIterator) step() error { ...@@ -156,7 +156,7 @@ func (it *NodeIterator) step() error {
for parent.child++; parent.child < len(node.Children); parent.child++ { for parent.child++; parent.child < len(node.Children); parent.child++ {
if current := node.Children[parent.child]; current != nil { if current := node.Children[parent.child]; current != nil {
it.stack = append(it.stack, &nodeIteratorState{ it.stack = append(it.stack, &nodeIteratorState{
hash: common.BytesToHash(node.hash), hash: common.BytesToHash(node.flags.hash),
node: current, node: current,
parent: ancestor, parent: ancestor,
child: -1, child: -1,
...@@ -164,14 +164,14 @@ func (it *NodeIterator) step() error { ...@@ -164,14 +164,14 @@ func (it *NodeIterator) step() error {
break break
} }
} }
} else if node, ok := parent.node.(shortNode); ok { } else if node, ok := parent.node.(*shortNode); ok {
// Short node, traverse the pointer singleton child, then the node itself // Short node, traverse the pointer singleton child, then the node itself
if parent.child >= 0 { if parent.child >= 0 {
break break
} }
parent.child++ parent.child++
it.stack = append(it.stack, &nodeIteratorState{ it.stack = append(it.stack, &nodeIteratorState{
hash: common.BytesToHash(node.hash), hash: common.BytesToHash(node.flags.hash),
node: node.Val, node: node.Val,
parent: ancestor, parent: ancestor,
child: -1, child: -1,
......
此差异已折叠。
// 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 trie
import "testing"
func TestCanUnload(t *testing.T) {
tests := []struct {
flag nodeFlag
cachegen, cachelimit uint16
want bool
}{
{
flag: nodeFlag{dirty: true, gen: 0},
want: false,
},
{
flag: nodeFlag{dirty: false, gen: 0},
cachegen: 0, cachelimit: 0,
want: true,
},
{
flag: nodeFlag{dirty: false, gen: 65534},
cachegen: 65535, cachelimit: 1,
want: true,
},
{
flag: nodeFlag{dirty: false, gen: 65534},
cachegen: 0, cachelimit: 1,
want: true,
},
{
flag: nodeFlag{dirty: false, gen: 1},
cachegen: 65535, cachelimit: 1,
want: true,
},
}
for _, test := range tests {
if got := test.flag.canUnload(test.cachegen, test.cachelimit); got != test.want {
t.Errorf("%+v\n got %t, want %t", test, got, test.want)
}
}
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册