aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/bench_test.go15
-rw-r--r--core/block_validator.go45
-rw-r--r--core/block_validator_test.go14
-rw-r--r--core/blockchain.go379
-rw-r--r--core/blockchain_test.go279
-rw-r--r--core/chain_indexer.go3
-rw-r--r--core/chain_makers.go82
-rw-r--r--core/chain_makers_test.go10
-rw-r--r--core/dao_test.go38
-rw-r--r--core/database_util_test.go14
-rw-r--r--core/evm.go2
-rw-r--r--core/gaspool.go34
-rw-r--r--core/gen_genesis.go6
-rw-r--r--core/gen_genesis_account.go8
-rw-r--r--core/genesis.go28
-rw-r--r--core/genesis_test.go6
-rw-r--r--core/state/database.go51
-rw-r--r--core/state/iterator_test.go17
-rw-r--r--core/state/journal.go2
-rw-r--r--core/state/state_object.go5
-rw-r--r--core/state/state_test.go6
-rw-r--r--core/state/statedb.go55
-rw-r--r--core/state/statedb_test.go18
-rw-r--r--core/state/sync_test.go44
-rw-r--r--core/state_processor.go34
-rw-r--r--core/state_transition.go131
-rw-r--r--core/tx_list.go13
-rw-r--r--core/tx_list_test.go3
-rw-r--r--core/tx_pool.go21
-rw-r--r--core/tx_pool_test.go285
-rw-r--r--core/types.go6
-rw-r--r--core/types/block.go27
-rw-r--r--core/types/block_test.go6
-rw-r--r--core/types/gen_header_json.go22
-rw-r--r--core/types/gen_log_json.go6
-rw-r--r--core/types/gen_receipt_json.go23
-rw-r--r--core/types/gen_tx_json.go14
-rw-r--r--core/types/receipt.go40
-rw-r--r--core/types/transaction.go69
-rw-r--r--core/types/transaction_signing_test.go8
-rw-r--r--core/types/transaction_test.go10
-rw-r--r--core/vm/evm.go45
-rw-r--r--core/vm/gas_table.go6
-rw-r--r--core/vm/gen_structlog.go34
-rw-r--r--core/vm/instructions.go2
-rw-r--r--core/vm/interface.go4
-rw-r--r--core/vm/interpreter.go23
-rw-r--r--core/vm/logger.go50
-rw-r--r--core/vm/noop.go4
-rw-r--r--core/vm/runtime/env.go4
50 files changed, 1287 insertions, 764 deletions
diff --git a/core/bench_test.go b/core/bench_test.go
index ab25c27d3..e23f0d19d 100644
--- a/core/bench_test.go
+++ b/core/bench_test.go
@@ -84,7 +84,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
return func(i int, gen *BlockGen) {
toaddr := common.Address{}
data := make([]byte, nbytes)
- gas := IntrinsicGas(data, false, false)
+ gas, _ := IntrinsicGas(data, false, false)
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey)
gen.AddTx(tx)
}
@@ -93,7 +93,6 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
var (
ringKeys = make([]*ecdsa.PrivateKey, 1000)
ringAddrs = make([]common.Address, len(ringKeys))
- bigTxGas = new(big.Int).SetUint64(params.TxGas)
)
func init() {
@@ -113,8 +112,8 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
return func(i int, gen *BlockGen) {
gas := CalcGasLimit(gen.PrevBlock(i - 1))
for {
- gas.Sub(gas, bigTxGas)
- if gas.Cmp(bigTxGas) < 0 {
+ gas -= params.TxGas
+ if gas < params.TxGas {
break
}
to := (from + 1) % naccounts
@@ -122,7 +121,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
gen.TxNonce(ringAddrs[from]),
ringAddrs[to],
benchRootFunds,
- bigTxGas,
+ params.TxGas,
nil,
nil,
)
@@ -170,11 +169,11 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
Alloc: GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}},
}
genesis := gspec.MustCommit(db)
- chain, _ := GenerateChain(gspec.Config, genesis, db, b.N, gen)
+ chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, b.N, gen)
// Time the insertion of the new chain.
// State and blocks are stored in the same DB.
- chainman, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
+ chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer chainman.Stop()
b.ReportAllocs()
b.ResetTimer()
@@ -284,7 +283,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err)
}
- chain, err := NewBlockChain(db, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
+ chain, err := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
if err != nil {
b.Fatalf("error creating chain: %v", err)
}
diff --git a/core/block_validator.go b/core/block_validator.go
index e9cfd0482..98958809b 100644
--- a/core/block_validator.go
+++ b/core/block_validator.go
@@ -18,9 +18,7 @@ package core
import (
"fmt"
- "math/big"
- "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
@@ -52,11 +50,14 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engin
// validated at this point.
func (v *BlockValidator) ValidateBody(block *types.Block) error {
// Check whether the block's known, and if not, that it's linkable
- if v.bc.HasBlockAndState(block.Hash()) {
+ if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
return ErrKnownBlock
}
- if !v.bc.HasBlockAndState(block.ParentHash()) {
- return consensus.ErrUnknownAncestor
+ if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
+ if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
+ return consensus.ErrUnknownAncestor
+ }
+ return consensus.ErrPrunedAncestor
}
// Header validity is known at this point, check the uncles and transactions
header := block.Header()
@@ -76,10 +77,10 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
// transition, such as amount of used gas, the receipt roots and the state root
// itself. ValidateState returns a database batch if the validation was a success
// otherwise nil and an error is returned.
-func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas *big.Int) error {
+func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error {
header := block.Header()
- if block.GasUsed().Cmp(usedGas) != 0 {
- return fmt.Errorf("invalid gas used (remote: %v local: %v)", block.GasUsed(), usedGas)
+ if block.GasUsed() != usedGas {
+ return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas)
}
// Validate the received block's bloom with the one derived from the generated receipts.
// For valid blocks this should always validate to true.
@@ -101,17 +102,13 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
}
// CalcGasLimit computes the gas limit of the next block after parent.
-// The result may be modified by the caller.
// This is miner strategy, not consensus protocol.
-func CalcGasLimit(parent *types.Block) *big.Int {
+func CalcGasLimit(parent *types.Block) uint64 {
// contrib = (parentGasUsed * 3 / 2) / 1024
- contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3))
- contrib = contrib.Div(contrib, big.NewInt(2))
- contrib = contrib.Div(contrib, params.GasLimitBoundDivisor)
+ contrib := (parent.GasUsed() + parent.GasUsed()/2) / params.GasLimitBoundDivisor
// decay = parentGasLimit / 1024 -1
- decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor)
- decay.Sub(decay, big.NewInt(1))
+ decay := parent.GasLimit()/params.GasLimitBoundDivisor - 1
/*
strategy: gasLimit of block-to-mine is set based on parent's
@@ -120,15 +117,17 @@ func CalcGasLimit(parent *types.Block) *big.Int {
at that usage) the amount increased/decreased depends on how far away
from parentGasLimit * (2/3) parentGasUsed is.
*/
- gl := new(big.Int).Sub(parent.GasLimit(), decay)
- gl = gl.Add(gl, contrib)
- gl.Set(math.BigMax(gl, params.MinGasLimit))
-
+ limit := parent.GasLimit() - decay + contrib
+ if limit < params.MinGasLimit {
+ limit = params.MinGasLimit
+ }
// however, if we're now below the target (TargetGasLimit) we increase the
// limit as much as we can (parentGasLimit / 1024 -1)
- if gl.Cmp(params.TargetGasLimit) < 0 {
- gl.Add(parent.GasLimit(), decay)
- gl.Set(math.BigMin(gl, params.TargetGasLimit))
+ if limit < params.TargetGasLimit {
+ limit = parent.GasLimit() + decay
+ if limit > params.TargetGasLimit {
+ limit = params.TargetGasLimit
+ }
}
- return gl
+ return limit
}
diff --git a/core/block_validator_test.go b/core/block_validator_test.go
index 6d54c2b93..e334b3c3c 100644
--- a/core/block_validator_test.go
+++ b/core/block_validator_test.go
@@ -35,14 +35,14 @@ func TestHeaderVerification(t *testing.T) {
testdb, _ = ethdb.NewMemDatabase()
gspec = &Genesis{Config: params.TestChainConfig}
genesis = gspec.MustCommit(testdb)
- blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
+ blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil)
)
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
headers[i] = block.Header()
}
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
- chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
+ chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
defer chain.Stop()
for i := 0; i < len(blocks); i++ {
@@ -87,7 +87,7 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
testdb, _ = ethdb.NewMemDatabase()
gspec = &Genesis{Config: params.TestChainConfig}
genesis = gspec.MustCommit(testdb)
- blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
+ blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil)
)
headers := make([]*types.Header, len(blocks))
seals := make([]bool, len(blocks))
@@ -106,11 +106,11 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
var results <-chan error
if valid {
- chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
+ chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
chain.Stop()
} else {
- chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{})
+ chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{})
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
chain.Stop()
}
@@ -159,7 +159,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
testdb, _ = ethdb.NewMemDatabase()
gspec = &Genesis{Config: params.TestChainConfig}
genesis = gspec.MustCommit(testdb)
- blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 1024, nil)
+ blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 1024, nil)
)
headers := make([]*types.Header, len(blocks))
seals := make([]bool, len(blocks))
@@ -173,7 +173,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
defer runtime.GOMAXPROCS(old)
// Start the verifications and immediately abort
- chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{})
+ chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{})
defer chain.Stop()
abort, results := chain.engine.VerifyHeaders(chain, headers, seals)
diff --git a/core/blockchain.go b/core/blockchain.go
index 325753c7a..8d141fddb 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -42,6 +42,7 @@ import (
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/hashicorp/golang-lru"
+ "gopkg.in/karalabe/cookiejar.v2/collections/prque"
)
var (
@@ -56,11 +57,20 @@ const (
maxFutureBlocks = 256
maxTimeFutureBlocks = 30
badBlockLimit = 10
+ triesInMemory = 128
// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
BlockChainVersion = 3
)
+// CacheConfig contains the configuration values for the trie caching/pruning
+// that's resident in a blockchain.
+type CacheConfig struct {
+ Disabled bool // Whether to disable trie write caching (archive node)
+ TrieNodeLimit int // Memory limit (MB) at which to flush the current in-memory trie to disk
+ TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
+}
+
// BlockChain represents the canonical chain given a database with a genesis
// block. The Blockchain manages chain imports, reverts, chain reorganisations.
//
@@ -76,10 +86,14 @@ const (
// included in the canonical one where as GetBlockByNumber always represents the
// canonical chain.
type BlockChain struct {
- config *params.ChainConfig // chain & network configuration
+ chainConfig *params.ChainConfig // Chain & network configuration
+ cacheConfig *CacheConfig // Cache configuration for pruning
+
+ db ethdb.Database // Low level persistent database to store final content in
+ triegc *prque.Prque // Priority queue mapping block numbers to tries to gc
+ gcproc time.Duration // Accumulates canonical block processing for trie dumping
hc *HeaderChain
- chainDb ethdb.Database
rmLogsFeed event.Feed
chainFeed event.Feed
chainSideFeed event.Feed
@@ -119,7 +133,13 @@ type BlockChain struct {
// NewBlockChain returns a fully initialised block chain using information
// available in the database. It initialises the default Ethereum Validator and
// Processor.
-func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) {
+func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) {
+ if cacheConfig == nil {
+ cacheConfig = &CacheConfig{
+ TrieNodeLimit: 256 * 1024 * 1024,
+ TrieTimeLimit: 5 * time.Minute,
+ }
+ }
bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit)
blockCache, _ := lru.New(blockCacheLimit)
@@ -127,9 +147,11 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine co
badBlocks, _ := lru.New(badBlockLimit)
bc := &BlockChain{
- config: config,
- chainDb: chainDb,
- stateCache: state.NewDatabase(chainDb),
+ chainConfig: chainConfig,
+ cacheConfig: cacheConfig,
+ db: db,
+ triegc: prque.New(),
+ stateCache: state.NewDatabase(db),
quit: make(chan struct{}),
bodyCache: bodyCache,
bodyRLPCache: bodyRLPCache,
@@ -139,11 +161,11 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine co
vmConfig: vmConfig,
badBlocks: badBlocks,
}
- bc.SetValidator(NewBlockValidator(config, bc, engine))
- bc.SetProcessor(NewStateProcessor(config, bc, engine))
+ bc.SetValidator(NewBlockValidator(chainConfig, bc, engine))
+ bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine))
var err error
- bc.hc, err = NewHeaderChain(chainDb, config, engine, bc.getProcInterrupt)
+ bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.getProcInterrupt)
if err != nil {
return nil, err
}
@@ -180,7 +202,7 @@ func (bc *BlockChain) getProcInterrupt() bool {
// assumes that the chain manager mutex is held.
func (bc *BlockChain) loadLastState() error {
// Restore the last known head block
- head := GetHeadBlockHash(bc.chainDb)
+ head := GetHeadBlockHash(bc.db)
if head == (common.Hash{}) {
// Corrupt or empty database, init from scratch
log.Warn("Empty database, resetting chain")
@@ -196,15 +218,17 @@ func (bc *BlockChain) loadLastState() error {
// Make sure the state associated with the block is available
if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
// Dangling block without a state associated, init from scratch
- log.Warn("Head state missing, resetting chain", "number", currentBlock.Number(), "hash", currentBlock.Hash())
- return bc.Reset()
+ log.Warn("Head state missing, repairing chain", "number", currentBlock.Number(), "hash", currentBlock.Hash())
+ if err := bc.repair(&currentBlock); err != nil {
+ return err
+ }
}
// Everything seems to be fine, set as the head block
bc.currentBlock = currentBlock
// Restore the last known head header
currentHeader := bc.currentBlock.Header()
- if head := GetHeadHeaderHash(bc.chainDb); head != (common.Hash{}) {
+ if head := GetHeadHeaderHash(bc.db); head != (common.Hash{}) {
if header := bc.GetHeaderByHash(head); header != nil {
currentHeader = header
}
@@ -213,7 +237,7 @@ func (bc *BlockChain) loadLastState() error {
// Restore the last known head fast block
bc.currentFastBlock = bc.currentBlock
- if head := GetHeadFastBlockHash(bc.chainDb); head != (common.Hash{}) {
+ if head := GetHeadFastBlockHash(bc.db); head != (common.Hash{}) {
if block := bc.GetBlockByHash(head); block != nil {
bc.currentFastBlock = block
}
@@ -243,7 +267,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
// Rewind the header chain, deleting all block bodies until then
delFn := func(hash common.Hash, num uint64) {
- DeleteBody(bc.chainDb, hash, num)
+ DeleteBody(bc.db, hash, num)
}
bc.hc.SetHead(head, delFn)
currentHeader := bc.hc.CurrentHeader()
@@ -275,10 +299,10 @@ func (bc *BlockChain) SetHead(head uint64) error {
if bc.currentFastBlock == nil {
bc.currentFastBlock = bc.genesisBlock
}
- if err := WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash()); err != nil {
+ if err := WriteHeadBlockHash(bc.db, bc.currentBlock.Hash()); err != nil {
log.Crit("Failed to reset head full block", "err", err)
}
- if err := WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash()); err != nil {
+ if err := WriteHeadFastBlockHash(bc.db, bc.currentFastBlock.Hash()); err != nil {
log.Crit("Failed to reset head fast block", "err", err)
}
return bc.loadLastState()
@@ -292,7 +316,7 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
if block == nil {
return fmt.Errorf("non existent block [%x…]", hash[:4])
}
- if _, err := trie.NewSecure(block.Root(), bc.chainDb, 0); err != nil {
+ if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB(), 0); err != nil {
return err
}
// If all checks out, manually set the head block
@@ -305,21 +329,13 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
}
// GasLimit returns the gas limit of the current HEAD block.
-func (bc *BlockChain) GasLimit() *big.Int {
+func (bc *BlockChain) GasLimit() uint64 {
bc.mu.RLock()
defer bc.mu.RUnlock()
return bc.currentBlock.GasLimit()
}
-// LastBlockHash return the hash of the HEAD block.
-func (bc *BlockChain) LastBlockHash() common.Hash {
- bc.mu.RLock()
- defer bc.mu.RUnlock()
-
- return bc.currentBlock.Hash()
-}
-
// CurrentBlock retrieves the current head block of the canonical chain. The
// block is retrieved from the blockchain's internal cache.
func (bc *BlockChain) CurrentBlock() *types.Block {
@@ -338,15 +354,6 @@ func (bc *BlockChain) CurrentFastBlock() *types.Block {
return bc.currentFastBlock
}
-// Status returns status information about the current chain such as the HEAD Td,
-// the HEAD hash and the hash of the genesis block.
-func (bc *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) {
- bc.mu.RLock()
- defer bc.mu.RUnlock()
-
- return bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64()), bc.currentBlock.Hash(), bc.genesisBlock.Hash()
-}
-
// SetProcessor sets the processor required for making state modifications.
func (bc *BlockChain) SetProcessor(processor Processor) {
bc.procmu.Lock()
@@ -404,7 +411,7 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
log.Crit("Failed to write genesis block TD", "err", err)
}
- if err := WriteBlock(bc.chainDb, genesis); err != nil {
+ if err := WriteBlock(bc.db, genesis); err != nil {
log.Crit("Failed to write genesis block", "err", err)
}
bc.genesisBlock = genesis
@@ -417,6 +424,24 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
return nil
}
+// repair tries to repair the current blockchain by rolling back the current block
+// until one with associated state is found. This is needed to fix incomplete db
+// writes caused either by crashes/power outages, or simply non-committed tries.
+//
+// This method only rolls back the current block. The current header and current
+// fast block are left intact.
+func (bc *BlockChain) repair(head **types.Block) error {
+ for {
+ // Abort if we've rewound to a head block that does have associated state
+ if _, err := state.New((*head).Root(), bc.stateCache); err == nil {
+ log.Info("Rewound blockchain to past state", "number", (*head).Number(), "hash", (*head).Hash())
+ return nil
+ }
+ // Otherwise rewind one block and recheck state availability there
+ (*head) = bc.GetBlock((*head).ParentHash(), (*head).NumberU64()-1)
+ }
+}
+
// Export writes the active chain to the given writer.
func (bc *BlockChain) Export(w io.Writer) error {
return bc.ExportN(w, uint64(0), bc.currentBlock.NumberU64())
@@ -454,22 +479,22 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
// Note, this function assumes that the `mu` mutex is held!
func (bc *BlockChain) insert(block *types.Block) {
// If the block is on a side chain or an unknown one, force other heads onto it too
- updateHeads := GetCanonicalHash(bc.chainDb, block.NumberU64()) != block.Hash()
+ updateHeads := GetCanonicalHash(bc.db, block.NumberU64()) != block.Hash()
// Add the block to the canonical chain number scheme and mark as the head
- if err := WriteCanonicalHash(bc.chainDb, block.Hash(), block.NumberU64()); err != nil {
+ if err := WriteCanonicalHash(bc.db, block.Hash(), block.NumberU64()); err != nil {
log.Crit("Failed to insert block number", "err", err)
}
- if err := WriteHeadBlockHash(bc.chainDb, block.Hash()); err != nil {
+ if err := WriteHeadBlockHash(bc.db, block.Hash()); err != nil {
log.Crit("Failed to insert head block hash", "err", err)
}
bc.currentBlock = block
- // If the block is better than out head or is on a different chain, force update heads
+ // If the block is better than our head or is on a different chain, force update heads
if updateHeads {
bc.hc.SetCurrentHeader(block.Header())
- if err := WriteHeadFastBlockHash(bc.chainDb, block.Hash()); err != nil {
+ if err := WriteHeadFastBlockHash(bc.db, block.Hash()); err != nil {
log.Crit("Failed to insert head fast block hash", "err", err)
}
bc.currentFastBlock = block
@@ -489,7 +514,7 @@ func (bc *BlockChain) GetBody(hash common.Hash) *types.Body {
body := cached.(*types.Body)
return body
}
- body := GetBody(bc.chainDb, hash, bc.hc.GetBlockNumber(hash))
+ body := GetBody(bc.db, hash, bc.hc.GetBlockNumber(hash))
if body == nil {
return nil
}
@@ -505,7 +530,7 @@ func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue {
if cached, ok := bc.bodyRLPCache.Get(hash); ok {
return cached.(rlp.RawValue)
}
- body := GetBodyRLP(bc.chainDb, hash, bc.hc.GetBlockNumber(hash))
+ body := GetBodyRLP(bc.db, hash, bc.hc.GetBlockNumber(hash))
if len(body) == 0 {
return nil
}
@@ -519,21 +544,25 @@ func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool {
if bc.blockCache.Contains(hash) {
return true
}
- ok, _ := bc.chainDb.Has(blockBodyKey(hash, number))
+ ok, _ := bc.db.Has(blockBodyKey(hash, number))
return ok
}
+// HasState checks if state trie is fully present in the database or not.
+func (bc *BlockChain) HasState(hash common.Hash) bool {
+ _, err := bc.stateCache.OpenTrie(hash)
+ return err == nil
+}
+
// HasBlockAndState checks if a block and associated state trie is fully present
// in the database or not, caching it if present.
-func (bc *BlockChain) HasBlockAndState(hash common.Hash) bool {
+func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool {
// Check first that the block itself is known
- block := bc.GetBlockByHash(hash)
+ block := bc.GetBlock(hash, number)
if block == nil {
return false
}
- // Ensure the associated state is also present
- _, err := bc.stateCache.OpenTrie(block.Root())
- return err == nil
+ return bc.HasState(block.Root())
}
// GetBlock retrieves a block from the database by hash and number,
@@ -543,7 +572,7 @@ func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
if block, ok := bc.blockCache.Get(hash); ok {
return block.(*types.Block)
}
- block := GetBlock(bc.chainDb, hash, number)
+ block := GetBlock(bc.db, hash, number)
if block == nil {
return nil
}
@@ -560,13 +589,18 @@ func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block {
// GetBlockByNumber retrieves a block from the database by number, caching it
// (associated with its hash) if found.
func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
- hash := GetCanonicalHash(bc.chainDb, number)
+ hash := GetCanonicalHash(bc.db, number)
if hash == (common.Hash{}) {
return nil
}
return bc.GetBlock(hash, number)
}
+// GetReceiptsByHash retrieves the receipts for all transactions in a given block.
+func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
+ return GetBlockReceipts(bc.db, hash, GetBlockNumber(bc.db, hash))
+}
+
// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
// [deprecated by eth/62]
func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) {
@@ -594,6 +628,12 @@ func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.
return uncles
}
+// TrieNode retrieves a blob of data associated with a trie node (or code hash)
+// either from ephemeral in-memory cache, or from persistent storage.
+func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) {
+ return bc.stateCache.TrieDB().Node(hash)
+}
+
// Stop stops the blockchain service. If any imports are currently in progress
// it will abort them using the procInterrupt.
func (bc *BlockChain) Stop() {
@@ -606,6 +646,33 @@ func (bc *BlockChain) Stop() {
atomic.StoreInt32(&bc.procInterrupt, 1)
bc.wg.Wait()
+
+ // Ensure the state of a recent block is also stored to disk before exiting.
+ // It is fine if this state does not exist (fast start/stop cycle), but it is
+ // advisable to leave an N block gap from the head so 1) a restart loads up
+ // the last N blocks as sync assistance to remote nodes; 2) a restart during
+ // a (small) reorg doesn't require deep reprocesses; 3) chain "repair" from
+ // missing states are constantly tested.
+ //
+ // This may be tuned a bit on mainnet if its too annoying to reprocess the last
+ // N blocks.
+ if !bc.cacheConfig.Disabled {
+ triedb := bc.stateCache.TrieDB()
+ if number := bc.CurrentBlock().NumberU64(); number >= triesInMemory {
+ recent := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - triesInMemory + 1)
+
+ log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root())
+ if err := triedb.Commit(recent.Root(), true); err != nil {
+ log.Error("Failed to commit recent state trie", "err", err)
+ }
+ }
+ for !bc.triegc.Empty() {
+ triedb.Dereference(bc.triegc.PopItem().(common.Hash), common.Hash{})
+ }
+ if size := triedb.Size(); size != 0 {
+ log.Error("Dangling trie nodes after full cleanup")
+ }
+ }
log.Info("Blockchain manager stopped")
}
@@ -650,11 +717,11 @@ func (bc *BlockChain) Rollback(chain []common.Hash) {
}
if bc.currentFastBlock.Hash() == hash {
bc.currentFastBlock = bc.GetBlock(bc.currentFastBlock.ParentHash(), bc.currentFastBlock.NumberU64()-1)
- WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash())
+ WriteHeadFastBlockHash(bc.db, bc.currentFastBlock.Hash())
}
if bc.currentBlock.Hash() == hash {
bc.currentBlock = bc.GetBlock(bc.currentBlock.ParentHash(), bc.currentBlock.NumberU64()-1)
- WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash())
+ WriteHeadBlockHash(bc.db, bc.currentBlock.Hash())
}
}
}
@@ -677,9 +744,9 @@ func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts ty
}
// The used gas can be calculated based on previous receipts
if j == 0 {
- receipts[j].GasUsed = new(big.Int).Set(receipts[j].CumulativeGasUsed)
+ receipts[j].GasUsed = receipts[j].CumulativeGasUsed
} else {
- receipts[j].GasUsed = new(big.Int).Sub(receipts[j].CumulativeGasUsed, receipts[j-1].CumulativeGasUsed)
+ receipts[j].GasUsed = receipts[j].CumulativeGasUsed - receipts[j-1].CumulativeGasUsed
}
// The derived log fields can simply be set from the block and transaction
for k := 0; k < len(receipts[j].Logs); k++ {
@@ -713,7 +780,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
stats = struct{ processed, ignored int32 }{}
start = time.Now()
bytes = 0
- batch = bc.chainDb.NewBatch()
+ batch = bc.db.NewBatch()
)
for i, block := range blockChain {
receipts := receiptChain[i]
@@ -731,7 +798,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
continue
}
// Compute all the non-consensus fields of the receipts
- SetReceiptsData(bc.config, block, receipts)
+ SetReceiptsData(bc.chainConfig, block, receipts)
// Write all the data out into the database
if err := WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()); err != nil {
return i, fmt.Errorf("failed to write block body: %v", err)
@@ -749,7 +816,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
return 0, err
}
bytes += batch.ValueSize()
- batch = bc.chainDb.NewBatch()
+ batch.Reset()
}
}
if batch.ValueSize() > 0 {
@@ -764,7 +831,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
head := blockChain[len(blockChain)-1]
if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
if bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64()).Cmp(td) < 0 {
- if err := WriteHeadFastBlockHash(bc.chainDb, head.Hash()); err != nil {
+ if err := WriteHeadFastBlockHash(bc.db, head.Hash()); err != nil {
log.Crit("Failed to update head fast block hash", "err", err)
}
bc.currentFastBlock = head
@@ -775,15 +842,33 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
log.Info("Imported new block receipts",
"count", stats.processed,
"elapsed", common.PrettyDuration(time.Since(start)),
- "bytes", bytes,
"number", head.Number(),
"hash", head.Hash(),
+ "size", common.StorageSize(bytes),
"ignored", stats.ignored)
return 0, nil
}
-// WriteBlock writes the block to the chain.
-func (bc *BlockChain) WriteBlockAndState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
+var lastWrite uint64
+
+// WriteBlockWithoutState writes only the block and its metadata to the database,
+// but does not write any state. This is used to construct competing side forks
+// up to the point where they exceed the canonical total difficulty.
+func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (err error) {
+ bc.wg.Add(1)
+ defer bc.wg.Done()
+
+ if err := bc.hc.WriteTd(block.Hash(), block.NumberU64(), td); err != nil {
+ return err
+ }
+ if err := WriteBlock(bc.db, block); err != nil {
+ return err
+ }
+ return nil
+}
+
+// WriteBlockWithState writes the block and all associated state to the database.
+func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
bc.wg.Add(1)
defer bc.wg.Done()
@@ -804,17 +889,73 @@ func (bc *BlockChain) WriteBlockAndState(block *types.Block, receipts []*types.R
return NonStatTy, err
}
// Write other block data using a batch.
- batch := bc.chainDb.NewBatch()
+ batch := bc.db.NewBatch()
if err := WriteBlock(batch, block); err != nil {
return NonStatTy, err
}
- if _, err := state.CommitTo(batch, bc.config.IsEIP158(block.Number())); err != nil {
+ root, err := state.Commit(bc.chainConfig.IsEIP158(block.Number()))
+ if err != nil {
return NonStatTy, err
}
+ triedb := bc.stateCache.TrieDB()
+
+ // If we're running an archive node, always flush
+ if bc.cacheConfig.Disabled {
+ if err := triedb.Commit(root, false); err != nil {
+ return NonStatTy, err
+ }
+ } else {
+ // Full but not archive node, do proper garbage collection
+ triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
+ bc.triegc.Push(root, -float32(block.NumberU64()))
+
+ if current := block.NumberU64(); current > triesInMemory {
+ // Find the next state trie we need to commit
+ header := bc.GetHeaderByNumber(current - triesInMemory)
+ chosen := header.Number.Uint64()
+
+ // Only write to disk if we exceeded our memory allowance *and* also have at
+ // least a given number of tries gapped.
+ var (
+ size = triedb.Size()
+ limit = common.StorageSize(bc.cacheConfig.TrieNodeLimit) * 1024 * 1024
+ )
+ if size > limit || bc.gcproc > bc.cacheConfig.TrieTimeLimit {
+ // If we're exceeding limits but haven't reached a large enough memory gap,
+ // warn the user that the system is becoming unstable.
+ if chosen < lastWrite+triesInMemory {
+ switch {
+ case size >= 2*limit:
+ log.Error("Trie memory critical, forcing to disk", "size", size, "limit", limit, "optimum", float64(chosen-lastWrite)/triesInMemory)
+ case bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit:
+ log.Error("Trie timing critical, forcing to disk", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/triesInMemory)
+ case size > limit:
+ log.Warn("Trie memory at dangerous levels", "size", size, "limit", limit, "optimum", float64(chosen-lastWrite)/triesInMemory)
+ case bc.gcproc > bc.cacheConfig.TrieTimeLimit:
+ log.Warn("Trie timing at dangerous levels", "time", bc.gcproc, "limit", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/triesInMemory)
+ }
+ }
+ // If optimum or critical limits reached, write to disk
+ if chosen >= lastWrite+triesInMemory || size >= 2*limit || bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
+ triedb.Commit(header.Root, true)
+ lastWrite = chosen
+ bc.gcproc = 0
+ }
+ }
+ // Garbage collect anything below our required write retention
+ for !bc.triegc.Empty() {
+ root, number := bc.triegc.Pop()
+ if uint64(-number) > chosen {
+ bc.triegc.Push(root, number)
+ break
+ }
+ triedb.Dereference(root.(common.Hash), common.Hash{})
+ }
+ }
+ }
if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil {
return NonStatTy, err
}
-
// If the total difficulty is higher than our known, add it to the canonical chain
// Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
@@ -835,7 +976,7 @@ func (bc *BlockChain) WriteBlockAndState(block *types.Block, receipts []*types.R
return NonStatTy, err
}
// Write hash preimages
- if err := WritePreimages(bc.chainDb, block.NumberU64(), state.Preimages()); err != nil {
+ if err := WritePreimages(bc.db, block.NumberU64(), state.Preimages()); err != nil {
return NonStatTy, err
}
status = CanonStatTy
@@ -927,31 +1068,60 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
if err == nil {
err = bc.Validator().ValidateBody(block)
}
- if err != nil {
- if err == ErrKnownBlock {
- stats.ignored++
- continue
+ switch {
+ case err == ErrKnownBlock:
+ stats.ignored++
+ continue
+
+ case err == consensus.ErrFutureBlock:
+ // Allow up to MaxFuture second in the future blocks. If this limit is exceeded
+ // the chain is discarded and processed at a later time if given.
+ max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks)
+ if block.Time().Cmp(max) > 0 {
+ return i, events, coalescedLogs, fmt.Errorf("future block: %v > %v", block.Time(), max)
}
+ bc.futureBlocks.Add(block.Hash(), block)
+ stats.queued++
+ continue
- if err == consensus.ErrFutureBlock {
- // Allow up to MaxFuture second in the future blocks. If this limit
- // is exceeded the chain is discarded and processed at a later time
- // if given.
- max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks)
- if block.Time().Cmp(max) > 0 {
- return i, events, coalescedLogs, fmt.Errorf("future block: %v > %v", block.Time(), max)
+ case err == consensus.ErrUnknownAncestor && bc.futureBlocks.Contains(block.ParentHash()):
+ bc.futureBlocks.Add(block.Hash(), block)
+ stats.queued++
+ continue
+
+ case err == consensus.ErrPrunedAncestor:
+ // Block competing with the canonical chain, store in the db, but don't process
+ // until the competitor TD goes above the canonical TD
+ localTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64())
+ externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64()-1), block.Difficulty())
+ if localTd.Cmp(externTd) > 0 {
+ if err = bc.WriteBlockWithoutState(block, externTd); err != nil {
+ return i, events, coalescedLogs, err
}
- bc.futureBlocks.Add(block.Hash(), block)
- stats.queued++
continue
}
+ // Competitor chain beat canonical, gather all blocks from the common ancestor
+ var winner []*types.Block
- if err == consensus.ErrUnknownAncestor && bc.futureBlocks.Contains(block.ParentHash()) {
- bc.futureBlocks.Add(block.Hash(), block)
- stats.queued++
- continue
+ parent := bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
+ for !bc.HasState(parent.Root()) {
+ winner = append(winner, parent)
+ parent = bc.GetBlock(parent.ParentHash(), parent.NumberU64()-1)
+ }
+ for j := 0; j < len(winner)/2; j++ {
+ winner[j], winner[len(winner)-1-j] = winner[len(winner)-1-j], winner[j]
+ }
+ // Import all the pruned blocks to make the state available
+ bc.chainmu.Unlock()
+ _, evs, logs, err := bc.insertChain(winner)
+ bc.chainmu.Lock()
+ events, coalescedLogs = evs, logs
+
+ if err != nil {
+ return i, events, coalescedLogs, err
}
+ case err != nil:
bc.reportBlock(block, nil, err)
return i, events, coalescedLogs, err
}
@@ -979,8 +1149,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
bc.reportBlock(block, receipts, err)
return i, events, coalescedLogs, err
}
+ proctime := time.Since(bstart)
+
// Write the block to the chain and get the status.
- status, err := bc.WriteBlockAndState(block, receipts, state)
+ status, err := bc.WriteBlockWithState(block, receipts, state)
if err != nil {
return i, events, coalescedLogs, err
}
@@ -994,6 +1166,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
events = append(events, ChainEvent{block, block.Hash(), logs})
lastCanon = block
+ // Only count canonical blocks for GC processing time
+ bc.gcproc += proctime
+
case SideStatTy:
log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed",
common.PrettyDuration(time.Since(bstart)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()))
@@ -1002,11 +1177,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
events = append(events, ChainSideEvent{block})
}
stats.processed++
- stats.usedGas += usedGas.Uint64()
- stats.report(chain, i)
+ stats.usedGas += usedGas
+ stats.report(chain, i, bc.stateCache.TrieDB().Size())
}
// Append a single chain head event if we've progressed the chain
- if lastCanon != nil && bc.LastBlockHash() == lastCanon.Hash() {
+ if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
events = append(events, ChainHeadEvent{lastCanon})
}
return 0, events, coalescedLogs, nil
@@ -1026,7 +1201,7 @@ const statsReportLimit = 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) {
+func (st *insertStats) report(chain []*types.Block, index int, cache common.StorageSize) {
// Fetch the timings for the batch
var (
now = mclock.Now()
@@ -1041,7 +1216,7 @@ func (st *insertStats) report(chain []*types.Block, index int) {
context := []interface{}{
"blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000,
"elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed),
- "number", end.Number(), "hash", end.Hash(),
+ "number", end.Number(), "hash", end.Hash(), "cache", cache,
}
if st.queued > 0 {
context = append(context, []interface{}{"queued", st.queued}...)
@@ -1077,7 +1252,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
// These logs are later announced as deleted.
collectLogs = func(h common.Hash) {
// Coalesce logs and set 'Removed'.
- receipts := GetBlockReceipts(bc.chainDb, h, bc.hc.GetBlockNumber(h))
+ receipts := GetBlockReceipts(bc.db, h, bc.hc.GetBlockNumber(h))
for _, receipt := range receipts {
for _, log := range receipt.Logs {
del := *log
@@ -1140,24 +1315,23 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
} else {
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash())
}
+ // Insert the new chain, taking care of the proper incremental order
var addedTxs types.Transactions
- // insert blocks. Order does not matter. Last block will be written in ImportChain itself which creates the new head properly
- for _, block := range newChain {
+ for i := len(newChain) - 1; i >= 0; i-- {
// insert the block in the canonical way, re-writing history
- bc.insert(block)
+ bc.insert(newChain[i])
// write lookup entries for hash based transaction/receipt searches
- if err := WriteTxLookupEntries(bc.chainDb, block); err != nil {
+ if err := WriteTxLookupEntries(bc.db, newChain[i]); err != nil {
return err
}
- addedTxs = append(addedTxs, block.Transactions()...)
+ addedTxs = append(addedTxs, newChain[i].Transactions()...)
}
-
// calculate the difference between deleted and added transactions
diff := types.TxDifference(deletedTxs, addedTxs)
// When transactions get deleted from the database that means the
// receipts that were created in the fork must also be deleted
for _, tx := range diff {
- DeleteTxLookupEntry(bc.chainDb, tx.Hash())
+ DeleteTxLookupEntry(bc.db, tx.Hash())
}
if len(deletedLogs) > 0 {
go bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs})
@@ -1196,10 +1370,11 @@ func (bc *BlockChain) PostChainEvents(events []interface{}, logs []*types.Log) {
}
func (bc *BlockChain) update() {
- futureTimer := time.Tick(5 * time.Second)
+ futureTimer := time.NewTicker(5 * time.Second)
+ defer futureTimer.Stop()
for {
select {
- case <-futureTimer:
+ case <-futureTimer.C:
bc.procFutureBlocks()
case <-bc.quit:
return
@@ -1248,7 +1423,7 @@ Hash: 0x%x
Error: %v
##############################
-`, bc.config, block.Number(), block.Hash(), receiptString, err))
+`, bc.chainConfig, block.Number(), block.Hash(), receiptString, err))
}
// InsertHeaderChain attempts to insert the given header chain in to the local
@@ -1355,7 +1530,7 @@ func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
}
// Config retrieves the blockchain's chain configuration.
-func (bc *BlockChain) Config() *params.ChainConfig { return bc.config }
+func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig }
// Engine retrieves the blockchain's consensus engine.
func (bc *BlockChain) Engine() consensus.Engine { return bc.engine }
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index cb1df8d4b..635379161 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -46,7 +46,7 @@ func newTestBlockChain(fake bool) *BlockChain {
if !fake {
engine = ethash.NewTester()
}
- blockchain, err := NewBlockChain(db, gspec.Config, engine, vm.Config{})
+ blockchain, err := NewBlockChain(db, nil, gspec.Config, engine, vm.Config{})
if err != nil {
panic(err)
}
@@ -57,7 +57,7 @@ func newTestBlockChain(fake bool) *BlockChain {
// Test fork of length N starting from block i
func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, comparator func(td1, td2 *big.Int)) {
// Copy old chain up to #i into a new db
- db, blockchain2, err := newCanonical(i, full)
+ db, blockchain2, err := newCanonical(ethash.NewFaker(), i, full)
if err != nil {
t.Fatal("could not make new canonical in testFork", err)
}
@@ -81,12 +81,12 @@ func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, compara
headerChainB []*types.Header
)
if full {
- blockChainB = makeBlockChain(blockchain2.CurrentBlock(), n, db, forkSeed)
+ blockChainB = makeBlockChain(blockchain2.CurrentBlock(), n, ethash.NewFaker(), db, forkSeed)
if _, err := blockchain2.InsertChain(blockChainB); err != nil {
t.Fatalf("failed to insert forking chain: %v", err)
}
} else {
- headerChainB = makeHeaderChain(blockchain2.CurrentHeader(), n, db, forkSeed)
+ headerChainB = makeHeaderChain(blockchain2.CurrentHeader(), n, ethash.NewFaker(), db, forkSeed)
if _, err := blockchain2.InsertHeaderChain(headerChainB, 1); err != nil {
t.Fatalf("failed to insert forking chain: %v", err)
}
@@ -148,9 +148,9 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
return err
}
blockchain.mu.Lock()
- WriteTd(blockchain.chainDb, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
- WriteBlock(blockchain.chainDb, block)
- statedb.CommitTo(blockchain.chainDb, false)
+ WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
+ WriteBlock(blockchain.db, block)
+ statedb.Commit(false)
blockchain.mu.Unlock()
}
return nil
@@ -166,8 +166,8 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error
}
// Manually insert the header into the database, but don't reorganise (allows subsequent testing)
blockchain.mu.Lock()
- WriteTd(blockchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
- WriteHeader(blockchain.chainDb, header)
+ WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
+ WriteHeader(blockchain.db, header)
blockchain.mu.Unlock()
}
return nil
@@ -186,9 +186,9 @@ func TestLastBlock(t *testing.T) {
bchain := newTestBlockChain(false)
defer bchain.Stop()
- block := makeBlockChain(bchain.CurrentBlock(), 1, bchain.chainDb, 0)[0]
+ block := makeBlockChain(bchain.CurrentBlock(), 1, ethash.NewFaker(), bchain.db, 0)[0]
bchain.insert(block)
- if block.Hash() != GetHeadBlockHash(bchain.chainDb) {
+ if block.Hash() != GetHeadBlockHash(bchain.db) {
t.Errorf("Write/Get HeadBlockHash failed")
}
}
@@ -202,7 +202,7 @@ func testExtendCanonical(t *testing.T, full bool) {
length := 5
// Make first chain starting from genesis
- _, processor, err := newCanonical(length, full)
+ _, processor, err := newCanonical(ethash.NewFaker(), length, full)
if err != nil {
t.Fatalf("failed to make new canonical chain: %v", err)
}
@@ -230,7 +230,7 @@ func testShorterFork(t *testing.T, full bool) {
length := 10
// Make first chain starting from genesis
- _, processor, err := newCanonical(length, full)
+ _, processor, err := newCanonical(ethash.NewFaker(), length, full)
if err != nil {
t.Fatalf("failed to make new canonical chain: %v", err)
}
@@ -260,7 +260,7 @@ func testLongerFork(t *testing.T, full bool) {
length := 10
// Make first chain starting from genesis
- _, processor, err := newCanonical(length, full)
+ _, processor, err := newCanonical(ethash.NewFaker(), length, full)
if err != nil {
t.Fatalf("failed to make new canonical chain: %v", err)
}
@@ -290,7 +290,7 @@ func testEqualFork(t *testing.T, full bool) {
length := 10
// Make first chain starting from genesis
- _, processor, err := newCanonical(length, full)
+ _, processor, err := newCanonical(ethash.NewFaker(), length, full)
if err != nil {
t.Fatalf("failed to make new canonical chain: %v", err)
}
@@ -317,7 +317,7 @@ func TestBrokenBlockChain(t *testing.T) { testBrokenChain(t, true) }
func testBrokenChain(t *testing.T, full bool) {
// Make chain starting from genesis
- db, blockchain, err := newCanonical(10, full)
+ db, blockchain, err := newCanonical(ethash.NewFaker(), 10, full)
if err != nil {
t.Fatalf("failed to make new canonical chain: %v", err)
}
@@ -325,12 +325,12 @@ func testBrokenChain(t *testing.T, full bool) {
// Create a forked chain, and try to insert with a missing link
if full {
- chain := makeBlockChain(blockchain.CurrentBlock(), 5, db, forkSeed)[1:]
+ chain := makeBlockChain(blockchain.CurrentBlock(), 5, ethash.NewFaker(), db, forkSeed)[1:]
if err := testBlockChainImport(chain, blockchain); err == nil {
t.Errorf("broken block chain not reported")
}
} else {
- chain := makeHeaderChain(blockchain.CurrentHeader(), 5, db, forkSeed)[1:]
+ chain := makeHeaderChain(blockchain.CurrentHeader(), 5, ethash.NewFaker(), db, forkSeed)[1:]
if err := testHeaderChainImport(chain, blockchain); err == nil {
t.Errorf("broken header chain not reported")
}
@@ -340,11 +340,11 @@ func testBrokenChain(t *testing.T, full bool) {
type bproc struct{}
func (bproc) ValidateBody(*types.Block) error { return nil }
-func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error {
+func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error {
return nil
}
-func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, *big.Int, error) {
- return nil, nil, new(big.Int), nil
+func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
+ return nil, nil, 0, nil
}
func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header {
@@ -496,7 +496,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
}
// Create a new BlockChain and check that it rolled back the state.
- ncm, err := NewBlockChain(bc.chainDb, bc.config, ethash.NewFaker(), vm.Config{})
+ ncm, err := NewBlockChain(bc.db, nil, bc.chainConfig, ethash.NewFaker(), vm.Config{})
if err != nil {
t.Fatalf("failed to create new chain manager: %v", err)
}
@@ -506,8 +506,8 @@ func testReorgBadHashes(t *testing.T, full bool) {
if ncm.CurrentBlock().Hash() != blocks[2].Header().Hash() {
t.Errorf("last block hash mismatch: have: %x, want %x", ncm.CurrentBlock().Hash(), blocks[2].Header().Hash())
}
- if blocks[2].Header().GasLimit.Cmp(ncm.GasLimit()) != 0 {
- t.Errorf("last block gasLimit mismatch: have: %x, want %x", ncm.GasLimit(), blocks[2].Header().GasLimit)
+ if blocks[2].Header().GasLimit != ncm.GasLimit() {
+ t.Errorf("last block gasLimit mismatch: have: %d, want %d", ncm.GasLimit(), blocks[2].Header().GasLimit)
}
} else {
if ncm.CurrentHeader().Hash() != headers[2].Hash() {
@@ -523,7 +523,7 @@ func TestBlocksInsertNonceError(t *testing.T) { testInsertNonceError(t, true) }
func testInsertNonceError(t *testing.T, full bool) {
for i := 1; i < 25 && !t.Failed(); i++ {
// Create a pristine chain and database
- db, blockchain, err := newCanonical(0, full)
+ db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full)
if err != nil {
t.Fatalf("failed to create pristine chain: %v", err)
}
@@ -536,7 +536,7 @@ func testInsertNonceError(t *testing.T, full bool) {
failNum uint64
)
if full {
- blocks := makeBlockChain(blockchain.CurrentBlock(), i, db, 0)
+ blocks := makeBlockChain(blockchain.CurrentBlock(), i, ethash.NewFaker(), db, 0)
failAt = rand.Int() % len(blocks)
failNum = blocks[failAt].NumberU64()
@@ -544,7 +544,7 @@ func testInsertNonceError(t *testing.T, full bool) {
blockchain.engine = ethash.NewFakeFailer(failNum)
failRes, err = blockchain.InsertChain(blocks)
} else {
- headers := makeHeaderChain(blockchain.CurrentHeader(), i, db, 0)
+ headers := makeHeaderChain(blockchain.CurrentHeader(), i, ethash.NewFaker(), db, 0)
failAt = rand.Int() % len(headers)
failNum = headers[failAt].Number.Uint64()
@@ -588,13 +588,13 @@ func TestFastVsFullChains(t *testing.T) {
genesis = gspec.MustCommit(gendb)
signer = types.NewEIP155Signer(gspec.Config.ChainId)
)
- blocks, receipts := GenerateChain(gspec.Config, genesis, gendb, 1024, func(i int, block *BlockGen) {
+ blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, 1024, func(i int, block *BlockGen) {
block.SetCoinbase(common.Address{0x00})
// If the block number is multiple of 3, send a few bonus transactions to the miner
if i%3 == 2 {
for j := 0; j < i%4+1; j++ {
- tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), bigTxGas, nil, nil), signer, key)
+ tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil), signer, key)
if err != nil {
panic(err)
}
@@ -609,7 +609,7 @@ func TestFastVsFullChains(t *testing.T) {
// Import the chain as an archive node for the comparison baseline
archiveDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(archiveDb)
- archive, _ := NewBlockChain(archiveDb, gspec.Config, ethash.NewFaker(), vm.Config{})
+ archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer archive.Stop()
if n, err := archive.InsertChain(blocks); err != nil {
@@ -618,7 +618,7 @@ func TestFastVsFullChains(t *testing.T) {
// Fast import the chain as a non-archive node to test
fastDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(fastDb)
- fast, _ := NewBlockChain(fastDb, gspec.Config, ethash.NewFaker(), vm.Config{})
+ fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer fast.Stop()
headers := make([]*types.Header, len(blocks))
@@ -673,7 +673,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
genesis = gspec.MustCommit(gendb)
)
height := uint64(1024)
- blocks, receipts := GenerateChain(gspec.Config, genesis, gendb, int(height), nil)
+ blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), nil)
// Configure a subchain to roll back
remove := []common.Hash{}
@@ -696,7 +696,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
archiveDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(archiveDb)
- archive, _ := NewBlockChain(archiveDb, gspec.Config, ethash.NewFaker(), vm.Config{})
+ archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err)
}
@@ -709,7 +709,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
// Import the chain as a non-archive node and ensure all pointers are updated
fastDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(fastDb)
- fast, _ := NewBlockChain(fastDb, gspec.Config, ethash.NewFaker(), vm.Config{})
+ fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer fast.Stop()
headers := make([]*types.Header, len(blocks))
@@ -730,7 +730,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
lightDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(lightDb)
- light, _ := NewBlockChain(lightDb, gspec.Config, ethash.NewFaker(), vm.Config{})
+ light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert header %d: %v", n, err)
}
@@ -767,8 +767,8 @@ func TestChainTxReorgs(t *testing.T) {
// Create two transactions shared between the chains:
// - postponed: transaction included at a later block in the forked chain
// - swapped: transaction included at the same block number in the forked chain
- postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
- swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
+ postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
+ swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
// Create two transactions that will be dropped by the forked chain:
// - pastDrop: transaction dropped retroactively from a past block
@@ -781,16 +781,16 @@ func TestChainTxReorgs(t *testing.T) {
// - futureAdd: transaction added after the reorg has already finished
var pastAdd, freshAdd, futureAdd *types.Transaction
- chain, _ := GenerateChain(gspec.Config, genesis, db, 3, func(i int, gen *BlockGen) {
+ chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {
switch i {
case 0:
- pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
+ pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point
gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork
case 2:
- freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
+ freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point
gen.AddTx(swapped) // This transaction will be swapped out at the exact height
@@ -799,28 +799,28 @@ func TestChainTxReorgs(t *testing.T) {
}
})
// Import the chain. This runs all block validation rules.
- blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
+ blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
if i, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
}
defer blockchain.Stop()
// overwrite the old chain
- chain, _ = GenerateChain(gspec.Config, genesis, db, 5, func(i int, gen *BlockGen) {
+ chain, _ = GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 5, func(i int, gen *BlockGen) {
switch i {
case 0:
- pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
+ pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
gen.AddTx(pastAdd) // This transaction needs to be injected during reorg
case 2:
gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain
gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain
- freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
+ freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time
case 3:
- futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
+ futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
}
})
@@ -870,14 +870,14 @@ func TestLogReorgs(t *testing.T) {
signer = types.NewEIP155Signer(gspec.Config.ChainId)
)
- blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
+ blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop()
rmLogsCh := make(chan RemovedLogsEvent)
blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
- chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 2, func(i int, gen *BlockGen) {
+ chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
if i == 1 {
- tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), code), signer, key1)
+ tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1)
if err != nil {
t.Fatalf("failed to create tx: %v", err)
}
@@ -888,7 +888,7 @@ func TestLogReorgs(t *testing.T) {
t.Fatalf("failed to insert chain: %v", err)
}
- chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {})
+ chain, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
if _, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert forked chain: %v", err)
}
@@ -917,16 +917,16 @@ func TestReorgSideEvent(t *testing.T) {
signer = types.NewEIP155Signer(gspec.Config.ChainId)
)
- blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
+ blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop()
- chain, _ := GenerateChain(gspec.Config, genesis, db, 3, func(i int, gen *BlockGen) {})
+ chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
if _, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert chain: %v", err)
}
- replacementBlocks, _ := GenerateChain(gspec.Config, genesis, db, 4, func(i int, gen *BlockGen) {
- tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil), signer, key1)
+ replacementBlocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, gen *BlockGen) {
+ tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil), signer, key1)
if i == 2 {
gen.OffsetTime(-9)
}
@@ -992,7 +992,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
bc := newTestBlockChain(true)
defer bc.Stop()
- chain, _ := GenerateChain(bc.config, bc.genesisBlock, bc.chainDb, 10, func(i int, gen *BlockGen) {})
+ chain, _ := GenerateChain(bc.chainConfig, bc.genesisBlock, ethash.NewFaker(), bc.db, 10, func(i int, gen *BlockGen) {})
var pend sync.WaitGroup
pend.Add(len(chain))
@@ -1003,14 +1003,14 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
// try to retrieve a block by its canonical hash and see if the block data can be retrieved.
for {
- ch := GetCanonicalHash(bc.chainDb, block.NumberU64())
+ ch := GetCanonicalHash(bc.db, block.NumberU64())
if ch == (common.Hash{}) {
continue // busy wait for canonical hash to be written
}
if ch != block.Hash() {
t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
}
- fb := GetBlock(bc.chainDb, ch, block.NumberU64())
+ fb := GetBlock(bc.db, ch, block.NumberU64())
if fb == nil {
t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
}
@@ -1043,15 +1043,15 @@ func TestEIP155Transition(t *testing.T) {
genesis = gspec.MustCommit(db)
)
- blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
+ blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop()
- blocks, _ := GenerateChain(gspec.Config, genesis, db, 4, func(i int, block *BlockGen) {
+ blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
var (
tx *types.Transaction
err error
basicTx = func(signer types.Signer) (*types.Transaction, error) {
- return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
+ return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key)
}
)
switch i {
@@ -1109,12 +1109,12 @@ func TestEIP155Transition(t *testing.T) {
// generate an invalid chain id transaction
config := &params.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
- blocks, _ = GenerateChain(config, blocks[len(blocks)-1], db, 4, func(i int, block *BlockGen) {
+ blocks, _ = GenerateChain(config, blocks[len(blocks)-1], ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
var (
tx *types.Transaction
err error
basicTx = func(signer types.Signer) (*types.Transaction, error) {
- return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
+ return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key)
}
)
switch i {
@@ -1151,10 +1151,10 @@ func TestEIP161AccountRemoval(t *testing.T) {
}
genesis = gspec.MustCommit(db)
)
- blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
+ blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop()
- blocks, _ := GenerateChain(gspec.Config, genesis, db, 3, func(i int, block *BlockGen) {
+ blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) {
var (
tx *types.Transaction
err error
@@ -1162,11 +1162,11 @@ func TestEIP161AccountRemoval(t *testing.T) {
)
switch i {
case 0:
- tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
+ tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key)
case 1:
- tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
+ tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key)
case 2:
- tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
+ tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key)
}
if err != nil {
t.Fatal(err)
@@ -1197,3 +1197,150 @@ func TestEIP161AccountRemoval(t *testing.T) {
t.Error("account should not exist")
}
}
+
+// This is a regression test (i.e. as weird as it is, don't delete it ever), which
+// tests that under weird reorg conditions the blockchain and its internal header-
+// chain return the same latest block/header.
+//
+// https://github.com/ethereum/go-ethereum/pull/15941
+func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
+ // Generate a canonical chain to act as the main dataset
+ engine := ethash.NewFaker()
+
+ db, _ := ethdb.NewMemDatabase()
+ genesis := new(Genesis).MustCommit(db)
+ blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
+
+ // Generate a bunch of fork blocks, each side forking from the canonical chain
+ forks := make([]*types.Block, len(blocks))
+ for i := 0; i < len(forks); i++ {
+ parent := genesis
+ if i > 0 {
+ parent = blocks[i-1]
+ }
+ fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
+ forks[i] = fork[0]
+ }
+ // Import the canonical and fork chain side by side, verifying the current block
+ // and current header consistency
+ diskdb, _ := ethdb.NewMemDatabase()
+ new(Genesis).MustCommit(diskdb)
+
+ chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
+ if err != nil {
+ t.Fatalf("failed to create tester chain: %v", err)
+ }
+ for i := 0; i < len(blocks); i++ {
+ if _, err := chain.InsertChain(blocks[i : i+1]); err != nil {
+ t.Fatalf("block %d: failed to insert into chain: %v", i, err)
+ }
+ if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
+ t.Errorf("block %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
+ }
+ if _, err := chain.InsertChain(forks[i : i+1]); err != nil {
+ t.Fatalf(" fork %d: failed to insert into chain: %v", i, err)
+ }
+ if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
+ t.Errorf(" fork %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
+ }
+ }
+}
+
+// Tests that importing small side forks doesn't leave junk in the trie database
+// cache (which would eventually cause memory issues).
+func TestTrieForkGC(t *testing.T) {
+ // Generate a canonical chain to act as the main dataset
+ engine := ethash.NewFaker()
+
+ db, _ := ethdb.NewMemDatabase()
+ genesis := new(Genesis).MustCommit(db)
+ blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
+
+ // Generate a bunch of fork blocks, each side forking from the canonical chain
+ forks := make([]*types.Block, len(blocks))
+ for i := 0; i < len(forks); i++ {
+ parent := genesis
+ if i > 0 {
+ parent = blocks[i-1]
+ }
+ fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
+ forks[i] = fork[0]
+ }
+ // Import the canonical and fork chain side by side, forcing the trie cache to cache both
+ diskdb, _ := ethdb.NewMemDatabase()
+ new(Genesis).MustCommit(diskdb)
+
+ chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
+ if err != nil {
+ t.Fatalf("failed to create tester chain: %v", err)
+ }
+ for i := 0; i < len(blocks); i++ {
+ if _, err := chain.InsertChain(blocks[i : i+1]); err != nil {
+ t.Fatalf("block %d: failed to insert into chain: %v", i, err)
+ }
+ if _, err := chain.InsertChain(forks[i : i+1]); err != nil {
+ t.Fatalf("fork %d: failed to insert into chain: %v", i, err)
+ }
+ }
+ // Dereference all the recent tries and ensure no past trie is left in
+ for i := 0; i < triesInMemory; i++ {
+ chain.stateCache.TrieDB().Dereference(blocks[len(blocks)-1-i].Root(), common.Hash{})
+ chain.stateCache.TrieDB().Dereference(forks[len(blocks)-1-i].Root(), common.Hash{})
+ }
+ if len(chain.stateCache.TrieDB().Nodes()) > 0 {
+ t.Fatalf("stale tries still alive after garbase collection")
+ }
+}
+
+// Tests that doing large reorgs works even if the state associated with the
+// forking point is not available any more.
+func TestLargeReorgTrieGC(t *testing.T) {
+ // Generate the original common chain segment and the two competing forks
+ engine := ethash.NewFaker()
+
+ db, _ := ethdb.NewMemDatabase()
+ genesis := new(Genesis).MustCommit(db)
+
+ shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
+ original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
+ competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
+
+ // Import the shared chain and the original canonical one
+ diskdb, _ := ethdb.NewMemDatabase()
+ new(Genesis).MustCommit(diskdb)
+
+ chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
+ if err != nil {
+ t.Fatalf("failed to create tester chain: %v", err)
+ }
+ if _, err := chain.InsertChain(shared); err != nil {
+ t.Fatalf("failed to insert shared chain: %v", err)
+ }
+ if _, err := chain.InsertChain(original); err != nil {
+ t.Fatalf("failed to insert shared chain: %v", err)
+ }
+ // Ensure that the state associated with the forking point is pruned away
+ if node, _ := chain.stateCache.TrieDB().Node(shared[len(shared)-1].Root()); node != nil {
+ t.Fatalf("common-but-old ancestor still cache")
+ }
+ // Import the competitor chain without exceeding the canonical's TD and ensure
+ // we have not processed any of the blocks (protection against malicious blocks)
+ if _, err := chain.InsertChain(competitor[:len(competitor)-2]); err != nil {
+ t.Fatalf("failed to insert competitor chain: %v", err)
+ }
+ for i, block := range competitor[:len(competitor)-2] {
+ if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil {
+ t.Fatalf("competitor %d: low TD chain became processed", i)
+ }
+ }
+ // Import the head of the competitor chain, triggering the reorg and ensure we
+ // successfully reprocess all the stashed away blocks.
+ if _, err := chain.InsertChain(competitor[len(competitor)-2:]); err != nil {
+ t.Fatalf("failed to finalize competitor chain: %v", err)
+ }
+ for i, block := range competitor[:len(competitor)-triesInMemory] {
+ if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil {
+ t.Fatalf("competitor %d: competing chain state missing", i)
+ }
+ }
+}
diff --git a/core/chain_indexer.go b/core/chain_indexer.go
index 7fb184aaa..158ed8324 100644
--- a/core/chain_indexer.go
+++ b/core/chain_indexer.go
@@ -203,6 +203,9 @@ func (c *ChainIndexer) eventLoop(currentHeader *types.Header, events chan ChainE
if header.ParentHash != prevHash {
// Reorg to the common ancestor (might not exist in light sync mode, skip reorg then)
// TODO(karalabe, zsfelfoldi): This seems a bit brittle, can we detect this case explicitly?
+
+ // TODO(karalabe): This operation is expensive and might block, causing the event system to
+ // potentially also lock up. We need to do with on a different thread somehow.
if h := FindCommonAncestor(c.chainDb, prevHeader, header); h != nil {
c.newHead(h.Number.Uint64(), true)
}
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 59af633df..6744428ff 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -21,7 +21,7 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/ethash"
+ "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
@@ -39,11 +39,12 @@ var (
// BlockGen creates blocks for testing.
// See GenerateChain for a detailed explanation.
type BlockGen struct {
- i int
- parent *types.Block
- chain []*types.Block
- header *types.Header
- statedb *state.StateDB
+ i int
+ parent *types.Block
+ chain []*types.Block
+ chainReader consensus.ChainReader
+ header *types.Header
+ statedb *state.StateDB
gasPool *GasPool
txs []*types.Transaction
@@ -51,6 +52,7 @@ type BlockGen struct {
uncles []*types.Header
config *params.ChainConfig
+ engine consensus.Engine
}
// SetCoinbase sets the coinbase of the generated block.
@@ -84,7 +86,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
b.SetCoinbase(common.Address{})
}
b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs))
- receipt, _, err := ApplyTransaction(b.config, nil, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
+ receipt, _, err := ApplyTransaction(b.config, nil, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
if err != nil {
panic(err)
}
@@ -141,7 +143,7 @@ func (b *BlockGen) OffsetTime(seconds int64) {
if b.header.Time.Cmp(b.parent.Header().Time) <= 0 {
panic("block time out of range")
}
- b.header.Difficulty = ethash.CalcDifficulty(b.config, b.header.Time.Uint64(), b.parent.Header())
+ b.header.Difficulty = b.engine.CalcDifficulty(b.chainReader, b.header.Time.Uint64(), b.parent.Header())
}
// GenerateChain creates a chain of n blocks. The first block's
@@ -156,44 +158,57 @@ func (b *BlockGen) OffsetTime(seconds int64) {
// Blocks created by GenerateChain do not contain valid proof of work
// values. Inserting them into BlockChain requires use of FakePow or
// a similar non-validating proof of work implementation.
-func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
+func GenerateChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
if config == nil {
config = params.TestChainConfig
}
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
- genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) {
- b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb, config: config}
+ genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) {
+ // TODO(karalabe): This is needed for clique, which depends on multiple blocks.
+ // It's nonetheless ugly to spin up a blockchain here. Get rid of this somehow.
+ blockchain, _ := NewBlockChain(db, nil, config, engine, vm.Config{})
+ defer blockchain.Stop()
+
+ b := &BlockGen{i: i, parent: parent, chain: blocks, chainReader: blockchain, statedb: statedb, config: config, engine: engine}
+ b.header = makeHeader(b.chainReader, parent, statedb, b.engine)
+
// Mutate the state and block according to any hard-fork specs
if daoBlock := config.DAOForkBlock; daoBlock != nil {
limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
- if h.Number.Cmp(daoBlock) >= 0 && h.Number.Cmp(limit) < 0 {
+ if b.header.Number.Cmp(daoBlock) >= 0 && b.header.Number.Cmp(limit) < 0 {
if config.DAOForkSupport {
- h.Extra = common.CopyBytes(params.DAOForkBlockExtra)
+ b.header.Extra = common.CopyBytes(params.DAOForkBlockExtra)
}
}
}
- if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(h.Number) == 0 {
+ if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 {
misc.ApplyDAOHardFork(statedb)
}
// Execute any user modifications to the block and finalize it
if gen != nil {
gen(i, b)
}
- ethash.AccumulateRewards(config, statedb, h, b.uncles)
- root, err := statedb.CommitTo(db, config.IsEIP158(h.Number))
- if err != nil {
- panic(fmt.Sprintf("state write error: %v", err))
+
+ if b.engine != nil {
+ block, _ := b.engine.Finalize(b.chainReader, b.header, statedb, b.txs, b.uncles, b.receipts)
+ // Write state changes to db
+ root, err := statedb.Commit(config.IsEIP158(b.header.Number))
+ if err != nil {
+ panic(fmt.Sprintf("state write error: %v", err))
+ }
+ if err := statedb.Database().TrieDB().Commit(root, false); err != nil {
+ panic(fmt.Sprintf("trie write error: %v", err))
+ }
+ return block, b.receipts
}
- h.Root = root
- return types.NewBlock(h, b.txs, b.uncles, b.receipts), b.receipts
+ return nil, nil
}
for i := 0; i < n; i++ {
statedb, err := state.New(parent.Root(), state.NewDatabase(db))
if err != nil {
panic(err)
}
- header := makeHeader(config, parent, statedb)
- block, receipt := genblock(i, header, statedb)
+ block, receipt := genblock(i, parent, statedb)
blocks[i] = block
receipts[i] = receipt
parent = block
@@ -201,7 +216,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Dat
return blocks, receipts
}
-func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.StateDB) *types.Header {
+func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header {
var time *big.Int
if parent.Time() == nil {
time = big.NewInt(10)
@@ -210,17 +225,16 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St
}
return &types.Header{
- Root: state.IntermediateRoot(config.IsEIP158(parent.Number())),
+ Root: state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())),
ParentHash: parent.Hash(),
Coinbase: parent.Coinbase(),
- Difficulty: ethash.CalcDifficulty(config, time.Uint64(), &types.Header{
+ Difficulty: engine.CalcDifficulty(chain, time.Uint64(), &types.Header{
Number: parent.Number(),
Time: new(big.Int).Sub(time, big.NewInt(10)),
Difficulty: parent.Difficulty(),
UncleHash: parent.UncleHash(),
}),
GasLimit: CalcGasLimit(parent),
- GasUsed: new(big.Int),
Number: new(big.Int).Add(parent.Number(), common.Big1),
Time: time,
}
@@ -229,32 +243,32 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St
// newCanonical creates a chain database, and injects a deterministic canonical
// chain. Depending on the full flag, if creates either a full block chain or a
// header only chain.
-func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) {
+func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *BlockChain, error) {
// Initialize a fresh chain with only a genesis block
gspec := new(Genesis)
db, _ := ethdb.NewMemDatabase()
genesis := gspec.MustCommit(db)
- blockchain, _ := NewBlockChain(db, params.AllEthashProtocolChanges, ethash.NewFaker(), vm.Config{})
+ blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{})
// Create and inject the requested chain
if n == 0 {
return db, blockchain, nil
}
if full {
// Full block-chain requested
- blocks := makeBlockChain(genesis, n, db, canonicalSeed)
+ blocks := makeBlockChain(genesis, n, engine, db, canonicalSeed)
_, err := blockchain.InsertChain(blocks)
return db, blockchain, err
}
// Header-only chain requested
- headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed)
+ headers := makeHeaderChain(genesis.Header(), n, engine, db, canonicalSeed)
_, err := blockchain.InsertHeaderChain(headers, 1)
return db, blockchain, err
}
// makeHeaderChain creates a deterministic chain of headers rooted at parent.
-func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) []*types.Header {
- blocks := makeBlockChain(types.NewBlockWithHeader(parent), n, db, seed)
+func makeHeaderChain(parent *types.Header, n int, engine consensus.Engine, db ethdb.Database, seed int) []*types.Header {
+ blocks := makeBlockChain(types.NewBlockWithHeader(parent), n, engine, db, seed)
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
headers[i] = block.Header()
@@ -263,8 +277,8 @@ func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) [
}
// makeBlockChain creates a deterministic chain of blocks rooted at parent.
-func makeBlockChain(parent *types.Block, n int, db ethdb.Database, seed int) []*types.Block {
- blocks, _ := GenerateChain(params.TestChainConfig, parent, db, n, func(i int, b *BlockGen) {
+func makeBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethdb.Database, seed int) []*types.Block {
+ blocks, _ := GenerateChain(params.TestChainConfig, parent, engine, db, n, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
})
return blocks
diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go
index 2260c62fb..93be43ddc 100644
--- a/core/chain_makers_test.go
+++ b/core/chain_makers_test.go
@@ -50,17 +50,17 @@ func ExampleGenerateChain() {
// each block and adds different features to gen based on the
// block index.
signer := types.HomesteadSigner{}
- chain, _ := GenerateChain(gspec.Config, genesis, db, 5, func(i int, gen *BlockGen) {
+ chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 5, func(i int, gen *BlockGen) {
switch i {
case 0:
// In block 1, addr1 sends addr2 some ether.
- tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), bigTxGas, nil, nil), signer, key1)
+ tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
gen.AddTx(tx)
case 1:
// In block 2, addr1 sends some more ether to addr2.
// addr2 passes it on to addr3.
- tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
- tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
+ tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
+ tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
gen.AddTx(tx1)
gen.AddTx(tx2)
case 2:
@@ -79,7 +79,7 @@ func ExampleGenerateChain() {
})
// Import the chain. This runs all block validation rules.
- blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
+ blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop()
if i, err := blockchain.InsertChain(chain); err != nil {
diff --git a/core/dao_test.go b/core/dao_test.go
index b9898ff7c..e0a3e3ff3 100644
--- a/core/dao_test.go
+++ b/core/dao_test.go
@@ -35,7 +35,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
gspec := new(Genesis)
genesis := gspec.MustCommit(db)
- prefix, _ := GenerateChain(params.TestChainConfig, genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
+ prefix, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
// Create the concurrent, conflicting two nodes
proDb, _ := ethdb.NewMemDatabase()
@@ -45,7 +45,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
proConf.DAOForkBlock = forkBlock
proConf.DAOForkSupport = true
- proBc, _ := NewBlockChain(proDb, &proConf, ethash.NewFaker(), vm.Config{})
+ proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{})
defer proBc.Stop()
conDb, _ := ethdb.NewMemDatabase()
@@ -55,7 +55,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
conConf.DAOForkBlock = forkBlock
conConf.DAOForkSupport = false
- conBc, _ := NewBlockChain(conDb, &conConf, ethash.NewFaker(), vm.Config{})
+ conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{})
defer conBc.Stop()
if _, err := proBc.InsertChain(prefix); err != nil {
@@ -69,7 +69,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Create a pro-fork block, and try to feed into the no-fork chain
db, _ = ethdb.NewMemDatabase()
gspec.MustCommit(db)
- bc, _ := NewBlockChain(db, &conConf, ethash.NewFaker(), vm.Config{})
+ bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{})
defer bc.Stop()
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
@@ -79,19 +79,22 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
}
- blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
+ if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
+ t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
+ }
+ blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
if _, err := conBc.InsertChain(blocks); err == nil {
t.Fatalf("contra-fork chain accepted pro-fork block: %v", blocks[0])
}
// Create a proper no-fork block for the contra-forker
- blocks, _ = GenerateChain(&conConf, conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
+ blocks, _ = GenerateChain(&conConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
if _, err := conBc.InsertChain(blocks); err != nil {
t.Fatalf("contra-fork chain didn't accepted no-fork block: %v", err)
}
// Create a no-fork block, and try to feed into the pro-fork chain
db, _ = ethdb.NewMemDatabase()
gspec.MustCommit(db)
- bc, _ = NewBlockChain(db, &proConf, ethash.NewFaker(), vm.Config{})
+ bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{})
defer bc.Stop()
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
@@ -101,12 +104,15 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
}
- blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
+ if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
+ t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
+ }
+ blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
if _, err := proBc.InsertChain(blocks); err == nil {
t.Fatalf("pro-fork chain accepted contra-fork block: %v", blocks[0])
}
// Create a proper pro-fork block for the pro-forker
- blocks, _ = GenerateChain(&proConf, proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
+ blocks, _ = GenerateChain(&proConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
if _, err := proBc.InsertChain(blocks); err != nil {
t.Fatalf("pro-fork chain didn't accepted pro-fork block: %v", err)
}
@@ -114,7 +120,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
db, _ = ethdb.NewMemDatabase()
gspec.MustCommit(db)
- bc, _ := NewBlockChain(db, &conConf, ethash.NewFaker(), vm.Config{})
+ bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{})
defer bc.Stop()
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
@@ -124,14 +130,17 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
}
- blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
+ if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
+ t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
+ }
+ blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
if _, err := conBc.InsertChain(blocks); err != nil {
t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err)
}
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes
db, _ = ethdb.NewMemDatabase()
gspec.MustCommit(db)
- bc, _ = NewBlockChain(db, &proConf, ethash.NewFaker(), vm.Config{})
+ bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{})
defer bc.Stop()
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
@@ -141,7 +150,10 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
}
- blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
+ if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
+ t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
+ }
+ blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
if _, err := proBc.InsertChain(blocks); err != nil {
t.Fatalf("pro-fork chain didn't accept contra-fork block post-fork: %v", err)
}
diff --git a/core/database_util_test.go b/core/database_util_test.go
index 36f43cf50..ab4e45a47 100644
--- a/core/database_util_test.go
+++ b/core/database_util_test.go
@@ -290,9 +290,9 @@ func TestHeadStorage(t *testing.T) {
func TestLookupStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
- tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), big.NewInt(1111), big.NewInt(11111), []byte{0x11, 0x11, 0x11})
- tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), big.NewInt(2222), big.NewInt(22222), []byte{0x22, 0x22, 0x22})
- tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), big.NewInt(3333), big.NewInt(33333), []byte{0x33, 0x33, 0x33})
+ tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
+ tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
+ tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
txs := []*types.Transaction{tx1, tx2, tx3}
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)
@@ -337,25 +337,25 @@ func TestBlockReceiptStorage(t *testing.T) {
receipt1 := &types.Receipt{
Status: types.ReceiptStatusFailed,
- CumulativeGasUsed: big.NewInt(1),
+ CumulativeGasUsed: 1,
Logs: []*types.Log{
{Address: common.BytesToAddress([]byte{0x11})},
{Address: common.BytesToAddress([]byte{0x01, 0x11})},
},
TxHash: common.BytesToHash([]byte{0x11, 0x11}),
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
- GasUsed: big.NewInt(111111),
+ GasUsed: 111111,
}
receipt2 := &types.Receipt{
PostState: common.Hash{2}.Bytes(),
- CumulativeGasUsed: big.NewInt(2),
+ CumulativeGasUsed: 2,
Logs: []*types.Log{
{Address: common.BytesToAddress([]byte{0x22})},
{Address: common.BytesToAddress([]byte{0x02, 0x22})},
},
TxHash: common.BytesToHash([]byte{0x22, 0x22}),
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
- GasUsed: big.NewInt(222222),
+ GasUsed: 222222,
}
receipts := []*types.Receipt{receipt1, receipt2}
diff --git a/core/evm.go b/core/evm.go
index 4912aa650..55db53927 100644
--- a/core/evm.go
+++ b/core/evm.go
@@ -53,7 +53,7 @@ func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author
BlockNumber: new(big.Int).Set(header.Number),
Time: new(big.Int).Set(header.Time),
Difficulty: new(big.Int).Set(header.Difficulty),
- GasLimit: new(big.Int).Set(header.GasLimit),
+ GasLimit: header.GasLimit,
GasPrice: new(big.Int).Set(msg.GasPrice()),
}
}
diff --git a/core/gaspool.go b/core/gaspool.go
index ef99908cf..e3795c1ee 100644
--- a/core/gaspool.go
+++ b/core/gaspool.go
@@ -16,31 +16,39 @@
package core
-import "math/big"
+import (
+ "fmt"
+ "math"
+)
-// GasPool tracks the amount of gas available during
-// execution of the transactions in a block.
-// The zero value is a pool with zero gas available.
-type GasPool big.Int
+// GasPool tracks the amount of gas available during execution of the transactions
+// in a block. The zero value is a pool with zero gas available.
+type GasPool uint64
// AddGas makes gas available for execution.
-func (gp *GasPool) AddGas(amount *big.Int) *GasPool {
- i := (*big.Int)(gp)
- i.Add(i, amount)
+func (gp *GasPool) AddGas(amount uint64) *GasPool {
+ if uint64(*gp) > math.MaxUint64-amount {
+ panic("gas pool pushed above uint64")
+ }
+ *(*uint64)(gp) += amount
return gp
}
// SubGas deducts the given amount from the pool if enough gas is
// available and returns an error otherwise.
-func (gp *GasPool) SubGas(amount *big.Int) error {
- i := (*big.Int)(gp)
- if i.Cmp(amount) < 0 {
+func (gp *GasPool) SubGas(amount uint64) error {
+ if uint64(*gp) < amount {
return ErrGasLimitReached
}
- i.Sub(i, amount)
+ *(*uint64)(gp) -= amount
return nil
}
+// Gas returns the amount of gas remaining in the pool.
+func (gp *GasPool) Gas() uint64 {
+ return uint64(*gp)
+}
+
func (gp *GasPool) String() string {
- return (*big.Int)(gp).String()
+ return fmt.Sprintf("%d", *gp)
}
diff --git a/core/gen_genesis.go b/core/gen_genesis.go
index 4d75704a6..bb8ea1d6a 100644
--- a/core/gen_genesis.go
+++ b/core/gen_genesis.go
@@ -13,6 +13,8 @@ import (
"github.com/ethereum/go-ethereum/params"
)
+var _ = (*genesisSpecMarshaling)(nil)
+
func (g Genesis) MarshalJSON() ([]byte, error) {
type Genesis struct {
Config *params.ChainConfig `json:"config"`
@@ -54,7 +56,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
Config *params.ChainConfig `json:"config"`
Nonce *math.HexOrDecimal64 `json:"nonce"`
Timestamp *math.HexOrDecimal64 `json:"timestamp"`
- ExtraData hexutil.Bytes `json:"extraData"`
+ ExtraData *hexutil.Bytes `json:"extraData"`
GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
Mixhash *common.Hash `json:"mixHash"`
@@ -78,7 +80,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
g.Timestamp = uint64(*dec.Timestamp)
}
if dec.ExtraData != nil {
- g.ExtraData = dec.ExtraData
+ g.ExtraData = *dec.ExtraData
}
if dec.GasLimit == nil {
return errors.New("missing required field 'gasLimit' for Genesis")
diff --git a/core/gen_genesis_account.go b/core/gen_genesis_account.go
index 15c9565a2..64fb9b924 100644
--- a/core/gen_genesis_account.go
+++ b/core/gen_genesis_account.go
@@ -38,18 +38,18 @@ func (g GenesisAccount) MarshalJSON() ([]byte, error) {
func (g *GenesisAccount) UnmarshalJSON(input []byte) error {
type GenesisAccount struct {
- Code hexutil.Bytes `json:"code,omitempty"`
+ Code *hexutil.Bytes `json:"code,omitempty"`
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
Nonce *math.HexOrDecimal64 `json:"nonce,omitempty"`
- PrivateKey hexutil.Bytes `json:"secretKey,omitempty"`
+ PrivateKey *hexutil.Bytes `json:"secretKey,omitempty"`
}
var dec GenesisAccount
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.Code != nil {
- g.Code = dec.Code
+ g.Code = *dec.Code
}
if dec.Storage != nil {
g.Storage = make(map[common.Hash]common.Hash, len(dec.Storage))
@@ -65,7 +65,7 @@ func (g *GenesisAccount) UnmarshalJSON(input []byte) error {
g.Nonce = uint64(*dec.Nonce)
}
if dec.PrivateKey != nil {
- g.PrivateKey = dec.PrivateKey
+ g.PrivateKey = *dec.PrivateKey
}
return nil
}
diff --git a/core/genesis.go b/core/genesis.go
index df491ce0f..b6ead2250 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -169,10 +169,9 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
// Check whether the genesis block is already written.
if genesis != nil {
- block, _ := genesis.ToBlock()
- hash := block.Hash()
+ hash := genesis.ToBlock(nil).Hash()
if hash != stored {
- return genesis.Config, block.Hash(), &GenesisMismatchError{stored, hash}
+ return genesis.Config, hash, &GenesisMismatchError{stored, hash}
}
}
@@ -220,9 +219,12 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
}
}
-// ToBlock creates the block and state of a genesis specification.
-func (g *Genesis) ToBlock() (*types.Block, *state.StateDB) {
- db, _ := ethdb.NewMemDatabase()
+// ToBlock creates the genesis block and writes state of a genesis specification
+// to the given database (or discards it if nil).
+func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
+ if db == nil {
+ db, _ = ethdb.NewMemDatabase()
+ }
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
for addr, account := range g.Alloc {
statedb.AddBalance(addr, account.Balance)
@@ -239,8 +241,8 @@ func (g *Genesis) ToBlock() (*types.Block, *state.StateDB) {
Time: new(big.Int).SetUint64(g.Timestamp),
ParentHash: g.ParentHash,
Extra: g.ExtraData,
- GasLimit: new(big.Int).SetUint64(g.GasLimit),
- GasUsed: new(big.Int).SetUint64(g.GasUsed),
+ GasLimit: g.GasLimit,
+ GasUsed: g.GasUsed,
Difficulty: g.Difficulty,
MixDigest: g.Mixhash,
Coinbase: g.Coinbase,
@@ -252,19 +254,19 @@ func (g *Genesis) ToBlock() (*types.Block, *state.StateDB) {
if g.Difficulty == nil {
head.Difficulty = params.GenesisDifficulty
}
- return types.NewBlock(head, nil, nil, nil), statedb
+ statedb.Commit(false)
+ statedb.Database().TrieDB().Commit(root, true)
+
+ return types.NewBlock(head, nil, nil, nil)
}
// Commit writes the block and state of a genesis specification to the database.
// The block is committed as the canonical head block.
func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) {
- block, statedb := g.ToBlock()
+ block := g.ToBlock(db)
if block.Number().Sign() != 0 {
return nil, fmt.Errorf("can't commit genesis block with number > 0")
}
- if _, err := statedb.CommitTo(db, false); err != nil {
- return nil, fmt.Errorf("cannot write state: %v", err)
- }
if err := WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty); err != nil {
return nil, err
}
diff --git a/core/genesis_test.go b/core/genesis_test.go
index 2fe931b24..cd548d4b1 100644
--- a/core/genesis_test.go
+++ b/core/genesis_test.go
@@ -30,11 +30,11 @@ import (
)
func TestDefaultGenesisBlock(t *testing.T) {
- block, _ := DefaultGenesisBlock().ToBlock()
+ block := DefaultGenesisBlock().ToBlock(nil)
if block.Hash() != params.MainnetGenesisHash {
t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash(), params.MainnetGenesisHash)
}
- block, _ = DefaultTestnetGenesisBlock().ToBlock()
+ block = DefaultTestnetGenesisBlock().ToBlock(nil)
if block.Hash() != params.TestnetGenesisHash {
t.Errorf("wrong testnet genesis hash, got %v, want %v", block.Hash(), params.TestnetGenesisHash)
}
@@ -118,7 +118,7 @@ func TestSetupGenesis(t *testing.T) {
// Commit the 'old' genesis block with Homestead transition at #2.
// Advance to block #4, past the homestead transition block of customg.
genesis := oldcustomg.MustCommit(db)
- bc, _ := NewBlockChain(db, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{})
+ bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{})
defer bc.Stop()
bc.SetValidator(bproc{})
bc.InsertChain(makeBlockChainWithDiff(genesis, []int{2, 3, 4, 5}, 0))
diff --git a/core/state/database.go b/core/state/database.go
index 946625e76..36926ec69 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -40,16 +40,23 @@ const (
// Database wraps access to tries and contract code.
type Database interface {
- // Accessing tries:
// OpenTrie opens the main account trie.
- // OpenStorageTrie opens the storage trie of an account.
OpenTrie(root common.Hash) (Trie, error)
+
+ // OpenStorageTrie opens the storage trie of an account.
OpenStorageTrie(addrHash, root common.Hash) (Trie, error)
- // Accessing contract code:
- ContractCode(addrHash, codeHash common.Hash) ([]byte, error)
- ContractCodeSize(addrHash, codeHash common.Hash) (int, error)
+
// CopyTrie returns an independent copy of the given trie.
CopyTrie(Trie) Trie
+
+ // ContractCode retrieves a particular contract's code.
+ ContractCode(addrHash, codeHash common.Hash) ([]byte, error)
+
+ // ContractCodeSize retrieves a particular contracts code's size.
+ ContractCodeSize(addrHash, codeHash common.Hash) (int, error)
+
+ // TrieDB retrieves the low level trie database used for data storage.
+ TrieDB() *trie.Database
}
// Trie is a Ethereum Merkle Trie.
@@ -57,26 +64,33 @@ type Trie interface {
TryGet(key []byte) ([]byte, error)
TryUpdate(key, value []byte) error
TryDelete(key []byte) error
- CommitTo(trie.DatabaseWriter) (common.Hash, error)
+ Commit(onleaf trie.LeafCallback) (common.Hash, error)
Hash() common.Hash
NodeIterator(startKey []byte) trie.NodeIterator
GetKey([]byte) []byte // TODO(fjl): remove this when SecureTrie is removed
+ Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error
}
// NewDatabase creates a backing store for state. The returned database is safe for
-// concurrent use and retains cached trie nodes in memory.
+// concurrent use and retains cached trie nodes in memory. The pool is an optional
+// intermediate trie-node memory pool between the low level storage layer and the
+// high level trie abstraction.
func NewDatabase(db ethdb.Database) Database {
csc, _ := lru.New(codeSizeCacheSize)
- return &cachingDB{db: db, codeSizeCache: csc}
+ return &cachingDB{
+ db: trie.NewDatabase(db),
+ codeSizeCache: csc,
+ }
}
type cachingDB struct {
- db ethdb.Database
+ db *trie.Database
mu sync.Mutex
pastTries []*trie.SecureTrie
codeSizeCache *lru.Cache
}
+// OpenTrie opens the main account trie.
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
db.mu.Lock()
defer db.mu.Unlock()
@@ -105,10 +119,12 @@ func (db *cachingDB) pushTrie(t *trie.SecureTrie) {
}
}
+// OpenStorageTrie opens the storage trie of an account.
func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {
return trie.NewSecure(root, db.db, 0)
}
+// CopyTrie returns an independent copy of the given trie.
func (db *cachingDB) CopyTrie(t Trie) Trie {
switch t := t.(type) {
case cachedTrie:
@@ -120,14 +136,16 @@ func (db *cachingDB) CopyTrie(t Trie) Trie {
}
}
+// ContractCode retrieves a particular contract's code.
func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
- code, err := db.db.Get(codeHash[:])
+ code, err := db.db.Node(codeHash)
if err == nil {
db.codeSizeCache.Add(codeHash, len(code))
}
return code, err
}
+// ContractCodeSize retrieves a particular contracts code's size.
func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) {
if cached, ok := db.codeSizeCache.Get(codeHash); ok {
return cached.(int), nil
@@ -139,16 +157,25 @@ func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, erro
return len(code), err
}
+// TrieDB retrieves any intermediate trie-node caching layer.
+func (db *cachingDB) TrieDB() *trie.Database {
+ return db.db
+}
+
// cachedTrie inserts its trie into a cachingDB on commit.
type cachedTrie struct {
*trie.SecureTrie
db *cachingDB
}
-func (m cachedTrie) CommitTo(dbw trie.DatabaseWriter) (common.Hash, error) {
- root, err := m.SecureTrie.CommitTo(dbw)
+func (m cachedTrie) Commit(onleaf trie.LeafCallback) (common.Hash, error) {
+ root, err := m.SecureTrie.Commit(onleaf)
if err == nil {
m.db.pushTrie(m.SecureTrie)
}
return root, err
}
+
+func (m cachedTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error {
+ return m.SecureTrie.Prove(key, fromLevel, proofDb)
+}
diff --git a/core/state/iterator_test.go b/core/state/iterator_test.go
index ff66ba7a9..9e46c851c 100644
--- a/core/state/iterator_test.go
+++ b/core/state/iterator_test.go
@@ -21,12 +21,13 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/ethdb"
)
// Tests that the node iterator indeed walks over the entire database contents.
func TestNodeIteratorCoverage(t *testing.T) {
// Create some arbitrary test state to iterate
- db, mem, root, _ := makeTestState()
+ db, root, _ := makeTestState()
state, err := New(root, db)
if err != nil {
@@ -39,14 +40,18 @@ func TestNodeIteratorCoverage(t *testing.T) {
hashes[it.Hash] = struct{}{}
}
}
-
- // Cross check the hashes and the database itself
+ // Cross check the iterated hashes and the database/nodepool content
for hash := range hashes {
- if _, err := mem.Get(hash.Bytes()); err != nil {
- t.Errorf("failed to retrieve reported node %x: %v", hash, err)
+ if _, err := db.TrieDB().Node(hash); err != nil {
+ t.Errorf("failed to retrieve reported node %x", hash)
+ }
+ }
+ for _, hash := range db.TrieDB().Nodes() {
+ if _, ok := hashes[hash]; !ok {
+ t.Errorf("state entry not reported %x", hash)
}
}
- for _, key := range mem.Keys() {
+ for _, key := range db.TrieDB().DiskDB().(*ethdb.MemDatabase).Keys() {
if bytes.HasPrefix(key, []byte("secure-key-")) {
continue
}
diff --git a/core/state/journal.go b/core/state/journal.go
index ddc819fe5..a89bb3d13 100644
--- a/core/state/journal.go
+++ b/core/state/journal.go
@@ -62,7 +62,7 @@ type (
// Changes to other state values.
refundChange struct {
- prev *big.Int
+ prev uint64
}
addLogChange struct {
txhash common.Hash
diff --git a/core/state/state_object.go b/core/state/state_object.go
index b2378c69c..b2112bfae 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -25,7 +25,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/trie"
)
var emptyCodeHash = crypto.Keccak256(nil)
@@ -238,12 +237,12 @@ func (self *stateObject) updateRoot(db Database) {
// CommitTrie the storage trie of the object to dwb.
// This updates the trie root.
-func (self *stateObject) CommitTrie(db Database, dbw trie.DatabaseWriter) error {
+func (self *stateObject) CommitTrie(db Database) error {
self.updateTrie(db)
if self.dbErr != nil {
return self.dbErr
}
- root, err := self.trie.CommitTo(dbw)
+ root, err := self.trie.Commit(nil)
if err == nil {
self.data.Root = root
}
diff --git a/core/state/state_test.go b/core/state/state_test.go
index bbae3685b..6d42d63d8 100644
--- a/core/state/state_test.go
+++ b/core/state/state_test.go
@@ -48,7 +48,7 @@ func (s *StateSuite) TestDump(c *checker.C) {
// write some of them to the trie
s.state.updateStateObject(obj1)
s.state.updateStateObject(obj2)
- s.state.CommitTo(s.db, false)
+ s.state.Commit(false)
// check that dump contains the state objects that are in trie
got := string(s.state.Dump())
@@ -97,7 +97,7 @@ func (s *StateSuite) TestNull(c *checker.C) {
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
var value common.Hash
s.state.SetState(address, common.Hash{}, value)
- s.state.CommitTo(s.db, false)
+ s.state.Commit(false)
value = s.state.GetState(address, common.Hash{})
if !common.EmptyHash(value) {
c.Errorf("expected empty hash. got %x", value)
@@ -155,7 +155,7 @@ func TestSnapshot2(t *testing.T) {
so0.deleted = false
state.setStateObject(so0)
- root, _ := state.CommitTo(db, false)
+ root, _ := state.Commit(false)
state.Reset(root)
// and one with deleted == true
diff --git a/core/state/statedb.go b/core/state/statedb.go
index de9fb367d..776693e24 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -36,6 +36,14 @@ type revision struct {
journalIndex int
}
+var (
+ // emptyState is the known hash of an empty state trie entry.
+ emptyState = crypto.Keccak256Hash(nil)
+
+ // emptyCode is the known hash of the empty EVM bytecode.
+ emptyCode = crypto.Keccak256Hash(nil)
+)
+
// StateDBs within the ethereum protocol are used to store anything
// within the merkle trie. StateDBs take care of caching and storing
// nested states. It's the general query interface to retrieve:
@@ -57,7 +65,7 @@ type StateDB struct {
dbErr error
// The refund counter, also used by state transitioning.
- refund *big.Int
+ refund uint64
thash, bhash common.Hash
txIndex int
@@ -86,7 +94,6 @@ func New(root common.Hash, db Database) (*StateDB, error) {
trie: tr,
stateObjects: make(map[common.Address]*stateObject),
stateObjectsDirty: make(map[common.Address]struct{}),
- refund: new(big.Int),
logs: make(map[common.Hash][]*types.Log),
preimages: make(map[common.Hash][]byte),
}, nil
@@ -161,9 +168,9 @@ func (self *StateDB) Preimages() map[common.Hash][]byte {
return self.preimages
}
-func (self *StateDB) AddRefund(gas *big.Int) {
- self.journal = append(self.journal, refundChange{prev: new(big.Int).Set(self.refund)})
- self.refund.Add(self.refund, gas)
+func (self *StateDB) AddRefund(gas uint64) {
+ self.journal = append(self.journal, refundChange{prev: self.refund})
+ self.refund += gas
}
// Exist reports whether the given account address exists in the state.
@@ -236,6 +243,11 @@ func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
return common.Hash{}
}
+// Database retrieves the low level database supporting the lower level trie ops.
+func (self *StateDB) Database() Database {
+ return self.db
+}
+
// StorageTrie returns the storage trie of an account.
// The return value is a copy and is nil for non-existent accounts.
func (self *StateDB) StorageTrie(a common.Address) Trie {
@@ -456,7 +468,7 @@ func (self *StateDB) Copy() *StateDB {
trie: self.db.CopyTrie(self.trie),
stateObjects: make(map[common.Address]*stateObject, len(self.stateObjectsDirty)),
stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)),
- refund: new(big.Int).Set(self.refund),
+ refund: self.refund,
logs: make(map[common.Hash][]*types.Log, len(self.logs)),
logSize: self.logSize,
preimages: make(map[common.Hash][]byte),
@@ -506,9 +518,7 @@ func (self *StateDB) RevertToSnapshot(revid int) {
}
// GetRefund returns the current value of the refund counter.
-// The return value must not be modified by the caller and will become
-// invalid at the next call to AddRefund.
-func (self *StateDB) GetRefund() *big.Int {
+func (self *StateDB) GetRefund() uint64 {
return self.refund
}
@@ -568,11 +578,11 @@ func (s *StateDB) DeleteSuicides() {
func (s *StateDB) clearJournalAndRefund() {
s.journal = nil
s.validRevisions = s.validRevisions[:0]
- s.refund = new(big.Int)
+ s.refund = 0
}
-// CommitTo writes the state to the given database.
-func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) {
+// Commit writes the state to the underlying in-memory trie database.
+func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
defer s.clearJournalAndRefund()
// Commit objects to the trie.
@@ -586,13 +596,11 @@ func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (ro
case isDirty:
// Write any contract code associated with the state object
if stateObject.code != nil && stateObject.dirtyCode {
- if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil {
- return common.Hash{}, err
- }
+ s.db.TrieDB().Insert(common.BytesToHash(stateObject.CodeHash()), stateObject.code)
stateObject.dirtyCode = false
}
// Write any storage changes in the state object to its storage trie.
- if err := stateObject.CommitTrie(s.db, dbw); err != nil {
+ if err := stateObject.CommitTrie(s.db); err != nil {
return common.Hash{}, err
}
// Update the object in the main account trie.
@@ -601,7 +609,20 @@ func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (ro
delete(s.stateObjectsDirty, addr)
}
// Write trie changes.
- root, err = s.trie.CommitTo(dbw)
+ root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error {
+ var account Account
+ if err := rlp.DecodeBytes(leaf, &account); err != nil {
+ return nil
+ }
+ if account.Root != emptyState {
+ s.db.TrieDB().Reference(account.Root, parent)
+ }
+ code := common.BytesToHash(account.CodeHash)
+ if code != emptyCode {
+ s.db.TrieDB().Reference(code, parent)
+ }
+ return nil
+ })
log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads())
return root, err
}
diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go
index e9944cd74..d9e3d9b79 100644
--- a/core/state/statedb_test.go
+++ b/core/state/statedb_test.go
@@ -97,10 +97,10 @@ func TestIntermediateLeaks(t *testing.T) {
}
// Commit and cross check the databases.
- if _, err := transState.CommitTo(transDb, false); err != nil {
+ if _, err := transState.Commit(false); err != nil {
t.Fatalf("failed to commit transition state: %v", err)
}
- if _, err := finalState.CommitTo(finalDb, false); err != nil {
+ if _, err := finalState.Commit(false); err != nil {
t.Fatalf("failed to commit final state: %v", err)
}
for _, key := range finalDb.Keys() {
@@ -122,8 +122,8 @@ func TestIntermediateLeaks(t *testing.T) {
// https://github.com/ethereum/go-ethereum/pull/15549.
func TestCopy(t *testing.T) {
// Create a random state test to copy and modify "independently"
- mem, _ := ethdb.NewMemDatabase()
- orig, _ := New(common.Hash{}, NewDatabase(mem))
+ db, _ := ethdb.NewMemDatabase()
+ orig, _ := New(common.Hash{}, NewDatabase(db))
for i := byte(0); i < 255; i++ {
obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
@@ -263,7 +263,7 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
{
name: "AddRefund",
fn: func(a testAction, s *StateDB) {
- s.AddRefund(big.NewInt(a.args[0]))
+ s.AddRefund(uint64(a.args[0]))
},
args: make([]int64, 1),
noAddr: true,
@@ -346,11 +346,10 @@ func (test *snapshotTest) run() bool {
}
action.fn(action, state)
}
-
// Revert all snapshots in reverse order. Each revert must yield a state
// that is equivalent to fresh state with all actions up the snapshot applied.
for sindex--; sindex >= 0; sindex-- {
- checkstate, _ := New(common.Hash{}, NewDatabase(db))
+ checkstate, _ := New(common.Hash{}, state.Database())
for _, action := range test.actions[:test.snapshots[sindex]] {
action.fn(action, checkstate)
}
@@ -396,7 +395,7 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
}
}
- if state.GetRefund().Cmp(checkstate.GetRefund()) != 0 {
+ if state.GetRefund() != checkstate.GetRefund() {
return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d",
state.GetRefund(), checkstate.GetRefund())
}
@@ -409,7 +408,7 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
func (s *StateSuite) TestTouchDelete(c *check.C) {
s.state.GetOrNewStateObject(common.Address{})
- root, _ := s.state.CommitTo(s.db, false)
+ root, _ := s.state.Commit(false)
s.state.Reset(root)
snapshot := s.state.Snapshot()
@@ -417,7 +416,6 @@ func (s *StateSuite) TestTouchDelete(c *check.C) {
if len(s.state.stateObjectsDirty) != 1 {
c.Fatal("expected one dirty state object")
}
-
s.state.RevertToSnapshot(snapshot)
if len(s.state.stateObjectsDirty) != 0 {
c.Fatal("expected no dirty state object")
diff --git a/core/state/sync_test.go b/core/state/sync_test.go
index 06c572ea6..8f14a44e7 100644
--- a/core/state/sync_test.go
+++ b/core/state/sync_test.go
@@ -36,10 +36,10 @@ type testAccount struct {
}
// makeTestState create a sample test state to test node-wise reconstruction.
-func makeTestState() (Database, *ethdb.MemDatabase, common.Hash, []*testAccount) {
+func makeTestState() (Database, common.Hash, []*testAccount) {
// Create an empty state
- mem, _ := ethdb.NewMemDatabase()
- db := NewDatabase(mem)
+ diskdb, _ := ethdb.NewMemDatabase()
+ db := NewDatabase(diskdb)
state, _ := New(common.Hash{}, db)
// Fill it with some arbitrary data
@@ -61,10 +61,10 @@ func makeTestState() (Database, *ethdb.MemDatabase, common.Hash, []*testAccount)
state.updateStateObject(obj)
accounts = append(accounts, acc)
}
- root, _ := state.CommitTo(mem, false)
+ root, _ := state.Commit(false)
// Return the generated state
- return db, mem, root, accounts
+ return db, root, accounts
}
// checkStateAccounts cross references a reconstructed state with an expected
@@ -96,7 +96,7 @@ func checkTrieConsistency(db ethdb.Database, root common.Hash) error {
if v, _ := db.Get(root[:]); v == nil {
return nil // Consider a non existent state consistent.
}
- trie, err := trie.New(root, db)
+ trie, err := trie.New(root, trie.NewDatabase(db))
if err != nil {
return err
}
@@ -138,7 +138,7 @@ func TestIterativeStateSyncBatched(t *testing.T) { testIterativeStateSync(t,
func testIterativeStateSync(t *testing.T, batch int) {
// Create a random state to copy
- _, srcMem, srcRoot, srcAccounts := makeTestState()
+ srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler
dstDb, _ := ethdb.NewMemDatabase()
@@ -148,9 +148,9 @@ func testIterativeStateSync(t *testing.T, batch int) {
for len(queue) > 0 {
results := make([]trie.SyncResult, len(queue))
for i, hash := range queue {
- data, err := srcMem.Get(hash.Bytes())
+ data, err := srcDb.TrieDB().Node(hash)
if err != nil {
- t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
+ t.Fatalf("failed to retrieve node data for %x", hash)
}
results[i] = trie.SyncResult{Hash: hash, Data: data}
}
@@ -170,7 +170,7 @@ func testIterativeStateSync(t *testing.T, batch int) {
// partial results are returned, and the others sent only later.
func TestIterativeDelayedStateSync(t *testing.T) {
// Create a random state to copy
- _, srcMem, srcRoot, srcAccounts := makeTestState()
+ srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler
dstDb, _ := ethdb.NewMemDatabase()
@@ -181,9 +181,9 @@ func TestIterativeDelayedStateSync(t *testing.T) {
// Sync only half of the scheduled nodes
results := make([]trie.SyncResult, len(queue)/2+1)
for i, hash := range queue[:len(results)] {
- data, err := srcMem.Get(hash.Bytes())
+ data, err := srcDb.TrieDB().Node(hash)
if err != nil {
- t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
+ t.Fatalf("failed to retrieve node data for %x", hash)
}
results[i] = trie.SyncResult{Hash: hash, Data: data}
}
@@ -207,7 +207,7 @@ func TestIterativeRandomStateSyncBatched(t *testing.T) { testIterativeRandomS
func testIterativeRandomStateSync(t *testing.T, batch int) {
// Create a random state to copy
- _, srcMem, srcRoot, srcAccounts := makeTestState()
+ srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler
dstDb, _ := ethdb.NewMemDatabase()
@@ -221,9 +221,9 @@ func testIterativeRandomStateSync(t *testing.T, batch int) {
// Fetch all the queued nodes in a random order
results := make([]trie.SyncResult, 0, len(queue))
for hash := range queue {
- data, err := srcMem.Get(hash.Bytes())
+ data, err := srcDb.TrieDB().Node(hash)
if err != nil {
- t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
+ t.Fatalf("failed to retrieve node data for %x", hash)
}
results = append(results, trie.SyncResult{Hash: hash, Data: data})
}
@@ -247,7 +247,7 @@ func testIterativeRandomStateSync(t *testing.T, batch int) {
// partial results are returned (Even those randomly), others sent only later.
func TestIterativeRandomDelayedStateSync(t *testing.T) {
// Create a random state to copy
- _, srcMem, srcRoot, srcAccounts := makeTestState()
+ srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler
dstDb, _ := ethdb.NewMemDatabase()
@@ -263,9 +263,9 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
for hash := range queue {
delete(queue, hash)
- data, err := srcMem.Get(hash.Bytes())
+ data, err := srcDb.TrieDB().Node(hash)
if err != nil {
- t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
+ t.Fatalf("failed to retrieve node data for %x", hash)
}
results = append(results, trie.SyncResult{Hash: hash, Data: data})
@@ -292,9 +292,9 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
// the database.
func TestIncompleteStateSync(t *testing.T) {
// Create a random state to copy
- _, srcMem, srcRoot, srcAccounts := makeTestState()
+ srcDb, srcRoot, srcAccounts := makeTestState()
- checkTrieConsistency(srcMem, srcRoot)
+ checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot)
// Create a destination state and sync with the scheduler
dstDb, _ := ethdb.NewMemDatabase()
@@ -306,9 +306,9 @@ func TestIncompleteStateSync(t *testing.T) {
// Fetch a batch of state nodes
results := make([]trie.SyncResult, len(queue))
for i, hash := range queue {
- data, err := srcMem.Get(hash.Bytes())
+ data, err := srcDb.TrieDB().Node(hash)
if err != nil {
- t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
+ t.Fatalf("failed to retrieve node data for %x", hash)
}
results[i] = trie.SyncResult{Hash: hash, Data: data}
}
diff --git a/core/state_processor.go b/core/state_processor.go
index 689c83785..4dc58b9de 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -17,8 +17,6 @@
package core
import (
- "math/big"
-
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc"
@@ -55,13 +53,13 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen
// Process returns the receipts and logs accumulated during the process and
// returns the amount of gas that was used in the process. If any of the
// transactions failed to execute due to insufficient gas it will return an error.
-func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, *big.Int, error) {
+func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
var (
- receipts types.Receipts
- totalUsedGas = big.NewInt(0)
- header = block.Header()
- allLogs []*types.Log
- gp = new(GasPool).AddGas(block.GasLimit())
+ receipts types.Receipts
+ usedGas = new(uint64)
+ header = block.Header()
+ allLogs []*types.Log
+ gp = new(GasPool).AddGas(block.GasLimit())
)
// Mutate the the block and state according to any hard-fork specs
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
@@ -70,9 +68,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// Iterate over and process the individual transactions
for i, tx := range block.Transactions() {
statedb.Prepare(tx.Hash(), block.Hash(), i)
- receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, totalUsedGas, cfg)
+ receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg)
if err != nil {
- return nil, nil, nil, err
+ return nil, nil, 0, err
}
receipts = append(receipts, receipt)
allLogs = append(allLogs, receipt.Logs...)
@@ -80,17 +78,17 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), receipts)
- return receipts, allLogs, totalUsedGas, nil
+ return receipts, allLogs, *usedGas, nil
}
// ApplyTransaction attempts to apply a transaction to the given state database
// and uses the input parameters for its environment. It returns the receipt
// for the transaction, gas used and an error if the transaction failed,
// indicating the block was invalid.
-func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *big.Int, error) {
+func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error) {
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
if err != nil {
- return nil, nil, err
+ return nil, 0, err
}
// Create a new context to be used in the EVM environment
context := NewEVMContext(msg, header, bc, author)
@@ -100,9 +98,8 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
// Apply the transaction to the current state (included in the env)
_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
if err != nil {
- return nil, nil, err
+ return nil, 0, err
}
-
// Update the state with pending changes
var root []byte
if config.IsByzantium(header.Number) {
@@ -110,18 +107,17 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
} else {
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
}
- usedGas.Add(usedGas, gas)
+ *usedGas += gas
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
// based on the eip phase, we're passing wether the root touch-delete accounts.
- receipt := types.NewReceipt(root, failed, usedGas)
+ receipt := types.NewReceipt(root, failed, *usedGas)
receipt.TxHash = tx.Hash()
- receipt.GasUsed = new(big.Int).Set(gas)
+ receipt.GasUsed = gas
// if the transaction created a contract, store the creation address in the receipt.
if msg.To() == nil {
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
}
-
// Set the receipt logs and create a bloom for filtering
receipt.Logs = statedb.GetLogs(tx.Hash())
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
diff --git a/core/state_transition.go b/core/state_transition.go
index e7a068589..390473fff 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -18,17 +18,16 @@ package core
import (
"errors"
+ "math"
"math/big"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)
var (
- Big0 = big.NewInt(0)
errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas")
)
@@ -54,7 +53,7 @@ type StateTransition struct {
msg Message
gas uint64
gasPrice *big.Int
- initialGas *big.Int
+ initialGas uint64
value *big.Int
data []byte
state vm.StateDB
@@ -68,7 +67,7 @@ type Message interface {
To() *common.Address
GasPrice() *big.Int
- Gas() *big.Int
+ Gas() uint64
Value() *big.Int
Nonce() uint64
@@ -76,45 +75,49 @@ type Message interface {
Data() []byte
}
-// IntrinsicGas computes the 'intrinsic gas' for a message
-// with the given data.
-//
-// TODO convert to uint64
-func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
- igas := new(big.Int)
+// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
+func IntrinsicGas(data []byte, contractCreation, homestead bool) (uint64, error) {
+ // Set the starting gas for the raw transaction
+ var gas uint64
if contractCreation && homestead {
- igas.SetUint64(params.TxGasContractCreation)
+ gas = params.TxGasContractCreation
} else {
- igas.SetUint64(params.TxGas)
+ gas = params.TxGas
}
+ // Bump the required gas by the amount of transactional data
if len(data) > 0 {
- var nz int64
+ // Zero and non-zero bytes are priced differently
+ var nz uint64
for _, byt := range data {
if byt != 0 {
nz++
}
}
- m := big.NewInt(nz)
- m.Mul(m, new(big.Int).SetUint64(params.TxDataNonZeroGas))
- igas.Add(igas, m)
- m.SetInt64(int64(len(data)) - nz)
- m.Mul(m, new(big.Int).SetUint64(params.TxDataZeroGas))
- igas.Add(igas, m)
+ // Make sure we don't exceed uint64 for all data combinations
+ if (math.MaxUint64-gas)/params.TxDataNonZeroGas < nz {
+ return 0, vm.ErrOutOfGas
+ }
+ gas += nz * params.TxDataNonZeroGas
+
+ z := uint64(len(data)) - nz
+ if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
+ return 0, vm.ErrOutOfGas
+ }
+ gas += z * params.TxDataZeroGas
}
- return igas
+ return gas, nil
}
// NewStateTransition initialises and returns a new state transition object.
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
return &StateTransition{
- gp: gp,
- evm: evm,
- msg: msg,
- gasPrice: msg.GasPrice(),
- initialGas: new(big.Int),
- value: msg.Value(),
- data: msg.Data(),
- state: evm.StateDB,
+ gp: gp,
+ evm: evm,
+ msg: msg,
+ gasPrice: msg.GasPrice(),
+ value: msg.Value(),
+ data: msg.Data(),
+ state: evm.StateDB,
}
}
@@ -125,11 +128,8 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
// the gas used (which includes gas refunds) and an error if it failed. An error always
// indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block.
-func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, bool, error) {
- st := NewStateTransition(evm, msg, gp)
-
- ret, _, gasUsed, failed, err := st.TransitionDb()
- return ret, gasUsed, failed, err
+func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, bool, error) {
+ return NewStateTransition(evm, msg, gp).TransitionDb()
}
func (st *StateTransition) from() vm.AccountRef {
@@ -166,26 +166,20 @@ func (st *StateTransition) useGas(amount uint64) error {
}
func (st *StateTransition) buyGas() error {
- mgas := st.msg.Gas()
- if mgas.BitLen() > 64 {
- return vm.ErrOutOfGas
- }
-
- mgval := new(big.Int).Mul(mgas, st.gasPrice)
-
var (
state = st.state
sender = st.from()
)
+ mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
if state.GetBalance(sender.Address()).Cmp(mgval) < 0 {
return errInsufficientBalanceForGas
}
- if err := st.gp.SubGas(mgas); err != nil {
+ if err := st.gp.SubGas(st.msg.Gas()); err != nil {
return err
}
- st.gas += mgas.Uint64()
+ st.gas += st.msg.Gas()
- st.initialGas.Set(mgas)
+ st.initialGas = st.msg.Gas()
state.SubBalance(sender.Address(), mgval)
return nil
}
@@ -206,10 +200,10 @@ func (st *StateTransition) preCheck() error {
return st.buyGas()
}
-// TransitionDb will transition the state by applying the current message and returning the result
-// including the required gas for the operation as well as the used gas. It returns an error if it
+// TransitionDb will transition the state by applying the current message and
+// returning the result including the the used gas. It returns an error if it
// failed. An error indicates a consensus issue.
-func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, failed bool, err error) {
+func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) {
if err = st.preCheck(); err != nil {
return
}
@@ -220,13 +214,9 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
contractCreation := msg.To() == nil
// Pay intrinsic gas
- // TODO convert to uint64
- intrinsicGas := IntrinsicGas(st.data, contractCreation, homestead)
- if intrinsicGas.BitLen() > 64 {
- return nil, nil, nil, false, vm.ErrOutOfGas
- }
- if err = st.useGas(intrinsicGas.Uint64()); err != nil {
- return nil, nil, nil, false, err
+ gas, err := IntrinsicGas(st.data, contractCreation, homestead)
+ if err = st.useGas(gas); err != nil {
+ return nil, 0, false, err
}
var (
@@ -249,36 +239,35 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
// sufficient balance to make the transfer happen. The first
// balance transfer may never fail.
if vmerr == vm.ErrInsufficientBalance {
- return nil, nil, nil, false, vmerr
+ return nil, 0, false, vmerr
}
}
- requiredGas = new(big.Int).Set(st.gasUsed())
-
st.refundGas()
- st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(st.gasUsed(), st.gasPrice))
+ st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
- return ret, requiredGas, st.gasUsed(), vmerr != nil, err
+ return ret, st.gasUsed(), vmerr != nil, err
}
func (st *StateTransition) refundGas() {
- // Return eth for remaining gas to the sender account,
- // exchanged at the original rate.
- sender := st.from() // err already checked
- remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
- st.state.AddBalance(sender.Address(), remaining)
-
// Apply refund counter, capped to half of the used gas.
- uhalf := remaining.Div(st.gasUsed(), common.Big2)
- refund := math.BigMin(uhalf, st.state.GetRefund())
- st.gas += refund.Uint64()
+ refund := st.gasUsed() / 2
+ if refund > st.state.GetRefund() {
+ refund = st.state.GetRefund()
+ }
+ st.gas += refund
- st.state.AddBalance(sender.Address(), refund.Mul(refund, st.gasPrice))
+ // Return ETH for remaining gas, exchanged at the original rate.
+ sender := st.from()
+
+ remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
+ st.state.AddBalance(sender.Address(), remaining)
// Also return remaining gas to the block gas counter so it is
// available for the next transaction.
- st.gp.AddGas(new(big.Int).SetUint64(st.gas))
+ st.gp.AddGas(st.gas)
}
-func (st *StateTransition) gasUsed() *big.Int {
- return new(big.Int).Sub(st.initialGas, new(big.Int).SetUint64(st.gas))
+// gasUsed returns the amount of gas used up by the state transition.
+func (st *StateTransition) gasUsed() uint64 {
+ return st.initialGas - st.gas
}
diff --git a/core/tx_list.go b/core/tx_list.go
index 838433b89..55fc42617 100644
--- a/core/tx_list.go
+++ b/core/tx_list.go
@@ -224,7 +224,7 @@ type txList struct {
txs *txSortedMap // Heap indexed sorted hash map of the transactions
costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
- gascap *big.Int // Gas limit of the highest spending transaction (reset only if exceeds block limit)
+ gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit)
}
// newTxList create a new transaction list for maintaining nonce-indexable fast,
@@ -234,7 +234,6 @@ func newTxList(strict bool) *txList {
strict: strict,
txs: newTxSortedMap(),
costcap: new(big.Int),
- gascap: new(big.Int),
}
}
@@ -266,7 +265,7 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
l.costcap = cost
}
- if gas := tx.Gas(); l.gascap.Cmp(gas) < 0 {
+ if gas := tx.Gas(); l.gascap < gas {
l.gascap = gas
}
return true, old
@@ -288,16 +287,16 @@ func (l *txList) Forward(threshold uint64) types.Transactions {
// a point in calculating all the costs or if the balance covers all. If the threshold
// is lower than the costgas cap, the caps will be reset to a new high after removing
// the newly invalidated transactions.
-func (l *txList) Filter(costLimit, gasLimit *big.Int) (types.Transactions, types.Transactions) {
+func (l *txList) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions, types.Transactions) {
// If all transactions are below the threshold, short circuit
- if l.costcap.Cmp(costLimit) <= 0 && l.gascap.Cmp(gasLimit) <= 0 {
+ if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit {
return nil, nil
}
l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds
- l.gascap = new(big.Int).Set(gasLimit)
+ l.gascap = gasLimit
// Filter out all the transactions above the account's funds
- removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(costLimit) > 0 || tx.Gas().Cmp(gasLimit) > 0 })
+ removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(costLimit) > 0 || tx.Gas() > gasLimit })
// If the list was strict, filter anything above the lowest nonce
var invalids types.Transactions
diff --git a/core/tx_list_test.go b/core/tx_list_test.go
index b4f0b5228..d579f501a 100644
--- a/core/tx_list_test.go
+++ b/core/tx_list_test.go
@@ -17,7 +17,6 @@
package core
import (
- "math/big"
"math/rand"
"testing"
@@ -33,7 +32,7 @@ func TestStrictTxListAdd(t *testing.T) {
txs := make(types.Transactions, 1024)
for i := 0; i < len(txs); i++ {
- txs[i] = transaction(uint64(i), new(big.Int), key)
+ txs[i] = transaction(uint64(i), 0, key)
}
// Insert the transactions in a random order
list := newTxList(true)
diff --git a/core/tx_pool.go b/core/tx_pool.go
index c3915575b..dc3ddc423 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -103,7 +103,7 @@ var (
underpricedTxCounter = metrics.NewCounter("txpool/underpriced")
)
-// TxStatus is the current status of a transaction as seen py the pool.
+// TxStatus is the current status of a transaction as seen by the pool.
type TxStatus uint
const (
@@ -197,9 +197,9 @@ type TxPool struct {
currentState *state.StateDB // Current state in the blockchain head
pendingState *state.ManagedState // Pending state tracking virtual nonces
- currentMaxGas *big.Int // Current gas limit for transaction caps
+ currentMaxGas uint64 // Current gas limit for transaction caps
- locals *accountSet // Set of local transaction to exepmt from evicion rules
+ locals *accountSet // Set of local transaction to exempt from eviction rules
journal *txJournal // Journal of local transaction to back up to disk
pending map[common.Address]*txList // All currently processable transactions
@@ -214,7 +214,7 @@ type TxPool struct {
}
// NewTxPool creates a new transaction pool to gather, sort and filter inbound
-// trnsactions from the network.
+// transactions from the network.
func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain blockChain) *TxPool {
// Sanitize the input to ensure no vulnerable gas prices are set
config = (&config).sanitize()
@@ -360,7 +360,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
newNum := newHead.Number.Uint64()
if depth := uint64(math.Abs(float64(oldNum) - float64(newNum))); depth > 64 {
- log.Warn("Skipping deep transaction reorg", "depth", depth)
+ log.Debug("Skipping deep transaction reorg", "depth", depth)
} else {
// Reorg seems shallow enough to pull in all transactions into memory
var discarded, included types.Transactions
@@ -564,7 +564,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
return ErrNegativeValue
}
// Ensure the transaction doesn't exceed the current block limit gas.
- if pool.currentMaxGas.Cmp(tx.Gas()) < 0 {
+ if pool.currentMaxGas < tx.Gas() {
return ErrGasLimit
}
// Make sure the transaction is signed properly
@@ -586,8 +586,11 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
return ErrInsufficientFunds
}
- intrGas := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
- if tx.Gas().Cmp(intrGas) < 0 {
+ intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
+ if err != nil {
+ return err
+ }
+ if tx.Gas() < intrGas {
return ErrIntrinsicGas
}
return nil
@@ -838,7 +841,7 @@ func (pool *TxPool) Status(hashes []common.Hash) []TxStatus {
for i, hash := range hashes {
if tx := pool.all[hash]; tx != nil {
from, _ := types.Sender(pool.signer, tx) // already validated
- if pool.pending[from].txs.items[tx.Nonce()] != nil {
+ if pool.pending[from] != nil && pool.pending[from].txs.items[tx.Nonce()] != nil {
status[i] = TxStatusPending
} else {
status[i] = TxStatusQueued
diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go
index 21171a737..158b9776b 100644
--- a/core/tx_pool_test.go
+++ b/core/tx_pool_test.go
@@ -46,7 +46,7 @@ func init() {
type testBlockChain struct {
statedb *state.StateDB
- gasLimit *big.Int
+ gasLimit uint64
chainHeadFeed *event.Feed
}
@@ -68,19 +68,19 @@ func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) even
return bc.chainHeadFeed.Subscribe(ch)
}
-func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
+func transaction(nonce uint64, gaslimit uint64, key *ecdsa.PrivateKey) *types.Transaction {
return pricedTransaction(nonce, gaslimit, big.NewInt(1), key)
}
-func pricedTransaction(nonce uint64, gaslimit, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
+func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, gasprice, nil), types.HomesteadSigner{}, key)
return tx
}
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
- db, _ := ethdb.NewMemDatabase()
- statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ diskdb, _ := ethdb.NewMemDatabase()
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(diskdb))
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
key, _ := crypto.GenerateKey()
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
@@ -184,10 +184,10 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
// setup pool with 2 transaction in it
statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether))
- blockchain := &testChain{&testBlockChain{statedb, big.NewInt(1000000000), new(event.Feed)}, address, &trigger}
+ blockchain := &testChain{&testBlockChain{statedb, 1000000000, new(event.Feed)}, address, &trigger}
- tx0 := transaction(0, big.NewInt(100000), key)
- tx1 := transaction(1, big.NewInt(100000), key)
+ tx0 := transaction(0, 100000, key)
+ tx1 := transaction(1, 100000, key)
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
defer pool.Stop()
@@ -230,7 +230,7 @@ func TestInvalidTransactions(t *testing.T) {
pool, key := setupTxPool()
defer pool.Stop()
- tx := transaction(0, big.NewInt(100), key)
+ tx := transaction(0, 100, key)
from, _ := deriveSender(tx)
pool.currentState.AddBalance(from, big.NewInt(1))
@@ -238,7 +238,7 @@ func TestInvalidTransactions(t *testing.T) {
t.Error("expected", ErrInsufficientFunds)
}
- balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(tx.Gas(), tx.GasPrice()))
+ balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(new(big.Int).SetUint64(tx.Gas()), tx.GasPrice()))
pool.currentState.AddBalance(from, balance)
if err := pool.AddRemote(tx); err != ErrIntrinsicGas {
t.Error("expected", ErrIntrinsicGas, "got", err)
@@ -246,12 +246,12 @@ func TestInvalidTransactions(t *testing.T) {
pool.currentState.SetNonce(from, 1)
pool.currentState.AddBalance(from, big.NewInt(0xffffffffffffff))
- tx = transaction(0, big.NewInt(100000), key)
+ tx = transaction(0, 100000, key)
if err := pool.AddRemote(tx); err != ErrNonceTooLow {
t.Error("expected", ErrNonceTooLow)
}
- tx = transaction(1, big.NewInt(100000), key)
+ tx = transaction(1, 100000, key)
pool.gasPrice = big.NewInt(1000)
if err := pool.AddRemote(tx); err != ErrUnderpriced {
t.Error("expected", ErrUnderpriced, "got", err)
@@ -267,7 +267,7 @@ func TestTransactionQueue(t *testing.T) {
pool, key := setupTxPool()
defer pool.Stop()
- tx := transaction(0, big.NewInt(100), key)
+ tx := transaction(0, 100, key)
from, _ := deriveSender(tx)
pool.currentState.AddBalance(from, big.NewInt(1000))
pool.lockedReset(nil, nil)
@@ -278,7 +278,7 @@ func TestTransactionQueue(t *testing.T) {
t.Error("expected valid txs to be 1 is", len(pool.pending))
}
- tx = transaction(1, big.NewInt(100), key)
+ tx = transaction(1, 100, key)
from, _ = deriveSender(tx)
pool.currentState.SetNonce(from, 2)
pool.enqueueTx(tx.Hash(), tx)
@@ -294,9 +294,9 @@ func TestTransactionQueue(t *testing.T) {
pool, key = setupTxPool()
defer pool.Stop()
- tx1 := transaction(0, big.NewInt(100), key)
- tx2 := transaction(10, big.NewInt(100), key)
- tx3 := transaction(11, big.NewInt(100), key)
+ tx1 := transaction(0, 100, key)
+ tx2 := transaction(10, 100, key)
+ tx3 := transaction(11, 100, key)
from, _ = deriveSender(tx1)
pool.currentState.AddBalance(from, big.NewInt(1000))
pool.lockedReset(nil, nil)
@@ -321,7 +321,7 @@ func TestTransactionNegativeValue(t *testing.T) {
pool, key := setupTxPool()
defer pool.Stop()
- tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), big.NewInt(100), big.NewInt(1), nil), types.HomesteadSigner{}, key)
+ tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), 100, big.NewInt(1), nil), types.HomesteadSigner{}, key)
from, _ := deriveSender(tx)
pool.currentState.AddBalance(from, big.NewInt(1))
if err := pool.AddRemote(tx); err != ErrNegativeValue {
@@ -341,12 +341,12 @@ func TestTransactionChainFork(t *testing.T) {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
statedb.AddBalance(addr, big.NewInt(100000000000000))
- pool.chain = &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
pool.lockedReset(nil, nil)
}
resetState()
- tx := transaction(0, big.NewInt(100000), key)
+ tx := transaction(0, 100000, key)
if _, err := pool.add(tx, false); err != nil {
t.Error("didn't expect error", err)
}
@@ -371,15 +371,15 @@ func TestTransactionDoubleNonce(t *testing.T) {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
statedb.AddBalance(addr, big.NewInt(100000000000000))
- pool.chain = &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
pool.lockedReset(nil, nil)
}
resetState()
signer := types.HomesteadSigner{}
- tx1, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(100000), big.NewInt(1), nil), signer, key)
- tx2, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(2), nil), signer, key)
- tx3, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(1), nil), signer, key)
+ tx1, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 100000, big.NewInt(1), nil), signer, key)
+ tx2, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(2), nil), signer, key)
+ tx3, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(1), nil), signer, key)
// Add the first two transaction, ensure higher priced stays only
if replace, err := pool.add(tx1, false); err != nil || replace {
@@ -418,7 +418,7 @@ func TestTransactionMissingNonce(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey)
pool.currentState.AddBalance(addr, big.NewInt(100000000000000))
- tx := transaction(1, big.NewInt(100000), key)
+ tx := transaction(1, 100000, key)
if _, err := pool.add(tx, false); err != nil {
t.Error("didn't expect error", err)
}
@@ -445,7 +445,7 @@ func TestTransactionNonceRecovery(t *testing.T) {
pool.currentState.AddBalance(addr, big.NewInt(100000000000000))
pool.lockedReset(nil, nil)
- tx := transaction(n, big.NewInt(100000), key)
+ tx := transaction(n, 100000, key)
if err := pool.AddRemote(tx); err != nil {
t.Error(err)
}
@@ -466,17 +466,17 @@ func TestTransactionDropping(t *testing.T) {
pool, key := setupTxPool()
defer pool.Stop()
- account, _ := deriveSender(transaction(0, big.NewInt(0), key))
+ account, _ := deriveSender(transaction(0, 0, key))
pool.currentState.AddBalance(account, big.NewInt(1000))
// Add some pending and some queued transactions
var (
- tx0 = transaction(0, big.NewInt(100), key)
- tx1 = transaction(1, big.NewInt(200), key)
- tx2 = transaction(2, big.NewInt(300), key)
- tx10 = transaction(10, big.NewInt(100), key)
- tx11 = transaction(11, big.NewInt(200), key)
- tx12 = transaction(12, big.NewInt(300), key)
+ tx0 = transaction(0, 100, key)
+ tx1 = transaction(1, 200, key)
+ tx2 = transaction(2, 300, key)
+ tx10 = transaction(10, 100, key)
+ tx11 = transaction(11, 200, key)
+ tx12 = transaction(12, 300, key)
)
pool.promoteTx(account, tx0.Hash(), tx0)
pool.promoteTx(account, tx1.Hash(), tx1)
@@ -531,7 +531,7 @@ func TestTransactionDropping(t *testing.T) {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
}
// Reduce the block gas limit, check that invalidated transactions are dropped
- pool.chain.(*testBlockChain).gasLimit = big.NewInt(100)
+ pool.chain.(*testBlockChain).gasLimit = 100
pool.lockedReset(nil, nil)
if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok {
@@ -561,7 +561,7 @@ func TestTransactionPostponing(t *testing.T) {
pool, key := setupTxPool()
defer pool.Stop()
- account, _ := deriveSender(transaction(0, big.NewInt(0), key))
+ account, _ := deriveSender(transaction(0, 0, key))
pool.currentState.AddBalance(account, big.NewInt(1000))
// Add a batch consecutive pending transactions for validation
@@ -569,9 +569,9 @@ func TestTransactionPostponing(t *testing.T) {
for i := 0; i < 100; i++ {
var tx *types.Transaction
if i%2 == 0 {
- tx = transaction(uint64(i), big.NewInt(100), key)
+ tx = transaction(uint64(i), 100, key)
} else {
- tx = transaction(uint64(i), big.NewInt(500), key)
+ tx = transaction(uint64(i), 500, key)
}
pool.promoteTx(account, tx.Hash(), tx)
txns = append(txns, tx)
@@ -638,7 +638,7 @@ func TestTransactionGapFilling(t *testing.T) {
pool, key := setupTxPool()
defer pool.Stop()
- account, _ := deriveSender(transaction(0, big.NewInt(0), key))
+ account, _ := deriveSender(transaction(0, 0, key))
pool.currentState.AddBalance(account, big.NewInt(1000000))
// Keep track of transaction events to ensure all executables get announced
@@ -647,10 +647,10 @@ func TestTransactionGapFilling(t *testing.T) {
defer sub.Unsubscribe()
// Create a pending and a queued transaction with a nonce-gap in between
- if err := pool.AddRemote(transaction(0, big.NewInt(100000), key)); err != nil {
+ if err := pool.AddRemote(transaction(0, 100000, key)); err != nil {
t.Fatalf("failed to add pending transaction: %v", err)
}
- if err := pool.AddRemote(transaction(2, big.NewInt(100000), key)); err != nil {
+ if err := pool.AddRemote(transaction(2, 100000, key)); err != nil {
t.Fatalf("failed to add queued transaction: %v", err)
}
pending, queued := pool.Stats()
@@ -667,7 +667,7 @@ func TestTransactionGapFilling(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Fill the nonce gap and ensure all transactions become pending
- if err := pool.AddRemote(transaction(1, big.NewInt(100000), key)); err != nil {
+ if err := pool.AddRemote(transaction(1, 100000, key)); err != nil {
t.Fatalf("failed to add gapped transaction: %v", err)
}
pending, queued = pool.Stats()
@@ -694,12 +694,12 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
pool, key := setupTxPool()
defer pool.Stop()
- account, _ := deriveSender(transaction(0, big.NewInt(0), key))
+ account, _ := deriveSender(transaction(0, 0, key))
pool.currentState.AddBalance(account, big.NewInt(1000000))
// Keep queuing up transactions and make sure all above a limit are dropped
for i := uint64(1); i <= testTxPoolConfig.AccountQueue+5; i++ {
- if err := pool.AddRemote(transaction(i, big.NewInt(100000), key)); err != nil {
+ if err := pool.AddRemote(transaction(i, 100000, key)); err != nil {
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
}
if len(pool.pending) != 0 {
@@ -738,7 +738,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
// Create the pool to test the limit enforcement with
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
config := testTxPoolConfig
config.NoLocals = nolocals
@@ -763,7 +763,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
key := keys[rand.Intn(len(keys)-1)] // skip adding transactions with the local account
addr := crypto.PubkeyToAddress(key.PublicKey)
- txs = append(txs, transaction(nonces[addr]+1, big.NewInt(100000), key))
+ txs = append(txs, transaction(nonces[addr]+1, 100000, key))
nonces[addr]++
}
// Import the batch and verify that limits have been enforced
@@ -782,7 +782,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
// Generate a batch of transactions from the local account and import them
txs = txs[:0]
for i := uint64(0); i < 3*config.GlobalQueue; i++ {
- txs = append(txs, transaction(i+1, big.NewInt(100000), local))
+ txs = append(txs, transaction(i+1, 100000, local))
}
pool.AddLocals(txs)
@@ -827,7 +827,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
// Create the pool to test the non-expiration enforcement
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
config := testTxPoolConfig
config.Lifetime = time.Second
@@ -844,10 +844,10 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
pool.currentState.AddBalance(crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000))
// Add the two transactions and ensure they both are queued up
- if err := pool.AddLocal(pricedTransaction(1, big.NewInt(100000), big.NewInt(1), local)); err != nil {
+ if err := pool.AddLocal(pricedTransaction(1, 100000, big.NewInt(1), local)); err != nil {
t.Fatalf("failed to add local transaction: %v", err)
}
- if err := pool.AddRemote(pricedTransaction(1, big.NewInt(100000), big.NewInt(1), remote)); err != nil {
+ if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(1), remote)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err)
}
pending, queued := pool.Stats()
@@ -891,7 +891,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
pool, key := setupTxPool()
defer pool.Stop()
- account, _ := deriveSender(transaction(0, big.NewInt(0), key))
+ account, _ := deriveSender(transaction(0, 0, key))
pool.currentState.AddBalance(account, big.NewInt(1000000))
// Keep track of transaction events to ensure all executables get announced
@@ -901,7 +901,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
// Keep queuing up transactions and make sure all above a limit are dropped
for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
- if err := pool.AddRemote(transaction(i, big.NewInt(100000), key)); err != nil {
+ if err := pool.AddRemote(transaction(i, 100000, key)); err != nil {
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
}
if pool.pending[account].Len() != int(i)+1 {
@@ -934,11 +934,11 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
pool1, key1 := setupTxPool()
defer pool1.Stop()
- account1, _ := deriveSender(transaction(0, big.NewInt(0), key1))
+ account1, _ := deriveSender(transaction(0, 0, key1))
pool1.currentState.AddBalance(account1, big.NewInt(1000000))
for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
- if err := pool1.AddRemote(transaction(origin+i, big.NewInt(100000), key1)); err != nil {
+ if err := pool1.AddRemote(transaction(origin+i, 100000, key1)); err != nil {
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
}
}
@@ -946,12 +946,12 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
pool2, key2 := setupTxPool()
defer pool2.Stop()
- account2, _ := deriveSender(transaction(0, big.NewInt(0), key2))
+ account2, _ := deriveSender(transaction(0, 0, key2))
pool2.currentState.AddBalance(account2, big.NewInt(1000000))
txns := []*types.Transaction{}
for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
- txns = append(txns, transaction(origin+i, big.NewInt(100000), key2))
+ txns = append(txns, transaction(origin+i, 100000, key2))
}
pool2.AddRemotes(txns)
@@ -982,7 +982,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
// Create the pool to test the limit enforcement with
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
config := testTxPoolConfig
config.GlobalSlots = config.AccountSlots * 10
@@ -1003,7 +1003,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
for _, key := range keys {
addr := crypto.PubkeyToAddress(key.PublicKey)
for j := 0; j < int(config.GlobalSlots)/len(keys)*2; j++ {
- txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key))
+ txs = append(txs, transaction(nonces[addr], 100000, key))
nonces[addr]++
}
}
@@ -1029,7 +1029,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
// Create the pool to test the limit enforcement with
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
config := testTxPoolConfig
config.AccountSlots = 2
@@ -1046,7 +1046,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
txs := types.Transactions{}
for j := 0; j < int(config.GlobalSlots)*2; j++ {
- txs = append(txs, transaction(uint64(j), big.NewInt(100000), key))
+ txs = append(txs, transaction(uint64(j), 100000, key))
}
// Import the batch and verify that limits have been enforced
pool.AddRemotes(txs)
@@ -1064,7 +1064,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
// Create the pool to test the limit enforcement with
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
config := testTxPoolConfig
config.GlobalSlots = 0
@@ -1085,7 +1085,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
for _, key := range keys {
addr := crypto.PubkeyToAddress(key.PublicKey)
for j := 0; j < int(config.AccountSlots)*2; j++ {
- txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key))
+ txs = append(txs, transaction(nonces[addr], 100000, key))
nonces[addr]++
}
}
@@ -1113,7 +1113,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
// Create the pool to test the pricing enforcement with
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
defer pool.Stop()
@@ -1132,15 +1132,15 @@ func TestTransactionPoolRepricing(t *testing.T) {
// Generate and queue a batch of transactions, both pending and queued
txs := types.Transactions{}
- txs = append(txs, pricedTransaction(0, big.NewInt(100000), big.NewInt(2), keys[0]))
- txs = append(txs, pricedTransaction(1, big.NewInt(100000), big.NewInt(1), keys[0]))
- txs = append(txs, pricedTransaction(2, big.NewInt(100000), big.NewInt(2), keys[0]))
+ txs = append(txs, pricedTransaction(0, 100000, big.NewInt(2), keys[0]))
+ txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[0]))
+ txs = append(txs, pricedTransaction(2, 100000, big.NewInt(2), keys[0]))
- txs = append(txs, pricedTransaction(1, big.NewInt(100000), big.NewInt(2), keys[1]))
- txs = append(txs, pricedTransaction(2, big.NewInt(100000), big.NewInt(1), keys[1]))
- txs = append(txs, pricedTransaction(3, big.NewInt(100000), big.NewInt(2), keys[1]))
+ txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[1]))
+ txs = append(txs, pricedTransaction(2, 100000, big.NewInt(1), keys[1]))
+ txs = append(txs, pricedTransaction(3, 100000, big.NewInt(2), keys[1]))
- ltx := pricedTransaction(0, big.NewInt(100000), big.NewInt(1), keys[2])
+ ltx := pricedTransaction(0, 100000, big.NewInt(1), keys[2])
// Import the batch and that both pending and queued transactions match up
pool.AddRemotes(txs)
@@ -1176,10 +1176,10 @@ func TestTransactionPoolRepricing(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Check that we can't add the old transactions back
- if err := pool.AddRemote(pricedTransaction(1, big.NewInt(100000), big.NewInt(1), keys[0])); err != ErrUnderpriced {
+ if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(1), keys[0])); err != ErrUnderpriced {
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
}
- if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100000), big.NewInt(1), keys[1])); err != ErrUnderpriced {
+ if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(1), keys[1])); err != ErrUnderpriced {
t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
}
if err := validateEvents(events, 0); err != nil {
@@ -1189,7 +1189,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// However we can add local underpriced transactions
- tx := pricedTransaction(1, big.NewInt(100000), big.NewInt(1), keys[2])
+ tx := pricedTransaction(1, 100000, big.NewInt(1), keys[2])
if err := pool.AddLocal(tx); err != nil {
t.Fatalf("failed to add underpriced local transaction: %v", err)
}
@@ -1212,7 +1212,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
// Create the pool to test the pricing enforcement with
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
defer pool.Stop()
@@ -1226,12 +1226,12 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
// Create transaction (both pending and queued) with a linearly growing gasprice
for i := uint64(0); i < 500; i++ {
// Add pending
- p_tx := pricedTransaction(i, big.NewInt(100000), big.NewInt(int64(i)), keys[2])
+ p_tx := pricedTransaction(i, 100000, big.NewInt(int64(i)), keys[2])
if err := pool.AddLocal(p_tx); err != nil {
t.Fatal(err)
}
// Add queued
- q_tx := pricedTransaction(i+501, big.NewInt(100000), big.NewInt(int64(i)), keys[2])
+ q_tx := pricedTransaction(i+501, 100000, big.NewInt(int64(i)), keys[2])
if err := pool.AddLocal(q_tx); err != nil {
t.Fatal(err)
}
@@ -1275,7 +1275,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
// Create the pool to test the pricing enforcement with
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
config := testTxPoolConfig
config.GlobalSlots = 2
@@ -1298,12 +1298,12 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
// Generate and queue a batch of transactions, both pending and queued
txs := types.Transactions{}
- txs = append(txs, pricedTransaction(0, big.NewInt(100000), big.NewInt(1), keys[0]))
- txs = append(txs, pricedTransaction(1, big.NewInt(100000), big.NewInt(2), keys[0]))
+ txs = append(txs, pricedTransaction(0, 100000, big.NewInt(1), keys[0]))
+ txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[0]))
- txs = append(txs, pricedTransaction(1, big.NewInt(100000), big.NewInt(1), keys[1]))
+ txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[1]))
- ltx := pricedTransaction(0, big.NewInt(100000), big.NewInt(1), keys[2])
+ ltx := pricedTransaction(0, 100000, big.NewInt(1), keys[2])
// Import the batch and that both pending and queued transactions match up
pool.AddRemotes(txs)
@@ -1323,17 +1323,17 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding an underpriced transaction on block limit fails
- if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), keys[1])); err != ErrUnderpriced {
+ if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), keys[1])); err != ErrUnderpriced {
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
}
// Ensure that adding high priced transactions drops cheap ones, but not own
- if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(3), keys[1])); err != nil {
+ if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(3), keys[1])); err != nil {
t.Fatalf("failed to add well priced transaction: %v", err)
}
- if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100000), big.NewInt(4), keys[1])); err != nil {
+ if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(4), keys[1])); err != nil {
t.Fatalf("failed to add well priced transaction: %v", err)
}
- if err := pool.AddRemote(pricedTransaction(3, big.NewInt(100000), big.NewInt(5), keys[1])); err != nil {
+ if err := pool.AddRemote(pricedTransaction(3, 100000, big.NewInt(5), keys[1])); err != nil {
t.Fatalf("failed to add well priced transaction: %v", err)
}
pending, queued = pool.Stats()
@@ -1350,7 +1350,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding local transactions can push out even higher priced ones
- tx := pricedTransaction(1, big.NewInt(100000), big.NewInt(0), keys[2])
+ tx := pricedTransaction(1, 100000, big.NewInt(0), keys[2])
if err := pool.AddLocal(tx); err != nil {
t.Fatalf("failed to add underpriced local transaction: %v", err)
}
@@ -1377,7 +1377,7 @@ func TestTransactionReplacement(t *testing.T) {
// Create the pool to test the pricing enforcement with
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
defer pool.Stop()
@@ -1395,49 +1395,49 @@ func TestTransactionReplacement(t *testing.T) {
price := int64(100)
threshold := (price * (100 + int64(testTxPoolConfig.PriceBump))) / 100
- if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), key)); err != nil {
+ if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), key)); err != nil {
t.Fatalf("failed to add original cheap pending transaction: %v", err)
}
- if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100001), big.NewInt(1), key)); err != ErrReplaceUnderpriced {
+ if err := pool.AddRemote(pricedTransaction(0, 100001, big.NewInt(1), key)); err != ErrReplaceUnderpriced {
t.Fatalf("original cheap pending transaction replacement error mismatch: have %v, want %v", err, ErrReplaceUnderpriced)
}
- if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(2), key)); err != nil {
+ if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(2), key)); err != nil {
t.Fatalf("failed to replace original cheap pending transaction: %v", err)
}
if err := validateEvents(events, 2); err != nil {
t.Fatalf("cheap replacement event firing failed: %v", err)
}
- if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(price), key)); err != nil {
+ if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(price), key)); err != nil {
t.Fatalf("failed to add original proper pending transaction: %v", err)
}
- if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100001), big.NewInt(threshold-1), key)); err != ErrReplaceUnderpriced {
+ if err := pool.AddRemote(pricedTransaction(0, 100001, big.NewInt(threshold-1), key)); err != ErrReplaceUnderpriced {
t.Fatalf("original proper pending transaction replacement error mismatch: have %v, want %v", err, ErrReplaceUnderpriced)
}
- if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(threshold), key)); err != nil {
+ if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(threshold), key)); err != nil {
t.Fatalf("failed to replace original proper pending transaction: %v", err)
}
if err := validateEvents(events, 2); err != nil {
t.Fatalf("proper replacement event firing failed: %v", err)
}
// Add queued transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too)
- if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100000), big.NewInt(1), key)); err != nil {
+ if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(1), key)); err != nil {
t.Fatalf("failed to add original cheap queued transaction: %v", err)
}
- if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100001), big.NewInt(1), key)); err != ErrReplaceUnderpriced {
+ if err := pool.AddRemote(pricedTransaction(2, 100001, big.NewInt(1), key)); err != ErrReplaceUnderpriced {
t.Fatalf("original cheap queued transaction replacement error mismatch: have %v, want %v", err, ErrReplaceUnderpriced)
}
- if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100000), big.NewInt(2), key)); err != nil {
+ if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(2), key)); err != nil {
t.Fatalf("failed to replace original cheap queued transaction: %v", err)
}
- if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100000), big.NewInt(price), key)); err != nil {
+ if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(price), key)); err != nil {
t.Fatalf("failed to add original proper queued transaction: %v", err)
}
- if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100001), big.NewInt(threshold-1), key)); err != ErrReplaceUnderpriced {
+ if err := pool.AddRemote(pricedTransaction(2, 100001, big.NewInt(threshold-1), key)); err != ErrReplaceUnderpriced {
t.Fatalf("original proper queued transaction replacement error mismatch: have %v, want %v", err, ErrReplaceUnderpriced)
}
- if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100000), big.NewInt(threshold), key)); err != nil {
+ if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(threshold), key)); err != nil {
t.Fatalf("failed to replace original proper queued transaction: %v", err)
}
@@ -1472,7 +1472,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
// Create the original pool to inject transaction into the journal
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
config := testTxPoolConfig
config.NoLocals = nolocals
@@ -1489,16 +1489,16 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
pool.currentState.AddBalance(crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000))
// Add three local and a remote transactions and ensure they are queued up
- if err := pool.AddLocal(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), local)); err != nil {
+ if err := pool.AddLocal(pricedTransaction(0, 100000, big.NewInt(1), local)); err != nil {
t.Fatalf("failed to add local transaction: %v", err)
}
- if err := pool.AddLocal(pricedTransaction(1, big.NewInt(100000), big.NewInt(1), local)); err != nil {
+ if err := pool.AddLocal(pricedTransaction(1, 100000, big.NewInt(1), local)); err != nil {
t.Fatalf("failed to add local transaction: %v", err)
}
- if err := pool.AddLocal(pricedTransaction(2, big.NewInt(100000), big.NewInt(1), local)); err != nil {
+ if err := pool.AddLocal(pricedTransaction(2, 100000, big.NewInt(1), local)); err != nil {
t.Fatalf("failed to add local transaction: %v", err)
}
- if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), remote)); err != nil {
+ if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), remote)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err)
}
pending, queued := pool.Stats()
@@ -1514,7 +1514,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
// Terminate the old pool, bump the local nonce, create a new pool and ensure relevant transaction survive
pool.Stop()
statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1)
- blockchain = &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ blockchain = &testBlockChain{statedb, 1000000, new(event.Feed)}
pool = NewTxPool(config, params.TestChainConfig, blockchain)
@@ -1541,7 +1541,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
pool.Stop()
statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1)
- blockchain = &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
+ blockchain = &testBlockChain{statedb, 1000000, new(event.Feed)}
pool = NewTxPool(config, params.TestChainConfig, blockchain)
pending, queued = pool.Stats()
@@ -1563,6 +1563,63 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
pool.Stop()
}
+// TestTransactionStatusCheck tests that the pool can correctly retrieve the
+// pending status of individual transactions.
+func TestTransactionStatusCheck(t *testing.T) {
+ t.Parallel()
+
+ // Create the pool to test the status retrievals with
+ db, _ := ethdb.NewMemDatabase()
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+
+ pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
+ defer pool.Stop()
+
+ // Create the test accounts to check various transaction statuses with
+ keys := make([]*ecdsa.PrivateKey, 3)
+ for i := 0; i < len(keys); i++ {
+ keys[i], _ = crypto.GenerateKey()
+ pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ }
+ // Generate and queue a batch of transactions, both pending and queued
+ txs := types.Transactions{}
+
+ txs = append(txs, pricedTransaction(0, 100000, big.NewInt(1), keys[0])) // Pending only
+ txs = append(txs, pricedTransaction(0, 100000, big.NewInt(1), keys[1])) // Pending and queued
+ txs = append(txs, pricedTransaction(2, 100000, big.NewInt(1), keys[1]))
+ txs = append(txs, pricedTransaction(2, 100000, big.NewInt(1), keys[2])) // Queued only
+
+ // Import the transaction and ensure they are correctly added
+ pool.AddRemotes(txs)
+
+ pending, queued := pool.Stats()
+ if pending != 2 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
+ }
+ if queued != 2 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
+ }
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+ // Retrieve the status of each transaction and validate them
+ hashes := make([]common.Hash, len(txs))
+ for i, tx := range txs {
+ hashes[i] = tx.Hash()
+ }
+ hashes = append(hashes, common.Hash{})
+
+ statuses := pool.Status(hashes)
+ expect := []TxStatus{TxStatusPending, TxStatusPending, TxStatusQueued, TxStatusQueued, TxStatusUnknown}
+
+ for i := 0; i < len(statuses); i++ {
+ if statuses[i] != expect[i] {
+ t.Errorf("transaction %d: status mismatch: have %v, want %v", i, statuses[i], expect[i])
+ }
+ }
+}
+
// Benchmarks the speed of validating the contents of the pending queue of the
// transaction pool.
func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) }
@@ -1574,11 +1631,11 @@ func benchmarkPendingDemotion(b *testing.B, size int) {
pool, key := setupTxPool()
defer pool.Stop()
- account, _ := deriveSender(transaction(0, big.NewInt(0), key))
+ account, _ := deriveSender(transaction(0, 0, key))
pool.currentState.AddBalance(account, big.NewInt(1000000))
for i := 0; i < size; i++ {
- tx := transaction(uint64(i), big.NewInt(100000), key)
+ tx := transaction(uint64(i), 100000, key)
pool.promoteTx(account, tx.Hash(), tx)
}
// Benchmark the speed of pool validation
@@ -1599,11 +1656,11 @@ func benchmarkFuturePromotion(b *testing.B, size int) {
pool, key := setupTxPool()
defer pool.Stop()
- account, _ := deriveSender(transaction(0, big.NewInt(0), key))
+ account, _ := deriveSender(transaction(0, 0, key))
pool.currentState.AddBalance(account, big.NewInt(1000000))
for i := 0; i < size; i++ {
- tx := transaction(uint64(1+i), big.NewInt(100000), key)
+ tx := transaction(uint64(1+i), 100000, key)
pool.enqueueTx(tx.Hash(), tx)
}
// Benchmark the speed of pool validation
@@ -1619,12 +1676,12 @@ func BenchmarkPoolInsert(b *testing.B) {
pool, key := setupTxPool()
defer pool.Stop()
- account, _ := deriveSender(transaction(0, big.NewInt(0), key))
+ account, _ := deriveSender(transaction(0, 0, key))
pool.currentState.AddBalance(account, big.NewInt(1000000))
txs := make(types.Transactions, b.N)
for i := 0; i < b.N; i++ {
- txs[i] = transaction(uint64(i), big.NewInt(100000), key)
+ txs[i] = transaction(uint64(i), 100000, key)
}
// Benchmark importing the transactions into the queue
b.ResetTimer()
@@ -1643,14 +1700,14 @@ func benchmarkPoolBatchInsert(b *testing.B, size int) {
pool, key := setupTxPool()
defer pool.Stop()
- account, _ := deriveSender(transaction(0, big.NewInt(0), key))
+ account, _ := deriveSender(transaction(0, 0, key))
pool.currentState.AddBalance(account, big.NewInt(1000000))
batches := make([]types.Transactions, b.N)
for i := 0; i < b.N; i++ {
batches[i] = make(types.Transactions, size)
for j := 0; j < size; j++ {
- batches[i][j] = transaction(uint64(size*i+j), big.NewInt(100000), key)
+ batches[i][j] = transaction(uint64(size*i+j), 100000, key)
}
}
// Benchmark importing the transactions into the queue
diff --git a/core/types.go b/core/types.go
index 1cfbbab29..d0bbaf0aa 100644
--- a/core/types.go
+++ b/core/types.go
@@ -17,8 +17,6 @@
package core
import (
- "math/big"
-
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@@ -34,7 +32,7 @@ type Validator interface {
// ValidateState validates the given statedb and optionally the receipts and
// gas used.
- ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error
+ ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error
}
// Processor is an interface for processing blocks using a given initial state.
@@ -44,5 +42,5 @@ type Validator interface {
// of gas used in the process and return an error if any of the internal rules
// failed.
type Processor interface {
- Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, *big.Int, error)
+ Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error)
}
diff --git a/core/types/block.go b/core/types/block.go
index 1d00d9f93..92b868d9d 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -25,6 +25,7 @@ import (
"sort"
"sync/atomic"
"time"
+ "unsafe"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
@@ -77,8 +78,8 @@ type Header struct {
Bloom Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
Number *big.Int `json:"number" gencodec:"required"`
- GasLimit *big.Int `json:"gasLimit" gencodec:"required"`
- GasUsed *big.Int `json:"gasUsed" gencodec:"required"`
+ GasLimit uint64 `json:"gasLimit" gencodec:"required"`
+ GasUsed uint64 `json:"gasUsed" gencodec:"required"`
Time *big.Int `json:"timestamp" gencodec:"required"`
Extra []byte `json:"extraData" gencodec:"required"`
MixDigest common.Hash `json:"mixHash" gencodec:"required"`
@@ -89,8 +90,8 @@ type Header struct {
type headerMarshaling struct {
Difficulty *hexutil.Big
Number *hexutil.Big
- GasLimit *hexutil.Big
- GasUsed *hexutil.Big
+ GasLimit hexutil.Uint64
+ GasUsed hexutil.Uint64
Time *hexutil.Big
Extra hexutil.Bytes
Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON
@@ -121,6 +122,12 @@ func (h *Header) HashNoNonce() common.Hash {
})
}
+// Size returns the approximate memory used by all internal contents. It is used
+// to approximate and limit the memory consumption of various caches.
+func (h *Header) Size() common.StorageSize {
+ return common.StorageSize(unsafe.Sizeof(*h)) + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+h.Time.BitLen())/8)
+}
+
func rlpHash(x interface{}) (h common.Hash) {
hw := sha3.NewKeccak256()
rlp.Encode(hw, x)
@@ -243,12 +250,6 @@ func CopyHeader(h *Header) *Header {
if cpy.Number = new(big.Int); h.Number != nil {
cpy.Number.Set(h.Number)
}
- if cpy.GasLimit = new(big.Int); h.GasLimit != nil {
- cpy.GasLimit.Set(h.GasLimit)
- }
- if cpy.GasUsed = new(big.Int); h.GasUsed != nil {
- cpy.GasUsed.Set(h.GasUsed)
- }
if len(h.Extra) > 0 {
cpy.Extra = make([]byte, len(h.Extra))
copy(cpy.Extra, h.Extra)
@@ -302,8 +303,8 @@ func (b *Block) Transaction(hash common.Hash) *Transaction {
}
func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) }
-func (b *Block) GasLimit() *big.Int { return new(big.Int).Set(b.header.GasLimit) }
-func (b *Block) GasUsed() *big.Int { return new(big.Int).Set(b.header.GasUsed) }
+func (b *Block) GasLimit() uint64 { return b.header.GasLimit }
+func (b *Block) GasUsed() uint64 { return b.header.GasUsed }
func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) }
func (b *Block) Time() *big.Int { return new(big.Int).Set(b.header.Time) }
@@ -328,6 +329,8 @@ func (b *Block) HashNoNonce() common.Hash {
return b.header.HashNoNonce()
}
+// Size returns the true RLP encoded storage size of the block, either by encoding
+// and returning it, or returning a previsouly cached value.
func (b *Block) Size() common.StorageSize {
if size := b.size.Load(); size != nil {
return size.(common.StorageSize)
diff --git a/core/types/block_test.go b/core/types/block_test.go
index 93435ca00..a35fbc25b 100644
--- a/core/types/block_test.go
+++ b/core/types/block_test.go
@@ -41,8 +41,8 @@ func TestBlockEncoding(t *testing.T) {
}
}
check("Difficulty", block.Difficulty(), big.NewInt(131072))
- check("GasLimit", block.GasLimit(), big.NewInt(3141592))
- check("GasUsed", block.GasUsed(), big.NewInt(21000))
+ check("GasLimit", block.GasLimit(), uint64(3141592))
+ check("GasUsed", block.GasUsed(), uint64(21000))
check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1"))
check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
@@ -51,7 +51,7 @@ func TestBlockEncoding(t *testing.T) {
check("Time", block.Time(), big.NewInt(1426516743))
check("Size", block.Size(), common.StorageSize(len(blockEnc)))
- tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), big.NewInt(50000), big.NewInt(10), nil)
+ tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil)
tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100"))
fmt.Println(block.Transactions()[0].Hash())
diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go
index bcff7a940..1b92cd9cf 100644
--- a/core/types/gen_header_json.go
+++ b/core/types/gen_header_json.go
@@ -11,6 +11,8 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
)
+var _ = (*headerMarshaling)(nil)
+
func (h Header) MarshalJSON() ([]byte, error) {
type Header struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
@@ -22,8 +24,8 @@ func (h Header) MarshalJSON() ([]byte, error) {
Bloom Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
Number *hexutil.Big `json:"number" gencodec:"required"`
- GasLimit *hexutil.Big `json:"gasLimit" gencodec:"required"`
- GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"`
+ GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
+ GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Time *hexutil.Big `json:"timestamp" gencodec:"required"`
Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
MixDigest common.Hash `json:"mixHash" gencodec:"required"`
@@ -40,8 +42,8 @@ func (h Header) MarshalJSON() ([]byte, error) {
enc.Bloom = h.Bloom
enc.Difficulty = (*hexutil.Big)(h.Difficulty)
enc.Number = (*hexutil.Big)(h.Number)
- enc.GasLimit = (*hexutil.Big)(h.GasLimit)
- enc.GasUsed = (*hexutil.Big)(h.GasUsed)
+ enc.GasLimit = hexutil.Uint64(h.GasLimit)
+ enc.GasUsed = hexutil.Uint64(h.GasUsed)
enc.Time = (*hexutil.Big)(h.Time)
enc.Extra = h.Extra
enc.MixDigest = h.MixDigest
@@ -61,10 +63,10 @@ func (h *Header) UnmarshalJSON(input []byte) error {
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
Number *hexutil.Big `json:"number" gencodec:"required"`
- GasLimit *hexutil.Big `json:"gasLimit" gencodec:"required"`
- GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"`
+ GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
+ GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Time *hexutil.Big `json:"timestamp" gencodec:"required"`
- Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
+ Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
MixDigest *common.Hash `json:"mixHash" gencodec:"required"`
Nonce *BlockNonce `json:"nonce" gencodec:"required"`
}
@@ -111,11 +113,11 @@ func (h *Header) UnmarshalJSON(input []byte) error {
if dec.GasLimit == nil {
return errors.New("missing required field 'gasLimit' for Header")
}
- h.GasLimit = (*big.Int)(dec.GasLimit)
+ h.GasLimit = uint64(*dec.GasLimit)
if dec.GasUsed == nil {
return errors.New("missing required field 'gasUsed' for Header")
}
- h.GasUsed = (*big.Int)(dec.GasUsed)
+ h.GasUsed = uint64(*dec.GasUsed)
if dec.Time == nil {
return errors.New("missing required field 'timestamp' for Header")
}
@@ -123,7 +125,7 @@ func (h *Header) UnmarshalJSON(input []byte) error {
if dec.Extra == nil {
return errors.New("missing required field 'extraData' for Header")
}
- h.Extra = dec.Extra
+ h.Extra = *dec.Extra
if dec.MixDigest == nil {
return errors.New("missing required field 'mixHash' for Header")
}
diff --git a/core/types/gen_log_json.go b/core/types/gen_log_json.go
index 92c699c2a..1b5ae3c65 100644
--- a/core/types/gen_log_json.go
+++ b/core/types/gen_log_json.go
@@ -10,6 +10,8 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
)
+var _ = (*logMarshaling)(nil)
+
func (l Log) MarshalJSON() ([]byte, error) {
type Log struct {
Address common.Address `json:"address" gencodec:"required"`
@@ -39,7 +41,7 @@ func (l *Log) UnmarshalJSON(input []byte) error {
type Log struct {
Address *common.Address `json:"address" gencodec:"required"`
Topics []common.Hash `json:"topics" gencodec:"required"`
- Data hexutil.Bytes `json:"data" gencodec:"required"`
+ Data *hexutil.Bytes `json:"data" gencodec:"required"`
BlockNumber *hexutil.Uint64 `json:"blockNumber"`
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
TxIndex *hexutil.Uint `json:"transactionIndex" gencodec:"required"`
@@ -62,7 +64,7 @@ func (l *Log) UnmarshalJSON(input []byte) error {
if dec.Data == nil {
return errors.New("missing required field 'data' for Log")
}
- l.Data = dec.Data
+ l.Data = *dec.Data
if dec.BlockNumber != nil {
l.BlockNumber = uint64(*dec.BlockNumber)
}
diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go
index b95d99c95..c297adebb 100644
--- a/core/types/gen_receipt_json.go
+++ b/core/types/gen_receipt_json.go
@@ -5,52 +5,53 @@ package types
import (
"encoding/json"
"errors"
- "math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
+var _ = (*receiptMarshaling)(nil)
+
func (r Receipt) MarshalJSON() ([]byte, error) {
type Receipt struct {
PostState hexutil.Bytes `json:"root"`
Status hexutil.Uint `json:"status"`
- CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed" gencodec:"required"`
+ CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
Bloom Bloom `json:"logsBloom" gencodec:"required"`
Logs []*Log `json:"logs" gencodec:"required"`
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress common.Address `json:"contractAddress"`
- GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"`
+ GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
}
var enc Receipt
enc.PostState = r.PostState
enc.Status = hexutil.Uint(r.Status)
- enc.CumulativeGasUsed = (*hexutil.Big)(r.CumulativeGasUsed)
+ enc.CumulativeGasUsed = hexutil.Uint64(r.CumulativeGasUsed)
enc.Bloom = r.Bloom
enc.Logs = r.Logs
enc.TxHash = r.TxHash
enc.ContractAddress = r.ContractAddress
- enc.GasUsed = (*hexutil.Big)(r.GasUsed)
+ enc.GasUsed = hexutil.Uint64(r.GasUsed)
return json.Marshal(&enc)
}
func (r *Receipt) UnmarshalJSON(input []byte) error {
type Receipt struct {
- PostState hexutil.Bytes `json:"root"`
+ PostState *hexutil.Bytes `json:"root"`
Status *hexutil.Uint `json:"status"`
- CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed" gencodec:"required"`
+ CumulativeGasUsed *hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
Logs []*Log `json:"logs" gencodec:"required"`
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress *common.Address `json:"contractAddress"`
- GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"`
+ GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
}
var dec Receipt
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.PostState != nil {
- r.PostState = dec.PostState
+ r.PostState = *dec.PostState
}
if dec.Status != nil {
r.Status = uint(*dec.Status)
@@ -58,7 +59,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
if dec.CumulativeGasUsed == nil {
return errors.New("missing required field 'cumulativeGasUsed' for Receipt")
}
- r.CumulativeGasUsed = (*big.Int)(dec.CumulativeGasUsed)
+ r.CumulativeGasUsed = uint64(*dec.CumulativeGasUsed)
if dec.Bloom == nil {
return errors.New("missing required field 'logsBloom' for Receipt")
}
@@ -77,6 +78,6 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
if dec.GasUsed == nil {
return errors.New("missing required field 'gasUsed' for Receipt")
}
- r.GasUsed = (*big.Int)(dec.GasUsed)
+ r.GasUsed = uint64(*dec.GasUsed)
return nil
}
diff --git a/core/types/gen_tx_json.go b/core/types/gen_tx_json.go
index 4fb658e0d..c27da6709 100644
--- a/core/types/gen_tx_json.go
+++ b/core/types/gen_tx_json.go
@@ -11,11 +11,13 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
)
+var _ = (*txdataMarshaling)(nil)
+
func (t txdata) MarshalJSON() ([]byte, error) {
type txdata struct {
AccountNonce hexutil.Uint64 `json:"nonce" gencodec:"required"`
Price *hexutil.Big `json:"gasPrice" gencodec:"required"`
- GasLimit *hexutil.Big `json:"gas" gencodec:"required"`
+ GasLimit hexutil.Uint64 `json:"gas" gencodec:"required"`
Recipient *common.Address `json:"to" rlp:"nil"`
Amount *hexutil.Big `json:"value" gencodec:"required"`
Payload hexutil.Bytes `json:"input" gencodec:"required"`
@@ -27,7 +29,7 @@ func (t txdata) MarshalJSON() ([]byte, error) {
var enc txdata
enc.AccountNonce = hexutil.Uint64(t.AccountNonce)
enc.Price = (*hexutil.Big)(t.Price)
- enc.GasLimit = (*hexutil.Big)(t.GasLimit)
+ enc.GasLimit = hexutil.Uint64(t.GasLimit)
enc.Recipient = t.Recipient
enc.Amount = (*hexutil.Big)(t.Amount)
enc.Payload = t.Payload
@@ -42,10 +44,10 @@ func (t *txdata) UnmarshalJSON(input []byte) error {
type txdata struct {
AccountNonce *hexutil.Uint64 `json:"nonce" gencodec:"required"`
Price *hexutil.Big `json:"gasPrice" gencodec:"required"`
- GasLimit *hexutil.Big `json:"gas" gencodec:"required"`
+ GasLimit *hexutil.Uint64 `json:"gas" gencodec:"required"`
Recipient *common.Address `json:"to" rlp:"nil"`
Amount *hexutil.Big `json:"value" gencodec:"required"`
- Payload hexutil.Bytes `json:"input" gencodec:"required"`
+ Payload *hexutil.Bytes `json:"input" gencodec:"required"`
V *hexutil.Big `json:"v" gencodec:"required"`
R *hexutil.Big `json:"r" gencodec:"required"`
S *hexutil.Big `json:"s" gencodec:"required"`
@@ -66,7 +68,7 @@ func (t *txdata) UnmarshalJSON(input []byte) error {
if dec.GasLimit == nil {
return errors.New("missing required field 'gas' for txdata")
}
- t.GasLimit = (*big.Int)(dec.GasLimit)
+ t.GasLimit = uint64(*dec.GasLimit)
if dec.Recipient != nil {
t.Recipient = dec.Recipient
}
@@ -77,7 +79,7 @@ func (t *txdata) UnmarshalJSON(input []byte) error {
if dec.Payload == nil {
return errors.New("missing required field 'input' for txdata")
}
- t.Payload = dec.Payload
+ t.Payload = *dec.Payload
if dec.V == nil {
return errors.New("missing required field 'v' for txdata")
}
diff --git a/core/types/receipt.go b/core/types/receipt.go
index bc3c996b4..f945f6f6a 100644
--- a/core/types/receipt.go
+++ b/core/types/receipt.go
@@ -20,7 +20,7 @@ import (
"bytes"
"fmt"
"io"
- "math/big"
+ "unsafe"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
@@ -45,46 +45,46 @@ const (
// Receipt represents the results of a transaction.
type Receipt struct {
// Consensus fields
- PostState []byte `json:"root"`
- Status uint `json:"status"`
- CumulativeGasUsed *big.Int `json:"cumulativeGasUsed" gencodec:"required"`
- Bloom Bloom `json:"logsBloom" gencodec:"required"`
- Logs []*Log `json:"logs" gencodec:"required"`
+ PostState []byte `json:"root"`
+ Status uint `json:"status"`
+ CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"`
+ Bloom Bloom `json:"logsBloom" gencodec:"required"`
+ Logs []*Log `json:"logs" gencodec:"required"`
// Implementation fields (don't reorder!)
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress common.Address `json:"contractAddress"`
- GasUsed *big.Int `json:"gasUsed" gencodec:"required"`
+ GasUsed uint64 `json:"gasUsed" gencodec:"required"`
}
type receiptMarshaling struct {
PostState hexutil.Bytes
Status hexutil.Uint
- CumulativeGasUsed *hexutil.Big
- GasUsed *hexutil.Big
+ CumulativeGasUsed hexutil.Uint64
+ GasUsed hexutil.Uint64
}
// receiptRLP is the consensus encoding of a receipt.
type receiptRLP struct {
PostStateOrStatus []byte
- CumulativeGasUsed *big.Int
+ CumulativeGasUsed uint64
Bloom Bloom
Logs []*Log
}
type receiptStorageRLP struct {
PostStateOrStatus []byte
- CumulativeGasUsed *big.Int
+ CumulativeGasUsed uint64
Bloom Bloom
TxHash common.Hash
ContractAddress common.Address
Logs []*LogForStorage
- GasUsed *big.Int
+ GasUsed uint64
}
// NewReceipt creates a barebone transaction receipt, copying the init fields.
-func NewReceipt(root []byte, failed bool, cumulativeGasUsed *big.Int) *Receipt {
- r := &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumulativeGasUsed)}
+func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt {
+ r := &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: cumulativeGasUsed}
if failed {
r.Status = ReceiptStatusFailed
} else {
@@ -137,6 +137,18 @@ func (r *Receipt) statusEncoding() []byte {
return r.PostState
}
+// Size returns the approximate memory used by all internal contents. It is used
+// to approximate and limit the memory consumption of various caches.
+func (r *Receipt) Size() common.StorageSize {
+ size := common.StorageSize(unsafe.Sizeof(*r)) + common.StorageSize(len(r.PostState))
+
+ size += common.StorageSize(len(r.Logs)) * common.StorageSize(unsafe.Sizeof(Log{}))
+ for _, log := range r.Logs {
+ size += common.StorageSize(len(log.Topics)*common.HashLength + len(log.Data))
+ }
+ return size
+}
+
// String implements the Stringer interface.
func (r *Receipt) String() string {
if len(r.PostState) == 0 {
diff --git a/core/types/transaction.go b/core/types/transaction.go
index 7e2933bb1..5660582ba 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -57,7 +57,7 @@ type Transaction struct {
type txdata struct {
AccountNonce uint64 `json:"nonce" gencodec:"required"`
Price *big.Int `json:"gasPrice" gencodec:"required"`
- GasLimit *big.Int `json:"gas" gencodec:"required"`
+ GasLimit uint64 `json:"gas" gencodec:"required"`
Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation
Amount *big.Int `json:"value" gencodec:"required"`
Payload []byte `json:"input" gencodec:"required"`
@@ -74,7 +74,7 @@ type txdata struct {
type txdataMarshaling struct {
AccountNonce hexutil.Uint64
Price *hexutil.Big
- GasLimit *hexutil.Big
+ GasLimit hexutil.Uint64
Amount *hexutil.Big
Payload hexutil.Bytes
V *hexutil.Big
@@ -82,15 +82,15 @@ type txdataMarshaling struct {
S *hexutil.Big
}
-func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
+func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data)
}
-func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
+func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data)
}
-func newTransaction(nonce uint64, to *common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
+func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
if len(data) > 0 {
data = common.CopyBytes(data)
}
@@ -99,7 +99,7 @@ func newTransaction(nonce uint64, to *common.Address, amount, gasLimit, gasPrice
Recipient: to,
Payload: data,
Amount: new(big.Int),
- GasLimit: new(big.Int),
+ GasLimit: gasLimit,
Price: new(big.Int),
V: new(big.Int),
R: new(big.Int),
@@ -108,9 +108,6 @@ func newTransaction(nonce uint64, to *common.Address, amount, gasLimit, gasPrice
if amount != nil {
d.Amount.Set(amount)
}
- if gasLimit != nil {
- d.GasLimit.Set(gasLimit)
- }
if gasPrice != nil {
d.Price.Set(gasPrice)
}
@@ -153,6 +150,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
return err
}
+// MarshalJSON encodes the web3 RPC transaction format.
func (tx *Transaction) MarshalJSON() ([]byte, error) {
hash := tx.Hash()
data := tx.data
@@ -168,8 +166,8 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
}
var V byte
if isProtectedV(dec.V) {
- chainId := deriveChainId(dec.V).Uint64()
- V = byte(dec.V.Uint64() - 35 - 2*chainId)
+ chainID := deriveChainId(dec.V).Uint64()
+ V = byte(dec.V.Uint64() - 35 - 2*chainID)
} else {
V = byte(dec.V.Uint64() - 27)
}
@@ -181,7 +179,7 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
}
func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
-func (tx *Transaction) Gas() *big.Int { return new(big.Int).Set(tx.data.GasLimit) }
+func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit }
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) }
func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
@@ -192,10 +190,9 @@ func (tx *Transaction) CheckNonce() bool { return true }
func (tx *Transaction) To() *common.Address {
if tx.data.Recipient == nil {
return nil
- } else {
- to := *tx.data.Recipient
- return &to
}
+ to := *tx.data.Recipient
+ return &to
}
// Hash hashes the RLP encoding of tx.
@@ -209,6 +206,8 @@ func (tx *Transaction) Hash() common.Hash {
return v
}
+// Size returns the true RLP encoded storage size of the transaction, either by
+// encoding and returning it, or returning a previsouly cached value.
func (tx *Transaction) Size() common.StorageSize {
if size := tx.size.Load(); size != nil {
return size.(common.StorageSize)
@@ -227,8 +226,8 @@ func (tx *Transaction) Size() common.StorageSize {
func (tx *Transaction) AsMessage(s Signer) (Message, error) {
msg := Message{
nonce: tx.data.AccountNonce,
- price: new(big.Int).Set(tx.data.Price),
- gasLimit: new(big.Int).Set(tx.data.GasLimit),
+ gasLimit: tx.data.GasLimit,
+ gasPrice: new(big.Int).Set(tx.data.Price),
to: tx.data.Recipient,
amount: tx.data.Amount,
data: tx.data.Payload,
@@ -254,7 +253,7 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e
// Cost returns amount + gasprice * gaslimit.
func (tx *Transaction) Cost() *big.Int {
- total := new(big.Int).Mul(tx.data.Price, tx.data.GasLimit)
+ total := new(big.Int).Mul(tx.data.Price, new(big.Int).SetUint64(tx.data.GasLimit))
total.Add(total, tx.data.Amount)
return total
}
@@ -315,22 +314,22 @@ func (tx *Transaction) String() string {
)
}
-// Transaction slice type for basic sorting.
+// Transactions is a Transaction slice type for basic sorting.
type Transactions []*Transaction
-// Len returns the length of s
+// Len returns the length of s.
func (s Transactions) Len() int { return len(s) }
-// Swap swaps the i'th and the j'th element in s
+// Swap swaps the i'th and the j'th element in s.
func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-// GetRlp implements Rlpable and returns the i'th element of s in rlp
+// GetRlp implements Rlpable and returns the i'th element of s in rlp.
func (s Transactions) GetRlp(i int) []byte {
enc, _ := rlp.EncodeToBytes(s[i])
return enc
}
-// Returns a new set t which is the difference between a to b
+// TxDifference returns a new set t which is the difference between a to b.
func TxDifference(a, b Transactions) (keep Transactions) {
keep = make(Transactions, 0, len(a))
@@ -378,7 +377,7 @@ func (s *TxByPrice) Pop() interface{} {
}
// TransactionsByPriceAndNonce represents a set of transactions that can return
-// transactions in a profit-maximising sorted order, while supporting removing
+// transactions in a profit-maximizing sorted order, while supporting removing
// entire batches of transactions for non-executable accounts.
type TransactionsByPriceAndNonce struct {
txs map[common.Address]Transactions // Per account nonce-sorted list of transactions
@@ -440,22 +439,24 @@ func (t *TransactionsByPriceAndNonce) Pop() {
//
// NOTE: In a future PR this will be removed.
type Message struct {
- to *common.Address
- from common.Address
- nonce uint64
- amount, price, gasLimit *big.Int
- data []byte
- checkNonce bool
+ to *common.Address
+ from common.Address
+ nonce uint64
+ amount *big.Int
+ gasLimit uint64
+ gasPrice *big.Int
+ data []byte
+ checkNonce bool
}
-func NewMessage(from common.Address, to *common.Address, nonce uint64, amount, gasLimit, price *big.Int, data []byte, checkNonce bool) Message {
+func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool) Message {
return Message{
from: from,
to: to,
nonce: nonce,
amount: amount,
- price: price,
gasLimit: gasLimit,
+ gasPrice: gasPrice,
data: data,
checkNonce: checkNonce,
}
@@ -463,9 +464,9 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount, g
func (m Message) From() common.Address { return m.from }
func (m Message) To() *common.Address { return m.to }
-func (m Message) GasPrice() *big.Int { return m.price }
+func (m Message) GasPrice() *big.Int { return m.gasPrice }
func (m Message) Value() *big.Int { return m.amount }
-func (m Message) Gas() *big.Int { return m.gasLimit }
+func (m Message) Gas() uint64 { return m.gasLimit }
func (m Message) Nonce() uint64 { return m.nonce }
func (m Message) Data() []byte { return m.data }
func (m Message) CheckNonce() bool { return m.checkNonce }
diff --git a/core/types/transaction_signing_test.go b/core/types/transaction_signing_test.go
index 7f799fb10..689fc38a9 100644
--- a/core/types/transaction_signing_test.go
+++ b/core/types/transaction_signing_test.go
@@ -30,7 +30,7 @@ func TestEIP155Signing(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey)
signer := NewEIP155Signer(big.NewInt(18))
- tx, err := SignTx(NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil), signer, key)
+ tx, err := SignTx(NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil), signer, key)
if err != nil {
t.Fatal(err)
}
@@ -49,7 +49,7 @@ func TestEIP155ChainId(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey)
signer := NewEIP155Signer(big.NewInt(18))
- tx, err := SignTx(NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil), signer, key)
+ tx, err := SignTx(NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil), signer, key)
if err != nil {
t.Fatal(err)
}
@@ -61,7 +61,7 @@ func TestEIP155ChainId(t *testing.T) {
t.Error("expected chainId to be", signer.chainId, "got", tx.ChainId())
}
- tx = NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil)
+ tx = NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil)
tx, err = SignTx(tx, HomesteadSigner{}, key)
if err != nil {
t.Fatal(err)
@@ -118,7 +118,7 @@ func TestEIP155SigningVitalik(t *testing.T) {
func TestChainId(t *testing.T) {
key, _ := defaultTestKey()
- tx := NewTransaction(0, common.Address{}, new(big.Int), new(big.Int), new(big.Int), nil)
+ tx := NewTransaction(0, common.Address{}, new(big.Int), 0, new(big.Int), nil)
var err error
tx, err = SignTx(tx, NewEIP155Signer(big.NewInt(1)), key)
diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go
index 82d74e3b3..d1861b14c 100644
--- a/core/types/transaction_test.go
+++ b/core/types/transaction_test.go
@@ -34,7 +34,7 @@ var (
emptyTx = NewTransaction(
0,
common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
- big.NewInt(0), big.NewInt(0), big.NewInt(0),
+ big.NewInt(0), 0, big.NewInt(0),
nil,
)
@@ -42,7 +42,7 @@ var (
3,
common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"),
big.NewInt(10),
- big.NewInt(2000),
+ 2000,
big.NewInt(1),
common.FromHex("5544"),
).WithSignature(
@@ -139,7 +139,7 @@ func TestTransactionPriceNonceSort(t *testing.T) {
for start, key := range keys {
addr := crypto.PubkeyToAddress(key.PublicKey)
for i := 0; i < 25; i++ {
- tx, _ := SignTx(NewTransaction(uint64(start+i), common.Address{}, big.NewInt(100), big.NewInt(100), big.NewInt(int64(start+i)), nil), signer, key)
+ tx, _ := SignTx(NewTransaction(uint64(start+i), common.Address{}, big.NewInt(100), 100, big.NewInt(int64(start+i)), nil), signer, key)
groups[addr] = append(groups[addr], tx)
}
}
@@ -204,9 +204,9 @@ func TestTransactionJSON(t *testing.T) {
var tx *Transaction
switch i % 2 {
case 0:
- tx = NewTransaction(i, common.Address{1}, common.Big0, common.Big1, common.Big2, []byte("abcdef"))
+ tx = NewTransaction(i, common.Address{1}, common.Big0, 1, common.Big2, []byte("abcdef"))
case 1:
- tx = NewContractCreation(i, common.Big0, common.Big1, common.Big2, []byte("abcdef"))
+ tx = NewContractCreation(i, common.Big0, 1, common.Big2, []byte("abcdef"))
}
tx, err := SignTx(tx, signer, key)
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 344435f73..46e7baff4 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -19,6 +19,7 @@ package vm
import (
"math/big"
"sync/atomic"
+ "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
@@ -38,7 +39,7 @@ type (
)
// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
-func run(evm *EVM, snapshot int, contract *Contract, input []byte) ([]byte, error) {
+func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
if contract.CodeAddr != nil {
precompiles := PrecompiledContractsHomestead
if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
@@ -48,7 +49,7 @@ func run(evm *EVM, snapshot int, contract *Contract, input []byte) ([]byte, erro
return RunPrecompiledContract(p, input, contract)
}
}
- return evm.interpreter.Run(snapshot, contract, input)
+ return evm.interpreter.Run(contract, input)
}
// Context provides the EVM with auxiliary information. Once provided
@@ -68,7 +69,7 @@ type Context struct {
// Block information
Coinbase common.Address // Provides information for COINBASE
- GasLimit *big.Int // Provides information for GASLIMIT
+ GasLimit uint64 // Provides information for GASLIMIT
BlockNumber *big.Int // Provides information for NUMBER
Time *big.Int // Provides information for TIME
Difficulty *big.Int // Provides information for DIFFICULTY
@@ -165,13 +166,23 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
}
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
- // initialise a new contract and set the code that is to be used by the
- // E The contract is a scoped environment for this execution context
- // only.
+ // Initialise a new contract and set the code that is to be used by the EVM.
+ // The contract is a scoped environment for this execution context only.
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
- ret, err = run(evm, snapshot, contract, input)
+ start := time.Now()
+
+ // Capture the tracer start/end events in debug mode
+ if evm.vmConfig.Debug && evm.depth == 0 {
+ evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
+
+ defer func() { // Lazy evaluation of the parameters
+ evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
+ }()
+ }
+ ret, err = run(evm, contract, input)
+
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors.
@@ -215,7 +226,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
- ret, err = run(evm, snapshot, contract, input)
+ ret, err = run(evm, contract, input)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
@@ -248,7 +259,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
contract := NewContract(caller, to, nil, gas).AsDelegate()
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
- ret, err = run(evm, snapshot, contract, input)
+ ret, err = run(evm, contract, input)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
@@ -291,7 +302,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in Homestead this also counts for code storage gas errors.
- ret, err = run(evm, snapshot, contract, input)
+ ret, err = run(evm, contract, input)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
@@ -338,7 +349,14 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, contractAddr, gas, nil
}
- ret, err = run(evm, snapshot, contract, nil)
+
+ if evm.vmConfig.Debug && evm.depth == 0 {
+ evm.vmConfig.Tracer.CaptureStart(caller.Address(), contractAddr, true, code, gas, value)
+ }
+ start := time.Now()
+
+ ret, err = run(evm, contract, nil)
+
// check whether the max code size has been exceeded
maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
// if the contract creation ran successfully and no errors were returned
@@ -367,10 +385,13 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
if maxCodeSizeExceeded && err == nil {
err = errMaxCodeSizeExceeded
}
+ if evm.vmConfig.Debug && evm.depth == 0 {
+ evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
+ }
return ret, contractAddr, contract.Gas, err
}
-// ChainConfig returns the evmironment's chain configuration
+// ChainConfig returns the environment's chain configuration
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
// Interpreter returns the EVM interpreter
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index ff109af57..83adba428 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -17,8 +17,6 @@
package vm
import (
- "math/big"
-
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/params"
@@ -130,7 +128,7 @@ func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, m
// 0 => non 0
return params.SstoreSetGas, nil
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
- evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SstoreRefundGas))
+ evm.StateDB.AddRefund(params.SstoreRefundGas)
return params.SstoreClearGas, nil
} else {
@@ -405,7 +403,7 @@ func gasSuicide(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack,
}
if !evm.StateDB.HasSuicided(contract.Address()) {
- evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SuicideRefundGas))
+ evm.StateDB.AddRefund(params.SuicideRefundGas)
}
return gas, nil
}
diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go
index 88df942dc..ade3ca631 100644
--- a/core/vm/gen_structlog.go
+++ b/core/vm/gen_structlog.go
@@ -11,19 +11,22 @@ import (
"github.com/ethereum/go-ethereum/common/math"
)
+var _ = (*structLogMarshaling)(nil)
+
func (s StructLog) MarshalJSON() ([]byte, error) {
type StructLog struct {
- Pc uint64 `json:"pc"`
- Op OpCode `json:"op"`
- Gas math.HexOrDecimal64 `json:"gas"`
- GasCost math.HexOrDecimal64 `json:"gasCost"`
- Memory hexutil.Bytes `json:"memory"`
- MemorySize int `json:"memSize"`
- Stack []*math.HexOrDecimal256 `json:"stack"`
- Storage map[common.Hash]common.Hash `json:"-"`
- Depth int `json:"depth"`
- Err error `json:"error"`
- OpName string `json:"opName"`
+ Pc uint64 `json:"pc"`
+ Op OpCode `json:"op"`
+ Gas math.HexOrDecimal64 `json:"gas"`
+ GasCost math.HexOrDecimal64 `json:"gasCost"`
+ Memory hexutil.Bytes `json:"memory"`
+ MemorySize int `json:"memSize"`
+ Stack []*math.HexOrDecimal256 `json:"stack"`
+ Storage map[common.Hash]common.Hash `json:"-"`
+ Depth int `json:"depth"`
+ Err error `json:"-"`
+ OpName string `json:"opName"`
+ ErrorString string `json:"error"`
}
var enc StructLog
enc.Pc = s.Pc
@@ -42,6 +45,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
enc.Depth = s.Depth
enc.Err = s.Err
enc.OpName = s.OpName()
+ enc.ErrorString = s.ErrorString()
return json.Marshal(&enc)
}
@@ -51,12 +55,12 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
Op *OpCode `json:"op"`
Gas *math.HexOrDecimal64 `json:"gas"`
GasCost *math.HexOrDecimal64 `json:"gasCost"`
- Memory hexutil.Bytes `json:"memory"`
+ Memory *hexutil.Bytes `json:"memory"`
MemorySize *int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth *int `json:"depth"`
- Err *error `json:"error"`
+ Err error `json:"-"`
}
var dec StructLog
if err := json.Unmarshal(input, &dec); err != nil {
@@ -75,7 +79,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
s.GasCost = uint64(*dec.GasCost)
}
if dec.Memory != nil {
- s.Memory = dec.Memory
+ s.Memory = *dec.Memory
}
if dec.MemorySize != nil {
s.MemorySize = *dec.MemorySize
@@ -93,7 +97,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
s.Depth = *dec.Depth
}
if dec.Err != nil {
- s.Err = *dec.Err
+ s.Err = dec.Err
}
return nil
}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 1d1585fca..766172501 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -472,7 +472,7 @@ func opDifficulty(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stac
}
func opGasLimit(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(math.U256(new(big.Int).Set(evm.GasLimit)))
+ stack.push(math.U256(new(big.Int).SetUint64(evm.GasLimit)))
return nil, nil
}
diff --git a/core/vm/interface.go b/core/vm/interface.go
index c0c52732b..1ef91cf1d 100644
--- a/core/vm/interface.go
+++ b/core/vm/interface.go
@@ -39,8 +39,8 @@ type StateDB interface {
SetCode(common.Address, []byte)
GetCodeSize(common.Address) int
- AddRefund(*big.Int)
- GetRefund() *big.Int
+ AddRefund(uint64)
+ GetRefund() uint64
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash)
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index ac6000f97..482e67a3a 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -107,9 +107,9 @@ func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack
// the return byte-slice and an error if one occurred.
//
// It's important to note that any errors returned by the interpreter should be
-// considered a revert-and-consume-all-gas operation. No error specific checks
-// should be handled to reduce complexity and errors further down the in.
-func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret []byte, err error) {
+// considered a revert-and-consume-all-gas operation except for
+// errExecutionReverted which means revert-and-keep-gas-left.
+func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) {
// Increment the call depth which is restricted to 1024
in.evm.depth++
defer func() { in.evm.depth-- }()
@@ -144,12 +144,17 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
)
contract.Input = input
- defer func() {
- if err != nil && !logged && in.cfg.Debug {
- in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
- }
- }()
-
+ if in.cfg.Debug {
+ defer func() {
+ if err != nil {
+ if !logged {
+ in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
+ } else {
+ in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
+ }
+ }
+ }()
+ }
// The Interpreter main run loop (contextual). This loop runs until either an
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
// the execution of one of the operations or until the done flag is set by the
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 75309da92..4c820d8b5 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -62,29 +62,39 @@ type StructLog struct {
Stack []*big.Int `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
- Err error `json:"error"`
+ Err error `json:"-"`
}
// overrides for gencodec
type structLogMarshaling struct {
- Stack []*math.HexOrDecimal256
- Gas math.HexOrDecimal64
- GasCost math.HexOrDecimal64
- Memory hexutil.Bytes
- OpName string `json:"opName"`
+ Stack []*math.HexOrDecimal256
+ Gas math.HexOrDecimal64
+ GasCost math.HexOrDecimal64
+ Memory hexutil.Bytes
+ OpName string `json:"opName"` // adds call to OpName() in MarshalJSON
+ ErrorString string `json:"error"` // adds call to ErrorString() in MarshalJSON
}
func (s *StructLog) OpName() string {
return s.Op.String()
}
+func (s *StructLog) ErrorString() string {
+ if s.Err != nil {
+ return s.Err.Error()
+ }
+ return ""
+}
+
// Tracer is used to collect execution traces from an EVM transaction
// execution. CaptureState is called for each step of the VM with the
// current VM state.
// Note that reference types are actual VM data structures; make copies
// if you need to retain them beyond the current call.
type Tracer interface {
+ CaptureStart(from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
+ CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
}
@@ -98,6 +108,8 @@ type StructLogger struct {
logs []StructLog
changedValues map[common.Address]Storage
+ output []byte
+ err error
}
// NewStructLogger returns a new logger
@@ -111,6 +123,10 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
return logger
}
+func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
+ return nil
+}
+
// CaptureState logs a new structured log message and pushes it out to the environment
//
// CaptureState also tracks SSTORE ops to track dirty values.
@@ -161,19 +177,25 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
return nil
}
-func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
- fmt.Printf("0x%x", output)
- if err != nil {
- fmt.Printf(" error: %v\n", err)
- }
+func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
return nil
}
-// StructLogs returns a list of captured log entries
-func (l *StructLogger) StructLogs() []StructLog {
- return l.logs
+func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
+ l.output = output
+ l.err = err
+ return nil
}
+// StructLogs returns the captured log entries.
+func (l *StructLogger) StructLogs() []StructLog { return l.logs }
+
+// Error returns the VM error captured by the trace.
+func (l *StructLogger) Error() error { return l.err }
+
+// Output returns the VM return value captured by the trace.
+func (l *StructLogger) Output() []byte { return l.output }
+
// WriteTrace writes a formatted trace to the given writer
func WriteTrace(writer io.Writer, logs []StructLog) {
for _, log := range logs {
diff --git a/core/vm/noop.go b/core/vm/noop.go
index 2a04a9565..b71ead0d7 100644
--- a/core/vm/noop.go
+++ b/core/vm/noop.go
@@ -55,8 +55,8 @@ func (NoopStateDB) GetCodeHash(common.Address) common.Hash
func (NoopStateDB) GetCode(common.Address) []byte { return nil }
func (NoopStateDB) SetCode(common.Address, []byte) {}
func (NoopStateDB) GetCodeSize(common.Address) int { return 0 }
-func (NoopStateDB) AddRefund(*big.Int) {}
-func (NoopStateDB) GetRefund() *big.Int { return nil }
+func (NoopStateDB) AddRefund(uint64) {}
+func (NoopStateDB) GetRefund() uint64 { return 0 }
func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} }
func (NoopStateDB) SetState(common.Address, common.Hash, common.Hash) {}
func (NoopStateDB) Suicide(common.Address) bool { return false }
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
index 818da1be2..31c9b9cf9 100644
--- a/core/vm/runtime/env.go
+++ b/core/vm/runtime/env.go
@@ -17,8 +17,6 @@
package runtime
import (
- "math/big"
-
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/vm"
@@ -35,7 +33,7 @@ func NewEnv(cfg *Config) *vm.EVM {
BlockNumber: cfg.BlockNumber,
Time: cfg.Time,
Difficulty: cfg.Difficulty,
- GasLimit: new(big.Int).SetUint64(cfg.GasLimit),
+ GasLimit: cfg.GasLimit,
GasPrice: cfg.GasPrice,
}