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

lightningnetwork / lnd / 16988342641

15 Aug 2025 10:40AM UTC coverage: 66.751% (-0.01%) from 66.763%
16988342641

Pull #10159

github

web-flow
Merge c32685723 into 365f1788e
Pull Request #10159: [2/2] discovery+lnwire: add support for DNS host name in NodeAnnouncement msg

274 of 360 new or added lines in 10 files covered. (76.11%)

96 existing lines in 19 files now uncovered.

136064 of 203838 relevant lines covered (66.75%)

21491.73 hits per line

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

55.51
/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
        // DefaultNumRestrictedSlots is the default max number of incoming
242
        // connections allowed in the server. Outbound connections are not
243
        // restricted.
244
        DefaultNumRestrictedSlots = 100
245

246
        // BitcoinChainName is a string that represents the Bitcoin blockchain.
247
        BitcoinChainName = "bitcoin"
248

249
        bitcoindBackendName = "bitcoind"
250
        btcdBackendName     = "btcd"
251
        neutrinoBackendName = "neutrino"
252

253
        defaultPrunedNodeMaxPeers = 4
254
        defaultNeutrinoMaxPeers   = 8
255

256
        // defaultNoDisconnectOnPongFailure is the default value for whether we
257
        // should *not* disconnect from a peer if we don't receive a pong
258
        // response in time after we send a ping.
259
        defaultNoDisconnectOnPongFailure = false
260
)
261

262
var (
263
        // DefaultLndDir is the default directory where lnd tries to find its
264
        // configuration file and store its data. This is a directory in the
265
        // user's application data, for example:
266
        //   C:\Users\<username>\AppData\Local\Lnd on Windows
267
        //   ~/.lnd on Linux
268
        //   ~/Library/Application Support/Lnd on MacOS
269
        DefaultLndDir = btcutil.AppDataDir("lnd", false)
270

271
        // DefaultConfigFile is the default full path of lnd's configuration
272
        // file.
273
        DefaultConfigFile = filepath.Join(DefaultLndDir, lncfg.DefaultConfigFilename)
274

275
        defaultDataDir = filepath.Join(DefaultLndDir, defaultDataDirname)
276
        defaultLogDir  = filepath.Join(DefaultLndDir, defaultLogDirname)
277

278
        defaultTowerDir = filepath.Join(defaultDataDir, defaultTowerSubDirname)
279

280
        defaultTLSCertPath    = filepath.Join(DefaultLndDir, defaultTLSCertFilename)
281
        defaultTLSKeyPath     = filepath.Join(DefaultLndDir, defaultTLSKeyFilename)
282
        defaultLetsEncryptDir = filepath.Join(DefaultLndDir, defaultLetsEncryptDirname)
283

284
        defaultBtcdDir         = btcutil.AppDataDir(btcdBackendName, false)
285
        defaultBtcdRPCCertFile = filepath.Join(defaultBtcdDir, "rpc.cert")
286

287
        defaultBitcoindDir = btcutil.AppDataDir(BitcoinChainName, false)
288

289
        defaultTorSOCKS   = net.JoinHostPort("localhost", strconv.Itoa(defaultTorSOCKSPort))
290
        defaultTorDNS     = net.JoinHostPort(defaultTorDNSHost, strconv.Itoa(defaultTorDNSPort))
291
        defaultTorControl = net.JoinHostPort("localhost", strconv.Itoa(defaultTorControlPort))
292

293
        // bitcoindEsimateModes defines all the legal values for bitcoind's
294
        // estimatesmartfee RPC call.
295
        defaultBitcoindEstimateMode = "CONSERVATIVE"
296
        bitcoindEstimateModes       = [2]string{"ECONOMICAL", defaultBitcoindEstimateMode}
297
)
298

299
// Config defines the configuration options for lnd.
300
//
301
// See LoadConfig for further details regarding the configuration
302
// loading+parsing process.
303
//
304
//nolint:ll
305
type Config struct {
306
        ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
307

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

313
        TLSCertPath        string        `long:"tlscertpath" description:"Path to write the TLS certificate for lnd's RPC and REST services"`
314
        TLSKeyPath         string        `long:"tlskeypath" description:"Path to write the TLS private key for lnd's RPC and REST services"`
315
        TLSExtraIPs        []string      `long:"tlsextraip" description:"Adds an extra ip to the generated certificate"`
316
        TLSExtraDomains    []string      `long:"tlsextradomain" description:"Adds an extra domain to the generated certificate"`
317
        TLSAutoRefresh     bool          `long:"tlsautorefresh" description:"Re-generate TLS certificate and key if the IPs or domains are changed"`
318
        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"`
319
        TLSCertDuration    time.Duration `long:"tlscertduration" description:"The duration for which the auto-generated TLS certificate will be valid for"`
320
        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"`
321

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

331
        LetsEncryptDir    string `long:"letsencryptdir" description:"The directory to store Let's Encrypt certificates within"`
332
        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."`
333
        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."`
334

335
        // We'll parse these 'raw' string arguments into real net.Addrs in the
336
        // loadConfig function. We need to expose the 'raw' strings so the
337
        // command line library can access them.
338
        // Only the parsed net.Addrs should be used!
339
        RawRPCListeners       []string `long:"rpclisten" description:"Add an interface/port/socket to listen for RPC connections"`
340
        RawRESTListeners      []string `long:"restlisten" description:"Add an interface/port/socket to listen for REST connections"`
341
        RawListeners          []string `long:"listen" description:"Add an interface/port to listen for peer connections"`
342
        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"`
343
        RawExternalDNSAddress string   `long:"external-dns-address" description:"Specify a DNS hostname for the node's external address. If no port is provided, the default (9735) is used."`
344
        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."`
345
        RPCListeners          []net.Addr
346
        RESTListeners         []net.Addr
347
        RestCORS              []string `long:"restcors" description:"Add an ip:port/hostname to allow cross origin access from. To allow all origins, set as \"*\"."`
348
        Listeners             []net.Addr
349
        ExternalIPs           []net.Addr
350
        ExternalDNSAddress    *lnwire.DNSAddress
351
        DisableListen         bool          `long:"nolisten" description:"Disable listening for incoming peer connections"`
352
        DisableRest           bool          `long:"norest" description:"Disable REST API"`
353
        DisableRestTLS        bool          `long:"no-rest-tls" description:"Disable TLS for REST connections"`
354
        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"`
355
        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"`
356
        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"`
357
        AddPeers              []string      `long:"addpeer" description:"Specify peers to connect to first"`
358
        MinBackoff            time.Duration `long:"minbackoff" description:"Shortest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."`
359
        MaxBackoff            time.Duration `long:"maxbackoff" description:"Longest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."`
360
        ConnectionTimeout     time.Duration `long:"connectiontimeout" description:"The timeout value for network connections. Valid time units are {ms, s, m, h}."`
361

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

364
        CPUProfile      string `long:"cpuprofile" description:"DEPRECATED: Use 'pprof.cpuprofile' option. Write CPU profile to the specified file" hidden:"true"`
365
        Profile         string `long:"profile" description:"DEPRECATED: Use 'pprof.profile' option. Enable HTTP profiling on either a port or host:port" hidden:"true"`
366
        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"`
367
        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"`
368

369
        Pprof *lncfg.Pprof `group:"Pprof" namespace:"pprof"`
370

371
        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"`
372
        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."`
373
        MaxPendingChannels int    `long:"maxpendingchannels" description:"The maximum number of incoming pending channels permitted per peer."`
374
        BackupFilePath     string `long:"backupfilepath" description:"The target location of the channel backup file"`
375

376
        NoBackupArchive bool `long:"no-backup-archive" description:"If set to true, channel backups will be deleted or replaced rather than being archived to a separate location."`
377

378
        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"`
379

380
        Bitcoin      *lncfg.Chain    `group:"Bitcoin" namespace:"bitcoin"`
381
        BtcdMode     *lncfg.Btcd     `group:"btcd" namespace:"btcd"`
382
        BitcoindMode *lncfg.Bitcoind `group:"bitcoind" namespace:"bitcoind"`
383
        NeutrinoMode *lncfg.Neutrino `group:"neutrino" namespace:"neutrino"`
384

385
        BlockCacheSize uint64 `long:"blockcachesize" description:"The maximum capacity of the block cache"`
386

387
        Autopilot *lncfg.AutoPilot `group:"Autopilot" namespace:"autopilot"`
388

389
        Tor *lncfg.Tor `group:"Tor" namespace:"tor"`
390

391
        SubRPCServers *subRPCServerConfigs `group:"subrpc"`
392

393
        Hodl *hodl.Config `group:"hodl" namespace:"hodl"`
394

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

397
        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."`
398
        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."`
399
        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."`
400

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

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

405
        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."`
406
        TrickleDelay                  int           `long:"trickledelay" description:"Time in milliseconds between each release of announcements to the network"`
407
        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."`
408
        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."`
409
        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."`
410
        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."`
411
        Alias                         string        `long:"alias" description:"The node alias. Used as a moniker by peers and intelligence services"`
412
        Color                         string        `long:"color" description:"The color of the node in hex format (i.e. '#3399FF'). Used to customize node appearance in intelligence services"`
413
        MinChanSize                   int64         `long:"minchansize" description:"The smallest channel size (in satoshis) that we should accept. Incoming channels smaller than this will be rejected"`
414
        MaxChanSize                   int64         `long:"maxchansize" description:"The largest channel size (in satoshis) that we should accept. Incoming channels larger than this will be rejected"`
415
        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."`
416

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

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

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

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

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

427
        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."`
428

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

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

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

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

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

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

444
        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"`
445

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

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

450
        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"`
451

452
        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."`
453

454
        net tor.Net
455

456
        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."`
457

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

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

462
        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]"`
463

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

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

468
        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"`
469

470
        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."`
471

472
        Fee *lncfg.Fee `group:"fee" namespace:"fee"`
473

474
        Invoices *lncfg.Invoices `group:"invoices" namespace:"invoices"`
475

476
        Routing *lncfg.Routing `group:"routing" namespace:"routing"`
477

478
        Gossip *lncfg.Gossip `group:"gossip" namespace:"gossip"`
479

480
        Workers *lncfg.Workers `group:"workers" namespace:"workers"`
481

482
        Caches *lncfg.Caches `group:"caches" namespace:"caches"`
483

484
        Prometheus lncfg.Prometheus `group:"prometheus" namespace:"prometheus"`
485

486
        WtClient *lncfg.WtClient `group:"wtclient" namespace:"wtclient"`
487

488
        Watchtower *lncfg.Watchtower `group:"watchtower" namespace:"watchtower"`
489

490
        ProtocolOptions *lncfg.ProtocolOptions `group:"protocol" namespace:"protocol"`
491

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

494
        HealthChecks *lncfg.HealthCheckConfig `group:"healthcheck" namespace:"healthcheck"`
495

496
        DB *lncfg.DB `group:"db" namespace:"db"`
497

498
        Cluster *lncfg.Cluster `group:"cluster" namespace:"cluster"`
499

500
        RPCMiddleware *lncfg.RPCMiddleware `group:"rpcmiddleware" namespace:"rpcmiddleware"`
501

502
        RemoteSigner *lncfg.RemoteSigner `group:"remotesigner" namespace:"remotesigner"`
503

504
        Sweeper *lncfg.Sweeper `group:"sweeper" namespace:"sweeper"`
505

506
        Htlcswitch *lncfg.Htlcswitch `group:"htlcswitch" namespace:"htlcswitch"`
507

508
        GRPC *GRPCConfig `group:"grpc" namespace:"grpc"`
509

510
        // SubLogMgr is the root logger that all the daemon's subloggers are
511
        // hooked up to.
512
        SubLogMgr  *build.SubLoggerManager
513
        LogRotator *build.RotatingLogWriter
514
        LogConfig  *build.LogConfig `group:"logging" namespace:"logging"`
515

516
        // networkDir is the path to the directory of the currently active
517
        // network. This path will hold the files related to each different
518
        // network.
519
        networkDir string
520

521
        // ActiveNetParams contains parameters of the target chain.
522
        ActiveNetParams chainreg.BitcoinNetParams
523

524
        // Estimator is used to estimate routing probabilities.
525
        Estimator routing.Estimator
526

527
        // Dev specifies configs used for integration tests, which is always
528
        // empty if not built with `integration` flag.
