From b86980ec40d7debfe750410ec6527f61e99e0b7c Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 22 Oct 2024 14:51:32 +0200 Subject: [PATCH] channeldb: remove graph db from channeldb Now that the channel.DB no longer uses the ChannelGraph pointer, we can completely remove it as a member of channeldb.DB. --- autopilot/prefattach_test.go | 23 +++++++++++-------- channeldb/db.go | 27 +++++----------------- channeldb/db_test.go | 13 ++++------- channeldb/meta_test.go | 22 +++++------------- config_builder.go | 43 +++++++++--------------------------- graph/db/graph.go | 29 ++++++++++++++++++++++++ graph/db/graph_test.go | 28 ----------------------- peer/test_utils.go | 31 ++++++++++++++++++++------ server.go | 18 ++++++--------- 9 files changed, 100 insertions(+), 134 deletions(-) diff --git a/autopilot/prefattach_test.go b/autopilot/prefattach_test.go index 524116aa4..ab52c55f6 100644 --- a/autopilot/prefattach_test.go +++ b/autopilot/prefattach_test.go @@ -8,7 +8,8 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" - "github.com/lightningnetwork/lnd/channeldb" + graphdb "github.com/lightningnetwork/lnd/graph/db" + "github.com/lightningnetwork/lnd/kvdb" "github.com/stretchr/testify/require" ) @@ -24,17 +25,21 @@ type testGraph interface { } func newDiskChanGraph(t *testing.T) (testGraph, error) { - // Next, create channeldb for the first time. - cdb, err := channeldb.Open(t.TempDir()) - if err != nil { - return nil, err - } - t.Cleanup(func() { - require.NoError(t, cdb.Close()) + backend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{ + DBPath: t.TempDir(), + DBFileName: "graph.db", + NoFreelistSync: true, + AutoCompact: false, + AutoCompactMinAge: kvdb.DefaultBoltAutoCompactMinAge, + DBTimeout: kvdb.DefaultDBTimeout, }) + require.NoError(t, err) + + graphDB, err := graphdb.NewChannelGraph(backend) + require.NoError(t, err) return &databaseChannelGraph{ - db: cdb.ChannelGraph(), + db: graphDB, }, nil } diff --git a/channeldb/db.go b/channeldb/db.go index 09cb61ea5..3773b5e7b 100644 --- a/channeldb/db.go +++ b/channeldb/db.go @@ -335,7 +335,6 @@ type DB struct { channelStateDB *ChannelStateDB dbPath string - graph *graphdb.ChannelGraph clock clock.Clock dryRun bool keepFailedPaymentAttempts bool @@ -362,22 +361,18 @@ func Open(dbPath string, modifiers ...OptionModifier) (*DB, error) { return nil, err } - graphDB, err := graphdb.NewChannelGraph(backend) - if err != nil { - return nil, err - } - - db, err := CreateWithBackend(backend, graphDB, modifiers...) + db, err := CreateWithBackend(backend, modifiers...) if err == nil { db.dbPath = dbPath } + return db, err } // CreateWithBackend creates channeldb instance using the passed kvdb.Backend. // Any necessary schemas migrations due to updates will take place as necessary. -func CreateWithBackend(backend kvdb.Backend, graph *graphdb.ChannelGraph, - modifiers ...OptionModifier) (*DB, error) { +func CreateWithBackend(backend kvdb.Backend, modifiers ...OptionModifier) (*DB, + error) { opts := DefaultOptions() for _, modifier := range modifiers { @@ -403,7 +398,6 @@ func CreateWithBackend(backend kvdb.Backend, graph *graphdb.ChannelGraph, keepFailedPaymentAttempts: opts.keepFailedPaymentAttempts, storeFinalHtlcResolutions: opts.storeFinalHtlcResolutions, noRevLogAmtData: opts.NoRevLogAmtData, - graph: graph, } // Set the parent pointer (only used in tests). @@ -1612,11 +1606,6 @@ func (d *DB) applyOptionalVersions(cfg OptionalMiragtionConfig) error { return nil } -// ChannelGraph returns the current instance of the directed channel graph. -func (d *DB) ChannelGraph() *graphdb.ChannelGraph { - return d.graph -} - // ChannelStateDB returns the sub database that is concerned with the channel // state. func (d *DB) ChannelStateDB() *ChannelStateDB { @@ -1832,13 +1821,7 @@ func MakeTestDB(t *testing.T, modifiers ...OptionModifier) (*DB, error) { return nil, err } - graphDB, err := graphdb.NewChannelGraph(backend) - if err != nil { - backendCleanup() - return nil, err - } - - cdb, err := CreateWithBackend(backend, graphDB, modifiers...) + cdb, err := CreateWithBackend(backend, modifiers...) if err != nil { backendCleanup() return nil, err diff --git a/channeldb/db_test.go b/channeldb/db_test.go index bc7e3809c..1fd1f9d41 100644 --- a/channeldb/db_test.go +++ b/channeldb/db_test.go @@ -51,10 +51,7 @@ func TestOpenWithCreate(t *testing.T) { require.NoError(t, err, "unable to get test db backend") t.Cleanup(cleanup) - graphDB, err := graphdb.NewChannelGraph(backend) - require.NoError(t, err) - - cdb, err := CreateWithBackend(backend, graphDB) + cdb, err := CreateWithBackend(backend) require.NoError(t, err, "unable to create channeldb") if err := cdb.Close(); err != nil { t.Fatalf("unable to close channeldb: %v", err) @@ -90,10 +87,7 @@ func TestWipe(t *testing.T) { require.NoError(t, err, "unable to get test db backend") t.Cleanup(cleanup) - graphDB, err := graphdb.NewChannelGraph(backend) - require.NoError(t, err) - - fullDB, err := CreateWithBackend(backend, graphDB) + fullDB, err := CreateWithBackend(backend) require.NoError(t, err, "unable to create channeldb") defer fullDB.Close() @@ -194,7 +188,8 @@ func TestMultiSourceAddrsForNode(t *testing.T) { fullDB, err := MakeTestDB(t) require.NoError(t, err, "unable to make test database") - graph := fullDB.ChannelGraph() + graph, err := graphdb.MakeTestGraph(t) + require.NoError(t, err) // We'll make a test vertex to insert into the database, as the source // node, but this node will only have half the number of addresses it diff --git a/channeldb/meta_test.go b/channeldb/meta_test.go index 9be0779e7..5b6bd29a9 100644 --- a/channeldb/meta_test.go +++ b/channeldb/meta_test.go @@ -25,10 +25,9 @@ func applyMigration(t *testing.T, beforeMigration, afterMigration func(d *DB), // Create a test node that will be our source node. testNode := createTestVertex(t) - graph := cdb.ChannelGraph() - if err := graph.SetSourceNode(testNode); err != nil { - t.Fatal(err) - } + graph, err := graphdb.MakeTestGraph(t) + require.NoError(t, err) + require.NoError(t, graph.SetSourceNode(testNode)) // beforeMigration usually used for populating the database // with test data. @@ -423,10 +422,7 @@ func TestMigrationReversion(t *testing.T) { backend, cleanup, err := kvdb.GetTestBackend(tempDirName, "cdb") require.NoError(t, err, "unable to get test db backend") - graphDB, err := graphdb.NewChannelGraph(backend) - require.NoError(t, err) - - cdb, err := CreateWithBackend(backend, graphDB) + cdb, err := CreateWithBackend(backend) if err != nil { cleanup() t.Fatalf("unable to open channeldb: %v", err) @@ -452,10 +448,7 @@ func TestMigrationReversion(t *testing.T) { require.NoError(t, err, "unable to get test db backend") t.Cleanup(cleanup) - graphDB, err = graphdb.NewChannelGraph(backend) - require.NoError(t, err) - - _, err = CreateWithBackend(backend, graphDB) + _, err = CreateWithBackend(backend) if err != ErrDBReversion { t.Fatalf("unexpected error when opening channeldb, "+ "want: %v, got: %v", ErrDBReversion, err) @@ -683,11 +676,8 @@ func TestMarkerAndTombstone(t *testing.T) { err = db.View(EnsureNoTombstone, func() {}) require.ErrorContains(t, err, string(tombstoneText)) - graphDB, err := graphdb.NewChannelGraph(db.Backend) - require.NoError(t, err) - // Now that the DB has a tombstone, we should no longer be able to open // it once we close it. - _, err = CreateWithBackend(db.Backend, graphDB) + _, err = CreateWithBackend(db.Backend) require.ErrorContains(t, err, string(tombstoneText)) } diff --git a/config_builder.go b/config_builder.go index 3931f50d3..42650bb68 100644 --- a/config_builder.go +++ b/config_builder.go @@ -901,18 +901,10 @@ func (d *RPCSignerWalletImpl) BuildChainControl( type DatabaseInstances struct { // GraphDB is the database that stores the channel graph used for path // finding. - // - // NOTE/TODO: This currently _needs_ to be the same instance as the - // ChanStateDB below until the separation of the two databases is fully - // complete! - GraphDB *channeldb.DB + GraphDB *graphdb.ChannelGraph // ChanStateDB is the database that stores all of our node's channel // state. - // - // NOTE/TODO: This currently _needs_ to be the same instance as the - // GraphDB above until the separation of the two databases is fully - // complete! ChanStateDB *channeldb.DB // HeightHintDB is the database that stores height hints for spends. @@ -1040,7 +1032,7 @@ func (d *DefaultDatabaseBuilder) BuildDatabase( ) } - graphDB, err := graphdb.NewChannelGraph( + dbs.GraphDB, err = graphdb.NewChannelGraph( databaseBackends.GraphDB, graphDBOptions..., ) if err != nil { @@ -1066,8 +1058,8 @@ func (d *DefaultDatabaseBuilder) BuildDatabase( // Otherwise, we'll open two instances, one for the state we only need // locally, and the other for things we want to ensure are replicated. - dbs.GraphDB, err = channeldb.CreateWithBackend( - databaseBackends.ChanStateDB, graphDB, dbOptions..., + dbs.ChanStateDB, err = channeldb.CreateWithBackend( + databaseBackends.ChanStateDB, dbOptions..., ) switch { // Give the DB a chance to dry run the migration. Since we know that @@ -1085,27 +1077,14 @@ func (d *DefaultDatabaseBuilder) BuildDatabase( return nil, nil, err } - // For now, we don't _actually_ split the graph and channel state DBs on - // the code level. Since they both are based upon the *channeldb.DB - // struct it will require more refactoring to fully separate them. With - // the full remote mode we at least know for now that they both point to - // the same DB backend (and also namespace within that) so we only need - // to apply any migration once. - // - // TODO(guggero): Once the full separation of anything graph related - // from the channeldb.DB is complete, the decorated instance of the - // channel state DB should be created here individually instead of just - // using the same struct (and DB backend) instance. - dbs.ChanStateDB = dbs.GraphDB - // Instantiate a native SQL invoice store if the flag is set. if d.cfg.DB.UseNativeSQL { - // KV invoice db resides in the same database as the graph and - // channel state DB. Let's query the database to see if we have - // any invoices there. If we do, we won't allow the user to - // start lnd with native SQL enabled, as we don't currently - // migrate the invoices to the new database schema. - invoiceSlice, err := dbs.GraphDB.QueryInvoices( + // KV invoice db resides in the same database as the channel + // state DB. Let's query the database to see if we have any + // invoices there. If we do, we won't allow the user to start + // lnd with native SQL enabled, as we don't currently migrate + // the invoices to the new database schema. + invoiceSlice, err := dbs.ChanStateDB.QueryInvoices( ctx, invoices.InvoiceQuery{ NumMaxInvoices: 1, }, @@ -1139,7 +1118,7 @@ func (d *DefaultDatabaseBuilder) BuildDatabase( executor, clock.NewDefaultClock(), ) } else { - dbs.InvoiceDB = dbs.GraphDB + dbs.InvoiceDB = dbs.ChanStateDB } // Wrap the watchtower client DB and make sure we clean up. diff --git a/graph/db/graph.go b/graph/db/graph.go index b7c4671a3..ca0f58fe5 100644 --- a/graph/db/graph.go +++ b/graph/db/graph.go @@ -12,6 +12,7 @@ import ( "net" "sort" "sync" + "testing" "time" "github.com/btcsuite/btcd/btcec/v2" @@ -4775,3 +4776,31 @@ func deserializeChanEdgePolicyRaw(r io.Reader) (*models.ChannelEdgePolicy, return edge, nil } + +// MakeTestGraph creates a new instance of the ChannelGraph for testing purposes. +func MakeTestGraph(t testing.TB, modifiers ...OptionModifier) (*ChannelGraph, error) { + opts := DefaultOptions() + for _, modifier := range modifiers { + modifier(opts) + } + + // Next, create channelgraph for the first time. + backend, backendCleanup, err := kvdb.GetTestBackend(t.TempDir(), "cgr") + if err != nil { + backendCleanup() + return nil, err + } + + graph, err := NewChannelGraph(backend) + if err != nil { + backendCleanup() + return nil, err + } + + t.Cleanup(func() { + _ = backend.Close() + backendCleanup() + }) + + return graph, nil +} diff --git a/graph/db/graph_test.go b/graph/db/graph_test.go index 7b27037a0..60b3621a3 100644 --- a/graph/db/graph_test.go +++ b/graph/db/graph_test.go @@ -65,34 +65,6 @@ var ( } ) -// MakeTestGraph creates a new instance of the ChannelGraph for testing purposes. -func MakeTestGraph(t testing.TB, modifiers ...OptionModifier) (*ChannelGraph, error) { - opts := DefaultOptions() - for _, modifier := range modifiers { - modifier(opts) - } - - // Next, create channelgraph for the first time. - backend, backendCleanup, err := kvdb.GetTestBackend(t.TempDir(), "cgr") - if err != nil { - backendCleanup() - return nil, err - } - - graph, err := NewChannelGraph(backend) - if err != nil { - backendCleanup() - return nil, err - } - - t.Cleanup(func() { - _ = backend.Close() - backendCleanup() - }) - - return graph, nil -} - func createLightningNode(db kvdb.Backend, priv *btcec.PrivateKey) (*LightningNode, error) { updateTime := prand.Int63() diff --git a/peer/test_utils.go b/peer/test_utils.go index 9034bb5a9..8f60d08b9 100644 --- a/peer/test_utils.go +++ b/peer/test_utils.go @@ -19,9 +19,11 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channelnotifier" "github.com/lightningnetwork/lnd/fn" + graphdb "github.com/lightningnetwork/lnd/graph/db" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lntest/channels" "github.com/lightningnetwork/lnd/lntest/mock" "github.com/lightningnetwork/lnd/lntypes" @@ -607,10 +609,25 @@ func createTestPeer(t *testing.T) *peerTestCtx { const chanActiveTimeout = time.Minute - dbAlice, err := channeldb.Open(t.TempDir()) + dbPath := t.TempDir() + + graphBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{ + DBPath: dbPath, + DBFileName: "graph.db", + NoFreelistSync: true, + AutoCompact: false, + AutoCompactMinAge: kvdb.DefaultBoltAutoCompactMinAge, + DBTimeout: kvdb.DefaultDBTimeout, + }) + require.NoError(t, err) + + dbAliceGraph, err := graphdb.NewChannelGraph(graphBackend) + require.NoError(t, err) + + dbAliceChannel, err := channeldb.Open(dbPath) require.NoError(t, err) t.Cleanup(func() { - require.NoError(t, dbAlice.Close()) + require.NoError(t, dbAliceChannel.Close()) }) nodeSignerAlice := netann.NewNodeSigner(aliceKeySigner) @@ -620,8 +637,8 @@ func createTestPeer(t *testing.T) *peerTestCtx { ChanStatusSampleInterval: 30 * time.Second, ChanEnableTimeout: chanActiveTimeout, ChanDisableTimeout: 2 * time.Minute, - DB: dbAlice.ChannelStateDB(), - Graph: dbAlice.ChannelGraph(), + DB: dbAliceChannel.ChannelStateDB(), + Graph: dbAliceGraph, MessageSigner: nodeSignerAlice, OurPubKey: aliceKeyPub, OurKeyLoc: testKeyLoc, @@ -663,7 +680,7 @@ func createTestPeer(t *testing.T) *peerTestCtx { mockSwitch := &mockMessageSwitch{} // TODO(yy): change ChannelNotifier to be an interface. - channelNotifier := channelnotifier.New(dbAlice.ChannelStateDB()) + channelNotifier := channelnotifier.New(dbAliceChannel.ChannelStateDB()) require.NoError(t, channelNotifier.Start()) t.Cleanup(func() { require.NoError(t, channelNotifier.Stop(), @@ -707,7 +724,7 @@ func createTestPeer(t *testing.T) *peerTestCtx { Switch: mockSwitch, ChanActiveTimeout: chanActiveTimeout, InterceptSwitch: interceptableSwitch, - ChannelDB: dbAlice.ChannelStateDB(), + ChannelDB: dbAliceChannel.ChannelStateDB(), FeeEstimator: estimator, Wallet: wallet, ChainNotifier: notifier, @@ -749,7 +766,7 @@ func createTestPeer(t *testing.T) *peerTestCtx { mockSwitch: mockSwitch, peer: alicePeer, notifier: notifier, - db: dbAlice, + db: dbAliceChannel, privKey: aliceKeyPriv, mockConn: mockConn, customChan: receivedCustomChan, diff --git a/server.go b/server.go index d441624b6..feb24cd2a 100644 --- a/server.go +++ b/server.go @@ -612,7 +612,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, s := &server{ cfg: cfg, implCfg: implCfg, - graphDB: dbs.GraphDB.ChannelGraph(), + graphDB: dbs.GraphDB, chanStateDB: dbs.ChanStateDB.ChannelStateDB(), addrSource: addrSource, miscDB: dbs.ChanStateDB, @@ -766,7 +766,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, IsChannelActive: s.htlcSwitch.HasActiveLink, ApplyChannelUpdate: s.applyChannelUpdate, DB: s.chanStateDB, - Graph: dbs.GraphDB.ChannelGraph(), + Graph: dbs.GraphDB, } chanStatusMgr, err := netann.NewChanStatusManager(chanStatusMgrCfg) @@ -856,10 +856,6 @@ func newServer(cfg *Config, listenAddrs []net.Addr, selfAddrs := make([]net.Addr, 0, len(externalIPs)) selfAddrs = append(selfAddrs, externalIPs...) - // As the graph can be obtained at anytime from the network, we won't - // replicate it, and instead it'll only be stored locally. - chanGraph := dbs.GraphDB.ChannelGraph() - // We'll now reconstruct a node announcement based on our current // configuration so we can send it out as a sort of heart beat within // the network. @@ -918,7 +914,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, // Finally, we'll update the representation on disk, and update our // cached in-memory version as well. - if err := chanGraph.SetSourceNode(selfNode); err != nil { + if err := dbs.GraphDB.SetSourceNode(selfNode); err != nil { return nil, fmt.Errorf("can't set self node: %w", err) } s.currentNodeAnn = nodeAnn @@ -1018,13 +1014,13 @@ func newServer(cfg *Config, listenAddrs []net.Addr, MinProbability: routingConfig.MinRouteProbability, } - sourceNode, err := chanGraph.SourceNode() + sourceNode, err := dbs.GraphDB.SourceNode() if err != nil { return nil, fmt.Errorf("error getting source node: %w", err) } paymentSessionSource := &routing.SessionSource{ GraphSessionFactory: graphsession.NewGraphSessionFactory( - chanGraph, + dbs.GraphDB, ), SourceNode: sourceNode, MissionControl: s.defaultMC, @@ -1041,7 +1037,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, s.graphBuilder, err = graph.NewBuilder(&graph.Config{ SelfNode: selfNode.PubKeyBytes, - Graph: chanGraph, + Graph: dbs.GraphDB, Chain: cc.ChainIO, ChainView: cc.ChainView, Notifier: cc.ChainNotifier, @@ -1058,7 +1054,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, s.chanRouter, err = routing.New(routing.Config{ SelfNode: selfNode.PubKeyBytes, - RoutingGraph: graphsession.NewRoutingGraph(chanGraph), + RoutingGraph: graphsession.NewRoutingGraph(dbs.GraphDB), Chain: cc.ChainIO, Payer: s.htlcSwitch, Control: s.controlTower,