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

lightningnetwork / lnd / 13558005087

27 Feb 2025 03:04AM UTC coverage: 58.834% (-0.001%) from 58.835%
13558005087

Pull #8453

github

Roasbeef
lnwallet/chancloser: increase test coverage of state machine
Pull Request #8453: [4/4] - multi: integrate new rbf coop close FSM into the existing peer flow

1079 of 1370 new or added lines in 23 files covered. (78.76%)

578 existing lines in 40 files now uncovered.

137063 of 232965 relevant lines covered (58.83%)

19205.84 hits per line

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

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

5
package lnd
6

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

285
        defaultPrunedNodeMaxPeers = 4
286
)
287

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

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

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

311
        NoMacaroons     bool          `long:"no-macaroons" description:"Disable macaroon authentication, can only be used if server is not listening on a public interface."`
312
        AdminMacPath    string        `long:"adminmacaroonpath" description:"Path to write the admin macaroon for lnd's RPC and REST services if it doesn't exist"`
313
        ReadMacPath     string        `long:"readonlymacaroonpath" description:"Path to write the read-only macaroon for lnd's RPC and REST services if it doesn't exist"`
314
        InvoiceMacPath  string        `long:"invoicemacaroonpath" description:"Path to the invoice-only macaroon for lnd's RPC and REST services if it doesn't exist"`
315
        LogDir          string        `long:"logdir" description:"Directory to log output."`
316
        MaxLogFiles     int           `long:"maxlogfiles" description:"Maximum logfiles to keep (0 for no rotation). DEPRECATED: use --logging.file.max-files instead" hidden:"true"`
317
        MaxLogFileSize  int           `long:"maxlogfilesize" description:"Maximum logfile size in MB. DEPRECATED: use --logging.file.max-file-size instead" hidden:"true"`
318
        AcceptorTimeout time.Duration `long:"acceptortimeout" description:"Time after which an RPCAcceptor will time out and return false if it hasn't yet received a response"`
319

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

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

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

351
        CPUProfile      string `long:"cpuprofile" description:"DEPRECATED: Use 'pprof.cpuprofile' option. Write CPU profile to the specified file" hidden:"true"`
352
        Profile         string `long:"profile" description:"DEPRECATED: Use 'pprof.profile' option. Enable HTTP profiling on either a port or host:port" hidden:"true"`
353
        BlockingProfile int    `long:"blockingprofile" description:"DEPRECATED: Use 'pprof.blockingprofile' option. Used to enable a blocking profile to be served on the profiling port. This takes a value from 0 to 1, with 1 including every blocking event, and 0 including no events." hidden:"true"`
354
        MutexProfile    int    `long:"mutexprofile" description:"DEPRECATED: Use 'pprof.mutexprofile' option. Used to Enable a mutex profile to be served on the profiling port. This takes a value from 0 to 1, with 1 including every mutex event, and 0 including no events." hidden:"true"`
355

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

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

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

365
        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"`
366

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

372
        BlockCacheSize uint64 `long:"blockcachesize" description:"The maximum capacity of the block cache"`
373

374
        Autopilot *lncfg.AutoPilot `group:"Autopilot" namespace:"autopilot"`
375

376
        Tor *lncfg.Tor `group:"Tor" namespace:"tor"`
377

378
        SubRPCServers *subRPCServerConfigs `group:"subrpc"`
379

380
        Hodl *hodl.Config `group:"hodl" namespace:"hodl"`
381

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

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

388
        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."`
389

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

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

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

406
        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."`
407

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

441
        net tor.Net
442

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

497
        // SubLogMgr is the root logger that all the daemon's subloggers are
498
        // hooked up to.
499
        SubLogMgr  *build.SubLoggerManager
500
        LogRotator *build.RotatingLogWriter
501
        LogConfig  *build.LogConfig `group:"logging" namespace:"logging"`
502

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

508
        // ActiveNetParams contains parameters of the target chain.
509
        ActiveNetParams chainreg.BitcoinNetParams
510

511
        // Estimator is used to estimate routing probabilities.
512
        Estimator routing.Estimator
513

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

518
        // HTTPHeaderTimeout is the maximum duration that the server will wait
519
        // before timing out reading the headers of an HTTP request.
520
        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."`
521
}
522

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

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

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

545
        // ClientAllowPingWithoutStream specifies whether pings from the client
546
        // are allowed even if there are no active gRPC streams. This might be
547
        // useful to keep the underlying HTTP/2 connection open for future
548
        // requests.
549
        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."`
550
}
551

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

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

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

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

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

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

1✔
782
                configFilePath = filepath.Join(
1✔
783
                        configFileDir, lncfg.DefaultConfigFilename,
1✔
784
                )
1✔
785

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

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

×
807
                        return nil, err
×
808
                }
×
809

810
                configFileError = err
1✔
811
        }
812

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

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

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

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

×
844
                return nil, err
×
845
        }
×
846

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

854
        // Finally, log warnings for deprecated config options if they are set.
855
        logWarningsForDeprecation(*cleanCfg)
1✔
856

1✔
857
        return cleanCfg, nil
1✔
858
}
859

860
// ValidateConfig check the given configuration to be sane. This makes sure no
861
// illegal values or combination of values are set. All file system paths are
862
// normalized. The cleaned up config is returned on success.
863
func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser,
864
        flagParser *flags.Parser) (*Config, error) {
1✔
865

1✔
866
        // Special show command to list supported subsystems and exit.
1✔
867
        if cfg.DebugLevel == "show" {
2✔
868
                subLogMgr := build.NewSubLoggerManager()
1✔
869

1✔
870
                // Initialize logging at the default logging level.
1✔
871
                SetupLoggers(subLogMgr, interceptor)
1✔
872

1✔
873
                fmt.Println("Supported subsystems",
1✔
874
                        subLogMgr.SupportedSubsystems())
1✔
875
                os.Exit(0)
1✔
876
        }
1✔
877

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

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

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

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

922
                return nil
1✔
923
        }
