@@ -332,3 +332,108 @@ func BenchmarkMissionControlStoreFlushing(b *testing.B) {
332332 })
333333 }
334334}
335+
336+ // TestMissionControlStoreDeletesCorruptedEntries tests that fetchAll() skips
337+ // entries that fail to deserialize, deletes them from the database, and
338+ // removes them from the in-memory tracking structures.
339+ func TestMissionControlStoreDeletesCorruptedEntries (t * testing.T ) {
340+ h := newMCStoreTestHarness (t , testMaxRecords , time .Second )
341+ store := h .store
342+
343+ failureSourceIdx := 1
344+
345+ // Create two valid results.
346+ result1 := newPaymentResult (
347+ 1 , mcStoreTestRoute , testTime , testTime ,
348+ fn .Some (newPaymentFailure (
349+ & failureSourceIdx ,
350+ lnwire .NewFailIncorrectDetails (100 , 1000 ),
351+ )),
352+ )
353+
354+ result2 := newPaymentResult (
355+ 2 , mcStoreTestRoute , testTime .Add (time .Hour ),
356+ testTime .Add (time .Hour ),
357+ fn .Some (newPaymentFailure (
358+ & failureSourceIdx ,
359+ lnwire .NewFailIncorrectDetails (100 , 1000 ),
360+ )),
361+ )
362+
363+ // Store both results.
364+ store .AddResult (result1 )
365+ store .AddResult (result2 )
366+ require .NoError (t , store .storeResults ())
367+
368+ // Insert a corrupted entry into the database.
369+ var corruptedKey [8 + 8 + 33 ]byte
370+ byteOrder .PutUint64 (corruptedKey [:], uint64 (testTime .Add (
371+ 30 * time .Minute ).UnixNano ()),
372+ )
373+ byteOrder .PutUint64 (corruptedKey [8 :], 99 ) // Unique ID.
374+ copy (corruptedKey [16 :], result1 .route .Val .sourcePubKey .Val [:])
375+
376+ err := store .db .update (func (bucket kvdb.RwBucket ) error {
377+ // Insert corrupted/invalid TLV data that will fail to
378+ // deserialize.
379+ corruptedValue := []byte {0xFF , 0xFF , 0xFF , 0xFF }
380+
381+ return bucket .Put (corruptedKey [:], corruptedValue )
382+ }, func () {})
383+ require .NoError (t , err )
384+
385+ // Add the corrupted key to in-memory tracking to simulate it being
386+ // loaded at startup (newMissionControlStore populates keysMap from
387+ // all DB keys).
388+ corruptedKeyStr := string (corruptedKey [:])
389+ store .keysMap [corruptedKeyStr ] = struct {}{}
390+ store .keys .PushBack (corruptedKeyStr )
391+
392+ // Verify the corrupted key is in the in-memory tracking.
393+ _ , exists := store .keysMap [corruptedKeyStr ]
394+ require .True (t , exists , "corrupted key should be in keysMap" )
395+
396+ // Verify we have 3 entries in the database before fetchAll.
397+ var dbEntryCountBefore int
398+ err = store .db .view (func (bucket kvdb.RBucket ) error {
399+ return bucket .ForEach (func (k , v []byte ) error {
400+ dbEntryCountBefore ++
401+ return nil
402+ })
403+ }, func () {
404+ dbEntryCountBefore = 0
405+ })
406+ require .NoError (t , err )
407+ require .Equal (t , 3 , dbEntryCountBefore , "should have 3 entries " +
408+ "in the database before cleanup" )
409+
410+ // Now fetch all results. The corrupted entry should be skipped,
411+ // deleted from the DB, and removed from in-memory tracking.
412+ results , err := store .fetchAll ()
413+ require .NoError (t , err , "fetchAll should not return an error " +
414+ "even when encountering corrupted entries" )
415+ require .Len (t , results , 2 , "should skip the corrupted entry and " +
416+ "return only valid results" )
417+
418+ // Verify we still have the correct results.
419+ require .Equal (t , result1 , results [0 ])
420+ require .Equal (t , result2 , results [1 ])
421+
422+ // Verify the corrupted entry was removed from in-memory tracking.
423+ _ , exists = store .keysMap [corruptedKeyStr ]
424+ require .False (t , exists , "corrupted key should not exist in keysMap" )
425+
426+ // Verify the corrupted entry was deleted from the database.
427+ var dbEntryCountAfter int
428+ err = store .db .view (func (bucket kvdb.RBucket ) error {
429+ return bucket .ForEach (func (k , v []byte ) error {
430+ dbEntryCountAfter ++
431+ return nil
432+ })
433+ }, func () {
434+ dbEntryCountAfter = 0
435+ })
436+ require .NoError (t , err )
437+ require .Equal (t , 2 , dbEntryCountAfter , "corrupted entry should be " +
438+ "deleted from the database" )
439+ }
0 commit comments