From 9e5f03b6c487175cc5aa1224e5e12fd573f483a7 Mon Sep 17 00:00:00 2001
From: Felix Lange <fjl@users.noreply.github.com>
Date: Tue, 27 Jun 2017 15:57:06 +0200
Subject: core/state: access trie through Database interface, track errors
 (#14589)

With this commit, core/state's access to the underlying key/value database is
mediated through an interface. Database errors are tracked in StateDB and
returned by CommitTo or the new Error method.

Motivation for this change: We can remove the light client's duplicated copy of
core/state. The light client now supports node iteration, so tracing and storage
enumeration can work with the light client (not implemented in this commit).
---
 core/block_validator.go          |  12 +--
 core/blockchain.go               |  38 ++++------
 core/blockchain_test.go          |  10 +--
 core/chain_makers.go             |   4 +-
 core/genesis.go                  |   2 +-
 core/state/database.go           | 154 +++++++++++++++++++++++++++++++++++++++
 core/state/dump.go               |   2 +-
 core/state/iterator.go           |  13 +---
 core/state/iterator_test.go      |   8 +-
 core/state/managed_state_test.go |   2 +-
 core/state/state_object.go       |  56 ++++++++------
 core/state/state_test.go         |  39 +++++-----
 core/state/statedb.go            | 139 ++++++++---------------------------
 core/state/statedb_test.go       |  40 +++++-----
 core/state/sync_test.go          |  75 +++++++++++--------
 core/tx_pool_test.go             |  24 +++---
 core/vm/runtime/runtime.go       |   4 +-
 core/vm/runtime/runtime_test.go  |   2 +-
 18 files changed, 354 insertions(+), 270 deletions(-)
 create mode 100644 core/state/database.go

(limited to 'core')

diff --git a/core/block_validator.go b/core/block_validator.go
index 4f85df007..e9cfd0482 100644
--- a/core/block_validator.go
+++ b/core/block_validator.go
@@ -52,16 +52,10 @@ 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.HasBlock(block.Hash()) {
-		if _, err := state.New(block.Root(), v.bc.chainDb); err == nil {
-			return ErrKnownBlock
-		}
+	if v.bc.HasBlockAndState(block.Hash()) {
+		return ErrKnownBlock
 	}
-	parent := v.bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
-	if parent == nil {
-		return consensus.ErrUnknownAncestor
-	}
-	if _, err := state.New(parent.Root(), v.bc.chainDb); err != nil {
+	if !v.bc.HasBlockAndState(block.ParentHash()) {
 		return consensus.ErrUnknownAncestor
 	}
 	// Header validity is known at this point, check the uncles and transactions
diff --git a/core/blockchain.go b/core/blockchain.go
index 073b91bab..aab2e72f3 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -92,7 +92,7 @@ type BlockChain struct {
 	currentBlock     *types.Block // Current head of the block chain
 	currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!)
 
-	stateCache   *state.StateDB // State database to reuse between imports (contains state cache)
+	stateCache   state.Database // State database to reuse between imports (contains state cache)
 	bodyCache    *lru.Cache     // Cache for the most recent block bodies
 	bodyRLPCache *lru.Cache     // Cache for the most recent block bodies in RLP encoded format
 	blockCache   *lru.Cache     // Cache for the most recent entire blocks
@@ -125,6 +125,7 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine co
 	bc := &BlockChain{
 		config:       config,
 		chainDb:      chainDb,
+		stateCache:   state.NewDatabase(chainDb),
 		eventMux:     mux,
 		quit:         make(chan struct{}),
 		bodyCache:    bodyCache,
@@ -190,7 +191,7 @@ func (bc *BlockChain) loadLastState() error {
 		return bc.Reset()
 	}
 	// Make sure the state associated with the block is available
-	if _, err := state.New(currentBlock.Root(), bc.chainDb); err != nil {
+	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()
@@ -214,12 +215,6 @@ func (bc *BlockChain) loadLastState() error {
 			bc.currentFastBlock = block
 		}
 	}
-	// Initialize a statedb cache to ensure singleton account bloom filter generation
-	statedb, err := state.New(bc.currentBlock.Root(), bc.chainDb)
-	if err != nil {
-		return err
-	}
-	bc.stateCache = statedb
 
 	// Issue a status log for the user
 	headerTd := bc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())
@@ -261,7 +256,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
 		bc.currentBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
 	}
 	if bc.currentBlock != nil {
-		if _, err := state.New(bc.currentBlock.Root(), bc.chainDb); err != nil {
+		if _, err := state.New(bc.currentBlock.Root(), bc.stateCache); err != nil {
 			// Rewound state missing, rolled back to before pivot, reset to genesis
 			bc.currentBlock = nil
 		}
@@ -384,7 +379,7 @@ func (bc *BlockChain) State() (*state.StateDB, error) {
 
 // StateAt returns a new mutable state based on a particular point in time.
 func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
-	return bc.stateCache.New(root)
+	return state.New(root, bc.stateCache)
 }
 
 // Reset purges the entire blockchain, restoring it to its genesis state.
@@ -531,7 +526,7 @@ func (bc *BlockChain) HasBlockAndState(hash common.Hash) bool {
 		return false
 	}
 	// Ensure the associated state is also present
-	_, err := state.New(block.Root(), bc.chainDb)
+	_, err := bc.stateCache.OpenTrie(block.Root())
 	return err == nil
 }
 
@@ -959,31 +954,30 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
 		}
 		// Create a new statedb using the parent block and report an
 		// error if it fails.
-		switch {
-		case i == 0:
-			err = bc.stateCache.Reset(bc.GetBlock(block.ParentHash(), block.NumberU64()-1).Root())
-		default:
-			err = bc.stateCache.Reset(chain[i-1].Root())
+		var parent *types.Block
+		if i == 0 {
+			parent = bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
+		} else {
+			parent = chain[i-1]
 		}
+		state, err := state.New(parent.Root(), bc.stateCache)
 		if err != nil {
-			bc.reportBlock(block, nil, err)
 			return i, err
 		}
 		// Process block using the parent state as reference point.
-		receipts, logs, usedGas, err := bc.processor.Process(block, bc.stateCache, bc.vmConfig)
+		receipts, logs, usedGas, err := bc.processor.Process(block, state, bc.vmConfig)
 		if err != nil {
 			bc.reportBlock(block, receipts, err)
 			return i, err
 		}
 		// Validate the state using the default validator
-		err = bc.Validator().ValidateState(block, bc.GetBlock(block.ParentHash(), block.NumberU64()-1), bc.stateCache, receipts, usedGas)
+		err = bc.Validator().ValidateState(block, parent, state, receipts, usedGas)
 		if err != nil {
 			bc.reportBlock(block, receipts, err)
 			return i, err
 		}
 		// Write state changes to database
-		_, err = bc.stateCache.Commit(bc.config.IsEIP158(block.Number()))
-		if err != nil {
+		if _, err = state.CommitTo(bc.chainDb, bc.config.IsEIP158(block.Number())); err != nil {
 			return i, err
 		}
 
@@ -1021,7 +1015,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
 				return i, err
 			}
 			// Write hash preimages
-			if err := WritePreimages(bc.chainDb, block.NumberU64(), bc.stateCache.Preimages()); err != nil {
+			if err := WritePreimages(bc.chainDb, block.NumberU64(), state.Preimages()); err != nil {
 				return i, err
 			}
 		case SideStatTy:
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 7505208e1..371522ab7 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -131,7 +131,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
 			}
 			return err
 		}
-		statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.chainDb)
+		statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.stateCache)
 		if err != nil {
 			return err
 		}
