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

lightningnetwork / lnd / 15736109134

18 Jun 2025 02:46PM UTC coverage: 58.197% (-10.1%) from 68.248%
15736109134

Pull #9752

github

web-flow
Merge d2634a68c into 31c74f20f
Pull Request #9752: routerrpc: reject payment to invoice that don't have payment secret or blinded paths

6 of 13 new or added lines in 2 files covered. (46.15%)

28331 existing lines in 455 files now uncovered.

97860 of 168153 relevant lines covered (58.2%)

1.81 hits per line

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

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

5
package lnd
6

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

241
        // DefaultNumRestrictedSlots is the default number of restricted slots
242
        // we'll allocate in the server.
243
        DefaultNumRestrictedSlots = 100
244

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

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

252
        defaultPrunedNodeMaxPeers = 4
253
        defaultNeutrinoMaxPeers   = 8
254

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

359
        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"`
360

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

366
        Pprof *lncfg.Pprof `group:"Pprof" namespace:"pprof"`
367

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

373
        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."`
374

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

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

382
        BlockCacheSize uint64 `long:"blockcachesize" description:"The maximum capacity of the block cache"`
383

384
        Autopilot *lncfg.AutoPilot `group:"Autopilot" namespace:"autopilot"`
385

386
        Tor *lncfg.Tor `group:"Tor" namespace:"tor"`
387

388
        SubRPCServers *subRPCServerConfigs `group:"subrpc"`
389

390
        Hodl *hodl.Config `group:"hodl" namespace:"hodl"`
391

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

394
        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."`
395
        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."`
396
        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."`
397

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

445
        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]."`
446

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

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

451
        net tor.Net
452

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

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

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

459
        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]"`
460

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

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

465
        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"`
466

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

469
        Fee *lncfg.Fee `group:"fee" namespace:"fee"`
470

471
        Invoices *lncfg.Invoices `group:"invoices" namespace:"invoices"`
472

473
        Routing *lncfg.Routing `group:"routing" namespace:"routing"`
474

475
        Gossip *lncfg.Gossip `group:"gossip" namespace:"gossip"`
476

477
        Workers *lncfg.Workers `group:"workers" namespace:"workers"`
478

479
        Caches *lncfg.Caches `group:"caches" namespace:"caches"`
480

481
        Prometheus lncfg.Prometheus `group:"prometheus" namespace:"prometheus"`
482

483
        WtClient *lncfg.WtClient `group:"wtclient" namespace:"wtclient"`
484

485
        Watchtower *lncfg.Watchtower `group:"watchtower" namespace:"watchtower"`
486

487
        ProtocolOptions *lncfg.ProtocolOptions `group:"protocol" namespace:"protocol"`
488

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

491
        HealthChecks *lncfg.HealthCheckConfig `group:"healthcheck" namespace:"healthcheck"`
492

493
        DB *lncfg.DB `group:"db" namespace:"db"`
494

495
        Cluster *lncfg.Cluster `group:"cluster" namespace:"cluster"`
496

497
        RPCMiddleware *lncfg.RPCMiddleware `group:"rpcmiddleware" namespace:"rpcmiddleware"`
498

499
        RemoteSigner *lncfg.RemoteSigner `group:"remotesigner" namespace:"remotesigner"`
500

501
        Sweeper *lncfg.Sweeper `group:"sweeper" namespace:"sweeper"`
502

503
        Htlcswitch *lncfg.Htlcswitch `group:"htlcswitch" namespace:"htlcswitch"`
504

505
        GRPC *GRPCConfig `group:"grpc" namespace:"grpc"`
506

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

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

518
        // ActiveNetParams contains parameters of the target chain.
519
        ActiveNetParams chainreg.BitcoinNetParams
520

521
        // Estimator is used to estimate routing probabilities.
522
        Estimator routing.Estimator
523

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3✔
805
                configFilePath = filepath.Join(
3✔
806
                        configFileDir, lncfg.DefaultConfigFilename,
3✔
807
                )
3✔
808

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

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

×
830
                        return nil, err
×
831
                }
×
832

833
                configFileError = err
3✔
834
        }
835

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

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

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

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

×
867
                return nil, err
×
868
        }
×
869

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

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

3✔
880
        return cleanCfg, nil
3✔
881
}
882

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

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

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

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

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

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

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

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

945
                return nil
3✔
946
        }
947

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

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

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

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

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

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

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

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

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

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

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

×
1042
                return nil, mkErr(str)
×
1043
        }
×
1044

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

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

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

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

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

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

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

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

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

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

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

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

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

×
1148
                return nil, mkErr(str)
×
1149
        }
×
1150

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

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

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

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

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

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

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

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

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

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

×
1288
                return nil, mkErr(str)
×
1289
        }
×
1290

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

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

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

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

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

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

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

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

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

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

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

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

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

×
1367
                return nil, mkErr(str)
×
1368
        }
×
1369

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1478
                cfg.LogConfig.File.MaxLogFileSize = cfg.MaxLogFileSize
×
1479
        }
1480

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1615
                // For the p2p port it makes no sense to listen to an Unix socket.
1616
                // Also, we would need to refactor the brontide listener to support
1617
                // that.
