• 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

21.43
/config.go
1
// Copyright (c) 2013-2017 The btcsuite developers
2
// Copyright (c) 2015-2016 The Decred developers
3
// Copyright (C) 2015-2022 The Lightning Network Developers
4

5
package lnd
6

7
import (
8
        "encoding/hex"
9
        "errors"
10
        "fmt"
11
        "io"
12
        "net"
13
        "os"
14
        "os/user"
15
        "path/filepath"
16
        "reflect"
17
        "regexp"
18
        "strconv"
19
        "strings"
20
        "time"
21

22
        "github.com/btcsuite/btcd/btcutil"
23
        "github.com/btcsuite/btcd/chaincfg"
24
        flags "github.com/jessevdk/go-flags"
25
        "github.com/lightninglabs/neutrino"
26
        "github.com/lightningnetwork/lnd/autopilot"
27
        "github.com/lightningnetwork/lnd/build"
28
        "github.com/lightningnetwork/lnd/chainreg"
29
        "github.com/lightningnetwork/lnd/chanbackup"
30
        "github.com/lightningnetwork/lnd/channeldb"
31
        "github.com/lightningnetwork/lnd/discovery"
32
        "github.com/lightningnetwork/lnd/funding"
33
        "github.com/lightningnetwork/lnd/htlcswitch"
34
        "github.com/lightningnetwork/lnd/htlcswitch/hodl"
35
        "github.com/lightningnetwork/lnd/input"
36
        "github.com/lightningnetwork/lnd/lncfg"
37
        "github.com/lightningnetwork/lnd/lnrpc"
38
        "github.com/lightningnetwork/lnd/lnrpc/peersrpc"
39
        "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
40
        "github.com/lightningnetwork/lnd/lnrpc/signrpc"
41
        "github.com/lightningnetwork/lnd/lnutils"
42
        "github.com/lightningnetwork/lnd/lnwallet"
43
        "github.com/lightningnetwork/lnd/lnwire"
44
        "github.com/lightningnetwork/lnd/routing"
45
        "github.com/lightningnetwork/lnd/signal"
46
        "github.com/lightningnetwork/lnd/tor"
47
)
48

49
const (
50
        defaultDataDirname        = "data"
51
        defaultChainSubDirname    = "chain"
52
        defaultGraphSubDirname    = "graph"
53
        defaultTowerSubDirname    = "watchtower"
54
        defaultTLSCertFilename    = "tls.cert"
55
        defaultTLSKeyFilename     = "tls.key"
56
        defaultAdminMacFilename   = "admin.macaroon"
57
        defaultReadMacFilename    = "readonly.macaroon"
58
        defaultInvoiceMacFilename = "invoice.macaroon"
59
        defaultLogLevel           = "info"
60
        defaultLogDirname         = "logs"
61
        defaultLogFilename        = "lnd.log"
62
        defaultLogCompressor      = build.Gzip
63
        defaultRPCPort            = 10009
64
        defaultRESTPort           = 8080
65
        defaultPeerPort           = 9735
66
        defaultRPCHost            = "localhost"
67

68
        defaultNoSeedBackup                  = false
69
        defaultPaymentsExpirationGracePeriod = time.Duration(0)
70
        defaultTrickleDelay                  = 90 * 1000
71
        defaultChanStatusSampleInterval      = time.Minute
72
        defaultChanEnableTimeout             = 19 * time.Minute
73
        defaultChanDisableTimeout            = 20 * time.Minute
74
        defaultHeightHintCacheQueryDisable   = false
75
        defaultMaxLogFiles                   = 3
76
        defaultMaxLogFileSize                = 10
77
        defaultMinBackoff                    = time.Second
78
        defaultMaxBackoff                    = time.Hour
79
        defaultLetsEncryptDirname            = "letsencrypt"
80
        defaultLetsEncryptListen             = ":80"
81

82
        defaultTorSOCKSPort            = 9050
83
        defaultTorDNSHost              = "soa.nodes.lightning.directory"
84
        defaultTorDNSPort              = 53
85
        defaultTorControlPort          = 9051
86
        defaultTorV2PrivateKeyFilename = "v2_onion_private_key"
87
        defaultTorV3PrivateKeyFilename = "v3_onion_private_key"
88

89
        // defaultZMQReadDeadline is the default read deadline to be used for
90
        // both the block and tx ZMQ subscriptions.
91
        defaultZMQReadDeadline = 5 * time.Second
92

93
        // DefaultAutogenValidity is the default validity of a self-signed
94
        // certificate. The value corresponds to 14 months
95
        // (14 months * 30 days * 24 hours).
96
        defaultTLSCertDuration = 14 * 30 * 24 * time.Hour
97

98
        // minTimeLockDelta is the minimum timelock we require for incoming
99
        // HTLCs on our channels.
100
        minTimeLockDelta = routing.MinCLTVDelta
101

102
        // MaxTimeLockDelta is the maximum CLTV delta that can be applied to
103
        // forwarded HTLCs.
104
        MaxTimeLockDelta = routing.MaxCLTVDelta
105

106
        // defaultAcceptorTimeout is the time after which an RPCAcceptor will time
107
        // out and return false if it hasn't yet received a response.
108
        defaultAcceptorTimeout = 15 * time.Second
109

110
        defaultAlias = ""
111
        defaultColor = "#3399FF"
112

113
        // defaultCoopCloseTargetConfs is the default confirmation target
114
        // that will be used to estimate a fee rate to use during a
115
        // cooperative channel closure initiated by a remote peer. By default
116
        // we'll set this to a lax value since we weren't the ones that
117
        // initiated the channel closure.
118
        defaultCoopCloseTargetConfs = 6
119

120
        // defaultBlockCacheSize is the size (in bytes) of blocks that will be
121
        // keep in memory if no size is specified.
122
        defaultBlockCacheSize uint64 = 20 * 1024 * 1024 // 20 MB
123

124
        // defaultHostSampleInterval is the default amount of time that the
125
        // HostAnnouncer will wait between DNS resolutions to check if the
126
        // backing IP of a host has changed.
127
        defaultHostSampleInterval = time.Minute * 5
128

129
        defaultChainInterval = time.Minute
130
        defaultChainTimeout  = time.Second * 30
131
        defaultChainBackoff  = time.Minute * 2
132
        defaultChainAttempts = 3
133

134
        // Set defaults for a health check which ensures that we have space
135
        // available on disk. Although this check is off by default so that we
136
        // avoid breaking any existing setups (particularly on mobile), we still
137
        // set the other default values so that the health check can be easily
138
        // enabled with sane defaults.
139
        defaultRequiredDisk = 0.1
140
        defaultDiskInterval = time.Hour * 12
141
        defaultDiskTimeout  = time.Second * 5
142
        defaultDiskBackoff  = time.Minute
143
        defaultDiskAttempts = 0
144

145
        // Set defaults for a health check which ensures that the TLS certificate
146
        // is not expired. Although this check is off by default (not all setups
147
        // require it), we still set the other default values so that the health
148
        // check can be easily enabled with sane defaults.
149
        defaultTLSInterval = time.Minute
150
        defaultTLSTimeout  = time.Second * 5
151
        defaultTLSBackoff  = time.Minute
152
        defaultTLSAttempts = 0
153

154
        // Set defaults for a health check which ensures that the tor
155
        // connection is alive. Although this check is off by default (not all
156
        // setups require it), we still set the other default values so that
157
        // the health check can be easily enabled with sane defaults.
158
        defaultTCInterval = time.Minute
159
        defaultTCTimeout  = time.Second * 5
160
        defaultTCBackoff  = time.Minute
161
        defaultTCAttempts = 0
162

163
        // Set defaults for a health check which ensures that the remote signer
164
        // RPC connection is alive. Although this check is off by default (only
165
        // active when remote signing is turned on), we still set the other
166
        // default values so that the health check can be easily enabled with
167
        // sane defaults.
168
        defaultRSInterval = time.Minute
169
        defaultRSTimeout  = time.Second * 1
170
        defaultRSBackoff  = time.Second * 30
171
        defaultRSAttempts = 1
172

173
        // Set defaults for a health check which ensures that the leader
174
        // election is functioning correctly. Although this check is off by
175
        // default (as etcd leader election is only used in a clustered setup),
176
        // we still set the default values so that the health check can be
177
        // easily enabled with sane defaults. Note that by default we only run
178
        // this check once, as it is critical for the node's operation.
179
        defaultLeaderCheckInterval = time.Minute
180
        defaultLeaderCheckTimeout  = time.Second * 5
181
        defaultLeaderCheckBackoff  = time.Second * 5
182
        defaultLeaderCheckAttempts = 1
183

184
        // defaultRemoteMaxHtlcs specifies the default limit for maximum
185
        // concurrent HTLCs the remote party may add to commitment transactions.
186
        // This value can be overridden with --default-remote-max-htlcs.
187
        defaultRemoteMaxHtlcs = 483
188

189
        // defaultMaxLocalCSVDelay is the maximum delay we accept on our
190
        // commitment output. The local csv delay maximum is now equal to
191
        // the remote csv delay maximum we require for the remote commitment
192
        // transaction.
193
        defaultMaxLocalCSVDelay = 2016
194

195
        // defaultChannelCommitInterval is the default maximum time between
196
        // receiving a channel state update and signing a new commitment.
197
        defaultChannelCommitInterval = 50 * time.Millisecond
198

199
        // maxChannelCommitInterval is the maximum time the commit interval can
200
        // be configured to.
201
        maxChannelCommitInterval = time.Hour
202

203
        // defaultPendingCommitInterval specifies the default timeout value
204
        // while waiting for the remote party to revoke a locally initiated
205
        // commitment state.
206
        defaultPendingCommitInterval = 1 * time.Minute
207

208
        // maxPendingCommitInterval specifies the max allowed duration when
209
        // waiting for the remote party to revoke a locally initiated
210
        // commitment state.
211
        maxPendingCommitInterval = 5 * time.Minute
212

213
        // defaultChannelCommitBatchSize is the default maximum number of
214
        // channel state updates that is accumulated before signing a new
215
        // commitment.
216
        defaultChannelCommitBatchSize = 10
217

218
        // defaultCoinSelectionStrategy is the coin selection strategy that is
219
        // used by default to fund transactions.
220
        defaultCoinSelectionStrategy = "largest"
221

222
        // defaultKeepFailedPaymentAttempts is the default setting for whether
223
        // to keep failed payments in the database.
224
        defaultKeepFailedPaymentAttempts = false
225

226
        // defaultGrpcServerPingTime is the default duration for the amount of
227
        // time of no activity after which the server pings the client to see if
228
        // the transport is still alive. If set below 1s, a minimum value of 1s
229
        // will be used instead.
230
        defaultGrpcServerPingTime = time.Minute
231

232
        // defaultGrpcServerPingTimeout is the default duration the server waits
233
        // after having pinged for keepalive check, and if no activity is seen
234
        // even after that the connection is closed.
235
        defaultGrpcServerPingTimeout = 20 * time.Second
236

237
        // defaultGrpcClientPingMinWait is the default minimum amount of time a
238
        // client should wait before sending a keepalive ping.
239
        defaultGrpcClientPingMinWait = 5 * time.Second
240

241
        // defaultHTTPHeaderTimeout is the default timeout for HTTP requests.
242
        DefaultHTTPHeaderTimeout = 5 * time.Second
243

244
        // BitcoinChainName is a string that represents the Bitcoin blockchain.
245
        BitcoinChainName = "bitcoin"
246

247
        bitcoindBackendName = "bitcoind"
248
        btcdBackendName     = "btcd"
249
        neutrinoBackendName = "neutrino"
250
)
251

252
var (
253
        // DefaultLndDir is the default directory where lnd tries to find its
254
        // configuration file and store its data. This is a directory in the
255
        // user's application data, for example:
256
        //   C:\Users\<username>\AppData\Local\Lnd on Windows
257
        //   ~/.lnd on Linux
258
        //   ~/Library/Application Support/Lnd on MacOS
259
        DefaultLndDir = btcutil.AppDataDir("lnd", false)
260

261
        // DefaultConfigFile is the default full path of lnd's configuration
262
        // file.
263
        DefaultConfigFile = filepath.Join(DefaultLndDir, lncfg.DefaultConfigFilename)
264

265
        defaultDataDir = filepath.Join(DefaultLndDir, defaultDataDirname)
266
        defaultLogDir  = filepath.Join(DefaultLndDir, defaultLogDirname)
267

268
        defaultTowerDir = filepath.Join(defaultDataDir, defaultTowerSubDirname)
269

270
        defaultTLSCertPath    = filepath.Join(DefaultLndDir, defaultTLSCertFilename)
271
        defaultTLSKeyPath     = filepath.Join(DefaultLndDir, defaultTLSKeyFilename)
272
        defaultLetsEncryptDir = filepath.Join(DefaultLndDir, defaultLetsEncryptDirname)
273

274
        defaultBtcdDir         = btcutil.AppDataDir(btcdBackendName, false)
275
        defaultBtcdRPCCertFile = filepath.Join(defaultBtcdDir, "rpc.cert")
276

277
        defaultBitcoindDir = btcutil.AppDataDir(BitcoinChainName, false)
278

279
        defaultTorSOCKS   = net.JoinHostPort("localhost", strconv.Itoa(defaultTorSOCKSPort))
280
        defaultTorDNS     = net.JoinHostPort(defaultTorDNSHost, strconv.Itoa(defaultTorDNSPort))
281
        defaultTorControl = net.JoinHostPort("localhost", strconv.Itoa(defaultTorControlPort))
282

283
        // bitcoindEsimateModes defines all the legal values for bitcoind's
284
        // estimatesmartfee RPC call.
285
        defaultBitcoindEstimateMode = "CONSERVATIVE"
286
        bitcoindEstimateModes       = [2]string{"ECONOMICAL", defaultBitcoindEstimateMode}
287

288
        defaultPrunedNodeMaxPeers = 4
289
)
290

