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

lightningnetwork / lnd / 19924300449

04 Dec 2025 09:35AM UTC coverage: 53.479% (-1.9%) from 55.404%
19924300449

Pull #10419

github

web-flow
Merge f811805c6 into 20473482d
Pull Request #10419: [docs] Document use-native-sql=true for SQL migration step 2

110496 of 206616 relevant lines covered (53.48%)

21221.61 hits per line

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

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

5
package lnd
6

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

241
        // DefaultNumRestrictedSlots is the default max number of incoming
242
        // connections allowed in the server. Outbound connections are not
243
        // restricted.
244
        DefaultNumRestrictedSlots = 100
245

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

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

253
        defaultPrunedNodeMaxPeers = 4
254
        defaultNeutrinoMaxPeers   = 8
255

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

452
        net tor.Net
453

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

541
        // UpfrontShutdownAddr specifies an address that our funds will be paid
542
        // out to on cooperative channel close. This applies to all new channel
543
        // opens unless overridden by an option in openchannel or by a channel
544
        // acceptor.
545
        // Note: If this field is set when opening a channel with a peer that
546
        // does not advertise support for the upfront shutdown feature, the
547
        // channel open will fail.
548
        UpfrontShutdownAddr string `long:"upfront-shutdown-address" description:"The address to which funds will be paid out during a cooperative channel close. This applies to all channels opened after this option is set, unless overridden for a specific channel opening. Note: If this option is set, any channel opening will fail if the peer does not explicitly advertise support for the upfront-shutdown feature bit."`
549
}
550

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

564
        // ServerPingTimeout is the duration the server waits after having
565
        // pinged for keepalive check, and if no activity is seen even after
566
        // that the connection is closed.
567
        ServerPingTimeout time.Duration `long:"server-ping-timeout" description:"How long the server waits for the response from the client for the keepalive ping response."`
568

569
        // ClientPingMinWait is the minimum amount of time a client should wait
570
        // before sending a keepalive ping.
571
        ClientPingMinWait time.Duration `long:"client-ping-min-wait" description:"The minimum amount of time the client should wait before sending a keepalive ping."`
572

573
        // ClientAllowPingWithoutStream specifies whether pings from the client
574
        // are allowed even if there are no active gRPC streams. This might be
575
        // useful to keep the underlying HTTP/2 connection open for future
576
        // requests.
577
        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."`
578
}
579

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

1✔
631
                Fee: &lncfg.Fee{
1✔
632
                        MinUpdateTimeout: lncfg.DefaultMinUpdateTimeout,
1✔
633
                        MaxUpdateTimeout: lncfg.DefaultMaxUpdateTimeout,
1✔
634
                },
1✔
635

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

781
// LoadConfig initializes and parses the config using a config file and command
782
// line options.
783
//
784
// The configuration proceeds as follows:
785
//  1. Start with a default config with sane settings
786
//  2. Pre-parse the command line to check for an alternative config file
787
//  3. Load configuration file overwriting defaults with any specified options
788
//  4. Parse CLI options and overwrite/add any specified options
789
func LoadConfig(interceptor signal.Interceptor) (*Config, error) {
×
790
        // Pre-parse the command line options to pick up an alternative config
×
791
        // file.
×
792
        preCfg := DefaultConfig()
×
793
        if _, err := flags.Parse(&preCfg); err != nil {
×
794
                return nil, err
×
795
        }
×
796

797
        // Show the version and exit if the version flag was specified.
798
        appName := filepath.Base(os.Args[0])
×
799
        appName = strings.TrimSuffix(appName, filepath.Ext(appName))
×
800
        usageMessage := fmt.Sprintf("Use %s -h to show usage", appName)
×
801
        if preCfg.ShowVersion {
×
802
                fmt.Println(appName, "version", build.Version(),
×
803
                        "commit="+build.Commit)
×
804
                os.Exit(0)
×
805
        }
×
806

807
        // If the config file path has not been modified by the user, then we'll
808
        // use the default config file path. However, if the user has modified
809
        // their lnddir, then we should assume they intend to use the config
810
        // file within it.
811
        configFileDir := CleanAndExpandPath(preCfg.LndDir)
×
812
        configFilePath := CleanAndExpandPath(preCfg.ConfigFile)
×
813
        switch {
×
814
        // User specified --lnddir but no --configfile. Update the config file
815
        // path to the lnd config directory, but don't require it to exist.
816
        case configFileDir != DefaultLndDir &&
817
                configFilePath == DefaultConfigFile:
×
818

×
819
                configFilePath = filepath.Join(
×
820
                        configFileDir, lncfg.DefaultConfigFilename,
×
821
                )
×
822

823
        // User did specify an explicit --configfile, so we check that it does
824
        // exist under that path to avoid surprises.
825
        case configFilePath != DefaultConfigFile:
×
826
                if !lnrpc.FileExists(configFilePath) {
×
827
                        return nil, fmt.Errorf("specified config file does "+
×
828
                                "not exist in %s", configFilePath)
×
829
                }
×
830
        }
831

832
        // Next, load any additional configuration options from the file.
833
        var configFileError error
×
834
        cfg := preCfg
×
835
        fileParser := flags.NewParser(&cfg, flags.Default)
×
836
        err := flags.NewIniParser(fileParser).ParseFile(configFilePath)
×
837
        if err != nil {
×
838
                // If it's a parsing related error, then we'll return
×
839
                // immediately, otherwise we can proceed as possibly the config
×
840
                // file doesn't exist which is OK.
×
841
                if lnutils.ErrorAs[*flags.IniError](err) ||
×
842
                        lnutils.ErrorAs[*flags.Error](err) {
×
843

×
844
                        return nil, err
×
845
                }
×
846

847
                configFileError = err
×
848
        }
849

850
        // Finally, parse the remaining command line options again to ensure
851
        // they take precedence.
852
        flagParser := flags.NewParser(&cfg, flags.Default)
×
853
        if _, err := flagParser.Parse(); err != nil {
×
854
                return nil, err
×
855
        }
×
856

857
        // Make sure everything we just loaded makes sense.
858
        cleanCfg, err := ValidateConfig(
×
859
                cfg, interceptor, fileParser, flagParser,
×
860
        )
×
861
        var usageErr *lncfg.UsageError
×
862
        if errors.As(err, &usageErr) {
×
863
                // The logging system might not yet be initialized, so we also
×
864
                // write to stderr to make sure the error appears somewhere.
×
865
                _, _ = fmt.Fprintln(os.Stderr, usageMessage)
×
866
                ltndLog.Warnf("Incorrect usage: %v", usageMessage)
×
867

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

×
873
                return nil, err
×
874
        }
×
875
        if err != nil {
×
876
                // The log subsystem might not yet be initialized. But we still
×
877
                // try to log the error there since some packaging solutions
×
878
                // might only look at the log and not stdout/stderr.
×
879
                ltndLog.Warnf("Error validating config: %v", err)
×
880

×
881
                return nil, err
×
882
        }
×
883

884
        // Warn about missing config file only after all other configuration is
885
        // done. This prevents the warning on help messages and invalid options.
886
        // Note this should go directly before the return.
887
        if configFileError != nil {
×
888
                ltndLog.Warnf("%v", configFileError)
×
889
        }
×
890

891
        // Finally, log warnings for deprecated config options if they are set.
892
        logWarningsForDeprecation(*cleanCfg)
×
893

×
894
        return cleanCfg, nil
×
895
}
896

