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

lightningnetwork / lnd / 14041117392

24 Mar 2025 04:53PM UTC coverage: 58.555% (-10.4%) from 68.975%
14041117392

Pull #9620

github

web-flow
Merge d757bb51e into 6a9ec1065
Pull Request #9620: chain: add testnet4 support

7 of 26 new or added lines in 5 files covered. (26.92%)

27953 existing lines in 447 files now uncovered.

96726 of 165188 relevant lines covered (58.56%)

1.82 hits per line

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

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

5
package lnd
6

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

252
        defaultPrunedNodeMaxPeers = 4
253
        defaultNeutrinoMaxPeers   = 8
254
)
255

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

368
        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."`
369

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

446
        net tor.Net
447

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

523
        // HTTPHeaderTimeout is the maximum duration that the server will wait
524
        // before timing out reading the headers of an HTTP request.
525
        HTTPHeaderTimeout time.Duration `long:"http-header-timeout" description:"The maximum duration that the server will wait before timing out reading the headers of an HTTP request."`
526

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

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

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

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

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

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

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

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

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

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

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

3✔
793
                configFilePath = filepath.Join(
3✔
794
                        configFileDir, lncfg.DefaultConfigFilename,
3✔
795
                )
3✔
796

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

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

×
818
                        return nil, err
×
819
                }
×
820

821
                configFileError = err
3✔
822
        }
823

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

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

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

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

×
855
                return nil, err
×
856
        }
×
857

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

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

3✔
868
        return cleanCfg, nil
3✔
869
}
870

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

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

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

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

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

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

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

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

933
                return nil
3✔
934
        }
935

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

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

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

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

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

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

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

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

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

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

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

×
1030
                return nil, mkErr(str)
×
1031
        }
×
1032

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

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

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

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

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

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

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

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

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

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

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

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

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

×
1136
                return nil, mkErr(str)
×
1137
        }
×
1138

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

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

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

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

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

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

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

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

1254
                if len(cfg.Bitcoin.SigNetSeedNode) > 0 {
×
1255
                        sigNetSeeds = make([]chaincfg.DNSSeed, len(
×
1256
                                cfg.Bitcoin.SigNetSeedNode,
×
1257
                        ))
×
1258
                        for idx, seed := range cfg.Bitcoin.SigNetSeedNode {
×
1259
                                sigNetSeeds[idx] = chaincfg.DNSSeed{
×
1260
                                        Host:         seed,
×
1261
                                        HasFiltering: false,
×
1262
                                }
×
1263
                        }
×
1264
                }
1265

1266
                chainParams := chaincfg.CustomSignetParams(
×
1267
                        sigNetChallenge, sigNetSeeds,
×
1268
                )
×
1269
                cfg.ActiveNetParams.Params = &chainParams
×
1270
        }
