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

lightningnetwork / lnd / 13974489001

20 Mar 2025 04:32PM UTC coverage: 56.292% (-2.9%) from 59.168%
13974489001

Pull #8754

github

web-flow
Merge aed149e6b into ea050d06f
Pull Request #8754: Add `Outbound` Remote Signer implementation

594 of 1713 new or added lines in 26 files covered. (34.68%)

23052 existing lines in 272 files now uncovered.

105921 of 188165 relevant lines covered (56.29%)

23796.34 hits per line

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

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

5
package lnd
6

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

252
        defaultPrunedNodeMaxPeers = 4
253
        defaultNeutrinoMaxPeers   = 8
254
)
255

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

446
        net tor.Net
447

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

494
        // RemoteSigner defines how to connect to a remote signer node. If this
495
        // is enabled, the node acts as a watch-only node in a remote signer
496
        // setup.
497
        RemoteSigner *lncfg.RemoteSigner `group:"remotesigner" namespace:"remotesigner"`
498

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

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

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

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

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

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

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

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

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

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

534
        // NumRestrictedSlots is the number of restricted slots we'll allocate
535
        // in the server.
536
        NumRestrictedSlots uint64 `long:"num-restricted-slots" description:"The number of restricted slots we'll allocate in the server."`
537
}
538

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

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

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

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

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

1✔
619
                Fee: &lncfg.Fee{
1✔
620
                        MinUpdateTimeout: lncfg.DefaultMinUpdateTimeout,
1✔
621
                        MaxUpdateTimeout: lncfg.DefaultMaxUpdateTimeout,
1✔
622
                },
1✔
623

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

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

777
        // Show the version and exit if the version flag was specified.
UNCOV
778
        appName := filepath.Base(os.Args[0])
×
UNCOV
779
        appName = strings.TrimSuffix(appName, filepath.Ext(appName))
×
UNCOV
780
        usageMessage := fmt.Sprintf("Use %s -h to show usage", appName)
×
UNCOV
781
        if preCfg.ShowVersion {
×
782
                fmt.Println(appName, "version", build.Version(),
×
783
                        "commit="+build.Commit)
×
784
                os.Exit(0)
×
785
        }
×
786

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

×
UNCOV
799
                configFilePath = filepath.Join(
×
UNCOV
800
                        configFileDir, lncfg.DefaultConfigFilename,
×
UNCOV
801
                )
×
802

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

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

×
824
                        return nil, err
×
825
                }
×
826

UNCOV
827
                configFileError = err
×
828
        }
829

830
        // Finally, parse the remaining command line options again to ensure
831
        // they take precedence.
UNCOV
832
        flagParser := flags.NewParser(&cfg, flags.Default)
×
UNCOV
833
        if _, err := flagParser.Parse(); err != nil {
×
834
                return nil, err
×
835
        }
×
836

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

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

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

×
861
                return nil, err
×
862
        }
×
863

864
        // Warn about missing config file only after all other configuration is
865
        // done. This prevents the warning on help messages and invalid options.
866
        // Note this should go directly before the return.
UNCOV
867
        if configFileError != nil {
×
UNCOV
868
                ltndLog.Warnf("%v", configFileError)
×
UNCOV
869
        }
×
870

871
        // Finally, log warnings for deprecated config options if they are set.
UNCOV
872
        logWarningsForDeprecation(*cleanCfg)
×
UNCOV
873

×
UNCOV
874
        return cleanCfg, nil
×
875
}
876

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

×
UNCOV
883
        // Special show command to list supported subsystems and exit.
×
UNCOV
884
        if cfg.DebugLevel == "show" {
×
UNCOV
885
                subLogMgr := build.NewSubLoggerManager()
×
UNCOV
886

×
UNCOV
887
                // Initialize logging at the default logging level.
×
UNCOV
888
                SetupLoggers(subLogMgr, interceptor)
×
UNCOV
889

×
UNCOV
890
                fmt.Println("Supported subsystems",
×
UNCOV
891
                        subLogMgr.SupportedSubsystems())
×
UNCOV
892
                os.Exit(0)
×
UNCOV
893
        }
×
894

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

×
UNCOV
907
                // If the watchtower's directory is set to the default, i.e. the
×
UNCOV
908
                // user has not requested a different location, we'll move the
×
UNCOV
909
                // location to be relative to the specified lnd directory.
