From a600dab7e58a72968f111f8430c0e92a0511bc9c Mon Sep 17 00:00:00 2001
From: gary rong <garyrong0905@gmail.com>
Date: Wed, 7 Apr 2021 15:30:26 +0800
Subject: [PATCH] eth, les: fix tracers (#22473)

* eth, les: fix tracer

* eth: isolate live trie database in tracer

* eth: fix nil

* eth: fix

* eth, les: add checkLive param

* eth/tracer: fix
---
 eth/api.go              |   3 +-
 eth/api_backend.go      |  10 +-
 eth/state_accessor.go   | 207 ++++++++++++++--------------------------
 eth/tracers/api.go      |  96 +++++++++++--------
 eth/tracers/api_test.go |  36 ++-----
 les/api_backend.go      |   8 +-
 les/state_accessor.go   |  36 +++----
 7 files changed, 155 insertions(+), 241 deletions(-)

diff --git a/eth/api.go b/eth/api.go
index fb51e78c1..e02c0ca4d 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -407,11 +407,10 @@ func (api *PrivateDebugAPI) StorageRangeAt(blockHash common.Hash, txIndex int, c
 	if block == nil {
 		return StorageRangeResult{}, fmt.Errorf("block %#x not found", blockHash)
 	}
-	_, _, statedb, release, err := api.eth.stateAtTransaction(block, txIndex, 0)
+	_, _, statedb, err := api.eth.stateAtTransaction(block, txIndex, 0)
 	if err != nil {
 		return StorageRangeResult{}, err
 	}
-	defer release()
 	st := statedb.StorageTrie(contractAddress)
 	if st == nil {
 		return StorageRangeResult{}, fmt.Errorf("account %x doesn't exist", contractAddress)
diff --git a/eth/api_backend.go b/eth/api_backend.go
index 2569972e5..cc780775c 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -332,14 +332,10 @@ func (b *EthAPIBackend) StartMining(threads int) error {
 	return b.eth.StartMining(threads)
 }
 
-func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64) (*state.StateDB, func(), error) {
-	return b.eth.stateAtBlock(block, reexec)
+func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) {
+	return b.eth.stateAtBlock(block, reexec, base, checkLive)
 }
 
-func (b *EthAPIBackend) StatesInRange(ctx context.Context, fromBlock *types.Block, toBlock *types.Block, reexec uint64) ([]*state.StateDB, func(), error) {
-	return b.eth.statesInRange(fromBlock, toBlock, reexec)
-}
-
-func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, func(), error) {
+func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) {
 	return b.eth.stateAtTransaction(block, txIndex, reexec)
 }
diff --git a/eth/state_accessor.go b/eth/state_accessor.go
index cbbd9a820..d7564a084 100644
--- a/eth/state_accessor.go
+++ b/eth/state_accessor.go
@@ -31,39 +31,58 @@ import (
 )
 
 // stateAtBlock 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 (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64) (statedb *state.StateDB, release func(), err error) {
-	// If we have the state fully available, use that
-	statedb, err = eth.blockchain.StateAt(block.Root())
-	if err == nil {
-		return statedb, func() {}, nil
+// If no state is locally available for the given block, a number of blocks
+// are attempted to be reexecuted to generate the desired state. The optional
+// base layer statedb can be passed then it's regarded as the statedb of the
+// parent block.
+func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (statedb *state.StateDB, err error) {
+	var (
+		current  *types.Block
+		database state.Database
+		report   = true
+		origin   = block.NumberU64()
+	)
+	// Check the live database first if we have the state fully available, use that.
+	if checkLive {
+		statedb, err = eth.blockchain.StateAt(block.Root())
+		if err == nil {
+			return statedb, nil
+		}
 	}
-	// Otherwise try to reexec blocks until we find a state or reach our limit
-	origin := block.NumberU64()
-	database := state.NewDatabaseWithConfig(eth.chainDb, &trie.Config{Cache: 16, Preimages: true})
+	if base != nil {
+		// The optional base statedb is given, mark the start point as parent block
+		statedb, database, report = base, base.Database(), false
+		current = eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
+	} else {
+		// Otherwise try to reexec blocks until we find a state or reach our limit
+		current = block
 
-	for i := uint64(0); i < reexec; i++ {
-		if block.NumberU64() == 0 {
-			return nil, nil, errors.New("genesis state is missing")
-		}
-		parent := eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
-		if parent == nil {
-			return nil, nil, fmt.Errorf("missing block %v %d", block.ParentHash(), block.NumberU64()-1)
-		}
-		block = parent
+		// Create an ephemeral trie.Database for isolating the live one. Otherwise
+		// the internal junks created by tracing will be persisted into the disk.
+		database = state.NewDatabaseWithConfig(eth.chainDb, &trie.Config{Cache: 16})
 
-		statedb, err = state.New(block.Root(), database, nil)
-		if err == nil {
-			break
+		for i := uint64(0); i < reexec; i++ {
+			if current.NumberU64() == 0 {
+				return nil, errors.New("genesis state is missing")
+			}
+			parent := eth.blockchain.GetBlock(current.ParentHash(), current.NumberU64()-1)
+			if parent == nil {
+				return nil, fmt.Errorf("missing block %v %d", current.ParentHash(), current.NumberU64()-1)
+			}
+			current = parent
+
+			statedb, err = state.New(current.Root(), database, nil)
+			if err == nil {
+				break
+			}
 		}
-	}
-	if err != nil {
-		switch err.(type) {
-		case *trie.MissingNodeError:
-			return nil, nil, fmt.Errorf("required historical state unavailable (reexec=%d)", reexec)
-		default:
-			return nil, nil, err
+		if err != nil {
+			switch err.(type) {
+			case *trie.MissingNodeError:
+				return nil, fmt.Errorf("required historical state unavailable (reexec=%d)", reexec)
+			default:
+				return nil, err
+			}
 		}
 	}
 	// State was available at historical point, regenerate
@@ -72,33 +91,29 @@ func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64) (statedb *s
 		logged time.Time
 		parent common.Hash
 	)
-	defer func() {
-		if err != nil && parent != (common.Hash{}) {
-			database.TrieDB().Dereference(parent)
-		}
-	}()
-	for block.NumberU64() < origin {
+	for current.NumberU64() < origin {
 		// Print progress logs if long enough time elapsed
-		if time.Since(logged) > 8*time.Second {
-			log.Info("Regenerating historical state", "block", block.NumberU64()+1, "target", origin, "remaining", origin-block.NumberU64()-1, "elapsed", time.Since(start))
+		if time.Since(logged) > 8*time.Second && report {
+			log.Info("Regenerating historical state", "block", current.NumberU64()+1, "target", origin, "remaining", origin-current.NumberU64()-1, "elapsed", time.Since(start))
 			logged = time.Now()
 		}
 		// Retrieve the next block to regenerate and process it
-		if block = eth.blockchain.GetBlockByNumber(block.NumberU64() + 1); block == nil {
-			return nil, nil, fmt.Errorf("block #%d not found", block.NumberU64()+1)
+		next := current.NumberU64() + 1
+		if current = eth.blockchain.GetBlockByNumber(next); current == nil {
+			return nil, fmt.Errorf("block #%d not found", next)
 		}
-		_, _, _, err := eth.blockchain.Processor().Process(block, statedb, vm.Config{})
+		_, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{})
 		if err != nil {
-			return nil, nil, fmt.Errorf("processing block %d failed: %v", block.NumberU64(), err)
+			return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err)
 		}
 		// Finalize the state so any modifications are written to the trie
-		root, err := statedb.Commit(eth.blockchain.Config().IsEIP158(block.Number()))
+		root, err := statedb.Commit(eth.blockchain.Config().IsEIP158(current.Number()))
 		if err != nil {
-			return nil, nil, err
+			return nil, err
 		}
 		statedb, err = state.New(root, database, nil)
 		if err != nil {
-			return nil, nil, fmt.Errorf("state reset after block %d failed: %v", block.NumberU64(), err)
+			return nil, fmt.Errorf("state reset after block %d failed: %v", current.NumberU64(), err)
 		}
 		database.TrieDB().Reference(root, common.Hash{})
 		if parent != (common.Hash{}) {
@@ -106,104 +121,32 @@ func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64) (statedb *s
 		}
 		parent = root
 	}
-	nodes, imgs := database.TrieDB().Size()
-	log.Info("Historical state regenerated", "block", block.NumberU64(), "elapsed", time.Since(start), "nodes", nodes, "preimages", imgs)
-	return statedb, func() { database.TrieDB().Dereference(parent) }, nil
-}
-
-// statesInRange retrieves a batch of state databases associated with the specific
-// block ranges. If no state is locally available for the given range, a number of
-// blocks are attempted to be reexecuted to generate the ancestor state.
-func (eth *Ethereum) statesInRange(fromBlock, toBlock *types.Block, reexec uint64) (states []*state.StateDB, release func(), err error) {
-	statedb, err := eth.blockchain.StateAt(fromBlock.Root())
-	if err != nil {
-		statedb, _, err = eth.stateAtBlock(fromBlock, reexec)
-	}
-	if err != nil {
-		return nil, nil, err
-	}
-	states = append(states, statedb.Copy())
-
-	var (
-		logged   time.Time
-		parent   common.Hash
-		start    = time.Now()
-		refs     = []common.Hash{fromBlock.Root()}
-		database = state.NewDatabaseWithConfig(eth.chainDb, &trie.Config{Cache: 16, Preimages: true})
-	)
-	// Release all resources(including the states referenced by `stateAtBlock`)
-	// if error is returned.
-	defer func() {
-		if err != nil {
-			for _, ref := range refs {
-				database.TrieDB().Dereference(ref)
-			}
-		}
-	}()
-	for i := fromBlock.NumberU64() + 1; i <= toBlock.NumberU64(); i++ {
-		// Print progress logs if long enough time elapsed
-		if time.Since(logged) > 8*time.Second {
-			logged = time.Now()
-			log.Info("Regenerating historical state", "block", i, "target", fromBlock.NumberU64(), "remaining", toBlock.NumberU64()-i, "elapsed", time.Since(start))
-		}
-		// Retrieve the next block to regenerate and process it
-		block := eth.blockchain.GetBlockByNumber(i)
-		if block == nil {
-			return nil, nil, fmt.Errorf("block #%d not found", i)
-		}
-		_, _, _, err := eth.blockchain.Processor().Process(block, statedb, vm.Config{})
-		if err != nil {
-			return nil, nil, fmt.Errorf("processing block %d failed: %v", block.NumberU64(), err)
-		}
-		// Finalize the state so any modifications are written to the trie
-		root, err := statedb.Commit(eth.blockchain.Config().IsEIP158(block.Number()))
-		if err != nil {
-			return nil, nil, err
-		}
-		statedb, err := eth.blockchain.StateAt(root)
-		if err != nil {
-			return nil, nil, fmt.Errorf("state reset after block %d failed: %v", block.NumberU64(), err)
-		}
-		states = append(states, statedb.Copy())
-
-		// Reference the trie twice, once for us, once for the tracer
-		database.TrieDB().Reference(root, common.Hash{})
-		database.TrieDB().Reference(root, common.Hash{})
-		refs = append(refs, root)
-
-		// Dereference all past tries we ourselves are done working with
-		if parent != (common.Hash{}) {
-			database.TrieDB().Dereference(parent)
-		}
-		parent = root
-	}
-	// release is handler to release all states referenced, including
-	// the one referenced in `stateAtBlock`.
-	release = func() {
-		for _, ref := range refs {
-			database.TrieDB().Dereference(ref)
-		}
+	if report {
+		nodes, imgs := database.TrieDB().Size()
+		log.Info("Historical state regenerated", "block", current.NumberU64(), "elapsed", time.Since(start), "nodes", nodes, "preimages", imgs)
 	}
-	return states, release, nil
+	return statedb, nil
 }
 
 // stateAtTransaction returns the execution environment of a certain transaction.
-func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, func(), error) {
+func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) {
 	// Short circuit if it's genesis block.
 	if block.NumberU64() == 0 {
-		return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis")
+		return nil, vm.BlockContext{}, nil, errors.New("no transaction in genesis")
 	}
 	// Create the parent state database
 	parent := eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
 	if parent == nil {
-		return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("parent %#x not found", block.ParentHash())
+		return nil, vm.BlockContext{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash())
 	}
-	statedb, release, err := eth.stateAtBlock(parent, reexec)
+	// Lookup the statedb of parent block from the live database,
+	// otherwise regenerate it on the flight.
+	statedb, err := eth.stateAtBlock(parent, reexec, nil, true)
 	if err != nil {
-		return nil, vm.BlockContext{}, nil, nil, err
+		return nil, vm.BlockContext{}, nil, err
 	}
 	if txIndex == 0 && len(block.Transactions()) == 0 {
-		return nil, vm.BlockContext{}, statedb, release, nil
+		return nil, vm.BlockContext{}, statedb, nil
 	}
 	// Recompute transactions up to the target index.
 	signer := types.MakeSigner(eth.blockchain.Config(), block.Number())
@@ -213,19 +156,17 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec
 		txContext := core.NewEVMTxContext(msg)
 		context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
 		if idx == txIndex {
-			return msg, context, statedb, release, nil
+			return msg, context, statedb, nil
 		}
 		// Not yet the searched for transaction, execute on top of the current state
 		vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{})
 		statedb.Prepare(tx.Hash(), block.Hash(), idx)
 		if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
-			release()
-			return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
+			return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
 		}
 		// Ensure any modifications are committed to the state
 		// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
 		statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
 	}
-	release()
-	return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
+	return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
 }
diff --git a/eth/tracers/api.go b/eth/tracers/api.go
index 75ab40347..5a28d6889 100644
--- a/eth/tracers/api.go
+++ b/eth/tracers/api.go
@@ -67,9 +67,8 @@ type Backend interface {
 	ChainConfig() *params.ChainConfig
 	Engine() consensus.Engine
 	ChainDb() ethdb.Database
-	StateAtBlock(ctx context.Context, block *types.Block, reexec uint64) (*state.StateDB, func(), error)
-	StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, func(), error)
-	StatesInRange(ctx context.Context, fromBlock *types.Block, toBlock *types.Block, reexec uint64) ([]*state.StateDB, func(), error)
+	StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error)
+	StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error)
 }
 
 // API is the collection of tracing APIs exposed over the private debugging endpoint.