1271
        if numNets > 1 {
3✔
NEW
1272
                str := "The mainnet, testnet, testnet4, regtest, simnet and " +
×
NEW
1273
                        "signet params can't be used together -- choose one " +
×
1274
                        "of the five"
×
1275

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

1279
        // The target network must be provided, otherwise, we won't
1280
        // know how to initialize the daemon.
1281
        if numNets == 0 {
3✔
NEW
1282
                str := "either --bitcoin.mainnet, or --bitcoin.testnet, " +
×
NEW
1283
                        "--bitcoin.testnet4, --bitcoin.simnet, " +
×
NEW
1284
                        "--bitcoin.regtest or --bitcoin.signet must be " +
×
NEW
1285
                        "specified"
×
1286

×
1287
                return nil, mkErr(str)
×
1288
        }
×
1289

1290
        err = cfg.Bitcoin.Validate(minTimeLockDelta, funding.MinBtcRemoteDelay)
3✔
1291
        if err != nil {
3✔
1292
                return nil, mkErr("error validating bitcoin params: %v", err)
×
1293
        }
×
1294

1295
        switch cfg.Bitcoin.Node {
3✔
1296
        case btcdBackendName:
1✔
1297
                err := parseRPCParams(
1✔
1298
                        cfg.Bitcoin, cfg.BtcdMode, cfg.ActiveNetParams,
1✔
1299
                )
1✔
1300
                if err != nil {
1✔
1301
                        return nil, mkErr("unable to load RPC "+
×
1302
                                "credentials for btcd: %v", err)
×
1303
                }
×
1304
        case bitcoindBackendName:
1✔
1305
                if cfg.Bitcoin.SimNet {
1✔
1306
                        return nil, mkErr("bitcoind does not " +
×
1307
                                "support simnet")
×
1308
                }
×
1309

1310
                err := parseRPCParams(
1✔
1311
                        cfg.Bitcoin, cfg.BitcoindMode, cfg.ActiveNetParams,
1✔
1312
                )
1✔
1313
                if err != nil {
1✔
1314
                        return nil, mkErr("unable to load RPC "+
×
1315
                                "credentials for bitcoind: %v", err)
×
1316
                }
×
1317
        case neutrinoBackendName:
1✔
1318
                // No need to get RPC parameters.
1319

1320
        case "nochainbackend":
×
1321
                // Nothing to configure, we're running without any chain
1322
                // backend whatsoever (pure signing mode).
1323

1324
        default:
×
1325
                str := "only btcd, bitcoind, and neutrino mode " +
×
1326
                        "supported for bitcoin at this time"
×
1327

×
1328
                return nil, mkErr(str)
×
1329
        }
1330

1331
        cfg.Bitcoin.ChainDir = filepath.Join(
3✔
1332
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
3✔
1333
        )
3✔
1334

3✔
1335
        // Ensure that the user didn't attempt to specify negative values for
3✔
1336
        // any of the autopilot params.
3✔
1337
        if cfg.Autopilot.MaxChannels < 0 {
3✔
1338
                str := "autopilot.maxchannels must be non-negative"
×
1339

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

×
1345
                return nil, mkErr(str)
×
1346
        }
×
1347
        if cfg.Autopilot.MinChannelSize < 0 {
3✔
1348
                str := "autopilot.minchansize must be non-negative"
×
1349

×
1350
                return nil, mkErr(str)
×
1351
        }
×
1352
        if cfg.Autopilot.MaxChannelSize < 0 {
3✔
1353
                str := "autopilot.maxchansize must be non-negative"
×
1354

×
1355
                return nil, mkErr(str)
×
1356
        }
×
1357

1358
        // Ensure that the specified values for the min and max channel size
1359
        // don't are within the bounds of the normal chan size constraints.
1360
        if cfg.Autopilot.MinChannelSize < int64(funding.MinChanFundingSize) {
3✔
1361
                cfg.Autopilot.MinChannelSize = int64(funding.MinChanFundingSize)
×
1362
        }
×
1363
        if cfg.Autopilot.MaxChannelSize > int64(MaxFundingAmount) {
3✔
1364
                cfg.Autopilot.MaxChannelSize = int64(MaxFundingAmount)
×
1365
        }
×
1366

1367
        // We'll now construct the network directory which will be where we
1368
        // store all the data specific to this chain/network.
1369
        cfg.networkDir = filepath.Join(
3✔
1370
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
3✔
1371
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
3✔
1372
        )
3✔
1373

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

1393
        towerDir := filepath.Join(
3✔
1394
                cfg.Watchtower.TowerDir, BitcoinChainName,
3✔
1395
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
3✔
1396
        )
3✔
1397

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

1416
        // Similarly, if a custom back up file path wasn't specified, then
1417
        // we'll update the file location to match our set network directory.
1418
        if cfg.BackupFilePath == "" {
6✔
1419
                cfg.BackupFilePath = filepath.Join(
3✔
1420
                        cfg.networkDir, chanbackup.DefaultBackupFileName,
3✔
1421
                )
3✔
1422
        }
3✔
1423

1424
        // Append the network type to the log directory so it is "namespaced"
1425
        // per network in the same fashion as the data directory.
1426
        cfg.LogDir = filepath.Join(
3✔
1427
                cfg.LogDir, BitcoinChainName,
3✔
1428
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
3✔
1429
        )
3✔
1430

3✔
1431
        if err := cfg.LogConfig.Validate(); err != nil {
3✔
1432
                return nil, mkErr("error validating logging config: %w", err)
×
1433
        }
×
1434

1435
        // If a sub-log manager was not already created, then we'll create one
1436
        // now using the default log handlers.
1437
        if cfg.SubLogMgr == nil {
6✔
1438
                cfg.SubLogMgr = build.NewSubLoggerManager(
3✔
1439
                        build.NewDefaultLogHandlers(
3✔
1440
                                cfg.LogConfig, cfg.LogRotator,
3✔
1441
                        )...,
3✔
1442
                )
3✔
1443
        }
3✔
1444

1445
        // Initialize logging at the default logging level.
1446
        SetupLoggers(cfg.SubLogMgr, interceptor)
3✔
1447

3✔
1448
        if cfg.MaxLogFiles != 0 {
3✔
1449
                if cfg.LogConfig.File.MaxLogFiles !=
×
1450
                        build.DefaultMaxLogFiles {
×
1451

×
1452
                        return nil, mkErr("cannot set both maxlogfiles and "+
×
1453
                                "logging.file.max-files", err)
×
1454
                }
×
1455

1456
                cfg.LogConfig.File.MaxLogFiles = cfg.MaxLogFiles
×
1457
        }
1458
        if cfg.MaxLogFileSize != 0 {
3✔
1459
                if cfg.LogConfig.File.MaxLogFileSize !=
×
1460
                        build.DefaultMaxLogFileSize {
×
1461

×
1462
                        return nil, mkErr("cannot set both maxlogfilesize and "+
×
1463
                                "logging.file.max-file-size", err)
×
1464
                }
×
1465

1466
                cfg.LogConfig.File.MaxLogFileSize = cfg.MaxLogFileSize
×
1467
        }
1468

1469
        err = cfg.LogRotator.InitLogRotator(
3✔
1470
                cfg.LogConfig.File,
3✔
1471
                filepath.Join(cfg.LogDir, defaultLogFilename),
3✔
1472
        )
3✔
1473
        if err != nil {
3✔
1474
                str := "log rotation setup failed: %v"
×
1475
                return nil, mkErr(str, err)
×
1476
        }
×
1477

1478
        // Parse, validate, and set debug log level(s).
1479
        err = build.ParseAndSetDebugLevels(cfg.DebugLevel, cfg.SubLogMgr)
3✔
1480
        if err != nil {
3✔
1481
                str := "error parsing debug level: %v"
×
1482
                return nil, &lncfg.UsageError{Err: mkErr(str, err)}
×
1483
        }
×
1484

1485
        // At least one RPCListener is required. So listen on localhost per
1486
        // default.
1487
        if len(cfg.RawRPCListeners) == 0 {
3✔
1488
                addr := fmt.Sprintf("localhost:%d", defaultRPCPort)
×
1489
                cfg.RawRPCListeners = append(cfg.RawRPCListeners, addr)
×
1490
        }
×
1491

1492
        // Listen on localhost if no REST listeners were specified.
1493
        if len(cfg.RawRESTListeners) == 0 {
3✔
1494
                addr := fmt.Sprintf("localhost:%d", defaultRESTPort)
×
1495
                cfg.RawRESTListeners = append(cfg.RawRESTListeners, addr)
×
1496
        }
×
1497

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

1511
        // Add default port to all RPC listener addresses if needed and remove
1512
        // duplicate addresses.
1513
        cfg.RPCListeners, err = lncfg.NormalizeAddresses(
3✔
1514
                cfg.RawRPCListeners, strconv.Itoa(defaultRPCPort),
3✔
1515
                cfg.net.ResolveTCPAddr,
3✔
1516
        )
3✔
1517
        if err != nil {
3✔
1518
                return nil, mkErr("error normalizing RPC listen addrs: %v", err)
×
1519
        }
×
1520

1521
        // Add default port to all REST listener addresses if needed and remove
1522
        // duplicate addresses.
1523
        cfg.RESTListeners, err = lncfg.NormalizeAddresses(
3✔
1524
                cfg.RawRESTListeners, strconv.Itoa(defaultRESTPort),
3✔
1525
                cfg.net.ResolveTCPAddr,
3✔
1526
        )
3✔
1527
        if err != nil {
3✔
1528
                return nil, mkErr("error normalizing REST listen addrs: %v", err)
×
1529
        }
×
1530

1531
        switch {
3✔
1532
        // The no seed backup and auto unlock are mutually exclusive.
1533
        case cfg.NoSeedBackup && cfg.WalletUnlockPasswordFile != "":
×
1534
                return nil, mkErr("cannot set noseedbackup and " +
×
1535
                        "wallet-unlock-password-file at the same time")
×
1536

1537
        // The "allow-create" flag cannot be set without the auto unlock file.
1538
        case cfg.WalletUnlockAllowCreate && cfg.WalletUnlockPasswordFile == "":
×
1539
                return nil, mkErr("cannot set wallet-unlock-allow-create " +
×
1540
                        "without wallet-unlock-password-file")
×
1541

1542
        // If a password file was specified, we need it to exist.
1543
        case cfg.WalletUnlockPasswordFile != "" &&
1544
                !lnrpc.FileExists(cfg.WalletUnlockPasswordFile):
×
1545

×
1546
                return nil, mkErr("wallet unlock password file %s does "+
×
1547
                        "not exist", cfg.WalletUnlockPasswordFile)
×
1548
        }
1549

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

1562
        if cfg.DisableRest {
3✔
1563
                ltndLog.Infof("REST API is disabled!")
×
1564
                cfg.RESTListeners = nil
×
1565
        } else {
3✔
1566
                err = lncfg.EnforceSafeAuthentication(
3✔
1567
                        cfg.RESTListeners, !cfg.NoMacaroons, !cfg.DisableRestTLS,
3✔
1568
                )
3✔
1569
                if err != nil {
3✔
1570
                        return nil, mkErr("error enforcing safe "+
×
1571
                                "authentication on REST ports: %v", err)
×
1572
                }
×
1573
        }
1574

1575
        // Remove the listening addresses specified if listening is disabled.
1576
        if cfg.DisableListen {
6✔
1577
                ltndLog.Infof("Listening on the p2p interface is disabled!")
3✔
1578
                cfg.Listeners = nil
3✔
1579
                cfg.ExternalIPs = nil
3✔
1580
        } else {
6✔
1581

3✔
1582
                // Add default port to all listener addresses if needed and remove
3✔
1583
                // duplicate addresses.
3✔
1584
                cfg.Listeners, err = lncfg.NormalizeAddresses(
3✔
1585
                        cfg.RawListeners, strconv.Itoa(defaultPeerPort),
3✔
1586
                        cfg.net.ResolveTCPAddr,
3✔
1587
                )
3✔
1588
                if err != nil {
3✔
1589
                        return nil, mkErr("error normalizing p2p listen "+
×
1590
                                "addrs: %v", err)
×
1591
                }
×
1592

1593
                // Add default port to all external IP addresses if needed and remove
1594
                // duplicate addresses.
1595
                cfg.ExternalIPs, err = lncfg.NormalizeAddresses(
3✔
1596
                        cfg.RawExternalIPs, strconv.Itoa(defaultPeerPort),
3✔
1597
                        cfg.net.ResolveTCPAddr,
3✔
1598
                )
3✔
1599
                if err != nil {
3✔
1600
                        return nil, err
×
1601
                }
×
1602

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

1615
        // Ensure that the specified minimum backoff is below or equal to the
1616
        // maximum backoff.
1617
        if cfg.MinBackoff > cfg.MaxBackoff {
3✔
1618
                return nil, mkErr("maxbackoff must be greater than minbackoff")
×
1619
        }
×
1620

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

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

1646
                case strings.HasPrefix(option, "auto_vacuum="):
×
1647
                        defaultAutoVacuum = false
×
1648

1649
                case strings.HasPrefix(option, "fullfsync="):
×
1650
                        defaultFullfsync = false
×
1651

1652
                default:
×
1653
                }
1654
        }
