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

lightningnetwork / lnd / 11880886288

17 Nov 2024 05:35PM UTC coverage: 57.94% (-1.0%) from 58.935%
11880886288

Pull #9274

github

ziggie1984
docs: add release-notes
Pull Request #9274: Decrease outgoing htlc budget

3 of 4 new or added lines in 1 file covered. (75.0%)

19093 existing lines in 233 files now uncovered.

100208 of 172952 relevant lines covered (57.94%)

25486.44 hits per line

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

20.86
/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
        defaultRPCPort            = 10009
63
        defaultRESTPort           = 8080
64
        defaultPeerPort           = 9735
65
        defaultRPCHost            = "localhost"
66

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

79
        defaultTorSOCKSPort            = 9050
80
        defaultTorDNSHost              = "soa.nodes.lightning.directory"
81
        defaultTorDNSPort              = 53
82
        defaultTorControlPort          = 9051
83
        defaultTorV2PrivateKeyFilename = "v2_onion_private_key"
84
        defaultTorV3PrivateKeyFilename = "v3_onion_private_key"
85

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

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

95
        // minTimeLockDelta is the minimum timelock we require for incoming
96
        // HTLCs on our channels.
97
        minTimeLockDelta = routing.MinCLTVDelta
98

99
        // MaxTimeLockDelta is the maximum CLTV delta that can be applied to
100
        // forwarded HTLCs.
101
        MaxTimeLockDelta = routing.MaxCLTVDelta
102

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

107
        defaultAlias = ""
108
        defaultColor = "#3399FF"
109

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

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

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

126
        defaultChainInterval = time.Minute
127
        defaultChainTimeout  = time.Second * 30
128
        defaultChainBackoff  = time.Minute * 2
129
        defaultChainAttempts = 3
130

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

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

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

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

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

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

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

192
        // defaultChannelCommitInterval is the default maximum time between
193
        // receiving a channel state update and signing a new commitment.
194
        defaultChannelCommitInterval = 50 * time.Millisecond
195

196
        // maxChannelCommitInterval is the maximum time the commit interval can
197
        // be configured to.
198
        maxChannelCommitInterval = time.Hour
199

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

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

210
        // defaultChannelCommitBatchSize is the default maximum number of
211
        // channel state updates that is accumulated before signing a new
212
        // commitment.
213
        defaultChannelCommitBatchSize = 10
214

215
        // defaultCoinSelectionStrategy is the coin selection strategy that is
216
        // used by default to fund transactions.
217
        defaultCoinSelectionStrategy = "largest"
218

219
        // defaultKeepFailedPaymentAttempts is the default setting for whether
220
        // to keep failed payments in the database.
221
        defaultKeepFailedPaymentAttempts = false
222

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

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

234
        // defaultGrpcClientPingMinWait is the default minimum amount of time a
235
        // client should wait before sending a keepalive ping.
236
        defaultGrpcClientPingMinWait = 5 * time.Second
237

238
        // defaultHTTPHeaderTimeout is the default timeout for HTTP requests.
239
        DefaultHTTPHeaderTimeout = 5 * time.Second
240

241
        // BitcoinChainName is a string that represents the Bitcoin blockchain.
242
        BitcoinChainName = "bitcoin"
243

244
        bitcoindBackendName = "bitcoind"
245
        btcdBackendName     = "btcd"
246
        neutrinoBackendName = "neutrino"
247
)
248

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

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

262
        defaultDataDir = filepath.Join(DefaultLndDir, defaultDataDirname)
263
        defaultLogDir  = filepath.Join(DefaultLndDir, defaultLogDirname)
264

265
        defaultTowerDir = filepath.Join(defaultDataDir, defaultTowerSubDirname)
266

267
        defaultTLSCertPath    = filepath.Join(DefaultLndDir, defaultTLSCertFilename)
268
        defaultTLSKeyPath     = filepath.Join(DefaultLndDir, defaultTLSKeyFilename)
269
        defaultLetsEncryptDir = filepath.Join(DefaultLndDir, defaultLetsEncryptDirname)
270

271
        defaultBtcdDir         = btcutil.AppDataDir(btcdBackendName, false)
272
        defaultBtcdRPCCertFile = filepath.Join(defaultBtcdDir, "rpc.cert")
273

274
        defaultBitcoindDir = btcutil.AppDataDir(BitcoinChainName, false)
275

276
        defaultTorSOCKS   = net.JoinHostPort("localhost", strconv.Itoa(defaultTorSOCKSPort))
277
        defaultTorDNS     = net.JoinHostPort(defaultTorDNSHost, strconv.Itoa(defaultTorDNSPort))
278
        defaultTorControl = net.JoinHostPort("localhost", strconv.Itoa(defaultTorControlPort))
279

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

285
        defaultPrunedNodeMaxPeers = 4
286
)
287

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

297
        LndDir       string `long:"lnddir" description:"The base directory that contains lnd's data, logs, configuration file, etc. This option overwrites all other directory options."`
298
        ConfigFile   string `short:"C" long:"configfile" description:"Path to configuration file"`
299
        DataDir      string `short:"b" long:"datadir" description:"The directory to store lnd's data within"`
300
        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."`
301

302
        TLSCertPath        string        `long:"tlscertpath" description:"Path to write the TLS certificate for lnd's RPC and REST services"`
303
        TLSKeyPath         string        `long:"tlskeypath" description:"Path to write the TLS private key for lnd's RPC and REST services"`
304
        TLSExtraIPs        []string      `long:"tlsextraip" description:"Adds an extra ip to the generated certificate"`
305
        TLSExtraDomains    []string      `long:"tlsextradomain" description:"Adds an extra domain to the generated certificate"`
306
        TLSAutoRefresh     bool          `long:"tlsautorefresh" description:"Re-generate TLS certificate and key if the IPs or domains are changed"`
307
        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"`
308
        TLSCertDuration    time.Duration `long:"tlscertduration" description:"The duration for which the auto-generated TLS certificate will be valid for"`
309
        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"`
310

311
        NoMacaroons     bool          `long:"no-macaroons" description:"Disable macaroon authentication, can only be used if server is not listening on a public interface."`
312
        AdminMacPath    string        `long:"adminmacaroonpath" description:"Path to write the admin macaroon for lnd's RPC and REST services if it doesn't exist"`
313
        ReadMacPath     string        `long:"readonlymacaroonpath" description:"Path to write the read-only macaroon for lnd's RPC and REST services if it doesn't exist"`
314
        InvoiceMacPath  string        `long:"invoicemacaroonpath" description:"Path to the invoice-only macaroon for lnd's RPC and REST services if it doesn't exist"`
315
        LogDir          string        `long:"logdir" description:"Directory to log output."`
316
        MaxLogFiles     int           `long:"maxlogfiles" description:"Maximum logfiles to keep (0 for no rotation). DEPRECATED: use --logging.file.max-files instead" hidden:"true"`
317
        MaxLogFileSize  int           `long:"maxlogfilesize" description:"Maximum logfile size in MB. DEPRECATED: use --logging.file.max-file-size instead" hidden:"true"`
318
        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"`
319

320
        LetsEncryptDir    string `long:"letsencryptdir" description:"The directory to store Let's Encrypt certificates within"`
321
        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."`
322
        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."`
323

324
        // We'll parse these 'raw' string arguments into real net.Addrs in the
325
        // loadConfig function. We need to expose the 'raw' strings so the
326
        // command line library can access them.
327
        // Only the parsed net.Addrs should be used!
328
        RawRPCListeners   []string `long:"rpclisten" description:"Add an interface/port/socket to listen for RPC connections"`
329
        RawRESTListeners  []string `long:"restlisten" description:"Add an interface/port/socket to listen for REST connections"`
330
        RawListeners      []string `long:"listen" description:"Add an interface/port to listen for peer connections"`
331
        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"`
332
        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."`
333
        RPCListeners      []net.Addr
334
        RESTListeners     []net.Addr
335
        RestCORS          []string `long:"restcors" description:"Add an ip:port/hostname to allow cross origin access from. To allow all origins, set as \"*\"."`
336
        Listeners         []net.Addr
337
        ExternalIPs       []net.Addr
338
        DisableListen     bool          `long:"nolisten" description:"Disable listening for incoming peer connections"`
339
        DisableRest       bool          `long:"norest" description:"Disable REST API"`
340
        DisableRestTLS    bool          `long:"no-rest-tls" description:"Disable TLS for REST connections"`
341
        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"`
342
        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"`
343
        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"`
344
        AddPeers          []string      `long:"addpeer" description:"Specify peers to connect to first"`
345
        MinBackoff        time.Duration `long:"minbackoff" description:"Shortest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."`
346
        MaxBackoff        time.Duration `long:"maxbackoff" description:"Longest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."`
347
        ConnectionTimeout time.Duration `long:"connectiontimeout" description:"The timeout value for network connections. Valid time units are {ms, s, m, h}."`
348

349
        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"`
350

351
        CPUProfile      string `long:"cpuprofile" description:"DEPRECATED: Use 'pprof.cpuprofile' option. Write CPU profile to the specified file" hidden:"true"`
352
        Profile         string `long:"profile" description:"DEPRECATED: Use 'pprof.profile' option. Enable HTTP profiling on either a port or host:port" hidden:"true"`
353
        BlockingProfile int    `long:"blockingprofile" description:"DEPRECATED: Use 'pprof.blockingprofile' option. 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." hidden:"true"`
354
        MutexProfile    int    `long:"mutexprofile" description:"DEPRECATED: Use 'pprof.mutexprofile' option. 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." hidden:"true"`
355

356
        Pprof *lncfg.Pprof `group:"Pprof" namespace:"pprof"`
357

358
        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"`
359
        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."`
360
        MaxPendingChannels int    `long:"maxpendingchannels" description:"The maximum number of incoming pending channels permitted per peer."`
361
        BackupFilePath     string `long:"backupfilepath" description:"The target location of the channel backup file"`
362

363
        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"`
364

365
        Bitcoin      *lncfg.Chain    `group:"Bitcoin" namespace:"bitcoin"`
366
        BtcdMode     *lncfg.Btcd     `group:"btcd" namespace:"btcd"`
367
        BitcoindMode *lncfg.Bitcoind `group:"bitcoind" namespace:"bitcoind"`
368
        NeutrinoMode *lncfg.Neutrino `group:"neutrino" namespace:"neutrino"`
369

370
        BlockCacheSize uint64 `long:"blockcachesize" description:"The maximum capacity of the block cache"`
371

372
        Autopilot *lncfg.AutoPilot `group:"Autopilot" namespace:"autopilot"`
373

374
        Tor *lncfg.Tor `group:"Tor" namespace:"tor"`
375

376
        SubRPCServers *subRPCServerConfigs `group:"subrpc"`
377

378
        Hodl *hodl.Config `group:"hodl" namespace:"hodl"`
379

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

382
        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."`
383
        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."`
384
        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."`
385

386
        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."`
387

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

390
        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."`
391
        TrickleDelay                  int           `long:"trickledelay" description:"Time in milliseconds between each release of announcements to the network"`
392
        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."`
393
        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."`
394
        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."`
395
        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."`
396
        Alias                         string        `long:"alias" description:"The node alias. Used as a moniker by peers and intelligence services"`
397
        Color                         string        `long:"color" description:"The color of the node in hex format (i.e. '#3399FF'). Used to customize node appearance in intelligence services"`
398
        MinChanSize                   int64         `long:"minchansize" description:"The smallest channel size (in satoshis) that we should accept. Incoming channels smaller than this will be rejected"`
399
        MaxChanSize                   int64         `long:"maxchansize" description:"The largest channel size (in satoshis) that we should accept. Incoming channels larger than this will be rejected"`
400
        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."`
401

402
        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."`
403

404
        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."`
405

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

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

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

412
        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."`
413

414
        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."`
415
        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."`
416

417
        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."`
418

419
        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."`
420

421
        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."`
422

423
        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."`
424

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

429
        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"`
430

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

433
        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]."`
434