529
        Dev *lncfg.DevConfig `group:"dev" namespace:"dev"`
530

531
        // HTTPHeaderTimeout is the maximum duration that the server will wait
532
        // before timing out reading the headers of an HTTP request.
533
        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."`
534

535
        // NumRestrictedSlots is the max number of incoming connections allowed
536
        // in the server. Outbound connections are not restricted.
537
        NumRestrictedSlots uint64 `long:"num-restricted-slots" description:"The max number of incoming connections allowed in the server. Outbound connections are not restricted."`
538

539
        // NoDisconnectOnPongFailure controls if we'll disconnect if a peer
540
        // doesn't respond to a pong in time.
541
        NoDisconnectOnPongFailure bool `long:"no-disconnect-on-pong-failure" description:"If true, a peer will *not* be disconnected if a pong is not received in time or is mismatched. Defaults to false, meaning peers *will* be disconnected on pong failure."`
542
}
543

544
// GRPCConfig holds the configuration options for the gRPC server.
545
// See https://github.com/grpc/grpc-go/blob/v1.41.0/keepalive/keepalive.go#L50
546
// for more details. Any value of 0 means we use the gRPC internal default
547
// values.
548
//
549
//nolint:ll
550
type GRPCConfig struct {
551
        // ServerPingTime is a duration for the amount of time of no activity
552
        // after which the server pings the client to see if the transport is
553
        // still alive. If set below 1s, a minimum value of 1s will be used
554
        // instead.
555
        ServerPingTime time.Duration `long:"server-ping-time" description:"How long the server waits on a gRPC stream with no activity before pinging the client."`
556

557
        // ServerPingTimeout is the duration the server waits after having
558
        // pinged for keepalive check, and if no activity is seen even after
559
        // that the connection is closed.
560
        ServerPingTimeout time.Duration `long:"server-ping-timeout" description:"How long the server waits for the response from the client for the keepalive ping response."`
561

562
        // ClientPingMinWait is the minimum amount of time a client should wait
563
        // before sending a keepalive ping.
564
        ClientPingMinWait time.Duration `long:"client-ping-min-wait" description:"The minimum amount of time the client should wait before sending a keepalive ping."`
565

566
        // ClientAllowPingWithoutStream specifies whether pings from the client
567
        // are allowed even if there are no active gRPC streams. This might be
568
        // useful to keep the underlying HTTP/2 connection open for future
569
        // requests.
570
        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."`
571
}
572