1655

1656
        if defaultSynchronous {
6✔
1657
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1658
                        cfg.DB.Sqlite.PragmaOptions, "synchronous=full",
3✔
1659
                )
3✔
1660
        }
3✔
1661

1662
        if defaultAutoVacuum {
6✔
1663
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1664
                        cfg.DB.Sqlite.PragmaOptions, "auto_vacuum=incremental",
3✔
1665
                )
3✔
1666
        }
3✔
1667

1668
        if defaultFullfsync {
6✔
1669
                cfg.DB.Sqlite.PragmaOptions = append(
3✔
1670
                        cfg.DB.Sqlite.PragmaOptions, "fullfsync=true",
3✔
1671
                )
3✔
1672
        }
3✔
1673

1674
        // Ensure that the user hasn't chosen a remote-max-htlc value greater
1675
        // than the protocol maximum.
1676
        maxRemoteHtlcs := uint16(input.MaxHTLCNumber / 2)
3✔
1677
        if cfg.DefaultRemoteMaxHtlcs > maxRemoteHtlcs {
3✔
1678
                return nil, mkErr("default-remote-max-htlcs (%v) must be "+
×
1679
                        "less than %v", cfg.DefaultRemoteMaxHtlcs,
×
1680
                        maxRemoteHtlcs)
×
1681
        }