1618
                for _, p2pListener := range cfg.Listeners {
6✔
1619
                        if lncfg.IsUnix(p2pListener) {
3✔
1620
                                return nil, mkErr("unix socket addresses "+
×
1621
                                        "cannot be used for the p2p "+
×
1622
                                        "connection listener: %s", p2pListener)
×
1623
                        }
×
1624
                }
1625
        }
1626

1627
        // Ensure that the specified minimum backoff is below or equal to the
1628
        // maximum backoff.
1629
        if cfg.MinBackoff > cfg.MaxBackoff {
3✔
1630
                return nil, mkErr("maxbackoff must be greater than minbackoff")
×
1631
        }
×
1632

1633
        // Newer versions of lnd added a new sub-config for bolt-specific
1634
        // parameters. However, we want to also allow existing users to use the
1635
        // value on the top-level config. If the outer config value is set,
1636
        // then we'll use that directly.
1637
        flagSet, err := isSet("SyncFreelist")
3✔
1638
        if err != nil {
3✔
1639
                return nil, mkErr("error parsing freelist sync flag: %v", err)
×
1640
        }
×
1641
        if flagSet {
3✔
1642
                cfg.DB.Bolt.NoFreelistSync = !cfg.SyncFreelist
×
1643
        }
×
1644

1645
        // Parse any extra sqlite pragma options that may have been provided
1646
        // to determine if they override any of the defaults that we will
1647
        // otherwise add.
1648
        var (
3✔
1649
                defaultSynchronous = true
3✔
1650
                defaultAutoVacuum  = true
3✔
1651
                defaultFullfsync   = true
3✔
1652
        )
3✔
1653
        for _, option := range cfg.DB.Sqlite.PragmaOptions {
3✔
1654
                switch {
×
1655
                case strings.HasPrefix(option, "synchronous="):
×
1656
                        defaultSynchronous = false
×
1657

1658
                case strings.HasPrefix(option, "auto_vacuum="):
×
1659
                        defaultAutoVacuum = false
×
1660

1661
                case strings.HasPrefix(option, "fullfsync="):
×
1662
                        defaultFullfsync = false
×
1663

1664
                default:
×
1665
                }
1666
        }
1667

1668
        if defaultSynchronous {
6✔
1669
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1670
                        cfg.DB.Sqlite.PragmaOptions, "synchronous=full",
3✔
1671
                )
3✔
1672
        }
3✔
1673

1674
        if defaultAutoVacuum {
6✔
1675
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1676
                        cfg.DB.Sqlite.PragmaOptions, "auto_vacuum=incremental",
3✔
1677
                )
3✔
1678
        }
3✔
1679

1680
        if defaultFullfsync {
6✔
1681
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1682
                        cfg.DB.Sqlite.PragmaOptions, "fullfsync=true",
3✔
1683
                )
3✔
1684
        }
3✔
1685

1686
        // Ensure that the user hasn't chosen a remote-max-htlc value greater
1687
        // than the protocol maximum.
1688
        maxRemoteHtlcs := uint16(input.MaxHTLCNumber / 2)
3✔
1689
        if cfg.DefaultRemoteMaxHtlcs > maxRemoteHtlcs {
3✔
1690
                return nil, mkErr("default-remote-max-htlcs (%v) must be "+
×
1691
                        "less than %v", cfg.DefaultRemoteMaxHtlcs,
×
1692
                        maxRemoteHtlcs)
×
1693
        }
×
1694

1695
        // Clamp the ChannelCommitInterval so that commitment updates can still
1696
        // happen in a reasonable timeframe.
1697
        if cfg.ChannelCommitInterval > maxChannelCommitInterval {
3✔
1698
                return nil, mkErr("channel-commit-interval (%v) must be less "+
×
1699
                        "than %v", cfg.ChannelCommitInterval,
×
1700
                        maxChannelCommitInterval)
×
1701
        }
×
1702

1703
        // Limit PendingCommitInterval so we don't wait too long for the remote
1704
        // party to send back a revoke.
1705
        if cfg.PendingCommitInterval > maxPendingCommitInterval {
3✔
1706
                return nil, mkErr("pending-commit-interval (%v) must be less "+
×
1707
                        "than %v", cfg.PendingCommitInterval,
×
1708
                        maxPendingCommitInterval)
×
1709
        }
×
1710

1711
        if err := cfg.Gossip.Parse(); err != nil {
3✔
1712
                return nil, mkErr("error parsing gossip syncer: %v", err)
×
1713
        }
×
1714

1715
        // If the experimental protocol options specify any protocol messages
1716
        // that we want to handle as custom messages, set them now.
1717
        customMsg := cfg.ProtocolOptions.CustomMessageOverrides()
3✔
1718

3✔
1719
        // We can safely set our custom override values during startup because
3✔
1720
        // startup is blocked on config parsing.
3✔
1721
        if err := lnwire.SetCustomOverrides(customMsg); err != nil {
3✔
1722
                return nil, mkErr("custom-message: %v", err)
×
1723
        }
×
1724

1725
        // Map old pprof flags to new pprof group flags.
1726
        //
1727
        // NOTE: This is a temporary measure to ensure compatibility with old
1728
        // flags.
1729
        if cfg.CPUProfile != "" {
3✔
1730
                if cfg.Pprof.CPUProfile != "" {
×
1731
                        return nil, mkErr("cpuprofile and pprof.cpuprofile " +
×
1732
                                "are mutually exclusive")
×
1733
                }
×
1734
                cfg.Pprof.CPUProfile = cfg.CPUProfile
×
1735
        }
