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

lightningnetwork / lnd / 12056196575

27 Nov 2024 06:23PM UTC coverage: 58.717% (-0.2%) from 58.921%
12056196575

Pull #9242

github

aakselrod
go.mod: update to latest btcwallet
Pull Request #9242: Reapply #8644

8 of 39 new or added lines in 3 files covered. (20.51%)

543 existing lines in 30 files now uncovered.

132924 of 226381 relevant lines covered (58.72%)

19504.16 hits per line

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

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

5
package lnd
6

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

285
        defaultPrunedNodeMaxPeers = 4
286
)
287

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

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

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

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

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

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

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

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

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

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

363
        FeeURL string `long:"feeurl" description:"DEPRECATED: Use 'fee.url' option. Optional URL for external fee estimation. If no URL is specified, the method for fee estimation will depend on the chosen backend and network. Must be set for neutrino on mainnet." hidden:"true"`
364

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

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

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

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

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

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

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

382
        NoSeedBackup             bool   `long:"noseedbackup" description:"If true, NO SEED WILL BE EXPOSED -- EVER, AND THE WALLET WILL BE ENCRYPTED USING THE DEFAULT PASSPHRASE. THIS FLAG IS ONLY FOR TESTING AND SHOULD NEVER BE USED ON MAINNET."`
383
        WalletUnlockPasswordFile string `long:"wallet-unlock-password-file" description:"The full path to a file (or pipe/device) that contains the password for unlocking the wallet; if set, no unlocking through RPC is possible and lnd will exit if no wallet exists or the password is incorrect; if wallet-unlock-allow-create is also set then lnd will ignore this flag if no wallet exists and allow a wallet to be created through RPC."`
384
        WalletUnlockAllowCreate  bool   `long:"wallet-unlock-allow-create" description:"Don't fail with an error if wallet-unlock-password-file is set but no wallet exists yet."`
385

386
        ResetWalletTransactions bool `long:"reset-wallet-transactions" description:"Removes all transaction history from the on-chain wallet on startup, forcing a full chain rescan starting at the wallet's birthday. Implements the same functionality as btcwallet's dropwtxmgr command. Should be set to false after successful execution to avoid rescanning on every restart of lnd."`
387

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

390
        PaymentsExpirationGracePeriod time.Duration `long:"payments-expiration-grace-period" description:"A period to wait before force closing channels with outgoing htlcs that have timed-out and are a result of this node initiated payments."`
391
        TrickleDelay                  int           `long:"trickledelay" description:"Time in milliseconds between each release of announcements to the network"`
392
        ChanEnableTimeout             time.Duration `long:"chan-enable-timeout" description:"The duration that a peer connection must be stable before attempting to send a channel update to re-enable or cancel a pending disables of the peer's channels on the network."`
393
        ChanDisableTimeout            time.Duration `long:"chan-disable-timeout" description:"The duration that must elapse after first detecting that an already active channel is actually inactive and sending channel update disabling it to the network. The pending disable can be canceled if the peer reconnects and becomes stable for chan-enable-timeout before the disable update is sent."`
394
        ChanStatusSampleInterval      time.Duration `long:"chan-status-sample-interval" description:"The polling interval between attempts to detect if an active channel has become inactive due to its peer going offline."`
395
        HeightHintCacheQueryDisable   bool          `long:"height-hint-cache-query-disable" description:"Disable queries from the height-hint cache to try to recover channels stuck in the pending close state. Disabling height hint queries may cause longer chain rescans, resulting in a performance hit. Unset this after channels are unstuck so you can get better performance again."`
396
        Alias                         string        `long:"alias" description:"The node alias. Used as a moniker by peers and intelligence services"`
397
        Color                         string        `long:"color" description:"The color of the node in hex format (i.e. '#3399FF'). Used to customize node appearance in intelligence services"`
398
        MinChanSize                   int64         `long:"minchansize" description:"The smallest channel size (in satoshis) that we should accept. Incoming channels smaller than this will be rejected"`
399
        MaxChanSize                   int64         `long:"maxchansize" description:"The largest channel size (in satoshis) that we should accept. Incoming channels larger than this will be rejected"`
400
        CoopCloseTargetConfs          uint32        `long:"coop-close-target-confs" description:"The target number of blocks that a cooperative channel close transaction should confirm in. This is used to estimate the fee to use as the lower bound during fee negotiation for the channel closure."`
