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

lightningnetwork / lnd / 12456011944

22 Dec 2024 04:45PM UTC coverage: 58.556% (-0.04%) from 58.598%
12456011944

Pull #9232

github

Abdulkbk
chanbackup: test archiving chan backups
Pull Request #9232: chanbackup: archive old channel backups

46 of 58 new or added lines in 2 files covered. (79.31%)

324 existing lines in 30 files now uncovered.

134919 of 230410 relevant lines covered (58.56%)

19202.13 hits per line

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

51.8
/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
        // defaultDisableBackupArchive indicates whether to disable archiving of
249
        // channel backups. When false (default), old backups are archived to a
250
        // designated location. When true, old backups are simply deleted or
251
        // replaced.
252
        defaultDisableBackupArchive = false
253
)
254

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

264
        // DefaultConfigFile is the default full path of lnd's configuration
265
        // file.
266
        DefaultConfigFile = filepath.Join(DefaultLndDir, lncfg.DefaultConfigFilename)
267

268
        defaultDataDir = filepath.Join(DefaultLndDir, defaultDataDirname)
269
        defaultLogDir  = filepath.Join(DefaultLndDir, defaultLogDirname)
270

271
        defaultTowerDir = filepath.Join(defaultDataDir, defaultTowerSubDirname)
272

273
        defaultTLSCertPath    = filepath.Join(DefaultLndDir, defaultTLSCertFilename)
274
        defaultTLSKeyPath     = filepath.Join(DefaultLndDir, defaultTLSKeyFilename)
275
        defaultLetsEncryptDir = filepath.Join(DefaultLndDir, defaultLetsEncryptDirname)
276

277
        defaultBtcdDir         = btcutil.AppDataDir(btcdBackendName, false)
278
        defaultBtcdRPCCertFile = filepath.Join(defaultBtcdDir, "rpc.cert")
279

280
        defaultBitcoindDir = btcutil.AppDataDir(BitcoinChainName, false)
281

282
        defaultTorSOCKS   = net.JoinHostPort("localhost", strconv.Itoa(defaultTorSOCKSPort))
283
        defaultTorDNS     = net.JoinHostPort(defaultTorDNSHost, strconv.Itoa(defaultTorDNSPort))
284
        defaultTorControl = net.JoinHostPort("localhost", strconv.Itoa(defaultTorControlPort))
285

286
        // bitcoindEsimateModes defines all the legal values for bitcoind's
287
        // estimatesmartfee RPC call.
288
        defaultBitcoindEstimateMode = "CONSERVATIVE"
289
        bitcoindEstimateModes       = [2]string{"ECONOMICAL", defaultBitcoindEstimateMode}
290

291
        defaultPrunedNodeMaxPeers = 4
292
)
293

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

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

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

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

326
        LetsEncryptDir    string `long:"letsencryptdir" description:"The directory to store Let's Encrypt certificates within"`
327
        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."`
328
        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."`
329

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

355
        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"`
356

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

362
        Pprof *lncfg.Pprof `group:"Pprof" namespace:"pprof"`
363

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

369
        DisableBackupArchive bool `long:"disable-backup-archive" description:"If set to true, channel backups will be deleted or replaced rather than being archived to a separate location."`
370

371
        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"`
372

373
        Bitcoin      *lncfg.Chain    `group:"Bitcoin" namespace:"bitcoin"`
374
        BtcdMode     *lncfg.Btcd     `group:"btcd" namespace:"btcd"`
375
        BitcoindMode *lncfg.Bitcoind `group:"bitcoind" namespace:"bitcoind"`
376
        NeutrinoMode *lncfg.Neutrino `group:"neutrino" namespace:"neutrino"`
377

378
        BlockCacheSize uint64 `long:"blockcachesize" description:"The maximum capacity of the block cache"`
379

380
        Autopilot *lncfg.AutoPilot `group:"Autopilot" namespace:"autopilot"`
381

382
        Tor *lncfg.Tor `group:"Tor" namespace:"tor"`
383

384
        SubRPCServers *subRPCServerConfigs `group:"subrpc"`
385

386
        Hodl *hodl.Config `group:"hodl" namespace:"hodl"`
387

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

390
        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."`
391
        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."`
392
        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."`
393

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

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

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

410
        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."`
411

412
        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."`
413

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

447
        net tor.Net
448

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

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

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

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

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

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

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

463
        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."`
464

465
        Fee *lncfg.Fee `group:"fee" namespace:"fee"`
466

467
        Invoices *lncfg.Invoices `group:"invoices" namespace:"invoices"`
468

469
        Routing *lncfg.Routing `group:"routing" namespace:"routing"`
470

471
        Gossip *lncfg.Gossip `group:"gossip" namespace:"gossip"`
472

473
        Workers *lncfg.Workers `group:"workers" namespace:"workers"`
474

475
        Caches *lncfg.Caches `group:"caches" namespace:"caches"`
476

477
        Prometheus lncfg.Prometheus `group:"prometheus" namespace:"prometheus"`
478

479
        WtClient *lncfg.WtClient `group:"wtclient" namespace:"wtclient"`
480

481
        Watchtower *lncfg.Watchtower `group:"watchtower" namespace:"watchtower"`
482

483
        ProtocolOptions *lncfg.ProtocolOptions `group:"protocol" namespace:"protocol"`
484

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

487
        HealthChecks *lncfg.HealthCheckConfig `group:"healthcheck" namespace:"healthcheck"`
488

489
        DB *lncfg.DB `group:"db" namespace:"db"`
490

491
        Cluster *lncfg.Cluster `group:"cluster" namespace:"cluster"`
492

493
        RPCMiddleware *lncfg.RPCMiddleware `group:"rpcmiddleware" namespace:"rpcmiddleware"`
494

495
        RemoteSigner *lncfg.RemoteSigner `group:"remotesigner" namespace:"remotesigner"`
496

497
        Sweeper *lncfg.Sweeper `group:"sweeper" namespace:"sweeper"`
498

499
        Htlcswitch *lncfg.Htlcswitch `group:"htlcswitch" namespace:"htlcswitch"`
500

501
        GRPC *GRPCConfig `group:"grpc" namespace:"grpc"`
502

503
        // SubLogMgr is the root logger that all the daemon's subloggers are
504
        // hooked up to.
505
        SubLogMgr  *build.SubLoggerManager
506
        LogRotator *build.RotatingLogWriter
507
        LogConfig  *build.LogConfig `group:"logging" namespace:"logging"`
508