×
UNCOV
910
                if cfg.Watchtower.TowerDir == defaultTowerDir {
×
UNCOV
911
                        cfg.Watchtower.TowerDir = filepath.Join(
×
UNCOV
912
                                cfg.DataDir, defaultTowerSubDirname,
×
UNCOV
913
                        )
×
UNCOV
914
                }
×
915
        }
916

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

935
                        str := "Failed to create lnd directory '%s': %v"
×
936
                        return mkErr(str, dir, err)
×
937
                }
938

UNCOV
939
                return nil
×
940
        }
941

942
        // IsSet returns true if an option has been set in either the config
943
        // file or by a flag.
UNCOV
944
        isSet := func(field string) (bool, error) {
×
UNCOV
945
                fieldName, ok := reflect.TypeOf(Config{}).FieldByName(field)
×
UNCOV
946
                if !ok {
×
947
                        str := "could not find field %s"
×
948
                        return false, mkErr(str, field)
×
949
                }
×
950

UNCOV
951
                long, ok := fieldName.Tag.Lookup("long")
×
UNCOV
952
                if !ok {
×
953
                        str := "field %s does not have a long tag"
×
954
                        return false, mkErr(str, field)
×
955
                }
×
956

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

×
UNCOV
974
                return (fileOption != nil && fileOption.IsSet()) ||
×
UNCOV
975
                                (fileOptionNested != nil && fileOptionNested.IsSet()) ||
×
UNCOV
976
                                (flagOption != nil && flagOption.IsSet()) ||
×
UNCOV
977
                                (flagOptionNested != nil && flagOptionNested.IsSet()),
×
UNCOV
978
                        nil
×
979
        }
980

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

×
UNCOV
1006
        // Ensure that the user didn't attempt to specify negative values for
×
UNCOV
1007
        // any of the autopilot params.
×
UNCOV
1008
        if cfg.Autopilot.MaxChannels < 0 {
×
1009
                str := "autopilot.maxchannels must be non-negative"
×
1010

×
1011
                return nil, mkErr(str)
×
1012
        }
×
UNCOV
1013
        if cfg.Autopilot.Allocation < 0 {
×
1014
                str := "autopilot.allocation must be non-negative"
×
1015

×
1016
                return nil, mkErr(str)
×
1017
        }
×
UNCOV
1018
        if cfg.Autopilot.MinChannelSize < 0 {
×
1019
                str := "autopilot.minchansize must be non-negative"
×
1020

×
1021
                return nil, mkErr(str)
×
1022
        }
×
UNCOV
1023
        if cfg.Autopilot.MaxChannelSize < 0 {
×
1024
                str := "autopilot.maxchansize must be non-negative"
×
1025

×
1026
                return nil, mkErr(str)
×
1027
        }
×
UNCOV
1028
        if cfg.Autopilot.MinConfs < 0 {
×
1029
                str := "autopilot.minconfs must be non-negative"
×
1030

×
1031
                return nil, mkErr(str)
×
1032
        }
×
UNCOV
1033
        if cfg.Autopilot.ConfTarget < 1 {
×
1034
                str := "autopilot.conftarget must be positive"
×
1035

×
1036
                return nil, mkErr(str)
×
1037
        }
×
1038

1039
        // Ensure that the specified values for the min and max channel size
1040
        // are within the bounds of the normal chan size constraints.
UNCOV
1041
        if cfg.Autopilot.MinChannelSize < int64(funding.MinChanFundingSize) {
×
1042
                cfg.Autopilot.MinChannelSize = int64(funding.MinChanFundingSize)
×
1043
        }
×
UNCOV
1044
        if cfg.Autopilot.MaxChannelSize > int64(MaxFundingAmount) {
×
1045
                cfg.Autopilot.MaxChannelSize = int64(MaxFundingAmount)
×
1046
        }
×
1047

UNCOV
1048
        if _, err := validateAtplCfg(cfg.Autopilot); err != nil {
×
1049
                return nil, mkErr("error validating autopilot: %v", err)
×
1050
        }
×
1051

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

1065
        // Ensure that the user specified values for the min and max channel
1066
        // size make sense.
UNCOV
1067
        if cfg.MaxChanSize < cfg.MinChanSize {
×
1068
                return nil, mkErr("invalid channel size parameters: "+
×
1069
                        "max channel size %v, must be no less than min chan "+
×
1070
                        "size %v", cfg.MaxChanSize, cfg.MinChanSize,
×
1071
                )
×
1072
        }
