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

lightningnetwork / lnd / 12583319996

02 Jan 2025 01:38PM UTC coverage: 57.522% (-1.1%) from 58.598%
12583319996

Pull #9361

github

starius
fn/ContextGuard: use context.AfterFunc to wait

Simplifies context cancellation handling by using context.AfterFunc instead of a
goroutine to wait for context cancellation. This approach avoids the overhead of
a goroutine during the waiting period.

For ctxQuitUnsafe, since g.quit is closed only in the Quit method (which also
cancels all associated contexts), waiting on context cancellation ensures the
same behavior without unnecessary dependency on g.quit.

Added a test to ensure that the Create method does not launch any goroutines.
Pull Request #9361: fn: optimize context guard

102587 of 178344 relevant lines covered (57.52%)

24734.33 hits per line

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

0.0
/config_builder.go
1
package lnd
2

3
import (
4
        "bytes"
5
        "context"
6
        "database/sql"
7
        "errors"
8
        "fmt"
9
        "net"
10
        "os"
11
        "path/filepath"
12
        "sort"
13
        "strconv"
14
        "strings"
15
        "sync/atomic"
16
        "time"
17

18
        "github.com/btcsuite/btcd/chaincfg"
19
        "github.com/btcsuite/btcd/chaincfg/chainhash"
20
        "github.com/btcsuite/btcd/wire"
21
        "github.com/btcsuite/btclog/v2"
22
        "github.com/btcsuite/btcwallet/chain"
23
        "github.com/btcsuite/btcwallet/waddrmgr"
24
        "github.com/btcsuite/btcwallet/wallet"
25
        "github.com/btcsuite/btcwallet/walletdb"
26
        proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
27
        "github.com/lightninglabs/neutrino"
28
        "github.com/lightninglabs/neutrino/blockntfns"
29
        "github.com/lightninglabs/neutrino/headerfs"
30
        "github.com/lightninglabs/neutrino/pushtx"
31
        "github.com/lightningnetwork/lnd/blockcache"
32
        "github.com/lightningnetwork/lnd/chainntnfs"
33
        "github.com/lightningnetwork/lnd/chainreg"
34
        "github.com/lightningnetwork/lnd/channeldb"
35
        "github.com/lightningnetwork/lnd/clock"
36
        "github.com/lightningnetwork/lnd/fn/v2"
37
        "github.com/lightningnetwork/lnd/funding"
38
        graphdb "github.com/lightningnetwork/lnd/graph/db"
39
        "github.com/lightningnetwork/lnd/htlcswitch"
40
        "github.com/lightningnetwork/lnd/invoices"
41
        "github.com/lightningnetwork/lnd/keychain"
42
        "github.com/lightningnetwork/lnd/kvdb"
43
        "github.com/lightningnetwork/lnd/lncfg"
44
        "github.com/lightningnetwork/lnd/lnrpc"
45
        "github.com/lightningnetwork/lnd/lnwallet"
46
        "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
47
        "github.com/lightningnetwork/lnd/lnwallet/chancloser"
48
        "github.com/lightningnetwork/lnd/lnwallet/rpcwallet"
49
        "github.com/lightningnetwork/lnd/macaroons"
50
        "github.com/lightningnetwork/lnd/msgmux"
51
        "github.com/lightningnetwork/lnd/rpcperms"
52
        "github.com/lightningnetwork/lnd/signal"
53
        "github.com/lightningnetwork/lnd/sqldb"
54
        "github.com/lightningnetwork/lnd/sweep"
55
        "github.com/lightningnetwork/lnd/walletunlocker"
56
        "github.com/lightningnetwork/lnd/watchtower"
57
        "github.com/lightningnetwork/lnd/watchtower/wtclient"
58
        "github.com/lightningnetwork/lnd/watchtower/wtdb"
59
        "google.golang.org/grpc"
60
        "gopkg.in/macaroon-bakery.v2/bakery"
61
)
62

63
// GrpcRegistrar is an interface that must be satisfied by an external subserver
64
// that wants to be able to register its own gRPC server onto lnd's main
65
// grpc.Server instance.
66
type GrpcRegistrar interface {
67
        // RegisterGrpcSubserver is called for each net.Listener on which lnd
68
        // creates a grpc.Server instance. External subservers implementing this
69
        // method can then register their own gRPC server structs to the main
70
        // server instance.
71
        RegisterGrpcSubserver(*grpc.Server) error
72
}
73

74
// RestRegistrar is an interface that must be satisfied by an external subserver
75
// that wants to be able to register its own REST mux onto lnd's main
76
// proxy.ServeMux instance.
77
type RestRegistrar interface {
78
        // RegisterRestSubserver is called after lnd creates the main
79
        // proxy.ServeMux instance. External subservers implementing this method
80
        // can then register their own REST proxy stubs to the main server
81
        // instance.
82
        RegisterRestSubserver(context.Context, *proxy.ServeMux, string,
83
                []grpc.DialOption) error
84
}
85

86
// ExternalValidator is an interface that must be satisfied by an external
87
// macaroon validator.
88
type ExternalValidator interface {
89
        macaroons.MacaroonValidator
90

91
        // Permissions returns the permissions that the external validator is
92
        // validating. It is a map between the full HTTP URI of each RPC and its
93
        // required macaroon permissions. If multiple action/entity tuples are
94
        // specified per URI, they are all required. See rpcserver.go for a list
95
        // of valid action and entity values.
96
        Permissions() map[string][]bakery.Op
97
}
98

99
// DatabaseBuilder is an interface that must be satisfied by the implementation
100
// that provides lnd's main database backend instances.
101
type DatabaseBuilder interface {
102
        // BuildDatabase extracts the current databases that we'll use for
103
        // normal operation in the daemon. A function closure that closes all
104
        // opened databases is also returned.
105
        BuildDatabase(ctx context.Context) (*DatabaseInstances, func(), error)
106
}
107

108
// WalletConfigBuilder is an interface that must be satisfied by a custom wallet
109
// implementation.
110
type WalletConfigBuilder interface {
111
        // BuildWalletConfig is responsible for creating or unlocking and then
112
        // fully initializing a wallet.
113
        BuildWalletConfig(context.Context, *DatabaseInstances, *AuxComponents,
114
                *rpcperms.InterceptorChain,
115
                []*ListenerWithSignal) (*chainreg.PartialChainControl,
116
                *btcwallet.Config, func(), error)
117
}
118

119
// ChainControlBuilder is an interface that must be satisfied by a custom wallet
120
// implementation.
121
type ChainControlBuilder interface {
122
        // BuildChainControl is responsible for creating a fully populated chain
123
        // control instance from a wallet.
124
        BuildChainControl(*chainreg.PartialChainControl,
125
                *btcwallet.Config) (*chainreg.ChainControl, func(), error)
126
}
127

128
// ImplementationCfg is a struct that holds all configuration items for
129
// components that can be implemented outside lnd itself.
130
type ImplementationCfg struct {
131
        // GrpcRegistrar is a type that can register additional gRPC subservers
132
        // before the main gRPC server is started.
133
        GrpcRegistrar
134

135
        // RestRegistrar is a type that can register additional REST subservers
136
        // before the main REST proxy is started.
137
        RestRegistrar
138

139
        // ExternalValidator is a type that can provide external macaroon
140
        // validation.
141
        ExternalValidator
142

143
        // DatabaseBuilder is a type that can provide lnd's main database
144
        // backend instances.
145
        DatabaseBuilder
146

147
        // WalletConfigBuilder is a type that can provide a wallet configuration
148
        // with a fully loaded and unlocked wallet.
149
        WalletConfigBuilder
150

151
        // ChainControlBuilder is a type that can provide a custom wallet
152
        // implementation.
153
        ChainControlBuilder
154

155
        // AuxComponents is a set of auxiliary components that can be used by
156
        // lnd for certain custom channel types.
157
        AuxComponents
158
}
159

160
// AuxComponents is a set of auxiliary components that can be used by lnd for
161
// certain custom channel types.
162
type AuxComponents struct {
163
        // AuxLeafStore is an optional data source that can be used by custom
164
        // channels to fetch+store various data.
165
        AuxLeafStore fn.Option[lnwallet.AuxLeafStore]
166

167
        // TrafficShaper is an optional traffic shaper that can be used to
168
        // control the outgoing channel of a payment.
169
        TrafficShaper fn.Option[htlcswitch.AuxTrafficShaper]
170

171
        // MsgRouter is an optional message router that if set will be used in
172
        // place of a new blank default message router.
173
        MsgRouter fn.Option[msgmux.Router]
174

175
        // AuxFundingController is an optional controller that can be used to
176
        // modify the way we handle certain custom channel types. It's also
177
        // able to automatically handle new custom protocol messages related to
178
        // the funding process.
179
        AuxFundingController fn.Option[funding.AuxFundingController]
180

181
        // AuxSigner is an optional signer that can be used to sign auxiliary
182
        // leaves for certain custom channel types.
183
        AuxSigner fn.Option[lnwallet.AuxSigner]
184

185
        // AuxDataParser is an optional data parser that can be used to parse
186
        // auxiliary data for certain custom channel types.
187
        AuxDataParser fn.Option[AuxDataParser]
188

189
        // AuxChanCloser is an optional channel closer that can be used to
190
        // modify the way a coop-close transaction is constructed.
191
        AuxChanCloser fn.Option[chancloser.AuxChanCloser]
192

193
        // AuxSweeper is an optional interface that can be used to modify the
194
        // way sweep transaction are generated.
195
        AuxSweeper fn.Option[sweep.AuxSweeper]
196

197
        // AuxContractResolver is an optional interface that can be used to
198
        // modify the way contracts are resolved.
199
        AuxContractResolver fn.Option[lnwallet.AuxContractResolver]
200
}
201

