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

lightningnetwork / lnd / 14358372723

09 Apr 2025 01:26PM UTC coverage: 56.696% (-12.3%) from 69.037%
14358372723

Pull #9696

github

web-flow
Merge e2837e400 into 867d27d68
Pull Request #9696: Add `development_guidelines.md` for both human and machine

107055 of 188823 relevant lines covered (56.7%)

22721.56 hits per line

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

4.62
/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

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

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

41
        defaultPostgresMaxConnections = 50
42
        defaultSqliteMaxConnections   = 2
43

44
        defaultSqliteBusyTimeout = 5 * time.Second
45

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

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

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

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

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

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

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

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

78
        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."`
79

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

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

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

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

88
        UseNativeSQL bool `long:"use-native-sql" description:"Use native SQL for tables that already support it."`
89

90
        SkipNativeSQLMigration bool `long:"skip-native-sql-migration" description:"Do not run the KV to native SQL migration. It should only be used if errors are encountered normally."`
91

92
        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."`
93

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

96
        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."`
97
}
98

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

125
// Validate validates the DB config.
126
func (db *DB) Validate() error {
×
127
        switch db.Backend {
×
128
        case BoltBackend:
×
129
                if db.UseNativeSQL {
×
130
                        return fmt.Errorf("cannot use native SQL with bolt " +
×
131
                                "backend")
×
132
                }
×
133

134
        case SqliteBackend:
×
135
        case PostgresBackend:
×
136
                if err := db.Postgres.Validate(); err != nil {
×
137
                        return err
×
138
                }
×
139

140
        case EtcdBackend:
×
141
                if db.UseNativeSQL {
×
142
                        return fmt.Errorf("cannot use native SQL with etcd " +
×
143
                                "backend")
×
144
                }
×
145

146
                if !db.Etcd.Embedded && db.Etcd.Host == "" {
×
147
                        return fmt.Errorf("etcd host must be set")
×
148
                }
×
149

150
        default:
×
151
                return fmt.Errorf("unknown backend, must be either '%v', "+
×
152
                        "'%v', '%v' or '%v'", BoltBackend, EtcdBackend,
×
153
                        PostgresBackend, SqliteBackend)
×
154
        }
155

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

169
        return nil
×
170
}
171

172
// Init should be called upon start to pre-initialize database access dependent
173
// on configuration.
174
func (db *DB) Init(ctx context.Context, dbPath string) error {
×
175
        // Start embedded etcd server if requested.
×
176
        switch {
×
177
        case db.Backend == EtcdBackend && db.Etcd.Embedded:
×
178
                cfg, _, err := kvdb.StartEtcdTestBackend(
×
179
                        dbPath, db.Etcd.EmbeddedClientPort,
×
180
                        db.Etcd.EmbeddedPeerPort, db.Etcd.EmbeddedLogFile,
×
181
                )
×
182
                if err != nil {
×
183
                        return err
×
184
                }
×
185

186
                // Override the original config with the config for
187
                // the embedded instance.
188
                db.Etcd = cfg
×
189

190
        case db.Backend == PostgresBackend:
×
191
                sqlbase.Init(db.Postgres.MaxConnections)
×
192

193
        case db.Backend == SqliteBackend:
×
194
                sqlbase.Init(db.Sqlite.MaxConnections)
×
195
        }
196

197
        return nil
×
198
}
199

