• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 17132206455

21 Aug 2025 03:56PM UTC coverage: 54.685% (-2.6%) from 57.321%
17132206455

Pull #10167

github

web-flow
Merge 5dd2ed093 into 0c2f045f5
Pull Request #10167: multi: bump Go to 1.24.6

4 of 31 new or added lines in 10 files covered. (12.9%)

23854 existing lines in 284 files now uncovered.

108937 of 199210 relevant lines covered (54.68%)

22026.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

4.88
/lncfg/db.go
1
package lncfg
2

3
import (
4
        "context"
5
        "fmt"
6
        "path"
7
        "path/filepath"
8
        "time"
9

10
        "github.com/btcsuite/btclog/v2"
11
        "github.com/lightningnetwork/lnd/kvdb"
12
        "github.com/lightningnetwork/lnd/kvdb/etcd"
13
        "github.com/lightningnetwork/lnd/kvdb/postgres"
14
        "github.com/lightningnetwork/lnd/kvdb/sqlbase"
15
        "github.com/lightningnetwork/lnd/kvdb/sqlite"
16
        "github.com/lightningnetwork/lnd/lnrpc"
17
        "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
18
        "github.com/lightningnetwork/lnd/sqldb"
19
)
20

21
const (
22
        ChannelDBName     = "channel.db"
23
        MacaroonDBName    = "macaroons.db"
24
        DecayedLogDbName  = "sphinxreplay.db"
25
        TowerClientDBName = "wtclient.db"
26
        TowerServerDBName = "watchtower.db"
27
        WalletDBName      = "wallet.db"
28
        NeutrinoDBName    = "neutrino.db"
29

30
        SqliteChannelDBName  = "channel.sqlite"
31
        SqliteChainDBName    = "chain.sqlite"
32
        SqliteNeutrinoDBName = "neutrino.sqlite"
33
        SqliteTowerDBName    = "watchtower.sqlite"
34
        SqliteNativeDBName   = "lnd.sqlite"
35

36
        BoltBackend                = "bolt"
37
        EtcdBackend                = "etcd"
38
        PostgresBackend            = "postgres"
39
        SqliteBackend              = "sqlite"
40
        DefaultBatchCommitInterval = 500 * time.Millisecond
41

42
        defaultPostgresMaxConnections = 50
43
        defaultSqliteMaxConnections   = 2
44

45
        defaultSqliteBusyTimeout = 5 * time.Second
46

47
        // NSChannelDB is the namespace name that we use for the combined graph
48
        // and channel state DB.
49
        NSChannelDB = "channeldb"
50

51
        // NSMacaroonDB is the namespace name that we use for the macaroon DB.
52
        NSMacaroonDB = "macaroondb"
53

54
        // NSDecayedLogDB is the namespace name that we use for the sphinx
55
        // replay a.k.a. decayed log DB.
56
        NSDecayedLogDB = "decayedlogdb"
57

58
        // NSTowerClientDB is the namespace name that we use for the watchtower
59
        // client DB.
60
        NSTowerClientDB = "towerclientdb"
61

62
        // NSTowerServerDB is the namespace name that we use for the watchtower
63
        // server DB.
64
        NSTowerServerDB = "towerserverdb"
65

66
        // NSWalletDB is the namespace name that we use for the wallet DB.
67
        NSWalletDB = "walletdb"
68

69
        // NSNeutrinoDB is the namespace name that we use for the neutrino DB.
70
        NSNeutrinoDB = "neutrinodb"
71
)
72

73
// DB holds database configuration for LND.
74
//
75
//nolint:ll
76
type DB struct {
77
        Backend string `long:"backend" description:"The selected database backend."`
78

79
        BatchCommitInterval time.Duration `long:"batch-commit-interval" description:"The maximum duration the channel graph batch schedulers will wait before attempting to commit a batch of pending updates. This can be tradeoff database contenion for commit latency."`
80

81
        Etcd *etcd.Config `group:"etcd" namespace:"etcd" description:"Etcd settings."`
82

83
        Bolt *kvdb.BoltConfig `group:"bolt" namespace:"bolt" description:"Bolt settings."`
84

85
        Postgres *sqldb.PostgresConfig `group:"postgres" namespace:"postgres" description:"Postgres settings."`
86

87
        Sqlite *sqldb.SqliteConfig `group:"sqlite" namespace:"sqlite" description:"Sqlite settings."`
88

89
        UseNativeSQL bool `long:"use-native-sql" description:"If set to true, native SQL will be used instead of KV emulation for tables that support it. Subsystems which support native SQL tables: Invoices."`
90

91
        SkipNativeSQLMigration bool `long:"skip-native-sql-migration" description:"If set to true, the KV to native SQL migration will be skipped. Note that this option is intended for users who experience non-resolvable migration errors. Enabling after there is a non-resolvable migration error that resulted in an incomplete migration will cause that partial migration to be abandoned and ignored and an empty database will be used instead. Since invoices are currently the only native SQL database used, our channels will still work but the invoice history will be forgotten. This option has no effect if native SQL is not in use (db.use-native-sql=false)."`
92

93
        NoGraphCache bool `long:"no-graph-cache" description:"Don't use the in-memory graph cache for path finding. Much slower but uses less RAM. Can only be used with a bolt database backend."`
94

95
        PruneRevocation bool `long:"prune-revocation" description:"Run the optional migration that prunes the revocation logs to save disk space."`
96

97
        NoRevLogAmtData bool `long:"no-rev-log-amt-data" description:"If set, the to-local and to-remote output amounts of revoked commitment transactions will not be stored in the revocation log. Note that once this data is lost, a watchtower client will not be able to back up the revoked state."`
98

99
        NoGcDecayedLog bool `long:"no-gc-decayed-log" description:"Do not run the optional migration that garbage collects the decayed log to save disk space."`
100
}
101