1736
        if cfg.Profile != "" {
3✔
1737
                if cfg.Pprof.Profile != "" {
×
1738
                        return nil, mkErr("profile and pprof.profile " +
×
1739
                                "are mutually exclusive")
×
1740
                }
×
1741
                cfg.Pprof.Profile = cfg.Profile
×
1742
        }
1743
        if cfg.BlockingProfile != 0 {
3✔
1744
                if cfg.Pprof.BlockingProfile != 0 {
×
1745
                        return nil, mkErr("blockingprofile and " +
×
1746
                                "pprof.blockingprofile are mutually exclusive")
×
1747
                }
×
1748
                cfg.Pprof.BlockingProfile = cfg.BlockingProfile
×
1749
        }
1750
        if cfg.MutexProfile != 0 {
3✔
1751
                if cfg.Pprof.MutexProfile != 0 {
×
1752
                        return nil, mkErr("mutexprofile and " +
×
1753
                                "pprof.mutexprofile are mutually exclusive")
×
1754
                }
×
1755
                cfg.Pprof.MutexProfile = cfg.MutexProfile
×
1756
        }
1757

1758
        // Don't allow both the old dust-threshold and the new
1759
        // channel-max-fee-exposure to be set.
1760
        if cfg.DustThreshold != 0 && cfg.MaxFeeExposure != 0 {
3✔
1761
                return nil, mkErr("cannot set both dust-threshold and " +
×
1762
                        "channel-max-fee-exposure")
×
1763
        }
×
1764

1765
        switch {
3✔
1766
        // Use the old dust-threshold as the max fee exposure if it is set and
1767
        // the new option is not.
1768
        case cfg.DustThreshold != 0:
×
1769
                cfg.MaxFeeExposure = cfg.DustThreshold
×
1770

1771
        // Use the default max fee exposure if the new option is not set and
1772
        // the old one is not set either.
1773
        case cfg.MaxFeeExposure == 0:
3✔
1774
                cfg.MaxFeeExposure = uint64(
3✔
1775
                        htlcswitch.DefaultMaxFeeExposure.ToSatoshis(),
3✔
1776
                )
3✔
1777
        }
1778

1779
        // Validate the subconfigs for workers, caches, and the tower client.
1780
        err = lncfg.Validate(
3✔
1781
                cfg.Workers,
3✔
1782
                cfg.Caches,
3✔
1783
                cfg.WtClient,
3✔
1784
                cfg.DB,
3✔
1785
                cfg.Cluster,
3✔
1786
                cfg.HealthChecks,
3✔
1787
                cfg.RPCMiddleware,
3✔
1788
                cfg.RemoteSigner,
3✔
1789
                cfg.Sweeper,
3✔
1790
                cfg.Htlcswitch,
3✔
1791
                cfg.Invoices,
3✔
1792
                cfg.Routing,
3✔
1793
                cfg.Pprof,
3✔
1794
                cfg.Gossip,
3✔
1795
        )
3✔
1796
        if err != nil {
3✔
1797
                return nil, err
×
1798
        }
×
1799

1800
        // Finally, ensure that the user's color is correctly formatted,
1801
        // otherwise the server will not be able to start after the unlocking
1802
        // the wallet.
1803
        _, err = lncfg.ParseHexColor(cfg.Color)
3✔
1804
        if err != nil {
3✔
1805
                return nil, mkErr("unable to parse node color: %v", err)
×
1806
        }
×
1807

1808
        // All good, return the sanitized result.
1809
        return &cfg, nil
3✔
1810
}
1811

1812
// graphDatabaseDir returns the default directory where the local bolt graph db
1813
// files are stored.
1814
func (c *Config) graphDatabaseDir() string {
3✔
1815
        return filepath.Join(
3✔
1816
                c.DataDir, defaultGraphSubDirname,
3✔
1817
                lncfg.NormalizeNetwork(c.ActiveNetParams.Name),
3✔
1818
        )
3✔
1819
}
3✔
1820

1821
// ImplementationConfig returns the configuration of what actual implementations
1822
// should be used when creating the main lnd instance.
1823
func (c *Config) ImplementationConfig(
1824
        interceptor signal.Interceptor) *ImplementationCfg {
3✔
1825

3✔
1826
        // If we're using a remote signer, we still need the base wallet as a
3✔
1827
        // watch-only source of chain and address data. But we don't need any
3✔
1828
        // private key material in that btcwallet base wallet.
3✔
1829
        if c.RemoteSigner.Enable {
6✔
1830
                rpcImpl := NewRPCSignerWalletImpl(
3✔
1831
                        c, ltndLog, interceptor,
3✔
1832
                        c.RemoteSigner.MigrateWatchOnly,
3✔
1833
                )
3✔
1834
                return &ImplementationCfg{
3✔
1835
                        GrpcRegistrar:     rpcImpl,
3✔
1836
                        RestRegistrar:     rpcImpl,
3✔
1837
                        ExternalValidator: rpcImpl,
3✔
1838
                        DatabaseBuilder: NewDefaultDatabaseBuilder(
3✔
1839
                                c, ltndLog,
3✔
1840
                        ),
3✔
1841
                        WalletConfigBuilder: rpcImpl,
3✔
1842
                        ChainControlBuilder: rpcImpl,
3✔
1843
                }
3✔
1844
        }
3✔
1845

1846
        defaultImpl := NewDefaultWalletImpl(c, ltndLog, interceptor, false)
3✔
1847
        return &ImplementationCfg{
3✔
1848
                GrpcRegistrar:       defaultImpl,
3✔
1849
                RestRegistrar:       defaultImpl,
3✔
1850
                ExternalValidator:   defaultImpl,
3✔
1851
                DatabaseBuilder:     NewDefaultDatabaseBuilder(c, ltndLog),
3✔
1852
                WalletConfigBuilder: defaultImpl,
3✔
1853
                ChainControlBuilder: defaultImpl,
3✔
1854
        }
3✔
1855
}
1856