×
1682

1683
        // Clamp the ChannelCommitInterval so that commitment updates can still
1684
        // happen in a reasonable timeframe.
1685
        if cfg.ChannelCommitInterval > maxChannelCommitInterval {
3✔
1686
                return nil, mkErr("channel-commit-interval (%v) must be less "+
×
1687
                        "than %v", cfg.ChannelCommitInterval,
×
1688
                        maxChannelCommitInterval)
×
1689
        }
×
1690

1691
        // Limit PendingCommitInterval so we don't wait too long for the remote
1692
        // party to send back a revoke.
1693
        if cfg.PendingCommitInterval > maxPendingCommitInterval {
3✔
1694
                return nil, mkErr("pending-commit-interval (%v) must be less "+
×
1695
                        "than %v", cfg.PendingCommitInterval,
×
1696
                        maxPendingCommitInterval)
×
1697
        }
×
1698

1699
        if err := cfg.Gossip.Parse(); err != nil {
3✔
1700
                return nil, mkErr("error parsing gossip syncer: %v", err)
×
1701
        }
×
1702

1703
        // If the experimental protocol options specify any protocol messages
1704
        // that we want to handle as custom messages, set them now.
1705
        customMsg := cfg.ProtocolOptions.CustomMessageOverrides()
3✔
1706

3✔
1707
        // We can safely set our custom override values during startup because
3✔
1708
        // startup is blocked on config parsing.
3✔
1709
        if err := lnwire.SetCustomOverrides(customMsg); err != nil {
3✔
1710
                return nil, mkErr("custom-message: %v", err)
×
1711
        }
×
1712

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

1746
        // Don't allow both the old dust-threshold and the new
1747
        // channel-max-fee-exposure to be set.
1748
        if cfg.DustThreshold != 0 && cfg.MaxFeeExposure != 0 {
3✔
1749
                return nil, mkErr("cannot set both dust-threshold and " +
×
1750
                        "channel-max-fee-exposure")
×
1751
        }
×
1752

1753
        switch {
3✔
1754
        // Use the old dust-threshold as the max fee exposure if it is set and
1755
        // the new option is not.
1756
        case cfg.DustThreshold != 0:
×
1757
                cfg.MaxFeeExposure = cfg.DustThreshold
×
1758

1759
        // Use the default max fee exposure if the new option is not set and
1760
        // the old one is not set either.
1761
        case cfg.MaxFeeExposure == 0:
3✔
1762
                cfg.MaxFeeExposure = uint64(
3✔
1763
                        htlcswitch.DefaultMaxFeeExposure.ToSatoshis(),
3✔
1764
                )
3✔
1765
        }
