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

lightningnetwork / lnd / 13843268750

13 Mar 2025 07:42PM UTC coverage: 58.354% (-10.3%) from 68.647%
13843268750

push

github

web-flow
Merge pull request #9502 from guggero/bandwidth-manager-fix

routing: don't set custom amount if manager isn't handling

1 of 17 new or added lines in 3 files covered. (5.88%)

27335 existing lines in 444 files now uncovered.

94848 of 162538 relevant lines covered (58.35%)

1.81 hits per line

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

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

5
package lnd
6

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

266
        defaultDataDir = filepath.Join(DefaultLndDir, defaultDataDirname)
267
        defaultLogDir  = filepath.Join(DefaultLndDir, defaultLogDirname)
268

269
        defaultTowerDir = filepath.Join(defaultDataDir, defaultTowerSubDirname)
270

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

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

278
        defaultBitcoindDir = btcutil.AppDataDir(BitcoinChainName, false)
279

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

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

289
        defaultPrunedNodeMaxPeers = 4
290
)
291

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

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

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

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

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

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

353
        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"`
354

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

360
        Pprof *lncfg.Pprof `group:"Pprof" namespace:"pprof"`
361

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

367
        NoBackupArchive bool `long:"no-backup-archive" description:"If set to true, channel backups will be deleted or replaced rather than being archived to a separate location."`
368

369
        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"`
370

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

376
        BlockCacheSize uint64 `long:"blockcachesize" description:"The maximum capacity of the block cache"`
377

378
        Autopilot *lncfg.AutoPilot `group:"Autopilot" namespace:"autopilot"`
379

380
        Tor *lncfg.Tor `group:"Tor" namespace:"tor"`
381

382
        SubRPCServers *subRPCServerConfigs `group:"subrpc"`
383

384
        Hodl *hodl.Config `group:"hodl" namespace:"hodl"`
385

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

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

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

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

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

408
        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."`
409

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

445
        net tor.Net
446

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

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

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

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

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

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

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

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

463
        Fee *lncfg.Fee `group:"fee" namespace:"fee"`
464

465
        Invoices *lncfg.Invoices `group:"invoices" namespace:"invoices"`
466

467
        Routing *lncfg.Routing `group:"routing" namespace:"routing"`
468

469
        Gossip *lncfg.Gossip `group:"gossip" namespace:"gossip"`
470

471
        Workers *lncfg.Workers `group:"workers" namespace:"workers"`
472

473
        Caches *lncfg.Caches `group:"caches" namespace:"caches"`
474

475
        Prometheus lncfg.Prometheus `group:"prometheus" namespace:"prometheus"`
476

477
        WtClient *lncfg.WtClient `group:"wtclient" namespace:"wtclient"`
478

479
        Watchtower *lncfg.Watchtower `group:"watchtower" namespace:"watchtower"`
480

481
        ProtocolOptions *lncfg.ProtocolOptions `group:"protocol" namespace:"protocol"`
482

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

485
        HealthChecks *lncfg.HealthCheckConfig `group:"healthcheck" namespace:"healthcheck"`
486

487
        DB *lncfg.DB `group:"db" namespace:"db"`
488

489
        Cluster *lncfg.Cluster `group:"cluster" namespace:"cluster"`
490

491
        RPCMiddleware *lncfg.RPCMiddleware `group:"rpcmiddleware" namespace:"rpcmiddleware"`
492

493
        RemoteSigner *lncfg.RemoteSigner `group:"remotesigner" namespace:"remotesigner"`
494

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

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

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

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

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

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

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

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

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

526
        // NumRestrictedSlots is the number of restricted slots we'll allocate
527
        // in the server.
528
        NumRestrictedSlots uint64 `long:"num-restricted-slots" description:"The number of restricted slots we'll allocate in the server."`
529
}
530

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

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

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

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

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

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

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

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

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

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

3✔
791
                configFilePath = filepath.Join(
3✔
792
                        configFileDir, lncfg.DefaultConfigFilename,
3✔
793
                )
3✔
794

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

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

×
816
                        return nil, err
×
817
                }
×
818

819
                configFileError = err
3✔
820
        }
821

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

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

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

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

×
853
                return nil, err
×
854
        }
×
855

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

863
        // Finally, log warnings for deprecated config options if they are set.
864
        logWarningsForDeprecation(*cleanCfg)
3✔
865

3✔
866
        return cleanCfg, nil
3✔
867
}
868

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

3✔
875
        // Special show command to list supported subsystems and exit.