435
        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"`
436

437
        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."`
438

439
        net tor.Net
440

441
        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."`
442

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

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

447
        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]"`
448

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

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

453
        DustThreshold uint64 `long:"dust-threshold" description:"DEPRECATED: Sets the max fee exposure in satoshis for a channel after which HTLC's will be failed." hidden:"true"`
454

455
        MaxFeeExposure uint64 `long:"channel-max-fee-exposure" description:" Limits the maximum fee exposure in satoshis of a channel. This value is enforced for all channels and is independent of the channel initiator."`
456

457
        Fee *lncfg.Fee `group:"fee" namespace:"fee"`
458

459
        Invoices *lncfg.Invoices `group:"invoices" namespace:"invoices"`
460

461
        Routing *lncfg.Routing `group:"routing" namespace:"routing"`
462

463
        Gossip *lncfg.Gossip `group:"gossip" namespace:"gossip"`
464

465
        Workers *lncfg.Workers `group:"workers" namespace:"workers"`
466

467
        Caches *lncfg.Caches `group:"caches" namespace:"caches"`
468

469
        Prometheus lncfg.Prometheus `group:"prometheus" namespace:"prometheus"`
470

471
        WtClient *lncfg.WtClient `group:"wtclient" namespace:"wtclient"`
472

473
        Watchtower *lncfg.Watchtower `group:"watchtower" namespace:"watchtower"`
474

475
        ProtocolOptions *lncfg.ProtocolOptions `group:"protocol" namespace:"protocol"`
476

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

479
        HealthChecks *lncfg.HealthCheckConfig `group:"healthcheck" namespace:"healthcheck"`
480

481
        DB *lncfg.DB `group:"db" namespace:"db"`
482

483
        Cluster *lncfg.Cluster `group:"cluster" namespace:"cluster"`
484

485
        RPCMiddleware *lncfg.RPCMiddleware `group:"rpcmiddleware" namespace:"rpcmiddleware"`
486

487
        RemoteSigner *lncfg.RemoteSigner `group:"remotesigner" namespace:"remotesigner"`
488

489
        Sweeper *lncfg.Sweeper `group:"sweeper" namespace:"sweeper"`
490

491
        Htlcswitch *lncfg.Htlcswitch `group:"htlcswitch" namespace:"htlcswitch"`
492

493
        GRPC *GRPCConfig `group:"grpc" namespace:"grpc"`
494

495
        // SubLogMgr is the root logger that all the daemon's subloggers are
496
        // hooked up to.
497
        SubLogMgr  *build.SubLoggerManager
498
        LogRotator *build.RotatingLogWriter
499
        LogConfig  *build.LogConfig `group:"logging" namespace:"logging"`
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
                MaxLogFiles:       build.DefaultMaxLogFiles,
1✔
566
                MaxLogFileSize:    build.DefaultMaxLogFileSize,
1✔
567
                AcceptorTimeout:   defaultAcceptorTimeout,
1✔
568
                WSPingInterval:    lnrpc.DefaultPingInterval,
1✔
569
                WSPongWait:        lnrpc.DefaultPongWait,
1✔
570
                Bitcoin: &lncfg.Chain{
1✔
571
                        MinHTLCIn:     chainreg.DefaultBitcoinMinHTLCInMSat,
1✔
572
                        MinHTLCOut:    chainreg.DefaultBitcoinMinHTLCOutMSat,
1✔
573
                        BaseFee:       chainreg.DefaultBitcoinBaseFeeMSat,
1✔
574
                        FeeRate:       chainreg.DefaultBitcoinFeeRate,
1✔
575
                        TimeLockDelta: chainreg.DefaultBitcoinTimeLockDelta,
1✔
576
                        MaxLocalDelay: defaultMaxLocalCSVDelay,
1✔
577
                        Node:          btcdBackendName,
1✔
578
                },
1✔
579
                BtcdMode: &lncfg.Btcd{
1✔
580
                        Dir:     defaultBtcdDir,
1✔
581
                        RPCHost: defaultRPCHost,
1✔
582
                        RPCCert: defaultBtcdRPCCertFile,
1✔
583
                },
1✔
584
                BitcoindMode: &lncfg.Bitcoind{
1✔
585
                        Dir:                defaultBitcoindDir,
1✔
586
                        RPCHost:            defaultRPCHost,
1✔
587
                        EstimateMode:       defaultBitcoindEstimateMode,
1✔
588
                        PrunedNodeMaxPeers: defaultPrunedNodeMaxPeers,
1✔
589
                        ZMQReadDeadline:    defaultZMQReadDeadline,
1✔
590
                },
1✔
591
                NeutrinoMode: &lncfg.Neutrino{
1✔
592
                        UserAgentName:    neutrino.UserAgentName,
1✔
593
                        UserAgentVersion: neutrino.UserAgentVersion,
1✔
594
                },
1✔
595
                BlockCacheSize:     defaultBlockCacheSize,
1✔
596
                MaxPendingChannels: lncfg.DefaultMaxPendingChannels,
1✔
597
                NoSeedBackup:       defaultNoSeedBackup,
1✔
598
                MinBackoff:         defaultMinBackoff,
1✔
599
                MaxBackoff:         defaultMaxBackoff,
1✔
600
                ConnectionTimeout:  tor.DefaultConnTimeout,
1✔
601

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

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

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

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

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

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

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

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

×
806
                        return nil, err
×
807
                }
×
808

UNCOV
809
                configFileError = err
×
810
        }
811

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

819
        // Make sure everything we just loaded makes sense.
UNCOV
820
        cleanCfg, err := ValidateConfig(
×
UNCOV
821
                cfg, interceptor, fileParser, flagParser,
×
UNCOV
822
        )
×
UNCOV
823
        var usageErr *lncfg.UsageError
