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

lightningnetwork / lnd / 12412879685

19 Dec 2024 12:40PM UTC coverage: 58.744% (+0.09%) from 58.653%
12412879685

Pull #8754

github

ViktorTigerstrom
itest: wrap deriveCustomScopeAccounts at 80 chars

This commit fixes that word wrapping for the deriveCustomScopeAccounts
function docs, and ensures that it wraps at 80 characters or less.
Pull Request #8754: Add `Outbound` Remote Signer implementation

1858 of 2816 new or added lines in 47 files covered. (65.98%)

267 existing lines in 51 files now uncovered.

136038 of 231578 relevant lines covered (58.74%)

19020.65 hits per line

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

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

5
package lnd
6

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

285
        defaultPrunedNodeMaxPeers = 4
286
)
287

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

439
        net tor.Net
440

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

487
        // RemoteSigner defines how to connect to a remote signer node. If this
488
        // is enabled, the node acts as a watch-only node in a remote signer
489
        // setup.
490
        RemoteSigner *lncfg.RemoteSigner `group:"remotesigner" namespace:"remotesigner"`
491

492
        // WatchOnlyNode defines how to connect to a watch-only node. If this is
493
        // enabled, the node acts as a remote signer in a remote signer setup.
494
        WatchOnlyNode *lncfg.WatchOnlyNode `group:"watchonlynode" namespace:"watchonlynode"`
495

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3✔
785
                configFilePath = filepath.Join(
3✔
786
                        configFileDir, lncfg.DefaultConfigFilename,
3✔
787
                )
3✔
788

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

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

×
810
                        return nil, err
×
811
                }
×
812

813
                configFileError = err
3✔
814
        }
815

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

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

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

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

×
847
                return nil, err
×
848
        }
×
849

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

857
        // Finally, log warnings for deprecated config options if they are set.
858
        logWarningsForDeprecation(*cleanCfg)
3✔
859

3✔
860
        return cleanCfg, nil
3✔
861
}
862

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

3✔
869
        // Special show command to list supported subsystems and exit.
3✔
870
        if cfg.DebugLevel == "show" {
6✔
871
                subLogMgr := build.NewSubLoggerManager()
3✔
872

3✔
873
                // Initialize logging at the default logging level.
3✔
874
                SetupLoggers(subLogMgr, interceptor)
3✔
875

3✔
876
                fmt.Println("Supported subsystems",
3✔
877
                        subLogMgr.SupportedSubsystems())
3✔
878
                os.Exit(0)
3✔
879
        }
3✔
880

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

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

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

921
                        str := "Failed to create lnd directory '%s': %v"
×
922
                        return mkErr(str, dir, err)
×
923
                }
924

925
                return nil
3✔
926
        }
927

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

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

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

3✔
960
                return (fileOption != nil && fileOption.IsSet()) ||
3✔
961
                                (fileOptionNested != nil && fileOptionNested.IsSet()) ||
3✔
962
                                (flagOption != nil && flagOption.IsSet()) ||
3✔
963
                                (flagOptionNested != nil && flagOptionNested.IsSet()),
3✔
964
                        nil
3✔
965
        }
966

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

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

×
997
                return nil, mkErr(str)
×
998
        }
×
999
        if cfg.Autopilot.Allocation < 0 {
3✔
1000
                str := "autopilot.allocation must be non-negative"
×
1001

×
1002
                return nil, mkErr(str)
×
1003
        }
×
1004
        if cfg.Autopilot.MinChannelSize < 0 {
3✔
1005
                str := "autopilot.minchansize must be non-negative"
×
1006

×
1007
                return nil, mkErr(str)
×
1008
        }
×
1009
        if cfg.Autopilot.MaxChannelSize < 0 {
3✔
1010
                str := "autopilot.maxchansize must be non-negative"
×
1011

×
1012
                return nil, mkErr(str)
×
1013
        }