3✔
876
        if cfg.DebugLevel == "show" {
6✔
877
                subLogMgr := build.NewSubLoggerManager()
3✔
878

3✔
879
                // Initialize logging at the default logging level.
3✔
880
                SetupLoggers(subLogMgr, interceptor)
3✔
881

3✔
882
                fmt.Println("Supported subsystems",
3✔
883
                        subLogMgr.SupportedSubsystems())
3✔
884
                os.Exit(0)
3✔
885
        }
3✔
886

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

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

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

927
                        str := "Failed to create lnd directory '%s': %v"
×
928
                        return mkErr(str, dir, err)
×
929
                }
930

931
                return nil
3✔
932
        }
933

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

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

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

3✔
966
                return (fileOption != nil && fileOption.IsSet()) ||
3✔
967
                                (fileOptionNested != nil && fileOptionNested.IsSet()) ||
3✔
968
                                (flagOption != nil && flagOption.IsSet()) ||
3✔
969
                                (flagOptionNested != nil && flagOptionNested.IsSet()),
3✔
970
                        nil
3✔
971
        }
972

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

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

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

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

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

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

×
1023
                return nil, mkErr(str)
×
1024
        }
×
1025
        if cfg.Autopilot.ConfTarget < 1 {
3✔
1026
                str := "autopilot.conftarget must be positive"
×
1027

×
1028
                return nil, mkErr(str)
×
1029
        }
×
1030

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

1040
        if _, err := validateAtplCfg(cfg.Autopilot); err != nil {
3✔
1041
                return nil, mkErr("error validating autopilot: %v", err)
×
1042
        }
×
1043

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

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

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

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

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

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

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

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

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

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

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

×
1134
                return nil, mkErr(str)
×
1135
        }
×
1136

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

1146
        if cfg.Tor.PrivateKeyPath == "" {
6✔
1147
                switch {
3✔
1148
                case cfg.Tor.V2:
×
1149
                        cfg.Tor.PrivateKeyPath = filepath.Join(
×
1150
                                lndDir, defaultTorV2PrivateKeyFilename,
×
1151
                        )
×
1152
                case cfg.Tor.V3:
×
1153
                        cfg.Tor.PrivateKeyPath = filepath.Join(
×
1154
                                lndDir, defaultTorV3PrivateKeyFilename,
×
1155
                        )
×
1156
                }
1157
        }
1158

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

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

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

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

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

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

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

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

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

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

×
1280
                return nil, mkErr(str)
×
1281
        }
×
1282

1283
        err = cfg.Bitcoin.Validate(minTimeLockDelta, funding.MinBtcRemoteDelay)
3✔
1284
        if err != nil {
3✔
1285
                return nil, mkErr("error validating bitcoin params: %v", err)
×
1286
        }
×
1287

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

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

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

1317
        default:
×
1318
                str := "only btcd, bitcoind, and neutrino mode " +
×
1319
                        "supported for bitcoin at this time"
×
1320

×
1321
                return nil, mkErr(str)
×
1322
        }
1323

1324
        cfg.Bitcoin.ChainDir = filepath.Join(
3✔
1325
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
3✔
1326
        )
3✔
1327

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

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

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

×
1343
                return nil, mkErr(str)
×
1344
        }
×
1345
        if cfg.Autopilot.MaxChannelSize < 0 {
3✔
1346
                str := "autopilot.maxchansize must be non-negative"
×
1347

×
1348
                return nil, mkErr(str)
×
1349
        }
×
1350

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

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

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

1386
        towerDir := filepath.Join(
3✔
1387
                cfg.Watchtower.TowerDir, BitcoinChainName,
3✔
1388
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
3✔
1389
        )
3✔
1390

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

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

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

3✔
1424
        if err := cfg.LogConfig.Validate(); err != nil {
3✔
1425
                return nil, mkErr("error validating logging config: %w", err)
×
1426
        }
×
1427

1428
        // If a sub-log manager was not already created, then we'll create one
1429
        // now using the default log handlers.
1430
        if cfg.SubLogMgr == nil {
6✔
1431
                cfg.SubLogMgr = build.NewSubLoggerManager(
3✔
1432
                        build.NewDefaultLogHandlers(
3✔
1433
                                cfg.LogConfig, cfg.LogRotator,
3✔
1434
                        )...,
3✔
1435
                )
3✔
1436
        }
3✔
1437

1438
        // Initialize logging at the default logging level.
1439
        SetupLoggers(cfg.SubLogMgr, interceptor)
3✔
1440

3✔
1441
        if cfg.MaxLogFiles != 0 {
3✔
1442
                if cfg.LogConfig.File.MaxLogFiles !=
×
1443
                        build.DefaultMaxLogFiles {
×
1444

×
1445
                        return nil, mkErr("cannot set both maxlogfiles and "+
×
1446
                                "logging.file.max-files", err)
×
1447
                }
×
1448

1449
                cfg.LogConfig.File.MaxLogFiles = cfg.MaxLogFiles
×
1450
        }