1766

1767
        // Validate the subconfigs for workers, caches, and the tower client.
1768
        err = lncfg.Validate(
3✔
1769
                cfg.Workers,
3✔
1770
                cfg.Caches,
3✔
1771
                cfg.WtClient,
3✔
1772
                cfg.DB,
3✔
1773
                cfg.Cluster,
3✔
1774
                cfg.HealthChecks,
3✔
1775
                cfg.RPCMiddleware,
3✔
1776
                cfg.RemoteSigner,
3✔
1777
                cfg.Sweeper,
3✔
1778
                cfg.Htlcswitch,
3✔
1779
                cfg.Invoices,
3✔
1780
                cfg.Routing,
3✔
1781
                cfg.Pprof,
3✔
1782
                cfg.Gossip,
3✔
1783
        )
3✔
1784
        if err != nil {
3✔
1785
                return nil, err
×
1786
        }
×
1787

1788
        // Finally, ensure that the user's color is correctly formatted,
1789
        // otherwise the server will not be able to start after the unlocking
1790
        // the wallet.
1791
        _, err = lncfg.ParseHexColor(cfg.Color)
3✔
1792
        if err != nil {
3✔
1793
                return nil, mkErr("unable to parse node color: %v", err)
×
1794
        }
×
1795

1796
        // All good, return the sanitized result.
1797
        return &cfg, nil
3✔
1798
}
1799

1800
// graphDatabaseDir returns the default directory where the local bolt graph db
1801
// files are stored.
1802
func (c *Config) graphDatabaseDir() string {
3✔
1803
        return filepath.Join(
3✔
1804
                c.DataDir, defaultGraphSubDirname,
3✔
1805
                lncfg.NormalizeNetwork(c.ActiveNetParams.Name),
3✔
1806
        )
3✔
1807
}
3✔
1808

1809
// ImplementationConfig returns the configuration of what actual implementations
1810
// should be used when creating the main lnd instance.
1811
func (c *Config) ImplementationConfig(
1812
        interceptor signal.Interceptor) *ImplementationCfg {
3✔
1813

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

1834
        defaultImpl := NewDefaultWalletImpl(c, ltndLog, interceptor, false)
3✔
1835
        return &ImplementationCfg{
3✔
1836
                GrpcRegistrar:       defaultImpl,
3✔
1837
                RestRegistrar:       defaultImpl,
3✔
1838
                ExternalValidator:   defaultImpl,
3✔
1839
                DatabaseBuilder:     NewDefaultDatabaseBuilder(c, ltndLog),
3✔
1840
                WalletConfigBuilder: defaultImpl,
3✔
1841
                ChainControlBuilder: defaultImpl,
3✔
1842
        }
3✔
1843
}
1844

1845
// CleanAndExpandPath expands environment variables and leading ~ in the
1846
// passed path, cleans the result, and returns it.
1847
// This function is taken from https://github.com/btcsuite/btcd
1848
func CleanAndExpandPath(path string) string {
3✔
1849
        if path == "" {
6✔
1850
                return ""
3✔
1851
        }
3✔
1852

1853
        // Expand initial ~ to OS specific home directory.
1854
        if strings.HasPrefix(path, "~") {
3✔
1855
                var homeDir string
×
1856
                u, err := user.Current()
×
1857
                if err == nil {
×
1858
                        homeDir = u.HomeDir
×
1859
                } else {
×
1860
                        homeDir = os.Getenv("HOME")
×
1861
                }
×
1862

1863
                path = strings.Replace(path, "~", homeDir, 1)
×
1864
        }
1865

1866
        // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
1867
        // but the variables can still be expanded via POSIX-style $VARIABLE.
1868
        return filepath.Clean(os.ExpandEnv(path))
3✔
1869
}
1870

1871
func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
1872
        netParams chainreg.BitcoinNetParams) error {
2✔
1873

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

1✔
1885
                // If both RPCUser and RPCPass are set, we assume those
1✔
1886
                // credentials are good to use.
1✔
1887
                if conf.RPCUser != "" && conf.RPCPass != "" {
2✔
1888
                        return nil
1✔
1889
                }
1✔
1890

1891
                // Set the daemon name for displaying proper errors.
1892
                daemonName = btcdBackendName
×
1893
                confDir = conf.Dir
×
1894
                confFileBase = btcdBackendName
×
1895

×
1896
                // If only ONE of RPCUser or RPCPass is set, we assume the
×
1897
                // user did that unintentionally.
×
1898
                if conf.RPCUser != "" || conf.RPCPass != "" {
×
1899
                        return fmt.Errorf("please set both or neither of "+
×
1900
                                "%[1]v.rpcuser, %[1]v.rpcpass", daemonName)
×
1901
                }
×
1902

1903
        case *lncfg.Bitcoind:
1✔
1904
                // Ensure that if the ZMQ options are set, that they are not
1✔
1905
                // equal.
1✔
1906
                if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
2✔
1907
                        err := checkZMQOptions(
1✔
1908
                                conf.ZMQPubRawBlock, conf.ZMQPubRawTx,
1✔
1909
                        )
1✔
1910
                        if err != nil {
1✔
1911
                                return err
×
1912
                        }
×
1913
                }