×
1014
        if cfg.Autopilot.MinConfs < 0 {
3✔
1015
                str := "autopilot.minconfs must be non-negative"
×
1016

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

×
1022
                return nil, mkErr(str)
×
1023
        }
×
1024

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

1034
        if _, err := validateAtplCfg(cfg.Autopilot); err != nil {
3✔
1035
                return nil, mkErr("error validating autopilot: %v", err)
×
1036
        }
×
1037

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

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

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

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

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

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

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

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

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

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

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

×
1128
                return nil, mkErr(str)
×
1129
        }
×
1130

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

1140
        if cfg.Tor.PrivateKeyPath == "" {
6✔
1141
                switch {
3✔
1142
                case cfg.Tor.V2:
×
1143
                        cfg.Tor.PrivateKeyPath = filepath.Join(
×
1144
                                lndDir, defaultTorV2PrivateKeyFilename,
×
1145
                        )
×
1146
                case cfg.Tor.V3:
×
1147
                        cfg.Tor.PrivateKeyPath = filepath.Join(
×
1148
                                lndDir, defaultTorV3PrivateKeyFilename,
×
1149
                        )
×
1150
                }
1151
        }
1152

1153
        if cfg.Tor.WatchtowerKeyPath == "" {
6✔
1154
                switch {
3✔
1155
                case cfg.Tor.V2:
×
1156
                        cfg.Tor.WatchtowerKeyPath = filepath.Join(
×
1157
                                cfg.Watchtower.TowerDir,
×
1158
                                defaultTorV2PrivateKeyFilename,
×
1159
                        )
×
1160
                case cfg.Tor.V3:
×
1161
                        cfg.Tor.WatchtowerKeyPath = filepath.Join(
×
1162
                                cfg.Watchtower.TowerDir,
×
1163
                                defaultTorV3PrivateKeyFilename,
×
1164
                        )
×
1165
                }
1166
        }
1167

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

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

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

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

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

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

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

×
1264
                return nil, mkErr(str)
×
1265
        }
×
1266

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

×
1274
                return nil, mkErr(str)
×
1275
        }
×
1276

1277
        err = cfg.Bitcoin.Validate(minTimeLockDelta, funding.MinBtcRemoteDelay)
3✔
1278
        if err != nil {
3✔
1279
                return nil, mkErr("error validating bitcoin params: %v", err)
×
1280
        }
×
1281

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

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

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

1311
        default:
×
1312
                str := "only btcd, bitcoind, and neutrino mode " +
×
1313
                        "supported for bitcoin at this time"
×
1314

×
1315
                return nil, mkErr(str)
×
1316
        }
1317

1318
        cfg.Bitcoin.ChainDir = filepath.Join(
3✔
1319
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
3✔
1320
        )
3✔
1321

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

×
1327
                return nil, mkErr(str)
×
1328
        }
×
1329
        if cfg.Autopilot.Allocation < 0 {
3✔
1330
                str := "autopilot.allocation must be non-negative"
×
1331

×
1332
                return nil, mkErr(str)
×
1333
        }
×
1334
        if cfg.Autopilot.MinChannelSize < 0 {
3✔
1335
                str := "autopilot.minchansize must be non-negative"
×
1336

×
1337
                return nil, mkErr(str)
×
1338
        }