1451
        if cfg.MaxLogFileSize != 0 {
3✔
1452
                if cfg.LogConfig.File.MaxLogFileSize !=
×
1453
                        build.DefaultMaxLogFileSize {
×
1454

×
1455
                        return nil, mkErr("cannot set both maxlogfilesize and "+
×
1456
                                "logging.file.max-file-size", err)
×
1457
                }
×
1458

1459
                cfg.LogConfig.File.MaxLogFileSize = cfg.MaxLogFileSize
×
1460
        }
1461

1462
        err = cfg.LogRotator.InitLogRotator(
3✔
1463
                cfg.LogConfig.File,
3✔
1464
                filepath.Join(cfg.LogDir, defaultLogFilename),
3✔
1465
        )
3✔
1466
        if err != nil {
3✔
1467
                str := "log rotation setup failed: %v"
×
1468
                return nil, mkErr(str, err)
×
1469
        }
×
1470

1471
        // Parse, validate, and set debug log level(s).
1472
        err = build.ParseAndSetDebugLevels(cfg.DebugLevel, cfg.SubLogMgr)
3✔
1473
        if err != nil {
3✔
1474
                str := "error parsing debug level: %v"
×
1475
                return nil, &lncfg.UsageError{Err: mkErr(str, err)}
×
1476
        }
×
1477

1478
        // At least one RPCListener is required. So listen on localhost per
1479
        // default.
1480
        if len(cfg.RawRPCListeners) == 0 {
3✔
1481
                addr := fmt.Sprintf("localhost:%d", defaultRPCPort)
×
1482
                cfg.RawRPCListeners = append(cfg.RawRPCListeners, addr)
×
1483
        }
×
1484

1485
        // Listen on localhost if no REST listeners were specified.
1486
        if len(cfg.RawRESTListeners) == 0 {
3✔
1487
                addr := fmt.Sprintf("localhost:%d", defaultRESTPort)
×
1488
                cfg.RawRESTListeners = append(cfg.RawRESTListeners, addr)
×
1489
        }
×
1490

1491
        // Listen on the default interface/port if no listeners were specified.
1492
        // An empty address string means default interface/address, which on
1493
        // most unix systems is the same as 0.0.0.0. If Tor is active, we
1494
        // default to only listening on localhost for hidden service
1495
        // connections.
1496
        if len(cfg.RawListeners) == 0 {
3✔
1497
                addr := fmt.Sprintf(":%d", defaultPeerPort)
×
1498
                if cfg.Tor.Active && !cfg.Tor.SkipProxyForClearNetTargets {
×
1499
                        addr = fmt.Sprintf("localhost:%d", defaultPeerPort)
×
1500
                }
×
1501
                cfg.RawListeners = append(cfg.RawListeners, addr)
×
1502
        }
1503

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

1514
        // Add default port to all REST listener addresses if needed and remove
1515
        // duplicate addresses.
1516
        cfg.RESTListeners, err = lncfg.NormalizeAddresses(
3✔
1517
                cfg.RawRESTListeners, strconv.Itoa(defaultRESTPort),
3✔
1518
                cfg.net.ResolveTCPAddr,
3✔
1519
        )
3✔
1520
        if err != nil {
3✔
1521
                return nil, mkErr("error normalizing REST listen addrs: %v", err)
×
1522
        }
×
1523

1524
        switch {
3✔
1525
        // The no seed backup and auto unlock are mutually exclusive.
1526
        case cfg.NoSeedBackup && cfg.WalletUnlockPasswordFile != "":
×
1527
                return nil, mkErr("cannot set noseedbackup and " +
×
1528
                        "wallet-unlock-password-file at the same time")
×
1529

1530
        // The "allow-create" flag cannot be set without the auto unlock file.
1531
        case cfg.WalletUnlockAllowCreate && cfg.WalletUnlockPasswordFile == "":
×
1532
                return nil, mkErr("cannot set wallet-unlock-allow-create " +
×
1533
                        "without wallet-unlock-password-file")
×
1534

1535
        // If a password file was specified, we need it to exist.
1536
        case cfg.WalletUnlockPasswordFile != "" &&
1537
                !lnrpc.FileExists(cfg.WalletUnlockPasswordFile):
×
1538

×
1539
                return nil, mkErr("wallet unlock password file %s does "+
×
1540
                        "not exist", cfg.WalletUnlockPasswordFile)
×
1541
        }
1542

1543
        // For each of the RPC listeners (REST+gRPC), we'll ensure that users
1544
        // have specified a safe combo for authentication. If not, we'll bail
1545
        // out with an error. Since we don't allow disabling TLS for gRPC