×
1073

1074
        // Don't allow superfluous --maxchansize greater than
1075
        // BOLT 02 soft-limit for non-wumbo channel
UNCOV
1076
        if !cfg.ProtocolOptions.Wumbo() &&
×
UNCOV
1077
                cfg.MaxChanSize > int64(MaxFundingAmount) {
×
1078

×
1079
                return nil, mkErr("invalid channel size parameters: "+
×
1080
                        "maximum channel size %v is greater than maximum "+
×
1081
                        "non-wumbo channel size %v", cfg.MaxChanSize,
×
1082
                        MaxFundingAmount,
×
1083
                )
×
1084
        }
×
1085

1086
        // Ensure that the amount data for revoked commitment transactions is
1087
        // stored if the watchtower client is active.
UNCOV
1088
        if cfg.DB.NoRevLogAmtData && cfg.WtClient.Active {
×
1089
                return nil, mkErr("revocation log amount data must be stored " +
×
1090
                        "if the watchtower client is active")
×
1091
        }
×
1092

1093
        // Ensure a valid max channel fee allocation was set.
UNCOV
1094
        if cfg.MaxChannelFeeAllocation <= 0 || cfg.MaxChannelFeeAllocation > 1 {
×
1095
                return nil, mkErr("invalid max channel fee allocation: %v, "+
×
1096
                        "must be within (0, 1]", cfg.MaxChannelFeeAllocation)
×
1097
        }
×
1098

UNCOV
1099
        if cfg.MaxCommitFeeRateAnchors < 1 {
×
1100
                return nil, mkErr("invalid max commit fee rate anchors: %v, "+
×
1101
                        "must be at least 1 sat/vByte",
×
1102
                        cfg.MaxCommitFeeRateAnchors)
×
1103
        }
×
1104

1105
        // Validate the Tor config parameters.
UNCOV
1106
        socks, err := lncfg.ParseAddressString(
×
UNCOV
1107
                cfg.Tor.SOCKS, strconv.Itoa(defaultTorSOCKSPort),
×
UNCOV
1108
                cfg.net.ResolveTCPAddr,
×
UNCOV
1109
        )
×
UNCOV
1110
        if err != nil {
×
1111
                return nil, err
×
1112
        }
×
UNCOV
1113
        cfg.Tor.SOCKS = socks.String()
×
UNCOV
1114

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

UNCOV
1128
        control, err := lncfg.ParseAddressString(
×
UNCOV
1129
                cfg.Tor.Control, strconv.Itoa(defaultTorControlPort),
×
UNCOV
1130
                cfg.net.ResolveTCPAddr,
×
UNCOV
1131
        )
×
UNCOV
1132
        if err != nil {
×
1133
                return nil, mkErr("error parsing tor control address: %v", err)
×
1134
        }
×
UNCOV
1135
        cfg.Tor.Control = control.String()
×
UNCOV
1136

×
UNCOV
1137
        // Ensure that tor socks host:port is not equal to tor control
×
UNCOV
1138
        // host:port. This would lead to lnd not starting up properly.
×
UNCOV
1139
        if cfg.Tor.SOCKS == cfg.Tor.Control {
×
1140
                str := "tor.socks and tor.control can not us the same host:port"
×
1141

×
1142
                return nil, mkErr(str)
×
1143
        }
×
1144

UNCOV
1145
        switch {
×
1146
        case cfg.Tor.V2 && cfg.Tor.V3:
×
1147
                return nil, mkErr("either tor.v2 or tor.v3 can be set, " +
×
1148
                        "but not both")
×
1149
        case cfg.DisableListen && (cfg.Tor.V2 || cfg.Tor.V3):
×
1150
                return nil, mkErr("listening must be enabled when enabling " +
×
1151
                        "inbound connections over Tor")
×
1152
        }
1153

UNCOV
1154
        if cfg.Tor.PrivateKeyPath == "" {
×
UNCOV
1155
                switch {
×
1156
                case cfg.Tor.V2:
×
1157
                        cfg.Tor.PrivateKeyPath = filepath.Join(
×
1158
                                lndDir, defaultTorV2PrivateKeyFilename,
×
1159
                        )
×
1160
                case cfg.Tor.V3:
×
1161
                        cfg.Tor.PrivateKeyPath = filepath.Join(
×
1162
                                lndDir, defaultTorV3PrivateKeyFilename,
×
1163
                        )
×
1164
                }
1165
        }