924

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

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

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

1✔
957
                return (fileOption != nil && fileOption.IsSet()) ||
1✔
958
                                (fileOptionNested != nil && fileOptionNested.IsSet()) ||
1✔
959
                                (flagOption != nil && flagOption.IsSet()) ||
1✔
960
                                (flagOptionNested != nil && flagOptionNested.IsSet()),
1✔
961
                        nil
1✔
962
        }
963

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1315
        cfg.Bitcoin.ChainDir = filepath.Join(
1✔
1316
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
1✔
1317
        )
1✔
1318

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

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

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

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

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

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

1351
        // We'll now construct the network directory which will be where we
1352
        // store all the data specific to this chain/network.
1353
        cfg.networkDir = filepath.Join(
1✔
1354
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
1✔
1355
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
1✔
1356
        )
1✔
1357

1✔
1358
        // If a custom macaroon directory wasn't specified and the data
1✔
1359
        // directory has changed from the default path, then we'll also update
1✔
1360
        // the path for the macaroons to be generated.
1✔
1361
        if cfg.AdminMacPath == "" {
1✔
1362
                cfg.AdminMacPath = filepath.Join(
×
1363
                        cfg.networkDir, defaultAdminMacFilename,
×
1364
                )
×
1365
        }
×
1366
        if cfg.ReadMacPath == "" {
1✔
1367
                cfg.ReadMacPath = filepath.Join(
×
1368
                        cfg.networkDir, defaultReadMacFilename,
×
1369
                )
×
1370
        }
×
1371
        if cfg.InvoiceMacPath == "" {
1✔
1372
                cfg.InvoiceMacPath = filepath.Join(
×
1373
                        cfg.networkDir, defaultInvoiceMacFilename,
×
1374
                )
×
1375
        }
×
1376

1377
        towerDir := filepath.Join(
1✔
1378
                cfg.Watchtower.TowerDir, BitcoinChainName,
1✔
1379
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
1✔
1380
        )
1✔
1381

1✔
1382
        // Create the lnd directory and all other sub-directories if they don't
1✔
1383
        // already exist. This makes sure that directory trees are also created
1✔
1384
        // for files that point to outside the lnddir.
1✔
1385
        dirs := []string{
1✔
1386
                lndDir, cfg.DataDir, cfg.networkDir,
1✔
1387
                cfg.LetsEncryptDir, towerDir, cfg.graphDatabaseDir(),
1✔
1388
                filepath.Dir(cfg.TLSCertPath), filepath.Dir(cfg.TLSKeyPath),
1✔
1389
                filepath.Dir(cfg.AdminMacPath), filepath.Dir(cfg.ReadMacPath),
1✔
1390
                filepath.Dir(cfg.InvoiceMacPath),
1✔
1391
                filepath.Dir(cfg.Tor.PrivateKeyPath),
1✔
1392
                filepath.Dir(cfg.Tor.WatchtowerKeyPath),
1✔
1393
        }
1✔
1394
        for _, dir := range dirs {
2✔
1395
                if err := makeDirectory(dir); err != nil {
1✔
1396
                        return nil, err
×
1397
                }
×
1398
        }
1399

1400
        // Similarly, if a custom back up file path wasn't specified, then
1401
        // we'll update the file location to match our set network directory.
1402
        if cfg.BackupFilePath == "" {
2✔
1403
                cfg.BackupFilePath = filepath.Join(
1✔
1404
                        cfg.networkDir, chanbackup.DefaultBackupFileName,
1✔
1405
                )
1✔
1406
        }
1✔
1407

1408
        // Append the network type to the log directory so it is "namespaced"
1409
        // per network in the same fashion as the data directory.
1410
        cfg.LogDir = filepath.Join(
1✔
1411
                cfg.LogDir, BitcoinChainName,
1✔
1412
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
1✔
1413
        )
1✔
1414

1✔
1415
        if err := cfg.LogConfig.Validate(); err != nil {
1✔
1416
                return nil, mkErr("error validating logging config: %w", err)
×
1417
        }
×
1418

1419
        cfg.SubLogMgr = build.NewSubLoggerManager(build.NewDefaultLogHandlers(
1✔
1420
                cfg.LogConfig, cfg.LogRotator,
1✔
1421
        )...)
1✔
1422

1✔
1423
        // Initialize logging at the default logging level.
1✔
1424
        SetupLoggers(cfg.SubLogMgr, interceptor)
1✔
1425

1✔
1426
        if cfg.MaxLogFiles != 0 {
1✔
1427
                if cfg.LogConfig.File.MaxLogFiles !=
×
1428
                        build.DefaultMaxLogFiles {
×
1429

×
1430
                        return nil, mkErr("cannot set both maxlogfiles and "+
×
1431
                                "logging.file.max-files", err)
×
1432
                }
×
1433

1434
                cfg.LogConfig.File.MaxLogFiles = cfg.MaxLogFiles
×
1435
        }
1436
        if cfg.MaxLogFileSize != 0 {
1✔
1437
                if cfg.LogConfig.File.MaxLogFileSize !=
×
1438
                        build.DefaultMaxLogFileSize {
×
1439

×
1440
                        return nil, mkErr("cannot set both maxlogfilesize and "+
×
1441
                                "logging.file.max-file-size", err)
×
1442
                }
×
1443

1444
                cfg.LogConfig.File.MaxLogFileSize = cfg.MaxLogFileSize
×
1445
        }