401

402
        ChannelCommitInterval time.Duration `long:"channel-commit-interval" description:"The maximum time that is allowed to pass between receiving a channel state update and signing the next commitment. Setting this to a longer duration allows for more efficient channel operations at the cost of latency."`
403

404
        PendingCommitInterval time.Duration `long:"pending-commit-interval" description:"The maximum time that is allowed to pass while waiting for the remote party to revoke a locally initiated commitment state. Setting this to a longer duration if a slow response is expected from the remote party or large number of payments are attempted at the same time."`
405

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

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

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

412
        DefaultRemoteMaxHtlcs uint16 `long:"default-remote-max-htlcs" description:"The default max_htlc applied when opening or accepting channels. This value limits the number of concurrent HTLCs that the remote party can add to the commitment. The maximum possible value is 483."`
413

414
        NumGraphSyncPeers      int           `long:"numgraphsyncpeers" description:"The number of peers that we should receive new graph updates from. This option can be tuned to save bandwidth for light clients or routing nodes."`
415
        HistoricalSyncInterval time.Duration `long:"historicalsyncinterval" description:"The polling interval between historical graph sync attempts. Each historical graph sync attempt ensures we reconcile with the remote peer's graph from the genesis block."`
416

417
        IgnoreHistoricalGossipFilters bool `long:"ignore-historical-gossip-filters" description:"If true, will not reply with historical data that matches the range specified by a remote peer's gossip_timestamp_filter. Doing so will result in lower memory and bandwidth requirements."`
418

419
        RejectPush bool `long:"rejectpush" description:"If true, lnd will not accept channel opening requests with non-zero push amounts. This should prevent accidental pushes to merchant nodes."`
420

421
        RejectHTLC bool `long:"rejecthtlc" description:"If true, lnd will not forward any HTLCs that are meant as onward payments. This option will still allow lnd to send HTLCs and receive HTLCs but lnd won't be used as a hop."`
422

423
        AcceptPositiveInboundFees bool `long:"accept-positive-inbound-fees" description:"If true, lnd will also allow setting positive inbound fees. By default, lnd only allows to set negative inbound fees (an inbound \"discount\") to remain backwards compatible with senders whose implementations do not yet support inbound fees."`
424

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

429
        StaggerInitialReconnect bool `long:"stagger-initial-reconnect" description:"If true, will apply a randomized staggering between 0s and 30s when reconnecting to persistent peers on startup. The first 10 reconnections will be attempted instantly, regardless of the flag's value"`
430

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

433
        MaxChannelFeeAllocation float64 `long:"max-channel-fee-allocation" description:"The maximum percentage of total funds that can be allocated to a channel's commitment fee. This only applies for the initiator of the channel. Valid values are within [0.1, 1]."`
434

435
        MaxCommitFeeRateAnchors uint64 `long:"max-commit-fee-rate-anchors" description:"The maximum fee rate in sat/vbyte that will be used for commitments of channels of the anchors type. Must be large enough to ensure transaction propagation"`
436

437
        DryRunMigration bool `long:"dry-run-migration" description:"If true, lnd will abort committing a migration if it would otherwise have been successful. This leaves the database unmodified, and still compatible with the previously active version of lnd."`
438

439
        net tor.Net
440

441
        EnableUpfrontShutdown bool `long:"enable-upfront-shutdown" description:"If true, option upfront shutdown script will be enabled. If peers that we open channels with support this feature, we will automatically set the script to which cooperative closes should be paid out to on channel open. This offers the partial protection of a channel peer disconnecting from us if cooperative close is attempted with a different script."`
442

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

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

447
        KeysendHoldTime time.Duration `long:"keysend-hold-time" description:"If non-zero, keysend payments are accepted but not immediately settled. If the payment isn't settled manually after the specified time, it is canceled automatically. [experimental]"`
448

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

506
        // ActiveNetParams contains parameters of the target chain.
507
        ActiveNetParams chainreg.BitcoinNetParams
508

509
        // Estimator is used to estimate routing probabilities.
510
        Estimator routing.Estimator
511

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

516
        // HTTPHeaderTimeout is the maximum duration that the server will wait
517
        // before timing out reading the headers of an HTTP request.
518
        HTTPHeaderTimeout time.Duration `long:"http-header-timeout" description:"The maximum duration that the server will wait before timing out reading the headers of an HTTP request."`