×
1339
        if cfg.Autopilot.MaxChannelSize < 0 {
3✔
1340
                str := "autopilot.maxchansize must be non-negative"
×
1341

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

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

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

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

1380
        towerDir := filepath.Join(
3✔
1381
                cfg.Watchtower.TowerDir, BitcoinChainName,
3✔
1382
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
3✔
1383
        )
3✔
1384

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

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

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

3✔
1418
        if err := cfg.LogConfig.Validate(); err != nil {
3✔
1419
                return nil, mkErr("error validating logging config: %w", err)
×
1420
        }
×
1421

1422
        cfg.SubLogMgr = build.NewSubLoggerManager(build.NewDefaultLogHandlers(
3✔
1423
                cfg.LogConfig, cfg.LogRotator,
3✔
1424
        )...)
3✔
1425

3✔
1426
        // Initialize logging at the default logging level.
3✔
1427
        SetupLoggers(cfg.SubLogMgr, interceptor)
3✔
1428

3✔
1429
        if cfg.MaxLogFiles != 0 {
3✔
1430
                if cfg.LogConfig.File.MaxLogFiles !=
×
1431
                        build.DefaultMaxLogFiles {
×
1432

×
1433
                        return nil, mkErr("cannot set both maxlogfiles and "+
×
1434
                                "logging.file.max-files", err)
×
1435
                }
×
1436

1437
                cfg.LogConfig.File.MaxLogFiles = cfg.MaxLogFiles
×
1438
        }
1439
        if cfg.MaxLogFileSize != 0 {
3✔
1440
                if cfg.LogConfig.File.MaxLogFileSize !=
×
1441
                        build.DefaultMaxLogFileSize {
×
1442

×
1443
                        return nil, mkErr("cannot set both maxlogfilesize and "+
×
1444
                                "logging.file.max-file-size", err)
×
1445
                }
×
1446

1447
                cfg.LogConfig.File.MaxLogFileSize = cfg.MaxLogFileSize
×
1448
        }
1449

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

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

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

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

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

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

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

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

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

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

×
1527
                return nil, mkErr("wallet unlock password file %s does "+
×
1528
                        "not exist", cfg.WalletUnlockPasswordFile)
×
1529
        }
1530

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

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

1556
        // Remove the listening addresses specified if listening is disabled.
1557
        if cfg.DisableListen {
6✔
1558
                ltndLog.Infof("Listening on the p2p interface is disabled!")
3✔
1559
                cfg.Listeners = nil
3✔
1560
                cfg.ExternalIPs = nil
3✔
1561
        } else {
6✔
1562

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

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

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

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

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

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

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

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

1633
                default:
×
1634
                }
1635
        }
1636

1637
        if defaultSynchronous {
6✔
1638
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1639
                        cfg.DB.Sqlite.PragmaOptions, "synchronous=full",
3✔
1640
                )
3✔
1641
        }
3✔
1642

1643
        if defaultAutoVacuum {
6✔
1644
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1645
                        cfg.DB.Sqlite.PragmaOptions, "auto_vacuum=incremental",
3✔
1646
                )
3✔
1647
        }
3✔
1648

1649
        if defaultFullfsync {
6✔
1650
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1651
                        cfg.DB.Sqlite.PragmaOptions, "fullfsync=true",
3✔
1652
                )
3✔
1653
        }
3✔
1654

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

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

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

1680
        if err := cfg.Gossip.Parse(); err != nil {
3✔
1681
                return nil, mkErr("error parsing gossip syncer: %v", err)
×
1682
        }
×
1683

1684
        // If the experimental protocol options specify any protocol messages
1685
        // that we want to handle as custom messages, set them now.
1686
        customMsg := cfg.ProtocolOptions.CustomMessageOverrides()
3✔
1687

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

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

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

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

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

1748
        // Validate that the node isn't configured as both a remote signer and a
1749
        // watch-only node.
1750
        if cfg.RemoteSigner.Enable && cfg.WatchOnlyNode.Enable {
3✔
NEW
1751
                return nil, fmt.Errorf("cannot enable both the remotesigner " +
×
NEW
1752
                        "and watchonly mode simultaneously")
×
NEW
1753
        }
×
1754

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

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

1784
        // All good, return the sanitized result.
1785
        return &cfg, nil
3✔
1786
}
1787

1788
// graphDatabaseDir returns the default directory where the local bolt graph db
1789
// files are stored.
1790
func (c *Config) graphDatabaseDir() string {
3✔
1791
        return filepath.Join(
3✔
1792
                c.DataDir, defaultGraphSubDirname,
3✔
1793
                lncfg.NormalizeNetwork(c.ActiveNetParams.Name),
3✔
1794
        )
3✔
1795
}
3✔
1796