573
// DefaultConfig returns all default values for the Config struct.
574
//
575
//nolint:ll
576
func DefaultConfig() Config {
4✔
577
        return Config{
4✔
578
                LndDir:            DefaultLndDir,
4✔
579
                ConfigFile:        DefaultConfigFile,
4✔
580
                DataDir:           defaultDataDir,
4✔
581
                DebugLevel:        defaultLogLevel,
4✔
582
                TLSCertPath:       defaultTLSCertPath,
4✔
583
                TLSKeyPath:        defaultTLSKeyPath,
4✔
584
                TLSCertDuration:   defaultTLSCertDuration,
4✔
585
                LetsEncryptDir:    defaultLetsEncryptDir,
4✔
586
                LetsEncryptListen: defaultLetsEncryptListen,
4✔
587
                LogDir:            defaultLogDir,
4✔
588
                AcceptorTimeout:   defaultAcceptorTimeout,
4✔
589
                WSPingInterval:    lnrpc.DefaultPingInterval,
4✔
590
                WSPongWait:        lnrpc.DefaultPongWait,
4✔
591
                Bitcoin: &lncfg.Chain{
4✔
592
                        MinHTLCIn:     chainreg.DefaultBitcoinMinHTLCInMSat,
4✔
593
                        MinHTLCOut:    chainreg.DefaultBitcoinMinHTLCOutMSat,
4✔
594
                        BaseFee:       chainreg.DefaultBitcoinBaseFeeMSat,
4✔
595
                        FeeRate:       chainreg.DefaultBitcoinFeeRate,
4✔
596
                        TimeLockDelta: chainreg.DefaultBitcoinTimeLockDelta,
4✔
597
                        MaxLocalDelay: defaultMaxLocalCSVDelay,
4✔
598
                        Node:          btcdBackendName,
4✔
599
                },
4✔
600
                BtcdMode: &lncfg.Btcd{
4✔
601
                        Dir:     defaultBtcdDir,
4✔
602
                        RPCHost: defaultRPCHost,
4✔
603
                        RPCCert: defaultBtcdRPCCertFile,
4✔
604
                },
4✔
605
                BitcoindMode: &lncfg.Bitcoind{
4✔
606
                        Dir:                defaultBitcoindDir,
4✔
607
                        RPCHost:            defaultRPCHost,
4✔
608
                        EstimateMode:       defaultBitcoindEstimateMode,
4✔
609
                        PrunedNodeMaxPeers: defaultPrunedNodeMaxPeers,
4✔
610
                        ZMQReadDeadline:    defaultZMQReadDeadline,
4✔
611
                },
4✔
612
                NeutrinoMode: &lncfg.Neutrino{
4✔
613
                        UserAgentName:    neutrino.UserAgentName,
4✔
614
                        UserAgentVersion: neutrino.UserAgentVersion,
4✔
615
                        MaxPeers:         defaultNeutrinoMaxPeers,
4✔
616
                },
4✔
617
                BlockCacheSize:     defaultBlockCacheSize,
4✔
618
                MaxPendingChannels: lncfg.DefaultMaxPendingChannels,
4✔
619
                NoSeedBackup:       defaultNoSeedBackup,
4✔
620
                MinBackoff:         defaultMinBackoff,
4✔
621
                MaxBackoff:         defaultMaxBackoff,
4✔
622
                ConnectionTimeout:  tor.DefaultConnTimeout,
4✔
623

4✔
624
                Fee: &lncfg.Fee{
4✔
625
                        MinUpdateTimeout: lncfg.DefaultMinUpdateTimeout,
4✔
626
                        MaxUpdateTimeout: lncfg.DefaultMaxUpdateTimeout,
4✔
627
                },
4✔
628

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

772
// LoadConfig initializes and parses the config using a config file and command
773
// line options.
774
//
775
// The configuration proceeds as follows:
776
//  1. Start with a default config with sane settings
777
//  2. Pre-parse the command line to check for an alternative config file
778
//  3. Load configuration file overwriting defaults with any specified options
779
//  4. Parse CLI options and overwrite/add any specified options
780
func LoadConfig(interceptor signal.Interceptor) (*Config, error) {
3✔
781
        // Pre-parse the command line options to pick up an alternative config
3✔
782
        // file.
3✔
783
        preCfg := DefaultConfig()
3✔
784
        if _, err := flags.Parse(&preCfg); err != nil {
3✔
785
                return nil, err
×
786
        }
×
787

788
        // Show the version and exit if the version flag was specified.
789
        appName := filepath.Base(os.Args[0])
3✔
790
        appName = strings.TrimSuffix(appName, filepath.Ext(appName))
3✔
791
        usageMessage := fmt.Sprintf("Use %s -h to show usage", appName)
3✔
792
        if preCfg.ShowVersion {
3✔
793
                fmt.Println(appName, "version", build.Version(),
×
794
                        "commit="+build.Commit)
×
795
                os.Exit(0)
×
796
        }
×
797

798
        // If the config file path has not been modified by the user, then we'll
799
        // use the default config file path. However, if the user has modified
800
        // their lnddir, then we should assume they intend to use the config
801
        // file within it.
802
        configFileDir := CleanAndExpandPath(preCfg.LndDir)
3✔
803
        configFilePath := CleanAndExpandPath(preCfg.ConfigFile)
3✔
804
        switch {
3✔
805
        // User specified --lnddir but no --configfile. Update the config file
806
        // path to the lnd config directory, but don't require it to exist.
807
        case configFileDir != DefaultLndDir &&
808
                configFilePath == DefaultConfigFile:
3✔
809

3✔
810
                configFilePath = filepath.Join(
3✔
811
                        configFileDir, lncfg.DefaultConfigFilename,
3✔
812
                )
3✔
813

814
        // User did specify an explicit --configfile, so we check that it does
815
        // exist under that path to avoid surprises.
816
        case configFilePath != DefaultConfigFile:
×
817
                if !lnrpc.FileExists(configFilePath) {
×
818
                        return nil, fmt.Errorf("specified config file does "+
×
819
                                "not exist in %s", configFilePath)
×
820
                }
×
821
        }
822

823
        // Next, load any additional configuration options from the file.
824
        var configFileError error
3✔
825
        cfg := preCfg
3✔
826
        fileParser := flags.NewParser(&cfg, flags.Default)
3✔
827
        err := flags.NewIniParser(fileParser).ParseFile(configFilePath)
3✔
828
        if err != nil {
6✔
829
                // If it's a parsing related error, then we'll return
3✔
830
                // immediately, otherwise we can proceed as possibly the config
3✔
831
                // file doesn't exist which is OK.
3✔
832
                if lnutils.ErrorAs[*flags.IniError](err) ||
3✔
833
                        lnutils.ErrorAs[*flags.Error](err) {
3✔
834

×
835
                        return nil, err
×
836
                }
×
837

838
                configFileError = err
3✔
839
        }
840

841
        // Finally, parse the remaining command line options again to ensure
842
        // they take precedence.
843
        flagParser := flags.NewParser(&cfg, flags.Default)
3✔
844
        if _, err := flagParser.Parse(); err != nil {
3✔
845
                return nil, err
×
846
        }
×
847

848
        // Make sure everything we just loaded makes sense.
849
        cleanCfg, err := ValidateConfig(
3✔
850
                cfg, interceptor, fileParser, flagParser,
3✔
851
        )
3✔
852
        var usageErr *lncfg.UsageError
3✔
853
        if errors.As(err, &usageErr) {
3✔
854
                // The logging system might not yet be initialized, so we also
×
855
                // write to stderr to make sure the error appears somewhere.
×
856
                _, _ = fmt.Fprintln(os.Stderr, usageMessage)
×
857
                ltndLog.Warnf("Incorrect usage: %v", usageMessage)
×
858

×
859
                // The log subsystem might not yet be initialized. But we still
×
860
                // try to log the error there since some packaging solutions
×
861
                // might only look at the log and not stdout/stderr.
×
862
                ltndLog.Warnf("Error validating config: %v", err)
×
863

×
864
                return nil, err
×
865
        }
×
866
        if err != nil {
3✔
867
                // The log subsystem might not yet be initialized. But we still
×
868
                // try to log the error there since some packaging solutions
×
869
                // might only look at the log and not stdout/stderr.
×
870
                ltndLog.Warnf("Error validating config: %v", err)
×
871

×
872
                return nil, err
×
873
        }
×
874

875
        // Warn about missing config file only after all other configuration is
876
        // done. This prevents the warning on help messages and invalid options.
877
        // Note this should go directly before the return.
878
        if configFileError != nil {
6✔
879
                ltndLog.Warnf("%v", configFileError)
3✔
880
        }
3✔
881

882
        // Finally, log warnings for deprecated config options if they are set.
883
        logWarningsForDeprecation(*cleanCfg)
3✔
884

3✔
885
        return cleanCfg, nil
3✔
886
}
887

888
// ValidateConfig check the given configuration to be sane. This makes sure no
889
// illegal values or combination of values are set. All file system paths are
890
// normalized. The cleaned up config is returned on success.
891
func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser,
892
        flagParser *flags.Parser) (*Config, error) {
3✔
893

3✔
894
        // Special show command to list supported subsystems and exit.
3✔
895
        if cfg.DebugLevel == "show" {
6✔
896
                subLogMgr := build.NewSubLoggerManager()
3✔
897

3✔
898
                // Initialize logging at the default logging level.
3✔
899
                SetupLoggers(subLogMgr, interceptor)
3✔
900

3✔
901
                fmt.Println("Supported subsystems",
3✔
902
                        subLogMgr.SupportedSubsystems())
3✔
903
                os.Exit(0)
3✔
904
        }
3✔
905

906
        // If the provided lnd directory is not the default, we'll modify the
907
        // path to all of the files and directories that will live within it.
908
        lndDir := CleanAndExpandPath(cfg.LndDir)
3✔
909
        if lndDir != DefaultLndDir {
6✔
910
                cfg.DataDir = filepath.Join(lndDir, defaultDataDirname)
3✔
911
                cfg.LetsEncryptDir = filepath.Join(
3✔
912
                        lndDir, defaultLetsEncryptDirname,
3✔
913
                )
3✔
914
                cfg.TLSCertPath = filepath.Join(lndDir, defaultTLSCertFilename)
3✔
915
                cfg.TLSKeyPath = filepath.Join(lndDir, defaultTLSKeyFilename)
3✔
916
                cfg.LogDir = filepath.Join(lndDir, defaultLogDirname)
3✔
917

3✔
918
                // If the watchtower's directory is set to the default, i.e. the
3✔
919
                // user has not requested a different location, we'll move the
3✔
920
                // location to be relative to the specified lnd directory.
3✔
921
                if cfg.Watchtower.TowerDir == defaultTowerDir {
6✔
922
                        cfg.Watchtower.TowerDir = filepath.Join(
3✔
923
                                cfg.DataDir, defaultTowerSubDirname,
3✔
924
                        )
3✔
925
                }
3✔
926
        }
927

928
        funcName := "ValidateConfig"
3✔
929
        mkErr := func(format string, args ...interface{}) error {
3✔
930
                return fmt.Errorf(funcName+": "+format, args...)
×
931
        }
×
932
        makeDirectory := func(dir string) error {
6✔
933
                err := os.MkdirAll(dir, 0700)
3✔
934
                if err != nil {
3✔
935
                        // Show a nicer error message if it's because a symlink
×
936
                        // is linked to a directory that does not exist
×
937
                        // (probably because it's not mounted).
×
938
                        if e, ok := err.(*os.PathError); ok && os.IsExist(err) {
×
939
                                link, lerr := os.Readlink(e.Path)
×
940
                                if lerr == nil {
×
941
                                        str := "is symlink %s -> %s mounted?"
×
942
                                        err = fmt.Errorf(str, e.Path, link)
×
943
                                }
×
944
                        }
945

946
                        str := "Failed to create lnd directory '%s': %v"
×
947
                        return mkErr(str, dir, err)
×
948
                }
949

950
                return nil
3✔
951
        }
952

953
        // IsSet returns true if an option has been set in either the config
954
        // file or by a flag.
955
        isSet := func(field string) (bool, error) {
6✔
956
                fieldName, ok := reflect.TypeOf(Config{}).FieldByName(field)
3✔
957
                if !ok {
3✔
958
                        str := "could not find field %s"
×
959
                        return false, mkErr(str, field)
×
960
                }
×
961

962
                long, ok := fieldName.Tag.Lookup("long")
3✔
963
                if !ok {
3✔
964
                        str := "field %s does not have a long tag"
×
965
                        return false, mkErr(str, field)
×
966
                }
×
967

968
                // The user has the option to set the flag in either the config
969
                // file or as a command line flag. If any is set, we consider it
970
                // to be set, not applying any precedence rules here (since it
971
                // is a boolean the default is false anyway which would screw up
972
                // any precedence rules). Additionally, we need to also support
973
                // the use case where the config struct is embedded _within_
974
                // another struct with a prefix (as is the case with
975
                // lightning-terminal).
976
                fileOption := fileParser.FindOptionByLongName(long)
3✔
977
                fileOptionNested := fileParser.FindOptionByLongName(
3✔
978
                        "lnd." + long,
3✔
979
                )
3✔
980
                flagOption := flagParser.FindOptionByLongName(long)
3✔
981
                flagOptionNested := flagParser.FindOptionByLongName(
3✔
982
                        "lnd." + long,
3✔
983
                )
3✔
984

3✔
985
                return (fileOption != nil && fileOption.IsSet()) ||
3✔
986
                                (fileOptionNested != nil && fileOptionNested.IsSet()) ||
3✔
987
                                (flagOption != nil && flagOption.IsSet()) ||
3✔
988
                                (flagOptionNested != nil && flagOptionNested.IsSet()),
3✔
989
                        nil
3✔
990
        }
991

992
        // As soon as we're done parsing configuration options, ensure all paths
993
        // to directories and files are cleaned and expanded before attempting
994
        // to use them later on.
995
        cfg.DataDir = CleanAndExpandPath(cfg.DataDir)
3✔
996
        cfg.TLSCertPath = CleanAndExpandPath(cfg.TLSCertPath)
3✔
997
        cfg.TLSKeyPath = CleanAndExpandPath(cfg.TLSKeyPath)
3✔
998
        cfg.LetsEncryptDir = CleanAndExpandPath(cfg.LetsEncryptDir)
3✔
999
        cfg.AdminMacPath = CleanAndExpandPath(cfg.AdminMacPath)
3✔
1000
        cfg.ReadMacPath = CleanAndExpandPath(cfg.ReadMacPath)
3✔
1001
        cfg.InvoiceMacPath = CleanAndExpandPath(cfg.InvoiceMacPath)
3✔
1002
        cfg.LogDir = CleanAndExpandPath(cfg.LogDir)
3✔
1003
        cfg.BtcdMode.Dir = CleanAndExpandPath(cfg.BtcdMode.Dir)
3✔
1004
        cfg.BitcoindMode.Dir = CleanAndExpandPath(cfg.BitcoindMode.Dir)
3✔
1005
        cfg.BitcoindMode.ConfigPath = CleanAndExpandPath(
3✔
1006
                cfg.BitcoindMode.ConfigPath,
3✔
1007
        )
3✔
1008
        cfg.BitcoindMode.RPCCookie = CleanAndExpandPath(cfg.BitcoindMode.RPCCookie)
3✔
1009
        cfg.Tor.PrivateKeyPath = CleanAndExpandPath(cfg.Tor.PrivateKeyPath)
3✔
1010
        cfg.Tor.WatchtowerKeyPath = CleanAndExpandPath(cfg.Tor.WatchtowerKeyPath)
3✔
1011
        cfg.Watchtower.TowerDir = CleanAndExpandPath(cfg.Watchtower.TowerDir)
3✔
1012
        cfg.BackupFilePath = CleanAndExpandPath(cfg.BackupFilePath)
3✔
1013
        cfg.WalletUnlockPasswordFile = CleanAndExpandPath(
3✔
1014
                cfg.WalletUnlockPasswordFile,
3✔
1015
        )
3✔
1016

3✔
1017
        // Ensure that the user didn't attempt to specify negative values for
3✔
1018
        // any of the autopilot params.
3✔
1019
        if cfg.Autopilot.MaxChannels < 0 {
3✔
1020
                str := "autopilot.maxchannels must be non-negative"
×
1021

×
1022
                return nil, mkErr(str)
×
1023
        }
×
1024
        if cfg.Autopilot.Allocation < 0 {
3✔
1025
                str := "autopilot.allocation must be non-negative"
×
1026

×
1027
                return nil, mkErr(str)
×
1028
        }
×
1029
        if cfg.Autopilot.MinChannelSize < 0 {
3✔
1030
                str := "autopilot.minchansize must be non-negative"
×
1031

×
1032
                return nil, mkErr(str)
×
1033
        }
×
1034
        if cfg.Autopilot.MaxChannelSize < 0 {
3✔
1035
                str := "autopilot.maxchansize must be non-negative"
×
1036

×
1037
                return nil, mkErr(str)
×
1038
        }
×
1039
        if cfg.Autopilot.MinConfs < 0 {
3✔
1040
                str := "autopilot.minconfs must be non-negative"
×
1041

×
1042
                return nil, mkErr(str)
×
1043
        }
×
1044
        if cfg.Autopilot.ConfTarget < 1 {
3✔
1045
                str := "autopilot.conftarget must be positive"
×
1046

×
1047
                return nil, mkErr(str)
×
1048
        }
×
1049

1050
        // Ensure that the specified values for the min and max channel size
1051
        // are within the bounds of the normal chan size constraints.
1052
        if cfg.Autopilot.MinChannelSize < int64(funding.MinChanFundingSize) {
3✔
1053
                cfg.Autopilot.MinChannelSize = int64(funding.MinChanFundingSize)
×
1054
        }
×
1055
        if cfg.Autopilot.MaxChannelSize > int64(MaxFundingAmount) {
3✔
1056
                cfg.Autopilot.MaxChannelSize = int64(MaxFundingAmount)
×
1057
        }
×
1058

1059
        if _, err := validateAtplCfg(cfg.Autopilot); err != nil {
3✔
1060
                return nil, mkErr("error validating autopilot: %v", err)
×
1061
        }
×
1062

1063
        // Ensure that --maxchansize is properly handled when set by user.
1064
        // For non-Wumbo channels this limit remains 16777215 satoshis by default
1065
        // as specified in BOLT-02. For wumbo channels this limit is 1,000,000,000.
1066
        // satoshis (10 BTC). Always enforce --maxchansize explicitly set by user.
1067
        // If unset (marked by 0 value), then enforce proper default.
1068
        if cfg.MaxChanSize == 0 {
6✔
1069
                if cfg.ProtocolOptions.Wumbo() {
6✔
1070
                        cfg.MaxChanSize = int64(funding.MaxBtcFundingAmountWumbo)
3✔
1071
                } else {
6✔
1072
                        cfg.MaxChanSize = int64(funding.MaxBtcFundingAmount)
3✔
1073
                }
3✔
1074
        }
1075

1076
        // Ensure that the user specified values for the min and max channel
1077
        // size make sense.
1078
        if cfg.MaxChanSize < cfg.MinChanSize {
3✔
1079
                return nil, mkErr("invalid channel size parameters: "+
×
1080
                        "max channel size %v, must be no less than min chan "+
×
1081
                        "size %v", cfg.MaxChanSize, cfg.MinChanSize,
×
1082
                )
×
1083
        }
×
1084

1085
        // Don't allow superfluous --maxchansize greater than
1086
        // BOLT 02 soft-limit for non-wumbo channel
1087
        if !cfg.ProtocolOptions.Wumbo() &&
3✔
1088
                cfg.MaxChanSize > int64(MaxFundingAmount) {
3✔
1089

×
1090
                return nil, mkErr("invalid channel size parameters: "+
×
1091
                        "maximum channel size %v is greater than maximum "+
×
1092
                        "non-wumbo channel size %v", cfg.MaxChanSize,
×
1093
                        MaxFundingAmount,
×
1094
                )
×
1095
        }
×
1096

1097
        // Ensure that the amount data for revoked commitment transactions is
1098
        // stored if the watchtower client is active.
1099
        if cfg.DB.NoRevLogAmtData && cfg.WtClient.Active {
3✔
1100
                return nil, mkErr("revocation log amount data must be stored " +
×
1101
                        "if the watchtower client is active")
×
1102
        }
×
1103

1104
        // Ensure a valid max channel fee allocation was set.
1105
        if cfg.MaxChannelFeeAllocation <= 0 || cfg.MaxChannelFeeAllocation > 1 {
3✔
1106
                return nil, mkErr("invalid max channel fee allocation: %v, "+
×
1107
                        "must be within (0, 1]", cfg.MaxChannelFeeAllocation)
×
1108
        }
×
1109

1110
        if cfg.MaxCommitFeeRateAnchors < 1 {
3✔
1111
                return nil, mkErr("invalid max commit fee rate anchors: %v, "+
×
1112
                        "must be at least 1 sat/vByte",
×
1113
                        cfg.MaxCommitFeeRateAnchors)
×
1114
        }
×
1115

1116
        // Validate the Tor config parameters.
1117
        socks, err := lncfg.ParseAddressString(
3✔
1118
                cfg.Tor.SOCKS, strconv.Itoa(defaultTorSOCKSPort),
3✔
1119
                cfg.net.ResolveTCPAddr,
3✔
1120
        )
3✔
1121
        if err != nil {
3✔
1122
                return nil, err
×
1123
        }
×
1124
        cfg.Tor.SOCKS = socks.String()
3✔
1125

3✔
1126
        // We'll only attempt to normalize and resolve the DNS host if it hasn't
3✔
1127
        // changed, as it doesn't need to be done for the default.
3✔
1128
        if cfg.Tor.DNS != defaultTorDNS {
3✔
1129
                dns, err := lncfg.ParseAddressString(
×
1130
                        cfg.Tor.DNS, strconv.Itoa(defaultTorDNSPort),
×
1131
                        cfg.net.ResolveTCPAddr,
×
1132
                )
×
1133
                if err != nil {
×
1134
                        return nil, mkErr("error parsing tor dns: %v", err)
×
1135
                }
×
1136
                cfg.Tor.DNS = dns.String()
×
1137
        }
1138

1139
        control, err := lncfg.ParseAddressString(
3✔
1140
                cfg.Tor.Control, strconv.Itoa(defaultTorControlPort),
3✔
1141
                cfg.net.ResolveTCPAddr,
3✔
1142
        )
3✔
1143
        if err != nil {
3✔
1144
                return nil, mkErr("error parsing tor control address: %v", err)
×
1145
        }
×
1146
        cfg.Tor.Control = control.String()
3✔
1147

3✔
1148
        // Ensure that tor socks host:port is not equal to tor control
3✔
1149
        // host:port. This would lead to lnd not starting up properly.
3✔
1150
        if cfg.Tor.SOCKS == cfg.Tor.Control {
3✔
1151
                str := "tor.socks and tor.control can not us the same host:port"
×
1152

×
1153
                return nil, mkErr(str)
×
1154
        }
×
1155

1156
        switch {
3✔
1157
        case cfg.Tor.V2 && cfg.Tor.V3:
×
1158
                return nil, mkErr("either tor.v2 or tor.v3 can be set, " +
×
1159
                        "but not both")
×
1160
        case cfg.DisableListen && (cfg.Tor.V2 || cfg.Tor.V3):
×
1161
                return nil, mkErr("listening must be enabled when enabling " +
×
1162
                        "inbound connections over Tor")
×
1163
        }
1164

1165
        if cfg.Tor.PrivateKeyPath == "" {
6✔
1166
                switch {
3✔
1167
                case cfg.Tor.V2:
×
1168
                        cfg.Tor.PrivateKeyPath = filepath.Join(
×
1169
                                lndDir, defaultTorV2PrivateKeyFilename,
×
1170
                        )
×
1171
                case cfg.Tor.V3:
×
1172
                        cfg.Tor.PrivateKeyPath = filepath.Join(
×
1173
                                lndDir, defaultTorV3PrivateKeyFilename,
×
1174
                        )
×
1175
                }
1176
        }
1177

1178
        if cfg.Tor.WatchtowerKeyPath == "" {
6✔
1179
                switch {
3✔
1180
                case cfg.Tor.V2:
×
1181
                        cfg.Tor.WatchtowerKeyPath = filepath.Join(
×
1182
                                cfg.Watchtower.TowerDir,
×
1183
                                defaultTorV2PrivateKeyFilename,
×
1184
                        )
×
1185
                case cfg.Tor.V3:
×
1186
                        cfg.Tor.WatchtowerKeyPath = filepath.Join(
×
1187
                                cfg.Watchtower.TowerDir,
×
1188
                                defaultTorV3PrivateKeyFilename,
×
1189
                        )
×
1190
                }
1191
        }
1192

1193
        // Set up the network-related functions that will be used throughout
1194
        // the daemon. We use the standard Go "net" package functions by
1195
        // default. If we should be proxying all traffic through Tor, then
1196
        // we'll use the Tor proxy specific functions in order to avoid leaking
1197
        // our real information.
1198
        if cfg.Tor.Active {
3✔
1199
                cfg.net = &tor.ProxyNet{
×
1200
                        SOCKS:                       cfg.Tor.SOCKS,
×
1201
                        DNS:                         cfg.Tor.DNS,
×
1202
                        StreamIsolation:             cfg.Tor.StreamIsolation,
×
1203
                        SkipProxyForClearNetTargets: cfg.Tor.SkipProxyForClearNetTargets,
×
1204
                }
×
1205
        }
×
1206

1207
        if cfg.DisableListen && cfg.NAT {
3✔
1208
                return nil, mkErr("NAT traversal cannot be used when " +
×
1209
                        "listening is disabled")
×
1210
        }
×
1211
        if cfg.NAT && len(cfg.ExternalHosts) != 0 {
3✔
1212
                return nil, mkErr("NAT support and externalhosts are " +
×
1213
                        "mutually exclusive, only one should be selected")
×
1214
        }
×
1215

1216
        // Multiple networks can't be selected simultaneously.  Count
1217
        // number of network flags passed; assign active network params
1218
        // while we're at it.
1219
        numNets := 0
3✔
1220
        if cfg.Bitcoin.MainNet {
3✔
1221
                numNets++
×
1222
                cfg.ActiveNetParams = chainreg.BitcoinMainNetParams
×
1223
        }
×
1224
        if cfg.Bitcoin.TestNet3 {
3✔
1225
                numNets++
×
1226
                cfg.ActiveNetParams = chainreg.BitcoinTestNetParams
×
1227
        }
×
1228
        if cfg.Bitcoin.TestNet4 {
3✔
1229
                numNets++
×
1230
                cfg.ActiveNetParams = chainreg.BitcoinTestNet4Params
×
1231
        }
×
1232
        if cfg.Bitcoin.RegTest {
6✔
1233
                numNets++
3✔
1234
                cfg.ActiveNetParams = chainreg.BitcoinRegTestNetParams
3✔
1235
        }
3✔
1236
        if cfg.Bitcoin.SimNet {
3✔
1237
                numNets++
×
1238
                cfg.ActiveNetParams = chainreg.BitcoinSimNetParams
×
1239

×
1240
                // For simnet, the btcsuite chain params uses a
×
1241
                // cointype of 115. However, we override this in
×
1242
                // chainreg/chainparams.go, but the raw ChainParam
×
1243
                // field is used elsewhere. To ensure everything is
×
1244
                // consistent, we'll also override the cointype within
×
1245
                // the raw params.
×
1246
                targetCoinType := chainreg.BitcoinSigNetParams.CoinType
×
1247
                cfg.ActiveNetParams.Params.HDCoinType = targetCoinType
×
1248
        }
×
1249
        if cfg.Bitcoin.SigNet {
3✔
1250
                numNets++
×
1251
                cfg.ActiveNetParams = chainreg.BitcoinSigNetParams
×
1252

×
1253
                // Let the user overwrite the default signet parameters.
×
1254
                // The challenge defines the actual signet network to
×
1255
                // join and the seed nodes are needed for network
×
1256
                // discovery.
×
1257
                sigNetChallenge := chaincfg.DefaultSignetChallenge
×
1258
                sigNetSeeds := chaincfg.DefaultSignetDNSSeeds
×
1259
                if cfg.Bitcoin.SigNetChallenge != "" {
×
1260
                        challenge, err := hex.DecodeString(
×
1261
                                cfg.Bitcoin.SigNetChallenge,
×
1262
                        )
×
1263
                        if err != nil {
×
1264
                                return nil, mkErr("Invalid "+
×
1265
                                        "signet challenge, hex decode "+
×
1266
                                        "failed: %v", err)
×
1267
                        }
×
1268
                        sigNetChallenge = challenge
×
1269
                }
1270

1271
                if len(cfg.Bitcoin.SigNetSeedNode) > 0 {
×
1272
                        sigNetSeeds = make([]chaincfg.DNSSeed, len(
×
1273
                                cfg.Bitcoin.SigNetSeedNode,
×
1274
                        ))
×
1275
                        for idx, seed := range cfg.Bitcoin.SigNetSeedNode {
×
1276
                                sigNetSeeds[idx] = chaincfg.DNSSeed{
×
1277
                                        Host:         seed,
×
1278
                                        HasFiltering: false,
×
1279
                                }
×
1280
                        }
×
1281
                }
1282

1283
                chainParams := chaincfg.CustomSignetParams(
×
1284
                        sigNetChallenge, sigNetSeeds,
×
1285
                )
×
1286
                cfg.ActiveNetParams.Params = &chainParams
×
1287
        }
1288
        if numNets > 1 {
3✔
1289
                str := "The mainnet, testnet, testnet4, regtest, simnet and " +
×
1290
                        "signet params can't be used together -- choose one " +
×
1291
                        "of the five"
×
1292

×
1293
                return nil, mkErr(str)
×
1294
        }
×
1295

1296
        // The target network must be provided, otherwise, we won't
1297
        // know how to initialize the daemon.
1298
        if numNets == 0 {
3✔
1299
                str := "either --bitcoin.mainnet, or --bitcoin.testnet, " +
×
1300
                        "--bitcoin.testnet4, --bitcoin.simnet, " +
×
1301
                        "--bitcoin.regtest or --bitcoin.signet must be " +
×
1302
                        "specified"
×
1303

×
1304
                return nil, mkErr(str)
×
1305
        }
×
1306

1307
        err = cfg.Bitcoin.Validate(minTimeLockDelta, funding.MinBtcRemoteDelay)
3✔
1308
        if err != nil {
3✔
1309
                return nil, mkErr("error validating bitcoin params: %v", err)
×
1310
        }
×
1311

1312
        switch cfg.Bitcoin.Node {
3✔
1313
        case btcdBackendName:
1✔
1314
                err := parseRPCParams(
1✔
1315
                        cfg.Bitcoin, cfg.BtcdMode, cfg.ActiveNetParams,
1✔
1316
                )
1✔
1317
                if err != nil {
1✔
1318
                        return nil, mkErr("unable to load RPC "+
×
1319
                                "credentials for btcd: %v", err)
×
1320
                }
×
1321
        case bitcoindBackendName:
1✔
1322
                if cfg.Bitcoin.SimNet {
1✔
1323
                        return nil, mkErr("bitcoind does not " +
×
1324
                                "support simnet")
×
1325
                }
×
1326

1327
                err := parseRPCParams(
1✔
1328
                        cfg.Bitcoin, cfg.BitcoindMode, cfg.ActiveNetParams,
1✔
1329
                )
1✔
1330
                if err != nil {
1✔
1331
                        return nil, mkErr("unable to load RPC "+
×
1332
                                "credentials for bitcoind: %v", err)
×
1333
                }
×
1334
        case neutrinoBackendName:
1✔
1335
                // No need to get RPC parameters.
1336

1337
        case "nochainbackend":
×
1338
                // Nothing to configure, we're running without any chain
1339
                // backend whatsoever (pure signing mode).
1340

1341
        default:
×
1342
                str := "only btcd, bitcoind, and neutrino mode " +
×
1343
                        "supported for bitcoin at this time"
×
1344

×
1345
                return nil, mkErr(str)
×
1346
        }
1347

1348
        cfg.Bitcoin.ChainDir = filepath.Join(
3✔
1349
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
3✔
1350
        )
3✔
1351

3✔
1352
        // Ensure that the user didn't attempt to specify negative values for
3✔
1353
        // any of the autopilot params.
3✔
1354
        if cfg.Autopilot.MaxChannels < 0 {
3✔
1355
                str := "autopilot.maxchannels must be non-negative"
×
1356

×
1357
                return nil, mkErr(str)
×
1358
        }
×
1359
        if cfg.Autopilot.Allocation < 0 {
3✔
1360
                str := "autopilot.allocation must be non-negative"
×
1361

×
1362
                return nil, mkErr(str)
×
1363
        }
×
1364
        if cfg.Autopilot.MinChannelSize < 0 {
3✔
1365
                str := "autopilot.minchansize must be non-negative"
×
1366

×
1367
                return nil, mkErr(str)
×
1368
        }
×
1369
        if cfg.Autopilot.MaxChannelSize < 0 {
3✔
1370
                str := "autopilot.maxchansize must be non-negative"
×
1371

×
1372
                return nil, mkErr(str)
×
1373
        }
×
1374

1375
        // Ensure that the specified values for the min and max channel size
1376
        // don't are within the bounds of the normal chan size constraints.
1377
        if cfg.Autopilot.MinChannelSize < int64(funding.MinChanFundingSize) {
3✔
1378
                cfg.Autopilot.MinChannelSize = int64(funding.MinChanFundingSize)
×
1379
        }
×
1380
        if cfg.Autopilot.MaxChannelSize > int64(MaxFundingAmount) {
3✔
1381
                cfg.Autopilot.MaxChannelSize = int64(MaxFundingAmount)
×
1382
        }
×
1383

1384
        // We'll now construct the network directory which will be where we
1385
        // store all the data specific to this chain/network.
1386
        cfg.networkDir = filepath.Join(
3✔
1387
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
3✔
1388
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
3✔
1389
        )
3✔
1390

3✔
1391
        // If a custom macaroon directory wasn't specified and the data
3✔
1392
        // directory has changed from the default path, then we'll also update
3✔
1393
        // the path for the macaroons to be generated.
3✔
1394
        if cfg.AdminMacPath == "" {
3✔
1395
                cfg.AdminMacPath = filepath.Join(
×
1396
                        cfg.networkDir, defaultAdminMacFilename,
×
1397
                )
×
1398
        }
×
1399
        if cfg.ReadMacPath == "" {
3✔
1400
                cfg.ReadMacPath = filepath.Join(
×
1401
                        cfg.networkDir, defaultReadMacFilename,
×
1402
                )
×
1403
        }
×
1404
        if cfg.InvoiceMacPath == "" {
3✔
1405
                cfg.InvoiceMacPath = filepath.Join(
×
1406
                        cfg.networkDir, defaultInvoiceMacFilename,
×
1407
                )
×
1408
        }
×
1409

1410
        towerDir := filepath.Join(
3✔
1411
                cfg.Watchtower.TowerDir, BitcoinChainName,
3✔
1412
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
3✔
1413
        )
3✔
1414

3✔
1415
        // Create the lnd directory and all other sub-directories if they don't
3✔
1416
        // already exist. This makes sure that directory trees are also created
3✔
1417
        // for files that point to outside the lnddir.
3✔
1418
        dirs := []string{
3✔
1419
                lndDir, cfg.DataDir, cfg.networkDir,
3✔
1420
                cfg.LetsEncryptDir, towerDir, cfg.graphDatabaseDir(),
3✔
1421
                filepath.Dir(cfg.TLSCertPath), filepath.Dir(cfg.TLSKeyPath),
3✔
1422
                filepath.Dir(cfg.AdminMacPath), filepath.Dir(cfg.ReadMacPath),
3✔
1423
                filepath.Dir(cfg.InvoiceMacPath),
3✔
1424
                filepath.Dir(cfg.Tor.PrivateKeyPath),
3✔
1425
                filepath.Dir(cfg.Tor.WatchtowerKeyPath),
3✔
1426
        }
3✔
1427
        for _, dir := range dirs {
6✔
1428
                if err := makeDirectory(dir); err != nil {
3✔
1429
                        return nil, err
×
1430
                }
×
1431
        }
1432

1433
        // Similarly, if a custom back up file path wasn't specified, then
1434
        // we'll update the file location to match our set network directory.
1435
        if cfg.BackupFilePath == "" {
6✔
1436
                cfg.BackupFilePath = filepath.Join(
3✔
1437
                        cfg.networkDir, chanbackup.DefaultBackupFileName,
3✔
1438
                )
3✔
1439
        }
3✔
1440

1441
        // Append the network type to the log directory so it is "namespaced"
1442
        // per network in the same fashion as the data directory.
1443
        cfg.LogDir = filepath.Join(
3✔
1444
                cfg.LogDir, BitcoinChainName,
3✔
1445
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
3✔
1446
        )
3✔
1447

3✔
1448
        if err := cfg.LogConfig.Validate(); err != nil {
3✔
1449
                return nil, mkErr("error validating logging config: %w", err)
×
1450
        }
×
1451

1452
        // If a sub-log manager was not already created, then we'll create one
1453
        // now using the default log handlers.
1454
        if cfg.SubLogMgr == nil {
6✔
1455
                cfg.SubLogMgr = build.NewSubLoggerManager(
3✔
1456
                        build.NewDefaultLogHandlers(
3✔
1457
                                cfg.LogConfig, cfg.LogRotator,
3✔
1458
                        )...,
3✔
1459
                )
3✔
1460
        }
3✔
1461

1462
        // Initialize logging at the default logging level.
1463
        SetupLoggers(cfg.SubLogMgr, interceptor)
3✔
1464

3✔
1465
        if cfg.MaxLogFiles != 0 {
3✔
1466
                if cfg.LogConfig.File.MaxLogFiles !=
×
1467
                        build.DefaultMaxLogFiles {
×
1468

×
1469
                        return nil, mkErr("cannot set both maxlogfiles and "+
×
1470
                                "logging.file.max-files", err)
×
1471
                }
×
1472

1473
                cfg.LogConfig.File.MaxLogFiles = cfg.MaxLogFiles
×
1474
        }
1475
        if cfg.MaxLogFileSize != 0 {
3✔
1476
                if cfg.LogConfig.File.MaxLogFileSize !=
×
1477
                        build.DefaultMaxLogFileSize {
×
1478

×
1479
                        return nil, mkErr("cannot set both maxlogfilesize and "+
×
1480
                                "logging.file.max-file-size", err)
×
1481
                }
×
1482

1483
                cfg.LogConfig.File.MaxLogFileSize = cfg.MaxLogFileSize
×
1484
        }
1485

1486
        err = cfg.LogRotator.InitLogRotator(
3✔
1487
                cfg.LogConfig.File,
3✔
1488
                filepath.Join(cfg.LogDir, defaultLogFilename),
3✔
1489
        )
3✔
1490
        if err != nil {
3✔
1491
                str := "log rotation setup failed: %v"
×
1492
                return nil, mkErr(str, err)
×
1493
        }
×
1494

1495
        // Parse, validate, and set debug log level(s).
1496
        err = build.ParseAndSetDebugLevels(cfg.DebugLevel, cfg.SubLogMgr)
3✔
1497
        if err != nil {
3✔
1498
                str := "error parsing debug level: %v"
×
1499
                return nil, &lncfg.UsageError{Err: mkErr(str, err)}
×
1500
        }
×
1501

1502
        // At least one RPCListener is required. So listen on localhost per
1503
        // default.
1504
        if len(cfg.RawRPCListeners) == 0 {
3✔
1505
                addr := fmt.Sprintf("localhost:%d", defaultRPCPort)
×
1506
                cfg.RawRPCListeners = append(cfg.RawRPCListeners, addr)
×
1507
        }
×
1508

1509
        // Listen on localhost if no REST listeners were specified.
1510
        if len(cfg.RawRESTListeners) == 0 {
3✔
1511
                addr := fmt.Sprintf("localhost:%d", defaultRESTPort)
×
1512
                cfg.RawRESTListeners = append(cfg.RawRESTListeners, addr)
×
1513
        }
×
1514

1515
        // Listen on the default interface/port if no listeners were specified.
1516
        // An empty address string means default interface/address, which on
1517
        // most unix systems is the same as 0.0.0.0. If Tor is active, we
1518
        // default to only listening on localhost for hidden service
1519
        // connections.
1520
        if len(cfg.RawListeners) == 0 {
3✔
1521
                addr := fmt.Sprintf(":%d", defaultPeerPort)
×
1522
                if cfg.Tor.Active && !cfg.Tor.SkipProxyForClearNetTargets {
×
1523
                        addr = fmt.Sprintf("localhost:%d", defaultPeerPort)
×
1524
                }
×
1525
                cfg.RawListeners = append(cfg.RawListeners, addr)
×
1526
        }
1527

1528
        // Add default port to all RPC listener addresses if needed and remove
1529
        // duplicate addresses.
1530
        cfg.RPCListeners, err = lncfg.NormalizeAddresses(
3✔
1531
                cfg.RawRPCListeners, strconv.Itoa(defaultRPCPort),
3✔
1532
                cfg.net.ResolveTCPAddr,
3✔
1533
        )
3✔
1534
        if err != nil {
3✔
1535
                return nil, mkErr("error normalizing RPC listen addrs: %v", err)
×
1536
        }
×
1537

1538
        // Add default port to all REST listener addresses if needed and remove
1539
        // duplicate addresses.
1540
        cfg.RESTListeners, err = lncfg.NormalizeAddresses(
3✔
1541
                cfg.RawRESTListeners, strconv.Itoa(defaultRESTPort),
3✔
1542
                cfg.net.ResolveTCPAddr,
3✔
1543
        )
3✔
1544
        if err != nil {
3✔
1545
                return nil, mkErr("error normalizing REST listen addrs: %v", err)
×
1546
        }
×
1547

1548
        switch {
3✔
1549
        // The no seed backup and auto unlock are mutually exclusive.
1550
        case cfg.NoSeedBackup && cfg.WalletUnlockPasswordFile != "":
×
1551
                return nil, mkErr("cannot set noseedbackup and " +
×
1552
                        "wallet-unlock-password-file at the same time")
×
1553

1554
        // The "allow-create" flag cannot be set without the auto unlock file.
1555
        case cfg.WalletUnlockAllowCreate && cfg.WalletUnlockPasswordFile == "":
×
1556
                return nil, mkErr("cannot set wallet-unlock-allow-create " +
×
1557
                        "without wallet-unlock-password-file")
×
1558

1559
        // If a password file was specified, we need it to exist.
1560
        case cfg.WalletUnlockPasswordFile != "" &&
1561
                !lnrpc.FileExists(cfg.WalletUnlockPasswordFile):
×
1562

×
1563
                return nil, mkErr("wallet unlock password file %s does "+
×
1564
                        "not exist", cfg.WalletUnlockPasswordFile)
×
1565
        }
1566

1567
        // For each of the RPC listeners (REST+gRPC), we'll ensure that users
1568
        // have specified a safe combo for authentication. If not, we'll bail
1569
        // out with an error. Since we don't allow disabling TLS for gRPC
1570
        // connections we pass in tlsActive=true.
1571
        err = lncfg.EnforceSafeAuthentication(
3✔
1572
                cfg.RPCListeners, !cfg.NoMacaroons, true,
3✔
1573
        )
3✔
1574
        if err != nil {
3✔
1575
                return nil, mkErr("error enforcing safe authentication on "+
×
1576
                        "RPC ports: %v", err)
×
1577
        }
×
1578

1579
        if cfg.DisableRest {
3✔
1580
                ltndLog.Infof("REST API is disabled!")
×
1581
                cfg.RESTListeners = nil
×
1582
        } else {
3✔
1583
                err = lncfg.EnforceSafeAuthentication(
3✔
1584
                        cfg.RESTListeners, !cfg.NoMacaroons, !cfg.DisableRestTLS,
3✔
1585
                )
3✔
1586
                if err != nil {
3✔
1587
                        return nil, mkErr("error enforcing safe "+
×
1588
                                "authentication on REST ports: %v", err)
×
1589
                }
×
1590
        }
1591

1592
        // Remove the listening addresses specified if listening is disabled.
1593
        if cfg.DisableListen {
6✔
1594
                ltndLog.Infof("Listening on the p2p interface is disabled!")
3✔
1595
                cfg.Listeners = nil
3✔
1596
                cfg.ExternalIPs = nil
3✔
1597
                cfg.ExternalDNSAddress = nil
3✔
1598
        } else {
6✔
1599

3✔
1600
                // Add default port to all listener addresses if needed and remove
3✔
1601
                // duplicate addresses.
3✔
1602
                cfg.Listeners, err = lncfg.NormalizeAddresses(
3✔
1603
                        cfg.RawListeners, strconv.Itoa(defaultPeerPort),
3✔
1604
                        cfg.net.ResolveTCPAddr,
3✔
1605
                )
3✔
1606
                if err != nil {
3✔
1607
                        return nil, mkErr("error normalizing p2p listen "+
×
1608
                                "addrs: %v", err)
×
1609
                }
×
1610

1611
                // Add default port to all external IP addresses if needed and remove
1612
                // duplicate addresses.
1613
                cfg.ExternalIPs, err = lncfg.NormalizeAddresses(
3✔
1614
                        cfg.RawExternalIPs, strconv.Itoa(defaultPeerPort),
3✔
1615
                        cfg.net.ResolveTCPAddr,
3✔
1616
                )
3✔
1617
                if err != nil {
3✔
1618
                        return nil, err
×
1619
                }
×
1620

1621
                // Parse the external DNS address if provided.
1622
                if cfg.RawExternalDNSAddress != "" {
6✔
1623
                        addr, err := parseDNSAddr(
3✔
1624
                                cfg.RawExternalDNSAddress, cfg.net,
3✔
1625
                        )
3✔
1626
                        if err != nil {
3✔
NEW
1627
                                return nil, err
×
NEW
1628
                        }
×
1629

1630
                        cfg.ExternalDNSAddress = addr
3✔
1631
                }
1632

1633
                // For the p2p port it makes no sense to listen to an Unix socket.
1634
                // Also, we would need to refactor the brontide listener to support
1635
                // that.
1636
                for _, p2pListener := range cfg.Listeners {
6✔
1637
                        if lncfg.IsUnix(p2pListener) {
3✔
1638
                                return nil, mkErr("unix socket addresses "+
×
1639
                                        "cannot be used for the p2p "+
×
1640
                                        "connection listener: %s", p2pListener)
×
1641
                        }
×
1642
                }
1643
        }
1644

1645
        // Ensure that the specified minimum backoff is below or equal to the
1646
        // maximum backoff.
1647
        if cfg.MinBackoff > cfg.MaxBackoff {
3✔
1648
                return nil, mkErr("maxbackoff must be greater than minbackoff")
×
1649
        }
×
1650

1651
        // Newer versions of lnd added a new sub-config for bolt-specific
1652
        // parameters. However, we want to also allow existing users to use the
1653
        // value on the top-level config. If the outer config value is set,
1654
        // then we'll use that directly.
1655
        flagSet, err := isSet("SyncFreelist")
3✔
1656
        if err != nil {
3✔
1657
                return nil, mkErr("error parsing freelist sync flag: %v", err)
×
1658
        }
×
1659
        if flagSet {
3✔
1660
                cfg.DB.Bolt.NoFreelistSync = !cfg.SyncFreelist
×
1661
        }
×
1662

1663
        // Parse any extra sqlite pragma options that may have been provided
1664
        // to determine if they override any of the defaults that we will
1665
        // otherwise add.
1666
        var (
3✔
1667
                defaultSynchronous = true
3✔
1668
                defaultAutoVacuum  = true
3✔
1669
                defaultFullfsync   = true
3✔
1670
        )
3✔
1671
        for _, option := range cfg.DB.Sqlite.PragmaOptions {
3✔
1672
                switch {
×
1673
                case strings.HasPrefix(option, "synchronous="):
×
1674
                        defaultSynchronous = false
×
1675

1676
                case strings.HasPrefix(option, "auto_vacuum="):
×
1677
                        defaultAutoVacuum = false
×
1678

1679
                case strings.HasPrefix(option, "fullfsync="):
×
1680
                        defaultFullfsync = false
×
1681

1682
                default:
×
1683
                }
1684
        }
1685

1686
        if defaultSynchronous {
6✔
1687
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1688
                        cfg.DB.Sqlite.PragmaOptions, "synchronous=full",
3✔
1689
                )
3✔
1690
        }
3✔
1691

1692
        if defaultAutoVacuum {
6✔
1693
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1694
                        cfg.DB.Sqlite.PragmaOptions, "auto_vacuum=incremental",
3✔
1695
                )
3✔
1696
        }