102
// DefaultDB creates and returns a new default DB config.
103
func DefaultDB() *DB {
2✔
104
        return &DB{
2✔
105
                Backend:             BoltBackend,
2✔
106
                BatchCommitInterval: DefaultBatchCommitInterval,
2✔
107
                Bolt: &kvdb.BoltConfig{
2✔
108
                        NoFreelistSync:    true,
2✔
109
                        AutoCompactMinAge: kvdb.DefaultBoltAutoCompactMinAge,
2✔
110
                        DBTimeout:         kvdb.DefaultDBTimeout,
2✔
111
                },
2✔
112
                Etcd: &etcd.Config{
2✔
113
                        // Allow at most 32 MiB messages by default.
2✔
114
                        MaxMsgSize: 32768 * 1024,
2✔
115
                },
2✔
116
                Postgres: &sqldb.PostgresConfig{
2✔
117
                        MaxConnections: defaultPostgresMaxConnections,
2✔
118
                        QueryConfig:    *sqldb.DefaultPostgresConfig(),
2✔
119
                },
2✔
120
                Sqlite: &sqldb.SqliteConfig{
2✔
121
                        MaxConnections: defaultSqliteMaxConnections,
2✔
122
                        BusyTimeout:    defaultSqliteBusyTimeout,
2✔
123
                        QueryConfig:    *sqldb.DefaultSQLiteConfig(),
2✔
124
                },
2✔
125
                UseNativeSQL:           false,
2✔
126
                SkipNativeSQLMigration: false,
2✔
127
        }
2✔
128
}
2✔
129

130
// Validate validates the DB config.
UNCOV
131
func (db *DB) Validate() error {
×
UNCOV
132
        switch db.Backend {
×
UNCOV
133
        case BoltBackend:
×
UNCOV
134
                if db.UseNativeSQL {
×
135
                        return fmt.Errorf("cannot use native SQL with bolt " +
×
136
                                "backend")
×
137
                }
×
138

139
        case SqliteBackend:
×
140
                if err := db.Sqlite.Validate(); err != nil {
×
141
                        return err
×
142
                }
×
143
        case PostgresBackend:
×
144
                if err := db.Postgres.Validate(); err != nil {
×
145
                        return err
×
146
                }
×
147

148
        case EtcdBackend:
×
149
                if db.UseNativeSQL {
×
150
                        return fmt.Errorf("cannot use native SQL with etcd " +
×
151
                                "backend")
×
152
                }
×
153

154
                if !db.Etcd.Embedded && db.Etcd.Host == "" {
×
155
                        return fmt.Errorf("etcd host must be set")
×
156
                }
×
157

158
        default:
×
159
                return fmt.Errorf("unknown backend, must be either '%v', "+
×
160
                        "'%v', '%v' or '%v'", BoltBackend, EtcdBackend,
×
161
                        PostgresBackend, SqliteBackend)
×
162
        }
163

164
        // The path finding uses a manual read transaction that's open for a
165
        // potentially long time. That works fine with the locking model of
166
        // bbolt but can lead to locks or rolled back transactions with etcd or
167
        // postgres. And since we already have a smaller memory footprint for
168
        // remote database setups (due to not needing to memory-map the bbolt DB
169
        // files), we can keep the graph in memory instead. But for mobile
170
        // devices the tradeoff between a smaller memory footprint and the
171
        // longer time needed for path finding might be a desirable one.
UNCOV
172
        if db.NoGraphCache && db.Backend != BoltBackend {
×
173
                return fmt.Errorf("cannot use no-graph-cache with database "+
×
174
                        "backend '%v'", db.Backend)
×
175
        }
×
176

UNCOV
177
        return nil
×
178
}
179