1166

UNCOV
1167
        if cfg.Tor.WatchtowerKeyPath == "" {
×
UNCOV
1168
                switch {
×
1169
                case cfg.Tor.V2:
×
1170
                        cfg.Tor.WatchtowerKeyPath = filepath.Join(
×
1171
                                cfg.Watchtower.TowerDir,
×
1172
                                defaultTorV2PrivateKeyFilename,
×
1173
                        )
×
1174
                case cfg.Tor.V3:
×
1175
                        cfg.Tor.WatchtowerKeyPath = filepath.Join(
×
1176
                                cfg.Watchtower.TowerDir,
×
1177
                                defaultTorV3PrivateKeyFilename,
×
1178
                        )
×
1179
                }
1180
        }
1181

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

UNCOV
1196
        if cfg.DisableListen && cfg.NAT {
×
1197
                return nil, mkErr("NAT traversal cannot be used when " +
×
1198
                        "listening is disabled")
×
1199
        }
×
UNCOV
1200
        if cfg.NAT && len(cfg.ExternalHosts) != 0 {
×
1201
                return nil, mkErr("NAT support and externalhosts are " +
×
1202
                        "mutually exclusive, only one should be selected")
×
1203
        }
×
1204

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

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

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

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

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

×
1278
                return nil, mkErr(str)
×
1279
        }
×
1280

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

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

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

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

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

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

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

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

UNCOV
1332
        cfg.Bitcoin.ChainDir = filepath.Join(
×
UNCOV
1333
                cfg.DataDir, defaultChainSubDirname, BitcoinChainName,
×
UNCOV
1334
        )
×
UNCOV
1335

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

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

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

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

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

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

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

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

UNCOV
1394
        towerDir := filepath.Join(
×
UNCOV
1395
                cfg.Watchtower.TowerDir, BitcoinChainName,
×
UNCOV
1396
                lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
×
UNCOV
1397
        )
×
UNCOV
1398

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

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

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

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

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

1446
        // Initialize logging at the default logging level.
UNCOV
1447
        SetupLoggers(cfg.SubLogMgr, interceptor)
×
UNCOV
1448

×
UNCOV
1449
        if cfg.MaxLogFiles != 0 {
×
1450
                if cfg.LogConfig.File.MaxLogFiles !=
×
1451
                        build.DefaultMaxLogFiles {
×
1452

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1653
                default:
×
1654
                }
1655
        }
1656

UNCOV
1657
        if defaultSynchronous {
×
UNCOV
1658
                cfg.DB.Sqlite.PragmaOptions = append(
×
UNCOV
1659
                        cfg.DB.Sqlite.PragmaOptions, "synchronous=full",
×
UNCOV
1660
                )
×
UNCOV
1661
        }
×
1662

UNCOV
1663
        if defaultAutoVacuum {
×
UNCOV
1664
                cfg.DB.Sqlite.PragmaOptions = append(
×
UNCOV
1665
                        cfg.DB.Sqlite.PragmaOptions, "auto_vacuum=incremental",
×
UNCOV
1666
                )
×
UNCOV
1667
        }
×
1668

UNCOV
1669
        if defaultFullfsync {
×
UNCOV
1670
                cfg.DB.Sqlite.PragmaOptions = append(
×
UNCOV
1671
                        cfg.DB.Sqlite.PragmaOptions, "fullfsync=true",
×
UNCOV
1672
                )
×
UNCOV
1673
        }
×
1674

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

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

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

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

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

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

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

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

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

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

1768
        // Validate that the node isn't configured as both a remote signer and a
1769
        // watch-only node.
NEW
1770
        if cfg.RemoteSigner.Enable && cfg.WatchOnlyNode.Enable {
×
NEW
1771
                return nil, fmt.Errorf("cannot enable both the remotesigner " +
×
NEW
1772
                        "and watchonly mode simultaneously")
×
UNCOV
1773
        }
×
1774

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

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

1805
        // All good, return the sanitized result.
UNCOV
1806
        return &cfg, nil
×
1807
}
1808

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

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

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

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

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

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

1872
                path = strings.Replace(path, "~", homeDir, 1)
×
1873
        }
1874

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

