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

lightningnetwork / lnd / 16969463412

14 Aug 2025 03:23PM UTC coverage: 66.776% (-0.2%) from 66.929%
16969463412

push

github

web-flow
Merge pull request #10155 from ziggie1984/add-missing-invoice-settle-index

Add missing invoice index for native sql

135916 of 203540 relevant lines covered (66.78%)

21469.17 hits per line

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

25.14
/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 {
5✔
104
        return &DB{
5✔
105
                Backend:             BoltBackend,
5✔
106
                BatchCommitInterval: DefaultBatchCommitInterval,
5✔
107
                Bolt: &kvdb.BoltConfig{
5✔
108
                        NoFreelistSync:    true,
5✔
109
                        AutoCompactMinAge: kvdb.DefaultBoltAutoCompactMinAge,
5✔
110
                        DBTimeout:         kvdb.DefaultDBTimeout,
5✔
111
                },
5✔
112
                Etcd: &etcd.Config{
5✔
113
                        // Allow at most 32 MiB messages by default.
5✔
114
                        MaxMsgSize: 32768 * 1024,
5✔
115
                },
5✔
116
                Postgres: &sqldb.PostgresConfig{
5✔
117
                        MaxConnections: defaultPostgresMaxConnections,
5✔
118
                        QueryConfig:    *sqldb.DefaultPostgresConfig(),
5✔
119
                },
5✔
120
                Sqlite: &sqldb.SqliteConfig{
5✔
121
                        MaxConnections: defaultSqliteMaxConnections,
5✔
122
                        BusyTimeout:    defaultSqliteBusyTimeout,
5✔
123
                        QueryConfig:    *sqldb.DefaultSQLiteConfig(),
5✔
124
                },
5✔
125
                UseNativeSQL:           false,
5✔
126
                SkipNativeSQLMigration: false,
5✔
127
        }
5✔
128
}
5✔
129

130
// Validate validates the DB config.
131
func (db *DB) Validate() error {
3✔
132
        switch db.Backend {
3✔
133
        case BoltBackend:
3✔
134
                if db.UseNativeSQL {
3✔
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.
172
        if db.NoGraphCache && db.Backend != BoltBackend {
3✔
173
                return fmt.Errorf("cannot use no-graph-cache with database "+
×
174
                        "backend '%v'", db.Backend)
×
175
        }
×
176

177
        return nil
3✔
178
}
179

180
// Init should be called upon start to pre-initialize database access dependent
181
// on configuration.
182
func (db *DB) Init(ctx context.Context, dbPath string) error {
3✔
183
        // Start embedded etcd server if requested.
3✔
184
        switch {
3✔
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

205
        return nil
3✔
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,
283
        error) {
3✔
284

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

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

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

303
        switch db.Backend {
3✔
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.
642
        boltBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
3✔
643
                DBPath:            chanDBPath,
3✔
644
                DBFileName:        ChannelDBName,
3✔
645
                DBTimeout:         db.Bolt.DBTimeout,
3✔
646
                NoFreelistSync:    db.Bolt.NoFreelistSync,
3✔
647
                AutoCompact:       db.Bolt.AutoCompact,
3✔
648
                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
3✔
649
        })
3✔
650
        if err != nil {
3✔
651
                return nil, fmt.Errorf("error opening bolt DB: %w", err)
×
652
        }
×
653
        closeFuncs[NSChannelDB] = boltBackend.Close
3✔
654

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

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

3✔
681
        // The tower client is optional and might not be enabled by the user. We
3✔
682
        // handle it being nil properly in the main server.
3✔
683
        var towerClientBackend kvdb.Backend
3✔
684
        if towerClientEnabled {
6✔
685
                towerClientBackend, err = kvdb.GetBoltBackend(
3✔
686
                        &kvdb.BoltBackendConfig{
3✔
687
                                DBPath:            chanDBPath,
3✔
688
                                DBFileName:        TowerClientDBName,
3✔
689
                                DBTimeout:         db.Bolt.DBTimeout,
3✔
690
                                NoFreelistSync:    db.Bolt.NoFreelistSync,
3✔
691
                                AutoCompact:       db.Bolt.AutoCompact,
3✔
692
                                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
3✔
693
                        },
3✔
694
                )
3✔
695
                if err != nil {
3✔
696
                        return nil, fmt.Errorf("error opening tower client "+
×
697
                                "DB: %v", err)
×
698
                }
×
699
                closeFuncs[NSTowerClientDB] = towerClientBackend.Close
3✔
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.
704
        var towerServerBackend kvdb.Backend
3✔
705
        if towerServerEnabled {
6✔
706
                towerServerBackend, err = kvdb.GetBoltBackend(
3✔
707
                        &kvdb.BoltBackendConfig{
3✔
708
                                DBPath:            towerServerDBPath,
3✔
709
                                DBFileName:        TowerServerDBName,
3✔
710
                                DBTimeout:         db.Bolt.DBTimeout,
3✔
711
                                NoFreelistSync:    db.Bolt.NoFreelistSync,
3✔
712
                                AutoCompact:       db.Bolt.AutoCompact,
3✔
713
                                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
3✔
714
                        },
3✔
715
                )
3✔
716
                if err != nil {
3✔
717
                        return nil, fmt.Errorf("error opening tower server "+
×
718
                                "DB: %v", err)
×
719
                }
×
720
                closeFuncs[NSTowerServerDB] = towerServerBackend.Close
3✔
721
        }
722

723
        returnEarly = false
3✔
724

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