897
// ValidateConfig check the given configuration to be sane. This makes sure no
898
// illegal values or combination of values are set. All file system paths are
899
// normalized. The cleaned up config is returned on success.
900
func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser,
901
        flagParser *flags.Parser) (*Config, error) {
×
902

×
903
        // Special show command to list supported subsystems and exit.
×
904
        if cfg.DebugLevel == "show" {
×
905
                subLogMgr := build.NewSubLoggerManager()
×
906

×
907
                // Initialize logging at the default logging level.
×
908
                SetupLoggers(subLogMgr, interceptor)
×
909

×
910
                fmt.Println("Supported subsystems",
×
911
                        subLogMgr.SupportedSubsystems())
×
912
                os.Exit(0)
×
913
        }
×
914

915
        // If the provided lnd directory is not the default, we'll modify the
916
        // path to all of the files and directories that will live within it.
917
        lndDir := CleanAndExpandPath(cfg.LndDir)
×
918
        if lndDir != DefaultLndDir {
×
919
                cfg.DataDir = filepath.Join(lndDir, defaultDataDirname)
×
920
                cfg.LetsEncryptDir = filepath.Join(
×
921
                        lndDir, defaultLetsEncryptDirname,
×
922
                )
×
923
                cfg.TLSCertPath = filepath.Join(lndDir, defaultTLSCertFilename)
×
924
                cfg.TLSKeyPath = filepath.Join(lndDir, defaultTLSKeyFilename)
×
925
                cfg.LogDir = filepath.Join(lndDir, defaultLogDirname)
×
926

×
927
                // If the watchtower's directory is set to the default, i.e. the
×
928
                // user has not requested a different location, we'll move the
×
929
                // location to be relative to the specified lnd directory.
×
930
                if cfg.Watchtower.TowerDir == defaultTowerDir {
×
931
                        cfg.Watchtower.TowerDir = filepath.Join(
×
932
                                cfg.DataDir, defaultTowerSubDirname,
×
933
                        )
×
934
                }
×
935
        }
936

937
        funcName := "ValidateConfig"
×
938
        mkErr := func(format string, args ...interface{}) error {
×
939
                return fmt.Errorf(funcName+": "+format, args...)
×
940
        }
×
941
        makeDirectory := func(dir string) error {
×
942
                err := os.MkdirAll(dir, 0700)
×
943
                if err != nil {
×
944
                        // Show a nicer error message if it's because a symlink
×
945
                        // is linked to a directory that does not exist
×
946
                        // (probably because it's not mounted).
×
947
                        if e, ok := err.(*os.PathError); ok && os.IsExist(err) {
×
948
                                link, lerr := os.Readlink(e.Path)
×
949
                                if lerr == nil {
×
950
                                        str := "is symlink %s -> %s mounted?"
×
951
                                        err = fmt.Errorf(str, e.Path, link)
×
952
                                }
×
953
                        }
954

955
                        str := "Failed to create lnd directory '%s': %v"
×
956
                        return mkErr(str, dir, err)
×
957
                }
958

959
                return nil
×
960
        }
961

962
        // IsSet returns true if an option has been set in either the config
963
        // file or by a flag.
964
        isSet := func(field string) (bool, error) {
×
965
                fieldName, ok := reflect.TypeOf(Config{}).FieldByName(field)
×
966
                if !ok {
×
967
                        str := "could not find field %s"
×
968
                        return false, mkErr(str, field)
×
969
                }
×
970

971
                long, ok := fieldName.Tag.Lookup("long")
×
972
                if !ok {
×
973
                        str := "field %s does not have a long tag"
×
974
                        return false, mkErr(str, field)
×
975
                }
×
976

977
                // The user has the option to set the flag in either the config
978
                // file or as a command line flag. If any is set, we consider it
979
                // to be set, not applying any precedence rules here (since it
980
                // is a boolean the default is false anyway which would screw up
981
                // any precedence rules). Additionally, we need to also support
982
                // the use case where the config struct is embedded _within_
983
                // another struct with a prefix (as is the case with
984
                // lightning-terminal).
985
                fileOption := fileParser.FindOptionByLongName(long)
×
986
                fileOptionNested := fileParser.FindOptionByLongName(
×
987
                        "lnd." + long,
×
988
                )
×
989
                flagOption := flagParser.FindOptionByLongName(long)
×
990
                flagOptionNested := flagParser.FindOptionByLongName(
×
991
                        "lnd." + long,
×
992
                )
×
993

×
994
                return (fileOption != nil && fileOption.IsSet()) ||
×
995
                                (fileOptionNested != nil && fileOptionNested.IsSet()) ||
×
996
                                (flagOption != nil && flagOption.IsSet()) ||
×
997
                                (flagOptionNested != nil && flagOptionNested.IsSet()),
×
998
                        nil
×
999
        }
1000

1001
        // As soon as we're done parsing configuration options, ensure all paths
1002
        // to directories and files are cleaned and expanded before attempting
1003
        // to use them later on.
1004
        cfg.DataDir = CleanAndExpandPath(cfg.DataDir)
×
1005
        cfg.TLSCertPath = CleanAndExpandPath(cfg.TLSCertPath)
×
1006
        cfg.TLSKeyPath = CleanAndExpandPath(cfg.TLSKeyPath)
×
1007
        cfg.LetsEncryptDir = CleanAndExpandPath(cfg.LetsEncryptDir)
×
1008
        cfg.AdminMacPath = CleanAndExpandPath(cfg.AdminMacPath)
×
1009
        cfg.ReadMacPath = CleanAndExpandPath(cfg.ReadMacPath)
×
1010
        cfg.InvoiceMacPath = CleanAndExpandPath(cfg.InvoiceMacPath)
×
1011
        cfg.LogDir = CleanAndExpandPath(cfg.LogDir)
×
1012
        cfg.BtcdMode.Dir = CleanAndExpandPath(cfg.BtcdMode.Dir)
×
1013
        cfg.BitcoindMode.Dir = CleanAndExpandPath(cfg.BitcoindMode.Dir)
×
1014
        cfg.BitcoindMode.ConfigPath = CleanAndExpandPath(
×
1015
                cfg.BitcoindMode.ConfigPath,
×
1016
        )
×
1017
        cfg.BitcoindMode.RPCCookie = CleanAndExpandPath(cfg.BitcoindMode.RPCCookie)
×
1018
        cfg.Tor.PrivateKeyPath = CleanAndExpandPath(cfg.Tor.PrivateKeyPath)
×
1019
        cfg.Tor.WatchtowerKeyPath = CleanAndExpandPath(cfg.Tor.WatchtowerKeyPath)
×
1020
        cfg.Watchtower.TowerDir = CleanAndExpandPath(cfg.Watchtower.TowerDir)
×
1021
        cfg.BackupFilePath = CleanAndExpandPath(cfg.BackupFilePath)
×
1022
        cfg.WalletUnlockPasswordFile = CleanAndExpandPath(
×
1023
                cfg.WalletUnlockPasswordFile,
×
1024
        )
×
1025

×
1026
        // Ensure that the user didn't attempt to specify negative values for
×
1027
        // any of the autopilot params.
×
1028
        if cfg.Autopilot.MaxChannels < 0 {
×
1029
                str := "autopilot.maxchannels must be non-negative"
×
1030

×
1031
                return nil, mkErr(str)
×
1032
        }
×
1033
        if cfg.Autopilot.Allocation < 0 {
×
1034
                str := "autopilot.allocation must be non-negative"
×
1035

×
1036
                return nil, mkErr(str)
×
1037
        }
×
1038
        if cfg.Autopilot.MinChannelSize < 0 {
×
1039
                str := "autopilot.minchansize must be non-negative"
×
1040

×
1041
                return nil, mkErr(str)
×
1042
        }
×
1043
        if cfg.Autopilot.MaxChannelSize < 0 {
×
1044
                str := "autopilot.maxchansize must be non-negative"
×
1045

×
1046
                return nil, mkErr(str)
×
1047
        }
×
1048
        if cfg.Autopilot.MinConfs < 0 {
×
1049
                str := "autopilot.minconfs must be non-negative"
×
1050

×
1051
                return nil, mkErr(str)
×
1052
        }
×
1053
        if cfg.Autopilot.ConfTarget < 1 {
×
1054
                str := "autopilot.conftarget must be positive"
×
1055

×
1056
                return nil, mkErr(str)
×
1057
        }
×
1058

1059
        // Ensure that the specified values for the min and max channel size
1060
        // are within the bounds of the normal chan size constraints.
1061
        if cfg.Autopilot.MinChannelSize < int64(funding.MinChanFundingSize) {
×
1062
                cfg.Autopilot.MinChannelSize = int64(funding.MinChanFundingSize)
×
1063
        }
×
1064
        if cfg.Autopilot.MaxChannelSize > int64(MaxFundingAmount) {
×
1065
                cfg.Autopilot.MaxChannelSize = int64(MaxFundingAmount)
×
1066
        }
×
1067

1068
        if _, err := validateAtplCfg(cfg.Autopilot); err != nil {
×
1069
                return nil, mkErr("error validating autopilot: %v", err)
×
1070
        }
×
1071

1072
        // Ensure that --maxchansize is properly handled when set by user.
1073
        // For non-Wumbo channels this limit remains 16777215 satoshis by default
1074
        // as specified in BOLT-02. For wumbo channels this limit is 1,000,000,000.
1075
        // satoshis (10 BTC). Always enforce --maxchansize explicitly set by user.
1076
        // If unset (marked by 0 value), then enforce proper default.
1077
        if cfg.MaxChanSize == 0 {
×
1078
                if cfg.ProtocolOptions.Wumbo() {
×
1079
                        cfg.MaxChanSize = int64(funding.MaxBtcFundingAmountWumbo)
×
1080
                } else {
×
1081
                        cfg.MaxChanSize = int64(funding.MaxBtcFundingAmount)
×
1082
                }
×
1083
        }
1084

1085
        // Ensure that the user specified values for the min and max channel
1086
        // size make sense.
1087
        if cfg.MaxChanSize < cfg.MinChanSize {
×
1088
                return nil, mkErr("invalid channel size parameters: "+
×
1089
                        "max channel size %v, must be no less than min chan "+
×
1090
                        "size %v", cfg.MaxChanSize, cfg.MinChanSize,
×
1091
                )
×
1092
        }
×
1093

1094
        // Don't allow superfluous --maxchansize greater than
1095
        // BOLT 02 soft-limit for non-wumbo channel
1096
        if !cfg.ProtocolOptions.Wumbo() &&
×
1097
                cfg.MaxChanSize > int64(MaxFundingAmount) {
×
1098

×
1099
                return nil, mkErr("invalid channel size parameters: "+
×
1100
                        "maximum channel size %v is greater than maximum "+
×
1101
                        "non-wumbo channel size %v", cfg.MaxChanSize,
×
1102
                        MaxFundingAmount,
×
1103
                )
×
1104
        }
×
1105

1106
        // Ensure that the amount data for revoked commitment transactions is
1107
        // stored if the watchtower client is active.
1108
        if cfg.DB.NoRevLogAmtData && cfg.WtClient.Active {
×
1109
                return nil, mkErr("revocation log amount data must be stored " +
×
1110
                        "if the watchtower client is active")
×
1111
        }
×
1112

1113
        // Ensure a valid max channel fee allocation was set.
1114
        if cfg.MaxChannelFeeAllocation <= 0 || cfg.MaxChannelFeeAllocation > 1 {
×
1115
                return nil, mkErr("invalid max channel fee allocation: %v, "+
×
1116
                        "must be within (0, 1]", cfg.MaxChannelFeeAllocation)
×
1117
        }
×
1118

1119
        if cfg.MaxCommitFeeRateAnchors < 1 {
×
1120
                return nil, mkErr("invalid max commit fee rate anchors: %v, "+
×
1121
                        "must be at least 1 sat/vByte",
×
1122
                        cfg.MaxCommitFeeRateAnchors)
×
1123
        }
×
1124

1125
        // Validate the Tor config parameters.
1126
        socks, err := lncfg.ParseAddressString(
×
1127
                cfg.Tor.SOCKS, strconv.Itoa(defaultTorSOCKSPort),
×
1128
                cfg.net.ResolveTCPAddr,
×
1129
        )
×
1130
        if err != nil {
×
1131
                return nil, err
×
1132
        }
×
1133
        cfg.Tor.SOCKS = socks.String()
×
1134

×
1135
        // We'll only attempt to normalize and resolve the DNS host if it hasn't
×
1136
        // changed, as it doesn't need to be done for the default.
×
1137
        if cfg.Tor.DNS != defaultTorDNS {
×
1138
                dns, err := lncfg.ParseAddressString(
×
1139
                        cfg.Tor.DNS, strconv.Itoa(defaultTorDNSPort),
×
1140
                        cfg.net.ResolveTCPAddr,
×
1141
                )
×
1142
                if err != nil {
×
1143
                        return nil, mkErr("error parsing tor dns: %v", err)
×
1144
                }
×
1145
                cfg.Tor.DNS = dns.String()
×
1146
        }
1147

1148
        control, err := lncfg.ParseAddressString(
×
1149
                cfg.Tor.Control, strconv.Itoa(defaultTorControlPort),
×
1150
                cfg.net.ResolveTCPAddr,
×
1151
        )
×
1152
        if err != nil {
×
1153
                return nil, mkErr("error parsing tor control address: %v", err)
×
1154
        }
×
1155
        cfg.Tor.Control = control.String()
×
1156

×
1157
        // Ensure that tor socks host:port is not equal to tor control
×
1158
        // host:port. This would lead to lnd not starting up properly.
×
1159
        if cfg.Tor.SOCKS == cfg.Tor.Control {
×
1160
                str := "tor.socks and tor.control can not us the same host:port"
×
1161

×
1162
                return nil, mkErr(str)
×
1163
        }
×
1164

1165
        switch {
×
1166
        case cfg.Tor.V2 && cfg.Tor.V3:
×
1167
                return nil, mkErr("either tor.v2 or tor.v3 can be set, " +
×
1168
                        "but not both")
×
1169
        case cfg.DisableListen && (cfg.Tor.V2 || cfg.Tor.V3):
×
1170
                return nil, mkErr("listening must be enabled when enabling " +
×
1171
                        "inbound connections over Tor")
×
1172
        }
1173

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

1187
        if cfg.Tor.WatchtowerKeyPath == "" {
×
1188
                switch {
×
1189
                case cfg.Tor.V2:
×
1190
                        cfg.Tor.WatchtowerKeyPath = filepath.Join(
×
1191
                                cfg.Watchtower.TowerDir,
×
1192
                                defaultTorV2PrivateKeyFilename,
×
1193
                        )
×
1194
                case cfg.Tor.V3:
×
1195
                        cfg.Tor.WatchtowerKeyPath = filepath.Join(
×
1196
                                cfg.Watchtower.TowerDir,
×
1197
                                defaultTorV3PrivateKeyFilename,
×
1198
                        )
×
1199
                }
1200
        }
1201

1202
        // Set up the network-related functions that will be used throughout
1203
        // the daemon. We use the standard Go "net" package functions by
1204
        // default. If we should be proxying all traffic through Tor, then
1205
        // we'll use the Tor proxy specific functions in order to avoid leaking
1206
        // our real information.
1207
        if cfg.Tor.Active {
×
1208
                cfg.net = &tor.ProxyNet{
×
1209
                        SOCKS:                       cfg.Tor.SOCKS,
×
1210
                        DNS:                         cfg.Tor.DNS,
×
1211
                        StreamIsolation:             cfg.Tor.StreamIsolation,
×
1212
                        SkipProxyForClearNetTargets: cfg.Tor.SkipProxyForClearNetTargets,
×
1213
                }
×
1214
        }
×
1215

1216
        if cfg.DisableListen && cfg.NAT {
×
1217
                return nil, mkErr("NAT traversal cannot be used when " +
×
1218
                        "listening is disabled")
×
1219
        }
×
1220
        if cfg.NAT && len(cfg.ExternalHosts) != 0 {
×
1221
                return nil, mkErr("NAT support and externalhosts are " +
×
1222
                        "mutually exclusive, only one should be selected")
×
1223
        }
×
1224

1225
        // Multiple networks can't be selected simultaneously.  Count
1226
        // number of network flags passed; assign active network params
1227
        // while we're at it.
1228
        numNets := 0
×
1229
        if cfg.Bitcoin.MainNet {
×
1230
                numNets++
×
1231
                cfg.ActiveNetParams = chainreg.BitcoinMainNetParams
×
1232
        }
×
1233
        if cfg.Bitcoin.TestNet3 {
×
1234
                numNets++
×
1235
                cfg.ActiveNetParams = chainreg.BitcoinTestNetParams
×
1236
        }
×
1237
        if cfg.Bitcoin.TestNet4 {
×
1238
                numNets++
×
1239
                cfg.ActiveNetParams = chainreg.BitcoinTestNet4Params
×
1240
        }
×
1241
        if cfg.Bitcoin.RegTest {
×
1242
                numNets++
×
1243
                cfg.ActiveNetParams = chainreg.BitcoinRegTestNetParams
×
1244
        }
×
1245
        if cfg.Bitcoin.SimNet {
×
1246
                numNets++
×
1247
                cfg.ActiveNetParams = chainreg.BitcoinSimNetParams
×
1248

×
1249
                // For simnet, the btcsuite chain params uses a
×
1250
                // cointype of 115. However, we override this in
×
1251
                // chainreg/chainparams.go, but the raw ChainParam
×
1252
                // field is used elsewhere. To ensure everything is
×
1253
                // consistent, we'll also override the cointype within
×
1254
                // the raw params.
×
1255
                targetCoinType := chainreg.BitcoinSigNetParams.CoinType
×
1256
                cfg.ActiveNetParams.Params.HDCoinType = targetCoinType
×
1257
        }
×
1258
        if cfg.Bitcoin.SigNet {
×
1259
                numNets++
×
1260
                cfg.ActiveNetParams = chainreg.BitcoinSigNetParams
×
1261

×
1262
                // Let the user overwrite the default signet parameters.
×
1263
                // The challenge defines the actual signet network to
×
1264
                // join and the seed nodes are needed for network
×
1265
                // discovery.
×
1266
                sigNetChallenge := chaincfg.DefaultSignetChallenge
×
1267
                sigNetSeeds := chaincfg.DefaultSignetDNSSeeds
×
1268
                if cfg.Bitcoin.SigNetChallenge != "" {
×
1269
                        challenge, err := hex.DecodeString(
×
1270
                                cfg.Bitcoin.SigNetChallenge,
×
1271
                        )
×
1272
                        if err != nil {
×
1273
                                return nil, mkErr("Invalid "+
×
1274
                                        "signet challenge, hex decode "+
×
1275
                                        "failed: %v", err)
×
1276
                        }
×
1277
                        sigNetChallenge = challenge
×
1278
                }
1279

1280
                if len(cfg.Bitcoin.SigNetSeedNode) > 0 {
×
1281
                        sigNetSeeds = make([]chaincfg.DNSSeed, len(
×
1282
                                cfg.Bitcoin.SigNetSeedNode,
×
1283
                        ))
×
1284
                        for idx, seed := range cfg.Bitcoin.SigNetSeedNode {
×
1285
                                sigNetSeeds[idx] = chaincfg.DNSSeed{
×
1286
                                        Host:         seed,
×
1287
                                        HasFiltering: false,
×
1288
                                }
×
1289
                        }
×
1290
                }
1291

1292
                chainParams := chaincfg.CustomSignetParams(
×
1293
                        sigNetChallenge, sigNetSeeds,
×
1294
                )
×
1295
                cfg.ActiveNetParams.Params = &chainParams
×
1296
        }
1297
        if numNets > 1 {
×
1298
                str := "The mainnet, testnet, testnet4, regtest, simnet and " +
×
1299
                        "signet params can't be used together -- choose one " +
×
1300
                        "of the five"
×
1301

×
1302
                return nil, mkErr(str)
×
1303
        }
×
1304

1305
        // The target network must be provided, otherwise, we won't
1306
        // know how to initialize the daemon.
1307
        if numNets == 0 {
×
1308
                str := "either --bitcoin.mainnet, or --bitcoin.testnet, " +
×
1309
                        "--bitcoin.testnet4, --bitcoin.simnet, " +
×
1310
                        "--bitcoin.regtest or --bitcoin.signet must be " +
×
1311
                        "specified"
×
1312

×
1313
                return nil, mkErr(str)
×
1314
        }
×
1315

1316
        err = cfg.Bitcoin.Validate(minTimeLockDelta, funding.MinBtcRemoteDelay)
×
1317
        if err != nil {
×
1318
                return nil, mkErr("error validating bitcoin params: %v", err)
×
1319
        }
×
1320

1321
        switch cfg.Bitcoin.Node {
×
1322
        case btcdBackendName:
×
1323
                err := parseRPCParams(
×
1324
                        cfg.Bitcoin, cfg.BtcdMode, cfg.ActiveNetParams,
×
1325
                )
×
1326
                if err != nil {
×
1327
                        return nil, mkErr("unable to load RPC "+
×
1328
                                "credentials for btcd: %v", err)
×
1329
                }
×
1330
        case bitcoindBackendName:
×
1331
                if cfg.Bitcoin.SimNet {
×
1332
                        return nil, mkErr("bitcoind does not " +
×
1333
                                "support simnet")
×
1334
                }
×
1335

1336
                err := parseRPCParams(
×
1337
                        cfg.Bitcoin, cfg.BitcoindMode, cfg.ActiveNetParams,
×
1338
                )
×
1339
                if err != nil {
×
1340
                        return nil, mkErr("unable to load RPC "+
×
1341
                                "credentials for bitcoind: %v", err)
×
1342
                }
×
1343
        case neutrinoBackendName:
×
1344
                // No need to get RPC parameters.
1345

1346
        case "nochainbackend":
×
1347
                // Nothing to configure, we're running without any chain
1348
                // backend whatsoever (pure signing mode).
1349

1350
        default:
×
1351
                str := "only btcd, bitcoind, and neutrino mode " +
×
1352
                        "supported for bitcoin at this time"
×
1353

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

1357
        cfg.Bitcoin.ChainDir = filepath.Join(
×
1358
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
×
1359
        )
×
1360

×
1361
        // Ensure that the user didn't attempt to specify negative values for
×
1362
        // any of the autopilot params.
×
1363
        if cfg.Autopilot.MaxChannels < 0 {
×
1364
                str := "autopilot.maxchannels must be non-negative"
×
1365

×
1366
                return nil, mkErr(str)
×
1367
        }
×
1368
        if cfg.Autopilot.Allocation < 0 {
×
1369
                str := "autopilot.allocation must be non-negative"
×
1370

×
1371
                return nil, mkErr(str)
×
1372
        }
×
1373
        if cfg.Autopilot.MinChannelSize < 0 {
×
1374
                str := "autopilot.minchansize must be non-negative"
×
1375

×
1376
                return nil, mkErr(str)
×
1377
        }
×
1378
        if cfg.Autopilot.MaxChannelSize < 0 {
×
1379
                str := "autopilot.maxchansize must be non-negative"
×
1380

×
1381
                return nil, mkErr(str)
×
1382
        }
×
1383

1384
        // Ensure that the specified values for the min and max channel size
1385
        // don't are within the bounds of the normal chan size constraints.
1386
        if cfg.Autopilot.MinChannelSize < int64(funding.MinChanFundingSize) {
×
1387
                cfg.Autopilot.MinChannelSize = int64(funding.MinChanFundingSize)
×
1388
        }
×
1389
        if cfg.Autopilot.MaxChannelSize > int64(MaxFundingAmount) {
×
1390
                cfg.Autopilot.MaxChannelSize = int64(MaxFundingAmount)
×
1391
        }
×
1392

1393
        // We'll now construct the network directory which will be where we
1394
        // store all the data specific to this chain/network.
1395
        cfg.networkDir = filepath.Join(
×
1396
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
×
1397
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
×
1398
        )
×
1399

×
1400
        // If a custom macaroon directory wasn't specified and the data
×
1401
        // directory has changed from the default path, then we'll also update
×
1402
        // the path for the macaroons to be generated.
×
1403
        if cfg.AdminMacPath == "" {
×
1404
                cfg.AdminMacPath = filepath.Join(
×
1405
                        cfg.networkDir, defaultAdminMacFilename,
×
1406
                )
×
1407
        }
×
1408
        if cfg.ReadMacPath == "" {
×
1409
                cfg.ReadMacPath = filepath.Join(
×
1410
                        cfg.networkDir, defaultReadMacFilename,
×
1411
                )
×
1412
        }
×
1413
        if cfg.InvoiceMacPath == "" {
×
1414
                cfg.InvoiceMacPath = filepath.Join(
×
1415
                        cfg.networkDir, defaultInvoiceMacFilename,
×
1416
                )
×
1417
        }
×
1418

1419
        towerDir := filepath.Join(
×
1420
                cfg.Watchtower.TowerDir, BitcoinChainName,
×
1421
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
×
1422
        )
×
1423

×
1424
        // Create the lnd directory and all other sub-directories if they don't
×
1425
        // already exist. This makes sure that directory trees are also created
×
1426
        // for files that point to outside the lnddir.
×
1427
        dirs := []string{
×
1428
                lndDir, cfg.DataDir, cfg.networkDir,
×
1429
                cfg.LetsEncryptDir, towerDir, cfg.graphDatabaseDir(),
×
1430
                filepath.Dir(cfg.TLSCertPath), filepath.Dir(cfg.TLSKeyPath),
×
1431
                filepath.Dir(cfg.AdminMacPath), filepath.Dir(cfg.ReadMacPath),
×
1432
                filepath.Dir(cfg.InvoiceMacPath),
×
1433
                filepath.Dir(cfg.Tor.PrivateKeyPath),
×
1434
                filepath.Dir(cfg.Tor.WatchtowerKeyPath),
×
1435
        }
×
1436
        for _, dir := range dirs {
×
1437
                if err := makeDirectory(dir); err != nil {
×
1438
                        return nil, err
×
1439
                }
×
1440
        }
1441

1442
        // Similarly, if a custom back up file path wasn't specified, then
1443
        // we'll update the file location to match our set network directory.
1444
        if cfg.BackupFilePath == "" {
×
1445
                cfg.BackupFilePath = filepath.Join(
×
1446
                        cfg.networkDir, chanbackup.DefaultBackupFileName,
×
1447
                )
×
1448
        }
×
1449

1450
        // Append the network type to the log directory so it is "namespaced"
1451
        // per network in the same fashion as the data directory.
1452
        cfg.LogDir = filepath.Join(
×
1453
                cfg.LogDir, BitcoinChainName,
×
1454
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
×
1455
        )
×
1456

×
1457
        if err := cfg.LogConfig.Validate(); err != nil {
×
1458
                return nil, mkErr("error validating logging config: %w", err)
×
1459
        }
×
1460

1461
        // If a sub-log manager was not already created, then we'll create one
1462
        // now using the default log handlers.
1463
        if cfg.SubLogMgr == nil {
×
1464
                cfg.SubLogMgr = build.NewSubLoggerManager(
×
1465
                        build.NewDefaultLogHandlers(
×
1466
                                cfg.LogConfig, cfg.LogRotator,
×
1467
                        )...,
×
1468
                )
×
1469
        }
×
1470

1471
        // Initialize logging at the default logging level.
1472
        SetupLoggers(cfg.SubLogMgr, interceptor)
×
1473

×
1474
        if cfg.MaxLogFiles != 0 {
×
1475
                if cfg.LogConfig.File.MaxLogFiles !=
×
1476
                        build.DefaultMaxLogFiles {
×
1477

×
1478
                        return nil, mkErr("cannot set both maxlogfiles and "+
×
1479
                                "logging.file.max-files", err)
×
1480
                }
×
1481

1482
                cfg.LogConfig.File.MaxLogFiles = cfg.MaxLogFiles
×
1483
        }
1484
        if cfg.MaxLogFileSize != 0 {
×
1485
                if cfg.LogConfig.File.MaxLogFileSize !=
×
1486
                        build.DefaultMaxLogFileSize {
×
1487

×
1488
                        return nil, mkErr("cannot set both maxlogfilesize and "+
×
1489
                                "logging.file.max-file-size", err)
×
1490
                }
×
1491

1492
                cfg.LogConfig.File.MaxLogFileSize = cfg.MaxLogFileSize
×
1493
        }
1494

1495
        err = cfg.LogRotator.InitLogRotator(
×
1496
                cfg.LogConfig.File,
×
1497
                filepath.Join(cfg.LogDir, defaultLogFilename),
×
1498
        )
×
1499
        if err != nil {
×
1500
                str := "log rotation setup failed: %v"
×
1501
                return nil, mkErr(str, err)
×
1502
        }
×
1503

1504
        // Parse, validate, and set debug log level(s).
1505
        err = build.ParseAndSetDebugLevels(cfg.DebugLevel, cfg.SubLogMgr)
×
1506
        if err != nil {
×
1507
                str := "error parsing debug level: %v"
×
1508
                return nil, &lncfg.UsageError{Err: mkErr(str, err)}
×
1509
        }
×
1510

1511
        // At least one RPCListener is required. So listen on localhost per
1512
        // default.
1513
        if len(cfg.RawRPCListeners) == 0 {
×
1514
                addr := fmt.Sprintf("localhost:%d", defaultRPCPort)
×
1515
                cfg.RawRPCListeners = append(cfg.RawRPCListeners, addr)
×
1516
        }
×
1517

1518
        // Listen on localhost if no REST listeners were specified.
1519
        if len(cfg.RawRESTListeners) == 0 {
×
1520
                addr := fmt.Sprintf("localhost:%d", defaultRESTPort)
×
1521
                cfg.RawRESTListeners = append(cfg.RawRESTListeners, addr)
×
1522
        }
×
1523

1524
        // Listen on the default interface/port if no listeners were specified.
1525
        // An empty address string means default interface/address, which on
1526
        // most unix systems is the same as 0.0.0.0. If Tor is active, we
1527
        // default to only listening on localhost for hidden service
1528
        // connections.
1529
        if len(cfg.RawListeners) == 0 {
×
1530
                addr := fmt.Sprintf(":%d", defaultPeerPort)
×
1531
                if cfg.Tor.Active && !cfg.Tor.SkipProxyForClearNetTargets {
×
1532
                        addr = fmt.Sprintf("localhost:%d", defaultPeerPort)
×
1533
                }
×
1534
                cfg.RawListeners = append(cfg.RawListeners, addr)
×
1535
        }
1536

1537
        // Add default port to all RPC listener addresses if needed and remove
1538
        // duplicate addresses.
1539
        cfg.RPCListeners, err = lncfg.NormalizeAddresses(
×
1540
                cfg.RawRPCListeners, strconv.Itoa(defaultRPCPort),
×
1541
                cfg.net.ResolveTCPAddr,
×
1542
        )
×
1543
        if err != nil {
×
1544
                return nil, mkErr("error normalizing RPC listen addrs: %v", err)
×
1545
        }
×
1546

1547
        // Add default port to all REST listener addresses if needed and remove
1548
        // duplicate addresses.
1549
        cfg.RESTListeners, err = lncfg.NormalizeAddresses(
×
1550
                cfg.RawRESTListeners, strconv.Itoa(defaultRESTPort),
×
1551
                cfg.net.ResolveTCPAddr,
×
1552
        )
×
1553
        if err != nil {
×
1554
                return nil, mkErr("error normalizing REST listen addrs: %v", err)
×
1555
        }
×
1556

1557
        switch {
×
1558
        // The no seed backup and auto unlock are mutually exclusive.
1559
        case cfg.NoSeedBackup && cfg.WalletUnlockPasswordFile != "":
×
1560
                return nil, mkErr("cannot set noseedbackup and " +
×
1561
                        "wallet-unlock-password-file at the same time")
×
1562

1563
        // The "allow-create" flag cannot be set without the auto unlock file.
1564
        case cfg.WalletUnlockAllowCreate && cfg.WalletUnlockPasswordFile == "":
×
1565
                return nil, mkErr("cannot set wallet-unlock-allow-create " +
×
1566
                        "without wallet-unlock-password-file")
×
1567

1568
        // If a password file was specified, we need it to exist.
1569
        case cfg.WalletUnlockPasswordFile != "" &&
1570
                !lnrpc.FileExists(cfg.WalletUnlockPasswordFile):
×
1571

×
1572
                return nil, mkErr("wallet unlock password file %s does "+
×
1573
                        "not exist", cfg.WalletUnlockPasswordFile)
×
1574
        }
1575

1576
        // For each of the RPC listeners (REST+gRPC), we'll ensure that users
1577
        // have specified a safe combo for authentication. If not, we'll bail
1578
        // out with an error. Since we don't allow disabling TLS for gRPC
1579
        // connections we pass in tlsActive=true.
1580
        err = lncfg.EnforceSafeAuthentication(
×
1581
                cfg.RPCListeners, !cfg.NoMacaroons, true,
×
1582
        )
×
1583
        if err != nil {
×
1584
                return nil, mkErr("error enforcing safe authentication on "+
×
1585
                        "RPC ports: %v", err)
×
1586
        }
×
1587

1588
        if cfg.DisableRest {
×
1589
                ltndLog.Infof("REST API is disabled!")
×
1590
                cfg.RESTListeners = nil
×
1591
        } else {
×
1592
                err = lncfg.EnforceSafeAuthentication(
×
1593
                        cfg.RESTListeners, !cfg.NoMacaroons, !cfg.DisableRestTLS,
×
1594
                )
×
1595
                if err != nil {
×
1596
                        return nil, mkErr("error enforcing safe "+
×
1597
                                "authentication on REST ports: %v", err)
×
1598
                }
×
1599
        }
1600

1601
        // Remove the listening addresses specified if listening is disabled.
1602
        if cfg.DisableListen {
×
1603
                ltndLog.Infof("Listening on the p2p interface is disabled!")
×
1604
                cfg.Listeners = nil
×
1605
                cfg.ExternalIPs = nil
×
1606
        } else {
×
1607

×
1608
                // Add default port to all listener addresses if needed and remove
×
1609
                // duplicate addresses.
×
1610
                cfg.Listeners, err = lncfg.NormalizeAddresses(
×
1611
                        cfg.RawListeners, strconv.Itoa(defaultPeerPort),
×
1612
                        cfg.net.ResolveTCPAddr,
×
1613
                )
×
1614
                if err != nil {
×
1615
                        return nil, mkErr("error normalizing p2p listen "+
×
1616
                                "addrs: %v", err)
×
1617
                }
×
1618

1619
                // Add default port to all external IP addresses if needed and remove
1620
                // duplicate addresses.
1621
                cfg.ExternalIPs, err = lncfg.NormalizeAddresses(
×
1622
                        cfg.RawExternalIPs, strconv.Itoa(defaultPeerPort),
×
1623
                        cfg.net.ResolveTCPAddr,
×
1624
                )
×
1625
                if err != nil {
×
1626
                        return nil, err
×
1627
                }
×
1628

1629
                // For the p2p port it makes no sense to listen to an Unix socket.
1630
                // Also, we would need to refactor the brontide listener to support
1631
                // that.
1632
                for _, p2pListener := range cfg.Listeners {
×
1633
                        if lncfg.IsUnix(p2pListener) {
×
1634
                                return nil, mkErr("unix socket addresses "+
×
1635
                                        "cannot be used for the p2p "+
×
1636
                                        "connection listener: %s", p2pListener)
×
1637
                        }
×
1638
                }
1639
        }
1640

1641
        // Ensure that the specified minimum backoff is below or equal to the
1642
        // maximum backoff.
1643
        if cfg.MinBackoff > cfg.MaxBackoff {
×
1644
                return nil, mkErr("maxbackoff must be greater than minbackoff")
×
1645
        }
×
1646

1647
        // Newer versions of lnd added a new sub-config for bolt-specific
1648
        // parameters. However, we want to also allow existing users to use the
1649
        // value on the top-level config. If the outer config value is set,
1650
        // then we'll use that directly.
1651
        flagSet, err := isSet("SyncFreelist")
×
1652
        if err != nil {
×
1653
                return nil, mkErr("error parsing freelist sync flag: %v", err)
×
1654
        }
×
1655
        if flagSet {
×
1656
                cfg.DB.Bolt.NoFreelistSync = !cfg.SyncFreelist
×
1657
        }
×
1658

1659
        // Parse any extra sqlite pragma options that may have been provided
1660
        // to determine if they override any of the defaults that we will
1661
        // otherwise add.
1662
        var (
×
1663
                defaultSynchronous = true
×
1664
                defaultAutoVacuum  = true
×
1665
                defaultFullfsync   = true
×
1666
        )
×
1667
        for _, option := range cfg.DB.Sqlite.PragmaOptions {
×
1668
                switch {
×
1669
                case strings.HasPrefix(option, "synchronous="):
×
1670
                        defaultSynchronous = false
×
1671

1672
                case strings.HasPrefix(option, "auto_vacuum="):
×
1673
                        defaultAutoVacuum = false
×
1674

1675
                case strings.HasPrefix(option, "fullfsync="):
×
1676
                        defaultFullfsync = false
×
1677

1678
                default:
×
1679
                }
1680
        }
1681

1682
        if defaultSynchronous {
×
1683
                cfg.DB.Sqlite.PragmaOptions = append(
×
1684
                        cfg.DB.Sqlite.PragmaOptions, "synchronous=full",
×
1685
                )
×
1686
        }
×
1687

1688
        if defaultAutoVacuum {
×
1689
                cfg.DB.Sqlite.PragmaOptions = append(
×
1690
                        cfg.DB.Sqlite.PragmaOptions, "auto_vacuum=incremental",
×
1691
                )
×
1692
        }
×
1693

1694
        if defaultFullfsync {
×
1695
                cfg.DB.Sqlite.PragmaOptions = append(
×
1696
                        cfg.DB.Sqlite.PragmaOptions, "fullfsync=true",
×
1697
                )
×
1698
        }
×
1699

1700
        // Ensure that the user hasn't chosen a remote-max-htlc value greater
1701
        // than the protocol maximum.
1702
        maxRemoteHtlcs := uint16(input.MaxHTLCNumber / 2)
×
1703
        if cfg.DefaultRemoteMaxHtlcs > maxRemoteHtlcs {
×
1704
                return nil, mkErr("default-remote-max-htlcs (%v) must be "+
×
1705
                        "less than %v", cfg.DefaultRemoteMaxHtlcs,
×
1706
                        maxRemoteHtlcs)
×
1707
        }
×
1708

1709
        // Clamp the ChannelCommitInterval so that commitment updates can still
1710
        // happen in a reasonable timeframe.
1711
        if cfg.ChannelCommitInterval > maxChannelCommitInterval {
×
1712
                return nil, mkErr("channel-commit-interval (%v) must be less "+
×
1713
                        "than %v", cfg.ChannelCommitInterval,
×
1714
                        maxChannelCommitInterval)
×
1715
        }
×
1716

1717
        // Limit PendingCommitInterval so we don't wait too long for the remote
1718
        // party to send back a revoke.
1719
        if cfg.PendingCommitInterval > maxPendingCommitInterval {
×
1720
                return nil, mkErr("pending-commit-interval (%v) must be less "+
×
1721
                        "than %v", cfg.PendingCommitInterval,
×
1722
                        maxPendingCommitInterval)
×
1723
        }
×
1724

1725
        if err := cfg.Gossip.Parse(); err != nil {
×
1726
                return nil, mkErr("error parsing gossip syncer: %v", err)
×
1727
        }
×
1728

1729
        // If the experimental protocol options specify any protocol messages
1730
        // that we want to handle as custom messages, set them now.
1731
        customMsg := cfg.ProtocolOptions.CustomMessageOverrides()
×
1732

×
1733
        // We can safely set our custom override values during startup because
×
1734
        // startup is blocked on config parsing.
×
1735
        if err := lnwire.SetCustomOverrides(customMsg); err != nil {
×
1736
                return nil, mkErr("custom-message: %v", err)
×
1737
        }
×
1738

1739
        // Map old pprof flags to new pprof group flags.
1740
        //
1741
        // NOTE: This is a temporary measure to ensure compatibility with old
1742
        // flags.
1743
        if cfg.CPUProfile != "" {
×
1744
                if cfg.Pprof.CPUProfile != "" {
×
1745
                        return nil, mkErr("cpuprofile and pprof.cpuprofile " +
×
1746
                                "are mutually exclusive")
×
1747
                }
×
1748
                cfg.Pprof.CPUProfile = cfg.CPUProfile
×
1749
        }
1750
        if cfg.Profile != "" {
×
1751
                if cfg.Pprof.Profile != "" {
×
1752
                        return nil, mkErr("profile and pprof.profile " +
×
1753
                                "are mutually exclusive")
×
1754
                }
×
1755
                cfg.Pprof.Profile = cfg.Profile
×
1756
        }
1757
        if cfg.BlockingProfile != 0 {
×
1758
                if cfg.Pprof.BlockingProfile != 0 {
×
1759
                        return nil, mkErr("blockingprofile and " +
×
1760
                                "pprof.blockingprofile are mutually exclusive")
×
1761
                }
×
1762
                cfg.Pprof.BlockingProfile = cfg.BlockingProfile
×
1763
        }
1764
        if cfg.MutexProfile != 0 {
×
1765
                if cfg.Pprof.MutexProfile != 0 {
×
1766
                        return nil, mkErr("mutexprofile and " +
×
1767
                                "pprof.mutexprofile are mutually exclusive")
×
1768
                }
×
1769
                cfg.Pprof.MutexProfile = cfg.MutexProfile
×
1770
        }
1771

1772
        // Don't allow both the old dust-threshold and the new
1773
        // channel-max-fee-exposure to be set.
1774
        if cfg.DustThreshold != 0 && cfg.MaxFeeExposure != 0 {
×
1775
                return nil, mkErr("cannot set both dust-threshold and " +
×
1776
                        "channel-max-fee-exposure")
×
1777
        }
×
1778

1779
        switch {
×
1780
        // Use the old dust-threshold as the max fee exposure if it is set and
1781
        // the new option is not.
1782
        case cfg.DustThreshold != 0:
×
1783
                cfg.MaxFeeExposure = cfg.DustThreshold
×
1784

1785
        // Use the default max fee exposure if the new option is not set and
1786
        // the old one is not set either.
1787
        case cfg.MaxFeeExposure == 0:
×
1788
                cfg.MaxFeeExposure = uint64(
×
1789
                        htlcswitch.DefaultMaxFeeExposure.ToSatoshis(),
×
1790
                )
×
1791
        }
1792

1793
        // Validate the subconfigs for workers, caches, and the tower client.
1794
        err = lncfg.Validate(
×
1795
                cfg.Workers,
×
1796
                cfg.Caches,
×
1797
                cfg.WtClient,
×
1798
                cfg.DB,
×
1799
                cfg.Cluster,
×
1800
                cfg.HealthChecks,
×
1801
                cfg.RPCMiddleware,
×
1802
                cfg.RemoteSigner,
×
1803
                cfg.Sweeper,
×
1804
                cfg.Htlcswitch,
×
1805
                cfg.Invoices,
×
1806
                cfg.Routing,
×
1807
                cfg.Pprof,
×
1808
                cfg.Gossip,
×
1809
        )
×
1810
        if err != nil {
×
1811
                return nil, err
×
1812
        }
×
1813

1814
        // Finally, ensure that the user's color is correctly formatted,
1815
        // otherwise the server will not be able to start after the unlocking
1816
        // the wallet.
1817
        _, err = lncfg.ParseHexColor(cfg.Color)
×
1818
        if err != nil {
×
1819
                return nil, mkErr("unable to parse node color: %v", err)
×
1820
        }
×
1821

1822
        // All good, return the sanitized result.
1823
        return &cfg, nil
×
1824
}
1825

1826
// graphDatabaseDir returns the default directory where the local bolt graph db
1827
// files are stored.
1828
func (c *Config) graphDatabaseDir() string {
×
1829
        return filepath.Join(
×
1830
                c.DataDir, defaultGraphSubDirname,
×
1831
                lncfg.NormalizeNetwork(c.ActiveNetParams.Name),
×
1832
        )
×
1833
}
×
1834

1835
// ImplementationConfig returns the configuration of what actual implementations
1836
// should be used when creating the main lnd instance.
1837
func (c *Config) ImplementationConfig(
1838
        interceptor signal.Interceptor) *ImplementationCfg {
×
1839

×
1840
        // If we're using a remote signer, we still need the base wallet as a
×
1841
        // watch-only source of chain and address data. But we don't need any
×
1842
        // private key material in that btcwallet base wallet.
×
1843
        if c.RemoteSigner.Enable {
×
1844
                rpcImpl := NewRPCSignerWalletImpl(
×
1845
                        c, ltndLog, interceptor,
×
1846
                        c.RemoteSigner.MigrateWatchOnly,
×
1847
                )
×
1848
                return &ImplementationCfg{
×
1849
                        GrpcRegistrar:     rpcImpl,
×
1850
                        RestRegistrar:     rpcImpl,
×
1851
                        ExternalValidator: rpcImpl,
×
1852
                        DatabaseBuilder: NewDefaultDatabaseBuilder(
×
1853
                                c, ltndLog,
×
1854
                        ),
×
1855
                        WalletConfigBuilder: rpcImpl,
×
1856
                        ChainControlBuilder: rpcImpl,
×
1857
                }
×
1858
        }
×
1859

1860
        defaultImpl := NewDefaultWalletImpl(c, ltndLog, interceptor, false)
×
1861
        return &ImplementationCfg{
×
1862
                GrpcRegistrar:       defaultImpl,
×
1863
                RestRegistrar:       defaultImpl,
×
1864
                ExternalValidator:   defaultImpl,
×
1865
                DatabaseBuilder:     NewDefaultDatabaseBuilder(c, ltndLog),
×
1866
                WalletConfigBuilder: defaultImpl,
×
1867
                ChainControlBuilder: defaultImpl,
×
1868
        }
×
1869
}
1870

1871
// CleanAndExpandPath expands environment variables and leading ~ in the
1872
// passed path, cleans the result, and returns it.
1873
// This function is taken from https://github.com/btcsuite/btcd
1874
func CleanAndExpandPath(path string) string {
×
1875
        if path == "" {
×
1876
                return ""
×
1877
        }
×
1878

1879
        // Expand initial ~ to OS specific home directory.
1880
        if strings.HasPrefix(path, "~") {
×
1881
                var homeDir string
×
1882
                u, err := user.Current()
×
1883
                if err == nil {
×
1884
                        homeDir = u.HomeDir
×
1885
                } else {
×
1886
                        homeDir = os.Getenv("HOME")
×
1887
                }
×
1888

1889
                path = strings.Replace(path, "~", homeDir, 1)
×
1890
        }
1891

1892
        // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
1893
        // but the variables can still be expanded via POSIX-style $VARIABLE.
1894
        return filepath.Clean(os.ExpandEnv(path))
×
1895
}
1896

1897
func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
1898
        netParams chainreg.BitcoinNetParams) error {
×
1899

×
1900
        // First, we'll check our node config to make sure the RPC parameters
×
1901
        // were set correctly. We'll also determine the path to the conf file
×
1902
        // depending on the backend node.
×
1903
        var daemonName, confDir, confFile, confFileBase string
×
1904
        switch conf := nodeConfig.(type) {
×
1905
        case *lncfg.Btcd:
×
1906
                // Resolves environment variable references in RPCUser and
×
1907
                // RPCPass fields.
×
1908
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
×
1909
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
×
1910

×
1911
                // If both RPCUser and RPCPass are set, we assume those
×
1912
                // credentials are good to use.
×
1913
                if conf.RPCUser != "" && conf.RPCPass != "" {
×
1914
                        return nil
×
1915
                }
×
1916

1917
                // Set the daemon name for displaying proper errors.
1918
                daemonName = btcdBackendName
×
1919
                confDir = conf.Dir
×
1920
                confFileBase = btcdBackendName
×
1921

×
1922
                // If only ONE of RPCUser or RPCPass is set, we assume the
×
1923
                // user did that unintentionally.
×
1924
                if conf.RPCUser != "" || conf.RPCPass != "" {
×
1925
                        return fmt.Errorf("please set both or neither of "+
×
1926
                                "%[1]v.rpcuser, %[1]v.rpcpass", daemonName)
×
1927
                }
×
1928

1929
        case *lncfg.Bitcoind:
×
1930
                // Ensure that if the ZMQ options are set, that they are not
×
1931
                // equal.
×
1932
                if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
×
1933
                        err := checkZMQOptions(
×
1934
                                conf.ZMQPubRawBlock, conf.ZMQPubRawTx,
×
1935
                        )
×
1936
                        if err != nil {
×
1937
                                return err
×
1938
                        }
×
1939
                }
1940

1941
                // Ensure that if the estimate mode is set, that it is a legal
1942
                // value.
1943
                if conf.EstimateMode != "" {
×
1944
                        err := checkEstimateMode(conf.EstimateMode)
×
1945
                        if err != nil {
×
1946
                                return err
×
1947
                        }
×
1948
                }
1949

1950
                // Set the daemon name for displaying proper errors.
1951
                daemonName = bitcoindBackendName
×
1952
                confDir = conf.Dir
×
1953
                confFile = conf.ConfigPath
×
1954
                confFileBase = BitcoinChainName
×
1955

×
1956
                // Resolves environment variable references in RPCUser
×
1957
                // and RPCPass fields.
×
1958
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
×
1959
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
×
1960

×
1961
                // Check that cookie and credentials don't contradict each
×
1962
                // other.
×
1963
                if (conf.RPCUser != "" || conf.RPCPass != "") &&
×
1964
                        conf.RPCCookie != "" {
×
1965

×
1966
                        return fmt.Errorf("please only provide either "+
×
1967
                                "%[1]v.rpccookie or %[1]v.rpcuser and "+
×
1968
                                "%[1]v.rpcpass", daemonName)
×
1969
                }
×
1970

1971
                // We convert the cookie into a user name and password.
1972
                if conf.RPCCookie != "" {
×
1973
                        cookie, err := os.ReadFile(conf.RPCCookie)
×
1974
                        if err != nil {
×
1975
                                return fmt.Errorf("cannot read cookie file: %w",
×
1976
                                        err)
×
1977
                        }
×
1978

1979
                        splitCookie := strings.Split(string(cookie), ":")
×
1980
                        if len(splitCookie) != 2 {
×
1981
                                return fmt.Errorf("cookie file has a wrong " +
×
1982
                                        "format")
×
1983
                        }
×
1984
                        conf.RPCUser = splitCookie[0]
×
1985
                        conf.RPCPass = splitCookie[1]
×
1986
                }
1987

1988
                if conf.RPCUser != "" && conf.RPCPass != "" {
×
1989
                        // If all of RPCUser, RPCPass, ZMQBlockHost, and
×
1990
                        // ZMQTxHost are set, we assume those parameters are
×
1991
                        // good to use.
×
1992
                        if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" {
×
1993
                                return nil
×
1994
                        }
×
1995

1996
                        // If RPCUser and RPCPass are set and RPCPolling is
1997
                        // enabled, we assume the parameters are good to use.
1998
                        if conf.RPCPolling {
×
1999
                                return nil
×
2000
                        }
×
2001
                }