1797
// ImplementationConfig returns the configuration of what actual implementations
1798
// should be used when creating the main lnd instance.
1799
func (c *Config) ImplementationConfig(
1800
        interceptor signal.Interceptor) *ImplementationCfg {
3✔
1801

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

1822
        defaultImpl := NewDefaultWalletImpl(c, ltndLog, interceptor, false)
3✔
1823
        return &ImplementationCfg{
3✔
1824
                GrpcRegistrar:       defaultImpl,
3✔
1825
                RestRegistrar:       defaultImpl,
3✔
1826
                ExternalValidator:   defaultImpl,
3✔
1827
                DatabaseBuilder:     NewDefaultDatabaseBuilder(c, ltndLog),
3✔
1828
                WalletConfigBuilder: defaultImpl,
3✔
1829
                ChainControlBuilder: defaultImpl,
3✔
1830
        }
3✔
1831
}
1832

1833
// CleanAndExpandPath expands environment variables and leading ~ in the
1834
// passed path, cleans the result, and returns it.
1835
// This function is taken from https://github.com/btcsuite/btcd
1836
func CleanAndExpandPath(path string) string {
3✔
1837
        if path == "" {
6✔
1838
                return ""
3✔
1839
        }
3✔
1840

1841
        // Expand initial ~ to OS specific home directory.
1842
        if strings.HasPrefix(path, "~") {
3✔
1843
                var homeDir string
×
1844
                u, err := user.Current()
×
1845
                if err == nil {
×
1846
                        homeDir = u.HomeDir
×
1847
                } else {
×
1848
                        homeDir = os.Getenv("HOME")
×
1849
                }
×
1850

1851
                path = strings.Replace(path, "~", homeDir, 1)
×
1852
        }
1853

1854
        // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
1855
        // but the variables can still be expanded via POSIX-style $VARIABLE.
1856
        return filepath.Clean(os.ExpandEnv(path))
3✔
1857
}
1858

1859
func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
1860
        netParams chainreg.BitcoinNetParams) error {
2✔
1861

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

1✔
1873
                // If both RPCUser and RPCPass are set, we assume those
1✔
1874
                // credentials are good to use.
1✔
1875
                if conf.RPCUser != "" && conf.RPCPass != "" {
2✔
1876
                        return nil
1✔
1877
                }
1✔
1878

1879
                // Set the daemon name for displaying proper errors.
1880
                daemonName = btcdBackendName
×
1881
                confDir = conf.Dir
×
1882
                confFileBase = btcdBackendName
×
1883

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

1891
        case *lncfg.Bitcoind:
1✔
1892
                // Ensure that if the ZMQ options are set, that they are not
1✔
1893
                // equal.
1✔
1894
                if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
2✔
1895
                        err := checkZMQOptions(
1✔
1896
                                conf.ZMQPubRawBlock, conf.ZMQPubRawTx,
1✔
1897
                        )
1✔
1898
                        if err != nil {
1✔
1899
                                return err
×
1900
                        }
×
1901
                }
1902

1903
                // Ensure that if the estimate mode is set, that it is a legal
1904
                // value.
1905
                if conf.EstimateMode != "" {
2✔
1906
                        err := checkEstimateMode(conf.EstimateMode)
1✔
1907
                        if err != nil {
1✔
1908
                                return err
×
1909
                        }
×
1910
                }
1911

1912
                // Set the daemon name for displaying proper errors.
1913
                daemonName = bitcoindBackendName
1✔
1914
                confDir = conf.Dir
1✔
1915
                confFile = conf.ConfigPath
1✔
1916
                confFileBase = BitcoinChainName
1✔
1917

1✔
1918
                // Resolves environment variable references in RPCUser
1✔
1919
                // and RPCPass fields.
1✔
1920
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
1✔
1921
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
1✔
1922