3✔
1697

1698
        if defaultFullfsync {
6✔
1699
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1700
                        cfg.DB.Sqlite.PragmaOptions, "fullfsync=true",
3✔
1701
                )
3✔
1702
        }
3✔
1703

1704
        // Ensure that the user hasn't chosen a remote-max-htlc value greater
1705
        // than the protocol maximum.
1706
        maxRemoteHtlcs := uint16(input.MaxHTLCNumber / 2)
3✔
1707
        if cfg.DefaultRemoteMaxHtlcs > maxRemoteHtlcs {
3✔
1708
                return nil, mkErr("default-remote-max-htlcs (%v) must be "+
×
1709
                        "less than %v", cfg.DefaultRemoteMaxHtlcs,
×
1710
                        maxRemoteHtlcs)
×
1711
        }
×
1712

1713
        // Clamp the ChannelCommitInterval so that commitment updates can still
1714
        // happen in a reasonable timeframe.
1715
        if cfg.ChannelCommitInterval > maxChannelCommitInterval {
3✔
1716
                return nil, mkErr("channel-commit-interval (%v) must be less "+
×
1717
                        "than %v", cfg.ChannelCommitInterval,
×
1718
                        maxChannelCommitInterval)
×
1719
        }
×
1720

1721
        // Limit PendingCommitInterval so we don't wait too long for the remote
