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

lightningnetwork / lnd / 15280233992

27 May 2025 04:07PM UTC coverage: 58.56% (+0.6%) from 57.977%
15280233992

Pull #9455

github

web-flow
Merge 015c776a9 into 93a6ab875
Pull Request #9455: discovery+lnwire: add support for DNS host name in NodeAnnouncement msg

145 of 291 new or added lines in 7 files covered. (49.83%)

120 existing lines in 10 files now uncovered.

97608 of 166681 relevant lines covered (58.56%)

1.82 hits per line

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

54.17
/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 number of restricted slots
242
        // we'll allocate in the server.
243
        DefaultNumRestrictedSlots = 30
244

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

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

252
        defaultPrunedNodeMaxPeers = 4
253
        defaultNeutrinoMaxPeers   = 8
254

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

375
        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."`
376

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

453
        net tor.Net
454

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

534
        // NumRestrictedSlots is the number of restricted slots we'll allocate
535
        // in the server.
536
        NumRestrictedSlots uint64 `long:"num-restricted-slots" description:"The number of restricted slots we'll allocate in the server."`
537

538
        // NoDisconnectOnPongFailure controls if we'll disconnect if a peer
539
        // doesn't respond to a pong in time.
540
        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."`
541
}
542

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

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

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

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

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

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

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

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

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

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

3✔
807
                configFilePath = filepath.Join(
3✔
808
                        configFileDir, lncfg.DefaultConfigFilename,
3✔
809
                )
3✔
810

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

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

×
832
                        return nil, err
×
833
                }
×
834

835
                configFileError = err
3✔
836
        }
837

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

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

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

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

×
869
                return nil, err
×
870
        }
×
871

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

879
        // Finally, log warnings for deprecated config options if they are set.
880
        logWarningsForDeprecation(*cleanCfg)
3✔
881

3✔
882
        return cleanCfg, nil
3✔
883
}
884

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

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

3✔
895
                // Initialize logging at the default logging level.
3✔
896
                SetupLoggers(subLogMgr, interceptor)
3✔
897

3✔
898
                fmt.Println("Supported subsystems",
3✔
899
                        subLogMgr.SupportedSubsystems())
3✔
900
                os.Exit(0)
3✔
901
        }
3✔
902

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

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

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

943
                        str := "Failed to create lnd directory '%s': %v"
×
944
                        return mkErr(str, dir, err)
×
945
                }
946

947
                return nil
3✔
948
        }
949

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

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

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

3✔
982
                return (fileOption != nil && fileOption.IsSet()) ||
3✔
983
                                (fileOptionNested != nil && fileOptionNested.IsSet()) ||
3✔
984
                                (flagOption != nil && flagOption.IsSet()) ||
3✔
985
                                (flagOptionNested != nil && flagOptionNested.IsSet()),
3✔
986
                        nil
3✔
987
        }
988

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

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

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

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

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

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

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

×
1044
                return nil, mkErr(str)
×
1045
        }
×
1046

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

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

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

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

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

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

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

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

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

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

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

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

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

×
1150
                return nil, mkErr(str)
×
1151
        }
×
1152

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

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

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

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

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

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

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

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

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

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

×
1290
                return nil, mkErr(str)
×
1291
        }
×
1292

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

×
1301
                return nil, mkErr(str)
×
1302
        }
×
1303

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

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

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

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

1338
        default:
×
1339
                str := "only btcd, bitcoind, and neutrino mode " +
×
1340
                        "supported for bitcoin at this time"
×
1341

×
1342
                return nil, mkErr(str)
×
1343
        }
1344

1345
        cfg.Bitcoin.ChainDir = filepath.Join(
3✔
1346
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
3✔
1347
        )
3✔
1348

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

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

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

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

×
1369
                return nil, mkErr(str)
×
1370
        }
×
1371

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

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

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

1407
        towerDir := filepath.Join(
3✔
1408
                cfg.Watchtower.TowerDir, BitcoinChainName,
3✔
1409
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
3✔
1410
        )
3✔
1411

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

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

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

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

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

1459
        // Initialize logging at the default logging level.
1460
        SetupLoggers(cfg.SubLogMgr, interceptor)
3✔
1461