291
// Config defines the configuration options for lnd.
292
//
293
// See LoadConfig for further details regarding the configuration
294
// loading+parsing process.
295
//
296
//nolint:lll
297
type Config struct {
298
        ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
299

300
        LndDir       string `long:"lnddir" description:"The base directory that contains lnd's data, logs, configuration file, etc. This option overwrites all other directory options."`
301
        ConfigFile   string `short:"C" long:"configfile" description:"Path to configuration file"`
302
        DataDir      string `short:"b" long:"datadir" description:"The directory to store lnd's data within"`
303
        SyncFreelist bool   `long:"sync-freelist" description:"Whether the databases used within lnd should sync their freelist to disk. This is disabled by default resulting in improved memory performance during operation, but with an increase in startup time."`
304

305
        TLSCertPath        string        `long:"tlscertpath" description:"Path to write the TLS certificate for lnd's RPC and REST services"`
306
        TLSKeyPath         string        `long:"tlskeypath" description:"Path to write the TLS private key for lnd's RPC and REST services"`
307
        TLSExtraIPs        []string      `long:"tlsextraip" description:"Adds an extra ip to the generated certificate"`
308
        TLSExtraDomains    []string      `long:"tlsextradomain" description:"Adds an extra domain to the generated certificate"`
309
        TLSAutoRefresh     bool          `long:"tlsautorefresh" description:"Re-generate TLS certificate and key if the IPs or domains are changed"`
310
        TLSDisableAutofill bool          `long:"tlsdisableautofill" description:"Do not include the interface IPs or the system hostname in TLS certificate, use first --tlsextradomain as Common Name instead, if set"`
311
        TLSCertDuration    time.Duration `long:"tlscertduration" description:"The duration for which the auto-generated TLS certificate will be valid for"`
312
        TLSEncryptKey      bool          `long:"tlsencryptkey" description:"Automatically encrypts the TLS private key and generates ephemeral TLS key pairs when the wallet is locked or not initialized"`
313

314
        NoMacaroons     bool          `long:"no-macaroons" description:"Disable macaroon authentication, can only be used if server is not listening on a public interface."`
315
        AdminMacPath    string        `long:"adminmacaroonpath" description:"Path to write the admin macaroon for lnd's RPC and REST services if it doesn't exist"`
316
        ReadMacPath     string        `long:"readonlymacaroonpath" description:"Path to write the read-only macaroon for lnd's RPC and REST services if it doesn't exist"`
317
        InvoiceMacPath  string        `long:"invoicemacaroonpath" description:"Path to the invoice-only macaroon for lnd's RPC and REST services if it doesn't exist"`
318
        LogDir          string        `long:"logdir" description:"Directory to log output."`
319
        LogCompressor   string        `long:"logcompressor" description:"Compression algorithm to use when rotating logs." choice:"gzip" choice:"zstd"`
320
        MaxLogFiles     int           `long:"maxlogfiles" description:"Maximum logfiles to keep (0 for no rotation)"`
321
        MaxLogFileSize  int           `long:"maxlogfilesize" description:"Maximum logfile size in MB"`
322
        AcceptorTimeout time.Duration `long:"acceptortimeout" description:"Time after which an RPCAcceptor will time out and return false if it hasn't yet received a response"`
323

324
        LetsEncryptDir    string `long:"letsencryptdir" description:"The directory to store Let's Encrypt certificates within"`
325
        LetsEncryptListen string `long:"letsencryptlisten" description:"The IP:port on which lnd will listen for Let's Encrypt challenges. Let's Encrypt will always try to contact on port 80. Often non-root processes are not allowed to bind to ports lower than 1024. This configuration option allows a different port to be used, but must be used in combination with port forwarding from port 80. This configuration can also be used to specify another IP address to listen on, for example an IPv6 address."`
326
        LetsEncryptDomain string `long:"letsencryptdomain" description:"Request a Let's Encrypt certificate for this domain. Note that the certificate is only requested and stored when the first rpc connection comes in."`
327

328
        // We'll parse these 'raw' string arguments into real net.Addrs in the
329
        // loadConfig function. We need to expose the 'raw' strings so the
330
        // command line library can access them.
331
        // Only the parsed net.Addrs should be used!
332
        RawRPCListeners   []string `long:"rpclisten" description:"Add an interface/port/socket to listen for RPC connections"`
333
        RawRESTListeners  []string `long:"restlisten" description:"Add an interface/port/socket to listen for REST connections"`
334
        RawListeners      []string `long:"listen" description:"Add an interface/port to listen for peer connections"`
335
        RawExternalIPs    []string `long:"externalip" description:"Add an ip:port to the list of local addresses we claim to listen on to peers. If a port is not specified, the default (9735) will be used regardless of other parameters"`
336
        ExternalHosts     []string `long:"externalhosts" description:"Add a hostname:port that should be periodically resolved to announce IPs for. If a port is not specified, the default (9735) will be used."`
337
        RPCListeners      []net.Addr
338
        RESTListeners     []net.Addr
339
        RestCORS          []string `long:"restcors" description:"Add an ip:port/hostname to allow cross origin access from. To allow all origins, set as \"*\"."`
340
        Listeners         []net.Addr
341
        ExternalIPs       []net.Addr
342
        DisableListen     bool          `long:"nolisten" description:"Disable listening for incoming peer connections"`
343
        DisableRest       bool          `long:"norest" description:"Disable REST API"`
344
        DisableRestTLS    bool          `long:"no-rest-tls" description:"Disable TLS for REST connections"`
345
        WSPingInterval    time.Duration `long:"ws-ping-interval" description:"The ping interval for REST based WebSocket connections, set to 0 to disable sending ping messages from the server side"`
346
        WSPongWait        time.Duration `long:"ws-pong-wait" description:"The time we wait for a pong response message on REST based WebSocket connections before the connection is closed as inactive"`
347
        NAT               bool          `long:"nat" description:"Toggle NAT traversal support (using either UPnP or NAT-PMP) to automatically advertise your external IP address to the network -- NOTE this does not support devices behind multiple NATs"`
348
        AddPeers          []string      `long:"addpeer" description:"Specify peers to connect to first"`
349
        MinBackoff        time.Duration `long:"minbackoff" description:"Shortest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."`
350
        MaxBackoff        time.Duration `long:"maxbackoff" description:"Longest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."`
351
        ConnectionTimeout time.Duration `long:"connectiontimeout" description:"The timeout value for network connections. Valid time units are {ms, s, m, h}."`
352

353
        DebugLevel string `short:"d" long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify <global-level>,<subsystem>=<level>,<subsystem2>=<level>,... to set the log level for individual subsystems -- Use show to list available subsystems"`
354

355
        CPUProfile string `long:"cpuprofile" description:"Write CPU profile to the specified file"`
356

357
        Profile string `long:"profile" description:"Enable HTTP profiling on either a port or host:port"`
358

359
        BlockingProfile int `long:"blockingprofile" description:"Used to enable a blocking profile to be served on the profiling port. This takes a value from 0 to 1, with 1 including every blocking event, and 0 including no events."`
360
        MutexProfile    int `long:"mutexprofile" description:"Used to Enable a mutex profile to be served on the profiling port. This takes a value from 0 to 1, with 1 including every mutex event, and 0 including no events."`
361

362
        UnsafeDisconnect   bool   `long:"unsafe-disconnect" description:"DEPRECATED: Allows the rpcserver to intentionally disconnect from peers with open channels. THIS FLAG WILL BE REMOVED IN 0.10.0" hidden:"true"`
363
        UnsafeReplay       bool   `long:"unsafe-replay" description:"Causes a link to replay the adds on its commitment txn after starting up, this enables testing of the sphinx replay logic."`
364
        MaxPendingChannels int    `long:"maxpendingchannels" description:"The maximum number of incoming pending channels permitted per peer."`
365
        BackupFilePath     string `long:"backupfilepath" description:"The target location of the channel backup file"`
366

367
        FeeURL string `long:"feeurl" description:"DEPRECATED: Use 'fee.url' option. Optional URL for external fee estimation. If no URL is specified, the method for fee estimation will depend on the chosen backend and network. Must be set for neutrino on mainnet." hidden:"true"`
368

369
        Bitcoin      *lncfg.Chain    `group:"Bitcoin" namespace:"bitcoin"`
370
        BtcdMode     *lncfg.Btcd     `group:"btcd" namespace:"btcd"`
371
        BitcoindMode *lncfg.Bitcoind `group:"bitcoind" namespace:"bitcoind"`
372
        NeutrinoMode *lncfg.Neutrino `group:"neutrino" namespace:"neutrino"`
373

374
        BlockCacheSize uint64 `long:"blockcachesize" description:"The maximum capacity of the block cache"`
375

376
        Autopilot *lncfg.AutoPilot `group:"Autopilot" namespace:"autopilot"`
377

378
        Tor *lncfg.Tor `group:"Tor" namespace:"tor"`
379

380
        SubRPCServers *subRPCServerConfigs `group:"subrpc"`
381

382
        Hodl *hodl.Config `group:"hodl" namespace:"hodl"`
383

384
        NoNetBootstrap bool `long:"nobootstrap" description:"If true, then automatic network bootstrapping will not be attempted."`
385

386
        NoSeedBackup             bool   `long:"noseedbackup" description:"If true, NO SEED WILL BE EXPOSED -- EVER, AND THE WALLET WILL BE ENCRYPTED USING THE DEFAULT PASSPHRASE. THIS FLAG IS ONLY FOR TESTING AND SHOULD NEVER BE USED ON MAINNET."`
387
        WalletUnlockPasswordFile string `long:"wallet-unlock-password-file" description:"The full path to a file (or pipe/device) that contains the password for unlocking the wallet; if set, no unlocking through RPC is possible and lnd will exit if no wallet exists or the password is incorrect; if wallet-unlock-allow-create is also set then lnd will ignore this flag if no wallet exists and allow a wallet to be created through RPC."`
388
        WalletUnlockAllowCreate  bool   `long:"wallet-unlock-allow-create" description:"Don't fail with an error if wallet-unlock-password-file is set but no wallet exists yet."`
389

390
        ResetWalletTransactions bool `long:"reset-wallet-transactions" description:"Removes all transaction history from the on-chain wallet on startup, forcing a full chain rescan starting at the wallet's birthday. Implements the same functionality as btcwallet's dropwtxmgr command. Should be set to false after successful execution to avoid rescanning on every restart of lnd."`
391

392
        CoinSelectionStrategy string `long:"coin-selection-strategy" description:"The strategy to use for selecting coins for wallet transactions." choice:"largest" choice:"random"`
393

394
        PaymentsExpirationGracePeriod time.Duration `long:"payments-expiration-grace-period" description:"A period to wait before force closing channels with outgoing htlcs that have timed-out and are a result of this node initiated payments."`
395
        TrickleDelay                  int           `long:"trickledelay" description:"Time in milliseconds between each release of announcements to the network"`
396
        ChanEnableTimeout             time.Duration `long:"chan-enable-timeout" description:"The duration that a peer connection must be stable before attempting to send a channel update to re-enable or cancel a pending disables of the peer's channels on the network."`
397
        ChanDisableTimeout            time.Duration `long:"chan-disable-timeout" description:"The duration that must elapse after first detecting that an already active channel is actually inactive and sending channel update disabling it to the network. The pending disable can be canceled if the peer reconnects and becomes stable for chan-enable-timeout before the disable update is sent."`
398
        ChanStatusSampleInterval      time.Duration `long:"chan-status-sample-interval" description:"The polling interval between attempts to detect if an active channel has become inactive due to its peer going offline."`
399
        HeightHintCacheQueryDisable   bool          `long:"height-hint-cache-query-disable" description:"Disable queries from the height-hint cache to try to recover channels stuck in the pending close state. Disabling height hint queries may cause longer chain rescans, resulting in a performance hit. Unset this after channels are unstuck so you can get better performance again."`
400
        Alias                         string        `long:"alias" description:"The node alias. Used as a moniker by peers and intelligence services"`
401
        Color                         string        `long:"color" description:"The color of the node in hex format (i.e. '#3399FF'). Used to customize node appearance in intelligence services"`
402
        MinChanSize                   int64         `long:"minchansize" description:"The smallest channel size (in satoshis) that we should accept. Incoming channels smaller than this will be rejected"`
403
        MaxChanSize                   int64         `long:"maxchansize" description:"The largest channel size (in satoshis) that we should accept. Incoming channels larger than this will be rejected"`
404
        CoopCloseTargetConfs          uint32        `long:"coop-close-target-confs" description:"The target number of blocks that a cooperative channel close transaction should confirm in. This is used to estimate the fee to use as the lower bound during fee negotiation for the channel closure."`
405

406
        ChannelCommitInterval time.Duration `long:"channel-commit-interval" description:"The maximum time that is allowed to pass between receiving a channel state update and signing the next commitment. Setting this to a longer duration allows for more efficient channel operations at the cost of latency."`
407

408
        PendingCommitInterval time.Duration `long:"pending-commit-interval" description:"The maximum time that is allowed to pass while waiting for the remote party to revoke a locally initiated commitment state. Setting this to a longer duration if a slow response is expected from the remote party or large number of payments are attempted at the same time."`
409

410
        ChannelCommitBatchSize uint32 `long:"channel-commit-batch-size" description:"The maximum number of channel state updates that is accumulated before signing a new commitment."`
411

412
        KeepFailedPaymentAttempts bool `long:"keep-failed-payment-attempts" description:"Keeps persistent record of all failed payment attempts for successfully settled payments."`
413

414
        StoreFinalHtlcResolutions bool `long:"store-final-htlc-resolutions" description:"Persistently store the final resolution of incoming htlcs."`
415

416
        DefaultRemoteMaxHtlcs uint16 `long:"default-remote-max-htlcs" description:"The default max_htlc applied when opening or accepting channels. This value limits the number of concurrent HTLCs that the remote party can add to the commitment. The maximum possible value is 483."`
417

418
        NumGraphSyncPeers      int           `long:"numgraphsyncpeers" description:"The number of peers that we should receive new graph updates from. This option can be tuned to save bandwidth for light clients or routing nodes."`
419
        HistoricalSyncInterval time.Duration `long:"historicalsyncinterval" description:"The polling interval between historical graph sync attempts. Each historical graph sync attempt ensures we reconcile with the remote peer's graph from the genesis block."`
420

421
        IgnoreHistoricalGossipFilters bool `long:"ignore-historical-gossip-filters" description:"If true, will not reply with historical data that matches the range specified by a remote peer's gossip_timestamp_filter. Doing so will result in lower memory and bandwidth requirements."`
422

423
        RejectPush bool `long:"rejectpush" description:"If true, lnd will not accept channel opening requests with non-zero push amounts. This should prevent accidental pushes to merchant nodes."`
424

425
        RejectHTLC bool `long:"rejecthtlc" description:"If true, lnd will not forward any HTLCs that are meant as onward payments. This option will still allow lnd to send HTLCs and receive HTLCs but lnd won't be used as a hop."`
426

427
        AcceptPositiveInboundFees bool `long:"accept-positive-inbound-fees" description:"If true, lnd will also allow setting positive inbound fees. By default, lnd only allows to set negative inbound fees (an inbound \"discount\") to remain backwards compatible with senders whose implementations do not yet support inbound fees."`
428

429
        // RequireInterceptor determines whether the HTLC interceptor is
430
        // registered regardless of whether the RPC is called or not.
431
        RequireInterceptor bool `long:"requireinterceptor" description:"Whether to always intercept HTLCs, even if no stream is attached"`
432

433
        StaggerInitialReconnect bool `long:"stagger-initial-reconnect" description:"If true, will apply a randomized staggering between 0s and 30s when reconnecting to persistent peers on startup. The first 10 reconnections will be attempted instantly, regardless of the flag's value"`
434

435
        MaxOutgoingCltvExpiry uint32 `long:"max-cltv-expiry" description:"The maximum number of blocks funds could be locked up for when forwarding payments."`
436

437
        MaxChannelFeeAllocation float64 `long:"max-channel-fee-allocation" description:"The maximum percentage of total funds that can be allocated to a channel's commitment fee. This only applies for the initiator of the channel. Valid values are within [0.1, 1]."`
438

439
        MaxCommitFeeRateAnchors uint64 `long:"max-commit-fee-rate-anchors" description:"The maximum fee rate in sat/vbyte that will be used for commitments of channels of the anchors type. Must be large enough to ensure transaction propagation"`
440

441
        DryRunMigration bool `long:"dry-run-migration" description:"If true, lnd will abort committing a migration if it would otherwise have been successful. This leaves the database unmodified, and still compatible with the previously active version of lnd."`
442

443
        net tor.Net
444

445
        EnableUpfrontShutdown bool `long:"enable-upfront-shutdown" description:"If true, option upfront shutdown script will be enabled. If peers that we open channels with support this feature, we will automatically set the script to which cooperative closes should be paid out to on channel open. This offers the partial protection of a channel peer disconnecting from us if cooperative close is attempted with a different script."`
446

447
        AcceptKeySend bool `long:"accept-keysend" description:"If true, spontaneous payments through keysend will be accepted. [experimental]"`
448

449
        AcceptAMP bool `long:"accept-amp" description:"If true, spontaneous payments via AMP will be accepted."`
450

451
        KeysendHoldTime time.Duration `long:"keysend-hold-time" description:"If non-zero, keysend payments are accepted but not immediately settled. If the payment isn't settled manually after the specified time, it is canceled automatically. [experimental]"`
452

453
        GcCanceledInvoicesOnStartup bool `long:"gc-canceled-invoices-on-startup" description:"If true, we'll attempt to garbage collect canceled invoices upon start."`
454

455
        GcCanceledInvoicesOnTheFly bool `long:"gc-canceled-invoices-on-the-fly" description:"If true, we'll delete newly canceled invoices on the fly."`
456

457
        MaxFeeExposure uint64 `long:"dust-threshold" description:"Sets the max fee exposure in satoshis for a channel after which HTLC's will be failed."`
458

459
        Fee *lncfg.Fee `group:"fee" namespace:"fee"`
460

461
        Invoices *lncfg.Invoices `group:"invoices" namespace:"invoices"`
462

463
        Routing *lncfg.Routing `group:"routing" namespace:"routing"`
464

465
        Gossip *lncfg.Gossip `group:"gossip" namespace:"gossip"`
466

467
        Workers *lncfg.Workers `group:"workers" namespace:"workers"`
468

469
        Caches *lncfg.Caches `group:"caches" namespace:"caches"`
470

471
        Prometheus lncfg.Prometheus `group:"prometheus" namespace:"prometheus"`
472

473
        WtClient *lncfg.WtClient `group:"wtclient" namespace:"wtclient"`
474

475
        Watchtower *lncfg.Watchtower `group:"watchtower" namespace:"watchtower"`
476

477
        ProtocolOptions *lncfg.ProtocolOptions `group:"protocol" namespace:"protocol"`
478

479
        AllowCircularRoute bool `long:"allow-circular-route" description:"If true, our node will allow htlc forwards that arrive and depart on the same channel."`
480

481
        HealthChecks *lncfg.HealthCheckConfig `group:"healthcheck" namespace:"healthcheck"`
482

483
        DB *lncfg.DB `group:"db" namespace:"db"`
484

485
        Cluster *lncfg.Cluster `group:"cluster" namespace:"cluster"`
486

487
        RPCMiddleware *lncfg.RPCMiddleware `group:"rpcmiddleware" namespace:"rpcmiddleware"`
488

489
        RemoteSigner *lncfg.RemoteSigner `group:"remotesigner" namespace:"remotesigner"`
490

491
        Sweeper *lncfg.Sweeper `group:"sweeper" namespace:"sweeper"`
492

493
        Htlcswitch *lncfg.Htlcswitch `group:"htlcswitch" namespace:"htlcswitch"`
494

495
        GRPC *GRPCConfig `group:"grpc" namespace:"grpc"`
496

497
        // LogWriter is the root logger that all of the daemon's subloggers are
498
        // hooked up to.
499
        LogWriter *build.RotatingLogWriter
500

501
        // networkDir is the path to the directory of the currently active
502
        // network. This path will hold the files related to each different
503
        // network.
504
        networkDir string
505

506
        // ActiveNetParams contains parameters of the target chain.
507
        ActiveNetParams chainreg.BitcoinNetParams
508

509
        // Estimator is used to estimate routing probabilities.
510
        Estimator routing.Estimator
511

512
        // Dev specifies configs used for integration tests, which is always
513
        // empty if not built with `integration` flag.
514
        Dev *lncfg.DevConfig `group:"dev" namespace:"dev"`
515

516
        // HTTPHeaderTimeout is the maximum duration that the server will wait
517
        // before timing out reading the headers of an HTTP request.
518
        HTTPHeaderTimeout time.Duration `long:"http-header-timeout" description:"The maximum duration that the server will wait before timing out reading the headers of an HTTP request."`
519
}
520