509
        // networkDir is the path to the directory of the currently active
510
        // network. This path will hold the files related to each different
511
        // network.
512
        networkDir string
513

514
        // ActiveNetParams contains parameters of the target chain.
515
        ActiveNetParams chainreg.BitcoinNetParams
516

517
        // Estimator is used to estimate routing probabilities.
518
        Estimator routing.Estimator
519

520
        // Dev specifies configs used for integration tests, which is always
521
        // empty if not built with `integration` flag.
522
        Dev *lncfg.DevConfig `group:"dev" namespace:"dev"`
523

524
        // HTTPHeaderTimeout is the maximum duration that the server will wait
525
        // before timing out reading the headers of an HTTP request.
526
        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."`
527
}
528

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

542
        // ServerPingTimeout is the duration the server waits after having
543
        // pinged for keepalive check, and if no activity is seen even after
544
        // that the connection is closed.
545
        ServerPingTimeout time.Duration `long:"server-ping-timeout" description:"How long the server waits for the response from the client for the keepalive ping response."`
546

547
        // ClientPingMinWait is the minimum amount of time a client should wait
548
        // before sending a keepalive ping.
549
        ClientPingMinWait time.Duration `long:"client-ping-min-wait" description:"The minimum amount of time the client should wait before sending a keepalive ping."`
550

551
        // ClientAllowPingWithoutStream specifies whether pings from the client
552
        // are allowed even if there are no active gRPC streams. This might be
553
        // useful to keep the underlying HTTP/2 connection open for future
554
        // requests.
555
        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."`
556
}
557

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

3✔
608
                Fee: &lncfg.Fee{
3✔
609
                        MinUpdateTimeout: lncfg.DefaultMinUpdateTimeout,
3✔
610
                        MaxUpdateTimeout: lncfg.DefaultMaxUpdateTimeout,
3✔
611
                },
3✔
612

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

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

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

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

2✔
788
                configFilePath = filepath.Join(
2✔
789
                        configFileDir, lncfg.DefaultConfigFilename,
2✔
790
                )
2✔
791

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

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

×
813
                        return nil, err
×
814
                }
×
815

816
                configFileError = err
2✔
817
        }
818

819
        // Finally, parse the remaining command line options again to ensure
820
        // they take precedence.
821
        flagParser := flags.NewParser(&cfg, flags.Default)
2✔
822
        if _, err := flagParser.Parse(); err != nil {
2✔
823
                return nil, err
×
824
        }
×
825

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

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

×
842
                return nil, err
×
843
        }
×
844
        if err != nil {
2✔
845
                // The log subsystem might not yet be initialized. But we still
×
846
                // try to log the error there since some packaging solutions
×
847
                // might only look at the log and not stdout/stderr.
×
848
                ltndLog.Warnf("Error validating config: %v", err)
×
849

×
850
                return nil, err
×
851
        }
×
852

853
        // Warn about missing config file only after all other configuration is
854
        // done. This prevents the warning on help messages and invalid options.
855
        // Note this should go directly before the return.
856
        if configFileError != nil {
4✔
857
                ltndLog.Warnf("%v", configFileError)
2✔
858
        }
2✔
859

860
        // Finally, log warnings for deprecated config options if they are set.
861
        logWarningsForDeprecation(*cleanCfg)
2✔
862

2✔
863
        return cleanCfg, nil
2✔
864
}
865

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

2✔
872
        // Special show command to list supported subsystems and exit.
2✔
873
        if cfg.DebugLevel == "show" {
4✔
874
                subLogMgr := build.NewSubLoggerManager()
2✔
875

2✔
876
                // Initialize logging at the default logging level.
2✔
877
                SetupLoggers(subLogMgr, interceptor)
2✔
878

2✔
879
                fmt.Println("Supported subsystems",
2✔
880
                        subLogMgr.SupportedSubsystems())
2✔
881
                os.Exit(0)
2✔
882
        }
2✔
883

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

2✔
896
                // If the watchtower's directory is set to the default, i.e. the
2✔
897
                // user has not requested a different location, we'll move the
2✔
898
                // location to be relative to the specified lnd directory.
2✔
899
                if cfg.Watchtower.TowerDir == defaultTowerDir {
4✔
900
                        cfg.Watchtower.TowerDir = filepath.Join(
2✔
901
                                cfg.DataDir, defaultTowerSubDirname,
2✔
902
                        )
2✔
903
                }
2✔
904
        }
905

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

924
                        str := "Failed to create lnd directory '%s': %v"
×
925
                        return mkErr(str, dir, err)
×
926
                }
927

928
                return nil
2✔
929
        }
930

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

940
                long, ok := fieldName.Tag.Lookup("long")
2✔
941
                if !ok {
2✔
942
                        str := "field %s does not have a long tag"
×
943
                        return false, mkErr(str, field)
×
944
                }
×
945

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

2✔
963
                return (fileOption != nil && fileOption.IsSet()) ||
2✔
964
                                (fileOptionNested != nil && fileOptionNested.IsSet()) ||
2✔
965
                                (flagOption != nil && flagOption.IsSet()) ||
2✔
966
                                (flagOptionNested != nil && flagOptionNested.IsSet()),
2✔
967
                        nil
2✔
968
        }
969

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

2✔
995
        // Ensure that the user didn't attempt to specify negative values for
2✔
996
        // any of the autopilot params.
2✔
997
        if cfg.Autopilot.MaxChannels < 0 {
2✔
998
                str := "autopilot.maxchannels must be non-negative"
×
999

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

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

×
1010
                return nil, mkErr(str)
×
1011
        }
×
1012
        if cfg.Autopilot.MaxChannelSize < 0 {
2✔
1013
                str := "autopilot.maxchansize must be non-negative"
×
1014

×
1015
                return nil, mkErr(str)
×
1016
        }
×
1017
        if cfg.Autopilot.MinConfs < 0 {
2✔
1018
                str := "autopilot.minconfs must be non-negative"
×
1019

×
1020
                return nil, mkErr(str)
×
1021
        }
×
1022
        if cfg.Autopilot.ConfTarget < 1 {
2✔
1023
                str := "autopilot.conftarget must be positive"
×
1024

×
1025
                return nil, mkErr(str)
×
1026
        }
×
1027

1028
        // Ensure that the specified values for the min and max channel size
1029
        // are within the bounds of the normal chan size constraints.
1030
        if cfg.Autopilot.MinChannelSize < int64(funding.MinChanFundingSize) {
2✔
1031
                cfg.Autopilot.MinChannelSize = int64(funding.MinChanFundingSize)
×
1032
        }