1857
// CleanAndExpandPath expands environment variables and leading ~ in the
1858
// passed path, cleans the result, and returns it.
1859
// This function is taken from https://github.com/btcsuite/btcd
1860
func CleanAndExpandPath(path string) string {
3✔
1861
        if path == "" {
6✔
1862
                return ""
3✔
1863
        }
3✔
1864

1865
        // Expand initial ~ to OS specific home directory.
1866
        if strings.HasPrefix(path, "~") {
3✔
1867
                var homeDir string
×
1868
                u, err := user.Current()
×
1869
                if err == nil {
×
1870
                        homeDir = u.HomeDir
×
1871
                } else {
×
1872
                        homeDir = os.Getenv("HOME")
×
1873
                }
×
1874

1875
                path = strings.Replace(path, "~", homeDir, 1)
×
1876
        }
1877

1878
        // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
1879
        // but the variables can still be expanded via POSIX-style $VARIABLE.
1880
        return filepath.Clean(os.ExpandEnv(path))
3✔
1881
}
1882

1883
func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
1884
        netParams chainreg.BitcoinNetParams) error {
2✔
1885

2✔
1886
        // First, we'll check our node config to make sure the RPC parameters
2✔
1887
        // were set correctly. We'll also determine the path to the conf file
2✔
1888
        // depending on the backend node.
2✔
1889
        var daemonName, confDir, confFile, confFileBase string
2✔
1890
        switch conf := nodeConfig.(type) {
2✔
1891
        case *lncfg.Btcd:
1✔
1892
                // Resolves environment variable references in RPCUser and
1✔
1893
                // RPCPass fields.
1✔
1894
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
1✔
1895
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
1✔
1896

1✔
1897
                // If both RPCUser and RPCPass are set, we assume those
1✔
1898
                // credentials are good to use.
1✔
1899
                if conf.RPCUser != "" && conf.RPCPass != "" {
2✔
1900
                        return nil
1✔
1901
                }
1✔
1902

1903
                // Set the daemon name for displaying proper errors.
1904
                daemonName = btcdBackendName
×
1905
                confDir = conf.Dir
×
1906
                confFileBase = btcdBackendName
×
1907

×
1908
                // If only ONE of RPCUser or RPCPass is set, we assume the
×
1909
                // user did that unintentionally.
×
1910
                if conf.RPCUser != "" || conf.RPCPass != "" {
×
1911
                        return fmt.Errorf("please set both or neither of "+
×
1912
                                "%[1]v.rpcuser, %[1]v.rpcpass", daemonName)
×
1913
                }
×
1914

1915
        case *lncfg.Bitcoind:
1✔
1916
                // Ensure that if the ZMQ options are set, that they are not
1✔
1917
                // equal.
1✔
1918
                if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
2✔
1919
                        err := checkZMQOptions(
1✔
1920
                                conf.ZMQPubRawBlock, conf.ZMQPubRawTx,
1✔
1921
                        )
1✔
1922
                        if err != nil {
1✔
1923
                                return err
×
1924
                        }
×
1925
                }
1926

1927
                // Ensure that if the estimate mode is set, that it is a legal
1928
                // value.
1929
                if conf.EstimateMode != "" {
2✔
1930
                        err := checkEstimateMode(conf.EstimateMode)
1✔
1931
                        if err != nil {
1✔
1932
                                return err
×
1933
                        }
×
1934
                }
1935

1936
                // Set the daemon name for displaying proper errors.
1937
                daemonName = bitcoindBackendName
1✔
1938
                confDir = conf.Dir
1✔
1939
                confFile = conf.ConfigPath
1✔
1940
                confFileBase = BitcoinChainName
1✔
1941

1✔
1942
                // Resolves environment variable references in RPCUser
1✔
1943
                // and RPCPass fields.
1✔
1944
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
1✔
1945
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
1✔
1946

1✔
1947
                // Check that cookie and credentials don't contradict each
1✔
1948
                // other.
1✔
1949
                if (conf.RPCUser != "" || conf.RPCPass != "") &&
1✔
1950
                        conf.RPCCookie != "" {
1✔
1951

×
1952
                        return fmt.Errorf("please only provide either "+
×
1953
                                "%[1]v.rpccookie or %[1]v.rpcuser and "+
×
1954
                                "%[1]v.rpcpass", daemonName)
×
1955
                }
×
1956

1957
                // We convert the cookie into a user name and password.
1958
                if conf.RPCCookie != "" {
1✔
1959
                        cookie, err := os.ReadFile(conf.RPCCookie)
×
1960
                        if err != nil {
×
1961
                                return fmt.Errorf("cannot read cookie file: %w",
×
1962
                                        err)
×
1963
                        }
×
1964

1965
                        splitCookie := strings.Split(string(cookie), ":")
×
1966
                        if len(splitCookie) != 2 {
×
1967
                                return fmt.Errorf("cookie file has a wrong " +
×
1968
                                        "format")
×
1969
                        }
×
1970
                        conf.RPCUser = splitCookie[0]
×
1971
                        conf.RPCPass = splitCookie[1]
×
1972
                }
1973

1974
                if conf.RPCUser != "" && conf.RPCPass != "" {
2✔
1975
                        // If all of RPCUser, RPCPass, ZMQBlockHost, and
1✔
1976
                        // ZMQTxHost are set, we assume those parameters are
1✔
1977
                        // good to use.
1✔
1978
                        if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
2✔
1979
                                return nil
1✔
1980
                        }
1✔
1981

1982
                        // If RPCUser and RPCPass are set and RPCPolling is
1983
                        // enabled, we assume the parameters are good to use.
1984
                        if conf.RPCPolling {
×
1985
                                return nil
×
1986
                        }
×
1987
                }