200
// DatabaseBackends is a two-tuple that holds the set of active database
201
// backends for the daemon. The two backends we expose are the graph database
202
// backend, and the channel state backend.
203
type DatabaseBackends struct {
204
        // GraphDB points to the database backend that contains the less
205
        // critical data that is accessed often, such as the channel graph and
206
        // chain height hints.
207
        GraphDB kvdb.Backend
208

209
        // ChanStateDB points to a possibly networked replicated backend that
210
        // contains the critical channel state related data.
211
        ChanStateDB kvdb.Backend
212

213
        // HeightHintDB points to a possibly networked replicated backend that
214
        // contains the chain height hint related data.
215
        HeightHintDB kvdb.Backend
216

217
        // MacaroonDB points to a database backend that stores the macaroon root
218
        // keys.
219
        MacaroonDB kvdb.Backend
220

221
        // DecayedLogDB points to a database backend that stores the decayed log
222
        // data.
223
        DecayedLogDB kvdb.Backend
224

225
        // TowerClientDB points to a database backend that stores the watchtower
226
        // client data. This might be nil if the watchtower client is disabled.
227
        TowerClientDB kvdb.Backend
228

229
        // TowerServerDB points to a database backend that stores the watchtower
230
        // server data. This might be nil if the watchtower server is disabled.
231
        TowerServerDB kvdb.Backend
232

233
        // WalletDB is an option that instructs the wallet loader where to load
234
        // the underlying wallet database from.
235
        WalletDB btcwallet.LoaderOption
236

237
        // NativeSQLStore holds a reference to the native SQL store that can
238
        // be used for native SQL queries for tables that already support it.
239
        // This may be nil if the use-native-sql flag was not set.
240
        NativeSQLStore sqldb.DB
241

242
        // Remote indicates whether the database backends are remote, possibly
243
        // replicated instances or local bbolt or sqlite backed databases.
244
        Remote bool
245

246
        // CloseFuncs is a map of close functions for each of the initialized
247
        // DB backends keyed by their namespace name.
248
        CloseFuncs map[string]func() error
249
}
250

251
// GetPostgresConfigKVDB converts a sqldb.PostgresConfig to a kvdb
252
// postgres.Config.
253
func GetPostgresConfigKVDB(cfg *sqldb.PostgresConfig) *postgres.Config {
×
254
        return &postgres.Config{
×
255
                Dsn:            cfg.Dsn,
×
256
                Timeout:        cfg.Timeout,
×
257
                MaxConnections: cfg.MaxConnections,
×
258
        }
×
259
}
×
260

261
// GetSqliteConfigKVDB converts a sqldb.SqliteConfig to a kvdb sqlite.Config.
262
func GetSqliteConfigKVDB(cfg *sqldb.SqliteConfig) *sqlite.Config {
×
263
        return &sqlite.Config{
×
264
                Timeout:        cfg.Timeout,
×
265
                BusyTimeout:    cfg.BusyTimeout,
×
266
                MaxConnections: cfg.MaxConnections,
×
267
                PragmaOptions:  cfg.PragmaOptions,
×
268
        }
×
269
}
×
270