×
UNCOV
824
        if errors.As(err, &usageErr) {
×
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", err)
×
834

×
835
                return nil, 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
// ValidateConfig check the given configuration to be sane. This makes sure no
860
// illegal values or combination of values are set. All file system paths are
861
// normalized. The cleaned up config is returned on success.
862
func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser,
UNCOV
863
        flagParser *flags.Parser) (*Config, error) {
×
UNCOV
864

×
UNCOV
865
        // If the provided lnd directory is not the default, we'll modify the
×
UNCOV
866
        // path to all of the files and directories that will live within it.
×
UNCOV
867
        lndDir := CleanAndExpandPath(cfg.LndDir)
×
UNCOV
868
        if lndDir != DefaultLndDir {
×
UNCOV
869
                cfg.DataDir = filepath.Join(lndDir, defaultDataDirname)
×
UNCOV
870
                cfg.LetsEncryptDir = filepath.Join(
×
UNCOV
871
                        lndDir, defaultLetsEncryptDirname,
×
UNCOV
872
                )
×
UNCOV
873
                cfg.TLSCertPath = filepath.Join(lndDir, defaultTLSCertFilename)
×
UNCOV
874
                cfg.TLSKeyPath = filepath.Join(lndDir, defaultTLSKeyFilename)
×
UNCOV
875
                cfg.LogDir = filepath.Join(lndDir, defaultLogDirname)
×
UNCOV
876

×
UNCOV
877
                // If the watchtower's directory is set to the default, i.e. the
×
UNCOV
878
                // user has not requested a different location, we'll move the
×
UNCOV
879
                // location to be relative to the specified lnd directory.
×
UNCOV
880
                if cfg.Watchtower.TowerDir == defaultTowerDir {
×
UNCOV
881
                        cfg.Watchtower.TowerDir = filepath.Join(
×
UNCOV
882
                                cfg.DataDir, defaultTowerSubDirname,
×
UNCOV
883
                        )
×
UNCOV
884
                }
×
885
        }
886

UNCOV
887
        funcName := "ValidateConfig"
×
UNCOV
888
        mkErr := func(format string, args ...interface{}) error {
×
889
                return fmt.Errorf(funcName+": "+format, args...)
×
890
        }
×
UNCOV
891
        makeDirectory := func(dir string) error {
×
UNCOV
892
                err := os.MkdirAll(dir, 0700)
×
UNCOV
893
                if err != nil {
×
894
                        // Show a nicer error message if it's because a symlink
×
895
                        // is linked to a directory that does not exist
×
896
                        // (probably because it's not mounted).
×
897
                        if e, ok := err.(*os.PathError); ok && os.IsExist(err) {
×
898
                                link, lerr := os.Readlink(e.Path)
×
899
                                if lerr == nil {
×
900
                                        str := "is symlink %s -> %s mounted?"
×
901
                                        err = fmt.Errorf(str, e.Path, link)
×
902
                                }
×
903
                        }
904

905
                        str := "Failed to create lnd directory '%s': %v"
×
906
                        return mkErr(str, dir, err)
×
907
                }
908

UNCOV
909
                return nil
×
910
        }
911

912
        // IsSet returns true if an option has been set in either the config
913
        // file or by a flag.
UNCOV
914
        isSet := func(field string) (bool, error) {
×
UNCOV
915
                fieldName, ok := reflect.TypeOf(Config{}).FieldByName(field)
×
UNCOV
916
                if !ok {
×
917
                        str := "could not find field %s"
×
918
                        return false, mkErr(str, field)
×
919
                }
×
920

UNCOV
921
                long, ok := fieldName.Tag.Lookup("long")
×
UNCOV
922
                if !ok {
×
923
                        str := "field %s does not have a long tag"
×
924
                        return false, mkErr(str, field)
×
925
                }
×
926

927
                // The user has the option to set the flag in either the config
928
                // file or as a command line flag. If any is set, we consider it
929
                // to be set, not applying any precedence rules here (since it
930
                // is a boolean the default is false anyway which would screw up
931
                // any precedence rules). Additionally, we need to also support
932
                // the use case where the config struct is embedded _within_
933
                // another struct with a prefix (as is the case with
934
                // lightning-terminal).
UNCOV
935
                fileOption := fileParser.FindOptionByLongName(long)
×
UNCOV
936
                fileOptionNested := fileParser.FindOptionByLongName(
×
UNCOV
937
                        "lnd." + long,
×
UNCOV
938
                )
×
UNCOV
939
                flagOption := flagParser.FindOptionByLongName(long)
×
UNCOV
940
                flagOptionNested := flagParser.FindOptionByLongName(
×
UNCOV
941
                        "lnd." + long,
×
UNCOV
942
                )
×
UNCOV
943

×
UNCOV
944
                return (fileOption != nil && fileOption.IsSet()) ||
×
UNCOV
945
                                (fileOptionNested != nil && fileOptionNested.IsSet()) ||
×
UNCOV
946
                                (flagOption != nil && flagOption.IsSet()) ||
×
UNCOV
947
                                (flagOptionNested != nil && flagOptionNested.IsSet()),
×
UNCOV
948
                        nil
×
949
        }
950

951
        // As soon as we're done parsing configuration options, ensure all paths
952
        // to directories and files are cleaned and expanded before attempting
953
        // to use them later on.
UNCOV
954
        cfg.DataDir = CleanAndExpandPath(cfg.DataDir)
×
UNCOV
955
        cfg.TLSCertPath = CleanAndExpandPath(cfg.TLSCertPath)
×
UNCOV
956
        cfg.TLSKeyPath = CleanAndExpandPath(cfg.TLSKeyPath)
×
UNCOV
957
        cfg.LetsEncryptDir = CleanAndExpandPath(cfg.LetsEncryptDir)
×
UNCOV
958
        cfg.AdminMacPath = CleanAndExpandPath(cfg.AdminMacPath)
×
UNCOV
959
        cfg.ReadMacPath = CleanAndExpandPath(cfg.ReadMacPath)
×
UNCOV
960
        cfg.InvoiceMacPath = CleanAndExpandPath(cfg.InvoiceMacPath)
×
UNCOV
961
        cfg.LogDir = CleanAndExpandPath(cfg.LogDir)
×
UNCOV
962
        cfg.BtcdMode.Dir = CleanAndExpandPath(cfg.BtcdMode.Dir)
×
UNCOV
963
        cfg.BitcoindMode.Dir = CleanAndExpandPath(cfg.BitcoindMode.Dir)
×
UNCOV
964
        cfg.BitcoindMode.ConfigPath = CleanAndExpandPath(
×
UNCOV
965
                cfg.BitcoindMode.ConfigPath,
×
UNCOV
966
        )
×
UNCOV
967
        cfg.BitcoindMode.RPCCookie = CleanAndExpandPath(cfg.BitcoindMode.RPCCookie)
×
UNCOV
968
        cfg.Tor.PrivateKeyPath = CleanAndExpandPath(cfg.Tor.PrivateKeyPath)
×
UNCOV
969
        cfg.Tor.WatchtowerKeyPath = CleanAndExpandPath(cfg.Tor.WatchtowerKeyPath)
×
UNCOV
970
        cfg.Watchtower.TowerDir = CleanAndExpandPath(cfg.Watchtower.TowerDir)
×
UNCOV
971
        cfg.BackupFilePath = CleanAndExpandPath(cfg.BackupFilePath)
×
UNCOV
972
        cfg.WalletUnlockPasswordFile = CleanAndExpandPath(
×
UNCOV
973
                cfg.WalletUnlockPasswordFile,
×
UNCOV
974
        )
×
UNCOV
975

×
UNCOV
976
        // Ensure that the user didn't attempt to specify negative values for
×
UNCOV
977
        // any of the autopilot params.
×
UNCOV
978
        if cfg.Autopilot.MaxChannels < 0 {
×
979
                str := "autopilot.maxchannels must be non-negative"
×
980

×
981
                return nil, mkErr(str)
×
982
        }
×
UNCOV
983
        if cfg.Autopilot.Allocation < 0 {
×
984
                str := "autopilot.allocation must be non-negative"
×
985

×
986
                return nil, mkErr(str)
×
987
        }
×
UNCOV
988
        if cfg.Autopilot.MinChannelSize < 0 {
×
989
                str := "autopilot.minchansize must be non-negative"
×
990

×
991
                return nil, mkErr(str)
×
992
        }
×
UNCOV
993
        if cfg.Autopilot.MaxChannelSize < 0 {
×
994
                str := "autopilot.maxchansize must be non-negative"
×
995

×
996
                return nil, mkErr(str)
×
997
        }
×
UNCOV
998
        if cfg.Autopilot.MinConfs < 0 {
×
999
                str := "autopilot.minconfs must be non-negative"
×
1000

×
1001
                return nil, mkErr(str)
×
1002
        }
×
UNCOV
1003
        if cfg.Autopilot.ConfTarget < 1 {
×
1004
                str := "autopilot.conftarget must be positive"
×
1005

×
1006
                return nil, mkErr(str)
×
1007
        }
×
1008

1009
        // Ensure that the specified values for the min and max channel size
1010
        // are within the bounds of the normal chan size constraints.
UNCOV
1011
        if cfg.Autopilot.MinChannelSize < int64(funding.MinChanFundingSize) {
×
1012
                cfg.Autopilot.MinChannelSize = int64(funding.MinChanFundingSize)
×
1013
        }
×
UNCOV
1014
        if cfg.Autopilot.MaxChannelSize > int64(MaxFundingAmount) {
×
1015
                cfg.Autopilot.MaxChannelSize = int64(MaxFundingAmount)
×
1016
        }
×
1017

UNCOV
1018
        if _, err := validateAtplCfg(cfg.Autopilot); err != nil {
×
1019
                return nil, mkErr("error validating autopilot: %v", err)
×
1020
        }
×
1021

1022
        // Ensure that --maxchansize is properly handled when set by user.
1023
        // For non-Wumbo channels this limit remains 16777215 satoshis by default
1024
        // as specified in BOLT-02. For wumbo channels this limit is 1,000,000,000.
1025
        // satoshis (10 BTC). Always enforce --maxchansize explicitly set by user.
1026
        // If unset (marked by 0 value), then enforce proper default.
UNCOV
1027
        if cfg.MaxChanSize == 0 {
×
UNCOV
1028
                if cfg.ProtocolOptions.Wumbo() {
×
UNCOV
1029
                        cfg.MaxChanSize = int64(funding.MaxBtcFundingAmountWumbo)
×
UNCOV
1030
                } else {
×
UNCOV
1031
                        cfg.MaxChanSize = int64(funding.MaxBtcFundingAmount)
×
UNCOV
1032
                }
×
1033
        }
1034

1035
        // Ensure that the user specified values for the min and max channel
1036
        // size make sense.
UNCOV
1037
        if cfg.MaxChanSize < cfg.MinChanSize {
×
1038
                return nil, mkErr("invalid channel size parameters: "+
×
1039
                        "max channel size %v, must be no less than min chan "+
×
1040
                        "size %v", cfg.MaxChanSize, cfg.MinChanSize,
×
1041
                )
×
1042
        }
×
1043

1044
        // Don't allow superfluous --maxchansize greater than
1045
        // BOLT 02 soft-limit for non-wumbo channel
UNCOV
1046
        if !cfg.ProtocolOptions.Wumbo() &&
×
UNCOV
1047
                cfg.MaxChanSize > int64(MaxFundingAmount) {
×
1048

×
1049
                return nil, mkErr("invalid channel size parameters: "+
×
1050
                        "maximum channel size %v is greater than maximum "+
×
1051
                        "non-wumbo channel size %v", cfg.MaxChanSize,
×
1052
                        MaxFundingAmount,
×
1053
                )
×
1054
        }
×
1055

1056
        // Ensure that the amount data for revoked commitment transactions is
1057
        // stored if the watchtower client is active.
UNCOV
1058
        if cfg.DB.NoRevLogAmtData && cfg.WtClient.Active {
×
1059
                return nil, mkErr("revocation log amount data must be stored " +
×
1060
                        "if the watchtower client is active")
×
1061
        }
×
1062

1063
        // Ensure a valid max channel fee allocation was set.
UNCOV
1064
        if cfg.MaxChannelFeeAllocation <= 0 || cfg.MaxChannelFeeAllocation > 1 {
×
1065
                return nil, mkErr("invalid max channel fee allocation: %v, "+
×
1066
                        "must be within (0, 1]", cfg.MaxChannelFeeAllocation)
×
1067
        }
×
1068

UNCOV
1069
        if cfg.MaxCommitFeeRateAnchors < 1 {
×
1070
                return nil, mkErr("invalid max commit fee rate anchors: %v, "+
×
1071
                        "must be at least 1 sat/vByte",
×
1072
                        cfg.MaxCommitFeeRateAnchors)
×
1073
        }
×
1074

1075
        // Validate the Tor config parameters.
UNCOV
1076
        socks, err := lncfg.ParseAddressString(
×
UNCOV
1077
                cfg.Tor.SOCKS, strconv.Itoa(defaultTorSOCKSPort),
×
UNCOV
1078
                cfg.net.ResolveTCPAddr,
×
UNCOV
1079
        )
×
UNCOV
1080
        if err != nil {
×
1081
                return nil, err
×
1082
        }
×
UNCOV
1083
        cfg.Tor.SOCKS = socks.String()
×
UNCOV
1084

×
UNCOV
1085
        // We'll only attempt to normalize and resolve the DNS host if it hasn't
×
UNCOV
1086
        // changed, as it doesn't need to be done for the default.
×
UNCOV
1087
        if cfg.Tor.DNS != defaultTorDNS {
×
1088
                dns, err := lncfg.ParseAddressString(
×
1089
                        cfg.Tor.DNS, strconv.Itoa(defaultTorDNSPort),
×
1090
                        cfg.net.ResolveTCPAddr,
×
1091
                )
×
1092
                if err != nil {
×
1093
                        return nil, mkErr("error parsing tor dns: %v", err)
×
1094
                }
×
1095
                cfg.Tor.DNS = dns.String()
×
1096
        }
1097

UNCOV
1098
        control, err := lncfg.ParseAddressString(
×
UNCOV
1099
                cfg.Tor.Control, strconv.Itoa(defaultTorControlPort),
×
UNCOV
1100
                cfg.net.ResolveTCPAddr,
×
UNCOV
1101
        )
×
UNCOV
1102
        if err != nil {
×
1103
                return nil, mkErr("error parsing tor control address: %v", err)
×
1104
        }
×
UNCOV
1105
        cfg.Tor.Control = control.String()
×
UNCOV
1106

×
UNCOV
1107
        // Ensure that tor socks host:port is not equal to tor control
×
UNCOV
1108
        // host:port. This would lead to lnd not starting up properly.
×
UNCOV
1109
        if cfg.Tor.SOCKS == cfg.Tor.Control {
×
1110
                str := "tor.socks and tor.control can not us the same host:port"
×
1111

×
1112
                return nil, mkErr(str)
×
1113
        }
×
1114

UNCOV
1115
        switch {
×
1116
        case cfg.Tor.V2 && cfg.Tor.V3:
×
1117
                return nil, mkErr("either tor.v2 or tor.v3 can be set, " +
×
1118
                        "but not both")
×
1119
        case cfg.DisableListen && (cfg.Tor.V2 || cfg.Tor.V3):
×
1120
                return nil, mkErr("listening must be enabled when enabling " +
×
1121
                        "inbound connections over Tor")
×
1122
        }
1123

UNCOV
1124
        if cfg.Tor.PrivateKeyPath == "" {
×
UNCOV
1125
                switch {
×
1126
                case cfg.Tor.V2:
×
1127
                        cfg.Tor.PrivateKeyPath = filepath.Join(
×
1128
                                lndDir, defaultTorV2PrivateKeyFilename,
×
1129
                        )
×
1130
                case cfg.Tor.V3:
×
1131
                        cfg.Tor.PrivateKeyPath = filepath.Join(
×
1132
                                lndDir, defaultTorV3PrivateKeyFilename,
×
1133
                        )
×
1134
                }
1135
        }
1136

UNCOV
1137
        if cfg.Tor.WatchtowerKeyPath == "" {
×
UNCOV
1138
                switch {
×
1139
                case cfg.Tor.V2:
×
1140
                        cfg.Tor.WatchtowerKeyPath = filepath.Join(
×
1141
                                cfg.Watchtower.TowerDir,
×
1142
                                defaultTorV2PrivateKeyFilename,
×
1143
                        )
×
1144
                case cfg.Tor.V3:
×
1145
                        cfg.Tor.WatchtowerKeyPath = filepath.Join(
×
1146
                                cfg.Watchtower.TowerDir,
×
1147
                                defaultTorV3PrivateKeyFilename,
×
1148
                        )
×
1149
                }
1150
        }
1151

1152
        // Set up the network-related functions that will be used throughout
1153
        // the daemon. We use the standard Go "net" package functions by
1154
        // default. If we should be proxying all traffic through Tor, then
1155
        // we'll use the Tor proxy specific functions in order to avoid leaking
1156
        // our real information.
UNCOV
1157
        if cfg.Tor.Active {
×
1158
                cfg.net = &tor.ProxyNet{
×
1159
                        SOCKS:                       cfg.Tor.SOCKS,
×
1160
                        DNS:                         cfg.Tor.DNS,
×
1161
                        StreamIsolation:             cfg.Tor.StreamIsolation,
×
1162
                        SkipProxyForClearNetTargets: cfg.Tor.SkipProxyForClearNetTargets,
×
1163
                }
×
1164
        }
×
1165

UNCOV
1166
        if cfg.DisableListen && cfg.NAT {
×
1167
                return nil, mkErr("NAT traversal cannot be used when " +
×
1168
                        "listening is disabled")
×
1169
        }
×
UNCOV
1170
        if cfg.NAT && len(cfg.ExternalHosts) != 0 {
×
1171
                return nil, mkErr("NAT support and externalhosts are " +
×
1172
                        "mutually exclusive, only one should be selected")
×
1173
        }
×
1174

1175
        // Multiple networks can't be selected simultaneously.  Count
1176
        // number of network flags passed; assign active network params
1177
        // while we're at it.
UNCOV
1178
        numNets := 0
×
UNCOV
1179
        if cfg.Bitcoin.MainNet {
×
1180
                numNets++
×
1181
                cfg.ActiveNetParams = chainreg.BitcoinMainNetParams
×
1182
        }
×
UNCOV
1183
        if cfg.Bitcoin.TestNet3 {
×
1184
                numNets++
×
1185
                cfg.ActiveNetParams = chainreg.BitcoinTestNetParams
×
1186
        }
×
UNCOV
1187
        if cfg.Bitcoin.RegTest {
×
UNCOV
1188
                numNets++
×
UNCOV
1189
                cfg.ActiveNetParams = chainreg.BitcoinRegTestNetParams
×
UNCOV
1190
        }
×
UNCOV
1191
        if cfg.Bitcoin.SimNet {
×
1192
                numNets++
×
1193
                cfg.ActiveNetParams = chainreg.BitcoinSimNetParams
×
1194

×
1195
                // For simnet, the btcsuite chain params uses a
×
1196
                // cointype of 115. However, we override this in
×
1197
                // chainreg/chainparams.go, but the raw ChainParam
×
1198
                // field is used elsewhere. To ensure everything is
×
1199
                // consistent, we'll also override the cointype within
×
1200
                // the raw params.
×
1201
                targetCoinType := chainreg.BitcoinSigNetParams.CoinType
×
1202
                cfg.ActiveNetParams.Params.HDCoinType = targetCoinType
×
1203
        }
×
UNCOV
1204
        if cfg.Bitcoin.SigNet {
×
1205
                numNets++
×
1206
                cfg.ActiveNetParams = chainreg.BitcoinSigNetParams
×
1207

×
1208
                // Let the user overwrite the default signet parameters.
×
1209
                // The challenge defines the actual signet network to
×
1210
                // join and the seed nodes are needed for network
×
1211
                // discovery.
×
1212
                sigNetChallenge := chaincfg.DefaultSignetChallenge
×
1213
                sigNetSeeds := chaincfg.DefaultSignetDNSSeeds
×
1214
                if cfg.Bitcoin.SigNetChallenge != "" {
×
1215
                        challenge, err := hex.DecodeString(
×
1216
                                cfg.Bitcoin.SigNetChallenge,
×
1217
                        )
×
1218
                        if err != nil {
×
1219
                                return nil, mkErr("Invalid "+
×
1220
                                        "signet challenge, hex decode "+
×
1221
                                        "failed: %v", err)
×
1222
                        }
×
1223
                        sigNetChallenge = challenge
×
1224
                }
1225

1226
                if len(cfg.Bitcoin.SigNetSeedNode) > 0 {
×
1227
                        sigNetSeeds = make([]chaincfg.DNSSeed, len(
×
1228
                                cfg.Bitcoin.SigNetSeedNode,
×
1229
                        ))
×
1230
                        for idx, seed := range cfg.Bitcoin.SigNetSeedNode {
×
1231
                                sigNetSeeds[idx] = chaincfg.DNSSeed{
×
1232
                                        Host:         seed,
×
1233
                                        HasFiltering: false,
×
1234
                                }
×
1235
                        }
×
1236
                }
1237

1238
                chainParams := chaincfg.CustomSignetParams(
×
1239
                        sigNetChallenge, sigNetSeeds,
×
1240
                )
×
1241
                cfg.ActiveNetParams.Params = &chainParams
×
1242
        }
UNCOV
1243
        if numNets > 1 {
×
1244
                str := "The mainnet, testnet, regtest, simnet and signet " +
×
1245
                        "params can't be used together -- choose one " +
×
1246
                        "of the five"
×
1247

×
1248
                return nil, mkErr(str)
×
1249
        }
×
1250

1251
        // The target network must be provided, otherwise, we won't
1252
        // know how to initialize the daemon.
UNCOV
1253
        if numNets == 0 {
×
1254
                str := "either --bitcoin.mainnet, or bitcoin.testnet," +
×
1255
                        "bitcoin.simnet, bitcoin.regtest or bitcoin.signet " +
×
1256
                        "must be specified"
×
1257

×
1258
                return nil, mkErr(str)
×
1259
        }
×
1260

UNCOV
1261
        err = cfg.Bitcoin.Validate(minTimeLockDelta, funding.MinBtcRemoteDelay)
×
UNCOV
1262
        if err != nil {
×
1263
                return nil, mkErr("error validating bitcoin params: %v", err)
×
1264
        }
×
1265

UNCOV
1266
        switch cfg.Bitcoin.Node {
×
UNCOV
1267
        case btcdBackendName:
×
UNCOV
1268
                err := parseRPCParams(
×
UNCOV
1269
                        cfg.Bitcoin, cfg.BtcdMode, cfg.ActiveNetParams,
×
UNCOV
1270
                )
×
UNCOV
1271
                if err != nil {
×
1272
                        return nil, mkErr("unable to load RPC "+
×
1273
                                "credentials for btcd: %v", err)
×
1274
                }
×
UNCOV
1275
        case bitcoindBackendName:
×
UNCOV
1276
                if cfg.Bitcoin.SimNet {
×
1277
                        return nil, mkErr("bitcoind does not " +
×
1278
                                "support simnet")
×
1279
                }
×
1280

UNCOV
1281
                err := parseRPCParams(
×
UNCOV
1282
                        cfg.Bitcoin, cfg.BitcoindMode, cfg.ActiveNetParams,
×
UNCOV
1283
                )
×
UNCOV
1284
                if err != nil {
×
1285
                        return nil, mkErr("unable to load RPC "+
×
1286
                                "credentials for bitcoind: %v", err)
×
1287
                }
×
UNCOV
1288
        case neutrinoBackendName:
×
1289
                // No need to get RPC parameters.
1290

1291
        case "nochainbackend":
×
1292
                // Nothing to configure, we're running without any chain
1293
                // backend whatsoever (pure signing mode).
1294

1295
        default:
×
1296
                str := "only btcd, bitcoind, and neutrino mode " +
×
1297
                        "supported for bitcoin at this time"
×
1298

×
1299
                return nil, mkErr(str)
×
1300
        }
1301

UNCOV
1302
        cfg.Bitcoin.ChainDir = filepath.Join(
×
UNCOV
1303
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
×
UNCOV
1304
        )
×
UNCOV
1305

×
UNCOV
1306
        // Ensure that the user didn't attempt to specify negative values for
×
UNCOV
1307
        // any of the autopilot params.
×
UNCOV
1308
        if cfg.Autopilot.MaxChannels < 0 {
×
1309
                str := "autopilot.maxchannels must be non-negative"
×
1310

×
1311
                return nil, mkErr(str)
×
1312
        }
×
UNCOV
1313
        if cfg.Autopilot.Allocation < 0 {
×
1314
                str := "autopilot.allocation must be non-negative"
×
1315

×
1316
                return nil, mkErr(str)
×
1317
        }
×
UNCOV
1318
        if cfg.Autopilot.MinChannelSize < 0 {
×
1319
                str := "autopilot.minchansize must be non-negative"
×
1320

×
1321
                return nil, mkErr(str)
×
1322
        }
×
UNCOV
1323
        if cfg.Autopilot.MaxChannelSize < 0 {
×
1324
                str := "autopilot.maxchansize must be non-negative"
×
1325

×
1326
                return nil, mkErr(str)
×
1327
        }
×
1328

1329
        // Ensure that the specified values for the min and max channel size
1330
        // don't are within the bounds of the normal chan size constraints.
UNCOV
1331
        if cfg.Autopilot.MinChannelSize < int64(funding.MinChanFundingSize) {
×
1332
                cfg.Autopilot.MinChannelSize = int64(funding.MinChanFundingSize)
×
1333
        }
×
UNCOV
1334
        if cfg.Autopilot.MaxChannelSize > int64(MaxFundingAmount) {
×
1335
                cfg.Autopilot.MaxChannelSize = int64(MaxFundingAmount)
×
1336
        }
×
1337

1338
        // We'll now construct the network directory which will be where we
1339
        // store all the data specific to this chain/network.
UNCOV
1340
        cfg.networkDir = filepath.Join(
×
UNCOV
1341
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
×
UNCOV
1342
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
×
UNCOV
1343
        )
×
UNCOV
1344

×
UNCOV
1345
        // If a custom macaroon directory wasn't specified and the data
×
UNCOV
1346
        // directory has changed from the default path, then we'll also update
×
UNCOV
1347
        // the path for the macaroons to be generated.
×
UNCOV
1348
        if cfg.AdminMacPath == "" {
×
1349
                cfg.AdminMacPath = filepath.Join(
×
1350
                        cfg.networkDir, defaultAdminMacFilename,
×
1351
                )
×
1352
        }
×
UNCOV
1353
        if cfg.ReadMacPath == "" {
×
1354
                cfg.ReadMacPath = filepath.Join(
×
1355
                        cfg.networkDir, defaultReadMacFilename,
×
1356
                )
×
1357
        }
×
UNCOV
1358
        if cfg.InvoiceMacPath == "" {
×
1359
                cfg.InvoiceMacPath = filepath.Join(
×
1360
                        cfg.networkDir, defaultInvoiceMacFilename,
×
1361
                )
×
1362
        }
×
1363

UNCOV
1364
        towerDir := filepath.Join(
×
UNCOV
1365
                cfg.Watchtower.TowerDir, BitcoinChainName,
×
UNCOV
1366
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
×
UNCOV
1367
        )
×
UNCOV
1368

×
UNCOV
1369
        // Create the lnd directory and all other sub-directories if they don't
×
UNCOV
1370
        // already exist. This makes sure that directory trees are also created
×
UNCOV
1371
        // for files that point to outside the lnddir.
×
UNCOV
1372
        dirs := []string{
×
UNCOV
1373
                lndDir, cfg.DataDir, cfg.networkDir,
×
UNCOV
1374
                cfg.LetsEncryptDir, towerDir, cfg.graphDatabaseDir(),
×
UNCOV
1375
                filepath.Dir(cfg.TLSCertPath), filepath.Dir(cfg.TLSKeyPath),
×
UNCOV
1376
                filepath.Dir(cfg.AdminMacPath), filepath.Dir(cfg.ReadMacPath),
×
UNCOV
1377
                filepath.Dir(cfg.InvoiceMacPath),
×
UNCOV
1378
                filepath.Dir(cfg.Tor.PrivateKeyPath),
×
UNCOV
1379
                filepath.Dir(cfg.Tor.WatchtowerKeyPath),
×
UNCOV
1380
        }
×
UNCOV
1381
        for _, dir := range dirs {
×
UNCOV
1382
                if err := makeDirectory(dir); err != nil {
×
1383
                        return nil, err
×
1384
                }
×
1385
        }
1386

1387
        // Similarly, if a custom back up file path wasn't specified, then
1388
        // we'll update the file location to match our set network directory.
UNCOV
1389
        if cfg.BackupFilePath == "" {
×
UNCOV
1390
                cfg.BackupFilePath = filepath.Join(
×
UNCOV
1391
                        cfg.networkDir, chanbackup.DefaultBackupFileName,
×
UNCOV
1392
                )
×
UNCOV
1393
        }
×
1394

1395
        // Append the network type to the log directory so it is "namespaced"
1396
        // per network in the same fashion as the data directory.
UNCOV
1397
        cfg.LogDir = filepath.Join(
×
UNCOV
1398
                cfg.LogDir, BitcoinChainName,
×
UNCOV
1399
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
×
UNCOV
1400
        )
×
UNCOV
1401

×
UNCOV
1402
        if err := cfg.LogConfig.Validate(); err != nil {
×
1403
                return nil, mkErr("error validating logging config: %w", err)
×
1404
        }
×
1405

UNCOV
1406
        cfg.SubLogMgr = build.NewSubLoggerManager(build.NewDefaultLogHandlers(
×
UNCOV
1407
                cfg.LogConfig, cfg.LogRotator,
×
UNCOV
1408
        )...)
×
UNCOV
1409

×
UNCOV
1410
        // Initialize logging at the default logging level.
×
UNCOV
1411
        SetupLoggers(cfg.SubLogMgr, interceptor)
×
UNCOV
1412

×
UNCOV
1413
        // Special show command to list supported subsystems and exit.
×
UNCOV
1414
        if cfg.DebugLevel == "show" {
×
1415
                fmt.Println("Supported subsystems",
×
1416
                        cfg.SubLogMgr.SupportedSubsystems())
×
1417
                os.Exit(0)
×
1418
        }
×
1419

UNCOV
1420
        if cfg.MaxLogFiles != build.DefaultMaxLogFiles {
×
1421
                if cfg.LogConfig.File.MaxLogFiles !=
×
1422
                        build.DefaultMaxLogFiles {
×
1423

×
1424
                        return nil, mkErr("cannot set both maxlogfiles and "+
×
1425
                                "logging.file.max-files", err)
×
1426
                }
×
1427

1428
                cfg.LogConfig.File.MaxLogFiles = cfg.MaxLogFiles
×
1429
        }
UNCOV
1430
        if cfg.MaxLogFileSize != build.DefaultMaxLogFileSize {
×
1431
                if cfg.LogConfig.File.MaxLogFileSize !=
×
1432
                        build.DefaultMaxLogFileSize {
×
1433

×
1434
                        return nil, mkErr("cannot set both maxlogfilesize and "+
×
1435
                                "logging.file.max-file-size", err)
×
1436
                }
×
1437

1438
                cfg.LogConfig.File.MaxLogFileSize = cfg.MaxLogFileSize
×
1439
        }
1440

UNCOV
1441
        err = cfg.LogRotator.InitLogRotator(
×
UNCOV
1442
                cfg.LogConfig.File,
×
UNCOV
1443
                filepath.Join(cfg.LogDir, defaultLogFilename),
×
UNCOV
1444
        )
×
UNCOV
1445
        if err != nil {
×
1446
                str := "log rotation setup failed: %v"
×
1447
                return nil, mkErr(str, err)
×
1448
        }
×
1449

1450
        // Parse, validate, and set debug log level(s).
UNCOV
1451
        err = build.ParseAndSetDebugLevels(cfg.DebugLevel, cfg.SubLogMgr)
×
UNCOV
1452
        if err != nil {
×
1453
                str := "error parsing debug level: %v"
×
1454
                return nil, &lncfg.UsageError{Err: mkErr(str, err)}
×
1455
        }
×
1456

1457
        // At least one RPCListener is required. So listen on localhost per
1458
        // default.
UNCOV
1459
        if len(cfg.RawRPCListeners) == 0 {
×
1460
                addr := fmt.Sprintf("localhost:%d", defaultRPCPort)
×
1461
                cfg.RawRPCListeners = append(cfg.RawRPCListeners, addr)
×
1462
        }
×
1463

1464
        // Listen on localhost if no REST listeners were specified.
UNCOV
1465
        if len(cfg.RawRESTListeners) == 0 {
×
1466
                addr := fmt.Sprintf("localhost:%d", defaultRESTPort)
×
1467
                cfg.RawRESTListeners = append(cfg.RawRESTListeners, addr)
×
1468
        }
×
1469

1470
        // Listen on the default interface/port if no listeners were specified.
1471
        // An empty address string means default interface/address, which on
1472
        // most unix systems is the same as 0.0.0.0. If Tor is active, we
1473
        // default to only listening on localhost for hidden service
1474
        // connections.
UNCOV
1475
        if len(cfg.RawListeners) == 0 {
×
1476
                addr := fmt.Sprintf(":%d", defaultPeerPort)
×
1477
                if cfg.Tor.Active && !cfg.Tor.SkipProxyForClearNetTargets {
×
1478
                        addr = fmt.Sprintf("localhost:%d", defaultPeerPort)
×
1479
                }
×
1480
                cfg.RawListeners = append(cfg.RawListeners, addr)
×
1481
        }
1482

1483
        // Add default port to all RPC listener addresses if needed and remove
1484
        // duplicate addresses.
UNCOV
1485
        cfg.RPCListeners, err = lncfg.NormalizeAddresses(
×
UNCOV
1486
                cfg.RawRPCListeners, strconv.Itoa(defaultRPCPort),
×
UNCOV
1487
                cfg.net.ResolveTCPAddr,
×
UNCOV
1488
        )
×
UNCOV
1489
        if err != nil {
×
1490
                return nil, mkErr("error normalizing RPC listen addrs: %v", err)
×
1491
        }
×
1492

1493
        // Add default port to all REST listener addresses if needed and remove
1494
        // duplicate addresses.
UNCOV
1495
        cfg.RESTListeners, err = lncfg.NormalizeAddresses(
×
UNCOV
1496
                cfg.RawRESTListeners, strconv.Itoa(defaultRESTPort),
×
UNCOV
1497
                cfg.net.ResolveTCPAddr,
×
UNCOV
1498
        )
×
UNCOV
1499
        if err != nil {
×
1500
                return nil, mkErr("error normalizing REST listen addrs: %v", err)
×
1501
        }
×
1502

UNCOV
1503
        switch {
×
1504
        // The no seed backup and auto unlock are mutually exclusive.
1505
        case cfg.NoSeedBackup && cfg.WalletUnlockPasswordFile != "":
×
1506
                return nil, mkErr("cannot set noseedbackup and " +
×
1507
                        "wallet-unlock-password-file at the same time")
×
1508

1509
        // The "allow-create" flag cannot be set without the auto unlock file.
1510
        case cfg.WalletUnlockAllowCreate && cfg.WalletUnlockPasswordFile == "":
×
1511
                return nil, mkErr("cannot set wallet-unlock-allow-create " +
×
1512
                        "without wallet-unlock-password-file")
×
1513

1514
        // If a password file was specified, we need it to exist.
1515
        case cfg.WalletUnlockPasswordFile != "" &&
1516
                !lnrpc.FileExists(cfg.WalletUnlockPasswordFile):
×
1517

×
1518
                return nil, mkErr("wallet unlock password file %s does "+
×
1519
                        "not exist", cfg.WalletUnlockPasswordFile)
×
1520
        }
1521

1522
        // For each of the RPC listeners (REST+gRPC), we'll ensure that users
1523
        // have specified a safe combo for authentication. If not, we'll bail
1524
        // out with an error. Since we don't allow disabling TLS for gRPC
1525
        // connections we pass in tlsActive=true.
UNCOV
1526
        err = lncfg.EnforceSafeAuthentication(
×
UNCOV
1527
                cfg.RPCListeners, !cfg.NoMacaroons, true,
×
UNCOV
1528
        )
×
UNCOV
1529
        if err != nil {
×
1530
                return nil, mkErr("error enforcing safe authentication on "+
×
1531
                        "RPC ports: %v", err)
×
1532
        }
×
1533

UNCOV
1534
        if cfg.DisableRest {
×
1535
                ltndLog.Infof("REST API is disabled!")
×
1536
                cfg.RESTListeners = nil
×
UNCOV
1537
        } else {
×
UNCOV
1538
                err = lncfg.EnforceSafeAuthentication(
×
UNCOV
1539
                        cfg.RESTListeners, !cfg.NoMacaroons, !cfg.DisableRestTLS,
×
UNCOV
1540
                )
×
UNCOV
1541
                if err != nil {
×
1542
                        return nil, mkErr("error enforcing safe "+
×
1543
                                "authentication on REST ports: %v", err)
×
1544
                }
×
1545
        }
1546

1547
        // Remove the listening addresses specified if listening is disabled.
UNCOV
1548
        if cfg.DisableListen {
×
UNCOV
1549
                ltndLog.Infof("Listening on the p2p interface is disabled!")
×
UNCOV
1550
                cfg.Listeners = nil
×
UNCOV
1551
                cfg.ExternalIPs = nil
×
UNCOV
1552
        } else {
×
UNCOV
1553

×
UNCOV
1554
                // Add default port to all listener addresses if needed and remove
×
UNCOV
1555
                // duplicate addresses.
×
UNCOV
1556
                cfg.Listeners, err = lncfg.NormalizeAddresses(
×
UNCOV
1557
                        cfg.RawListeners, strconv.Itoa(defaultPeerPort),
×
UNCOV
1558
                        cfg.net.ResolveTCPAddr,
×
UNCOV
1559
                )
×
UNCOV
1560
                if err != nil {
×
1561
                        return nil, mkErr("error normalizing p2p listen "+
×
1562
                                "addrs: %v", err)
×
1563
                }
×
1564

1565
                // Add default port to all external IP addresses if needed and remove
1566
                // duplicate addresses.
UNCOV
1567
                cfg.ExternalIPs, err = lncfg.NormalizeAddresses(
×
UNCOV
1568
                        cfg.RawExternalIPs, strconv.Itoa(defaultPeerPort),
×
UNCOV
1569
                        cfg.net.ResolveTCPAddr,
×
UNCOV
1570
                )
×
UNCOV
1571
                if err != nil {
×
1572
                        return nil, err
×
1573
                }
×
1574

1575
                // For the p2p port it makes no sense to listen to an Unix socket.
1576
                // Also, we would need to refactor the brontide listener to support
1577
                // that.
UNCOV
1578
                for _, p2pListener := range cfg.Listeners {
×
UNCOV
1579
                        if lncfg.IsUnix(p2pListener) {
×
1580
                                return nil, mkErr("unix socket addresses "+
×
1581
                                        "cannot be used for the p2p "+
×
1582
                                        "connection listener: %s", p2pListener)
×
1583
                        }
×
1584
                }
1585
        }
1586

1587
        // Ensure that the specified minimum backoff is below or equal to the
1588
        // maximum backoff.
UNCOV
1589
        if cfg.MinBackoff > cfg.MaxBackoff {
×
1590
                return nil, mkErr("maxbackoff must be greater than minbackoff")
×
1591
        }
×
1592

1593
        // Newer versions of lnd added a new sub-config for bolt-specific
1594
        // parameters. However, we want to also allow existing users to use the
1595
        // value on the top-level config. If the outer config value is set,
1596
        // then we'll use that directly.
UNCOV
1597
        flagSet, err := isSet("SyncFreelist")
×
UNCOV
1598
        if err != nil {
×
1599
                return nil, mkErr("error parsing freelist sync flag: %v", err)
×
1600
        }
×
UNCOV
1601
        if flagSet {
×
1602
                cfg.DB.Bolt.NoFreelistSync = !cfg.SyncFreelist
×
1603
        }
×
1604

1605
        // Parse any extra sqlite pragma options that may have been provided
1606
        // to determine if they override any of the defaults that we will
1607
        // otherwise add.
UNCOV
1608
        var (
×
UNCOV
1609
                defaultSynchronous = true
×
UNCOV
1610
                defaultAutoVacuum  = true
×
UNCOV
1611
                defaultFullfsync   = true
×
UNCOV
1612
        )
×
UNCOV
1613
        for _, option := range cfg.DB.Sqlite.PragmaOptions {
×
1614
                switch {
×
1615
                case strings.HasPrefix(option, "synchronous="):
×
1616
                        defaultSynchronous = false
×
1617

1618
                case strings.HasPrefix(option, "auto_vacuum="):
×
1619
                        defaultAutoVacuum = false
×
1620

1621
                case strings.HasPrefix(option, "fullfsync="):
×
1622
                        defaultFullfsync = false
×
1623

1624
                default:
×
1625
                }
1626
        }
1627

UNCOV
1628
        if defaultSynchronous {
×
UNCOV
1629
                cfg.DB.Sqlite.PragmaOptions = append(
×
UNCOV
1630
                        cfg.DB.Sqlite.PragmaOptions, "synchronous=full",
×
UNCOV
1631
                )
×
UNCOV
1632
        }
×
1633

UNCOV
1634
        if defaultAutoVacuum {
×
UNCOV
1635
                cfg.DB.Sqlite.PragmaOptions = append(
×
UNCOV
1636
                        cfg.DB.Sqlite.PragmaOptions, "auto_vacuum=incremental",
×
UNCOV
1637
                )
×
UNCOV
1638
        }
×
1639

UNCOV
1640
        if defaultFullfsync {
×
UNCOV
1641
                cfg.DB.Sqlite.PragmaOptions = append(
×
UNCOV
1642
                        cfg.DB.Sqlite.PragmaOptions, "fullfsync=true",
×
UNCOV
1643
                )
×
UNCOV
1644
        }
×
1645

1646
        // Ensure that the user hasn't chosen a remote-max-htlc value greater
1647
        // than the protocol maximum.
UNCOV
1648
        maxRemoteHtlcs := uint16(input.MaxHTLCNumber / 2)
×
UNCOV
1649
        if cfg.DefaultRemoteMaxHtlcs > maxRemoteHtlcs {
×
1650
                return nil, mkErr("default-remote-max-htlcs (%v) must be "+
×
1651
                        "less than %v", cfg.DefaultRemoteMaxHtlcs,
×
1652
                        maxRemoteHtlcs)
×
1653
        }
×
1654

1655
        // Clamp the ChannelCommitInterval so that commitment updates can still
1656
        // happen in a reasonable timeframe.
UNCOV
1657
        if cfg.ChannelCommitInterval > maxChannelCommitInterval {
×
1658
                return nil, mkErr("channel-commit-interval (%v) must be less "+
×
1659
                        "than %v", cfg.ChannelCommitInterval,
×
1660
                        maxChannelCommitInterval)
×
1661
        }
×
1662

1663
        // Limit PendingCommitInterval so we don't wait too long for the remote
1664
        // party to send back a revoke.
UNCOV
1665
        if cfg.PendingCommitInterval > maxPendingCommitInterval {
×
1666
                return nil, mkErr("pending-commit-interval (%v) must be less "+
×
1667
                        "than %v", cfg.PendingCommitInterval,
×
1668
                        maxPendingCommitInterval)
×
1669
        }
×
1670

UNCOV
1671
        if err := cfg.Gossip.Parse(); err != nil {
×
1672
                return nil, mkErr("error parsing gossip syncer: %v", err)
×
1673
        }
×
1674

1675
        // If the experimental protocol options specify any protocol messages
1676
        // that we want to handle as custom messages, set them now.
UNCOV
1677
        customMsg := cfg.ProtocolOptions.CustomMessageOverrides()
×
UNCOV
1678

×
UNCOV
1679
        // We can safely set our custom override values during startup because
×
UNCOV
1680
        // startup is blocked on config parsing.
×
UNCOV
1681
        if err := lnwire.SetCustomOverrides(customMsg); err != nil {
×
1682
                return nil, mkErr("custom-message: %v", err)
×
1683
        }
×
1684

1685
        // Map old pprof flags to new pprof group flags.
1686
        //
1687
        // NOTE: This is a temporary measure to ensure compatibility with old
1688
        // flags.
UNCOV
1689
        if cfg.CPUProfile != "" {
×
1690
                if cfg.Pprof.CPUProfile != "" {
×
1691
                        return nil, mkErr("cpuprofile and pprof.cpuprofile " +
×
1692
                                "are mutually exclusive")
×
1693
                }
×
1694
                cfg.Pprof.CPUProfile = cfg.CPUProfile
×
1695
        }
UNCOV
1696
        if cfg.Profile != "" {
×
1697
                if cfg.Pprof.Profile != "" {
×
1698
                        return nil, mkErr("profile and pprof.profile " +
×
1699
                                "are mutually exclusive")
×
1700
                }
×
1701
                cfg.Pprof.Profile = cfg.Profile
×
1702
        }
UNCOV
1703
        if cfg.BlockingProfile != 0 {
×
1704
                if cfg.Pprof.BlockingProfile != 0 {
×
1705
                        return nil, mkErr("blockingprofile and " +
×
1706
                                "pprof.blockingprofile are mutually exclusive")
×
1707
                }
×
1708
                cfg.Pprof.BlockingProfile = cfg.BlockingProfile
×
1709
        }
UNCOV
1710
        if cfg.MutexProfile != 0 {
×
1711
                if cfg.Pprof.MutexProfile != 0 {
×
1712
                        return nil, mkErr("mutexprofile and " +
×
1713
                                "pprof.mutexprofile are mutually exclusive")
×
1714
                }
×
1715
                cfg.Pprof.MutexProfile = cfg.MutexProfile
×
1716
        }
1717

1718
        // Don't allow both the old dust-threshold and the new
1719
        // channel-max-fee-exposure to be set.
UNCOV
1720
        if cfg.DustThreshold != 0 && cfg.MaxFeeExposure != 0 {
×
1721
                return nil, mkErr("cannot set both dust-threshold and " +
×
1722
                        "channel-max-fee-exposure")
×
1723
        }
×
1724

UNCOV
1725
        switch {
×
1726
        // Use the old dust-threshold as the max fee exposure if it is set and
1727
        // the new option is not.
1728
        case cfg.DustThreshold != 0:
×
1729
                cfg.MaxFeeExposure = cfg.DustThreshold
×
1730

1731
        // Use the default max fee exposure if the new option is not set and
1732
        // the old one is not set either.
UNCOV
1733
        case cfg.MaxFeeExposure == 0:
×
UNCOV
1734
                cfg.MaxFeeExposure = uint64(
×
UNCOV
1735
                        htlcswitch.DefaultMaxFeeExposure.ToSatoshis(),
×
UNCOV
1736
                )
×
1737
        }
1738

1739
        // Validate the subconfigs for workers, caches, and the tower client.
UNCOV
1740
        err = lncfg.Validate(
×
UNCOV
1741
                cfg.Workers,
×
UNCOV
1742
                cfg.Caches,
×
UNCOV
1743
                cfg.WtClient,
×
UNCOV
1744
                cfg.DB,
×
UNCOV
1745
                cfg.Cluster,
×
UNCOV
1746
                cfg.HealthChecks,
×
UNCOV
1747
                cfg.RPCMiddleware,
×
UNCOV
1748
                cfg.RemoteSigner,
×
UNCOV
1749
                cfg.Sweeper,
×
UNCOV
1750
                cfg.Htlcswitch,
×
UNCOV
1751
                cfg.Invoices,
×
UNCOV
1752
                cfg.Routing,
×
UNCOV
1753
                cfg.Pprof,
×
UNCOV
1754
        )
×
UNCOV
1755
        if err != nil {
×
1756
                return nil, err
×
1757
        }
×
1758

1759
        // Finally, ensure that the user's color is correctly formatted,
1760
        // otherwise the server will not be able to start after the unlocking
1761
        // the wallet.
UNCOV
1762
        _, err = lncfg.ParseHexColor(cfg.Color)
×
UNCOV
1763
        if err != nil {
×
1764
                return nil, mkErr("unable to parse node color: %v", err)
×
1765
        }
×
1766

1767
        // All good, return the sanitized result.
UNCOV
1768
        return &cfg, nil
×
1769
}
1770