1✔
1923
                // Check that cookie and credentials don't contradict each
1✔
1924
                // other.
1✔
1925
                if (conf.RPCUser != "" || conf.RPCPass != "") &&
1✔
1926
                        conf.RPCCookie != "" {
1✔
1927

×
1928
                        return fmt.Errorf("please only provide either "+
×
1929
                                "%[1]v.rpccookie or %[1]v.rpcuser and "+
×
1930
                                "%[1]v.rpcpass", daemonName)
×
1931
                }
×
1932

1933
                // We convert the cookie into a user name and password.
1934
                if conf.RPCCookie != "" {
1✔
1935
                        cookie, err := os.ReadFile(conf.RPCCookie)
×
1936
                        if err != nil {
×
1937
                                return fmt.Errorf("cannot read cookie file: %w",
×
1938
                                        err)
×
1939
                        }
×
1940

1941
                        splitCookie := strings.Split(string(cookie), ":")
×
1942
                        if len(splitCookie) != 2 {
×
1943
                                return fmt.Errorf("cookie file has a wrong " +
×
1944
                                        "format")
×
1945
                        }
×
1946
                        conf.RPCUser = splitCookie[0]
×
1947
                        conf.RPCPass = splitCookie[1]
×
1948
                }
1949

1950
                if conf.RPCUser != "" && conf.RPCPass != "" {
2✔
1951
                        // If all of RPCUser, RPCPass, ZMQBlockHost, and
1✔
1952
                        // ZMQTxHost are set, we assume those parameters are
1✔
1953
                        // good to use.
1✔
1954
                        if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
2✔
1955
                                return nil
1✔
1956
                        }
1✔
1957

1958
                        // If RPCUser and RPCPass are set and RPCPolling is
1959
                        // enabled, we assume the parameters are good to use.
UNCOV
1960
                        if conf.RPCPolling {
×
UNCOV
1961
                                return nil
×
UNCOV
1962
                        }
×
1963
                }
1964

1965
                // If not all of the parameters are set, we'll assume the user
1966
                // did this unintentionally.
1967
                if conf.RPCUser != "" || conf.RPCPass != "" ||
×
1968
                        conf.ZMQPubRawBlock != "" || conf.ZMQPubRawTx != "" {
×
1969

×
1970
                        return fmt.Errorf("please set %[1]v.rpcuser and "+
×
1971
                                "%[1]v.rpcpass (or %[1]v.rpccookie) together "+
×
1972
                                "with %[1]v.zmqpubrawblock, %[1]v.zmqpubrawtx",
×
1973
                                daemonName)
×
1974
                }
×
1975
        }
1976

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

1985
        fmt.Println("Attempting automatic RPC configuration to " + daemonName)
×
1986

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

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

2014
        fmt.Printf("Automatically obtained %v's RPC credentials\n", daemonName)
×
2015
        return nil
×
2016
}
2017

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

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

9✔
2047
        // Regex for ${ENV_VAR:-DEFAULT} format.