521
// GRPCConfig holds the configuration options for the gRPC server.
522
// See https://github.com/grpc/grpc-go/blob/v1.41.0/keepalive/keepalive.go#L50
523
// for more details. Any value of 0 means we use the gRPC internal default
524
// values.
525
//
526
//nolint:lll
527
type GRPCConfig struct {
528
        // ServerPingTime is a duration for the amount of time of no activity
529
        // after which the server pings the client to see if the transport is
530
        // still alive. If set below 1s, a minimum value of 1s will be used
531
        // instead.
532
        ServerPingTime time.Duration `long:"server-ping-time" description:"How long the server waits on a gRPC stream with no activity before pinging the client."`
533

534
        // ServerPingTimeout is the duration the server waits after having
535
        // pinged for keepalive check, and if no activity is seen even after
536
        // that the connection is closed.
537
        ServerPingTimeout time.Duration `long:"server-ping-timeout" description:"How long the server waits for the response from the client for the keepalive ping response."`
538

539
        // ClientPingMinWait is the minimum amount of time a client should wait
540
        // before sending a keepalive ping.
541
        ClientPingMinWait time.Duration `long:"client-ping-min-wait" description:"The minimum amount of time the client should wait before sending a keepalive ping."`
542

543
        // ClientAllowPingWithoutStream specifies whether pings from the client
544
        // are allowed even if there are no active gRPC streams. This might be
545
        // useful to keep the underlying HTTP/2 connection open for future
546
        // requests.
547
        ClientAllowPingWithoutStream bool `long:"client-allow-ping-without-stream" description:"If true, the server allows keepalive pings from the client even when there are no active gRPC streams. This might be useful to keep the underlying HTTP/2 connection open for future requests."`
548
}
549

550
// DefaultConfig returns all default values for the Config struct.
551
//
552
//nolint:lll
553
func DefaultConfig() Config {
1✔
554
        return Config{
1✔
555
                LndDir:            DefaultLndDir,
1✔
556
                ConfigFile:        DefaultConfigFile,
1✔
557
                DataDir:           defaultDataDir,
1✔
558
                DebugLevel:        defaultLogLevel,
1✔
559
                TLSCertPath:       defaultTLSCertPath,
1✔
560
                TLSKeyPath:        defaultTLSKeyPath,
1✔
561
                TLSCertDuration:   defaultTLSCertDuration,
1✔
562
                LetsEncryptDir:    defaultLetsEncryptDir,
1✔
563
                LetsEncryptListen: defaultLetsEncryptListen,
1✔
564
                LogDir:            defaultLogDir,
1✔
565
                LogCompressor:     defaultLogCompressor,
1✔
566
                MaxLogFiles:       defaultMaxLogFiles,
1✔
567
                MaxLogFileSize:    defaultMaxLogFileSize,
1✔
568
                AcceptorTimeout:   defaultAcceptorTimeout,
1✔
569
                WSPingInterval:    lnrpc.DefaultPingInterval,
1✔
570
                WSPongWait:        lnrpc.DefaultPongWait,
1✔
571
                Bitcoin: &lncfg.Chain{
1✔
572
                        MinHTLCIn:     chainreg.DefaultBitcoinMinHTLCInMSat,
1✔
573
                        MinHTLCOut:    chainreg.DefaultBitcoinMinHTLCOutMSat,
1✔
574
                        BaseFee:       chainreg.DefaultBitcoinBaseFeeMSat,
1✔
575
                        FeeRate:       chainreg.DefaultBitcoinFeeRate,
1✔
576
                        TimeLockDelta: chainreg.DefaultBitcoinTimeLockDelta,
1✔
577
                        MaxLocalDelay: defaultMaxLocalCSVDelay,
1✔
578
                        Node:          btcdBackendName,
1✔
579
                },
1✔
580
                BtcdMode: &lncfg.Btcd{
1✔
581
                        Dir:     defaultBtcdDir,
1✔
582
                        RPCHost: defaultRPCHost,
1✔
583
                        RPCCert: defaultBtcdRPCCertFile,
1✔
584
                },
1✔
585
                BitcoindMode: &lncfg.Bitcoind{
1✔
586
                        Dir:                defaultBitcoindDir,
1✔
587
                        RPCHost:            defaultRPCHost,
1✔
588
                        EstimateMode:       defaultBitcoindEstimateMode,
1✔
589
                        PrunedNodeMaxPeers: defaultPrunedNodeMaxPeers,
1✔
590
                        ZMQReadDeadline:    defaultZMQReadDeadline,
1✔
591
                },
1✔
592
                NeutrinoMode: &lncfg.Neutrino{
1✔
593
                        UserAgentName:    neutrino.UserAgentName,
1✔
594
                        UserAgentVersion: neutrino.UserAgentVersion,
1✔
595
                },
1✔
596
                BlockCacheSize:     defaultBlockCacheSize,
1✔
597
                MaxPendingChannels: lncfg.DefaultMaxPendingChannels,
1✔
598
                NoSeedBackup:       defaultNoSeedBackup,
1✔
599
                MinBackoff:         defaultMinBackoff,
1✔
600
                MaxBackoff:         defaultMaxBackoff,
1✔
601
                ConnectionTimeout:  tor.DefaultConnTimeout,
1✔
602

1✔
603
                Fee: &lncfg.Fee{
1✔
604
                        MinUpdateTimeout: lncfg.DefaultMinUpdateTimeout,
1✔
605
                        MaxUpdateTimeout: lncfg.DefaultMaxUpdateTimeout,
1✔
606
                },
1✔
607

1✔
608
                SubRPCServers: &subRPCServerConfigs{
1✔
609
                        SignRPC:   &signrpc.Config{},
1✔
610
                        RouterRPC: routerrpc.DefaultConfig(),
1✔
611
                        PeersRPC:  &peersrpc.Config{},
1✔
612
                },
1✔
613
                Autopilot: &lncfg.AutoPilot{
1✔
614
                        MaxChannels:    5,
1✔
615
                        Allocation:     0.6,
1✔
616
                        MinChannelSize: int64(funding.MinChanFundingSize),
1✔
617
                        MaxChannelSize: int64(MaxFundingAmount),
1✔
618
                        MinConfs:       1,
1✔
619
                        ConfTarget:     autopilot.DefaultConfTarget,
1✔
620
                        Heuristic: map[string]float64{
1✔
621
                                "top_centrality": 1.0,
1✔
622
                        },
1✔
623
                },
1✔
624
                PaymentsExpirationGracePeriod: defaultPaymentsExpirationGracePeriod,
1✔
625
                TrickleDelay:                  defaultTrickleDelay,
1✔
626
                ChanStatusSampleInterval:      defaultChanStatusSampleInterval,
1✔
627
                ChanEnableTimeout:             defaultChanEnableTimeout,
1✔
628
                ChanDisableTimeout:            defaultChanDisableTimeout,
1✔
629
                HeightHintCacheQueryDisable:   defaultHeightHintCacheQueryDisable,
1✔
630
                Alias:                         defaultAlias,
1✔
631
                Color:                         defaultColor,
1✔
632
                MinChanSize:                   int64(funding.MinChanFundingSize),
1✔
633
                MaxChanSize:                   int64(0),
1✔
634
                CoopCloseTargetConfs:          defaultCoopCloseTargetConfs,
1✔
635
                DefaultRemoteMaxHtlcs:         defaultRemoteMaxHtlcs,
1✔
636
                NumGraphSyncPeers:             defaultMinPeers,
1✔
637
                HistoricalSyncInterval:        discovery.DefaultHistoricalSyncInterval,
1✔
638
                Tor: &lncfg.Tor{
1✔
639
                        SOCKS:   defaultTorSOCKS,
1✔
640
                        DNS:     defaultTorDNS,
1✔
641
                        Control: defaultTorControl,
1✔
642
                },
1✔
643
                net: &tor.ClearNet{},
1✔
644
                Workers: &lncfg.Workers{
1✔
645
                        Read:  lncfg.DefaultReadWorkers,
1✔
646
                        Write: lncfg.DefaultWriteWorkers,
1✔
647
                        Sig:   lncfg.DefaultSigWorkers,
1✔
648
                },
1✔
649
                Caches: &lncfg.Caches{
1✔
650
                        RejectCacheSize:  channeldb.DefaultRejectCacheSize,
1✔
651
                        ChannelCacheSize: channeldb.DefaultChannelCacheSize,
1✔
652
                },
1✔
653
                Prometheus: lncfg.DefaultPrometheus(),
1✔
654
                Watchtower: lncfg.DefaultWatchtowerCfg(defaultTowerDir),
1✔
655
                HealthChecks: &lncfg.HealthCheckConfig{
1✔
656
                        ChainCheck: &lncfg.CheckConfig{
1✔
657
                                Interval: defaultChainInterval,
1✔
658
                                Timeout:  defaultChainTimeout,
1✔
659
                                Attempts: defaultChainAttempts,
1✔
660
                                Backoff:  defaultChainBackoff,
1✔
661
                        },
1✔
662
                        DiskCheck: &lncfg.DiskCheckConfig{
1✔
663
                                RequiredRemaining: defaultRequiredDisk,
1✔
664
                                CheckConfig: &lncfg.CheckConfig{
1✔
665
                                        Interval: defaultDiskInterval,
1✔
666
                                        Attempts: defaultDiskAttempts,
1✔
667
                                        Timeout:  defaultDiskTimeout,
1✔
668
                                        Backoff:  defaultDiskBackoff,
1✔
669
                                },
1✔
670
                        },
1✔
671
                        TLSCheck: &lncfg.CheckConfig{
1✔
672
                                Interval: defaultTLSInterval,
1✔
673
                                Timeout:  defaultTLSTimeout,
1✔
674
                                Attempts: defaultTLSAttempts,
1✔
675
                                Backoff:  defaultTLSBackoff,
1✔
676
                        },
1✔
677
                        TorConnection: &lncfg.CheckConfig{
1✔
678
                                Interval: defaultTCInterval,
1✔
679
                                Timeout:  defaultTCTimeout,
1✔
680
                                Attempts: defaultTCAttempts,
1✔
681
                                Backoff:  defaultTCBackoff,
1✔
682
                        },
1✔
683
                        RemoteSigner: &lncfg.CheckConfig{
1✔
684
                                Interval: defaultRSInterval,
1✔
685
                                Timeout:  defaultRSTimeout,
1✔
686
                                Attempts: defaultRSAttempts,
1✔
687
                                Backoff:  defaultRSBackoff,
1✔
688
                        },
1✔
689
                        LeaderCheck: &lncfg.CheckConfig{
1✔
690
                                Interval: defaultLeaderCheckInterval,
1✔
691
                                Timeout:  defaultLeaderCheckTimeout,
1✔
692
                                Attempts: defaultLeaderCheckAttempts,
1✔
693
                                Backoff:  defaultLeaderCheckBackoff,
1✔
694
                        },
1✔
695
                },
1✔
696
                Gossip: &lncfg.Gossip{
1✔
697
                        MaxChannelUpdateBurst: discovery.DefaultMaxChannelUpdateBurst,
1✔
698
                        ChannelUpdateInterval: discovery.DefaultChannelUpdateInterval,
1✔
699
                        SubBatchDelay:         discovery.DefaultSubBatchDelay,
1✔
700
                },
1✔
701
                Invoices: &lncfg.Invoices{
1✔
702
                        HoldExpiryDelta: lncfg.DefaultHoldInvoiceExpiryDelta,
1✔
703
                },
1✔
704
                Routing: &lncfg.Routing{
1✔
705
                        BlindedPaths: lncfg.BlindedPaths{
1✔
706
                                MinNumRealHops:           lncfg.DefaultMinNumRealBlindedPathHops,
1✔
707
                                NumHops:                  lncfg.DefaultNumBlindedPathHops,
1✔
708
                                MaxNumPaths:              lncfg.DefaultMaxNumBlindedPaths,
1✔
709
                                PolicyIncreaseMultiplier: lncfg.DefaultBlindedPathPolicyIncreaseMultiplier,
1✔
710
                                PolicyDecreaseMultiplier: lncfg.DefaultBlindedPathPolicyDecreaseMultiplier,
1✔
711
                        },
1✔
712
                },
1✔
713
                MaxOutgoingCltvExpiry:     htlcswitch.DefaultMaxOutgoingCltvExpiry,
1✔
714
                MaxChannelFeeAllocation:   htlcswitch.DefaultMaxLinkFeeAllocation,
1✔
715
                MaxCommitFeeRateAnchors:   lnwallet.DefaultAnchorsCommitMaxFeeRateSatPerVByte,
1✔
716
                MaxFeeExposure:            uint64(htlcswitch.DefaultMaxFeeExposure.ToSatoshis()),
1✔
717
                LogWriter:                 build.NewRotatingLogWriter(),
1✔
718
                DB:                        lncfg.DefaultDB(),
1✔
719
                Cluster:                   lncfg.DefaultCluster(),
1✔
720
                RPCMiddleware:             lncfg.DefaultRPCMiddleware(),
1✔
721
                ActiveNetParams:           chainreg.BitcoinTestNetParams,
1✔
722
                ChannelCommitInterval:     defaultChannelCommitInterval,
1✔
723
                PendingCommitInterval:     defaultPendingCommitInterval,
1✔
724
                ChannelCommitBatchSize:    defaultChannelCommitBatchSize,
1✔
725
                CoinSelectionStrategy:     defaultCoinSelectionStrategy,
1✔
726
                KeepFailedPaymentAttempts: defaultKeepFailedPaymentAttempts,
1✔
727
                RemoteSigner: &lncfg.RemoteSigner{
1✔
728
                        Timeout: lncfg.DefaultRemoteSignerRPCTimeout,
1✔
729
                },
1✔
730
                Sweeper: lncfg.DefaultSweeperConfig(),
1✔
731
                Htlcswitch: &lncfg.Htlcswitch{
1✔
732
                        MailboxDeliveryTimeout: htlcswitch.DefaultMailboxDeliveryTimeout,
1✔
733
                },
1✔
734
                GRPC: &GRPCConfig{
1✔
735
                        ServerPingTime:    defaultGrpcServerPingTime,
1✔
736
                        ServerPingTimeout: defaultGrpcServerPingTimeout,
1✔
737
                        ClientPingMinWait: defaultGrpcClientPingMinWait,
1✔
738
                },
1✔
739
                WtClient:          lncfg.DefaultWtClientCfg(),
1✔
740
                HTTPHeaderTimeout: DefaultHTTPHeaderTimeout,
1✔
741
        }
1✔
742
}
1✔
743

