Skip to content

Commit

Permalink
Merge pull request #9552 from ellemouton/graph16
Browse files Browse the repository at this point in the history
graph: extract cache from CRUD [5]
  • Loading branch information
ellemouton authored Feb 27, 2025
2 parents aafbc98 + 9912c25 commit 5eb96dc
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 67 deletions.
1 change: 1 addition & 0 deletions docs/release-notes/release-notes-0.19.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ The underlying functionality between those two options remain the same.
- [2](https://github.com/lightningnetwork/lnd/pull/9545)
- [3](https://github.com/lightningnetwork/lnd/pull/9550)
- [4](https://github.com/lightningnetwork/lnd/pull/9551)
- [5](https://github.com/lightningnetwork/lnd/pull/9552)

* [Golang was updated to
`v1.22.11`](https://github.com/lightningnetwork/lnd/pull/9462).
Expand Down
106 changes: 106 additions & 0 deletions graph/db/graph.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package graphdb

import (
"errors"
"sync"
"time"

Expand Down Expand Up @@ -386,3 +387,108 @@ func (c *ChannelGraph) PruneGraphNodes() error {

return nil
}

// FilterKnownChanIDs takes a set of channel IDs and return the subset of chan
// ID's that we don't know and are not known zombies of the passed set. In other
// words, we perform a set difference of our set of chan ID's and the ones
// passed in. This method can be used by callers to determine the set of
// channels another peer knows of that we don't.
func (c *ChannelGraph) FilterKnownChanIDs(chansInfo []ChannelUpdateInfo,
isZombieChan func(time.Time, time.Time) bool) ([]uint64, error) {

unknown, knownZombies, err := c.KVStore.FilterKnownChanIDs(chansInfo)
if err != nil {
return nil, err
}

for _, info := range knownZombies {
// TODO(ziggie): Make sure that for the strict pruning case we
// compare the pubkeys and whether the right timestamp is not
// older than the `ChannelPruneExpiry`.
//
// NOTE: The timestamp data has no verification attached to it
// in the `ReplyChannelRange` msg so we are trusting this data
// at this point. However it is not critical because we are just
// removing the channel from the db when the timestamps are more
// recent. During the querying of the gossip msg verification
// happens as usual. However we should start punishing peers
// when they don't provide us honest data ?
isStillZombie := isZombieChan(
info.Node1UpdateTimestamp, info.Node2UpdateTimestamp,
)

if isStillZombie {
continue
}

// If we have marked it as a zombie but the latest update
// timestamps could bring it back from the dead, then we mark it
// alive, and we let it be added to the set of IDs to query our
// peer for.
err := c.KVStore.MarkEdgeLive(
info.ShortChannelID.ToUint64(),
)
// Since there is a chance that the edge could have been marked
// as "live" between the FilterKnownChanIDs call and the
// MarkEdgeLive call, we ignore the error if the edge is already
// marked as live.
if err != nil && !errors.Is(err, ErrZombieEdgeNotFound) {
return nil, err
}
}

return unknown, nil
}

// MarkEdgeZombie attempts to mark a channel identified by its channel ID as a
// zombie. This method is used on an ad-hoc basis, when channels need to be
// marked as zombies outside the normal pruning cycle.
func (c *ChannelGraph) MarkEdgeZombie(chanID uint64,
pubKey1, pubKey2 [33]byte) error {

c.cacheMu.Lock()
defer c.cacheMu.Unlock()

err := c.KVStore.MarkEdgeZombie(chanID, pubKey1, pubKey2)
if err != nil {
return err
}

if c.graphCache != nil {
c.graphCache.RemoveChannel(pubKey1, pubKey2, chanID)
}

return nil
}

// UpdateEdgePolicy updates the edge routing policy for a single directed edge
// within the database for the referenced channel. The `flags` attribute within
// the ChannelEdgePolicy determines which of the directed edges are being
// updated. If the flag is 1, then the first node's information is being
// updated, otherwise it's the second node's information. The node ordering is
// determined by the lexicographical ordering of the identity public keys of the
// nodes on either side of the channel.
func (c *ChannelGraph) UpdateEdgePolicy(edge *models.ChannelEdgePolicy,
op ...batch.SchedulerOption) error {

c.cacheMu.Lock()
defer c.cacheMu.Unlock()

from, to, err := c.KVStore.UpdateEdgePolicy(edge, op...)
if err != nil {
return err
}

if c.graphCache == nil {
return nil
}

var isUpdate1 bool
if edge.ChannelFlags&lnwire.ChanUpdateDirection == 0 {
isUpdate1 = true
}

c.graphCache.UpdatePolicy(edge, from, to, isUpdate1)

return nil
}
70 changes: 70 additions & 0 deletions graph/db/graph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1919,6 +1919,76 @@ func TestNodeUpdatesInHorizon(t *testing.T) {
}
}

// TestFilterKnownChanIDsZombieRevival tests that if a ChannelUpdateInfo is
// passed to FilterKnownChanIDs that contains a channel that we have marked as
// a zombie, then we will mark it as live again if the new ChannelUpdate has
// timestamps that would make the channel be considered live again.
//
// NOTE: this tests focuses on zombie revival. The main logic of
// FilterKnownChanIDs is tested in TestFilterKnownChanIDs.
func TestFilterKnownChanIDsZombieRevival(t *testing.T) {
t.Parallel()

graph, err := MakeTestGraph(t)
require.NoError(t, err)

var (
scid1 = lnwire.ShortChannelID{BlockHeight: 1}
scid2 = lnwire.ShortChannelID{BlockHeight: 2}
scid3 = lnwire.ShortChannelID{BlockHeight: 3}
)

isZombie := func(scid lnwire.ShortChannelID) bool {
zombie, _, _ := graph.IsZombieEdge(scid.ToUint64())
return zombie
}

// Mark channel 1 and 2 as zombies.
err = graph.MarkEdgeZombie(scid1.ToUint64(), [33]byte{}, [33]byte{})
require.NoError(t, err)
err = graph.MarkEdgeZombie(scid2.ToUint64(), [33]byte{}, [33]byte{})
require.NoError(t, err)

require.True(t, isZombie(scid1))
require.True(t, isZombie(scid2))
require.False(t, isZombie(scid3))

// Call FilterKnownChanIDs with an isStillZombie call-back that would
// result in the current zombies still be considered as zombies.
_, err = graph.FilterKnownChanIDs([]ChannelUpdateInfo{
{ShortChannelID: scid1},
{ShortChannelID: scid2},
{ShortChannelID: scid3},
}, func(_ time.Time, _ time.Time) bool {
return true
})
require.NoError(t, err)

require.True(t, isZombie(scid1))
require.True(t, isZombie(scid2))
require.False(t, isZombie(scid3))

// Now call it again but this time with a isStillZombie call-back that
// would result in channel with SCID 2 no longer being considered a
// zombie.
_, err = graph.FilterKnownChanIDs([]ChannelUpdateInfo{
{ShortChannelID: scid1},
{
ShortChannelID: scid2,
Node1UpdateTimestamp: time.Unix(1000, 0),
},
{ShortChannelID: scid3},
}, func(t1 time.Time, _ time.Time) bool {
return !t1.Equal(time.Unix(1000, 0))
})
require.NoError(t, err)

// Show that SCID 2 has been marked as live.
require.True(t, isZombie(scid1))
require.False(t, isZombie(scid2))
require.False(t, isZombie(scid3))
}

// TestFilterKnownChanIDs tests that we're able to properly perform the set
// differences of an incoming set of channel ID's, and those that we already
// know of on disk.
Expand Down
Loading

0 comments on commit 5eb96dc

Please sign in to comment.