2002

2003
                // If not all of the parameters are set, we'll assume the user
2004
                // did this unintentionally.
2005
                if conf.RPCUser != "" || conf.RPCPass != "" ||
×
2006
                        conf.ZMQPubRawBlock != "" || conf.ZMQPubRawTx != "" {
×
2007

×
2008
                        return fmt.Errorf("please set %[1]v.rpcuser and "+
×
2009
                                "%[1]v.rpcpass (or %[1]v.rpccookie) together "+
×
2010
                                "with %[1]v.zmqpubrawblock, %[1]v.zmqpubrawtx",
×
2011
                                daemonName)
×
2012
                }
×
2013
        }
2014

2015
        // If we're in simnet mode, then the running btcd instance won't read
2016
        // the RPC credentials from the configuration. So if lnd wasn't
2017
        // specified the parameters, then we won't be able to start.
2018
        if cConfig.SimNet {
×
2019
                return fmt.Errorf("rpcuser and rpcpass must be set to your " +
×
2020
                        "btcd node's RPC parameters for simnet mode")
×
2021
        }
×
2022

2023
        fmt.Println("Attempting automatic RPC configuration to " + daemonName)
×
2024

×
2025
        if confFile == "" {
×
2026
                confFile = filepath.Join(confDir, fmt.Sprintf("%v.conf",
×
2027
                        confFileBase))
×
2028
        }
