Skip to content

Commit 56f5ad6

Browse files
committed
routing: allow misson control manager to startup despite errors
We now allow the mission control manager to skip over deserializable errors. We cannot repair this these results but we just skip over it so we can startup properly.
1 parent 194a9f7 commit 56f5ad6

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

routing/missioncontrol_store.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,14 @@ func (b *missionControlStore) fetchAll() ([]*paymentResult, error) {
141141

142142
return resultBucket.ForEach(func(k, v []byte) error {
143143
result, err := deserializeResult(k, v)
144+
145+
// In case of an error, we skip the result, otherwise we
146+
// would not start up the node.
144147
if err != nil {
145-
return err
148+
log.Warnf("Failed to deserialize result: %v",
149+
err)
150+
151+
return nil
146152
}
147153

148154
results = append(results, result)

routing/missioncontrol_store_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,3 +332,72 @@ func BenchmarkMissionControlStoreFlushing(b *testing.B) {
332332
})
333333
}
334334
}
335+
336+
// TestMissionControlStoreSkipsCorruptedEntries tests that fetchAll() skips
337+
// entries that fail to deserialize rather than returning an error.
338+
func TestMissionControlStoreSkipsCorruptedEntries(t *testing.T) {
339+
h := newMCStoreTestHarness(t, testMaxRecords, time.Second)
340+
store := h.store
341+
342+
failureSourceIdx := 1
343+
344+
// Create two valid results.
345+
result1 := newPaymentResult(
346+
1, mcStoreTestRoute, testTime, testTime,
347+
fn.Some(newPaymentFailure(
348+
&failureSourceIdx,
349+
lnwire.NewFailIncorrectDetails(100, 1000),
350+
)),
351+
)
352+
353+
result2 := newPaymentResult(
354+
2, mcStoreTestRoute, testTime.Add(time.Hour),
355+
testTime.Add(time.Hour),
356+
fn.Some(newPaymentFailure(
357+
&failureSourceIdx,
358+
lnwire.NewFailIncorrectDetails(100, 1000),
359+
)),
360+
)
361+
362+
// Store both results.
363+
store.AddResult(result1)
364+
store.AddResult(result2)
365+
require.NoError(t, store.storeResults())
366+
367+
// Verify both results are stored correctly.
368+
results, err := store.fetchAll()
369+
require.NoError(t, err)
370+
require.Len(t, results, 2)
371+
372+
// Now manually insert a corrupted entry into the database.
373+
// We'll create a key that looks valid but has corrupted value data.
374+
err = store.db.update(func(bucket kvdb.RwBucket) error {
375+
// Create a valid-looking key (8 bytes timestamp + 8 bytes id +
376+
// 33 bytes pubkey).
377+
var corruptedKey [8 + 8 + 33]byte
378+
byteOrder.PutUint64(corruptedKey[:], uint64(testTime.Add(
379+
30*time.Minute).UnixNano()),
380+
)
381+
byteOrder.PutUint64(corruptedKey[8:], 99) // Unique ID.
382+
copy(corruptedKey[16:], result1.route.Val.sourcePubKey.Val[:])
383+
384+
// Insert corrupted/invalid TLV data that will fail to
385+
// deserialize.
386+
corruptedValue := []byte{0xFF, 0xFF, 0xFF, 0xFF}
387+
388+
return bucket.Put(corruptedKey[:], corruptedValue)
389+
}, func() {})
390+
require.NoError(t, err)
391+
392+
// Now fetch all results. The corrupted entry should be skipped,
393+
// and we should get back only the two valid results.
394+
results, err = store.fetchAll()
395+
require.NoError(t, err, "fetchAll should not return an error "+
396+
"even when encountering corrupted entries")
397+
require.Len(t, results, 2, "should skip the corrupted entry and "+
398+
"return only valid results")
399+
400+
// Verify we still have the correct results.
401+
require.Equal(t, result1, results[0])
402+
require.Equal(t, result2, results[1])
403+
}

0 commit comments

Comments
 (0)