×
1033
        if cfg.Autopilot.MaxChannelSize > int64(MaxFundingAmount) {
2✔
1034
                cfg.Autopilot.MaxChannelSize = int64(MaxFundingAmount)
×
1035
        }
×
1036

1037
        if _, err := validateAtplCfg(cfg.Autopilot); err != nil {
2✔
1038
                return nil, mkErr("error validating autopilot: %v", err)
×
1039
        }
×
1040

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

1054
        // Ensure that the user specified values for the min and max channel
1055
        // size make sense.
1056
        if cfg.MaxChanSize < cfg.MinChanSize {
2✔
1057
                return nil, mkErr("invalid channel size parameters: "+
×
1058
                        "max channel size %v, must be no less than min chan "+
×
1059
                        "size %v", cfg.MaxChanSize, cfg.MinChanSize,
×
1060
                )
×
1061
        }
×
1062

1063
        // Don't allow superfluous --maxchansize greater than
1064
        // BOLT 02 soft-limit for non-wumbo channel
1065
        if !cfg.ProtocolOptions.Wumbo() &&
2✔
1066
                cfg.MaxChanSize > int64(MaxFundingAmount) {
2✔
1067

×
1068
                return nil, mkErr("invalid channel size parameters: "+
×
1069
                        "maximum channel size %v is greater than maximum "+
×
1070
                        "non-wumbo channel size %v", cfg.MaxChanSize,
×
1071
                        MaxFundingAmount,
×
1072
                )
×
1073
        }
×
1074

1075
        // Ensure that the amount data for revoked commitment transactions is
1076
        // stored if the watchtower client is active.
1077
        if cfg.DB.NoRevLogAmtData && cfg.WtClient.Active {
2✔
1078
                return nil, mkErr("revocation log amount data must be stored " +
×
1079
                        "if the watchtower client is active")
×
1080
        }
×
1081

1082
        // Ensure a valid max channel fee allocation was set.
1083
        if cfg.MaxChannelFeeAllocation <= 0 || cfg.MaxChannelFeeAllocation > 1 {
2✔
1084
                return nil, mkErr("invalid max channel fee allocation: %v, "+
×
1085
                        "must be within (0, 1]", cfg.MaxChannelFeeAllocation)
×
1086
        }
×
1087

1088
        if cfg.MaxCommitFeeRateAnchors < 1 {
2✔
1089
                return nil, mkErr("invalid max commit fee rate anchors: %v, "+
×
1090
                        "must be at least 1 sat/vByte",
×
1091
                        cfg.MaxCommitFeeRateAnchors)
×
1092
        }
×
1093

1094
        // Validate the Tor config parameters.
1095
        socks, err := lncfg.ParseAddressString(
2✔
1096
                cfg.Tor.SOCKS, strconv.Itoa(defaultTorSOCKSPort),
2✔
1097
                cfg.net.ResolveTCPAddr,
2✔
1098
        )
2✔
1099
        if err != nil {
2✔
1100
                return nil, err
×
1101
        }
×
1102
        cfg.Tor.SOCKS = socks.String()
2✔
1103

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

1117
        control, err := lncfg.ParseAddressString(
2✔
1118
                cfg.Tor.Control, strconv.Itoa(defaultTorControlPort),
2✔
1119
                cfg.net.ResolveTCPAddr,
2✔
1120
        )
2✔
1121
        if err != nil {
2✔
1122
                return nil, mkErr("error parsing tor control address: %v", err)
×
1123
        }
×
1124
        cfg.Tor.Control = control.String()
2✔
1125

2✔
1126
        // Ensure that tor socks host:port is not equal to tor control
2✔
1127
        // host:port. This would lead to lnd not starting up properly.
2✔
1128
        if cfg.Tor.SOCKS == cfg.Tor.Control {
2✔
1129
                str := "tor.socks and tor.control can not us the same host:port"
×
1130

×
1131
                return nil, mkErr(str)
×
1132
        }
×
1133

1134
        switch {
2✔
1135
        case cfg.Tor.V2 && cfg.Tor.V3:
×
1136
                return nil, mkErr("either tor.v2 or tor.v3 can be set, " +
×
1137
                        "but not both")
×
1138
        case cfg.DisableListen && (cfg.Tor.V2 || cfg.Tor.V3):
×
1139
                return nil, mkErr("listening must be enabled when enabling " +
×
1140
                        "inbound connections over Tor")
×
1141
        }
1142

1143
        if cfg.Tor.PrivateKeyPath == "" {
4✔
1144
                switch {
2✔
1145
                case cfg.Tor.V2:
×
1146
                        cfg.Tor.PrivateKeyPath = filepath.Join(
×
1147
                                lndDir, defaultTorV2PrivateKeyFilename,
×
1148
                        )
×
1149
                case cfg.Tor.V3:
×
1150
                        cfg.Tor.PrivateKeyPath = filepath.Join(
×
1151
                                lndDir, defaultTorV3PrivateKeyFilename,
×
1152
                        )
×
1153
                }
1154
        }
1155

1156
        if cfg.Tor.WatchtowerKeyPath == "" {
4✔
1157
                switch {
2✔
1158
                case cfg.Tor.V2:
×
1159
                        cfg.Tor.WatchtowerKeyPath = filepath.Join(
×
1160
                                cfg.Watchtower.TowerDir,
×
1161
                                defaultTorV2PrivateKeyFilename,
×
1162
                        )
×
1163
                case cfg.Tor.V3:
×
1164
                        cfg.Tor.WatchtowerKeyPath = filepath.Join(
×
1165
                                cfg.Watchtower.TowerDir,
×
1166
                                defaultTorV3PrivateKeyFilename,
×
1167
                        )
×
1168
                }
1169
        }
1170

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

1185
        if cfg.DisableListen && cfg.NAT {
2✔
1186
                return nil, mkErr("NAT traversal cannot be used when " +
×
1187
                        "listening is disabled")
×
1188
        }
×
1189
        if cfg.NAT && len(cfg.ExternalHosts) != 0 {
2✔
1190
                return nil, mkErr("NAT support and externalhosts are " +
×
1191
                        "mutually exclusive, only one should be selected")
×
1192
        }
×
1193

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

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

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