180
// Init should be called upon start to pre-initialize database access dependent
181
// on configuration.
UNCOV
182
func (db *DB) Init(ctx context.Context, dbPath string) error {
×
UNCOV
183
        // Start embedded etcd server if requested.
×
UNCOV
184
        switch {
×
185
        case db.Backend == EtcdBackend && db.Etcd.Embedded:
×
186
                cfg, _, err := kvdb.StartEtcdTestBackend(
×
187
                        dbPath, db.Etcd.EmbeddedClientPort,
×
188
                        db.Etcd.EmbeddedPeerPort, db.Etcd.EmbeddedLogFile,
×
189
                )
×
190
                if err != nil {
×
191
                        return err
×
192
                }
×
193

194
                // Override the original config with the config for
195
                // the embedded instance.
196
                db.Etcd = cfg
×
197

198
        case db.Backend == PostgresBackend:
×
199
                sqlbase.Init(db.Postgres.MaxConnections)
×
200

201
        case db.Backend == SqliteBackend:
×
202
                sqlbase.Init(db.Sqlite.MaxConnections)
×
203
        }
204

UNCOV
205
        return nil
×
206
}
207

208
// DatabaseBackends is a two-tuple that holds the set of active database
209
// backends for the daemon. The two backends we expose are the graph database
210
// backend, and the channel state backend.
211
type DatabaseBackends struct {
212
        // GraphDB points to the database backend that contains the less
213
        // critical data that is accessed often, such as the channel graph and
214
        // chain height hints.
215
        GraphDB kvdb.Backend
216

217
        // ChanStateDB points to a possibly networked replicated backend that
218
        // contains the critical channel state related data.
219
        ChanStateDB kvdb.Backend
220

221
        // HeightHintDB points to a possibly networked replicated backend that
222
        // contains the chain height hint related data.
223
        HeightHintDB kvdb.Backend
224

225
        // MacaroonDB points to a database backend that stores the macaroon root
226
        // keys.
227
        MacaroonDB kvdb.Backend
228

229
        // DecayedLogDB points to a database backend that stores the decayed log
230
        // data.
231
        DecayedLogDB kvdb.Backend
232

233
        // TowerClientDB points to a database backend that stores the watchtower
234
        // client data. This might be nil if the watchtower client is disabled.
235
        TowerClientDB kvdb.Backend
236

237
        // TowerServerDB points to a database backend that stores the watchtower
238
        // server data. This might be nil if the watchtower server is disabled.
239
        TowerServerDB kvdb.Backend
240

241
        // WalletDB is an option that instructs the wallet loader where to load
242
        // the underlying wallet database from.
243
        WalletDB btcwallet.LoaderOption
244

245
        // NativeSQLStore holds a reference to the native SQL store that can
246
        // be used for native SQL queries for tables that already support it.
247
        // This may be nil if the use-native-sql flag was not set.
248
        NativeSQLStore sqldb.DB
249

250
        // Remote indicates whether the database backends are remote, possibly
251
        // replicated instances or local bbolt or sqlite backed databases.
252
        Remote bool
253

254
        // CloseFuncs is a map of close functions for each of the initialized
255
        // DB backends keyed by their namespace name.
256
        CloseFuncs map[string]func() error
257
}
258

259
// GetPostgresConfigKVDB converts a sqldb.PostgresConfig to a kvdb
260
// postgres.Config.
261
func GetPostgresConfigKVDB(cfg *sqldb.PostgresConfig) *postgres.Config {
×
262
        return &postgres.Config{
×
263
                Dsn:            cfg.Dsn,
×
264
                Timeout:        cfg.Timeout,
×
265
                MaxConnections: cfg.MaxConnections,
×
266
        }
×
267
}
×
268

269
// GetSqliteConfigKVDB converts a sqldb.SqliteConfig to a kvdb sqlite.Config.
270
func GetSqliteConfigKVDB(cfg *sqldb.SqliteConfig) *sqlite.Config {
×
271
        return &sqlite.Config{
×
272
                Timeout:        cfg.Timeout,
×
273
                BusyTimeout:    cfg.BusyTimeout,
×
274
                MaxConnections: cfg.MaxConnections,
×
275
                PragmaOptions:  cfg.PragmaOptions,
×
276
        }
×
277
}
×
278