1988

1989
                // If not all of the parameters are set, we'll assume the user
1990
                // did this unintentionally.
1991
                if conf.RPCUser != "" || conf.RPCPass != "" ||
×
1992
                        conf.ZMQPubRawBlock != "" || conf.ZMQPubRawTx != "" {
×
1993

×
1994
                        return fmt.Errorf("please set %[1]v.rpcuser and "+
×
1995
                                "%[1]v.rpcpass (or %[1]v.rpccookie) together "+
×
1996
                                "with %[1]v.zmqpubrawblock, %[1]v.zmqpubrawtx",
×
1997
                                daemonName)
×
1998
                }
×
1999
        }
2000

2001
        // If we're in simnet mode, then the running btcd instance won't read
2002
        // the RPC credentials from the configuration. So if lnd wasn't
2003
        // specified the parameters, then we won't be able to start.
2004
        if cConfig.SimNet {
×
2005
                return fmt.Errorf("rpcuser and rpcpass must be set to your " +
×
2006
                        "btcd node's RPC parameters for simnet mode")
×
2007
        }
×
2008

2009
        fmt.Println("Attempting automatic RPC configuration to " + daemonName)
×
2010

×
2011
        if confFile == "" {
×
2012
                confFile = filepath.Join(confDir, fmt.Sprintf("%v.conf",
×
2013
                        confFileBase))
×
2014
        }
×
2015
        switch cConfig.Node {
×
2016
        case btcdBackendName:
×
2017
                nConf := nodeConfig.(*lncfg.Btcd)
×
2018
                rpcUser, rpcPass, err := extractBtcdRPCParams(confFile)
×
2019
                if err != nil {
×
2020
                        return fmt.Errorf("unable to extract RPC credentials: "+
×
2021
                                "%v, cannot start w/o RPC connection", err)
×
2022
                }
×
2023
                nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass
×
2024

2025
        case bitcoindBackendName:
×
2026
                nConf := nodeConfig.(*lncfg.Bitcoind)
×
2027
                rpcUser, rpcPass, zmqBlockHost, zmqTxHost, err :=
×
2028
                        extractBitcoindRPCParams(netParams.Params.Name,
×
2029
                                nConf.Dir, confFile, nConf.RPCCookie)
×
2030
                if err != nil {
×
2031
                        return fmt.Errorf("unable to extract RPC credentials: "+
×
2032
                                "%v, cannot start w/o RPC connection", err)
×
2033
                }
×
2034
                nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass
×
2035
                nConf.ZMQPubRawBlock, nConf.ZMQPubRawTx = zmqBlockHost, zmqTxHost
×
2036
        }
2037

2038
        fmt.Printf("Automatically obtained %v's RPC credentials\n", daemonName)
×
2039
        return nil
×
2040
}
2041

2042
// supplyEnvValue supplies the value of an environment variable from a string.
2043
// It supports the following formats:
2044
// 1) $ENV_VAR
2045
// 2) ${ENV_VAR}
2046
// 3) ${ENV_VAR:-DEFAULT}
2047
//
2048
// Standard environment variable naming conventions:
2049
// - ENV_VAR contains letters, digits, and underscores, and does
2050
// not start with a digit.
2051
// - DEFAULT follows the rule that it can contain any characters except
2052
// whitespace.
2053
//
2054
// Parameters:
2055
// - value: The input string containing references to environment variables
2056
// (if any).
2057
//
2058
// Returns:
2059
// - string: The value of the specified environment variable, the default
2060
// value if provided, or the original input string if no matching variable is
2061
// found or set.
2062
func supplyEnvValue(value string) string {
2✔
2063
        // Regex for $ENV_VAR format.
2✔
2064
        var reEnvVar = regexp.MustCompile(`^\$([a-zA-Z_][a-zA-Z0-9_]*)$`)
2✔
2065

2✔
2066
        // Regex for ${ENV_VAR} format.
2✔
2067
        var reEnvVarWithBrackets = regexp.MustCompile(
2✔
2068
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}$`,
2✔
2069
        )
2✔
2070

2✔
2071
        // Regex for ${ENV_VAR:-DEFAULT} format.
2✔
2072
        var reEnvVarWithDefault = regexp.MustCompile(
2✔
2073
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*):-([\S]+)\}$`,
2✔
2074
        )
2✔
2075