1245
                if len(cfg.Bitcoin.SigNetSeedNode) > 0 {
×
1246
                        sigNetSeeds = make([]chaincfg.DNSSeed, len(
×
1247
                                cfg.Bitcoin.SigNetSeedNode,
×
1248
                        ))
×
1249
                        for idx, seed := range cfg.Bitcoin.SigNetSeedNode {
×
1250
                                sigNetSeeds[idx] = chaincfg.DNSSeed{
×
1251
                                        Host:         seed,
×
1252
                                        HasFiltering: false,
×
1253
                                }
×
1254
                        }
×
1255
                }
1256

1257
                chainParams := chaincfg.CustomSignetParams(
×
1258
                        sigNetChallenge, sigNetSeeds,
×
1259
                )
×
1260
                cfg.ActiveNetParams.Params = &chainParams
×
1261
        }
1262
        if numNets > 1 {
2✔
1263
                str := "The mainnet, testnet, regtest, simnet and signet " +
×
1264
                        "params can't be used together -- choose one " +
×
1265
                        "of the five"
×
1266

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

1270
        // The target network must be provided, otherwise, we won't
1271
        // know how to initialize the daemon.
1272
        if numNets == 0 {
2✔
1273
                str := "either --bitcoin.mainnet, or bitcoin.testnet, " +
×
1274
                        "bitcoin.simnet, bitcoin.regtest or bitcoin.signet " +
×
1275
                        "must be specified"
×
1276

×
1277
                return nil, mkErr(str)
×
1278
        }
×
1279

1280
        err = cfg.Bitcoin.Validate(minTimeLockDelta, funding.MinBtcRemoteDelay)
2✔
1281
        if err != nil {
2✔
1282
                return nil, mkErr("error validating bitcoin params: %v", err)
×
1283
        }
×
1284

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

UNCOV
1300
                err := parseRPCParams(
×
UNCOV
1301
                        cfg.Bitcoin, cfg.BitcoindMode, cfg.ActiveNetParams,
×
UNCOV
1302
                )
×
UNCOV
1303
                if err != nil {
×
1304
                        return nil, mkErr("unable to load RPC "+
×
1305
                                "credentials for bitcoind: %v", err)
×
1306
                }
×
1307
        case neutrinoBackendName:
1✔
1308
                // No need to get RPC parameters.
1309

1310
        case "nochainbackend":
×
1311
                // Nothing to configure, we're running without any chain
1312
                // backend whatsoever (pure signing mode).
1313

1314
        default:
×
1315
                str := "only btcd, bitcoind, and neutrino mode " +
×
1316
                        "supported for bitcoin at this time"
×
1317

×
1318
                return nil, mkErr(str)
×
1319
        }
1320

1321
        cfg.Bitcoin.ChainDir = filepath.Join(
2✔
1322
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
2✔
1323
        )
2✔
1324

2✔
1325
        // Ensure that the user didn't attempt to specify negative values for
2✔
1326
        // any of the autopilot params.
2✔
1327
        if cfg.Autopilot.MaxChannels < 0 {
2✔
1328
                str := "autopilot.maxchannels must be non-negative"
×
1329

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

×
1335
                return nil, mkErr(str)
×
1336
        }
×
1337
        if cfg.Autopilot.MinChannelSize < 0 {
2✔
1338
                str := "autopilot.minchansize must be non-negative"
×
1339

×
1340
                return nil, mkErr(str)
×
1341
        }