1446

1447
        err = cfg.LogRotator.InitLogRotator(
1✔
1448
                cfg.LogConfig.File,
1✔
1449
                filepath.Join(cfg.LogDir, defaultLogFilename),
1✔
1450
        )
1✔
1451
        if err != nil {
1✔
1452
                str := "log rotation setup failed: %v"
×
1453
                return nil, mkErr(str, err)
×
1454
        }
×
1455

1456
        // Parse, validate, and set debug log level(s).
1457
        err = build.ParseAndSetDebugLevels(cfg.DebugLevel, cfg.SubLogMgr)
1✔
1458
        if err != nil {
1✔
1459
                str := "error parsing debug level: %v"
×
1460
                return nil, &lncfg.UsageError{Err: mkErr(str, err)}
×
1461
        }
×
1462

1463
        // At least one RPCListener is required. So listen on localhost per
1464
        // default.
1465
        if len(cfg.RawRPCListeners) == 0 {
1✔
1466
                addr := fmt.Sprintf("localhost:%d", defaultRPCPort)
×
1467
                cfg.RawRPCListeners = append(cfg.RawRPCListeners, addr)
×
1468
        }
×
1469

1470
        // Listen on localhost if no REST listeners were specified.
1471
        if len(cfg.RawRESTListeners) == 0 {
1✔
1472
                addr := fmt.Sprintf("localhost:%d", defaultRESTPort)
×
1473
                cfg.RawRESTListeners = append(cfg.RawRESTListeners, addr)
×
1474
        }
×
1475

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

1489
        // Add default port to all RPC listener addresses if needed and remove
1490
        // duplicate addresses.
1491
        cfg.RPCListeners, err = lncfg.NormalizeAddresses(
1✔
1492
                cfg.RawRPCListeners, strconv.Itoa(defaultRPCPort),
1✔
1493
                cfg.net.ResolveTCPAddr,
1✔
1494
        )
1✔
1495
        if err != nil {
1✔
1496
                return nil, mkErr("error normalizing RPC listen addrs: %v", err)
×
1497
        }
×
1498

1499
        // Add default port to all REST listener addresses if needed and remove
1500
        // duplicate addresses.
1501
        cfg.RESTListeners, err = lncfg.NormalizeAddresses(
1✔
1502
                cfg.RawRESTListeners, strconv.Itoa(defaultRESTPort),
1✔
1503
                cfg.net.ResolveTCPAddr,
1✔
1504
        )
1✔
1505
        if err != nil {
1✔
1506
                return nil, mkErr("error normalizing REST listen addrs: %v", err)
×
1507
        }
×
1508

1509
        switch {
1✔
1510
        // The no seed backup and auto unlock are mutually exclusive.
1511
        case cfg.NoSeedBackup && cfg.WalletUnlockPasswordFile != "":
×
1512
                return nil, mkErr("cannot set noseedbackup and " +
×
1513
                        "wallet-unlock-password-file at the same time")
×
1514

1515
        // The "allow-create" flag cannot be set without the auto unlock file.
1516
        case cfg.WalletUnlockAllowCreate && cfg.WalletUnlockPasswordFile == "":
×
1517
                return nil, mkErr("cannot set wallet-unlock-allow-create " +
×
1518
                        "without wallet-unlock-password-file")
×
1519

1520
        // If a password file was specified, we need it to exist.
1521
        case cfg.WalletUnlockPasswordFile != "" &&
1522
                !lnrpc.FileExists(cfg.WalletUnlockPasswordFile):
×
1523

×
1524
                return nil, mkErr("wallet unlock password file %s does "+
×
1525
                        "not exist", cfg.WalletUnlockPasswordFile)
×
1526
        }
1527

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

1540
        if cfg.DisableRest {
1✔
1541
                ltndLog.Infof("REST API is disabled!")
×
1542
                cfg.RESTListeners = nil
×
1543
        } else {
1✔
1544
                err = lncfg.EnforceSafeAuthentication(
1✔
1545
                        cfg.RESTListeners, !cfg.NoMacaroons, !cfg.DisableRestTLS,
1✔
1546
                )
1✔
1547
                if err != nil {
1✔
1548
                        return nil, mkErr("error enforcing safe "+
×
1549
                                "authentication on REST ports: %v", err)
×
1550
                }
×
1551
        }
1552

1553
        // Remove the listening addresses specified if listening is disabled.
1554
        if cfg.DisableListen {
2✔
1555
                ltndLog.Infof("Listening on the p2p interface is disabled!")
1✔
1556
                cfg.Listeners = nil
1✔
1557
                cfg.ExternalIPs = nil
1✔
1558
        } else {
2✔
1559

1✔
1560
                // Add default port to all listener addresses if needed and remove
1✔
1561
                // duplicate addresses.
1✔
1562
                cfg.Listeners, err = lncfg.NormalizeAddresses(
1✔
1563
                        cfg.RawListeners, strconv.Itoa(defaultPeerPort),
1✔
1564
                        cfg.net.ResolveTCPAddr,
1✔
1565
                )
1✔
1566
                if err != nil {
1✔
1567
                        return nil, mkErr("error normalizing p2p listen "+
×
1568
                                "addrs: %v", err)
×
1569
                }
×
1570

1571
                // Add default port to all external IP addresses if needed and remove
1572
                // duplicate addresses.
1573
                cfg.ExternalIPs, err = lncfg.NormalizeAddresses(
1✔
1574
                        cfg.RawExternalIPs, strconv.Itoa(defaultPeerPort),
1✔
1575
                        cfg.net.ResolveTCPAddr,
1✔
1576
                )
1✔
1577
                if err != nil {
1✔
1578
                        return nil, err
×
1579
                }
×
1580

1581
                // For the p2p port it makes no sense to listen to an Unix socket.
1582
                // Also, we would need to refactor the brontide listener to support
1583
                // that.
1584
                for _, p2pListener := range cfg.Listeners {
2✔
1585
                        if lncfg.IsUnix(p2pListener) {
1✔
1586
                                return nil, mkErr("unix socket addresses "+
×
1587
                                        "cannot be used for the p2p "+
×
1588
                                        "connection listener: %s", p2pListener)
×
1589
                        }
×
1590
                }
1591
        }