9✔
2048
        var reEnvVarWithDefault = regexp.MustCompile(
9✔
2049
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*):-([\S]+)\}$`,
9✔
2050
        )
9✔
2051

9✔
2052
        // Match against supported formats.
9✔
2053
        switch {
9✔
2054
        case reEnvVarWithDefault.MatchString(value):
3✔
2055
                matches := reEnvVarWithDefault.FindStringSubmatch(value)
3✔
2056
                envVariable := matches[1]
3✔
2057
                defaultValue := matches[2]
3✔
2058
                if envValue := os.Getenv(envVariable); envValue != "" {
4✔
2059
                        return envValue
1✔
2060
                }
1✔
2061

2062
                return defaultValue
2✔
2063

2064
        case reEnvVarWithBrackets.MatchString(value):
×
2065
                matches := reEnvVarWithBrackets.FindStringSubmatch(value)
×
2066
                envVariable := matches[1]
×
2067
                envValue := os.Getenv(envVariable)
×
2068

×
2069
                return envValue
×
2070

2071
        case reEnvVar.MatchString(value):
3✔
2072
                matches := reEnvVar.FindStringSubmatch(value)
3✔
2073
                envVariable := matches[1]
3✔
2074
                envValue := os.Getenv(envVariable)
3✔
2075

3✔
2076
                return envValue
3✔
2077
        }
2078

2079
        return value
3✔
2080
}
2081

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

2094
        // With the file open extract the contents of the configuration file so
2095
        // we can attempt to locate the RPC credentials.
2096
        configContents, err := io.ReadAll(btcdConfigFile)
×
2097
        if err != nil {
×
2098
                return "", "", err
×
2099
        }
×
2100

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

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

2125
        return supplyEnvValue(string(userSubmatches[1])),
×
2126
                supplyEnvValue(string(passSubmatches[1])), nil
×
2127
}
2128

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

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

2144
        // With the file open extract the contents of the configuration file so
2145
        // we can attempt to locate the RPC credentials.
2146
        configContents, err := io.ReadAll(bitcoindConfigFile)
×
2147
        if err != nil {
×
2148
                return "", "", "", "", err
×
2149
        }
×
2150

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

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

2194
        var chainDir string
×
2195
        switch networkName {
×
2196
        case "mainnet":
×
2197
                chainDir = ""
×
2198
        case "regtest", "testnet3", "signet":
×
2199
                chainDir = networkName
×
2200
        default:
×
2201
                return "", "", "", "", fmt.Errorf("unexpected networkname %v", networkName)
×
2202
        }
2203

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

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

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

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

2242
        if userSubmatches == nil {
×
2243
                return "", "", "", "", fmt.Errorf("unable to find rpcuser in " +
×
2244
                        "config")
×
2245
        }
×
2246
        if passSubmatches == nil {
×
2247
                return "", "", "", "", fmt.Errorf("unable to find rpcpassword " +
×
2248
                        "in config")
×
2249
        }
×
2250

2251
        return supplyEnvValue(string(userSubmatches[1])),
×
2252
                supplyEnvValue(string(passSubmatches[1])),
×
2253
                zmqBlockHost, zmqTxHost, nil
×
2254
}
2255

2256
// checkZMQOptions ensures that the provided addresses to use as the hosts for
2257
// ZMQ rawblock and rawtx notifications are different.
2258
func checkZMQOptions(zmqBlockHost, zmqTxHost string) error {
1✔
2259
        if zmqBlockHost == zmqTxHost {
1✔
2260
                return errors.New("zmqpubrawblock and zmqpubrawtx must be set " +
×
2261
                        "to different addresses")
×
2262
        }
×
2263

2264
        return nil
1✔
2265
}
2266

2267
// checkEstimateMode ensures that the provided estimate mode is legal.
2268
func checkEstimateMode(estimateMode string) error {
1✔
2269
        for _, mode := range bitcoindEstimateModes {
2✔
2270
                if estimateMode == mode {
2✔
2271
                        return nil
1✔
2272
                }
1✔
2273
        }
2274

2275
        return fmt.Errorf("estimatemode must be one of the following: %v",
×
2276
                bitcoindEstimateModes[:])
×
2277
}
2278

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

4✔
2286
        result := make(map[string]string)
4✔
2287

4✔
2288
        // deprecated stores a map of deprecated options found in the config
4✔
2289
        // that are set by the users. A config option is considered as
4✔
2290
        // deprecated if it has a `hidden` flag.
4✔
2291
        deprecated := make(map[string]struct{})
4✔
2292

4✔
2293
        // redact is the helper function that redacts sensitive values like
4✔
2294
        // passwords.
4✔
2295
        redact := func(key, value string) string {
324✔
2296
                sensitiveKeySuffixes := []string{
320✔
2297
                        "pass",
320✔
2298
                        "password",
320✔
2299
                        "dsn",
320✔
2300
                }
320✔
2301
                for _, suffix := range sensitiveKeySuffixes {
1,267✔
2302
                        if strings.HasSuffix(key, suffix) {
955✔
2303
                                return "[redacted]"
8✔
2304
                        }
8✔
2305
                }
2306

2307
                return value
315✔
2308
        }
2309

2310
        // printConfig is the helper function that goes into nested structs
2311
        // recursively. Because we call it recursively, we need to declare it
2312
        // before we define it.
2313
        var printConfig func(reflect.Value, string)
4✔
2314
        printConfig = func(obj reflect.Value, prefix string) {
72✔
2315
                // Turn struct pointers into the actual struct, so we can
68✔
2316
                // iterate over the fields as we would with a struct value.
68✔
2317
                if obj.Kind() == reflect.Ptr {
126✔
2318
                        obj = obj.Elem()
58✔
2319
                }
58✔
2320

2321
                // Abort on nil values.
2322
                if !obj.IsValid() {
80✔
2323
                        return
12✔
2324
                }
12✔
2325

2326
                // Loop over all fields of the struct and inspect the type.
2327
                for i := 0; i < obj.NumField(); i++ {
457✔
2328
                        field := obj.Field(i)
401✔
2329
                        fieldType := obj.Type().Field(i)
401✔
2330

401✔
2331
                        longName := fieldType.Tag.Get("long")
401✔
2332
                        namespace := fieldType.Tag.Get("namespace")
401✔
2333
                        group := fieldType.Tag.Get("group")
401✔
2334
                        hidden := fieldType.Tag.Get("hidden")
401✔
2335

401✔
2336
                        switch {
401✔
2337
                        // We have a long name defined, this is a config value.
2338
                        case longName != "":
320✔
2339
                                key := longName
320✔
2340
                                if prefix != "" {
544✔
2341
                                        key = prefix + "." + key
224✔
2342
                                }
224✔
2343

2344
                                // Add the value directly to the flattened map.
2345
                                result[key] = redact(key, fmt.Sprintf(
320✔
2346
                                        "%v", field.Interface(),
320✔
2347
                                ))
320✔
2348

320✔
2349
                                // If there's a hidden flag, it's deprecated.
320✔
2350
                                if hidden == "true" && !field.IsZero() {
321✔
2351
                                        deprecated[key] = struct{}{}
1✔
2352
                                }
1✔
2353

2354
                        // We have no long name but a namespace, this is a
2355
                        // nested struct.
2356
                        case longName == "" && namespace != "":
58✔
2357
                                key := namespace
58✔
2358
                                if prefix != "" {
76✔
2359
                                        key = prefix + "." + key
18✔
2360
                                }
18✔
2361

2362
                                printConfig(field, key)
58✔
2363

2364
                        // Just a group means this is a dummy struct to house
2365
                        // multiple config values, the group name doesn't go
2366
                        // into the final field name.
2367
                        case longName == "" && group != "":
4✔
2368
                                printConfig(field, prefix)
4✔
2369

2370
                        // Anonymous means embedded struct. We need to recurse
2371
                        // into it but without adding anything to the prefix.
2372
                        case fieldType.Anonymous:
11✔
2373
                                printConfig(field, prefix)
11✔
2374

2375
                        default:
20✔
2376
                                continue
20✔
2377
                        }
2378
                }
2379
        }
2380

2381
        // Turn the whole config struct into a flat map.
2382
        printConfig(reflect.ValueOf(cfg), "")
4✔
2383

4✔
2384
        return result, deprecated, nil
4✔
2385
}
2386

2387
// logWarningsForDeprecation logs a warning if a deprecated config option is
2388
// set.
2389
func logWarningsForDeprecation(cfg Config) {
3✔
2390
        _, deprecated, err := configToFlatMap(cfg)
3✔
2391
        if err != nil {
3✔
2392
                ltndLog.Errorf("Convert configs to map: %v", err)
×
2393
        }
×
2394

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