×
1342
        if cfg.Autopilot.MaxChannelSize < 0 {
2✔
1343
                str := "autopilot.maxchansize must be non-negative"
×
1344

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

1348
        // Ensure that the specified values for the min and max channel size
1349
        // don't are within the bounds of the normal chan size constraints.
1350
        if cfg.Autopilot.MinChannelSize < int64(funding.MinChanFundingSize) {
2✔
1351
                cfg.Autopilot.MinChannelSize = int64(funding.MinChanFundingSize)
×
1352
        }
×
1353
        if cfg.Autopilot.MaxChannelSize > int64(MaxFundingAmount) {
2✔
1354
                cfg.Autopilot.MaxChannelSize = int64(MaxFundingAmount)
×
1355
        }
×
1356

1357
        // We'll now construct the network directory which will be where we
1358
        // store all the data specific to this chain/network.
1359
        cfg.networkDir = filepath.Join(
2✔
1360
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
2✔
1361
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
2✔
1362
        )
2✔
1363

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

1383
        towerDir := filepath.Join(
2✔
1384
                cfg.Watchtower.TowerDir, BitcoinChainName,
2✔
1385
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
2✔
1386
        )
2✔
1387

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

1406
        // Similarly, if a custom back up file path wasn't specified, then
1407
        // we'll update the file location to match our set network directory.
1408
        if cfg.BackupFilePath == "" {
4✔
1409
                cfg.BackupFilePath = filepath.Join(
2✔
1410
                        cfg.networkDir, chanbackup.DefaultBackupFileName,
2✔
1411
                )
2✔
1412
        }
2✔
1413

1414
        // Append the network type to the log directory so it is "namespaced"
1415
        // per network in the same fashion as the data directory.
1416
        cfg.LogDir = filepath.Join(
2✔
1417
                cfg.LogDir, BitcoinChainName,
2✔
1418
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
2✔
1419
        )
2✔
1420

2✔
1421
        if err := cfg.LogConfig.Validate(); err != nil {
2✔
1422
                return nil, mkErr("error validating logging config: %w", err)
×
1423
        }
×
1424

1425
        cfg.SubLogMgr = build.NewSubLoggerManager(build.NewDefaultLogHandlers(
2✔
1426
                cfg.LogConfig, cfg.LogRotator,
2✔
1427
        )...)
2✔
1428

2✔
1429
        // Initialize logging at the default logging level.
2✔
1430
        SetupLoggers(cfg.SubLogMgr, interceptor)
2✔
1431

2✔
1432
        if cfg.MaxLogFiles != 0 {
2✔
1433
                if cfg.LogConfig.File.MaxLogFiles !=
×
1434
                        build.DefaultMaxLogFiles {
×
1435

×
1436
                        return nil, mkErr("cannot set both maxlogfiles and "+
×
1437
                                "logging.file.max-files", err)
×
1438
                }
×
1439

1440
                cfg.LogConfig.File.MaxLogFiles = cfg.MaxLogFiles
×
1441
        }
1442
        if cfg.MaxLogFileSize != 0 {
2✔
1443
                if cfg.LogConfig.File.MaxLogFileSize !=
×
1444
                        build.DefaultMaxLogFileSize {
×
1445

×
1446
                        return nil, mkErr("cannot set both maxlogfilesize and "+
×
1447
                                "logging.file.max-file-size", err)
×
1448
                }
×
1449

1450
                cfg.LogConfig.File.MaxLogFileSize = cfg.MaxLogFileSize
×
1451
        }
1452

1453
        err = cfg.LogRotator.InitLogRotator(
2✔
1454
                cfg.LogConfig.File,
2✔
1455
                filepath.Join(cfg.LogDir, defaultLogFilename),
2✔
1456
        )
2✔
1457
        if err != nil {
2✔
1458
                str := "log rotation setup failed: %v"
×
1459
                return nil, mkErr(str, err)
×
1460
        }
×
1461

1462
        // Parse, validate, and set debug log level(s).
1463
        err = build.ParseAndSetDebugLevels(cfg.DebugLevel, cfg.SubLogMgr)
2✔
1464
        if err != nil {
2✔
1465
                str := "error parsing debug level: %v"
×
1466
                return nil, &lncfg.UsageError{Err: mkErr(str, err)}
×
1467
        }
×
1468

1469
        // At least one RPCListener is required. So listen on localhost per
1470
        // default.
1471
        if len(cfg.RawRPCListeners) == 0 {
2✔
1472
                addr := fmt.Sprintf("localhost:%d", defaultRPCPort)
×
1473
                cfg.RawRPCListeners = append(cfg.RawRPCListeners, addr)
×
1474
        }
×
1475

1476
        // Listen on localhost if no REST listeners were specified.
1477
        if len(cfg.RawRESTListeners) == 0 {
2✔
1478
                addr := fmt.Sprintf("localhost:%d", defaultRESTPort)
×
1479
                cfg.RawRESTListeners = append(cfg.RawRESTListeners, addr)
×
1480
        }
×
1481

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

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

1505
        // Add default port to all REST listener addresses if needed and remove
1506
        // duplicate addresses.
1507
        cfg.RESTListeners, err = lncfg.NormalizeAddresses(
2✔
1508
                cfg.RawRESTListeners, strconv.Itoa(defaultRESTPort),
2✔
1509
                cfg.net.ResolveTCPAddr,
2✔
1510
        )
2✔
1511
        if err != nil {
2✔
1512
                return nil, mkErr("error normalizing REST listen addrs: %v", err)
×
1513
        }
×
1514

1515
        switch {
2✔
1516
        // The no seed backup and auto unlock are mutually exclusive.
1517
        case cfg.NoSeedBackup && cfg.WalletUnlockPasswordFile != "":
×
1518
                return nil, mkErr("cannot set noseedbackup and " +
×
1519
                        "wallet-unlock-password-file at the same time")
×
1520

1521
        // The "allow-create" flag cannot be set without the auto unlock file.
1522
        case cfg.WalletUnlockAllowCreate && cfg.WalletUnlockPasswordFile == "":
×
1523
                return nil, mkErr("cannot set wallet-unlock-allow-create " +
×
1524
                        "without wallet-unlock-password-file")
×
1525

1526
        // If a password file was specified, we need it to exist.
1527
        case cfg.WalletUnlockPasswordFile != "" &&
1528
                !lnrpc.FileExists(cfg.WalletUnlockPasswordFile):
×
1529

×
1530
                return nil, mkErr("wallet unlock password file %s does "+
×
1531
                        "not exist", cfg.WalletUnlockPasswordFile)
×
1532
        }
1533

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

1546
        if cfg.DisableRest {
2✔
1547
                ltndLog.Infof("REST API is disabled!")
×
1548
                cfg.RESTListeners = nil
×
1549
        } else {
2✔
1550
                err = lncfg.EnforceSafeAuthentication(
2✔
1551
                        cfg.RESTListeners, !cfg.NoMacaroons, !cfg.DisableRestTLS,
2✔
1552
                )
2✔
1553
                if err != nil {
2✔
1554
                        return nil, mkErr("error enforcing safe "+
×
1555
                                "authentication on REST ports: %v", err)
×
1556
                }
×
1557
        }
1558

1559
        // Remove the listening addresses specified if listening is disabled.
1560
        if cfg.DisableListen {
4✔
1561
                ltndLog.Infof("Listening on the p2p interface is disabled!")
2✔
1562
                cfg.Listeners = nil
2✔
1563
                cfg.ExternalIPs = nil
2✔
1564
        } else {
4✔
1565

2✔
1566
                // Add default port to all listener addresses if needed and remove
2✔
1567
                // duplicate addresses.
2✔
1568
                cfg.Listeners, err = lncfg.NormalizeAddresses(
2✔
1569
                        cfg.RawListeners, strconv.Itoa(defaultPeerPort),
2✔
1570
                        cfg.net.ResolveTCPAddr,
2✔
1571
                )
2✔
1572
                if err != nil {
2✔
1573
                        return nil, mkErr("error normalizing p2p listen "+
×
1574
                                "addrs: %v", err)
×
1575
                }
×
1576

1577
                // Add default port to all external IP addresses if needed and remove
1578
                // duplicate addresses.
1579
                cfg.ExternalIPs, err = lncfg.NormalizeAddresses(
2✔
1580
                        cfg.RawExternalIPs, strconv.Itoa(defaultPeerPort),
2✔
1581
                        cfg.net.ResolveTCPAddr,
2✔
1582
                )
2✔
1583
                if err != nil {
2✔
1584
                        return nil, err
×
1585
                }
×
1586

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

1599
        // Ensure that the specified minimum backoff is below or equal to the
1600
        // maximum backoff.
1601
        if cfg.MinBackoff > cfg.MaxBackoff {
2✔
1602
                return nil, mkErr("maxbackoff must be greater than minbackoff")
×
1603
        }
×
1604

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

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

1630
                case strings.HasPrefix(option, "auto_vacuum="):
×
1631
                        defaultAutoVacuum = false
×
1632

1633
                case strings.HasPrefix(option, "fullfsync="):
×
1634
                        defaultFullfsync = false
×
1635

1636
                default:
×
1637
                }
1638
        }
1639

1640
        if defaultSynchronous {
4✔
1641
                cfg.DB.Sqlite.PragmaOptions = append(
2✔
1642
                        cfg.DB.Sqlite.PragmaOptions, "synchronous=full",
2✔
1643
                )
2✔
1644
        }
2✔
1645

1646
        if defaultAutoVacuum {
4✔
1647
                cfg.DB.Sqlite.PragmaOptions = append(
2✔
1648
                        cfg.DB.Sqlite.PragmaOptions, "auto_vacuum=incremental",
2✔
1649
                )
2✔
1650
        }