×
2029
        switch cConfig.Node {
×
2030
        case btcdBackendName:
×
2031
                nConf := nodeConfig.(*lncfg.Btcd)
×
2032
                rpcUser, rpcPass, err := extractBtcdRPCParams(confFile)
×
2033
                if err != nil {
×
2034
                        return fmt.Errorf("unable to extract RPC credentials: "+
×
2035
                                "%v, cannot start w/o RPC connection", err)
×
2036
                }
×
2037
                nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass
×
2038

2039
        case bitcoindBackendName:
×
2040
                nConf := nodeConfig.(*lncfg.Bitcoind)
×
2041
                rpcUser, rpcPass, zmqBlockHost, zmqTxHost, err :=
×
2042
                        extractBitcoindRPCParams(netParams.Params.Name,
×
2043
                                nConf.Dir, confFile, nConf.RPCCookie)
×
2044
                if err != nil {
×
2045
                        return fmt.Errorf("unable to extract RPC credentials: "+
×
2046
                                "%v, cannot start w/o RPC connection", err)
×
2047
                }
×
2048
                nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass
×
2049
                nConf.ZMQPubRawBlock, nConf.ZMQPubRawTx = zmqBlockHost, zmqTxHost
×
2050
        }
2051

2052
        fmt.Printf("Automatically obtained %v's RPC credentials\n", daemonName)