2✔
2076
        // Match against supported formats.
2✔
2077
        switch {
2✔
UNCOV
2078
        case reEnvVarWithDefault.MatchString(value):
×
UNCOV
2079
                matches := reEnvVarWithDefault.FindStringSubmatch(value)
×
UNCOV
2080
                envVariable := matches[1]
×
UNCOV
2081
                defaultValue := matches[2]
×
UNCOV
2082
                if envValue := os.Getenv(envVariable); envValue != "" {
×
UNCOV
2083
                        return envValue
×
UNCOV
2084
                }
×
2085

UNCOV
2086
                return defaultValue
×
2087

2088
        case reEnvVarWithBrackets.MatchString(value):
×
2089
                matches := reEnvVarWithBrackets.FindStringSubmatch(value)
×
2090
                envVariable := matches[1]
×
2091
                envValue := os.Getenv(envVariable)
×
2092

×
2093
                return envValue
×
2094

UNCOV
2095
        case reEnvVar.MatchString(value):
×
UNCOV
2096
                matches := reEnvVar.FindStringSubmatch(value)
×
UNCOV
2097
                envVariable := matches[1]
×
UNCOV
2098
                envValue := os.Getenv(envVariable)
×
UNCOV
2099

×
UNCOV
2100
                return envValue
×
2101
        }
2102

2103
        return value
2✔
2104
}
2105

2106
// extractBtcdRPCParams attempts to extract the RPC credentials for an existing
2107
// btcd instance. The passed path is expected to be the location of btcd's
2108
// application data directory on the target system.
2109
func extractBtcdRPCParams(btcdConfigPath string) (string, string, error) {
×
2110
        // First, we'll open up the btcd configuration file found at the target
×
2111
        // destination.
×
2112
        btcdConfigFile, err := os.Open(btcdConfigPath)
×
2113
        if err != nil {
×
2114
                return "", "", err
×
2115
        }
×
2116
        defer func() { _ = btcdConfigFile.Close() }()
×
2117

2118
        // With the file open extract the contents of the configuration file so
2119
        // we can attempt to locate the RPC credentials.
2120
        configContents, err := io.ReadAll(btcdConfigFile)
×
2121
        if err != nil {
×
2122
                return "", "", err
×
2123
        }
×
2124

2125
        // Attempt to locate the RPC user using a regular expression. If we
2126
        // don't have a match for our regular expression then we'll exit with
2127
        // an error.
2128
        rpcUserRegexp, err := regexp.Compile(`(?m)^\s*rpcuser\s*=\s*([^\s]+)`)
×
2129
        if err != nil {
×
2130
                return "", "", err
×
2131
        }
×
2132
        userSubmatches := rpcUserRegexp.FindSubmatch(configContents)
×
2133
        if userSubmatches == nil {
×
2134
                return "", "", fmt.Errorf("unable to find rpcuser in config")
×
2135
        }
×
2136

2137
        // Similarly, we'll use another regular expression to find the set
2138
        // rpcpass (if any). If we can't find the pass, then we'll exit with an
2139
        // error.
2140
        rpcPassRegexp, err := regexp.Compile(`(?m)^\s*rpcpass\s*=\s*([^\s]+)`)
×
2141
        if err != nil {
×
2142
                return "", "", err
×
2143
        }
×
2144
        passSubmatches := rpcPassRegexp.FindSubmatch(configContents)
×
2145
        if passSubmatches == nil {
×
2146
                return "", "", fmt.Errorf("unable to find rpcuser in config")
×
2147
        }
×
2148

2149
        return supplyEnvValue(string(userSubmatches[1])),
×
2150
                supplyEnvValue(string(passSubmatches[1])), nil
×
2151
}
2152