271
// GetBackends returns a set of kvdb.Backends as set in the DB config.
272
func (db *DB) GetBackends(ctx context.Context, chanDBPath,
273
        walletDBPath, towerServerDBPath string, towerClientEnabled,
274
        towerServerEnabled bool, logger btclog.Logger) (*DatabaseBackends,
275
        error) {
×
276

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

×
282
        // If we need to return early because of an error, we invoke any close
×
283
        // function that has been initialized so far.
×
284
        returnEarly := true
×
285
        defer func() {
×
286
                if !returnEarly {
×
287
                        return
×
288
                }
×
289

290
                for _, closeFunc := range closeFuncs {
×
291
                        _ = closeFunc()
×
292
                }
×
293
        }()
294

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

×
315
                etcdMacaroonBackend, err := kvdb.Open(
×
316
                        kvdb.EtcdBackendName, ctx,
×
317
                        db.Etcd.CloneWithSubNamespace(NSMacaroonDB),
×
318
                )
×
319
                if err != nil {
×
320
                        return nil, fmt.Errorf("error opening etcd macaroon "+
×
321
                                "DB: %v", err)
×
322
                }
×
323
                closeFuncs[NSMacaroonDB] = etcdMacaroonBackend.Close
×
324

×
325
                etcdDecayedLogBackend, err := kvdb.Open(
×
326
                        kvdb.EtcdBackendName, ctx,
×
327
                        db.Etcd.CloneWithSubNamespace(NSDecayedLogDB),
×
328
                )
×
329
                if err != nil {
×
330
                        return nil, fmt.Errorf("error opening etcd decayed "+
×
331
                                "log DB: %v", err)
×
332
                }
×
333
                closeFuncs[NSDecayedLogDB] = etcdDecayedLogBackend.Close
×
334

×
335
                etcdTowerClientBackend, err := kvdb.Open(
×
336
                        kvdb.EtcdBackendName, ctx,
×
337
                        db.Etcd.CloneWithSubNamespace(NSTowerClientDB),
×
338
                )
×
339
                if err != nil {
×
340
                        return nil, fmt.Errorf("error opening etcd tower "+
×
341
                                "client DB: %v", err)
×
342
                }
×
343
                closeFuncs[NSTowerClientDB] = etcdTowerClientBackend.Close
×
344

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

×
355
                etcdWalletBackend, err := kvdb.Open(
×
356
                        kvdb.EtcdBackendName, ctx,
×
357
                        db.Etcd.
×
358
                                CloneWithSubNamespace(NSWalletDB).
×
359
                                CloneWithSingleWriter(),
×
360
                )
×
361
                if err != nil {
×
362
                        return nil, fmt.Errorf("error opening etcd macaroon "+
×
363
                                "DB: %v", err)
×
364
                }
×
365
                closeFuncs[NSWalletDB] = etcdWalletBackend.Close
×
366

×
367
                returnEarly = false
×
368

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

389
        case PostgresBackend:
×
390
                // Convert the sqldb PostgresConfig to a kvdb postgres.Config.
×
391
                // This is a temporary measure until we migrate all kvdb SQL
×
392
                // users to native SQL.
×
393
                postgresConfig := GetPostgresConfigKVDB(db.Postgres)
×
394

×
395
                postgresBackend, err := kvdb.Open(
×
396
                        kvdb.PostgresBackendName, ctx,
×
397
                        postgresConfig, NSChannelDB,
×
398
                )
×
399
                if err != nil {
×
400
                        return nil, fmt.Errorf("error opening postgres graph "+
×
401
                                "DB: %v", err)
×
402
                }
×
403
                closeFuncs[NSChannelDB] = postgresBackend.Close
×
404

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

×
415
                postgresDecayedLogBackend, err := kvdb.Open(
×
416
                        kvdb.PostgresBackendName, ctx,
×
417
                        postgresConfig, NSDecayedLogDB,
×
418
                )
×
419
                if err != nil {
×
420
                        return nil, fmt.Errorf("error opening postgres "+
×
421
                                "decayed log DB: %v", err)
×
422
                }
×
423
                closeFuncs[NSDecayedLogDB] = postgresDecayedLogBackend.Close
×
424

×
425
                postgresTowerClientBackend, err := kvdb.Open(
×
426
                        kvdb.PostgresBackendName, ctx,
×
427
                        postgresConfig, NSTowerClientDB,
×
428
                )
×
429
                if err != nil {
×
430
                        return nil, fmt.Errorf("error opening postgres tower "+
×
431
                                "client DB: %v", err)
×
432
                }
×
433
                closeFuncs[NSTowerClientDB] = postgresTowerClientBackend.Close
×
434

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

×
445
                postgresWalletBackend, err := kvdb.Open(
×
446
                        kvdb.PostgresBackendName, ctx,
×
447
                        postgresConfig, NSWalletDB,
×
448
                )
×
449
                if err != nil {
×
450
                        return nil, fmt.Errorf("error opening postgres macaroon "+
×
451
                                "DB: %v", err)
×
452
                }
×
453
                closeFuncs[NSWalletDB] = postgresWalletBackend.Close
×
454

×
455
                var nativeSQLStore sqldb.DB
×
456
                if db.UseNativeSQL {
×
457
                        nativePostgresStore, err := sqldb.NewPostgresStore(
×
458
                                db.Postgres,
×
459
                        )
×
460
                        if err != nil {
×
461
                                return nil, fmt.Errorf("error opening "+
×
462
                                        "native postgres store: %v", err)
×
463
                        }
×
464

465
                        nativeSQLStore = nativePostgresStore
×
466
                        closeFuncs[PostgresBackend] = nativePostgresStore.Close
×
467
                }
468

469
                // Warn if the user is trying to switch over to a Postgres DB
470
                // while there is a wallet or channel bbolt DB still present.
471
                warnExistingBoltDBs(
×
472
                        logger, "postgres", walletDBPath, WalletDBName,
×
473
                )
×
474
                warnExistingBoltDBs(
×
475
                        logger, "postgres", chanDBPath, ChannelDBName,
×
476
                )
×
477

×
478
                returnEarly = false
×
479

×
480
                return &DatabaseBackends{
×
481
                        GraphDB:       postgresBackend,
×
482
                        ChanStateDB:   postgresBackend,
×
483
                        HeightHintDB:  postgresBackend,
×
484
                        MacaroonDB:    postgresMacaroonBackend,
×
485
                        DecayedLogDB:  postgresDecayedLogBackend,
×
486
                        TowerClientDB: postgresTowerClientBackend,
×
487
                        TowerServerDB: postgresTowerServerBackend,
×
488
                        // The wallet loader will attempt to use/create the
×
489
                        // wallet in the replicated remote DB if we're running
×
490
                        // in a clustered environment. This will ensure that all
×
491
                        // members of the cluster have access to the same wallet
×
492
                        // state.
×
493
                        WalletDB: btcwallet.LoaderWithExternalWalletDB(
×
494
                                postgresWalletBackend,
×
495
                        ),
×
496
                        NativeSQLStore: nativeSQLStore,
×
497
                        Remote:         true,
×
498
                        CloseFuncs:     closeFuncs,
×
499
                }, nil
×
500

501
        case SqliteBackend:
×
502
                // Convert the sqldb SqliteConfig to a kvdb sqlite.Config.
×
503
                // This is a temporary measure until we migrate all kvdb SQL
×
504
                // users to native SQL.
×
505
                sqliteConfig := GetSqliteConfigKVDB(db.Sqlite)
×
506

×
507
                // Note that for sqlite, we put kv tables for the channel.db,
×
508
                // wtclient.db and sphinxreplay.db all in the channel.sqlite db.
×
509
                // The tables for wallet.db and macaroon.db are in the
×
510
                // chain.sqlite db and watchtower.db tables are in the
×
511
                // watchtower.sqlite db. The reason for the multiple sqlite dbs
×
512
                // is twofold. The first reason is that it maintains the file
×
513
                // structure that users are used to. The second reason is the
×
514
                // fact that sqlite only supports one writer at a time which
×
515
                // would cause deadlocks in the code due to the wallet db often
×
516
                // being accessed during a write to another db.
×
517
                sqliteBackend, err := kvdb.Open(
×
518
                        kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
×
519
                        SqliteChannelDBName, NSChannelDB,
×
520
                )
×
521
                if err != nil {
×
522
                        return nil, fmt.Errorf("error opening sqlite graph "+
×
523
                                "DB: %v", err)
×
524
                }
×
525
                closeFuncs[NSChannelDB] = sqliteBackend.Close
×
526

×
527
                sqliteMacaroonBackend, err := kvdb.Open(
×
528
                        kvdb.SqliteBackendName, ctx, sqliteConfig, walletDBPath,
×
529
                        SqliteChainDBName, NSMacaroonDB,
×
530
                )
×
531
                if err != nil {
×
532
                        return nil, fmt.Errorf("error opening sqlite "+
×
533
                                "macaroon DB: %v", err)
×
534
                }
×
535
                closeFuncs[NSMacaroonDB] = sqliteMacaroonBackend.Close
×
536

×
537
                sqliteDecayedLogBackend, err := kvdb.Open(
×
538
                        kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
×
539
                        SqliteChannelDBName, NSDecayedLogDB,
×
540
                )
×
541
                if err != nil {
×
542
                        return nil, fmt.Errorf("error opening sqlite decayed "+
×
543
                                "log DB: %v", err)
×
544
                }
×
545
                closeFuncs[NSDecayedLogDB] = sqliteDecayedLogBackend.Close
×
546

×
547
                sqliteTowerClientBackend, err := kvdb.Open(
×
548
                        kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
×
549
                        SqliteChannelDBName, NSTowerClientDB,
×
550
                )
×
551
                if err != nil {
×
552
                        return nil, fmt.Errorf("error opening sqlite tower "+
×
553
                                "client DB: %v", err)
×
554
                }
×
555
                closeFuncs[NSTowerClientDB] = sqliteTowerClientBackend.Close
×
556

×
557
                sqliteTowerServerBackend, err := kvdb.Open(
×
558
                        kvdb.SqliteBackendName, ctx, sqliteConfig,
×
559
                        towerServerDBPath, SqliteTowerDBName, NSTowerServerDB,
×
560
                )
×
561
                if err != nil {
×
562
                        return nil, fmt.Errorf("error opening sqlite tower "+
×
563
                                "server DB: %v", err)
×
564
                }
×
565
                closeFuncs[NSTowerServerDB] = sqliteTowerServerBackend.Close
×
566

×
567
                sqliteWalletBackend, err := kvdb.Open(
×
568
                        kvdb.SqliteBackendName, ctx, sqliteConfig, walletDBPath,
×
569
                        SqliteChainDBName, NSWalletDB,
×
570
                )
×
571
                if err != nil {
×
572
                        return nil, fmt.Errorf("error opening sqlite macaroon "+
×
573
                                "DB: %v", err)
×
574
                }
×
575
                closeFuncs[NSWalletDB] = sqliteWalletBackend.Close
×
576

×
577
                var nativeSQLStore sqldb.DB
×
578
                if db.UseNativeSQL {
×
579
                        nativeSQLiteStore, err := sqldb.NewSqliteStore(
×
580
                                db.Sqlite,
×
581
                                path.Join(chanDBPath, SqliteNativeDBName),
×
582
                        )
×
583
                        if err != nil {
×
584
                                return nil, fmt.Errorf("error opening "+
×
585
                                        "native SQLite store: %v", err)
×
586
                        }
×
587

588
                        nativeSQLStore = nativeSQLiteStore
×
589
                        closeFuncs[SqliteBackend] = nativeSQLiteStore.Close
×
590
                }
591

592
                // Warn if the user is trying to switch over to a sqlite DB
593
                // while there is a wallet or channel bbolt DB still present.
594
                warnExistingBoltDBs(
×
595
                        logger, "sqlite", walletDBPath, WalletDBName,
×
596
                )
×
597
                warnExistingBoltDBs(
×
598
                        logger, "sqlite", chanDBPath, ChannelDBName,
×
599
                )
×
600

×
601
                returnEarly = false
×
602

×
603
                return &DatabaseBackends{
×
604
                        GraphDB:       sqliteBackend,
×
605
                        ChanStateDB:   sqliteBackend,
×
606
                        HeightHintDB:  sqliteBackend,
×
607
                        MacaroonDB:    sqliteMacaroonBackend,
×
608
                        DecayedLogDB:  sqliteDecayedLogBackend,
×
609
                        TowerClientDB: sqliteTowerClientBackend,
×
610
                        TowerServerDB: sqliteTowerServerBackend,
×
611
                        // The wallet loader will attempt to use/create the
×
612
                        // wallet in the replicated remote DB if we're running
×
613
                        // in a clustered environment. This will ensure that all
×
614
                        // members of the cluster have access to the same wallet
×
615
                        // state.
×
616
                        WalletDB: btcwallet.LoaderWithExternalWalletDB(
×
617
                                sqliteWalletBackend,
×
618
                        ),
×
619
                        NativeSQLStore: nativeSQLStore,
×
620
                        CloseFuncs:     closeFuncs,
×
621
                }, nil
×
622
        }
