@@ -207,9 +207,10 @@ type BlockChain struct {
207
207
processor Processor // Block transaction processor interface
208
208
vmConfig vm.Config
209
209
210
- badBlocks * lru.Cache // Bad block cache
211
- shouldPreserve func (* types.Block ) bool // Function used to determine whether should preserve the given block.
212
- terminateInsert func (common.Hash , uint64 ) bool // Testing hook used to terminate ancient receipt chain insertion.
210
+ badBlocks * lru.Cache // Bad block cache
211
+ shouldPreserve func (* types.Block ) bool // Function used to determine whether should preserve the given block.
212
+ terminateInsert func (common.Hash , uint64 ) bool // Testing hook used to terminate ancient receipt chain insertion.
213
+ writeLegacyJournal bool // Testing flag used to flush the snapshot journal in legacy format.
213
214
}
214
215
215
216
// NewBlockChain returns a fully initialised block chain using information
@@ -281,9 +282,29 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
281
282
// Make sure the state associated with the block is available
282
283
head := bc .CurrentBlock ()
283
284
if _ , err := state .New (head .Root (), bc .stateCache , bc .snaps ); err != nil {
284
- log .Warn ("Head state missing, repairing" , "number" , head .Number (), "hash" , head .Hash ())
285
- if err := bc .SetHead (head .NumberU64 ()); err != nil {
286
- return nil , err
285
+ // Head state is missing, before the state recovery, find out the
286
+ // disk layer point of snapshot(if it's enabled). Make sure the
287
+ // rewound point is lower than disk layer.
288
+ var diskRoot common.Hash
289
+ if bc .cacheConfig .SnapshotLimit > 0 {
290
+ diskRoot = rawdb .ReadSnapshotRoot (bc .db )
291
+ }
292
+ if diskRoot != (common.Hash {}) {
293
+ log .Warn ("Head state missing, repairing" , "number" , head .Number (), "hash" , head .Hash (), "snaproot" , diskRoot )
294
+
295
+ snapDisk , err := bc .SetHeadBeyondRoot (head .NumberU64 (), diskRoot )
296
+ if err != nil {
297
+ return nil , err
298
+ }
299
+ // Chain rewound, persist old snapshot number to indicate recovery procedure
300
+ if snapDisk != 0 {
301
+ rawdb .WriteSnapshotRecoveryNumber (bc .db , snapDisk )
302
+ }
303
+ } else {
304
+ log .Warn ("Head state missing, repairing" , "number" , head .Number (), "hash" , head .Hash ())
305
+ if err := bc .SetHead (head .NumberU64 ()); err != nil {
306
+ return nil , err
307
+ }
287
308
}
288
309
}
289
310
// Ensure that a previous crash in SetHead doesn't leave extra ancients
@@ -339,7 +360,18 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
339
360
}
340
361
// Load any existing snapshot, regenerating it if loading failed
341
362
if bc .cacheConfig .SnapshotLimit > 0 {
342
- bc .snaps = snapshot .New (bc .db , bc .stateCache .TrieDB (), bc .cacheConfig .SnapshotLimit , bc .CurrentBlock ().Root (), ! bc .cacheConfig .SnapshotWait )
363
+ // If the chain was rewound past the snapshot persistent layer (causing
364
+ // a recovery block number to be persisted to disk), check if we're still
365
+ // in recovery mode and in that case, don't invalidate the snapshot on a
366
+ // head mismatch.
367
+ var recover bool
368
+
369
+ head := bc .CurrentBlock ()
370
+ if layer := rawdb .ReadSnapshotRecoveryNumber (bc .db ); layer != nil && * layer > head .NumberU64 () {
371
+ log .Warn ("Enabling snapshot recovery" , "chainhead" , head .NumberU64 (), "diskbase" , * layer )
372
+ recover = true
373
+ }
374
+ bc .snaps = snapshot .New (bc .db , bc .stateCache .TrieDB (), bc .cacheConfig .SnapshotLimit , head .Root (), ! bc .cacheConfig .SnapshotWait , recover )
343
375
}
344
376
// Take ownership of this particular state
345
377
go bc .update ()
@@ -444,9 +476,25 @@ func (bc *BlockChain) loadLastState() error {
444
476
// was fast synced or full synced and in which state, the method will try to
445
477
// delete minimal data from disk whilst retaining chain consistency.
446
478
func (bc * BlockChain ) SetHead (head uint64 ) error {
479
+ _ , err := bc .SetHeadBeyondRoot (head , common.Hash {})
480
+ return err
481
+ }
482
+
483
+ // SetHeadBeyondRoot rewinds the local chain to a new head with the extra condition
484
+ // that the rewind must pass the specified state root. This method is meant to be
485
+ // used when rewiding with snapshots enabled to ensure that we go back further than
486
+ // persistent disk layer. Depending on whether the node was fast synced or full, and
487
+ // in which state, the method will try to delete minimal data from disk whilst
488
+ // retaining chain consistency.
489
+ //
490
+ // The method returns the block number where the requested root cap was found.
491
+ func (bc * BlockChain ) SetHeadBeyondRoot (head uint64 , root common.Hash ) (uint64 , error ) {
447
492
bc .chainmu .Lock ()
448
493
defer bc .chainmu .Unlock ()
449
494
495
+ // Track the block number of the requested root hash
496
+ var rootNumber uint64 // (no root == always 0)
497
+
450
498
// Retrieve the last pivot block to short circuit rollbacks beyond it and the
451
499
// current freezer limit to start nuking id underflown
452
500
pivot := rawdb .ReadLastPivotNumber (bc .db )
@@ -462,8 +510,16 @@ func (bc *BlockChain) SetHead(head uint64) error {
462
510
log .Error ("Gap in the chain, rewinding to genesis" , "number" , header .Number , "hash" , header .Hash ())
463
511
newHeadBlock = bc .genesisBlock
464
512
} else {
465
- // Block exists, keep rewinding until we find one with state
513
+ // Block exists, keep rewinding until we find one with state,
514
+ // keeping rewinding until we exceed the optional threshold
515
+ // root hash
516
+ beyondRoot := (root == common.Hash {}) // Flag whether we're beyond the requested root (no root, always true)
517
+
466
518
for {
519
+ // If a root threshold was requested but not yet crossed, check
520
+ if root != (common.Hash {}) && ! beyondRoot && newHeadBlock .Root () == root {
521
+ beyondRoot , rootNumber = true , newHeadBlock .NumberU64 ()
522
+ }
467
523
if _ , err := state .New (newHeadBlock .Root (), bc .stateCache , bc .snaps ); err != nil {
468
524
log .Trace ("Block state missing, rewinding further" , "number" , newHeadBlock .NumberU64 (), "hash" , newHeadBlock .Hash ())
469
525
if pivot == nil || newHeadBlock .NumberU64 () > * pivot {
@@ -474,8 +530,12 @@ func (bc *BlockChain) SetHead(head uint64) error {
474
530
newHeadBlock = bc .genesisBlock
475
531
}
476
532
}
477
- log .Debug ("Rewound to block with state" , "number" , newHeadBlock .NumberU64 (), "hash" , newHeadBlock .Hash ())
478
- break
533
+ if beyondRoot || newHeadBlock .NumberU64 () == 0 {
534
+ log .Debug ("Rewound to block with state" , "number" , newHeadBlock .NumberU64 (), "hash" , newHeadBlock .Hash ())
535
+ break
536
+ }
537
+ log .Debug ("Skipping block with threshold state" , "number" , newHeadBlock .NumberU64 (), "hash" , newHeadBlock .Hash (), "root" , newHeadBlock .Root ())
538
+ newHeadBlock = bc .GetBlock (newHeadBlock .ParentHash (), newHeadBlock .NumberU64 ()- 1 ) // Keep rewinding
479
539
}
480
540
}
481
541
rawdb .WriteHeadBlockHash (db , newHeadBlock .Hash ())
@@ -555,7 +615,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
555
615
bc .txLookupCache .Purge ()
556
616
bc .futureBlocks .Purge ()
557
617
558
- return bc .loadLastState ()
618
+ return rootNumber , bc .loadLastState ()
559
619
}
560
620
561
621
// FastSyncCommitHead sets the current head block to the one defined by the hash
@@ -940,8 +1000,14 @@ func (bc *BlockChain) Stop() {
940
1000
var snapBase common.Hash
941
1001
if bc .snaps != nil {
942
1002
var err error
943
- if snapBase , err = bc .snaps .Journal (bc .CurrentBlock ().Root ()); err != nil {
944
- log .Error ("Failed to journal state snapshot" , "err" , err )
1003
+ if bc .writeLegacyJournal {
1004
+ if snapBase , err = bc .snaps .LegacyJournal (bc .CurrentBlock ().Root ()); err != nil {
1005
+ log .Error ("Failed to journal state snapshot" , "err" , err )
1006
+ }
1007
+ } else {
1008
+ if snapBase , err = bc .snaps .Journal (bc .CurrentBlock ().Root ()); err != nil {
1009
+ log .Error ("Failed to journal state snapshot" , "err" , err )
1010
+ }
945
1011
}
946
1012
}
947
1013
// Ensure the state of a recent block is also stored to disk before exiting.
0 commit comments