1771
// graphDatabaseDir returns the default directory where the local bolt graph db
1772
// files are stored.
UNCOV
1773
func (c *Config) graphDatabaseDir() string {
×
UNCOV
1774
        return filepath.Join(
×
UNCOV
1775
                c.DataDir, defaultGraphSubDirname,
×
UNCOV
1776
                lncfg.NormalizeNetwork(c.ActiveNetParams.Name),
×
UNCOV
1777
        )
×
UNCOV
1778
}
×
1779

1780
// ImplementationConfig returns the configuration of what actual implementations
1781
// should be used when creating the main lnd instance.
1782
func (c *Config) ImplementationConfig(
UNCOV
1783
        interceptor signal.Interceptor) *ImplementationCfg {
×
UNCOV
1784

×
UNCOV
1785
        // If we're using a remote signer, we still need the base wallet as a
×
UNCOV
1786
        // watch-only source of chain and address data. But we don't need any
×
UNCOV
1787
        // private key material in that btcwallet base wallet.
×
UNCOV
1788
        if c.RemoteSigner.Enable {
×
UNCOV
1789
                rpcImpl := NewRPCSignerWalletImpl(
×
UNCOV
1790
                        c, ltndLog, interceptor,
×
UNCOV
1791
                        c.RemoteSigner.MigrateWatchOnly,
×
UNCOV
1792
                )
×
UNCOV
1793
                return &ImplementationCfg{
×
UNCOV
1794
                        GrpcRegistrar:     rpcImpl,
×
UNCOV
1795
                        RestRegistrar:     rpcImpl,
×
UNCOV
1796
                        ExternalValidator: rpcImpl,
×
UNCOV
1797
                        DatabaseBuilder: NewDefaultDatabaseBuilder(
×
UNCOV
1798
                                c, ltndLog,
×
UNCOV
1799
                        ),
×
UNCOV
1800
                        WalletConfigBuilder: rpcImpl,
×
UNCOV
1801
                        ChainControlBuilder: rpcImpl,
×
UNCOV
1802
                }
×
UNCOV
1803
        }
×
1804

UNCOV
1805
        defaultImpl := NewDefaultWalletImpl(c, ltndLog, interceptor, false)
×
UNCOV
1806
        return &ImplementationCfg{
×
UNCOV
1807
                GrpcRegistrar:       defaultImpl,
×
UNCOV
1808
                RestRegistrar:       defaultImpl,
×
UNCOV
1809
                ExternalValidator:   defaultImpl,
×
UNCOV
1810
                DatabaseBuilder:     NewDefaultDatabaseBuilder(c, ltndLog),
×
UNCOV
1811
                WalletConfigBuilder: defaultImpl,
×
UNCOV
1812
                ChainControlBuilder: defaultImpl,
×
UNCOV
1813
        }
×
1814
}
1815