202
// DefaultWalletImpl is the default implementation of our normal, btcwallet
203
// backed configuration.
204
type DefaultWalletImpl struct {
205
        cfg         *Config
206
        logger      btclog.Logger
207
        interceptor signal.Interceptor
208

209
        watchOnly        bool
210
        migrateWatchOnly bool
211
        pwService        *walletunlocker.UnlockerService
212
}
213

214
// NewDefaultWalletImpl creates a new default wallet implementation.
215
func NewDefaultWalletImpl(cfg *Config, logger btclog.Logger,
216
        interceptor signal.Interceptor, watchOnly bool) *DefaultWalletImpl {
×
217

×
218
        return &DefaultWalletImpl{
×
219
                cfg:         cfg,
×
220
                logger:      logger,
×
221
                interceptor: interceptor,
×
222
                watchOnly:   watchOnly,
×
223
                pwService:   createWalletUnlockerService(cfg),
×
224
        }
×
225
}
×
226

227
// RegisterRestSubserver is called after lnd creates the main proxy.ServeMux
228
// instance. External subservers implementing this method can then register
229
// their own REST proxy stubs to the main server instance.
230
//
231
// NOTE: This is part of the GrpcRegistrar interface.
232
func (d *DefaultWalletImpl) RegisterRestSubserver(ctx context.Context,
233
        mux *proxy.ServeMux, restProxyDest string,
234
        restDialOpts []grpc.DialOption) error {
×
235

×
236
        return lnrpc.RegisterWalletUnlockerHandlerFromEndpoint(
×
237
                ctx, mux, restProxyDest, restDialOpts,
×
238
        )
×
239
}
×
240

241
// RegisterGrpcSubserver is called for each net.Listener on which lnd creates a
242
// grpc.Server instance. External subservers implementing this method can then
243
// register their own gRPC server structs to the main server instance.
244
//
245
// NOTE: This is part of the GrpcRegistrar interface.
246
func (d *DefaultWalletImpl) RegisterGrpcSubserver(s *grpc.Server) error {
×
247
        lnrpc.RegisterWalletUnlockerServer(s, d.pwService)
×
248

×
249
        return nil
×
250
}
×
251

252
// ValidateMacaroon extracts the macaroon from the context's gRPC metadata,
253
// checks its signature, makes sure all specified permissions for the called
254
// method are contained within and finally ensures all caveat conditions are
255
// met. A non-nil error is returned if any of the checks fail.
256
//
257
// NOTE: This is part of the ExternalValidator interface.
258
func (d *DefaultWalletImpl) ValidateMacaroon(ctx context.Context,
259
        requiredPermissions []bakery.Op, fullMethod string) error {
×
260

×
261
        // Because the default implementation does not return any permissions,
×
262
        // we shouldn't be registered as an external validator at all and this
×
263
        // should never be invoked.
×
264
        return fmt.Errorf("default implementation does not support external " +
×
265
                "macaroon validation")
×
266
}
×
267

268
// Permissions returns the permissions that the external validator is
269
// validating. It is a map between the full HTTP URI of each RPC and its
270
// required macaroon permissions. If multiple action/entity tuples are specified
271
// per URI, they are all required. See rpcserver.go for a list of valid action
272
// and entity values.
273
//
274
// NOTE: This is part of the ExternalValidator interface.
275
func (d *DefaultWalletImpl) Permissions() map[string][]bakery.Op {
×
276
        return nil
×
277
}
×
278

279
// BuildWalletConfig is responsible for creating or unlocking and then
280
// fully initializing a wallet.
281
//
282
// NOTE: This is part of the WalletConfigBuilder interface.
283
func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context,
284
        dbs *DatabaseInstances, aux *AuxComponents,
285
        interceptorChain *rpcperms.InterceptorChain,