1722
        // party to send back a revoke.
1723
        if cfg.PendingCommitInterval > maxPendingCommitInterval {
3✔
1724
                return nil, mkErr("pending-commit-interval (%v) must be less "+
×
1725
                        "than %v", cfg.PendingCommitInterval,
×
1726
                        maxPendingCommitInterval)
×
1727
        }
×
1728

1729
        if err := cfg.Gossip.Parse(); err != nil {
3✔
1730
                return nil, mkErr("error parsing gossip syncer: %v", err)
×
1731
        }
×
1732

1733
        // If the experimental protocol options specify any protocol messages
1734
        // that we want to handle as custom messages, set them now.
1735
        customMsg := cfg.ProtocolOptions.CustomMessageOverrides()
3✔
1736

3✔
1737
        // We can safely set our custom override values during startup because
3✔
1738
        // startup is blocked on config parsing.
3✔
1739
        if err := lnwire.SetCustomOverrides(customMsg); err != nil {
3✔
1740
                return nil, mkErr("custom-message: %v", err)
×
1741
        }
×
1742

1743
        // Map old pprof flags to new pprof group flags.
1744
        //
1745
        // NOTE: This is a temporary measure to ensure compatibility with old
1746
        // flags.
1747
        if cfg.CPUProfile != "" {
3✔
1748
                if cfg.Pprof.CPUProfile != "" {
×
1749
                        return nil, mkErr("cpuprofile and pprof.cpuprofile " +
×
1750
                                "are mutually exclusive")
×
1751
                }
×
1752
                cfg.Pprof.CPUProfile = cfg.CPUProfile
×
1753
        }