1816
// CleanAndExpandPath expands environment variables and leading ~ in the
1817
// passed path, cleans the result, and returns it.
1818
// This function is taken from https://github.com/btcsuite/btcd
UNCOV
1819
func CleanAndExpandPath(path string) string {
×
UNCOV
1820
        if path == "" {
×
UNCOV
1821
                return ""
×
UNCOV
1822
        }
×
1823

1824
        // Expand initial ~ to OS specific home directory.
UNCOV
1825
        if strings.HasPrefix(path, "~") {
×
1826
                var homeDir string
×
1827
                u, err := user.Current()
×
1828
                if err == nil {
×
1829
                        homeDir = u.HomeDir
×
1830
                } else {
×
1831
                        homeDir = os.Getenv("HOME")
×
1832
                }
×
1833

1834
                path = strings.Replace(path, "~", homeDir, 1)
×
1835
        }
1836

1837
        // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
1838
        // but the variables can still be expanded via POSIX-style $VARIABLE.
UNCOV
1839
        return filepath.Clean(os.ExpandEnv(path))
×
1840
}
1841

1842
func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
UNCOV
1843
        netParams chainreg.BitcoinNetParams) error {
×
UNCOV
1844

×
UNCOV
1845
        // First, we'll check our node config to make sure the RPC parameters
×
UNCOV
1846
        // were set correctly. We'll also determine the path to the conf file
×
UNCOV
1847
        // depending on the backend node.
×
UNCOV
1848
        var daemonName, confDir, confFile, confFileBase string
×
UNCOV
1849
        switch conf := nodeConfig.(type) {
×
UNCOV
1850
        case *lncfg.Btcd:
×
UNCOV
1851
                // Resolves environment variable references in RPCUser and
×
UNCOV
1852
                // RPCPass fields.
×
UNCOV
1853
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
×
UNCOV
1854
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
×
UNCOV
1855

×
UNCOV
1856
                // If both RPCUser and RPCPass are set, we assume those
×
UNCOV
1857
                // credentials are good to use.
×
UNCOV
1858
                if conf.RPCUser != "" && conf.RPCPass != "" {
×
UNCOV
1859
                        return nil
×
UNCOV
1860
                }
×
1861

1862
                // Set the daemon name for displaying proper errors.
1863
                daemonName = btcdBackendName
×
1864
                confDir = conf.Dir
×
1865
                confFileBase = btcdBackendName
×
1866

×
1867
                // If only ONE of RPCUser or RPCPass is set, we assume the
×
1868
                // user did that unintentionally.
×
1869
                if conf.RPCUser != "" || conf.RPCPass != "" {
×
1870
                        return fmt.Errorf("please set both or neither of "+
×
1871
                                "%[1]v.rpcuser, %[1]v.rpcpass", daemonName)
×
1872
                }
×
1873

UNCOV
1874
        case *lncfg.Bitcoind:
×
UNCOV
1875
                // Ensure that if the ZMQ options are set, that they are not
×
UNCOV
1876
                // equal.
×
UNCOV
1877
                if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
×
UNCOV
1878
                        err := checkZMQOptions(
×
UNCOV
1879
                                conf.ZMQPubRawBlock, conf.ZMQPubRawTx,
×
UNCOV
1880
                        )
×
UNCOV
1881
                        if err != nil {
×
1882
                                return err
×
1883
                        }
×
1884
                }
1885

1886
                // Ensure that if the estimate mode is set, that it is a legal
1887
                // value.
UNCOV
1888
                if conf.EstimateMode != "" {
×
UNCOV
1889
                        err := checkEstimateMode(conf.EstimateMode)
×
UNCOV
1890
                        if err != nil {
×
1891
                                return err
×
1892
                        }
×
1893
                }
1894

1895
                // Set the daemon name for displaying proper errors.
UNCOV
1896
                daemonName = bitcoindBackendName
×
UNCOV
1897
                confDir = conf.Dir
×
UNCOV
1898
                confFile = conf.ConfigPath
×
UNCOV
1899
                confFileBase = BitcoinChainName
×
UNCOV
1900

×
UNCOV
1901
                // Resolves environment variable references in RPCUser
×
UNCOV
1902
                // and RPCPass fields.
×
UNCOV
1903
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
×
UNCOV
1904
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
×
UNCOV
1905

×
UNCOV
1906
                // Check that cookie and credentials don't contradict each
×
UNCOV
1907
                // other.
×
UNCOV
1908
                if (conf.RPCUser != "" || conf.RPCPass != "") &&
×
UNCOV
1909
                        conf.RPCCookie != "" {
×
1910

×
1911
                        return fmt.Errorf("please only provide either "+
×
1912
                                "%[1]v.rpccookie or %[1]v.rpcuser and "+
×
1913
                                "%[1]v.rpcpass", daemonName)
×
1914
                }
×
1915

1916
                // We convert the cookie into a user name and password.
UNCOV
1917
                if conf.RPCCookie != "" {
×
1918
                        cookie, err := os.ReadFile(conf.RPCCookie)
×
1919
                        if err != nil {
×
1920
                                return fmt.Errorf("cannot read cookie file: %w",
×
1921
                                        err)
×
1922
                        }
×
1923

1924
                        splitCookie := strings.Split(string(cookie), ":")
×
1925
                        if len(splitCookie) != 2 {
×
1926
                                return fmt.Errorf("cookie file has a wrong " +
×
1927
                                        "format")
×
1928
                        }
×
1929
                        conf.RPCUser = splitCookie[0]
×
1930
                        conf.RPCPass = splitCookie[1]
×
1931
                }
1932

UNCOV
1933
                if conf.RPCUser != "" && conf.RPCPass != "" {
×
UNCOV
1934
                        // If all of RPCUser, RPCPass, ZMQBlockHost, and
×
UNCOV
1935
                        // ZMQTxHost are set, we assume those parameters are
×
UNCOV
1936
                        // good to use.
×
UNCOV
1937
                        if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
×
UNCOV
1938
                                return nil
×
UNCOV
1939
                        }
×
1940

1941
                        // If RPCUser and RPCPass are set and RPCPolling is
1942
                        // enabled, we assume the parameters are good to use.
UNCOV
1943
                        if conf.RPCPolling {
×
UNCOV
1944
                                return nil
×
UNCOV
1945
                        }
×
1946
                }