623

624
        // We're using all bbolt based databases by default.
625
        boltBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
×
626
                DBPath:            chanDBPath,
×
627
                DBFileName:        ChannelDBName,
×
628
                DBTimeout:         db.Bolt.DBTimeout,
×
629
                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
630
                AutoCompact:       db.Bolt.AutoCompact,
×
631
                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
632
        })
×
633
        if err != nil {
×
634
                return nil, fmt.Errorf("error opening bolt DB: %w", err)
×
635
        }
×
636
        closeFuncs[NSChannelDB] = boltBackend.Close
×
637

×
638
        macaroonBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
×
639
                DBPath:            walletDBPath,
×
640
                DBFileName:        MacaroonDBName,
×
641
                DBTimeout:         db.Bolt.DBTimeout,
×
642
                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
643
                AutoCompact:       db.Bolt.AutoCompact,
×
644
                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
645
        })
×
646
        if err != nil {
×
647
                return nil, fmt.Errorf("error opening macaroon DB: %w", err)
×
648
        }
×
649
        closeFuncs[NSMacaroonDB] = macaroonBackend.Close
×
650

×
651
        decayedLogBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
×
652
                DBPath:            chanDBPath,
×
653
                DBFileName:        DecayedLogDbName,
×
654
                DBTimeout:         db.Bolt.DBTimeout,