1914

1915
                // Ensure that if the estimate mode is set, that it is a legal
1916
                // value.
1917
                if conf.EstimateMode != "" {
2✔
1918
                        err := checkEstimateMode(conf.EstimateMode)
1✔
1919
                        if err != nil {
1✔
1920
                                return err
×
1921
                        }
×
1922
                }
1923

1924
                // Set the daemon name for displaying proper errors.
1925
                daemonName = bitcoindBackendName
1✔
1926
                confDir = conf.Dir
1✔
1927
                confFile = conf.ConfigPath
1✔
1928
                confFileBase = BitcoinChainName
1✔
1929

1✔
1930
                // Resolves environment variable references in RPCUser
1✔
1931
                // and RPCPass fields.
1✔
1932
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
1✔
1933
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
1✔
1934

1✔
1935
                // Check that cookie and credentials don't contradict each
1✔
1936
                // other.
1✔
1937
                if (conf.RPCUser != "" || conf.RPCPass != "") &&
1✔
1938
                        conf.RPCCookie != "" {
1✔
1939

×
1940
                        return fmt.Errorf("please only provide either "+
×
1941
                                "%[1]v.rpccookie or %[1]v.rpcuser and "+
×
1942
                                "%[1]v.rpcpass", daemonName)
×
1943
                }
×
1944

1945
                // We convert the cookie into a user name and password.
1946
                if conf.RPCCookie != "" {
1✔
1947
                        cookie, err := os.ReadFile(conf.RPCCookie)
×
1948
                        if err != nil {
×
1949
                                return fmt.Errorf("cannot read cookie file: %w",
×
1950
                                        err)
×
1951
                        }
×
1952

1953
                        splitCookie := strings.Split(string(cookie), ":")
×
1954
                        if len(splitCookie) != 2 {
×
1955
                                return fmt.Errorf("cookie file has a wrong " +
×
1956
                                        "format")
×
1957
                        }
×
1958
                        conf.RPCUser = splitCookie[0]
×
1959
                        conf.RPCPass = splitCookie[1]
×
1960
                }
1961

1962
                if conf.RPCUser != "" && conf.RPCPass != "" {
2✔
1963
                        // If all of RPCUser, RPCPass, ZMQBlockHost, and
1✔
1964
                        // ZMQTxHost are set, we assume those parameters are
1✔
1965
                        // good to use.
1✔
1966
                        if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
2✔
1967
                                return nil
1✔
1968
                        }
1✔
1969

1970
                        // If RPCUser and RPCPass are set and RPCPolling is
1971
                        // enabled, we assume the parameters are good to use.
1972
                        if conf.RPCPolling {
×
1973
                                return nil
×
1974
                        }
×
1975
                }
1976

1977
                // If not all of the parameters are set, we'll assume the user
1978
                // did this unintentionally.
1979
                if conf.RPCUser != "" || conf.RPCPass != "" ||
×
1980
                        conf.ZMQPubRawBlock != "" || conf.ZMQPubRawTx != "" {
×
1981

×
1982
                        return fmt.Errorf("please set %[1]v.rpcuser and "+
×
1983
                                "%[1]v.rpcpass (or %[1]v.rpccookie) together "+
×
1984
                                "with %[1]v.zmqpubrawblock, %[1]v.zmqpubrawtx",
×
1985
                                daemonName)
×
1986
                }
×
1987
        }
1988

1989
        // If we're in simnet mode, then the running btcd instance won't read
1990
        // the RPC credentials from the configuration. So if lnd wasn't
1991
        // specified the parameters, then we won't be able to start.
1992
        if cConfig.SimNet {
×
1993
                return fmt.Errorf("rpcuser and rpcpass must be set to your " +
×
1994
                        "btcd node's RPC parameters for simnet mode")
×
1995
        }
×
1996

1997
        fmt.Println("Attempting automatic RPC configuration to " + daemonName)
×
1998

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

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

2026
        fmt.Printf("Automatically obtained %v's RPC credentials\n", daemonName)
×
2027
        return nil
×
2028
}
2029

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

2✔
2054
        // Regex for ${ENV_VAR} format.
2✔
2055
        var reEnvVarWithBrackets = regexp.MustCompile(
2✔
2056
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}$`,
2✔
2057
        )
2✔
2058

2✔
2059
        // Regex for ${ENV_VAR:-DEFAULT} format.
2✔
2060
        var reEnvVarWithDefault = regexp.MustCompile(
2✔
2061
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*):-([\S]+)\}$`,
2✔
2062
        )