1754
        if cfg.Profile != "" {
3✔
1755
                if cfg.Pprof.Profile != "" {
×
1756
                        return nil, mkErr("profile and pprof.profile " +
×
1757
                                "are mutually exclusive")
×
1758
                }
×
1759
                cfg.Pprof.Profile = cfg.Profile
×
1760
        }
1761
        if cfg.BlockingProfile != 0 {
3✔
1762
                if cfg.Pprof.BlockingProfile != 0 {
×
1763
                        return nil, mkErr("blockingprofile and " +
×
1764
                                "pprof.blockingprofile are mutually exclusive")
×
1765
                }
×
1766
                cfg.Pprof.BlockingProfile = cfg.BlockingProfile
×
1767
        }
1768
        if cfg.MutexProfile != 0 {
3✔
1769
                if cfg.Pprof.MutexProfile != 0 {
×
1770
                        return nil, mkErr("mutexprofile and " +
×
1771
                                "pprof.mutexprofile are mutually exclusive")
×
1772
                }
×
1773
                cfg.Pprof.MutexProfile = cfg.MutexProfile
×
1774
        }
1775

1776
        // Don't allow both the old dust-threshold and the new
1777
        // channel-max-fee-exposure to be set.
1778
        if cfg.DustThreshold != 0 && cfg.MaxFeeExposure != 0 {
3✔
1779
                return nil, mkErr("cannot set both dust-threshold and " +
×
1780
                        "channel-max-fee-exposure")
×
1781
        }
×
1782

1783
        switch {
3✔
1784
        // Use the old dust-threshold as the max fee exposure if it is set and
1785
        // the new option is not.
1786
        case cfg.DustThreshold != 0:
×
1787
                cfg.MaxFeeExposure = cfg.DustThreshold
×
1788

1789
        // Use the default max fee exposure if the new option is not set and
1790
        // the old one is not set either.
1791
        case cfg.MaxFeeExposure == 0:
3✔
1792
                cfg.MaxFeeExposure = uint64(
3✔
1793
                        htlcswitch.DefaultMaxFeeExposure.ToSatoshis(),
3✔
1794
                )
3✔
1795
        }
1796

1797
        // Validate the subconfigs for workers, caches, and the tower client.
1798
        err = lncfg.Validate(
3✔
1799
                cfg.Workers,
3✔
1800
                cfg.Caches,
3✔
1801
                cfg.WtClient,
3✔
1802
                cfg.DB,
3✔
1803
                cfg.Cluster,
3✔
1804
                cfg.HealthChecks,
3✔
1805
                cfg.RPCMiddleware,
3✔
1806
                cfg.RemoteSigner,
3✔
1807
                cfg.Sweeper,
3✔
1808
                cfg.Htlcswitch,
3✔
1809
                cfg.Invoices,
3✔
1810
                cfg.Routing,
3✔
1811
                cfg.Pprof,
3✔
1812
                cfg.Gossip,
3✔
1813
        )
3✔
1814
        if err != nil {
3✔
1815
                return nil, err
×
1816
        }
×
1817

1818
        // Finally, ensure that the user's color is correctly formatted,
1819
        // otherwise the server will not be able to start after the unlocking
1820
        // the wallet.
1821
        _, err = lncfg.ParseHexColor(cfg.Color)
3✔
1822
        if err != nil {
3✔
1823
                return nil, mkErr("unable to parse node color: %v", err)
×
1824
        }
×
1825

1826
        // All good, return the sanitized result.
1827
        return &cfg, nil
3✔
1828
}
1829

1830
// graphDatabaseDir returns the default directory where the local bolt graph db
1831
// files are stored.
1832
func (c *Config) graphDatabaseDir() string {
3✔
1833
        return filepath.Join(
3✔
1834
                c.DataDir, defaultGraphSubDirname,
3✔
1835
                lncfg.NormalizeNetwork(c.ActiveNetParams.Name),
3✔
1836
        )
3✔
1837
}
3✔
1838

1839
// ImplementationConfig returns the configuration of what actual implementations
1840
// should be used when creating the main lnd instance.
1841
func (c *Config) ImplementationConfig(
1842
        interceptor signal.Interceptor) *ImplementationCfg {
3✔
1843

3✔
1844
        // If we're using a remote signer, we still need the base wallet as a
3✔
1845
        // watch-only source of chain and address data. But we don't need any
3✔
1846
        // private key material in that btcwallet base wallet.
3✔
1847
        if c.RemoteSigner.Enable {
6✔
1848
                rpcImpl := NewRPCSignerWalletImpl(
3✔
1849
                        c, ltndLog, interceptor,
3✔
1850
                        c.RemoteSigner.MigrateWatchOnly,
3✔
1851
                )
3✔
1852
                return &ImplementationCfg{
3✔
1853
                        GrpcRegistrar:     rpcImpl,
3✔
1854
                        RestRegistrar:     rpcImpl,
3✔
1855
                        ExternalValidator: rpcImpl,
3✔
1856
                        DatabaseBuilder: NewDefaultDatabaseBuilder(
3✔
1857
                                c, ltndLog,
3✔
1858
                        ),
3✔
1859
                        WalletConfigBuilder: rpcImpl,
3✔
1860
                        ChainControlBuilder: rpcImpl,
3✔
1861
                }
3✔
1862
        }
3✔
1863

1864
        defaultImpl := NewDefaultWalletImpl(c, ltndLog, interceptor, false)
3✔
1865
        return &ImplementationCfg{
3✔
1866
                GrpcRegistrar:       defaultImpl,
3✔
1867
                RestRegistrar:       defaultImpl,
3✔
1868
                ExternalValidator:   defaultImpl,
3✔
1869
                DatabaseBuilder:     NewDefaultDatabaseBuilder(c, ltndLog),
3✔
1870
                WalletConfigBuilder: defaultImpl,
3✔
1871
                ChainControlBuilder: defaultImpl,
3✔
1872
        }
3✔
1873
}
1874

1875
// CleanAndExpandPath expands environment variables and leading ~ in the
1876
// passed path, cleans the result, and returns it.
1877
// This function is taken from https://github.com/btcsuite/btcd
1878
func CleanAndExpandPath(path string) string {
3✔
1879
        if path == "" {
6✔
1880
                return ""
3✔
1881
        }
3✔
1882

1883
        // Expand initial ~ to OS specific home directory.
1884
        if strings.HasPrefix(path, "~") {
3✔
1885
                var homeDir string
×
1886
                u, err := user.Current()
×
1887
                if err == nil {
×
1888
                        homeDir = u.HomeDir
×
1889
                } else {
×
1890
                        homeDir = os.Getenv("HOME")
×
1891
                }
×
1892

1893
                path = strings.Replace(path, "~", homeDir, 1)
×
1894
        }
1895

1896
        // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
1897
        // but the variables can still be expanded via POSIX-style $VARIABLE.
1898
        return filepath.Clean(os.ExpandEnv(path))
3✔
1899
}
1900