×
655
                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
656
                AutoCompact:       db.Bolt.AutoCompact,
×
657
                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
658
        })
×
659
        if err != nil {
×
660
                return nil, fmt.Errorf("error opening decayed log DB: %w", err)
×
661
        }
×
662
        closeFuncs[NSDecayedLogDB] = decayedLogBackend.Close
×
663

×
664
        // The tower client is optional and might not be enabled by the user. We
×
665
        // handle it being nil properly in the main server.
×
666
        var towerClientBackend kvdb.Backend
×
667
        if towerClientEnabled {
×
668
                towerClientBackend, err = kvdb.GetBoltBackend(
×
669
                        &kvdb.BoltBackendConfig{
×
670
                                DBPath:            chanDBPath,
×
671
                                DBFileName:        TowerClientDBName,
×
672
                                DBTimeout:         db.Bolt.DBTimeout,
×
673
                                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
674
                                AutoCompact:       db.Bolt.AutoCompact,
×
675
                                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
676
                        },
×
677
                )
×
678
                if err != nil {
×
679
                        return nil, fmt.Errorf("error opening tower client "+
×
680
                                "DB: %v", err)
×
681
                }
×
682
                closeFuncs[NSTowerClientDB] = towerClientBackend.Close
×
683
        }
684

685
        // The tower server is optional and might not be enabled by the user. We