1880
func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
UNCOV
1881
        netParams chainreg.BitcoinNetParams) error {
×
UNCOV
1882

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

×
UNCOV
1894
                // If both RPCUser and RPCPass are set, we assume those
×
UNCOV
1895
                // credentials are good to use.
×
UNCOV
1896
                if conf.RPCUser != "" && conf.RPCPass != "" {
×
UNCOV
1897
                        return nil
×
UNCOV
1898
                }
×
1899

1900
                // Set the daemon name for displaying proper errors.
1901
                daemonName = btcdBackendName
×
1902
                confDir = conf.Dir
×
1903
                confFileBase = btcdBackendName
×
1904

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

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

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

1933
                // Set the daemon name for displaying proper errors.
UNCOV
1934
                daemonName = bitcoindBackendName
×
UNCOV
1935
                confDir = conf.Dir
×
UNCOV
1936
                confFile = conf.ConfigPath
×
UNCOV
1937
                confFileBase = BitcoinChainName
×
UNCOV
1938

×
UNCOV
1939
                // Resolves environment variable references in RPCUser
×
UNCOV
1940
                // and RPCPass fields.
×
UNCOV
1941
                conf.RPCUser = supplyEnvValue(conf.RPCUser)
×
UNCOV
1942
                conf.RPCPass = supplyEnvValue(conf.RPCPass)
×
UNCOV
1943

×
UNCOV
1944
                // Check that cookie and credentials don't contradict each
×
UNCOV
1945
                // other.
×
UNCOV
1946
                if (conf.RPCUser != "" || conf.RPCPass != "") &&
×
UNCOV
1947
                        conf.RPCCookie != "" {
×
1948

×
1949
                        return fmt.Errorf("please only provide either "+
×
1950
                                "%[1]v.rpccookie or %[1]v.rpcuser and "+
×
1951
                                "%[1]v.rpcpass", daemonName)
×
1952
                }
×
1953

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

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

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

1979
                        // If RPCUser and RPCPass are set and RPCPolling is
1980
                        // enabled, we assume the parameters are good to use.
1981
                        if conf.RPCPolling {
×
1982
                                return nil
×
1983
                        }
×
1984
                }
1985

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

×
1991
                        return fmt.Errorf("please set %[1]v.rpcuser and "+
×
1992
                                "%[1]v.rpcpass (or %[1]v.rpccookie) together "+
×
1993
                                "with %[1]v.zmqpubrawblock, %[1]v.zmqpubrawtx",
×
1994
                                daemonName)
×
1995
                }
×
1996
        }
1997

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

2006
        fmt.Println("Attempting automatic RPC configuration to " + daemonName)
×
2007

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

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

2035
        fmt.Printf("Automatically obtained %v's RPC credentials\n", daemonName)
×
2036
        return nil
×
2037
}
2038

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

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

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

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

2083
                return defaultValue
2✔
2084

2085
        case reEnvVarWithBrackets.MatchString(value):
×
2086
                matches := reEnvVarWithBrackets.FindStringSubmatch(value)
×
2087
                envVariable := matches[1]
×
2088
                envValue := os.Getenv(envVariable)
×
2089

×
2090
                return envValue
×
2091

2092
        case reEnvVar.MatchString(value):
3✔
2093
                matches := reEnvVar.FindStringSubmatch(value)
3✔
2094
                envVariable := matches[1]
3✔
2095
                envValue := os.Getenv(envVariable)
3✔
2096

3✔
2097
                return envValue
3✔
2098
        }
2099

2100
        return value
1✔
2101
}
2102

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

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

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

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

2146
        return supplyEnvValue(string(userSubmatches[1])),
×
2147
                supplyEnvValue(string(passSubmatches[1])), nil
×
2148
}
2149

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

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

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

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

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

2215
        var chainDir string
×
2216
        switch networkName {
×
2217
        case "mainnet":
×
2218
                chainDir = ""
×
2219
        case "regtest", "testnet3", "signet":
×
2220
                chainDir = networkName
×
2221
        default:
×
2222
                return "", "", "", "", fmt.Errorf("unexpected networkname %v", networkName)
×
2223
        }
2224

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

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

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

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

2263
        if userSubmatches == nil {
×
2264
                return "", "", "", "", fmt.Errorf("unable to find rpcuser in " +
×
2265
                        "config")
×
2266
        }