1901
func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
1902
        netParams chainreg.BitcoinNetParams) error {
2✔
1903

2✔
1904
        // First, we'll check our node config to make sure the RPC parameters
2✔
1905
        // were set correctly. We'll also determine the path to the conf file
2✔
1906
        // depending on the backend node.
2✔
1907
        var daemonName, confDir, confFile, confFileBase string
2✔
1908
        switch conf := nodeConfig.(type) {
2✔
1909
        case *lncfg.Btcd:
1✔
1910
                // Resolves environment variable references in RPCUser and
1✔
1911
                // RPCPass fields.
1✔
1912
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
1✔
1913
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
1✔
1914

1✔
1915
                // If both RPCUser and RPCPass are set, we assume those
1✔
1916
                // credentials are good to use.
1✔
1917
                if conf.RPCUser != "" && conf.RPCPass != "" {
2✔
1918
                        return nil
1✔
1919
                }
1✔
1920

1921
                // Set the daemon name for displaying proper errors.
1922
                daemonName = btcdBackendName
×
1923
                confDir = conf.Dir
×
1924
                confFileBase = btcdBackendName
×
1925

×
1926
                // If only ONE of RPCUser or RPCPass is set, we assume the
×
1927
                // user did that unintentionally.
×
1928
                if conf.RPCUser != "" || conf.RPCPass != "" {
×
1929
                        return fmt.Errorf("please set both or neither of "+
×
1930
                                "%[1]v.rpcuser, %[1]v.rpcpass", daemonName)
×
1931
                }
×
1932

1933
        case *lncfg.Bitcoind:
1✔
1934
                // Ensure that if the ZMQ options are set, that they are not
1✔
1935
                // equal.
1✔
1936
                if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
2✔
1937
                        err := checkZMQOptions(
1✔
1938
                                conf.ZMQPubRawBlock, conf.ZMQPubRawTx,
1✔
1939
                        )
1✔
1940
                        if err != nil {
1✔
1941
                                return err
×
1942
                        }
×
1943
                }
1944

1945
                // Ensure that if the estimate mode is set, that it is a legal
1946
                // value.
1947
                if conf.EstimateMode != "" {
2✔
1948
                        err := checkEstimateMode(conf.EstimateMode)
1✔
1949
                        if err != nil {
1✔
1950
                                return err
×
1951
                        }
×
1952
                }
1953

1954
                // Set the daemon name for displaying proper errors.
1955
                daemonName = bitcoindBackendName
1✔
1956
                confDir = conf.Dir
1✔
1957
                confFile = conf.ConfigPath
1✔
1958
                confFileBase = BitcoinChainName
1✔
1959

1✔
1960
                // Resolves environment variable references in RPCUser
1✔
1961
                // and RPCPass fields.
1✔
1962
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
1✔
1963
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
1✔
1964

1✔
1965
                // Check that cookie and credentials don't contradict each
1✔
1966
                // other.
1✔
1967
                if (conf.RPCUser != "" || conf.RPCPass != "") &&
1✔
1968
                        conf.RPCCookie != "" {
1✔
1969

×
1970
                        return fmt.Errorf("please only provide either "+
×
1971
                                "%[1]v.rpccookie or %[1]v.rpcuser and "+
×
1972
                                "%[1]v.rpcpass", daemonName)
×
1973
                }
×
1974

1975
                // We convert the cookie into a user name and password.
1976
                if conf.RPCCookie != "" {
1✔
1977
                        cookie, err := os.ReadFile(conf.RPCCookie)
×
1978
                        if err != nil {
×
1979
                                return fmt.Errorf("cannot read cookie file: %w",
×
1980
                                        err)
×
1981
                        }
×
1982

1983
                        splitCookie := strings.Split(string(cookie), ":")
×
1984
                        if len(splitCookie) != 2 {
×
1985
                                return fmt.Errorf("cookie file has a wrong " +
×
1986
                                        "format")
×
1987
                        }
×
1988
                        conf.RPCUser = splitCookie[0]
×
1989
                        conf.RPCPass = splitCookie[1]
×
1990
                }
1991

1992
                if conf.RPCUser != "" && conf.RPCPass != "" {
2✔
1993
                        // If all of RPCUser, RPCPass, ZMQBlockHost, and
1✔
1994
                        // ZMQTxHost are set, we assume those parameters are
1✔
1995
                        // good to use.
1✔
1996
                        if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
2✔
1997
                                return nil
1✔
1998
                        }
1✔
1999

2000
                        // If RPCUser and RPCPass are set and RPCPolling is
2001
                        // enabled, we assume the parameters are good to use.
2002
                        if conf.RPCPolling {
×
2003
                                return nil
×
2004
                        }
×
2005
                }
2006

2007
                // If not all of the parameters are set, we'll assume the user
2008
                // did this unintentionally.
2009
                if conf.RPCUser != "" || conf.RPCPass != "" ||
×
2010
                        conf.ZMQPubRawBlock != "" || conf.ZMQPubRawTx != "" {
×
2011

×
2012
                        return fmt.Errorf("please set %[1]v.rpcuser and "+
×
2013
                                "%[1]v.rpcpass (or %[1]v.rpccookie) together "+
×
2014
                                "with %[1]v.zmqpubrawblock, %[1]v.zmqpubrawtx",
×
2015
                                daemonName)
×
2016
                }
×
2017
        }
2018

2019
        // If we're in simnet mode, then the running btcd instance won't read
2020
        // the RPC credentials from the configuration. So if lnd wasn't
2021
        // specified the parameters, then we won't be able to start.
2022
        if cConfig.SimNet {
×
2023
                return fmt.Errorf("rpcuser and rpcpass must be set to your " +
×
2024
                        "btcd node's RPC parameters for simnet mode")
×
2025
        }
×
2026

2027
        fmt.Println("Attempting automatic RPC configuration to " + daemonName)
×
2028

×
2029
        if confFile == "" {
×
2030
                confFile = filepath.Join(confDir, fmt.Sprintf("%v.conf",
×
2031
                        confFileBase))
×
2032
        }
×
2033
        switch cConfig.Node {
×
2034
        case btcdBackendName:
×
2035
                nConf := nodeConfig.(*lncfg.Btcd)
×
2036
                rpcUser, rpcPass, err := extractBtcdRPCParams(confFile)
×
2037
                if err != nil {
×
2038
                        return fmt.Errorf("unable to extract RPC credentials: "+
×
2039
                                "%v, cannot start w/o RPC connection", err)
×
2040
                }
×
2041
                nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass
×
2042

2043
        case bitcoindBackendName:
×
2044
                nConf := nodeConfig.(*lncfg.Bitcoind)
×
2045
                rpcUser, rpcPass, zmqBlockHost, zmqTxHost, err :=
×
2046
                        extractBitcoindRPCParams(netParams.Params.Name,
×
2047
                                nConf.Dir, confFile, nConf.RPCCookie)
×
2048
                if err != nil {
×
2049
                        return fmt.Errorf("unable to extract RPC credentials: "+
×
2050
                                "%v, cannot start w/o RPC connection", err)
×
2051
                }
×
2052
                nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass
×
2053
                nConf.ZMQPubRawBlock, nConf.ZMQPubRawTx = zmqBlockHost, zmqTxHost
×
2054
        }
2055

2056
        fmt.Printf("Automatically obtained %v's RPC credentials\n", daemonName)
×
2057
        return nil
×
2058
}
2059

2060
// supplyEnvValue supplies the value of an environment variable from a string.
2061
// It supports the following formats:
2062
// 1) $ENV_VAR
2063
// 2) ${ENV_VAR}
2064
// 3) ${ENV_VAR:-DEFAULT}
2065
//
2066
// Standard environment variable naming conventions:
2067
// - ENV_VAR contains letters, digits, and underscores, and does
2068
// not start with a digit.
2069
// - DEFAULT follows the rule that it can contain any characters except
2070
// whitespace.
2071
//
2072
// Parameters:
2073
// - value: The input string containing references to environment variables
2074
// (if any).
2075
//
2076
// Returns:
2077
// - string: The value of the specified environment variable, the default
2078
// value if provided, or the original input string if no matching variable is
2079
// found or set.
2080
func supplyEnvValue(value string) string {
9✔
2081
        // Regex for $ENV_VAR format.
9✔
2082
        var reEnvVar = regexp.MustCompile(`^\$([a-zA-Z_][a-zA-Z0-9_]*)$`)
9✔
2083

9✔
2084
        // Regex for ${ENV_VAR} format.
9✔
2085
        var reEnvVarWithBrackets = regexp.MustCompile(
9✔
2086
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}$`,
9✔
2087
        )
9✔
2088

9✔
2089
        // Regex for ${ENV_VAR:-DEFAULT} format.
9✔
2090
        var reEnvVarWithDefault = regexp.MustCompile(
9✔
2091
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*):-([\S]+)\}$`,
9✔
2092
        )
9✔
2093

9✔
2094
        // Match against supported formats.
9✔
2095
        switch {
9✔
2096
        case reEnvVarWithDefault.MatchString(value):
3✔
2097
                matches := reEnvVarWithDefault.FindStringSubmatch(value)
3✔
2098
                envVariable := matches[1]
3✔
2099
                defaultValue := matches[2]
3✔
2100
                if envValue := os.Getenv(envVariable); envValue != "" {
4✔
2101
                        return envValue
1✔
2102
                }
1✔
2103

2104
                return defaultValue
2✔
2105

2106
        case reEnvVarWithBrackets.MatchString(value):
×
2107
                matches := reEnvVarWithBrackets.FindStringSubmatch(value)
×
2108
                envVariable := matches[1]
×
2109
                envValue := os.Getenv(envVariable)
×
2110

×
2111
                return envValue
×
2112

2113
        case reEnvVar.MatchString(value):
3✔
2114
                matches := reEnvVar.FindStringSubmatch(value)
3✔
2115
                envVariable := matches[1]
3✔
2116
                envValue := os.Getenv(envVariable)
3✔
2117

3✔
2118
                return envValue
3✔
2119
        }
2120

2121
        return value
3✔
2122
}
2123

2124
// extractBtcdRPCParams attempts to extract the RPC credentials for an existing
2125
// btcd instance. The passed path is expected to be the location of btcd's
2126
// application data directory on the target system.
2127
func extractBtcdRPCParams(btcdConfigPath string) (string, string, error) {
×
2128
        // First, we'll open up the btcd configuration file found at the target
×
2129
        // destination.
×
2130
        btcdConfigFile, err := os.Open(btcdConfigPath)
×
2131
        if err != nil {
×
2132
                return "", "", err
×
2133
        }
×
2134
        defer func() { _ = btcdConfigFile.Close() }()
×
2135

2136
        // With the file open extract the contents of the configuration file so
2137
        // we can attempt to locate the RPC credentials.
2138
        configContents, err := io.ReadAll(btcdConfigFile)
×
2139
        if err != nil {
×
2140
                return "", "", err
×
2141
        }
×
2142

2143
        // Attempt to locate the RPC user using a regular expression. If we
2144
        // don't have a match for our regular expression then we'll exit with
2145
        // an error.
2146
        rpcUserRegexp, err := regexp.Compile(`(?m)^\s*rpcuser\s*=\s*([^\s]+)`)
×
2147
        if err != nil {
×
2148
                return "", "", err
×
2149
        }
×
2150
        userSubmatches := rpcUserRegexp.FindSubmatch(configContents)
×
2151
        if userSubmatches == nil {
×
2152
                return "", "", fmt.Errorf("unable to find rpcuser in config")
×
2153
        }
×
2154

2155
        // Similarly, we'll use another regular expression to find the set
2156
        // rpcpass (if any). If we can't find the pass, then we'll exit with an
2157
        // error.
2158
        rpcPassRegexp, err := regexp.Compile(`(?m)^\s*rpcpass\s*=\s*([^\s]+)`)
×
2159
        if err != nil {
×
2160
                return "", "", err
×
2161
        }
×
2162
        passSubmatches := rpcPassRegexp.FindSubmatch(configContents)
×
2163
        if passSubmatches == nil {
×
2164
                return "", "", fmt.Errorf("unable to find rpcuser in config")
×
2165
        }
×
2166

2167
        return supplyEnvValue(string(userSubmatches[1])),
×
2168
                supplyEnvValue(string(passSubmatches[1])), nil
×
2169
}
2170