686
        // handle it being nil properly in the main server.
687
        var towerServerBackend kvdb.Backend
×
688
        if towerServerEnabled {
×
689
                towerServerBackend, err = kvdb.GetBoltBackend(
×
690
                        &kvdb.BoltBackendConfig{
×
691
                                DBPath:            towerServerDBPath,
×
692
                                DBFileName:        TowerServerDBName,
×
693
                                DBTimeout:         db.Bolt.DBTimeout,
×
694
                                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
695
                                AutoCompact:       db.Bolt.AutoCompact,
×
696
                                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
697
                        },
×
698
                )
×
699
                if err != nil {
×
700
                        return nil, fmt.Errorf("error opening tower server "+
×
701
                                "DB: %v", err)
×
702
                }
×
703
                closeFuncs[NSTowerServerDB] = towerServerBackend.Close
×
704
        }
705

706
        returnEarly = false
×
707

×
708
        return &DatabaseBackends{
×
709
                GraphDB:       boltBackend,
×
710
                ChanStateDB:   boltBackend,
×
711
                HeightHintDB:  boltBackend,
×
712
                MacaroonDB:    macaroonBackend,
×
713
                DecayedLogDB:  decayedLogBackend,
×
714
                TowerClientDB: towerClientBackend,
×
715
                TowerServerDB: towerServerBackend,
×
716
                // When "running locally", LND will use the bbolt wallet.db to
×
717
                // store the wallet located in the chain data dir, parametrized
×
718
                // by the active network. The wallet loader has its own cleanup
×
719
                // method so we don't need to add anything to our map (in fact
×
720
                // nothing is opened just yet).
×
721
                WalletDB: btcwallet.LoaderWithLocalWalletDB(
×
722
                        walletDBPath, db.Bolt.NoFreelistSync, db.Bolt.DBTimeout,
×
723
                ),
×
724
                CloseFuncs: closeFuncs,
×
725
        }, nil
×
726
}
727

728
// warnExistingBoltDBs checks if there is an existing bbolt database in the
729
// given location and logs a warning if so.
730
func warnExistingBoltDBs(log btclog.Logger, dbType, dir, fileName string) {
×
731
        if lnrpc.FileExists(filepath.Join(dir, fileName)) {
×
732
                log.Warnf("Found existing bbolt database file in %s/%s while "+
×
733
                        "using database type %s. Existing data will NOT be "+
×
734
                        "migrated to %s automatically!", dir, fileName, dbType,
×
735
                        dbType)
×
736
        }
×
737
}
738

739
// Compile-time constraint to ensure Workers implements the Validator interface.
740
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