1546
        // connections we pass in tlsActive=true.
1547
        err = lncfg.EnforceSafeAuthentication(
3✔
1548
                cfg.RPCListeners, !cfg.NoMacaroons, true,
3✔
1549
        )
3✔
1550
        if err != nil {
3✔
1551
                return nil, mkErr("error enforcing safe authentication on "+
×
1552
                        "RPC ports: %v", err)
×
1553
        }
×
1554

1555
        if cfg.DisableRest {
3✔
1556
                ltndLog.Infof("REST API is disabled!")
×
1557
                cfg.RESTListeners = nil
×
1558
        } else {
3✔
1559
                err = lncfg.EnforceSafeAuthentication(
3✔
1560
                        cfg.RESTListeners, !cfg.NoMacaroons, !cfg.DisableRestTLS,
3✔
1561
                )
3✔
1562
                if err != nil {
3✔
1563
                        return nil, mkErr("error enforcing safe "+
×
1564
                                "authentication on REST ports: %v", err)
×
1565
                }
×
1566
        }
1567

1568
        // Remove the listening addresses specified if listening is disabled.
1569
        if cfg.DisableListen {
6✔
1570
                ltndLog.Infof("Listening on the p2p interface is disabled!")
3✔
1571
                cfg.Listeners = nil
3✔
1572
                cfg.ExternalIPs = nil
3✔
1573
        } else {
6✔
1574

3✔
1575
                // Add default port to all listener addresses if needed and remove
3✔
1576
                // duplicate addresses.
3✔
1577
                cfg.Listeners, err = lncfg.NormalizeAddresses(
3✔
1578
                        cfg.RawListeners, strconv.Itoa(defaultPeerPort),
3✔
1579
                        cfg.net.ResolveTCPAddr,
3✔
1580
                )
3✔
1581
                if err != nil {
3✔
1582
                        return nil, mkErr("error normalizing p2p listen "+
×
1583
                                "addrs: %v", err)
×
1584
                }
×
1585

1586
                // Add default port to all external IP addresses if needed and remove
1587
                // duplicate addresses.
1588
                cfg.ExternalIPs, err = lncfg.NormalizeAddresses(
3✔
1589
                        cfg.RawExternalIPs, strconv.Itoa(defaultPeerPort),
3✔
1590
                        cfg.net.ResolveTCPAddr,
3✔
1591
                )
3✔
1592
                if err != nil {
3✔
1593
                        return nil, err
×
1594
                }
×
1595

1596
                // For the p2p port it makes no sense to listen to an Unix socket.
1597
                // Also, we would need to refactor the brontide listener to support
1598
                // that.
1599
                for _, p2pListener := range cfg.Listeners {
6✔
1600
                        if lncfg.IsUnix(p2pListener) {
3✔
1601
                                return nil, mkErr("unix socket addresses "+
×
1602
                                        "cannot be used for the p2p "+
×
1603
                                        "connection listener: %s", p2pListener)
×
1604
                        }
×
1605
                }
1606
        }
1607

1608
        // Ensure that the specified minimum backoff is below or equal to the
1609
        // maximum backoff.
1610
        if cfg.MinBackoff > cfg.MaxBackoff {
3✔
1611
                return nil, mkErr("maxbackoff must be greater than minbackoff")
×
1612
        }
×
1613

1614
        // Newer versions of lnd added a new sub-config for bolt-specific
1615
        // parameters. However, we want to also allow existing users to use the
1616
        // value on the top-level config. If the outer config value is set,
1617
        // then we'll use that directly.
1618
        flagSet, err := isSet("SyncFreelist")
3✔
1619
        if err != nil {
3✔
1620
                return nil, mkErr("error parsing freelist sync flag: %v", err)
×
1621
        }
×
1622
        if flagSet {
3✔
1623
                cfg.DB.Bolt.NoFreelistSync = !cfg.SyncFreelist
×
1624
        }
×
1625

1626
        // Parse any extra sqlite pragma options that may have been provided
1627
        // to determine if they override any of the defaults that we will
1628
        // otherwise add.
1629
        var (
3✔
1630
                defaultSynchronous = true
3✔
1631
                defaultAutoVacuum  = true
3✔
1632
                defaultFullfsync   = true
3✔
1633
        )
3✔
1634
        for _, option := range cfg.DB.Sqlite.PragmaOptions {
3✔
1635
                switch {
×
1636
                case strings.HasPrefix(option, "synchronous="):
×
1637
                        defaultSynchronous = false
×
1638

1639
                case strings.HasPrefix(option, "auto_vacuum="):
×
1640
                        defaultAutoVacuum = false
×
1641

1642
                case strings.HasPrefix(option, "fullfsync="):
×
1643
                        defaultFullfsync = false
×
1644

1645
                default:
×
1646
                }
1647
        }