279
// GetBackends returns a set of kvdb.Backends as set in the DB config.
280
func (db *DB) GetBackends(ctx context.Context, chanDBPath,
281
        walletDBPath, towerServerDBPath string, towerClientEnabled,
282
        towerServerEnabled bool, logger btclog.Logger) (*DatabaseBackends,
UNCOV
283
        error) {
×
UNCOV
284

×
UNCOV
285
        // We keep track of all the kvdb backends we actually open and return a
×
UNCOV
286
        // reference to their close function so they can be cleaned up properly
×
UNCOV
287
        // on error or shutdown.
×
UNCOV
288
        closeFuncs := make(map[string]func() error)
×
UNCOV
289

×
UNCOV
290
        // If we need to return early because of an error, we invoke any close
×
UNCOV
291
        // function that has been initialized so far.
×
UNCOV
292
        returnEarly := true
×
UNCOV
293
        defer func() {
×
UNCOV
294
                if !returnEarly {
×
UNCOV
295
                        return
×
UNCOV
296
                }
×
297

298
                for _, closeFunc := range closeFuncs {
×
299
                        _ = closeFunc()
×
300
                }
×
301
        }()
302

UNCOV
303
        switch db.Backend {
×
304
        case EtcdBackend:
×
305
                // As long as the graph data, channel state and height hint
×
306
                // cache are all still in the channel.db file in bolt, we
×
307
                // replicate the same behavior here and use the same etcd
×
308
                // backend for those three sub DBs. But we namespace it properly
×
309
                // to make such a split even easier in the future. This will
×
310
                // break lnd for users that ran on etcd with 0.13.x since that
×
311
                // code used the root namespace. We assume that nobody used etcd
×
312
                // for mainnet just yet since that feature was clearly marked as
×
313
                // experimental in 0.13.x.
×
314
                etcdBackend, err := kvdb.Open(
×
315
                        kvdb.EtcdBackendName, ctx,
×
316
                        db.Etcd.CloneWithSubNamespace(NSChannelDB),
×
317
                )
×
318
                if err != nil {
×
319
                        return nil, fmt.Errorf("error opening etcd DB: %w", err)
×
320
                }
×
321
                closeFuncs[NSChannelDB] = etcdBackend.Close
×
322

×
323
                etcdMacaroonBackend, err := kvdb.Open(
×
324
                        kvdb.EtcdBackendName, ctx,
×
325
                        db.Etcd.CloneWithSubNamespace(NSMacaroonDB),
×
326
                )
×
327
                if err != nil {
×
328
                        return nil, fmt.Errorf("error opening etcd macaroon "+
×
329
                                "DB: %v", err)
×
330
                }
×
331
                closeFuncs[NSMacaroonDB] = etcdMacaroonBackend.Close
×
332

×
333
                etcdDecayedLogBackend, err := kvdb.Open(
×
334
                        kvdb.EtcdBackendName, ctx,
×
335
                        db.Etcd.CloneWithSubNamespace(NSDecayedLogDB),
×
336
                )
×
337
                if err != nil {
×
338
                        return nil, fmt.Errorf("error opening etcd decayed "+
×
339
                                "log DB: %v", err)
×
340
                }
×
341
                closeFuncs[NSDecayedLogDB] = etcdDecayedLogBackend.Close
×
342

×
343
                etcdTowerClientBackend, err := kvdb.Open(
×
344
                        kvdb.EtcdBackendName, ctx,
×
345
                        db.Etcd.CloneWithSubNamespace(NSTowerClientDB),
×
346
                )
×
347
                if err != nil {
×
348
                        return nil, fmt.Errorf("error opening etcd tower "+
×
349
                                "client DB: %v", err)
×
350
                }
×
351
                closeFuncs[NSTowerClientDB] = etcdTowerClientBackend.Close
×
352

×
353
                etcdTowerServerBackend, err := kvdb.Open(
×
354
                        kvdb.EtcdBackendName, ctx,
×
355
                        db.Etcd.CloneWithSubNamespace(NSTowerServerDB),
×
356
                )
×
357
                if err != nil {
×
358
                        return nil, fmt.Errorf("error opening etcd tower "+
×
359
                                "server DB: %v", err)
×
360
                }
×
361
                closeFuncs[NSTowerServerDB] = etcdTowerServerBackend.Close
×
362

×
363
                etcdWalletBackend, err := kvdb.Open(
×
364
                        kvdb.EtcdBackendName, ctx,
×
365
                        db.Etcd.
×
366
                                CloneWithSubNamespace(NSWalletDB).
×
367
                                CloneWithSingleWriter(),
×
368
                )
×
369
                if err != nil {
×
370
                        return nil, fmt.Errorf("error opening etcd macaroon "+
×
371
                                "DB: %v", err)
×
372
                }
×
373
                closeFuncs[NSWalletDB] = etcdWalletBackend.Close
×
374

×
375
                returnEarly = false
×
376

×
377
                return &DatabaseBackends{
×
378
                        GraphDB:       etcdBackend,
×
379
                        ChanStateDB:   etcdBackend,
×
380
                        HeightHintDB:  etcdBackend,
×
381
                        MacaroonDB:    etcdMacaroonBackend,
×
382
                        DecayedLogDB:  etcdDecayedLogBackend,
×
383
                        TowerClientDB: etcdTowerClientBackend,
×
384
                        TowerServerDB: etcdTowerServerBackend,
×
385
                        // The wallet loader will attempt to use/create the
×
386
                        // wallet in the replicated remote DB if we're running
×
387
                        // in a clustered environment. This will ensure that all
×
388
                        // members of the cluster have access to the same wallet
×
389
                        // state.
×
390
                        WalletDB: btcwallet.LoaderWithExternalWalletDB(
×
391
                                etcdWalletBackend,
×
392
                        ),
×
393
                        Remote:     true,
×
394
                        CloseFuncs: closeFuncs,
×
395
                }, nil
×
396

397
        case PostgresBackend:
×
398
                // Convert the sqldb PostgresConfig to a kvdb postgres.Config.
×
399
                // This is a temporary measure until we migrate all kvdb SQL
×
400
                // users to native SQL.
×
401
                postgresConfig := GetPostgresConfigKVDB(db.Postgres)
×
402

×
403
                postgresBackend, err := kvdb.Open(
×
404
                        kvdb.PostgresBackendName, ctx,
×
405
                        postgresConfig, NSChannelDB,
×
406
                )
×
407
                if err != nil {
×
408
                        return nil, fmt.Errorf("error opening postgres graph "+
×
409
                                "DB: %v", err)
×
410
                }
×
411
                closeFuncs[NSChannelDB] = postgresBackend.Close
×
412

×
413
                postgresMacaroonBackend, err := kvdb.Open(
×
414
                        kvdb.PostgresBackendName, ctx,
×
415
                        postgresConfig, NSMacaroonDB,
×
416
                )
×
417
                if err != nil {
×
418
                        return nil, fmt.Errorf("error opening postgres "+
×
419
                                "macaroon DB: %v", err)
×
420
                }
×
421
                closeFuncs[NSMacaroonDB] = postgresMacaroonBackend.Close
×
422

×
423
                postgresDecayedLogBackend, err := kvdb.Open(
×
424
                        kvdb.PostgresBackendName, ctx,
×
425
                        postgresConfig, NSDecayedLogDB,
×
426
                )
×
427
                if err != nil {
×
428
                        return nil, fmt.Errorf("error opening postgres "+
×
429
                                "decayed log DB: %v", err)
×
430
                }
×
431
                closeFuncs[NSDecayedLogDB] = postgresDecayedLogBackend.Close
×
432

×
433
                postgresTowerClientBackend, err := kvdb.Open(
×
434
                        kvdb.PostgresBackendName, ctx,
×
435
                        postgresConfig, NSTowerClientDB,
×
436
                )
×
437
                if err != nil {
×
438
                        return nil, fmt.Errorf("error opening postgres tower "+
×
439
                                "client DB: %v", err)
×
440
                }
×
441
                closeFuncs[NSTowerClientDB] = postgresTowerClientBackend.Close
×
442

×
443
                postgresTowerServerBackend, err := kvdb.Open(
×
444
                        kvdb.PostgresBackendName, ctx,
×
445
                        postgresConfig, NSTowerServerDB,
×
446
                )
×
447
                if err != nil {
×
448
                        return nil, fmt.Errorf("error opening postgres tower "+
×
449
                                "server DB: %v", err)
×
450
                }
×
451
                closeFuncs[NSTowerServerDB] = postgresTowerServerBackend.Close
×
452

×
453
                // The wallet subsystem is still not robust enough to run it
×
454
                // without a single writer in postgres therefore we create a
×
455
                // new config with the global lock enabled.
×
456
                //
×
457
                // NOTE: This is a temporary measure and should be removed as
×
458
                // soon as the wallet code is more robust.
×
459
                postgresConfigWalletDB := GetPostgresConfigKVDB(db.Postgres)
×
460
                postgresConfigWalletDB.WithGlobalLock = true
×
461

×
462
                postgresWalletBackend, err := kvdb.Open(
×
463
                        kvdb.PostgresBackendName, ctx,
×
464
                        postgresConfigWalletDB, NSWalletDB,
×
465
                )
×
466
                if err != nil {
×
467
                        return nil, fmt.Errorf("error opening postgres wallet "+
×
468
                                "DB: %v", err)
×
469
                }
×
470
                closeFuncs[NSWalletDB] = postgresWalletBackend.Close
×
471

×
472
                var nativeSQLStore sqldb.DB
×
473
                if db.UseNativeSQL {
×
474
                        nativePostgresStore, err := sqldb.NewPostgresStore(
×
475
                                db.Postgres,
×
476
                        )
×
477
                        if err != nil {
×
478
                                return nil, fmt.Errorf("error opening "+
×
479
                                        "native postgres store: %v", err)
×
480
                        }
×
481

482
                        nativeSQLStore = nativePostgresStore
×
483
                        closeFuncs[PostgresBackend] = nativePostgresStore.Close
×
484
                }
485

486
                // Warn if the user is trying to switch over to a Postgres DB
487
                // while there is a wallet or channel bbolt DB still present.
488
                warnExistingBoltDBs(
×
489
                        logger, "postgres", walletDBPath, WalletDBName,
×
490
                )
×
491
                warnExistingBoltDBs(
×
492
                        logger, "postgres", chanDBPath, ChannelDBName,
×
493
                )
×
494

×
495
                returnEarly = false
×
496

×
497
                return &DatabaseBackends{
×
498
                        GraphDB:       postgresBackend,
×
499
                        ChanStateDB:   postgresBackend,
×
500
                        HeightHintDB:  postgresBackend,
×
501
                        MacaroonDB:    postgresMacaroonBackend,
×
502
                        DecayedLogDB:  postgresDecayedLogBackend,
×
503
                        TowerClientDB: postgresTowerClientBackend,
×
504
                        TowerServerDB: postgresTowerServerBackend,
×
505
                        // The wallet loader will attempt to use/create the
×
506
                        // wallet in the replicated remote DB if we're running
×
507
                        // in a clustered environment. This will ensure that all
×
508
                        // members of the cluster have access to the same wallet
×
509
                        // state.
×
510
                        WalletDB: btcwallet.LoaderWithExternalWalletDB(
×
511
                                postgresWalletBackend,
×
512
                        ),
×
513
                        NativeSQLStore: nativeSQLStore,
×
514
                        Remote:         true,
×
515
                        CloseFuncs:     closeFuncs,
×
516
                }, nil
×
517

518
        case SqliteBackend:
×
519
                // Convert the sqldb SqliteConfig to a kvdb sqlite.Config.
×
520
                // This is a temporary measure until we migrate all kvdb SQL
×
521
                // users to native SQL.
×
522
                sqliteConfig := GetSqliteConfigKVDB(db.Sqlite)
×
523

×
524
                // Note that for sqlite, we put kv tables for the channel.db,
×
525
                // wtclient.db and sphinxreplay.db all in the channel.sqlite db.
×
526
                // The tables for wallet.db and macaroon.db are in the
×
527
                // chain.sqlite db and watchtower.db tables are in the
×
528
                // watchtower.sqlite db. The reason for the multiple sqlite dbs
×
529
                // is twofold. The first reason is that it maintains the file
×
530
                // structure that users are used to. The second reason is the
×
531
                // fact that sqlite only supports one writer at a time which
×
532
                // would cause deadlocks in the code due to the wallet db often
×
533
                // being accessed during a write to another db.
×
534
                sqliteBackend, err := kvdb.Open(
×
535
                        kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
×
536
                        SqliteChannelDBName, NSChannelDB,
×
537
                )
×
538
                if err != nil {
×
539
                        return nil, fmt.Errorf("error opening sqlite graph "+
×
540
                                "DB: %v", err)
×
541
                }
×
542
                closeFuncs[NSChannelDB] = sqliteBackend.Close
×
543

×
544
                sqliteMacaroonBackend, err := kvdb.Open(
×
545
                        kvdb.SqliteBackendName, ctx, sqliteConfig, walletDBPath,
×
546
                        SqliteChainDBName, NSMacaroonDB,
×
547
                )
×
548
                if err != nil {
×
549
                        return nil, fmt.Errorf("error opening sqlite "+
×
550
                                "macaroon DB: %v", err)
×
551
                }
×
552
                closeFuncs[NSMacaroonDB] = sqliteMacaroonBackend.Close
×
553

×
554
                sqliteDecayedLogBackend, err := kvdb.Open(
×
555
                        kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
×
556
                        SqliteChannelDBName, NSDecayedLogDB,
×
557
                )
×
558
                if err != nil {
×
559
                        return nil, fmt.Errorf("error opening sqlite decayed "+
×
560
                                "log DB: %v", err)
×
561
                }
×
562
                closeFuncs[NSDecayedLogDB] = sqliteDecayedLogBackend.Close
×
563

×
564
                sqliteTowerClientBackend, err := kvdb.Open(
×
565
                        kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
×
566
                        SqliteChannelDBName, NSTowerClientDB,
×
567
                )
×
568
                if err != nil {
×
569
                        return nil, fmt.Errorf("error opening sqlite tower "+
×
570
                                "client DB: %v", err)
×
571
                }
×
572
                closeFuncs[NSTowerClientDB] = sqliteTowerClientBackend.Close
×
573

×
574
                sqliteTowerServerBackend, err := kvdb.Open(
×
575
                        kvdb.SqliteBackendName, ctx, sqliteConfig,
×
576
                        towerServerDBPath, SqliteTowerDBName, NSTowerServerDB,
×
577
                )
×
578
                if err != nil {
×
579
                        return nil, fmt.Errorf("error opening sqlite tower "+
×
580
                                "server DB: %v", err)
×
581
                }
×
582
                closeFuncs[NSTowerServerDB] = sqliteTowerServerBackend.Close
×
583

×
584
                sqliteWalletBackend, err := kvdb.Open(
×
585
                        kvdb.SqliteBackendName, ctx, sqliteConfig, walletDBPath,
×
586
                        SqliteChainDBName, NSWalletDB,
×
587
                )
×
588
                if err != nil {
×
589
                        return nil, fmt.Errorf("error opening sqlite macaroon "+
×
590
                                "DB: %v", err)
×
591
                }
×
592
                closeFuncs[NSWalletDB] = sqliteWalletBackend.Close
×
593

×
594
                var nativeSQLStore sqldb.DB
×
595
                if db.UseNativeSQL {
×
596
                        nativeSQLiteStore, err := sqldb.NewSqliteStore(
×
597
                                db.Sqlite,
×
598
                                path.Join(chanDBPath, SqliteNativeDBName),
×
599
                        )
×
600
                        if err != nil {
×
601
                                return nil, fmt.Errorf("error opening "+
×
602
                                        "native SQLite store: %v", err)
×
603
                        }
×
604

605
                        nativeSQLStore = nativeSQLiteStore
×
606
                        closeFuncs[SqliteBackend] = nativeSQLiteStore.Close
×
607
                }
608

609
                // Warn if the user is trying to switch over to a sqlite DB
610
                // while there is a wallet or channel bbolt DB still present.
611
                warnExistingBoltDBs(
×
612
                        logger, "sqlite", walletDBPath, WalletDBName,
×
613
                )
×
614
                warnExistingBoltDBs(
×
615
                        logger, "sqlite", chanDBPath, ChannelDBName,
×
616
                )
×
617

×
618
                returnEarly = false
×
619

×
620
                return &DatabaseBackends{
×
621
                        GraphDB:       sqliteBackend,
×
622
                        ChanStateDB:   sqliteBackend,
×
623
                        HeightHintDB:  sqliteBackend,
×
624
                        MacaroonDB:    sqliteMacaroonBackend,
×
625
                        DecayedLogDB:  sqliteDecayedLogBackend,
×
626
                        TowerClientDB: sqliteTowerClientBackend,
×
627
                        TowerServerDB: sqliteTowerServerBackend,
×
628
                        // The wallet loader will attempt to use/create the
×
629
                        // wallet in the replicated remote DB if we're running
×
630
                        // in a clustered environment. This will ensure that all
×
631
                        // members of the cluster have access to the same wallet
×
632
                        // state.
×
633
                        WalletDB: btcwallet.LoaderWithExternalWalletDB(
×
634
                                sqliteWalletBackend,
×
635
                        ),
×
636
                        NativeSQLStore: nativeSQLStore,
×
637
                        CloseFuncs:     closeFuncs,
×
638
                }, nil
×
639
        }
640

641
        // We're using all bbolt based databases by default.
UNCOV
642
        boltBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
×
UNCOV
643
                DBPath:            chanDBPath,
×
UNCOV
644
                DBFileName:        ChannelDBName,
×
UNCOV
645
                DBTimeout:         db.Bolt.DBTimeout,
×
UNCOV
646
                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
UNCOV
647
                AutoCompact:       db.Bolt.AutoCompact,
×
UNCOV
648
                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
UNCOV
649
        })
