Merge pull request #9544 from lightningnetwork/elle-graphCacheBase
graph: move graph cache out of CRUD layer
This commit is contained in:
17 files changed
+5435
-4788
No files matched your search
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/graph"
|
graphdb "github.com/lightningnetwork/lnd/graph/db"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
)
|
)
|
||||||
@@ -36,7 +36,7 @@ type ManagerCfg struct {
|
|||||||
|
|
||||||
// SubscribeTopology is used to get a subscription for topology changes
|
// SubscribeTopology is used to get a subscription for topology changes
|
||||||
// on the network.
|
// on the network.
|
||||||
SubscribeTopology func() (*graph.TopologyClient, error)
|
SubscribeTopology func() (*graphdb.TopologyClient, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manager is struct that manages an autopilot agent, making it possible to
|
// Manager is struct that manages an autopilot agent, making it possible to
|
||||||
|
|||||||
@@ -46,9 +46,14 @@ func newDiskChanGraph(t *testing.T) (testGraph, error) {
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
graphDB, err := graphdb.NewChannelGraph(backend)
|
graphDB, err := graphdb.NewChannelGraph(&graphdb.Config{KVDB: backend})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, graphDB.Start())
|
||||||
|
t.Cleanup(func() {
|
||||||
|
require.NoError(t, graphDB.Stop())
|
||||||
|
})
|
||||||
|
|
||||||
return &testDBGraph{
|
return &testDBGraph{
|
||||||
db: graphDB,
|
db: graphDB,
|
||||||
databaseChannelGraph: databaseChannelGraph{
|
databaseChannelGraph: databaseChannelGraph{
|
||||||
|
|||||||
+10
-6
@@ -1026,26 +1026,30 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
|
|||||||
"instances")
|
"instances")
|
||||||
}
|
}
|
||||||
|
|
||||||
graphDBOptions := []graphdb.OptionModifier{
|
graphDBOptions := []graphdb.KVStoreOptionModifier{
|
||||||
graphdb.WithRejectCacheSize(cfg.Caches.RejectCacheSize),
|
graphdb.WithRejectCacheSize(cfg.Caches.RejectCacheSize),
|
||||||
graphdb.WithChannelCacheSize(cfg.Caches.ChannelCacheSize),
|
graphdb.WithChannelCacheSize(cfg.Caches.ChannelCacheSize),
|
||||||
graphdb.WithBatchCommitInterval(cfg.DB.BatchCommitInterval),
|
graphdb.WithBatchCommitInterval(cfg.DB.BatchCommitInterval),
|
||||||
|
}
|
||||||
|
|
||||||
|
chanGraphOpts := []graphdb.ChanGraphOption{
|
||||||
graphdb.WithUseGraphCache(!cfg.DB.NoGraphCache),
|
graphdb.WithUseGraphCache(!cfg.DB.NoGraphCache),
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want to pre-allocate the channel graph cache according to what we
|
// We want to pre-allocate the channel graph cache according to what we
|
||||||
// expect for mainnet to speed up memory allocation.
|
// expect for mainnet to speed up memory allocation.
|
||||||
if cfg.ActiveNetParams.Name == chaincfg.MainNetParams.Name {
|
if cfg.ActiveNetParams.Name == chaincfg.MainNetParams.Name {
|
||||||
graphDBOptions = append(
|
chanGraphOpts = append(
|
||||||
graphDBOptions, graphdb.WithPreAllocCacheNumNodes(
|
chanGraphOpts, graphdb.WithPreAllocCacheNumNodes(
|
||||||
graphdb.DefaultPreAllocCacheNumNodes,
|
graphdb.DefaultPreAllocCacheNumNodes,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
dbs.GraphDB, err = graphdb.NewChannelGraph(
|
dbs.GraphDB, err = graphdb.NewChannelGraph(&graphdb.Config{
|
||||||
databaseBackends.GraphDB, graphDBOptions...,
|
KVDB: databaseBackends.GraphDB,
|
||||||
)
|
KVStoreOpts: graphDBOptions,
|
||||||
|
}, chanGraphOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cleanUp()
|
cleanUp()
|
||||||
|
|
||||||
|
|||||||
@@ -339,6 +339,11 @@ The underlying functionality between those two options remain the same.
|
|||||||
- [Abstract autopilot access](https://github.com/lightningnetwork/lnd/pull/9480)
|
- [Abstract autopilot access](https://github.com/lightningnetwork/lnd/pull/9480)
|
||||||
- [Abstract invoicerpc server access](https://github.com/lightningnetwork/lnd/pull/9516)
|
- [Abstract invoicerpc server access](https://github.com/lightningnetwork/lnd/pull/9516)
|
||||||
- [Refactor to hide DB transactions](https://github.com/lightningnetwork/lnd/pull/9513)
|
- [Refactor to hide DB transactions](https://github.com/lightningnetwork/lnd/pull/9513)
|
||||||
|
- Move the [graph cache out of the graph
|
||||||
|
CRUD](https://github.com/lightningnetwork/lnd/pull/9544) layer.
|
||||||
|
- Move [topology
|
||||||
|
subscription](https://github.com/lightningnetwork/lnd/pull/9577) and
|
||||||
|
notification handling from the graph.Builder to the ChannelGraph.
|
||||||
|
|
||||||
* [Golang was updated to
|
* [Golang was updated to
|
||||||
`v1.22.11`](https://github.com/lightningnetwork/lnd/pull/9462).
|
`v1.22.11`](https://github.com/lightningnetwork/lnd/pull/9462).
|
||||||
|
|||||||
+6
-111
@@ -109,8 +109,7 @@ type Builder struct {
|
|||||||
started atomic.Bool
|
started atomic.Bool
|
||||||
stopped atomic.Bool
|
stopped atomic.Bool
|
||||||
|
|
||||||
ntfnClientCounter atomic.Uint64
|
bestHeight atomic.Uint32
|
||||||
bestHeight atomic.Uint32
|
|
||||||
|
|
||||||
cfg *Config
|
cfg *Config
|
||||||
|
|
||||||
@@ -123,22 +122,6 @@ type Builder struct {
|
|||||||
// of our currently known best chain are sent over.
|
// of our currently known best chain are sent over.
|
||||||
staleBlocks <-chan *chainview.FilteredBlock
|
staleBlocks <-chan *chainview.FilteredBlock
|
||||||
|
|
||||||
// topologyUpdates is a channel that carries new topology updates
|
|
||||||
// messages from outside the Builder to be processed by the
|
|
||||||
// networkHandler.
|
|
||||||
topologyUpdates chan any
|
|
||||||
|
|
||||||
// topologyClients maps a client's unique notification ID to a
|
|
||||||
// topologyClient client that contains its notification dispatch
|
|
||||||
// channel.
|
|
||||||
topologyClients *lnutils.SyncMap[uint64, *topologyClient]
|
|
||||||
|
|
||||||
// ntfnClientUpdates is a channel that's used to send new updates to
|
|
||||||
// topology notification clients to the Builder. Updates either
|
|
||||||
// add a new notification client, or cancel notifications for an
|
|
||||||
// existing client.
|
|
||||||
ntfnClientUpdates chan *topologyClientUpdate
|
|
||||||
|
|
||||||
// channelEdgeMtx is a mutex we use to make sure we process only one
|
// channelEdgeMtx is a mutex we use to make sure we process only one
|
||||||
// ChannelEdgePolicy at a time for a given channelID, to ensure
|
// ChannelEdgePolicy at a time for a given channelID, to ensure
|
||||||
// consistency between the various database accesses.
|
// consistency between the various database accesses.
|
||||||
@@ -163,14 +146,11 @@ var _ ChannelGraphSource = (*Builder)(nil)
|
|||||||
// NewBuilder constructs a new Builder.
|
// NewBuilder constructs a new Builder.
|
||||||
func NewBuilder(cfg *Config) (*Builder, error) {
|
func NewBuilder(cfg *Config) (*Builder, error) {
|
||||||
return &Builder{
|
return &Builder{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
topologyUpdates: make(chan any),
|
channelEdgeMtx: multimutex.NewMutex[uint64](),
|
||||||
topologyClients: &lnutils.SyncMap[uint64, *topologyClient]{},
|
statTicker: ticker.New(defaultStatInterval),
|
||||||
ntfnClientUpdates: make(chan *topologyClientUpdate),
|
stats: new(builderStats),
|
||||||
channelEdgeMtx: multimutex.NewMutex[uint64](),
|
quit: make(chan struct{}),
|
||||||
statTicker: ticker.New(defaultStatInterval),
|
|
||||||
stats: new(builderStats),
|
|
||||||
quit: make(chan struct{}),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,28 +636,6 @@ func (b *Builder) pruneZombieChans() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleTopologyUpdate is responsible for sending any topology changes
|
|
||||||
// notifications to registered clients.
|
|
||||||
//
|
|
||||||
// NOTE: must be run inside goroutine.
|
|
||||||
func (b *Builder) handleTopologyUpdate(update any) {
|
|
||||||
defer b.wg.Done()
|
|
||||||
|
|
||||||
topChange := &TopologyChange{}
|
|
||||||
err := addToTopologyChange(b.cfg.Graph, topChange, update)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("unable to update topology change notification: %v",
|
|
||||||
err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if topChange.isEmpty() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
b.notifyTopologyChange(topChange)
|
|
||||||
}
|
|
||||||
|
|
||||||
// networkHandler is the primary goroutine for the Builder. The roles of
|
// networkHandler is the primary goroutine for the Builder. The roles of
|
||||||
// this goroutine include answering queries related to the state of the
|
// this goroutine include answering queries related to the state of the
|
||||||
// network, pruning the graph on new block notification, applying network
|
// network, pruning the graph on new block notification, applying network
|
||||||
@@ -701,16 +659,6 @@ func (b *Builder) networkHandler() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
// A new fully validated topology update has just arrived.
|
|
||||||
// We'll notify any registered clients.
|
|
||||||
case update := <-b.topologyUpdates:
|
|
||||||
b.wg.Add(1)
|
|
||||||
go b.handleTopologyUpdate(update)
|
|
||||||
|
|
||||||
// TODO(roasbeef): remove all unconnected vertexes
|
|
||||||
// after N blocks pass with no corresponding
|
|
||||||
// announcements.
|
|
||||||
|
|
||||||
case chainUpdate, ok := <-b.staleBlocks:
|
case chainUpdate, ok := <-b.staleBlocks:
|
||||||
// If the channel has been closed, then this indicates
|
// If the channel has been closed, then this indicates
|
||||||
// the daemon is shutting down, so we exit ourselves.
|
// the daemon is shutting down, so we exit ourselves.
|
||||||
@@ -783,31 +731,6 @@ func (b *Builder) networkHandler() {
|
|||||||
" processed.", chainUpdate.Height)
|
" processed.", chainUpdate.Height)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A new notification client update has arrived. We're either
|
|
||||||
// gaining a new client, or cancelling notifications for an
|
|
||||||
// existing client.
|
|
||||||
case ntfnUpdate := <-b.ntfnClientUpdates:
|
|
||||||
clientID := ntfnUpdate.clientID
|
|
||||||
|
|
||||||
if ntfnUpdate.cancel {
|
|
||||||
client, ok := b.topologyClients.LoadAndDelete(
|
|
||||||
clientID,
|
|
||||||
)
|
|
||||||
if ok {
|
|
||||||
close(client.exit)
|
|
||||||
client.wg.Wait()
|
|
||||||
|
|
||||||
close(client.ntfnChan)
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
b.topologyClients.Store(clientID, &topologyClient{
|
|
||||||
ntfnChan: ntfnUpdate.ntfnChan,
|
|
||||||
exit: make(chan struct{}),
|
|
||||||
})
|
|
||||||
|
|
||||||
// The graph prune ticker has ticked, so we'll examine the
|
// The graph prune ticker has ticked, so we'll examine the
|
||||||
// state of the known graph to filter out any zombie channels
|
// state of the known graph to filter out any zombie channels
|
||||||
// for pruning.
|
// for pruning.
|
||||||
@@ -934,16 +857,6 @@ func (b *Builder) updateGraphWithClosedChannels(
|
|||||||
log.Infof("Block %v (height=%v) closed %v channels", chainUpdate.Hash,
|
log.Infof("Block %v (height=%v) closed %v channels", chainUpdate.Hash,
|
||||||
blockHeight, len(chansClosed))
|
blockHeight, len(chansClosed))
|
||||||
|
|
||||||
if len(chansClosed) == 0 {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify all currently registered clients of the newly closed channels.
|
|
||||||
closeSummaries := createCloseSummaries(blockHeight, chansClosed...)
|
|
||||||
b.notifyTopologyChange(&TopologyChange{
|
|
||||||
ClosedChannels: closeSummaries,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1067,12 +980,6 @@ func (b *Builder) AddNode(node *models.LightningNode,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
|
||||||
case b.topologyUpdates <- node:
|
|
||||||
case <-b.quit:
|
|
||||||
return ErrGraphBuilderShuttingDown
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1117,12 +1024,6 @@ func (b *Builder) AddEdge(edge *models.ChannelEdgeInfo,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
|
||||||
case b.topologyUpdates <- edge:
|
|
||||||
case <-b.quit:
|
|
||||||
return ErrGraphBuilderShuttingDown
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1224,12 +1125,6 @@ func (b *Builder) UpdateEdge(update *models.ChannelEdgePolicy,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
|
||||||
case b.topologyUpdates <- update:
|
|
||||||
case <-b.quit:
|
|
||||||
return ErrGraphBuilderShuttingDown
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+346
-4550
File diff suppressed because it is too large.
Load diff
+139
-33
@@ -155,62 +155,70 @@ func TestNodeInsertionAndDeletion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TestPartialNode checks that we can add and retrieve a LightningNode where
|
// TestPartialNode checks that we can add and retrieve a LightningNode where
|
||||||
// where only the pubkey is known to the database.
|
// only the pubkey is known to the database.
|
||||||
func TestPartialNode(t *testing.T) {
|
func TestPartialNode(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
graph, err := MakeTestGraph(t)
|
graph, err := MakeTestGraph(t)
|
||||||
require.NoError(t, err, "unable to make test database")
|
require.NoError(t, err, "unable to make test database")
|
||||||
|
|
||||||
// We want to be able to insert nodes into the graph that only has the
|
// To insert a partial node, we need to add a channel edge that has
|
||||||
// PubKey set.
|
// node keys for nodes we are not yet aware
|
||||||
node := &models.LightningNode{
|
var node1, node2 models.LightningNode
|
||||||
HaveNodeAnnouncement: false,
|
copy(node1.PubKeyBytes[:], pubKey1Bytes)
|
||||||
PubKeyBytes: testPub,
|
copy(node2.PubKeyBytes[:], pubKey2Bytes)
|
||||||
}
|
|
||||||
|
|
||||||
if err := graph.AddLightningNode(node); err != nil {
|
// Create an edge attached to these nodes and add it to the graph.
|
||||||
t.Fatalf("unable to add node: %v", err)
|
edgeInfo, _ := createEdge(140, 0, 0, 0, &node1, &node2)
|
||||||
}
|
require.NoError(t, graph.AddChannelEdge(&edgeInfo))
|
||||||
assertNodeInCache(t, graph, node, nil)
|
|
||||||
|
|
||||||
// Next, fetch the node from the database to ensure everything was
|
// Both of the nodes should now be in both the graph (as partial/shell)
|
||||||
|
// nodes _and_ the cache should also have an awareness of both nodes.
|
||||||
|
assertNodeInCache(t, graph, &node1, nil)
|
||||||
|
assertNodeInCache(t, graph, &node2, nil)
|
||||||
|
|
||||||
|
// Next, fetch the node2 from the database to ensure everything was
|
||||||
// serialized properly.
|
// serialized properly.
|
||||||
dbNode, err := graph.FetchLightningNode(testPub)
|
dbNode1, err := graph.FetchLightningNode(pubKey1)
|
||||||
require.NoError(t, err, "unable to locate node")
|
require.NoError(t, err)
|
||||||
|
dbNode2, err := graph.FetchLightningNode(pubKey2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, exists, err := graph.HasLightningNode(dbNode.PubKeyBytes)
|
_, exists, err := graph.HasLightningNode(dbNode1.PubKeyBytes)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatalf("unable to query for node: %v", err)
|
require.True(t, exists)
|
||||||
} else if !exists {
|
|
||||||
t.Fatalf("node should be found but wasn't")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The two nodes should match exactly! (with default values for
|
// The two nodes should match exactly! (with default values for
|
||||||
// LastUpdate and db set to satisfy compareNodes())
|
// LastUpdate and db set to satisfy compareNodes())
|
||||||
node = &models.LightningNode{
|
expectedNode1 := &models.LightningNode{
|
||||||
HaveNodeAnnouncement: false,
|
HaveNodeAnnouncement: false,
|
||||||
LastUpdate: time.Unix(0, 0),
|
LastUpdate: time.Unix(0, 0),
|
||||||
PubKeyBytes: testPub,
|
PubKeyBytes: pubKey1,
|
||||||
}
|
}
|
||||||
|
require.NoError(t, compareNodes(dbNode1, expectedNode1))
|
||||||
|
|
||||||
if err := compareNodes(node, dbNode); err != nil {
|
_, exists, err = graph.HasLightningNode(dbNode2.PubKeyBytes)
|
||||||
t.Fatalf("nodes don't match: %v", err)
|
require.NoError(t, err)
|
||||||
|
require.True(t, exists)
|
||||||
|
|
||||||
|
// The two nodes should match exactly! (with default values for
|
||||||
|
// LastUpdate and db set to satisfy compareNodes())
|
||||||
|
expectedNode2 := &models.LightningNode{
|
||||||
|
HaveNodeAnnouncement: false,
|
||||||
|
LastUpdate: time.Unix(0, 0),
|
||||||
|
PubKeyBytes: pubKey2,
|
||||||
}
|
}
|
||||||
|
require.NoError(t, compareNodes(dbNode2, expectedNode2))
|
||||||
|
|
||||||
// Next, delete the node from the graph, this should purge all data
|
// Next, delete the node from the graph, this should purge all data
|
||||||
// related to the node.
|
// related to the node.
|
||||||
if err := graph.DeleteLightningNode(testPub); err != nil {
|
require.NoError(t, graph.DeleteLightningNode(pubKey1))
|
||||||
t.Fatalf("unable to delete node: %v", err)
|
|
||||||
}
|
|
||||||
assertNodeNotInCache(t, graph, testPub)
|
assertNodeNotInCache(t, graph, testPub)
|
||||||
|
|
||||||
// Finally, attempt to fetch the node again. This should fail as the
|
// Finally, attempt to fetch the node again. This should fail as the
|
||||||
// node should have been deleted from the database.
|
// node should have been deleted from the database.
|
||||||
_, err = graph.FetchLightningNode(testPub)
|
_, err = graph.FetchLightningNode(testPub)
|
||||||
if err != ErrGraphNodeNotFound {
|
require.ErrorIs(t, err, ErrGraphNodeNotFound)
|
||||||
t.Fatalf("fetch after delete should fail!")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAliasLookup(t *testing.T) {
|
func TestAliasLookup(t *testing.T) {
|
||||||
@@ -964,6 +972,23 @@ func randEdgePolicy(chanID uint64, db kvdb.Backend) *models.ChannelEdgePolicy {
|
|||||||
return newEdgePolicy(chanID, db, update)
|
return newEdgePolicy(chanID, db, update)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func copyEdgePolicy(p *models.ChannelEdgePolicy) *models.ChannelEdgePolicy {
|
||||||
|
return &models.ChannelEdgePolicy{
|
||||||
|
SigBytes: p.SigBytes,
|
||||||
|
ChannelID: p.ChannelID,
|
||||||
|
LastUpdate: p.LastUpdate,
|
||||||
|
MessageFlags: p.MessageFlags,
|
||||||
|
ChannelFlags: p.ChannelFlags,
|
||||||
|
TimeLockDelta: p.TimeLockDelta,
|
||||||
|
MinHTLC: p.MinHTLC,
|
||||||
|
MaxHTLC: p.MaxHTLC,
|
||||||
|
FeeBaseMSat: p.FeeBaseMSat,
|
||||||
|
FeeProportionalMillionths: p.FeeProportionalMillionths,
|
||||||
|
ToNode: p.ToNode,
|
||||||
|
ExtraOpaqueData: p.ExtraOpaqueData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newEdgePolicy(chanID uint64, db kvdb.Backend,
|
func newEdgePolicy(chanID uint64, db kvdb.Backend,
|
||||||
updateTime int64) *models.ChannelEdgePolicy {
|
updateTime int64) *models.ChannelEdgePolicy {
|
||||||
|
|
||||||
@@ -1919,6 +1944,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
|
// 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
|
// differences of an incoming set of channel ID's, and those that we already
|
||||||
// know of on disk.
|
// know of on disk.
|
||||||
@@ -2859,6 +2954,7 @@ func TestChannelEdgePruningUpdateIndexDeletion(t *testing.T) {
|
|||||||
if err := graph.UpdateEdgePolicy(edge1); err != nil {
|
if err := graph.UpdateEdgePolicy(edge1); err != nil {
|
||||||
t.Fatalf("unable to update edge: %v", err)
|
t.Fatalf("unable to update edge: %v", err)
|
||||||
}
|
}
|
||||||
|
edge1 = copyEdgePolicy(edge1) // Avoid read/write race conditions.
|
||||||
|
|
||||||
edge2 := randEdgePolicy(chanID.ToUint64(), graph.db)
|
edge2 := randEdgePolicy(chanID.ToUint64(), graph.db)
|
||||||
edge2.ChannelFlags = 1
|
edge2.ChannelFlags = 1
|
||||||
@@ -2867,6 +2963,7 @@ func TestChannelEdgePruningUpdateIndexDeletion(t *testing.T) {
|
|||||||
if err := graph.UpdateEdgePolicy(edge2); err != nil {
|
if err := graph.UpdateEdgePolicy(edge2); err != nil {
|
||||||
t.Fatalf("unable to update edge: %v", err)
|
t.Fatalf("unable to update edge: %v", err)
|
||||||
}
|
}
|
||||||
|
edge2 = copyEdgePolicy(edge2) // Avoid read/write race conditions.
|
||||||
|
|
||||||
// checkIndexTimestamps is a helper function that checks the edge update
|
// checkIndexTimestamps is a helper function that checks the edge update
|
||||||
// index only includes the given timestamps.
|
// index only includes the given timestamps.
|
||||||
@@ -3952,7 +4049,7 @@ func TestGraphCacheForEachNodeChannel(t *testing.T) {
|
|||||||
|
|
||||||
getSingleChannel := func() *DirectedChannel {
|
getSingleChannel := func() *DirectedChannel {
|
||||||
var ch *DirectedChannel
|
var ch *DirectedChannel
|
||||||
err = graph.forEachNodeDirectedChannel(nil, node1.PubKeyBytes,
|
err = graph.ForEachNodeDirectedChannel(node1.PubKeyBytes,
|
||||||
func(c *DirectedChannel) error {
|
func(c *DirectedChannel) error {
|
||||||
require.Nil(t, ch)
|
require.Nil(t, ch)
|
||||||
ch = c
|
ch = c
|
||||||
@@ -3974,6 +4071,7 @@ func TestGraphCacheForEachNodeChannel(t *testing.T) {
|
|||||||
253, 217, 3, 8, 0, 0, 0, 10, 0, 0, 0, 20,
|
253, 217, 3, 8, 0, 0, 0, 10, 0, 0, 0, 20,
|
||||||
}
|
}
|
||||||
require.NoError(t, graph.UpdateEdgePolicy(edge1))
|
require.NoError(t, graph.UpdateEdgePolicy(edge1))
|
||||||
|
edge1 = copyEdgePolicy(edge1) // Avoid read/write race conditions.
|
||||||
|
|
||||||
directedChan := getSingleChannel()
|
directedChan := getSingleChannel()
|
||||||
require.NotNil(t, directedChan)
|
require.NotNil(t, directedChan)
|
||||||
@@ -4005,8 +4103,12 @@ func TestGraphLoading(t *testing.T) {
|
|||||||
defer backend.Close()
|
defer backend.Close()
|
||||||
defer backendCleanup()
|
defer backendCleanup()
|
||||||
|
|
||||||
graph, err := NewChannelGraph(backend)
|
graph, err := NewChannelGraph(&Config{KVDB: backend})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, graph.Start())
|
||||||
|
t.Cleanup(func() {
|
||||||
|
require.NoError(t, graph.Stop())
|
||||||
|
})
|
||||||
|
|
||||||
// Populate the graph with test data.
|
// Populate the graph with test data.
|
||||||
const numNodes = 100
|
const numNodes = 100
|
||||||
@@ -4015,8 +4117,12 @@ func TestGraphLoading(t *testing.T) {
|
|||||||
|
|
||||||
// Recreate the graph. This should cause the graph cache to be
|
// Recreate the graph. This should cause the graph cache to be
|
||||||
// populated.
|
// populated.
|
||||||
graphReloaded, err := NewChannelGraph(backend)
|
graphReloaded, err := NewChannelGraph(&Config{KVDB: backend})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, graphReloaded.Start())
|
||||||
|
t.Cleanup(func() {
|
||||||
|
require.NoError(t, graphReloaded.Stop())
|
||||||
|
})
|
||||||
|
|
||||||
// Assert that the cache content is identical.
|
// Assert that the cache content is identical.
|
||||||
require.Equal(
|
require.Equal(
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -1,19 +1,54 @@
|
|||||||
package graph
|
package graphdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/graph/db/models"
|
"github.com/lightningnetwork/lnd/graph/db/models"
|
||||||
|
"github.com/lightningnetwork/lnd/lnutils"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// topologyManager holds all the fields required to manage the network topology
|
||||||
|
// subscriptions and notifications.
|
||||||
|
type topologyManager struct {
|
||||||
|
// ntfnClientCounter is an atomic counter that's used to assign unique
|
||||||
|
// notification client IDs to new clients.
|
||||||
|
ntfnClientCounter atomic.Uint64
|
||||||
|
|
||||||
|
// topologyUpdate is a channel that carries new topology updates
|
||||||
|
// messages from outside the ChannelGraph to be processed by the
|
||||||
|
// networkHandler.
|
||||||
|
topologyUpdate chan any
|
||||||
|
|
||||||
|
// topologyClients maps a client's unique notification ID to a
|
||||||
|
// topologyClient client that contains its notification dispatch
|
||||||
|
// channel.
|
||||||
|
topologyClients *lnutils.SyncMap[uint64, *topologyClient]
|
||||||
|
|
||||||
|
// ntfnClientUpdates is a channel that's used to send new updates to
|
||||||
|
// topology notification clients to the ChannelGraph. Updates either
|
||||||
|
// add a new notification client, or cancel notifications for an
|
||||||
|
// existing client.
|
||||||
|
ntfnClientUpdates chan *topologyClientUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
// newTopologyManager creates a new instance of the topologyManager.
|
||||||
|
func newTopologyManager() *topologyManager {
|
||||||
|
return &topologyManager{
|
||||||
|
topologyUpdate: make(chan any),
|
||||||
|
topologyClients: &lnutils.SyncMap[uint64, *topologyClient]{},
|
||||||
|
ntfnClientUpdates: make(chan *topologyClientUpdate),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TopologyClient represents an intent to receive notifications from the
|
// TopologyClient represents an intent to receive notifications from the
|
||||||
// channel router regarding changes to the topology of the channel graph. The
|
// channel router regarding changes to the topology of the channel graph. The
|
||||||
// TopologyChanges channel will be sent upon with new updates to the channel
|
// TopologyChanges channel will be sent upon with new updates to the channel
|
||||||
@@ -54,16 +89,16 @@ type topologyClientUpdate struct {
|
|||||||
// topology occurs. Changes that will be sent at notifications include: new
|
// topology occurs. Changes that will be sent at notifications include: new
|
||||||
// nodes appearing, node updating their attributes, new channels, channels
|
// nodes appearing, node updating their attributes, new channels, channels
|
||||||
// closing, and updates in the routing policies of a channel's directed edges.
|
// closing, and updates in the routing policies of a channel's directed edges.
|
||||||
func (b *Builder) SubscribeTopology() (*TopologyClient, error) {
|
func (c *ChannelGraph) SubscribeTopology() (*TopologyClient, error) {
|
||||||
// If the router is not yet started, return an error to avoid a
|
// If the router is not yet started, return an error to avoid a
|
||||||
// deadlock waiting for it to handle the subscription request.
|
// deadlock waiting for it to handle the subscription request.
|
||||||
if !b.started.Load() {
|
if !c.started.Load() {
|
||||||
return nil, fmt.Errorf("router not started")
|
return nil, fmt.Errorf("router not started")
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll first atomically obtain the next ID for this client from the
|
// We'll first atomically obtain the next ID for this client from the
|
||||||
// incrementing client ID counter.
|
// incrementing client ID counter.
|
||||||
clientID := b.ntfnClientCounter.Add(1)
|
clientID := c.ntfnClientCounter.Add(1)
|
||||||
|
|
||||||
log.Debugf("New graph topology client subscription, client %v",
|
log.Debugf("New graph topology client subscription, client %v",
|
||||||
clientID)
|
clientID)
|
||||||
@@ -71,12 +106,12 @@ func (b *Builder) SubscribeTopology() (*TopologyClient, error) {
|
|||||||
ntfnChan := make(chan *TopologyChange, 10)
|
ntfnChan := make(chan *TopologyChange, 10)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case b.ntfnClientUpdates <- &topologyClientUpdate{
|
case c.ntfnClientUpdates <- &topologyClientUpdate{
|
||||||
cancel: false,
|
cancel: false,
|
||||||
clientID: clientID,
|
clientID: clientID,
|
||||||
ntfnChan: ntfnChan,
|
ntfnChan: ntfnChan,
|
||||||
}:
|
}:
|
||||||
case <-b.quit:
|
case <-c.quit:
|
||||||
return nil, errors.New("ChannelRouter shutting down")
|
return nil, errors.New("ChannelRouter shutting down")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,11 +119,11 @@ func (b *Builder) SubscribeTopology() (*TopologyClient, error) {
|
|||||||
TopologyChanges: ntfnChan,
|
TopologyChanges: ntfnChan,
|
||||||
Cancel: func() {
|
Cancel: func() {
|
||||||
select {
|
select {
|
||||||
case b.ntfnClientUpdates <- &topologyClientUpdate{
|
case c.ntfnClientUpdates <- &topologyClientUpdate{
|
||||||
cancel: true,
|
cancel: true,
|
||||||
clientID: clientID,
|
clientID: clientID,
|
||||||
}:
|
}:
|
||||||
case <-b.quit:
|
case <-c.quit:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -114,7 +149,7 @@ type topologyClient struct {
|
|||||||
|
|
||||||
// notifyTopologyChange notifies all registered clients of a new change in
|
// notifyTopologyChange notifies all registered clients of a new change in
|
||||||
// graph topology in a non-blocking.
|
// graph topology in a non-blocking.
|
||||||
func (b *Builder) notifyTopologyChange(topologyDiff *TopologyChange) {
|
func (c *ChannelGraph) notifyTopologyChange(topologyDiff *TopologyChange) {
|
||||||
// notifyClient is a helper closure that will send topology updates to
|
// notifyClient is a helper closure that will send topology updates to
|
||||||
// the given client.
|
// the given client.
|
||||||
notifyClient := func(clientID uint64, client *topologyClient) bool {
|
notifyClient := func(clientID uint64, client *topologyClient) bool {
|
||||||
@@ -127,23 +162,22 @@ func (b *Builder) notifyTopologyChange(topologyDiff *TopologyChange) {
|
|||||||
len(topologyDiff.ChannelEdgeUpdates),
|
len(topologyDiff.ChannelEdgeUpdates),
|
||||||
len(topologyDiff.ClosedChannels))
|
len(topologyDiff.ClosedChannels))
|
||||||
|
|
||||||
go func(c *topologyClient) {
|
go func(t *topologyClient) {
|
||||||
defer c.wg.Done()
|
defer t.wg.Done()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
|
||||||
// In this case we'll try to send the notification
|
// In this case we'll try to send the notification
|
||||||
// directly to the upstream client consumer.
|
// directly to the upstream client consumer.
|
||||||
case c.ntfnChan <- topologyDiff:
|
case t.ntfnChan <- topologyDiff:
|
||||||
|
|
||||||
// If the client cancels the notifications, then we'll
|
// If the client cancels the notifications, then we'll
|
||||||
// exit early.
|
// exit early.
|
||||||
case <-c.exit:
|
case <-t.exit:
|
||||||
|
|
||||||
// Similarly, if the ChannelRouter itself exists early,
|
// Similarly, if the ChannelRouter itself exists early,
|
||||||
// then we'll also exit ourselves.
|
// then we'll also exit ourselves.
|
||||||
case <-b.quit:
|
case <-c.quit:
|
||||||
|
|
||||||
}
|
}
|
||||||
}(client)
|
}(client)
|
||||||
|
|
||||||
@@ -154,7 +188,29 @@ func (b *Builder) notifyTopologyChange(topologyDiff *TopologyChange) {
|
|||||||
|
|
||||||
// Range over the set of active clients, and attempt to send the
|
// Range over the set of active clients, and attempt to send the
|
||||||
// topology updates.
|
// topology updates.
|
||||||
b.topologyClients.Range(notifyClient)
|
c.topologyClients.Range(notifyClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleTopologyUpdate is responsible for sending any topology changes
|
||||||
|
// notifications to registered clients.
|
||||||
|
//
|
||||||
|
// NOTE: must be run inside goroutine.
|
||||||
|
func (c *ChannelGraph) handleTopologyUpdate(update any) {
|
||||||
|
defer c.wg.Done()
|
||||||
|
|
||||||
|
topChange := &TopologyChange{}
|
||||||
|
err := c.addToTopologyChange(topChange, update)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to update topology change notification: %v",
|
||||||
|
err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if topChange.isEmpty() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.notifyTopologyChange(topChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TopologyChange represents a new set of modifications to the channel graph.
|
// TopologyChange represents a new set of modifications to the channel graph.
|
||||||
@@ -310,8 +366,8 @@ type ChannelEdgeUpdate struct {
|
|||||||
// constitutes. This function will also fetch any required auxiliary
|
// constitutes. This function will also fetch any required auxiliary
|
||||||
// information required to create the topology change update from the graph
|
// information required to create the topology change update from the graph
|
||||||
// database.
|
// database.
|
||||||
func addToTopologyChange(graph DB, update *TopologyChange,
|
func (c *ChannelGraph) addToTopologyChange(update *TopologyChange,
|
||||||
msg interface{}) error {
|
msg any) error {
|
||||||
|
|
||||||
switch m := msg.(type) {
|
switch m := msg.(type) {
|
||||||
|
|
||||||
@@ -345,7 +401,7 @@ func addToTopologyChange(graph DB, update *TopologyChange,
|
|||||||
// We'll need to fetch the edge's information from the database
|
// We'll need to fetch the edge's information from the database
|
||||||
// in order to get the information concerning which nodes are
|
// in order to get the information concerning which nodes are
|
||||||
// being connected.
|
// being connected.
|
||||||
edgeInfo, _, _, err := graph.FetchChannelEdgesByID(m.ChannelID)
|
edgeInfo, _, _, err := c.FetchChannelEdgesByID(m.ChannelID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Errorf("unable fetch channel edge: %v",
|
return errors.Errorf("unable fetch channel edge: %v",
|
||||||
err)
|
err)
|
||||||
+58
-41
@@ -20,8 +20,49 @@ const (
|
|||||||
DefaultPreAllocCacheNumNodes = 15000
|
DefaultPreAllocCacheNumNodes = 15000
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options holds parameters for tuning and customizing a graph.DB.
|
// chanGraphOptions holds parameters for tuning and customizing the
|
||||||
type Options struct {
|
// ChannelGraph.
|
||||||
|
type chanGraphOptions struct {
|
||||||
|
// useGraphCache denotes whether the in-memory graph cache should be
|
||||||
|
// used or a fallback version that uses the underlying database for
|
||||||
|
// path finding.
|
||||||
|
useGraphCache bool
|
||||||
|
|
||||||
|
// preAllocCacheNumNodes is the number of nodes we expect to be in the
|
||||||
|
// graph cache, so we can pre-allocate the map accordingly.
|
||||||
|
preAllocCacheNumNodes int
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultChanGraphOptions returns a new chanGraphOptions instance populated
|
||||||
|
// with default values.
|
||||||
|
func defaultChanGraphOptions() *chanGraphOptions {
|
||||||
|
return &chanGraphOptions{
|
||||||
|
useGraphCache: true,
|
||||||
|
preAllocCacheNumNodes: DefaultPreAllocCacheNumNodes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChanGraphOption describes the signature of a functional option that can be
|
||||||
|
// used to customize a ChannelGraph instance.
|
||||||
|
type ChanGraphOption func(*chanGraphOptions)
|
||||||
|
|
||||||
|
// WithUseGraphCache sets whether the in-memory graph cache should be used.
|
||||||
|
func WithUseGraphCache(use bool) ChanGraphOption {
|
||||||
|
return func(o *chanGraphOptions) {
|
||||||
|
o.useGraphCache = use
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPreAllocCacheNumNodes sets the number of nodes we expect to be in the
|
||||||
|
// graph cache, so we can pre-allocate the map accordingly.
|
||||||
|
func WithPreAllocCacheNumNodes(n int) ChanGraphOption {
|
||||||
|
return func(o *chanGraphOptions) {
|
||||||
|
o.preAllocCacheNumNodes = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// KVStoreOptions holds parameters for tuning and customizing a graph.DB.
|
||||||
|
type KVStoreOptions struct {
|
||||||
// RejectCacheSize is the maximum number of rejectCacheEntries to hold
|
// RejectCacheSize is the maximum number of rejectCacheEntries to hold
|
||||||
// in the rejection cache.
|
// in the rejection cache.
|
||||||
RejectCacheSize int
|
RejectCacheSize int
|
||||||
@@ -34,67 +75,43 @@ type Options struct {
|
|||||||
// wait before attempting to commit a pending set of updates.
|
// wait before attempting to commit a pending set of updates.
|
||||||
BatchCommitInterval time.Duration
|
BatchCommitInterval time.Duration
|
||||||
|
|
||||||
// PreAllocCacheNumNodes is the number of nodes we expect to be in the
|
|
||||||
// graph cache, so we can pre-allocate the map accordingly.
|
|
||||||
PreAllocCacheNumNodes int
|
|
||||||
|
|
||||||
// UseGraphCache denotes whether the in-memory graph cache should be
|
|
||||||
// used or a fallback version that uses the underlying database for
|
|
||||||
// path finding.
|
|
||||||
UseGraphCache bool
|
|
||||||
|
|
||||||
// NoMigration specifies that underlying backend was opened in read-only
|
// NoMigration specifies that underlying backend was opened in read-only
|
||||||
// mode and migrations shouldn't be performed. This can be useful for
|
// mode and migrations shouldn't be performed. This can be useful for
|
||||||
// applications that use the channeldb package as a library.
|
// applications that use the channeldb package as a library.
|
||||||
NoMigration bool
|
NoMigration bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultOptions returns an Options populated with default values.
|
// DefaultOptions returns a KVStoreOptions populated with default values.
|
||||||
func DefaultOptions() *Options {
|
func DefaultOptions() *KVStoreOptions {
|
||||||
return &Options{
|
return &KVStoreOptions{
|
||||||
RejectCacheSize: DefaultRejectCacheSize,
|
RejectCacheSize: DefaultRejectCacheSize,
|
||||||
ChannelCacheSize: DefaultChannelCacheSize,
|
ChannelCacheSize: DefaultChannelCacheSize,
|
||||||
PreAllocCacheNumNodes: DefaultPreAllocCacheNumNodes,
|
NoMigration: false,
|
||||||
UseGraphCache: true,
|
|
||||||
NoMigration: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OptionModifier is a function signature for modifying the default Options.
|
// KVStoreOptionModifier is a function signature for modifying the default
|
||||||
type OptionModifier func(*Options)
|
// KVStoreOptions.
|
||||||
|
type KVStoreOptionModifier func(*KVStoreOptions)
|
||||||
|
|
||||||
// WithRejectCacheSize sets the RejectCacheSize to n.
|
// WithRejectCacheSize sets the RejectCacheSize to n.
|
||||||
func WithRejectCacheSize(n int) OptionModifier {
|
func WithRejectCacheSize(n int) KVStoreOptionModifier {
|
||||||
return func(o *Options) {
|
return func(o *KVStoreOptions) {
|
||||||
o.RejectCacheSize = n
|
o.RejectCacheSize = n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithChannelCacheSize sets the ChannelCacheSize to n.
|
// WithChannelCacheSize sets the ChannelCacheSize to n.
|
||||||
func WithChannelCacheSize(n int) OptionModifier {
|
func WithChannelCacheSize(n int) KVStoreOptionModifier {
|
||||||
return func(o *Options) {
|
return func(o *KVStoreOptions) {
|
||||||
o.ChannelCacheSize = n
|
o.ChannelCacheSize = n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithPreAllocCacheNumNodes sets the PreAllocCacheNumNodes to n.
|
|
||||||
func WithPreAllocCacheNumNodes(n int) OptionModifier {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.PreAllocCacheNumNodes = n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithBatchCommitInterval sets the batch commit interval for the interval batch
|
// WithBatchCommitInterval sets the batch commit interval for the interval batch
|
||||||
// schedulers.
|
// schedulers.
|
||||||
func WithBatchCommitInterval(interval time.Duration) OptionModifier {
|
func WithBatchCommitInterval(interval time.Duration) KVStoreOptionModifier {
|
||||||
return func(o *Options) {
|
return func(o *KVStoreOptions) {
|
||||||
o.BatchCommitInterval = interval
|
o.BatchCommitInterval = interval
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithUseGraphCache sets the UseGraphCache option to the given value.
|
|
||||||
func WithUseGraphCache(use bool) OptionModifier {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.UseGraphCache = use
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+20
-11
@@ -469,7 +469,7 @@ func TestEdgeUpdateNotification(t *testing.T) {
|
|||||||
|
|
||||||
// With the channel edge now in place, we'll subscribe for topology
|
// With the channel edge now in place, we'll subscribe for topology
|
||||||
// notifications.
|
// notifications.
|
||||||
ntfnClient, err := ctx.builder.SubscribeTopology()
|
ntfnClient, err := ctx.graph.SubscribeTopology()
|
||||||
require.NoError(t, err, "unable to subscribe for channel notifications")
|
require.NoError(t, err, "unable to subscribe for channel notifications")
|
||||||
|
|
||||||
// Create random policy edges that are stemmed to the channel id
|
// Create random policy edges that are stemmed to the channel id
|
||||||
@@ -489,7 +489,8 @@ func TestEdgeUpdateNotification(t *testing.T) {
|
|||||||
t.Fatalf("unable to add edge update: %v", err)
|
t.Fatalf("unable to add edge update: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEdgeCorrect := func(t *testing.T, edgeUpdate *ChannelEdgeUpdate,
|
assertEdgeCorrect := func(t *testing.T,
|
||||||
|
edgeUpdate *graphdb.ChannelEdgeUpdate,
|
||||||
edgeAnn *models.ChannelEdgePolicy) {
|
edgeAnn *models.ChannelEdgePolicy) {
|
||||||
|
|
||||||
if edgeUpdate.ChanID != edgeAnn.ChannelID {
|
if edgeUpdate.ChanID != edgeAnn.ChannelID {
|
||||||
@@ -659,7 +660,7 @@ func TestNodeUpdateNotification(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a new client to receive notifications.
|
// Create a new client to receive notifications.
|
||||||
ntfnClient, err := ctx.builder.SubscribeTopology()
|
ntfnClient, err := ctx.graph.SubscribeTopology()
|
||||||
require.NoError(t, err, "unable to subscribe for channel notifications")
|
require.NoError(t, err, "unable to subscribe for channel notifications")
|
||||||
|
|
||||||
// Change network topology by adding the updated info for the two nodes
|
// Change network topology by adding the updated info for the two nodes
|
||||||
@@ -672,7 +673,7 @@ func TestNodeUpdateNotification(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assertNodeNtfnCorrect := func(t *testing.T, ann *models.LightningNode,
|
assertNodeNtfnCorrect := func(t *testing.T, ann *models.LightningNode,
|
||||||
nodeUpdate *NetworkNodeUpdate) {
|
nodeUpdate *graphdb.NetworkNodeUpdate) {
|
||||||
|
|
||||||
nodeKey, _ := ann.PubKey()
|
nodeKey, _ := ann.PubKey()
|
||||||
|
|
||||||
@@ -699,9 +700,10 @@ func TestNodeUpdateNotification(t *testing.T) {
|
|||||||
t.Fatalf("node alias doesn't match: expected %v, got %v",
|
t.Fatalf("node alias doesn't match: expected %v, got %v",
|
||||||
ann.Alias, nodeUpdate.Alias)
|
ann.Alias, nodeUpdate.Alias)
|
||||||
}
|
}
|
||||||
if nodeUpdate.Color != EncodeHexColor(ann.Color) {
|
if nodeUpdate.Color != graphdb.EncodeHexColor(ann.Color) {
|
||||||
t.Fatalf("node color doesn't match: expected %v, got %v",
|
t.Fatalf("node color doesn't match: expected %v, "+
|
||||||
EncodeHexColor(ann.Color), nodeUpdate.Color)
|
"got %v", graphdb.EncodeHexColor(ann.Color),
|
||||||
|
nodeUpdate.Color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -793,7 +795,7 @@ func TestNotificationCancellation(t *testing.T) {
|
|||||||
ctx := createTestCtxSingleNode(t, startingBlockHeight)
|
ctx := createTestCtxSingleNode(t, startingBlockHeight)
|
||||||
|
|
||||||
// Create a new client to receive notifications.
|
// Create a new client to receive notifications.
|
||||||
ntfnClient, err := ctx.builder.SubscribeTopology()
|
ntfnClient, err := ctx.graph.SubscribeTopology()
|
||||||
require.NoError(t, err, "unable to subscribe for channel notifications")
|
require.NoError(t, err, "unable to subscribe for channel notifications")
|
||||||
|
|
||||||
// We'll create the utxo for a new channel.
|
// We'll create the utxo for a new channel.
|
||||||
@@ -919,7 +921,7 @@ func TestChannelCloseNotification(t *testing.T) {
|
|||||||
|
|
||||||
// With the channel edge now in place, we'll subscribe for topology
|
// With the channel edge now in place, we'll subscribe for topology
|
||||||
// notifications.
|
// notifications.
|
||||||
ntfnClient, err := ctx.builder.SubscribeTopology()
|
ntfnClient, err := ctx.graph.SubscribeTopology()
|
||||||
require.NoError(t, err, "unable to subscribe for channel notifications")
|
require.NoError(t, err, "unable to subscribe for channel notifications")
|
||||||
|
|
||||||
// Next, we'll simulate the closure of our channel by generating a new
|
// Next, we'll simulate the closure of our channel by generating a new
|
||||||
@@ -1002,7 +1004,9 @@ func TestEncodeHexColor(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range colorTestCases {
|
for _, tc := range colorTestCases {
|
||||||
encoded := EncodeHexColor(color.RGBA{tc.R, tc.G, tc.B, 0})
|
encoded := graphdb.EncodeHexColor(
|
||||||
|
color.RGBA{tc.R, tc.G, tc.B, 0},
|
||||||
|
)
|
||||||
if (encoded == tc.encoded) != tc.isValid {
|
if (encoded == tc.encoded) != tc.isValid {
|
||||||
t.Fatalf("incorrect color encoding, "+
|
t.Fatalf("incorrect color encoding, "+
|
||||||
"want: %v, got: %v", tc.encoded, encoded)
|
"want: %v, got: %v", tc.encoded, encoded)
|
||||||
@@ -1094,11 +1098,16 @@ func makeTestGraph(t *testing.T, useCache bool) (*graphdb.ChannelGraph,
|
|||||||
t.Cleanup(backendCleanup)
|
t.Cleanup(backendCleanup)
|
||||||
|
|
||||||
graph, err := graphdb.NewChannelGraph(
|
graph, err := graphdb.NewChannelGraph(
|
||||||
backend, graphdb.WithUseGraphCache(useCache),
|
&graphdb.Config{KVDB: backend},
|
||||||
|
graphdb.WithUseGraphCache(useCache),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
require.NoError(t, graph.Start())
|
||||||
|
t.Cleanup(func() {
|
||||||
|
require.NoError(t, graph.Stop())
|
||||||
|
})
|
||||||
|
|
||||||
return graph, backend, nil
|
return graph, backend, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -341,10 +341,10 @@ func testUpdateChannelPolicy(ht *lntest.HarnessTest) {
|
|||||||
// but not the second, as she only allows two updates per day and a day
|
// but not the second, as she only allows two updates per day and a day
|
||||||
// has yet to elapse from the previous update.
|
// has yet to elapse from the previous update.
|
||||||
|
|
||||||
// assertAliceAndBob is a helper closure which updates Alice's policy
|
// updateAndAssertAliceAndBob is a helper closure which updates Alice's
|
||||||
// and asserts that both Alice and Bob have heard and updated the
|
// policy and asserts that both Alice and Bob have heard and updated the
|
||||||
// policy in their graph.
|
// policy in their graph.
|
||||||
assertAliceAndBob := func(req *lnrpc.PolicyUpdateRequest,
|
updateAndAssertAliceAndBob := func(req *lnrpc.PolicyUpdateRequest,
|
||||||
expectedPolicy *lnrpc.RoutingPolicy) {
|
expectedPolicy *lnrpc.RoutingPolicy) {
|
||||||
|
|
||||||
alice.RPC.UpdateChannelPolicy(req)
|
alice.RPC.UpdateChannelPolicy(req)
|
||||||
@@ -384,7 +384,7 @@ func testUpdateChannelPolicy(ht *lntest.HarnessTest) {
|
|||||||
expectedPolicy.FeeBaseMsat = baseFee1
|
expectedPolicy.FeeBaseMsat = baseFee1
|
||||||
req.BaseFeeMsat = baseFee1
|
req.BaseFeeMsat = baseFee1
|
||||||
req.InboundFee = nil
|
req.InboundFee = nil
|
||||||
assertAliceAndBob(req, expectedPolicy)
|
updateAndAssertAliceAndBob(req, expectedPolicy)
|
||||||
|
|
||||||
// Check that Carol has both heard the policy and updated it in her
|
// Check that Carol has both heard the policy and updated it in her
|
||||||
// graph.
|
// graph.
|
||||||
@@ -407,7 +407,7 @@ func testUpdateChannelPolicy(ht *lntest.HarnessTest) {
|
|||||||
baseFee2 := baseFee1 * 2
|
baseFee2 := baseFee1 * 2
|
||||||
expectedPolicy.FeeBaseMsat = baseFee2
|
expectedPolicy.FeeBaseMsat = baseFee2
|
||||||
req.BaseFeeMsat = baseFee2
|
req.BaseFeeMsat = baseFee2
|
||||||
assertAliceAndBob(req, expectedPolicy)
|
updateAndAssertAliceAndBob(req, expectedPolicy)
|
||||||
|
|
||||||
// Since Carol didn't receive the last update, she still has Alice's
|
// Since Carol didn't receive the last update, she still has Alice's
|
||||||
// old policy. We validate this by checking the base fee is the older
|
// old policy. We validate this by checking the base fee is the older
|
||||||
|
|||||||
+7
-1
@@ -615,8 +615,14 @@ func createTestPeer(t *testing.T) *peerTestCtx {
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
dbAliceGraph, err := graphdb.NewChannelGraph(graphBackend)
|
dbAliceGraph, err := graphdb.NewChannelGraph(&graphdb.Config{
|
||||||
|
KVDB: graphBackend,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, dbAliceGraph.Start())
|
||||||
|
t.Cleanup(func() {
|
||||||
|
require.NoError(t, dbAliceGraph.Stop())
|
||||||
|
})
|
||||||
|
|
||||||
dbAliceChannel := channeldb.OpenForTesting(t, dbPath)
|
dbAliceChannel := channeldb.OpenForTesting(t, dbPath)
|
||||||
|
|
||||||
|
|||||||
@@ -295,6 +295,6 @@ func initAutoPilot(svr *server, cfg *lncfg.AutoPilot,
|
|||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
SubscribeTransactions: svr.cc.Wallet.SubscribeTransactions,
|
SubscribeTransactions: svr.cc.Wallet.SubscribeTransactions,
|
||||||
SubscribeTopology: svr.graphBuilder.SubscribeTopology,
|
SubscribeTopology: svr.graphDB.SubscribeTopology,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -167,11 +167,16 @@ func makeTestGraph(t *testing.T, useCache bool) (*graphdb.ChannelGraph,
|
|||||||
t.Cleanup(backendCleanup)
|
t.Cleanup(backendCleanup)
|
||||||
|
|
||||||
graph, err := graphdb.NewChannelGraph(
|
graph, err := graphdb.NewChannelGraph(
|
||||||
backend, graphdb.WithUseGraphCache(useCache),
|
&graphdb.Config{KVDB: backend},
|
||||||
|
graphdb.WithUseGraphCache(useCache),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
require.NoError(t, graph.Start())
|
||||||
|
t.Cleanup(func() {
|
||||||
|
require.NoError(t, graph.Stop())
|
||||||
|
})
|
||||||
|
|
||||||
return graph, backend, nil
|
return graph, backend, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-5
@@ -48,7 +48,6 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/feature"
|
"github.com/lightningnetwork/lnd/feature"
|
||||||
"github.com/lightningnetwork/lnd/fn/v2"
|
"github.com/lightningnetwork/lnd/fn/v2"
|
||||||
"github.com/lightningnetwork/lnd/funding"
|
"github.com/lightningnetwork/lnd/funding"
|
||||||
"github.com/lightningnetwork/lnd/graph"
|
|
||||||
graphdb "github.com/lightningnetwork/lnd/graph/db"
|
graphdb "github.com/lightningnetwork/lnd/graph/db"
|
||||||
"github.com/lightningnetwork/lnd/graph/db/models"
|
"github.com/lightningnetwork/lnd/graph/db/models"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||||
@@ -3347,7 +3346,7 @@ func (r *rpcServer) GetInfo(_ context.Context,
|
|||||||
// TODO(roasbeef): add synced height n stuff
|
// TODO(roasbeef): add synced height n stuff
|
||||||
|
|
||||||
isTestNet := chainreg.IsTestnet(&r.cfg.ActiveNetParams)
|
isTestNet := chainreg.IsTestnet(&r.cfg.ActiveNetParams)
|
||||||
nodeColor := graph.EncodeHexColor(nodeAnn.RGBColor)
|
nodeColor := graphdb.EncodeHexColor(nodeAnn.RGBColor)
|
||||||
version := build.Version() + " commit=" + build.Commit
|
version := build.Version() + " commit=" + build.Commit
|
||||||
|
|
||||||
return &lnrpc.GetInfoResponse{
|
return &lnrpc.GetInfoResponse{
|
||||||
@@ -6960,7 +6959,7 @@ func marshalNode(node *models.LightningNode) *lnrpc.LightningNode {
|
|||||||
PubKey: hex.EncodeToString(node.PubKeyBytes[:]),
|
PubKey: hex.EncodeToString(node.PubKeyBytes[:]),
|
||||||
Addresses: nodeAddrs,
|
Addresses: nodeAddrs,
|
||||||
Alias: node.Alias,
|
Alias: node.Alias,
|
||||||
Color: graph.EncodeHexColor(node.Color),
|
Color: graphdb.EncodeHexColor(node.Color),
|
||||||
Features: features,
|
Features: features,
|
||||||
CustomRecords: customRecords,
|
CustomRecords: customRecords,
|
||||||
}
|
}
|
||||||
@@ -7158,7 +7157,7 @@ func (r *rpcServer) SubscribeChannelGraph(req *lnrpc.GraphTopologySubscription,
|
|||||||
|
|
||||||
// First, we start by subscribing to a new intent to receive
|
// First, we start by subscribing to a new intent to receive
|
||||||
// notifications from the channel router.
|
// notifications from the channel router.
|
||||||
client, err := r.server.graphBuilder.SubscribeTopology()
|
client, err := r.server.graphDB.SubscribeTopology()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -7211,7 +7210,7 @@ func (r *rpcServer) SubscribeChannelGraph(req *lnrpc.GraphTopologySubscription,
|
|||||||
// returned by the router to the form of notifications expected by the current
|
// returned by the router to the form of notifications expected by the current
|
||||||
// gRPC service.
|
// gRPC service.
|
||||||
func marshallTopologyChange(
|
func marshallTopologyChange(
|
||||||
topChange *graph.TopologyChange) *lnrpc.GraphTopologyUpdate {
|
topChange *graphdb.TopologyChange) *lnrpc.GraphTopologyUpdate {
|
||||||
|
|
||||||
// encodeKey is a simple helper function that converts a live public
|
// encodeKey is a simple helper function that converts a live public
|
||||||
// key into a hex-encoded version of the compressed serialization for
|
// key into a hex-encoded version of the compressed serialization for
|
||||||
|
|||||||
@@ -417,7 +417,7 @@ type server struct {
|
|||||||
// updatePersistentPeerAddrs subscribes to topology changes and stores
|
// updatePersistentPeerAddrs subscribes to topology changes and stores
|
||||||
// advertised addresses for any NodeAnnouncements from our persisted peers.
|
// advertised addresses for any NodeAnnouncements from our persisted peers.
|
||||||
func (s *server) updatePersistentPeerAddrs() error {
|
func (s *server) updatePersistentPeerAddrs() error {
|
||||||
graphSub, err := s.graphBuilder.SubscribeTopology()
|
graphSub, err := s.graphDB.SubscribeTopology()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -2379,6 +2379,12 @@ func (s *server) Start() error {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanup = cleanup.add(s.graphDB.Stop)
|
||||||
|
if err := s.graphDB.Start(); err != nil {
|
||||||
|
startErr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
cleanup = cleanup.add(s.graphBuilder.Stop)
|
cleanup = cleanup.add(s.graphBuilder.Stop)
|
||||||
if err := s.graphBuilder.Start(); err != nil {
|
if err := s.graphBuilder.Start(); err != nil {
|
||||||
startErr = err
|
startErr = err
|
||||||
@@ -2691,6 +2697,9 @@ func (s *server) Stop() error {
|
|||||||
if err := s.graphBuilder.Stop(); err != nil {
|
if err := s.graphBuilder.Stop(); err != nil {
|
||||||
srvrLog.Warnf("failed to stop graphBuilder %v", err)
|
srvrLog.Warnf("failed to stop graphBuilder %v", err)
|
||||||
}
|
}
|
||||||
|
if err := s.graphDB.Stop(); err != nil {
|
||||||
|
srvrLog.Warnf("failed to stop graphDB %v", err)
|
||||||
|
}
|
||||||
if err := s.chainArb.Stop(); err != nil {
|
if err := s.chainArb.Stop(); err != nil {
|
||||||
srvrLog.Warnf("failed to stop chainArb: %v", err)
|
srvrLog.Warnf("failed to stop chainArb: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user