1648

1649
        if defaultSynchronous {
6✔
1650
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1651
                        cfg.DB.Sqlite.PragmaOptions, "synchronous=full",
3✔
1652
                )
3✔
1653
        }
3✔
1654

1655
        if defaultAutoVacuum {
6✔
1656
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1657
                        cfg.DB.Sqlite.PragmaOptions, "auto_vacuum=incremental",
3✔
1658
                )
3✔
1659
        }
3✔
1660

1661
        if defaultFullfsync {
6✔
1662
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1663
                        cfg.DB.Sqlite.PragmaOptions, "fullfsync=true",
3✔
1664
                )
3✔
1665
        }
3✔
1666

1667
        // Ensure that the user hasn't chosen a remote-max-htlc value greater
1668
        // than the protocol maximum.
1669
        maxRemoteHtlcs := uint16(input.MaxHTLCNumber / 2)
3✔
1670
        if cfg.DefaultRemoteMaxHtlcs > maxRemoteHtlcs {
3✔
1671
                return nil, mkErr("default-remote-max-htlcs (%v) must be "+
×
1672
                        "less than %v", cfg.DefaultRemoteMaxHtlcs,
×
1673
                        maxRemoteHtlcs)
×
1674
        }
×
1675

1676
        // Clamp the ChannelCommitInterval so that commitment updates can still
1677
        // happen in a reasonable timeframe.
1678
        if cfg.ChannelCommitInterval > maxChannelCommitInterval {
3✔
1679
                return nil, mkErr("channel-commit-interval (%v) must be less "+
×
1680
                        "than %v", cfg.ChannelCommitInterval,
×
1681
                        maxChannelCommitInterval)
×
1682
        }
×
1683

1684
        // Limit PendingCommitInterval so we don't wait too long for the remote
1685
        // party to send back a revoke.
1686
        if cfg.PendingCommitInterval > maxPendingCommitInterval {
3✔
1687
                return nil, mkErr("pending-commit-interval (%v) must be less "+
×
1688
                        "than %v", cfg.PendingCommitInterval,
×
1689
                        maxPendingCommitInterval)
×
1690
        }
×
1691

1692
        if err := cfg.Gossip.Parse(); err != nil {
3✔
1693
                return nil, mkErr("error parsing gossip syncer: %v", err)
×
1694
        }
×
1695

1696
        // If the experimental protocol options specify any protocol messages
1697
        // that we want to handle as custom messages, set them now.
1698
        customMsg := cfg.ProtocolOptions.CustomMessageOverrides()
3✔
1699

3✔
1700
        // We can safely set our custom override values during startup because
3✔
1701
        // startup is blocked on config parsing.
3✔
1702
        if err := lnwire.SetCustomOverrides(customMsg); err != nil {
3✔
1703
                return nil, mkErr("custom-message: %v", err)
×
1704
        }
×
1705

1706
        // Map old pprof flags to new pprof group flags.
1707
        //
1708
        // NOTE: This is a temporary measure to ensure compatibility with old
1709
        // flags.
1710
        if cfg.CPUProfile != "" {
3✔
1711
                if cfg.Pprof.CPUProfile != "" {
×
1712
                        return nil, mkErr("cpuprofile and pprof.cpuprofile " +
×
1713
                                "are mutually exclusive")
×
1714
                }
×
1715
                cfg.Pprof.CPUProfile = cfg.CPUProfile
×
1716
        }
1717
        if cfg.Profile != "" {
3✔
1718
                if cfg.Pprof.Profile != "" {
×
1719
                        return nil, mkErr("profile and pprof.profile " +
×
1720
                                "are mutually exclusive")
×
1721
                }
×
1722
                cfg.Pprof.Profile = cfg.Profile
×
1723
        }
1724
        if cfg.BlockingProfile != 0 {
3✔
1725
                if cfg.Pprof.BlockingProfile != 0 {
×
1726
                        return nil, mkErr("blockingprofile and " +
×
1727
                                "pprof.blockingprofile are mutually exclusive")
×
1728
                }
×
1729
                cfg.Pprof.BlockingProfile = cfg.BlockingProfile
×
1730
        }
1731
        if cfg.MutexProfile != 0 {
3✔
1732
                if cfg.Pprof.MutexProfile != 0 {
×
1733
                        return nil, mkErr("mutexprofile and " +
×
1734
                                "pprof.mutexprofile are mutually exclusive")
×
1735
                }
×
1736
                cfg.Pprof.MutexProfile = cfg.MutexProfile
×
1737
        }
1738

1739
        // Don't allow both the old dust-threshold and the new