519
}
520

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

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

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

543
        // ClientAllowPingWithoutStream specifies whether pings from the client
544
        // are allowed even if there are no active gRPC streams. This might be
545
        // useful to keep the underlying HTTP/2 connection open for future
546
        // requests.
547
        ClientAllowPingWithoutStream bool `long:"client-allow-ping-without-stream" description:"If true, the server allows keepalive pings from the client even when there are no active gRPC streams. This might be useful to keep the underlying HTTP/2 connection open for future requests."`
548
}
549

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

3✔
600
                Fee: &lncfg.Fee{
3✔
601
                        MinUpdateTimeout: lncfg.DefaultMinUpdateTimeout,
3✔
602
                        MaxUpdateTimeout: lncfg.DefaultMaxUpdateTimeout,
3✔
603
                },
3✔
604

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

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

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

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

2✔
779
                configFilePath = filepath.Join(
2✔
780
                        configFileDir, lncfg.DefaultConfigFilename,
2✔
781
                )
2✔
782

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

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

×
804
                        return nil, err
×
805
                }
×
806

807
                configFileError = err
2✔
808
        }
809

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

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

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

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

×
841
                return nil, err
×
842
        }
×
843

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

851
        // Finally, log warnings for deprecated config options if they are set.
852
        logWarningsForDeprecation(*cleanCfg)
2✔
853

2✔
854
        return cleanCfg, nil
2✔
855
}
856

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

2✔
863
        // Special show command to list supported subsystems and exit.
2✔
864
        if cfg.DebugLevel == "show" {
4✔
865
                subLogMgr := build.NewSubLoggerManager()
2✔
866

2✔
867
                // Initialize logging at the default logging level.
2✔
868
                SetupLoggers(subLogMgr, interceptor)
2✔
869

2✔
870
                fmt.Println("Supported subsystems",
2✔
871
                        subLogMgr.SupportedSubsystems())
2✔
872
                os.Exit(0)
2✔
873
        }
2✔
874

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

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

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

915
                        str := "Failed to create lnd directory '%s': %v"
×
916
                        return mkErr(str, dir, err)
×
917
                }
918

919
                return nil
2✔
920
        }
921

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

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

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

2✔
954
                return (fileOption != nil && fileOption.IsSet()) ||
2✔
955
                                (fileOptionNested != nil && fileOptionNested.IsSet()) ||
2✔
956
                                (flagOption != nil && flagOption.IsSet()) ||
2✔
957
                                (flagOptionNested != nil && flagOptionNested.IsSet()),
2✔
958
                        nil
2✔
959
        }
960

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

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

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

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

×
1001
                return nil, mkErr(str)
×
1002
        }
×
1003
        if cfg.Autopilot.MaxChannelSize < 0 {
2✔
1004
                str := "autopilot.maxchansize must be non-negative"
×
1005

×
1006
                return nil, mkErr(str)
×
1007
        }
×
1008
        if cfg.Autopilot.MinConfs < 0 {
2✔
1009
                str := "autopilot.minconfs must be non-negative"
×
1010

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

×
1016
                return nil, mkErr(str)
×
1017
        }
×
1018

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

1028
        if _, err := validateAtplCfg(cfg.Autopilot); err != nil {
2✔
1029
                return nil, mkErr("error validating autopilot: %v", err)
×
1030
        }
×
1031

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

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

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

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

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

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

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

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

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

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

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

×
1122
                return nil, mkErr(str)
×
1123
        }
×
1124

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

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

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

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

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

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

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

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

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

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

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

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

×
1268
                return nil, mkErr(str)
×
1269
        }
×
1270

1271
        err = cfg.Bitcoin.Validate(minTimeLockDelta, funding.MinBtcRemoteDelay)
2✔
1272
        if err != nil {
2✔
1273
                return nil, mkErr("error validating bitcoin params: %v", err)
×
1274
        }
×
1275

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

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

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

1305
        default:
×
1306
                str := "only btcd, bitcoind, and neutrino mode " +
×
1307
                        "supported for bitcoin at this time"
×
1308

×
1309
                return nil, mkErr(str)
×
1310
        }
1311

1312
        cfg.Bitcoin.ChainDir = filepath.Join(
2✔
1313
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
2✔
1314
        )
2✔
1315

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

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

×
1326
                return nil, mkErr(str)
×
1327
        }