744
// LoadConfig initializes and parses the config using a config file and command
745
// line options.
746
//
747
// The configuration proceeds as follows:
748
//  1. Start with a default config with sane settings
749
//  2. Pre-parse the command line to check for an alternative config file
750
//  3. Load configuration file overwriting defaults with any specified options
751
//  4. Parse CLI options and overwrite/add any specified options
UNCOV
752
func LoadConfig(interceptor signal.Interceptor) (*Config, error) {
×
UNCOV
753
        // Pre-parse the command line options to pick up an alternative config
×
UNCOV
754
        // file.
×
UNCOV
755
        preCfg := DefaultConfig()
×
UNCOV
756
        if _, err := flags.Parse(&preCfg); err != nil {
×
757
                return nil, err
×
758
        }
×
759

760
        // Show the version and exit if the version flag was specified.
UNCOV
761
        appName := filepath.Base(os.Args[0])
×
UNCOV
762
        appName = strings.TrimSuffix(appName, filepath.Ext(appName))
×
UNCOV
763
        usageMessage := fmt.Sprintf("Use %s -h to show usage", appName)
×
UNCOV
764
        if preCfg.ShowVersion {
×
765
                fmt.Println(appName, "version", build.Version(),
×
766
                        "commit="+build.Commit)
×
767
                os.Exit(0)
×
768
        }
×
769

770
        // If the config file path has not been modified by the user, then we'll
771
        // use the default config file path. However, if the user has modified
772
        // their lnddir, then we should assume they intend to use the config
773
        // file within it.
UNCOV
774
        configFileDir := CleanAndExpandPath(preCfg.LndDir)
×
UNCOV
775
        configFilePath := CleanAndExpandPath(preCfg.ConfigFile)
×
UNCOV
776
        switch {
×
777
        // User specified --lnddir but no --configfile. Update the config file
778
        // path to the lnd config directory, but don't require it to exist.
779
        case configFileDir != DefaultLndDir &&
UNCOV
780
                configFilePath == DefaultConfigFile:
×
UNCOV
781

×
UNCOV
782
                configFilePath = filepath.Join(
×
UNCOV
783
                        configFileDir, lncfg.DefaultConfigFilename,
×
UNCOV
784
                )
×
785

786
        // User did specify an explicit --configfile, so we check that it does
787
        // exist under that path to avoid surprises.
788
        case configFilePath != DefaultConfigFile:
×
789
                if !lnrpc.FileExists(configFilePath) {
×
790
                        return nil, fmt.Errorf("specified config file does "+
×
791
                                "not exist in %s", configFilePath)
×
792
                }
×
793
        }
794

795
        // Next, load any additional configuration options from the file.
UNCOV
796
        var configFileError error
×
UNCOV
797
        cfg := preCfg
×
UNCOV
798
        fileParser := flags.NewParser(&cfg, flags.Default)
×
UNCOV
799
        err := flags.NewIniParser(fileParser).ParseFile(configFilePath)
×
UNCOV
800
        if err != nil {
×
UNCOV
801
                // If it's a parsing related error, then we'll return
×
UNCOV
802
                // immediately, otherwise we can proceed as possibly the config
×
UNCOV
803
                // file doesn't exist which is OK.
×
UNCOV
804
                if lnutils.ErrorAs[*flags.IniError](err) ||
×
UNCOV
805
                        lnutils.ErrorAs[*flags.Error](err) {
×
806

×
807
                        return nil, err
×
808
                }
×
809

UNCOV
810
                configFileError = err
×
811
        }
812

813
        // Finally, parse the remaining command line options again to ensure
814
        // they take precedence.
UNCOV
815
        flagParser := flags.NewParser(&cfg, flags.Default)
×
UNCOV
816
        if _, err := flagParser.Parse(); err != nil {
×
817
                return nil, err
×
818
        }
×
819

820
        // Make sure everything we just loaded makes sense.
UNCOV
821
        cleanCfg, err := ValidateConfig(
×
UNCOV
822
                cfg, interceptor, fileParser, flagParser,
×
UNCOV
823
        )
×
UNCOV
824
        if usageErr, ok := err.(*usageError); ok {
×
825
                // The logging system might not yet be initialized, so we also
×
826
                // write to stderr to make sure the error appears somewhere.
×
827
                _, _ = fmt.Fprintln(os.Stderr, usageMessage)
×
828
                ltndLog.Warnf("Incorrect usage: %v", usageMessage)
×
829

×
830
                // The log subsystem might not yet be initialized. But we still
×
831
                // try to log the error there since some packaging solutions
×
832
                // might only look at the log and not stdout/stderr.
×
833
                ltndLog.Warnf("Error validating config: %v", usageErr.err)
×
834

×
835
                return nil, usageErr.err
×
836
        }
×
UNCOV
837
        if err != nil {
×
838
                // The log subsystem might not yet be initialized. But we still
×
839
                // try to log the error there since some packaging solutions
×
840
                // might only look at the log and not stdout/stderr.
×
841
                ltndLog.Warnf("Error validating config: %v", err)
×
842

×
843
                return nil, err
×
844
        }
×
845

846
        // Warn about missing config file only after all other configuration is
847
        // done. This prevents the warning on help messages and invalid options.
848
        // Note this should go directly before the return.
UNCOV
849
        if configFileError != nil {
×
UNCOV
850
                ltndLog.Warnf("%v", configFileError)
×
UNCOV
851
        }
×
852

853
        // Finally, log warnings for deprecated config options if they are set.
UNCOV
854
        logWarningsForDeprecation(*cleanCfg)
×
UNCOV
855

×
UNCOV
856
        return cleanCfg, nil
×
857
}
858

859
// usageError is an error type that signals a problem with the supplied flags.
860
type usageError struct {
861
        err error
862
}
863

864
// Error returns the error string.
865
//
866
// NOTE: This is part of the error interface.
867
func (u *usageError) Error() string {
×
868
        return u.err.Error()
×
869
}
×
870