1740
        // channel-max-fee-exposure to be set.
1741
        if cfg.DustThreshold != 0 && cfg.MaxFeeExposure != 0 {
3✔
1742
                return nil, mkErr("cannot set both dust-threshold and " +
×
1743
                        "channel-max-fee-exposure")
×
1744
        }
×
1745

1746
        switch {
3✔
1747
        // Use the old dust-threshold as the max fee exposure if it is set and
1748
        // the new option is not.
1749
        case cfg.DustThreshold != 0:
×
1750
                cfg.MaxFeeExposure = cfg.DustThreshold
×
1751

1752
        // Use the default max fee exposure if the new option is not set and
1753
        // the old one is not set either.
1754
        case cfg.MaxFeeExposure == 0:
3✔
1755
                cfg.MaxFeeExposure = uint64(
3✔
1756
                        htlcswitch.DefaultMaxFeeExposure.ToSatoshis(),
3✔
1757
                )
3✔
1758
        }
1759

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

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

1789
        // All good, return the sanitized result.
1790
        return &cfg, nil
3✔
1791
}
1792

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

1802
// ImplementationConfig returns the configuration of what actual implementations
1803
// should be used when creating the main lnd instance.
1804
func (c *Config) ImplementationConfig(
1805
        interceptor signal.Interceptor) *ImplementationCfg {
3✔
1806

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

1827
        defaultImpl := NewDefaultWalletImpl(c, ltndLog, interceptor, false)
3✔
1828
        return &ImplementationCfg{
3✔
1829
                GrpcRegistrar:       defaultImpl,
3✔
1830
                RestRegistrar:       defaultImpl,
3✔
1831
                ExternalValidator:   defaultImpl,
3✔
1832
                DatabaseBuilder:     NewDefaultDatabaseBuilder(c, ltndLog),
3✔
1833
                WalletConfigBuilder: defaultImpl,
3✔
1834
                ChainControlBuilder: defaultImpl,
3✔
1835
        }
3✔
1836
}
1837

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

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

1856
                path = strings.Replace(path, "~", homeDir, 1)
×
1857
        }
1858

1859
        // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
1860
        // but the variables can still be expanded via POSIX-style $VARIABLE.
1861
        return filepath.Clean(os.ExpandEnv(path))
3✔
1862
}
1863

1864
func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
1865
        netParams chainreg.BitcoinNetParams) error {
2✔
1866

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

1✔
1878
                // If both RPCUser and RPCPass are set, we assume those
1✔
1879
                // credentials are good to use.
1✔
1880
                if conf.RPCUser != "" && conf.RPCPass != "" {
2✔
1881
                        return nil
1✔
1882
                }
1✔
1883

1884
                // Set the daemon name for displaying proper errors.
1885
                daemonName = btcdBackendName
×
1886
                confDir = conf.Dir
×
1887
                confFileBase = btcdBackendName
×
1888

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

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

1908
                // Ensure that if the estimate mode is set, that it is a legal
1909
                // value.
1910
                if conf.EstimateMode != "" {
2✔
1911
                        err := checkEstimateMode(conf.EstimateMode)
1✔
1912
                        if err != nil {
1✔
1913
                                return err
×
1914
                        }
×
1915
                }
1916

1917
                // Set the daemon name for displaying proper errors.
1918
                daemonName = bitcoindBackendName
1✔
1919
                confDir = conf.Dir
1✔
1920
                confFile = conf.ConfigPath
1✔
1921
                confFileBase = BitcoinChainName
1✔
1922

1✔
1923
                // Resolves environment variable references in RPCUser
1✔
1924
                // and RPCPass fields.
1✔
1925
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
1✔
1926
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
1✔
1927

1✔
1928
                // Check that cookie and credentials don't contradict each
1✔
1929
                // other.
1✔
1930
                if (conf.RPCUser != "" || conf.RPCPass != "") &&
1✔
1931
                        conf.RPCCookie != "" {
1✔
1932

×
1933
                        return fmt.Errorf("please only provide either "+
×
1934
                                "%[1]v.rpccookie or %[1]v.rpcuser and "+
×
1935
                                "%[1]v.rpcpass", daemonName)
×
1936
                }
×
1937

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

1946
                        splitCookie := strings.Split(string(cookie), ":")
×
1947
                        if len(splitCookie) != 2 {
×
1948
                                return fmt.Errorf("cookie file has a wrong " +
×
1949
                                        "format")
×
1950
                        }
×
1951
                        conf.RPCUser = splitCookie[0]
×
1952
                        conf.RPCPass = splitCookie[1]
×
1953
                }
1954