1592

1593
        // Ensure that the specified minimum backoff is below or equal to the
1594
        // maximum backoff.
1595
        if cfg.MinBackoff > cfg.MaxBackoff {
1✔
1596
                return nil, mkErr("maxbackoff must be greater than minbackoff")
×
1597
        }
×
1598

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

1611
        // Parse any extra sqlite pragma options that may have been provided
1612
        // to determine if they override any of the defaults that we will
1613
        // otherwise add.
1614
        var (
1✔
1615
                defaultSynchronous = true
1✔
1616
                defaultAutoVacuum  = true
1✔
1617
                defaultFullfsync   = true
1✔
1618
        )
1✔
1619
        for _, option := range cfg.DB.Sqlite.PragmaOptions {
1✔
1620
                switch {
×
1621
                case strings.HasPrefix(option, "synchronous="):
×
1622
                        defaultSynchronous = false
×
1623

1624
                case strings.HasPrefix(option, "auto_vacuum="):
×
1625
                        defaultAutoVacuum = false
×
1626

1627
                case strings.HasPrefix(option, "fullfsync="):
×
1628
                        defaultFullfsync = false
×
1629

1630
                default:
×
1631
                }
1632
        }
1633

1634
        if defaultSynchronous {
2✔
1635
                cfg.DB.Sqlite.PragmaOptions = append(
1✔
1636
                        cfg.DB.Sqlite.PragmaOptions, "synchronous=full",
1✔
1637
                )
1✔
1638
        }
1✔
1639

1640
        if defaultAutoVacuum {
2✔
1641
                cfg.DB.Sqlite.PragmaOptions = append(
1✔
1642
                        cfg.DB.Sqlite.PragmaOptions, "auto_vacuum=incremental",
1✔
1643
                )
1✔
1644
        }
1✔
1645

1646
        if defaultFullfsync {
2✔
1647
                cfg.DB.Sqlite.PragmaOptions = append(
1✔
1648
                        cfg.DB.Sqlite.PragmaOptions, "fullfsync=true",
1✔
1649
                )
1✔
1650
        }
1✔
1651

1652
        // Ensure that the user hasn't chosen a remote-max-htlc value greater
1653
        // than the protocol maximum.
1654
        maxRemoteHtlcs := uint16(input.MaxHTLCNumber / 2)
1✔
1655
        if cfg.DefaultRemoteMaxHtlcs > maxRemoteHtlcs {
1✔
1656
                return nil, mkErr("default-remote-max-htlcs (%v) must be "+
×
1657
                        "less than %v", cfg.DefaultRemoteMaxHtlcs,
×
1658
                        maxRemoteHtlcs)
×
1659
        }
×
1660

1661
        // Clamp the ChannelCommitInterval so that commitment updates can still
1662
        // happen in a reasonable timeframe.
1663
        if cfg.ChannelCommitInterval > maxChannelCommitInterval {
1✔
1664
                return nil, mkErr("channel-commit-interval (%v) must be less "+
×
1665
                        "than %v", cfg.ChannelCommitInterval,
×
1666
                        maxChannelCommitInterval)
×
1667
        }
×
1668

1669
        // Limit PendingCommitInterval so we don't wait too long for the remote
1670
        // party to send back a revoke.
1671
        if cfg.PendingCommitInterval > maxPendingCommitInterval {
1✔
1672
                return nil, mkErr("pending-commit-interval (%v) must be less "+
×
1673
                        "than %v", cfg.PendingCommitInterval,
×
1674
                        maxPendingCommitInterval)
×
1675
        }
×
1676

1677
        if err := cfg.Gossip.Parse(); err != nil {
1✔
1678
                return nil, mkErr("error parsing gossip syncer: %v", err)
×
1679
        }
×
1680

1681
        // If the experimental protocol options specify any protocol messages
1682
        // that we want to handle as custom messages, set them now.
1683
        customMsg := cfg.ProtocolOptions.CustomMessageOverrides()
1✔
1684

1✔
1685
        // We can safely set our custom override values during startup because
1✔
1686
        // startup is blocked on config parsing.
1✔
1687
        if err := lnwire.SetCustomOverrides(customMsg); err != nil {
1✔
1688
                return nil, mkErr("custom-message: %v", err)
×
1689
        }
×
1690

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

1724
        // Don't allow both the old dust-threshold and the new
1725
        // channel-max-fee-exposure to be set.
1726
        if cfg.DustThreshold != 0 && cfg.MaxFeeExposure != 0 {
1✔
1727
                return nil, mkErr("cannot set both dust-threshold and " +
×
1728
                        "channel-max-fee-exposure")
×
1729
        }
×
1730