@@ -187,6 +186,7 @@ type txTraceResult struct {
 type blockTraceTask struct {
 	statedb *state.StateDB   // Intermediate state prepped for tracing
 	block   *types.Block     // Block to trace the transactions from
+	rootref common.Hash      // Trie root reference held for this task
 	results []*txTraceResult // Trace results procudes by the task
 }
 
@@ -233,33 +233,22 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
 	}
 	sub := notifier.CreateSubscription()
 
-	// Shift the border to a block ahead in order to get the states
-	// before these blocks.
-	endBlock, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(end.NumberU64()-1), end.ParentHash())
-	if err != nil {
-		return nil, err
-	}
 	// Prepare all the states for tracing. Note this procedure can take very
 	// long time. Timeout mechanism is necessary.
 	reexec := defaultTraceReexec
 	if config != nil && config.Reexec != nil {
 		reexec = *config.Reexec
 	}
-	states, release, err := api.backend.StatesInRange(ctx, start, endBlock, reexec)
-	if err != nil {
-		return nil, err
-	}
-	defer release() // Release all the resources in the last step.
-
 	blocks := int(end.NumberU64() - start.NumberU64())
 	threads := runtime.NumCPU()
 	if threads > blocks {
 		threads = blocks
 	}
 	var (
-		pend    = new(sync.WaitGroup)
-		tasks   = make(chan *blockTraceTask, threads)
-		results = make(chan *blockTraceTask, threads)
+		pend     = new(sync.WaitGroup)
+		tasks    = make(chan *blockTraceTask, threads)
+		results  = make(chan *blockTraceTask, threads)
+		localctx = context.Background()
 	)
 	for th := 0; th < threads; th++ {
 		pend.Add(1)
@@ -269,7 +258,7 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
 			// Fetch and execute the next block trace tasks
 			for task := range tasks {
 				signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number())
-				blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(ctx), nil)
+				blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), nil)
 				// Trace all the transactions contained within
 				for i, tx := range task.block.Transactions() {
 					msg, _ := tx.AsMessage(signer)
@@ -278,7 +267,7 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
 						hash:  tx.Hash(),
 						block: task.block.Hash(),
 					}