×
1328
        if cfg.Autopilot.MinChannelSize < 0 {
2✔
1329
                str := "autopilot.minchansize must be non-negative"
×
1330

×
1331
                return nil, mkErr(str)
×
1332
        }
×
1333
        if cfg.Autopilot.MaxChannelSize < 0 {
2✔
1334
                str := "autopilot.maxchansize must be non-negative"
×
1335

×
1336
                return nil, mkErr(str)
×
1337
        }
×
1338

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

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

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

1374
        towerDir := filepath.Join(
2✔
1375
                cfg.Watchtower.TowerDir, BitcoinChainName,
2✔
1376
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
2✔
1377
        )
2✔
1378

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

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

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

2✔
1412
        if err := cfg.LogConfig.Validate(); err != nil {
2✔
1413
                return nil, mkErr("error validating logging config: %w", err)
×
1414
        }
×
1415

1416
        cfg.SubLogMgr = build.NewSubLoggerManager(build.NewDefaultLogHandlers(
2✔
1417
                cfg.LogConfig, cfg.LogRotator,
2✔
1418
        )...)
2✔
1419

2✔
1420
        // Initialize logging at the default logging level.
2✔
1421
        SetupLoggers(cfg.SubLogMgr, interceptor)
2✔
1422

2✔
1423
        if cfg.MaxLogFiles != 0 {
2✔
1424
                if cfg.LogConfig.File.MaxLogFiles !=
×
1425
                        build.DefaultMaxLogFiles {
×
1426

×
1427
                        return nil, mkErr("cannot set both maxlogfiles and "+
×
1428
                                "logging.file.max-files", err)
×
1429
                }
×
1430

1431
                cfg.LogConfig.File.MaxLogFiles = cfg.MaxLogFiles
×
1432
        }
1433
        if cfg.MaxLogFileSize != 0 {
2✔
1434
                if cfg.LogConfig.File.MaxLogFileSize !=
×
1435
                        build.DefaultMaxLogFileSize {
×
1436

×
1437
                        return nil, mkErr("cannot set both maxlogfilesize and "+
×
1438
                                "logging.file.max-file-size", err)
×
1439
                }
×
1440

1441
                cfg.LogConfig.File.MaxLogFileSize = cfg.MaxLogFileSize
×
1442
        }
1443

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

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

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

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

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

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

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

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

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

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

×
1521
                return nil, mkErr("wallet unlock password file %s does "+
×
1522
                        "not exist", cfg.WalletUnlockPasswordFile)
×
1523
        }
1524

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

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

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

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

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

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

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

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

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

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

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

1627
                default:
×
1628
                }
1629
        }
1630

1631
        if defaultSynchronous {
4✔
1632
                cfg.DB.Sqlite.PragmaOptions = append(
2✔
1633
                        cfg.DB.Sqlite.PragmaOptions, "synchronous=full",
2✔
1634
                )
2✔
1635
        }
2✔
1636

1637
        if defaultAutoVacuum {
4✔
1638
                cfg.DB.Sqlite.PragmaOptions = append(
2✔
1639
                        cfg.DB.Sqlite.PragmaOptions, "auto_vacuum=incremental",
2✔
1640
                )
2✔
1641
        }
2✔
1642

1643
        if defaultFullfsync {
4✔
1644
                cfg.DB.Sqlite.PragmaOptions = append(
2✔
1645
                        cfg.DB.Sqlite.PragmaOptions, "fullfsync=true",
2✔
1646
                )
2✔
1647
        }
2✔
1648

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

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

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

1674
        if err := cfg.Gossip.Parse(); err != nil {
2✔
1675
                return nil, mkErr("error parsing gossip syncer: %v", err)
×
1676
        }
×
1677

1678
        // If the experimental protocol options specify any protocol messages
1679
        // that we want to handle as custom messages, set them now.
1680
        customMsg := cfg.ProtocolOptions.CustomMessageOverrides()
2✔
1681

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

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

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

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

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

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

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

1770
        // All good, return the sanitized result.
1771
        return &cfg, nil
2✔
1772
}
1773

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