1731
        switch {
1✔
1732
        // Use the old dust-threshold as the max fee exposure if it is set and
1733
        // the new option is not.
1734
        case cfg.DustThreshold != 0:
×
1735
                cfg.MaxFeeExposure = cfg.DustThreshold
×
1736

1737
        // Use the default max fee exposure if the new option is not set and
1738
        // the old one is not set either.
1739
        case cfg.MaxFeeExposure == 0:
1✔
1740
                cfg.MaxFeeExposure = uint64(
1✔
1741
                        htlcswitch.DefaultMaxFeeExposure.ToSatoshis(),
1✔
1742
                )
1✔
1743
        }
1744

1745
        // Validate the subconfigs for workers, caches, and the tower client.
1746
        err = lncfg.Validate(
1✔
1747
                cfg.Workers,
1✔
1748
                cfg.Caches,
1✔
1749
                cfg.WtClient,
1✔
1750
                cfg.DB,
1✔
1751
                cfg.Cluster,
1✔
1752
                cfg.HealthChecks,
1✔
1753
                cfg.RPCMiddleware,
1✔
1754
                cfg.RemoteSigner,
1✔
1755
                cfg.Sweeper,
1✔
1756
                cfg.Htlcswitch,
1✔
1757
                cfg.Invoices,
1✔
1758
                cfg.Routing,
1✔
1759
                cfg.Pprof,
1✔
1760
                cfg.Gossip,
1✔
1761
        )
1✔
1762
        if err != nil {
1✔
1763
                return nil, err
×
1764
        }
×
1765

1766
        // Finally, ensure that the user's color is correctly formatted,
1767
        // otherwise the server will not be able to start after the unlocking
1768
        // the wallet.
1769
        _, err = lncfg.ParseHexColor(cfg.Color)
1✔
1770
        if err != nil {
1✔
1771
                return nil, mkErr("unable to parse node color: %v", err)
×
1772
        }
×
1773

1774
        // All good, return the sanitized result.
1775
        return &cfg, nil
1✔
1776
}
1777

1778
// graphDatabaseDir returns the default directory where the local bolt graph db
1779
// files are stored.
1780
func (c *Config) graphDatabaseDir() string {
1✔
1781
        return filepath.Join(
1✔
1782
                c.DataDir, defaultGraphSubDirname,
1✔
1783
                lncfg.NormalizeNetwork(c.ActiveNetParams.Name),
1✔
1784
        )
1✔
1785
}
1✔
1786

1787
// ImplementationConfig returns the configuration of what actual implementations
1788
// should be used when creating the main lnd instance.
1789
func (c *Config) ImplementationConfig(
1790
        interceptor signal.Interceptor) *ImplementationCfg {
1✔
1791

1✔
1792
        // If we're using a remote signer, we still need the base wallet as a
1✔
1793
        // watch-only source of chain and address data. But we don't need any
1✔
1794
        // private key material in that btcwallet base wallet.
1✔
1795
        if c.RemoteSigner.Enable {
2✔
1796
                rpcImpl := NewRPCSignerWalletImpl(
1✔
1797
                        c, ltndLog, interceptor,
1✔
1798
                        c.RemoteSigner.MigrateWatchOnly,
1✔
1799
                )
1✔
1800
                return &ImplementationCfg{
1✔
1801
                        GrpcRegistrar:     rpcImpl,
1✔
1802
                        RestRegistrar:     rpcImpl,
1✔
1803
                        ExternalValidator: rpcImpl,
1✔
1804
                        DatabaseBuilder: NewDefaultDatabaseBuilder(
1✔
1805
                                c, ltndLog,
1✔
1806
                        ),
1✔
1807
                        WalletConfigBuilder: rpcImpl,
1✔
1808
                        ChainControlBuilder: rpcImpl,
1✔
1809
                }
1✔
1810
        }
1✔
1811

1812
        defaultImpl := NewDefaultWalletImpl(c, ltndLog, interceptor, false)
1✔
1813
        return &ImplementationCfg{
1✔
1814
                GrpcRegistrar:       defaultImpl,
1✔
1815
                RestRegistrar:       defaultImpl,
1✔
1816
                ExternalValidator:   defaultImpl,
1✔
1817
                DatabaseBuilder:     NewDefaultDatabaseBuilder(c, ltndLog),
1✔
1818
                WalletConfigBuilder: defaultImpl,
1✔
1819
                ChainControlBuilder: defaultImpl,
1✔
1820
        }
1✔
1821
}
1822

1823
// CleanAndExpandPath expands environment variables and leading ~ in the
1824
// passed path, cleans the result, and returns it.
1825
// This function is taken from https://github.com/btcsuite/btcd
1826
func CleanAndExpandPath(path string) string {
1✔
1827
        if path == "" {
2✔
1828
                return ""
1✔
1829
        }
1✔
1830

1831
        // Expand initial ~ to OS specific home directory.
1832
        if strings.HasPrefix(path, "~") {
1✔
1833
                var homeDir string
×
1834
                u, err := user.Current()
×
1835
                if err == nil {
×
1836
                        homeDir = u.HomeDir
×
1837
                } else {
×
1838
                        homeDir = os.Getenv("HOME")
×
1839
                }
×
1840

1841
                path = strings.Replace(path, "~", homeDir, 1)
×
1842
        }
1843

1844
        // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
1845
        // but the variables can still be expanded via POSIX-style $VARIABLE.
1846
        return filepath.Clean(os.ExpandEnv(path))
1✔
1847
}
1848