871
// ValidateConfig check the given configuration to be sane. This makes sure no
872
// illegal values or combination of values are set. All file system paths are
873
// normalized. The cleaned up config is returned on success.
874
func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser,
UNCOV
875
        flagParser *flags.Parser) (*Config, error) {
×
UNCOV
876

×
UNCOV
877
        // If the provided lnd directory is not the default, we'll modify the
×
UNCOV
878
        // path to all of the files and directories that will live within it.
×
UNCOV
879
        lndDir := CleanAndExpandPath(cfg.LndDir)
×
UNCOV
880
        if lndDir != DefaultLndDir {
×
UNCOV
881
                cfg.DataDir = filepath.Join(lndDir, defaultDataDirname)
×
UNCOV
882
                cfg.LetsEncryptDir = filepath.Join(
×
UNCOV
883
                        lndDir, defaultLetsEncryptDirname,
×
UNCOV
884
                )
×
UNCOV
885
                cfg.TLSCertPath = filepath.Join(lndDir, defaultTLSCertFilename)
×
UNCOV
886
                cfg.TLSKeyPath = filepath.Join(lndDir, defaultTLSKeyFilename)
×
UNCOV
887
                cfg.LogDir = filepath.Join(lndDir, defaultLogDirname)
×
UNCOV
888

×
UNCOV
889
                // If the watchtower's directory is set to the default, i.e. the
×
UNCOV
890
                // user has not requested a different location, we'll move the
×
UNCOV
891
                // location to be relative to the specified lnd directory.
×
UNCOV
892
                if cfg.Watchtower.TowerDir == defaultTowerDir {
×
UNCOV
893
                        cfg.Watchtower.TowerDir = filepath.Join(
×
UNCOV
894
                                cfg.DataDir, defaultTowerSubDirname,
×
UNCOV
895
                        )
×
UNCOV
896
                }
×
897
        }
898

UNCOV
899
        funcName := "ValidateConfig"
×
UNCOV
900
        mkErr := func(format string, args ...interface{}) error {
×
901
                return fmt.Errorf(funcName+": "+format, args...)
×
902
        }
×
UNCOV
903
        makeDirectory := func(dir string) error {
×
UNCOV
904
                err := os.MkdirAll(dir, 0700)
×
UNCOV
905
                if err != nil {
×
906
                        // Show a nicer error message if it's because a symlink
×
907
                        // is linked to a directory that does not exist
×
908
                        // (probably because it's not mounted).
×
909
                        if e, ok := err.(*os.PathError); ok && os.IsExist(err) {
×
910
                                link, lerr := os.Readlink(e.Path)
×
911
                                if lerr == nil {
×
912
                                        str := "is symlink %s -> %s mounted?"
×
913
                                        err = fmt.Errorf(str, e.Path, link)
×
914
                                }
×
915
                        }
916

917
                        str := "Failed to create lnd directory '%s': %v"
×
918
                        return mkErr(str, dir, err)
×
919
                }
920

UNCOV
921
                return nil
×
922
        }
923

924
        // IsSet returns true if an option has been set in either the config
925
        // file or by a flag.
UNCOV
926
        isSet := func(field string) (bool, error) {
×
UNCOV
927
                fieldName, ok := reflect.TypeOf(Config{}).FieldByName(field)
×
UNCOV
928
                if !ok {
×
929
                        str := "could not find field %s"
×
930
                        return false, mkErr(str, field)
×
931
                }
×
932

UNCOV
933
                long, ok := fieldName.Tag.Lookup("long")
×
UNCOV
934
                if !ok {
×
935
                        str := "field %s does not have a long tag"
×
936
                        return false, mkErr(str, field)
×
937
                }
×
938

939
                // The user has the option to set the flag in either the config
940
                // file or as a command line flag. If any is set, we consider it
941
                // to be set, not applying any precedence rules here (since it
942
                // is a boolean the default is false anyway which would screw up
943
                // any precedence rules). Additionally, we need to also support
944
                // the use case where the config struct is embedded _within_
945
                // another struct with a prefix (as is the case with
946
                // lightning-terminal).
UNCOV
947
                fileOption := fileParser.FindOptionByLongName(long)
×
UNCOV
948
                fileOptionNested := fileParser.FindOptionByLongName(
×
UNCOV
949
                        "lnd." + long,
×
UNCOV
950
                )
×
UNCOV
951
                flagOption := flagParser.FindOptionByLongName(long)
×
UNCOV
952
                flagOptionNested := flagParser.FindOptionByLongName(
×
UNCOV
953
                        "lnd." + long,
×
UNCOV
954
                )
×
UNCOV
955

×
UNCOV
956
                return (fileOption != nil && fileOption.IsSet()) ||
×
UNCOV
957
                                (fileOptionNested != nil && fileOptionNested.IsSet()) ||
×
UNCOV
958
                                (flagOption != nil && flagOption.IsSet()) ||
×
UNCOV
959
                                (flagOptionNested != nil && flagOptionNested.IsSet()),
×
UNCOV
960
                        nil
×
961
        }
962

963
        // As soon as we're done parsing configuration options, ensure all paths
964
        // to directories and files are cleaned and expanded before attempting
965
        // to use them later on.
UNCOV
966
        cfg.DataDir = CleanAndExpandPath(cfg.DataDir)
×
UNCOV
967
        cfg.TLSCertPath = CleanAndExpandPath(cfg.TLSCertPath)
×
UNCOV
968
        cfg.TLSKeyPath = CleanAndExpandPath(cfg.TLSKeyPath)
×
UNCOV
969
        cfg.LetsEncryptDir = CleanAndExpandPath(cfg.LetsEncryptDir)
×
UNCOV
970
        cfg.AdminMacPath = CleanAndExpandPath(cfg.AdminMacPath)
×
UNCOV
971
        cfg.ReadMacPath = CleanAndExpandPath(cfg.ReadMacPath)
×
UNCOV
972
        cfg.InvoiceMacPath = CleanAndExpandPath(cfg.InvoiceMacPath)
×
UNCOV
973
        cfg.LogDir = CleanAndExpandPath(cfg.LogDir)
×
UNCOV
974
        cfg.BtcdMode.Dir = CleanAndExpandPath(cfg.BtcdMode.Dir)
×
UNCOV
975
        cfg.BitcoindMode.Dir = CleanAndExpandPath(cfg.BitcoindMode.Dir)
×
UNCOV
976
        cfg.BitcoindMode.ConfigPath = CleanAndExpandPath(
×
UNCOV
977
                cfg.BitcoindMode.ConfigPath,
×
UNCOV
978
        )
×
UNCOV
979
        cfg.BitcoindMode.RPCCookie = CleanAndExpandPath(cfg.BitcoindMode.RPCCookie)
×
UNCOV
980
        cfg.Tor.PrivateKeyPath = CleanAndExpandPath(cfg.Tor.PrivateKeyPath)
×
UNCOV
981
        cfg.Tor.WatchtowerKeyPath = CleanAndExpandPath(cfg.Tor.WatchtowerKeyPath)
×
UNCOV
982
        cfg.Watchtower.TowerDir = CleanAndExpandPath(cfg.Watchtower.TowerDir)
×
UNCOV
983
        cfg.BackupFilePath = CleanAndExpandPath(cfg.BackupFilePath)
×
UNCOV
984
        cfg.WalletUnlockPasswordFile = CleanAndExpandPath(
×
UNCOV
985
                cfg.WalletUnlockPasswordFile,
×
UNCOV
986
        )
×
UNCOV
987

×
UNCOV
988
        // Ensure that the user didn't attempt to specify negative values for
×
UNCOV
989
        // any of the autopilot params.
×
UNCOV
990
        if cfg.Autopilot.MaxChannels < 0 {
×
991
                str := "autopilot.maxchannels must be non-negative"
×
992

×
993
                return nil, mkErr(str)
×
994
        }
×
UNCOV
995
        if cfg.Autopilot.Allocation < 0 {
×
996
                str := "autopilot.allocation must be non-negative"
×
997

×
998
                return nil, mkErr(str)
×
999
        }
×
UNCOV
1000
        if cfg.Autopilot.MinChannelSize < 0 {
×
1001
                str := "autopilot.minchansize must be non-negative"
×
1002

×
1003
                return nil, mkErr(str)
×
1004
        }
×
UNCOV
1005
        if cfg.Autopilot.MaxChannelSize < 0 {
×
1006
                str := "autopilot.maxchansize must be non-negative"
×
1007

×
1008
                return nil, mkErr(str)
×
1009
        }
×
UNCOV
1010
        if cfg.Autopilot.MinConfs < 0 {
×
1011
                str := "autopilot.minconfs must be non-negative"
×
1012

×
1013
                return nil, mkErr(str)
×
1014
        }
×
UNCOV
1015
        if cfg.Autopilot.ConfTarget < 1 {
×
1016
                str := "autopilot.conftarget must be positive"
×
1017

×
1018
                return nil, mkErr(str)
×
1019
        }
×
1020

1021
        // Ensure that the specified values for the min and max channel size
1022
        // are within the bounds of the normal chan size constraints.
UNCOV
1023
        if cfg.Autopilot.MinChannelSize < int64(funding.MinChanFundingSize) {
×
1024
                cfg.Autopilot.MinChannelSize = int64(funding.MinChanFundingSize)
×
1025
        }
×
UNCOV
1026
        if cfg.Autopilot.MaxChannelSize > int64(MaxFundingAmount) {
×
1027
                cfg.Autopilot.MaxChannelSize = int64(MaxFundingAmount)
×
1028
        }
×
1029

UNCOV
1030
        if _, err := validateAtplCfg(cfg.Autopilot); err != nil {
×
1031
                return nil, mkErr("error validating autopilot: %v", err)
×
1032
        }
×
1033

1034
        // Ensure that --maxchansize is properly handled when set by user.
1035
        // For non-Wumbo channels this limit remains 16777215 satoshis by default
1036
        // as specified in BOLT-02. For wumbo channels this limit is 1,000,000,000.
1037
        // satoshis (10 BTC). Always enforce --maxchansize explicitly set by user.
1038
        // If unset (marked by 0 value), then enforce proper default.
UNCOV
1039
        if cfg.MaxChanSize == 0 {
×
UNCOV
1040
                if cfg.ProtocolOptions.Wumbo() {
×
UNCOV
1041
                        cfg.MaxChanSize = int64(funding.MaxBtcFundingAmountWumbo)
×
UNCOV
1042
                } else {
×
UNCOV
1043
                        cfg.MaxChanSize = int64(funding.MaxBtcFundingAmount)
×
UNCOV
1044
                }
×
1045
        }
1046

1047
        // Ensure that the user specified values for the min and max channel
1048
        // size make sense.
UNCOV
1049
        if cfg.MaxChanSize < cfg.MinChanSize {
×
1050
                return nil, mkErr("invalid channel size parameters: "+
×
1051
                        "max channel size %v, must be no less than min chan "+
×
1052
                        "size %v", cfg.MaxChanSize, cfg.MinChanSize,
×
1053
                )
×
1054
        }
×
1055

1056
        // Don't allow superfluous --maxchansize greater than
1057
        // BOLT 02 soft-limit for non-wumbo channel
UNCOV
1058
        if !cfg.ProtocolOptions.Wumbo() &&
×
UNCOV
1059
                cfg.MaxChanSize > int64(MaxFundingAmount) {
×
1060

×
1061
                return nil, mkErr("invalid channel size parameters: "+
×
1062
                        "maximum channel size %v is greater than maximum "+
×
1063
                        "non-wumbo channel size %v", cfg.MaxChanSize,
×
1064
                        MaxFundingAmount,
×
1065
                )
×
1066
        }
×
1067

1068
        // Ensure that the amount data for revoked commitment transactions is
1069
        // stored if the watchtower client is active.
UNCOV
1070
        if cfg.DB.NoRevLogAmtData && cfg.WtClient.Active {
×
1071
                return nil, mkErr("revocation log amount data must be stored " +
×
1072
                        "if the watchtower client is active")
×
1073
        }
×
1074

1075
        // Ensure a valid max channel fee allocation was set.
UNCOV
1076
        if cfg.MaxChannelFeeAllocation <= 0 || cfg.MaxChannelFeeAllocation > 1 {
×
1077
                return nil, mkErr("invalid max channel fee allocation: %v, "+
×
1078
                        "must be within (0, 1]", cfg.MaxChannelFeeAllocation)
×
1079
        }
×
1080

UNCOV
1081
        if cfg.MaxCommitFeeRateAnchors < 1 {
×
1082
                return nil, mkErr("invalid max commit fee rate anchors: %v, "+
×
1083
                        "must be at least 1 sat/vByte",
×
1084
                        cfg.MaxCommitFeeRateAnchors)
×
1085
        }
×
1086

1087
        // Validate the Tor config parameters.
UNCOV
1088
        socks, err := lncfg.ParseAddressString(
×
UNCOV
1089
                cfg.Tor.SOCKS, strconv.Itoa(defaultTorSOCKSPort),
×
UNCOV
1090
                cfg.net.ResolveTCPAddr,
×
UNCOV
1091
        )
×
UNCOV
1092
        if err != nil {
×
1093
                return nil, err
×
1094
        }
×
UNCOV
1095
        cfg.Tor.SOCKS = socks.String()
×
UNCOV
1096

×
UNCOV
1097
        // We'll only attempt to normalize and resolve the DNS host if it hasn't
×
UNCOV
1098
        // changed, as it doesn't need to be done for the default.
×
UNCOV
1099
        if cfg.Tor.DNS != defaultTorDNS {
×
1100
                dns, err := lncfg.ParseAddressString(
×
1101
                        cfg.Tor.DNS, strconv.Itoa(defaultTorDNSPort),
×
1102
                        cfg.net.ResolveTCPAddr,
×
1103
                )
×
1104
                if err != nil {
×
1105
                        return nil, mkErr("error parsing tor dns: %v", err)
×
1106
                }
×
1107
                cfg.Tor.DNS = dns.String()
×
1108
        }
1109

UNCOV
1110
        control, err := lncfg.ParseAddressString(
×
UNCOV
1111
                cfg.Tor.Control, strconv.Itoa(defaultTorControlPort),
×
UNCOV
1112
                cfg.net.ResolveTCPAddr,
×
UNCOV
1113
        )
×
UNCOV
1114
        if err != nil {
×
1115
                return nil, mkErr("error parsing tor control address: %v", err)
×
1116
        }
×
UNCOV
1117
        cfg.Tor.Control = control.String()
×
UNCOV
1118

×
UNCOV
1119
        // Ensure that tor socks host:port is not equal to tor control
×
UNCOV
1120
        // host:port. This would lead to lnd not starting up properly.
×
UNCOV
1121
        if cfg.Tor.SOCKS == cfg.Tor.Control {
×
1122
                str := "tor.socks and tor.control can not us the same host:port"
×
1123

×
1124
                return nil, mkErr(str)
×
1125
        }
×
1126

UNCOV
1127
        switch {
×
1128
        case cfg.Tor.V2 && cfg.Tor.V3:
×
1129
                return nil, mkErr("either tor.v2 or tor.v3 can be set, " +
×
1130
                        "but not both")
×
1131
        case cfg.DisableListen && (cfg.Tor.V2 || cfg.Tor.V3):
×
1132
                return nil, mkErr("listening must be enabled when enabling " +
×
1133
                        "inbound connections over Tor")
×
1134
        }
1135

UNCOV
1136
        if cfg.Tor.PrivateKeyPath == "" {
×
UNCOV
1137
                switch {
×
1138
                case cfg.Tor.V2:
×
1139
                        cfg.Tor.PrivateKeyPath = filepath.Join(
×
1140
                                lndDir, defaultTorV2PrivateKeyFilename,
×
1141
                        )
×
1142
                case cfg.Tor.V3:
×
1143
                        cfg.Tor.PrivateKeyPath = filepath.Join(
×
1144
                                lndDir, defaultTorV3PrivateKeyFilename,
×
1145
                        )
×
1146
                }
1147
        }
1148

UNCOV
1149
        if cfg.Tor.WatchtowerKeyPath == "" {
×
UNCOV
1150
                switch {
×
1151
                case cfg.Tor.V2:
×
1152
                        cfg.Tor.WatchtowerKeyPath = filepath.Join(
×
1153
                                cfg.Watchtower.TowerDir,
×
1154
                                defaultTorV2PrivateKeyFilename,
×
1155
                        )
×
1156
                case cfg.Tor.V3:
×
1157
                        cfg.Tor.WatchtowerKeyPath = filepath.Join(
×
1158
                                cfg.Watchtower.TowerDir,
×
1159
                                defaultTorV3PrivateKeyFilename,
×
1160
                        )
×
1161
                }
1162
        }
1163

1164
        // Set up the network-related functions that will be used throughout
1165
        // the daemon. We use the standard Go "net" package functions by
1166
        // default. If we should be proxying all traffic through Tor, then
1167
        // we'll use the Tor proxy specific functions in order to avoid leaking
1168
        // our real information.
UNCOV
1169
        if cfg.Tor.Active {
×
1170
                cfg.net = &tor.ProxyNet{
×
1171
                        SOCKS:                       cfg.Tor.SOCKS,
×
1172
                        DNS:                         cfg.Tor.DNS,
×
1173
                        StreamIsolation:             cfg.Tor.StreamIsolation,
×
1174
                        SkipProxyForClearNetTargets: cfg.Tor.SkipProxyForClearNetTargets,
×
1175
                }
×
1176
        }
×
1177

UNCOV
1178
        if cfg.DisableListen && cfg.NAT {
×
1179
                return nil, mkErr("NAT traversal cannot be used when " +
×
1180
                        "listening is disabled")
×
1181
        }
×
UNCOV
1182
        if cfg.NAT && len(cfg.ExternalHosts) != 0 {
×
1183
                return nil, mkErr("NAT support and externalhosts are " +
×
1184
                        "mutually exclusive, only one should be selected")
×
1185
        }
×
1186

1187
        // Multiple networks can't be selected simultaneously.  Count
1188
        // number of network flags passed; assign active network params
1189
        // while we're at it.
UNCOV
1190
        numNets := 0
×
UNCOV
1191
        if cfg.Bitcoin.MainNet {
×
1192
                numNets++
×
1193
                cfg.ActiveNetParams = chainreg.BitcoinMainNetParams
×
1194
        }
×
UNCOV
1195
        if cfg.Bitcoin.TestNet3 {
×
1196
                numNets++
×
1197
                cfg.ActiveNetParams = chainreg.BitcoinTestNetParams
×
1198
        }
×
UNCOV
1199
        if cfg.Bitcoin.RegTest {
×
UNCOV
1200
                numNets++
×
UNCOV
1201
                cfg.ActiveNetParams = chainreg.BitcoinRegTestNetParams
×
UNCOV
1202
        }
×
UNCOV
1203
        if cfg.Bitcoin.SimNet {
×
1204
                numNets++
×
1205
                cfg.ActiveNetParams = chainreg.BitcoinSimNetParams
×
1206

×
1207
                // For simnet, the btcsuite chain params uses a
×
1208
                // cointype of 115. However, we override this in
×
1209
                // chainreg/chainparams.go, but the raw ChainParam
×
1210
                // field is used elsewhere. To ensure everything is
×
1211
                // consistent, we'll also override the cointype within
×
1212
                // the raw params.
×
1213
                targetCoinType := chainreg.BitcoinSigNetParams.CoinType
×
1214
                cfg.ActiveNetParams.Params.HDCoinType = targetCoinType
×
1215
        }
×
UNCOV
1216
        if cfg.Bitcoin.SigNet {
×
1217
                numNets++
×
1218
                cfg.ActiveNetParams = chainreg.BitcoinSigNetParams
×
1219

×
1220
                // Let the user overwrite the default signet parameters.
×
1221
                // The challenge defines the actual signet network to
×
1222
                // join and the seed nodes are needed for network
×
1223
                // discovery.
×
1224
                sigNetChallenge := chaincfg.DefaultSignetChallenge
×
1225
                sigNetSeeds := chaincfg.DefaultSignetDNSSeeds
×
1226
                if cfg.Bitcoin.SigNetChallenge != "" {
×
1227
                        challenge, err := hex.DecodeString(
×
1228
                                cfg.Bitcoin.SigNetChallenge,
×
1229
                        )
×
1230
                        if err != nil {
×
1231
                                return nil, mkErr("Invalid "+
×
1232
                                        "signet challenge, hex decode "+
×
1233
                                        "failed: %v", err)
×
1234
                        }
×
1235
                        sigNetChallenge = challenge
×
1236
                }
1237

1238
                if len(cfg.Bitcoin.SigNetSeedNode) > 0 {
×
1239
                        sigNetSeeds = make([]chaincfg.DNSSeed, len(
×
1240
                                cfg.Bitcoin.SigNetSeedNode,
×
1241
                        ))
×
1242
                        for idx, seed := range cfg.Bitcoin.SigNetSeedNode {
×
1243
                                sigNetSeeds[idx] = chaincfg.DNSSeed{
×
1244
                                        Host:         seed,
×
1245
                                        HasFiltering: false,
×
1246
                                }
×
1247
                        }
×
1248
                }
1249

1250
                chainParams := chaincfg.CustomSignetParams(
×
1251
                        sigNetChallenge, sigNetSeeds,
×
1252
                )
×
1253
                cfg.ActiveNetParams.Params = &chainParams
×
1254
        }
UNCOV
1255
        if numNets > 1 {
×
1256
                str := "The mainnet, testnet, regtest, simnet and signet " +
×
1257
                        "params can't be used together -- choose one " +
×
1258
                        "of the five"
×
1259

×
1260
                return nil, mkErr(str)
×
1261
        }
×
1262

1263
        // The target network must be provided, otherwise, we won't
1264
        // know how to initialize the daemon.
UNCOV
1265
        if numNets == 0 {
×
1266
                str := "either --bitcoin.mainnet, or bitcoin.testnet," +
×
1267
                        "bitcoin.simnet, bitcoin.regtest or bitcoin.signet " +
×
1268
                        "must be specified"
×
1269

×
1270
                return nil, mkErr(str)
×
1271
        }
×
1272

UNCOV
1273
        err = cfg.Bitcoin.Validate(minTimeLockDelta, funding.MinBtcRemoteDelay)
×
UNCOV
1274
        if err != nil {
×
1275
                return nil, mkErr("error validating bitcoin params: %v", err)
×
1276
        }
×
1277

UNCOV
1278
        switch cfg.Bitcoin.Node {
×
UNCOV
1279
        case btcdBackendName:
×
UNCOV
1280
                err := parseRPCParams(
×
UNCOV
1281
                        cfg.Bitcoin, cfg.BtcdMode, cfg.ActiveNetParams,
×
UNCOV
1282
                )
×
UNCOV
1283
                if err != nil {
×
1284
                        return nil, mkErr("unable to load RPC "+
×
1285
                                "credentials for btcd: %v", err)
×
1286
                }
×
UNCOV
1287
        case bitcoindBackendName:
×
UNCOV
1288
                if cfg.Bitcoin.SimNet {
×
1289
                        return nil, mkErr("bitcoind does not " +
×
1290
                                "support simnet")
×
1291
                }
×
1292

UNCOV
1293
                err := parseRPCParams(
×
UNCOV
1294
                        cfg.Bitcoin, cfg.BitcoindMode, cfg.ActiveNetParams,
×
UNCOV
1295
                )
×
UNCOV
1296
                if err != nil {
×
1297
                        return nil, mkErr("unable to load RPC "+
×
1298
                                "credentials for bitcoind: %v", err)
×
1299
                }
×
UNCOV
1300
        case neutrinoBackendName:
×
1301
                // No need to get RPC parameters.
1302

1303
        case "nochainbackend":
×
1304
                // Nothing to configure, we're running without any chain
1305
                // backend whatsoever (pure signing mode).
1306

1307
        default:
×
1308
                str := "only btcd, bitcoind, and neutrino mode " +
×
1309
                        "supported for bitcoin at this time"
×
1310

×
1311
                return nil, mkErr(str)
×
1312
        }
1313

UNCOV
1314
        cfg.Bitcoin.ChainDir = filepath.Join(
×
UNCOV
1315
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
×
UNCOV
1316
        )
×
UNCOV
1317

×
UNCOV
1318
        // Ensure that the user didn't attempt to specify negative values for
×
UNCOV
1319
        // any of the autopilot params.
×
UNCOV
1320
        if cfg.Autopilot.MaxChannels < 0 {
×
1321
                str := "autopilot.maxchannels must be non-negative"
×
1322

×
1323
                return nil, mkErr(str)
×
1324
        }
×
UNCOV
1325
        if cfg.Autopilot.Allocation < 0 {
×
1326
                str := "autopilot.allocation must be non-negative"
×
1327

×
1328
                return nil, mkErr(str)
×
1329
        }
×
UNCOV
1330
        if cfg.Autopilot.MinChannelSize < 0 {
×
1331
                str := "autopilot.minchansize must be non-negative"
×
1332

×
1333
                return nil, mkErr(str)
×
1334
        }
×
UNCOV
1335
        if cfg.Autopilot.MaxChannelSize < 0 {
×
1336
                str := "autopilot.maxchansize must be non-negative"
×
1337

×
1338
                return nil, mkErr(str)
×
1339
        }
×
1340

1341
        // Ensure that the specified values for the min and max channel size
1342
        // don't are within the bounds of the normal chan size constraints.
UNCOV
1343
        if cfg.Autopilot.MinChannelSize < int64(funding.MinChanFundingSize) {
×
1344
                cfg.Autopilot.MinChannelSize = int64(funding.MinChanFundingSize)
×
1345
        }
×
UNCOV
1346
        if cfg.Autopilot.MaxChannelSize > int64(MaxFundingAmount) {
×
1347
                cfg.Autopilot.MaxChannelSize = int64(MaxFundingAmount)
×
1348
        }
×
1349

1350
        // Validate profile port or host:port.
UNCOV
1351
        if cfg.Profile != "" {
×
UNCOV
1352
                str := "%s: The profile port must be between 1024 and 65535"
×
UNCOV
1353

×
UNCOV
1354
                // Try to parse Profile as a host:port.
×
UNCOV
1355
                _, hostPort, err := net.SplitHostPort(cfg.Profile)
×
UNCOV
1356
                if err == nil {
×
1357
                        // Determine if the port is valid.
×
1358
                        profilePort, err := strconv.Atoi(hostPort)
×
1359
                        if err != nil || profilePort < 1024 || profilePort > 65535 {
×
1360
                                return nil, &usageError{mkErr(str)}
×
1361
                        }
×
UNCOV
1362
                } else {
×
UNCOV
1363
                        // Try to parse Profile as a port.
×
UNCOV
1364
                        profilePort, err := strconv.Atoi(cfg.Profile)
×
UNCOV
1365
                        if err != nil || profilePort < 1024 || profilePort > 65535 {
×
1366
                                return nil, &usageError{mkErr(str)}
×
1367
                        }
×
1368

1369
                        // Since the user just set a port, we will serve debugging
1370
                        // information over localhost.
UNCOV
1371
                        cfg.Profile = net.JoinHostPort("127.0.0.1", cfg.Profile)
×
1372
                }
1373
        }
1374

1375
        // We'll now construct the network directory which will be where we
1376
        // store all the data specific to this chain/network.
UNCOV
1377
        cfg.networkDir = filepath.Join(
×
UNCOV
1378
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
×
UNCOV
1379
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
×
UNCOV
1380
        )
×
UNCOV
1381

×
UNCOV
1382
        // If a custom macaroon directory wasn't specified and the data
×
UNCOV
1383
        // directory has changed from the default path, then we'll also update
×
UNCOV
1384
        // the path for the macaroons to be generated.
×
UNCOV
1385
        if cfg.AdminMacPath == "" {
×
1386
                cfg.AdminMacPath = filepath.Join(
×
1387
                        cfg.networkDir, defaultAdminMacFilename,
×
1388
                )
×
1389
        }
×
UNCOV
1390
        if cfg.ReadMacPath == "" {
×
1391
                cfg.ReadMacPath = filepath.Join(
×
1392
                        cfg.networkDir, defaultReadMacFilename,
×
1393
                )
×
1394
        }
×
UNCOV
1395
        if cfg.InvoiceMacPath == "" {
×
1396
                cfg.InvoiceMacPath = filepath.Join(
×
1397
                        cfg.networkDir, defaultInvoiceMacFilename,
×
1398
                )
×
1399
        }
×
1400

UNCOV
1401
        towerDir := filepath.Join(
×
UNCOV
1402
                cfg.Watchtower.TowerDir, BitcoinChainName,
×
UNCOV
1403
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
×
UNCOV
1404
        )
×
UNCOV
1405

×
UNCOV
1406
        // Create the lnd directory and all other sub-directories if they don't
×
UNCOV
1407
        // already exist. This makes sure that directory trees are also created
×
UNCOV
1408
        // for files that point to outside the lnddir.
×
UNCOV
1409
        dirs := []string{
×
UNCOV
1410
                lndDir, cfg.DataDir, cfg.networkDir,
×
UNCOV
1411
                cfg.LetsEncryptDir, towerDir, cfg.graphDatabaseDir(),
×
UNCOV
1412
                filepath.Dir(cfg.TLSCertPath), filepath.Dir(cfg.TLSKeyPath),
×
UNCOV
1413
                filepath.Dir(cfg.AdminMacPath), filepath.Dir(cfg.ReadMacPath),
×
UNCOV
1414
                filepath.Dir(cfg.InvoiceMacPath),
×
UNCOV
1415
                filepath.Dir(cfg.Tor.PrivateKeyPath),
×
UNCOV
1416
                filepath.Dir(cfg.Tor.WatchtowerKeyPath),
×
UNCOV
1417
        }
×
UNCOV
1418
        for _, dir := range dirs {
×
UNCOV
1419
                if err := makeDirectory(dir); err != nil {
×
1420
                        return nil, err
×
1421
                }
×
1422
        }
1423

1424
        // Similarly, if a custom back up file path wasn't specified, then
1425
        // we'll update the file location to match our set network directory.
UNCOV
1426
        if cfg.BackupFilePath == "" {
×
UNCOV
1427
                cfg.BackupFilePath = filepath.Join(
×
UNCOV
1428
                        cfg.networkDir, chanbackup.DefaultBackupFileName,
×
UNCOV
1429
                )
×
UNCOV
1430
        }
×
1431

1432
        // Append the network type to the log directory so it is "namespaced"
1433
        // per network in the same fashion as the data directory.
UNCOV
1434
        cfg.LogDir = filepath.Join(
×
UNCOV
1435
                cfg.LogDir, BitcoinChainName,
×
UNCOV
1436
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
×
UNCOV
1437
        )
×
UNCOV
1438

×
UNCOV
1439
        // A log writer must be passed in, otherwise we can't function and would
×
UNCOV
1440
        // run into a panic later on.
×
UNCOV
1441
        if cfg.LogWriter == nil {
×
1442
                return nil, mkErr("log writer missing in config")
×
1443
        }
×
1444

1445
        // Special show command to list supported subsystems and exit.
UNCOV
1446
        if cfg.DebugLevel == "show" {
×
1447
                fmt.Println("Supported subsystems",
×
1448
                        cfg.LogWriter.SupportedSubsystems())
×
1449
                os.Exit(0)
×
1450
        }
×
1451

UNCOV
1452
        if !build.SuportedLogCompressor(cfg.LogCompressor) {
×
1453
                return nil, mkErr("invalid log compressor: %v",
×
1454
                        cfg.LogCompressor)
×
1455
        }
×
1456

1457
        // Initialize logging at the default logging level.
UNCOV
1458
        SetupLoggers(cfg.LogWriter, interceptor)
×
UNCOV
1459
        err = cfg.LogWriter.InitLogRotator(
×
UNCOV
1460
                filepath.Join(cfg.LogDir, defaultLogFilename),
×
UNCOV
1461
                cfg.LogCompressor, cfg.MaxLogFileSize, cfg.MaxLogFiles,
×
UNCOV
1462
        )
×
UNCOV
1463
        if err != nil {
×
1464
                str := "log rotation setup failed: %v"
×
1465
                return nil, mkErr(str, err)
×
1466
        }
×
1467

1468
        // Parse, validate, and set debug log level(s).
UNCOV
1469
        err = build.ParseAndSetDebugLevels(cfg.DebugLevel, cfg.LogWriter)
×
UNCOV
1470
        if err != nil {
×
1471
                str := "error parsing debug level: %v"
×
1472
                return nil, &usageError{mkErr(str, err)}
×
1473
        }
×
1474

1475
        // At least one RPCListener is required. So listen on localhost per
1476
        // default.
UNCOV
1477
        if len(cfg.RawRPCListeners) == 0 {
×
1478
                addr := fmt.Sprintf("localhost:%d", defaultRPCPort)
×
1479
                cfg.RawRPCListeners = append(cfg.RawRPCListeners, addr)
×
1480
        }
×
1481

1482
        // Listen on localhost if no REST listeners were specified.
UNCOV
1483
        if len(cfg.RawRESTListeners) == 0 {
×
1484
                addr := fmt.Sprintf("localhost:%d", defaultRESTPort)
×
1485
                cfg.RawRESTListeners = append(cfg.RawRESTListeners, addr)
×
1486
        }
×
1487

1488
        // Listen on the default interface/port if no listeners were specified.
1489
        // An empty address string means default interface/address, which on
1490
        // most unix systems is the same as 0.0.0.0. If Tor is active, we
1491
        // default to only listening on localhost for hidden service
1492
        // connections.
UNCOV
1493
        if len(cfg.RawListeners) == 0 {
×
1494
                addr := fmt.Sprintf(":%d", defaultPeerPort)
×
1495
                if cfg.Tor.Active && !cfg.Tor.SkipProxyForClearNetTargets {
×
1496
                        addr = fmt.Sprintf("localhost:%d", defaultPeerPort)
×
1497
                }
×
1498
                cfg.RawListeners = append(cfg.RawListeners, addr)
×
1499
        }
1500

1501
        // Add default port to all RPC listener addresses if needed and remove
1502
        // duplicate addresses.
UNCOV
1503
        cfg.RPCListeners, err = lncfg.NormalizeAddresses(
×
UNCOV
1504
                cfg.RawRPCListeners, strconv.Itoa(defaultRPCPort),
×
UNCOV
1505
                cfg.net.ResolveTCPAddr,
×
UNCOV
1506
        )
×
UNCOV
1507
        if err != nil {
×
1508
                return nil, mkErr("error normalizing RPC listen addrs: %v", err)
×
1509
        }
×
1510

1511
        // Add default port to all REST listener addresses if needed and remove
1512
        // duplicate addresses.
UNCOV
1513
        cfg.RESTListeners, err = lncfg.NormalizeAddresses(
×
UNCOV
1514
                cfg.RawRESTListeners, strconv.Itoa(defaultRESTPort),
×
UNCOV
1515
                cfg.net.ResolveTCPAddr,
×
UNCOV
1516
        )
×
UNCOV
1517
        if err != nil {
×
1518
                return nil, mkErr("error normalizing REST listen addrs: %v", err)
×
1519
        }
×
1520

UNCOV
1521
        switch {
×
1522
        // The no seed backup and auto unlock are mutually exclusive.
1523
        case cfg.NoSeedBackup && cfg.WalletUnlockPasswordFile != "":
×
1524
                return nil, mkErr("cannot set noseedbackup and " +
×
1525
                        "wallet-unlock-password-file at the same time")
×
1526

1527
        // The "allow-create" flag cannot be set without the auto unlock file.
1528
        case cfg.WalletUnlockAllowCreate && cfg.WalletUnlockPasswordFile == "":
×
1529
                return nil, mkErr("cannot set wallet-unlock-allow-create " +
×
1530
                        "without wallet-unlock-password-file")
×
1531

1532
        // If a password file was specified, we need it to exist.
1533
        case cfg.WalletUnlockPasswordFile != "" &&
1534
                !lnrpc.FileExists(cfg.WalletUnlockPasswordFile):
×
1535

×
1536
                return nil, mkErr("wallet unlock password file %s does "+
×
1537
                        "not exist", cfg.WalletUnlockPasswordFile)
×
1538
        }
1539

1540
        // For each of the RPC listeners (REST+gRPC), we'll ensure that users
1541
        // have specified a safe combo for authentication. If not, we'll bail
1542
        // out with an error. Since we don't allow disabling TLS for gRPC
1543
        // connections we pass in tlsActive=true.
UNCOV
1544
        err = lncfg.EnforceSafeAuthentication(
×
UNCOV
1545
                cfg.RPCListeners, !cfg.NoMacaroons, true,
×
UNCOV
1546
        )
×
UNCOV
1547
        if err != nil {
×
1548
                return nil, mkErr("error enforcing safe authentication on "+
×
1549
                        "RPC ports: %v", err)
×
1550
        }
×
1551

UNCOV
1552
        if cfg.DisableRest {
×
1553
                ltndLog.Infof("REST API is disabled!")
×
1554
                cfg.RESTListeners = nil
×
UNCOV
1555
        } else {
×
UNCOV
1556
                err = lncfg.EnforceSafeAuthentication(
×
UNCOV
1557
                        cfg.RESTListeners, !cfg.NoMacaroons, !cfg.DisableRestTLS,
×
UNCOV
1558
                )
×
UNCOV
1559
                if err != nil {
×
1560
                        return nil, mkErr("error enforcing safe "+
×
1561
                                "authentication on REST ports: %v", err)
×
1562
                }
×
1563
        }
1564

1565
        // Remove the listening addresses specified if listening is disabled.
UNCOV
1566
        if cfg.DisableListen {
×
UNCOV
1567
                ltndLog.Infof("Listening on the p2p interface is disabled!")
×
UNCOV
1568
                cfg.Listeners = nil
×
UNCOV
1569
                cfg.ExternalIPs = nil
×
UNCOV
1570
        } else {
×
UNCOV
1571

×
UNCOV
1572
                // Add default port to all listener addresses if needed and remove
×
UNCOV
1573
                // duplicate addresses.
×
UNCOV
1574
                cfg.Listeners, err = lncfg.NormalizeAddresses(
×
UNCOV
1575
                        cfg.RawListeners, strconv.Itoa(defaultPeerPort),
×
UNCOV
1576
                        cfg.net.ResolveTCPAddr,
×
UNCOV
1577
                )
×
UNCOV
1578
                if err != nil {
×
1579
                        return nil, mkErr("error normalizing p2p listen "+
×
1580
                                "addrs: %v", err)
×
1581
                }
×
1582

1583
                // Add default port to all external IP addresses if needed and remove
1584
                // duplicate addresses.
UNCOV
1585
                cfg.ExternalIPs, err = lncfg.NormalizeAddresses(
×
UNCOV
1586
                        cfg.RawExternalIPs, strconv.Itoa(defaultPeerPort),
×
UNCOV
1587
                        cfg.net.ResolveTCPAddr,
×
UNCOV
1588
                )
×
UNCOV
1589
                if err != nil {
×
1590
                        return nil, err
×
1591
                }
×
1592

1593
                // For the p2p port it makes no sense to listen to an Unix socket.
1594
                // Also, we would need to refactor the brontide listener to support
1595
                // that.
UNCOV
1596
                for _, p2pListener := range cfg.Listeners {
×
UNCOV
1597
                        if lncfg.IsUnix(p2pListener) {
×
1598
                                return nil, mkErr("unix socket addresses "+
×
1599
                                        "cannot be used for the p2p "+
×
1600
                                        "connection listener: %s", p2pListener)
×
1601
                        }
×
1602
                }
1603
        }
1604

1605
        // Ensure that the specified minimum backoff is below or equal to the
1606
        // maximum backoff.
UNCOV
1607
        if cfg.MinBackoff > cfg.MaxBackoff {
×
1608
                return nil, mkErr("maxbackoff must be greater than minbackoff")
×
1609
        }
×
1610

1611
        // Newer versions of lnd added a new sub-config for bolt-specific
1612
        // parameters. However, we want to also allow existing users to use the
1613
        // value on the top-level config. If the outer config value is set,
1614
        // then we'll use that directly.
UNCOV
1615
        flagSet, err := isSet("SyncFreelist")
×
UNCOV
1616
        if err != nil {
×
1617
                return nil, mkErr("error parsing freelist sync flag: %v", err)
×
1618
        }
×
UNCOV
1619
        if flagSet {
×
1620
                cfg.DB.Bolt.NoFreelistSync = !cfg.SyncFreelist
×
1621
        }
×
1622

1623
        // Parse any extra sqlite pragma options that may have been provided
1624
        // to determine if they override any of the defaults that we will
1625
        // otherwise add.
UNCOV
1626
        var (
×
UNCOV
1627
                defaultSynchronous = true
×
UNCOV
1628
                defaultAutoVacuum  = true
×
UNCOV
1629
                defaultFullfsync   = true
×
UNCOV
1630
        )
×
UNCOV
1631
        for _, option := range cfg.DB.Sqlite.PragmaOptions {
×
1632
                switch {
×
1633
                case strings.HasPrefix(option, "synchronous="):
×
1634
                        defaultSynchronous = false
×
1635

1636
                case strings.HasPrefix(option, "auto_vacuum="):
×
1637
                        defaultAutoVacuum = false
×
1638

1639
                case strings.HasPrefix(option, "fullfsync="):
×
1640
                        defaultFullfsync = false
×
1641

1642
                default:
×
1643
                }
1644
        }
1645

UNCOV
1646
        if defaultSynchronous {
×
UNCOV
1647
                cfg.DB.Sqlite.PragmaOptions = append(
×
UNCOV
1648
                        cfg.DB.Sqlite.PragmaOptions, "synchronous=full",
×
UNCOV
1649
                )
×
UNCOV
1650
        }
×
1651

UNCOV
1652
        if defaultAutoVacuum {
×
UNCOV
1653
                cfg.DB.Sqlite.PragmaOptions = append(
×
UNCOV
1654
                        cfg.DB.Sqlite.PragmaOptions, "auto_vacuum=incremental",
×
UNCOV
1655
                )
×
UNCOV
1656
        }
×
1657

UNCOV
1658
        if defaultFullfsync {
×
UNCOV
1659
                cfg.DB.Sqlite.PragmaOptions = append(
×
UNCOV
1660
                        cfg.DB.Sqlite.PragmaOptions, "fullfsync=true",
×
UNCOV
1661
                )
×
UNCOV
1662
        }
×
1663

1664
        // Ensure that the user hasn't chosen a remote-max-htlc value greater
1665
        // than the protocol maximum.
UNCOV
1666
        maxRemoteHtlcs := uint16(input.MaxHTLCNumber / 2)
×
UNCOV
1667
        if cfg.DefaultRemoteMaxHtlcs > maxRemoteHtlcs {
×
1668
                return nil, mkErr("default-remote-max-htlcs (%v) must be "+
×
1669
                        "less than %v", cfg.DefaultRemoteMaxHtlcs,
×
1670
                        maxRemoteHtlcs)
×
1671
        }
×
1672

1673
        // Clamp the ChannelCommitInterval so that commitment updates can still
1674
        // happen in a reasonable timeframe.
UNCOV
1675
        if cfg.ChannelCommitInterval > maxChannelCommitInterval {
×
1676
                return nil, mkErr("channel-commit-interval (%v) must be less "+
×
1677
                        "than %v", cfg.ChannelCommitInterval,
×
1678
                        maxChannelCommitInterval)
×
1679
        }
×
1680

1681
        // Limit PendingCommitInterval so we don't wait too long for the remote
1682
        // party to send back a revoke.
UNCOV
1683
        if cfg.PendingCommitInterval > maxPendingCommitInterval {
×
1684
                return nil, mkErr("pending-commit-interval (%v) must be less "+
×
1685
                        "than %v", cfg.PendingCommitInterval,
×
1686
                        maxPendingCommitInterval)
×
1687
        }
×
1688

UNCOV
1689
        if err := cfg.Gossip.Parse(); err != nil {
×
1690
                return nil, mkErr("error parsing gossip syncer: %v", err)
×
1691
        }
×
1692

1693
        // If the experimental protocol options specify any protocol messages
1694
        // that we want to handle as custom messages, set them now.
UNCOV
1695
        customMsg := cfg.ProtocolOptions.CustomMessageOverrides()
×
UNCOV
1696

×
UNCOV
1697
        // We can safely set our custom override values during startup because
×
UNCOV
1698
        // startup is blocked on config parsing.
×
UNCOV
1699
        if err := lnwire.SetCustomOverrides(customMsg); err != nil {
×
1700
                return nil, mkErr("custom-message: %v", err)
×
1701
        }
×
1702

1703
        // Validate the subconfigs for workers, caches, and the tower client.
UNCOV
1704
        err = lncfg.Validate(
×
UNCOV
1705
                cfg.Workers,
×
UNCOV
1706
                cfg.Caches,
×
UNCOV
1707
                cfg.WtClient,
×
UNCOV
1708
                cfg.DB,
×
UNCOV
1709
                cfg.Cluster,
×
UNCOV
1710
                cfg.HealthChecks,
×
UNCOV
1711
                cfg.RPCMiddleware,
×
UNCOV
1712
                cfg.RemoteSigner,
×
UNCOV
1713
                cfg.Sweeper,
×
UNCOV
1714
                cfg.Htlcswitch,
×
UNCOV
1715
                cfg.Invoices,
×
UNCOV
1716
                cfg.Routing,
×
UNCOV
1717
        )
×
UNCOV
1718
        if err != nil {
×
1719
                return nil, err
×
1720
        }
×
1721

1722
        // Finally, ensure that the user's color is correctly formatted,
1723
        // otherwise the server will not be able to start after the unlocking
1724
        // the wallet.
UNCOV
1725
        _, err = lncfg.ParseHexColor(cfg.Color)
×
UNCOV
1726
        if err != nil {
×
1727
                return nil, mkErr("unable to parse node color: %v", err)
×
1728
        }
×
1729

1730
        // All good, return the sanitized result.
UNCOV
1731
        return &cfg, nil
×
1732
}
1733

