From dba3363b52bfd6d1d3f34f1da61ca73d684e2e74 Mon Sep 17 00:00:00 2001
From: ledgerwatch <akhounov@gmail.com>
Date: Wed, 27 May 2020 17:24:34 +0100
Subject: [PATCH] Fix RestAPI for tx_retrace and make it work for local boltdb
 (#584)

* Not hash, keep the files

* Calculate savings

* Fix

* Fix

* Fix

* Fix

* RestAPI to support local boltdb

* Not error on read-only db

* Changes so far

* Continue

* More

* Roll back a bit

* Restore newline

* something compiles

* Fix restapi

* Fix block number

* Fix reads

* Use plain writer

* Maps for storage reads and writes

* Clean up coersions

* Fix accounts/abi/bind

* Fix tests

* More fixes

* more fixes

* More fixes

* Fixes

* Fixed core/state

* Fixed eth tests

* Move code, fix linter

* Fix test

* Fix linter

* Fix linter

* Fix linter, badger_db to support AbstractKV

* Increase IdealBatchSize for badger

* Fix linter

* Fix linter
---
 accounts/abi/bind/backends/simulated.go       |  27 +-
 accounts/abi/bind/backends/simulated_test.go  |   3 +-
 cmd/evm/runner.go                             |   5 +-
 cmd/evm/staterunner.go                        |   9 +-
 cmd/geth/chaincmd.go                          |   5 +-
 cmd/geth/retesteth.go                         |  90 +---
 cmd/hack/hack.go                              |  83 +---
 cmd/restapi/apis/common.go                    |   1 +
 cmd/restapi/apis/remote_reader.go             | 158 +++----
 cmd/restapi/apis/retrace_tx_api.go            |  45 +-
 cmd/restapi/commands/root.go                  |   6 +-
 cmd/restapi/rest/serve_rest.go                |  14 +-
 cmd/rpcdaemon/commands/daemon.go              |   4 +-
 cmd/state/commands/state_snapshot.go          |  19 -
 cmd/state/stateless/check_change_sets.go      |   2 +-
 cmd/state/stateless/deps.go                   |   2 +-
 cmd/state/stateless/naked_accouts.go          |   2 +-
 cmd/state/stateless/naked_storage.go          |   2 +-
 cmd/state/stateless/spec_exec.go              |   2 +-
 cmd/state/stateless/state.go                  |   4 +-
 cmd/state/stateless/state_snapshot.go         | 122 ------
 cmd/state/stateless/tokens.go                 |   6 +-
 cmd/state/stateless/transaction_stats.go      |   2 +-
 core/blockchain.go                            |  37 --
 core/blockchain_test.go                       |  24 +-
 core/chain_makers_test.go                     |   9 +-
 core/state/database.go                        |  45 +-
 core/state/database_test.go                   |  44 +-
 core/state/dump.go                            |  16 +-
 core/state/history_test.go                    |   8 +-
 core/state/intra_block_state_test.go          |   4 +-
 core/state/readonly.go                        |  30 +-
 core/state/state_test.go                      |   9 +-
 core/tx_pool.go                               |   2 -
 eth/api.go                                    |  10 +-
 eth/api_backend.go                            |  10 +-
 eth/api_test.go                               |  14 +-
 eth/api_tracer.go                             |  14 +-
 eth/backend.go                                |  16 +-
 eth/handler.go                                | 334 --------------
 eth/helper_test.go                            |   2 +-
 ethdb/badger_db.go                            |   6 +-
 ethdb/bolt_db.go                              | 408 +-----------------
 ethdb/history.go                              | 111 +++++
 ethdb/interface.go                            |   6 -
 ethdb/kv_abstract.go                          |   4 +-
 ethdb/kv_bolt.go                              |  34 +-
 ethdb/mutation.go                             |  18 -
 ethdb/remote_bolt_db.go                       |  41 --
 ethdb/walk.go                                 | 328 +++++++++++++-
 miner/worker.go                               |   5 +-
 tests/block_test_util.go                      |   5 +-
 tests/statedb_chain_test.go                   |   5 +-
 .../statedb_insert_chain_transaction_test.go  |  70 +--
 54 files changed, 804 insertions(+), 1478 deletions(-)
 delete mode 100644 cmd/state/commands/state_snapshot.go
 create mode 100644 ethdb/history.go

diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index 5270a2758f..443741fb41 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -64,6 +64,7 @@ var (
 type SimulatedBackend struct {
 	prependDb  ethdb.Database
 	database   ethdb.Database // In memory database to store our testing data
+	kv         ethdb.KV       // Same as database, but different interface
 	engine     consensus.Engine
 	blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
 
@@ -92,10 +93,15 @@ func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.Genesis
 	}
 	blockchain.EnableReceipts(true)
 
+	var kv ethdb.KV
+	if hasKV, ok := database.(ethdb.HasAbstractKV); ok {
+		kv = hasKV.AbstractKV()
+	}
 	backend := &SimulatedBackend{
 		prependBlock: genesisBlock,
 		prependDb:    database.MemCopy(),
 		database:     database,
+		kv:           kv,
 		engine:       engine,
 		blockchain:   blockchain,
 		config:       genesis.Config,
@@ -114,14 +120,14 @@ func NewSimulatedBackendWithConfig(alloc core.GenesisAlloc, config *params.Chain
 	engine := ethash.NewFaker()
 	blockchain, err := core.NewBlockChain(database, nil, genesis.Config, engine, vm.Config{}, nil, nil)
 	if err != nil {
-		panic(fmt.Sprintf("%v", err))
+		panic(err)
 	}
 	blockchain.EnableReceipts(true)
-
 	backend := &SimulatedBackend{
 		prependBlock: genesisBlock,
 		prependDb:    database.MemCopy(),
 		database:     database,
+		kv:           database.AbstractKV(),
 		engine:       engine,
 		blockchain:   blockchain,
 		config:       genesis.Config,
@@ -137,6 +143,10 @@ func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBac
 	return NewSimulatedBackendWithDatabase(ethdb.NewMemDatabase(), alloc, gasLimit)
 }
 
+func (b *SimulatedBackend) KV() ethdb.KV {
+	return b.kv
+}
+
 // Close terminates the underlying blockchain's update loop.
 func (b *SimulatedBackend) Close() error {
 	b.blockchain.Stop()
@@ -185,11 +195,9 @@ func (b *SimulatedBackend) prependingState() *state.IntraBlockState {
 // stateByBlockNumber retrieves a state by a given blocknumber.
 func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *big.Int) (*state.IntraBlockState, error) {
 	if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) == 0 {
-		s, _, err := b.blockchain.State()
-		return s, err
+		return state.New(state.NewDbState(b.kv, b.blockchain.CurrentBlock().NumberU64())), nil
 	}
-	state, _, err := b.blockchain.StateAt(uint64(blockNumber.Int64()))
-	return state, err
+	return state.New(state.NewDbState(b.kv, uint64(blockNumber.Int64()))), nil
 }
 
 // CodeAt returns the code associated with a certain account in the blockchain.
@@ -395,11 +403,8 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
 	if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
 		return nil, errBlockNumberUnsupported
 	}
-	state, _, err := b.blockchain.State()
-	if err != nil {
-		return nil, err
-	}
-	res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
+	s := state.New(state.NewDbState(b.kv, b.blockchain.CurrentBlock().NumberU64()))
+	res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), s)
 	if err != nil {
 		return nil, err
 	}
diff --git a/accounts/abi/bind/backends/simulated_test.go b/accounts/abi/bind/backends/simulated_test.go
index 2aa8f8bf69..e669a8ccce 100644
--- a/accounts/abi/bind/backends/simulated_test.go
+++ b/accounts/abi/bind/backends/simulated_test.go
@@ -32,6 +32,7 @@ import (
 	"github.com/ledgerwatch/turbo-geth/accounts/abi/bind"
 	"github.com/ledgerwatch/turbo-geth/common"
 	"github.com/ledgerwatch/turbo-geth/core"
+	"github.com/ledgerwatch/turbo-geth/core/state"
 	"github.com/ledgerwatch/turbo-geth/core/types"
 	"github.com/ledgerwatch/turbo-geth/crypto"
 	"github.com/ledgerwatch/turbo-geth/params"
@@ -126,7 +127,7 @@ func TestNewSimulatedBackend(t *testing.T) {
 		t.Errorf("expected sim blockchain config to equal params.AllEthashProtocolChanges, got %v", sim.config)
 	}
 
-	statedb, _, _ := sim.blockchain.State()
+	statedb := state.New(state.NewDbState(sim.KV(), sim.blockchain.CurrentBlock().NumberU64()))
 	bal := statedb.GetBalance(testAddr)
 	if !bal.Eq(expectedBal) {
 		t.Errorf("expected balance for test address not received. expected: %v actual: %v", expectedBal, bal)
diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go
index dcfd5c1273..5f1dc7c45c 100644
--- a/cmd/evm/runner.go
+++ b/cmd/evm/runner.go
@@ -120,7 +120,6 @@ func runCmd(ctx *cli.Context) error {
 		tracer        vm.Tracer
 		debugLogger   *vm.StructLogger
 		statedb       *state.IntraBlockState
-		db            ethdb.Database
 		chainConfig   *params.ChainConfig
 		sender        = common.BytesToAddress([]byte("sender"))
 		receiver      = common.BytesToAddress([]byte("receiver"))
@@ -134,7 +133,7 @@ func runCmd(ctx *cli.Context) error {
 	} else {
 		debugLogger = vm.NewStructLogger(logconfig)
 	}
-	db = ethdb.NewMemDatabase()
+	db := ethdb.NewMemDatabase()
 	if ctx.GlobalString(GenesisFlag.Name) != "" {
 		gen := readGenesis(ctx.GlobalString(GenesisFlag.Name))
 		genesisConfig = gen
@@ -280,7 +279,7 @@ func runCmd(ctx *cli.Context) error {
 			fmt.Println("Could not commit state: ", err)
 			os.Exit(1)
 		}
-		fmt.Println(string(state.NewDumper(db, 0).DefaultDump()))
+		fmt.Println(string(state.NewDumper(db.AbstractKV(), 0).DefaultDump()))
 	}
 
 	if memProfilePath := ctx.GlobalString(MemProfileFlag.Name); memProfilePath != "" {
diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go
index 28ccef4242..fb88d4318b 100644
--- a/cmd/evm/staterunner.go
+++ b/cmd/evm/staterunner.go
@@ -26,6 +26,7 @@ import (
 
 	"github.com/ledgerwatch/turbo-geth/core/state"
 	"github.com/ledgerwatch/turbo-geth/core/vm"
+	"github.com/ledgerwatch/turbo-geth/ethdb"
 	"github.com/ledgerwatch/turbo-geth/log"
 	"github.com/ledgerwatch/turbo-geth/tests"
 
@@ -106,8 +107,12 @@ func stateTestCmd(ctx *cli.Context) error {
 				// Test failed, mark as so and dump any state to aid debugging
 				result.Pass, *result.Error = false, err.Error()
 				if ctx.GlobalBool(DumpFlag.Name) && statedb != nil {
-					dump := state.NewDumper(tds.Database(), tds.GetBlockNr()).DefaultRawDump()
-					result.State = &dump
+					if hasKV, ok := tds.Database().(ethdb.HasAbstractKV); ok {
+						dump := state.NewDumper(hasKV.AbstractKV(), tds.GetBlockNr()).DefaultRawDump()
+						result.State = &dump
+					} else {
+						fmt.Fprintf(os.Stderr, "database does not implement AbstractKV: %T\n", tds.Database())
+					}
 				}
 			}
 
diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index 422e2d0fab..688f875743 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -523,7 +523,10 @@ func dump(ctx *cli.Context) error {
 			excludeCode := ctx.Bool(utils.ExcludeCodeFlag.Name)
 			excludeStorage := ctx.Bool(utils.ExcludeStorageFlag.Name)
 			includeMissing := ctx.Bool(utils.IncludeIncompletesFlag.Name)
-			fmt.Printf("%s\n", state.NewDumper(chainDb, block.NumberU64()).Dump(excludeCode, excludeStorage, !includeMissing))
+			if hasKV, ok := chainDb.(ethdb.HasAbstractKV); ok {
+				fmt.Printf("%s\n", state.NewDumper(hasKV.AbstractKV(), block.NumberU64()).Dump(excludeCode, excludeStorage, !includeMissing))
+			}
+			fmt.Printf("database %T does not support AbstracKV\n", chainDb)
 		}
 	}
 	return nil
diff --git a/cmd/geth/retesteth.go b/cmd/geth/retesteth.go
index d92570e562..ab06cb8ab0 100644
--- a/cmd/geth/retesteth.go
+++ b/cmd/geth/retesteth.go
@@ -109,6 +109,7 @@ type RetestWeb3API interface {
 
 type RetestethAPI struct {
 	ethDb ethdb.Database
+	kv    ethdb.KV
 	//db            state.Database
 	chainConfig   *params.ChainConfig
 	author        common.Address
@@ -415,6 +416,9 @@ func (api *RetestethAPI) SetChainParams(_ context.Context, chainParams ChainPara
 	api.author = chainParams.Genesis.Author
 	api.extraData = chainParams.Genesis.ExtraData
 	api.ethDb = ethDb
+	if hasKV, ok := api.ethDb.(ethdb.HasAbstractKV); ok {
+		api.kv = hasKV.AbstractKV()
+	}
 	api.engine = engine
 	api.blockchain = blockchain
 	//api.db = state.NewDatabase(api.ethDb)
@@ -672,19 +676,9 @@ func (api *RetestethAPI) AccountRange(ctx context.Context,
 	}
 
 	parentHeader := api.blockchain.GetHeaderByHash(header.ParentHash)
-	var dbState *state.DbState
-	var err error
-	if parentHeader == nil || int(txIndex) >= len(block.Transactions()) {
-		_, dbState, err = api.blockchain.StateAt(header.Number.Uint64())
-		if err != nil {
-			return AccountRangeResult{}, err
-		}
-	} else {
-		var statedb *state.IntraBlockState
-		statedb, dbState, err = api.blockchain.StateAt(parentHeader.Number.Uint64())
-		if err != nil {
-			return AccountRangeResult{}, err
-		}
+	dbState := state.NewDbState(api.kv, header.Number.Uint64())
+	if parentHeader != nil && int(txIndex) < len(block.Transactions()) {
+		statedb := state.New(dbState)
 		// Recompute transactions up to the target index.
 		signer := types.MakeSigner(api.blockchain.Config(), block.Number())
 		for idx, tx := range block.Transactions() {
@@ -693,13 +687,12 @@ func (api *RetestethAPI) AccountRange(ctx context.Context,
 			context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil)
 			// Not yet the searched for transaction, execute on top of the current state
 			vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
-			if _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
+			if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
 				return AccountRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
 			}
 			// Ensure any modifications are committed to the state
 			if idx == int(txIndex) {
-				err = statedb.CommitBlock(ctx, dbState)
-				if err != nil {
+				if err := statedb.CommitBlock(ctx, dbState); err != nil {
 					return AccountRangeResult{}, err
 				}
 				break
@@ -707,7 +700,7 @@ func (api *RetestethAPI) AccountRange(ctx context.Context,
 		}
 	}
 
-	iterator, err1 := dbState.Dumper().IteratorDump(false, false, false, common.BigToHash((*big.Int)(addressHash)).Bytes(), int(maxResults))
+	iterator, err1 := state.NewDumper(api.kv, header.Number.Uint64()).IteratorDump(false, false, false, common.BigToHash((*big.Int)(addressHash)).Bytes(), int(maxResults))
 	if err1 != nil {
 		return AccountRangeResult{}, err1
 	}
@@ -728,19 +721,13 @@ func (api *RetestethAPI) AccountRange(ctx context.Context,
 func (api *RetestethAPI) GetBalance(_ context.Context, address common.Address, blockNr math.HexOrDecimal64) (*math.HexOrDecimal256, error) {
 	//fmt.Printf("GetBalance %x, block %d\n", address, blockNr)
 	header := api.blockchain.GetHeaderByNumber(uint64(blockNr))
-	statedb, _, err := api.blockchain.StateAt(header.Number.Uint64())
-	if err != nil {
-		return nil, err
-	}
+	statedb := state.New(state.NewDbState(api.kv, header.Number.Uint64()))
 	return (*math.HexOrDecimal256)(statedb.GetBalance(address).ToBig()), nil
 }
 
 func (api *RetestethAPI) GetCode(_ context.Context, address common.Address, blockNr math.HexOrDecimal64) (hexutil.Bytes, error) {
 	header := api.blockchain.GetHeaderByNumber(uint64(blockNr))
-	statedb, _, err := api.blockchain.StateAt(header.Number.Uint64())
-	if err != nil {
-		return nil, err
-	}
+	statedb := state.New(state.NewDbState(api.kv, header.Number.Uint64()))
 	return getCode(statedb.GetCode(address)), nil
 }
 
@@ -762,10 +749,7 @@ func getCode(c hexutil.Bytes) hexutil.Bytes {
 
 func (api *RetestethAPI) GetTransactionCount(_ context.Context, address common.Address, blockNr math.HexOrDecimal64) (uint64, error) {
 	header := api.blockchain.GetHeaderByNumber(uint64(blockNr))
-	statedb, _, err := api.blockchain.StateAt(header.Number.Uint64())
-	if err != nil {
-		return 0, err
-	}
+	statedb := state.New(state.NewDbState(api.kv, header.Number.Uint64()))
 	return statedb.GetNonce(address), nil
 }
 
@@ -798,58 +782,16 @@ func (api *RetestethAPI) StorageRangeAt(ctx context.Context,
 
 	parentHeader := api.blockchain.GetHeaderByHash(header.ParentHash)
 	var dbstate *state.DbState
-	var err error
 	if parentHeader == nil || int(txIndex) >= len(block.Transactions()) {
-		_, dbstate, err = api.blockchain.StateAt(header.Number.Uint64())
-		if err != nil {
-			return StorageRangeResult{}, err
-		}
+		dbstate = state.NewDbState(api.kv, header.Number.Uint64())
 	} else {
-		_, _, _, dbstate, err = eth.ComputeTxEnv(ctx, api.blockchain, api.blockchain.Config(), api.blockchain, api.blockchain.ChainDb(), block.Hash(), txIndex)
+		var err error
+		_, _, _, dbstate, err = eth.ComputeTxEnv(ctx, api.blockchain, api.blockchain.Config(), api.blockchain, api.kv, block.Hash(), txIndex)
 		if err != nil {
 			return StorageRangeResult{}, err
 		}
 	}
-	/*
-		storageTrie := statedb.StorageTrie(address)
-		it := trie.NewIterator(storageTrie.NodeIterator(common.BigToHash((*big.Int)(begin)).Bytes()))
-	*/
-
 	result := StorageRangeResult{Storage: make(map[common.Hash]SRItem)}
-
-	/*
-		for i := 0; i < int(maxResults) && it.Next(); i++ {
-			if preimage := storageTrie.GetKey(it.Key); preimage != nil {
-				key := (*math.HexOrDecimal256)(big.NewInt(0).SetBytes(preimage))
-				v, _, err := rlp.SplitString(it.Value)
-				if err != nil {
-					return StorageRangeResult{}, err
-				}
-				value := (*math.HexOrDecimal256)(big.NewInt(0).SetBytes(v))
-				ks, _ := key.MarshalText()
-				vs, _ := value.MarshalText()
-				if len(ks)%2 != 0 {
-					ks = append(append(append([]byte{}, ks[:2]...), byte('0')), ks[2:]...)
-				}
-				if len(vs)%2 != 0 {
-					vs = append(append(append([]byte{}, vs[:2]...), byte('0')), vs[2:]...)
-				}
-				result.Storage[common.BytesToHash(it.Key)] = SRItem{
-					Key:   string(ks),
-					Value: string(vs),
-				}
-				//fmt.Printf("Key: %s, Value: %s\n", ks, vs)
-			} else {
-				//fmt.Printf("Did not find preimage for %x\n", it.Key)
-			}
-		}
-		if it.Next() {
-			result.Complete = false
-		} else {
-			result.Complete = true
-		}
-	*/
-
 	beginHash := common.BigToHash((*big.Int)(begin))
 
 	rangeResults, err := eth.StorageRangeAt(dbstate, address, beginHash.Bytes(), int(maxResults))
diff --git a/cmd/hack/hack.go b/cmd/hack/hack.go
index a617f72d4c..3b62352cf9 100644
--- a/cmd/hack/hack.go
+++ b/cmd/hack/hack.go
@@ -1145,67 +1145,6 @@ func addPreimage(chaindata string, image common.Hash, preimage []byte) {
 	check(err)
 }
 
-func loadAccount() {
-	ethDb, err := ethdb.NewBoltDatabase("/home/akhounov/.ethereum/geth/chaindata")
-	//ethDb, err := ethdb.NewBoltDatabase(node.DefaultDataDir() + "/geth/chaindata")
-	//ethDb, err := ethdb.NewBoltDatabase("/Volumes/tb4/turbo-geth/geth/chaindata")
-	check(err)
-	defer ethDb.Close()
-	blockNr := uint64(*block)
-	blockSuffix := dbutils.EncodeTimestamp(blockNr)
-	accountBytes := common.FromHex(*account)
-	secKey := crypto.Keccak256(accountBytes)
-	accountData, err := ethDb.GetAsOf(dbutils.CurrentStateBucket, dbutils.AccountsHistoryBucket, secKey, blockNr+1)
-	check(err)
-	fmt.Printf("Account data: %x\n", accountData)
-	startkey := make([]byte, len(accountBytes)+32)
-	copy(startkey, accountBytes)
-	t := trie.New(common.Hash{})
-	count := 0
-	if err := ethDb.WalkAsOf(dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, startkey, len(accountBytes)*8, blockNr, func(k, v []byte) (bool, error) {
-		key := k[len(accountBytes):]
-		//fmt.Printf("%x: %x\n", key, v)
-		t.Update(key, v)
-		count++
-		return true, nil
-	}); err != nil {
-		panic(err)
-	}
-	fmt.Printf("After %d updates, reconstructed storage root: %x\n", count, t.Hash())
-	var keys [][]byte
-	if err := ethDb.Walk(dbutils.StorageHistoryBucket, accountBytes, len(accountBytes)*8, func(k, v []byte) (bool, error) {
-		if !bytes.HasSuffix(k, blockSuffix) {
-			return true, nil
-		}
-		key := k[:len(k)-len(blockSuffix)]
-		keys = append(keys, common.CopyBytes(key))
-		return true, nil
-	}); err != nil {
-		panic(err)
-	}
-	fmt.Printf("%d keys updated\n", len(keys))
-	for _, k := range keys {
-		v, err := ethDb.GetAsOf(dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, k, blockNr+1)
-		if err != nil {
-			fmt.Printf("for key %x err %v\n", k, err)
-		}
-		vOrig, err := ethDb.GetAsOf(dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, k, blockNr)
-		if err != nil {
-			fmt.Printf("for key %x err %v\n", k, err)
-		}
-		key := ([]byte(k))[len(accountBytes):]
-		if len(v) > 0 {
-			fmt.Printf("Updated %x: %x from %x\n", key, v, vOrig)
-			t.Update(key, v)
-			check(err)
-		} else {
-			fmt.Printf("Deleted %x from %x\n", key, vOrig)
-			t.Delete(key)
-		}
-	}
-	fmt.Printf("Updated storage root: %x\n", t.Hash())
-}
-
 func printBranches(block uint64) {
 	ethDb, err := ethdb.NewBoltDatabase(node.DefaultDataDir() + "/testnet/geth/chaindata")
 	//ethDb, err := ethdb.NewBoltDatabase("/home/akhounov/.ethereum/geth/chaindata")
@@ -1994,23 +1933,6 @@ func getModifiedAccounts(chaindata string) {
 	fmt.Printf("Len(addrs)=%d\n", len(addrs))
 }
 
-func walkOverStorage(chaindata string) {
-	db, err := ethdb.NewBoltDatabase(chaindata)
-	check(err)
-	var startkey [32 + 8 + 32]byte
-	h, err := common.HashData(common.FromHex("0x109c4f2ccc82c4d77bde15f306707320294aea3f"))
-	check(err)
-	copy(startkey[:], h[:])
-	binary.BigEndian.PutUint64(startkey[32:], ^uint64(1))
-	fmt.Printf("startkey %x\n", startkey)
-	err = db.WalkAsOf(dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, startkey[:], 8*(32+8), 50796, func(k []byte, v []byte) (bool, error) {
-		fmt.Printf("%x: %x\n", k, v)
-		return true, nil
-	})
-	check(err)
-	fmt.Printf("Success\n")
-}
-
 func resetState(chaindata string) {
 	db, err := ethdb.NewBoltDatabase(chaindata)
 	check(err)
@@ -2049,6 +1971,8 @@ func resetHistoryIndex(chaindata string) {
 	check(err)
 	err = downloader.SaveStageProgress(db, downloader.StorageHistoryIndex, 0)
 	check(err)
+	err = downloader.SaveStageProgress(db, downloader.HashCheck, 0)
+	check(err)
 	fmt.Printf("Reset history index done\n")
 }
 
@@ -2372,9 +2296,6 @@ func main() {
 	if *action == "modiAccounts" {
 		getModifiedAccounts(*chaindata)
 	}
-	if *action == "storage" {
-		walkOverStorage(*chaindata)
-	}
 	if *action == "slice" {
 		dbSlice(*chaindata, common.FromHex(*hash))
 	}
diff --git a/cmd/restapi/apis/common.go b/cmd/restapi/apis/common.go
index b7550e4264..c8a414c661 100644
--- a/cmd/restapi/apis/common.go
+++ b/cmd/restapi/apis/common.go
@@ -10,5 +10,6 @@ var ErrEntityNotFound = errors.New("entity not found")
 
 type Env struct {
 	DB              ethdb.KV
+	BoltPath        string
 	RemoteDBAddress string
 }
diff --git a/cmd/restapi/apis/remote_reader.go b/cmd/restapi/apis/remote_reader.go
index 2eb55efef6..336d45c87c 100644
--- a/cmd/restapi/apis/remote_reader.go
+++ b/cmd/restapi/apis/remote_reader.go
@@ -2,7 +2,6 @@ package apis
 
 import (
 	"bytes"
-	"encoding/binary"
 	"math/big"
 
 	"github.com/ledgerwatch/turbo-geth/common"
@@ -21,8 +20,9 @@ import (
 
 // ChangeSetReader is a mock StateWriter that accumulates changes in-memory into ChangeSets.
 type RemoteReader struct {
-	accountReads map[common.Address]bool
-	storageReads map[common.Hash]bool
+	accountReads map[common.Address]struct{}
+	storageReads map[common.Address]map[common.Hash]struct{}
+	codeReads    map[common.Address]struct{}
 	blockNr      uint64
 	db           ethdb.KV
 }
@@ -80,8 +80,9 @@ func (c *powEngine) Author(header *types.Header) (common.Address, error) {
 
 func NewRemoteReader(db ethdb.KV, blockNr uint64) *RemoteReader {
 	return &RemoteReader{
-		accountReads: make(map[common.Address]bool),
-		storageReads: make(map[common.Hash]bool),
+		accountReads: make(map[common.Address]struct{}),
+		storageReads: make(map[common.Address]map[common.Hash]struct{}),
+		codeReads:    make(map[common.Address]struct{}),
 		db:           db,
 		blockNr:      blockNr,
 	}
@@ -89,104 +90,80 @@ func NewRemoteReader(db ethdb.KV, blockNr uint64) *RemoteReader {
 
 func (r *RemoteReader) GetAccountReads() [][]byte {
 	output := make([][]byte, 0)
-	for key := range r.accountReads {
-		output = append(output, key.Bytes())
+	for address := range r.accountReads {
+		output = append(output, address.Bytes())
 	}
 	return output
 }
 
 func (r *RemoteReader) GetStorageReads() [][]byte {
 	output := make([][]byte, 0)
-	for key := range r.storageReads {
+	for address, m := range r.storageReads {
+		for key := range m {
+			output = append(output, append(address.Bytes(), key.Bytes()...))
+		}
+	}
+	return output
+}
+
+func (r *RemoteReader) GetCodeReads() [][]byte {
+	output := make([][]byte, 0)
+	for key := range r.codeReads {
 		output = append(output, key.Bytes())
 	}
 	return output
 }
 
 func (r *RemoteReader) ReadAccountData(address common.Address) (*accounts.Account, error) {
-	r.accountReads[address] = true
-	addrHash, _ := common.HashData(address[:])
-	key := addrHash[:]
-	r.accountReads[address] = true
-	composite, _ := dbutils.CompositeKeySuffix(key, r.blockNr)
-	acc := accounts.NewAccount()
-	err := r.db.View(context.Background(), func(tx ethdb.Tx) error {
-		{
-			hB := tx.Bucket(dbutils.AccountsHistoryBucket)
-			hC := hB.Cursor()
-			hK, hV, err := hC.Seek(composite)
-			if err != nil {
-				return err
-			}
-
-			if hK != nil && bytes.HasPrefix(hK, key) {
-				err = acc.DecodeForStorage(hV)
-				if err != nil {
-					return err
-				}
-				return nil
-			}
-		}
-		{
-			v, err := tx.Bucket(dbutils.CurrentStateBucket).Get(key)
-			if err != nil {
-				return err
-			}
-			if v == nil {
-				return nil
-			}
-
-			root, err := tx.Bucket(dbutils.IntermediateTrieHashBucket).Get(key)
-			if err != nil {
-				return err
-			}
-			if root == nil {
-				return nil
-			}
-
-			err = acc.DecodeForStorage(v)
-			if err != nil {
-				return err
-			}
-			acc.Root = common.BytesToHash(root)
-			return nil
-		}
-
-		return ethdb.ErrKeyNotFound
-	})
+	r.accountReads[address] = struct{}{}
+	addrHash, err := common.HashData(address[:])
 	if err != nil {
+		return nil, err
+	}
+	enc, err := ethdb.GetAsOf(r.db, dbutils.CurrentStateBucket, dbutils.AccountsHistoryBucket, addrHash[:], r.blockNr)
+	if err != nil || enc == nil || len(enc) == 0 {
 		return nil, nil
 	}
-
+	var acc accounts.Account
+	if err := acc.DecodeForStorage(enc); err != nil {
+		return nil, err
+	}
 	return &acc, nil
 }
 
 func (r *RemoteReader) ReadAccountStorage(address common.Address, incarnation uint64, key *common.Hash) ([]byte, error) {
-	keyHash, _ := common.HashData(key[:])
-	addrHash, _ := common.HashData(address[:])
+	m, ok := r.storageReads[address]
+	if !ok {
+		m = make(map[common.Hash]struct{})
+		r.storageReads[address] = m
+	}
+	m[*key] = struct{}{}
+	keyHash, err := common.HashData(key[:])
+	if err != nil {
+		return nil, err
+	}
 
-	compositeKey := dbutils.GenerateCompositeStorageKey(addrHash, incarnation, keyHash)
-	var val []byte
-	err := r.db.View(context.Background(), func(tx ethdb.Tx) error {
-		b := tx.Bucket(dbutils.CurrentStateBucket)
-		v, err := b.Get(compositeKey)
-		val = v
-		return err
-	})
+	addrHash, err := common.HashData(address[:])
 	if err != nil {
 		return nil, err
 	}
-	r.storageReads[*key] = true
-	return val, nil
+
+	compositeKey := dbutils.GenerateCompositeStorageKey(addrHash, incarnation, keyHash)
+	enc, err := ethdb.GetAsOf(r.db, dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, compositeKey, r.blockNr)
+	if err != nil || enc == nil {
+		return nil, nil
+	}
+	return enc, nil
 }
 
 func (r *RemoteReader) ReadAccountCode(address common.Address, codeHash common.Hash) ([]byte, error) {
+	r.codeReads[address] = struct{}{}
 	if bytes.Equal(codeHash[:], crypto.Keccak256(nil)) {
 		return nil, nil
 	}
 	var val []byte
 	err := r.db.View(context.Background(), func(tx ethdb.Tx) error {
-		b := tx.Bucket(dbutils.CurrentStateBucket)
+		b := tx.Bucket(dbutils.CodeBucket)
 		v, err := b.Get(codeHash[:])
 		val = v
 		return err
@@ -198,47 +175,14 @@ func (r *RemoteReader) ReadAccountCode(address common.Address, codeHash common.H
 }
 
 func (r *RemoteReader) ReadAccountCodeSize(address common.Address, codeHash common.Hash) (int, error) {
-	if bytes.Equal(codeHash[:], crypto.Keccak256(nil)) {
-		return 0, nil
-	}
-	var val []byte
-	err := r.db.View(context.Background(), func(tx ethdb.Tx) error {
-		b := tx.Bucket(dbutils.CurrentStateBucket)
-		v, err := b.Get(codeHash[:])
-		val = v
-		return err
-	})
+	code, err := r.ReadAccountCode(address, codeHash)
 	if err != nil {
 		return 0, err
 	}
-	return len(val), nil
+	return len(code), nil
 }
 
 func (r *RemoteReader) ReadAccountIncarnation(address common.Address) (uint64, error) {
-	addrHash, _ := common.HashData(address[:])
-	var incarnationBytes [common.IncarnationLength]byte
-	startkey := make([]byte, common.HashLength+common.IncarnationLength+common.HashLength)
-	copy(startkey, addrHash[:])
-	found := false
-	err := r.db.View(context.Background(), func(tx ethdb.Tx) error {
-		b := tx.Bucket(dbutils.CurrentStateBucket)
-		c := b.Cursor()
-		k, _, err := c.Seek(startkey)
-		if err != nil {
-			return err
-		}
-		if k != nil && bytes.HasPrefix(k, addrHash[:]) {
-			copy(incarnationBytes[:], k[common.HashLength:])
-			found = true
-		}
-		return nil
-	})
-	if err != nil {
-		return 0, err
-	}
-	if found {
-		return (^binary.BigEndian.Uint64(incarnationBytes[:])), nil
-	}
 	return 0, nil
 }
 
diff --git a/cmd/restapi/apis/retrace_tx_api.go b/cmd/restapi/apis/retrace_tx_api.go
index a4890da71d..7c94187d0c 100644
--- a/cmd/restapi/apis/retrace_tx_api.go
+++ b/cmd/restapi/apis/retrace_tx_api.go
@@ -35,17 +35,24 @@ func (e *Env) GetWritesReads(c *gin.Context) {
 	c.JSON(http.StatusOK, results)
 }
 
-type WritesReads struct {
+type AccountWritesReads struct {
 	Reads  []string `json:"reads"`
 	Writes []string `json:"writes"`
 }
+type StorageWriteReads struct {
+	Reads  map[string][]string
+	Writes map[string][]string
+}
 type RetraceResponse struct {
-	Storage WritesReads `json:"storage"`
-	Account WritesReads `json:"accounts"`
+	Storage StorageWriteReads  `json:"storage"`
+	Account AccountWritesReads `json:"accounts"`
 }
 
 func Retrace(blockNumber, chain string, remoteDB ethdb.KV) (RetraceResponse, error) {
-	chainConfig := ReadChainConfig(remoteDB, chain)
+	chainConfig, err := ReadChainConfig(remoteDB, chain)
+	if err != nil {
+		return RetraceResponse{}, err
+	}
 	noOpWriter := state.NewNoopWriter()
 	bn, err := strconv.Atoi(blockNumber)
 	if err != nil {
@@ -56,7 +63,7 @@ func Retrace(blockNumber, chain string, remoteDB ethdb.KV) (RetraceResponse, err
 	if err != nil {
 		return RetraceResponse{}, err
 	}
-	writer := state.NewChangeSetWriter()
+	writer := state.NewChangeSetWriterPlain()
 	reader := NewRemoteReader(remoteDB, uint64(bn))
 	intraBlockState := state.New(reader)
 
@@ -77,11 +84,19 @@ func Retrace(blockNumber, chain string, remoteDB ethdb.KV) (RetraceResponse, err
 	}
 
 	storageChanges, _ := writer.GetStorageChanges()
+	output.Storage.Writes = make(map[string][]string)
 	for _, ch := range storageChanges.Changes {
-		output.Storage.Writes = append(output.Storage.Writes, common.Bytes2Hex(ch.Key))
+		addrKey := common.Bytes2Hex(ch.Key[:common.AddressLength])
+		l := output.Storage.Writes[addrKey]
+		l = append(l, common.Bytes2Hex(ch.Key[common.AddressLength+common.IncarnationLength:]))
+		output.Storage.Writes[addrKey] = l
 	}
-	for _, ch := range reader.GetStorageReads() {
-		output.Storage.Reads = append(output.Storage.Reads, common.Bytes2Hex(ch))
+	output.Storage.Reads = make(map[string][]string)
+	for _, key := range reader.GetStorageReads() {
+		addrKey := common.Bytes2Hex(key[:common.AddressLength])
+		l := output.Storage.Reads[addrKey]
+		l = append(l, common.Bytes2Hex(key[common.AddressLength+common.IncarnationLength:]))
+		output.Storage.Reads[addrKey] = l
 	}
 	return output, nil
 }
@@ -131,7 +146,7 @@ func GetBlockByNumber(db ethdb.KV, number uint64) (*types.Block, error) {
 }
 
 // ReadChainConfig retrieves the consensus settings based on the given genesis hash.
-func ReadChainConfig(db ethdb.KV, chain string) *params.ChainConfig {
+func ReadChainConfig(db ethdb.KV, chain string) (*params.ChainConfig, error) {
 	var k []byte
 	var data []byte
 	switch chain {
@@ -144,13 +159,17 @@ func ReadChainConfig(db ethdb.KV, chain string) *params.ChainConfig {
 	case "goerli":
 		k = params.GoerliGenesisHash[:]
 	}
-	_ = db.View(context.Background(), func(tx ethdb.Tx) error {
+	if err := db.View(context.Background(), func(tx ethdb.Tx) error {
 		b := tx.Bucket(dbutils.ConfigPrefix)
 		d, _ := b.Get(k)
 		data = d
 		return nil
-	})
+	}); err != nil {
+		return nil, err
+	}
 	var config params.ChainConfig
-	_ = json.Unmarshal(data, &config)
-	return &config
+	if err := json.Unmarshal(data, &config); err != nil {
+		return nil, err
+	}
+	return &config, nil
 }
diff --git a/cmd/restapi/commands/root.go b/cmd/restapi/commands/root.go
index 7003c9f38e..67af2d745c 100644
--- a/cmd/restapi/commands/root.go
+++ b/cmd/restapi/commands/root.go
@@ -14,11 +14,13 @@ import (
 
 var (
 	remoteDbAddress string
+	boltPath        string
 	listenAddress   string
 )
 
 func init() {
-	rootCmd.Flags().StringVar(&remoteDbAddress, "remote-db-addr", "localhost:9999", "address of remote DB listener of a turbo-geth node")
+	rootCmd.Flags().StringVar(&remoteDbAddress, "remote-db-addr", "", "address of remote DB listener of a turbo-geth node")
+	rootCmd.Flags().StringVar(&boltPath, "bolt-path", "", "path to the boltdb database")
 	rootCmd.Flags().StringVar(&listenAddress, "rpcaddr", "localhost:8080", "REST server listening interface")
 }
 
@@ -26,7 +28,7 @@ var rootCmd = &cobra.Command{
 	Use:   "restapi",
 	Short: "restapi exposes read-only blockchain APIs through REST (requires running turbo-geth node)",
 	RunE: func(cmd *cobra.Command, args []string) error {
-		return rest.ServeREST(cmd.Context(), listenAddress, remoteDbAddress)
+		return rest.ServeREST(cmd.Context(), listenAddress, remoteDbAddress, boltPath)
 	},
 }
 
diff --git a/cmd/restapi/rest/serve_rest.go b/cmd/restapi/rest/serve_rest.go
index 05a65fc7c1..71bf1397d8 100644
--- a/cmd/restapi/rest/serve_rest.go
+++ b/cmd/restapi/rest/serve_rest.go
@@ -2,6 +2,7 @@ package rest
 
 import (
 	"context"
+	"fmt"
 	"log"
 	"net/http"
 	"time"
@@ -19,7 +20,7 @@ func printError(name string, err error) {
 	}
 }
 
-func ServeREST(ctx context.Context, localAddress, remoteDBAddress string) error {
+func ServeREST(ctx context.Context, localAddress, remoteDBAddress string, boltPath string) error {
 	r := gin.Default()
 	root := r.Group("api/v1")
 	allowCORS(root)
@@ -30,7 +31,15 @@ func ServeREST(ctx context.Context, localAddress, remoteDBAddress string) error
 		}
 	})
 
-	db, err := ethdb.NewRemote().Path(remoteDBAddress).Open(ctx)
+	var db ethdb.KV
+	var err error
+	if remoteDBAddress != "" {
+		db, err = ethdb.NewRemote().Path(remoteDBAddress).Open(ctx)
+	} else if boltPath != "" {
+		db, err = ethdb.NewBolt().Path(boltPath).ReadOnly().Open(ctx)
+	} else {
+		err = fmt.Errorf("either remote db or bolt db must be specified")
+	}
 	if err != nil {
 		return err
 	}
@@ -38,6 +47,7 @@ func ServeREST(ctx context.Context, localAddress, remoteDBAddress string) error
 	e := &apis.Env{
 		DB:              db,
 		RemoteDBAddress: remoteDBAddress,
+		BoltPath:        boltPath,
 	}
 
 	if err = apis.RegisterRemoteDBAPI(root.Group("remote-db"), e); err != nil {
diff --git a/cmd/rpcdaemon/commands/daemon.go b/cmd/rpcdaemon/commands/daemon.go
index 818ed43c85..e773a8d135 100644
--- a/cmd/rpcdaemon/commands/daemon.go
+++ b/cmd/rpcdaemon/commands/daemon.go
@@ -226,7 +226,7 @@ func (api *APIImpl) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber
 
 // StorageRangeAt re-implementation of eth/api.go:StorageRangeAt
 func (api *PrivateDebugAPIImpl) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex uint64, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (eth.StorageRangeResult, error) {
-	_, _, _, dbstate, err := eth.ComputeTxEnv(ctx, &blockGetter{api.dbReader}, params.MainnetChainConfig, &chainContext{db: api.dbReader}, api.dbReader, blockHash, txIndex)
+	_, _, _, dbstate, err := eth.ComputeTxEnv(ctx, &blockGetter{api.dbReader}, params.MainnetChainConfig, &chainContext{db: api.dbReader}, api.db, blockHash, txIndex)
 	if err != nil {
 		return eth.StorageRangeResult{}, err
 	}
@@ -241,7 +241,7 @@ func (api *PrivateDebugAPIImpl) StorageRangeAt(ctx context.Context, blockHash co
 // attempted to be reexecuted to generate the desired state.
 func (api *PrivateDebugAPIImpl) computeIntraBlockState(block *types.Block) (*state.IntraBlockState, *state.DbState) {
 	// If we have the state fully available, use that
-	dbstate := state.NewDbState(api.dbReader, block.NumberU64())
+	dbstate := state.NewDbState(api.db, block.NumberU64())
 	statedb := state.New(dbstate)
 	return statedb, dbstate
 }
diff --git a/cmd/state/commands/state_snapshot.go b/cmd/state/commands/state_snapshot.go
deleted file mode 100644
index 96a4701d3d..0000000000
--- a/cmd/state/commands/state_snapshot.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package commands
-
-import (
-	"github.com/ledgerwatch/turbo-geth/cmd/state/stateless"
-	"github.com/spf13/cobra"
-)
-
-func init() {
-	withBlock(stateSnapshotCmd)
-	rootCmd.AddCommand(stateSnapshotCmd)
-}
-
-var stateSnapshotCmd = &cobra.Command{
-	Use:   "stateSnapshot",
-	Short: "stateSnapshot",
-	RunE: func(cmd *cobra.Command, args []string) error {
-		return stateless.StateSnapshot(block)
-	},
-}
diff --git a/cmd/state/stateless/check_change_sets.go b/cmd/state/stateless/check_change_sets.go
index c1866b4cc1..153dde6271 100644
--- a/cmd/state/stateless/check_change_sets.go
+++ b/cmd/state/stateless/check_change_sets.go
@@ -66,7 +66,7 @@ func CheckChangeSets(genesis *core.Genesis, blockNum uint64, chaindata string, h
 			break
 		}
 
-		dbstate := state.NewDbState(historyDb, block.NumberU64()-1)
+		dbstate := state.NewDbState(historyDb.AbstractKV(), block.NumberU64()-1)
 		intraBlockState := state.New(dbstate)
 		csw := state.NewChangeSetWriter()
 		var blockWriter state.StateWriter
diff --git a/cmd/state/stateless/deps.go b/cmd/state/stateless/deps.go
index bf21bc0adc..21becb5c73 100644
--- a/cmd/state/stateless/deps.go
+++ b/cmd/state/stateless/deps.go
@@ -140,7 +140,7 @@ func dataDependencies(blockNum uint64) {
 		if block == nil {
 			break
 		}
-		dbstate := state.NewDbState(ethDb, block.NumberU64()-1)
+		dbstate := state.NewDbState(ethDb.AbstractKV(), block.NumberU64()-1)
 		statedb := state.New(dbstate)
 		statedb.SetTracer(dt)
 		signer := types.MakeSigner(chainConfig, block.Number())
diff --git a/cmd/state/stateless/naked_accouts.go b/cmd/state/stateless/naked_accouts.go
index 486a3955c4..cf3598672e 100644
--- a/cmd/state/stateless/naked_accouts.go
+++ b/cmd/state/stateless/naked_accouts.go
@@ -105,7 +105,7 @@ func accountsReadWrites(blockNum uint64) {
 		if block == nil {
 			break
 		}
-		dbstate := state.NewDbState(ethDb, block.NumberU64()-1)
+		dbstate := state.NewDbState(ethDb.AbstractKV(), block.NumberU64()-1)
 		statedb := state.New(dbstate)
 		statedb.SetTracer(at)
 		signer := types.MakeSigner(chainConfig, block.Number())
diff --git a/cmd/state/stateless/naked_storage.go b/cmd/state/stateless/naked_storage.go
index 8efc5b8068..a910f703b1 100644
--- a/cmd/state/stateless/naked_storage.go
+++ b/cmd/state/stateless/naked_storage.go
@@ -133,7 +133,7 @@ func storageReadWrites(blockNum uint64) {
 		if block == nil {
 			break
 		}
-		dbstate := state.NewDbState(ethDb, block.NumberU64()-1)
+		dbstate := state.NewDbState(ethDb.AbstractKV(), block.NumberU64()-1)
 		statedb := state.New(dbstate)
 		signer := types.MakeSigner(chainConfig, block.Number())
 		st.loaded = make(map[common.Address]map[common.Hash]struct{})
diff --git a/cmd/state/stateless/spec_exec.go b/cmd/state/stateless/spec_exec.go
index fb31de8063..97b923e7fd 100644
--- a/cmd/state/stateless/spec_exec.go
+++ b/cmd/state/stateless/spec_exec.go
@@ -180,7 +180,7 @@ func speculativeExecution(blockNum uint64) {
 		if block == nil {
 			break
 		}
-		dbstate := state.NewDbState(ethDb, block.NumberU64()-1)
+		dbstate := state.NewDbState(ethDb.AbstractKV(), block.NumberU64()-1)
 
 		// First pass - execute transactions in sequence
 		statedb1 := state.New(dbstate)
diff --git a/cmd/state/stateless/state.go b/cmd/state/stateless/state.go
index 6d74c5d4d3..4357dd60e4 100644
--- a/cmd/state/stateless/state.go
+++ b/cmd/state/stateless/state.go
@@ -1391,7 +1391,7 @@ func makeCreators(blockNum uint64) {
 		if block == nil {
 			break
 		}
-		dbstate := state.NewDbState(ethDb, block.NumberU64()-1)
+		dbstate := state.NewDbState(ethDb.AbstractKV(), block.NumberU64()-1)
 		statedb := state.New(dbstate)
 		signer := types.MakeSigner(chainConfig, block.Number())
 		for _, tx := range block.Transactions() {
@@ -1977,7 +1977,7 @@ func makeSha3Preimages(blockNum uint64) {
 		if block == nil {
 			break
 		}
-		dbstate := state.NewDbState(ethDb, block.NumberU64()-1)
+		dbstate := state.NewDbState(ethDb.AbstractKV(), block.NumberU64()-1)
 		statedb := state.New(dbstate)
 		signer := types.MakeSigner(chainConfig, block.Number())
 		for _, tx := range block.Transactions() {
diff --git a/cmd/state/stateless/state_snapshot.go b/cmd/state/stateless/state_snapshot.go
index ea4ed6afa5..b5408e5d28 100644
--- a/cmd/state/stateless/state_snapshot.go
+++ b/cmd/state/stateless/state_snapshot.go
@@ -4,113 +4,21 @@ import (
 	"bytes"
 	"encoding/binary"
 	"fmt"
-	"os"
 	"time"
 
 	"github.com/ledgerwatch/bolt"
 	"github.com/ledgerwatch/turbo-geth/common"
 	"github.com/ledgerwatch/turbo-geth/common/dbutils"
-	"github.com/ledgerwatch/turbo-geth/consensus/ethash"
-	"github.com/ledgerwatch/turbo-geth/core"
 	"github.com/ledgerwatch/turbo-geth/core/rawdb"
 	"github.com/ledgerwatch/turbo-geth/core/types/accounts"
-	"github.com/ledgerwatch/turbo-geth/core/vm"
-	"github.com/ledgerwatch/turbo-geth/crypto"
 	"github.com/ledgerwatch/turbo-geth/ethdb"
 	"github.com/ledgerwatch/turbo-geth/migrations"
-	"github.com/ledgerwatch/turbo-geth/params"
 	"github.com/ledgerwatch/turbo-geth/trie"
 )
 
 // NOTE: This file is not the part of the Turbo-Geth binary. It i s part of the experimental utility, state
 // to perform data analysis related to the state growth, state rent, and statelesss clients
 
-func constructSnapshot(ethDb ethdb.Database, blockNum uint64) {
-	diskDb, err := bolt.Open(fmt.Sprintf("/Volumes/tb4/turbo-geth-copy/state_%d", blockNum), 0600, &bolt.Options{})
-	check(err)
-	defer diskDb.Close()
-	var startKey [32]byte
-	txDisk, err := diskDb.Begin(true)
-	check(err)
-	bDisk, err := txDisk.CreateBucket(dbutils.CurrentStateBucket, true)
-	check(err)
-	count := 0
-	err = ethDb.WalkAsOf(dbutils.CurrentStateBucket, dbutils.AccountsHistoryBucket, startKey[:], 0, blockNum+1,
-		func(key []byte, value []byte) (bool, error) {
-			if len(key) != 32 {
-				return false, nil
-			}
-			if len(value) == 0 {
-				return true, nil
-			}
-			if err = bDisk.Put(common.CopyBytes(key), common.CopyBytes(value)); err != nil {
-				return false, err
-			}
-			count++
-			if count%1000 == 0 {
-				if err := txDisk.Commit(); err != nil {
-					return false, err
-				}
-				fmt.Printf("Committed %d records\n", count)
-				var err error
-				txDisk, err = diskDb.Begin(true)
-				if err != nil {
-					return false, err
-				}
-				bDisk = txDisk.Bucket(dbutils.CurrentStateBucket)
-			}
-			return true, nil
-		},
-	)
-	check(err)
-	err = txDisk.Commit()
-	check(err)
-	txDisk, err = diskDb.Begin(true)
-	check(err)
-	b := txDisk.Bucket(dbutils.CurrentStateBucket)
-	count = 0
-	var address common.Address
-	//var hash common.Hash
-	exist := make(map[common.Address]bool)
-	var sk [52]byte
-	err = ethDb.WalkAsOf(dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, sk[:], 0, blockNum,
-		func(key []byte, value []byte) (bool, error) {
-			if len(value) == 0 {
-				return true, nil
-			}
-			copy(address[:], key[:20])
-			if e, ok := exist[address]; ok {
-				if !e {
-					return true, nil
-				}
-			} else {
-				v, _ := b.Get(crypto.Keccak256(address[:]))
-				exist[address] = v != nil
-			}
-			if err = b.Put(common.CopyBytes(key), common.CopyBytes(value)); err != nil {
-				return false, err
-			}
-			count++
-			if count%1000 == 0 {
-				if err := txDisk.Commit(); err != nil {
-					return false, err
-				}
-				fmt.Printf("Committed %d records\n", count)
-				var err error
-				txDisk, err = diskDb.Begin(true)
-				if err != nil {
-					return false, err
-				}
-				b = txDisk.Bucket(dbutils.CurrentStateBucket)
-			}
-			return true, nil
-		},
-	)
-	check(err)
-	err = txDisk.Commit()
-	check(err)
-}
-
 type bucketWriter struct {
 	db      ethdb.Database
 	bucket  []byte
@@ -384,36 +292,6 @@ func checkRoots(stateDb ethdb.Database, rootHash common.Hash, blockNum uint64) {
 	fmt.Printf("Storage trie computation took %v\n", time.Since(startTime))
 }
 
-func StateSnapshot(blockNum uint64) error {
-	startTime := time.Now()
-	//ethDb, err := ethdb.NewBoltDatabase("/Users/alexeyakhunov/Library/Ethereum/geth/chaindata")
-	ethDb, err := ethdb.NewBoltDatabase("/Volumes/tb4/turbo-geth-copy/geth/chaindata")
-	//ethDb, err := ethdb.NewBoltDatabase("/home/akhounov/.ethereum/geth/chaindata1")
-	check(err)
-	defer ethDb.Close()
-	stateDb, db := ethdb.NewMemDatabase2()
-	defer stateDb.Close()
-	if _, err := os.Stat("statedb0"); err == nil {
-		loadSnapshot(stateDb, "statedb0", func(path string) (ethdb.Database, error) {
-			return ethdb.NewBoltDatabase(path)
-		})
-		if err := loadCodes(db, ethDb); err != nil {
-			return err
-		}
-	} else {
-		constructSnapshot(ethDb, blockNum)
-	}
-	fmt.Printf("Snapshot took %v\n", time.Since(startTime))
-	startTime = time.Now()
-	bc, err := core.NewBlockChain(ethDb, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
-	check(err)
-	block := bc.GetBlockByNumber(blockNum)
-	fmt.Printf("Block number: %d\n", blockNum)
-	fmt.Printf("Block root hash: %x\n", block.Root())
-	checkRoots(ethDb, block.Root(), blockNum)
-	return nil
-}
-
 func VerifySnapshot(path string) {
 	ethDb, err := ethdb.NewBoltDatabase(path)
 	check(err)
diff --git a/cmd/state/stateless/tokens.go b/cmd/state/stateless/tokens.go
index 3b6b33f4b4..c6684c44f3 100644
--- a/cmd/state/stateless/tokens.go
+++ b/cmd/state/stateless/tokens.go
@@ -151,7 +151,7 @@ func makeTokens(blockNum uint64) {
 		if block == nil {
 			break
 		}
-		dbstate := state.NewDbState(ethDb, block.NumberU64()-1)
+		dbstate := state.NewDbState(ethDb.AbstractKV(), block.NumberU64()-1)
 		statedb := state.New(dbstate)
 		signer := types.MakeSigner(chainConfig, block.Number())
 		for _, tx := range block.Transactions() {
@@ -234,7 +234,7 @@ func makeTokenBalances() {
 		fmt.Printf("Analysing token %x...", token)
 		count := 0
 		addrCount := 0
-		dbstate := state.NewDbState(ethDb, currentBlockNr)
+		dbstate := state.NewDbState(ethDb.AbstractKV(), currentBlockNr)
 		statedb := state.New(dbstate)
 		msg := types.NewMessage(
 			caller,
@@ -454,7 +454,7 @@ func makeTokenAllowances() {
 		fmt.Printf("Analysing token %x...", token)
 		count := 0
 		addrCount := 0
-		dbstate := state.NewDbState(ethDb, currentBlockNr)
+		dbstate := state.NewDbState(ethDb.AbstractKV(), currentBlockNr)
 		statedb := state.New(dbstate)
 		msg := types.NewMessage(
 			caller,
diff --git a/cmd/state/stateless/transaction_stats.go b/cmd/state/stateless/transaction_stats.go
index 1cd8908923..29825205c7 100644
--- a/cmd/state/stateless/transaction_stats.go
+++ b/cmd/state/stateless/transaction_stats.go
@@ -202,7 +202,7 @@ func transactionStats(blockNum uint64) {
 		if block == nil {
 			break
 		}
-		dbstate := state.NewDbState(ethDb, block.NumberU64()-1)
+		dbstate := state.NewDbState(ethDb.AbstractKV(), block.NumberU64()-1)
 		statedb := state.New(dbstate)
 		signer := types.MakeSigner(chainConfig, block.Number())
 		for txIdx, tx := range block.Transactions() {
diff --git a/core/blockchain.go b/core/blockchain.go
index 7a25fdf42a..872e255ba7 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -591,35 +591,6 @@ func (bc *BlockChain) Processor() Processor {
 	return bc.processor
 }
 
-// State returns a new mutable state based on the current HEAD block.
-func (bc *BlockChain) State() (*state.IntraBlockState, *state.DbState, error) {
-	return bc.StateAt(bc.CurrentBlock().NumberU64())
-}
-
-// StateAt returns a new mutable state based on a particular point in time.
-func (bc *BlockChain) StateAt(blockNr uint64) (*state.IntraBlockState, *state.DbState, error) {
-	dbstate := state.NewDbState(bc.db, blockNr)
-	return state.New(dbstate), dbstate, nil
-}
-
-// GetAddressFromItsHash returns the preimage of a given address hash.
-func (bc *BlockChain) GetAddressFromItsHash(hash common.Hash) (common.Address, error) {
-	var addr common.Address
-
-	_, dbstate, err := bc.State()
-	if err != nil {
-		return addr, err
-	}
-
-	key := dbstate.GetKey(hash.Bytes())
-	if len(key) != common.AddressLength {
-		return addr, ErrNotFound
-	}
-
-	addr.SetBytes(key)
-	return addr, nil
-}
-
 // Reset purges the entire blockchain, restoring it to its genesis state.
 func (bc *BlockChain) Reset() error {
 	return bc.ResetWithGenesisBlock(bc.genesisBlock)
@@ -900,14 +871,6 @@ func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.
 	return uncles
 }
 
-// ByteCode retrieves the runtime byte code associated with an account.
-func (bc *BlockChain) ByteCode(addr common.Address) ([]byte, error) {
-	stateDB, _, err := bc.State()
-	if err != nil {
-		return nil, err
-	}
-	return stateDB.GetCode(addr), nil
-}
 
 // Stop stops the blockchain service. If any imports are currently in progress
 // it will abort them using the procInterrupt.
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 72a5015933..06b022c84c 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -1669,7 +1669,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
 	if _, err := blockchain.InsertChain(context.Background(), types.Blocks{blocks[0]}); err != nil {
 		t.Fatal(err)
 	}
-	if st, _, _ := blockchain.State(); !st.Exist(theAddr) {
+	if st := state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64())); !st.Exist(theAddr) {
 		t.Error("expected account to exist")
 	}
 
@@ -1677,7 +1677,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
 	if _, err := blockchain.InsertChain(context.Background(), types.Blocks{blocks[1]}); err != nil {
 		t.Fatal(err)
 	}
-	if st, _, _ := blockchain.State(); st.Exist(theAddr) {
+	if st := state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64())); st.Exist(theAddr) {
 		t.Error("account should not exist")
 	}
 
@@ -1685,7 +1685,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
 	if _, err := blockchain.InsertChain(context.Background(), types.Blocks{blocks[2]}); err != nil {
 		t.Fatal(err)
 	}
-	if st, _, _ := blockchain.State(); st.Exist(theAddr) {
+	if st := state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64())); st.Exist(theAddr) {
 		t.Error("account should not exist")
 	}
 }
@@ -1744,19 +1744,19 @@ func TestDoubleAccountRemoval(t *testing.T) {
 	_, err = blockchain.db.Commit()
 	assert.NoError(t, err)
 
-	st, _, err := blockchain.State()
+	st := state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	assert.NoError(t, err)
 	assert.False(t, st.Exist(theAddr), "Contract should've been removed")
 
-	st, _, err = blockchain.StateAt(0)
+	st = state.New(state.NewDbState(db.AbstractKV(), 0))
 	assert.NoError(t, err)
 	assert.False(t, st.Exist(theAddr), "Contract should not exist at block #0")
 
-	st, _, err = blockchain.StateAt(1)
+	st = state.New(state.NewDbState(db.AbstractKV(), 1))
 	assert.NoError(t, err)
 	assert.True(t, st.Exist(theAddr), "Contract should exist at block #1")
 
-	st, _, err = blockchain.StateAt(2)
+	st = state.New(state.NewDbState(db.AbstractKV(), 2))
 	assert.NoError(t, err)
 	assert.True(t, st.Exist(theAddr), "Contract should exist at block #2")
 }
@@ -2760,7 +2760,7 @@ func TestDeleteRecreateSlots(t *testing.T) {
 	if n, err := chain.InsertChain(context.Background(), blocks); err != nil {
 		t.Fatalf("block %d: failed to insert into chain: %v", n, err)
 	}
-	statedb, _, _ := chain.State()
+	statedb := state.New(state.NewDbState(diskdb.AbstractKV(), chain.CurrentBlock().NumberU64()))
 
 	// If all is correct, then slot 1 and 2 are zero
 	key1 := common.HexToHash("01")
@@ -2857,7 +2857,7 @@ func TestDeleteRecreateAccount(t *testing.T) {
 	if n, err := chain.InsertChain(context.Background(), blocks); err != nil {
 		t.Fatalf("block %d: failed to insert into chain: %v", n, err)
 	}
-	statedb, _, _ := chain.State()
+	statedb := state.New(state.NewDbState(diskdb.AbstractKV(), chain.CurrentBlock().NumberU64()))
 
 	// If all is correct, then both slots are zero
 	key1 := common.HexToHash("01")
@@ -3051,7 +3051,7 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) {
 		if n, err := chain.InsertChain(context.Background(), []*types.Block{block}); err != nil {
 			t.Fatalf("block %d: failed to insert into chain: %v", n, err)
 		}
-		statedb, _, _ := chain.State()
+		statedb := state.New(state.NewDbState(diskdb.AbstractKV(), chain.CurrentBlock().NumberU64()))
 		// If all is correct, then slot 1 and 2 are zero
 		key1 := common.HexToHash("01")
 		var got uint256.Int
@@ -3196,7 +3196,7 @@ func TestInitThenFailCreateContract(t *testing.T) {
 	if err != nil {
 		t.Fatalf("failed to create tester chain: %v", err)
 	}
-	statedb, _, _ := chain.State()
+	statedb := state.New(state.NewDbState(diskdb.AbstractKV(), chain.CurrentBlock().NumberU64()))
 	if got, exp := statedb.GetBalance(aa), uint64(100000); got.Uint64() != exp {
 		t.Fatalf("Genesis err, got %v exp %v", got, exp)
 	}
@@ -3206,7 +3206,7 @@ func TestInitThenFailCreateContract(t *testing.T) {
 		if _, err := chain.InsertChain(context.Background(), []*types.Block{blocks[0]}); err != nil {
 			t.Fatalf("block %d: failed to insert into chain: %v", block.NumberU64(), err)
 		}
-		statedb, _, _ = chain.State()
+		statedb = state.New(state.NewDbState(diskdb.AbstractKV(), chain.CurrentBlock().NumberU64()))
 		if got, exp := statedb.GetBalance(aa), uint64(100000); got.Uint64() != exp {
 			t.Fatalf("block %d: got %v exp %v", block.NumberU64(), got, exp)
 		}
diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go
index 43a8ef723c..1d935ffeb3 100644
--- a/core/chain_makers_test.go
+++ b/core/chain_makers_test.go
@@ -22,6 +22,7 @@ import (
 	"math/big"
 
 	"github.com/ledgerwatch/turbo-geth/consensus/ethash"
+	"github.com/ledgerwatch/turbo-geth/core/state"
 	"github.com/ledgerwatch/turbo-geth/core/types"
 	"github.com/ledgerwatch/turbo-geth/core/vm"
 	"github.com/ledgerwatch/turbo-geth/crypto"
@@ -96,11 +97,11 @@ func ExampleGenerateChain() {
 		return
 	}
 
-	state, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	fmt.Printf("last block: #%d\n", blockchain.CurrentBlock().Number())
-	fmt.Println("balance of addr1:", state.GetBalance(addr1))
-	fmt.Println("balance of addr2:", state.GetBalance(addr2))
-	fmt.Println("balance of addr3:", state.GetBalance(addr3))
+	fmt.Println("balance of addr1:", st.GetBalance(addr1))
+	fmt.Println("balance of addr2:", st.GetBalance(addr2))
+	fmt.Println("balance of addr3:", st.GetBalance(addr3))
 	// Output:
 	// last block: #5
 	// balance of addr1: 989000
diff --git a/core/state/database.go b/core/state/database.go
index ac3dbd2b31..0fd5a18994 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -1014,35 +1014,11 @@ func (tds *TrieDbState) readAccountDataByHash(addrHash common.Hash) (*accounts.A
 	}
 
 	// Not present in the trie, try the database
-	var err error
-	var enc []byte
 	var a accounts.Account
-	if tds.historical {
-		enc, err = tds.db.GetAsOf(dbutils.CurrentStateBucket, dbutils.AccountsHistoryBucket, addrHash[:], tds.blockNr+1)
-		if err != nil {
-			enc = nil
-		}
-		if len(enc) == 0 {
-			return nil, nil
-		}
-		if err := a.DecodeForStorage(enc); err != nil {
-			return nil, err
-		}
-	} else {
-		if ok, err := rawdb.ReadAccount(tds.db, addrHash, &a); err != nil {
-			return nil, err
-		} else if !ok {
-			return nil, nil
-		}
-	}
-
-	if tds.historical && a.Incarnation > 0 {
-		codeHash, err := tds.db.Get(dbutils.ContractCodeBucket, dbutils.GenerateStoragePrefix(addrHash[:], a.Incarnation))
-		if err == nil {
-			a.CodeHash = common.BytesToHash(codeHash)
-		} else {
-			log.Error("Get code hash is incorrect", "err", err)
-		}
+	if ok, err := rawdb.ReadAccount(tds.db, addrHash, &a); err != nil {
+		return nil, err
+	} else if !ok {
+		return nil, nil
 	}
 	return &a, nil
 }
@@ -1105,16 +1081,9 @@ func (tds *TrieDbState) ReadAccountStorage(address common.Address, incarnation u
 	enc, ok := tds.t.Get(dbutils.GenerateCompositeTrieKey(addrHash, seckey))
 	if !ok {
 		// Not present in the trie, try database
-		if tds.historical {
-			enc, err = tds.db.GetAsOf(dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, dbutils.GenerateCompositeStorageKey(addrHash, incarnation, seckey), tds.blockNr)
-			if err != nil {
-				enc = nil
-			}
-		} else {
-			enc, err = tds.db.Get(dbutils.CurrentStateBucket, dbutils.GenerateCompositeStorageKey(addrHash, incarnation, seckey))
-			if err != nil {
-				enc = nil
-			}
+		enc, err = tds.db.Get(dbutils.CurrentStateBucket, dbutils.GenerateCompositeStorageKey(addrHash, incarnation, seckey))
+		if err != nil {
+			enc = nil
 		}
 	}
 	return enc, nil
diff --git a/core/state/database_test.go b/core/state/database_test.go
index 238d019487..8562f72c0d 100644
--- a/core/state/database_test.go
+++ b/core/state/database_test.go
@@ -132,7 +132,7 @@ func TestCreate2Revive(t *testing.T) {
 		contractBackend.Commit()
 	})
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(address) {
 		t.Error("expected account to exist")
 	}
@@ -145,7 +145,7 @@ func TestCreate2Revive(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(contractAddress) {
 		t.Error("expected contractAddress to exist at the block 1", contractAddress.String())
 	}
@@ -165,7 +165,7 @@ func TestCreate2Revive(t *testing.T) {
 	if it.Event.D != create2address {
 		t.Errorf("Wrong create2address: %x, expected %x", it.Event.D, create2address)
 	}
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(create2address) {
 		t.Error("expected create2address to exist at the block 2", create2address.String())
 	}
@@ -181,7 +181,7 @@ func TestCreate2Revive(t *testing.T) {
 	if _, err = blockchain.InsertChain(context.Background(), types.Blocks{blocks[2]}); err != nil {
 		t.Fatal(err)
 	}
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if st.Exist(create2address) {
 		t.Error("expected create2address to be self-destructed at the block 3", create2address.String())
 	}
@@ -200,7 +200,7 @@ func TestCreate2Revive(t *testing.T) {
 	if it.Event.D != create2address {
 		t.Errorf("Wrong create2address: %x, expected %x", it.Event.D, create2address)
 	}
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(create2address) {
 		t.Error("expected create2address to exist at the block 2", create2address.String())
 	}
@@ -304,7 +304,7 @@ func TestReorgOverSelfDestruct(t *testing.T) {
 		contractBackendLonger.Commit()
 	})
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(address) {
 		t.Error("expected account to exist")
 	}
@@ -317,7 +317,7 @@ func TestReorgOverSelfDestruct(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(contractAddress) {
 		t.Error("expected contractAddress to exist at the block 1", contractAddress.String())
 	}
@@ -332,7 +332,7 @@ func TestReorgOverSelfDestruct(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if st.Exist(contractAddress) {
 		t.Error("expected contractAddress to not exist at the block 3", contractAddress.String())
 	}
@@ -342,7 +342,7 @@ func TestReorgOverSelfDestruct(t *testing.T) {
 	if _, err = blockchain.InsertChain(context.Background(), types.Blocks{longerBlocks[1], longerBlocks[2], longerBlocks[3]}); err != nil {
 		t.Fatal(err)
 	}
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(contractAddress) {
 		t.Error("expected contractAddress to exist at the block 4", contractAddress.String())
 	}
@@ -352,7 +352,7 @@ func TestReorgOverSelfDestruct(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	var valueX uint256.Int
 	st.GetState(contractAddress, &key0, &valueX)
 	if valueX != correctValueX {
@@ -440,7 +440,7 @@ func TestReorgOverStateChange(t *testing.T) {
 		contractBackendLonger.Commit()
 	})
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(address) {
 		t.Error("expected account to exist")
 	}
@@ -453,7 +453,7 @@ func TestReorgOverStateChange(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(contractAddress) {
 		t.Error("expected contractAddress to exist at the block 1", contractAddress.String())
 	}
@@ -474,7 +474,7 @@ func TestReorgOverStateChange(t *testing.T) {
 	if _, err = blockchain.InsertChain(context.Background(), types.Blocks{longerBlocks[1], longerBlocks[2]}); err != nil {
 		t.Fatal(err)
 	}
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(contractAddress) {
 		t.Error("expected contractAddress to exist at the block 4", contractAddress.String())
 	}
@@ -484,7 +484,7 @@ func TestReorgOverStateChange(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	var valueX uint256.Int
 	st.GetState(contractAddress, &key0, &valueX)
 	if valueX != correctValueX {
@@ -731,7 +731,7 @@ func TestCreateOnExistingStorage(t *testing.T) {
 		contractBackend.Commit()
 	})
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(address) {
 		t.Error("expected account to exist")
 	}
@@ -744,7 +744,7 @@ func TestCreateOnExistingStorage(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(contractAddress) {
 		t.Error("expected contractAddress to exist at the block 1", contractAddress.String())
 	}
@@ -877,7 +877,7 @@ func TestEip2200Gas(t *testing.T) {
 		contractBackend.Commit()
 	})
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(address) {
 		t.Error("expected account to exist")
 	}
@@ -891,7 +891,7 @@ func TestEip2200Gas(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(contractAddress) {
 		t.Error("expected contractAddress to exist at the block 1", contractAddress.String())
 	}
@@ -963,7 +963,7 @@ func TestWrongIncarnation(t *testing.T) {
 		contractBackend.Commit()
 	})
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(address) {
 		t.Error("expected account to exist")
 	}
@@ -990,7 +990,7 @@ func TestWrongIncarnation(t *testing.T) {
 		t.Fatal("Incorrect incarnation", acc.Incarnation)
 	}
 
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(contractAddress) {
 		t.Error("expected contractAddress to exist at the block 1", contractAddress.String())
 	}
@@ -1121,7 +1121,7 @@ func TestWrongIncarnation2(t *testing.T) {
 		contractBackendLonger.Commit()
 	})
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(address) {
 		t.Error("expected account to exist")
 	}
@@ -1136,7 +1136,7 @@ func TestWrongIncarnation2(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(contractAddress) {
 		t.Error("expected contractAddress to exist at the block 1", contractAddress.String())
 	}
diff --git a/core/state/dump.go b/core/state/dump.go
index 915b4562a2..fc6db4805e 100644
--- a/core/state/dump.go
+++ b/core/state/dump.go
@@ -35,7 +35,7 @@ type trieHasher interface {
 
 type Dumper struct {
 	blockNumber uint64
-	db          ethdb.Getter
+	db          ethdb.KV
 }
 
 // DumpAccount represents an account in the state.
@@ -114,7 +114,7 @@ func (d iterativeDump) onRoot(root common.Hash) {
 	}{root})
 }
 
-func NewDumper(db ethdb.Getter, blockNumber uint64) *Dumper {
+func NewDumper(db ethdb.KV, blockNumber uint64) *Dumper {
 	return &Dumper{db: db, blockNumber: blockNumber}
 }
 
@@ -128,7 +128,7 @@ func (d *Dumper) dump(c collector, excludeCode, excludeStorage, _ bool, start []
 
 	var acc accounts.Account
 	numberOfResults := 0
-	err = d.db.WalkAsOf(dbutils.CurrentStateBucket, dbutils.AccountsHistoryBucket, start, 0, d.blockNumber+1, func(k, v []byte) (bool, error) {
+	err = ethdb.WalkAsOf(d.db, dbutils.CurrentStateBucket, dbutils.AccountsHistoryBucket, start, 0, d.blockNumber+1, func(k, v []byte) (bool, error) {
 		if maxResults > 0 && numberOfResults >= maxResults {
 			if nextKey == nil {
 				nextKey = make([]byte, len(k))
@@ -167,14 +167,14 @@ func (d *Dumper) dump(c collector, excludeCode, excludeStorage, _ bool, start []
 		account := accountList[i]
 		incarnation := incarnationList[i]
 		var addr []byte
-		addr, err = d.db.Get(dbutils.PreimagePrefix, addrHash[:])
+		addr, err = ethdb.Get(d.db, dbutils.PreimagePrefix, addrHash[:])
 		if err != nil {
 			return nil, fmt.Errorf("getting preimage of %x: %v", addrHash, err)
 		}
 		storagePrefix := dbutils.GenerateStoragePrefix(addrHash[:], incarnation)
 		if incarnation > 0 {
 			var codeHash []byte
-			codeHash, err = d.db.Get(dbutils.ContractCodeBucket, storagePrefix)
+			codeHash, err = ethdb.Get(d.db, dbutils.ContractCodeBucket, storagePrefix)
 			if err != nil && err != ethdb.ErrKeyNotFound {
 				return nil, fmt.Errorf("getting code hash for %x: %v", addrHash, err)
 			}
@@ -185,7 +185,7 @@ func (d *Dumper) dump(c collector, excludeCode, excludeStorage, _ bool, start []
 			}
 			if !excludeCode && codeHash != nil && !bytes.Equal(emptyCodeHash[:], codeHash) {
 				var code []byte
-				if code, err = d.db.Get(dbutils.CodeBucket, codeHash); err != nil {
+				if code, err = ethdb.Get(d.db, dbutils.CodeBucket, codeHash); err != nil {
 					return nil, err
 				}
 				account.Code = common.Bytes2Hex(code)
@@ -193,7 +193,7 @@ func (d *Dumper) dump(c collector, excludeCode, excludeStorage, _ bool, start []
 		}
 		if !excludeStorage {
 			storageMap := make(map[common.Hash][]byte)
-			err = d.db.WalkAsOf(
+			err = ethdb.WalkAsOf(d.db,
 				dbutils.CurrentStateBucket,
 				dbutils.StorageHistoryBucket,
 				storagePrefix,
@@ -208,7 +208,7 @@ func (d *Dumper) dump(c collector, excludeCode, excludeStorage, _ bool, start []
 			}
 			for keyHash, vs := range storageMap {
 				var key []byte
-				key, err = d.db.Get(dbutils.PreimagePrefix, keyHash[:]) //remove account address and version from composite key
+				key, err = ethdb.Get(d.db, dbutils.PreimagePrefix, keyHash[:]) //remove account address and version from composite key
 				if err != nil {
 					return nil, fmt.Errorf("getting preimage for storage %x %x: %v", addrHash, keyHash, err)
 				}
diff --git a/core/state/history_test.go b/core/state/history_test.go
index 928d47582a..51e025ec75 100644
--- a/core/state/history_test.go
+++ b/core/state/history_test.go
@@ -155,7 +155,7 @@ func TestMutationCommitThinHistory(t *testing.T) {
 		}
 
 		for k, v := range accHistoryStateStorage[i] {
-			res, err := db.GetAsOf(dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, dbutils.GenerateCompositeStorageKey(addrHash, acc.Incarnation, k), 1)
+			res, err := ethdb.GetAsOf(db.AbstractKV(), dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, dbutils.GenerateCompositeStorageKey(addrHash, acc.Incarnation, k), 1)
 			if err != nil {
 				t.Fatal(err)
 			}
@@ -426,7 +426,7 @@ func TestBoltDB_WalkAsOf1(t *testing.T) {
 	//walk and collect walkAsOf result
 	var err error
 	var startKey [72]byte
-	err = db.WalkAsOf(dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, startKey[:], 0, 2, func(k []byte, v []byte) (b bool, e error) {
+	err = ethdb.WalkAsOf(db.AbstractKV(), dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, startKey[:], 0, 2, func(k []byte, v []byte) (b bool, e error) {
 		err = block2.Add(common.CopyBytes(k), common.CopyBytes(v))
 		if err != nil {
 			t.Fatal(err)
@@ -438,7 +438,7 @@ func TestBoltDB_WalkAsOf1(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	err = db.WalkAsOf(dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, startKey[:], 0, 4, func(k []byte, v []byte) (b bool, e error) {
+	err = ethdb.WalkAsOf(db.AbstractKV(), dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, startKey[:], 0, 4, func(k []byte, v []byte) (b bool, e error) {
 		err = block4.Add(common.CopyBytes(k), common.CopyBytes(v))
 		if err != nil {
 			t.Fatal(err)
@@ -450,7 +450,7 @@ func TestBoltDB_WalkAsOf1(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	err = db.WalkAsOf(dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, startKey[:], 0, 6, func(k []byte, v []byte) (b bool, e error) {
+	err = ethdb.WalkAsOf(db.AbstractKV(), dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, startKey[:], 0, 6, func(k []byte, v []byte) (b bool, e error) {
 		err = block6.Add(common.CopyBytes(k), common.CopyBytes(v))
 		if err != nil {
 			t.Fatal(err)
diff --git a/core/state/intra_block_state_test.go b/core/state/intra_block_state_test.go
index 81ee007a99..ad5b707e28 100644
--- a/core/state/intra_block_state_test.go
+++ b/core/state/intra_block_state_test.go
@@ -364,7 +364,7 @@ func (test *snapshotTest) run() bool {
 	// Run all actions and create snapshots.
 	var (
 		db           = ethdb.NewMemDatabase()
-		ds           = NewDbState(db, 0)
+		ds           = NewDbState(db.AbstractKV(), 0)
 		state        = New(ds)
 		snapshotRevs = make([]int, len(test.snapshots))
 		sindex       = 0
@@ -379,7 +379,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-- {
-		checkds := NewDbState(db, 0)
+		checkds := NewDbState(db.AbstractKV(), 0)
 		checkstate := New(checkds)
 		for _, action := range test.actions[:test.snapshots[sindex]] {
 			action.fn(action, checkstate)
diff --git a/core/state/readonly.go b/core/state/readonly.go
index abbd3dc3b5..2d5ce8ed1e 100644
--- a/core/state/readonly.go
+++ b/core/state/readonly.go
@@ -49,12 +49,12 @@ func (a *storageItem) Less(b llrb.Item) bool {
 
 // Implements StateReader by wrapping database only, without trie
 type DbState struct {
-	db      ethdb.Getter
+	db      ethdb.KV
 	blockNr uint64
 	storage map[common.Address]*llrb.LLRB
 }
 
-func NewDbState(db ethdb.Getter, blockNr uint64) *DbState {
+func NewDbState(db ethdb.KV, blockNr uint64) *DbState {
 	return &DbState{
 		db:      db,
 		blockNr: blockNr,
@@ -80,7 +80,7 @@ func (dbs *DbState) ForEachStorage(addr common.Address, start []byte, cb func(ke
 	st := llrb.New()
 	var s [common.HashLength + common.IncarnationLength + common.HashLength]byte
 	copy(s[:], addrHash[:])
-	accData, _ := dbs.db.GetAsOf(dbutils.CurrentStateBucket, dbutils.AccountsHistoryBucket, addrHash[:], dbs.blockNr+1)
+	accData, _ := ethdb.GetAsOf(dbs.db, dbutils.CurrentStateBucket, dbutils.AccountsHistoryBucket, addrHash[:], dbs.blockNr+1)
 	var acc accounts.Account
 	if err = acc.DecodeForStorage(accData); err != nil {
 		log.Error("Error decoding account", "error", err)
@@ -105,7 +105,7 @@ func (dbs *DbState) ForEachStorage(addr common.Address, start []byte, cb func(ke
 		})
 	}
 	numDeletes := st.Len() - overrideCounter
-	err = dbs.db.WalkAsOf(dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, s[:], 8*(common.HashLength+common.IncarnationLength), dbs.blockNr+1, func(ks, vs []byte) (bool, error) {
+	err = ethdb.WalkAsOf(dbs.db, dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, s[:], 8*(common.HashLength+common.IncarnationLength), dbs.blockNr+1, func(ks, vs []byte) (bool, error) {
 		if !bytes.HasPrefix(ks, addrHash[:]) {
 			return false, nil
 		}
@@ -139,7 +139,7 @@ func (dbs *DbState) ForEachStorage(addr common.Address, start []byte, cb func(ke
 		if !item.value.IsZero() {
 			// Skip if value == 0
 			if item.key == emptyHash {
-				key, err := dbs.db.Get(dbutils.PreimagePrefix, item.seckey[:])
+				key, err := ethdb.Get(dbs.db, dbutils.PreimagePrefix, item.seckey[:])
 				if err == nil {
 					copy(item.key[:], key)
 				} else {
@@ -158,7 +158,7 @@ func (dbs *DbState) ForEachStorage(addr common.Address, start []byte, cb func(ke
 
 func (dbs *DbState) ForEachAccount(start []byte, cb func(address *common.Address, addrHash common.Hash), maxResults int) {
 	results := 0
-	err := dbs.db.WalkAsOf(dbutils.CurrentStateBucket, dbutils.AccountsHistoryBucket, start[:], 0, dbs.blockNr+1, func(ks, vs []byte) (bool, error) {
+	err := ethdb.WalkAsOf(dbs.db, dbutils.CurrentStateBucket, dbutils.AccountsHistoryBucket, start[:], 0, dbs.blockNr+1, func(ks, vs []byte) (bool, error) {
 		if vs == nil || len(vs) == 0 {
 			// Skip deleted entries
 			return true, nil
@@ -168,7 +168,7 @@ func (dbs *DbState) ForEachAccount(start []byte, cb func(address *common.Address
 			return true, nil
 		}
 		addrHash := common.BytesToHash(ks[:common.HashLength])
-		preimage, err := dbs.db.Get(dbutils.PreimagePrefix, addrHash[:])
+		preimage, err := ethdb.Get(dbs.db, dbutils.PreimagePrefix, addrHash[:])
 		if err == nil {
 			addr := &common.Address{}
 			addr.SetBytes(preimage)
@@ -189,7 +189,7 @@ func (dbs *DbState) ReadAccountData(address common.Address) (*accounts.Account,
 	if err != nil {
 		return nil, err
 	}
-	enc, err := dbs.db.GetAsOf(dbutils.CurrentStateBucket, dbutils.AccountsHistoryBucket, addrHash[:], dbs.blockNr+1)
+	enc, err := ethdb.GetAsOf(dbs.db, dbutils.CurrentStateBucket, dbutils.AccountsHistoryBucket, addrHash[:], dbs.blockNr+1)
 	if err != nil || enc == nil || len(enc) == 0 {
 		return nil, nil
 	}
@@ -212,7 +212,7 @@ func (dbs *DbState) ReadAccountStorage(address common.Address, incarnation uint6
 	}
 
 	compositeKey := dbutils.GenerateCompositeStorageKey(addrHash, incarnation, keyHash)
-	enc, err := dbs.db.GetAsOf(dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, compositeKey, dbs.blockNr+1)
+	enc, err := ethdb.GetAsOf(dbs.db, dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, compositeKey, dbs.blockNr+1)
 	if err != nil || enc == nil {
 		return nil, nil
 	}
@@ -223,7 +223,7 @@ func (dbs *DbState) ReadAccountCode(address common.Address, codeHash common.Hash
 	if bytes.Equal(codeHash[:], emptyCodeHash) {
 		return nil, nil
 	}
-	return dbs.db.Get(dbutils.CodeBucket, codeHash[:])
+	return ethdb.Get(dbs.db, dbutils.CodeBucket, codeHash[:])
 }
 
 func (dbs *DbState) ReadAccountCodeSize(address common.Address, codeHash common.Hash) (int, error) {
@@ -281,14 +281,10 @@ func (dbs *DbState) CreateContract(address common.Address) error {
 }
 
 func (dbs *DbState) GetKey(shaKey []byte) []byte {
-	key, _ := dbs.db.Get(dbutils.PreimagePrefix, shaKey)
+	key, _ := ethdb.Get(dbs.db, dbutils.PreimagePrefix, shaKey)
 	return key
 }
 
-func (dbs *DbState) Dumper() *Dumper {
-	return &Dumper{db: dbs.db, blockNumber: dbs.blockNr}
-}
-
 // WalkStorageRange calls the walker for each storage item whose key starts with a given prefix,
 // for no more than maxItems.
 // Returns whether all matching storage items were traversed (provided there was no error).
@@ -306,7 +302,7 @@ func (dbs *DbState) WalkStorageRange(addrHash common.Hash, prefix trie.Keybytes,
 
 	i := 0
 
-	err := dbs.db.WalkAsOf(dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, startkey, fixedbits, dbs.blockNr+1,
+	err := ethdb.WalkAsOf(dbs.db, dbutils.CurrentStateBucket, dbutils.StorageHistoryBucket, startkey, fixedbits, dbs.blockNr+1,
 		func(key []byte, value []byte) (bool, error) {
 			val := new(big.Int).SetBytes(value)
 
@@ -336,7 +332,7 @@ func (dbs *DbState) WalkRangeOfAccounts(prefix trie.Keybytes, maxItems int, walk
 	i := 0
 
 	var acc accounts.Account
-	err := dbs.db.WalkAsOf(dbutils.CurrentStateBucket, dbutils.AccountsHistoryBucket, startkey, fixedbits, dbs.blockNr+1,
+	err := ethdb.WalkAsOf(dbs.db, dbutils.CurrentStateBucket, dbutils.AccountsHistoryBucket, startkey, fixedbits, dbs.blockNr+1,
 		func(key []byte, value []byte) (bool, error) {
 			if len(key) > 32 {
 				return true, nil
diff --git a/core/state/state_test.go b/core/state/state_test.go
index 2b729f645a..405672c3e8 100644
--- a/core/state/state_test.go
+++ b/core/state/state_test.go
@@ -32,6 +32,7 @@ import (
 
 type StateSuite struct {
 	db    ethdb.Database
+	kv    ethdb.KV // Same as db, but with a different interface
 	state *IntraBlockState
 	tds   *TrieDbState
 }
@@ -68,7 +69,7 @@ func (s *StateSuite) TestDump(c *checker.C) {
 	c.Check(err, checker.IsNil)
 
 	// check that dump contains the state objects that are in trie
-	got := string(NewDumper(s.db, 1).DefaultDump())
+	got := string(NewDumper(s.kv, 1).DefaultDump())
 	want := `{
     "root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2",
     "accounts": {
@@ -99,7 +100,9 @@ func (s *StateSuite) TestDump(c *checker.C) {
 }
 
 func (s *StateSuite) SetUpTest(c *checker.C) {
-	s.db = ethdb.NewMemDatabase()
+	db := ethdb.NewMemDatabase()
+	s.db = db
+	s.kv = db.AbstractKV()
 	s.tds = NewTrieDbState(common.Hash{}, s.db, 0)
 	s.state = New(s.tds)
 	s.tds.StartNewBuffer()
@@ -343,7 +346,7 @@ func TestDump(t *testing.T) {
 	}
 
 	// check that dump contains the state objects that are in trie
-	got := string(NewDumper(db, 2).DefaultDump())
+	got := string(NewDumper(db.AbstractKV(), 2).DefaultDump())
 	want := `{
     "root": "0000000000000000000000000000000000000000000000000000000000000000",
     "accounts": {
diff --git a/core/tx_pool.go b/core/tx_pool.go
index a2fbfcf7cd..ebd2cc9f2a 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -126,8 +126,6 @@ const (
 type blockChain interface {
 	CurrentBlock() *types.Block
 	GetBlock(hash common.Hash, number uint64) *types.Block
-	StateAt(blockNr uint64) (*state.IntraBlockState, *state.DbState, error)
-
 	SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription
 	GetTrieDbState() (*state.TrieDbState, error)
 }
diff --git a/eth/api.go b/eth/api.go
index bf37c39f1c..419dff8704 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -298,11 +298,7 @@ func (api *PublicDebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error
 	if block == nil {
 		return state.Dump{}, fmt.Errorf("block #%d not found", blockNr)
 	}
-	_, tds, err := api.eth.BlockChain().StateAt(uint64(blockNr))
-	if err != nil {
-		return state.Dump{}, err
-	}
-	return tds.Dumper().DefaultRawDump(), nil
+	return state.NewDumper(api.eth.ChainKV(), block.NumberU64()).DefaultRawDump(), nil
 }
 
 // PrivateDebugAPI is the collection of Ethereum full node APIs exposed over
@@ -382,7 +378,7 @@ func (api *PublicDebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, sta
 	if maxResults > AccountRangeMaxResults || maxResults <= 0 {
 		maxResults = AccountRangeMaxResults
 	}
-	dumper := state.NewDumper(api.eth.chainDb, blockNumber)
+	dumper := state.NewDumper(api.eth.ChainKV(), blockNumber)
 	return dumper.IteratorDump(nocode, nostorage, incompletes, start, maxResults)
 }
 
@@ -401,7 +397,7 @@ type StorageEntry struct {
 
 // StorageRangeAt returns the storage at the given block height and transaction index.
 func (api *PrivateDebugAPI) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex uint64, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
-	_, _, _, dbstate, err := ComputeTxEnv(ctx, api.eth.blockchain, api.eth.blockchain.Config(), api.eth.blockchain, api.eth.ChainDb(), blockHash, txIndex)
+	_, _, _, dbstate, err := ComputeTxEnv(ctx, api.eth.blockchain, api.eth.blockchain.Config(), api.eth.blockchain, api.eth.ChainKV(), blockHash, txIndex)
 	if err != nil {
 		return StorageRangeResult{}, err
 	}
diff --git a/eth/api_backend.go b/eth/api_backend.go
index c73b40cadd..c537c4fdbf 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -155,9 +155,10 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.
 	if header == nil {
 		return nil, nil, errors.New("header not found")
 	}
-	ds := state.NewDbState(b.eth.chainDb, bn)
+	ds := state.NewDbState(b.eth.ChainKV(), bn)
 	stateDb := state.New(ds)
 	return stateDb, header, nil
+
 }
 
 func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.IntraBlockState, *types.Header, error) {
@@ -175,8 +176,9 @@ func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockN
 		if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
 			return nil, nil, errors.New("hash is not currently canonical")
 		}
-		stateDb, _, err := b.eth.BlockChain().StateAt(header.Number.Uint64())
-		return stateDb, header, err
+		ds := state.NewDbState(b.eth.ChainKV(), header.Number.Uint64())
+		stateDb := state.New(ds)
+		return stateDb, header, nil
 	}
 	return nil, nil, errors.New("invalid arguments; neither block nor hash specified")
 }
@@ -197,7 +199,7 @@ func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (type
 }
 
 func (b *EthAPIBackend) getReceiptsByReApplyingTransactions(block *types.Block, number uint64) (types.Receipts, error) {
-	dbstate := state.NewDbState(b.eth.chainDb, number-1)
+	dbstate := state.NewDbState(b.eth.ChainKV(), number-1)
 	statedb := state.New(dbstate)
 	header := block.Header()
 	var receipts types.Receipts
diff --git a/eth/api_test.go b/eth/api_test.go
index d24b87310a..6518f13f84 100644
--- a/eth/api_test.go
+++ b/eth/api_test.go
@@ -37,7 +37,7 @@ import (
 
 var dumper = spew.ConfigState{Indent: "    "}
 
-func accountRangeTest(t *testing.T, trie *trie.Trie, db ethdb.Getter, blockNumber uint64, sdb *state.IntraBlockState, start common.Hash, requestedNum int, expectedNum int) state.IteratorDump { //nolint: unparam
+func accountRangeTest(t *testing.T, trie *trie.Trie, db ethdb.KV, blockNumber uint64, sdb *state.IntraBlockState, start common.Hash, requestedNum int, expectedNum int) state.IteratorDump { //nolint: unparam
 	result, err := state.NewDumper(db, blockNumber).IteratorDump(true, true, false, start.Bytes(), requestedNum)
 	if err != nil {
 		t.Fatal(err)
@@ -95,10 +95,10 @@ func TestAccountRange(t *testing.T) {
 
 	trie := tds.Trie()
 
-	accountRangeTest(t, trie, db, 0, sdb, common.Hash{}, AccountRangeMaxResults/2, AccountRangeMaxResults/2)
+	accountRangeTest(t, trie, db.AbstractKV(), 0, sdb, common.Hash{}, AccountRangeMaxResults/2, AccountRangeMaxResults/2)
 	// test pagination
-	firstResult := accountRangeTest(t, trie, db, 0, sdb, common.Hash{}, AccountRangeMaxResults, AccountRangeMaxResults)
-	secondResult := accountRangeTest(t, trie, db, 0, sdb, common.BytesToHash(firstResult.Next), AccountRangeMaxResults, AccountRangeMaxResults)
+	firstResult := accountRangeTest(t, trie, db.AbstractKV(), 0, sdb, common.Hash{}, AccountRangeMaxResults, AccountRangeMaxResults)
+	secondResult := accountRangeTest(t, trie, db.AbstractKV(), 0, sdb, common.BytesToHash(firstResult.Next), AccountRangeMaxResults, AccountRangeMaxResults)
 
 	hList := make(resultHash, 0)
 	for addr1 := range firstResult.Accounts {
@@ -116,7 +116,7 @@ func TestAccountRange(t *testing.T) {
 	// set and get an even split between the first and second sets.
 	sort.Sort(hList)
 	middleH := hList[AccountRangeMaxResults/2]
-	middleResult := accountRangeTest(t, trie, db, 0, sdb, middleH, AccountRangeMaxResults, AccountRangeMaxResults)
+	middleResult := accountRangeTest(t, trie, db.AbstractKV(), 0, sdb, middleH, AccountRangeMaxResults, AccountRangeMaxResults)
 	missing, infirst, insecond := 0, 0, 0
 	for h := range middleResult.Accounts {
 		if _, ok := firstResult.Accounts[h]; ok {
@@ -148,7 +148,7 @@ func TestEmptyAccountRange(t *testing.T) {
 	if err != nil {
 		t.Error(err)
 	}
-	results, err1 := state.NewDumper(db, 0).IteratorDump(true, true, true, (common.Hash{}).Bytes(), AccountRangeMaxResults)
+	results, err1 := state.NewDumper(db.AbstractKV(), 0).IteratorDump(true, true, true, (common.Hash{}).Bytes(), AccountRangeMaxResults)
 	if err1 != nil {
 		t.Fatal(err1)
 	}
@@ -233,7 +233,7 @@ func TestStorageRangeAt(t *testing.T) {
 			want: StorageRangeResult{StorageMap{keys[1]: storage[keys[1]], keys[2]: storage[keys[2]]}, &keys[3]},
 		},
 	}
-	dbs := state.NewDbState(db, 1)
+	dbs := state.NewDbState(db.AbstractKV(), 1)
 	for i, test := range tests {
 		test := test
 		t.Run("test_"+strconv.Itoa(i), func(t *testing.T) {
diff --git a/eth/api_tracer.go b/eth/api_tracer.go
index 21635b3cba..1a619a58f7 100644
--- a/eth/api_tracer.go
+++ b/eth/api_tracer.go
@@ -433,7 +433,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
 	if parent == nil {
 		return nil, fmt.Errorf("parent %#x not found", block.ParentHash())
 	}
-	statedb, dbstate := ComputeIntraBlockState(api.eth.ChainDb(), parent)
+	statedb, dbstate := ComputeIntraBlockState(api.eth.ChainKV(), parent)
 	// Execute all the transaction contained within the block concurrently
 	var (
 		signer = types.MakeSigner(api.eth.blockchain.Config(), block.Number())
@@ -520,7 +520,7 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block
 			reexec = *config.Reexec
 		}
 	*/
-	statedb, dbstate := ComputeIntraBlockState(api.eth.ChainDb(), parent)
+	statedb, dbstate := ComputeIntraBlockState(api.eth.ChainKV(), parent)
 	// Retrieve the tracing configurations, or use default values
 	var (
 		logConfig vm.LogConfig
@@ -608,9 +608,9 @@ func containsTx(block *types.Block, hash common.Hash) bool {
 // computeIntraBlockState retrieves the state database associated with a certain block.
 // If no state is locally available for the given block, a number of blocks are
 // attempted to be reexecuted to generate the desired state.
-func ComputeIntraBlockState(chainDb ethdb.Getter, block *types.Block) (*state.IntraBlockState, *state.DbState) {
+func ComputeIntraBlockState(chainKV ethdb.KV, block *types.Block) (*state.IntraBlockState, *state.DbState) {
 	// If we have the state fully available, use that
-	dbstate := state.NewDbState(chainDb, block.NumberU64())
+	dbstate := state.NewDbState(chainKV, block.NumberU64())
 	statedb := state.New(dbstate)
 	return statedb, dbstate
 }
@@ -623,7 +623,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Ha
 	if tx == nil {
 		return nil, fmt.Errorf("transaction %#x not found", hash)
 	}
-	msg, vmctx, statedb, _, err := ComputeTxEnv(ctx, api.eth.blockchain, api.eth.blockchain.Config(), api.eth.blockchain, api.eth.ChainDb(), blockHash, index)
+	msg, vmctx, statedb, _, err := ComputeTxEnv(ctx, api.eth.blockchain, api.eth.blockchain.Config(), api.eth.blockchain, api.eth.ChainKV(), blockHash, index)
 	if err != nil {
 		return nil, err
 	}
@@ -702,7 +702,7 @@ type BlockGetter interface {
 }
 
 // computeTxEnv returns the execution environment of a certain transaction.
-func ComputeTxEnv(ctx context.Context, blockGetter BlockGetter, cfg *params.ChainConfig, chain core.ChainContext, chainDb ethdb.Getter, blockHash common.Hash, txIndex uint64) (core.Message, vm.Context, *state.IntraBlockState, *state.DbState, error) {
+func ComputeTxEnv(ctx context.Context, blockGetter BlockGetter, cfg *params.ChainConfig, chain core.ChainContext, chainKV ethdb.KV, blockHash common.Hash, txIndex uint64) (core.Message, vm.Context, *state.IntraBlockState, *state.DbState, error) {
 	// Create the parent state database
 	block := blockGetter.GetBlockByHash(blockHash)
 	if block == nil {
@@ -713,7 +713,7 @@ func ComputeTxEnv(ctx context.Context, blockGetter BlockGetter, cfg *params.Chai
 		return nil, vm.Context{}, nil, nil, fmt.Errorf("parent %x not found", block.ParentHash())
 	}
 
-	statedb, dbstate := ComputeIntraBlockState(chainDb, parent)
+	statedb, dbstate := ComputeIntraBlockState(chainKV, parent)
 
 	if txIndex == 0 && len(block.Transactions()) == 0 {
 		return nil, vm.Context{}, statedb, dbstate, nil
diff --git a/eth/backend.go b/eth/backend.go
index 058358ff46..5db07c4083 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -80,6 +80,7 @@ type Ethereum struct {
 
 	// DB interfaces
 	chainDb ethdb.Database // Block chain database
+	chainKV ethdb.KV       // Same as chainDb, but different interface
 
 	eventMux       *event.TypeMux
 	engine         consensus.Engine
@@ -144,10 +145,14 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
 	if err != nil {
 		return nil, err
 	}
+	var chainKV ethdb.KV
+	if hasKV, ok := chainDb.(ethdb.HasAbstractKV); ok {
+		chainKV = hasKV.AbstractKV()
+	} else {
+		return nil, fmt.Errorf("database %T does not implement AbstractKV", chainDb)
+	}
 	if ctx.Config.RemoteDbListenAddress != "" {
-		if casted, ok := chainDb.(ethdb.HasAbstractKV); ok {
-			remotedbserver.StartDeprecated(casted.AbstractKV(), ctx.Config.RemoteDbListenAddress)
-		}
+		remotedbserver.StartDeprecated(chainKV, ctx.Config.RemoteDbListenAddress)
 	}
 
 	chainConfig, genesisHash, _, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis, config.StorageMode.History)
@@ -160,6 +165,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
 	eth := &Ethereum{
 		config:            config,
 		chainDb:           chainDb,
+		chainKV:           chainKV,
 		eventMux:          ctx.EventMux,
 		accountManager:    ctx.AccountManager,
 		engine:            CreateConsensusEngine(ctx, chainConfig, &config.Ethash, config.Miner.Notify, config.Miner.Noverify, chainDb),
@@ -575,6 +581,7 @@ func (s *Ethereum) TxPool() *core.TxPool               { return s.txPool }
 func (s *Ethereum) EventMux() *event.TypeMux           { return s.eventMux }
 func (s *Ethereum) Engine() consensus.Engine           { return s.engine }
 func (s *Ethereum) ChainDb() ethdb.Database            { return s.chainDb }
+func (s *Ethereum) ChainKV() ethdb.KV                  { return s.chainKV }
 func (s *Ethereum) IsListening() bool                  { return true } // Always listening
 func (s *Ethereum) EthVersion() int                    { return int(ProtocolVersions[0]) }
 func (s *Ethereum) NetVersion() uint64                 { return s.networkID }
@@ -592,9 +599,6 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
 		protos[i].DialCandidates = s.dialCandiates
 	}
 
-	// Firehose
-	protos = append(protos, s.protocolManager.makeFirehoseProtocol())
-
 	// Debug
 	protos = append(protos, s.protocolManager.makeDebugProtocol())
 
diff --git a/eth/handler.go b/eth/handler.go
index f6b87e9649..e7f8bd838e 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -19,7 +19,6 @@ package eth
 import (
 	"bytes"
 	"context"
-	"encoding/binary"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -35,7 +34,6 @@ import (
 	"github.com/ledgerwatch/turbo-geth/core"
 	"github.com/ledgerwatch/turbo-geth/core/forkid"
 	"github.com/ledgerwatch/turbo-geth/core/types"
-	"github.com/ledgerwatch/turbo-geth/core/types/accounts"
 	"github.com/ledgerwatch/turbo-geth/core/vm"
 	"github.com/ledgerwatch/turbo-geth/crypto"
 	"github.com/ledgerwatch/turbo-geth/eth/downloader"
@@ -227,36 +225,6 @@ func initPm(manager *ProtocolManager, txpool txPool, engine consensus.Engine, bl
 	manager.chainSync = newChainSyncer(manager)
 }
 
-func (pm *ProtocolManager) makeFirehoseProtocol() p2p.Protocol {
-	// Initiate Firehose
-	log.Info("Initialising Firehose protocol", "versions", FirehoseVersions)
-	return p2p.Protocol{
-		Name:    FirehoseName,
-		Version: FirehoseVersions[0],
-		Length:  FirehoseLengths[0],
-		Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
-			peer := &firehosePeer{Peer: p, rw: rw}
-			select {
-			case <-pm.quitSync:
-				return p2p.DiscQuitting
-			default:
-				pm.wg.Add(1)
-				defer pm.wg.Done()
-				return pm.handleFirehose(peer)
-			}
-		},
-		NodeInfo: func() interface{} {
-			return pm.NodeInfo()
-		},
-		PeerInfo: func(id enode.ID) interface{} {
-			if p := pm.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil {
-				return p.Info()
-			}
-			return nil
-		},
-	}
-}
-
 func (pm *ProtocolManager) makeDebugProtocol() p2p.Protocol {
 	// Initiate Debug protocol
 	log.Info("Initialising Debug protocol", "versions", FirehoseVersions)
@@ -500,15 +468,6 @@ func (pm *ProtocolManager) handle(p *peer) error {
 	}
 }
 
-func (pm *ProtocolManager) handleFirehose(p *firehosePeer) error {
-	for {
-		if err := pm.handleFirehoseMsg(p); err != nil {
-			p.Log().Debug("Firehose message handling failed", "err", err)
-			return err
-		}
-	}
-}
-
 func (pm *ProtocolManager) handleDebug(p *debugPeer) error {
 	for {
 		if err := pm.handleDebugMsg(p); err != nil {
@@ -1000,299 +959,6 @@ func (pm *ProtocolManager) extractAddressHash(addressOrHash []byte) (common.Hash
 	}
 }
 
-func (pm *ProtocolManager) handleFirehoseMsg(p *firehosePeer) error {
-	msg, readErr := p.rw.ReadMsg()
-	if readErr != nil {
-		return readErr
-	}
-	if msg.Size > FirehoseMaxMsgSize {
-		return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, FirehoseMaxMsgSize)
-	}
-	defer msg.Discard()
-
-	switch msg.Code {
-	case GetStateRangesCode:
-		msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
-		var request getStateRangesOrNodes
-		if err := msgStream.Decode(&request); err != nil {
-			return errResp(ErrDecode, "msg %v: %v", msg, err)
-		}
-
-		n := len(request.Prefixes)
-
-		var response stateRangesMsg
-		response.ID = request.ID
-		response.Entries = make([]firehoseAccountRange, n)
-
-		for i := 0; i < n; i++ {
-			response.Entries[i].Status = NoData
-		}
-
-		block := pm.blockchain.GetBlockByHash(request.Block)
-		if block != nil {
-			_, dbstate, err := pm.blockchain.StateAt(block.NumberU64())
-			if err != nil {
-				return err
-			}
-			for i, responseSize := 0, 0; i < n && responseSize < softResponseLimit; i++ {
-				var leaves []accountLeaf
-				allTraversed, err := dbstate.WalkRangeOfAccounts(request.Prefixes[i], MaxLeavesPerPrefix,
-					func(key common.Hash, value *accounts.Account) {
-						leaves = append(leaves, accountLeaf{key, value})
-					},
-				)
-				if err != nil {
-					return err
-				}
-				if allTraversed {
-					response.Entries[i].Status = OK
-					response.Entries[i].Leaves = leaves
-					responseSize += len(leaves)
-				} else {
-					response.Entries[i].Status = TooManyLeaves
-				}
-			}
-		} else {
-			response.AvailableBlocks = pm.blockchain.AvailableBlocks()
-		}
-
-		return p2p.Send(p.rw, StateRangesCode, response)
-
-	case StateRangesCode:
-		return errResp(ErrNotImplemented, "Not implemented yet")
-
-	case GetStorageRangesCode:
-		msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
-		var request getStorageRangesOrNodes
-		if err := msgStream.Decode(&request); err != nil {
-			return errResp(ErrDecode, "msg %v: %v", msg, err)
-		}
-
-		numReq := len(request.Requests)
-
-		var response storageRangesMsg
-		response.ID = request.ID
-		response.Entries = make([][]storageRange, numReq)
-
-		block := pm.blockchain.GetBlockByHash(request.Block)
-		if block != nil {
-			_, dbstate, err := pm.blockchain.StateAt(block.NumberU64())
-			if err != nil {
-				return err
-			}
-
-			for j, responseSize := 0, 0; j < numReq; j++ {
-				req := request.Requests[j]
-
-				n := len(req.Prefixes)
-				response.Entries[j] = make([]storageRange, n)
-				for i := 0; i < n; i++ {
-					response.Entries[j][i].Status = NoData
-				}
-
-				addrHash, err := pm.extractAddressHash(req.Account)
-				if err != nil {
-					return err
-				}
-
-				for i := 0; i < n && responseSize < softResponseLimit; i++ {
-					var leaves []storageLeaf
-					allTraversed, err := dbstate.WalkStorageRange(addrHash, req.Prefixes[i], MaxLeavesPerPrefix,
-						func(key common.Hash, value big.Int) {
-							leaves = append(leaves, storageLeaf{key, value})
-						},
-					)
-					if err != nil {
-						return err
-					}
-					if allTraversed {
-						response.Entries[j][i].Status = OK
-						response.Entries[j][i].Leaves = leaves
-						responseSize += len(leaves)
-					} else {
-						response.Entries[j][i].Status = TooManyLeaves
-					}
-				}
-			}
-		} else {
-			response.AvailableBlocks = pm.blockchain.AvailableBlocks()
-		}
-
-		return p2p.Send(p.rw, StorageRangesCode, response)
-
-	case StorageRangesCode:
-		return errResp(ErrNotImplemented, "Not implemented yet")
-
-	case GetStateNodesCode:
-		msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
-		var request getStateRangesOrNodes
-		if err := msgStream.Decode(&request); err != nil {
-			return errResp(ErrDecode, "msg %v: %v", msg, err)
-		}
-
-		n := len(request.Prefixes)
-
-		var response stateNodesMsg
-		response.ID = request.ID
-		response.Nodes = make([][]byte, n)
-
-		block := pm.blockchain.GetBlockByHash(request.Block)
-		if block != nil {
-			//tr := trie.New(common.Hash{})
-
-			for i, responseSize := 0, 0; i < n && responseSize < softResponseLimit; i++ {
-				//prefix := request.Prefixes[i]
-				//rr := tr.NewResolveRequest(nil, prefix.ToHex(), prefix.Nibbles())
-				//rr.RequiresRLP = true
-
-				loader := trie.NewSubTrieLoader(block.NumberU64())
-
-				rl := trie.NewRetainList(0)
-				if _, err2 := loader.LoadSubTries(pm.blockchain.ChainDb(), block.NumberU64(), rl, [][]byte{nil}, []int{0}, false); err2 != nil {
-					return err2
-				}
-				/*
-					node := rr.NodeRLP
-					response.Nodes[i] = make([]byte, len(node))
-					copy(response.Nodes[i], node)
-					responseSize += len(node)
-				*/
-			}
-		} else {
-			response.AvailableBlocks = pm.blockchain.AvailableBlocks()
-		}
-
-		return p2p.Send(p.rw, StateNodesCode, response)
-
-	case StateNodesCode:
-		return errResp(ErrNotImplemented, "Not implemented yet")
-
-	case GetStorageNodesCode:
-		msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
-		var request getStorageRangesOrNodes
-		if err := msgStream.Decode(&request); err != nil {
-			return errResp(ErrDecode, "msg %v: %v", msg, err)
-		}
-
-		numReq := len(request.Requests)
-
-		var response storageNodesMsg
-		response.ID = request.ID
-		response.Nodes = make([][][]byte, numReq)
-
-		block := pm.blockchain.GetBlockByHash(request.Block)
-		if block != nil {
-
-			for j, responseSize := 0, 0; j < numReq; j++ {
-				req := request.Requests[j]
-
-				n := len(req.Prefixes)
-				response.Nodes[j] = make([][]byte, n)
-
-				addrHash, err := pm.extractAddressHash(req.Account)
-				if err != nil {
-					return err
-				}
-
-				//tr := trie.New(common.Hash{})
-
-				for i := 0; i < n && responseSize < softResponseLimit; i++ {
-					contractPrefix := make([]byte, common.HashLength+common.IncarnationLength)
-					copy(contractPrefix, addrHash.Bytes())
-					binary.BigEndian.PutUint64(contractPrefix[common.HashLength:], ^uint64(1))
-					// TODO [Issue 99] support incarnations
-					//storagePrefix := req.Prefixes[i]
-					//rr := tr.NewResolveRequest(contractPrefix, storagePrefix.ToHex(), storagePrefix.Nibbles())
-					//rr.RequiresRLP = true
-
-					loader := trie.NewSubTrieLoader(block.NumberU64())
-
-					rl := trie.NewRetainList(0)
-					if _, err2 := loader.LoadSubTries(pm.blockchain.ChainDb(), block.NumberU64(), rl, [][]byte{nil}, []int{0}, false); err2 != nil {
-						return err2
-					}
-					/*
-						node := rr.NodeRLP
-						response.Nodes[j][i] = make([]byte, len(node))
-						copy(response.Nodes[j][i], node)
-						responseSize += len(node)
-					*/
-				}
-			}
-		} else {
-			response.AvailableBlocks = pm.blockchain.AvailableBlocks()
-		}
-
-		return p2p.Send(p.rw, StorageNodesCode, response)
-
-	case StorageNodesCode:
-		return errResp(ErrNotImplemented, "Not implemented yet")
-
-	case GetBytecodeCode:
-		// Decode the retrieval message
-		msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
-		if _, err := msgStream.List(); err != nil {
-			return err
-		}
-		var reqID uint64
-		if err := msgStream.Decode(&reqID); err != nil {
-			return errResp(ErrDecode, "msg %v: %v", msg, err)
-		}
-		if _, err := msgStream.List(); err != nil {
-			return err
-		}
-
-		// Gather bytecodes until the fetch or network limit is reached
-		var (
-			responseSize int
-			code         [][]byte
-		)
-		for responseSize < softResponseLimit && len(code) < downloader.MaxStateFetch {
-			var req bytecodeRef
-			if err := msgStream.Decode(&req); err == rlp.EOL {
-				break
-			} else if err != nil {
-				return errResp(ErrDecode, "msg %v: %v", msg, err)
-			}
-
-			var addr common.Address
-			if len(req.Account) == common.AddressLength {
-				addr.SetBytes(req.Account)
-			} else if len(req.Account) == common.HashLength {
-				var preimageErr error
-				addr, preimageErr = pm.blockchain.GetAddressFromItsHash(common.BytesToHash(req.Account))
-				if preimageErr == core.ErrNotFound {
-					code = append(code, []byte{})
-					break
-				} else if preimageErr != nil {
-					return preimageErr
-				}
-			} else {
-				return errResp(ErrDecode, "not an account address or its hash")
-			}
-
-			// Retrieve requested byte code, stopping if enough was found
-			if entry, err := pm.blockchain.ByteCode(addr); err == nil {
-				code = append(code, entry)
-				responseSize += len(entry)
-			}
-		}
-		return p.SendByteCode(reqID, code)
-
-	case BytecodeCode:
-		return errResp(ErrNotImplemented, "Not implemented yet")
-
-	case GetStorageSizesCode:
-		return errResp(ErrNotImplemented, "Not implemented yet")
-
-	case StorageSizesCode:
-		return errResp(ErrNotImplemented, "Not implemented yet")
-
-	default:
-		return errResp(ErrInvalidMsgCode, "%v", msg.Code)
-	}
-}
-
 func (pm *ProtocolManager) handleDebugMsg(p *debugPeer) error {
 	msg, readErr := p.rw.ReadMsg()
 	if readErr != nil {
diff --git a/eth/helper_test.go b/eth/helper_test.go
index 3cfefc8889..9475b9cd7f 100644
--- a/eth/helper_test.go
+++ b/eth/helper_test.go
@@ -230,7 +230,7 @@ func newFirehoseTestPeer(name string, pm *ProtocolManager) (*testFirehosePeer, <
 		case <-pm.quitSync:
 			errc <- p2p.DiscQuitting
 		default:
-			errc <- pm.handleFirehose(peer)
+			//errc <- pm.handleFirehose(peer)
 		}
 	}()
 
diff --git a/ethdb/badger_db.go b/ethdb/badger_db.go
index 769c803366..0fc269532a 100644
--- a/ethdb/badger_db.go
+++ b/ethdb/badger_db.go
@@ -101,6 +101,10 @@ func NewEphemeralBadger() (*BadgerDatabase, error) {
 	}, nil
 }
 
+func (db *BadgerDatabase) AbstractKV() KV {
+	return &badgerDB{badger: db.db}
+}
+
 // Close closes the database.
 func (db *BadgerDatabase) Close() {
 	if db.gcTicker != nil {
@@ -372,7 +376,7 @@ func (db *BadgerDatabase) NewBatch() DbWithPendingMutations {
 
 // IdealBatchSize defines the size of the data batches should ideally add in one write.
 func (db *BadgerDatabase) IdealBatchSize() int {
-	return 100 * 1024
+	return 50 * 1024 * 1024
 }
 
 // DiskSize returns the total disk size of the database in bytes.
diff --git a/ethdb/bolt_db.go b/ethdb/bolt_db.go
index cc6f5eb7c7..0c20ffb7f4 100644
--- a/ethdb/bolt_db.go
+++ b/ethdb/bolt_db.go
@@ -20,23 +20,16 @@ package ethdb
 import (
 	"bytes"
 	"context"
-	"encoding/binary"
-	"fmt"
 	"os"
 	"path"
 
 	"github.com/ledgerwatch/bolt"
 	"github.com/ledgerwatch/turbo-geth/common"
-	"github.com/ledgerwatch/turbo-geth/common/changeset"
 	"github.com/ledgerwatch/turbo-geth/common/dbutils"
 	"github.com/ledgerwatch/turbo-geth/core/types/accounts"
 	"github.com/ledgerwatch/turbo-geth/log"
 )
 
-var OpenFileLimit = 64
-
-const HeapSize = 512 * 1024 * 1024
-
 // BoltDatabase is a wrapper over BoltDb,
 // compatible with the Database interface.
 type BoltDatabase struct {
@@ -197,6 +190,26 @@ func (db *BoltDatabase) Get(bucket, key []byte) ([]byte, error) {
 	return dat, err
 }
 
+func Get(db KV, bucket, key []byte) ([]byte, error) {
+	// Retrieve the key and increment the miss counter if not found
+	var dat []byte
+	err := db.View(context.Background(), func(tx Tx) error {
+		b := tx.Bucket(bucket)
+		if b != nil {
+			v, _ := b.Get(key)
+			if v != nil {
+				dat = make([]byte, len(v))
+				copy(dat, v)
+			}
+		}
+		return nil
+	})
+	if dat == nil {
+		return nil, ErrKeyNotFound
+	}
+	return dat, err
+}
+
 // GetIndexChunk returns proper index chunk or return error if index is not created.
 // key must contain inverted block number in the end
 func (db *BoltDatabase) GetIndexChunk(bucket, key []byte, timestamp uint64) ([]byte, error) {
@@ -244,32 +257,6 @@ func (db *BoltDatabase) GetChangeSetByBlock(hBucket []byte, timestamp uint64) ([
 	return dat, nil
 }
 
-// GetAsOf returns the value valid as of a given timestamp.
-func (db *BoltDatabase) GetAsOf(bucket, hBucket, key []byte, timestamp uint64) ([]byte, error) {
-	var dat []byte
-	err := db.db.View(func(tx *bolt.Tx) error {
-		v, err := BoltDBFindByHistory(tx, hBucket, key, timestamp)
-		if err != nil {
-			log.Debug("BoltDB BoltDBFindByHistory err", "err", err)
-		} else {
-			dat = make([]byte, len(v))
-			copy(dat, v)
-			return nil
-		}
-		{
-			v, _ := tx.Bucket(bucket).Get(key)
-			if v == nil {
-				return ErrKeyNotFound
-			}
-
-			dat = make([]byte, len(v))
-			copy(dat, v)
-			return nil
-		}
-	})
-	return dat, err
-}
-
 func HackAddRootToAccountBytes(accNoRoot []byte, root []byte) (accWithRoot []byte, err error) {
 	var acc accounts.Account
 	if err := acc.DecodeForStorage(accNoRoot); err != nil {
@@ -369,287 +356,6 @@ func (db *BoltDatabase) MultiWalk(bucket []byte, startkeys [][]byte, fixedbits [
 	return err
 }
 
-func (db *BoltDatabase) walkAsOfThinAccounts(startkey []byte, fixedbits int, timestamp uint64, walker func(k []byte, v []byte) (bool, error)) error {
-	fixedbytes, mask := Bytesmask(fixedbits)
-	err := db.db.View(func(tx *bolt.Tx) error {
-		b := tx.Bucket(dbutils.CurrentStateBucket)
-		if b == nil {
-			return fmt.Errorf("currentStateBucket not found")
-		}
-		hB := tx.Bucket(dbutils.AccountsHistoryBucket)
-		if hB == nil {
-			return fmt.Errorf("accountsHistoryBucket not found")
-		}
-		csB := tx.Bucket(dbutils.AccountChangeSetBucket)
-		if csB == nil {
-			return fmt.Errorf("accountChangeBucket not found")
-		}
-		//for state
-		mainCursor := b.Cursor()
-		//for historic data
-		historyCursor := newSplitCursor(
-			hB,
-			startkey,
-			fixedbits,
-			common.HashLength,   /* part1end */
-			common.HashLength,   /* part2start */
-			common.HashLength+8, /* part3start */
-		)
-		k, v := mainCursor.Seek(startkey)
-		for k != nil && len(k) > common.HashLength {
-			k, v = mainCursor.Next()
-		}
-		hK, tsEnc, _, hV := historyCursor.Seek()
-		for hK != nil && binary.BigEndian.Uint64(tsEnc) < timestamp {
-			hK, tsEnc, _, hV = historyCursor.Next()
-		}
-		goOn := true
-		var err error
-		for goOn {
-			//exit or next conditions
-			if k != nil && fixedbits > 0 && !bytes.Equal(k[:fixedbytes-1], startkey[:fixedbytes-1]) {
-				k = nil
-			}
-			if k != nil && fixedbits > 0 && (k[fixedbytes-1]&mask) != (startkey[fixedbytes-1]&mask) {
-				k = nil
-			}
-			var cmp int
-			if k == nil {
-				if hK == nil {
-					break
-				} else {
-					cmp = 1
-				}
-			} else if hK == nil {
-				cmp = -1
-			} else {
-				cmp = bytes.Compare(k, hK)
-			}
-			if cmp < 0 {
-				goOn, err = walker(k, v)
-			} else {
-				index := dbutils.WrapHistoryIndex(hV)
-				if changeSetBlock, set, ok := index.Search(timestamp); ok {
-					// set == true if this change was from empty record (non-existent account) to non-empty
-					// In such case, we do not need to examine changeSet and simply skip the record
-					if !set {
-						// Extract value from the changeSet
-						csKey := dbutils.EncodeTimestamp(changeSetBlock)
-						changeSetData, _ := csB.Get(csKey)
-						if changeSetData == nil {
-							return fmt.Errorf("could not find ChangeSet record for index entry %d (query timestamp %d)", changeSetBlock, timestamp)
-						}
-						data, err1 := changeset.AccountChangeSetBytes(changeSetData).FindLast(hK)
-						if err1 != nil {
-							return fmt.Errorf("could not find key %x in the ChangeSet record for index entry %d (query timestamp %d)",
-								hK,
-								changeSetBlock,
-								timestamp,
-							)
-						}
-						if len(data) > 0 { // Skip accounts did not exist
-							goOn, err = walker(hK, data)
-						}
-					}
-				} else if cmp == 0 {
-					goOn, err = walker(k, v)
-				}
-			}
-			if goOn {
-				if cmp <= 0 {
-					k, v = mainCursor.Next()
-					for k != nil && len(k) > common.HashLength {
-						k, v = mainCursor.Next()
-					}
-				}
-				if cmp >= 0 {
-					hK0 := hK
-					for hK != nil && (bytes.Equal(hK0, hK) || binary.BigEndian.Uint64(tsEnc) < timestamp) {
-						hK, tsEnc, _, hV = historyCursor.Next()
-					}
-				}
-			}
-		}
-		return err
-	})
-	return err
-}
-
-// splitCursor implements cursor with two keys
-// it is used to ignore incarnations in the middle
-// of composite storage key, but without
-// reconstructing the key
-// Instead, the key is split into two parts and
-// functions `Seek` and `Next` deliver both
-// parts as well as the corresponding value
-type splitCursor struct {
-	c          *bolt.Cursor // Unlerlying bolt cursor
-	startkey   []byte       // Starting key (also contains bits that need to be preserved)
-	matchBytes int
-	mask       uint8
-	part1end   int // Position in the key where the first part ends
-	part2start int // Position in the key where the second part starts
-	part3start int // Position in the key where the third part starts
-}
-
-func newSplitCursor(b *bolt.Bucket, startkey []byte, matchBits int, part1end, part2start, part3start int) *splitCursor {
-	var sc splitCursor
-	sc.c = b.Cursor()
-	sc.startkey = startkey
-	sc.part1end = part1end
-	sc.part2start = part2start
-	sc.part3start = part3start
-	sc.matchBytes, sc.mask = Bytesmask(matchBits)
-	return &sc
-}
-
-func (sc *splitCursor) matchKey(k []byte) bool {
-	if k == nil {
-		return false
-	}
-	if sc.matchBytes == 0 {
-		return true
-	}
-	if len(k) < sc.matchBytes {
-		return false
-	}
-	if !bytes.Equal(k[:sc.matchBytes-1], sc.startkey[:sc.matchBytes-1]) {
-		return false
-	}
-	return (k[sc.matchBytes-1] & sc.mask) == (sc.startkey[sc.matchBytes-1] & sc.mask)
-}
-
-func (sc *splitCursor) Seek() (key1, key2, key3, val []byte) {
-	k, v := sc.c.Seek(sc.startkey)
-	if !sc.matchKey(k) {
-		return nil, nil, nil, nil
-	}
-	return k[:sc.part1end], k[sc.part2start:sc.part3start], k[sc.part3start:], v
-}
-
-func (sc *splitCursor) Next() (key1, key2, key3, val []byte) {
-	k, v := sc.c.Next()
-	if !sc.matchKey(k) {
-		return nil, nil, nil, nil
-	}
-	return k[:sc.part1end], k[sc.part2start:sc.part3start], k[sc.part3start:], v
-}
-
-func (db *BoltDatabase) walkAsOfThinStorage(startkey []byte, fixedbits int, timestamp uint64, walker func(k1, k2, v []byte) (bool, error)) error {
-	err := db.db.View(func(tx *bolt.Tx) error {
-		b := tx.Bucket(dbutils.CurrentStateBucket)
-		if b == nil {
-			return fmt.Errorf("storageBucket not found")
-		}
-		hB := tx.Bucket(dbutils.StorageHistoryBucket)
-		if hB == nil {
-			return fmt.Errorf("storageHistoryBucket not found")
-		}
-		csB := tx.Bucket(dbutils.StorageChangeSetBucket)
-		if csB == nil {
-			return fmt.Errorf("storageChangeBucket not found")
-		}
-		startkeyNoInc := make([]byte, len(startkey)-common.IncarnationLength)
-		copy(startkeyNoInc, startkey[:common.HashLength])
-		copy(startkeyNoInc[common.HashLength:], startkey[common.HashLength+common.IncarnationLength:])
-		//for storage
-		mainCursor := newSplitCursor(
-			b,
-			startkey,
-			fixedbits,
-			common.HashLength, /* part1end */
-			common.HashLength+common.IncarnationLength,                   /* part2start */
-			common.HashLength+common.IncarnationLength+common.HashLength, /* part3start */
-		)
-		//for historic data
-		historyCursor := newSplitCursor(
-			hB,
-			startkeyNoInc,
-			fixedbits-8*common.IncarnationLength,
-			common.HashLength,   /* part1end */
-			common.HashLength,   /* part2start */
-			common.HashLength*2, /* part3start */
-		)
-		addrHash, keyHash, _, v := mainCursor.Seek()
-		hAddrHash, hKeyHash, tsEnc, hV := historyCursor.Seek()
-		for hKeyHash != nil && binary.BigEndian.Uint64(tsEnc) < timestamp {
-			hAddrHash, hKeyHash, tsEnc, hV = historyCursor.Next()
-		}
-		goOn := true
-		var err error
-		for goOn {
-			var cmp int
-			if keyHash == nil {
-				if hKeyHash == nil {
-					break
-				} else {
-					cmp = 1
-				}
-			} else if hKeyHash == nil {
-				cmp = -1
-			} else {
-				cmp = bytes.Compare(keyHash, hKeyHash)
-			}
-			if cmp < 0 {
-				goOn, err = walker(addrHash, keyHash, v)
-			} else {
-				index := dbutils.WrapHistoryIndex(hV)
-				if changeSetBlock, set, ok := index.Search(timestamp); ok {
-					// set == true if this change was from empty record (non-existent storage item) to non-empty
-					// In such case, we do not need to examine changeSet and simply skip the record
-					if !set {
-						// Extract value from the changeSet
-						csKey := dbutils.EncodeTimestamp(changeSetBlock)
-						changeSetData, _ := csB.Get(csKey)
-						if changeSetData == nil {
-							return fmt.Errorf("could not find ChangeSet record for index entry %d (query timestamp %d)", changeSetBlock, timestamp)
-						}
-						data, err1 := changeset.StorageChangeSetBytes(changeSetData).FindWithoutIncarnation(hAddrHash, hKeyHash)
-						if err1 != nil {
-							return fmt.Errorf("could not find key %x%x in the ChangeSet record for index entry %d (query timestamp %d): %v",
-								hAddrHash, hKeyHash,
-								changeSetBlock,
-								timestamp,
-								err1,
-							)
-						}
-						if len(data) > 0 { // Skip deleted entries
-							goOn, err = walker(hAddrHash, hKeyHash, data)
-						}
-					}
-				} else if cmp == 0 {
-					goOn, err = walker(addrHash, keyHash, v)
-				}
-			}
-			if goOn {
-				if cmp <= 0 {
-					addrHash, keyHash, _, v = mainCursor.Next()
-				}
-				if cmp >= 0 {
-					hKeyHash0 := hKeyHash
-					for hKeyHash != nil && (bytes.Equal(hKeyHash0, hKeyHash) || binary.BigEndian.Uint64(tsEnc) < timestamp) {
-						hAddrHash, hKeyHash, tsEnc, hV = historyCursor.Next()
-					}
-				}
-			}
-		}
-		return err
-	})
-	return err
-}
-
-func (db *BoltDatabase) WalkAsOf(bucket, hBucket, startkey []byte, fixedbits int, timestamp uint64, walker func(k []byte, v []byte) (bool, error)) error {
-	//fmt.Printf("WalkAsOf %x %x %x %d %d\n", bucket, hBucket, startkey, fixedbits, timestamp)
-	if bytes.Equal(bucket, dbutils.CurrentStateBucket) && bytes.Equal(hBucket, dbutils.AccountsHistoryBucket) {
-		return db.walkAsOfThinAccounts(startkey, fixedbits, timestamp, walker)
-	} else if bytes.Equal(bucket, dbutils.CurrentStateBucket) && bytes.Equal(hBucket, dbutils.StorageHistoryBucket) {
-		return db.walkAsOfThinStorage(startkey, fixedbits, timestamp, func(k1, k2, v []byte) (bool, error) {
-			return walker(append(common.CopyBytes(k1), k2...), v)
-		})
-	}
-	panic("Not implemented for arbitrary buckets")
-}
-
 // Delete deletes the key from the queue and database
 func (db *BoltDatabase) Delete(bucket, key []byte) error {
 	// Execute the actual operation
@@ -748,77 +454,3 @@ func NewDatabaseWithFreezer(db Database, dir, suffix string) (Database, error) {
 	// FIXME: implement freezer in Turbo-Geth
 	return db, nil
 }
-
-func BoltDBFindByHistory(tx *bolt.Tx, hBucket []byte, key []byte, timestamp uint64) ([]byte, error) {
-	//check
-	hB := tx.Bucket(hBucket)
-	if hB == nil {
-		return nil, ErrKeyNotFound
-	}
-	var keyF []byte
-	if bytes.Equal(dbutils.StorageHistoryBucket, hBucket) {
-		keyF = make([]byte, len(key)-common.IncarnationLength)
-		copy(keyF, key[:common.HashLength])
-		copy(keyF[common.HashLength:], key[common.HashLength+common.IncarnationLength:])
-	} else {
-		keyF = common.CopyBytes(key)
-	}
-
-	c := hB.Cursor()
-	k, v := c.Seek(dbutils.IndexChunkKey(key, timestamp))
-	if !bytes.HasPrefix(k, keyF) {
-		return nil, ErrKeyNotFound
-	}
-	index := dbutils.WrapHistoryIndex(v)
-
-	changeSetBlock, set, ok := index.Search(timestamp)
-	if !ok {
-		return nil, ErrKeyNotFound
-	}
-	// set == true if this change was from empty record (non-existent account) to non-empty
-	// In such case, we do not need to examine changeSet and return empty data
-	if set {
-		return []byte{}, nil
-	}
-	csB := tx.Bucket(dbutils.ChangeSetByIndexBucket(hBucket))
-	if csB == nil {
-		return nil, ErrKeyNotFound
-	}
-
-	csKey := dbutils.EncodeTimestamp(changeSetBlock)
-	changeSetData, _ := csB.Get(csKey)
-
-	var (
-		data []byte
-		err  error
-	)
-	switch {
-	case bytes.Equal(dbutils.AccountsHistoryBucket, hBucket):
-		data, err = changeset.AccountChangeSetBytes(changeSetData).FindLast(key)
-	case bytes.Equal(dbutils.StorageHistoryBucket, hBucket):
-		data, err = changeset.StorageChangeSetBytes(changeSetData).FindWithoutIncarnation(key[:common.HashLength], key[common.HashLength+common.IncarnationLength:])
-	}
-	if err != nil {
-		return nil, ErrKeyNotFound
-	}
-
-	//restore codehash
-	if bytes.Equal(dbutils.AccountsHistoryBucket, hBucket) {
-		var acc accounts.Account
-		if err := acc.DecodeForStorage(data); err != nil {
-			return nil, err
-		}
-		if acc.Incarnation > 0 && acc.IsEmptyCodeHash() {
-			codeBucket := tx.Bucket(dbutils.ContractCodeBucket)
-			codeHash, _ := codeBucket.Get(dbutils.GenerateStoragePrefix(key, acc.Incarnation))
-			if len(codeHash) > 0 {
-				acc.CodeHash = common.BytesToHash(codeHash)
-			}
-			data = make([]byte, acc.EncodingLengthForStorage())
-			acc.EncodeForStorage(data)
-		}
-		return data, nil
-	}
-
-	return data, nil
-}
diff --git a/ethdb/history.go b/ethdb/history.go
new file mode 100644
index 0000000000..58fc2c0beb
--- /dev/null
+++ b/ethdb/history.go
@@ -0,0 +1,111 @@
+package ethdb
+
+import (
+	"bytes"
+	"context"
+
+	"github.com/ledgerwatch/turbo-geth/common"
+	"github.com/ledgerwatch/turbo-geth/common/changeset"
+	"github.com/ledgerwatch/turbo-geth/common/dbutils"
+	"github.com/ledgerwatch/turbo-geth/core/types/accounts"
+	"github.com/ledgerwatch/turbo-geth/log"
+)
+
+func GetAsOf(db KV, bucket, hBucket, key []byte, timestamp uint64) ([]byte, error) {
+	var dat []byte
+	err := db.View(context.Background(), func(tx Tx) error {
+		v, err := FindByHistory(tx, hBucket, key, timestamp)
+		if err != nil {
+			log.Debug("FindByHistory err", "err", err)
+		} else {
+			dat = make([]byte, len(v))
+			copy(dat, v)
+			return nil
+		}
+		{
+			v, _ := tx.Bucket(bucket).Get(key)
+			if v == nil {
+				return ErrKeyNotFound
+			}
+
+			dat = make([]byte, len(v))
+			copy(dat, v)
+			return nil
+		}
+	})
+	return dat, err
+}
+
+func FindByHistory(tx Tx, hBucket []byte, key []byte, timestamp uint64) ([]byte, error) {
+	//check
+	hB := tx.Bucket(hBucket)
+	if hB == nil {
+		return nil, ErrKeyNotFound
+	}
+	var keyF []byte
+	if bytes.Equal(dbutils.StorageHistoryBucket, hBucket) {
+		keyF = make([]byte, len(key)-common.IncarnationLength)
+		copy(keyF, key[:common.HashLength])
+		copy(keyF[common.HashLength:], key[common.HashLength+common.IncarnationLength:])
+	} else {
+		keyF = common.CopyBytes(key)
+	}
+
+	c := hB.Cursor()
+	k, v, err := c.Seek(dbutils.IndexChunkKey(key, timestamp))
+	if err != nil {
+		return nil, err
+	}
+	if !bytes.HasPrefix(k, keyF) {
+		return nil, ErrKeyNotFound
+	}
+	index := dbutils.WrapHistoryIndex(v)
+
+	changeSetBlock, set, ok := index.Search(timestamp)
+	if !ok {
+		return nil, ErrKeyNotFound
+	}
+	// set == true if this change was from empty record (non-existent account) to non-empty
+	// In such case, we do not need to examine changeSet and return empty data
+	if set {
+		return []byte{}, nil
+	}
+	csB := tx.Bucket(dbutils.ChangeSetByIndexBucket(hBucket))
+	if csB == nil {
+		return nil, ErrKeyNotFound
+	}
+
+	csKey := dbutils.EncodeTimestamp(changeSetBlock)
+	changeSetData, _ := csB.Get(csKey)
+
+	var data []byte
+	switch {
+	case bytes.Equal(dbutils.AccountsHistoryBucket, hBucket):
+		data, err = changeset.AccountChangeSetBytes(changeSetData).FindLast(key)
+	case bytes.Equal(dbutils.StorageHistoryBucket, hBucket):
+		data, err = changeset.StorageChangeSetBytes(changeSetData).FindWithoutIncarnation(key[:common.HashLength], key[common.HashLength+common.IncarnationLength:])
+	}
+	if err != nil {
+		return nil, ErrKeyNotFound
+	}
+
+	//restore codehash
+	if bytes.Equal(dbutils.AccountsHistoryBucket, hBucket) {
+		var acc accounts.Account
+		if err := acc.DecodeForStorage(data); err != nil {
+			return nil, err
+		}
+		if acc.Incarnation > 0 && acc.IsEmptyCodeHash() {
+			codeBucket := tx.Bucket(dbutils.ContractCodeBucket)
+			codeHash, _ := codeBucket.Get(dbutils.GenerateStoragePrefix(key, acc.Incarnation))
+			if len(codeHash) > 0 {
+				acc.CodeHash = common.BytesToHash(codeHash)
+			}
+			data = make([]byte, acc.EncodingLengthForStorage())
+			acc.EncodeForStorage(data)
+		}
+		return data, nil
+	}
+
+	return data, nil
+}
diff --git a/ethdb/interface.go b/ethdb/interface.go
index 3f554749f1..066b5ba5f4 100644
--- a/ethdb/interface.go
+++ b/ethdb/interface.go
@@ -42,10 +42,6 @@ type Getter interface {
 	// Key must contain 8byte inverted block number in the end.
 	GetIndexChunk(bucket, key []byte, timestamp uint64) ([]byte, error)
 
-	// GetAsOf returns the value valid as of a given timestamp.
-	// timestamp == block number
-	GetAsOf(bucket, hBucket, key []byte, timestamp uint64) ([]byte, error)
-
 	// Has indicates whether a key exists in the database.
 	Has(bucket, key []byte) (bool, error)
 
@@ -57,8 +53,6 @@ type Getter interface {
 
 	// MultiWalk is similar to multiple Walk calls folded into one.
 	MultiWalk(bucket []byte, startkeys [][]byte, fixedbits []int, walker func(int, []byte, []byte) error) error
-
-	WalkAsOf(bucket, hBucket, startkey []byte, fixedbits int, timestamp uint64, walker func([]byte, []byte) (bool, error)) error
 }
 
 type GetterPutter interface {
diff --git a/ethdb/kv_abstract.go b/ethdb/kv_abstract.go
index c1f15a5280..58073413d4 100644
--- a/ethdb/kv_abstract.go
+++ b/ethdb/kv_abstract.go
@@ -5,8 +5,8 @@ import (
 )
 
 type KV interface {
-	View(ctx context.Context, f func(tx Tx) error) (err error)
-	Update(ctx context.Context, f func(tx Tx) error) (err error)
+	View(ctx context.Context, f func(tx Tx) error) error
+	Update(ctx context.Context, f func(tx Tx) error) error
 	Close()
 
 	Begin(ctx context.Context, writable bool) (Tx, error)
diff --git a/ethdb/kv_bolt.go b/ethdb/kv_bolt.go
index 33a58ce74f..4efd135e5e 100644
--- a/ethdb/kv_bolt.go
+++ b/ethdb/kv_bolt.go
@@ -64,21 +64,21 @@ func (opts boltOpts) Path(path string) boltOpts {
 	return opts
 }
 
-func (opts boltOpts) Open(ctx context.Context) (db KV, err error) {
-	boltDB, err := bolt.Open(opts.path, 0600, opts.Bolt)
-	if err != nil {
-		return nil, err
-	}
-	if err := boltDB.Update(func(tx *bolt.Tx) error {
-		for _, name := range dbutils.Buckets {
-			_, createErr := tx.CreateBucketIfNotExists(name, false)
-			if createErr != nil {
-				return createErr
+// WrapBoltDb provides a way for the code to gradually migrate
+// to the abstract interface
+func (opts boltOpts) WrapBoltDb(boltDB *bolt.DB) (db KV, err error) {
+	if !opts.Bolt.ReadOnly {
+		if err := boltDB.Update(func(tx *bolt.Tx) error {
+			for _, name := range dbutils.Buckets {
+				_, createErr := tx.CreateBucketIfNotExists(name, false)
+				if createErr != nil {
+					return createErr
+				}
 			}
+			return nil
+		}); err != nil {
+			return nil, err
 		}
-		return nil
-	}); err != nil {
-		return nil, err
 	}
 	return &BoltKV{
 		opts: opts,
@@ -87,6 +87,14 @@ func (opts boltOpts) Open(ctx context.Context) (db KV, err error) {
 	}, nil
 }
 
+func (opts boltOpts) Open(ctx context.Context) (db KV, err error) {
+	boltDB, err := bolt.Open(opts.path, 0600, opts.Bolt)
+	if err != nil {
+		return nil, err
+	}
+	return opts.WrapBoltDb(boltDB)
+}
+
 func (opts boltOpts) MustOpen(ctx context.Context) KV {
 	db, err := opts.Open(ctx)
 	if err != nil {
diff --git a/ethdb/mutation.go b/ethdb/mutation.go
index 4b96ca04a6..234d41d385 100644
--- a/ethdb/mutation.go
+++ b/ethdb/mutation.go
@@ -121,11 +121,6 @@ func (m *mutation) IdealBatchSize() int {
 	return m.db.IdealBatchSize()
 }
 
-func (m *mutation) GetAsOf(bucket, hBucket, key []byte, timestamp uint64) ([]byte, error) {
-	m.panicOnEmptyDB()
-	return m.db.GetAsOf(bucket, hBucket, key, timestamp)
-}
-
 // WARNING: Merged mem/DB walk is not implemented
 func (m *mutation) Walk(bucket, startkey []byte, fixedbits int, walker func([]byte, []byte) (bool, error)) error {
 	m.panicOnEmptyDB()
@@ -138,11 +133,6 @@ func (m *mutation) MultiWalk(bucket []byte, startkeys [][]byte, fixedbits []int,
 	return m.db.MultiWalk(bucket, startkeys, fixedbits, walker)
 }
 
-func (m *mutation) WalkAsOf(bucket, hBucket, startkey []byte, fixedbits int, timestamp uint64, walker func([]byte, []byte) (bool, error)) error {
-	m.panicOnEmptyDB()
-	return m.db.WalkAsOf(bucket, hBucket, startkey, fixedbits, timestamp, walker)
-}
-
 func (m *mutation) Delete(bucket, key []byte) error {
 	m.mu.Lock()
 	defer m.mu.Unlock()
@@ -271,10 +261,6 @@ func (d *RWCounterDecorator) Get(bucket, key []byte) ([]byte, error) {
 	return d.Database.Get(bucket, key)
 }
 
-func (d *RWCounterDecorator) GetAsOf(bucket, hBucket, key []byte, timestamp uint64) ([]byte, error) {
-	atomic.AddUint64(&d.DBCounterStats.GetAsOf, 1)
-	return d.Database.GetAsOf(bucket, hBucket, key, timestamp)
-}
 func (d *RWCounterDecorator) Has(bucket, key []byte) (bool, error) {
 	atomic.AddUint64(&d.DBCounterStats.Has, 1)
 	return d.Database.Has(bucket, key)
@@ -287,10 +273,6 @@ func (d *RWCounterDecorator) MultiWalk(bucket []byte, startkeys [][]byte, fixedb
 	atomic.AddUint64(&d.DBCounterStats.MultiWalk, 1)
 	return d.Database.MultiWalk(bucket, startkeys, fixedbits, walker)
 }
-func (d *RWCounterDecorator) WalkAsOf(bucket, hBucket, startkey []byte, fixedbits int, timestamp uint64, walker func([]byte, []byte) (bool, error)) error {
-	atomic.AddUint64(&d.DBCounterStats.WalkAsOf, 1)
-	return d.Database.WalkAsOf(bucket, hBucket, startkey, fixedbits, timestamp, walker)
-}
 func (d *RWCounterDecorator) Delete(bucket, key []byte) error {
 	atomic.AddUint64(&d.DBCounterStats.Delete, 1)
 	return d.Database.Delete(bucket, key)
diff --git a/ethdb/remote_bolt_db.go b/ethdb/remote_bolt_db.go
index 4c2e55771f..4704ec7937 100644
--- a/ethdb/remote_bolt_db.go
+++ b/ethdb/remote_bolt_db.go
@@ -129,47 +129,6 @@ func (db *RemoteBoltDatabase) GetIndexChunk(bucket, key []byte, timestamp uint64
 	return dat, err
 }
 
-// GetAsOf returns the value valid as of a given timestamp.
-//func (db *RemoteBoltDatabase) GetAsOf(bucket, hBucket, key []byte, timestamp uint64) ([]byte, error) {
-//	return db.db.CmdGetAsOf(context.Background(), bucket, hBucket, key, timestamp)
-//}
-
-// GetAsOf returns the value valid as of a given timestamp.
-func (db *RemoteBoltDatabase) GetAsOf(bucket, hBucket, key []byte, timestamp uint64) ([]byte, error) {
-	composite, _ := dbutils.CompositeKeySuffix(key, timestamp)
-	var dat []byte
-	err := db.db.View(context.Background(), func(tx Tx) error {
-		{
-			hK, hV, err := tx.Bucket(hBucket).Cursor().Seek(composite)
-			if err != nil {
-				return err
-			}
-
-			if hK != nil && bytes.HasPrefix(hK, key) {
-				dat = make([]byte, len(hV))
-				copy(dat, hV)
-				return nil
-			}
-		}
-		{
-			v, err := tx.Bucket(bucket).Get(key)
-			if err != nil {
-				return err
-			}
-			if v == nil {
-				return ErrKeyNotFound
-			}
-
-			dat = make([]byte, len(v))
-			copy(dat, v)
-			return nil
-		}
-
-		return ErrKeyNotFound
-	})
-	return dat, err
-}
-
 func (db *RemoteBoltDatabase) Walk(bucket, startkey []byte, fixedbits int, walker func(k, v []byte) (bool, error)) error {
 	fixedbytes, mask := Bytesmask(fixedbits)
 	err := db.db.View(context.Background(), func(tx Tx) error {
diff --git a/ethdb/walk.go b/ethdb/walk.go
index cb633e1dce..af506b6a21 100644
--- a/ethdb/walk.go
+++ b/ethdb/walk.go
@@ -17,6 +17,9 @@
 package ethdb
 
 import (
+	"bytes"
+	"context"
+	"encoding/binary"
 	"fmt"
 
 	"github.com/ledgerwatch/turbo-geth/common/changeset"
@@ -25,8 +28,331 @@ import (
 	"github.com/ledgerwatch/turbo-geth/common/dbutils"
 )
 
-var EndSuffix = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+func walkAsOfThinAccounts(db KV, startkey []byte, fixedbits int, timestamp uint64, walker func(k []byte, v []byte) (bool, error)) error {
+	fixedbytes, mask := Bytesmask(fixedbits)
+	err := db.View(context.Background(), func(tx Tx) error {
+		b := tx.Bucket(dbutils.CurrentStateBucket)
+		if b == nil {
+			return fmt.Errorf("currentStateBucket not found")
+		}
+		hB := tx.Bucket(dbutils.AccountsHistoryBucket)
+		if hB == nil {
+			return fmt.Errorf("accountsHistoryBucket not found")
+		}
+		csB := tx.Bucket(dbutils.AccountChangeSetBucket)
+		if csB == nil {
+			return fmt.Errorf("accountChangeBucket not found")
+		}
+		//for state
+		mainCursor := b.Cursor()
+		//for historic data
+		historyCursor := newSplitCursor(
+			hB,
+			startkey,
+			fixedbits,
+			common.HashLength,   /* part1end */
+			common.HashLength,   /* part2start */
+			common.HashLength+8, /* part3start */
+		)
+		k, v, err1 := mainCursor.Seek(startkey)
+		if err1 != nil {
+			return err1
+		}
+		for k != nil && len(k) > common.HashLength {
+			k, v, err1 = mainCursor.Next()
+			if err1 != nil {
+				return err1
+			}
+		}
+		hK, tsEnc, _, hV, err1 := historyCursor.Seek()
+		if err1 != nil {
+			return err1
+		}
+		for hK != nil && binary.BigEndian.Uint64(tsEnc) < timestamp {
+			hK, tsEnc, _, hV, err1 = historyCursor.Next()
+			if err1 != nil {
+				return err1
+			}
+		}
+		goOn := true
+		var err error
+		for goOn {
+			//exit or next conditions
+			if k != nil && fixedbits > 0 && !bytes.Equal(k[:fixedbytes-1], startkey[:fixedbytes-1]) {
+				k = nil
+			}
+			if k != nil && fixedbits > 0 && (k[fixedbytes-1]&mask) != (startkey[fixedbytes-1]&mask) {
+				k = nil
+			}
+			var cmp int
+			if k == nil {
+				if hK == nil {
+					break
+				} else {
+					cmp = 1
+				}
+			} else if hK == nil {
+				cmp = -1
+			} else {
+				cmp = bytes.Compare(k, hK)
+			}
+			if cmp < 0 {
+				goOn, err = walker(k, v)
+			} else {
+				index := dbutils.WrapHistoryIndex(hV)
+				if changeSetBlock, set, ok := index.Search(timestamp); ok {
+					// set == true if this change was from empty record (non-existent account) to non-empty
+					// In such case, we do not need to examine changeSet and simply skip the record
+					if !set {
+						// Extract value from the changeSet
+						csKey := dbutils.EncodeTimestamp(changeSetBlock)
+						changeSetData, _ := csB.Get(csKey)
+						if changeSetData == nil {
+							return fmt.Errorf("could not find ChangeSet record for index entry %d (query timestamp %d)", changeSetBlock, timestamp)
+						}
+						data, err2 := changeset.AccountChangeSetBytes(changeSetData).FindLast(hK)
+						if err2 != nil {
+							return fmt.Errorf("could not find key %x in the ChangeSet record for index entry %d (query timestamp %d): %v",
+								hK,
+								changeSetBlock,
+								timestamp,
+								err2,
+							)
+						}
+						if len(data) > 0 { // Skip accounts did not exist
+							goOn, err = walker(hK, data)
+						}
+					}
+				} else if cmp == 0 {
+					goOn, err = walker(k, v)
+				}
+			}
+			if goOn {
+				if cmp <= 0 {
+					k, v, err1 = mainCursor.Next()
+					if err1 != nil {
+						return err1
+					}
+					for k != nil && len(k) > common.HashLength {
+						k, v, err1 = mainCursor.Next()
+						if err1 != nil {
+							return err1
+						}
+					}
+				}
+				if cmp >= 0 {
+					hK0 := hK
+					for hK != nil && (bytes.Equal(hK0, hK) || binary.BigEndian.Uint64(tsEnc) < timestamp) {
+						hK, tsEnc, _, hV, err1 = historyCursor.Next()
+						if err1 != nil {
+							return err1
+						}
+					}
+				}
+			}
+		}
+		return err
+	})
+	return err
+}
 
+// splitCursor implements cursor with two keys
+// it is used to ignore incarnations in the middle
+// of composite storage key, but without
+// reconstructing the key
+// Instead, the key is split into two parts and
+// functions `Seek` and `Next` deliver both
+// parts as well as the corresponding value
+type splitCursor struct {
+	c          Cursor // Unlerlying bolt cursor
+	startkey   []byte // Starting key (also contains bits that need to be preserved)
+	matchBytes int
+	mask       uint8
+	part1end   int // Position in the key where the first part ends
+	part2start int // Position in the key where the second part starts
+	part3start int // Position in the key where the third part starts
+}
+
+func newSplitCursor(b Bucket, startkey []byte, matchBits int, part1end, part2start, part3start int) *splitCursor {
+	var sc splitCursor
+	sc.c = b.Cursor()
+	sc.startkey = startkey
+	sc.part1end = part1end
+	sc.part2start = part2start
+	sc.part3start = part3start
+	sc.matchBytes, sc.mask = Bytesmask(matchBits)
+	return &sc
+}
+
+func (sc *splitCursor) matchKey(k []byte) bool {
+	if k == nil {
+		return false
+	}
+	if sc.matchBytes == 0 {
+		return true
+	}
+	if len(k) < sc.matchBytes {
+		return false
+	}
+	if !bytes.Equal(k[:sc.matchBytes-1], sc.startkey[:sc.matchBytes-1]) {
+		return false
+	}
+	return (k[sc.matchBytes-1] & sc.mask) == (sc.startkey[sc.matchBytes-1] & sc.mask)
+}
+
+func (sc *splitCursor) Seek() (key1, key2, key3, val []byte, err error) {
+	k, v, err1 := sc.c.Seek(sc.startkey)
+	if err1 != nil {
+		return nil, nil, nil, nil, err1
+	}
+	if !sc.matchKey(k) {
+		return nil, nil, nil, nil, nil
+	}
+	return k[:sc.part1end], k[sc.part2start:sc.part3start], k[sc.part3start:], v, nil
+}
+
+func (sc *splitCursor) Next() (key1, key2, key3, val []byte, err error) {
+	k, v, err1 := sc.c.Next()
+	if err1 != nil {
+		return nil, nil, nil, nil, err1
+	}
+	if !sc.matchKey(k) {
+		return nil, nil, nil, nil, nil
+	}
+	return k[:sc.part1end], k[sc.part2start:sc.part3start], k[sc.part3start:], v, nil
+}
+
+func walkAsOfThinStorage(db KV, startkey []byte, fixedbits int, timestamp uint64, walker func(k1, k2, v []byte) (bool, error)) error {
+	err := db.View(context.Background(), func(tx Tx) error {
+		b := tx.Bucket(dbutils.CurrentStateBucket)
+		if b == nil {
+			return fmt.Errorf("storageBucket not found")
+		}
+		hB := tx.Bucket(dbutils.StorageHistoryBucket)
+		if hB == nil {
+			return fmt.Errorf("storageHistoryBucket not found")
+		}
+		csB := tx.Bucket(dbutils.StorageChangeSetBucket)
+		if csB == nil {
+			return fmt.Errorf("storageChangeBucket not found")
+		}
+		startkeyNoInc := make([]byte, len(startkey)-common.IncarnationLength)
+		copy(startkeyNoInc, startkey[:common.HashLength])
+		copy(startkeyNoInc[common.HashLength:], startkey[common.HashLength+common.IncarnationLength:])
+		//for storage
+		mainCursor := newSplitCursor(
+			b,
+			startkey,
+			fixedbits,
+			common.HashLength, /* part1end */
+			common.HashLength+common.IncarnationLength,                   /* part2start */
+			common.HashLength+common.IncarnationLength+common.HashLength, /* part3start */
+		)
+		//for historic data
+		historyCursor := newSplitCursor(
+			hB,
+			startkeyNoInc,
+			fixedbits-8*common.IncarnationLength,
+			common.HashLength,   /* part1end */
+			common.HashLength,   /* part2start */
+			common.HashLength*2, /* part3start */
+		)
+		addrHash, keyHash, _, v, err1 := mainCursor.Seek()
+		if err1 != nil {
+			return err1
+		}
+		hAddrHash, hKeyHash, tsEnc, hV, err2 := historyCursor.Seek()
+		if err2 != nil {
+			return err2
+		}
+		for hKeyHash != nil && binary.BigEndian.Uint64(tsEnc) < timestamp {
+			hAddrHash, hKeyHash, tsEnc, hV, err2 = historyCursor.Next()
+			if err2 != nil {
+				return err2
+			}
+		}
+		goOn := true
+		var err error
+		for goOn {
+			var cmp int
+			if keyHash == nil {
+				if hKeyHash == nil {
+					break
+				} else {
+					cmp = 1
+				}
+			} else if hKeyHash == nil {
+				cmp = -1
+			} else {
+				cmp = bytes.Compare(keyHash, hKeyHash)
+			}
+			if cmp < 0 {
+				goOn, err = walker(addrHash, keyHash, v)
+			} else {
+				index := dbutils.WrapHistoryIndex(hV)
+				if changeSetBlock, set, ok := index.Search(timestamp); ok {
+					// set == true if this change was from empty record (non-existent storage item) to non-empty
+					// In such case, we do not need to examine changeSet and simply skip the record
+					if !set {
+						// Extract value from the changeSet
+						csKey := dbutils.EncodeTimestamp(changeSetBlock)
+						changeSetData, _ := csB.Get(csKey)
+						if changeSetData == nil {
+							return fmt.Errorf("could not find ChangeSet record for index entry %d (query timestamp %d)", changeSetBlock, timestamp)
+						}
+						data, err3 := changeset.StorageChangeSetBytes(changeSetData).FindWithoutIncarnation(hAddrHash, hKeyHash)
+						if err3 != nil {
+							return fmt.Errorf("could not find key %x%x in the ChangeSet record for index entry %d (query timestamp %d): %v",
+								hAddrHash, hKeyHash,
+								changeSetBlock,
+								timestamp,
+								err3,
+							)
+						}
+						if len(data) > 0 { // Skip deleted entries
+							goOn, err = walker(hAddrHash, hKeyHash, data)
+						}
+					}
+				} else if cmp == 0 {
+					goOn, err = walker(addrHash, keyHash, v)
+				}
+			}
+			if goOn {
+				if cmp <= 0 {
+					addrHash, keyHash, _, v, err1 = mainCursor.Next()
+					if err1 != nil {
+						return err1
+					}
+				}
+				if cmp >= 0 {
+					hKeyHash0 := hKeyHash
+					for hKeyHash != nil && (bytes.Equal(hKeyHash0, hKeyHash) || binary.BigEndian.Uint64(tsEnc) < timestamp) {
+						hAddrHash, hKeyHash, tsEnc, hV, err2 = historyCursor.Next()
+						if err2 != nil {
+							return err2
+						}
+					}
+				}
+			}
+		}
+		return err
+	})
+	return err
+}
+
+func WalkAsOf(db KV, bucket, hBucket, startkey []byte, fixedbits int, timestamp uint64, walker func(k []byte, v []byte) (bool, error)) error {
+	//fmt.Printf("WalkAsOf %x %x %x %d %d\n", bucket, hBucket, startkey, fixedbits, timestamp)
+	if bytes.Equal(bucket, dbutils.CurrentStateBucket) && bytes.Equal(hBucket, dbutils.AccountsHistoryBucket) {
+		return walkAsOfThinAccounts(db, startkey, fixedbits, timestamp, walker)
+	} else if bytes.Equal(bucket, dbutils.CurrentStateBucket) && bytes.Equal(hBucket, dbutils.StorageHistoryBucket) {
+		return walkAsOfThinStorage(db, startkey, fixedbits, timestamp, func(k1, k2, v []byte) (bool, error) {
+			return walker(append(common.CopyBytes(k1), k2...), v)
+		})
+	}
+	panic("Not implemented for arbitrary buckets")
+}
+
+var EndSuffix = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
 
 func GetModifiedAccounts(db Getter, startTimestamp, endTimestamp uint64) ([]common.Address, error) {
 	keys := make(map[common.Hash]struct{})
diff --git a/miner/worker.go b/miner/worker.go
index b471f17297..4f91e52fc3 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -1179,10 +1179,7 @@ func GetState(blockchain *core.BlockChain, parent *types.Block) (*state.IntraBlo
 	tds.SetResolveReads(false)
 	tds.SetNoHistory(true)
 
-	statedb, _, err := blockchain.StateAt(parent.NumberU64())
-	if err != nil {
-		return nil, nil, err
-	}
+	statedb := state.New(tds)
 
 	return statedb, tds, nil
 }
diff --git a/tests/block_test_util.go b/tests/block_test_util.go
index 4d91e7881a..3ae9a64f5f 100644
--- a/tests/block_test_util.go
+++ b/tests/block_test_util.go
@@ -133,10 +133,7 @@ func (t *BlockTest) Run(_ bool) error {
 	if common.Hash(t.json.BestBlock) != cmlast {
 		return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.json.BestBlock, cmlast)
 	}
-	newDB, _, err := chain.State()
-	if err != nil {
-		return err
-	}
+	newDB := state.New(state.NewDbState(db.AbstractKV(), chain.CurrentBlock().NumberU64()))
 	if err = t.validatePostState(newDB); err != nil {
 		return fmt.Errorf("post state validation failed: %v", err)
 	}
diff --git a/tests/statedb_chain_test.go b/tests/statedb_chain_test.go
index 02451b3173..776cfe6563 100644
--- a/tests/statedb_chain_test.go
+++ b/tests/statedb_chain_test.go
@@ -26,6 +26,7 @@ import (
 	"github.com/ledgerwatch/turbo-geth/common"
 	"github.com/ledgerwatch/turbo-geth/consensus/ethash"
 	"github.com/ledgerwatch/turbo-geth/core"
+	"github.com/ledgerwatch/turbo-geth/core/state"
 	"github.com/ledgerwatch/turbo-geth/core/types"
 	"github.com/ledgerwatch/turbo-geth/core/vm"
 	"github.com/ledgerwatch/turbo-geth/crypto"
@@ -101,7 +102,7 @@ func TestSelfDestructReceive(t *testing.T) {
 		contractBackend.Commit()
 	})
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(address) {
 		t.Error("expected account to exist")
 	}
@@ -130,7 +131,7 @@ func TestSelfDestructReceive(t *testing.T) {
 	// and that means that the state of the accounts written in the first block was correct.
 	// This test checks that the storage root of the account is properly set to the root of the empty tree
 
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(db.AbstractKV(), blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(address) {
 		t.Error("expected account to exist")
 	}
diff --git a/tests/statedb_insert_chain_transaction_test.go b/tests/statedb_insert_chain_transaction_test.go
index eddc63198c..d65107540e 100644
--- a/tests/statedb_insert_chain_transaction_test.go
+++ b/tests/statedb_insert_chain_transaction_test.go
@@ -12,6 +12,7 @@ import (
 	"github.com/ledgerwatch/turbo-geth/common"
 	"github.com/ledgerwatch/turbo-geth/consensus/ethash"
 	"github.com/ledgerwatch/turbo-geth/core"
+	"github.com/ledgerwatch/turbo-geth/core/state"
 	"github.com/ledgerwatch/turbo-geth/core/types"
 	"github.com/ledgerwatch/turbo-geth/core/vm"
 	"github.com/ledgerwatch/turbo-geth/crypto"
@@ -26,7 +27,7 @@ func TestInsertIncorrectStateRootDifferentAccounts(t *testing.T) {
 	fromKey := data.keys[0]
 	to := common.Address{1}
 
-	blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
+	blockchain, _, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
 		0: {
 			getBlockTx(from, to, big.NewInt(1000)),
 			fromKey,
@@ -54,7 +55,8 @@ func TestInsertIncorrectStateRootDifferentAccounts(t *testing.T) {
 	}
 
 	// insert a correct block
-	blockchain, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{
+	var kv ethdb.KV
+	blockchain, kv, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{
 		0: {
 			getBlockTx(data.addresses[1], to, big.NewInt(5000)),
 			data.keys[1],
@@ -68,7 +70,7 @@ func TestInsertIncorrectStateRootDifferentAccounts(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(kv, blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(to) {
 		t.Error("expected account to exist")
 	}
@@ -90,7 +92,7 @@ func TestInsertIncorrectStateRootSameAccount(t *testing.T) {
 	fromKey := data.keys[0]
 	to := common.Address{1}
 
-	blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
+	blockchain, _, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
 		0: {
 			getBlockTx(from, to, big.NewInt(1000)),
 			fromKey,
@@ -118,7 +120,8 @@ func TestInsertIncorrectStateRootSameAccount(t *testing.T) {
 	}
 
 	// insert a correct block
-	blockchain, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{
+	var kv ethdb.KV
+	blockchain, kv, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{
 		0: {
 			getBlockTx(from, to, big.NewInt(5000)),
 			fromKey,
@@ -132,7 +135,7 @@ func TestInsertIncorrectStateRootSameAccount(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(kv, blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(to) {
 		t.Error("expected account to exist")
 	}
@@ -151,7 +154,7 @@ func TestInsertIncorrectStateRootSameAccountSameAmount(t *testing.T) {
 	fromKey := data.keys[0]
 	to := common.Address{1}
 
-	blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
+	blockchain, _, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
 		0: {
 			getBlockTx(from, to, big.NewInt(1000)),
 			fromKey,
@@ -176,7 +179,8 @@ func TestInsertIncorrectStateRootSameAccountSameAmount(t *testing.T) {
 	}
 
 	// insert a correct block
-	blockchain, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{
+	var kv ethdb.KV
+	blockchain, kv, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{
 		0: {
 			getBlockTx(from, to, big.NewInt(1000)),
 			fromKey,
@@ -190,7 +194,7 @@ func TestInsertIncorrectStateRootSameAccountSameAmount(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(kv, blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(to) {
 		t.Error("expected account to exist")
 	}
@@ -209,7 +213,7 @@ func TestInsertIncorrectStateRootAllFundsRoot(t *testing.T) {
 	fromKey := data.keys[0]
 	to := common.Address{1}
 
-	blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
+	blockchain, _, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
 		0: {
 			getBlockTx(from, to, big.NewInt(1000)),
 			fromKey,
@@ -234,7 +238,8 @@ func TestInsertIncorrectStateRootAllFundsRoot(t *testing.T) {
 	}
 
 	// insert a correct block
-	blockchain, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{
+	var kv ethdb.KV
+	blockchain, kv, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{
 		0: {
 			getBlockTx(from, to, big.NewInt(1000)),
 			fromKey,
@@ -248,7 +253,7 @@ func TestInsertIncorrectStateRootAllFundsRoot(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(kv, blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(to) {
 		t.Error("expected account to exist")
 	}
@@ -267,7 +272,7 @@ func TestInsertIncorrectStateRootAllFunds(t *testing.T) {
 	fromKey := data.keys[0]
 	to := common.Address{1}
 
-	blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
+	blockchain, _, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
 		0: {
 			getBlockTx(from, to, big.NewInt(3000)),
 			fromKey,
@@ -291,7 +296,8 @@ func TestInsertIncorrectStateRootAllFunds(t *testing.T) {
 	}
 
 	// insert a correct block
-	blockchain, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{
+	var kv ethdb.KV
+	blockchain, kv, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{
 		0: {
 			getBlockTx(from, to, big.NewInt(1000)),
 			fromKey,
@@ -305,7 +311,7 @@ func TestInsertIncorrectStateRootAllFunds(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(kv, blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(to) {
 		t.Error("expected account to exist")
 	}
@@ -327,7 +333,7 @@ func TestAccountDeployIncorrectRoot(t *testing.T) {
 	var contractAddress common.Address
 	eipContract := new(contracts.Testcontract)
 
-	blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
+	blockchain, kv, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
 		0: {
 			getBlockTx(from, to, big.NewInt(10)),
 			fromKey,
@@ -346,7 +352,7 @@ func TestAccountDeployIncorrectRoot(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(kv, blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(from) {
 		t.Error("expected account to exist")
 	}
@@ -364,7 +370,7 @@ func TestAccountDeployIncorrectRoot(t *testing.T) {
 		t.Fatal("should fail")
 	}
 
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(kv, blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(from) {
 		t.Error("expected account to exist")
 	}
@@ -378,7 +384,7 @@ func TestAccountDeployIncorrectRoot(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(kv, blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(from) {
 		t.Error("expected account to exist")
 	}
@@ -397,7 +403,7 @@ func TestAccountCreateIncorrectRoot(t *testing.T) {
 	var contractAddress common.Address
 	eipContract := new(contracts.Testcontract)
 
-	blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
+	blockchain, kv, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
 		0: {
 			getBlockTx(from, to, big.NewInt(10)),
 			fromKey,
@@ -420,7 +426,7 @@ func TestAccountCreateIncorrectRoot(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(kv, blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(from) {
 		t.Error("expected account to exist")
 	}
@@ -434,7 +440,7 @@ func TestAccountCreateIncorrectRoot(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(kv, blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(from) {
 		t.Error("expected account to exist")
 	}
@@ -467,7 +473,7 @@ func TestAccountUpdateIncorrectRoot(t *testing.T) {
 	var contractAddress common.Address
 	eipContract := new(contracts.Testcontract)
 
-	blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
+	blockchain, kv, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
 		0: {
 			getBlockTx(from, to, big.NewInt(10)),
 			fromKey,
@@ -494,7 +500,7 @@ func TestAccountUpdateIncorrectRoot(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(kv, blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(from) {
 		t.Error("expected account to exist")
 	}
@@ -508,7 +514,7 @@ func TestAccountUpdateIncorrectRoot(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(kv, blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(from) {
 		t.Error("expected account to exist")
 	}
@@ -546,7 +552,7 @@ func TestAccountDeleteIncorrectRoot(t *testing.T) {
 	var contractAddress common.Address
 	eipContract := new(contracts.Testcontract)
 
-	blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
+	blockchain, kv, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{
 		0: {
 			getBlockTx(from, to, big.NewInt(10)),
 			fromKey,
@@ -573,7 +579,7 @@ func TestAccountDeleteIncorrectRoot(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ := blockchain.State()
+	st := state.New(state.NewDbState(kv, blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(from) {
 		t.Error("expected account to exist")
 	}
@@ -587,7 +593,7 @@ func TestAccountDeleteIncorrectRoot(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	st, _, _ = blockchain.State()
+	st = state.New(state.NewDbState(kv, blockchain.CurrentBlock().NumberU64()))
 	if !st.Exist(from) {
 		t.Error("expected account to exist")
 	}
@@ -669,7 +675,7 @@ type tx struct {
 	key  *ecdsa.PrivateKey
 }
 
-func genBlocks(gspec *core.Genesis, txs map[int]tx) (*core.BlockChain, []*types.Block, []types.Receipts, error) {
+func genBlocks(gspec *core.Genesis, txs map[int]tx) (*core.BlockChain, ethdb.KV, []*types.Block, []types.Receipts, error) {
 	engine := ethash.NewFaker()
 	db := ethdb.NewMemDatabase()
 	genesis := gspec.MustCommit(db)
@@ -677,7 +683,7 @@ func genBlocks(gspec *core.Genesis, txs map[int]tx) (*core.BlockChain, []*types.
 
 	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
 	if err != nil {
-		return nil, nil, nil, err
+		return nil, nil, nil, nil, err
 	}
 	blockchain.EnableReceipts(true)
 
@@ -715,10 +721,10 @@ func genBlocks(gspec *core.Genesis, txs map[int]tx) (*core.BlockChain, []*types.
 	})
 
 	if err != nil {
-		err = fmt.Errorf("block %d, error %v", blockNumber, err)
+		return nil, nil, nil, nil, fmt.Errorf("block %d, error %v", blockNumber, err)
 	}
 
-	return blockchain, blocks, receipts, err
+	return blockchain, db.AbstractKV(), blocks, receipts, err
 }
 
 type blockTx func(_ *core.BlockGen, backend bind.ContractBackend) (*types.Transaction, bool)
-- 
GitLab