1849
func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
1850
        netParams chainreg.BitcoinNetParams) error {
1✔
1851

1✔
1852
        // First, we'll check our node config to make sure the RPC parameters
1✔
1853
        // were set correctly. We'll also determine the path to the conf file
1✔
1854
        // depending on the backend node.
1✔
1855
        var daemonName, confDir, confFile, confFileBase string
1✔
1856
        switch conf := nodeConfig.(type) {
1✔
UNCOV
1857
        case *lncfg.Btcd:
×
UNCOV
1858
                // Resolves environment variable references in RPCUser and
×
UNCOV
1859
                // RPCPass fields.
×
UNCOV
1860
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
×
UNCOV
1861
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
×
UNCOV
1862

×
UNCOV
1863
                // If both RPCUser and RPCPass are set, we assume those
×
UNCOV
1864
                // credentials are good to use.
×
UNCOV
1865
                if conf.RPCUser != "" && conf.RPCPass != "" {
×
UNCOV
1866
                        return nil
×
UNCOV
1867
                }
×
1868

1869
                // Set the daemon name for displaying proper errors.
1870
                daemonName = btcdBackendName
×
1871
                confDir = conf.Dir
×
1872
                confFileBase = btcdBackendName
×
1873

×
1874
                // If only ONE of RPCUser or RPCPass is set, we assume the
×
1875
                // user did that unintentionally.
×
1876
                if conf.RPCUser != "" || conf.RPCPass != "" {
×
1877
                        return fmt.Errorf("please set both or neither of "+
×
1878
                                "%[1]v.rpcuser, %[1]v.rpcpass", daemonName)
×
1879
                }
×
1880

1881
        case *lncfg.Bitcoind:
1✔
1882
                // Ensure that if the ZMQ options are set, that they are not
1✔
1883
                // equal.
1✔
1884
                if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
2✔
1885
                        err := checkZMQOptions(
1✔
1886
                                conf.ZMQPubRawBlock, conf.ZMQPubRawTx,
1✔
1887
                        )
1✔
1888
                        if err != nil {
1✔
1889
                                return err
×
1890
                        }
×
1891
                }
1892

1893
                // Ensure that if the estimate mode is set, that it is a legal
1894
                // value.
1895
                if conf.EstimateMode != "" {
2✔
1896
                        err := checkEstimateMode(conf.EstimateMode)
1✔
1897
                        if err != nil {
1✔
1898
                                return err
×
1899
                        }
×
1900
                }
1901

1902
                // Set the daemon name for displaying proper errors.
1903
                daemonName = bitcoindBackendName
1✔
1904
                confDir = conf.Dir
1✔
1905
                confFile = conf.ConfigPath
1✔
1906
                confFileBase = BitcoinChainName
1✔
1907

1✔
1908
                // Resolves environment variable references in RPCUser
1✔
1909
                // and RPCPass fields.
1✔
1910
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
1✔
1911
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
1✔
1912

1✔
1913
                // Check that cookie and credentials don't contradict each
1✔
1914
                // other.
1✔
1915
                if (conf.RPCUser != "" || conf.RPCPass != "") &&
1✔
1916
                        conf.RPCCookie != "" {
1✔
1917

×
1918
                        return fmt.Errorf("please only provide either "+
×
1919
                                "%[1]v.rpccookie or %[1]v.rpcuser and "+
×
1920
                                "%[1]v.rpcpass", daemonName)
×
1921
                }
×
1922

1923
                // We convert the cookie into a user name and password.
1924
                if conf.RPCCookie != "" {
1✔
1925
                        cookie, err := os.ReadFile(conf.RPCCookie)
×
1926
                        if err != nil {
×
1927
                                return fmt.Errorf("cannot read cookie file: %w",
×
1928
                                        err)
×
1929
                        }
×
1930

1931
                        splitCookie := strings.Split(string(cookie), ":")
×
1932
                        if len(splitCookie) != 2 {
×
1933
                                return fmt.Errorf("cookie file has a wrong " +
×
1934
                                        "format")
×
1935
                        }
×
1936
                        conf.RPCUser = splitCookie[0]
×
1937
                        conf.RPCPass = splitCookie[1]
×
1938
                }
1939

1940
                if conf.RPCUser != "" && conf.RPCPass != "" {
2✔
1941
                        // If all of RPCUser, RPCPass, ZMQBlockHost, and
1✔
1942
                        // ZMQTxHost are set, we assume those parameters are
1✔
1943
                        // good to use.
1✔
1944
                        if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
2✔
1945
                                return nil
1✔
1946
                        }
1✔
1947

1948
                        // If RPCUser and RPCPass are set and RPCPolling is
1949
                        // enabled, we assume the parameters are good to use.
1950
                        if conf.RPCPolling {
×
1951
                                return nil
×
1952
                        }
×
1953
                }
1954

1955
                // If not all of the parameters are set, we'll assume the user
1956
                // did this unintentionally.
1957
                if conf.RPCUser != "" || conf.RPCPass != "" ||
×
1958
                        conf.ZMQPubRawBlock != "" || conf.ZMQPubRawTx != "" {
×
1959

×
1960
                        return fmt.Errorf("please set %[1]v.rpcuser and "+
×
1961
                                "%[1]v.rpcpass (or %[1]v.rpccookie) together "+
×
1962
                                "with %[1]v.zmqpubrawblock, %[1]v.zmqpubrawtx",
×
1963
                                daemonName)
×
1964
                }
×
1965
        }
1966

1967
        // If we're in simnet mode, then the running btcd instance won't read
1968
        // the RPC credentials from the configuration. So if lnd wasn't
1969
        // specified the parameters, then we won't be able to start.
1970
        if cConfig.SimNet {
×
1971
                return fmt.Errorf("rpcuser and rpcpass must be set to your " +
×
1972
                        "btcd node's RPC parameters for simnet mode")
×
1973
        }
×
1974