286
        grpcListeners []*ListenerWithSignal) (*chainreg.PartialChainControl,
287
        *btcwallet.Config, func(), error) {
×
288

×
289
        // Keep track of our various cleanup functions. We use a defer function
×
290
        // as well to not repeat ourselves with every return statement.
×
291
        var (
×
292
                cleanUpTasks []func()
×
293
                earlyExit    = true
×
294
                cleanUp      = func() {
×
295
                        for _, fn := range cleanUpTasks {
×
296
                                if fn == nil {
×
297
                                        continue
×
298
                                }
299

300
                                fn()
×
301
                        }
302
                }
303
        )
304
        defer func() {
×
305
                if earlyExit {
×
306
                        cleanUp()
×
307
                }
×
308
        }()
309

310
        // Initialize a new block cache.
311
        blockCache := blockcache.NewBlockCache(d.cfg.BlockCacheSize)
×
312

×
313
        // Before starting the wallet, we'll create and start our Neutrino
×
314
        // light client instance, if enabled, in order to allow it to sync
×
315
        // while the rest of the daemon continues startup.
×
316
        mainChain := d.cfg.Bitcoin
×
317
        var neutrinoCS *neutrino.ChainService
×
318
        if mainChain.Node == "neutrino" {
×
319
                neutrinoBackend, neutrinoCleanUp, err := initNeutrinoBackend(
×
320
                        ctx, d.cfg, mainChain.ChainDir, blockCache,
×
321
                )
×
322
                if err != nil {
×
323
                        err := fmt.Errorf("unable to initialize neutrino "+
×
324
                                "backend: %v", err)
×
325
                        d.logger.Error(err)
×
326
                        return nil, nil, nil, err
×
327
                }
×
328
                cleanUpTasks = append(cleanUpTasks, neutrinoCleanUp)
×
329
                neutrinoCS = neutrinoBackend
×
330
        }
331

332
        var (
×
333
                walletInitParams = walletunlocker.WalletUnlockParams{
×
334
                        // In case we do auto-unlock, we need to be able to send
×
335
                        // into the channel without blocking so we buffer it.
×
336
                        MacResponseChan: make(chan []byte, 1),
×
337
                }
×
338
                privateWalletPw = lnwallet.DefaultPrivatePassphrase
×
339
                publicWalletPw  = lnwallet.DefaultPublicPassphrase
×
340
        )
×
341

×
342
        // If the user didn't request a seed, then we'll manually assume a
×
343
        // wallet birthday of now, as otherwise the seed would've specified
×
344
        // this information.
×
345
        walletInitParams.Birthday = time.Now()
×
346

×
347
        d.pwService.SetLoaderOpts([]btcwallet.LoaderOption{dbs.WalletDB})
×
348
        d.pwService.SetMacaroonDB(dbs.MacaroonDB)
×
349
        walletExists, err := d.pwService.WalletExists()
×
350
        if err != nil {
×
351
                return nil, nil, nil, err
×
352
        }
×
353

354
        if !walletExists {
×
355
                interceptorChain.SetWalletNotCreated()
×
356
        } else {
×
357
                interceptorChain.SetWalletLocked()
×
358
        }
×
359

360
        // If we've started in auto unlock mode, then a wallet should already
361
        // exist because we don't want to enable the RPC unlocker in that case
362
        // for security reasons (an attacker could inject their seed since the
363
        // RPC is unauthenticated). Only if the user explicitly wants to allow
364
        // wallet creation we don't error out here.
365
        if d.cfg.WalletUnlockPasswordFile != "" && !walletExists &&
×
366
                !d.cfg.WalletUnlockAllowCreate {
×
367

×
368
                return nil, nil, nil, fmt.Errorf("wallet unlock password file " +
×
369
                        "was specified but wallet does not exist; initialize " +
×
370
                        "the wallet before using auto unlocking")
×
371
        }
×
372

373
        // What wallet mode are we running in? We've already made sure the no
374
        // seed backup and auto unlock aren't both set during config parsing.
375
        switch {
×
376
        // No seed backup means we're also using the default password.
377
        case d.cfg.NoSeedBackup:
×
378
                // We continue normally, the default password has already been
379
                // set above.
380

381
        // A password for unlocking is provided in a file.
382
        case d.cfg.WalletUnlockPasswordFile != "" && walletExists:
×
383
                d.logger.Infof("Attempting automatic wallet unlock with " +
×
384
                        "password provided in file")
×
385
                pwBytes, err := os.ReadFile(d.cfg.WalletUnlockPasswordFile)
×
386
                if err != nil {
×
387
                        return nil, nil, nil, fmt.Errorf("error reading "+
×
388
                                "password from file %s: %v",
×
389
                                d.cfg.WalletUnlockPasswordFile, err)
×
390
                }
×
391

392
                // Remove any newlines at the end of the file. The lndinit tool
393
                // won't ever write a newline but maybe the file was provisioned
394
                // by another process or user.
395
                pwBytes = bytes.TrimRight(pwBytes, "\r\n")
×
396

×
397
                // We have the password now, we can ask the unlocker service to
×
398
                // do the unlock for us.
×
399
                unlockedWallet, unloadWalletFn, err := d.pwService.LoadAndUnlock(
×
400
                        pwBytes, 0,
×
401
                )
×
402
                if err != nil {
×
403
                        return nil, nil, nil, fmt.Errorf("error unlocking "+
×
404
                                "wallet with password from file: %v", err)
×
405
                }
×
406

407
                cleanUpTasks = append(cleanUpTasks, func() {
×
408
                        if err := unloadWalletFn(); err != nil {
×
409
                                d.logger.Errorf("Could not unload wallet: %v",
×
410
                                        err)
×
411
                        }
×
412
                })
413

414
                privateWalletPw = pwBytes
×
415
                publicWalletPw = pwBytes
×
416
                walletInitParams.Wallet = unlockedWallet
×
417
                walletInitParams.UnloadWallet = unloadWalletFn
×
418

419
        // If none of the automatic startup options are selected, we fall back
420
        // to the default behavior of waiting for the wallet creation/unlocking
421
        // over RPC.
422
        default:
×
423
                if err := d.interceptor.Notifier.NotifyReady(false); err != nil {
×
424
                        return nil, nil, nil, err
×
425
                }
×
426

427
                params, err := waitForWalletPassword(
×
428
                        d.cfg, d.pwService, []btcwallet.LoaderOption{dbs.WalletDB},
×
429
                        d.interceptor.ShutdownChannel(),
×
430
                )
×
431
                if err != nil {
×
432
                        err := fmt.Errorf("unable to set up wallet password "+
×
433
                                "listeners: %v", err)
×
434
                        d.logger.Error(err)
×
435
                        return nil, nil, nil, err
×
436
                }
×
437

438
                walletInitParams = *params
×
439
                privateWalletPw = walletInitParams.Password
×
440
                publicWalletPw = walletInitParams.Password
×
441
                cleanUpTasks = append(cleanUpTasks, func() {
×
442
                        if err := walletInitParams.UnloadWallet(); err != nil {
×
443
                                d.logger.Errorf("Could not unload wallet: %v",
×
444
                                        err)
×
445
                        }
×
446
                })
447

448
                if walletInitParams.RecoveryWindow > 0 {
×
449
                        d.logger.Infof("Wallet recovery mode enabled with "+
×
450
                                "address lookahead of %d addresses",
×
451
                                walletInitParams.RecoveryWindow)
×
452
                }
×
453
        }
454

455
        var macaroonService *macaroons.Service
×
456
        if !d.cfg.NoMacaroons {
×
457
                // Create the macaroon authentication/authorization service.
×
458
                rootKeyStore, err := macaroons.NewRootKeyStorage(dbs.MacaroonDB)
×
459
                if err != nil {
×
460
                        return nil, nil, nil, err
×
461
                }
×
462
                macaroonService, err = macaroons.NewService(
×
463
                        rootKeyStore, "lnd", walletInitParams.StatelessInit,
×
464
                        macaroons.IPLockChecker,
×
465
                        macaroons.CustomChecker(interceptorChain),
×
466
                )
×
467
                if err != nil {
×
468
                        err := fmt.Errorf("unable to set up macaroon "+
×
469
                                "authentication: %v", err)
×
470
                        d.logger.Error(err)
×
471
                        return nil, nil, nil, err
×
472
                }
×
473
                cleanUpTasks = append(cleanUpTasks, func() {
×
474
                        if err := macaroonService.Close(); err != nil {
×
475
                                d.logger.Errorf("Could not close macaroon "+
×
476
                                        "service: %v", err)
×
477
                        }
×
478
                })
479

480
                // Try to unlock the macaroon store with the private password.
481
                // Ignore ErrAlreadyUnlocked since it could be unlocked by the
482
                // wallet unlocker.
483
                err = macaroonService.CreateUnlock(&privateWalletPw)
×
484
                if err != nil && err != macaroons.ErrAlreadyUnlocked {
×
485
                        err := fmt.Errorf("unable to unlock macaroons: %w", err)
×
486
                        d.logger.Error(err)
×
487
                        return nil, nil, nil, err
×
488
                }
×
489

490
                // If we have a macaroon root key from the init wallet params,
491
                // set the root key before baking any macaroons.
492
                if len(walletInitParams.MacRootKey) > 0 {
×
493
                        err := macaroonService.SetRootKey(
×
494
                                walletInitParams.MacRootKey,
×
495
                        )
×
496
                        if err != nil {
×
497
                                return nil, nil, nil, err
×
498
                        }
×
499
                }
500

501
                // Send an admin macaroon to all our listeners that requested
502
                // one by setting a non-nil macaroon channel.
503
                adminMacBytes, err := bakeMacaroon(
×
504
                        ctx, macaroonService, adminPermissions(),
×
505
                )
×
506
                if err != nil {
×
507
                        return nil, nil, nil, err
×
508
                }
×
509
                for _, lis := range grpcListeners {
×
510
                        if lis.MacChan != nil {
×
511
                                lis.MacChan <- adminMacBytes
×
512
                        }
×
513
                }
514

515
                // In case we actually needed to unlock the wallet, we now need
516
                // to create an instance of the admin macaroon and send it to
517
                // the unlocker so it can forward it to the user. In no seed
518
                // backup mode, there's nobody listening on the channel and we'd
519
                // block here forever.
520
                if !d.cfg.NoSeedBackup {
×
521
                        // The channel is buffered by one element so writing
×
522
                        // should not block here.
×
523
                        walletInitParams.MacResponseChan <- adminMacBytes
×
524
                }
×
525

526
                // If the user requested a stateless initialization, no macaroon
527
                // files should be created.
528
                if !walletInitParams.StatelessInit {
×
529
                        // Create default macaroon files for lncli to use if
×
530
                        // they don't exist.
×
531
                        err = genDefaultMacaroons(
×
532
                                ctx, macaroonService, d.cfg.AdminMacPath,
×
533
                                d.cfg.ReadMacPath, d.cfg.InvoiceMacPath,
×
534
                        )
×
535
                        if err != nil {
×
536
                                err := fmt.Errorf("unable to create macaroons "+
×
537
                                        "%v", err)
×
538
                                d.logger.Error(err)
×
539
                                return nil, nil, nil, err
×
540
                        }
×
541
                }
542

543
                // As a security service to the user, if they requested
544
                // stateless initialization and there are macaroon files on disk
545
                // we log a warning.
546
                if walletInitParams.StatelessInit {
×
547
                        msg := "Found %s macaroon on disk (%s) even though " +
×
548
                                "--stateless_init was requested. Unencrypted " +
×
549
                                "state is accessible by the host system. You " +
×
550
                                "should change the password and use " +
×
551
                                "--new_mac_root_key with --stateless_init to " +
×
552
                                "clean up and invalidate old macaroons."
×
553

×
554
                        if lnrpc.FileExists(d.cfg.AdminMacPath) {
×
555
                                d.logger.Warnf(msg, "admin", d.cfg.AdminMacPath)
×
556
                        }
×
557
                        if lnrpc.FileExists(d.cfg.ReadMacPath) {
×
558
                                d.logger.Warnf(msg, "readonly", d.cfg.ReadMacPath)
×
559
                        }
×
560
                        if lnrpc.FileExists(d.cfg.InvoiceMacPath) {
×
561
                                d.logger.Warnf(msg, "invoice", d.cfg.InvoiceMacPath)
×
562
                        }
×
563
                }
564

565
                // We add the macaroon service to our RPC interceptor. This
566
                // will start checking macaroons against permissions on every
567
                // RPC invocation.
568
                interceptorChain.AddMacaroonService(macaroonService)
×
569
        }
570

571
        // Now that the wallet password has been provided, transition the RPC
572
        // state into Unlocked.
573
        interceptorChain.SetWalletUnlocked()
×
574

×
575
        // Since calls to the WalletUnlocker service wait for a response on the
×
576
        // macaroon channel, we close it here to make sure they return in case
×
577
        // we did not return the admin macaroon above. This will be the case if
×
578
        // --no-macaroons is used.
×
579
        close(walletInitParams.MacResponseChan)
×
580

×
581
        // We'll also close all the macaroon channels since lnd is done sending
×
582
        // macaroon data over it.
×
583
        for _, lis := range grpcListeners {
×
584
                if lis.MacChan != nil {
×
585
                        close(lis.MacChan)
×
586
                }
×
587
        }
588

589
        // With the information parsed from the configuration, create valid
590
        // instances of the pertinent interfaces required to operate the
591
        // Lightning Network Daemon.
592
        //
593
        // When we create the chain control, we need storage for the height
594
        // hints and also the wallet itself, for these two we want them to be
595
        // replicated, so we'll pass in the remote channel DB instance.
596
        chainControlCfg := &chainreg.Config{
×
597
                Bitcoin:                     d.cfg.Bitcoin,
×
598
                HeightHintCacheQueryDisable: d.cfg.HeightHintCacheQueryDisable,
×
599
                NeutrinoMode:                d.cfg.NeutrinoMode,
×
600
                BitcoindMode:                d.cfg.BitcoindMode,
×
601
                BtcdMode:                    d.cfg.BtcdMode,
×
602
                HeightHintDB:                dbs.HeightHintDB,
×
603
                ChanStateDB:                 dbs.ChanStateDB.ChannelStateDB(),
×
604
                NeutrinoCS:                  neutrinoCS,
×
605
                AuxLeafStore:                aux.AuxLeafStore,
×
606
                AuxSigner:                   aux.AuxSigner,
×
607
                ActiveNetParams:             d.cfg.ActiveNetParams,
×
608
                FeeURL:                      d.cfg.FeeURL,
×
609
                Fee: &lncfg.Fee{
×
610
                        URL:              d.cfg.Fee.URL,
×
611
                        MinUpdateTimeout: d.cfg.Fee.MinUpdateTimeout,
×
612
                        MaxUpdateTimeout: d.cfg.Fee.MaxUpdateTimeout,
×
613
                },
×
614
                Dialer: func(addr string) (net.Conn, error) {
×
615
                        return d.cfg.net.Dial(
×
616
                                "tcp", addr, d.cfg.ConnectionTimeout,
×
617
                        )
×
618
                },
×
619
                BlockCache:         blockCache,
620
                WalletUnlockParams: &walletInitParams,
621
        }
622

623
        // Let's go ahead and create the partial chain control now that is only
624
        // dependent on our configuration and doesn't require any wallet
625
        // specific information.
626
        partialChainControl, pccCleanup, err := chainreg.NewPartialChainControl(
×
627
                chainControlCfg,
×
628
        )
×
629
        cleanUpTasks = append(cleanUpTasks, pccCleanup)
×
630
        if err != nil {
×
631
                err := fmt.Errorf("unable to create partial chain control: %w",
×
632
                        err)
×
633
                d.logger.Error(err)
×
634
                return nil, nil, nil, err
×
635
        }
×
636

637
        walletConfig := &btcwallet.Config{
×
638
                PrivatePass:      privateWalletPw,
×
639
                PublicPass:       publicWalletPw,
×
640
                Birthday:         walletInitParams.Birthday,
×
641
                RecoveryWindow:   walletInitParams.RecoveryWindow,
×
642
                NetParams:        d.cfg.ActiveNetParams.Params,
×
643
                CoinType:         d.cfg.ActiveNetParams.CoinType,
×
644
                Wallet:           walletInitParams.Wallet,
×
645
                LoaderOptions:    []btcwallet.LoaderOption{dbs.WalletDB},
×
646
                ChainSource:      partialChainControl.ChainSource,
×
647
                WatchOnly:        d.watchOnly,
×
648
                MigrateWatchOnly: d.migrateWatchOnly,
×
649
        }
×
650

×
651
        // Parse coin selection strategy.
×
652
        switch d.cfg.CoinSelectionStrategy {
×
653
        case "largest":
×
654
                walletConfig.CoinSelectionStrategy = wallet.CoinSelectionLargest
×
655

656
        case "random":
×
657
                walletConfig.CoinSelectionStrategy = wallet.CoinSelectionRandom
×
658

659
        default:
×
660
                return nil, nil, nil, fmt.Errorf("unknown coin selection "+
×
661
                        "strategy %v", d.cfg.CoinSelectionStrategy)
×
662
        }
663

664
        earlyExit = false
×
665
        return partialChainControl, walletConfig, cleanUp, nil
×
666
}
667

668
// proxyBlockEpoch proxies a block epoch subsections to the underlying neutrino
669
// rebroadcaster client.
670
func proxyBlockEpoch(
671
        notifier chainntnfs.ChainNotifier) func() (*blockntfns.Subscription,
672
        error) {
×
673

×
674
        return func() (*blockntfns.Subscription, error) {
×
675
                blockEpoch, err := notifier.RegisterBlockEpochNtfn(
×
676
                        nil,
×
677
                )
×
678
                if err != nil {
×
679
                        return nil, err
×
680
                }
×
681

682
                sub := blockntfns.Subscription{
×
683
                        Notifications: make(chan blockntfns.BlockNtfn, 6),
×
684
                        Cancel:        blockEpoch.Cancel,
×
685
                }
×
686
                go func() {
×
687
                        for blk := range blockEpoch.Epochs {
×
688
                                ntfn := blockntfns.NewBlockConnected(
×
689
                                        *blk.BlockHeader,
×
690
                                        uint32(blk.Height),
×
691
                                )
×
692

×
693
                                sub.Notifications <- ntfn
×
694
                        }
×
695
                }()
696

697
                return &sub, nil
×
698
        }
699
}
700

701
// walletReBroadcaster is a simple wrapper around the pushtx.Broadcaster
702
// interface to adhere to the expanded lnwallet.Rebroadcaster interface.
703
type walletReBroadcaster struct {
704
        started atomic.Bool
705

706
        *pushtx.Broadcaster
707
}
708

709
// newWalletReBroadcaster creates a new instance of the walletReBroadcaster.
710
func newWalletReBroadcaster(
711
        broadcaster *pushtx.Broadcaster) *walletReBroadcaster {
×
712

×
713
        return &walletReBroadcaster{
×
714
                Broadcaster: broadcaster,
×
715
        }
×
716
}
×
717

718
// Start launches all goroutines the rebroadcaster needs to operate.
719
func (w *walletReBroadcaster) Start() error {
×
720
        defer w.started.Store(true)
×
721

×
722
        return w.Broadcaster.Start()
×
723
}
×
724

725
// Started returns true if the broadcaster is already active.
726
func (w *walletReBroadcaster) Started() bool {
×
727
        return w.started.Load()
×
728
}
×
729

730
// BuildChainControl is responsible for creating a fully populated chain
731
// control instance from a wallet.
732
//
733
// NOTE: This is part of the ChainControlBuilder interface.
734
func (d *DefaultWalletImpl) BuildChainControl(
735
        partialChainControl *chainreg.PartialChainControl,
736
        walletConfig *btcwallet.Config) (*chainreg.ChainControl, func(), error) {
×
737

×
738
        walletController, err := btcwallet.New(
×
739
                *walletConfig, partialChainControl.Cfg.BlockCache,
×
740
        )
×
741
        if err != nil {
×
742
                err := fmt.Errorf("unable to create wallet controller: %w", err)
×
743
                d.logger.Error(err)
×
744
                return nil, nil, err
×
745
        }
×
746

747
        keyRing := keychain.NewBtcWalletKeyRing(
×
748
                walletController.InternalWallet(), walletConfig.CoinType,
×
749
        )
×
750

×
751
        // Create, and start the lnwallet, which handles the core payment
×
752
        // channel logic, and exposes control via proxy state machines.
×
753
        lnWalletConfig := lnwallet.Config{
×
754
                Database:              partialChainControl.Cfg.ChanStateDB,
×
755
                Notifier:              partialChainControl.ChainNotifier,
×
756
                WalletController:      walletController,
×
757
                Signer:                walletController,
×
758
                FeeEstimator:          partialChainControl.FeeEstimator,
×
759
                SecretKeyRing:         keyRing,
×
760
                ChainIO:               walletController,
×
761
                NetParams:             *walletConfig.NetParams,
×
762
                CoinSelectionStrategy: walletConfig.CoinSelectionStrategy,
×
763
                AuxLeafStore:          partialChainControl.Cfg.AuxLeafStore,
×
764
                AuxSigner:             partialChainControl.Cfg.AuxSigner,
×
765
        }
×
766

×
767
        // The broadcast is already always active for neutrino nodes, so we
×
768
        // don't want to create a rebroadcast loop.
×
769
        if partialChainControl.Cfg.NeutrinoCS == nil {
×
770
                cs := partialChainControl.ChainSource
×
771
                broadcastCfg := pushtx.Config{
×
772
                        Broadcast: func(tx *wire.MsgTx) error {
×
773
                                _, err := cs.SendRawTransaction(
×
774
                                        tx, true,
×
775
                                )
×
776

×
777
                                return err
×
778
                        },
×
779
                        SubscribeBlocks: proxyBlockEpoch(
780
                                partialChainControl.ChainNotifier,
781
                        ),
782
                        RebroadcastInterval: pushtx.DefaultRebroadcastInterval,
783
                        // In case the backend is different from neutrino we
784
                        // make sure that broadcast backend errors are mapped
785
                        // to the neutrino broadcastErr.
786
                        MapCustomBroadcastError: func(err error) error {
×
787
                                rpcErr := cs.MapRPCErr(err)
×
788
                                return broadcastErrorMapper(rpcErr)
×
789
                        },
×
790
                }
791

792
                lnWalletConfig.Rebroadcaster = newWalletReBroadcaster(
×
793
                        pushtx.NewBroadcaster(&broadcastCfg),
×
794
                )
×
795
        }
796

797
        // We've created the wallet configuration now, so we can finish
798
        // initializing the main chain control.
799
        activeChainControl, cleanUp, err := chainreg.NewChainControl(
×
800
                lnWalletConfig, walletController, partialChainControl,
×
801
        )
×
802
        if err != nil {
×
803
                err := fmt.Errorf("unable to create chain control: %w", err)
×
804
                d.logger.Error(err)
×
805
                return nil, nil, err
×
806
        }
×
807

808
        return activeChainControl, cleanUp, nil
×
809
}
810

811
// RPCSignerWalletImpl is a wallet implementation that uses a remote signer over
812
// an RPC interface.
813
type RPCSignerWalletImpl struct {
814
        // DefaultWalletImpl is the embedded instance of the default
815
        // implementation that the remote signer uses as its watch-only wallet
816
        // for keeping track of addresses and UTXOs.
817
        *DefaultWalletImpl
818
}
819

820
// NewRPCSignerWalletImpl creates a new instance of the remote signing wallet
821
// implementation.
822
func NewRPCSignerWalletImpl(cfg *Config, logger btclog.Logger,
823
        interceptor signal.Interceptor,
824
        migrateWatchOnly bool) *RPCSignerWalletImpl {
×
825

×
826
        return &RPCSignerWalletImpl{
×
827
                DefaultWalletImpl: &DefaultWalletImpl{
×
828
                        cfg:              cfg,
×
829
                        logger:           logger,
×
830
                        interceptor:      interceptor,
×
831
                        watchOnly:        true,
×
832
                        migrateWatchOnly: migrateWatchOnly,
×
833
                        pwService:        createWalletUnlockerService(cfg),
×
834
                },
×
835
        }
×
836
}
×
837

838
// BuildChainControl is responsible for creating or unlocking and then fully
839
// initializing a wallet and returning it as part of a fully populated chain
840
// control instance.
841
//
842
// NOTE: This is part of the ChainControlBuilder interface.
843
func (d *RPCSignerWalletImpl) BuildChainControl(
844
        partialChainControl *chainreg.PartialChainControl,
845
        walletConfig *btcwallet.Config) (*chainreg.ChainControl, func(), error) {
×
846

×
847
        walletController, err := btcwallet.New(
×
848
                *walletConfig, partialChainControl.Cfg.BlockCache,
×
849
        )
×
850
        if err != nil {
×
851
                err := fmt.Errorf("unable to create wallet controller: %w", err)
×
852
                d.logger.Error(err)
×
853
                return nil, nil, err
×
854
        }
×
855

856
        baseKeyRing := keychain.NewBtcWalletKeyRing(
×
857
                walletController.InternalWallet(), walletConfig.CoinType,
×
858
        )
×
859

×
860
        rpcKeyRing, err := rpcwallet.NewRPCKeyRing(
×
861
                baseKeyRing, walletController,
×
862
                d.DefaultWalletImpl.cfg.RemoteSigner, walletConfig.NetParams,
×
863
        )
×
864
        if err != nil {
×
865
                err := fmt.Errorf("unable to create RPC remote signing wallet "+
×
866
                        "%v", err)
×
867
                d.logger.Error(err)
×
868
                return nil, nil, err
×
869
        }
×
870

871
        // Create, and start the lnwallet, which handles the core payment
872
        // channel logic, and exposes control via proxy state machines.
873
        lnWalletConfig := lnwallet.Config{
×
874
                Database:              partialChainControl.Cfg.ChanStateDB,
×
875
                Notifier:              partialChainControl.ChainNotifier,
×
876
                WalletController:      rpcKeyRing,
×
877
                Signer:                rpcKeyRing,
×
878
                FeeEstimator:          partialChainControl.FeeEstimator,
×
879
                SecretKeyRing:         rpcKeyRing,
×
880
                ChainIO:               walletController,
×
881
                NetParams:             *walletConfig.NetParams,
×
882
                CoinSelectionStrategy: walletConfig.CoinSelectionStrategy,
×
883
        }
×
884

×
885
        // We've created the wallet configuration now, so we can finish
×
886
        // initializing the main chain control.
×
887
        activeChainControl, cleanUp, err := chainreg.NewChainControl(
×
888
                lnWalletConfig, rpcKeyRing, partialChainControl,
×
889
        )
×
890
        if err != nil {
×
891
                err := fmt.Errorf("unable to create chain control: %w", err)
×
892
                d.logger.Error(err)
×
893
                return nil, nil, err
×
894
        }
×
895

896
        return activeChainControl, cleanUp, nil
×
897
}
898

899
// DatabaseInstances is a struct that holds all instances to the actual
900
// databases that are used in lnd.
901
type DatabaseInstances struct {
902
        // GraphDB is the database that stores the channel graph used for path
903
        // finding.
904
        GraphDB *graphdb.ChannelGraph
905

906
        // ChanStateDB is the database that stores all of our node's channel
907
        // state.
908
        ChanStateDB *channeldb.DB
909

910
        // HeightHintDB is the database that stores height hints for spends.
911
        HeightHintDB kvdb.Backend
912

913
        // InvoiceDB is the database that stores information about invoices.
914
        InvoiceDB invoices.InvoiceDB
915

916
        // MacaroonDB is the database that stores macaroon root keys.
917
        MacaroonDB kvdb.Backend
918

919
        // DecayedLogDB is the database that stores p2p related encryption
920
        // information.
921
        DecayedLogDB kvdb.Backend
922

923
        // TowerClientDB is the database that stores the watchtower client's
924
        // configuration.
925
        TowerClientDB wtclient.DB
926

927
        // TowerServerDB is the database that stores the watchtower server's
928
        // configuration.
929
        TowerServerDB watchtower.DB
930

931
        // WalletDB is the configuration for loading the wallet database using
932
        // the btcwallet's loader.
933
        WalletDB btcwallet.LoaderOption
934

935
        // NativeSQLStore is a pointer to a native SQL store that can be used
936
        // for native SQL queries for tables that already support it. This may
937
        // be nil if the use-native-sql flag was not set.
938
        NativeSQLStore *sqldb.BaseDB
939
}
940

941
// DefaultDatabaseBuilder is a type that builds the default database backends
942
// for lnd, using the given configuration to decide what actual implementation
943
// to use.
944
type DefaultDatabaseBuilder struct {
945
        cfg    *Config
946
        logger btclog.Logger
947
}
948

949
// NewDefaultDatabaseBuilder returns a new instance of the default database
950
// builder.
951
func NewDefaultDatabaseBuilder(cfg *Config,
952
        logger btclog.Logger) *DefaultDatabaseBuilder {
×
953

×
954
        return &DefaultDatabaseBuilder{
×
955
                cfg:    cfg,
×
956
                logger: logger,
×
957
        }
×
958
}
×
959

960
// BuildDatabase extracts the current databases that we'll use for normal
961
// operation in the daemon. A function closure that closes all opened databases
962
// is also returned.
963
func (d *DefaultDatabaseBuilder) BuildDatabase(
964
        ctx context.Context) (*DatabaseInstances, func(), error) {
×
965

×
966
        d.logger.Infof("Opening the main database, this might take a few " +
×
967
                "minutes...")
×
968

×
969
        cfg := d.cfg
×
970
        if cfg.DB.Backend == lncfg.BoltBackend {
×
971
                d.logger.Infof("Opening bbolt database, sync_freelist=%v, "+
×
972
                        "auto_compact=%v", !cfg.DB.Bolt.NoFreelistSync,
×
973
                        cfg.DB.Bolt.AutoCompact)
×
974
        }
×
975

976
        startOpenTime := time.Now()
×
977

×
978
        databaseBackends, err := cfg.DB.GetBackends(
×
979
                ctx, cfg.graphDatabaseDir(), cfg.networkDir, filepath.Join(
×
980
                        cfg.Watchtower.TowerDir, BitcoinChainName,
×
981
                        lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
×
982
                ), cfg.WtClient.Active, cfg.Watchtower.Active, d.logger,
×
983
        )
×
984
        if err != nil {
×
985
                return nil, nil, fmt.Errorf("unable to obtain database "+
×
986
                        "backends: %v", err)
×
987
        }
×
988

989
        // With the full remote mode we made sure both the graph and channel
990
        // state DB point to the same local or remote DB and the same namespace
991
        // within that DB.
992
        dbs := &DatabaseInstances{
×
993
                HeightHintDB:   databaseBackends.HeightHintDB,
×
994
                MacaroonDB:     databaseBackends.MacaroonDB,
×
995
                DecayedLogDB:   databaseBackends.DecayedLogDB,
×
996
                WalletDB:       databaseBackends.WalletDB,
×
997
                NativeSQLStore: databaseBackends.NativeSQLStore,
×
998
        }
×
999
        cleanUp := func() {
×
1000
                // We can just close the returned close functions directly. Even
×
1001
                // if we decorate the channel DB with an additional struct, its
×
1002
                // close function still just points to the kvdb backend.
×
1003
                for name, closeFunc := range databaseBackends.CloseFuncs {
×
1004
                        if err := closeFunc(); err != nil {
×
1005
                                d.logger.Errorf("Error closing %s "+
×
1006
                                        "database: %v", name, err)
×
1007
                        }
×
1008
                }
1009
        }
1010
        if databaseBackends.Remote {
×
1011
                d.logger.Infof("Using remote %v database! Creating "+
×
1012
                        "graph and channel state DB instances", cfg.DB.Backend)
×
1013
        } else {
×
1014
                d.logger.Infof("Creating local graph and channel state DB " +
×
1015
                        "instances")
×
1016
        }
×
1017

1018
        graphDBOptions := []graphdb.OptionModifier{
×
1019
                graphdb.WithRejectCacheSize(cfg.Caches.RejectCacheSize),
×
1020
                graphdb.WithChannelCacheSize(cfg.Caches.ChannelCacheSize),
×
1021
                graphdb.WithBatchCommitInterval(cfg.DB.BatchCommitInterval),
×
1022
                graphdb.WithUseGraphCache(!cfg.DB.NoGraphCache),
×
1023
        }
×
1024

×
1025
        // We want to pre-allocate the channel graph cache according to what we
×
1026
        // expect for mainnet to speed up memory allocation.
×
1027
        if cfg.ActiveNetParams.Name == chaincfg.MainNetParams.Name {
×
1028
                graphDBOptions = append(
×
1029
                        graphDBOptions, graphdb.WithPreAllocCacheNumNodes(
×
1030
                                graphdb.DefaultPreAllocCacheNumNodes,
×
1031
                        ),
×
1032
                )
×
1033
        }
×
1034

1035
        dbs.GraphDB, err = graphdb.NewChannelGraph(
×
1036
                databaseBackends.GraphDB, graphDBOptions...,
×
1037
        )
×
1038
        if err != nil {
×
1039
                cleanUp()
×
1040

×
1041
                err := fmt.Errorf("unable to open graph DB: %w", err)
×
1042
                d.logger.Error(err)
×
1043

×
1044
                return nil, nil, err
×
1045
        }
×
1046

1047
        dbOptions := []channeldb.OptionModifier{
×
1048
                channeldb.OptionDryRunMigration(cfg.DryRunMigration),
×
1049
                channeldb.OptionKeepFailedPaymentAttempts(
×
1050
                        cfg.KeepFailedPaymentAttempts,
×
1051
                ),
×
1052
                channeldb.OptionStoreFinalHtlcResolutions(
×
1053
                        cfg.StoreFinalHtlcResolutions,
×
1054
                ),
×
1055
                channeldb.OptionPruneRevocationLog(cfg.DB.PruneRevocation),
×
1056
                channeldb.OptionNoRevLogAmtData(cfg.DB.NoRevLogAmtData),
×
1057
        }
×
1058

×
1059
        // Otherwise, we'll open two instances, one for the state we only need
×
1060
        // locally, and the other for things we want to ensure are replicated.
×
1061
        dbs.ChanStateDB, err = channeldb.CreateWithBackend(
×
1062
                databaseBackends.ChanStateDB, dbOptions...,
×
1063
        )
×
1064
        switch {
×
1065
        // Give the DB a chance to dry run the migration. Since we know that
1066
        // both the channel state and graph DBs are still always behind the same
1067
        // backend, we know this would be applied to both of those DBs.
1068
        case err == channeldb.ErrDryRunMigrationOK:
×
1069
                d.logger.Infof("Channel DB dry run migration successful")
×
1070
                return nil, nil, err
×
1071

1072
        case err != nil:
×
1073
                cleanUp()
×
1074

×
1075
                err := fmt.Errorf("unable to open graph DB: %w", err)
×
1076
                d.logger.Error(err)
×
1077
                return nil, nil, err
×
1078
        }
1079

1080
        // Instantiate a native SQL invoice store if the flag is set.
1081
        if d.cfg.DB.UseNativeSQL {
×
1082
                // KV invoice db resides in the same database as the channel
×
1083
                // state DB. Let's query the database to see if we have any
×
1084
                // invoices there. If we do, we won't allow the user to start
×
1085
                // lnd with native SQL enabled, as we don't currently migrate
×
1086
                // the invoices to the new database schema.
×
1087
                invoiceSlice, err := dbs.ChanStateDB.QueryInvoices(
×
1088
                        ctx, invoices.InvoiceQuery{
×
1089
                                NumMaxInvoices: 1,
×
1090
                        },
×
1091
                )
×
1092
                if err != nil {
×
1093
                        cleanUp()
×
1094
                        d.logger.Errorf("Unable to query KV invoice DB: %v",
×
1095
                                err)
×
1096

×
1097
                        return nil, nil, err
×
1098
                }
×
1099

1100
                if len(invoiceSlice.Invoices) > 0 {
×
1101
                        cleanUp()
×
1102
                        err := fmt.Errorf("found invoices in the KV invoice " +
×
1103
                                "DB, migration to native SQL is not yet " +
×
1104
                                "supported")
×
1105
                        d.logger.Error(err)
×
1106

×
1107
                        return nil, nil, err
×
1108
                }
×
1109

1110
                executor := sqldb.NewTransactionExecutor(
×
1111
                        dbs.NativeSQLStore,
×
1112
                        func(tx *sql.Tx) invoices.SQLInvoiceQueries {
×
1113
                                return dbs.NativeSQLStore.WithTx(tx)
×
1114
                        },
×
1115
                )
1116

1117
                dbs.InvoiceDB = invoices.NewSQLStore(
×
1118
                        executor, clock.NewDefaultClock(),
×
1119
                )
×
1120
        } else {
×
1121
                dbs.InvoiceDB = dbs.ChanStateDB
×
1122
        }
×
1123

1124
        // Wrap the watchtower client DB and make sure we clean up.
1125
        if cfg.WtClient.Active {
×
1126
                dbs.TowerClientDB, err = wtdb.OpenClientDB(
×
1127
                        databaseBackends.TowerClientDB,
×
1128
                )
×
1129
                if err != nil {
×
1130
                        cleanUp()
×
1131

×
1132
                        err := fmt.Errorf("unable to open %s database: %w",
×
1133
                                lncfg.NSTowerClientDB, err)
×
1134
                        d.logger.Error(err)
×
1135
                        return nil, nil, err
×
1136
                }
×
1137
        }
1138

1139
        // Wrap the watchtower server DB and make sure we clean up.
1140
        if cfg.Watchtower.Active {
×
1141
                dbs.TowerServerDB, err = wtdb.OpenTowerDB(
×
1142
                        databaseBackends.TowerServerDB,
×
1143
                )
×
1144
                if err != nil {
×
1145
                        cleanUp()
×
1146

×
1147
                        err := fmt.Errorf("unable to open %s database: %w",
×
1148
                                lncfg.NSTowerServerDB, err)
×
1149
                        d.logger.Error(err)
×
1150
                        return nil, nil, err
×
1151
                }
×
1152
        }
1153

1154
        openTime := time.Since(startOpenTime)
×
1155
        d.logger.Infof("Database(s) now open (time_to_open=%v)!", openTime)
×
1156

×
1157
        return dbs, cleanUp, nil
×
1158
}
1159

1160
// waitForWalletPassword blocks until a password is provided by the user to
1161
// this RPC server.
1162
func waitForWalletPassword(cfg *Config,
1163
        pwService *walletunlocker.UnlockerService,
1164
        loaderOpts []btcwallet.LoaderOption, shutdownChan <-chan struct{}) (
1165
        *walletunlocker.WalletUnlockParams, error) {
×
1166

×
1167
        // Wait for user to provide the password.
×
1168
        ltndLog.Infof("Waiting for wallet encryption password. Use `lncli " +
×
1169
                "create` to create a wallet, `lncli unlock` to unlock an " +
×
1170
                "existing wallet, or `lncli changepassword` to change the " +
×
1171
                "password of an existing wallet and unlock it.")
×
1172

×
1173
        // We currently don't distinguish between getting a password to be used
×
1174
        // for creation or unlocking, as a new wallet db will be created if
×
1175
        // none exists when creating the chain control.
×
1176
        select {
×
1177
        // The wallet is being created for the first time, we'll check to see
1178
        // if the user provided any entropy for seed creation. If so, then
1179
        // we'll create the wallet early to load the seed.
1180
        case initMsg := <-pwService.InitMsgs:
×
1181
                password := initMsg.Passphrase
×
1182
                cipherSeed := initMsg.WalletSeed
×
1183
                extendedKey := initMsg.WalletExtendedKey
×
1184
                watchOnlyAccounts := initMsg.WatchOnlyAccounts
×
1185
                recoveryWindow := initMsg.RecoveryWindow
×
1186

×
1187
                // Before we proceed, we'll check the internal version of the
×
1188
                // seed. If it's greater than the current key derivation
×
1189
                // version, then we'll return an error as we don't understand
×
1190
                // this.
×
1191
                if cipherSeed != nil &&
×
1192
                        !keychain.IsKnownVersion(cipherSeed.InternalVersion) {
×
1193

×
1194
                        return nil, fmt.Errorf("invalid internal "+
×
1195
                                "seed version %v, current max version is %v",
×
1196
                                cipherSeed.InternalVersion,
×
1197
                                keychain.CurrentKeyDerivationVersion)
×
1198
                }
×
1199

1200
                loader, err := btcwallet.NewWalletLoader(
×
1201
                        cfg.ActiveNetParams.Params, recoveryWindow,
×
1202
                        loaderOpts...,
×
1203
                )
×
1204
                if err != nil {
×
1205
                        return nil, err
×
1206
                }
×
1207

1208
                // With the seed, we can now use the wallet loader to create
1209
                // the wallet, then pass it back to avoid unlocking it again.
1210
                var (
×
1211
                        birthday  time.Time
×
1212
                        newWallet *wallet.Wallet
×
1213
                )
×
1214
                switch {
×
1215
                // A normal cipher seed was given, use the birthday encoded in
1216
                // it and create the wallet from that.
1217
                case cipherSeed != nil:
×
1218
                        birthday = cipherSeed.BirthdayTime()
×
1219
                        newWallet, err = loader.CreateNewWallet(
×
1220
                                password, password, cipherSeed.Entropy[:],
×
1221
                                birthday,
×
1222
                        )
×
1223

1224
                // No seed was given, we're importing a wallet from its extended
1225
                // private key.
1226
                case extendedKey != nil:
×
1227
                        birthday = initMsg.ExtendedKeyBirthday
×
1228
                        newWallet, err = loader.CreateNewWalletExtendedKey(
×
1229
                                password, password, extendedKey, birthday,
×
1230
                        )
×
1231

1232
                // Neither seed nor extended private key was given, so maybe the
1233
                // third option was chosen, the watch-only initialization. In
1234
                // this case we need to import each of the xpubs individually.
1235
                case watchOnlyAccounts != nil:
×
1236
                        if !cfg.RemoteSigner.Enable {
×
1237
                                return nil, fmt.Errorf("cannot initialize " +
×
1238
                                        "watch only wallet with remote " +
×
1239
                                        "signer config disabled")
×
1240
                        }
×
1241

1242
                        birthday = initMsg.WatchOnlyBirthday
×
1243
                        newWallet, err = loader.CreateNewWatchingOnlyWallet(
×
1244
                                password, birthday,
×
1245
                        )
×
1246
                        if err != nil {
×
1247
                                break
×
1248
                        }
1249

1250
                        err = importWatchOnlyAccounts(newWallet, initMsg)
×
1251

1252
                default:
×
1253
                        // The unlocker service made sure either the cipher seed
×
1254
                        // or the extended key is set so, we shouldn't get here.
×
1255
                        // The default case is just here for readability and
×
1256
                        // completeness.
×
1257
                        err = fmt.Errorf("cannot create wallet, neither seed " +
×
1258
                                "nor extended key was given")
×
1259
                }
1260
                if err != nil {
×
1261
                        // Don't leave the file open in case the new wallet
×
1262
                        // could not be created for whatever reason.
×
1263
                        if err := loader.UnloadWallet(); err != nil {
×
1264
                                ltndLog.Errorf("Could not unload new "+
×
1265
                                        "wallet: %v", err)
×
1266
                        }
×
1267
                        return nil, err
×
1268
                }
1269

1270
                // For new wallets, the ResetWalletTransactions flag is a no-op.
1271
                if cfg.ResetWalletTransactions {
×
1272
                        ltndLog.Warnf("Ignoring reset-wallet-transactions " +
×
1273
                                "flag for new wallet as it has no effect")
×
1274
                }
×
1275

1276
                return &walletunlocker.WalletUnlockParams{
×
1277
                        Password:        password,
×
1278
                        Birthday:        birthday,
×
1279
                        RecoveryWindow:  recoveryWindow,
×
1280
                        Wallet:          newWallet,
×
1281
                        ChansToRestore:  initMsg.ChanBackups,
×
1282
                        UnloadWallet:    loader.UnloadWallet,
×
1283
                        StatelessInit:   initMsg.StatelessInit,
×
1284
                        MacResponseChan: pwService.MacResponseChan,
×
1285
                        MacRootKey:      initMsg.MacRootKey,
×
1286
                }, nil
×
1287

1288
        // The wallet has already been created in the past, and is simply being
1289
        // unlocked. So we'll just return these passphrases.
1290
        case unlockMsg := <-pwService.UnlockMsgs:
×
1291
                // Resetting the transactions is something the user likely only
×
1292
                // wants to do once so we add a prominent warning to the log to
×
1293
                // remind the user to turn off the setting again after
×
1294
                // successful completion.
×
1295
                if cfg.ResetWalletTransactions {
×
1296
                        ltndLog.Warnf("Dropped all transaction history from " +
×
1297
                                "on-chain wallet. Remember to disable " +
×
1298
                                "reset-wallet-transactions flag for next " +
×
1299
                                "start of lnd")
×
1300
                }
×
1301

1302
                return &walletunlocker.WalletUnlockParams{
×
1303
                        Password:        unlockMsg.Passphrase,
×
1304
                        RecoveryWindow:  unlockMsg.RecoveryWindow,
×
1305
                        Wallet:          unlockMsg.Wallet,
×
1306
                        ChansToRestore:  unlockMsg.ChanBackups,
×
1307
                        UnloadWallet:    unlockMsg.UnloadWallet,
×
1308
                        StatelessInit:   unlockMsg.StatelessInit,
×
1309
                        MacResponseChan: pwService.MacResponseChan,
×
1310
                }, nil
×
1311

1312
        // If we got a shutdown signal we just return with an error immediately
1313
        case <-shutdownChan:
×
1314
                return nil, fmt.Errorf("shutting down")
×
1315
        }
1316
}
1317

1318
// importWatchOnlyAccounts imports all individual account xpubs into our wallet
1319
// which we created as watch-only.
1320
func importWatchOnlyAccounts(wallet *wallet.Wallet,
1321
        initMsg *walletunlocker.WalletInitMsg) error {
×
1322

×
1323
        scopes := make([]waddrmgr.ScopedIndex, 0, len(initMsg.WatchOnlyAccounts))
×
1324
        for scope := range initMsg.WatchOnlyAccounts {
×
1325
                scopes = append(scopes, scope)
×
1326
        }
×
1327

1328
        // We need to import the accounts in the correct order, otherwise the
1329
        // indices will be incorrect.
1330
        sort.Slice(scopes, func(i, j int) bool {
×
1331
                return scopes[i].Scope.Purpose < scopes[j].Scope.Purpose ||
×
1332
                        scopes[i].Index < scopes[j].Index
×
1333
        })
×
1334

1335
        for _, scope := range scopes {
×
1336
                addrSchema := waddrmgr.ScopeAddrMap[waddrmgr.KeyScopeBIP0084]
×
1337

×
1338
                // We want witness pubkey hash by default, except for BIP49
×
1339
                // where we want mixed and BIP86 where we want taproot address
×
1340
                // formats.
×
1341
                switch scope.Scope.Purpose {
×
1342
                case waddrmgr.KeyScopeBIP0049Plus.Purpose,
1343
                        waddrmgr.KeyScopeBIP0086.Purpose:
×
1344

×
1345
                        addrSchema = waddrmgr.ScopeAddrMap[scope.Scope]
×
1346
                }
1347

1348
                // We want a human-readable account name. But for the default
1349
                // on-chain wallet we actually need to call it "default" to make
1350
                // sure everything works correctly.
1351
                name := fmt.Sprintf("%s/%d'", scope.Scope.String(), scope.Index)
×
1352
                if scope.Index == 0 {
×
1353
                        name = "default"
×
1354
                }
×
1355

1356
                _, err := wallet.ImportAccountWithScope(
×
1357
                        name, initMsg.WatchOnlyAccounts[scope],
×
1358
                        initMsg.WatchOnlyMasterFingerprint, scope.Scope,
×
1359
                        addrSchema,
×
1360
                )
×
1361
                if err != nil {
×
1362
                        return fmt.Errorf("could not import account %v: %w",
×
1363
                                name, err)
×
1364
                }
×
1365
        }
1366

1367
        return nil
×
1368
}
1369

1370
// initNeutrinoBackend inits a new instance of the neutrino light client
1371
// backend given a target chain directory to store the chain state.
1372
func initNeutrinoBackend(ctx context.Context, cfg *Config, chainDir string,
1373
        blockCache *blockcache.BlockCache) (*neutrino.ChainService,
1374
        func(), error) {
×
1375

×
1376
        // Both channel validation flags are false by default but their meaning
×
1377
        // is the inverse of each other. Therefore both cannot be true. For
×
1378
        // every other case, the neutrino.validatechannels overwrites the
×
1379
        // routing.assumechanvalid value.
×
1380
        if cfg.NeutrinoMode.ValidateChannels && cfg.Routing.AssumeChannelValid {
×
1381
                return nil, nil, fmt.Errorf("can't set both " +
×
1382
                        "neutrino.validatechannels and routing." +
×
1383
                        "assumechanvalid to true at the same time")
×
1384
        }
×
1385
        cfg.Routing.AssumeChannelValid = !cfg.NeutrinoMode.ValidateChannels
×
1386

×
1387
        // First we'll open the database file for neutrino, creating the
×
1388
        // database if needed. We append the normalized network name here to
×
1389
        // match the behavior of btcwallet.
×
1390
        dbPath := filepath.Join(
×
1391
                chainDir, lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
×
1392
        )
×
1393

×
1394
        // Ensure that the neutrino db path exists.
×
1395
        if err := os.MkdirAll(dbPath, 0700); err != nil {
×
1396
                return nil, nil, err
×
1397
        }
×
1398

1399
        var (
×
1400
                db  walletdb.DB
×
1401
                err error
×
1402
        )
×
1403
        switch {
×
1404
        case cfg.DB.Backend == kvdb.SqliteBackendName:
×
1405
                sqliteConfig := lncfg.GetSqliteConfigKVDB(cfg.DB.Sqlite)
×
1406
                db, err = kvdb.Open(
×
1407
                        kvdb.SqliteBackendName, ctx, sqliteConfig, dbPath,
×
1408
                        lncfg.SqliteNeutrinoDBName, lncfg.NSNeutrinoDB,
×
1409
                )
×
1410

1411
        default:
×
1412
                dbName := filepath.Join(dbPath, "neutrino.db")
×
1413
                db, err = walletdb.Create(
×
1414
                        "bdb", dbName, !cfg.SyncFreelist, cfg.DB.Bolt.DBTimeout,
×
1415
                )
×
1416
        }
1417
        if err != nil {
×
1418
                return nil, nil, fmt.Errorf("unable to create "+
×
1419
                        "neutrino database: %v", err)
×
1420
        }
×
1421

1422
        headerStateAssertion, err := parseHeaderStateAssertion(
×
1423
                cfg.NeutrinoMode.AssertFilterHeader,
×
1424
        )
×
1425
        if err != nil {
×
1426
                db.Close()
×
1427
                return nil, nil, err
×
1428
        }
×
1429

1430
        // With the database open, we can now create an instance of the
1431
        // neutrino light client. We pass in relevant configuration parameters
1432
        // required.
1433
        config := neutrino.Config{
×
1434
                DataDir:      dbPath,
×
1435
                Database:     db,
×
1436
                ChainParams:  *cfg.ActiveNetParams.Params,
×
1437
                AddPeers:     cfg.NeutrinoMode.AddPeers,
×
1438
                ConnectPeers: cfg.NeutrinoMode.ConnectPeers,
×
1439
                Dialer: func(addr net.Addr) (net.Conn, error) {
×
1440
                        return cfg.net.Dial(
×
1441
                                addr.Network(), addr.String(),
×
1442
                                cfg.ConnectionTimeout,
×
1443
                        )
×
1444
                },
×
1445
                NameResolver: func(host string) ([]net.IP, error) {
×
1446
                        addrs, err := cfg.net.LookupHost(host)
×
1447
                        if err != nil {
×
1448
                                return nil, err
×
1449
                        }
×
1450

1451
                        ips := make([]net.IP, 0, len(addrs))
×
1452
                        for _, strIP := range addrs {
×
1453
                                ip := net.ParseIP(strIP)
×
1454
                                if ip == nil {
×
1455
                                        continue
×
1456
                                }
1457

1458
                                ips = append(ips, ip)
×
1459
                        }
1460

1461
                        return ips, nil
×
1462
                },
1463
                AssertFilterHeader: headerStateAssertion,
1464
                BlockCache:         blockCache.Cache,
1465
                BroadcastTimeout:   cfg.NeutrinoMode.BroadcastTimeout,
1466
                PersistToDisk:      cfg.NeutrinoMode.PersistFilters,
1467
        }
1468

1469
        neutrino.MaxPeers = 8
×
1470
        neutrino.BanDuration = time.Hour * 48
×
1471
        neutrino.UserAgentName = cfg.NeutrinoMode.UserAgentName
×
1472
        neutrino.UserAgentVersion = cfg.NeutrinoMode.UserAgentVersion
×
1473

×
1474
        neutrinoCS, err := neutrino.NewChainService(config)
×
1475
        if err != nil {
×
1476
                db.Close()
×
1477
                return nil, nil, fmt.Errorf("unable to create neutrino light "+
×
1478
                        "client: %v", err)
×
1479
        }
×
1480

1481
        if err := neutrinoCS.Start(); err != nil {
×
1482
                db.Close()
×
1483
                return nil, nil, err
×
1484
        }
×
1485

1486
        cleanUp := func() {
×
1487
                if err := neutrinoCS.Stop(); err != nil {
×
1488
                        ltndLog.Infof("Unable to stop neutrino light client: "+
×
1489
                                "%v", err)
×
1490
                }
×
1491
                db.Close()
×
1492
        }
1493

1494
        return neutrinoCS, cleanUp, nil
×
1495
}
1496

1497
// parseHeaderStateAssertion parses the user-specified neutrino header state
1498
// into a headerfs.FilterHeader.
1499
func parseHeaderStateAssertion(state string) (*headerfs.FilterHeader, error) {
×
1500
        if len(state) == 0 {
×
1501
                return nil, nil
×
1502
        }
×
1503

1504
        split := strings.Split(state, ":")
×
1505
        if len(split) != 2 {
×
1506
                return nil, fmt.Errorf("header state assertion %v in "+
×
1507
                        "unexpected format, expected format height:hash", state)
×
1508
        }
×
1509

1510
        height, err := strconv.ParseUint(split[0], 10, 32)
×
1511
        if err != nil {
×
1512
                return nil, fmt.Errorf("invalid filter header height: %w", err)
×
1513
        }
×
1514

1515
        hash, err := chainhash.NewHashFromStr(split[1])
×
1516
        if err != nil {
×
1517
                return nil, fmt.Errorf("invalid filter header hash: %w", err)
×
1518
        }
×
1519

1520
        return &headerfs.FilterHeader{
×
1521
                Height:     uint32(height),
×
1522
                FilterHash: *hash,
×
1523
        }, nil
×
1524
}
1525

1526
// broadcastErrorMapper maps errors from bitcoin backends other than neutrino to
1527
// the neutrino BroadcastError which allows the Rebroadcaster which currently
1528
// resides in the neutrino package to use all of its functionalities.
1529
func broadcastErrorMapper(err error) error {
×
1530
        var returnErr error
×
1531

×
1532
        // We only filter for specific backend errors which are relevant for the
×
1533
        // Rebroadcaster.
×
1534
        switch {
×
1535
        // This makes sure the tx is removed from the rebroadcaster once it is
1536
        // confirmed.
1537
        case errors.Is(err, chain.ErrTxAlreadyKnown),
1538
                errors.Is(err, chain.ErrTxAlreadyConfirmed):
×
1539

×
1540
                returnErr = &pushtx.BroadcastError{
×
1541
                        Code:   pushtx.Confirmed,
×
1542
                        Reason: err.Error(),
×
1543
                }
×
1544

1545
        // Transactions which are still in mempool but might fall out because
1546
        // of low fees are rebroadcasted despite of their backend error.
1547
        case errors.Is(err, chain.ErrTxAlreadyInMempool):
×
1548
                returnErr = &pushtx.BroadcastError{
×
1549
                        Code:   pushtx.Mempool,
×
1550
                        Reason: err.Error(),
×
1551
                }
×
1552

1553
        // Transactions which are not accepted into mempool because of low fees
1554
        // in the first place are rebroadcasted despite of their backend error.
1555
        // Mempool conditions change over time so it makes sense to retry
1556
        // publishing the transaction. Moreover we log the detailed error so the
1557
        // user can intervene and increase the size of his mempool.
1558
        case errors.Is(err, chain.ErrMempoolMinFeeNotMet):
×
1559
                ltndLog.Warnf("Error while broadcasting transaction: %v", err)
×
1560

×
1561
                returnErr = &pushtx.BroadcastError{
×
1562
                        Code:   pushtx.Mempool,
×
1563
                        Reason: err.Error(),
×
1564
                }
×
1565
        }
1566

1567
        return returnErr
×
1568
}
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