1947

1948
                // If not all of the parameters are set, we'll assume the user
1949
                // did this unintentionally.
1950
                if conf.RPCUser != "" || conf.RPCPass != "" ||
×
1951
                        conf.ZMQPubRawBlock != "" || conf.ZMQPubRawTx != "" {
×
1952

×
1953
                        return fmt.Errorf("please set %[1]v.rpcuser and "+
×
1954
                                "%[1]v.rpcpass (or %[1]v.rpccookie) together "+
×
1955
                                "with %[1]v.zmqpubrawblock, %[1]v.zmqpubrawtx",
×
1956
                                daemonName)
×
1957
                }
×
1958
        }
1959

1960
        // If we're in simnet mode, then the running btcd instance won't read
1961
        // the RPC credentials from the configuration. So if lnd wasn't
1962
        // specified the parameters, then we won't be able to start.
1963
        if cConfig.SimNet {
×
1964
                return fmt.Errorf("rpcuser and rpcpass must be set to your " +
×
1965
                        "btcd node's RPC parameters for simnet mode")
×
1966
        }
×
1967

1968
        fmt.Println("Attempting automatic RPC configuration to " + daemonName)
×
1969

×
1970
        if confFile == "" {
×
1971
                confFile = filepath.Join(confDir, fmt.Sprintf("%v.conf",
×
1972
                        confFileBase))
×
1973
        }