-					res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
+					res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config)
 					if err != nil {
 						task.results[i] = &txTraceResult{Error: err.Error()}
 						log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
@@ -302,10 +291,12 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
 
 	go func() {
 		var (
-			logged time.Time
-			number uint64
-			traced uint64
-			failed error
+			logged  time.Time
+			number  uint64
+			traced  uint64
+			failed  error
+			parent  common.Hash
+			statedb *state.StateDB
 		)
 		// Ensure everything is properly cleaned up on any exit path
 		defer func() {
@@ -323,7 +314,7 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
 			close(results)
 		}()
 		// Feed all the blocks both into the tracer, as well as fast process concurrently
-		for number = start.NumberU64() + 1; number <= end.NumberU64(); number++ {
+		for number = start.NumberU64(); number < end.NumberU64(); number++ {
 			// Stop tracing if interruption was requested
 			select {
 			case <-notifier.Closed():
@@ -335,16 +326,39 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
 				logged = time.Now()
 				log.Info("Tracing chain segment", "start", start.NumberU64(), "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin))
 			}
-			// Retrieve the next block to trace
-			block, err := api.blockByNumber(ctx, rpc.BlockNumber(number))
+			// Retrieve the parent state to trace on top
+			block, err := api.blockByNumber(localctx, rpc.BlockNumber(number))
+			if err != nil {
+				failed = err
+				break
+			}
+			// Prepare the statedb for tracing. Don't use the live database for
+			// tracing to avoid persisting state junks into the database.
+			statedb, err = api.backend.StateAtBlock(localctx, block, reexec, statedb, false)
+			if err != nil {
+				failed = err
+				break
+			}
+			if statedb.Database().TrieDB() != nil {
+				// Hold the reference for tracer, will be released at the final stage
+				statedb.Database().TrieDB().Reference(block.Root(), common.Hash{})
+
+				// Release the parent state because it's already held by the tracer
+				if parent != (common.Hash{}) {
+					statedb.Database().TrieDB().Dereference(parent)
+				}
+			}
+			parent = block.Root()
+
+			next, err := api.blockByNumber(localctx, rpc.BlockNumber(number+1))
 			if err != nil {
 				failed = err
 				break
 			}
 			// Send the block over to the concurrent tracers (if not in the fast-forward phase)
-			txs := block.Transactions()
+			txs := next.Transactions()
 			select {
-			case tasks <- &blockTraceTask{statedb: states[int(number-start.NumberU64()-1)], block: block, results: make([]*txTraceResult, len(txs))}:
+			case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: next, rootref: block.Root(), results: make([]*txTraceResult, len(txs))}:
 			case <-notifier.Closed():
 				return
 			}
@@ -367,6 +381,10 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
 			}
 			done[uint64(result.Block)] = result
 
+			// Dereference any parent tries held in memory by this task
+			if res.statedb.Database().TrieDB() != nil {
+				res.statedb.Database().TrieDB().Dereference(res.rootref)
+			}
 			// Stream completed traces to the user, aborting on the first error
 			for result, ok := done[next]; ok; result, ok = done[next] {
 				if len(result.Traces) > 0 || next == end.NumberU64() {
@@ -470,12 +488,10 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
 	if config != nil && config.Reexec != nil {
 		reexec = *config.Reexec
 	}
-	statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec)
+	statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true)
 	if err != nil {
 		return nil, err
 	}
-	defer release()
-
 	// Execute all the transaction contained within the block concurrently
 	var (
 		signer  = types.MakeSigner(api.backend.ChainConfig(), block.Number())
@@ -561,12 +577,10 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
 	if config != nil && config.Reexec != nil {
 		reexec = *config.Reexec
 	}
-	statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec)
+	statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true)
 	if err != nil {
 		return nil, err
 	}
-	defer release()
-
 	// Retrieve the tracing configurations, or use default values
 	var (
 		logConfig vm.LogConfig
@@ -690,12 +704,10 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
 	if err != nil {
 		return nil, err
 	}
-	msg, vmctx, statedb, release, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec)
+	msg, vmctx, statedb, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec)
 	if err != nil {
 		return nil, err
 	}
-	defer release()
-
 	txctx := &txTraceContext{
 		index: int(index),
 		hash:  hash,
@@ -727,12 +739,10 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHa
 	if config != nil && config.Reexec != nil {
 		reexec = *config.Reexec
 	}
-	statedb, release, err := api.backend.StateAtBlock(ctx, block, reexec)
+	statedb, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true)
 	if err != nil {
 		return nil, err
 	}
-	defer release()
-
 	// Execute the trace
 	msg := args.ToMessage(api.backend.RPCGasCap())
 	vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
@@ -767,7 +777,9 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *txTrac
 		deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
 		go func() {
 			<-deadlineCtx.Done()
-			tracer.(*Tracer).Stop(errors.New("execution timeout"))
+			if deadlineCtx.Err() == context.DeadlineExceeded {
+				tracer.(*Tracer).Stop(errors.New("execution timeout"))
+			}
 		}()
 		defer cancel()
 
diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go
index 688b983ba..7ca90a660 100644
--- a/eth/tracers/api_test.go
+++ b/eth/tracers/api_test.go
@@ -137,25 +137,25 @@ func (b *testBackend) ChainDb() ethdb.Database {
 	return b.chaindb
 }
 
-func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64) (*state.StateDB, func(), error) {
+func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) {
 	statedb, err := b.chain.StateAt(block.Root())
 	if err != nil {
-		return nil, nil, errStateNotFound
+		return nil, errStateNotFound
 	}
-	return statedb, func() {}, nil
+	return statedb, nil
 }
 