1955
                if conf.RPCUser != "" && conf.RPCPass != "" {
2✔
1956
                        // If all of RPCUser, RPCPass, ZMQBlockHost, and
1✔
1957
                        // ZMQTxHost are set, we assume those parameters are
1✔
1958
                        // good to use.
1✔
1959
                        if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
2✔
1960
                                return nil
1✔
1961
                        }
1✔
1962

1963
                        // If RPCUser and RPCPass are set and RPCPolling is
1964
                        // enabled, we assume the parameters are good to use.
1965
                        if conf.RPCPolling {
×
1966
                                return nil
×
1967
                        }
×
1968
                }
1969

1970
                // If not all of the parameters are set, we'll assume the user
1971
                // did this unintentionally.
1972
                if conf.RPCUser != "" || conf.RPCPass != "" ||
×
1973
                        conf.ZMQPubRawBlock != "" || conf.ZMQPubRawTx != "" {
×
1974

×
1975
                        return fmt.Errorf("please set %[1]v.rpcuser and "+
×
1976
                                "%[1]v.rpcpass (or %[1]v.rpccookie) together "+
×
1977
                                "with %[1]v.zmqpubrawblock, %[1]v.zmqpubrawtx",
×
1978
                                daemonName)
×
1979
                }
×
1980
        }
1981

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

1990
        fmt.Println("Attempting automatic RPC configuration to " + daemonName)
×
1991

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

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

2019
        fmt.Printf("Automatically obtained %v's RPC credentials\n", daemonName)
×
2020
        return nil
×
2021
}
2022

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

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

2✔
2052
        // Regex for ${ENV_VAR:-DEFAULT} format.
