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

lightningnetwork / lnd / 11216766535

07 Oct 2024 01:37PM UTC coverage: 57.817% (-1.0%) from 58.817%
11216766535

Pull #9148

github

ProofOfKeags
lnwire: remove kickoff feerate from propose/commit
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

571 of 879 new or added lines in 16 files covered. (64.96%)

23253 existing lines in 251 files now uncovered.

99022 of 171268 relevant lines covered (57.82%)

38420.67 hits per line

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

0.0
/chainreg/chainregistry.go
1
package chainreg
2

3
import (
4
        "encoding/hex"
5
        "encoding/json"
6
        "errors"
7
        "fmt"
8
        "io"
9
        "net"
10
        "net/url"
11
        "os"
12
        "strconv"
13
        "strings"
14
        "time"
15

16
        "github.com/btcsuite/btcd/chaincfg/chainhash"
17
        "github.com/btcsuite/btcd/rpcclient"
18
        "github.com/btcsuite/btcwallet/chain"
19
        "github.com/lightninglabs/neutrino"
20
        "github.com/lightningnetwork/lnd/blockcache"
21
        "github.com/lightningnetwork/lnd/chainntnfs"
22
        "github.com/lightningnetwork/lnd/chainntnfs/bitcoindnotify"
23
        "github.com/lightningnetwork/lnd/chainntnfs/btcdnotify"
24
        "github.com/lightningnetwork/lnd/chainntnfs/neutrinonotify"
25
        "github.com/lightningnetwork/lnd/channeldb"
26
        "github.com/lightningnetwork/lnd/channeldb/models"
27
        "github.com/lightningnetwork/lnd/fn"
28
        "github.com/lightningnetwork/lnd/input"
29
        "github.com/lightningnetwork/lnd/keychain"
30
        "github.com/lightningnetwork/lnd/kvdb"
31
        "github.com/lightningnetwork/lnd/lncfg"
32
        "github.com/lightningnetwork/lnd/lnwallet"
33
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
34
        "github.com/lightningnetwork/lnd/lnwire"
35
        "github.com/lightningnetwork/lnd/routing/chainview"
36
        "github.com/lightningnetwork/lnd/walletunlocker"
37
)
38

39
// Config houses necessary fields that a chainControl instance needs to
40
// function.
41
type Config struct {
42
        // Bitcoin defines settings for the Bitcoin chain.
43
        Bitcoin *lncfg.Chain
44

45
        // HeightHintCacheQueryDisable is a boolean that disables height hint
46
        // queries if true.
47
        HeightHintCacheQueryDisable bool
48

49
        // NeutrinoMode defines settings for connecting to a neutrino
50
        // light-client.
51
        NeutrinoMode *lncfg.Neutrino
52

53
        // BitcoindMode defines settings for connecting to a bitcoind node.
54
        BitcoindMode *lncfg.Bitcoind
55

56
        // BtcdMode defines settings for connecting to a btcd node.
57
        BtcdMode *lncfg.Btcd
58

59
        // HeightHintDB is a pointer to the database that stores the height
60
        // hints.
61
        HeightHintDB kvdb.Backend
62

63
        // ChanStateDB is a pointer to the database that stores the channel
64
        // state.
65
        ChanStateDB *channeldb.ChannelStateDB
66

67
        // AuxLeafStore is an optional store that can be used to store auxiliary
68
        // leaves for certain custom channel types.
69
        AuxLeafStore fn.Option[lnwallet.AuxLeafStore]
70

71
        // AuxSigner is an optional signer that can be used to sign auxiliary
72
        // leaves for certain custom channel types.
73
        AuxSigner fn.Option[lnwallet.AuxSigner]
74

75
        // BlockCache is the main cache for storing block information.
76
        BlockCache *blockcache.BlockCache
77

78
        // WalletUnlockParams are the parameters that were used for unlocking
79
        // the main wallet.
80
        WalletUnlockParams *walletunlocker.WalletUnlockParams
81

82
        // NeutrinoCS is a pointer to a neutrino ChainService. Must be non-nil
83
        // if using neutrino.
84
        NeutrinoCS *neutrino.ChainService
85

86
        // ActiveNetParams details the current chain we are on.
87
        ActiveNetParams BitcoinNetParams
88

89
        // Deprecated: Use Fee.URL. FeeURL defines the URL for fee estimation
90
        // we will use. This field is optional.
91
        FeeURL string
92

93
        // Fee defines settings for the web fee estimator. This field is
94
        // optional.
95
        Fee *lncfg.Fee
96

97
        // Dialer is a function closure that will be used to establish outbound
98
        // TCP connections to Bitcoin peers in the event of a pruned block being
99
        // requested.
100
        Dialer chain.Dialer
101
}
102