2✔
2063

2✔
2064
        // Match against supported formats.
2✔
2065
        switch {
2✔
UNCOV
2066
        case reEnvVarWithDefault.MatchString(value):
×
UNCOV
2067
                matches := reEnvVarWithDefault.FindStringSubmatch(value)
×
UNCOV
2068
                envVariable := matches[1]
×
UNCOV
2069
                defaultValue := matches[2]
×
UNCOV
2070
                if envValue := os.Getenv(envVariable); envValue != "" {
×
UNCOV
2071
                        return envValue
×
UNCOV
2072
                }
×
2073

UNCOV
2074
                return defaultValue
×
2075

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

×
2081
                return envValue
×
2082

UNCOV
2083
        case reEnvVar.MatchString(value):
×
UNCOV
2084
                matches := reEnvVar.FindStringSubmatch(value)
×
UNCOV
2085
                envVariable := matches[1]
×
UNCOV
2086
                envValue := os.Getenv(envVariable)
×
UNCOV
2087

×
UNCOV
2088
                return envValue
×
2089
        }
2090

2091
        return value
2✔
2092
}
2093

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

2106
        // With the file open extract the contents of the configuration file so
2107
        // we can attempt to locate the RPC credentials.
2108
        configContents, err := io.ReadAll(btcdConfigFile)
×
2109
        if err != nil {
×
2110
                return "", "", err
×
2111
        }
×
2112

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

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

2137
        return supplyEnvValue(string(userSubmatches[1])),
×
2138
                supplyEnvValue(string(passSubmatches[1])), nil
×
2139
}
2140

2141
// extractBitcoindRPCParams attempts to extract the RPC credentials for an
2142
// existing bitcoind node instance. The routine looks for a cookie first,
2143
// optionally following the datadir configuration option in the bitcoin.conf. If
2144
// it doesn't find one, it looks for rpcuser/rpcpassword.
2145
func extractBitcoindRPCParams(networkName, bitcoindDataDir, bitcoindConfigPath,
2146
        rpcCookiePath string) (string, string, string, string, error) {
×
2147

×
2148
        // First, we'll open up the bitcoind configuration file found at the
×
2149
        // target destination.
×
2150
        bitcoindConfigFile, err := os.Open(bitcoindConfigPath)
×
2151
        if err != nil {
×
2152
                return "", "", "", "", err
×
2153
        }
×
2154
        defer func() { _ = bitcoindConfigFile.Close() }()
×
2155

2156
        // With the file open extract the contents of the configuration file so
2157
        // we can attempt to locate the RPC credentials.
2158
        configContents, err := io.ReadAll(bitcoindConfigFile)
×
2159
        if err != nil {
×
2160
                return "", "", "", "", err
×
2161
        }
×
2162

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

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

2206
        var chainDir string
×
2207
        switch networkName {
×
2208
        case "mainnet":
×
2209
                chainDir = ""
×
NEW
2210
        case "regtest", "testnet3", "testnet4", "signet":
×
2211
                chainDir = networkName
×
2212
        default:
×
2213
                return "", "", "", "", fmt.Errorf("unexpected networkname %v", networkName)
×
2214
        }
2215

2216
        cookiePath := filepath.Join(dataDir, chainDir, ".cookie")
×
2217
        if rpcCookiePath != "" {
×
2218
                cookiePath = rpcCookiePath
×
2219
        }
×
2220
        cookie, err := os.ReadFile(cookiePath)
×
2221
        if err == nil {
×
2222
                splitCookie := strings.Split(string(cookie), ":")
×
2223
                if len(splitCookie) == 2 {
×
2224
                        return splitCookie[0], splitCookie[1], zmqBlockHost,
×
2225
                                zmqTxHost, nil
×
2226
                }
×
2227
        }
2228

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

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

×
2247
        // Exit with an error if the cookie file, is defined in config, and
×
2248
        // can not be found, with both rpcuser and rpcpassword undefined.
×
2249
        if rpcCookiePath != "" && userSubmatches == nil && passSubmatches == nil {
×
2250
                return "", "", "", "", fmt.Errorf("unable to open cookie file (%v)",
×
2251
                        rpcCookiePath)
×
2252
        }
×
2253

2254
        if userSubmatches == nil {
×
2255
                return "", "", "", "", fmt.Errorf("unable to find rpcuser in " +
×
2256
                        "config")
×
2257
        }
×
2258
        if passSubmatches == nil {
×
2259
                return "", "", "", "", fmt.Errorf("unable to find rpcpassword " +
×
2260
                        "in config")
×
2261
        }
×
2262

2263
        return supplyEnvValue(string(userSubmatches[1])),
×
2264
                supplyEnvValue(string(passSubmatches[1])),
×
2265
                zmqBlockHost, zmqTxHost, nil
×
2266
}
2267

