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

lightningnetwork / lnd / 15869455718

25 Jun 2025 06:53AM UTC coverage: 55.806% (-12.2%) from 67.978%
15869455718

Pull #9148

github

web-flow
Merge c64e3a6c3 into 4335d9cfb
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

232 of 270 new or added lines in 5 files covered. (85.93%)

23628 existing lines in 289 files now uncovered.

108377 of 194204 relevant lines covered (55.81%)

22552.93 hits per line

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

20.93
/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 max number of incoming
242
        // connections allowed in the server. Outbound connections are not
243
        // restricted.
244
        DefaultNumRestrictedSlots = 100
245

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

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

253
        defaultPrunedNodeMaxPeers = 4
254
        defaultNeutrinoMaxPeers   = 8
255

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

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

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

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

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

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

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

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

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

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

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

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

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

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

331
        LetsEncryptDir    string `long:"letsencryptdir" description:"The directory to store Let's Encrypt certificates within"`
332
        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."`
333
        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."`
334

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

452
        net tor.Net
453

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

533
        // NumRestrictedSlots is the max number of incoming connections allowed
534
        // in the server. Outbound connections are not restricted.
535
        NumRestrictedSlots uint64 `long:"num-restricted-slots" description:"The max number of incoming connections allowed in the server. Outbound connections are not restricted."`
536

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

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

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

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

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

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

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

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

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

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

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

×
UNCOV
806
                configFilePath = filepath.Join(
×
UNCOV
807
                        configFileDir, lncfg.DefaultConfigFilename,
×
UNCOV
808
                )
×
809

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

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

×
831
                        return nil, err
×
832
                }
×
833

UNCOV
834
                configFileError = err
×
835
        }
836

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

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

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

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

×
868
                return nil, err
×
869
        }
×
870

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

878
        // Finally, log warnings for deprecated config options if they are set.
UNCOV
879
        logWarningsForDeprecation(*cleanCfg)
×
UNCOV
880

×
UNCOV
881
        return cleanCfg, nil
×
882
}
883

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

×
UNCOV
890
        // Special show command to list supported subsystems and exit.
×
UNCOV
891
        if cfg.DebugLevel == "show" {
×
UNCOV
892
                subLogMgr := build.NewSubLoggerManager()
×
UNCOV
893

×
UNCOV
894
                // Initialize logging at the default logging level.
×
UNCOV
895
                SetupLoggers(subLogMgr, interceptor)
×
UNCOV
896

×
UNCOV
897
                fmt.Println("Supported subsystems",
×
UNCOV
898
                        subLogMgr.SupportedSubsystems())
×
UNCOV
899
                os.Exit(0)
×
UNCOV
900
        }
×
901

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

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

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

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

UNCOV
946
                return nil
×
947
        }
948

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

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

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

×
UNCOV
981
                return (fileOption != nil && fileOption.IsSet()) ||
×
UNCOV
982
                                (fileOptionNested != nil && fileOptionNested.IsSet()) ||
×
UNCOV
983
                                (flagOption != nil && flagOption.IsSet()) ||
×
UNCOV
984
                                (flagOptionNested != nil && flagOptionNested.IsSet()),
×
UNCOV
985
                        nil
×
986
        }
987

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
1344
        cfg.Bitcoin.ChainDir = filepath.Join(
×
UNCOV
1345
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
×
UNCOV
1346
        )
×
UNCOV
1347

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

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

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

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

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

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

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

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

UNCOV
1406
        towerDir := filepath.Join(
×
UNCOV
1407
                cfg.Watchtower.TowerDir, BitcoinChainName,
×
UNCOV
1408
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
×
UNCOV
1409
        )
×
UNCOV
1410

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

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

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

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

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

1458
        // Initialize logging at the default logging level.
UNCOV
1459
        SetupLoggers(cfg.SubLogMgr, interceptor)
×
UNCOV
1460

×
UNCOV
1461
        if cfg.MaxLogFiles != 0 {
×
1462
                if cfg.LogConfig.File.MaxLogFiles !=
×
1463
                        build.DefaultMaxLogFiles {
×
1464

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1665
                default:
×
1666
                }
1667
        }
1668

UNCOV
1669
        if defaultSynchronous {
×
UNCOV
1670
                cfg.DB.Sqlite.PragmaOptions = append(
×
UNCOV
1671
                        cfg.DB.Sqlite.PragmaOptions, "synchronous=full",
×
UNCOV
1672
                )
×
UNCOV
1673
        }
×
1674

UNCOV
1675
        if defaultAutoVacuum {
×
UNCOV
1676
                cfg.DB.Sqlite.PragmaOptions = append(
×
UNCOV
1677
                        cfg.DB.Sqlite.PragmaOptions, "auto_vacuum=incremental",
×
UNCOV
1678
                )
×
UNCOV
1679
        }
×
1680

UNCOV
1681
        if defaultFullfsync {
×
UNCOV
1682
                cfg.DB.Sqlite.PragmaOptions = append(
×
UNCOV
1683
                        cfg.DB.Sqlite.PragmaOptions, "fullfsync=true",
×
UNCOV
1684
                )
×
UNCOV
1685
        }
×
1686

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

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

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

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

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

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

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

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

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

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

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

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

1809
        // All good, return the sanitized result.
UNCOV
1810
        return &cfg, nil
×
1811
}
1812

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

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

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

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

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

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

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

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

1884
func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
UNCOV
1885
        netParams chainreg.BitcoinNetParams) error {
×
UNCOV
1886

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

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

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

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

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

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

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

×
UNCOV
1943
                // Resolves environment variable references in RPCUser
×
UNCOV
1944
                // and RPCPass fields.
×
UNCOV
1945
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
×
UNCOV
1946
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
×
UNCOV
1947

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2087
                return defaultValue
2✔
2088

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

×
2094
                return envValue
×
2095

2096
        case reEnvVar.MatchString(value):
3✔
2097
                matches := reEnvVar.FindStringSubmatch(value)
3✔
2098
                envVariable := matches[1]
3✔
2099
                envValue := os.Getenv(envVariable)
3✔
2100

3✔
2101
                return envValue
3✔
2102
        }
2103

2104
        return value
1✔
2105
}
2106

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
2289
        return nil
×
2290
}
2291

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

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

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

1✔
2311
        result := make(map[string]string)
1✔
2312

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

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

2332
                return value
312✔
2333
        }
2334

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

2346
                // Abort on nil values.
2347
                if !obj.IsValid() {
73✔
2348
                        return
12✔
2349
                }
12✔
2350

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

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

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

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

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

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

2387
                                printConfig(field, key)
54✔
2388

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

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

2400
                        default:
17✔
2401
                                continue
17✔
2402
                        }
2403
                }
2404
        }
2405

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

1✔
2409
        return result, deprecated, nil
1✔
2410
}
2411

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

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