1783
// ImplementationConfig returns the configuration of what actual implementations
1784
// should be used when creating the main lnd instance.
1785
func (c *Config) ImplementationConfig(
1786
        interceptor signal.Interceptor) *ImplementationCfg {
2✔
1787

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

1808
        defaultImpl := NewDefaultWalletImpl(c, ltndLog, interceptor, false)
2✔
1809
        return &ImplementationCfg{
2✔
1810
                GrpcRegistrar:       defaultImpl,
2✔
1811
                RestRegistrar:       defaultImpl,
2✔
1812
                ExternalValidator:   defaultImpl,
2✔
1813
                DatabaseBuilder:     NewDefaultDatabaseBuilder(c, ltndLog),
2✔
1814
                WalletConfigBuilder: defaultImpl,
2✔
1815
                ChainControlBuilder: defaultImpl,
2✔
1816
        }
2✔
1817
}
1818

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

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

1837
                path = strings.Replace(path, "~", homeDir, 1)
×
1838
        }
1839

1840
        // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
1841
        // but the variables can still be expanded via POSIX-style $VARIABLE.
1842
        return filepath.Clean(os.ExpandEnv(path))
2✔
1843
}
1844

1845
func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
1846
        netParams chainreg.BitcoinNetParams) error {
2✔
1847

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

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

1865
                // Set the daemon name for displaying proper errors.
1866
                daemonName = btcdBackendName
×
1867
                confDir = conf.Dir
×
1868
                confFileBase = btcdBackendName
×
1869

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

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

1889
                // Ensure that if the estimate mode is set, that it is a legal
1890
                // value.
1891
                if conf.EstimateMode != "" {
4✔
1892
                        err := checkEstimateMode(conf.EstimateMode)
2✔
1893
                        if err != nil {
2✔
1894
                                return err
×
1895
                        }
×
1896
                }
1897

1898
                // Set the daemon name for displaying proper errors.
1899
                daemonName = bitcoindBackendName
2✔
1900
                confDir = conf.Dir
2✔
1901
                confFile = conf.ConfigPath
2✔
1902
                confFileBase = BitcoinChainName
2✔
1903

2✔
1904
                // Resolves environment variable references in RPCUser
2✔
1905
                // and RPCPass fields.
2✔
1906
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
2✔
1907
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
2✔
1908

2✔
1909
                // Check that cookie and credentials don't contradict each
2✔
1910
                // other.
2✔
1911
                if (conf.RPCUser != "" || conf.RPCPass != "") &&
2✔
1912
                        conf.RPCCookie != "" {
2✔
1913

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

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

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

1936
                if conf.RPCUser != "" && conf.RPCPass != "" {
4✔
1937
                        // If all of RPCUser, RPCPass, ZMQBlockHost, and
2✔
1938
                        // ZMQTxHost are set, we assume those parameters are
2✔
1939
                        // good to use.
2✔
1940
                        if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
3✔
1941
                                return nil
1✔
1942
                        }
1✔
1943

1944
                        // If RPCUser and RPCPass are set and RPCPolling is
1945
                        // enabled, we assume the parameters are good to use.
1946
                        if conf.RPCPolling {
2✔
1947
                                return nil
1✔
1948
                        }
1✔
1949
                }
1950

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

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

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

1971
        fmt.Println("Attempting automatic RPC configuration to " + daemonName)
×
1972

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

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

2000
        fmt.Printf("Automatically obtained %v's RPC credentials\n", daemonName)
×
2001
        return nil
×
2002
}
2003

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

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

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

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

2048
                return defaultValue
2✔
2049

2050
        case reEnvVarWithBrackets.MatchString(value):
×
2051
                matches := reEnvVarWithBrackets.FindStringSubmatch(value)
×
2052
                envVariable := matches[1]
×
2053
                envValue := os.Getenv(envVariable)
×
2054

×
2055
                return envValue
×
2056

2057
        case reEnvVar.MatchString(value):
3✔
2058
                matches := reEnvVar.FindStringSubmatch(value)
3✔
2059
                envVariable := matches[1]
3✔
2060
                envValue := os.Getenv(envVariable)
3✔
2061

3✔
2062
                return envValue
3✔
2063
        }
2064

2065
        return value
3✔
2066
}
2067

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

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

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

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

2111
        return supplyEnvValue(string(userSubmatches[1])),
×
2112
                supplyEnvValue(string(passSubmatches[1])), nil
×
2113
}
2114

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

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

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

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

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

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

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

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

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

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

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

2237
        return supplyEnvValue(string(userSubmatches[1])),
×
2238
                supplyEnvValue(string(passSubmatches[1])),