2153
// extractBitcoindRPCParams attempts to extract the RPC credentials for an
2154
// existing bitcoind node instance. The routine looks for a cookie first,
2155
// optionally following the datadir configuration option in the bitcoin.conf. If
2156
// it doesn't find one, it looks for rpcuser/rpcpassword.
2157
func extractBitcoindRPCParams(networkName, bitcoindDataDir, bitcoindConfigPath,
2158
        rpcCookiePath string) (string, string, string, string, error) {
×
2159

×
2160
        // First, we'll open up the bitcoind configuration file found at the
×
2161
        // target destination.
×
2162
        bitcoindConfigFile, err := os.Open(bitcoindConfigPath)
×
2163
        if err != nil {
×
2164
                return "", "", "", "", err
×
2165
        }
×
2166
        defer func() { _ = bitcoindConfigFile.Close() }()
×
2167

2168
        // With the file open extract the contents of the configuration file so
2169
        // we can attempt to locate the RPC credentials.
2170
        configContents, err := io.ReadAll(bitcoindConfigFile)
×
2171
        if err != nil {
×
2172
                return "", "", "", "", err
×
2173
        }
×
2174

2175
        // First, we'll look for the ZMQ hosts providing raw block and raw
2176
        // transaction notifications.
2177
        zmqBlockHostRE, err := regexp.Compile(
×
2178
                `(?m)^\s*zmqpubrawblock\s*=\s*([^\s]+)`,
×
2179
        )
×
2180
        if err != nil {
×
2181
                return "", "", "", "", err
×
2182
        }
×
2183
        zmqBlockHostSubmatches := zmqBlockHostRE.FindSubmatch(configContents)
×
2184
        if len(zmqBlockHostSubmatches) < 2 {
×
2185
                return "", "", "", "", fmt.Errorf("unable to find " +
×
2186
                        "zmqpubrawblock in config")
×
2187
        }
×
2188
        zmqTxHostRE, err := regexp.Compile(`(?m)^\s*zmqpubrawtx\s*=\s*([^\s]+)`)
×
2189
        if err != nil {
×
2190
                return "", "", "", "", err
×
2191
        }
×
2192
        zmqTxHostSubmatches := zmqTxHostRE.FindSubmatch(configContents)
×
2193
        if len(zmqTxHostSubmatches) < 2 {
×
2194
                return "", "", "", "", errors.New("unable to find zmqpubrawtx " +
×
2195
                        "in config")
×
2196
        }
×
2197
        zmqBlockHost := string(zmqBlockHostSubmatches[1])
×
2198
        zmqTxHost := string(zmqTxHostSubmatches[1])
×
2199
        if err := checkZMQOptions(zmqBlockHost, zmqTxHost); err != nil {
×
2200
                return "", "", "", "", err
×
2201
        }
×
2202

2203
        // Next, we'll try to find an auth cookie. We need to detect the chain
2204
        // by seeing if one is specified in the configuration file.
2205
        dataDir := filepath.Dir(bitcoindConfigPath)
×
2206
        if bitcoindDataDir != "" {
×
2207
                dataDir = bitcoindDataDir
×
2208
        }
×
2209
        dataDirRE, err := regexp.Compile(`(?m)^\s*datadir\s*=\s*([^\s]+)`)
×
2210
        if err != nil {
×
2211
                return "", "", "", "", err
×
2212
        }
×
2213
        dataDirSubmatches := dataDirRE.FindSubmatch(configContents)
×
2214
        if dataDirSubmatches != nil {
×
2215
                dataDir = string(dataDirSubmatches[1])
×
2216
        }
×
2217

2218
        var chainDir string
×
2219
        switch networkName {
×
2220
        case "mainnet":
×
2221
                chainDir = ""
×
2222
        case "regtest", "testnet3", "testnet4", "signet":
×
2223
                chainDir = networkName
×
2224
        default:
×
2225
                return "", "", "", "", fmt.Errorf("unexpected networkname %v", networkName)
×
2226
        }
2227

2228
        cookiePath := filepath.Join(dataDir, chainDir, ".cookie")
×
2229
        if rpcCookiePath != "" {
×
2230
                cookiePath = rpcCookiePath
×
2231
        }
×
2232
        cookie, err := os.ReadFile(cookiePath)
×
2233
        if err == nil {
×
2234
                splitCookie := strings.Split(string(cookie), ":")
×
2235
                if len(splitCookie) == 2 {
×
2236
                        return splitCookie[0], splitCookie[1], zmqBlockHost,
×
2237
                                zmqTxHost, nil
×
2238
                }
×
2239
        }
2240

2241
        // We didn't find a cookie, so we attempt to locate the RPC user using
2242
        // a regular expression. If we  don't have a match for our regular
2243
        // expression then we'll exit with an error.
2244
        rpcUserRegexp, err := regexp.Compile(`(?m)^\s*rpcuser\s*=\s*([^\s]+)`)
×
2245
        if err != nil {
×
2246
                return "", "", "", "", err
×
2247
        }
×
2248
        userSubmatches := rpcUserRegexp.FindSubmatch(configContents)
×
2249

×
2250
        // Similarly, we'll use another regular expression to find the set
×
2251
        // rpcpass (if any). If we can't find the pass, then we'll exit with an
×
2252
        // error.
×
2253
        rpcPassRegexp, err := regexp.Compile(`(?m)^\s*rpcpassword\s*=\s*([^\s]+)`)
×
2254
        if err != nil {
×
2255
                return "", "", "", "", err
×
2256
        }
×
2257
        passSubmatches := rpcPassRegexp.FindSubmatch(configContents)
×
2258

×
2259
        // Exit with an error if the cookie file, is defined in config, and
×
2260
        // can not be found, with both rpcuser and rpcpassword undefined.
×
2261
        if rpcCookiePath != "" && userSubmatches == nil && passSubmatches == nil {
×
2262
                return "", "", "", "", fmt.Errorf("unable to open cookie file (%v)",
×
2263
                        rpcCookiePath)
×
2264
        }
×
2265

2266
        if userSubmatches == nil {
×
2267
                return "", "", "", "", fmt.Errorf("unable to find rpcuser in " +
×
2268
                        "config")
×
2269
        }
×
2270
        if passSubmatches == nil {
×
2271
                return "", "", "", "", fmt.Errorf("unable to find rpcpassword " +
×
2272
                        "in config")
×
2273
        }
×
2274

2275
        return supplyEnvValue(string(userSubmatches[1])),
×
2276
                supplyEnvValue(string(passSubmatches[1])),
×
2277
                zmqBlockHost, zmqTxHost, nil
×
2278
}
2279

2280
// checkZMQOptions ensures that the provided addresses to use as the hosts for
2281
// ZMQ rawblock and rawtx notifications are different.
2282
func checkZMQOptions(zmqBlockHost, zmqTxHost string) error {
1✔
2283
        if zmqBlockHost == zmqTxHost {
1✔
2284
                return errors.New("zmqpubrawblock and zmqpubrawtx must be set " +
×
2285
                        "to different addresses")
×
2286
        }
×
2287

2288
        return nil
1✔
2289
}
2290