2✔
1651

1652
        if defaultFullfsync {
4✔
1653
                cfg.DB.Sqlite.PragmaOptions = append(
2✔
1654
                        cfg.DB.Sqlite.PragmaOptions, "fullfsync=true",
2✔
1655
                )
2✔
1656
        }
2✔
1657

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

1667
        // Clamp the ChannelCommitInterval so that commitment updates can still
1668
        // happen in a reasonable timeframe.
1669
        if cfg.ChannelCommitInterval > maxChannelCommitInterval {
2✔
1670
                return nil, mkErr("channel-commit-interval (%v) must be less "+
×
1671
                        "than %v", cfg.ChannelCommitInterval,
×
1672
                        maxChannelCommitInterval)
×
1673
        }
×
1674

1675
        // Limit PendingCommitInterval so we don't wait too long for the remote
1676
        // party to send back a revoke.
1677
        if cfg.PendingCommitInterval > maxPendingCommitInterval {
2✔
1678
                return nil, mkErr("pending-commit-interval (%v) must be less "+
×
1679
                        "than %v", cfg.PendingCommitInterval,
×
1680
                        maxPendingCommitInterval)
×
1681
        }
×
1682

1683
        if err := cfg.Gossip.Parse(); err != nil {
2✔
1684
                return nil, mkErr("error parsing gossip syncer: %v", err)
×
1685
        }
×
1686

1687
        // If the experimental protocol options specify any protocol messages
1688
        // that we want to handle as custom messages, set them now.
1689
        customMsg := cfg.ProtocolOptions.CustomMessageOverrides()
2✔
1690

2✔
1691
        // We can safely set our custom override values during startup because
2✔
1692
        // startup is blocked on config parsing.
2✔
1693
        if err := lnwire.SetCustomOverrides(customMsg); err != nil {
2✔
1694
                return nil, mkErr("custom-message: %v", err)
×
1695
        }
×
1696

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

1730
        // Don't allow both the old dust-threshold and the new
1731
        // channel-max-fee-exposure to be set.
1732
        if cfg.DustThreshold != 0 && cfg.MaxFeeExposure != 0 {
2✔
1733
                return nil, mkErr("cannot set both dust-threshold and " +
×
1734
                        "channel-max-fee-exposure")
×
1735
        }
×
1736

1737
        switch {
2✔
1738
        // Use the old dust-threshold as the max fee exposure if it is set and
1739
        // the new option is not.
1740
        case cfg.DustThreshold != 0:
×
1741
                cfg.MaxFeeExposure = cfg.DustThreshold
×
1742

1743
        // Use the default max fee exposure if the new option is not set and
1744
        // the old one is not set either.
1745
        case cfg.MaxFeeExposure == 0:
2✔
1746
                cfg.MaxFeeExposure = uint64(
2✔
1747
                        htlcswitch.DefaultMaxFeeExposure.ToSatoshis(),
2✔
1748
                )
2✔
1749
        }
1750

1751
        // Validate the subconfigs for workers, caches, and the tower client.
1752
        err = lncfg.Validate(
2✔
1753
                cfg.Workers,
2✔
1754
                cfg.Caches,
2✔
1755
                cfg.WtClient,
2✔
1756
                cfg.DB,
2✔
1757
                cfg.Cluster,
2✔
1758
                cfg.HealthChecks,
2✔
1759
                cfg.RPCMiddleware,
2✔
1760
                cfg.RemoteSigner,
2✔
1761
                cfg.Sweeper,
2✔
1762
                cfg.Htlcswitch,
2✔
1763
                cfg.Invoices,
2✔
1764
                cfg.Routing,
2✔
1765
                cfg.Pprof,
2✔
1766
        )
2✔
1767
        if err != nil {
2✔
1768
                return nil, err
×
1769
        }
×
1770

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

1779
        // All good, return the sanitized result.
1780
        return &cfg, nil
2✔
1781
}
1782

1783
// graphDatabaseDir returns the default directory where the local bolt graph db
1784
// files are stored.
1785
func (c *Config) graphDatabaseDir() string {
2✔
1786
        return filepath.Join(
2✔
1787
                c.DataDir, defaultGraphSubDirname,
2✔
1788
                lncfg.NormalizeNetwork(c.ActiveNetParams.Name),
2✔
1789
        )
2✔
1790
}
2✔
1791

1792
// ImplementationConfig returns the configuration of what actual implementations
1793
// should be used when creating the main lnd instance.
1794
func (c *Config) ImplementationConfig(
1795
        interceptor signal.Interceptor) *ImplementationCfg {
2✔
1796

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

1817
        defaultImpl := NewDefaultWalletImpl(c, ltndLog, interceptor, false)
2✔
1818
        return &ImplementationCfg{
2✔
1819
                GrpcRegistrar:       defaultImpl,
2✔
1820
                RestRegistrar:       defaultImpl,
2✔
1821
                ExternalValidator:   defaultImpl,
2✔
1822
                DatabaseBuilder:     NewDefaultDatabaseBuilder(c, ltndLog),
2✔
1823
                WalletConfigBuilder: defaultImpl,
2✔
1824
                ChainControlBuilder: defaultImpl,
2✔
1825
        }
2✔
1826
}
1827

1828
// CleanAndExpandPath expands environment variables and leading ~ in the
1829
// passed path, cleans the result, and returns it.
1830
// This function is taken from https://github.com/btcsuite/btcd
1831
func CleanAndExpandPath(path string) string {
2✔
1832
        if path == "" {
4✔
1833
                return ""
2✔
1834
        }
2✔
1835

1836
        // Expand initial ~ to OS specific home directory.
1837
        if strings.HasPrefix(path, "~") {
2✔
1838
                var homeDir string
×
1839
                u, err := user.Current()
×
1840
                if err == nil {
×
1841
                        homeDir = u.HomeDir
×
1842
                } else {
×
1843
                        homeDir = os.Getenv("HOME")
×
1844
                }
×
1845

1846
                path = strings.Replace(path, "~", homeDir, 1)
×
1847
        }
1848

1849
        // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
1850
        // but the variables can still be expanded via POSIX-style $VARIABLE.
1851
        return filepath.Clean(os.ExpandEnv(path))
2✔
1852
}
1853