×
2267
        if passSubmatches == nil {
×
2268
                return "", "", "", "", fmt.Errorf("unable to find rpcpassword " +
×
2269
                        "in config")
×
2270
        }
×
2271

2272
        return supplyEnvValue(string(userSubmatches[1])),
×
2273
                supplyEnvValue(string(passSubmatches[1])),
×
2274
                zmqBlockHost, zmqTxHost, nil
×
2275
}
2276

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

UNCOV
2285
        return nil
×
2286
}
2287

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

2296
        return fmt.Errorf("estimatemode must be one of the following: %v",
×
2297
                bitcoindEstimateModes[:])
×
2298
}
2299

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

1✔
2307
        result := make(map[string]string)
1✔
2308

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

1✔
2314
        // redact is the helper function that redacts sensitive values like
1✔
2315
        // passwords.
1✔
2316
        redact := func(key, value string) string {
322✔
2317
                sensitiveKeySuffixes := []string{
321✔
2318
                        "pass",
321✔
2319
                        "password",
321✔
2320
                        "dsn",
321✔
2321
                }
321✔
2322
                for _, suffix := range sensitiveKeySuffixes {
1,277✔
2323
                        if strings.HasSuffix(key, suffix) {
961✔
2324
                                return "[redacted]"
5✔
2325
                        }
5✔
2326
                }
2327

2328
                return value
316✔
2329
        }
2330

2331
        // printConfig is the helper function that goes into nested structs
2332
        // recursively. Because we call it recursively, we need to declare it
2333
        // before we define it.
2334
        var printConfig func(reflect.Value, string)
1✔
2335
        printConfig = func(obj reflect.Value, prefix string) {
66✔
2336
                // Turn struct pointers into the actual struct, so we can
65✔
2337
                // iterate over the fields as we would with a struct value.
65✔
2338
                if obj.Kind() == reflect.Ptr {
122✔
2339
                        obj = obj.Elem()
57✔
2340
                }
57✔
2341

2342
                // Abort on nil values.
2343
                if !obj.IsValid() {
77✔
2344
                        return
12✔
2345
                }
12✔
2346

2347
                // Loop over all fields of the struct and inspect the type.
2348
                for i := 0; i < obj.NumField(); i++ {
455✔
2349
                        field := obj.Field(i)
402✔
2350
                        fieldType := obj.Type().Field(i)
402✔
2351

402✔
2352
                        longName := fieldType.Tag.Get("long")
402✔
2353
                        namespace := fieldType.Tag.Get("namespace")
402✔
2354
                        group := fieldType.Tag.Get("group")
402✔
2355
                        hidden := fieldType.Tag.Get("hidden")
402✔
2356

402✔
2357
                        switch {
402✔
2358
                        // We have a long name defined, this is a config value.
2359
                        case longName != "":
321✔
2360
                                key := longName
321✔
2361
                                if prefix != "" {
544✔
2362
                                        key = prefix + "." + key
223✔
2363
                                }
223✔
2364

2365
                                // Add the value directly to the flattened map.
2366
                                result[key] = redact(key, fmt.Sprintf(
321✔
2367
                                        "%v", field.Interface(),
321✔
2368
                                ))
321✔
2369

321✔
2370
                                // If there's a hidden flag, it's deprecated.
321✔
2371
                                if hidden == "true" && !field.IsZero() {
322✔
2372
                                        deprecated[key] = struct{}{}
1✔
2373
                                }
1✔
2374

2375
                        // We have no long name but a namespace, this is a
2376
                        // nested struct.
2377
                        case longName == "" && namespace != "":
55✔
2378
                                key := namespace
55✔
2379
                                if prefix != "" {
70✔
2380
                                        key = prefix + "." + key
15✔
2381
                                }
15✔
2382

2383
                                printConfig(field, key)
55✔
2384

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

2391
                        // Anonymous means embedded struct. We need to recurse
2392
                        // into it but without adding anything to the prefix.
2393
                        case fieldType.Anonymous:
8✔
2394
                                printConfig(field, prefix)
8✔
2395

2396
                        default:
17✔
2397
                                continue
17✔
2398
                        }
2399
                }
2400
        }
2401

2402
        // Turn the whole config struct into a flat map.
2403
        printConfig(reflect.ValueOf(cfg), "")
1✔
2404

1✔
2405
        return result, deprecated, nil
1✔
2406
}
2407

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

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