2291
// checkEstimateMode ensures that the provided estimate mode is legal.
2292
func checkEstimateMode(estimateMode string) error {
1✔
2293
        for _, mode := range bitcoindEstimateModes {
2✔
2294
                if estimateMode == mode {
2✔
2295
                        return nil
1✔
2296
                }
1✔
2297
        }
2298

2299
        return fmt.Errorf("estimatemode must be one of the following: %v",
×
2300
                bitcoindEstimateModes[:])
×
2301
}
2302

2303
// configToFlatMap converts the given config struct into a flat map of
2304
// key/value pairs using the dot notation we are used to from the config file
2305
// or command line flags. It also returns a map containing deprecated config
2306
// options.
2307
func configToFlatMap(cfg Config) (map[string]string,
2308
        map[string]struct{}, error) {
3✔
2309

3✔
2310
        result := make(map[string]string)
3✔
2311

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

3✔
2317
        // redact is the helper function that redacts sensitive values like
3✔
2318
        // passwords.
3✔
2319
        redact := func(key, value string) string {
6✔
2320
                sensitiveKeySuffixes := []string{
3✔
2321
                        "pass",
3✔
2322
                        "password",
3✔
2323
                        "dsn",
3✔
2324
                }
3✔
2325
                for _, suffix := range sensitiveKeySuffixes {
6✔
2326
                        if strings.HasSuffix(key, suffix) {
6✔
2327
                                return "[redacted]"
3✔
2328
                        }
3✔
2329
                }
2330

2331
                return value
3✔
2332
        }
2333

2334
        // printConfig is the helper function that goes into nested structs
2335
        // recursively. Because we call it recursively, we need to declare it
2336
        // before we define it.
2337
        var printConfig func(reflect.Value, string)
3✔
2338
        printConfig = func(obj reflect.Value, prefix string) {
6✔
2339
                // Turn struct pointers into the actual struct, so we can
3✔
2340
                // iterate over the fields as we would with a struct value.
3✔
2341
                if obj.Kind() == reflect.Ptr {
6✔
2342
                        obj = obj.Elem()
3✔
2343
                }
3✔
2344

2345
                // Abort on nil values.
2346
                if !obj.IsValid() {
3✔
UNCOV
2347
                        return
×
UNCOV
2348
                }
×
2349

2350
                // Loop over all fields of the struct and inspect the type.
2351
                for i := 0; i < obj.NumField(); i++ {
6✔
2352
                        field := obj.Field(i)
3✔
2353
                        fieldType := obj.Type().Field(i)
3✔
2354

3✔
2355
                        longName := fieldType.Tag.Get("long")
3✔
2356
                        namespace := fieldType.Tag.Get("namespace")
3✔
2357
                        group := fieldType.Tag.Get("group")
3✔
2358
                        hidden := fieldType.Tag.Get("hidden")
3✔
2359

3✔
2360
                        switch {
3✔
2361
                        // We have a long name defined, this is a config value.
2362
                        case longName != "":
3✔
2363
                                key := longName
3✔
2364
                                if prefix != "" {
6✔
2365
                                        key = prefix + "." + key
3✔
2366
                                }
3✔
2367

2368
                                // Add the value directly to the flattened map.
2369
                                result[key] = redact(key, fmt.Sprintf(
3✔
2370
                                        "%v", field.Interface(),
3✔
2371
                                ))
3✔
2372

3✔
2373
                                // If there's a hidden flag, it's deprecated.
3✔
2374
                                if hidden == "true" && !field.IsZero() {
3✔
UNCOV
2375
                                        deprecated[key] = struct{}{}
×
UNCOV
2376
                                }
×
2377

2378
                        // We have no long name but a namespace, this is a
2379
                        // nested struct.
2380
                        case longName == "" && namespace != "":
3✔
2381
                                key := namespace
3✔
2382
                                if prefix != "" {
6✔
2383
                                        key = prefix + "." + key
3✔
2384
                                }
3✔
2385

2386
                                printConfig(field, key)
3✔
2387

2388
                        // Just a group means this is a dummy struct to house
2389
                        // multiple config values, the group name doesn't go
2390
                        // into the final field name.
2391
                        case longName == "" && group != "":
3✔
2392
                                printConfig(field, prefix)
3✔
2393

2394
                        // Anonymous means embedded struct. We need to recurse
2395
                        // into it but without adding anything to the prefix.
2396
                        case fieldType.Anonymous:
3✔
2397
                                printConfig(field, prefix)
3✔
2398

2399
                        default:
3✔
2400
                                continue
3✔
2401
                        }
2402
                }
2403
        }
2404

2405
        // Turn the whole config struct into a flat map.
2406
        printConfig(reflect.ValueOf(cfg), "")
3✔
2407

3✔
2408
        return result, deprecated, nil
3✔
2409
}
2410

2411
// logWarningsForDeprecation logs a warning if a deprecated config option is
2412
// set.
2413
func logWarningsForDeprecation(cfg Config) {
3✔
2414
        _, deprecated, err := configToFlatMap(cfg)
3✔
2415
        if err != nil {
3✔
2416
                ltndLog.Errorf("Convert configs to map: %v", err)
×
2417
        }
×
2418

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