×
UNCOV
650
        if err != nil {
×
651
                return nil, fmt.Errorf("error opening bolt DB: %w", err)
×
652
        }
×
UNCOV
653
        closeFuncs[NSChannelDB] = boltBackend.Close
×
UNCOV
654

×
UNCOV
655
        macaroonBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
×
UNCOV
656
                DBPath:            walletDBPath,
×
UNCOV
657
                DBFileName:        MacaroonDBName,
×
UNCOV
658
                DBTimeout:         db.Bolt.DBTimeout,
×
UNCOV
659
                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
UNCOV
660
                AutoCompact:       db.Bolt.AutoCompact,
×
UNCOV
661
                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
UNCOV
662
        })
×
UNCOV
663
        if err != nil {
×
664
                return nil, fmt.Errorf("error opening macaroon DB: %w", err)
×
665
        }
×
UNCOV
666
        closeFuncs[NSMacaroonDB] = macaroonBackend.Close
×
UNCOV
667

×
UNCOV
668
        decayedLogBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
×
UNCOV
669
                DBPath:            chanDBPath,
×
UNCOV
670
                DBFileName:        DecayedLogDbName,
×
UNCOV
671
                DBTimeout:         db.Bolt.DBTimeout,
×
UNCOV
672
                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
UNCOV
673
                AutoCompact:       db.Bolt.AutoCompact,