1854
func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
1855
        netParams chainreg.BitcoinNetParams) error {
1✔
1856

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

1✔
1868
                // If both RPCUser and RPCPass are set, we assume those
1✔
1869
                // credentials are good to use.
1✔
1870
                if conf.RPCUser != "" && conf.RPCPass != "" {
2✔
1871
                        return nil
1✔
1872
                }
1✔
1873

1874
                // Set the daemon name for displaying proper errors.
1875
                daemonName = btcdBackendName
×
1876
                confDir = conf.Dir
×
1877
                confFileBase = btcdBackendName
×
1878

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

UNCOV
1886
        case *lncfg.Bitcoind:
×
UNCOV
1887
                // Ensure that if the ZMQ options are set, that they are not
×
UNCOV
1888
                // equal.
×
UNCOV
1889
                if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
×
UNCOV
1890
                        err := checkZMQOptions(
×
UNCOV
1891
                                conf.ZMQPubRawBlock, conf.ZMQPubRawTx,
×
UNCOV
1892
                        )
×
UNCOV
1893
                        if err != nil {
×
1894
                                return err
×
1895
                        }
×
1896
                }
1897

1898
                // Ensure that if the estimate mode is set, that it is a legal
1899
                // value.
UNCOV
1900
                if conf.EstimateMode != "" {
×
UNCOV
1901
                        err := checkEstimateMode(conf.EstimateMode)
×
UNCOV
1902
                        if err != nil {
×
1903
                                return err
×
1904
                        }
×
1905
                }
1906

1907
                // Set the daemon name for displaying proper errors.
UNCOV
1908
                daemonName = bitcoindBackendName
×
UNCOV
1909
                confDir = conf.Dir
×
UNCOV
1910
                confFile = conf.ConfigPath
×
UNCOV
1911
                confFileBase = BitcoinChainName
×
UNCOV
1912

×
UNCOV
1913
                // Resolves environment variable references in RPCUser
×
UNCOV
1914
                // and RPCPass fields.
×
UNCOV
1915
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
×
UNCOV
1916
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
×
UNCOV
1917

×
UNCOV
1918
                // Check that cookie and credentials don't contradict each
×
UNCOV
1919
                // other.
×
UNCOV
1920
                if (conf.RPCUser != "" || conf.RPCPass != "") &&
×
UNCOV
1921
                        conf.RPCCookie != "" {
×
1922

×
1923
                        return fmt.Errorf("please only provide either "+
×
1924
                                "%[1]v.rpccookie or %[1]v.rpcuser and "+
×
1925
                                "%[1]v.rpcpass", daemonName)
×
1926
                }
×
1927

1928
                // We convert the cookie into a user name and password.
UNCOV
1929
                if conf.RPCCookie != "" {
×
1930
                        cookie, err := os.ReadFile(conf.RPCCookie)
×
1931
                        if err != nil {
×
1932
                                return fmt.Errorf("cannot read cookie file: %w",
×
1933
                                        err)
×
1934
                        }
×
1935

1936
                        splitCookie := strings.Split(string(cookie), ":")
×
1937
                        if len(splitCookie) != 2 {
×
1938
                                return fmt.Errorf("cookie file has a wrong " +
×
1939
                                        "format")
×
1940
                        }
×
1941
                        conf.RPCUser = splitCookie[0]
×
1942
                        conf.RPCPass = splitCookie[1]
×
1943
                }
1944

UNCOV
1945
                if conf.RPCUser != "" && conf.RPCPass != "" {
×
UNCOV
1946
                        // If all of RPCUser, RPCPass, ZMQBlockHost, and
×
UNCOV
1947
                        // ZMQTxHost are set, we assume those parameters are
×
UNCOV
1948
                        // good to use.
×
UNCOV
1949
                        if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
×
UNCOV
1950
                                return nil
×
UNCOV
1951
                        }
×
1952

1953
                        // If RPCUser and RPCPass are set and RPCPolling is
1954
                        // enabled, we assume the parameters are good to use.
1955
                        if conf.RPCPolling {
×
1956
                                return nil
×
1957
                        }
×
1958
                }
1959

1960
                // If not all of the parameters are set, we'll assume the user
1961
                // did this unintentionally.
1962
                if conf.RPCUser != "" || conf.RPCPass != "" ||
×
1963
                        conf.ZMQPubRawBlock != "" || conf.ZMQPubRawTx != "" {
×
1964

×
1965
                        return fmt.Errorf("please set %[1]v.rpcuser and "+
×
1966
                                "%[1]v.rpcpass (or %[1]v.rpccookie) together "+
×
1967
                                "with %[1]v.zmqpubrawblock, %[1]v.zmqpubrawtx",
×
1968
                                daemonName)
×
1969
                }
×
1970
        }
1971

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

1980
        fmt.Println("Attempting automatic RPC configuration to " + daemonName)
×
1981

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

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

2009
        fmt.Printf("Automatically obtained %v's RPC credentials\n", daemonName)
×
2010
        return nil
×
2011
}
2012

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

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

8✔
2042
        // Regex for ${ENV_VAR:-DEFAULT} format.