×
2239
                zmqBlockHost, zmqTxHost, nil
×
2240
}
2241

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

2250
        return nil
1✔
2251
}
2252

2253
// checkEstimateMode ensures that the provided estimate mode is legal.
2254
func checkEstimateMode(estimateMode string) error {
2✔
2255
        for _, mode := range bitcoindEstimateModes {
4✔
2256
                if estimateMode == mode {
4✔
2257
                        return nil
2✔
2258
                }
2✔
2259
        }
2260

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

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

3✔
2272
        result := make(map[string]string)
3✔
2273

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

3✔
2279
        // redact is the helper function that redacts sensitive values like
3✔
2280
        // passwords.
3✔
2281
        redact := func(key, value string) string {
312✔
2282
                sensitiveKeySuffixes := []string{
309✔
2283
                        "pass",
309✔
2284
                        "password",
309✔
2285
                        "dsn",
309✔
2286
                }
309✔
2287
                for _, suffix := range sensitiveKeySuffixes {
1,225✔
2288
                        if strings.HasSuffix(key, suffix) {
923✔
2289
                                return "[redacted]"
7✔
2290
                        }
7✔
2291
                }
2292

2293
                return value
304✔
2294
        }
2295

2296
        // printConfig is the helper function that goes into nested structs
2297
        // recursively. Because we call it recursively, we need to declare it
2298
        // before we define it.
2299
        var printConfig func(reflect.Value, string)
3✔
2300
        printConfig = func(obj reflect.Value, prefix string) {
66✔
2301
                // Turn struct pointers into the actual struct, so we can
63✔
2302
                // iterate over the fields as we would with a struct value.
63✔
2303
                if obj.Kind() == reflect.Ptr {
119✔
2304
                        obj = obj.Elem()
56✔
2305
                }
56✔
2306

2307
                // Abort on nil values.
2308
                if !obj.IsValid() {
75✔
2309
                        return
12✔
2310
                }
12✔
2311

2312
                // Loop over all fields of the struct and inspect the type.
2313
                for i := 0; i < obj.NumField(); i++ {
437✔
2314
                        field := obj.Field(i)
386✔
2315
                        fieldType := obj.Type().Field(i)
386✔
2316

386✔
2317
                        longName := fieldType.Tag.Get("long")
386✔
2318
                        namespace := fieldType.Tag.Get("namespace")
386✔
2319
                        group := fieldType.Tag.Get("group")
386✔
2320
                        hidden := fieldType.Tag.Get("hidden")
386✔
2321

386✔
2322
                        switch {
386✔
2323
                        // We have a long name defined, this is a config value.
2324
                        case longName != "":
309✔
2325
                                key := longName
309✔
2326
                                if prefix != "" {
522✔
2327
                                        key = prefix + "." + key
213✔
2328
                                }
213✔
2329

2330
                                // Add the value directly to the flattened map.
2331
                                result[key] = redact(key, fmt.Sprintf(
309✔
2332
                                        "%v", field.Interface(),
309✔
2333
                                ))
309✔
2334

309✔
2335
                                // If there's a hidden flag, it's deprecated.
309✔
2336
                                if hidden == "true" && !field.IsZero() {
310✔
2337
                                        deprecated[key] = struct{}{}
1✔
2338
                                }
1✔
2339

2340
                        // We have no long name but a namespace, this is a
2341
                        // nested struct.
2342
                        case longName == "" && namespace != "":
56✔
2343
                                key := namespace
56✔
2344
                                if prefix != "" {
73✔
2345
                                        key = prefix + "." + key
17✔
2346
                                }
17✔
2347

2348
                                printConfig(field, key)
56✔
2349

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

2356
                        // Anonymous means embedded struct. We need to recurse
2357
                        // into it but without adding anything to the prefix.
2358
                        case fieldType.Anonymous:
7✔
2359
                                printConfig(field, prefix)
7✔
2360

2361
                        default:
19✔
2362
                                continue
19✔
2363
                        }
2364
                }
2365
        }
2366

2367
        // Turn the whole config struct into a flat map.
2368
        printConfig(reflect.ValueOf(cfg), "")
3✔
2369

3✔
2370
        return result, deprecated, nil
3✔
2371
}
2372

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

2381
        for k := range deprecated {
2✔
2382
                ltndLog.Warnf("Config '%s' is deprecated, please remove it", k)
×
2383
        }
×
2384
}
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