×
UNCOV
674
                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
UNCOV
675
        })
×
UNCOV
676
        if err != nil {
×
677
                return nil, fmt.Errorf("error opening decayed log DB: %w", err)
×
678
        }
×
UNCOV
679
        closeFuncs[NSDecayedLogDB] = decayedLogBackend.Close
×
UNCOV
680

×
UNCOV
681
        // The tower client is optional and might not be enabled by the user. We
×
UNCOV
682
        // handle it being nil properly in the main server.
×
UNCOV
683
        var towerClientBackend kvdb.Backend
×
UNCOV
684
        if towerClientEnabled {
×
UNCOV
685
                towerClientBackend, err = kvdb.GetBoltBackend(
×
UNCOV
686
                        &kvdb.BoltBackendConfig{
×
UNCOV
687
                                DBPath:            chanDBPath,
×
UNCOV
688
                                DBFileName:        TowerClientDBName,
×
UNCOV
689
                                DBTimeout:         db.Bolt.DBTimeout,
×
UNCOV
690
                                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
UNCOV
691
                                AutoCompact:       db.Bolt.AutoCompact,
×
UNCOV
692
                                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
UNCOV
693
                        },
×
UNCOV
694
                )
×
UNCOV
695
                if err != nil {
×
696
                        return nil, fmt.Errorf("error opening tower client "+
×
697
                                "DB: %v", err)
×
698
                }
×
UNCOV
699
                closeFuncs[NSTowerClientDB] = towerClientBackend.Close
×
700
        }