103
const (
104
        // DefaultBitcoinMinHTLCInMSat is the default smallest value htlc this
105
        // node will accept. This value is proposed in the channel open sequence
106
        // and cannot be changed during the life of the channel. It is 1 msat by
107
        // default to allow maximum flexibility in deciding what size payments
108
        // to forward.
109
        //
110
        // All forwarded payments are subjected to the min htlc constraint of
111
        // the routing policy of the outgoing channel. This implicitly controls
112
        // the minimum htlc value on the incoming channel too.
113
        DefaultBitcoinMinHTLCInMSat = lnwire.MilliSatoshi(1)
114

115
        // DefaultBitcoinMinHTLCOutMSat is the default minimum htlc value that
116
        // we require for sending out htlcs. Our channel peer may have a lower
117
        // min htlc channel parameter, but we - by default - don't forward
118
        // anything under the value defined here.
119
        DefaultBitcoinMinHTLCOutMSat = lnwire.MilliSatoshi(1000)
120

121
        // DefaultBitcoinBaseFeeMSat is the default forwarding base fee.
122
        DefaultBitcoinBaseFeeMSat = lnwire.MilliSatoshi(1000)
123

124
        // DefaultBitcoinFeeRate is the default forwarding fee rate.
125
        DefaultBitcoinFeeRate = lnwire.MilliSatoshi(1)
126

127
        // DefaultBitcoinTimeLockDelta is the default forwarding time lock
128
        // delta.
129
        DefaultBitcoinTimeLockDelta = 80
130

131
        // DefaultBitcoinStaticFeePerKW is the fee rate of 50 sat/vbyte
132
        // expressed in sat/kw.
133
        DefaultBitcoinStaticFeePerKW = chainfee.SatPerKWeight(12500)
134

135
        // DefaultBitcoinStaticMinRelayFeeRate is the min relay fee used for
136
        // static estimators.
137
        DefaultBitcoinStaticMinRelayFeeRate = chainfee.FeePerKwFloor
138

139
        // DefaultMinOutboundPeers is the min number of connected
140
        // outbound peers the chain backend should have to maintain a
141
        // healthy connection to the network.
142
        DefaultMinOutboundPeers = 6
143
)
144

145
// PartialChainControl contains all the primary interfaces of the chain control
146
// that can be purely constructed from the global configuration. No wallet
147
// instance is required for constructing this partial state.
148
type PartialChainControl struct {
149
        // Cfg is the configuration that was used to create the partial chain
150
        // control.
151
        Cfg *Config
152

153
        // HealthCheck is a function which can be used to send a low-cost, fast
154
        // query to the chain backend to ensure we still have access to our
155
        // node.
156
        HealthCheck func() error
157

158
        // FeeEstimator is used to estimate an optimal fee for transactions
159
        // important to us.
160
        FeeEstimator chainfee.Estimator
161

162
        // ChainNotifier is used to receive blockchain events that we are
163
        // interested in.
164
        ChainNotifier chainntnfs.ChainNotifier
165

166
        // BestBlockTracker is used to maintain a view of the global
167
        // chain state that changes over time
168
        BestBlockTracker *chainntnfs.BestBlockTracker
169

170
        // MempoolNotifier is used to watch for spending events happened in
171
        // mempool.
172
        MempoolNotifier chainntnfs.MempoolWatcher
173

174
        // ChainView is used in the router for maintaining an up-to-date graph.
175
        ChainView chainview.FilteredChainView
176

177
        // ChainSource is the primary chain interface. This is used to operate
178
        // the wallet and do things such as rescanning, sending transactions,
179
        // notifications for received funds, etc.
180
        ChainSource chain.Interface
181

182
        // RoutingPolicy is the routing policy we have decided to use.
183
        RoutingPolicy models.ForwardingPolicy
184

185
        // MinHtlcIn is the minimum HTLC we will accept.
186
        MinHtlcIn lnwire.MilliSatoshi
187
}
188

189
// ChainControl couples the three primary interfaces lnd utilizes for a
190
// particular chain together. A single ChainControl instance will exist for all
191
// the chains lnd is currently active on.
192
type ChainControl struct {
193
        // PartialChainControl is the part of the chain control that was
194
        // initialized purely from the configuration and doesn't contain any
195
        // wallet related elements.
196
        *PartialChainControl
197

198
        // ChainIO represents an abstraction over a source that can query the
199
        // blockchain.
200
        ChainIO lnwallet.BlockChainIO
201

202
        // Signer is used to provide signatures over things like transactions.
203
        Signer input.Signer
204

205
        // KeyRing represents a set of keys that we have the private keys to.
206
        KeyRing keychain.SecretKeyRing
207

208
        // Wc is an abstraction over some basic wallet commands. This base set
209
        // of commands will be provided to the Wallet *LightningWallet raw
210
        // pointer below.
211
        Wc lnwallet.WalletController
212

213
        // MsgSigner is used to sign arbitrary messages.
214
        MsgSigner lnwallet.MessageSigner
215

216
        // Wallet is our LightningWallet that also contains the abstract Wc
217
        // above. This wallet handles all of the lightning operations.
218
        Wallet *lnwallet.LightningWallet
219
}
220

221
// NewPartialChainControl creates a new partial chain control that contains all
222
// the parts that can be purely constructed from the passed in global
223
// configuration and doesn't need any wallet instance yet.
224
//
225
//nolint:lll
UNCOV
226
func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) {
×
UNCOV
227
        cc := &PartialChainControl{
×
UNCOV
228
                Cfg: cfg,
×
UNCOV
229
                RoutingPolicy: models.ForwardingPolicy{
×
UNCOV
230
                        MinHTLCOut:    cfg.Bitcoin.MinHTLCOut,
×
UNCOV
231
                        BaseFee:       cfg.Bitcoin.BaseFee,
×
UNCOV
232
                        FeeRate:       cfg.Bitcoin.FeeRate,
×
UNCOV
233
                        TimeLockDelta: cfg.Bitcoin.TimeLockDelta,
×
UNCOV
234
                },
×
UNCOV
235
                MinHtlcIn: cfg.Bitcoin.MinHTLCIn,
×
UNCOV
236
                FeeEstimator: chainfee.NewStaticEstimator(
×
UNCOV
237
                        DefaultBitcoinStaticFeePerKW,
×
UNCOV
238
                        DefaultBitcoinStaticMinRelayFeeRate,
×
UNCOV
239
                ),
×
UNCOV
240
        }
×
UNCOV
241

×
UNCOV
242
        var err error
×
UNCOV
243
        heightHintCacheConfig := channeldb.CacheConfig{
×
UNCOV
244
                QueryDisable: cfg.HeightHintCacheQueryDisable,
×
UNCOV
245
        }
×
UNCOV
246
        if cfg.HeightHintCacheQueryDisable {
×
247
                log.Infof("Height Hint Cache Queries disabled")
×
248
        }
×
249

250
        // Initialize the height hint cache within the chain directory.
UNCOV
251
        hintCache, err := channeldb.NewHeightHintCache(
×
UNCOV
252
                heightHintCacheConfig, cfg.HeightHintDB,
×
UNCOV
253
        )
×
UNCOV
254
        if err != nil {
×
255
                return nil, nil, fmt.Errorf("unable to initialize height hint "+
×
256
                        "cache: %v", err)
×
257
        }
×
258

259
        // Map the deprecated feeurl flag to fee.url.
UNCOV
260
        if cfg.FeeURL != "" {
×
261
                if cfg.Fee.URL != "" {
×
262
                        return nil, nil, errors.New("fee.url and " +
×
263
                                "feeurl are mutually exclusive")
×
264
                }
×
265

266
                cfg.Fee.URL = cfg.FeeURL
×
267
        }
268

269
        // If spv mode is active, then we'll be using a distinct set of
270
        // chainControl interfaces that interface directly with the p2p network
271
        // of the selected chain.
UNCOV
272
        switch cfg.Bitcoin.Node {
×
UNCOV
273
        case "neutrino":
×
UNCOV
274
                // We'll create ChainNotifier and FilteredChainView instances,
×
UNCOV
275
                // along with the wallet's ChainSource, which are all backed by
×
UNCOV
276
                // the neutrino light client.
×
UNCOV
277
                cc.ChainNotifier = neutrinonotify.New(
×
UNCOV
278
                        cfg.NeutrinoCS, hintCache, hintCache, cfg.BlockCache,
×
UNCOV
279
                )
×
UNCOV
280
                cc.ChainView, err = chainview.NewCfFilteredChainView(
×
UNCOV
281
                        cfg.NeutrinoCS, cfg.BlockCache,
×
UNCOV
282
                )
×
UNCOV
283
                if err != nil {
×
284
                        return nil, nil, err
×
285
                }
×
286

UNCOV
287
                cc.ChainSource = chain.NewNeutrinoClient(
×
UNCOV
288
                        cfg.ActiveNetParams.Params, cfg.NeutrinoCS,
×
UNCOV
289
                )
×
UNCOV
290

×
UNCOV
291
                // Get our best block as a health check.
×
UNCOV
292
                cc.HealthCheck = func() error {
×
UNCOV
293
                        _, _, err := cc.ChainSource.GetBestBlock()
×
UNCOV
294
                        return err
×
UNCOV
295
                }
×
296

UNCOV
297
        case "bitcoind":
×
UNCOV
298
                bitcoindMode := cfg.BitcoindMode
×
UNCOV
299

×
UNCOV
300
                // Otherwise, we'll be speaking directly via RPC and ZMQ to a
×
UNCOV
301
                // bitcoind node. If the specified host for the btcd RPC
×
UNCOV
302
                // server already has a port specified, then we use that
×
UNCOV
303
                // directly. Otherwise, we assume the default port according to
×
UNCOV
304
                // the selected chain parameters.
×
UNCOV
305
                var bitcoindHost string
×
UNCOV
306
                if strings.Contains(bitcoindMode.RPCHost, ":") {
×
UNCOV
307
                        bitcoindHost = bitcoindMode.RPCHost
×
UNCOV
308
                } else {
×
309
                        // The RPC ports specified in chainparams.go assume
×
310
                        // btcd, which picks a different port so that btcwallet
×
311
                        // can use the same RPC port as bitcoind. We convert
×
312
                        // this back to the btcwallet/bitcoind port.
×
313
                        rpcPort, err := strconv.Atoi(cfg.ActiveNetParams.RPCPort)
×
314
                        if err != nil {
×
315
                                return nil, nil, err
×
316
                        }
×
317
                        rpcPort -= 2
×
318
                        bitcoindHost = fmt.Sprintf("%v:%d",
×
319
                                bitcoindMode.RPCHost, rpcPort)
×
320
                        if cfg.Bitcoin.RegTest || cfg.Bitcoin.SigNet {
×
321
                                conn, err := net.Dial("tcp", bitcoindHost)
×
322
                                if err != nil || conn == nil {
×
323
                                        switch {
×
324
                                        case cfg.Bitcoin.RegTest:
×
325
                                                rpcPort = 18443
×
326
                                        case cfg.Bitcoin.SigNet:
×
327
                                                rpcPort = 38332
×
328
                                        }
329
                                        bitcoindHost = fmt.Sprintf("%v:%d",
×
330
                                                bitcoindMode.RPCHost,
×
331
                                                rpcPort)
×
332
                                } else {
×
333
                                        conn.Close()
×
334
                                }
×
335
                        }
336
                }
337

UNCOV
338
                bitcoindCfg := &chain.BitcoindConfig{
×
UNCOV
339
                        ChainParams:        cfg.ActiveNetParams.Params,
×
UNCOV
340
                        Host:               bitcoindHost,
×
UNCOV
341
                        User:               bitcoindMode.RPCUser,
×
UNCOV
342
                        Pass:               bitcoindMode.RPCPass,
×
UNCOV
343
                        Dialer:             cfg.Dialer,
×
UNCOV
344
                        PrunedModeMaxPeers: bitcoindMode.PrunedNodeMaxPeers,
×
UNCOV
345
                }
×
UNCOV
346

×
UNCOV
347
                if bitcoindMode.RPCPolling {
×
UNCOV
348
                        bitcoindCfg.PollingConfig = &chain.PollingConfig{
×
UNCOV
349
                                BlockPollingInterval:    bitcoindMode.BlockPollingInterval,
×
UNCOV
350
                                TxPollingInterval:       bitcoindMode.TxPollingInterval,
×
UNCOV
351
                                TxPollingIntervalJitter: lncfg.DefaultTxPollingJitter,
×
UNCOV
352
                        }
×
UNCOV
353
                } else {
×
UNCOV
354
                        bitcoindCfg.ZMQConfig = &chain.ZMQConfig{
×
UNCOV
355
                                ZMQBlockHost:           bitcoindMode.ZMQPubRawBlock,
×
UNCOV
356
                                ZMQTxHost:              bitcoindMode.ZMQPubRawTx,
×
UNCOV
357
                                ZMQReadDeadline:        bitcoindMode.ZMQReadDeadline,
×
UNCOV
358
                                MempoolPollingInterval: bitcoindMode.TxPollingInterval,
×
UNCOV
359
                                PollingIntervalJitter:  lncfg.DefaultTxPollingJitter,
×
UNCOV
360
                        }
×
UNCOV
361
                }
×
362

363
                // Establish the connection to bitcoind and create the clients
364
                // required for our relevant subsystems.
UNCOV
365
                bitcoindConn, err := chain.NewBitcoindConn(bitcoindCfg)
×
UNCOV
366
                if err != nil {
×
367
                        return nil, nil, err
×
368
                }
×
369

UNCOV
370
                if err := bitcoindConn.Start(); err != nil {
×
371
                        return nil, nil, fmt.Errorf("unable to connect to "+
×
372
                                "bitcoind: %v", err)
×
373
                }
×
374

UNCOV
375
                chainNotifier := bitcoindnotify.New(
×
UNCOV
376
                        bitcoindConn, cfg.ActiveNetParams.Params, hintCache,
×
UNCOV
377
                        hintCache, cfg.BlockCache,
×
UNCOV
378
                )
×
UNCOV
379

×
UNCOV
380
                cc.ChainNotifier = chainNotifier
×
UNCOV
381
                cc.MempoolNotifier = chainNotifier
×
UNCOV
382

×
UNCOV
383
                cc.ChainView = chainview.NewBitcoindFilteredChainView(
×
UNCOV
384
                        bitcoindConn, cfg.BlockCache,
×
UNCOV
385
                )
×
UNCOV
386
                cc.ChainSource = bitcoindConn.NewBitcoindClient()
×
UNCOV
387

×
UNCOV
388
                // If we're not in regtest mode, then we'll attempt to use a
×
UNCOV
389
                // proper fee estimator for testnet.
×
UNCOV
390
                rpcConfig := &rpcclient.ConnConfig{
×
UNCOV
391
                        Host:                 bitcoindHost,
×
UNCOV
392
                        User:                 bitcoindMode.RPCUser,
×
UNCOV
393
                        Pass:                 bitcoindMode.RPCPass,
×
UNCOV
394
                        DisableConnectOnNew:  true,
×
UNCOV
395
                        DisableAutoReconnect: false,
×
UNCOV
396
                        DisableTLS:           true,
×
UNCOV
397
                        HTTPPostMode:         true,
×
UNCOV
398
                }
×
UNCOV
399
                if !cfg.Bitcoin.RegTest {
×
400
                        log.Infof("Initializing bitcoind backed fee estimator "+
×
401
                                "in %s mode", bitcoindMode.EstimateMode)
×
402

×
403
                        // Finally, we'll re-initialize the fee estimator, as
×
404
                        // if we're using bitcoind as a backend, then we can
×
405
                        // use live fee estimates, rather than a statically
×
406
                        // coded value.
×
407
                        fallBackFeeRate := chainfee.SatPerKVByte(25 * 1000)
×
408
                        cc.FeeEstimator, err = chainfee.NewBitcoindEstimator(
×
409
                                *rpcConfig, bitcoindMode.EstimateMode,
×
410
                                fallBackFeeRate.FeePerKWeight(),
×
411
                        )
×
412
                        if err != nil {
×
413
                                return nil, nil, err
×
414
                        }
×
415
                }
416

417
                // We need to use some apis that are not exposed by btcwallet,
418
                // for a health check function so we create an ad-hoc bitcoind
419
                // connection.
UNCOV
420
                chainConn, err := rpcclient.New(rpcConfig, nil)
×
UNCOV
421
                if err != nil {
×
422
                        return nil, nil, err
×
423
                }
×
424

425
                // Before we continue any further, we'll ensure that the
426
                // backend understands Taproot. If not, then all the default
427
                // features can't be used.
UNCOV
428
                if !backendSupportsTaproot(chainConn) {
×
429
                        return nil, nil, fmt.Errorf("node backend does not " +
×
430
                                "support taproot")
×
431
                }
×
432

433
                // The api we will use for our health check depends on the
434
                // bitcoind version.
UNCOV
435
                cmd, ver, err := getBitcoindHealthCheckCmd(chainConn)
×
UNCOV
436
                if err != nil {
×
437
                        return nil, nil, err
×
438
                }
×
439

440
                // If the getzmqnotifications api is available (was added in
441
                // version 0.17.0) we make sure lnd subscribes to the correct
442
                // zmq events. We do this to avoid a situation in which we are
443
                // not notified of new transactions or blocks.
UNCOV
444
                if ver >= 170000 && !bitcoindMode.RPCPolling {
×
UNCOV
445
                        zmqPubRawBlockURL, err := url.Parse(bitcoindMode.ZMQPubRawBlock)
×
UNCOV
446
                        if err != nil {
×
447
                                return nil, nil, err
×
448
                        }
×
UNCOV
449
                        zmqPubRawTxURL, err := url.Parse(bitcoindMode.ZMQPubRawTx)
×
UNCOV
450
                        if err != nil {
×
451
                                return nil, nil, err
×
452
                        }
×
453

454
                        // Fetch all active zmq notifications from the bitcoind client.
UNCOV
455
                        resp, err := chainConn.RawRequest("getzmqnotifications", nil)
×
UNCOV
456
                        if err != nil {
×
457
                                return nil, nil, err
×
458
                        }
×
459

UNCOV
460
                        zmq := []struct {
×
UNCOV
461
                                Type    string `json:"type"`
×
UNCOV
462
                                Address string `json:"address"`
×
UNCOV
463
                        }{}
×
UNCOV
464

×
UNCOV
465
                        if err = json.Unmarshal([]byte(resp), &zmq); err != nil {
×
466
                                return nil, nil, err
×
467
                        }
×
468

UNCOV
469
                        pubRawBlockActive := false
×
UNCOV
470
                        pubRawTxActive := false
×
UNCOV
471

×
UNCOV
472
                        for i := range zmq {
×
UNCOV
473
                                if zmq[i].Type == "pubrawblock" {
×
UNCOV
474
                                        url, err := url.Parse(zmq[i].Address)
×
UNCOV
475
                                        if err != nil {
×
476
                                                return nil, nil, err
×
477
                                        }
×
UNCOV
478
                                        if url.Port() != zmqPubRawBlockURL.Port() {
×
479
                                                log.Warnf(
×
480
                                                        "unable to subscribe to zmq block events on "+
×
481
                                                                "%s (bitcoind is running on %s)",
×
482
                                                        zmqPubRawBlockURL.Host,
×
483
                                                        url.Host,
×
484
                                                )
×
485
                                        }
×
UNCOV
486
                                        pubRawBlockActive = true
×
487
                                }
UNCOV
488
                                if zmq[i].Type == "pubrawtx" {
×
UNCOV
489
                                        url, err := url.Parse(zmq[i].Address)
×
UNCOV
490
                                        if err != nil {
×
491
                                                return nil, nil, err
×
492
                                        }
×
UNCOV
493
                                        if url.Port() != zmqPubRawTxURL.Port() {
×
494
                                                log.Warnf(
×
495
                                                        "unable to subscribe to zmq tx events on "+
×
496
                                                                "%s (bitcoind is running on %s)",
×
497
                                                        zmqPubRawTxURL.Host,
×
498
                                                        url.Host,
×
499
                                                )
×
500
                                        }
×
UNCOV
501
                                        pubRawTxActive = true
×
502
                                }
503
                        }
504

505
                        // Return an error if raw tx or raw block notification over
506
                        // zmq is inactive.
UNCOV
507
                        if !pubRawBlockActive {
×
508
                                return nil, nil, errors.New(
×
509
                                        "block notification over zmq is inactive on " +
×
510
                                                "bitcoind",
×
511
                                )
×
512
                        }
×
UNCOV
513
                        if !pubRawTxActive {
×
514
                                return nil, nil, errors.New(
×
515
                                        "tx notification over zmq is inactive on " +
×
516
                                                "bitcoind",
×
517
                                )
×
518
                        }
×
519
                }
520

UNCOV
521
                cc.HealthCheck = func() error {
×
UNCOV
522
                        _, err := chainConn.RawRequest(cmd, nil)
×
UNCOV
523
                        if err != nil {
×
UNCOV
524
                                return err
×
UNCOV
525
                        }
×
526

527
                        // On local test networks we usually don't have multiple
528
                        // chain backend peers, so we can skip
529
                        // the checkOutboundPeers test.
UNCOV
530
                        if cfg.Bitcoin.SimNet || cfg.Bitcoin.RegTest {
×
UNCOV
531
                                return nil
×
UNCOV
532
                        }
×
533

534
                        // Make sure the bitcoind chain backend maintains a
535
                        // healthy connection to the network by checking the
536
                        // number of outbound peers.
537
                        return checkOutboundPeers(chainConn)
×
538
                }
539

UNCOV
540
        case "btcd":
×
UNCOV
541
                // Otherwise, we'll be speaking directly via RPC to a node.
×
UNCOV
542
                //
×
UNCOV
543
                // So first we'll load btcd's TLS cert for the RPC
×
UNCOV
544
                // connection. If a raw cert was specified in the config, then
×
UNCOV
545
                // we'll set that directly. Otherwise, we attempt to read the
×
UNCOV
546
                // cert from the path specified in the config.
×
UNCOV
547
                var (
×
UNCOV
548
                        rpcCert  []byte
×
UNCOV
549
                        btcdMode = cfg.BtcdMode
×
UNCOV
550
                )
×
UNCOV
551
                if btcdMode.RawRPCCert != "" {
×
UNCOV
552
                        rpcCert, err = hex.DecodeString(btcdMode.RawRPCCert)
×
UNCOV
553
                        if err != nil {
×
554
                                return nil, nil, err
×
555
                        }
×
556
                } else {
×
557
                        certFile, err := os.Open(btcdMode.RPCCert)
×
558
                        if err != nil {
×
559
                                return nil, nil, err
×
560
                        }
×
561
                        rpcCert, err = io.ReadAll(certFile)
×
562
                        if err != nil {
×
563
                                return nil, nil, err
×
564
                        }
×
565
                        if err := certFile.Close(); err != nil {
×
566
                                return nil, nil, err
×
567
                        }
×
568
                }
569

570
                // If the specified host for the btcd RPC server already
571
                // has a port specified, then we use that directly. Otherwise,
572
                // we assume the default port according to the selected chain
573
                // parameters.
UNCOV
574
                var btcdHost string
×
UNCOV
575
                if strings.Contains(btcdMode.RPCHost, ":") {
×
UNCOV
576
                        btcdHost = btcdMode.RPCHost
×
UNCOV
577
                } else {
×
578
                        btcdHost = fmt.Sprintf("%v:%v", btcdMode.RPCHost,
×
579
                                cfg.ActiveNetParams.RPCPort)
×
580
                }
×
581

UNCOV
582
                btcdUser := btcdMode.RPCUser
×
UNCOV
583
                btcdPass := btcdMode.RPCPass
×
UNCOV
584
                rpcConfig := &rpcclient.ConnConfig{
×
UNCOV
585
                        Host:                 btcdHost,
×
UNCOV
586
                        Endpoint:             "ws",
×
UNCOV
587
                        User:                 btcdUser,
×
UNCOV
588
                        Pass:                 btcdPass,
×
UNCOV
589
                        Certificates:         rpcCert,
×
UNCOV
590
                        DisableTLS:           false,
×
UNCOV
591
                        DisableConnectOnNew:  true,
×
UNCOV
592
                        DisableAutoReconnect: false,
×
UNCOV
593
                }
×
UNCOV
594

×
UNCOV
595
                chainNotifier, err := btcdnotify.New(
×
UNCOV
596
                        rpcConfig, cfg.ActiveNetParams.Params, hintCache,
×
UNCOV
597
                        hintCache, cfg.BlockCache,
×
UNCOV
598
                )
×
UNCOV
599
                if err != nil {
×
600
                        return nil, nil, err
×
601
                }
×
602

UNCOV
603
                cc.ChainNotifier = chainNotifier
×
UNCOV
604
                cc.MempoolNotifier = chainNotifier
×
UNCOV
605

×
UNCOV
606
                // Finally, we'll create an instance of the default chain view
×
UNCOV
607
                // to be used within the routing layer.
×
UNCOV
608
                cc.ChainView, err = chainview.NewBtcdFilteredChainView(
×
UNCOV
609
                        *rpcConfig, cfg.BlockCache,
×
UNCOV
610
                )
×
UNCOV
611
                if err != nil {
×
612
                        log.Errorf("unable to create chain view: %v", err)
×
613
                        return nil, nil, err
×
614
                }
×
615

616
                // Create a special websockets rpc client for btcd which will be
617
                // used by the wallet for notifications, calls, etc.
UNCOV
618
                chainRPC, err := chain.NewRPCClient(
×
UNCOV
619
                        cfg.ActiveNetParams.Params, btcdHost, btcdUser,
×
UNCOV
620
                        btcdPass, rpcCert, false, 20,
×
UNCOV
621
                )
×
UNCOV
622
                if err != nil {
×
623
                        return nil, nil, err
×
624
                }
×
625

626
                // Before we continue any further, we'll ensure that the
627
                // backend understands Taproot. If not, then all the default
628
                // features can't be used.
UNCOV
629
                restConfCopy := *rpcConfig
×
UNCOV
630
                restConfCopy.Endpoint = ""
×
UNCOV
631
                restConfCopy.HTTPPostMode = true
×
UNCOV
632
                chainConn, err := rpcclient.New(&restConfCopy, nil)
×
UNCOV
633
                if err != nil {
×
634
                        return nil, nil, err
×
635
                }
×
UNCOV
636
                if !backendSupportsTaproot(chainConn) {
×
637
                        return nil, nil, fmt.Errorf("node backend does not " +
×
638
                                "support taproot")
×
639
                }
×
640

UNCOV
641
                cc.ChainSource = chainRPC
×
UNCOV
642

×
UNCOV
643
                // Use a query for our best block as a health check.
×
UNCOV
644
                cc.HealthCheck = func() error {
×
UNCOV
645
                        _, _, err := cc.ChainSource.GetBestBlock()
×
UNCOV
646
                        if err != nil {
×
UNCOV
647
                                return err
×
UNCOV
648
                        }
×
649

650
                        // On local test networks we usually don't have multiple
651
                        // chain backend peers, so we can skip
652
                        // the checkOutboundPeers test.
UNCOV
653
                        if cfg.Bitcoin.SimNet || cfg.Bitcoin.RegTest {
×
UNCOV
654
                                return nil
×
UNCOV
655
                        }
×
656

657
                        // Make sure the btcd chain backend maintains a
658
                        // healthy connection to the network by checking the
659
                        // number of outbound peers.
660
                        return checkOutboundPeers(chainRPC.Client)
×
661
                }
662

663
                // If we're not in simnet or regtest mode, then we'll attempt
664
                // to use a proper fee estimator for testnet.
UNCOV
665
                if !cfg.Bitcoin.SimNet && !cfg.Bitcoin.RegTest {
×
666
                        log.Info("Initializing btcd backed fee estimator")
×
667

×
668
                        // Finally, we'll re-initialize the fee estimator, as
×
669
                        // if we're using btcd as a backend, then we can use
×
670
                        // live fee estimates, rather than a statically coded
×
671
                        // value.
×
672
                        fallBackFeeRate := chainfee.SatPerKVByte(25 * 1000)
×
673
                        cc.FeeEstimator, err = chainfee.NewBtcdEstimator(
×
674
                                *rpcConfig, fallBackFeeRate.FeePerKWeight(),
×
675
                        )
×
676
                        if err != nil {
×
677
                                return nil, nil, err
×
678
                        }
×
679
                }
680

681
        case "nochainbackend":
×
682
                backend := &NoChainBackend{}
×
683
                source := &NoChainSource{
×
684
                        BestBlockTime: time.Now(),
×
685
                }
×
686

×
687
                cc.ChainNotifier = backend
×
688
                cc.ChainView = backend
×
689
                cc.FeeEstimator = backend
×
690

×
691
                cc.ChainSource = source
×
692
                cc.HealthCheck = func() error {
×
693
                        return nil
×
694
                }
×
695

696
        default:
×
697
                return nil, nil, fmt.Errorf("unknown node type: %s",
×
698
                        cfg.Bitcoin.Node)
×
699
        }
700

UNCOV
701
        cc.BestBlockTracker =
×
UNCOV
702
                chainntnfs.NewBestBlockTracker(cc.ChainNotifier)
×
UNCOV
703

×
UNCOV
704
        switch {
×
705
        // If the fee URL isn't set, and the user is running mainnet, then
706
        // we'll return an error to instruct them to set a proper fee
707
        // estimator.
708
        case cfg.Fee.URL == "" && cfg.Bitcoin.MainNet &&
709
                cfg.Bitcoin.Node == "neutrino":
×
710

×
711
                return nil, nil, fmt.Errorf("--fee.url parameter required " +
×
712
                        "when running neutrino on mainnet")
×
713

714
        // Override default fee estimator if an external service is specified.
UNCOV
715
        case cfg.Fee.URL != "":
×
UNCOV
716
                // Do not cache fees on regtest to make it easier to execute
×
UNCOV
717
                // manual or automated test cases.
×
UNCOV
718
                cacheFees := !cfg.Bitcoin.RegTest
×
UNCOV
719

×
UNCOV
720
                log.Infof("Using external fee estimator %v: cached=%v: "+
×
UNCOV
721
                        "min update timeout=%v, max update timeout=%v",
×
UNCOV
722
                        cfg.Fee.URL, cacheFees, cfg.Fee.MinUpdateTimeout,
×
UNCOV
723
                        cfg.Fee.MaxUpdateTimeout)
×
UNCOV
724

×
UNCOV
725
                cc.FeeEstimator, err = chainfee.NewWebAPIEstimator(
×
UNCOV
726
                        chainfee.SparseConfFeeSource{
×
UNCOV
727
                                URL: cfg.Fee.URL,
×
UNCOV
728
                        },
×
UNCOV
729
                        !cacheFees,
×
UNCOV
730
                        cfg.Fee.MinUpdateTimeout,
×
UNCOV
731
                        cfg.Fee.MaxUpdateTimeout,
×
UNCOV
732
                )
×
UNCOV
733
                if err != nil {
×
734
                        return nil, nil, err
×
735
                }
×
736
        }
737

UNCOV
738
        ccCleanup := func() {
×
UNCOV
739
                if cc.FeeEstimator != nil {
×
UNCOV
740
                        if err := cc.FeeEstimator.Stop(); err != nil {
×
741
                                log.Errorf("Failed to stop feeEstimator: %v",
×
742
                                        err)
×
743
                        }
×
744
                }
745
        }
746

747
        // Start fee estimator.
UNCOV
748
        if err := cc.FeeEstimator.Start(); err != nil {
×
749
                return nil, nil, err
×
750
        }
×
751

UNCOV
752
        return cc, ccCleanup, nil
×
753
}
754

755
// NewChainControl attempts to create a ChainControl instance according
756
// to the parameters in the passed configuration. Currently three
757
// branches of ChainControl instances exist: one backed by a running btcd
758
// full-node, another backed by a running bitcoind full-node, and the other
759
// backed by a running neutrino light client instance. When running with a
760
// neutrino light client instance, `neutrinoCS` must be non-nil.
761
func NewChainControl(walletConfig lnwallet.Config,
762
        msgSigner lnwallet.MessageSigner,
UNCOV
763
        pcc *PartialChainControl) (*ChainControl, func(), error) {
×
UNCOV
764

×
UNCOV
765
        cc := &ChainControl{
×
UNCOV
766
                PartialChainControl: pcc,
×
UNCOV
767
                MsgSigner:           msgSigner,
×
UNCOV
768
                Signer:              walletConfig.Signer,
×
UNCOV
769
                ChainIO:             walletConfig.ChainIO,
×
UNCOV
770
                Wc:                  walletConfig.WalletController,
×
UNCOV
771
                KeyRing:             walletConfig.SecretKeyRing,
×
UNCOV
772
        }
×
UNCOV
773

×
UNCOV
774
        ccCleanup := func() {
×
UNCOV
775
                if cc.Wallet != nil {
×
UNCOV
776
                        if err := cc.Wallet.Shutdown(); err != nil {
×
777
                                log.Errorf("Failed to shutdown wallet: %v", err)
×
778
                        }
×
779
                }
780
        }
781

UNCOV
782
        lnWallet, err := lnwallet.NewLightningWallet(walletConfig)
×
UNCOV
783
        if err != nil {
×
784
                return nil, ccCleanup, fmt.Errorf("unable to create wallet: %w",
×
785
                        err)
×
786
        }
×
UNCOV
787
        if err := lnWallet.Startup(); err != nil {
×
788
                return nil, ccCleanup, fmt.Errorf("unable to create wallet: %w",
×
789
                        err)
×
790
        }
×
791

UNCOV
792
        log.Info("LightningWallet opened")
×
UNCOV
793
        cc.Wallet = lnWallet
×
UNCOV
794

×
UNCOV
795
        return cc, ccCleanup, nil
×
796
}
797

798
// getBitcoindHealthCheckCmd queries bitcoind for its version to decide which
799
// api we should use for our health check. We prefer to use the uptime
800
// command, because it has no locking and is an inexpensive call, which was
801
// added in version 0.15. If we are on an earlier version, we fallback to using
802
// getblockchaininfo.
UNCOV
803
func getBitcoindHealthCheckCmd(client *rpcclient.Client) (string, int64, error) {
×
UNCOV
804
        // Query bitcoind to get our current version.
×
UNCOV
805
        resp, err := client.RawRequest("getnetworkinfo", nil)
×
UNCOV
806
        if err != nil {
×
807
                return "", 0, err
×
808
        }
×
809

810
        // Parse the response to retrieve bitcoind's version.
UNCOV
811
        info := struct {
×
UNCOV
812
                Version int64 `json:"version"`
×
UNCOV
813
        }{}
×
UNCOV
814
        if err := json.Unmarshal(resp, &info); err != nil {
×
815
                return "", 0, err
×
816
        }
×
817

818
        // Bitcoind returns a single value representing the semantic version:
819
        // 1000000 * CLIENT_VERSION_MAJOR + 10000 * CLIENT_VERSION_MINOR
820
        // + 100 * CLIENT_VERSION_REVISION + 1 * CLIENT_VERSION_BUILD
821
        //
822
        // The uptime call was added in version 0.15.0, so we return it for
823
        // any version value >= 150000, as per the above calculation.
UNCOV
824
        if info.Version >= 150000 {
×
UNCOV
825
                return "uptime", info.Version, nil
×
UNCOV
826
        }
×
827

828
        return "getblockchaininfo", info.Version, nil
×
829
}
830

831
var (
832
        // BitcoinTestnetGenesis is the genesis hash of Bitcoin's testnet
833
        // chain.
834
        BitcoinTestnetGenesis = chainhash.Hash([chainhash.HashSize]byte{
835
                0x43, 0x49, 0x7f, 0xd7, 0xf8, 0x26, 0x95, 0x71,
836
                0x08, 0xf4, 0xa3, 0x0f, 0xd9, 0xce, 0xc3, 0xae,
837
                0xba, 0x79, 0x97, 0x20, 0x84, 0xe9, 0x0e, 0xad,
838
                0x01, 0xea, 0x33, 0x09, 0x00, 0x00, 0x00, 0x00,
839
        })
840

841
        // BitcoinSignetGenesis is the genesis hash of Bitcoin's signet chain.
842
        BitcoinSignetGenesis = chainhash.Hash([chainhash.HashSize]byte{
843
                0xf6, 0x1e, 0xee, 0x3b, 0x63, 0xa3, 0x80, 0xa4,
844
                0x77, 0xa0, 0x63, 0xaf, 0x32, 0xb2, 0xbb, 0xc9,
845
                0x7c, 0x9f, 0xf9, 0xf0, 0x1f, 0x2c, 0x42, 0x25,
846
                0xe9, 0x73, 0x98, 0x81, 0x08, 0x00, 0x00, 0x00,
847
        })
848

849
        // BitcoinMainnetGenesis is the genesis hash of Bitcoin's main chain.
850
        BitcoinMainnetGenesis = chainhash.Hash([chainhash.HashSize]byte{
851
                0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
852
                0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
853
                0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
854
                0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
855
        })
856

857
        // ChainDNSSeeds is a map of a chain's hash to the set of DNS seeds
858
        // that will be use to bootstrap peers upon first startup.
859
        //
860
        // The first item in the array is the primary host we'll use to attempt
861
        // the SRV lookup we require. If we're unable to receive a response
862
        // over UDP, then we'll fall back to manual TCP resolution. The second
863
        // item in the array is a special A record that we'll query in order to
864
        // receive the IP address of the current authoritative DNS server for
865
        // the network seed.
866
        //
867
        // TODO(roasbeef): extend and collapse these and chainparams.go into
868
        // struct like chaincfg.Params.
869
        ChainDNSSeeds = map[chainhash.Hash][][2]string{
870
                BitcoinMainnetGenesis: {
871
                        {
872
                                "nodes.lightning.directory",
873
                                "soa.nodes.lightning.directory",
874
                        },
875
                        {
876
                                "lseed.bitcoinstats.com",
877
                        },
878
                },
879

880
                BitcoinTestnetGenesis: {
881
                        {
882
                                "test.nodes.lightning.directory",
883
                                "soa.nodes.lightning.directory",
884
                        },
885
                },
886

887
                BitcoinSignetGenesis: {
888
                        {
889
                                "ln.signet.secp.tech",
890
                        },
891
                },
892
        }
893
)
894

895
// checkOutboundPeers checks the number of outbound peers connected to the
896
// provided RPC client. If the number of outbound peers is below 6, a warning
897
// is logged. This function is intended to ensure that the chain backend
898
// maintains a healthy connection to the network.
899
func checkOutboundPeers(client *rpcclient.Client) error {
×
900
        peers, err := client.GetPeerInfo()
×
901
        if err != nil {
×
902
                return err
×
903
        }
×
904

905
        var outboundPeers int
×
906
        for _, peer := range peers {
×
907
                if !peer.Inbound {
×
908
                        outboundPeers++
×
909
                }
×
910
        }
911

912
        if outboundPeers < DefaultMinOutboundPeers {
×
913
                log.Warnf("The chain backend has an insufficient number "+
×
914
                        "of connected outbound peers (%d connected, expected "+
×
915
                        "minimum is %d) which can be a security issue. "+
×
916
                        "Connect to more trusted nodes manually if necessary.",
×
917
                        outboundPeers, DefaultMinOutboundPeers)
×
918
        }
×
919

920
        return nil
×
921
}
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