2268
// checkZMQOptions ensures that the provided addresses to use as the hosts for
2269
// ZMQ rawblock and rawtx notifications are different.
2270
func checkZMQOptions(zmqBlockHost, zmqTxHost string) error {
1✔
2271
        if zmqBlockHost == zmqTxHost {
1✔
2272
                return errors.New("zmqpubrawblock and zmqpubrawtx must be set " +
×
2273
                        "to different addresses")
×
2274
        }
×
2275

2276
        return nil
1✔
2277
}
2278

2279
// checkEstimateMode ensures that the provided estimate mode is legal.
2280
func checkEstimateMode(estimateMode string) error {
1✔
2281
        for _, mode := range bitcoindEstimateModes {
2✔
2282
                if estimateMode == mode {
2✔
2283
                        return nil
1✔
2284
                }
1✔
2285
        }
2286

2287
        return fmt.Errorf("estimatemode must be one of the following: %v",
×
2288
                bitcoindEstimateModes[:])
×
2289
}
2290

2291
// configToFlatMap converts the given config struct into a flat map of
2292
// key/value pairs using the dot notation we are used to from the config file
2293
// or command line flags. It also returns a map containing deprecated config
2294
// options.
2295
func configToFlatMap(cfg Config) (map[string]string,
2296
        map[string]struct{}, error) {
3✔
2297

3✔
2298
        result := make(map[string]string)
3✔
2299

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

3✔
2305
        // redact is the helper function that redacts sensitive values like
3✔
2306
        // passwords.
3✔
2307
        redact := func(key, value string) string {
6✔
2308
                sensitiveKeySuffixes := []string{
3✔
2309
                        "pass",
3✔
2310
                        "password",
3✔
2311
                        "dsn",
3✔
2312
                }
3✔
2313
                for _, suffix := range sensitiveKeySuffixes {
6✔
2314
                        if strings.HasSuffix(key, suffix) {
6✔
2315
                                return "[redacted]"
3✔
2316
                        }
3✔
2317
                }
2318

2319
                return value
3✔
2320
        }
2321

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

2333
                // Abort on nil values.
2334
                if !obj.IsValid() {
3✔
UNCOV
2335
                        return
×
UNCOV
2336
                }
×
2337

2338
                // Loop over all fields of the struct and inspect the type.
2339
                for i := 0; i < obj.NumField(); i++ {
6✔
2340
                        field := obj.Field(i)
3✔
2341
                        fieldType := obj.Type().Field(i)
3✔
2342

3✔
2343
                        longName := fieldType.Tag.Get("long")
3✔
2344
                        namespace := fieldType.Tag.Get("namespace")
3✔
2345
                        group := fieldType.Tag.Get("group")
3✔
2346
                        hidden := fieldType.Tag.Get("hidden")
3✔
2347

3✔
2348
                        switch {
3✔
2349
                        // We have a long name defined, this is a config value.
2350
                        case longName != "":
3✔
2351
                                key := longName
3✔
2352
                                if prefix != "" {
6✔
2353
                                        key = prefix + "." + key
3✔
2354
                                }
3✔
2355

2356
                                // Add the value directly to the flattened map.
2357
                                result[key] = redact(key, fmt.Sprintf(
3✔
2358
                                        "%v", field.Interface(),
3✔
2359
                                ))
3✔
2360

3✔
2361
                                // If there's a hidden flag, it's deprecated.
3✔
2362
                                if hidden == "true" && !field.IsZero() {
3✔
UNCOV
2363
                                        deprecated[key] = struct{}{}
×
UNCOV
2364
                                }
×
2365

2366
                        // We have no long name but a namespace, this is a
2367
                        // nested struct.
2368
                        case longName == "" && namespace != "":
3✔
2369
                                key := namespace
3✔
2370
                                if prefix != "" {
6✔
2371
                                        key = prefix + "." + key
3✔
2372
                                }
3✔
2373

2374
                                printConfig(field, key)
3✔
2375

2376
                        // Just a group means this is a dummy struct to house
2377
                        // multiple config values, the group name doesn't go
2378
                        // into the final field name.
2379
                        case longName == "" && group != "":
3✔
2380
                                printConfig(field, prefix)
3✔
2381

2382
                        // Anonymous means embedded struct. We need to recurse
2383
                        // into it but without adding anything to the prefix.
2384
                        case fieldType.Anonymous:
3✔
2385
                                printConfig(field, prefix)
3✔
2386

2387
                        default:
3✔
2388
                                continue
3✔
2389
                        }
2390
                }
2391
        }
2392

2393
        // Turn the whole config struct into a flat map.
2394
        printConfig(reflect.ValueOf(cfg), "")
3✔
2395

3✔
2396
        return result, deprecated, nil
3✔
2397
}
2398

2399
// logWarningsForDeprecation logs a warning if a deprecated config option is
2400
// set.
2401
func logWarningsForDeprecation(cfg Config) {
3✔
2402
        _, deprecated, err := configToFlatMap(cfg)
3✔
2403
        if err != nil {
3✔
2404
                ltndLog.Errorf("Convert configs to map: %v", err)
×
2405
        }
×
2406

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