1975
        fmt.Println("Attempting automatic RPC configuration to " + daemonName)
×
1976

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

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

2004
        fmt.Printf("Automatically obtained %v's RPC credentials\n", daemonName)
×
2005
        return nil
×
2006
}
2007

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

8✔
2032
        // Regex for ${ENV_VAR} format.
8✔
2033
        var reEnvVarWithBrackets = regexp.MustCompile(
8✔
2034
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}$`,
8✔
2035
        )
8✔
2036

8✔
2037
        // Regex for ${ENV_VAR:-DEFAULT} format.
8✔
2038
        var reEnvVarWithDefault = regexp.MustCompile(
8✔
2039
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*):-([\S]+)\}$`,
8✔
2040
        )
8✔
2041

8✔
2042
        // Match against supported formats.
8✔
2043
        switch {
8✔
2044
        case reEnvVarWithDefault.MatchString(value):
3✔
2045
                matches := reEnvVarWithDefault.FindStringSubmatch(value)
3✔
2046
                envVariable := matches[1]
3✔
2047
                defaultValue := matches[2]
3✔
2048
                if envValue := os.Getenv(envVariable); envValue != "" {
4✔
2049
                        return envValue
1✔
2050
                }
1✔
2051

2052
                return defaultValue
2✔
2053

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

×
2059
                return envValue
×
2060

2061
        case reEnvVar.MatchString(value):
3✔
2062
                matches := reEnvVar.FindStringSubmatch(value)
3✔
2063
                envVariable := matches[1]
3✔
2064
                envValue := os.Getenv(envVariable)
3✔
2065

3✔
2066
                return envValue
3✔
2067
        }
2068

2069
        return value
2✔
2070
}
2071

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

2084
        // With the file open extract the contents of the configuration file so
2085
        // we can attempt to locate the RPC credentials.
2086
        configContents, err := io.ReadAll(btcdConfigFile)
×
2087
        if err != nil {
×
2088
                return "", "", err
×
2089
        }
×
2090

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

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

2115
        return supplyEnvValue(string(userSubmatches[1])),
×
2116
                supplyEnvValue(string(passSubmatches[1])), nil
×
2117
}
2118

