@@ -70,11 +70,25 @@ type BlockHeaderStore interface {
7070 // single atomic transaction.
7171 WriteHeaders (... BlockHeader ) error
7272
73+ // RollbackBlockHeaders rolls back a specified number of headers from
74+ // the tip of the chain. It removes the most recent 'numHeaders' from
75+ // the block headers file and updates the corresponding indices. This
76+ // method is used during blockchain reorganizations to remove headers
77+ // that are no longer part of the main chain. The function will return
78+ // an error if the rollback would reach or go before the genesis block
79+ // (height 0). The information about the new header tip after truncation
80+ // is returned.
81+ RollbackBlockHeaders (numHeaders uint32 ) (* BlockStamp , error )
82+
7383 // RollbackLastBlock rolls back the BlockHeaderStore by a _single_
7484 // header. This method is meant to be used in the case of re-org which
7585 // disconnects the latest block header from the end of the main chain.
7686 // The information about the new header tip after truncation is
7787 // returned.
88+ //
89+ // NOTE: This function is maintained for backward compatibility since it
90+ // is a publicly exposed function. It now internally utilizes
91+ // RollbackBlockHeaders API.
7892 RollbackLastBlock () (* BlockStamp , error )
7993}
8094
@@ -393,55 +407,81 @@ func (h *blockHeaderStore) HeightFromHash(hash *chainhash.Hash) (uint32, error)
393407 return h .heightFromHash (hash )
394408}
395409
396- // RollbackLastBlock rollsback both the index, and on-disk header file by a
397- // _single_ header. This method is meant to be used in the case of re-org which
398- // disconnects the latest block header from the end of the main chain. The
399- // information about the new header tip after truncation is returned .
410+ // RollbackBlockHeaders removes the specified number of block headers from the
411+ // end of the chain. It returns a BlockStamp representing the new chain tip. If
412+ // numHeaders is 0, it returns an empty BlockStamp without performing any
413+ // operations .
400414//
401- // NOTE: Part of the BlockHeaderStore interface.
402- func (h * blockHeaderStore ) RollbackLastBlock () (* BlockStamp , error ) {
403- // Lock store for write.
415+ // The function ensures rollback doesn't remove or go beyond the genesis block
416+ // (height 0). It determines the current chain tip height, reads the header
417+ // range to be removed along with the new tip header, truncates the headers file
418+ // to remove the specified number of headers, and updates the header indices to
419+ // reflect the new chain tip.
420+ func (h * blockHeaderStore ) RollbackBlockHeaders (n uint32 ) (* BlockStamp , error ) {
421+ if n == 0 {
422+ return & BlockStamp {}, nil
423+ }
424+
425+ // Lock store for rollback.
404426 h .mtx .Lock ()
405427 defer h .mtx .Unlock ()
406428
407429 // First, we'll obtain the latest height that the index knows of.
408- lastBlock , chainTipHeight , err := h .chainTip ()
430+ _ , chainTipHeight , err := h .chainTip ()
409431 if err != nil {
410432 return nil , err
411433 }
412434
435+ // Ensure the rollback doesn't remove or go beyond the genesis block.
436+ if n > chainTipHeight {
437+ return nil , fmt .Errorf ("cannot roll back %d headers when " +
438+ "chain height is %d" , n , chainTipHeight )
439+ }
440+
413441 // With this height obtained, we'll use it to read the previous header
414442 // from disk, so we can populate our return value which requires the
415443 // prev header hash.
416- prevHeader , err := h .readHeader (chainTipHeight - 1 )
444+ headers , err := h .readHeaderRange (chainTipHeight - n , chainTipHeight )
417445 if err != nil {
418- return nil , err
446+ return nil , fmt . Errorf ( "failed to read headers range: %v" , err )
419447 }
448+ prevHeader := headers [0 ]
420449 prevHeaderHash := prevHeader .BlockHash ()
421450
422- // Compute the block headers to truncate.
423- headersToTruncate := []* chainhash.Hash {lastBlock }
451+ // Transform to blockhashes for downstream operations, starting at
452+ // headers + 1 skipping the previous header.
453+ headersToTruncate := make ([]* chainhash.Hash , len (headers )- 1 )
454+ for i , header := range headers [1 :] {
455+ blkHash := header .BlockHash ()
456+ headersToTruncate [i ] = & blkHash
457+ }
424458
425- // Now that we have the information we need to return from this
426- // function, we can now truncate the header file, and then use the hash
427- // of the prevHeader to set the proper index chain tip.
428- if err := h .truncateHeaders (1 , h .indexType ); err != nil {
459+ if err := h .truncateHeaders (n , h .indexType ); err != nil {
429460 return nil , err
430461 }
431462
432- if err := h .truncateIndices (
433- & prevHeaderHash , headersToTruncate , true ,
434- ); err != nil {
463+ err = h .truncateIndices (& prevHeaderHash , headersToTruncate , true )
464+ if err != nil {
435465 return nil , err
436466 }
437467
438468 return & BlockStamp {
439- Height : int32 (chainTipHeight ) - 1 ,
469+ Height : int32 (chainTipHeight - n ) ,
440470 Hash : prevHeaderHash ,
441471 Timestamp : prevHeader .Timestamp ,
442472 }, nil
443473}
444474
475+ // RollbackLastBlock rollsback both the index, and on-disk header file by a
476+ // _single_ header. This method is meant to be used in the case of re-org which
477+ // disconnects the latest block header from the end of the main chain. The
478+ // information about the new header tip after truncation is returned.
479+ //
480+ // NOTE: Part of the BlockHeaderStore interface.
481+ func (h * blockHeaderStore ) RollbackLastBlock () (* BlockStamp , error ) {
482+ return h .RollbackBlockHeaders (1 )
483+ }
484+
445485// BlockHeader is a Bitcoin block header that also has its height included.
446486type BlockHeader struct {
447487 * wire.BlockHeader
0 commit comments