3✔
1462
        if cfg.MaxLogFiles != 0 {
3✔
1463
                if cfg.LogConfig.File.MaxLogFiles !=
×
1464
                        build.DefaultMaxLogFiles {
×
1465

×
1466
                        return nil, mkErr("cannot set both maxlogfiles and "+
×
1467
                                "logging.file.max-files", err)
×
1468
                }
×
1469

1470
                cfg.LogConfig.File.MaxLogFiles = cfg.MaxLogFiles
×
1471
        }
1472
        if cfg.MaxLogFileSize != 0 {
3✔
1473
                if cfg.LogConfig.File.MaxLogFileSize !=
×
1474
                        build.DefaultMaxLogFileSize {
×
1475

×
1476
                        return nil, mkErr("cannot set both maxlogfilesize and "+
×
1477
                                "logging.file.max-file-size", err)
×
1478
                }
×
1479

1480
                cfg.LogConfig.File.MaxLogFileSize = cfg.MaxLogFileSize
×
1481
        }
1482

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

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

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

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

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

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

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

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

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

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

×
1560
                return nil, mkErr("wallet unlock password file %s does "+
×
1561
                        "not exist", cfg.WalletUnlockPasswordFile)
×
1562
        }
1563

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

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

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

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

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

1618
                // Parse the external DNS address if provided. This will add
1619
                // the default port if needed and validate that it's a proper
1620
                // DNS hostname address.
1621
                if cfg.RawExternalDNSAddress != "" {
6✔
1622
                        addr, err := parseDNSAddr(
3✔
1623
                                cfg.RawExternalDNSAddress, cfg.net,
3✔
1624
                        )
3✔
1625
                        if err != nil {
3✔
NEW
1626
                                return nil, err
×
NEW
1627
                        }
×
1628
                        cfg.ExternalDNSAddress = addr
3✔
1629
                }
1630

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

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

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

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

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

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

1680
                default:
×
1681
                }
1682
        }
1683

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1891
                path = strings.Replace(path, "~", homeDir, 1)
×
1892
        }
1893

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2025
        fmt.Println("Attempting automatic RPC configuration to " + daemonName)
×
2026

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

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

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

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

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

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

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

2102
                return defaultValue
×
2103

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

×
2109
                return envValue
×
2110

2111
        case reEnvVar.MatchString(value):
×
2112
                matches := reEnvVar.FindStringSubmatch(value)
×
2113
                envVariable := matches[1]
×
2114
                envValue := os.Getenv(envVariable)
×
2115

×
2116
                return envValue
×
2117
        }
2118

2119
        return value
2✔
2120
}
2121

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2304
        return nil
1✔
2305
}
2306

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

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

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

3✔
2326
        result := make(map[string]string)
3✔
2327

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

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

2347
                return value
3✔
2348
        }
2349

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

2361
                // Abort on nil values.
2362
                if !obj.IsValid() {
3✔
2363
                        return
×
2364
                }
×
2365

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

3✔
2371
                        longName := fieldType.Tag.Get("long")
3✔
2372
                        namespace := fieldType.Tag.Get("namespace")
3✔
2373
                        group := fieldType.Tag.Get("group")
3✔
2374
                        hidden := fieldType.Tag.Get("hidden")
3✔
2375

3✔
2376
                        switch {
3✔
2377
                        // We have a long name defined, this is a config value.
2378
                        case longName != "":
3✔
2379
                                key := longName
3✔
2380
                                if prefix != "" {
6✔
2381
                                        key = prefix + "." + key
3✔
2382
                                }
3✔
2383

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

3✔
2389
                                // If there's a hidden flag, it's deprecated.
3✔
2390
                                if hidden == "true" && !field.IsZero() {
3✔
2391
                                        deprecated[key] = struct{}{}
×
2392
                                }
×
2393

2394
                        // We have no long name but a namespace, this is a
2395
                        // nested struct.
2396
                        case longName == "" && namespace != "":
3✔
2397
                                key := namespace
3✔
2398
                                if prefix != "" {
6✔
2399
                                        key = prefix + "." + key
3✔
2400
                                }
3✔
2401

2402
                                printConfig(field, key)
3✔
2403

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

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

2415
                        default:
3✔
2416
                                continue
3✔
2417
                        }
2418
                }
2419
        }
2420

2421
        // Turn the whole config struct into a flat map.
2422
        printConfig(reflect.ValueOf(cfg), "")
3✔
2423

3✔
2424
        return result, deprecated, nil
3✔
2425
}
2426

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

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