-func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, func(), error) {
+func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) {
 	parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1)
 	if parent == nil {
-		return nil, vm.BlockContext{}, nil, nil, errBlockNotFound
+		return nil, vm.BlockContext{}, nil, errBlockNotFound
 	}
 	statedb, err := b.chain.StateAt(parent.Root())
 	if err != nil {
-		return nil, vm.BlockContext{}, nil, nil, errStateNotFound
+		return nil, vm.BlockContext{}, nil, errStateNotFound
 	}
 	if txIndex == 0 && len(block.Transactions()) == 0 {
-		return nil, vm.BlockContext{}, statedb, func() {}, nil
+		return nil, vm.BlockContext{}, statedb, nil
 	}
 	// Recompute transactions up to the target index.
 	signer := types.MakeSigner(b.chainConfig, block.Number())
@@ -164,31 +164,15 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
 		txContext := core.NewEVMTxContext(msg)
 		context := core.NewEVMBlockContext(block.Header(), b.chain, nil)
 		if idx == txIndex {
-			return msg, context, statedb, func() {}, nil
+			return msg, context, statedb, nil
 		}
 		vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{})
 		if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
-			return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
+			return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
 		}
 		statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
 	}
-	return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
-}
-
-func (b *testBackend) StatesInRange(ctx context.Context, fromBlock *types.Block, toBlock *types.Block, reexec uint64) ([]*state.StateDB, func(), error) {
-	var result []*state.StateDB
-	for number := fromBlock.NumberU64(); number <= toBlock.NumberU64(); number += 1 {
-		block := b.chain.GetBlockByNumber(number)
-		if block == nil {
-			return nil, nil, errBlockNotFound
-		}
-		statedb, err := b.chain.StateAt(block.Root())
-		if err != nil {
-			return nil, nil, errStateNotFound
-		}
-		result = append(result, statedb)
-	}
-	return result, func() {}, nil
+	return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
 }
 
 func TestTraceCall(t *testing.T) {
diff --git a/les/api_backend.go b/les/api_backend.go
index f5d2354b6..fc7821fb0 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -298,14 +298,10 @@ func (b *LesApiBackend) CurrentHeader() *types.Header {
 	return b.eth.blockchain.CurrentHeader()
 }
 
-func (b *LesApiBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64) (*state.StateDB, func(), error) {
+func (b *LesApiBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) {
 	return b.eth.stateAtBlock(ctx, block, reexec)
 }
 
-func (b *LesApiBackend) StatesInRange(ctx context.Context, fromBlock *types.Block, toBlock *types.Block, reexec uint64) ([]*state.StateDB, func(), error) {
-	return b.eth.statesInRange(ctx, fromBlock, toBlock, reexec)
-}
-
-func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, func(), error) {
+func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) {
 	return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
 }
diff --git a/les/state_accessor.go b/les/state_accessor.go
index 2f49bb921..af5df3650 100644
--- a/les/state_accessor.go
+++ b/les/state_accessor.go
@@ -29,41 +29,27 @@ import (
 )
 
 // stateAtBlock retrieves the state database associated with a certain block.
-func (leth *LightEthereum) stateAtBlock(ctx context.Context, block *types.Block, reexec uint64) (*state.StateDB, func(), error) {
-	return light.NewState(ctx, block.Header(), leth.odr), func() {}, nil
-}
-
-// statesInRange retrieves a batch of state databases associated with the specific
-// block ranges.
-func (leth *LightEthereum) statesInRange(ctx context.Context, fromBlock *types.Block, toBlock *types.Block, reexec uint64) ([]*state.StateDB, func(), error) {
-	var states []*state.StateDB
-	for number := fromBlock.NumberU64(); number <= toBlock.NumberU64(); number++ {
-		header, err := leth.blockchain.GetHeaderByNumberOdr(ctx, number)
-		if err != nil {
-			return nil, nil, err
-		}
-		states = append(states, light.NewState(ctx, header, leth.odr))
-	}
-	return states, nil, nil
+func (leth *LightEthereum) stateAtBlock(ctx context.Context, block *types.Block, reexec uint64) (*state.StateDB, error) {
+	return light.NewState(ctx, block.Header(), leth.odr), nil
 }
 
 // stateAtTransaction returns the execution environment of a certain transaction.
-func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, func(), error) {
+func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) {
 	// Short circuit if it's genesis block.
 	if block.NumberU64() == 0 {
-		return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis")
+		return nil, vm.BlockContext{}, nil, errors.New("no transaction in genesis")
 	}
 	// Create the parent state database
 	parent, err := leth.blockchain.GetBlock(ctx, block.ParentHash(), block.NumberU64()-1)
 	if err != nil {
-		return nil, vm.BlockContext{}, nil, nil, err
+		return nil, vm.BlockContext{}, nil, err
 	}
-	statedb, _, err := leth.stateAtBlock(ctx, parent, reexec)
+	statedb, err := leth.stateAtBlock(ctx, parent, reexec)
 	if err != nil {
-		return nil, vm.BlockContext{}, nil, nil, err
+		return nil, vm.BlockContext{}, nil, err
 	}
 	if txIndex == 0 && len(block.Transactions()) == 0 {
-		return nil, vm.BlockContext{}, statedb, func() {}, nil
+		return nil, vm.BlockContext{}, statedb, nil
 	}
 	// Recompute transactions up to the target index.
 	signer := types.MakeSigner(leth.blockchain.Config(), block.Number())
@@ -74,16 +60,16 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.
 		context := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil)
 		statedb.Prepare(tx.Hash(), block.Hash(), idx)
 		if idx == txIndex {
-			return msg, context, statedb, func() {}, nil
+			return msg, context, statedb, nil
 		}
 		// Not yet the searched for transaction, execute on top of the current state
 		vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{})
 		if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
-			return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
+			return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
 		}
 		// Ensure any modifications are committed to the state
 		// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
 		statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
 	}
-	return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
+	return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
 }
-- 
GitLab