8✔
2043
        var reEnvVarWithDefault = regexp.MustCompile(
8✔
2044
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*):-([\S]+)\}$`,
8✔
2045
        )
8✔
2046

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

2057
                return defaultValue
2✔
2058

2059
        case reEnvVarWithBrackets.MatchString(value):
×
2060
                matches := reEnvVarWithBrackets.FindStringSubmatch(value)
×
2061
                envVariable := matches[1]
×
2062
                envValue := os.Getenv(envVariable)
×
2063

×
2064
                return envValue
×
2065

2066
        case reEnvVar.MatchString(value):
3✔
2067
                matches := reEnvVar.FindStringSubmatch(value)
3✔
2068
                envVariable := matches[1]
3✔
2069
                envValue := os.Getenv(envVariable)
3✔
2070

3✔
2071
                return envValue
3✔
2072
        }
2073

2074
        return value
2✔
2075
}
2076

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

2089
        // With the file open extract the contents of the configuration file so
2090
        // we can attempt to locate the RPC credentials.
2091
        configContents, err := io.ReadAll(btcdConfigFile)
×
2092
        if err != nil {
×
2093
                return "", "", err
×
2094
        }
×
2095

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

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

2120
        return supplyEnvValue(string(userSubmatches[1])),
×
2121
                supplyEnvValue(string(passSubmatches[1])), nil
×
2122
}
2123

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

×
2131
        // First, we'll open up the bitcoind configuration file found at the
×
2132
        // target destination.
×
2133
        bitcoindConfigFile, err := os.Open(bitcoindConfigPath)
×
2134
        if err != nil {
×
2135
                return "", "", "", "", err
×
2136
        }
×
2137
        defer func() { _ = bitcoindConfigFile.Close() }()
×
2138

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

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

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

2189
        var chainDir string
×
2190
        switch networkName {
×
2191
        case "mainnet":
×
2192
                chainDir = ""
×
2193
        case "regtest", "testnet3", "signet":
×
2194
                chainDir = networkName
×
2195
        default:
×
2196
                return "", "", "", "", fmt.Errorf("unexpected networkname %v", networkName)
×
2197
        }
2198

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

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

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

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

2237
        if userSubmatches == nil {
×
2238
                return "", "", "", "", fmt.Errorf("unable to find rpcuser in " +
×
2239
                        "config")
×
2240
        }
×
2241
        if passSubmatches == nil {
×
2242
                return "", "", "", "", fmt.Errorf("unable to find rpcpassword " +
×
2243
                        "in config")
×
2244
        }
×
2245

2246
        return supplyEnvValue(string(userSubmatches[1])),
×
2247
                supplyEnvValue(string(passSubmatches[1])),
×
2248
                zmqBlockHost, zmqTxHost, nil
×
2249
}
2250

2251
// checkZMQOptions ensures that the provided addresses to use as the hosts for
2252
// ZMQ rawblock and rawtx notifications are different.
UNCOV
2253
func checkZMQOptions(zmqBlockHost, zmqTxHost string) error {
×
UNCOV
2254
        if zmqBlockHost == zmqTxHost {
×
2255
                return errors.New("zmqpubrawblock and zmqpubrawtx must be set " +
×
2256
                        "to different addresses")
×
2257
        }
×
2258

UNCOV
2259
        return nil
×
2260
}
2261

2262
// checkEstimateMode ensures that the provided estimate mode is legal.
UNCOV
2263
func checkEstimateMode(estimateMode string) error {
×
UNCOV
2264
        for _, mode := range bitcoindEstimateModes {
×
UNCOV
2265
                if estimateMode == mode {
×
UNCOV
2266
                        return nil
×
UNCOV
2267
                }
×
2268
        }
2269

2270
        return fmt.Errorf("estimatemode must be one of the following: %v",
×
2271
                bitcoindEstimateModes[:])
×
2272
}
2273

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

3✔
2281
        result := make(map[string]string)
3✔
2282

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

3✔
2288
        // redact is the helper function that redacts sensitive values like
3✔
2289
        // passwords.
3✔
2290
        redact := func(key, value string) string {
314✔
2291
                sensitiveKeySuffixes := []string{
311✔
2292
                        "pass",
311✔
2293
                        "password",
311✔
2294
                        "dsn",
311✔
2295
                }
311✔
2296
                for _, suffix := range sensitiveKeySuffixes {
1,233✔
2297
                        if strings.HasSuffix(key, suffix) {
929✔
2298
                                return "[redacted]"
7✔
2299
                        }
7✔
2300
                }
2301

2302
                return value
306✔
2303
        }
2304

2305
        // printConfig is the helper function that goes into nested structs
2306
        // recursively. Because we call it recursively, we need to declare it
2307
        // before we define it.
2308
        var printConfig func(reflect.Value, string)
3✔
2309
        printConfig = func(obj reflect.Value, prefix string) {
66✔
2310
                // Turn struct pointers into the actual struct, so we can
63✔
2311
                // iterate over the fields as we would with a struct value.
63✔
2312
                if obj.Kind() == reflect.Ptr {
119✔
2313
                        obj = obj.Elem()
56✔
2314
                }
56✔
2315

2316
                // Abort on nil values.
2317
                if !obj.IsValid() {
75✔
2318
                        return
12✔
2319
                }
12✔
2320

2321
                // Loop over all fields of the struct and inspect the type.
2322
                for i := 0; i < obj.NumField(); i++ {
439✔
2323
                        field := obj.Field(i)
388✔
2324
                        fieldType := obj.Type().Field(i)
388✔
2325

388✔
2326
                        longName := fieldType.Tag.Get("long")
388✔
2327
                        namespace := fieldType.Tag.Get("namespace")
388✔
2328
                        group := fieldType.Tag.Get("group")
388✔
2329
                        hidden := fieldType.Tag.Get("hidden")
388✔
2330

388✔
2331
                        switch {
388✔
2332
                        // We have a long name defined, this is a config value.
2333
                        case longName != "":
311✔
2334
                                key := longName
311✔
2335
                                if prefix != "" {
525✔
2336
                                        key = prefix + "." + key
214✔
2337
                                }
214✔
2338

2339
                                // Add the value directly to the flattened map.
2340
                                result[key] = redact(key, fmt.Sprintf(
311✔
2341
                                        "%v", field.Interface(),
311✔
2342
                                ))
311✔
2343

311✔
2344
                                // If there's a hidden flag, it's deprecated.
311✔
2345
                                if hidden == "true" && !field.IsZero() {
312✔
2346
                                        deprecated[key] = struct{}{}
1✔
2347
                                }
1✔
2348

2349
                        // We have no long name but a namespace, this is a
2350
                        // nested struct.
2351
                        case longName == "" && namespace != "":
56✔
2352
                                key := namespace
56✔
2353
                                if prefix != "" {
73✔
2354
                                        key = prefix + "." + key
17✔
2355
                                }
17✔
2356

2357
                                printConfig(field, key)
56✔
2358

2359
                        // Just a group means this is a dummy struct to house
2360
                        // multiple config values, the group name doesn't go
2361
                        // into the final field name.
2362
                        case longName == "" && group != "":
3✔
2363
                                printConfig(field, prefix)
3✔
2364

2365
                        // Anonymous means embedded struct. We need to recurse
2366
                        // into it but without adding anything to the prefix.
2367
                        case fieldType.Anonymous:
7✔
2368
                                printConfig(field, prefix)
7✔
2369

2370
                        default:
19✔
2371
                                continue
19✔
2372
                        }
2373
                }
2374
        }
2375

2376
        // Turn the whole config struct into a flat map.
2377
        printConfig(reflect.ValueOf(cfg), "")
3✔
2378

3✔
2379
        return result, deprecated, nil
3✔
2380
}
2381

2382
// logWarningsForDeprecation logs a warning if a deprecated config option is
2383
// set.
2384
func logWarningsForDeprecation(cfg Config) {
2✔
2385
        _, deprecated, err := configToFlatMap(cfg)
2✔
2386
        if err != nil {
2✔
2387
                ltndLog.Errorf("Convert configs to map: %v", err)
×
2388
        }
×
2389

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