2✔
2053
        var reEnvVarWithDefault = regexp.MustCompile(
2✔
2054
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*):-([\S]+)\}$`,
2✔
2055
        )
2✔
2056

2✔
2057
        // Match against supported formats.
2✔
2058
        switch {
2✔
UNCOV
2059
        case reEnvVarWithDefault.MatchString(value):
×
UNCOV
2060
                matches := reEnvVarWithDefault.FindStringSubmatch(value)
×
UNCOV
2061
                envVariable := matches[1]
×
UNCOV
2062
                defaultValue := matches[2]
×
UNCOV
2063
                if envValue := os.Getenv(envVariable); envValue != "" {
×
UNCOV
2064
                        return envValue
×
UNCOV
2065
                }
×
2066

UNCOV
2067
                return defaultValue
×
2068

2069
        case reEnvVarWithBrackets.MatchString(value):
×
2070
                matches := reEnvVarWithBrackets.FindStringSubmatch(value)
×
2071
                envVariable := matches[1]
×
2072
                envValue := os.Getenv(envVariable)
×
2073

×
2074
                return envValue
×
2075

UNCOV
2076
        case reEnvVar.MatchString(value):
×
UNCOV
2077
                matches := reEnvVar.FindStringSubmatch(value)
×
UNCOV
2078
                envVariable := matches[1]
×
UNCOV
2079
                envValue := os.Getenv(envVariable)
×
UNCOV
2080

×
UNCOV
2081
                return envValue
×
2082
        }
2083

2084
        return value
2✔
2085
}
2086

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

2099
        // With the file open extract the contents of the configuration file so
2100
        // we can attempt to locate the RPC credentials.
2101
        configContents, err := io.ReadAll(btcdConfigFile)
×
2102
        if err != nil {
×
2103
                return "", "", err
×
2104
        }
×
2105

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

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

2130
        return supplyEnvValue(string(userSubmatches[1])),
×
2131
                supplyEnvValue(string(passSubmatches[1])), nil
×
2132
}
2133

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

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

2149
        // With the file open extract the contents of the configuration file so
2150
        // we can attempt to locate the RPC credentials.
2151
        configContents, err := io.ReadAll(bitcoindConfigFile)
×
2152
        if err != nil {
×
2153
                return "", "", "", "", err
×
2154
        }
×
2155

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

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

2199
        var chainDir string
×
2200
        switch networkName {
×
2201
        case "mainnet":
×
2202
                chainDir = ""
×
2203
        case "regtest", "testnet3", "signet":
×
2204
                chainDir = networkName
×
2205
        default:
×
2206
                return "", "", "", "", fmt.Errorf("unexpected networkname %v", networkName)
×
2207
        }
2208

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

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

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

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

2247
        if userSubmatches == nil {
×
2248
                return "", "", "", "", fmt.Errorf("unable to find rpcuser in " +
×
2249
                        "config")
×
2250
        }
×
2251
        if passSubmatches == nil {
×
2252
                return "", "", "", "", fmt.Errorf("unable to find rpcpassword " +
×
2253
                        "in config")
×
2254
        }
×
2255

2256
        return supplyEnvValue(string(userSubmatches[1])),
×
2257
                supplyEnvValue(string(passSubmatches[1])),
×
2258
                zmqBlockHost, zmqTxHost, nil
×
2259
}
2260

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

2269
        return nil
1✔
2270
}
2271

2272
// checkEstimateMode ensures that the provided estimate mode is legal.
2273
func checkEstimateMode(estimateMode string) error {
1✔
2274
        for _, mode := range bitcoindEstimateModes {
2✔
2275
                if estimateMode == mode {
2✔
2276
                        return nil
1✔
2277
                }
1✔
2278
        }
2279

2280
        return fmt.Errorf("estimatemode must be one of the following: %v",
×
2281
                bitcoindEstimateModes[:])
×
2282
}
2283

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

3✔
2291
        result := make(map[string]string)
3✔
2292

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

3✔
2298
        // redact is the helper function that redacts sensitive values like
3✔
2299
        // passwords.
3✔
2300
        redact := func(key, value string) string {
6✔
2301
                sensitiveKeySuffixes := []string{
3✔
2302
                        "pass",
3✔
2303
                        "password",
3✔
2304
                        "dsn",
3✔
2305
                }
3✔
2306
                for _, suffix := range sensitiveKeySuffixes {
6✔
2307
                        if strings.HasSuffix(key, suffix) {
6✔
2308
                                return "[redacted]"
3✔
2309
                        }
3✔
2310
                }
2311

2312
                return value
3✔
2313
        }
2314

2315
        // printConfig is the helper function that goes into nested structs
2316
        // recursively. Because we call it recursively, we need to declare it
2317
        // before we define it.
2318
        var printConfig func(reflect.Value, string)
3✔
2319
        printConfig = func(obj reflect.Value, prefix string) {
6✔
2320
                // Turn struct pointers into the actual struct, so we can
3✔
2321
                // iterate over the fields as we would with a struct value.
3✔
2322
                if obj.Kind() == reflect.Ptr {
6✔
2323
                        obj = obj.Elem()
3✔
2324
                }
3✔
2325

2326
                // Abort on nil values.
2327
                if !obj.IsValid() {
3✔
UNCOV
2328
                        return
×
UNCOV
2329
                }
×
2330

2331
                // Loop over all fields of the struct and inspect the type.
2332
                for i := 0; i < obj.NumField(); i++ {
6✔
2333
                        field := obj.Field(i)
3✔
2334
                        fieldType := obj.Type().Field(i)
3✔
2335

3✔
2336
                        longName := fieldType.Tag.Get("long")
3✔
2337
                        namespace := fieldType.Tag.Get("namespace")
3✔
2338
                        group := fieldType.Tag.Get("group")
3✔
2339
                        hidden := fieldType.Tag.Get("hidden")
3✔
2340

3✔
2341
                        switch {
3✔
2342
                        // We have a long name defined, this is a config value.
2343
                        case longName != "":
3✔
2344
                                key := longName
3✔
2345
                                if prefix != "" {
6✔
2346
                                        key = prefix + "." + key
3✔
2347
                                }
3✔
2348

2349
                                // Add the value directly to the flattened map.
2350
                                result[key] = redact(key, fmt.Sprintf(
3✔
2351
                                        "%v", field.Interface(),
3✔
2352
                                ))
3✔
2353

3✔
2354
                                // If there's a hidden flag, it's deprecated.
3✔
2355
                                if hidden == "true" && !field.IsZero() {
3✔
UNCOV
2356
                                        deprecated[key] = struct{}{}
×
UNCOV
2357
                                }
×
2358

2359
                        // We have no long name but a namespace, this is a
2360
                        // nested struct.
2361
                        case longName == "" && namespace != "":
3✔
2362
                                key := namespace
3✔
2363
                                if prefix != "" {
6✔
2364
                                        key = prefix + "." + key
3✔
2365
                                }
3✔
2366

2367
                                printConfig(field, key)
3✔
2368

2369
                        // Just a group means this is a dummy struct to house
2370
                        // multiple config values, the group name doesn't go
2371
                        // into the final field name.
2372
                        case longName == "" && group != "":
3✔
2373
                                printConfig(field, prefix)
3✔
2374

2375
                        // Anonymous means embedded struct. We need to recurse
2376
                        // into it but without adding anything to the prefix.
2377
                        case fieldType.Anonymous:
3✔
2378
                                printConfig(field, prefix)
3✔
2379

2380
                        default:
3✔
2381
                                continue
3✔
2382
                        }
2383
                }
2384
        }
2385

2386
        // Turn the whole config struct into a flat map.
2387
        printConfig(reflect.ValueOf(cfg), "")
3✔
2388

3✔
2389
        return result, deprecated, nil
3✔
2390
}
2391

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

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