@@ -345,6 +345,106 @@ func (s *Store[H]) HasAt(_ context.Context, height uint64) bool {
345
345
return height != uint64 (0 ) && s .Height () >= height
346
346
}
347
347
348
+ // DeleteTo implements [header.Store] interface.
349
+ func (s * Store [H ]) DeleteTo (ctx context.Context , to uint64 ) error {
350
+ var from uint64
351
+
352
+ if tailPtr := s .tailHeader .Load (); tailPtr != nil {
353
+ from = (* tailPtr ).Height ()
354
+ }
355
+ if from >= to {
356
+ return nil
357
+ }
358
+ if headPtr := s .contiguousHead .Load (); headPtr != nil {
359
+ if height := (* headPtr ).Height (); to > height {
360
+ return fmt .Errorf ("header/store: higher then head (%d vs %d)" , to , height )
361
+ }
362
+ }
363
+
364
+ if from >= to {
365
+ log .Debugw ("header/store: attempt to delete empty range(%d, %d)" , from , to )
366
+ return nil
367
+ }
368
+
369
+ if err := s .deleteRange (ctx , from , to ); err != nil {
370
+ return fmt .Errorf ("header/store: delete to height %d: %w" , to , err )
371
+ }
372
+ return nil
373
+ }
374
+
375
+ func (s * Store [H ]) deleteRange (ctx context.Context , from , to uint64 ) error {
376
+ batch , err := s .ds .Batch (ctx )
377
+ if err != nil {
378
+ return fmt .Errorf ("delete batch: %w" , err )
379
+ }
380
+
381
+ if err := s .prepareDeleteRangeBatch (ctx , batch , from , to ); err != nil {
382
+ return fmt .Errorf ("prepare: %w" , err )
383
+ }
384
+
385
+ if err := s .heightIndex .deleteRange (ctx , batch , from , to ); err != nil {
386
+ return fmt .Errorf ("height index: %w" , err )
387
+ }
388
+
389
+ newTail , err := s .updateTail (ctx , batch , to )
390
+ if err != nil {
391
+ return fmt .Errorf ("update tail: %w" , err )
392
+ }
393
+
394
+ if err := batch .Commit (ctx ); err != nil {
395
+ return fmt .Errorf ("delete commit: %w" , err )
396
+ }
397
+
398
+ s .tailHeader .Store (& newTail )
399
+ return nil
400
+ }
401
+
402
+ func (s * Store [H ]) prepareDeleteRangeBatch (
403
+ ctx context.Context , batch datastore.Batch , from , to uint64 ,
404
+ ) error {
405
+ for h := from ; h < to ; h ++ {
406
+ hash , err := s .heightIndex .HashByHeight (ctx , h )
407
+ if err != nil {
408
+ if errors .Is (err , datastore .ErrNotFound ) {
409
+ log .Errorw ("removing non-existent header" , "height" , h )
410
+ continue
411
+ }
412
+ return fmt .Errorf ("hash by height(%d): %w" , h , err )
413
+ }
414
+ s .cache .Remove (hash .String ())
415
+
416
+ if err := batch .Delete (ctx , hashKey (hash )); err != nil {
417
+ return fmt .Errorf ("delete hash key: %w" , err )
418
+ }
419
+ }
420
+
421
+ s .pending .DeleteRange (from , to )
422
+ return nil
423
+ }
424
+
425
+ func (s * Store [H ]) updateTail (
426
+ ctx context.Context , batch datastore.Batch , to uint64 ,
427
+ ) (H , error ) {
428
+ var zero H
429
+
430
+ newTail , err := s .getByHeight (ctx , to )
431
+ if err != nil {
432
+ if ! errors .Is (err , header .ErrNotFound ) {
433
+ return zero , fmt .Errorf ("cannot fetch next tail: %w" , err )
434
+ }
435
+ return zero , err
436
+ }
437
+
438
+ b , err := newTail .Hash ().MarshalJSON ()
439
+ if err != nil {
440
+ return zero , err
441
+ }
442
+ if err := batch .Put (ctx , tailKey , b ); err != nil {
443
+ return zero , err
444
+ }
445
+ return newTail , nil
446
+ }
447
+
348
448
func (s * Store [H ]) Append (ctx context.Context , headers ... H ) error {
349
449
lh := len (headers )
350
450
if lh == 0 {
0 commit comments