@@ -197,6 +197,9 @@ type BlockChain struct {
197197 triegc * prque.Prque [int64 , common.Hash ] // Priority queue mapping block numbers to tries to gc
198198 gcproc time.Duration // Accumulates canonical block processing for trie dumping
199199
200+ lastWrite uint64
201+ flushInterval atomic.Int64
202+
200203 // txLookupLimit is the maximum number of blocks from head whose tx indices
201204 // are reserved:
202205 // * 0: means no limit and regenerate any missing indexes
@@ -806,17 +809,16 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
806809 // Ignore the error here since light client won't hit this path
807810 frozen , _ := bc .db .Ancients ()
808811 if num + 1 <= frozen {
809- // Truncate all relative data(header, total difficulty, body, receipt
810- // and canonical hash) from ancient store.
811- if _ , err := bc .db .TruncateHead (num ); err != nil {
812- log .Crit ("Failed to truncate ancient data" , "number" , num , "err" , err )
813- }
814- // Remove the hash <-> number mapping from the active store.
815- rawdb .DeleteHeaderNumber (db , hash )
812+ // The chain segment, such as the block header, canonical hash,
813+ // body, and receipt, will be removed from the ancient store
814+ // in one go.
815+ //
816+ // The hash-to-number mapping in the key-value store will be
817+ // removed by the hc.SetHead function.
816818 } else {
817- // Remove relative body and receipts from the active store.
818- // The header, total difficulty and canonical hash will be
819- // removed in the hc.SetHead function.
819+ // Remove the associated body and receipts from the key-value store.
820+ // The header, hash-to-number mapping, and canonical hash will be
821+ // removed by the hc.SetHead function.
820822 rawdb .DeleteBody (db , hash , num )
821823 rawdb .DeleteReceipts (db , hash , num )
822824 }
@@ -1237,7 +1239,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
12371239 size += writeSize
12381240
12391241 // Sync the ancient store explicitly to ensure all data has been flushed to disk.
1240- if err := bc .db .Sync (); err != nil {
1242+ if err := bc .db .SyncAncient (); err != nil {
12411243 return 0 , err
12421244 }
12431245 // Update the current fast block because all block data is now present in DB.
@@ -1366,8 +1368,6 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
13661368 return 0 , nil
13671369}
13681370
1369- var lastWrite uint64
1370-
13711371// writeBlockWithoutState writes only the block and its metadata to the database,
13721372// but does not write any state. This is used to construct competing side forks
13731373// up to the point where they exceed the canonical total difficulty.
@@ -1475,12 +1475,12 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
14751475 } else {
14761476 // If we're exceeding limits but haven't reached a large enough memory gap,
14771477 // warn the user that the system is becoming unstable.
1478- if chosen < lastWrite + state .TriesInMemory && bc .gcproc >= 2 * bc .cacheConfig .TrieTimeLimit {
1479- log .Info ("State in memory for too long, committing" , "time" , bc .gcproc , "allowance" , bc .cacheConfig .TrieTimeLimit , "optimum" , float64 (chosen - lastWrite )/ state .TriesInMemory )
1478+ if chosen < bc . lastWrite + state .TriesInMemory && bc .gcproc >= 2 * bc .cacheConfig .TrieTimeLimit {
1479+ log .Info ("State in memory for too long, committing" , "time" , bc .gcproc , "allowance" , bc .cacheConfig .TrieTimeLimit , "optimum" , float64 (chosen - bc . lastWrite )/ state .TriesInMemory )
14801480 }
14811481 // Flush an entire trie and restart the counters
14821482 triedb .Commit (header .Root , true )
1483- lastWrite = chosen
1483+ bc . lastWrite = chosen
14841484 bc .gcproc = 0
14851485 }
14861486 }
@@ -2402,3 +2402,92 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i
24022402 _ , err := bc .hc .InsertHeaderChain (chain , start )
24032403 return 0 , err
24042404}
2405+
2406+ // InsertHeadersBeforeCutoff inserts the given headers into the ancient store
2407+ // as they are claimed older than the configured chain cutoff point. All the
2408+ // inserted headers are regarded as canonical and chain reorg is not supported.
2409+ func (bc * BlockChain ) InsertHeadersBeforeCutoff (headers []* types.Header ) (int , error ) {
2410+ if len (headers ) == 0 {
2411+ return 0 , nil
2412+ }
2413+ // TODO(rjl493456442): Headers before the configured cutoff have already
2414+ // been verified by the hash of cutoff header. Theoretically, header validation
2415+ // could be skipped here.
2416+ if n , err := bc .hc .ValidateHeaderChain (headers , 0 ); err != nil {
2417+ return n , err
2418+ }
2419+ if ! bc .chainmu .TryLock () {
2420+ return 0 , errChainStopped
2421+ }
2422+ defer bc .chainmu .Unlock ()
2423+
2424+ // Initialize the ancient store with genesis block if it's empty.
2425+ var (
2426+ frozen , _ = bc .db .Ancients ()
2427+ first = headers [0 ].Number .Uint64 ()
2428+ )
2429+ if first == 1 && frozen == 0 {
2430+ _ , err := rawdb .WriteAncientBlocks (bc .db , []* types.Block {bc .genesisBlock }, []types.Receipts {nil }, big .NewInt (0 ))
2431+ if err != nil {
2432+ log .Error ("Error writing genesis to ancients" , "err" , err )
2433+ return 0 , err
2434+ }
2435+ log .Info ("Wrote genesis to ancient store" )
2436+ } else if frozen != first {
2437+ return 0 , fmt .Errorf ("headers are gapped with the ancient store, first: %d, ancient: %d" , first , frozen )
2438+ }
2439+
2440+ // Write headers to the ancient store, with block bodies and receipts set to nil
2441+ // to ensure consistency across tables in the freezer.
2442+ _ , err := rawdb .WriteAncientHeaderChain (bc .db , headers )
2443+ if err != nil {
2444+ return 0 , err
2445+ }
2446+ // Sync the ancient store explicitly to ensure all data has been flushed to disk.
2447+ if err := bc .db .SyncAncient (); err != nil {
2448+ return 0 , err
2449+ }
2450+ // Write hash to number mappings
2451+ batch := bc .db .NewBatch ()
2452+ for _ , header := range headers {
2453+ rawdb .WriteHeaderNumber (batch , header .Hash (), header .Number .Uint64 ())
2454+ }
2455+ // Write head header and head snap block flags
2456+ last := headers [len (headers )- 1 ]
2457+ rawdb .WriteHeadHeaderHash (batch , last .Hash ())
2458+ rawdb .WriteHeadFastBlockHash (batch , last .Hash ())
2459+ if err := batch .Write (); err != nil {
2460+ return 0 , err
2461+ }
2462+ // Truncate the useless chain segment (zero bodies and receipts) in the
2463+ // ancient store.
2464+ if _ , err := bc .db .TruncateTail (last .Number .Uint64 () + 1 ); err != nil {
2465+ return 0 , err
2466+ }
2467+ // Last step update all in-memory markers
2468+ bc .hc .currentHeader .Store (last )
2469+ bc .currentFastBlock .Store (last )
2470+ headHeaderGauge .Update (last .Number .Int64 ())
2471+ headFastBlockGauge .Update (last .Number .Int64 ())
2472+ return 0 , nil
2473+ }
2474+
2475+ // SetBlockValidatorAndProcessorForTesting sets the current validator and processor.
2476+ // This method can be used to force an invalid blockchain to be verified for tests.
2477+ // This method is unsafe and should only be used before block import starts.
2478+ func (bc * BlockChain ) SetBlockValidatorAndProcessorForTesting (v Validator , p Processor ) {
2479+ bc .validator = v
2480+ bc .processor = p
2481+ }
2482+
2483+ // SetTrieFlushInterval configures how often in-memory tries are persisted to disk.
2484+ // The interval is in terms of block processing time, not wall clock.
2485+ // It is thread-safe and can be called repeatedly without side effects.
2486+ func (bc * BlockChain ) SetTrieFlushInterval (interval time.Duration ) {
2487+ bc .flushInterval .Store (int64 (interval ))
2488+ }
2489+
2490+ // GetTrieFlushInterval gets the in-memory tries flushAlloc interval
2491+ func (bc * BlockChain ) GetTrieFlushInterval () time.Duration {
2492+ return time .Duration (bc .flushInterval .Load ())
2493+ }
0 commit comments