2119
// extractBitcoindRPCParams attempts to extract the RPC credentials for an
2120
// existing bitcoind node instance. The routine looks for a cookie first,
2121
// optionally following the datadir configuration option in the bitcoin.conf. If
2122
// it doesn't find one, it looks for rpcuser/rpcpassword.
2123
func extractBitcoindRPCParams(networkName, bitcoindDataDir, bitcoindConfigPath,
2124
        rpcCookiePath string) (string, string, string, string, error) {
×
2125

×
2126
        // First, we'll open up the bitcoind configuration file found at the
×
2127
        // target destination.
×
2128
        bitcoindConfigFile, err := os.Open(bitcoindConfigPath)
×
2129
        if err != nil {
×
2130
                return "", "", "", "", err
×
2131
        }
×
2132
        defer func() { _ = bitcoindConfigFile.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(bitcoindConfigFile)
×
2137
        if err != nil {
×
2138
                return "", "", "", "", err
×
2139
        }
×
2140

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

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

2184
        var chainDir string
×
2185
        switch networkName {
×
2186
        case "mainnet":
×
2187
                chainDir = ""
×
2188
        case "regtest", "testnet3", "signet":
×
2189
                chainDir = networkName
×
2190
        default:
×
2191
                return "", "", "", "", fmt.Errorf("unexpected networkname %v", networkName)
×
2192
        }
2193

2194
        cookiePath := filepath.Join(dataDir, chainDir, ".cookie")
×
2195
        if rpcCookiePath != "" {
×
2196
                cookiePath = rpcCookiePath
×
2197
        }
×
2198
        cookie, err := os.ReadFile(cookiePath)
×
2199
        if err == nil {
×
2200
                splitCookie := strings.Split(string(cookie), ":")
×
2201
                if len(splitCookie) == 2 {
×
2202
                        return splitCookie[0], splitCookie[1], zmqBlockHost,
×
2203
                                zmqTxHost, nil
×
2204
                }
×
2205
        }
2206

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

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

×
2225
        // Exit with an error if the cookie file, is defined in config, and
×
2226
        // can not be found, with both rpcuser and rpcpassword undefined.
×
2227
        if rpcCookiePath != "" && userSubmatches == nil && passSubmatches == nil {
×
2228
                return "", "", "", "", fmt.Errorf("unable to open cookie file (%v)",
×
2229
                        rpcCookiePath)
×
2230
        }
×
2231

2232
        if userSubmatches == nil {
×
2233
                return "", "", "", "", fmt.Errorf("unable to find rpcuser in " +
×
2234
                        "config")
×
2235
        }
×
2236
        if passSubmatches == nil {
×
2237
                return "", "", "", "", fmt.Errorf("unable to find rpcpassword " +
×
2238
                        "in config")
×
2239
        }
×
2240

2241
        return supplyEnvValue(string(userSubmatches[1])),
×
2242
                supplyEnvValue(string(passSubmatches[1])),
×
2243
                zmqBlockHost, zmqTxHost, nil
×
2244
}
2245

2246
// checkZMQOptions ensures that the provided addresses to use as the hosts for
2247
// ZMQ rawblock and rawtx notifications are different.
2248
func checkZMQOptions(zmqBlockHost, zmqTxHost string) error {
1✔
2249
        if zmqBlockHost == zmqTxHost {
1✔
2250
                return errors.New("zmqpubrawblock and zmqpubrawtx must be set " +
×
2251
                        "to different addresses")
×
2252
        }
×
2253

2254
        return nil
1✔
2255
}
2256

2257
// checkEstimateMode ensures that the provided estimate mode is legal.
2258
func checkEstimateMode(estimateMode string) error {
1✔
2259
        for _, mode := range bitcoindEstimateModes {
2✔
2260
                if estimateMode == mode {
2✔
2261
                        return nil
1✔
2262
                }
1✔
2263
        }
2264

2265
        return fmt.Errorf("estimatemode must be one of the following: %v",
×
2266
                bitcoindEstimateModes[:])
×
2267
}
2268

2269
// configToFlatMap converts the given config struct into a flat map of
2270
// key/value pairs using the dot notation we are used to from the config file
2271
// or command line flags. It also returns a map containing deprecated config
2272
// options.
2273
func configToFlatMap(cfg Config) (map[string]string,
2274
        map[string]struct{}, error) {
2✔
2275

2✔
2276
        result := make(map[string]string)
2✔
2277

2✔
2278
        // deprecated stores a map of deprecated options found in the config
2✔
2279
        // that are set by the users. A config option is considered as
2✔
2280
        // deprecated if it has a `hidden` flag.
2✔
2281
        deprecated := make(map[string]struct{})
2✔
2282

2✔
2283
        // redact is the helper function that redacts sensitive values like
2✔
2284
        // passwords.
2✔
2285
        redact := func(key, value string) string {
314✔
2286
                sensitiveKeySuffixes := []string{
312✔
2287
                        "pass",
312✔
2288
                        "password",
312✔
2289
                        "dsn",
312✔
2290
                }
312✔
2291
                for _, suffix := range sensitiveKeySuffixes {
1,239✔
2292
                        if strings.HasSuffix(key, suffix) {
933✔
2293
                                return "[redacted]"
6✔
2294
                        }
6✔
2295
                }
2296

2297
                return value
307✔
2298
        }
2299

2300
        // printConfig is the helper function that goes into nested structs
2301
        // recursively. Because we call it recursively, we need to declare it
2302
        // before we define it.
2303
        var printConfig func(reflect.Value, string)
2✔
2304
        printConfig = func(obj reflect.Value, prefix string) {
64✔
2305
                // Turn struct pointers into the actual struct, so we can
62✔
2306
                // iterate over the fields as we would with a struct value.
62✔
2307
                if obj.Kind() == reflect.Ptr {
117✔
2308
                        obj = obj.Elem()
55✔
2309
                }
55✔
2310

2311
                // Abort on nil values.
2312
                if !obj.IsValid() {
74✔
2313
                        return
12✔
2314
                }
12✔
2315

2316
                // Loop over all fields of the struct and inspect the type.
2317
                for i := 0; i < obj.NumField(); i++ {
439✔
2318
                        field := obj.Field(i)
389✔
2319
                        fieldType := obj.Type().Field(i)
389✔
2320

389✔
2321
                        longName := fieldType.Tag.Get("long")
389✔
2322
                        namespace := fieldType.Tag.Get("namespace")
389✔
2323
                        group := fieldType.Tag.Get("group")
389✔
2324
                        hidden := fieldType.Tag.Get("hidden")
389✔
2325

389✔
2326
                        switch {
389✔
2327
                        // We have a long name defined, this is a config value.
2328
                        case longName != "":
312✔
2329
                                key := longName
312✔
2330
                                if prefix != "" {
527✔
2331
                                        key = prefix + "." + key
215✔
2332
                                }
215✔
2333

2334
                                // Add the value directly to the flattened map.
2335
                                result[key] = redact(key, fmt.Sprintf(
312✔
2336
                                        "%v", field.Interface(),
312✔
2337
                                ))
312✔
2338

312✔
2339
                                // If there's a hidden flag, it's deprecated.
312✔
2340
                                if hidden == "true" && !field.IsZero() {
313✔
2341
                                        deprecated[key] = struct{}{}
1✔
2342
                                }
1✔
2343

2344
                        // We have no long name but a namespace, this is a
2345
                        // nested struct.
2346
                        case longName == "" && namespace != "":
55✔
2347
                                key := namespace
55✔
2348
                                if prefix != "" {
71✔
2349
                                        key = prefix + "." + key
16✔
2350
                                }
16✔
2351

2352
                                printConfig(field, key)
55✔
2353

2354
                        // Just a group means this is a dummy struct to house
2355
                        // multiple config values, the group name doesn't go
2356
                        // into the final field name.
2357
                        case longName == "" && group != "":
2✔
2358
                                printConfig(field, prefix)
2✔
2359

2360
                        // Anonymous means embedded struct. We need to recurse
2361
                        // into it but without adding anything to the prefix.
2362
                        case fieldType.Anonymous:
6✔
2363
                                printConfig(field, prefix)
6✔
2364

2365
                        default:
18✔
2366
                                continue
18✔
2367
                        }
2368
                }
2369
        }
2370

2371
        // Turn the whole config struct into a flat map.
2372
        printConfig(reflect.ValueOf(cfg), "")
2✔
2373

2✔
2374
        return result, deprecated, nil
2✔
2375
}
2376

2377
// logWarningsForDeprecation logs a warning if a deprecated config option is
2378
// set.
2379
func logWarningsForDeprecation(cfg Config) {
1✔
2380
        _, deprecated, err := configToFlatMap(cfg)
1✔
2381
        if err != nil {
1✔
2382
                ltndLog.Errorf("Convert configs to map: %v", err)
×
2383
        }
×
2384

2385
        for k := range deprecated {
1✔
2386
                ltndLog.Warnf("Config '%s' is deprecated, please remove it", k)
×
2387
        }
×
2388
}
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