×
1974
        switch cConfig.Node {
×
1975
        case btcdBackendName:
×
1976
                nConf := nodeConfig.(*lncfg.Btcd)
×
1977
                rpcUser, rpcPass, err := extractBtcdRPCParams(confFile)
×
1978
                if err != nil {
×
1979
                        return fmt.Errorf("unable to extract RPC credentials: "+
×
1980
                                "%v, cannot start w/o RPC connection", err)
×
1981
                }
×
1982
                nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass
×
1983

1984
        case bitcoindBackendName:
×
1985
                nConf := nodeConfig.(*lncfg.Bitcoind)
×
1986
                rpcUser, rpcPass, zmqBlockHost, zmqTxHost, err :=
×
1987
                        extractBitcoindRPCParams(netParams.Params.Name,
×
1988
                                nConf.Dir, confFile, nConf.RPCCookie)
×
1989
                if err != nil {
×
1990
                        return fmt.Errorf("unable to extract RPC credentials: "+
×
1991
                                "%v, cannot start w/o RPC connection", err)
×
1992
                }
×
1993
                nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass
×
1994
                nConf.ZMQPubRawBlock, nConf.ZMQPubRawTx = zmqBlockHost, zmqTxHost
×
1995
        }
1996

1997
        fmt.Printf("Automatically obtained %v's RPC credentials\n", daemonName)
×
1998
        return nil
×
1999
}
2000

2001
// supplyEnvValue supplies the value of an environment variable from a string.
2002
// It supports the following formats:
2003
// 1) $ENV_VAR
2004
// 2) ${ENV_VAR}
2005
// 3) ${ENV_VAR:-DEFAULT}
2006
//
2007
// Standard environment variable naming conventions:
2008
// - ENV_VAR contains letters, digits, and underscores, and does
2009
// not start with a digit.
2010
// - DEFAULT follows the rule that it can contain any characters except
2011
// whitespace.
2012
//
2013
// Parameters:
2014
// - value: The input string containing references to environment variables
2015
// (if any).
2016
//
2017
// Returns:
2018
// - string: The value of the specified environment variable, the default
2019
// value if provided, or the original input string if no matching variable is
2020
// found or set.
2021
func supplyEnvValue(value string) string {
7✔
2022
        // Regex for $ENV_VAR format.
7✔
2023
        var reEnvVar = regexp.MustCompile(`^\$([a-zA-Z_][a-zA-Z0-9_]*)$`)
7✔
2024

7✔
2025
        // Regex for ${ENV_VAR} format.
7✔
2026
        var reEnvVarWithBrackets = regexp.MustCompile(
7✔
2027
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}$`,
7✔
2028
        )
7✔
2029

7✔
2030
        // Regex for ${ENV_VAR:-DEFAULT} format.
7✔
2031
        var reEnvVarWithDefault = regexp.MustCompile(
7✔
2032
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*):-([\S]+)\}$`,
7✔
2033
        )
7✔
2034

7✔
2035
        // Match against supported formats.
7✔
2036
        switch {
7✔
2037
        case reEnvVarWithDefault.MatchString(value):
3✔
2038
                matches := reEnvVarWithDefault.FindStringSubmatch(value)
3✔
2039
                envVariable := matches[1]
3✔
2040
                defaultValue := matches[2]
3✔
2041
                if envValue := os.Getenv(envVariable); envValue != "" {
4✔
2042
                        return envValue
1✔
2043
                }
1✔
2044

2045
                return defaultValue
2✔
2046

2047
        case reEnvVarWithBrackets.MatchString(value):
×
2048
                matches := reEnvVarWithBrackets.FindStringSubmatch(value)
×
2049
                envVariable := matches[1]
×
2050
                envValue := os.Getenv(envVariable)
×
2051

×
2052
                return envValue
×
2053

2054
        case reEnvVar.MatchString(value):
3✔
2055
                matches := reEnvVar.FindStringSubmatch(value)
3✔
2056
                envVariable := matches[1]
3✔
2057
                envValue := os.Getenv(envVariable)
3✔
2058

3✔
2059
                return envValue
3✔
2060
        }
2061

2062
        return value
1✔
2063
}
2064

2065
// extractBtcdRPCParams attempts to extract the RPC credentials for an existing
2066
// btcd instance. The passed path is expected to be the location of btcd's
2067
// application data directory on the target system.
2068
func extractBtcdRPCParams(btcdConfigPath string) (string, string, error) {
×
2069
        // First, we'll open up the btcd configuration file found at the target
×
2070
        // destination.
×
2071
        btcdConfigFile, err := os.Open(btcdConfigPath)
×
2072
        if err != nil {
×
2073
                return "", "", err
×
2074
        }
×
2075
        defer func() { _ = btcdConfigFile.Close() }()
×
2076

2077
        // With the file open extract the contents of the configuration file so
2078
        // we can attempt to locate the RPC credentials.
2079
        configContents, err := io.ReadAll(btcdConfigFile)
×
2080
        if err != nil {
×
2081
                return "", "", err
×
2082
        }
×
2083

2084
        // Attempt to locate the RPC user using a regular expression. If we
2085
        // don't have a match for our regular expression then we'll exit with
2086
        // an error.
2087
        rpcUserRegexp, err := regexp.Compile(`(?m)^\s*rpcuser\s*=\s*([^\s]+)`)
×
2088
        if err != nil {
×
2089
                return "", "", err
×
2090
        }
×
2091
        userSubmatches := rpcUserRegexp.FindSubmatch(configContents)
×
2092
        if userSubmatches == nil {
×
2093
                return "", "", fmt.Errorf("unable to find rpcuser in config")
×
2094
        }
×
2095

2096
        // Similarly, we'll use another regular expression to find the set
2097
        // rpcpass (if any). If we can't find the pass, then we'll exit with an
2098
        // error.
2099
        rpcPassRegexp, err := regexp.Compile(`(?m)^\s*rpcpass\s*=\s*([^\s]+)`)
×
2100
        if err != nil {
×
2101
                return "", "", err
×
2102
        }
×
2103
        passSubmatches := rpcPassRegexp.FindSubmatch(configContents)
×
2104
        if passSubmatches == nil {
×
2105
                return "", "", fmt.Errorf("unable to find rpcuser in config")
×
2106
        }
×
2107

2108
        return supplyEnvValue(string(userSubmatches[1])),
×
2109
                supplyEnvValue(string(passSubmatches[1])), nil
×
2110
}
2111

