diff --git a/channeldb/error.go b/channeldb/error.go index f3b89bbab..859af9746 100644 --- a/channeldb/error.go +++ b/channeldb/error.go @@ -75,6 +75,10 @@ var ( // but it is marked as a zombie within the zombie index. ErrZombieEdge = errors.New("edge marked as zombie") + // ErrZombieEdgeNotFound is an error returned when we attempt to find an + // edge in the zombie index which is not there. + ErrZombieEdgeNotFound = errors.New("edge not found in zombie index") + // ErrEdgeAlreadyExist is returned when edge with specific // channel id can't be added because it already exist. ErrEdgeAlreadyExist = fmt.Errorf("edge already exist") diff --git a/channeldb/graph.go b/channeldb/graph.go index c0cbc3287..4712412f5 100644 --- a/channeldb/graph.go +++ b/channeldb/graph.go @@ -3512,6 +3512,11 @@ func (c *ChannelGraph) MarkEdgeLive(chanID uint64) error { var k [8]byte byteOrder.PutUint64(k[:], chanID) + + if len(zombieIndex.Get(k[:])) == 0 { + return ErrZombieEdgeNotFound + } + return zombieIndex.Delete(k[:]) }, func() {}) if err != nil { diff --git a/channeldb/graph_test.go b/channeldb/graph_test.go index 12e20a9e2..41c65e353 100644 --- a/channeldb/graph_test.go +++ b/channeldb/graph_test.go @@ -3067,16 +3067,12 @@ func TestGraphZombieIndex(t *testing.T) { } edge, _, _ := createChannelEdge(graph.db, node1, node2) - if err := graph.AddChannelEdge(edge); err != nil { - t.Fatalf("unable to create channel edge: %v", err) - } + require.NoError(t, graph.AddChannelEdge(edge)) // Since the edge is known the graph and it isn't a zombie, IsZombieEdge // should not report the channel as a zombie. isZombie, _, _ := graph.IsZombieEdge(edge.ChannelID) - if isZombie { - t.Fatal("expected edge to not be marked as zombie") - } + require.False(t, isZombie) assertNumZombies(t, graph, 0) // If we delete the edge and mark it as a zombie, then we should expect @@ -3084,28 +3080,24 @@ func TestGraphZombieIndex(t *testing.T) { err = graph.DeleteChannelEdges(false, true, edge.ChannelID) require.NoError(t, err, "unable to mark edge as zombie") isZombie, pubKey1, pubKey2 := graph.IsZombieEdge(edge.ChannelID) - if !isZombie { - t.Fatal("expected edge to be marked as zombie") - } - if pubKey1 != node1.PubKeyBytes { - t.Fatalf("expected pubKey1 %x, got %x", node1.PubKeyBytes, - pubKey1) - } - if pubKey2 != node2.PubKeyBytes { - t.Fatalf("expected pubKey2 %x, got %x", node2.PubKeyBytes, - pubKey2) - } + require.True(t, isZombie) + require.Equal(t, node1.PubKeyBytes, pubKey1) + require.Equal(t, node2.PubKeyBytes, pubKey2) assertNumZombies(t, graph, 1) // Similarly, if we mark the same edge as live, we should no longer see // it within the index. - if err := graph.MarkEdgeLive(edge.ChannelID); err != nil { - t.Fatalf("unable to mark edge as live: %v", err) - } + require.NoError(t, graph.MarkEdgeLive(edge.ChannelID)) + + // Attempting to mark the edge as live again now that it is no longer + // in the zombie index should fail. + require.ErrorIs( + t, graph.MarkEdgeLive(edge.ChannelID), ErrZombieEdgeNotFound, + ) + isZombie, _, _ = graph.IsZombieEdge(edge.ChannelID) - if isZombie { - t.Fatal("expected edge to not be marked as zombie") - } + require.False(t, isZombie) + assertNumZombies(t, graph, 0) // If we mark the edge as a zombie manually, then it should show up as @@ -3114,10 +3106,9 @@ func TestGraphZombieIndex(t *testing.T) { edge.ChannelID, node1.PubKeyBytes, node2.PubKeyBytes, ) require.NoError(t, err, "unable to mark edge as zombie") + isZombie, _, _ = graph.IsZombieEdge(edge.ChannelID) - if !isZombie { - t.Fatal("expected edge to be marked as zombie") - } + require.True(t, isZombie) assertNumZombies(t, graph, 1) } diff --git a/discovery/gossiper.go b/discovery/gossiper.go index d2d2fb993..17ef933e0 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -2022,8 +2022,12 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( // processZombieUpdate determines whether the provided channel update should // resurrect a given zombie edge. +// +// NOTE: only the NodeKey1Bytes and NodeKey2Bytes members of the ChannelEdgeInfo +// should be inspected. func (d *AuthenticatedGossiper) processZombieUpdate( - chanInfo *models.ChannelEdgeInfo, msg *lnwire.ChannelUpdate) error { + chanInfo *models.ChannelEdgeInfo, scid lnwire.ShortChannelID, + msg *lnwire.ChannelUpdate) error { // The least-significant bit in the flag on the channel update tells us // which edge is being updated. @@ -2031,7 +2035,7 @@ func (d *AuthenticatedGossiper) processZombieUpdate( // Since we've deemed the update as not stale above, before marking it // live, we'll make sure it has been signed by the correct party. If we - // have both pubkeys, either party can resurect the channel. If we've + // have both pubkeys, either party can resurrect the channel. If we've // already marked this with the stricter, single-sided resurrection we // will only have the pubkey of the node with the oldest timestamp. var pubKey *btcec.PublicKey @@ -2055,12 +2059,20 @@ func (d *AuthenticatedGossiper) processZombieUpdate( // With the signature valid, we'll proceed to mark the // edge as live and wait for the channel announcement to // come through again. - baseScid := lnwire.NewShortChanIDFromInt(chanInfo.ChannelID) - err = d.cfg.Router.MarkEdgeLive(baseScid) - if err != nil { + err = d.cfg.Router.MarkEdgeLive(scid) + switch { + case errors.Is(err, channeldb.ErrZombieEdgeNotFound): + log.Errorf("edge with chan_id=%v was not found in the "+ + "zombie index: %v", err) + + return nil + + case err != nil: return fmt.Errorf("unable to remove edge with "+ "chan_id=%v from zombie index: %v", msg.ShortChannelID, err) + + default: } log.Debugf("Removed edge with chan_id=%v from zombie "+ @@ -2731,7 +2743,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg, break case channeldb.ErrZombieEdge: - err = d.processZombieUpdate(chanInfo, upd) + err = d.processZombieUpdate(chanInfo, graphScid, upd) if err != nil { log.Debug(err) nMsg.err <- err diff --git a/docs/release-notes/release-notes-0.18.0.md b/docs/release-notes/release-notes-0.18.0.md index b269d8339..b146861df 100644 --- a/docs/release-notes/release-notes-0.18.0.md +++ b/docs/release-notes/release-notes-0.18.0.md @@ -39,6 +39,9 @@ * [Fixed a case](https://github.com/lightningnetwork/lnd/pull/7503) where it's possible a failed payment might be stuck in pending. + +* [Ensure that a valid SCID](https://github.com/lightningnetwork/lnd/pull/8171) + is used when marking a zombie edge as live. # New Features ## Functional Enhancements