2171
// extractBitcoindRPCParams attempts to extract the RPC credentials for an
2172
// existing bitcoind node instance. The routine looks for a cookie first,
2173
// optionally following the datadir configuration option in the bitcoin.conf. If
2174
// it doesn't find one, it looks for rpcuser/rpcpassword.
2175
func extractBitcoindRPCParams(networkName, bitcoindDataDir, bitcoindConfigPath,
2176
        rpcCookiePath string) (string, string, string, string, error) {
×
2177

×
2178
        // First, we'll open up the bitcoind configuration file found at the
×
2179
        // target destination.
×
2180
        bitcoindConfigFile, err := os.Open(bitcoindConfigPath)
×
2181
        if err != nil {
×
2182
                return "", "", "", "", err
×
2183
        }
×
2184
        defer func() { _ = bitcoindConfigFile.Close() }()
×
2185

2186
        // With the file open extract the contents of the configuration file so
2187
        // we can attempt to locate the RPC credentials.
2188
        configContents, err := io.ReadAll(bitcoindConfigFile)
×
2189
        if err != nil {
×
2190
                return "", "", "", "", err
×
2191
        }
×
2192

2193
        // First, we'll look for the ZMQ hosts providing raw block and raw
2194
        // transaction notifications.
2195
        zmqBlockHostRE, err := regexp.Compile(
×
2196
                `(?m)^\s*zmqpubrawblock\s*=\s*([^\s]+)`,
×
2197
        )
×
2198
        if err != nil {
×
2199
                return "", "", "", "", err
×
2200
        }
×
2201
        zmqBlockHostSubmatches := zmqBlockHostRE.FindSubmatch(configContents)
×
2202
        if len(zmqBlockHostSubmatches) < 2 {
×
2203
                return "", "", "", "", fmt.Errorf("unable to find " +
×
2204
                        "zmqpubrawblock in config")
×
2205
        }
×
2206
        zmqTxHostRE, err := regexp.Compile(`(?m)^\s*zmqpubrawtx\s*=\s*([^\s]+)`)
×
2207
        if err != nil {
×
2208
                return "", "", "", "", err
×
2209
        }
×
2210
        zmqTxHostSubmatches := zmqTxHostRE.FindSubmatch(configContents)
×
2211
        if len(zmqTxHostSubmatches) < 2 {
×
2212
                return "", "", "", "", errors.New("unable to find zmqpubrawtx " +
×
2213
                        "in config")
×
2214
        }
×
2215
        zmqBlockHost := string(zmqBlockHostSubmatches[1])
×
2216
        zmqTxHost := string(zmqTxHostSubmatches[1])
×
2217
        if err := checkZMQOptions(zmqBlockHost, zmqTxHost); err != nil {
×
2218
                return "", "", "", "", err
×
2219
        }
×
2220

2221
        // Next, we'll try to find an auth cookie. We need to detect the chain
2222
        // by seeing if one is specified in the configuration file.
2223
        dataDir := filepath.Dir(bitcoindConfigPath)
×
2224
        if bitcoindDataDir != "" {
×
2225
                dataDir = bitcoindDataDir
×
2226
        }
×
2227
        dataDirRE, err := regexp.Compile(`(?m)^\s*datadir\s*=\s*([^\s]+)`)
×
2228
        if err != nil {
×
2229
                return "", "", "", "", err
×
2230
        }
×
2231
        dataDirSubmatches := dataDirRE.FindSubmatch(configContents)
×
2232
        if dataDirSubmatches != nil {
×
2233
                dataDir = string(dataDirSubmatches[1])
×
2234
        }
×
2235

2236
        var chainDir string
×
2237
        switch networkName {
×
2238
        case "mainnet":
×
2239
                chainDir = ""
×
2240
        case "regtest", "testnet3", "testnet4", "signet":
×
2241
                chainDir = networkName
×
2242
        default:
×
2243
                return "", "", "", "", fmt.Errorf("unexpected networkname %v", networkName)
×
2244
        }
2245

2246
        cookiePath := filepath.Join(dataDir, chainDir, ".cookie")
×
2247
        if rpcCookiePath != "" {
×
2248
                cookiePath = rpcCookiePath
×
2249
        }
×
2250
        cookie, err := os.ReadFile(cookiePath)
×
2251
        if err == nil {
×
2252
                splitCookie := strings.Split(string(cookie), ":")
×
2253
                if len(splitCookie) == 2 {
×
2254
                        return splitCookie[0], splitCookie[1], zmqBlockHost,
×
2255
                                zmqTxHost, nil
×
2256
                }
×
2257
        }
2258

2259
        // We didn't find a cookie, so we attempt to locate the RPC user using
2260
        // a regular expression. If we  don't have a match for our regular
2261
        // expression then we'll exit with an error.
2262
        rpcUserRegexp, err := regexp.Compile(`(?m)^\s*rpcuser\s*=\s*([^\s]+)`)
×
2263
        if err != nil {
×
2264
                return "", "", "", "", err
×
2265
        }
×
2266
        userSubmatches := rpcUserRegexp.FindSubmatch(configContents)
×
2267

×
2268
        // Similarly, we'll use another regular expression to find the set
×
2269
        // rpcpass (if any). If we can't find the pass, then we'll exit with an
×
2270
        // error.
×
2271
        rpcPassRegexp, err := regexp.Compile(`(?m)^\s*rpcpassword\s*=\s*([^\s]+)`)
×
2272
        if err != nil {
×
2273
                return "", "", "", "", err
×
2274
        }
×
2275
        passSubmatches := rpcPassRegexp.FindSubmatch(configContents)
×
2276

×
2277
        // Exit with an error if the cookie file, is defined in config, and
×
2278
        // can not be found, with both rpcuser and rpcpassword undefined.
×
2279
        if rpcCookiePath != "" && userSubmatches == nil && passSubmatches == nil {
×
2280
                return "", "", "", "", fmt.Errorf("unable to open cookie file (%v)",
×
2281
                        rpcCookiePath)
×
2282
        }
×
2283

2284
        if userSubmatches == nil {
×
2285
                return "", "", "", "", fmt.Errorf("unable to find rpcuser in " +
×
2286
                        "config")
×
2287
        }
×
2288
        if passSubmatches == nil {
×
2289
                return "", "", "", "", fmt.Errorf("unable to find rpcpassword " +
×
2290
                        "in config")
×
2291
        }
×
2292

2293
        return supplyEnvValue(string(userSubmatches[1])),
×
2294
                supplyEnvValue(string(passSubmatches[1])),
×
2295
                zmqBlockHost, zmqTxHost, nil
×
2296
}
2297

2298
// checkZMQOptions ensures that the provided addresses to use as the hosts for
2299
// ZMQ rawblock and rawtx notifications are different.
2300
func checkZMQOptions(zmqBlockHost, zmqTxHost string) error {
1✔
2301
        if zmqBlockHost == zmqTxHost {
1✔
2302
                return errors.New("zmqpubrawblock and zmqpubrawtx must be set " +
×
2303
                        "to different addresses")
×
2304
        }
×
2305

2306
        return nil
1✔
2307
}
2308

2309
// checkEstimateMode ensures that the provided estimate mode is legal.
2310
func checkEstimateMode(estimateMode string) error {
1✔
2311
        for _, mode := range bitcoindEstimateModes {
2✔
2312
                if estimateMode == mode {
2✔
2313
                        return nil
1✔
2314
                }
1✔
2315
        }
2316

2317
        return fmt.Errorf("estimatemode must be one of the following: %v",
×
2318
                bitcoindEstimateModes[:])
×
2319
}
2320

2321
// configToFlatMap converts the given config struct into a flat map of
2322
// key/value pairs using the dot notation we are used to from the config file
2323
// or command line flags. It also returns a map containing deprecated config
2324
// options.
2325
func configToFlatMap(cfg Config) (map[string]string,
2326
        map[string]struct{}, error) {
4✔
2327

4✔
2328
        result := make(map[string]string)
4✔
2329

4✔
2330
        // deprecated stores a map of deprecated options found in the config
4✔
2331
        // that are set by the users. A config option is considered as
4✔
2332
        // deprecated if it has a `hidden` flag.
4✔
2333
        deprecated := make(map[string]struct{})
4✔
2334

4✔
2335
        // redact is the helper function that redacts sensitive values like
4✔
2336
        // passwords.
4✔
2337
        redact := func(key, value string) string {
331✔
2338
                sensitiveKeySuffixes := []string{
327✔
2339
                        "pass",
327✔
2340
                        "password",
327✔
2341
                        "dsn",
327✔
2342
                }
327✔
2343
                for _, suffix := range sensitiveKeySuffixes {
1,295✔
2344
                        if strings.HasSuffix(key, suffix) {
976✔
2345
                                return "[redacted]"
8✔
2346
                        }
8✔
2347
                }
2348

2349
                return value
322✔
2350
        }
2351

2352
        // printConfig is the helper function that goes into nested structs
2353
        // recursively. Because we call it recursively, we need to declare it
2354
        // before we define it.
2355
        var printConfig func(reflect.Value, string)
4✔
2356
        printConfig = func(obj reflect.Value, prefix string) {
70✔
2357
                // Turn struct pointers into the actual struct, so we can
66✔
2358
                // iterate over the fields as we would with a struct value.
66✔
2359
                if obj.Kind() == reflect.Ptr {
125✔
2360
                        obj = obj.Elem()
59✔
2361
                }
59✔
2362

2363
                // Abort on nil values.
2364
                if !obj.IsValid() {
78✔
2365
                        return
12✔
2366
                }
12✔
2367

2368
                // Loop over all fields of the struct and inspect the type.
2369
                for i := 0; i < obj.NumField(); i++ {
461✔
2370
                        field := obj.Field(i)
407✔
2371
                        fieldType := obj.Type().Field(i)
407✔
2372

407✔
2373
                        longName := fieldType.Tag.Get("long")
407✔
2374
                        namespace := fieldType.Tag.Get("namespace")
407✔
2375
                        group := fieldType.Tag.Get("group")
407✔
2376
                        hidden := fieldType.Tag.Get("hidden")
407✔
2377

407✔
2378
                        switch {
407✔
2379
                        // We have a long name defined, this is a config value.
2380
                        case longName != "":
327✔
2381
                                key := longName
327✔
2382
                                if prefix != "" {
554✔
2383
                                        key = prefix + "." + key
227✔
2384
                                }
227✔
2385

2386
                                // Add the value directly to the flattened map.
2387
                                result[key] = redact(key, fmt.Sprintf(
327✔
2388
                                        "%v", field.Interface(),
327✔
2389
                                ))
327✔
2390

327✔
2391
                                // If there's a hidden flag, it's deprecated.
327✔
2392
                                if hidden == "true" && !field.IsZero() {
328✔
2393
                                        deprecated[key] = struct{}{}
1✔
2394
                                }
1✔
2395

2396
                        // We have no long name but a namespace, this is a
2397
                        // nested struct.
2398
                        case longName == "" && namespace != "":
59✔
2399
                                key := namespace
59✔
2400
                                if prefix != "" {
79✔
2401
                                        key = prefix + "." + key
20✔
2402
                                }
20✔
2403

2404
                                printConfig(field, key)
59✔
2405

2406
                        // Just a group means this is a dummy struct to house
2407
                        // multiple config values, the group name doesn't go
2408
                        // into the final field name.
2409
                        case longName == "" && group != "":
4✔
2410
                                printConfig(field, prefix)
4✔
2411

2412
                        // Anonymous means embedded struct. We need to recurse
2413
                        // into it but without adding anything to the prefix.
2414
                        case fieldType.Anonymous:
8✔
2415
                                printConfig(field, prefix)
8✔
2416

2417
                        default:
21✔
2418
                                continue
21✔
2419
                        }
2420
                }
2421
        }
2422

2423
        // Turn the whole config struct into a flat map.
2424
        printConfig(reflect.ValueOf(cfg), "")
4✔
2425

4✔
2426
        return result, deprecated, nil
4✔
2427
}
2428

2429
// logWarningsForDeprecation logs a warning if a deprecated config option is
2430
// set.
2431
func logWarningsForDeprecation(cfg Config) {
3✔
2432
        _, deprecated, err := configToFlatMap(cfg)
3✔
2433
        if err != nil {
3✔
2434
                ltndLog.Errorf("Convert configs to map: %v", err)
×
2435
        }
×
2436

2437
        for k := range deprecated {
3✔
2438
                ltndLog.Warnf("Config '%s' is deprecated, please remove it", k)
×
2439
        }
×
2440
}
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