2112
// extractBitcoindRPCParams attempts to extract the RPC credentials for an
2113
// existing bitcoind node instance. The routine looks for a cookie first,
2114
// optionally following the datadir configuration option in the bitcoin.conf. If
2115
// it doesn't find one, it looks for rpcuser/rpcpassword.
2116
func extractBitcoindRPCParams(networkName, bitcoindDataDir, bitcoindConfigPath,
2117
        rpcCookiePath string) (string, string, string, string, error) {
×
2118

×
2119
        // First, we'll open up the bitcoind configuration file found at the
×
2120
        // target destination.
×
2121
        bitcoindConfigFile, err := os.Open(bitcoindConfigPath)
×
2122
        if err != nil {
×
2123
                return "", "", "", "", err
×
2124
        }
×
2125
        defer func() { _ = bitcoindConfigFile.Close() }()
×
2126

2127
        // With the file open extract the contents of the configuration file so
2128
        // we can attempt to locate the RPC credentials.
2129
        configContents, err := io.ReadAll(bitcoindConfigFile)
×
2130
        if err != nil {
×
2131
                return "", "", "", "", err
×
2132
        }
×
2133

2134
        // First, we'll look for the ZMQ hosts providing raw block and raw
2135
        // transaction notifications.
2136
        zmqBlockHostRE, err := regexp.Compile(
×
2137
                `(?m)^\s*zmqpubrawblock\s*=\s*([^\s]+)`,
×
2138
        )
×
2139
        if err != nil {
×
2140
                return "", "", "", "", err
×
2141
        }
×
2142
        zmqBlockHostSubmatches := zmqBlockHostRE.FindSubmatch(configContents)
×
2143
        if len(zmqBlockHostSubmatches) < 2 {
×
2144
                return "", "", "", "", fmt.Errorf("unable to find " +
×
2145
                        "zmqpubrawblock in config")
×
2146
        }
×
2147
        zmqTxHostRE, err := regexp.Compile(`(?m)^\s*zmqpubrawtx\s*=\s*([^\s]+)`)
×
2148
        if err != nil {
×
2149
                return "", "", "", "", err
×
2150
        }
×
2151
        zmqTxHostSubmatches := zmqTxHostRE.FindSubmatch(configContents)
×
2152
        if len(zmqTxHostSubmatches) < 2 {
×
2153
                return "", "", "", "", errors.New("unable to find zmqpubrawtx " +
×
2154
                        "in config")
×
2155
        }
×
2156
        zmqBlockHost := string(zmqBlockHostSubmatches[1])
×
2157
        zmqTxHost := string(zmqTxHostSubmatches[1])
×
2158
        if err := checkZMQOptions(zmqBlockHost, zmqTxHost); err != nil {
×
2159
                return "", "", "", "", err
×
2160
        }
×
2161

2162
        // Next, we'll try to find an auth cookie. We need to detect the chain
2163
        // by seeing if one is specified in the configuration file.
2164
        dataDir := filepath.Dir(bitcoindConfigPath)
×
2165
        if bitcoindDataDir != "" {
×
2166
                dataDir = bitcoindDataDir
×
2167
        }
×
2168
        dataDirRE, err := regexp.Compile(`(?m)^\s*datadir\s*=\s*([^\s]+)`)
×
2169
        if err != nil {
×
2170
                return "", "", "", "", err
×
2171
        }
×
2172
        dataDirSubmatches := dataDirRE.FindSubmatch(configContents)
×
2173
        if dataDirSubmatches != nil {
×
2174
                dataDir = string(dataDirSubmatches[1])
×
2175
        }
×
2176

2177
        var chainDir string
×
2178
        switch networkName {
×
2179
        case "mainnet":
×
2180
                chainDir = ""
×
2181
        case "regtest", "testnet3", "signet":
×
2182
                chainDir = networkName
×
2183
        default:
×
2184
                return "", "", "", "", fmt.Errorf("unexpected networkname %v", networkName)
×
2185
        }
2186

2187
        cookiePath := filepath.Join(dataDir, chainDir, ".cookie")
×
2188
        if rpcCookiePath != "" {
×
2189
                cookiePath = rpcCookiePath
×
2190
        }
×
2191
        cookie, err := os.ReadFile(cookiePath)
×
2192
        if err == nil {
×
2193
                splitCookie := strings.Split(string(cookie), ":")
×
2194
                if len(splitCookie) == 2 {
×
2195
                        return splitCookie[0], splitCookie[1], zmqBlockHost,
×
2196
                                zmqTxHost, nil
×
2197
                }
×
2198
        }
2199

2200
        // We didn't find a cookie, so we attempt to locate the RPC user using
2201
        // a regular expression. If we  don't have a match for our regular
2202
        // expression then we'll exit with an error.
2203
        rpcUserRegexp, err := regexp.Compile(`(?m)^\s*rpcuser\s*=\s*([^\s]+)`)
×
2204
        if err != nil {
×
2205
                return "", "", "", "", err
×
2206
        }
×
2207
        userSubmatches := rpcUserRegexp.FindSubmatch(configContents)
×
2208

×
2209
        // Similarly, we'll use another regular expression to find the set
×
2210
        // rpcpass (if any). If we can't find the pass, then we'll exit with an
×
2211
        // error.
×
2212
        rpcPassRegexp, err := regexp.Compile(`(?m)^\s*rpcpassword\s*=\s*([^\s]+)`)
×
2213
        if err != nil {
×
2214
                return "", "", "", "", err
×
2215
        }
×
2216
        passSubmatches := rpcPassRegexp.FindSubmatch(configContents)
×
2217

×
2218
        // Exit with an error if the cookie file, is defined in config, and
×
2219
        // can not be found, with both rpcuser and rpcpassword undefined.
×
2220
        if rpcCookiePath != "" && userSubmatches == nil && passSubmatches == nil {
×
2221
                return "", "", "", "", fmt.Errorf("unable to open cookie file (%v)",
×
2222
                        rpcCookiePath)
×
2223
        }
×
2224

2225
        if userSubmatches == nil {
×
2226
                return "", "", "", "", fmt.Errorf("unable to find rpcuser in " +
×
2227
                        "config")
×
2228
        }
×
2229
        if passSubmatches == nil {
×
2230
                return "", "", "", "", fmt.Errorf("unable to find rpcpassword " +
×
2231
                        "in config")
×
2232
        }
×
2233

2234
        return supplyEnvValue(string(userSubmatches[1])),
×
2235
                supplyEnvValue(string(passSubmatches[1])),
×
2236
                zmqBlockHost, zmqTxHost, nil
×
2237
}
2238

2239
// checkZMQOptions ensures that the provided addresses to use as the hosts for
2240
// ZMQ rawblock and rawtx notifications are different.
UNCOV
2241
func checkZMQOptions(zmqBlockHost, zmqTxHost string) error {
×
UNCOV
2242
        if zmqBlockHost == zmqTxHost {
×
2243
                return errors.New("zmqpubrawblock and zmqpubrawtx must be set " +
×
2244
                        "to different addresses")
×
2245
        }
×
2246

UNCOV
2247
        return nil
×
2248
}
2249

2250
// checkEstimateMode ensures that the provided estimate mode is legal.
UNCOV
2251
func checkEstimateMode(estimateMode string) error {
×
UNCOV
2252
        for _, mode := range bitcoindEstimateModes {
×
UNCOV
2253
                if estimateMode == mode {
×
UNCOV
2254
                        return nil
×
UNCOV
2255
                }
×
2256
        }
2257

2258
        return fmt.Errorf("estimatemode must be one of the following: %v",
×
2259
                bitcoindEstimateModes[:])
×
2260
}
2261

2262
// configToFlatMap converts the given config struct into a flat map of
2263
// key/value pairs using the dot notation we are used to from the config file
2264
// or command line flags. It also returns a map containing deprecated config
2265
// options.
2266
func configToFlatMap(cfg Config) (map[string]string,
2267
        map[string]struct{}, error) {
1✔
2268

1✔
2269
        result := make(map[string]string)
1✔
2270

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

1✔
2276
        // redact is the helper function that redacts sensitive values like
1✔
2277
        // passwords.
1✔
2278
        redact := func(key, value string) string {
308✔
2279
                sensitiveKeySuffixes := []string{
307✔
2280
                        "pass",
307✔
2281
                        "password",
307✔
2282
                        "dsn",
307✔
2283
                }
307✔
2284
                for _, suffix := range sensitiveKeySuffixes {
1,221✔
2285
                        if strings.HasSuffix(key, suffix) {
919✔
2286
                                return "[redacted]"
5✔
2287
                        }
5✔
2288
                }
2289

2290
                return value
302✔
2291
        }
2292

2293
        // printConfig is the helper function that goes into nested structs
2294
        // recursively. Because we call it recursively, we need to declare it
2295
        // before we define it.
2296
        var printConfig func(reflect.Value, string)
1✔
2297
        printConfig = func(obj reflect.Value, prefix string) {
62✔
2298
                // Turn struct pointers into the actual struct, so we can
61✔
2299
                // iterate over the fields as we would with a struct value.
61✔
2300
                if obj.Kind() == reflect.Ptr {
115✔
2301
                        obj = obj.Elem()
54✔
2302
                }
54✔
2303

2304
                // Abort on nil values.
2305
                if !obj.IsValid() {
73✔
2306
                        return
12✔
2307
                }
12✔
2308

2309
                // Loop over all fields of the struct and inspect the type.
2310
                for i := 0; i < obj.NumField(); i++ {
433✔
2311
                        field := obj.Field(i)
384✔
2312
                        fieldType := obj.Type().Field(i)
384✔
2313

384✔
2314
                        longName := fieldType.Tag.Get("long")
384✔
2315
                        namespace := fieldType.Tag.Get("namespace")
384✔
2316
                        group := fieldType.Tag.Get("group")
384✔
2317
                        hidden := fieldType.Tag.Get("hidden")
384✔
2318

384✔
2319
                        switch {
384✔
2320
                        // We have a long name defined, this is a config value.
2321
                        case longName != "":
307✔
2322
                                key := longName
307✔
2323
                                if prefix != "" {
518✔
2324
                                        key = prefix + "." + key
211✔
2325
                                }
211✔
2326

2327
                                // Add the value directly to the flattened map.
2328
                                result[key] = redact(key, fmt.Sprintf(
307✔
2329
                                        "%v", field.Interface(),
307✔
2330
                                ))
307✔
2331

307✔
2332
                                // If there's a hidden flag, it's deprecated.
307✔
2333
                                if hidden == "true" && !field.IsZero() {
310✔
2334
                                        deprecated[key] = struct{}{}
3✔
2335
                                }
3✔
2336

2337
                        // We have no long name but a namespace, this is a
2338
                        // nested struct.
2339
                        case longName == "" && namespace != "":
54✔
2340
                                key := namespace
54✔
2341
                                if prefix != "" {
69✔
2342
                                        key = prefix + "." + key
15✔
2343
                                }
15✔
2344

2345
                                printConfig(field, key)
54✔
2346

2347
                        // Just a group means this is a dummy struct to house
2348
                        // multiple config values, the group name doesn't go
2349
                        // into the final field name.
2350
                        case longName == "" && group != "":
1✔
2351
                                printConfig(field, prefix)
1✔
2352

2353
                        // Anonymous means embedded struct. We need to recurse
2354
                        // into it but without adding anything to the prefix.
2355
                        case fieldType.Anonymous:
5✔
2356
                                printConfig(field, prefix)
5✔
2357

2358
                        default:
17✔
2359
                                continue
17✔
2360
                        }
2361
                }
2362
        }
2363

2364
        // Turn the whole config struct into a flat map.
2365
        printConfig(reflect.ValueOf(cfg), "")
1✔
2366

1✔
2367
        return result, deprecated, nil
1✔
2368
}
2369

2370
// logWarningsForDeprecation logs a warning if a deprecated config option is
2371
// set.
UNCOV
2372
func logWarningsForDeprecation(cfg Config) {
×
UNCOV
2373
        _, deprecated, err := configToFlatMap(cfg)
×
UNCOV
2374
        if err != nil {
×
2375
                ltndLog.Errorf("Convert configs to map: %v", err)
×
2376
        }
×
2377

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