@@ -148,7 +148,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
 		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.Commit(false)
+		statedb.CommitTo(blockchain.chainDb, false)
 		blockchain.mu.Unlock()
 	}
 	return nil
@@ -1131,7 +1131,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
 	if _, err := blockchain.InsertChain(types.Blocks{blocks[0]}); err != nil {
 		t.Fatal(err)
 	}
-	if !blockchain.stateCache.Exist(theAddr) {
+	if st, _ := blockchain.State(); !st.Exist(theAddr) {
 		t.Error("expected account to exist")
 	}
 
@@ -1139,7 +1139,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
 	if _, err := blockchain.InsertChain(types.Blocks{blocks[1]}); err != nil {
 		t.Fatal(err)
 	}
-	if blockchain.stateCache.Exist(theAddr) {
+	if st, _ := blockchain.State(); st.Exist(theAddr) {
 		t.Error("account should not exist")
 	}
 
@@ -1147,7 +1147,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
 	if _, err := blockchain.InsertChain(types.Blocks{blocks[2]}); err != nil {
 		t.Fatal(err)
 	}
-	if blockchain.stateCache.Exist(theAddr) {
+	if st, _ := blockchain.State(); st.Exist(theAddr) {
 		t.Error("account should not exist")
 	}
 }
diff --git a/core/chain_makers.go b/core/chain_makers.go
index cc14f8fb8..38a69d42a 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -181,7 +181,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Dat
 			gen(i, b)
 		}
 		ethash.AccumulateRewards(statedb, h, b.uncles)
-		root, err := statedb.Commit(config.IsEIP158(h.Number))
+		root, err := statedb.CommitTo(db, config.IsEIP158(h.Number))
 		if err != nil {
 			panic(fmt.Sprintf("state write error: %v", err))
 		}
@@ -189,7 +189,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Dat
 		return types.NewBlock(h, b.txs, b.uncles, b.receipts), b.receipts
 	}
 	for i := 0; i < n; i++ {
-		statedb, err := state.New(parent.Root(), db)
+		statedb, err := state.New(parent.Root(), state.NewDatabase(db))
 		if err != nil {
 			panic(err)
 		}
diff --git a/core/genesis.go b/core/genesis.go
index 947a53c70..5815d5901 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -176,7 +176,7 @@ 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()
-	statedb, _ := state.New(common.Hash{}, db)
+	statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
 	for addr, account := range g.Alloc {
 		statedb.AddBalance(addr, account.Balance)
 		statedb.SetCode(addr, account.Code)
diff --git a/core/state/database.go b/core/state/database.go
new file mode 100644
index 000000000..946625e76
--- /dev/null
+++ b/core/state/database.go
@@ -0,0 +1,154 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package state
+
+import (
+	"fmt"
+	"sync"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/ethdb"
+	"github.com/ethereum/go-ethereum/trie"
+	lru "github.com/hashicorp/golang-lru"
+)
+
+// Trie cache generation limit after which to evic trie nodes from memory.
+var MaxTrieCacheGen = uint16(120)
+
+const (
+	// Number of past tries to keep. This value is chosen such that
+	// reasonable chain reorg depths will hit an existing trie.
+	maxPastTries = 12
+
+	// Number of codehash->size associations to keep.
+	codeSizeCacheSize = 100000
+)
+
+// 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(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
+}
+
+// Trie is a Ethereum Merkle Trie.
+type Trie interface {
+	TryGet(key []byte) ([]byte, error)
+	TryUpdate(key, value []byte) error
+	TryDelete(key []byte) error
+	CommitTo(trie.DatabaseWriter) (common.Hash, error)
+	Hash() common.Hash
+	NodeIterator(startKey []byte) trie.NodeIterator
+	GetKey([]byte) []byte // TODO(fjl): remove this when SecureTrie is removed
+}
+
+// NewDatabase creates a backing store for state. The returned database is safe for
+// concurrent use and retains cached trie nodes in memory.
+func NewDatabase(db ethdb.Database) Database {
+	csc, _ := lru.New(codeSizeCacheSize)
+	return &cachingDB{db: db, codeSizeCache: csc}
+}
+
+type cachingDB struct {
+	db            ethdb.Database
+	mu            sync.Mutex
+	pastTries     []*trie.SecureTrie
+	codeSizeCache *lru.Cache
+}
+
+func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
+	db.mu.Lock()
+	defer db.mu.Unlock()
+
+	for i := len(db.pastTries) - 1; i >= 0; i-- {
+		if db.pastTries[i].Hash() == root {
+			return cachedTrie{db.pastTries[i].Copy(), db}, nil
+		}
+	}
+	tr, err := trie.NewSecure(root, db.db, MaxTrieCacheGen)
+	if err != nil {
+		return nil, err
+	}
+	return cachedTrie{tr, db}, nil
+}
+
+func (db *cachingDB) pushTrie(t *trie.SecureTrie) {
+	db.mu.Lock()
+	defer db.mu.Unlock()
+
+	if len(db.pastTries) >= maxPastTries {
+		copy(db.pastTries, db.pastTries[1:])
+		db.pastTries[len(db.pastTries)-1] = t
+	} else {
+		db.pastTries = append(db.pastTries, t)
+	}
+}
+
+func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {
+	return trie.NewSecure(root, db.db, 0)
+}
+
+func (db *cachingDB) CopyTrie(t Trie) Trie {
+	switch t := t.(type) {
+	case cachedTrie:
+		return cachedTrie{t.SecureTrie.Copy(), db}
+	case *trie.SecureTrie:
+		return t.Copy()
+	default:
+		panic(fmt.Errorf("unknown trie type %T", t))
+	}
+}
+
+func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
+	code, err := db.db.Get(codeHash[:])
+	if err == nil {
+		db.codeSizeCache.Add(codeHash, len(code))
+	}
+	return code, err
+}
+
+func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) {
+	if cached, ok := db.codeSizeCache.Get(codeHash); ok {
+		return cached.(int), nil
+	}
+	code, err := db.ContractCode(addrHash, codeHash)
+	if err == nil {
+		db.codeSizeCache.Add(codeHash, len(code))
+	}
+	return len(code), err
+}
+
+// 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)
+	if err == nil {
+		m.db.pushTrie(m.SecureTrie)
+	}
+	return root, err
+}
diff --git a/core/state/dump.go b/core/state/dump.go
index ffa1a7283..46e612850 100644
--- a/core/state/dump.go
+++ b/core/state/dump.go
@@ -41,7 +41,7 @@ type Dump struct {
 
 func (self *StateDB) RawDump() Dump {
 	dump := Dump{
-		Root:     common.Bytes2Hex(self.trie.Root()),
+		Root:     fmt.Sprintf("%x", self.trie.Hash()),
 		Accounts: make(map[string]DumpAccount),
 	}
 
diff --git a/core/state/iterator.go b/core/state/iterator.go
index a8a2722ae..6a5c73d3d 100644
--- a/core/state/iterator.go
+++ b/core/state/iterator.go
@@ -19,7 +19,6 @@ package state
 import (
 	"bytes"
 	"fmt"
-	"math/big"
 
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/rlp"
@@ -105,16 +104,11 @@ func (it *NodeIterator) step() error {
 		return nil
 	}
 	// Otherwise we've reached an account node, initiate data iteration
-	var account struct {
-		Nonce    uint64
-		Balance  *big.Int
-		Root     common.Hash
-		CodeHash []byte
-	}
+	var account Account
 	if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob()), &account); err != nil {
 		return err
 	}
-	dataTrie, err := trie.New(account.Root, it.state.db)
+	dataTrie, err := it.state.db.OpenStorageTrie(common.BytesToHash(it.stateIt.LeafKey()), account.Root)
 	if err != nil {
 		return err
 	}
@@ -124,7 +118,8 @@ func (it *NodeIterator) step() error {
 	}
 	if !bytes.Equal(account.CodeHash, emptyCodeHash) {
 		it.codeHash = common.BytesToHash(account.CodeHash)
-		it.code, err = it.state.db.Get(account.CodeHash)
+		addrHash := common.BytesToHash(it.stateIt.LeafKey())
+		it.code, err = it.state.db.ContractCode(addrHash, common.BytesToHash(account.CodeHash))
 		if err != nil {
 			return fmt.Errorf("code %x: %v", account.CodeHash, err)
 		}
diff --git a/core/state/iterator_test.go b/core/state/iterator_test.go
index aa9c5b728..ff66ba7a9 100644
--- a/core/state/iterator_test.go
+++ b/core/state/iterator_test.go
@@ -21,13 +21,12 @@ 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, root, _ := makeTestState()
+	db, mem, root, _ := makeTestState()
 
 	state, err := New(root, db)
 	if err != nil {
@@ -40,13 +39,14 @@ func TestNodeIteratorCoverage(t *testing.T) {
 			hashes[it.Hash] = struct{}{}
 		}
 	}
+
 	// Cross check the hashes and the database itself
 	for hash := range hashes {
-		if _, err := db.Get(hash.Bytes()); err != nil {
+		if _, err := mem.Get(hash.Bytes()); err != nil {
 			t.Errorf("failed to retrieve reported node %x: %v", hash, err)
 		}
 	}
-	for _, key := range db.(*ethdb.MemDatabase).Keys() {
+	for _, key := range mem.Keys() {
 		if bytes.HasPrefix(key, []byte("secure-key-")) {
 			continue
 		}
diff --git a/core/state/managed_state_test.go b/core/state/managed_state_test.go
index ea5737a08..1cfdd3a89 100644
--- a/core/state/managed_state_test.go
+++ b/core/state/managed_state_test.go
@@ -27,7 +27,7 @@ var addr = common.BytesToAddress([]byte("test"))
 
 func create() (*ManagedState, *account) {
 	db, _ := ethdb.NewMemDatabase()
-	statedb, _ := New(common.Hash{}, db)
+	statedb, _ := New(common.Hash{}, NewDatabase(db))
 	ms := ManageState(statedb)
 	ms.StateDB.SetNonce(addr, 100)
 	ms.accounts[addr] = newAccount(ms.StateDB.getStateObject(addr))
diff --git a/core/state/state_object.go b/core/state/state_object.go
index dcad9d068..b2378c69c 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -62,9 +62,10 @@ func (self Storage) Copy() Storage {
 // Account values can be accessed and modified through the object.
 // Finally, call CommitTrie to write the modified storage trie into a database.
 type stateObject struct {
-	address common.Address // Ethereum address of this account
-	data    Account
-	db      *StateDB
+	address  common.Address
+	addrHash common.Hash // hash of ethereum address of the account
+	data     Account
+	db       *StateDB
 
 	// DB error.
 	// State objects are used by the consensus core and VM which are
@@ -74,8 +75,8 @@ type stateObject struct {
 	dbErr error
 
 	// Write caches.
-	trie *trie.SecureTrie // storage trie, which becomes non-nil on first access
-	code Code             // contract bytecode, which gets set when code is loaded
+	trie Trie // storage trie, which becomes non-nil on first access
+	code Code // contract bytecode, which gets set when code is loaded
 
 	cachedStorage Storage // Storage entry cache to avoid duplicate reads
 	dirtyStorage  Storage // Storage entries that need to be flushed to disk
@@ -112,7 +113,15 @@ func newObject(db *StateDB, address common.Address, data Account, onDirty func(a
 	if data.CodeHash == nil {
 		data.CodeHash = emptyCodeHash
 	}
-	return &stateObject{db: db, address: address, data: data, cachedStorage: make(Storage), dirtyStorage: make(Storage), onDirty: onDirty}
+	return &stateObject{
+		db:            db,
+		address:       address,
+		addrHash:      crypto.Keccak256Hash(address[:]),
+		data:          data,
+		cachedStorage: make(Storage),
+		dirtyStorage:  make(Storage),
+		onDirty:       onDirty,
+	}
 }
 
 // EncodeRLP implements rlp.Encoder.
@@ -148,12 +157,12 @@ func (c *stateObject) touch() {
 	c.touched = true
 }
 
-func (c *stateObject) getTrie(db trie.Database) *trie.SecureTrie {
+func (c *stateObject) getTrie(db Database) Trie {
 	if c.trie == nil {
 		var err error
-		c.trie, err = trie.NewSecure(c.data.Root, db, 0)
+		c.trie, err = db.OpenStorageTrie(c.addrHash, c.data.Root)
 		if err != nil {
-			c.trie, _ = trie.NewSecure(common.Hash{}, db, 0)
+			c.trie, _ = db.OpenStorageTrie(c.addrHash, common.Hash{})
 			c.setError(fmt.Errorf("can't create storage trie: %v", err))
 		}
 	}
@@ -161,13 +170,18 @@ func (c *stateObject) getTrie(db trie.Database) *trie.SecureTrie {
 }
 
 // GetState returns a value in account storage.
-func (self *stateObject) GetState(db trie.Database, key common.Hash) common.Hash {
+func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
 	value, exists := self.cachedStorage[key]
 	if exists {
 		return value
 	}
 	// Load from DB in case it is missing.
-	if enc := self.getTrie(db).Get(key[:]); len(enc) > 0 {
+	enc, err := self.getTrie(db).TryGet(key[:])
+	if err != nil {
+		self.setError(err)
+		return common.Hash{}
+	}
+	if len(enc) > 0 {
 		_, content, _, err := rlp.Split(enc)
 		if err != nil {
 			self.setError(err)
@@ -181,7 +195,7 @@ func (self *stateObject) GetState(db trie.Database, key common.Hash) common.Hash
 }
 
 // SetState updates a value in account storage.
-func (self *stateObject) SetState(db trie.Database, key, value common.Hash) {
+func (self *stateObject) SetState(db Database, key, value common.Hash) {
 	self.db.journal = append(self.db.journal, storageChange{
 		account:  &self.address,
 		key:      key,
@@ -201,30 +215,30 @@ func (self *stateObject) setState(key, value common.Hash) {
 }
 
 // updateTrie writes cached storage modifications into the object's storage trie.
-func (self *stateObject) updateTrie(db trie.Database) *trie.SecureTrie {
+func (self *stateObject) updateTrie(db Database) Trie {
 	tr := self.getTrie(db)
 	for key, value := range self.dirtyStorage {
 		delete(self.dirtyStorage, key)
 		if (value == common.Hash{}) {
-			tr.Delete(key[:])
+			self.setError(tr.TryDelete(key[:]))
 			continue
 		}
 		// Encoding []byte cannot fail, ok to ignore the error.
 		v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
-		tr.Update(key[:], v)
+		self.setError(tr.TryUpdate(key[:], v))
 	}
 	return tr
 }
 
 // UpdateRoot sets the trie root to the current root hash of
-func (self *stateObject) updateRoot(db trie.Database) {
+func (self *stateObject) updateRoot(db Database) {
 	self.updateTrie(db)
 	self.data.Root = self.trie.Hash()
 }
 
 // CommitTrie the storage trie of the object to dwb.
 // This updates the trie root.
-func (self *stateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) error {
+func (self *stateObject) CommitTrie(db Database, dbw trie.DatabaseWriter) error {
 	self.updateTrie(db)
 	if self.dbErr != nil {
 		return self.dbErr
@@ -282,9 +296,7 @@ func (c *stateObject) ReturnGas(gas *big.Int) {}
 func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject {
 	stateObject := newObject(db, self.address, self.data, onDirty)
 	if self.trie != nil {
-		// A shallow copy makes the two tries independent.
-		cpy := *self.trie
-		stateObject.trie = &cpy
+		stateObject.trie = db.db.CopyTrie(self.trie)
 	}
 	stateObject.code = self.code
 	stateObject.dirtyStorage = self.dirtyStorage.Copy()
@@ -305,14 +317,14 @@ func (c *stateObject) Address() common.Address {
 }
 
 // Code returns the contract code associated with this object, if any.
-func (self *stateObject) Code(db trie.Database) []byte {
+func (self *stateObject) Code(db Database) []byte {
 	if self.code != nil {
 		return self.code
 	}
 	if bytes.Equal(self.CodeHash(), emptyCodeHash) {
 		return nil
 	}
-	code, err := db.Get(self.CodeHash())
+	code, err := db.ContractCode(self.addrHash, common.BytesToHash(self.CodeHash()))
 	if err != nil {
 		self.setError(fmt.Errorf("can't load code hash %x: %v", self.CodeHash(), err))
 	}
diff --git a/core/state/state_test.go b/core/state/state_test.go
index 3bc63c148..bbae3685b 100644
--- a/core/state/state_test.go
+++ b/core/state/state_test.go
@@ -21,14 +21,14 @@ import (
 	"math/big"
 	"testing"
 
-	checker "gopkg.in/check.v1"
-
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/ethdb"
+	checker "gopkg.in/check.v1"
 )
 
 type StateSuite struct {
+	db    *ethdb.MemDatabase
 	state *StateDB
 }
 
@@ -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.Commit(false)
+	s.state.CommitTo(s.db, false)
 
 	// check that dump contains the state objects that are in trie
 	got := string(s.state.Dump())
@@ -87,23 +87,20 @@ func (s *StateSuite) TestDump(c *checker.C) {
 }
 
 func (s *StateSuite) SetUpTest(c *checker.C) {
-	db, _ := ethdb.NewMemDatabase()
-	s.state, _ = New(common.Hash{}, db)
+	s.db, _ = ethdb.NewMemDatabase()
+	s.state, _ = New(common.Hash{}, NewDatabase(s.db))
 }
 
-func TestNull(t *testing.T) {
-	db, _ := ethdb.NewMemDatabase()
-	state, _ := New(common.Hash{}, db)
-
+func (s *StateSuite) TestNull(c *checker.C) {
 	address := common.HexToAddress("0x823140710bf13990e4500136726d8b55")
-	state.CreateAccount(address)
+	s.state.CreateAccount(address)
 	//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
 	var value common.Hash
-	state.SetState(address, common.Hash{}, value)
-	state.Commit(false)
-	value = state.GetState(address, common.Hash{})
+	s.state.SetState(address, common.Hash{}, value)
+	s.state.CommitTo(s.db, false)
+	value = s.state.GetState(address, common.Hash{})
 	if !common.EmptyHash(value) {
-		t.Errorf("expected empty hash. got %x", value)
+		c.Errorf("expected empty hash. got %x", value)
 	}
 }
 
@@ -129,17 +126,15 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
 	c.Assert(data1, checker.DeepEquals, res)
 }
 
-func TestSnapshotEmpty(t *testing.T) {
-	db, _ := ethdb.NewMemDatabase()
-	state, _ := New(common.Hash{}, db)
-	state.RevertToSnapshot(state.Snapshot())
+func (s *StateSuite) TestSnapshotEmpty(c *checker.C) {
+	s.state.RevertToSnapshot(s.state.Snapshot())
 }
 
 // use testing instead of checker because checker does not support
 // printing/logging in tests (-check.vv does not work)
 func TestSnapshot2(t *testing.T) {
 	db, _ := ethdb.NewMemDatabase()
-	state, _ := New(common.Hash{}, db)
+	state, _ := New(common.Hash{}, NewDatabase(db))
 
 	stateobjaddr0 := toAddr([]byte("so0"))
 	stateobjaddr1 := toAddr([]byte("so1"))
@@ -160,7 +155,7 @@ func TestSnapshot2(t *testing.T) {
 	so0.deleted = false
 	state.setStateObject(so0)
 
-	root, _ := state.Commit(false)
+	root, _ := state.CommitTo(db, false)
 	state.Reset(root)
 
 	// and one with deleted == true
@@ -182,8 +177,8 @@ func TestSnapshot2(t *testing.T) {
 
 	so0Restored := state.getStateObject(stateobjaddr0)
 	// Update lazily-loaded values before comparing.
-	so0Restored.GetState(db, storageaddr)
-	so0Restored.Code(db)
+	so0Restored.GetState(state.db, storageaddr)
+	so0Restored.Code(state.db)
 	// non-deleted is equal (restored)
 	compareStateObjects(so0Restored, so0, t)
 
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 05869a0c8..694374f82 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -26,23 +26,9 @@ import (
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/crypto"
-	"github.com/ethereum/go-ethereum/ethdb"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/trie"
-	lru "github.com/hashicorp/golang-lru"
-)
-
-// Trie cache generation limit after which to evic trie nodes from memory.
-var MaxTrieCacheGen = uint16(120)
-
-const (
-	// Number of past tries to keep. This value is chosen such that
-	// reasonable chain reorg depths will hit an existing trie.
-	maxPastTries = 12
-
-	// Number of codehash->size associations to keep.
-	codeSizeCacheSize = 100000
 )
 
 type revision struct {
@@ -56,16 +42,21 @@ type revision struct {
 // * Contracts
 // * Accounts
 type StateDB struct {
-	db            ethdb.Database
-	trie          *trie.SecureTrie
-	pastTries     []*trie.SecureTrie
-	codeSizeCache *lru.Cache
+	db   Database
+	trie Trie
 
 	// This map holds 'live' objects, which will get modified while processing a state transition.
 	stateObjects           map[common.Address]*stateObject
 	stateObjectsDirty      map[common.Address]struct{}
 	stateObjectsDestructed map[common.Address]struct{}
 
+	// DB error.
+	// State objects are used by the consensus core and VM which are
+	// unable to deal with database-level errors. Any error that occurs
+	// during a database read is memoized here and will eventually be returned
+	// by StateDB.Commit.
+	dbErr error
+
 	// The refund counter, also used by state transitioning.
 	refund *big.Int
 
@@ -86,16 +77,14 @@ type StateDB struct {
 }
 
 // Create a new state from a given trie
-func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
-	tr, err := trie.NewSecure(root, db, MaxTrieCacheGen)
+func New(root common.Hash, db Database) (*StateDB, error) {
+	tr, err := db.OpenTrie(root)
 	if err != nil {
 		return nil, err
 	}
-	csc, _ := lru.New(codeSizeCacheSize)
 	return &StateDB{
 		db:                     db,
 		trie:                   tr,
-		codeSizeCache:          csc,
 		stateObjects:           make(map[common.Address]*stateObject),
 		stateObjectsDirty:      make(map[common.Address]struct{}),
 		stateObjectsDestructed: make(map[common.Address]struct{}),
@@ -105,36 +94,21 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
 	}, nil
 }
 
-// New creates a new statedb by reusing any journalled tries to avoid costly
-// disk io.
-func (self *StateDB) New(root common.Hash) (*StateDB, error) {
-	self.lock.Lock()
-	defer self.lock.Unlock()
-
-	tr, err := self.openTrie(root)
-	if err != nil {
-		return nil, err
+// setError remembers the first non-nil error it is called with.
+func (self *StateDB) setError(err error) {
+	if self.dbErr == nil {
+		self.dbErr = err
 	}
-	return &StateDB{
-		db:                     self.db,
-		trie:                   tr,
-		codeSizeCache:          self.codeSizeCache,
-		stateObjects:           make(map[common.Address]*stateObject),
-		stateObjectsDirty:      make(map[common.Address]struct{}),
-		stateObjectsDestructed: make(map[common.Address]struct{}),
-		refund:                 new(big.Int),
-		logs:                   make(map[common.Hash][]*types.Log),
-		preimages:              make(map[common.Hash][]byte),
-	}, nil
+}
+
+func (self *StateDB) Error() error {
+	return self.dbErr
 }
 
 // Reset clears out all emphemeral state objects from the state db, but keeps
 // the underlying state trie to avoid reloading data for the next operations.
 func (self *StateDB) Reset(root common.Hash) error {
-	self.lock.Lock()
-	defer self.lock.Unlock()
-
-	tr, err := self.openTrie(root)
+	tr, err := self.db.OpenTrie(root)
 	if err != nil {
 		return err
 	}
@@ -149,34 +123,9 @@ func (self *StateDB) Reset(root common.Hash) error {
 	self.logSize = 0
 	self.preimages = make(map[common.Hash][]byte)
 	self.clearJournalAndRefund()
-
 	return nil
 }
 
-// openTrie creates a trie. It uses an existing trie if one is available
-// from the journal if available.
-func (self *StateDB) openTrie(root common.Hash) (*trie.SecureTrie, error) {
-	for i := len(self.pastTries) - 1; i >= 0; i-- {
-		if self.pastTries[i].Hash() == root {
-			tr := *self.pastTries[i]
-			return &tr, nil
-		}
-	}
-	return trie.NewSecure(root, self.db, MaxTrieCacheGen)
-}
-
-func (self *StateDB) pushTrie(t *trie.SecureTrie) {
-	self.lock.Lock()
-	defer self.lock.Unlock()
-
-	if len(self.pastTries) >= maxPastTries {
-		copy(self.pastTries, self.pastTries[1:])
-		self.pastTries[len(self.pastTries)-1] = t
-	} else {
-		self.pastTries = append(self.pastTries, t)
-	}
-}
-
 func (self *StateDB) AddLog(log *types.Log) {
 	self.journal = append(self.journal, addLogChange{txhash: self.thash})
 
@@ -254,10 +203,7 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
 func (self *StateDB) GetCode(addr common.Address) []byte {
 	stateObject := self.getStateObject(addr)
 	if stateObject != nil {
-		code := stateObject.Code(self.db)
-		key := common.BytesToHash(stateObject.CodeHash())
-		self.codeSizeCache.Add(key, len(code))
-		return code
+		return stateObject.Code(self.db)
 	}
 	return nil
 }
@@ -267,13 +213,12 @@ func (self *StateDB) GetCodeSize(addr common.Address) int {
 	if stateObject == nil {
 		return 0
 	}
-	key := common.BytesToHash(stateObject.CodeHash())
-	if cached, ok := self.codeSizeCache.Get(key); ok {
-		return cached.(int)
+	if stateObject.code != nil {
+		return len(stateObject.code)
 	}
-	size := len(stateObject.Code(self.db))
-	if stateObject.dbErr == nil {
-		self.codeSizeCache.Add(key, size)
+	size, err := self.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash()))
+	if err != nil {
+		self.setError(err)
 	}
 	return size
 }
@@ -296,7 +241,7 @@ func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
 
 // 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.SecureTrie {
+func (self *StateDB) StorageTrie(a common.Address) Trie {
 	stateObject := self.getStateObject(a)
 	if stateObject == nil {
 		return nil
@@ -394,14 +339,14 @@ func (self *StateDB) updateStateObject(stateObject *stateObject) {
 	if err != nil {
 		panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
 	}
-	self.trie.Update(addr[:], data)
+	self.setError(self.trie.TryUpdate(addr[:], data))
 }
 
 // deleteStateObject removes the given object from the state trie.
 func (self *StateDB) deleteStateObject(stateObject *stateObject) {
 	stateObject.deleted = true
 	addr := stateObject.Address()
-	self.trie.Delete(addr[:])
+	self.setError(self.trie.TryDelete(addr[:]))
 }
 
 // Retrieve a state object given my the address. Returns nil if not found.
@@ -415,8 +360,9 @@ func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObje
 	}
 
 	// Load the object from the database.
-	enc := self.trie.Get(addr[:])
+	enc, err := self.trie.TryGet(addr[:])
 	if len(enc) == 0 {
+		self.setError(err)
 		return nil
 	}
 	var data Account
@@ -512,8 +458,6 @@ func (self *StateDB) Copy() *StateDB {
 	state := &StateDB{
 		db:                     self.db,
 		trie:                   self.trie,
-		pastTries:              self.pastTries,
-		codeSizeCache:          self.codeSizeCache,
 		stateObjects:           make(map[common.Address]*stateObject, len(self.stateObjectsDirty)),
 		stateObjectsDirty:      make(map[common.Address]struct{}, len(self.stateObjectsDirty)),
 		stateObjectsDestructed: make(map[common.Address]struct{}, len(self.stateObjectsDestructed)),
@@ -636,23 +580,6 @@ func (s *StateDB) DeleteSuicides() {
 	}
 }
 
-// Commit commits all state changes to the database.
-func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
-	root, batch := s.CommitBatch(deleteEmptyObjects)
-	return root, batch.Write()
-}
-
-// CommitBatch commits all state changes to a write batch but does not
-// execute the batch. It is used to validate state changes against
-// the root hash stored in a block.
-func (s *StateDB) CommitBatch(deleteEmptyObjects bool) (root common.Hash, batch ethdb.Batch) {
-	batch = s.db.NewBatch()
-	root, _ = s.CommitTo(batch, deleteEmptyObjects)
-
-	log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads())
-	return root, batch
-}
-
 func (s *StateDB) clearJournalAndRefund() {
 	s.journal = nil
 	s.validRevisions = s.validRevisions[:0]
@@ -690,8 +617,6 @@ func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (ro
 	}
 	// Write trie changes.
 	root, err = s.trie.CommitTo(dbw)
-	if err == nil {
-		s.pushTrie(s.trie)
-	}
+	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 72b638f97..b2bd18e65 100644
--- a/core/state/statedb_test.go
+++ b/core/state/statedb_test.go
@@ -28,6 +28,8 @@ import (
 	"testing"
 	"testing/quick"
 
+	check "gopkg.in/check.v1"
+
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/ethdb"
@@ -38,7 +40,7 @@ import (
 func TestUpdateLeaks(t *testing.T) {
 	// Create an empty state database
 	db, _ := ethdb.NewMemDatabase()
-	state, _ := New(common.Hash{}, db)
+	state, _ := New(common.Hash{}, NewDatabase(db))
 
 	// Update it with some accounts
 	for i := byte(0); i < 255; i++ {
@@ -66,8 +68,8 @@ func TestIntermediateLeaks(t *testing.T) {
 	// Create two state databases, one transitioning to the final state, the other final from the beginning
 	transDb, _ := ethdb.NewMemDatabase()
 	finalDb, _ := ethdb.NewMemDatabase()
-	transState, _ := New(common.Hash{}, transDb)
-	finalState, _ := New(common.Hash{}, finalDb)
+	transState, _ := New(common.Hash{}, NewDatabase(transDb))
+	finalState, _ := New(common.Hash{}, NewDatabase(finalDb))
 
 	modify := func(state *StateDB, addr common.Address, i, tweak byte) {
 		state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak)))
@@ -95,10 +97,10 @@ func TestIntermediateLeaks(t *testing.T) {
 	}
 
 	// Commit and cross check the databases.
-	if _, err := transState.Commit(false); err != nil {
+	if _, err := transState.CommitTo(transDb, false); err != nil {
 		t.Fatalf("failed to commit transition state: %v", err)
 	}
-	if _, err := finalState.Commit(false); err != nil {
+	if _, err := finalState.CommitTo(finalDb, false); err != nil {
 		t.Fatalf("failed to commit final state: %v", err)
 	}
 	for _, key := range finalDb.Keys() {
@@ -282,7 +284,7 @@ func (test *snapshotTest) run() bool {
 	// Run all actions and create snapshots.
 	var (
 		db, _        = ethdb.NewMemDatabase()
-		state, _     = New(common.Hash{}, db)
+		state, _     = New(common.Hash{}, NewDatabase(db))
 		snapshotRevs = make([]int, len(test.snapshots))
 		sindex       = 0
 	)
@@ -297,7 +299,7 @@ func (test *snapshotTest) run() bool {
 	// 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{}, db)
+		checkstate, _ := New(common.Hash{}, NewDatabase(db))
 		for _, action := range test.actions[:test.snapshots[sindex]] {
 			action.fn(action, checkstate)
 		}
@@ -354,21 +356,19 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
 	return nil
 }
 
-func TestTouchDelete(t *testing.T) {
-	db, _ := ethdb.NewMemDatabase()
-	state, _ := New(common.Hash{}, db)
-	state.GetOrNewStateObject(common.Address{})
-	root, _ := state.Commit(false)
-	state.Reset(root)
+func (s *StateSuite) TestTouchDelete(c *check.C) {
+	s.state.GetOrNewStateObject(common.Address{})
+	root, _ := s.state.CommitTo(s.db, false)
+	s.state.Reset(root)
 
-	snapshot := state.Snapshot()
-	state.AddBalance(common.Address{}, new(big.Int))
-	if len(state.stateObjectsDirty) != 1 {
-		t.Fatal("expected one dirty state object")
+	snapshot := s.state.Snapshot()
+	s.state.AddBalance(common.Address{}, new(big.Int))
+	if len(s.state.stateObjectsDirty) != 1 {
+		c.Fatal("expected one dirty state object")
 	}
 
-	state.RevertToSnapshot(snapshot)
-	if len(state.stateObjectsDirty) != 0 {
-		t.Fatal("expected no 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 108ebb320..06c572ea6 100644
--- a/core/state/sync_test.go
+++ b/core/state/sync_test.go
@@ -36,9 +36,10 @@ type testAccount struct {
 }
 
 // makeTestState create a sample test state to test node-wise reconstruction.
-func makeTestState() (ethdb.Database, common.Hash, []*testAccount) {
+func makeTestState() (Database, *ethdb.MemDatabase, common.Hash, []*testAccount) {
 	// Create an empty state
-	db, _ := ethdb.NewMemDatabase()
+	mem, _ := ethdb.NewMemDatabase()
+	db := NewDatabase(mem)
 	state, _ := New(common.Hash{}, db)
 
 	// Fill it with some arbitrary data
@@ -60,17 +61,17 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) {
 		state.updateStateObject(obj)
 		accounts = append(accounts, acc)
 	}
-	root, _ := state.Commit(false)
+	root, _ := state.CommitTo(mem, false)
 
 	// Return the generated state
-	return db, root, accounts
+	return db, mem, root, accounts
 }
 
 // checkStateAccounts cross references a reconstructed state with an expected
 // account array.
 func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accounts []*testAccount) {
 	// Check root availability and state contents
-	state, err := New(root, db)
+	state, err := New(root, NewDatabase(db))
 	if err != nil {
 		t.Fatalf("failed to create state trie at %x: %v", root, err)
 	}
@@ -90,13 +91,28 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accou
 	}
 }
 
-// checkStateConsistency checks that all nodes in a state trie are indeed present.
+// checkTrieConsistency checks that all nodes in a (sub-)trie are indeed present.
+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)
+	if err != nil {
+		return err
+	}
+	it := trie.NodeIterator(nil)
+	for it.Next(true) {
+	}
+	return it.Error()
+}
+
+// checkStateConsistency checks that all data of a state root is present.
 func checkStateConsistency(db ethdb.Database, root common.Hash) error {
 	// Create and iterate a state trie rooted in a sub-node
 	if _, err := db.Get(root.Bytes()); err != nil {
-		return nil // Consider a non existent state consistent
+		return nil // Consider a non existent state consistent.
 	}
-	state, err := New(root, db)
+	state, err := New(root, NewDatabase(db))
 	if err != nil {
 		return err
 	}
@@ -122,7 +138,7 @@ func TestIterativeStateSyncBatched(t *testing.T)    { testIterativeStateSync(t,
 
 func testIterativeStateSync(t *testing.T, batch int) {
 	// Create a random state to copy
-	srcDb, srcRoot, srcAccounts := makeTestState()
+	_, srcMem, srcRoot, srcAccounts := makeTestState()
 
 	// Create a destination state and sync with the scheduler
 	dstDb, _ := ethdb.NewMemDatabase()
@@ -132,7 +148,7 @@ func testIterativeStateSync(t *testing.T, batch int) {
 	for len(queue) > 0 {
 		results := make([]trie.SyncResult, len(queue))
 		for i, hash := range queue {
-			data, err := srcDb.Get(hash.Bytes())
+			data, err := srcMem.Get(hash.Bytes())
 			if err != nil {
 				t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
 			}
@@ -154,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
-	srcDb, srcRoot, srcAccounts := makeTestState()
+	_, srcMem, srcRoot, srcAccounts := makeTestState()
 
 	// Create a destination state and sync with the scheduler
 	dstDb, _ := ethdb.NewMemDatabase()
@@ -165,7 +181,7 @@ 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 := srcDb.Get(hash.Bytes())
+			data, err := srcMem.Get(hash.Bytes())
 			if err != nil {
 				t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
 			}
@@ -191,7 +207,7 @@ func TestIterativeRandomStateSyncBatched(t *testing.T)    { testIterativeRandomS
 
 func testIterativeRandomStateSync(t *testing.T, batch int) {
 	// Create a random state to copy
-	srcDb, srcRoot, srcAccounts := makeTestState()
+	_, srcMem, srcRoot, srcAccounts := makeTestState()
 
 	// Create a destination state and sync with the scheduler
 	dstDb, _ := ethdb.NewMemDatabase()
@@ -205,7 +221,7 @@ 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 := srcDb.Get(hash.Bytes())
+			data, err := srcMem.Get(hash.Bytes())
 			if err != nil {
 				t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
 			}
@@ -231,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
-	srcDb, srcRoot, srcAccounts := makeTestState()
+	_, srcMem, srcRoot, srcAccounts := makeTestState()
 
 	// Create a destination state and sync with the scheduler
 	dstDb, _ := ethdb.NewMemDatabase()
@@ -247,7 +263,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
 		for hash := range queue {
 			delete(queue, hash)
 
-			data, err := srcDb.Get(hash.Bytes())
+			data, err := srcMem.Get(hash.Bytes())
 			if err != nil {
 				t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
 			}
@@ -276,7 +292,9 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
 // the database.
 func TestIncompleteStateSync(t *testing.T) {
 	// Create a random state to copy
-	srcDb, srcRoot, srcAccounts := makeTestState()
+	_, srcMem, srcRoot, srcAccounts := makeTestState()
+
+	checkTrieConsistency(srcMem, srcRoot)
 
 	// Create a destination state and sync with the scheduler
 	dstDb, _ := ethdb.NewMemDatabase()
@@ -288,7 +306,7 @@ func TestIncompleteStateSync(t *testing.T) {
 		// Fetch a batch of state nodes
 		results := make([]trie.SyncResult, len(queue))
 		for i, hash := range queue {
-			data, err := srcDb.Get(hash.Bytes())
+			data, err := srcMem.Get(hash.Bytes())
 			if err != nil {
 				t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
 			}
@@ -304,21 +322,18 @@ func TestIncompleteStateSync(t *testing.T) {
 		for _, result := range results {
 			added = append(added, result.Hash)
 		}
-		// Check that all known sub-tries in the synced state is complete
-		for _, root := range added {
-			// Skim through the accounts and make sure the root hash is not a code node
-			codeHash := false
+		// Check that all known sub-tries added so far are complete or missing entirely.
+	checkSubtries:
+		for _, hash := range added {
 			for _, acc := range srcAccounts {
-				if root == crypto.Keccak256Hash(acc.code) {
-					codeHash = true
-					break
+				if hash == crypto.Keccak256Hash(acc.code) {
+					continue checkSubtries // skip trie check of code nodes.
 				}
 			}
-			// If the root is a real trie node, check consistency
-			if !codeHash {
-				if err := checkStateConsistency(dstDb, root); err != nil {
-					t.Fatalf("state inconsistent: %v", err)
-				}
+			// Can't use checkStateConsistency here because subtrie keys may have odd
+			// length and crash in LeafKey.
+			if err := checkTrieConsistency(dstDb, hash); err != nil {
+				t.Fatalf("state inconsistent: %v", err)
 			}
 		}
 		// Fetch the next batch to retrieve
diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go
index 4e28522e9..4903bc3ca 100644
--- a/core/tx_pool_test.go
+++ b/core/tx_pool_test.go
@@ -44,7 +44,7 @@ func pricedTransaction(nonce uint64, gaslimit, gasprice *big.Int, key *ecdsa.Pri
 
 func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
 	db, _ := ethdb.NewMemDatabase()
-	statedb, _ := state.New(common.Hash{}, db)
+	statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
 
 	key, _ := crypto.GenerateKey()
 	newPool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
@@ -95,7 +95,7 @@ func TestStateChangeDuringPoolReset(t *testing.T) {
 		key, _     = crypto.GenerateKey()
 		address    = crypto.PubkeyToAddress(key.PublicKey)
 		mux        = new(event.TypeMux)
-		statedb, _ = state.New(common.Hash{}, db)
+		statedb, _ = state.New(common.Hash{}, state.NewDatabase(db))
 		trigger    = false
 	)
 
@@ -114,7 +114,7 @@ func TestStateChangeDuringPoolReset(t *testing.T) {
 		// a state change between those fetches.
 		stdb := statedb
 		if trigger {
-			statedb, _ = state.New(common.Hash{}, db)
+			statedb, _ = state.New(common.Hash{}, state.NewDatabase(db))
 			// simulate that the new head block included tx0 and tx1
 			statedb.SetNonce(address, 2)
 			statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether))
@@ -292,7 +292,7 @@ func TestTransactionChainFork(t *testing.T) {
 	addr := crypto.PubkeyToAddress(key.PublicKey)
 	resetState := func() {
 		db, _ := ethdb.NewMemDatabase()
-		statedb, _ := state.New(common.Hash{}, db)
+		statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
 		pool.currentState = func() (*state.StateDB, error) { return statedb, nil }
 		currentState, _ := pool.currentState()
 		currentState.AddBalance(addr, big.NewInt(100000000000000))
@@ -318,7 +318,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
 	addr := crypto.PubkeyToAddress(key.PublicKey)
 	resetState := func() {
 		db, _ := ethdb.NewMemDatabase()
-		statedb, _ := state.New(common.Hash{}, db)
+		statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
 		pool.currentState = func() (*state.StateDB, error) { return statedb, nil }
 		currentState, _ := pool.currentState()
 		currentState.AddBalance(addr, big.NewInt(100000000000000))
@@ -628,7 +628,7 @@ func TestTransactionQueueGlobalLimiting(t *testing.T) {
 
 	// Create the pool to test the limit enforcement with
 	db, _ := ethdb.NewMemDatabase()
-	statedb, _ := state.New(common.Hash{}, db)
+	statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
 
 	pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
 	pool.resetState()
@@ -783,7 +783,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
 
 	// Create the pool to test the limit enforcement with
 	db, _ := ethdb.NewMemDatabase()
-	statedb, _ := state.New(common.Hash{}, db)
+	statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
 
 	pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
 	pool.resetState()
@@ -835,7 +835,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
 
 	// Create the pool to test the limit enforcement with
 	db, _ := ethdb.NewMemDatabase()
-	statedb, _ := state.New(common.Hash{}, db)
+	statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
 
 	pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
 	pool.resetState()
@@ -868,7 +868,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
 
 	// Create the pool to test the limit enforcement with
 	db, _ := ethdb.NewMemDatabase()
-	statedb, _ := state.New(common.Hash{}, db)
+	statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
 
 	pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
 	pool.resetState()
@@ -913,7 +913,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
 func TestTransactionPoolRepricing(t *testing.T) {
 	// Create the pool to test the pricing enforcement with
 	db, _ := ethdb.NewMemDatabase()
-	statedb, _ := state.New(common.Hash{}, db)
+	statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
 
 	pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
 	pool.resetState()
@@ -1006,7 +1006,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
 
 	// Create the pool to test the pricing enforcement with
 	db, _ := ethdb.NewMemDatabase()
-	statedb, _ := state.New(common.Hash{}, db)
+	statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
 
 	pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
 	pool.resetState()
@@ -1091,7 +1091,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
 func TestTransactionReplacement(t *testing.T) {
 	// Create the pool to test the pricing enforcement with
 	db, _ := ethdb.NewMemDatabase()
-	statedb, _ := state.New(common.Hash{}, db)
+	statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
 
 	pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
 	pool.resetState()
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index aa386a995..44cde4f70 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -102,7 +102,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
 
 	if cfg.State == nil {
 		db, _ := ethdb.NewMemDatabase()
-		cfg.State, _ = state.New(common.Hash{}, db)
+		cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(db))
 	}
 	var (
 		address = common.StringToAddress("contract")
@@ -133,7 +133,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
 
 	if cfg.State == nil {
 		db, _ := ethdb.NewMemDatabase()
-		cfg.State, _ = state.New(common.Hash{}, db)
+		cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(db))
 	}
 	var (
 		vmenv  = NewEnv(cfg, cfg.State)
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index 7f40770d2..2c4dc5026 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -95,7 +95,7 @@ func TestExecute(t *testing.T) {
 
 func TestCall(t *testing.T) {
 	db, _ := ethdb.NewMemDatabase()
-	state, _ := state.New(common.Hash{}, db)
+	state, _ := state.New(common.Hash{}, state.NewDatabase(db))
 	address := common.HexToAddress("0x0a")
 	state.SetCode(address, []byte{
 		byte(vm.PUSH1), 10,
-- 
cgit v1.2.3