1734
// graphDatabaseDir returns the default directory where the local bolt graph db
1735
// files are stored.
UNCOV
1736
func (c *Config) graphDatabaseDir() string {
×
UNCOV
1737
        return filepath.Join(
×
UNCOV
1738
                c.DataDir, defaultGraphSubDirname,
×
UNCOV
1739
                lncfg.NormalizeNetwork(c.ActiveNetParams.Name),
×
UNCOV
1740
        )
×
UNCOV
1741
}
×
1742

1743
// ImplementationConfig returns the configuration of what actual implementations
1744
// should be used when creating the main lnd instance.
1745
func (c *Config) ImplementationConfig(
UNCOV
1746
        interceptor signal.Interceptor) *ImplementationCfg {
×
UNCOV
1747

×
UNCOV
1748
        // If we're using a remote signer, we still need the base wallet as a
×
UNCOV
1749
        // watch-only source of chain and address data. But we don't need any
×
UNCOV
1750
        // private key material in that btcwallet base wallet.
×
UNCOV
1751
        if c.RemoteSigner.Enable {
×
UNCOV
1752
                rpcImpl := NewRPCSignerWalletImpl(
×
UNCOV
1753
                        c, ltndLog, interceptor,
×
UNCOV
1754
                        c.RemoteSigner.MigrateWatchOnly,
×
UNCOV
1755
                )
×
UNCOV
1756
                return &ImplementationCfg{
×
UNCOV
1757
                        GrpcRegistrar:     rpcImpl,
×
UNCOV
1758
                        RestRegistrar:     rpcImpl,
×
UNCOV
1759
                        ExternalValidator: rpcImpl,
×
UNCOV
1760
                        DatabaseBuilder: NewDefaultDatabaseBuilder(
×
UNCOV
1761
                                c, ltndLog,
×
UNCOV
1762
                        ),
×
UNCOV
1763
                        WalletConfigBuilder: rpcImpl,
×
UNCOV
1764
                        ChainControlBuilder: rpcImpl,
×
UNCOV
1765
                }
×
UNCOV
1766
        }
×
1767

UNCOV
1768
        defaultImpl := NewDefaultWalletImpl(c, ltndLog, interceptor, false)
×
UNCOV
1769
        return &ImplementationCfg{
×
UNCOV
1770
                GrpcRegistrar:       defaultImpl,
×
UNCOV
1771
                RestRegistrar:       defaultImpl,
×
UNCOV
1772
                ExternalValidator:   defaultImpl,
×
UNCOV
1773
                DatabaseBuilder:     NewDefaultDatabaseBuilder(c, ltndLog),
×
UNCOV
1774
                WalletConfigBuilder: defaultImpl,
×
UNCOV
1775
                ChainControlBuilder: defaultImpl,
×
UNCOV
1776
        }
×
1777
}
1778

1779
// CleanAndExpandPath expands environment variables and leading ~ in the
1780
// passed path, cleans the result, and returns it.
1781
// This function is taken from https://github.com/btcsuite/btcd
UNCOV
1782
func CleanAndExpandPath(path string) string {
×
UNCOV
1783
        if path == "" {
×
UNCOV
1784
                return ""
×
UNCOV
1785
        }
×
1786

1787
        // Expand initial ~ to OS specific home directory.
UNCOV
1788
        if strings.HasPrefix(path, "~") {
×
1789
                var homeDir string
×
1790
                u, err := user.Current()
×
1791
                if err == nil {
×
1792
                        homeDir = u.HomeDir
×
1793
                } else {
×
1794
                        homeDir = os.Getenv("HOME")
×
1795
                }
×
1796

1797
                path = strings.Replace(path, "~", homeDir, 1)
×
1798
        }
1799

1800
        // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
1801
        // but the variables can still be expanded via POSIX-style $VARIABLE.
UNCOV
1802
        return filepath.Clean(os.ExpandEnv(path))
×
1803
}
1804

1805
func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
UNCOV
1806
        netParams chainreg.BitcoinNetParams) error {
×
UNCOV
1807

×
UNCOV
1808
        // First, we'll check our node config to make sure the RPC parameters
×
UNCOV
1809
        // were set correctly. We'll also determine the path to the conf file
×
UNCOV
1810
        // depending on the backend node.
×
UNCOV
1811
        var daemonName, confDir, confFile, confFileBase string
×
UNCOV
1812
        switch conf := nodeConfig.(type) {
×
UNCOV
1813
        case *lncfg.Btcd:
×
UNCOV
1814
                // Resolves environment variable references in RPCUser and
×
UNCOV
1815
                // RPCPass fields.
×
UNCOV
1816
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
×
UNCOV
1817
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
×
UNCOV
1818

×
UNCOV
1819
                // If both RPCUser and RPCPass are set, we assume those
×
UNCOV
1820
                // credentials are good to use.
×
UNCOV
1821
                if conf.RPCUser != "" && conf.RPCPass != "" {
×
UNCOV
1822
                        return nil
×
UNCOV
1823
                }
×
1824

1825
                // Set the daemon name for displaying proper errors.
1826
                daemonName = btcdBackendName
×
1827
                confDir = conf.Dir
×
1828
                confFileBase = btcdBackendName
×
1829

×
1830
                // If only ONE of RPCUser or RPCPass is set, we assume the
×
1831
                // user did that unintentionally.
×
1832
                if conf.RPCUser != "" || conf.RPCPass != "" {
×
1833
                        return fmt.Errorf("please set both or neither of "+
×
1834
                                "%[1]v.rpcuser, %[1]v.rpcpass", daemonName)
×
1835
                }
×
1836

UNCOV
1837
        case *lncfg.Bitcoind:
×
UNCOV
1838
                // Ensure that if the ZMQ options are set, that they are not
×
UNCOV
1839
                // equal.
×
UNCOV
1840
                if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
×
UNCOV
1841
                        err := checkZMQOptions(
×
UNCOV
1842
                                conf.ZMQPubRawBlock, conf.ZMQPubRawTx,
×
UNCOV
1843
                        )
×
UNCOV
1844
                        if err != nil {
×
1845
                                return err
×
1846
                        }
×
1847
                }
1848

1849
                // Ensure that if the estimate mode is set, that it is a legal
1850
                // value.
UNCOV
1851
                if conf.EstimateMode != "" {
×
UNCOV
1852
                        err := checkEstimateMode(conf.EstimateMode)
×
UNCOV
1853
                        if err != nil {
×
1854
                                return err
×
1855
                        }
×
1856
                }
1857

1858
                // Set the daemon name for displaying proper errors.
UNCOV
1859
                daemonName = bitcoindBackendName
×
UNCOV
1860
                confDir = conf.Dir
×
UNCOV
1861
                confFile = conf.ConfigPath
×
UNCOV
1862
                confFileBase = BitcoinChainName
×
UNCOV
1863

×
UNCOV
1864
                // Resolves environment variable references in RPCUser
×
UNCOV
1865
                // and RPCPass fields.
×
UNCOV
1866
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
×
UNCOV
1867
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
×
UNCOV
1868

×
UNCOV
1869
                // Check that cookie and credentials don't contradict each
×
UNCOV
1870
                // other.
×
UNCOV
1871
                if (conf.RPCUser != "" || conf.RPCPass != "") &&
×
UNCOV
1872
                        conf.RPCCookie != "" {
×
1873

×
1874
                        return fmt.Errorf("please only provide either "+
×
1875
                                "%[1]v.rpccookie or %[1]v.rpcuser and "+
×
1876
                                "%[1]v.rpcpass", daemonName)
×
1877
                }
×
1878

1879
                // We convert the cookie into a user name and password.
UNCOV
1880
                if conf.RPCCookie != "" {
×
1881
                        cookie, err := os.ReadFile(conf.RPCCookie)
×
1882
                        if err != nil {
×
1883
                                return fmt.Errorf("cannot read cookie file: %w",
×
1884
                                        err)
×
1885
                        }
×
1886

1887
                        splitCookie := strings.Split(string(cookie), ":")
×
1888
                        if len(splitCookie) != 2 {
×
1889
                                return fmt.Errorf("cookie file has a wrong " +
×
1890
                                        "format")
×
1891
                        }
×
1892
                        conf.RPCUser = splitCookie[0]
×
1893
                        conf.RPCPass = splitCookie[1]
×
1894
                }
1895

UNCOV
1896
                if conf.RPCUser != "" && conf.RPCPass != "" {
×
UNCOV
1897
                        // If all of RPCUser, RPCPass, ZMQBlockHost, and
×
UNCOV
1898
                        // ZMQTxHost are set, we assume those parameters are
×
UNCOV
1899
                        // good to use.
×
UNCOV
1900
                        if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
×
UNCOV
1901
                                return nil
×
UNCOV
1902
                        }
×
1903

1904
                        // If RPCUser and RPCPass are set and RPCPolling is
1905
                        // enabled, we assume the parameters are good to use.
UNCOV
1906
                        if conf.RPCPolling {
×
UNCOV
1907
                                return nil
×
UNCOV
1908
                        }
×
1909
                }
1910

1911
                // If not all of the parameters are set, we'll assume the user
1912
                // did this unintentionally.
1913
                if conf.RPCUser != "" || conf.RPCPass != "" ||
×
1914
                        conf.ZMQPubRawBlock != "" || conf.ZMQPubRawTx != "" {
×
1915

×
1916
                        return fmt.Errorf("please set %[1]v.rpcuser and "+
×
1917
                                "%[1]v.rpcpass (or %[1]v.rpccookie) together "+
×
1918
                                "with %[1]v.zmqpubrawblock, %[1]v.zmqpubrawtx",
×
1919
                                daemonName)
×
1920
                }
×
1921
        }
1922

1923
        // If we're in simnet mode, then the running btcd instance won't read
1924
        // the RPC credentials from the configuration. So if lnd wasn't
1925
        // specified the parameters, then we won't be able to start.
1926
        if cConfig.SimNet {
×
1927
                return fmt.Errorf("rpcuser and rpcpass must be set to your " +
×
1928
                        "btcd node's RPC parameters for simnet mode")
×
1929
        }
×
1930

1931
        fmt.Println("Attempting automatic RPC configuration to " + daemonName)
×
1932

×
1933
        if confFile == "" {
×
1934
                confFile = filepath.Join(confDir, fmt.Sprintf("%v.conf",
×
1935
                        confFileBase))
×
1936
        }
×
1937
        switch cConfig.Node {
×
1938
        case btcdBackendName:
×
1939
                nConf := nodeConfig.(*lncfg.Btcd)
×
1940
                rpcUser, rpcPass, err := extractBtcdRPCParams(confFile)
×
1941
                if err != nil {
×
1942
                        return fmt.Errorf("unable to extract RPC credentials: "+
×
1943
                                "%v, cannot start w/o RPC connection", err)
×
1944
                }
×
1945
                nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass
×
1946

1947
        case bitcoindBackendName:
×
1948
                nConf := nodeConfig.(*lncfg.Bitcoind)
×
1949
                rpcUser, rpcPass, zmqBlockHost, zmqTxHost, err :=
×
1950
                        extractBitcoindRPCParams(netParams.Params.Name,
×
1951
                                nConf.Dir, confFile, nConf.RPCCookie)
×
1952
                if err != nil {
×
1953
                        return fmt.Errorf("unable to extract RPC credentials: "+
×
1954
                                "%v, cannot start w/o RPC connection", err)
×
1955
                }
×
1956
                nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass
×
1957
                nConf.ZMQPubRawBlock, nConf.ZMQPubRawTx = zmqBlockHost, zmqTxHost
×
1958
        }
1959

1960
        fmt.Printf("Automatically obtained %v's RPC credentials\n", daemonName)
×
1961
        return nil
×
1962
}
1963

1964
// supplyEnvValue supplies the value of an environment variable from a string.
1965
// It supports the following formats:
1966
// 1) $ENV_VAR
1967
// 2) ${ENV_VAR}
1968
// 3) ${ENV_VAR:-DEFAULT}
1969
//
1970
// Standard environment variable naming conventions:
1971
// - ENV_VAR contains letters, digits, and underscores, and does
1972
// not start with a digit.
1973
// - DEFAULT follows the rule that it can contain any characters except
1974
// whitespace.
1975
//
1976
// Parameters:
1977
// - value: The input string containing references to environment variables
1978
// (if any).
1979
//
1980
// Returns:
1981
// - string: The value of the specified environment variable, the default
1982
// value if provided, or the original input string if no matching variable is
1983
// found or set.
1984
func supplyEnvValue(value string) string {
7✔
1985
        // Regex for $ENV_VAR format.
7✔
1986
        var reEnvVar = regexp.MustCompile(`^\$([a-zA-Z_][a-zA-Z0-9_]*)$`)
7✔
1987

7✔
1988
        // Regex for ${ENV_VAR} format.
7✔
1989
        var reEnvVarWithBrackets = regexp.MustCompile(
7✔
1990
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}$`,
7✔
1991
        )
7✔
1992

7✔
1993
        // Regex for ${ENV_VAR:-DEFAULT} format.
7✔
1994
        var reEnvVarWithDefault = regexp.MustCompile(
7✔
1995
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*):-([\S]+)\}$`,
7✔
1996
        )