701

702
        // The tower server is optional and might not be enabled by the user. We
703
        // handle it being nil properly in the main server.
UNCOV
704
        var towerServerBackend kvdb.Backend
×
UNCOV
705
        if towerServerEnabled {
×
UNCOV
706
                towerServerBackend, err = kvdb.GetBoltBackend(
×
UNCOV
707
                        &kvdb.BoltBackendConfig{
×
UNCOV
708
                                DBPath:            towerServerDBPath,
×
UNCOV
709
                                DBFileName:        TowerServerDBName,
×
UNCOV
710
                                DBTimeout:         db.Bolt.DBTimeout,
×
UNCOV
711
                                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
UNCOV
712
                                AutoCompact:       db.Bolt.AutoCompact,
×
UNCOV
713
                                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
UNCOV
714
                        },
×
UNCOV
715
                )
×
UNCOV
716
                if err != nil {
×
717
                        return nil, fmt.Errorf("error opening tower server "+
×
718
                                "DB: %v", err)
×
719
                }
×
UNCOV
720
                closeFuncs[NSTowerServerDB] = towerServerBackend.Close
×
721
        }
722

UNCOV
723
        returnEarly = false
×
UNCOV
724

×
UNCOV
725
        return &DatabaseBackends{
×
UNCOV
726
                GraphDB:       boltBackend,
×
UNCOV
727
                ChanStateDB:   boltBackend,
×
UNCOV
728
                HeightHintDB:  boltBackend,
×
UNCOV
729
                MacaroonDB:    macaroonBackend,
×
UNCOV
730
                DecayedLogDB:  decayedLogBackend,
×
UNCOV
731
                TowerClientDB: towerClientBackend,
×
UNCOV
732
                TowerServerDB: towerServerBackend,
×
UNCOV
733
                // When "running locally", LND will use the bbolt wallet.db to
×
UNCOV
734
                // store the wallet located in the chain data dir, parametrized
×
UNCOV
735
                // by the active network. The wallet loader has its own cleanup
×
UNCOV
736
                // method so we don't need to add anything to our map (in fact
×
UNCOV
737
                // nothing is opened just yet).
×
UNCOV
738
                WalletDB: btcwallet.LoaderWithLocalWalletDB(
×
UNCOV
739
                        walletDBPath, db.Bolt.NoFreelistSync, db.Bolt.DBTimeout,
×
UNCOV
740
                ),
×
UNCOV
741
                CloseFuncs: closeFuncs,
×
UNCOV
742
        }, nil
×
743
}
744

745
// warnExistingBoltDBs checks if there is an existing bbolt database in the
746
// given location and logs a warning if so.
747
func warnExistingBoltDBs(log btclog.Logger, dbType, dir, fileName string) {
×
748
        if lnrpc.FileExists(filepath.Join(dir, fileName)) {
×
749
                log.Warnf("Found existing bbolt database file in %s/%s while "+
×
750
                        "using database type %s. Existing data will NOT be "+
×
751
                        "migrated to %s automatically!", dir, fileName, dbType,
×
752
                        dbType)
×
753
        }
×
754
}
755

756
// Compile-time constraint to ensure Workers implements the Validator interface.
757
var _ Validator = (*DB)(nil)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc