@@ -43,7 +43,7 @@ import (
4343 "github.com/ethereum/go-ethereum/params"
4444 "github.com/ethereum/go-ethereum/rlp"
4545 "github.com/ethereum/go-ethereum/trie"
46- lru "github.com/hashicorp/golang-lru"
46+ "github.com/hashicorp/golang-lru"
4747)
4848
4949var (
@@ -115,6 +115,7 @@ type CacheConfig struct {
115115 TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk
116116 TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node)
117117 TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
118+ StateDiffing bool // Whether or not the statediffing service is running
118119}
119120
120121// BlockChain represents the canonical chain given a database with a genesis
@@ -177,6 +178,10 @@ type BlockChain struct {
177178 badBlocks * lru.Cache // Bad block cache
178179 shouldPreserve func (* types.Block ) bool // Function used to determine whether should preserve the given block.
179180 terminateInsert func (common.Hash , uint64 ) bool // Testing hook used to terminate ancient receipt chain insertion.
181+
182+ // Locked roots and their mutex
183+ trieLock sync.Mutex
184+ lockedRoots map [common.Hash ]bool
180185}
181186
182187// NewBlockChain returns a fully initialised block chain using information
@@ -214,6 +219,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
214219 engine : engine ,
215220 vmConfig : vmConfig ,
216221 badBlocks : badBlocks ,
222+ lockedRoots : make (map [common.Hash ]bool ),
217223 }
218224 bc .validator = NewBlockValidator (chainConfig , bc , engine )
219225 bc .prefetcher = newStatePrefetcher (chainConfig , bc , engine )
@@ -857,7 +863,10 @@ func (bc *BlockChain) Stop() {
857863 }
858864 }
859865 for ! bc .triegc .Empty () {
860- triedb .Dereference (bc .triegc .PopItem ().(common.Hash ))
866+ pruneRoot := bc .triegc .PopItem ().(common.Hash )
867+ if ! bc .TrieLocked (pruneRoot ) {
868+ triedb .Dereference (pruneRoot )
869+ }
861870 }
862871 if size , _ := triedb .Size (); size != 0 {
863872 log .Error ("Dangling trie nodes after full cleanup" )
@@ -1342,6 +1351,11 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
13421351 triedb .Reference (root , common.Hash {}) // metadata reference to keep trie alive
13431352 bc .triegc .Push (root , - int64 (block .NumberU64 ()))
13441353
1354+ // If we are statediffing, lock the trie until the statediffing service is done using it
1355+ if bc .cacheConfig .StateDiffing {
1356+ bc .LockTrie (root )
1357+ }
1358+
13451359 if current := block .NumberU64 (); current > TriesInMemory {
13461360 // If we exceeded our memory allowance, flush matured singleton nodes to disk
13471361 var (
@@ -1380,8 +1394,11 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
13801394 bc .triegc .Push (root , number )
13811395 break
13821396 }
1383- log .Debug ("Dereferencing" , "root" , root .(common.Hash ).Hex ())
1384- triedb .Dereference (root .(common.Hash ))
1397+ pruneRoot := root .(common.Hash )
1398+ if ! bc .TrieLocked (pruneRoot ) {
1399+ log .Debug ("Dereferencing" , "root" , root .(common.Hash ).Hex ())
1400+ triedb .Dereference (pruneRoot )
1401+ }
13851402 }
13861403 }
13871404 }
@@ -2254,3 +2271,30 @@ func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscript
22542271func (bc * BlockChain ) SubscribeBlockProcessingEvent (ch chan <- bool ) event.Subscription {
22552272 return bc .scope .Track (bc .blockProcFeed .Subscribe (ch ))
22562273}
2274+
2275+ // TrieLocked returns whether the trie associated with the provided root is locked for use
2276+ func (bc * BlockChain ) TrieLocked (root common.Hash ) bool {
2277+ bc .trieLock .Lock ()
2278+ locked , ok := bc .lockedRoots [root ]
2279+ bc .trieLock .Unlock ()
2280+ if ! ok {
2281+ return false
2282+ }
2283+ return locked
2284+ }
2285+
2286+ // LockTrie prevents dereferencing of the provided root
2287+ func (bc * BlockChain ) LockTrie (root common.Hash ) {
2288+ bc .trieLock .Lock ()
2289+ bc .lockedRoots [root ] = true
2290+ bc .trieLock .Unlock ()
2291+ return
2292+ }
2293+
2294+ // UnlockTrie allows dereferencing of the provided root- provided it was previously locked
2295+ func (bc * BlockChain ) UnlockTrie (root common.Hash ) {
2296+ bc .trieLock .Lock ()
2297+ bc .lockedRoots [root ] = false
2298+ bc .trieLock .Unlock ()
2299+ return
2300+ }
0 commit comments