7✔
1997

7✔
1998
        // Match against supported formats.
7✔
1999
        switch {
7✔
2000
        case reEnvVarWithDefault.MatchString(value):
3✔
2001
                matches := reEnvVarWithDefault.FindStringSubmatch(value)
3✔
2002
                envVariable := matches[1]
3✔
2003
                defaultValue := matches[2]
3✔
2004
                if envValue := os.Getenv(envVariable); envValue != "" {
4✔
2005
                        return envValue
1✔
2006
                }
1✔
2007

2008
                return defaultValue
2✔
2009

2010
        case reEnvVarWithBrackets.MatchString(value):
×
2011
                matches := reEnvVarWithBrackets.FindStringSubmatch(value)
×
2012
                envVariable := matches[1]
×
2013
                envValue := os.Getenv(envVariable)
×
2014

×
2015
                return envValue
×
2016

2017
        case reEnvVar.MatchString(value):
3✔
2018
                matches := reEnvVar.FindStringSubmatch(value)
3✔
2019
                envVariable := matches[1]
3✔
2020
                envValue := os.Getenv(envVariable)
3✔
2021

3✔
2022
                return envValue
3✔
2023
        }
2024

2025
        return value
1✔
2026
}
2027

2028
// extractBtcdRPCParams attempts to extract the RPC credentials for an existing
2029
// btcd instance. The passed path is expected to be the location of btcd's
2030
// application data directory on the target system.
2031
func extractBtcdRPCParams(btcdConfigPath string) (string, string, error) {
×
2032
        // First, we'll open up the btcd configuration file found at the target
×
2033
        // destination.
×
2034
        btcdConfigFile, err := os.Open(btcdConfigPath)
×
2035
        if err != nil {
×
2036
                return "", "", err
×
2037
        }
×
2038
        defer func() { _ = btcdConfigFile.Close() }()
×
2039

2040
        // With the file open extract the contents of the configuration file so
2041
        // we can attempt to locate the RPC credentials.
2042
        configContents, err := io.ReadAll(btcdConfigFile)
×
2043
        if err != nil {
×
2044
                return "", "", err
×
2045
        }
×
2046

2047
        // Attempt to locate the RPC user using a regular expression. If we
2048
        // don't have a match for our regular expression then we'll exit with
2049
        // an error.
2050
        rpcUserRegexp, err := regexp.Compile(`(?m)^\s*rpcuser\s*=\s*([^\s]+)`)
×
2051
        if err != nil {
×
2052
                return "", "", err
×
2053
        }
×
2054
        userSubmatches := rpcUserRegexp.FindSubmatch(configContents)
×
2055
        if userSubmatches == nil {
×
2056
                return "", "", fmt.Errorf("unable to find rpcuser in config")
×
2057
        }
×
2058

2059
        // Similarly, we'll use another regular expression to find the set
2060
        // rpcpass (if any). If we can't find the pass, then we'll exit with an
2061
        // error.
2062
        rpcPassRegexp, err := regexp.Compile(`(?m)^\s*rpcpass\s*=\s*([^\s]+)`)
×
2063
        if err != nil {
×
2064
                return "", "", err
×
2065
        }
×
2066
        passSubmatches := rpcPassRegexp.FindSubmatch(configContents)
×
2067
        if passSubmatches == nil {
×
2068
                return "", "", fmt.Errorf("unable to find rpcuser in config")
×
2069
        }
×
2070

2071
        return supplyEnvValue(string(userSubmatches[1])),
×
2072
                supplyEnvValue(string(passSubmatches[1])), nil
×
2073
}
2074

2075
// extractBitcoindRPCParams attempts to extract the RPC credentials for an
2076
// existing bitcoind node instance. The routine looks for a cookie first,
2077
// optionally following the datadir configuration option in the bitcoin.conf. If
2078
// it doesn't find one, it looks for rpcuser/rpcpassword.
2079
func extractBitcoindRPCParams(networkName, bitcoindDataDir, bitcoindConfigPath,
2080
        rpcCookiePath string) (string, string, string, string, error) {
×
2081

×
2082
        // First, we'll open up the bitcoind configuration file found at the
×
2083
        // target destination.
×
2084
        bitcoindConfigFile, err := os.Open(bitcoindConfigPath)
×
2085
        if err != nil {
×
2086
                return "", "", "", "", err
×
2087
        }
×
2088
        defer func() { _ = bitcoindConfigFile.Close() }()
×
2089

2090
        // With the file open extract the contents of the configuration file so
2091
        // we can attempt to locate the RPC credentials.
2092
        configContents, err := io.ReadAll(bitcoindConfigFile)
×
2093
        if err != nil {
×
2094
                return "", "", "", "", err
×
2095
        }
×
2096

2097
        // First, we'll look for the ZMQ hosts providing raw block and raw
2098
        // transaction notifications.
2099
        zmqBlockHostRE, err := regexp.Compile(
×
2100
                `(?m)^\s*zmqpubrawblock\s*=\s*([^\s]+)`,
×
2101
        )
×
2102
        if err != nil {
×
2103
                return "", "", "", "", err
×
2104
        }
×
2105
        zmqBlockHostSubmatches := zmqBlockHostRE.FindSubmatch(configContents)
×
2106
        if len(zmqBlockHostSubmatches) < 2 {
×
2107
                return "", "", "", "", fmt.Errorf("unable to find " +
×
2108
                        "zmqpubrawblock in config")
×
2109
        }
×
2110
        zmqTxHostRE, err := regexp.Compile(`(?m)^\s*zmqpubrawtx\s*=\s*([^\s]+)`)
×
2111
        if err != nil {
×
2112
                return "", "", "", "", err
×
2113
        }
×
2114
        zmqTxHostSubmatches := zmqTxHostRE.FindSubmatch(configContents)
×
2115
        if len(zmqTxHostSubmatches) < 2 {
×
2116
                return "", "", "", "", errors.New("unable to find zmqpubrawtx " +
×
2117
                        "in config")
×
2118
        }
×
2119
        zmqBlockHost := string(zmqBlockHostSubmatches[1])
×
2120
        zmqTxHost := string(zmqTxHostSubmatches[1])
×
2121
        if err := checkZMQOptions(zmqBlockHost, zmqTxHost); err != nil {
×
2122
                return "", "", "", "", err
×
2123
        }
×
2124

2125
        // Next, we'll try to find an auth cookie. We need to detect the chain
2126
        // by seeing if one is specified in the configuration file.
2127
        dataDir := filepath.Dir(bitcoindConfigPath)
×
2128
        if bitcoindDataDir != "" {
×
2129
                dataDir = bitcoindDataDir
×
2130
        }
×
2131
        dataDirRE, err := regexp.Compile(`(?m)^\s*datadir\s*=\s*([^\s]+)`)
×
2132
        if err != nil {
×
2133
                return "", "", "", "", err
×
2134
        }
×
2135
        dataDirSubmatches := dataDirRE.FindSubmatch(configContents)
×
2136
        if dataDirSubmatches != nil {
×
2137
                dataDir = string(dataDirSubmatches[1])
×
2138
        }
×
2139

2140
        var chainDir string
×
2141
        switch networkName {
×
2142
        case "mainnet":
×
2143
                chainDir = ""
×
2144
        case "regtest", "testnet3", "signet":
×
2145
                chainDir = networkName
×
2146
        default:
×
2147
                return "", "", "", "", fmt.Errorf("unexpected networkname %v", networkName)
×
2148
        }
2149

2150
        cookiePath := filepath.Join(dataDir, chainDir, ".cookie")
×
2151
        if rpcCookiePath != "" {
×
2152
                cookiePath = rpcCookiePath
×
2153
        }
×
2154
        cookie, err := os.ReadFile(cookiePath)
×
2155
        if err == nil {
×
2156
                splitCookie := strings.Split(string(cookie), ":")
×
2157
                if len(splitCookie) == 2 {
×
2158
                        return splitCookie[0], splitCookie[1], zmqBlockHost,
×
2159
                                zmqTxHost, nil
×
2160
                }
×
2161
        }
2162

2163
        // We didn't find a cookie, so we attempt to locate the RPC user using
2164
        // a regular expression. If we  don't have a match for our regular
2165
        // expression then we'll exit with an error.
2166
        rpcUserRegexp, err := regexp.Compile(`(?m)^\s*rpcuser\s*=\s*([^\s]+)`)
×
2167
        if err != nil {
×
2168
                return "", "", "", "", err
×
2169
        }
×
2170
        userSubmatches := rpcUserRegexp.FindSubmatch(configContents)
×
2171

×
2172
        // Similarly, we'll use another regular expression to find the set
×
2173
        // rpcpass (if any). If we can't find the pass, then we'll exit with an
×
2174
        // error.
×
2175
        rpcPassRegexp, err := regexp.Compile(`(?m)^\s*rpcpassword\s*=\s*([^\s]+)`)
×
2176
        if err != nil {
×
2177
                return "", "", "", "", err
×
2178
        }
×
2179
        passSubmatches := rpcPassRegexp.FindSubmatch(configContents)
×
2180

×
2181
        // Exit with an error if the cookie file, is defined in config, and
×
2182
        // can not be found, with both rpcuser and rpcpassword undefined.
×
2183
        if rpcCookiePath != "" && userSubmatches == nil && passSubmatches == nil {
×
2184
                return "", "", "", "", fmt.Errorf("unable to open cookie file (%v)",
×
2185
                        rpcCookiePath)
×
2186
        }
×
2187

2188
        if userSubmatches == nil {
×
2189
                return "", "", "", "", fmt.Errorf("unable to find rpcuser in " +
×
2190
                        "config")
×
2191
        }
×
2192
        if passSubmatches == nil {
×
2193
                return "", "", "", "", fmt.Errorf("unable to find rpcpassword " +
×
2194
                        "in config")
×
2195
        }
×
2196

2197
        return supplyEnvValue(string(userSubmatches[1])),
×
2198
                supplyEnvValue(string(passSubmatches[1])),
×
2199
                zmqBlockHost, zmqTxHost, nil
×
2200
}
2201

2202
// checkZMQOptions ensures that the provided addresses to use as the hosts for
2203
// ZMQ rawblock and rawtx notifications are different.
UNCOV
2204
func checkZMQOptions(zmqBlockHost, zmqTxHost string) error {
×
UNCOV
2205
        if zmqBlockHost == zmqTxHost {
×
2206
                return errors.New("zmqpubrawblock and zmqpubrawtx must be set " +
×
2207
                        "to different addresses")
×
2208
        }
×
2209

UNCOV
2210
        return nil
×
2211
}
2212

2213
// checkEstimateMode ensures that the provided estimate mode is legal.
UNCOV
2214
func checkEstimateMode(estimateMode string) error {
×
UNCOV
2215
        for _, mode := range bitcoindEstimateModes {
×
UNCOV
2216
                if estimateMode == mode {
×
UNCOV
2217
                        return nil
×
UNCOV
2218
                }
×
2219
        }
2220

2221
        return fmt.Errorf("estimatemode must be one of the following: %v",
×
2222
                bitcoindEstimateModes[:])
×
2223
}
2224

2225
// configToFlatMap converts the given config struct into a flat map of
2226
// key/value pairs using the dot notation we are used to from the config file
2227
// or command line flags. It also returns a map containing deprecated config
2228
// options.
2229
func configToFlatMap(cfg Config) (map[string]string,
2230
        map[string]struct{}, error) {
1✔
2231

1✔
2232
        result := make(map[string]string)
1✔
2233

1✔
2234
        // deprecated stores a map of deprecated options found in the config
1✔
2235
        // that are set by the users. A config option is considered as
1✔
2236
        // deprecated if it has a `hidden` flag.
1✔
2237
        deprecated := make(map[string]struct{})
1✔
2238

1✔
2239
        // redact is the helper function that redacts sensitive values like
1✔
2240
        // passwords.
1✔
2241
        redact := func(key, value string) string {
298✔
2242
                sensitiveKeySuffixes := []string{
297✔
2243
                        "pass",
297✔
2244
                        "password",
297✔
2245
                        "dsn",
297✔
2246
                }
297✔
2247
                for _, suffix := range sensitiveKeySuffixes {
1,181✔
2248
                        if strings.HasSuffix(key, suffix) {
889✔
2249
                                return "[redacted]"
5✔
2250
                        }
5✔
2251
                }
2252

2253
                return value
292✔
2254
        }
2255

2256
        // printConfig is the helper function that goes into nested structs
2257
        // recursively. Because we call it recursively, we need to declare it
2258
        // before we define it.
2259
        var printConfig func(reflect.Value, string)
1✔
2260
        printConfig = func(obj reflect.Value, prefix string) {
56✔
2261
                // Turn struct pointers into the actual struct, so we can
55✔
2262
                // iterate over the fields as we would with a struct value.
55✔
2263
                if obj.Kind() == reflect.Ptr {
105✔
2264
                        obj = obj.Elem()
50✔
2265
                }
50✔
2266

2267
                // Abort on nil values.
2268
                if !obj.IsValid() {
66✔
2269
                        return
11✔
2270
                }
11✔
2271

2272
                // Loop over all fields of the struct and inspect the type.
2273
                for i := 0; i < obj.NumField(); i++ {
411✔
2274
                        field := obj.Field(i)
367✔
2275
                        fieldType := obj.Type().Field(i)
367✔
2276

367✔
2277
                        longName := fieldType.Tag.Get("long")
367✔
2278
                        namespace := fieldType.Tag.Get("namespace")
367✔
2279
                        group := fieldType.Tag.Get("group")
367✔
2280
                        hidden := fieldType.Tag.Get("hidden")
367✔
2281

367✔
2282
                        switch {
367✔
2283
                        // We have a long name defined, this is a config value.
2284
                        case longName != "":
297✔
2285
                                key := longName
297✔
2286
                                if prefix != "" {
498✔
2287
                                        key = prefix + "." + key
201✔
2288
                                }
201✔
2289

2290
                                // Add the value directly to the flattened map.
2291
                                result[key] = redact(key, fmt.Sprintf(
297✔
2292
                                        "%v", field.Interface(),
297✔
2293
                                ))
297✔
2294

297✔
2295
                                // If there's a hidden flag, it's deprecated.
297✔
2296
                                if hidden == "true" && !field.IsZero() {
298✔
2297
                                        deprecated[key] = struct{}{}
1✔
2298
                                }
1✔
2299

2300
                        // We have no long name but a namespace, this is a
2301
                        // nested struct.
2302
                        case longName == "" && namespace != "":
50✔
2303
                                key := namespace
50✔
2304
                                if prefix != "" {
63✔
2305
                                        key = prefix + "." + key
13✔
2306
                                }
13✔
2307

2308
                                printConfig(field, key)
50✔
2309

2310
                        // Just a group means this is a dummy struct to house
2311
                        // multiple config values, the group name doesn't go
2312
                        // into the final field name.
2313
                        case longName == "" && group != "":
1✔
2314
                                printConfig(field, prefix)
1✔
2315

2316
                        // Anonymous means embedded struct. We need to recurse
2317
                        // into it but without adding anything to the prefix.
2318
                        case fieldType.Anonymous:
3✔
2319
                                printConfig(field, prefix)
3✔
2320

2321
                        default:
16✔
2322
                                continue
16✔
2323
                        }
2324
                }
2325
        }
2326

2327
        // Turn the whole config struct into a flat map.
2328
        printConfig(reflect.ValueOf(cfg), "")
1✔
2329

1✔
2330
        return result, deprecated, nil
1✔
2331
}
2332

2333
// logWarningsForDeprecation logs a warning if a deprecated config option is
2334
// set.
UNCOV
2335
func logWarningsForDeprecation(cfg Config) {
×
UNCOV
2336
        _, deprecated, err := configToFlatMap(cfg)
×
UNCOV
2337
        if err != nil {
×
2338
                ltndLog.Errorf("Convert configs to map: %v", err)
×
2339
        }
×
2340

UNCOV
2341
        for k := range deprecated {
×
2342
                ltndLog.Warnf("Config '%s' is deprecated, please remove it", k)
×
2343
        }
×
2344
}
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