×
2053
        return nil
×
2054
}
2055

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

7✔
2080
        // Regex for ${ENV_VAR} format.
7✔
2081
        var reEnvVarWithBrackets = regexp.MustCompile(
7✔
2082
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}$`,
7✔
2083
        )
7✔
2084

7✔
2085
        // Regex for ${ENV_VAR:-DEFAULT} format.
7✔
2086
        var reEnvVarWithDefault = regexp.MustCompile(
7✔
2087
                `^\$\{([a-zA-Z_][a-zA-Z0-9_]*):-([\S]+)\}$`,
7✔
2088
        )
7✔
2089

7✔
2090
        // Match against supported formats.
7✔
2091
        switch {
7✔
2092
        case reEnvVarWithDefault.MatchString(value):
3✔
2093
                matches := reEnvVarWithDefault.FindStringSubmatch(value)
3✔
2094
                envVariable := matches[1]
3✔
2095
                defaultValue := matches[2]
3✔
2096
                if envValue := os.Getenv(envVariable); envValue != "" {
4✔
2097
                        return envValue
1✔
2098
                }
1✔
2099

2100
                return defaultValue
2✔
2101

2102
        case reEnvVarWithBrackets.MatchString(value):
×
2103
                matches := reEnvVarWithBrackets.FindStringSubmatch(value)
×
2104
                envVariable := matches[1]
×
2105
                envValue := os.Getenv(envVariable)
×
2106

×
2107
                return envValue
×
2108

2109
        case reEnvVar.MatchString(value):
3✔
2110
                matches := reEnvVar.FindStringSubmatch(value)
3✔
2111
                envVariable := matches[1]
3✔
2112
                envValue := os.Getenv(envVariable)
3✔
2113

3✔
2114
                return envValue
3✔
2115
        }
2116

2117
        return value
1✔
2118
}
2119

2120
// extractBtcdRPCParams attempts to extract the RPC credentials for an existing
2121
// btcd instance. The passed path is expected to be the location of btcd's
2122
// application data directory on the target system.
2123
func extractBtcdRPCParams(btcdConfigPath string) (string, string, error) {
×
2124
        // First, we'll open up the btcd configuration file found at the target
×
2125
        // destination.
×
2126
        btcdConfigFile, err := os.Open(btcdConfigPath)
×
2127
        if err != nil {
×
2128
                return "", "", err
×
2129
        }
×
2130
        defer func() { _ = btcdConfigFile.Close() }()
×
2131

2132
        // With the file open extract the contents of the configuration file so
2133
        // we can attempt to locate the RPC credentials.
2134
        configContents, err := io.ReadAll(btcdConfigFile)
×
2135
        if err != nil {
×
2136
                return "", "", err
×
2137
        }
×
2138

2139
        // Attempt to locate the RPC user using a regular expression. If we
2140
        // don't have a match for our regular expression then we'll exit with
2141
        // an error.
2142
        rpcUserRegexp, err := regexp.Compile(`(?m)^\s*rpcuser\s*=\s*([^\s]+)`)
×
2143
        if err != nil {
×
2144
                return "", "", err
×
2145
        }
×
2146
        userSubmatches := rpcUserRegexp.FindSubmatch(configContents)
×
2147
        if userSubmatches == nil {
×
2148
                return "", "", fmt.Errorf("unable to find rpcuser in config")
×
2149
        }
×
2150

2151
        // Similarly, we'll use another regular expression to find the set
2152
        // rpcpass (if any). If we can't find the pass, then we'll exit with an
2153
        // error.
2154
        rpcPassRegexp, err := regexp.Compile(`(?m)^\s*rpcpass\s*=\s*([^\s]+)`)
×
2155
        if err != nil {
×
2156
                return "", "", err
×
2157
        }
×
2158
        passSubmatches := rpcPassRegexp.FindSubmatch(configContents)
×
2159
        if passSubmatches == nil {
×
2160
                return "", "", fmt.Errorf("unable to find rpcuser in config")
×
2161
        }
×
2162

2163
        return supplyEnvValue(string(userSubmatches[1])),
×
2164
                supplyEnvValue(string(passSubmatches[1])), nil
×
2165
}
2166

2167
// extractBitcoindRPCParams attempts to extract the RPC credentials for an
2168
// existing bitcoind node instance. The routine looks for a cookie first,
2169
// optionally following the datadir configuration option in the bitcoin.conf. If
2170
// it doesn't find one, it looks for rpcuser/rpcpassword.
2171
func extractBitcoindRPCParams(networkName, bitcoindDataDir, bitcoindConfigPath,
2172
        rpcCookiePath string) (string, string, string, string, error) {
×
2173

×
2174
        // First, we'll open up the bitcoind configuration file found at the
×
2175
        // target destination.
×
2176
        bitcoindConfigFile, err := os.Open(bitcoindConfigPath)
×
2177
        if err != nil {
×
2178
                return "", "", "", "", err
×
2179
        }
×
2180
        defer func() { _ = bitcoindConfigFile.Close() }()
×
2181

2182
        // With the file open extract the contents of the configuration file so
2183
        // we can attempt to locate the RPC credentials.
2184
        configContents, err := io.ReadAll(bitcoindConfigFile)
×
2185
        if err != nil {
×
2186
                return "", "", "", "", err
×
2187
        }
×
2188

2189
        // First, we'll look for the ZMQ hosts providing raw block and raw
2190
        // transaction notifications.
2191
        zmqBlockHostRE, err := regexp.Compile(
×
2192
                `(?m)^\s*zmqpubrawblock\s*=\s*([^\s]+)`,
×
2193
        )
×
2194
        if err != nil {
×
2195
                return "", "", "", "", err
×
2196
        }
×
2197
        zmqBlockHostSubmatches := zmqBlockHostRE.FindSubmatch(configContents)
×
2198
        if len(zmqBlockHostSubmatches) < 2 {
×
2199
                return "", "", "", "", fmt.Errorf("unable to find " +
×
2200
                        "zmqpubrawblock in config")
×
2201
        }
×
2202
        zmqTxHostRE, err := regexp.Compile(`(?m)^\s*zmqpubrawtx\s*=\s*([^\s]+)`)
×
2203
        if err != nil {
×
2204
                return "", "", "", "", err
×
2205
        }
×
2206
        zmqTxHostSubmatches := zmqTxHostRE.FindSubmatch(configContents)
×
2207
        if len(zmqTxHostSubmatches) < 2 {
×
2208
                return "", "", "", "", errors.New("unable to find zmqpubrawtx " +
×
2209
                        "in config")
×
2210
        }
×
2211
        zmqBlockHost := string(zmqBlockHostSubmatches[1])
×
2212
        zmqTxHost := string(zmqTxHostSubmatches[1])
×
2213
        if err := checkZMQOptions(zmqBlockHost, zmqTxHost); err != nil {
×
2214
                return "", "", "", "", err
×
2215
        }
×
2216

2217
        // Next, we'll try to find an auth cookie. We need to detect the chain
2218
        // by seeing if one is specified in the configuration file.
2219
        dataDir := filepath.Dir(bitcoindConfigPath)
×
2220
        if bitcoindDataDir != "" {
×
2221
                dataDir = bitcoindDataDir
×
2222
        }
×
2223
        dataDirRE, err := regexp.Compile(`(?m)^\s*datadir\s*=\s*([^\s]+)`)
×
2224
        if err != nil {
×
2225
                return "", "", "", "", err
×
2226
        }
×
2227
        dataDirSubmatches := dataDirRE.FindSubmatch(configContents)
×
2228
        if dataDirSubmatches != nil {
×
2229
                dataDir = string(dataDirSubmatches[1])
×
2230
        }
×
2231

2232
        var chainDir string
×
2233
        switch networkName {
×
2234
        case "mainnet":
×
2235
                chainDir = ""
×
2236
        case "regtest", "testnet3", "testnet4", "signet":
×
2237
                chainDir = networkName
×
2238
        default:
×
2239
                return "", "", "", "", fmt.Errorf("unexpected networkname %v", networkName)
×
2240
        }
2241

2242
        cookiePath := filepath.Join(dataDir, chainDir, ".cookie")
×
2243
        if rpcCookiePath != "" {
×
2244
                cookiePath = rpcCookiePath
×
2245
        }
×
2246
        cookie, err := os.ReadFile(cookiePath)
×
2247
        if err == nil {
×
2248
                splitCookie := strings.Split(string(cookie), ":")
×
2249
                if len(splitCookie) == 2 {
×
2250
                        return splitCookie[0], splitCookie[1], zmqBlockHost,
×
2251
                                zmqTxHost, nil
×
2252
                }
×
2253
        }
2254

2255
        // We didn't find a cookie, so we attempt to locate the RPC user using
2256
        // a regular expression. If we  don't have a match for our regular
2257
        // expression then we'll exit with an error.
2258
        rpcUserRegexp, err := regexp.Compile(`(?m)^\s*rpcuser\s*=\s*([^\s]+)`)
×
2259
        if err != nil {
×
2260
                return "", "", "", "", err
×
2261
        }
×
2262
        userSubmatches := rpcUserRegexp.FindSubmatch(configContents)
×
2263

×
2264
        // Similarly, we'll use another regular expression to find the set
×
2265
        // rpcpass (if any). If we can't find the pass, then we'll exit with an
×
2266
        // error.
×
2267
        rpcPassRegexp, err := regexp.Compile(`(?m)^\s*rpcpassword\s*=\s*([^\s]+)`)
×
2268
        if err != nil {
×
2269
                return "", "", "", "", err
×
2270
        }
×
2271
        passSubmatches := rpcPassRegexp.FindSubmatch(configContents)
×
2272

×
2273
        // Exit with an error if the cookie file, is defined in config, and
×
2274
        // can not be found, with both rpcuser and rpcpassword undefined.
×
2275
        if rpcCookiePath != "" && userSubmatches == nil && passSubmatches == nil {
×
2276
                return "", "", "", "", fmt.Errorf("unable to open cookie file (%v)",
×
2277
                        rpcCookiePath)
×
2278
        }
×
2279

2280
        if userSubmatches == nil {
×
2281
                return "", "", "", "", fmt.Errorf("unable to find rpcuser in " +
×
2282
                        "config")
×
2283
        }
×
2284
        if passSubmatches == nil {
×
2285
                return "", "", "", "", fmt.Errorf("unable to find rpcpassword " +
×
2286
                        "in config")
×
2287
        }
×
2288

2289
        return supplyEnvValue(string(userSubmatches[1])),
×
2290
                supplyEnvValue(string(passSubmatches[1])),
×
2291
                zmqBlockHost, zmqTxHost, nil
×
2292
}
2293

2294
// checkZMQOptions ensures that the provided addresses to use as the hosts for
2295
// ZMQ rawblock and rawtx notifications are different.
2296
func checkZMQOptions(zmqBlockHost, zmqTxHost string) error {
×
2297
        if zmqBlockHost == zmqTxHost {
×
2298
                return errors.New("zmqpubrawblock and zmqpubrawtx must be set " +
×
2299
                        "to different addresses")
×
2300
        }
×
2301

2302
        return nil
×
2303
}
2304

2305
// checkEstimateMode ensures that the provided estimate mode is legal.
2306
func checkEstimateMode(estimateMode string) error {
×
2307
        for _, mode := range bitcoindEstimateModes {
×
2308
                if estimateMode == mode {
×
2309
                        return nil
×
2310
                }
×
2311
        }
2312

2313
        return fmt.Errorf("estimatemode must be one of the following: %v",
×
2314
                bitcoindEstimateModes[:])
×
2315
}
2316

2317
// configToFlatMap converts the given config struct into a flat map of
2318
// key/value pairs using the dot notation we are used to from the config file
2319
// or command line flags. It also returns a map containing deprecated config
2320
// options.
2321
func configToFlatMap(cfg Config) (map[string]string,
2322
        map[string]struct{}, error) {
1✔
2323

1✔
2324
        result := make(map[string]string)
1✔
2325

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

1✔
2331
        // redact is the helper function that redacts sensitive values like
1✔
2332
        // passwords.
1✔
2333
        redact := func(key, value string) string {
329✔
2334
                sensitiveKeySuffixes := []string{
328✔
2335
                        "pass",
328✔
2336
                        "password",
328✔
2337
                        "dsn",
328✔
2338
                }
328✔
2339
                for _, suffix := range sensitiveKeySuffixes {
1,305✔
2340
                        if strings.HasSuffix(key, suffix) {
982✔
2341
                                return "[redacted]"
5✔
2342
                        }
5✔
2343
                }
2344

2345
                return value
323✔
2346
        }
2347

2348
        // printConfig is the helper function that goes into nested structs
2349
        // recursively. Because we call it recursively, we need to declare it
2350
        // before we define it.
2351
        var printConfig func(reflect.Value, string)
1✔
2352
        printConfig = func(obj reflect.Value, prefix string) {
64✔
2353
                // Turn struct pointers into the actual struct, so we can
63✔
2354
                // iterate over the fields as we would with a struct value.
63✔
2355
                if obj.Kind() == reflect.Ptr {
119✔
2356
                        obj = obj.Elem()
56✔
2357
                }
56✔
2358

2359
                // Abort on nil values.
2360
                if !obj.IsValid() {
75✔
2361
                        return
12✔
2362
                }
12✔
2363

2364
                // Loop over all fields of the struct and inspect the type.
2365
                for i := 0; i < obj.NumField(); i++ {
458✔
2366
                        field := obj.Field(i)
407✔
2367
                        fieldType := obj.Type().Field(i)
407✔
2368

407✔
2369
                        longName := fieldType.Tag.Get("long")
407✔
2370
                        namespace := fieldType.Tag.Get("namespace")
407✔
2371
                        group := fieldType.Tag.Get("group")
407✔
2372
                        hidden := fieldType.Tag.Get("hidden")
407✔
2373

407✔
2374
                        switch {
407✔
2375
                        // We have a long name defined, this is a config value.
2376
                        case longName != "":
328✔
2377
                                key := longName
328✔
2378
                                if prefix != "" {
556✔
2379
                                        key = prefix + "." + key
228✔
2380
                                }
228✔
2381

2382
                                // Add the value directly to the flattened map.
2383
                                result[key] = redact(key, fmt.Sprintf(
328✔
2384
                                        "%v", field.Interface(),
328✔
2385
                                ))
328✔
2386

328✔
2387
                                // If there's a hidden flag, it's deprecated.
328✔
2388
                                if hidden == "true" && !field.IsZero() {
330✔
2389
                                        deprecated[key] = struct{}{}
2✔
2390
                                }
2✔
2391

2392
                        // We have no long name but a namespace, this is a
2393
                        // nested struct.
2394
                        case longName == "" && namespace != "":
56✔
2395
                                key := namespace
56✔
2396
                                if prefix != "" {
73✔
2397
                                        key = prefix + "." + key
17✔
2398
                                }
17✔
2399

2400
                                printConfig(field, key)
56✔
2401

2402
                        // Just a group means this is a dummy struct to house
2403
                        // multiple config values, the group name doesn't go
2404
                        // into the final field name.
2405
                        case longName == "" && group != "":
1✔
2406
                                printConfig(field, prefix)
1✔
2407

2408
                        // Anonymous means embedded struct. We need to recurse
2409
                        // into it but without adding anything to the prefix.
2410
                        case fieldType.Anonymous:
5✔
2411
                                printConfig(field, prefix)
5✔
2412

2413
                        default:
17✔
2414
                                continue
17✔
2415
                        }
2416
                }
2417
        }
2418

2419
        // Turn the whole config struct into a flat map.
2420
        printConfig(reflect.ValueOf(cfg), "")
1✔
2421

1✔
2422
        return result, deprecated, nil
1✔
2423
}
2424

2425
// logWarningsForDeprecation logs a warning if a deprecated config option is
2426
// set.
2427
func logWarningsForDeprecation(cfg Config) {
×
2428
        _, deprecated, err := configToFlatMap(cfg)
×
2429
        if err != nil {
×
2430
                ltndLog.Errorf("Convert configs to map: %v", err)
×
2431
        }
×
2432

2433
        for k := range deprecated {
×
2434
                ltndLog.Warnf("Config '%s' is deprecated, please remove it", k)
×
2435
        }
×
2436
}
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