From 409d2c9a90de520fa6a7decf40e5fadd14e77306 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 30 Apr 2020 09:34:10 +0200 Subject: [PATCH 01/20] lnd+config: export config struct and LoadConfig As a preparation to be moved to the lncfg package, the main struct and functions related to configuration are exported. --- chainregistry.go | 2 +- config.go | 12 ++++++------ fundingmanager_test.go | 2 +- lnd.go | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/chainregistry.go b/chainregistry.go index 4f5320fa..5e8e0e30 100644 --- a/chainregistry.go +++ b/chainregistry.go @@ -160,7 +160,7 @@ type chainControl struct { // full-node, another backed by a running bitcoind full-node, and the other // backed by a running neutrino light client instance. When running with a // neutrino light client instance, `neutrinoCS` must be non-nil. -func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, +func newChainControlFromConfig(cfg *Config, chanDB *channeldb.DB, privateWalletPw, publicWalletPw []byte, birthday time.Time, recoveryWindow uint32, wallet *wallet.Wallet, neutrinoCS *neutrino.ChainService) (*chainControl, error) { diff --git a/config.go b/config.go index bc599a16..005d7e26 100644 --- a/config.go +++ b/config.go @@ -238,11 +238,11 @@ type torConfig struct { WatchtowerKeyPath string `long:"watchtowerkeypath" description:"The path to the private key of the watchtower onion service being created"` } -// config defines the configuration options for lnd. +// Config defines the configuration options for lnd. // -// See loadConfig for further details regarding the configuration +// See LoadConfig for further details regarding the configuration // loading+parsing process. -type config struct { +type Config struct { ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"` LndDir string `long:"lnddir" description:"The base directory that contains lnd's data, logs, configuration file, etc."` @@ -365,7 +365,7 @@ type config struct { AllowCircularRoute bool `long:"allow-circular-route" description:"If true, our node will allow htlc forwards that arrive and depart on the same channel."` } -// loadConfig initializes and parses the config using a config file and command +// LoadConfig initializes and parses the config using a config file and command // line options. // // The configuration proceeds as follows: @@ -373,8 +373,8 @@ type config struct { // 2) Pre-parse the command line to check for an alternative config file // 3) Load configuration file overwriting defaults with any specified options // 4) Parse CLI options and overwrite/add any specified options -func loadConfig() (*config, error) { - defaultCfg := config{ +func LoadConfig() (*Config, error) { + defaultCfg := Config{ LndDir: defaultLndDir, ConfigFile: defaultConfigFile, DataDir: defaultDataDir, diff --git a/fundingmanager_test.go b/fundingmanager_test.go index 408c1052..45a9bdee 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -3084,7 +3084,7 @@ func TestGetUpfrontShutdownScript(t *testing.T) { } // Set the command line option in config as needed. - cfg = &config{EnableUpfrontShutdown: test.localEnabled} + cfg = &Config{EnableUpfrontShutdown: test.localEnabled} addr, err := getUpfrontShutdownScript( &mockPeer, test.upfrontScript, test.getScript, diff --git a/lnd.go b/lnd.go index 291a33cf..7f3404ba 100644 --- a/lnd.go +++ b/lnd.go @@ -52,7 +52,7 @@ import ( ) var ( - cfg *config + cfg *Config registeredChains = newChainRegistry() // networkDir is the path to the directory of the currently active @@ -156,7 +156,7 @@ func Main(lisCfg ListenerCfg) error { // Load the configuration, and parse any command line options. This // function will also set up logging properly. - loadedConfig, err := loadConfig() + loadedConfig, err := LoadConfig() if err != nil { return err } From 9ed1cd623f197959a2b4993951b2ca62abf0793c Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 13 May 2020 15:57:30 +0200 Subject: [PATCH 02/20] config+lncfg: move config structs to lncfg, drop suffix --- chainregistry.go | 5 +- config.go | 120 +++++++++------------------------------------ lncfg/autopilot.go | 14 ++++++ lncfg/bitcoind.go | 13 +++++ lncfg/btcd.go | 11 +++++ lncfg/chain.go | 24 +++++++++ lncfg/neutrino.go | 15 ++++++ lncfg/tor.go | 16 ++++++ pilot.go | 7 +-- 9 files changed, 122 insertions(+), 103 deletions(-) create mode 100644 lncfg/autopilot.go create mode 100644 lncfg/bitcoind.go create mode 100644 lncfg/btcd.go create mode 100644 lncfg/chain.go create mode 100644 lncfg/neutrino.go create mode 100644 lncfg/tor.go diff --git a/chainregistry.go b/chainregistry.go index 5e8e0e30..be1ec453 100644 --- a/chainregistry.go +++ b/chainregistry.go @@ -28,6 +28,7 @@ import ( "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" @@ -263,7 +264,7 @@ func newChainControlFromConfig(cfg *Config, chanDB *channeldb.DB, ) case "bitcoind", "litecoind": - var bitcoindMode *bitcoindConfig + var bitcoindMode *lncfg.Bitcoind switch { case cfg.Bitcoin.Active: bitcoindMode = cfg.BitcoindMode @@ -388,7 +389,7 @@ func newChainControlFromConfig(cfg *Config, chanDB *channeldb.DB, // connection. If a raw cert was specified in the config, then // we'll set that directly. Otherwise, we attempt to read the // cert from the path specified in the config. - var btcdMode *btcdConfig + var btcdMode *lncfg.Btcd switch { case cfg.Bitcoin.Active: btcdMode = cfg.BtcdMode diff --git a/config.go b/config.go index 005d7e26..1acc9840 100644 --- a/config.go +++ b/config.go @@ -30,7 +30,6 @@ import ( "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" - "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/tor" ) @@ -163,81 +162,6 @@ var ( bitcoindEstimateModes = [2]string{"ECONOMICAL", defaultBitcoindEstimateMode} ) -type chainConfig struct { - Active bool `long:"active" description:"If the chain should be active or not."` - ChainDir string `long:"chaindir" description:"The directory to store the chain's data within."` - - Node string `long:"node" description:"The blockchain interface to use." choice:"btcd" choice:"bitcoind" choice:"neutrino" choice:"ltcd" choice:"litecoind"` - - MainNet bool `long:"mainnet" description:"Use the main network"` - TestNet3 bool `long:"testnet" description:"Use the test network"` - SimNet bool `long:"simnet" description:"Use the simulation test network"` - RegTest bool `long:"regtest" description:"Use the regression test network"` - - DefaultNumChanConfs int `long:"defaultchanconfs" description:"The default number of confirmations a channel must have before it's considered open. If this is not set, we will scale the value according to the channel size."` - DefaultRemoteDelay int `long:"defaultremotedelay" description:"The default number of blocks we will require our channel counterparty to wait before accessing its funds in case of unilateral close. If this is not set, we will scale the value according to the channel size."` - MinHTLCIn lnwire.MilliSatoshi `long:"minhtlc" description:"The smallest HTLC we are willing to accept on our channels, in millisatoshi"` - MinHTLCOut lnwire.MilliSatoshi `long:"minhtlcout" description:"The smallest HTLC we are willing to send out on our channels, in millisatoshi"` - BaseFee lnwire.MilliSatoshi `long:"basefee" description:"The base fee in millisatoshi we will charge for forwarding payments on our channels"` - FeeRate lnwire.MilliSatoshi `long:"feerate" description:"The fee rate used when forwarding payments on our channels. The total fee charged is basefee + (amount * feerate / 1000000), where amount is the forwarded amount."` - TimeLockDelta uint32 `long:"timelockdelta" description:"The CLTV delta we will subtract from a forwarded HTLC's timelock value"` -} - -type neutrinoConfig struct { - AddPeers []string `short:"a" long:"addpeer" description:"Add a peer to connect with at startup"` - ConnectPeers []string `long:"connect" description:"Connect only to the specified peers at startup"` - MaxPeers int `long:"maxpeers" description:"Max number of inbound and outbound peers"` - BanDuration time.Duration `long:"banduration" description:"How long to ban misbehaving peers. Valid time units are {s, m, h}. Minimum 1 second"` - BanThreshold uint32 `long:"banthreshold" description:"Maximum allowed ban score before disconnecting and banning misbehaving peers."` - FeeURL string `long:"feeurl" description:"Optional URL for fee estimation. If a URL is not specified, static fees will be used for estimation."` - AssertFilterHeader string `long:"assertfilterheader" description:"Optional filter header in height:hash format to assert the state of neutrino's filter header chain on startup. If the assertion does not hold, then the filter header chain will be re-synced from the genesis block."` -} - -type btcdConfig struct { - Dir string `long:"dir" description:"The base directory that contains the node's data, logs, configuration file, etc."` - RPCHost string `long:"rpchost" description:"The daemon's rpc listening address. If a port is omitted, then the default port for the selected chain parameters will be used."` - RPCUser string `long:"rpcuser" description:"Username for RPC connections"` - RPCPass string `long:"rpcpass" default-mask:"-" description:"Password for RPC connections"` - RPCCert string `long:"rpccert" description:"File containing the daemon's certificate file"` - RawRPCCert string `long:"rawrpccert" description:"The raw bytes of the daemon's PEM-encoded certificate chain which will be used to authenticate the RPC connection."` -} - -type bitcoindConfig struct { - Dir string `long:"dir" description:"The base directory that contains the node's data, logs, configuration file, etc."` - RPCHost string `long:"rpchost" description:"The daemon's rpc listening address. If a port is omitted, then the default port for the selected chain parameters will be used."` - RPCUser string `long:"rpcuser" description:"Username for RPC connections"` - RPCPass string `long:"rpcpass" default-mask:"-" description:"Password for RPC connections"` - ZMQPubRawBlock string `long:"zmqpubrawblock" description:"The address listening for ZMQ connections to deliver raw block notifications"` - ZMQPubRawTx string `long:"zmqpubrawtx" description:"The address listening for ZMQ connections to deliver raw transaction notifications"` - EstimateMode string `long:"estimatemode" description:"The fee estimate mode. Must be either ECONOMICAL or CONSERVATIVE."` -} - -type autoPilotConfig struct { - Active bool `long:"active" description:"If the autopilot agent should be active or not."` - Heuristic map[string]float64 `long:"heuristic" description:"Heuristic to activate, and the weight to give it during scoring."` - MaxChannels int `long:"maxchannels" description:"The maximum number of channels that should be created"` - Allocation float64 `long:"allocation" description:"The percentage of total funds that should be committed to automatic channel establishment"` - MinChannelSize int64 `long:"minchansize" description:"The smallest channel that the autopilot agent should create"` - MaxChannelSize int64 `long:"maxchansize" description:"The largest channel that the autopilot agent should create"` - Private bool `long:"private" description:"Whether the channels created by the autopilot agent should be private or not. Private channels won't be announced to the network."` - MinConfs int32 `long:"minconfs" description:"The minimum number of confirmations each of your inputs in funding transactions created by the autopilot agent must have."` - ConfTarget uint32 `long:"conftarget" description:"The confirmation target (in blocks) for channels opened by autopilot."` -} - -type torConfig struct { - Active bool `long:"active" description:"Allow outbound and inbound connections to be routed through Tor"` - SOCKS string `long:"socks" description:"The host:port that Tor's exposed SOCKS5 proxy is listening on"` - DNS string `long:"dns" description:"The DNS server as host:port that Tor will use for SRV queries - NOTE must have TCP resolution enabled"` - StreamIsolation bool `long:"streamisolation" description:"Enable Tor stream isolation by randomizing user credentials for each connection."` - Control string `long:"control" description:"The host:port that Tor is listening on for Tor control connections"` - TargetIPAddress string `long:"targetipaddress" description:"IP address that Tor should use as the target of the hidden service"` - Password string `long:"password" description:"The password used to arrive at the HashedControlPassword for the control port. If provided, the HASHEDPASSWORD authentication method will be used instead of the SAFECOOKIE one."` - V2 bool `long:"v2" description:"Automatically set up a v2 onion service to listen for inbound connections"` - V3 bool `long:"v3" description:"Automatically set up a v3 onion service to listen for inbound connections"` - PrivateKeyPath string `long:"privatekeypath" description:"The path to the private key of the onion service being created"` - WatchtowerKeyPath string `long:"watchtowerkeypath" description:"The path to the private key of the watchtower onion service being created"` -} - // Config defines the configuration options for lnd. // // See LoadConfig for further details regarding the configuration @@ -294,18 +218,18 @@ type Config struct { MaxPendingChannels int `long:"maxpendingchannels" description:"The maximum number of incoming pending channels permitted per peer."` BackupFilePath string `long:"backupfilepath" description:"The target location of the channel backup file"` - Bitcoin *chainConfig `group:"Bitcoin" namespace:"bitcoin"` - BtcdMode *btcdConfig `group:"btcd" namespace:"btcd"` - BitcoindMode *bitcoindConfig `group:"bitcoind" namespace:"bitcoind"` - NeutrinoMode *neutrinoConfig `group:"neutrino" namespace:"neutrino"` + Bitcoin *lncfg.Chain `group:"Bitcoin" namespace:"bitcoin"` + BtcdMode *lncfg.Btcd `group:"btcd" namespace:"btcd"` + BitcoindMode *lncfg.Bitcoind `group:"bitcoind" namespace:"bitcoind"` + NeutrinoMode *lncfg.Neutrino `group:"neutrino" namespace:"neutrino"` - Litecoin *chainConfig `group:"Litecoin" namespace:"litecoin"` - LtcdMode *btcdConfig `group:"ltcd" namespace:"ltcd"` - LitecoindMode *bitcoindConfig `group:"litecoind" namespace:"litecoind"` + Litecoin *lncfg.Chain `group:"Litecoin" namespace:"litecoin"` + LtcdMode *lncfg.Btcd `group:"ltcd" namespace:"ltcd"` + LitecoindMode *lncfg.Bitcoind `group:"litecoind" namespace:"litecoind"` - Autopilot *autoPilotConfig `group:"Autopilot" namespace:"autopilot"` + Autopilot *lncfg.AutoPilot `group:"Autopilot" namespace:"autopilot"` - Tor *torConfig `group:"Tor" namespace:"tor"` + Tor *lncfg.Tor `group:"Tor" namespace:"tor"` SubRPCServers *subRPCServerConfigs `group:"subrpc"` @@ -385,7 +309,7 @@ func LoadConfig() (*Config, error) { MaxLogFiles: defaultMaxLogFiles, MaxLogFileSize: defaultMaxLogFileSize, AcceptorTimeout: defaultAcceptorTimeout, - Bitcoin: &chainConfig{ + Bitcoin: &lncfg.Chain{ MinHTLCIn: defaultBitcoinMinHTLCInMSat, MinHTLCOut: defaultBitcoinMinHTLCOutMSat, BaseFee: DefaultBitcoinBaseFeeMSat, @@ -393,17 +317,17 @@ func LoadConfig() (*Config, error) { TimeLockDelta: DefaultBitcoinTimeLockDelta, Node: "btcd", }, - BtcdMode: &btcdConfig{ + BtcdMode: &lncfg.Btcd{ Dir: defaultBtcdDir, RPCHost: defaultRPCHost, RPCCert: defaultBtcdRPCCertFile, }, - BitcoindMode: &bitcoindConfig{ + BitcoindMode: &lncfg.Bitcoind{ Dir: defaultBitcoindDir, RPCHost: defaultRPCHost, EstimateMode: defaultBitcoindEstimateMode, }, - Litecoin: &chainConfig{ + Litecoin: &lncfg.Chain{ MinHTLCIn: defaultLitecoinMinHTLCInMSat, MinHTLCOut: defaultLitecoinMinHTLCOutMSat, BaseFee: defaultLitecoinBaseFeeMSat, @@ -411,12 +335,12 @@ func LoadConfig() (*Config, error) { TimeLockDelta: defaultLitecoinTimeLockDelta, Node: "ltcd", }, - LtcdMode: &btcdConfig{ + LtcdMode: &lncfg.Btcd{ Dir: defaultLtcdDir, RPCHost: defaultRPCHost, RPCCert: defaultLtcdRPCCertFile, }, - LitecoindMode: &bitcoindConfig{ + LitecoindMode: &lncfg.Bitcoind{ Dir: defaultLitecoindDir, RPCHost: defaultRPCHost, EstimateMode: defaultBitcoindEstimateMode, @@ -430,7 +354,7 @@ func LoadConfig() (*Config, error) { SignRPC: &signrpc.Config{}, RouterRPC: routerrpc.DefaultConfig(), }, - Autopilot: &autoPilotConfig{ + Autopilot: &lncfg.AutoPilot{ MaxChannels: 5, Allocation: 0.6, MinChannelSize: int64(minChanFundingSize), @@ -451,7 +375,7 @@ func LoadConfig() (*Config, error) { MinChanSize: int64(minChanFundingSize), NumGraphSyncPeers: defaultMinPeers, HistoricalSyncInterval: discovery.DefaultHistoricalSyncInterval, - Tor: &torConfig{ + Tor: &lncfg.Tor{ SOCKS: defaultTorSOCKS, DNS: defaultTorDNS, Control: defaultTorControl, @@ -1205,7 +1129,7 @@ func cleanAndExpandPath(path string) string { return filepath.Clean(os.ExpandEnv(path)) } -func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode, +func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{}, net chainCode, funcName string) error { // First, we'll check our node config to make sure the RPC parameters @@ -1213,7 +1137,7 @@ func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode, // depending on the backend node. var daemonName, confDir, confFile string switch conf := nodeConfig.(type) { - case *btcdConfig: + case *lncfg.Btcd: // If both RPCUser and RPCPass are set, we assume those // credentials are good to use. if conf.RPCUser != "" && conf.RPCPass != "" { @@ -1239,7 +1163,7 @@ func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode, "%[1]v.rpcuser, %[1]v.rpcpass", daemonName) } - case *bitcoindConfig: + case *lncfg.Bitcoind: // Ensure that if the ZMQ options are set, that they are not // equal. if conf.ZMQPubRawBlock != "" && conf.ZMQPubRawTx != "" { @@ -1305,7 +1229,7 @@ func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode, confFile = filepath.Join(confDir, fmt.Sprintf("%v.conf", confFile)) switch cConfig.Node { case "btcd", "ltcd": - nConf := nodeConfig.(*btcdConfig) + nConf := nodeConfig.(*lncfg.Btcd) rpcUser, rpcPass, err := extractBtcdRPCParams(confFile) if err != nil { return fmt.Errorf("unable to extract RPC credentials:"+ @@ -1314,7 +1238,7 @@ func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode, } nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass case "bitcoind", "litecoind": - nConf := nodeConfig.(*bitcoindConfig) + nConf := nodeConfig.(*lncfg.Bitcoind) rpcUser, rpcPass, zmqBlockHost, zmqTxHost, err := extractBitcoindRPCParams(confFile) if err != nil { diff --git a/lncfg/autopilot.go b/lncfg/autopilot.go new file mode 100644 index 00000000..65d24287 --- /dev/null +++ b/lncfg/autopilot.go @@ -0,0 +1,14 @@ +package lncfg + +// AutoPilot holds the configuration options for the daemon's autopilot. +type AutoPilot struct { + Active bool `long:"active" description:"If the autopilot agent should be active or not."` + Heuristic map[string]float64 `long:"heuristic" description:"Heuristic to activate, and the weight to give it during scoring."` + MaxChannels int `long:"maxchannels" description:"The maximum number of channels that should be created"` + Allocation float64 `long:"allocation" description:"The percentage of total funds that should be committed to automatic channel establishment"` + MinChannelSize int64 `long:"minchansize" description:"The smallest channel that the autopilot agent should create"` + MaxChannelSize int64 `long:"maxchansize" description:"The largest channel that the autopilot agent should create"` + Private bool `long:"private" description:"Whether the channels created by the autopilot agent should be private or not. Private channels won't be announced to the network."` + MinConfs int32 `long:"minconfs" description:"The minimum number of confirmations each of your inputs in funding transactions created by the autopilot agent must have."` + ConfTarget uint32 `long:"conftarget" description:"The confirmation target (in blocks) for channels opened by autopilot."` +} diff --git a/lncfg/bitcoind.go b/lncfg/bitcoind.go new file mode 100644 index 00000000..6076a3b1 --- /dev/null +++ b/lncfg/bitcoind.go @@ -0,0 +1,13 @@ +package lncfg + +// Bitcoind holds the configuration options for the daemon's connection to +// bitcoind. +type Bitcoind struct { + Dir string `long:"dir" description:"The base directory that contains the node's data, logs, configuration file, etc."` + RPCHost string `long:"rpchost" description:"The daemon's rpc listening address. If a port is omitted, then the default port for the selected chain parameters will be used."` + RPCUser string `long:"rpcuser" description:"Username for RPC connections"` + RPCPass string `long:"rpcpass" default-mask:"-" description:"Password for RPC connections"` + ZMQPubRawBlock string `long:"zmqpubrawblock" description:"The address listening for ZMQ connections to deliver raw block notifications"` + ZMQPubRawTx string `long:"zmqpubrawtx" description:"The address listening for ZMQ connections to deliver raw transaction notifications"` + EstimateMode string `long:"estimatemode" description:"The fee estimate mode. Must be either ECONOMICAL or CONSERVATIVE."` +} diff --git a/lncfg/btcd.go b/lncfg/btcd.go new file mode 100644 index 00000000..f214bc88 --- /dev/null +++ b/lncfg/btcd.go @@ -0,0 +1,11 @@ +package lncfg + +// Btcd holds the configuration options for the daemon's connection to btcd. +type Btcd struct { + Dir string `long:"dir" description:"The base directory that contains the node's data, logs, configuration file, etc."` + RPCHost string `long:"rpchost" description:"The daemon's rpc listening address. If a port is omitted, then the default port for the selected chain parameters will be used."` + RPCUser string `long:"rpcuser" description:"Username for RPC connections"` + RPCPass string `long:"rpcpass" default-mask:"-" description:"Password for RPC connections"` + RPCCert string `long:"rpccert" description:"File containing the daemon's certificate file"` + RawRPCCert string `long:"rawrpccert" description:"The raw bytes of the daemon's PEM-encoded certificate chain which will be used to authenticate the RPC connection."` +} diff --git a/lncfg/chain.go b/lncfg/chain.go new file mode 100644 index 00000000..dbf3643b --- /dev/null +++ b/lncfg/chain.go @@ -0,0 +1,24 @@ +package lncfg + +import "github.com/lightningnetwork/lnd/lnwire" + +// Chain holds the configuration options for the daemon's chain settings. +type Chain struct { + Active bool `long:"active" description:"If the chain should be active or not."` + ChainDir string `long:"chaindir" description:"The directory to store the chain's data within."` + + Node string `long:"node" description:"The blockchain interface to use." choice:"btcd" choice:"bitcoind" choice:"neutrino" choice:"ltcd" choice:"litecoind"` + + MainNet bool `long:"mainnet" description:"Use the main network"` + TestNet3 bool `long:"testnet" description:"Use the test network"` + SimNet bool `long:"simnet" description:"Use the simulation test network"` + RegTest bool `long:"regtest" description:"Use the regression test network"` + + DefaultNumChanConfs int `long:"defaultchanconfs" description:"The default number of confirmations a channel must have before it's considered open. If this is not set, we will scale the value according to the channel size."` + DefaultRemoteDelay int `long:"defaultremotedelay" description:"The default number of blocks we will require our channel counterparty to wait before accessing its funds in case of unilateral close. If this is not set, we will scale the value according to the channel size."` + MinHTLCIn lnwire.MilliSatoshi `long:"minhtlc" description:"The smallest HTLC we are willing to accept on our channels, in millisatoshi"` + MinHTLCOut lnwire.MilliSatoshi `long:"minhtlcout" description:"The smallest HTLC we are willing to send out on our channels, in millisatoshi"` + BaseFee lnwire.MilliSatoshi `long:"basefee" description:"The base fee in millisatoshi we will charge for forwarding payments on our channels"` + FeeRate lnwire.MilliSatoshi `long:"feerate" description:"The fee rate used when forwarding payments on our channels. The total fee charged is basefee + (amount * feerate / 1000000), where amount is the forwarded amount."` + TimeLockDelta uint32 `long:"timelockdelta" description:"The CLTV delta we will subtract from a forwarded HTLC's timelock value"` +} diff --git a/lncfg/neutrino.go b/lncfg/neutrino.go new file mode 100644 index 00000000..b6f892bf --- /dev/null +++ b/lncfg/neutrino.go @@ -0,0 +1,15 @@ +package lncfg + +import "time" + +// Neutrino holds the configuration options for the daemon's connection to +// neutrino. +type Neutrino struct { + AddPeers []string `short:"a" long:"addpeer" description:"Add a peer to connect with at startup"` + ConnectPeers []string `long:"connect" description:"Connect only to the specified peers at startup"` + MaxPeers int `long:"maxpeers" description:"Max number of inbound and outbound peers"` + BanDuration time.Duration `long:"banduration" description:"How long to ban misbehaving peers. Valid time units are {s, m, h}. Minimum 1 second"` + BanThreshold uint32 `long:"banthreshold" description:"Maximum allowed ban score before disconnecting and banning misbehaving peers."` + FeeURL string `long:"feeurl" description:"Optional URL for fee estimation. If a URL is not specified, static fees will be used for estimation."` + AssertFilterHeader string `long:"assertfilterheader" description:"Optional filter header in height:hash format to assert the state of neutrino's filter header chain on startup. If the assertion does not hold, then the filter header chain will be re-synced from the genesis block."` +} diff --git a/lncfg/tor.go b/lncfg/tor.go new file mode 100644 index 00000000..e7070c38 --- /dev/null +++ b/lncfg/tor.go @@ -0,0 +1,16 @@ +package lncfg + +// Tor holds the configuration options for the daemon's connection to tor. +type Tor struct { + Active bool `long:"active" description:"Allow outbound and inbound connections to be routed through Tor"` + SOCKS string `long:"socks" description:"The host:port that Tor's exposed SOCKS5 proxy is listening on"` + DNS string `long:"dns" description:"The DNS server as host:port that Tor will use for SRV queries - NOTE must have TCP resolution enabled"` + StreamIsolation bool `long:"streamisolation" description:"Enable Tor stream isolation by randomizing user credentials for each connection."` + Control string `long:"control" description:"The host:port that Tor is listening on for Tor control connections"` + TargetIPAddress string `long:"targetipaddress" description:"IP address that Tor should use as the target of the hidden service"` + Password string `long:"password" description:"The password used to arrive at the HashedControlPassword for the control port. If provided, the HASHEDPASSWORD authentication method will be used instead of the SAFECOOKIE one."` + V2 bool `long:"v2" description:"Automatically set up a v2 onion service to listen for inbound connections"` + V3 bool `long:"v3" description:"Automatically set up a v3 onion service to listen for inbound connections"` + PrivateKeyPath string `long:"privatekeypath" description:"The path to the private key of the onion service being created"` + WatchtowerKeyPath string `long:"watchtowerkeypath" description:"The path to the private key of the watchtower onion service being created"` +} diff --git a/pilot.go b/pilot.go index b3acc423..8d9bc61e 100644 --- a/pilot.go +++ b/pilot.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/autopilot" + "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/tor" ) @@ -17,7 +18,7 @@ import ( // configuration is sane. Currently it checks that the heuristic configuration // makes sense. In case the config is valid, it will return a list of // WeightedHeuristics that can be combined for use with the autopilot agent. -func validateAtplCfg(cfg *autoPilotConfig) ([]*autopilot.WeightedHeuristic, +func validateAtplCfg(cfg *lncfg.AutoPilot) ([]*autopilot.WeightedHeuristic, error) { var ( @@ -138,8 +139,8 @@ var _ autopilot.ChannelController = (*chanController)(nil) // Agent instance based on the passed configuration structs. The agent and all // interfaces needed to drive it won't be launched before the Manager's // StartAgent method is called. -func initAutoPilot(svr *server, cfg *autoPilotConfig, chainCfg *chainConfig) ( - *autopilot.ManagerCfg, error) { +func initAutoPilot(svr *server, cfg *lncfg.AutoPilot, + chainCfg *lncfg.Chain) (*autopilot.ManagerCfg, error) { atplLog.Infof("Instantiating autopilot with active=%v, "+ "max_channels=%d, allocation=%f, min_chan_size=%d, "+ From d4d10b5c71780404c38ce3cc28f43b72d580869e Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 30 Apr 2020 09:55:32 +0200 Subject: [PATCH 03/20] config: extract default config into function To make it easy to extract the configuration parsing out of the main package, the default config is exported as its own function. --- config.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/config.go b/config.go index 1acc9840..46e06f6c 100644 --- a/config.go +++ b/config.go @@ -289,16 +289,9 @@ type Config struct { AllowCircularRoute bool `long:"allow-circular-route" description:"If true, our node will allow htlc forwards that arrive and depart on the same channel."` } -// LoadConfig initializes and parses the config using a config file and command -// line options. -// -// The configuration proceeds as follows: -// 1) Start with a default config with sane settings -// 2) Pre-parse the command line to check for an alternative config file -// 3) Load configuration file overwriting defaults with any specified options -// 4) Parse CLI options and overwrite/add any specified options -func LoadConfig() (*Config, error) { - defaultCfg := Config{ +// DefaultConfig returns all default values for the Config struct. +func DefaultConfig() Config { + return Config{ LndDir: defaultLndDir, ConfigFile: defaultConfigFile, DataDir: defaultDataDir, @@ -397,10 +390,20 @@ func LoadConfig() (*Config, error) { MaxOutgoingCltvExpiry: htlcswitch.DefaultMaxOutgoingCltvExpiry, MaxChannelFeeAllocation: htlcswitch.DefaultMaxLinkFeeAllocation, } +} +// LoadConfig initializes and parses the config using a config file and command +// line options. +// +// The configuration proceeds as follows: +// 1) Start with a default config with sane settings +// 2) Pre-parse the command line to check for an alternative config file +// 3) Load configuration file overwriting defaults with any specified options +// 4) Parse CLI options and overwrite/add any specified options +func LoadConfig() (*Config, error) { // Pre-parse the command line options to pick up an alternative config // file. - preCfg := defaultCfg + preCfg := DefaultConfig() if _, err := flags.Parse(&preCfg); err != nil { return nil, err } From aba29df84d556046887584400b187c8eb0717fec Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 30 Apr 2020 10:00:46 +0200 Subject: [PATCH 04/20] config: extract config validation To allow external configuration parsing and validation, this commit exports the function that checks a configuration for sanity. Also because we touch the code, we need to fix all linter errors. --- config.go | 62 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/config.go b/config.go index 46e06f6c..780f3328 100644 --- a/config.go +++ b/config.go @@ -452,6 +452,26 @@ func LoadConfig() (*Config, error) { return nil, err } + // Make sure everything we just loaded makes sense. + cleanCfg, err := ValidateConfig(cfg, usageMessage) + if err != nil { + return nil, err + } + + // Warn about missing config file only after all other configuration is + // done. This prevents the warning on help messages and invalid + // options. Note this should go directly before the return. + if configFileError != nil { + ltndLog.Warnf("%v", configFileError) + } + + return cleanCfg, nil +} + +// ValidateConfig check the given configuration to be sane. This makes sure no +// illegal values or combination of values are set. All file system paths are +// normalized. The cleaned up config is returned on success. +func ValidateConfig(cfg Config, usageMessage string) (*Config, error) { // If the provided lnd directory is not the default, we'll modify the // path to all of the files and directories that will live within it. lndDir := cleanAndExpandPath(cfg.LndDir) @@ -485,7 +505,7 @@ func LoadConfig() (*Config, error) { str := "%s: Failed to create lnd directory: %v" err := fmt.Errorf(str, funcName, err) - fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, err) return nil, err } @@ -512,37 +532,37 @@ func LoadConfig() (*Config, error) { if cfg.Autopilot.MaxChannels < 0 { str := "%s: autopilot.maxchannels must be non-negative" err := fmt.Errorf(str, funcName) - fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, err) return nil, err } if cfg.Autopilot.Allocation < 0 { str := "%s: autopilot.allocation must be non-negative" err := fmt.Errorf(str, funcName) - fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, err) return nil, err } if cfg.Autopilot.MinChannelSize < 0 { str := "%s: autopilot.minchansize must be non-negative" err := fmt.Errorf(str, funcName) - fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, err) return nil, err } if cfg.Autopilot.MaxChannelSize < 0 { str := "%s: autopilot.maxchansize must be non-negative" err := fmt.Errorf(str, funcName) - fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, err) return nil, err } if cfg.Autopilot.MinConfs < 0 { str := "%s: autopilot.minconfs must be non-negative" err := fmt.Errorf(str, funcName) - fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, err) return nil, err } if cfg.Autopilot.ConfTarget < 1 { str := "%s: autopilot.conftarget must be positive" err := fmt.Errorf(str, funcName) - fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, err) return nil, err } @@ -851,25 +871,25 @@ func LoadConfig() (*Config, error) { if cfg.Autopilot.MaxChannels < 0 { str := "%s: autopilot.maxchannels must be non-negative" err := fmt.Errorf(str, funcName) - fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, err) return nil, err } if cfg.Autopilot.Allocation < 0 { str := "%s: autopilot.allocation must be non-negative" err := fmt.Errorf(str, funcName) - fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, err) return nil, err } if cfg.Autopilot.MinChannelSize < 0 { str := "%s: autopilot.minchansize must be non-negative" err := fmt.Errorf(str, funcName) - fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, err) return nil, err } if cfg.Autopilot.MaxChannelSize < 0 { str := "%s: autopilot.maxchansize must be non-negative" err := fmt.Errorf(str, funcName) - fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, err) return nil, err } @@ -888,8 +908,8 @@ func LoadConfig() (*Config, error) { if err != nil || profilePort < 1024 || profilePort > 65535 { str := "%s: The profile port must be between 1024 and 65535" err := fmt.Errorf(str, funcName) - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, usageMessage) + _, _ = fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, usageMessage) return nil, err } } @@ -950,7 +970,7 @@ func LoadConfig() (*Config, error) { if err != nil { str := "%s: log rotation setup failed: %v" err = fmt.Errorf(str, funcName, err.Error()) - fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, err) return nil, err } @@ -958,8 +978,8 @@ func LoadConfig() (*Config, error) { err = build.ParseAndSetDebugLevels(cfg.DebugLevel, logWriter) if err != nil { err = fmt.Errorf("%s: %v", funcName, err.Error()) - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, usageMessage) + _, _ = fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, usageMessage) return nil, err } @@ -1096,14 +1116,8 @@ func LoadConfig() (*Config, error) { return nil, fmt.Errorf("unable to parse node color: %v", err) } - // Warn about missing config file only after all other configuration is - // done. This prevents the warning on help messages and invalid - // options. Note this should go directly before the return. - if configFileError != nil { - ltndLog.Warnf("%v", configFileError) - } - - return &cfg, nil + // All good, return the sanitized result. + return &cfg, err } // cleanAndExpandPath expands environment variables and leading ~ in the From a56ca0d0cf6f42a9e48865ee6b6df3844d9997cd Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 13 May 2020 14:56:19 +0200 Subject: [PATCH 05/20] config: export default values for config file Variables related to the default configuration file location are needed if the config parsing is happening externally. We export them so they don't need to be copied to projects that use lnd as a library. --- config.go | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/config.go b/config.go index 780f3328..31790c32 100644 --- a/config.go +++ b/config.go @@ -35,7 +35,10 @@ import ( ) const ( - defaultConfigFilename = "lnd.conf" + // DefaultConfigFilename is the default configuration file name lnd + // tries to load. + DefaultConfigFilename = "lnd.conf" + defaultDataDirname = "data" defaultChainSubDirname = "chain" defaultGraphSubDirname = "graph" @@ -133,15 +136,25 @@ const ( ) var ( - defaultLndDir = btcutil.AppDataDir("lnd", false) - defaultConfigFile = filepath.Join(defaultLndDir, defaultConfigFilename) - defaultDataDir = filepath.Join(defaultLndDir, defaultDataDirname) - defaultLogDir = filepath.Join(defaultLndDir, defaultLogDirname) + // DefaultLndDir is the default directory where lnd tries to find its + // configuration file and store its data. This is a directory in the + // user's application data, for example: + // C:\Users\\AppData\Local\Lnd on Windows + // ~/.lnd on Linux + // ~/Library/Application Support/Lnd on MacOS + DefaultLndDir = btcutil.AppDataDir("lnd", false) + + // DefaultConfigFile is the default full path of lnd's configuration + // file. + DefaultConfigFile = filepath.Join(DefaultLndDir, DefaultConfigFilename) + + defaultDataDir = filepath.Join(DefaultLndDir, defaultDataDirname) + defaultLogDir = filepath.Join(DefaultLndDir, defaultLogDirname) defaultTowerDir = filepath.Join(defaultDataDir, defaultTowerSubDirname) - defaultTLSCertPath = filepath.Join(defaultLndDir, defaultTLSCertFilename) - defaultTLSKeyPath = filepath.Join(defaultLndDir, defaultTLSKeyFilename) + defaultTLSCertPath = filepath.Join(DefaultLndDir, defaultTLSCertFilename) + defaultTLSKeyPath = filepath.Join(DefaultLndDir, defaultTLSKeyFilename) defaultBtcdDir = btcutil.AppDataDir("btcd", false) defaultBtcdRPCCertFile = filepath.Join(defaultBtcdDir, "rpc.cert") @@ -292,8 +305,8 @@ type Config struct { // DefaultConfig returns all default values for the Config struct. func DefaultConfig() Config { return Config{ - LndDir: defaultLndDir, - ConfigFile: defaultConfigFile, + LndDir: DefaultLndDir, + ConfigFile: DefaultConfigFile, DataDir: defaultDataDir, DebugLevel: defaultLogLevel, TLSCertPath: defaultTLSCertPath, @@ -424,10 +437,10 @@ func LoadConfig() (*Config, error) { // file within it. configFileDir := cleanAndExpandPath(preCfg.LndDir) configFilePath := cleanAndExpandPath(preCfg.ConfigFile) - if configFileDir != defaultLndDir { - if configFilePath == defaultConfigFile { + if configFileDir != DefaultLndDir { + if configFilePath == DefaultConfigFile { configFilePath = filepath.Join( - configFileDir, defaultConfigFilename, + configFileDir, DefaultConfigFilename, ) } } @@ -475,7 +488,7 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) { // If the provided lnd directory is not the default, we'll modify the // path to all of the files and directories that will live within it. lndDir := cleanAndExpandPath(cfg.LndDir) - if lndDir != defaultLndDir { + if lndDir != DefaultLndDir { cfg.DataDir = filepath.Join(lndDir, defaultDataDirname) cfg.TLSCertPath = filepath.Join(lndDir, defaultTLSCertFilename) cfg.TLSKeyPath = filepath.Join(lndDir, defaultTLSKeyFilename) From bc3909050ed501217ce70494f7f97bdc20632662 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 13 May 2020 15:04:57 +0200 Subject: [PATCH 06/20] multi: rename and export logWriter as RootLogWriter --- config.go | 6 +++--- lnd.go | 2 +- log.go | 26 ++++++++++++++------------ rpcserver.go | 4 ++-- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/config.go b/config.go index 31790c32..b8a1a7ea 100644 --- a/config.go +++ b/config.go @@ -971,12 +971,12 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) { // Special show command to list supported subsystems and exit. if cfg.DebugLevel == "show" { fmt.Println("Supported subsystems", - logWriter.SupportedSubsystems()) + RootLogWriter.SupportedSubsystems()) os.Exit(0) } // Initialize logging at the default logging level. - err = logWriter.InitLogRotator( + err = RootLogWriter.InitLogRotator( filepath.Join(cfg.LogDir, defaultLogFilename), cfg.MaxLogFileSize, cfg.MaxLogFiles, ) @@ -988,7 +988,7 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) { } // Parse, validate, and set debug log level(s). - err = build.ParseAndSetDebugLevels(cfg.DebugLevel, logWriter) + err = build.ParseAndSetDebugLevels(cfg.DebugLevel, RootLogWriter) if err != nil { err = fmt.Errorf("%s: %v", funcName, err.Error()) _, _ = fmt.Fprintln(os.Stderr, err) diff --git a/lnd.go b/lnd.go index 7f3404ba..e796a762 100644 --- a/lnd.go +++ b/lnd.go @@ -163,7 +163,7 @@ func Main(lisCfg ListenerCfg) error { cfg = loadedConfig defer func() { ltndLog.Info("Shutdown complete") - err := logWriter.Close() + err := RootLogWriter.Close() if err != nil { ltndLog.Errorf("Could not close log rotator: %v", err) } diff --git a/log.go b/log.go index 51754cca..08008414 100644 --- a/log.go +++ b/log.go @@ -47,21 +47,23 @@ import ( // // Loggers can not be used before the log rotator has been initialized with a // log file. This must be performed early during application startup by -// calling logWriter.InitLogRotator. +// calling RootLogWriter.InitLogRotator. var ( - logWriter = build.NewRotatingLogWriter() + // RootLogWriter is the main parent log writer all sub loggers should be + // appended to. + RootLogWriter = build.NewRotatingLogWriter() // Loggers that need to be accessible from the lnd package can be placed // here. Loggers that are only used in sub modules can be added directly // by using the addSubLogger method. - ltndLog = build.NewSubLogger("LTND", logWriter.GenSubLogger) - peerLog = build.NewSubLogger("PEER", logWriter.GenSubLogger) - rpcsLog = build.NewSubLogger("RPCS", logWriter.GenSubLogger) - srvrLog = build.NewSubLogger("SRVR", logWriter.GenSubLogger) - fndgLog = build.NewSubLogger("FNDG", logWriter.GenSubLogger) - utxnLog = build.NewSubLogger("UTXN", logWriter.GenSubLogger) - brarLog = build.NewSubLogger("BRAR", logWriter.GenSubLogger) - atplLog = build.NewSubLogger("ATPL", logWriter.GenSubLogger) + ltndLog = build.NewSubLogger("LTND", RootLogWriter.GenSubLogger) + peerLog = build.NewSubLogger("PEER", RootLogWriter.GenSubLogger) + rpcsLog = build.NewSubLogger("RPCS", RootLogWriter.GenSubLogger) + srvrLog = build.NewSubLogger("SRVR", RootLogWriter.GenSubLogger) + fndgLog = build.NewSubLogger("FNDG", RootLogWriter.GenSubLogger) + utxnLog = build.NewSubLogger("UTXN", RootLogWriter.GenSubLogger) + brarLog = build.NewSubLogger("BRAR", RootLogWriter.GenSubLogger) + atplLog = build.NewSubLogger("ATPL", RootLogWriter.GenSubLogger) ) // Initialize package-global logger variables. @@ -112,7 +114,7 @@ func init() { func addSubLogger(subsystem string, useLoggers ...func(btclog.Logger)) { // Create and register just a single logger to prevent them from // overwriting each other internally. - logger := build.NewSubLogger(subsystem, logWriter.GenSubLogger) + logger := build.NewSubLogger(subsystem, RootLogWriter.GenSubLogger) setSubLogger(subsystem, logger, useLoggers...) } @@ -121,7 +123,7 @@ func addSubLogger(subsystem string, useLoggers ...func(btclog.Logger)) { func setSubLogger(subsystem string, logger btclog.Logger, useLoggers ...func(btclog.Logger)) { - logWriter.RegisterSubLogger(subsystem, logger) + RootLogWriter.RegisterSubLogger(subsystem, logger) for _, useLogger := range useLoggers { useLogger(logger) } diff --git a/rpcserver.go b/rpcserver.go index c725b67b..faa16f0d 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -5220,7 +5220,7 @@ func (r *rpcServer) DebugLevel(ctx context.Context, if req.Show { return &lnrpc.DebugLevelResponse{ SubSystems: strings.Join( - logWriter.SupportedSubsystems(), " ", + RootLogWriter.SupportedSubsystems(), " ", ), }, nil } @@ -5229,7 +5229,7 @@ func (r *rpcServer) DebugLevel(ctx context.Context, // Otherwise, we'll attempt to set the logging level using the // specified level spec. - err := build.ParseAndSetDebugLevels(req.LevelSpec, logWriter) + err := build.ParseAndSetDebugLevels(req.LevelSpec, RootLogWriter) if err != nil { return nil, err } From 1f3a5ce0d4fcc72892dcaa6ed944df063931f971 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 30 Apr 2020 10:02:56 +0200 Subject: [PATCH 07/20] config: export CleanAndExpandPath --- config.go | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/config.go b/config.go index b8a1a7ea..0679d6d2 100644 --- a/config.go +++ b/config.go @@ -435,8 +435,8 @@ func LoadConfig() (*Config, error) { // use the default config file path. However, if the user has modified // their lnddir, then we should assume they intend to use the config // file within it. - configFileDir := cleanAndExpandPath(preCfg.LndDir) - configFilePath := cleanAndExpandPath(preCfg.ConfigFile) + configFileDir := CleanAndExpandPath(preCfg.LndDir) + configFilePath := CleanAndExpandPath(preCfg.ConfigFile) if configFileDir != DefaultLndDir { if configFilePath == DefaultConfigFile { configFilePath = filepath.Join( @@ -487,7 +487,7 @@ func LoadConfig() (*Config, error) { func ValidateConfig(cfg Config, usageMessage string) (*Config, error) { // If the provided lnd directory is not the default, we'll modify the // path to all of the files and directories that will live within it. - lndDir := cleanAndExpandPath(cfg.LndDir) + lndDir := CleanAndExpandPath(cfg.LndDir) if lndDir != DefaultLndDir { cfg.DataDir = filepath.Join(lndDir, defaultDataDirname) cfg.TLSCertPath = filepath.Join(lndDir, defaultTLSCertFilename) @@ -525,20 +525,20 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) { // As soon as we're done parsing configuration options, ensure all paths // to directories and files are cleaned and expanded before attempting // to use them later on. - cfg.DataDir = cleanAndExpandPath(cfg.DataDir) - cfg.TLSCertPath = cleanAndExpandPath(cfg.TLSCertPath) - cfg.TLSKeyPath = cleanAndExpandPath(cfg.TLSKeyPath) - cfg.AdminMacPath = cleanAndExpandPath(cfg.AdminMacPath) - cfg.ReadMacPath = cleanAndExpandPath(cfg.ReadMacPath) - cfg.InvoiceMacPath = cleanAndExpandPath(cfg.InvoiceMacPath) - cfg.LogDir = cleanAndExpandPath(cfg.LogDir) - cfg.BtcdMode.Dir = cleanAndExpandPath(cfg.BtcdMode.Dir) - cfg.LtcdMode.Dir = cleanAndExpandPath(cfg.LtcdMode.Dir) - cfg.BitcoindMode.Dir = cleanAndExpandPath(cfg.BitcoindMode.Dir) - cfg.LitecoindMode.Dir = cleanAndExpandPath(cfg.LitecoindMode.Dir) - cfg.Tor.PrivateKeyPath = cleanAndExpandPath(cfg.Tor.PrivateKeyPath) - cfg.Tor.WatchtowerKeyPath = cleanAndExpandPath(cfg.Tor.WatchtowerKeyPath) - cfg.Watchtower.TowerDir = cleanAndExpandPath(cfg.Watchtower.TowerDir) + cfg.DataDir = CleanAndExpandPath(cfg.DataDir) + cfg.TLSCertPath = CleanAndExpandPath(cfg.TLSCertPath) + cfg.TLSKeyPath = CleanAndExpandPath(cfg.TLSKeyPath) + cfg.AdminMacPath = CleanAndExpandPath(cfg.AdminMacPath) + cfg.ReadMacPath = CleanAndExpandPath(cfg.ReadMacPath) + cfg.InvoiceMacPath = CleanAndExpandPath(cfg.InvoiceMacPath) + cfg.LogDir = CleanAndExpandPath(cfg.LogDir) + cfg.BtcdMode.Dir = CleanAndExpandPath(cfg.BtcdMode.Dir) + cfg.LtcdMode.Dir = CleanAndExpandPath(cfg.LtcdMode.Dir) + cfg.BitcoindMode.Dir = CleanAndExpandPath(cfg.BitcoindMode.Dir) + cfg.LitecoindMode.Dir = CleanAndExpandPath(cfg.LitecoindMode.Dir) + cfg.Tor.PrivateKeyPath = CleanAndExpandPath(cfg.Tor.PrivateKeyPath) + cfg.Tor.WatchtowerKeyPath = CleanAndExpandPath(cfg.Tor.WatchtowerKeyPath) + cfg.Watchtower.TowerDir = CleanAndExpandPath(cfg.Watchtower.TowerDir) // Ensure that the user didn't attempt to specify negative values for // any of the autopilot params. @@ -1133,10 +1133,10 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) { return &cfg, err } -// cleanAndExpandPath expands environment variables and leading ~ in the +// CleanAndExpandPath expands environment variables and leading ~ in the // passed path, cleans the result, and returns it. // This function is taken from https://github.com/btcsuite/btcd -func cleanAndExpandPath(path string) string { +func CleanAndExpandPath(path string) string { if path == "" { return "" } From 25f8a0804da0902cdd1cb0c1675a1e423d7d4d88 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 13 May 2020 15:08:27 +0200 Subject: [PATCH 08/20] config: fix comment and linter errors --- config.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/config.go b/config.go index 0679d6d2..6134c981 100644 --- a/config.go +++ b/config.go @@ -1,6 +1,6 @@ // Copyright (c) 2013-2017 The btcsuite developers // Copyright (c) 2015-2016 The Decred developers -// Copyright (C) 2015-2017 The Lightning Network Developers +// Copyright (C) 2015-2020 The Lightning Network Developers package lnd @@ -1144,9 +1144,9 @@ func CleanAndExpandPath(path string) string { // Expand initial ~ to OS specific home directory. if strings.HasPrefix(path, "~") { var homeDir string - user, err := user.Current() + u, err := user.Current() if err == nil { - homeDir = user.HomeDir + homeDir = u.HomeDir } else { homeDir = os.Getenv("HOME") } @@ -1160,7 +1160,7 @@ func CleanAndExpandPath(path string) string { } func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{}, net chainCode, - funcName string) error { + funcName string) error { // nolint:unparam // First, we'll check our node config to make sure the RPC parameters // were set correctly. We'll also determine the path to the conf file @@ -1294,7 +1294,7 @@ func extractBtcdRPCParams(btcdConfigPath string) (string, string, error) { if err != nil { return "", "", err } - defer btcdConfigFile.Close() + defer func() { _ = btcdConfigFile.Close() }() // With the file open extract the contents of the configuration file so // we can attempt to locate the RPC credentials. @@ -1335,14 +1335,16 @@ func extractBtcdRPCParams(btcdConfigPath string) (string, string, error) { // location of bitcoind's bitcoin.conf on the target system. The routine looks // for a cookie first, optionally following the datadir configuration option in // the bitcoin.conf. If it doesn't find one, it looks for rpcuser/rpcpassword. -func extractBitcoindRPCParams(bitcoindConfigPath string) (string, string, string, string, error) { +func extractBitcoindRPCParams(bitcoindConfigPath string) (string, string, string, + string, error) { + // First, we'll open up the bitcoind configuration file found at the // target destination. bitcoindConfigFile, err := os.Open(bitcoindConfigPath) if err != nil { return "", "", "", "", err } - defer bitcoindConfigFile.Close() + defer func() { _ = bitcoindConfigFile.Close() }() // With the file open extract the contents of the configuration file so // we can attempt to locate the RPC credentials. From 3b2188d6898075a6272ff27149a0429f190ea218 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 13 May 2020 15:47:45 +0200 Subject: [PATCH 09/20] multi: move exported items to lncfg --- config.go | 59 +----------- fundingmanager_test.go | 3 +- lncfg/config.go | 89 +++++++++++++++++++ ...d_multi-hop_htlc_local_chain_claim_test.go | 3 +- .../lnd_multi-hop_htlc_local_timeout_test.go | 3 +- ...ulti-hop_htlc_receiver_chain_claim_test.go | 3 +- ..._multi-hop_htlc_remote_chain_claim_test.go | 3 +- lntest/itest/lnd_test.go | 3 +- server.go | 8 +- 9 files changed, 108 insertions(+), 66 deletions(-) create mode 100644 lncfg/config.go diff --git a/config.go b/config.go index 6134c981..f2f9de88 100644 --- a/config.go +++ b/config.go @@ -35,10 +35,6 @@ import ( ) const ( - // DefaultConfigFilename is the default configuration file name lnd - // tries to load. - DefaultConfigFilename = "lnd.conf" - defaultDataDirname = "data" defaultChainSubDirname = "chain" defaultGraphSubDirname = "graph" @@ -56,10 +52,6 @@ const ( defaultPeerPort = 9735 defaultRPCHost = "localhost" - // DefaultMaxPendingChannels is the default maximum number of incoming - // pending channels permitted per peer. - DefaultMaxPendingChannels = 1 - defaultNoSeedBackup = false defaultPaymentsExpirationGracePeriod = time.Duration(0) defaultTrickleDelay = 90 * 1000 @@ -78,51 +70,6 @@ const ( defaultTorV2PrivateKeyFilename = "v2_onion_private_key" defaultTorV3PrivateKeyFilename = "v3_onion_private_key" - // DefaultIncomingBroadcastDelta defines the number of blocks before the - // expiry of an incoming htlc at which we force close the channel. We - // only go to chain if we also have the preimage to actually pull in the - // htlc. BOLT #2 suggests 7 blocks. We use a few more for extra safety. - // Within this window we need to get our sweep or 2nd level success tx - // confirmed, because after that the remote party is also able to claim - // the htlc using the timeout path. - DefaultIncomingBroadcastDelta = 10 - - // defaultFinalCltvRejectDelta defines the number of blocks before the - // expiry of an incoming exit hop htlc at which we cancel it back - // immediately. It is an extra safety measure over the final cltv - // requirement as it is defined in the invoice. It ensures that we - // cancel back htlcs that, when held on to, may cause us to force close - // the channel because we enter the incoming broadcast window. Bolt #11 - // suggests 9 blocks here. We use a few more for additional safety. - // - // There is still a small gap that remains between receiving the - // RevokeAndAck and canceling back. If a new block arrives within that - // window, we may still force close the channel. There is currently no - // way to reject an UpdateAddHtlc of which we already know that it will - // push us in the broadcast window. - defaultFinalCltvRejectDelta = DefaultIncomingBroadcastDelta + 3 - - // DefaultOutgoingBroadcastDelta defines the number of blocks before the - // expiry of an outgoing htlc at which we force close the channel. We - // are not in a hurry to force close, because there is nothing to claim - // for us. We do need to time the htlc out, because there may be an - // incoming htlc that will time out too (albeit later). Bolt #2 suggests - // a value of -1 here, but we allow one block less to prevent potential - // confusion around the negative value. It means we force close the - // channel at exactly the htlc expiry height. - DefaultOutgoingBroadcastDelta = 0 - - // defaultOutgoingCltvRejectDelta defines the number of blocks before - // the expiry of an outgoing htlc at which we don't want to offer it to - // the next peer anymore. If that happens, we cancel back the incoming - // htlc. This is to prevent the situation where we have an outstanding - // htlc that brings or will soon bring us inside the outgoing broadcast - // window and trigger us to force close the channel. Bolt #2 suggests a - // value of 0. We pad it a bit, to prevent a slow round trip to the next - // peer and a block arriving during that round trip to trigger force - // closure. - defaultOutgoingCltvRejectDelta = DefaultOutgoingBroadcastDelta + 3 - // minTimeLockDelta is the minimum timelock we require for incoming // HTLCs on our channels. minTimeLockDelta = 4 @@ -146,7 +93,7 @@ var ( // DefaultConfigFile is the default full path of lnd's configuration // file. - DefaultConfigFile = filepath.Join(DefaultLndDir, DefaultConfigFilename) + DefaultConfigFile = filepath.Join(DefaultLndDir, lncfg.DefaultConfigFilename) defaultDataDir = filepath.Join(DefaultLndDir, defaultDataDirname) defaultLogDir = filepath.Join(DefaultLndDir, defaultLogDirname) @@ -352,7 +299,7 @@ func DefaultConfig() Config { EstimateMode: defaultBitcoindEstimateMode, }, UnsafeDisconnect: true, - MaxPendingChannels: DefaultMaxPendingChannels, + MaxPendingChannels: lncfg.DefaultMaxPendingChannels, NoSeedBackup: defaultNoSeedBackup, MinBackoff: defaultMinBackoff, MaxBackoff: defaultMaxBackoff, @@ -440,7 +387,7 @@ func LoadConfig() (*Config, error) { if configFileDir != DefaultLndDir { if configFilePath == DefaultConfigFile { configFilePath = filepath.Join( - configFileDir, DefaultConfigFilename, + configFileDir, lncfg.DefaultConfigFilename, ) } } diff --git a/fundingmanager_test.go b/fundingmanager_test.go index 45a9bdee..db870d70 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -21,6 +21,7 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" + "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chanacceptor" @@ -413,7 +414,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, }, ZombieSweeperInterval: 1 * time.Hour, ReservationTimeout: 1 * time.Nanosecond, - MaxPendingChannels: DefaultMaxPendingChannels, + MaxPendingChannels: lncfg.DefaultMaxPendingChannels, NotifyOpenChannelEvent: evt.NotifyOpenChannelEvent, OpenChannelPredicate: chainedAcceptor, NotifyPendingOpenChannelEvent: evt.NotifyPendingOpenChannelEvent, diff --git a/lncfg/config.go b/lncfg/config.go new file mode 100644 index 00000000..f0a1905a --- /dev/null +++ b/lncfg/config.go @@ -0,0 +1,89 @@ +package lncfg + +import ( + "os" + "os/user" + "path/filepath" + "strings" +) + +const ( + // DefaultConfigFilename is the default configuration file name lnd + // tries to load. + DefaultConfigFilename = "lnd.conf" + + // DefaultMaxPendingChannels is the default maximum number of incoming + // pending channels permitted per peer. + DefaultMaxPendingChannels = 1 + + // DefaultIncomingBroadcastDelta defines the number of blocks before the + // expiry of an incoming htlc at which we force close the channel. We + // only go to chain if we also have the preimage to actually pull in the + // htlc. BOLT #2 suggests 7 blocks. We use a few more for extra safety. + // Within this window we need to get our sweep or 2nd level success tx + // confirmed, because after that the remote party is also able to claim + // the htlc using the timeout path. + DefaultIncomingBroadcastDelta = 10 + + // DefaultFinalCltvRejectDelta defines the number of blocks before the + // expiry of an incoming exit hop htlc at which we cancel it back + // immediately. It is an extra safety measure over the final cltv + // requirement as it is defined in the invoice. It ensures that we + // cancel back htlcs that, when held on to, may cause us to force close + // the channel because we enter the incoming broadcast window. Bolt #11 + // suggests 9 blocks here. We use a few more for additional safety. + // + // There is still a small gap that remains between receiving the + // RevokeAndAck and canceling back. If a new block arrives within that + // window, we may still force close the channel. There is currently no + // way to reject an UpdateAddHtlc of which we already know that it will + // push us in the broadcast window. + DefaultFinalCltvRejectDelta = DefaultIncomingBroadcastDelta + 3 + + // DefaultOutgoingBroadcastDelta defines the number of blocks before the + // expiry of an outgoing htlc at which we force close the channel. We + // are not in a hurry to force close, because there is nothing to claim + // for us. We do need to time the htlc out, because there may be an + // incoming htlc that will time out too (albeit later). Bolt #2 suggests + // a value of -1 here, but we allow one block less to prevent potential + // confusion around the negative value. It means we force close the + // channel at exactly the htlc expiry height. + DefaultOutgoingBroadcastDelta = 0 + + // DefaultOutgoingCltvRejectDelta defines the number of blocks before + // the expiry of an outgoing htlc at which we don't want to offer it to + // the next peer anymore. If that happens, we cancel back the incoming + // htlc. This is to prevent the situation where we have an outstanding + // htlc that brings or will soon bring us inside the outgoing broadcast + // window and trigger us to force close the channel. Bolt #2 suggests a + // value of 0. We pad it a bit, to prevent a slow round trip to the next + // peer and a block arriving during that round trip to trigger force + // closure. + DefaultOutgoingCltvRejectDelta = DefaultOutgoingBroadcastDelta + 3 +) + +// CleanAndExpandPath expands environment variables and leading ~ in the +// passed path, cleans the result, and returns it. +// This function is taken from https://github.com/btcsuite/btcd +func CleanAndExpandPath(path string) string { + if path == "" { + return "" + } + + // Expand initial ~ to OS specific home directory. + if strings.HasPrefix(path, "~") { + var homeDir string + u, err := user.Current() + if err == nil { + homeDir = u.HomeDir + } else { + homeDir = os.Getenv("HOME") + } + + path = strings.Replace(path, "~", homeDir, 1) + } + + // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%, + // but the variables can still be expanded via POSIX-style $VARIABLE. + return filepath.Clean(os.ExpandEnv(path)) +} diff --git a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go index 16ac3987..f63ef57d 100644 --- a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd" + "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lntest" @@ -136,7 +137,7 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest, // We'll now mine enough blocks so Carol decides that she needs to go // on-chain to claim the HTLC as Bob has been inactive. numBlocks := padCLTV(uint32(invoiceReq.CltvExpiry - - lnd.DefaultIncomingBroadcastDelta)) + lncfg.DefaultIncomingBroadcastDelta)) if _, err := net.Miner.Node.Generate(numBlocks); err != nil { t.Fatalf("unable to generate blocks") diff --git a/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go b/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go index 1a7feed4..d751bca1 100644 --- a/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcutil" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd" + "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest/wait" @@ -101,7 +102,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest, // timeout. With the default outgoing broadcast delta of zero, this will // be the same height as the htlc expiry height. numBlocks := padCLTV( - uint32(finalCltvDelta - lnd.DefaultOutgoingBroadcastDelta), + uint32(finalCltvDelta - lncfg.DefaultOutgoingBroadcastDelta), ) if _, err := net.Miner.Node.Generate(numBlocks); err != nil { t.Fatalf("unable to generate blocks: %v", err) diff --git a/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go index d7335311..8669383f 100644 --- a/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd" + "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lntest" @@ -118,7 +119,7 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest, // chain in order to sweep her HTLC since the value is high enough. // TODO(roasbeef): modify once go to chain policy changes numBlocks := padCLTV(uint32( - invoiceReq.CltvExpiry - lnd.DefaultIncomingBroadcastDelta, + invoiceReq.CltvExpiry - lncfg.DefaultIncomingBroadcastDelta, )) if _, err := net.Miner.Node.Generate(numBlocks); err != nil { t.Fatalf("unable to generate blocks") diff --git a/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go index c73a066b..e4f61b19 100644 --- a/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd" + "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lntest" @@ -148,7 +149,7 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest // We'll now mine enough blocks so Carol decides that she needs to go // on-chain to claim the HTLC as Bob has been inactive. numBlocks := padCLTV(uint32(invoiceReq.CltvExpiry- - lnd.DefaultIncomingBroadcastDelta) - defaultCSV) + lncfg.DefaultIncomingBroadcastDelta) - defaultCSV) if _, err := net.Miner.Node.Generate(numBlocks); err != nil { t.Fatalf("unable to generate blocks") diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go index 12a31881..ef7d0a32 100644 --- a/lntest/itest/lnd_test.go +++ b/lntest/itest/lnd_test.go @@ -34,6 +34,7 @@ import ( "github.com/lightningnetwork/lnd/chanbackup" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" @@ -6695,7 +6696,7 @@ func testBasicChannelCreationAndUpdates(net *lntest.NetworkHarness, t *harnessTe func testMaxPendingChannels(net *lntest.NetworkHarness, t *harnessTest) { ctxb := context.Background() - maxPendingChannels := lnd.DefaultMaxPendingChannels + 1 + maxPendingChannels := lncfg.DefaultMaxPendingChannels + 1 amount := lnd.MaxBtcFundingAmount // Create a new node (Carol) with greater number of max pending diff --git a/server.go b/server.go index 27086b1e..6c4ab2b1 100644 --- a/server.go +++ b/server.go @@ -399,7 +399,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, } registryConfig := invoices.RegistryConfig{ - FinalCltvRejectDelta: defaultFinalCltvRejectDelta, + FinalCltvRejectDelta: lncfg.DefaultFinalCltvRejectDelta, HtlcHoldDuration: invoices.DefaultHtlcHoldDuration, Clock: clock.NewDefaultClock(), AcceptKeySend: cfg.AcceptKeySend, @@ -867,8 +867,8 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, s.chainArb = contractcourt.NewChainArbitrator(contractcourt.ChainArbitratorConfig{ ChainHash: *activeNetParams.GenesisHash, - IncomingBroadcastDelta: DefaultIncomingBroadcastDelta, - OutgoingBroadcastDelta: DefaultOutgoingBroadcastDelta, + IncomingBroadcastDelta: lncfg.DefaultIncomingBroadcastDelta, + OutgoingBroadcastDelta: lncfg.DefaultOutgoingBroadcastDelta, NewSweepAddr: newSweepPkScriptGen(cc.wallet), PublishTx: cc.wallet.PublishTransaction, DeliverResolutionMsg: func(msgs ...contractcourt.ResolutionMsg) error { @@ -2782,7 +2782,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq, p, err := newPeer( conn, connReq, s, peerAddr, inbound, initFeatures, legacyFeatures, cfg.ChanEnableTimeout, - defaultOutgoingCltvRejectDelta, errBuffer, + lncfg.DefaultOutgoingCltvRejectDelta, errBuffer, ) if err != nil { srvrLog.Errorf("unable to create peer %v", err) From 7158103d4d0b50536b0db418974f5c2f043e92c2 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 30 Apr 2020 09:39:29 +0200 Subject: [PATCH 10/20] lnd+config: move config parsing to cmd Now that we have access to the configuration parsing outside of the main package, we can move the actual parsing to the command line package. --- cmd/lnd/main.go | 12 ++++++++++-- lnd.go | 10 ++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/cmd/lnd/main.go b/cmd/lnd/main.go index 3448b77a..42fab333 100644 --- a/cmd/lnd/main.go +++ b/cmd/lnd/main.go @@ -9,12 +9,20 @@ import ( ) func main() { + // Load the configuration, and parse any command line options. This + // function will also set up logging properly. + loadedConfig, err := lnd.LoadConfig() + if err != nil { + _, _ = fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + // Call the "real" main in a nested manner so the defers will properly // be executed in the case of a graceful shutdown. - if err := lnd.Main(lnd.ListenerCfg{}); err != nil { + if err := lnd.Main(loadedConfig, lnd.ListenerCfg{}); err != nil { if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { } else { - fmt.Fprintln(os.Stderr, err) + _, _ = fmt.Fprintln(os.Stderr, err) } os.Exit(1) } diff --git a/lnd.go b/lnd.go index e796a762..9eb35bdd 100644 --- a/lnd.go +++ b/lnd.go @@ -150,17 +150,11 @@ type rpcListeners func() ([]*ListenerWithSignal, func(), error) // Main is the true entry point for lnd. This function is required since defers // created in the top-level scope of a main method aren't executed if os.Exit() // is called. -func Main(lisCfg ListenerCfg) error { +func Main(config *Config, lisCfg ListenerCfg) error { // Hook interceptor for os signals. signal.Intercept() - // Load the configuration, and parse any command line options. This - // function will also set up logging properly. - loadedConfig, err := LoadConfig() - if err != nil { - return err - } - cfg = loadedConfig + cfg = config defer func() { ltndLog.Info("Shutdown complete") err := RootLogWriter.Close() From 620eaa319951237aef3e2990289f97804cbd0cda Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 30 Apr 2020 09:42:28 +0200 Subject: [PATCH 11/20] lnd+cmd: move interrupt into cmd If the main package is used as a library, we don't want it to register interrupt signals itself. Rather we want to pass in the shutdown channel manually. We do this in the cmd now. --- cmd/lnd/main.go | 11 +++++++++-- lnd.go | 14 ++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/cmd/lnd/main.go b/cmd/lnd/main.go index 42fab333..9364afbe 100644 --- a/cmd/lnd/main.go +++ b/cmd/lnd/main.go @@ -4,8 +4,9 @@ import ( "fmt" "os" - flags "github.com/jessevdk/go-flags" + "github.com/jessevdk/go-flags" "github.com/lightningnetwork/lnd" + "github.com/lightningnetwork/lnd/signal" ) func main() { @@ -17,9 +18,15 @@ func main() { os.Exit(1) } + // Hook interceptor for os signals. + signal.Intercept() + // Call the "real" main in a nested manner so the defers will properly // be executed in the case of a graceful shutdown. - if err := lnd.Main(loadedConfig, lnd.ListenerCfg{}); err != nil { + err = lnd.Main( + loadedConfig, lnd.ListenerCfg{}, signal.ShutdownChannel(), + ) + if err != nil { if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { } else { _, _ = fmt.Fprintln(os.Stderr, err) diff --git a/lnd.go b/lnd.go index 9eb35bdd..edd0987b 100644 --- a/lnd.go +++ b/lnd.go @@ -147,13 +147,11 @@ type ListenerCfg struct { // listeners. type rpcListeners func() ([]*ListenerWithSignal, func(), error) -// Main is the true entry point for lnd. This function is required since defers -// created in the top-level scope of a main method aren't executed if os.Exit() -// is called. -func Main(config *Config, lisCfg ListenerCfg) error { - // Hook interceptor for os signals. - signal.Intercept() - +// Main is the true entry point for lnd. It accepts a fully populated and +// validated main configuration struct and an optional listener config struct. +// This function starts all main system components then blocks until a signal +// is received on the shutdownChan at which point everything is shut down again. +func Main(config *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { cfg = config defer func() { ltndLog.Info("Shutdown complete") @@ -728,7 +726,7 @@ func Main(config *Config, lisCfg ListenerCfg) error { // Wait for shutdown signal from either a graceful server stop or from // the interrupt handler. - <-signal.ShutdownChannel() + <-shutdownChan return nil } From a7e78112b778b271608583cfb539c4e7ad9ed136 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 May 2020 18:15:03 +0200 Subject: [PATCH 12/20] multi: allow external subservers to register themselves With two new callbacks we allow processes that use lnd as a library to register additional gRPC and REST subservers to the main server instances that lnd creates. --- lnd.go | 47 +++++++++++++++++++++++++++ rpcserver.go | 90 ++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 121 insertions(+), 16 deletions(-) diff --git a/lnd.go b/lnd.go index edd0987b..c899db86 100644 --- a/lnd.go +++ b/lnd.go @@ -119,6 +119,45 @@ func AdminAuthOptions() ([]grpc.DialOption, error) { return opts, nil } +// GrpcRegistrar is an interface that must be satisfied by an external subserver +// that wants to be able to register its own gRPC server onto lnd's main +// grpc.Server instance. +type GrpcRegistrar interface { + // RegisterGrpcSubserver is called for each net.Listener on which lnd + // creates a grpc.Server instance. External subservers implementing this + // method can then register their own gRPC server structs to the main + // server instance. + RegisterGrpcSubserver(*grpc.Server) error +} + +// RestRegistrar is an interface that must be satisfied by an external subserver +// that wants to be able to register its own REST mux onto lnd's main +// proxy.ServeMux instance. +type RestRegistrar interface { + // RegisterRestSubserver is called after lnd creates the main + // proxy.ServeMux instance. External subservers implementing this method + // can then register their own REST proxy stubs to the main server + // instance. + RegisterRestSubserver(context.Context, *proxy.ServeMux, string, + []grpc.DialOption) error +} + +// RPCSubserverConfig is a struct that can be used to register an external +// subserver with the custom permissions that map to the gRPC server that is +// going to be registered with the GrpcRegistrar. +type RPCSubserverConfig struct { + // Registrar is a callback that is invoked for each net.Listener on + // which lnd creates a grpc.Server instance. + Registrar GrpcRegistrar + + // Permissions is the permissions required for the external subserver. + // It is a map between the full HTTP URI of each RPC and its required + // macaroon permissions. If multiple action/entity tuples are specified + // per URI, they are all required. See rpcserver.go for a list of valid + // action and entity values. + Permissions map[string][]bakery.Op +} + // ListenerWithSignal is a net.Listener that has an additional Ready channel that // will be closed when a server starts listening. type ListenerWithSignal struct { @@ -126,6 +165,14 @@ type ListenerWithSignal struct { // Ready will be closed by the server listening on Listener. Ready chan struct{} + + // ExternalRPCSubserverCfg is optional and specifies the registration + // callback and permissions to register external gRPC subservers. + ExternalRPCSubserverCfg *RPCSubserverConfig + + // ExternalRestRegistrar is optional and specifies the registration + // callback to register external REST subservers. + ExternalRestRegistrar RestRegistrar } // ListenerCfg is a wrapper around custom listeners that can be passed to lnd diff --git a/rpcserver.go b/rpcserver.go index faa16f0d..80732992 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -628,6 +628,32 @@ func newRPCServer(s *server, macService *macaroons.Service, } } + // Get the listeners and server options to use for this rpc server. + listeners, cleanup, err := getListeners() + if err != nil { + return nil, err + } + + // External subserver possibly need to register their own permissions. + for _, lis := range listeners { + extSubserver := lis.ExternalRPCSubserverCfg + if extSubserver != nil { + for method, ops := range extSubserver.Permissions { + // For each new method:ops combo, we also ensure + // that non of the sub-servers try to override + // each other. + if _, ok := permissions[method]; ok { + return nil, fmt.Errorf("detected "+ + "duplicate macaroon "+ + "constraints for path: %v", + method) + } + + permissions[method] = ops + } + } + } + // If macaroons aren't disabled (a non-nil service), then we'll set up // our set of interceptors which will allow us to handle the macaroon // authentication in a single location. @@ -659,12 +685,6 @@ func newRPCServer(s *server, macService *macaroons.Service, strmInterceptors, errorLogStreamServerInterceptor(rpcsLog), ) - // Get the listeners and server options to use for this rpc server. - listeners, cleanup, err := getListeners() - if err != nil { - return nil, err - } - // If any interceptors have been set up, add them to the server options. if len(unaryInterceptors) != 0 && len(strmInterceptors) != 0 { chainedUnary := grpc_middleware.WithUnaryServerChain( @@ -736,9 +756,29 @@ func (r *rpcServer) Start() error { go func(lis *ListenerWithSignal) { rpcsLog.Infof("RPC server listening on %s", lis.Addr()) + // Before actually listening on the gRPC listener, give + // external subservers the chance to register to our + // gRPC server. Those external subservers (think GrUB) + // are responsible for starting/stopping on their own, + // we just let them register their services to the same + // server instance so all of them can be exposed on the + // same port/listener. + extSubCfg := lis.ExternalRPCSubserverCfg + if extSubCfg != nil && extSubCfg.Registrar != nil { + registerer := extSubCfg.Registrar + err := registerer.RegisterGrpcSubserver( + r.grpcServer, + ) + if err != nil { + rpcsLog.Errorf("error registering "+ + "external gRPC subserver: %v", + err) + } + } + // Close the ready chan to indicate we are listening. close(lis.Ready) - r.grpcServer.Serve(lis) + _ = r.grpcServer.Serve(lis) }(lis) } @@ -770,32 +810,50 @@ func (r *rpcServer) Start() error { // // TODO(roasbeef): eventually also allow the sub-servers to themselves // have a REST proxy. - mux := proxy.NewServeMux(customMarshalerOption) + restMux := proxy.NewServeMux(customMarshalerOption) + restCtx, restCancel := context.WithCancel(context.Background()) + r.listenerCleanUp = append(r.listenerCleanUp, restCancel) err := lnrpc.RegisterLightningHandlerFromEndpoint( - context.Background(), mux, r.restProxyDest, - r.restDialOpts, + restCtx, restMux, r.restProxyDest, r.restDialOpts, ) if err != nil { return err } + + // Before listening on any of the interfaces, we also want to give the + // external subservers a chance to register their own REST proxy stub + // with our mux instance. + for _, lis := range r.listeners { + if lis.ExternalRestRegistrar != nil { + err := lis.ExternalRestRegistrar.RegisterRestSubserver( + restCtx, restMux, r.restProxyDest, + r.restDialOpts, + ) + if err != nil { + rpcsLog.Errorf("error registering "+ + "external REST subserver: %v", err) + } + } + } + + // Now spin up a network listener for each requested port and start a + // goroutine that serves REST with the created mux there. for _, restEndpoint := range cfg.RESTListeners { lis, err := lncfg.TLSListenOnAddress(restEndpoint, r.tlsCfg) if err != nil { - ltndLog.Errorf( - "gRPC proxy unable to listen on %s", - restEndpoint, - ) + ltndLog.Errorf("gRPC proxy unable to listen on %s", + restEndpoint) return err } r.listenerCleanUp = append(r.listenerCleanUp, func() { - lis.Close() + _ = lis.Close() }) go func() { rpcsLog.Infof("gRPC proxy started at %s", lis.Addr()) - http.Serve(lis, mux) + _ = http.Serve(lis, restMux) }() } From 85d5cdfbfd374fe3b21b68b436d667106730254d Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 14 May 2020 13:37:32 +0200 Subject: [PATCH 13/20] multi: move global registeredChains to cfg --- chainregistry.go | 10 +++++----- config.go | 13 +++++++++---- fundingmanager.go | 2 +- lnd.go | 15 +++++++-------- rpcserver.go | 6 +++--- server.go | 2 +- subrpcserver_config.go | 2 +- 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/chainregistry.go b/chainregistry.go index be1ec453..b5a38035 100644 --- a/chainregistry.go +++ b/chainregistry.go @@ -169,15 +169,15 @@ func newChainControlFromConfig(cfg *Config, chanDB *channeldb.DB, // Set the RPC config from the "home" chain. Multi-chain isn't yet // active, so we'll restrict usage to a particular chain for now. homeChainConfig := cfg.Bitcoin - if registeredChains.PrimaryChain() == litecoinChain { + if cfg.registeredChains.PrimaryChain() == litecoinChain { homeChainConfig = cfg.Litecoin } ltndLog.Infof("Primary chain is set to: %v", - registeredChains.PrimaryChain()) + cfg.registeredChains.PrimaryChain()) cc := &chainControl{} - switch registeredChains.PrimaryChain() { + switch cfg.registeredChains.PrimaryChain() { case bitcoinChain: cc.routingPolicy = htlcswitch.ForwardingPolicy{ MinHTLCOut: cfg.Bitcoin.MinHTLCOut, @@ -203,7 +203,7 @@ func newChainControlFromConfig(cfg *Config, chanDB *channeldb.DB, ) default: return nil, fmt.Errorf("default routing policy for chain %v is "+ - "unknown", registeredChains.PrimaryChain()) + "unknown", cfg.registeredChains.PrimaryChain()) } walletConfig := &btcwallet.Config{ @@ -505,7 +505,7 @@ func newChainControlFromConfig(cfg *Config, chanDB *channeldb.DB, // Select the default channel constraints for the primary chain. channelConstraints := defaultBtcChannelConstraints - if registeredChains.PrimaryChain() == litecoinChain { + if cfg.registeredChains.PrimaryChain() == litecoinChain { channelConstraints = defaultLtcChannelConstraints } diff --git a/config.go b/config.go index f2f9de88..05424661 100644 --- a/config.go +++ b/config.go @@ -247,6 +247,10 @@ type Config struct { ProtocolOptions *lncfg.ProtocolOptions `group:"protocol" namespace:"protocol"` AllowCircularRoute bool `long:"allow-circular-route" description:"If true, our node will allow htlc forwards that arrive and depart on the same channel."` + + // registeredChains keeps track of all chains that have been registered + // with the daemon. + registeredChains *chainRegistry } // DefaultConfig returns all default values for the Config struct. @@ -349,6 +353,7 @@ func DefaultConfig() Config { }, MaxOutgoingCltvExpiry: htlcswitch.DefaultMaxOutgoingCltvExpiry, MaxChannelFeeAllocation: htlcswitch.DefaultMaxLinkFeeAllocation, + registeredChains: newChainRegistry(), } } @@ -736,7 +741,7 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) { // Finally we'll register the litecoin chain as our current // primary chain. - registeredChains.RegisterPrimaryChain(litecoinChain) + cfg.registeredChains.RegisterPrimaryChain(litecoinChain) MaxFundingAmount = maxLtcFundingAmount MaxPaymentMSat = maxLtcPaymentMSat @@ -823,7 +828,7 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) { // Finally we'll register the bitcoin chain as our current // primary chain. - registeredChains.RegisterPrimaryChain(bitcoinChain) + cfg.registeredChains.RegisterPrimaryChain(bitcoinChain) } // Ensure that the user didn't attempt to specify negative values for @@ -878,7 +883,7 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) { // store all the data specific to this chain/network. networkDir = filepath.Join( cfg.DataDir, defaultChainSubDirname, - registeredChains.PrimaryChain().String(), + cfg.registeredChains.PrimaryChain().String(), normalizeNetwork(activeNetParams.Name), ) @@ -912,7 +917,7 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) { // Append the network type to the log directory so it is "namespaced" // per network in the same fashion as the data directory. cfg.LogDir = filepath.Join(cfg.LogDir, - registeredChains.PrimaryChain().String(), + cfg.registeredChains.PrimaryChain().String(), normalizeNetwork(activeNetParams.Name)) // Special show command to list supported subsystems and exit. diff --git a/fundingmanager.go b/fundingmanager.go index 6ae890c4..d1af81b0 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -3030,7 +3030,7 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) { // We'll determine our dust limit depending on which chain is active. var ourDustLimit btcutil.Amount - switch registeredChains.PrimaryChain() { + switch cfg.registeredChains.PrimaryChain() { case bitcoinChain: ourDustLimit = lnwallet.DefaultDustLimit() case litecoinChain: diff --git a/lnd.go b/lnd.go index c899db86..b2b2f5d0 100644 --- a/lnd.go +++ b/lnd.go @@ -52,8 +52,7 @@ import ( ) var ( - cfg *Config - registeredChains = newChainRegistry() + cfg *Config // networkDir is the path to the directory of the currently active // network. This path will hold the files related to each different @@ -229,7 +228,7 @@ func Main(config *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) erro } ltndLog.Infof("Active chain: %v (network=%v)", - strings.Title(registeredChains.PrimaryChain().String()), + strings.Title(cfg.registeredChains.PrimaryChain().String()), network, ) @@ -324,7 +323,7 @@ func Main(config *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) erro // light client instance, if enabled, in order to allow it to sync // while the rest of the daemon continues startup. mainChain := cfg.Bitcoin - if registeredChains.PrimaryChain() == litecoinChain { + if cfg.registeredChains.PrimaryChain() == litecoinChain { mainChain = cfg.Litecoin } var neutrinoCS *neutrino.ChainService @@ -481,8 +480,8 @@ func Main(config *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) erro // Finally before we start the server, we'll register the "holy // trinity" of interface for our current "home chain" with the active // chainRegistry interface. - primaryChain := registeredChains.PrimaryChain() - registeredChains.RegisterChain(primaryChain, activeChainControl) + primaryChain := cfg.registeredChains.PrimaryChain() + cfg.registeredChains.RegisterChain(primaryChain, activeChainControl) // TODO(roasbeef): add rotation idPrivKey, err := activeChainControl.wallet.DerivePrivKey(keychain.KeyDescriptor{ @@ -546,7 +545,7 @@ func Main(config *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) erro // Segment the watchtower directory by chain and network. towerDBDir := filepath.Join( cfg.Watchtower.TowerDir, - registeredChains.PrimaryChain().String(), + cfg.registeredChains.PrimaryChain().String(), normalizeNetwork(activeNetParams.Name), ) @@ -992,7 +991,7 @@ func waitForWalletPassword(restEndpoints []net.Addr, defer grpcServer.GracefulStop() chainConfig := cfg.Bitcoin - if registeredChains.PrimaryChain() == litecoinChain { + if cfg.registeredChains.PrimaryChain() == litecoinChain { chainConfig = cfg.Litecoin } diff --git a/rpcserver.go b/rpcserver.go index 80732992..d2879dc2 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2404,8 +2404,8 @@ func (r *rpcServer) GetInfo(ctx context.Context, } network := normalizeNetwork(activeNetParams.Name) - activeChains := make([]*lnrpc.Chain, registeredChains.NumActiveChains()) - for i, chain := range registeredChains.ActiveChains() { + activeChains := make([]*lnrpc.Chain, cfg.registeredChains.NumActiveChains()) + for i, chain := range cfg.registeredChains.ActiveChains() { activeChains[i] = &lnrpc.Chain{ Chain: chain.String(), Network: network, @@ -4365,7 +4365,7 @@ func (r *rpcServer) AddInvoice(ctx context.Context, invoice *lnrpc.Invoice) (*lnrpc.AddInvoiceResponse, error) { defaultDelta := cfg.Bitcoin.TimeLockDelta - if registeredChains.PrimaryChain() == litecoinChain { + if cfg.registeredChains.PrimaryChain() == litecoinChain { defaultDelta = cfg.Litecoin.TimeLockDelta } diff --git a/server.go b/server.go index 6c4ab2b1..c9bb5d7d 100644 --- a/server.go +++ b/server.go @@ -959,7 +959,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, // Select the configuration and furnding parameters for Bitcoin or // Litecoin, depending on the primary registered chain. - primaryChain := registeredChains.PrimaryChain() + primaryChain := cfg.registeredChains.PrimaryChain() chainCfg := cfg.Bitcoin minRemoteDelay := minBtcRemoteDelay maxRemoteDelay := maxBtcRemoteDelay diff --git a/subrpcserver_config.go b/subrpcserver_config.go index 31632028..af76d40a 100644 --- a/subrpcserver_config.go +++ b/subrpcserver_config.go @@ -203,7 +203,7 @@ func (s *subRPCServerConfigs) PopulateDependencies(cc *chainControl, reflect.ValueOf(nodeSigner), ) defaultDelta := cfg.Bitcoin.TimeLockDelta - if registeredChains.PrimaryChain() == litecoinChain { + if cfg.registeredChains.PrimaryChain() == litecoinChain { defaultDelta = cfg.Litecoin.TimeLockDelta } subCfgValue.FieldByName("DefaultCLTVExpiry").Set( From d44f205e3f2bbdfc6d117b0ce25ca77111a87d37 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 14 May 2020 13:40:18 +0200 Subject: [PATCH 14/20] multi: move global networkDir to cfg --- config.go | 15 ++++++++++----- lnd.go | 9 ++------- rpcserver.go | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/config.go b/config.go index 05424661..3fe0059f 100644 --- a/config.go +++ b/config.go @@ -251,6 +251,11 @@ type Config struct { // registeredChains keeps track of all chains that have been registered // with the daemon. registeredChains *chainRegistry + + // networkDir is the path to the directory of the currently active + // network. This path will hold the files related to each different + // network. + networkDir string } // DefaultConfig returns all default values for the Config struct. @@ -881,7 +886,7 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) { // We'll now construct the network directory which will be where we // store all the data specific to this chain/network. - networkDir = filepath.Join( + cfg.networkDir = filepath.Join( cfg.DataDir, defaultChainSubDirname, cfg.registeredChains.PrimaryChain().String(), normalizeNetwork(activeNetParams.Name), @@ -892,17 +897,17 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) { // the path for the macaroons to be generated. if cfg.AdminMacPath == "" { cfg.AdminMacPath = filepath.Join( - networkDir, defaultAdminMacFilename, + cfg.networkDir, defaultAdminMacFilename, ) } if cfg.ReadMacPath == "" { cfg.ReadMacPath = filepath.Join( - networkDir, defaultReadMacFilename, + cfg.networkDir, defaultReadMacFilename, ) } if cfg.InvoiceMacPath == "" { cfg.InvoiceMacPath = filepath.Join( - networkDir, defaultInvoiceMacFilename, + cfg.networkDir, defaultInvoiceMacFilename, ) } @@ -910,7 +915,7 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) { // we'll update the file location to match our set network directory. if cfg.BackupFilePath == "" { cfg.BackupFilePath = filepath.Join( - networkDir, chanbackup.DefaultBackupFileName, + cfg.networkDir, chanbackup.DefaultBackupFileName, ) } diff --git a/lnd.go b/lnd.go index b2b2f5d0..c6bd7b09 100644 --- a/lnd.go +++ b/lnd.go @@ -53,11 +53,6 @@ import ( var ( cfg *Config - - // networkDir is the path to the directory of the currently active - // network. This path will hold the files related to each different - // network. - networkDir string ) // WalletUnlockerAuthOptions returns a list of DialOptions that can be used to @@ -428,7 +423,7 @@ func Main(config *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) erro if !cfg.NoMacaroons { // Create the macaroon authentication/authorization service. macaroonService, err = macaroons.NewService( - networkDir, macaroons.IPLockChecker, + cfg.networkDir, macaroons.IPLockChecker, ) if err != nil { err := fmt.Errorf("unable to set up macaroon "+ @@ -1000,7 +995,7 @@ func waitForWalletPassword(restEndpoints []net.Addr, // deleted within it and recreated when successfully changing the // wallet's password. macaroonFiles := []string{ - filepath.Join(networkDir, macaroons.DBFilename), + filepath.Join(cfg.networkDir, macaroons.DBFilename), cfg.AdminMacPath, cfg.ReadMacPath, cfg.InvoiceMacPath, } pwService := walletunlocker.New( diff --git a/rpcserver.go b/rpcserver.go index d2879dc2..22c0561f 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -585,7 +585,7 @@ func newRPCServer(s *server, macService *macaroons.Service, // the dependencies they need are properly populated within each sub // server configuration struct. err = subServerCgs.PopulateDependencies( - s.cc, networkDir, macService, atpl, invoiceRegistry, + s.cc, cfg.networkDir, macService, atpl, invoiceRegistry, s.htlcSwitch, activeNetParams.Params, s.chanRouter, routerBackend, s.nodeSigner, s.chanDB, s.sweeper, tower, s.towerClient, cfg.net.ResolveTCPAddr, genInvoiceFeatures, From 7ce70321acbeba944e92ccfddc86dc726e73c77d Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 14 May 2020 13:53:33 +0200 Subject: [PATCH 15/20] fundingmgr+server: don't use global cfg --- fundingmanager.go | 20 +++++++++++++++----- fundingmanager_test.go | 7 +++---- server.go | 2 ++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/fundingmanager.go b/fundingmanager.go index d1af81b0..c62f7526 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -369,6 +369,14 @@ type fundingConfig struct { // NotifyPendingOpenChannelEvent informs the ChannelNotifier when channels // enter a pending state. NotifyPendingOpenChannelEvent func(wire.OutPoint, *channeldb.OpenChannel) + + // EnableUpfrontShutdown specifies whether the upfront shutdown script + // is enabled. + EnableUpfrontShutdown bool + + // RegisteredChains keeps track of all chains that have been registered + // with the daemon. + RegisteredChains *chainRegistry } // fundingManager acts as an orchestrator/bridge between the wallet's @@ -1322,7 +1330,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) { // A nil address is set in place of user input, because this channel open // was not initiated by the user. shutdown, err := getUpfrontShutdownScript( - fmsg.peer, nil, + f.cfg.EnableUpfrontShutdown, fmsg.peer, nil, func() (lnwire.DeliveryAddress, error) { addr, err := f.cfg.Wallet.NewAddress(lnwallet.WitnessPubKey, false) if err != nil { @@ -2981,7 +2989,8 @@ func (f *fundingManager) initFundingWorkflow(peer lnpeer.Peer, req *openChanReq) // our peer does support the feature, we will return the user provided script // if non-zero, or a freshly generated script if our node is configured to set // upfront shutdown scripts automatically. -func getUpfrontShutdownScript(peer lnpeer.Peer, script lnwire.DeliveryAddress, +func getUpfrontShutdownScript(enableUpfrontShutdown bool, peer lnpeer.Peer, + script lnwire.DeliveryAddress, getScript func() (lnwire.DeliveryAddress, error)) (lnwire.DeliveryAddress, error) { @@ -3010,7 +3019,7 @@ func getUpfrontShutdownScript(peer lnpeer.Peer, script lnwire.DeliveryAddress, // If we do not have setting of upfront shutdown script enabled, return // an empty script. - if !cfg.EnableUpfrontShutdown { + if !enableUpfrontShutdown { return nil, nil } @@ -3030,7 +3039,7 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) { // We'll determine our dust limit depending on which chain is active. var ourDustLimit btcutil.Amount - switch cfg.registeredChains.PrimaryChain() { + switch f.cfg.RegisteredChains.PrimaryChain() { case bitcoinChain: ourDustLimit = lnwallet.DefaultDustLimit() case litecoinChain: @@ -3084,7 +3093,8 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) { // address from the wallet if our node is configured to set shutdown // address by default). shutdown, err := getUpfrontShutdownScript( - msg.peer, msg.openChanReq.shutdownScript, + f.cfg.EnableUpfrontShutdown, msg.peer, + msg.openChanReq.shutdownScript, func() (lnwire.DeliveryAddress, error) { addr, err := f.cfg.Wallet.NewAddress( lnwallet.WitnessPubKey, false, diff --git a/fundingmanager_test.go b/fundingmanager_test.go index db870d70..a4fdbc8a 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -418,6 +418,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, NotifyOpenChannelEvent: evt.NotifyOpenChannelEvent, OpenChannelPredicate: chainedAcceptor, NotifyPendingOpenChannelEvent: evt.NotifyPendingOpenChannelEvent, + RegisteredChains: newChainRegistry(), } for _, op := range options { @@ -3084,11 +3085,9 @@ func TestGetUpfrontShutdownScript(t *testing.T) { } } - // Set the command line option in config as needed. - cfg = &Config{EnableUpfrontShutdown: test.localEnabled} - addr, err := getUpfrontShutdownScript( - &mockPeer, test.upfrontScript, test.getScript, + test.localEnabled, &mockPeer, test.upfrontScript, + test.getScript, ) if err != test.expectedErr { t.Fatalf("got: %v, expected error: %v", err, test.expectedErr) diff --git a/server.go b/server.go index c9bb5d7d..872f0748 100644 --- a/server.go +++ b/server.go @@ -1144,6 +1144,8 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, NotifyOpenChannelEvent: s.channelNotifier.NotifyOpenChannelEvent, OpenChannelPredicate: chanPredicate, NotifyPendingOpenChannelEvent: s.channelNotifier.NotifyPendingOpenChannelEvent, + EnableUpfrontShutdown: cfg.EnableUpfrontShutdown, + RegisteredChains: cfg.registeredChains, }) if err != nil { return nil, err From 7e4d0aba2f93b3597dfa8fc4cd133aae46c006ca Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 14 May 2020 14:07:26 +0200 Subject: [PATCH 16/20] lnd+rpcserver: don't use global cfg --- lnd.go | 2 +- rpcserver.go | 33 ++++++++++++++++++--------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/lnd.go b/lnd.go index c6bd7b09..720380d0 100644 --- a/lnd.go +++ b/lnd.go @@ -669,7 +669,7 @@ func Main(config *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) erro // Initialize, and register our implementation of the gRPC interface // exported by the rpcServer. rpcServer, err := newRPCServer( - server, macaroonService, cfg.SubRPCServers, serverOpts, + cfg, server, macaroonService, cfg.SubRPCServers, serverOpts, restDialOpts, restProxyDest, atplManager, server.invoices, tower, tlsCfg, rpcListeners, chainedAcceptor, ) diff --git a/rpcserver.go b/rpcserver.go index 22c0561f..4997d811 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -461,6 +461,8 @@ type rpcServer struct { server *server + cfg *Config + // subServers are a set of sub-RPC servers that use the same gRPC and // listening sockets as the main RPC server, but which maintain their // own independent service. This allows us to expose a set of @@ -521,7 +523,7 @@ var _ lnrpc.LightningServer = (*rpcServer)(nil) // of the sub-servers that it maintains. The set of serverOpts should be the // base level options passed to the grPC server. This typically includes things // like requiring TLS, etc. -func newRPCServer(s *server, macService *macaroons.Service, +func newRPCServer(cfg *Config, s *server, macService *macaroons.Service, subServerCgs *subRPCServerConfigs, serverOpts []grpc.ServerOption, restDialOpts []grpc.DialOption, restProxyDest string, atpl *autopilot.Manager, invoiceRegistry *invoices.InvoiceRegistry, @@ -700,6 +702,7 @@ func newRPCServer(s *server, macService *macaroons.Service, // gRPC server, and register the main lnrpc server along side. grpcServer := grpc.NewServer(serverOpts...) rootRPCServer := &rpcServer{ + cfg: cfg, restDialOpts: restDialOpts, listeners: listeners, listenerCleanUp: []func(){cleanup}, @@ -783,9 +786,9 @@ func (r *rpcServer) Start() error { } // If Prometheus monitoring is enabled, start the Prometheus exporter. - if cfg.Prometheus.Enabled() { + if r.cfg.Prometheus.Enabled() { err := monitoring.ExportPrometheusMetrics( - r.grpcServer, cfg.Prometheus, + r.grpcServer, r.cfg.Prometheus, ) if err != nil { return err @@ -839,7 +842,7 @@ func (r *rpcServer) Start() error { // Now spin up a network listener for each requested port and start a // goroutine that serves REST with the created mux there. - for _, restEndpoint := range cfg.RESTListeners { + for _, restEndpoint := range r.cfg.RESTListeners { lis, err := lncfg.TLSListenOnAddress(restEndpoint, r.tlsCfg) if err != nil { ltndLog.Errorf("gRPC proxy unable to listen on %s", @@ -1477,7 +1480,7 @@ func (r *rpcServer) DisconnectPeer(ctx context.Context, // In order to avoid erroneously disconnecting from a peer that we have // an active channel with, if we have any channels active with this // peer, then we'll disallow disconnecting from them. - if len(nodeChannels) > 0 && !cfg.UnsafeDisconnect { + if len(nodeChannels) > 0 && !r.cfg.UnsafeDisconnect { return nil, fmt.Errorf("cannot disconnect from peer(%x), "+ "all active channels with the peer need to be closed "+ "first", pubKeyBytes) @@ -2404,8 +2407,8 @@ func (r *rpcServer) GetInfo(ctx context.Context, } network := normalizeNetwork(activeNetParams.Name) - activeChains := make([]*lnrpc.Chain, cfg.registeredChains.NumActiveChains()) - for i, chain := range cfg.registeredChains.ActiveChains() { + activeChains := make([]*lnrpc.Chain, r.cfg.registeredChains.NumActiveChains()) + for i, chain := range r.cfg.registeredChains.ActiveChains() { activeChains[i] = &lnrpc.Chain{ Chain: chain.String(), Network: network, @@ -3813,7 +3816,7 @@ func (r *rpcServer) extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPayme // Take the CLTV limit from the request if set, otherwise use the max. cltvLimit, err := routerrpc.ValidateCLTVLimit( - rpcPayReq.CltvLimit, cfg.MaxOutgoingCltvExpiry, + rpcPayReq.CltvLimit, r.cfg.MaxOutgoingCltvExpiry, ) if err != nil { return payIntent, err @@ -3942,7 +3945,7 @@ func (r *rpcServer) extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPayme // use when creating an invoice. We do not assume the default of // 9 blocks that is defined in BOLT-11, because this is never // enough for other lnd nodes. - payIntent.cltvDelta = uint16(cfg.Bitcoin.TimeLockDelta) + payIntent.cltvDelta = uint16(r.cfg.Bitcoin.TimeLockDelta) } // If the user is manually specifying payment details, then the payment @@ -4364,9 +4367,9 @@ func (r *rpcServer) sendPaymentSync(ctx context.Context, func (r *rpcServer) AddInvoice(ctx context.Context, invoice *lnrpc.Invoice) (*lnrpc.AddInvoiceResponse, error) { - defaultDelta := cfg.Bitcoin.TimeLockDelta - if cfg.registeredChains.PrimaryChain() == litecoinChain { - defaultDelta = cfg.Litecoin.TimeLockDelta + defaultDelta := r.cfg.Bitcoin.TimeLockDelta + if r.cfg.registeredChains.PrimaryChain() == litecoinChain { + defaultDelta = r.cfg.Litecoin.TimeLockDelta } addInvoiceCfg := &invoicesrpc.AddInvoiceConfig{ @@ -6070,14 +6073,14 @@ func (r *rpcServer) ChannelAcceptor(stream lnrpc.Lightning_ChannelAcceptorServer } // timeout is the time after which ChannelAcceptRequests expire. - timeout := time.After(cfg.AcceptorTimeout) + timeout := time.After(r.cfg.AcceptorTimeout) // Send the request to the newRequests channel. select { case newRequests <- newRequest: case <-timeout: rpcsLog.Errorf("RPCAcceptor returned false - reached timeout of %d", - cfg.AcceptorTimeout) + r.cfg.AcceptorTimeout) return false case <-quit: return false @@ -6092,7 +6095,7 @@ func (r *rpcServer) ChannelAcceptor(stream lnrpc.Lightning_ChannelAcceptorServer return resp case <-timeout: rpcsLog.Errorf("RPCAcceptor returned false - reached timeout of %d", - cfg.AcceptorTimeout) + r.cfg.AcceptorTimeout) return false case <-quit: return false From 4343f9e9a6496f9d389d5e1e798fcea3dbde54ba Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 14 May 2020 14:18:11 +0200 Subject: [PATCH 17/20] server+rpcserver: don't use global cfg --- lnd.go | 2 +- rpcserver.go | 2 +- server.go | 66 ++++++++++++++++++++++++++++------------------------ 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/lnd.go b/lnd.go index 720380d0..ba59b3c5 100644 --- a/lnd.go +++ b/lnd.go @@ -619,7 +619,7 @@ func Main(config *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) erro // Set up the core server which will listen for incoming peer // connections. server, err := newServer( - cfg.Listeners, chanDB, towerClientDB, activeChainControl, + cfg, cfg.Listeners, chanDB, towerClientDB, activeChainControl, idPrivKey, walletInitParams.ChansToRestore, chainedAcceptor, torController, ) diff --git a/rpcserver.go b/rpcserver.go index 4997d811..a5e0933b 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -1423,7 +1423,7 @@ func (r *rpcServer) ConnectPeer(ctx context.Context, return nil, fmt.Errorf("cannot make connection to self") } - addr, err := parseAddr(in.Addr.Host) + addr, err := parseAddr(in.Addr.Host, r.cfg.net) if err != nil { return nil, err } diff --git a/server.go b/server.go index 872f0748..eeb37b74 100644 --- a/server.go +++ b/server.go @@ -133,6 +133,8 @@ type server struct { start sync.Once stop sync.Once + cfg *Config + // identityPriv is the private key used to authenticate any incoming // connections. identityPriv *btcec.PrivateKey @@ -272,7 +274,7 @@ type server struct { } // parseAddr parses an address from its string format to a net.Addr. -func parseAddr(address string) (net.Addr, error) { +func parseAddr(address string, netCfg tor.Net) (net.Addr, error) { var ( host string port int @@ -304,21 +306,23 @@ func parseAddr(address string) (net.Addr, error) { // addresses over Tor in order to prevent leaking your real IP // address. hostPort := net.JoinHostPort(host, strconv.Itoa(port)) - return cfg.net.ResolveTCPAddr("tcp", hostPort) + return netCfg.ResolveTCPAddr("tcp", hostPort) } // noiseDial is a factory function which creates a connmgr compliant dialing // function by returning a closure which includes the server's identity key. -func noiseDial(idPriv *btcec.PrivateKey) func(net.Addr) (net.Conn, error) { +func noiseDial(idPriv *btcec.PrivateKey, + netCfg tor.Net) func(net.Addr) (net.Conn, error) { + return func(a net.Addr) (net.Conn, error) { lnAddr := a.(*lnwire.NetAddress) - return brontide.Dial(idPriv, lnAddr, cfg.net.Dial) + return brontide.Dial(idPriv, lnAddr, netCfg.Dial) } } // newServer creates a new instance of the server which is to listen using the // passed listener address. -func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, +func newServer(cfg *Config, listenAddrs []net.Addr, chanDB *channeldb.DB, towerClientDB *wtdb.ClientDB, cc *chainControl, privKey *btcec.PrivateKey, chansToRestore walletunlocker.ChannelsToRecover, @@ -406,6 +410,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, } s := &server{ + cfg: cfg, chanDB: chanDB, cc: cc, sigPool: lnwallet.NewSigPool(cfg.Workers.Sig, cc.signer), @@ -1222,7 +1227,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, OnAccept: s.InboundPeerConnected, RetryDuration: time.Second * 5, TargetOutbound: 100, - Dial: noiseDial(s.identityPriv), + Dial: noiseDial(s.identityPriv, s.cfg.net), OnConnection: s.OutboundPeerConnected, }) if err != nil { @@ -1404,8 +1409,9 @@ func (s *server) Start() error { // configure the set of active bootstrappers, and launch a // dedicated goroutine to maintain a set of persistent // connections. - if !cfg.NoNetBootstrap && !(cfg.Bitcoin.SimNet || cfg.Litecoin.SimNet) && - !(cfg.Bitcoin.RegTest || cfg.Litecoin.RegTest) { + if !s.cfg.NoNetBootstrap && + !(s.cfg.Bitcoin.SimNet || s.cfg.Litecoin.SimNet) && + !(s.cfg.Bitcoin.RegTest || s.cfg.Litecoin.RegTest) { bootstrappers, err := initNetworkBootstrappers(s) if err != nil { @@ -1548,7 +1554,7 @@ func (s *server) watchExternalIP() { // Keep track of the external IPs set by the user to avoid replacing // them when detecting a new IP. ipsSetByUser := make(map[string]struct{}) - for _, ip := range cfg.ExternalIPs { + for _, ip := range s.cfg.ExternalIPs { ipsSetByUser[ip.String()] = struct{}{} } @@ -1692,7 +1698,7 @@ func initNetworkBootstrappers(s *server) ([]discovery.NetworkPeerBootstrapper, e // If this isn't simnet mode, then one of our additional bootstrapping // sources will be the set of running DNS seeds. - if !cfg.Bitcoin.SimNet || !cfg.Litecoin.SimNet { + if !s.cfg.Bitcoin.SimNet || !s.cfg.Litecoin.SimNet { dnsSeeds, ok := chainDNSSeeds[*activeNetParams.GenesisHash] // If we have a set of DNS seeds for this chain, then we'll add @@ -1702,7 +1708,7 @@ func initNetworkBootstrappers(s *server) ([]discovery.NetworkPeerBootstrapper, e "seeds: %v", dnsSeeds) dnsBootStrapper := discovery.NewDNSSeedBootstrapper( - dnsSeeds, cfg.net, + dnsSeeds, s.cfg.net, ) bootStrappers = append(bootStrappers, dnsBootStrapper) } @@ -1974,13 +1980,13 @@ func (s *server) createNewHiddenService() error { onionCfg := tor.AddOnionConfig{ VirtualPort: defaultPeerPort, TargetPorts: listenPorts, - Store: tor.NewOnionFile(cfg.Tor.PrivateKeyPath, 0600), + Store: tor.NewOnionFile(s.cfg.Tor.PrivateKeyPath, 0600), } switch { - case cfg.Tor.V2: + case s.cfg.Tor.V2: onionCfg.Type = tor.V2 - case cfg.Tor.V3: + case s.cfg.Tor.V3: onionCfg.Type = tor.V3 } @@ -2133,7 +2139,7 @@ func (s *server) establishPersistentConnections() error { // We'll only attempt to connect to Tor addresses if Tor // outbound support is enabled. case *tor.OnionAddr: - if cfg.Tor.Active { + if s.cfg.Tor.Active { addrSet[addr.String()] = addr } } @@ -2151,7 +2157,7 @@ func (s *server) establishPersistentConnections() error { // We'll only attempt to connect to Tor // addresses if Tor outbound support is enabled. case *tor.OnionAddr: - if cfg.Tor.Active { + if s.cfg.Tor.Active { addrSet[lnAddress.String()] = lnAddress } } @@ -2195,7 +2201,7 @@ func (s *server) establishPersistentConnections() error { // been requested as perm by the user. s.persistentPeers[pubStr] = false if _, ok := s.persistentPeersBackoff[pubStr]; !ok { - s.persistentPeersBackoff[pubStr] = cfg.MinBackoff + s.persistentPeersBackoff[pubStr] = s.cfg.MinBackoff } for _, address := range nodeAddr.addresses { @@ -2228,7 +2234,7 @@ func (s *server) establishPersistentConnections() error { // nodes are able to disperse the costs of connecting to // all peers at once. if numOutboundConns < numInstantInitReconnect || - !cfg.StaggerInitialReconnect { + !s.cfg.StaggerInitialReconnect { go s.connMgr.Connect(connReq) } else { @@ -2437,14 +2443,14 @@ func (s *server) nextPeerBackoff(pubStr string, backoff, ok := s.persistentPeersBackoff[pubStr] if !ok { // If an existing backoff was unknown, use the default. - return cfg.MinBackoff + return s.cfg.MinBackoff } // If the peer failed to start properly, we'll just use the previous // backoff to compute the subsequent randomized exponential backoff // duration. This will roughly double on average. if startTime.IsZero() { - return computeNextBackoff(backoff) + return computeNextBackoff(backoff, s.cfg.MaxBackoff) } // The peer succeeded in starting. If the connection didn't last long @@ -2452,7 +2458,7 @@ func (s *server) nextPeerBackoff(pubStr string, // with this peer. connDuration := time.Since(startTime) if connDuration < defaultStableConnDuration { - return computeNextBackoff(backoff) + return computeNextBackoff(backoff, s.cfg.MaxBackoff) } // The peer succeed in starting and this was stable peer, so we'll @@ -2460,8 +2466,8 @@ func (s *server) nextPeerBackoff(pubStr string, // applying randomized exponential backoff. We'll only apply this in the // case that: // reb(curBackoff) - connDuration > cfg.MinBackoff - relaxedBackoff := computeNextBackoff(backoff) - connDuration - if relaxedBackoff > cfg.MinBackoff { + relaxedBackoff := computeNextBackoff(backoff, s.cfg.MaxBackoff) - connDuration + if relaxedBackoff > s.cfg.MinBackoff { return relaxedBackoff } @@ -2469,7 +2475,7 @@ func (s *server) nextPeerBackoff(pubStr string, // the stable connection lasted much longer than our previous backoff. // To reward such good behavior, we'll reconnect after the default // timeout. - return cfg.MinBackoff + return s.cfg.MinBackoff } // shouldDropConnection determines if our local connection to a remote peer @@ -2783,7 +2789,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq, // closed when the htlc is outstanding and a new block comes in. p, err := newPeer( conn, connReq, s, peerAddr, inbound, initFeatures, - legacyFeatures, cfg.ChanEnableTimeout, + legacyFeatures, s.cfg.ChanEnableTimeout, lncfg.DefaultOutgoingCltvRejectDelta, errBuffer, ) if err != nil { @@ -3226,7 +3232,7 @@ func (s *server) ConnectToPeer(addr *lnwire.NetAddress, perm bool) error { // zero. s.persistentPeers[targetPub] = true if _, ok := s.persistentPeersBackoff[targetPub]; !ok { - s.persistentPeersBackoff[targetPub] = cfg.MinBackoff + s.persistentPeersBackoff[targetPub] = s.cfg.MinBackoff } s.persistentConnReqs[targetPub] = append( s.persistentConnReqs[targetPub], connReq, @@ -3258,7 +3264,7 @@ func (s *server) ConnectToPeer(addr *lnwire.NetAddress, perm bool) error { // notify the caller if the connection attempt has failed. Otherwise, it will be // closed. func (s *server) connectToPeer(addr *lnwire.NetAddress, errChan chan<- error) { - conn, err := brontide.Dial(s.identityPriv, addr, cfg.net.Dial) + conn, err := brontide.Dial(s.identityPriv, addr, s.cfg.net.Dial) if err != nil { srvrLog.Errorf("Unable to connect to %v: %v", addr, err) select { @@ -3409,11 +3415,11 @@ func parseHexColor(colorStr string) (color.RGBA, error) { // backoff using the value of the exiting backoff. The returned duration is // randomized in either direction by 1/20 to prevent tight loops from // stabilizing. -func computeNextBackoff(currBackoff time.Duration) time.Duration { +func computeNextBackoff(currBackoff, maxBackoff time.Duration) time.Duration { // Double the current backoff, truncating if it exceeds our maximum. nextBackoff := 2 * currBackoff - if nextBackoff > cfg.MaxBackoff { - nextBackoff = cfg.MaxBackoff + if nextBackoff > maxBackoff { + nextBackoff = maxBackoff } // Using 1/10 of our duration as a margin, compute a random offset to From 7c1304b31c18806e4abf3aa028b718c6b22d54e2 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 14 May 2020 14:26:07 +0200 Subject: [PATCH 18/20] peer: don't use global cfg --- peer.go | 14 +++++++++----- server.go | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/peer.go b/peer.go index f73e8c5c..aa4cbce9 100644 --- a/peer.go +++ b/peer.go @@ -114,6 +114,8 @@ type peer struct { started int32 disconnect int32 + cfg *Config + // The following fields are only meant to be used *atomically* bytesReceived uint64 bytesSent uint64 @@ -263,7 +265,7 @@ var _ lnpeer.Peer = (*peer)(nil) // pointer to the main server. It takes an error buffer which may contain errors // from a previous connection with the peer if we have been connected to them // before. -func newPeer(conn net.Conn, connReq *connmgr.ConnReq, server *server, +func newPeer(cfg *Config, conn net.Conn, connReq *connmgr.ConnReq, server *server, addr *lnwire.NetAddress, inbound bool, features, legacyFeatures *lnwire.FeatureVector, chanActiveTimeout time.Duration, @@ -277,6 +279,8 @@ func newPeer(conn net.Conn, connReq *connmgr.ConnReq, server *server, conn: conn, addr: addr, + cfg: cfg, + activeSignal: make(chan struct{}), inbound: inbound, @@ -651,7 +655,7 @@ func (p *peer) addLink(chanPoint *wire.OutPoint, DecodeHopIterators: p.server.sphinx.DecodeHopIterators, ExtractErrorEncrypter: p.server.sphinx.ExtractErrorEncrypter, FetchLastChannelUpdate: p.server.fetchLastChanUpdate(), - HodlMask: cfg.Hodl.Mask(), + HodlMask: p.cfg.Hodl.Mask(), Registry: p.server.invoices, Switch: p.server.htlcSwitch, Circuits: p.server.htlcSwitch.CircuitModifier(), @@ -671,13 +675,13 @@ func (p *peer) addLink(chanPoint *wire.OutPoint, FwdPkgGCTicker: ticker.New(time.Minute), PendingCommitTicker: ticker.New(time.Minute), BatchSize: 10, - UnsafeReplay: cfg.UnsafeReplay, + UnsafeReplay: p.cfg.UnsafeReplay, MinFeeUpdateTimeout: htlcswitch.DefaultMinLinkFeeUpdateTimeout, MaxFeeUpdateTimeout: htlcswitch.DefaultMaxLinkFeeUpdateTimeout, OutgoingCltvRejectDelta: p.outgoingCltvRejectDelta, TowerClient: p.server.towerClient, - MaxOutgoingCltvExpiry: cfg.MaxOutgoingCltvExpiry, - MaxFeeAllocation: cfg.MaxChannelFeeAllocation, + MaxOutgoingCltvExpiry: p.cfg.MaxOutgoingCltvExpiry, + MaxFeeAllocation: p.cfg.MaxChannelFeeAllocation, NotifyActiveLink: p.server.channelNotifier.NotifyActiveLinkEvent, NotifyActiveChannel: p.server.channelNotifier.NotifyActiveChannelEvent, NotifyInactiveChannel: p.server.channelNotifier.NotifyInactiveChannelEvent, diff --git a/server.go b/server.go index eeb37b74..8e84b38d 100644 --- a/server.go +++ b/server.go @@ -2788,7 +2788,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq, // htlcs, an extra block is added to prevent the channel from being // closed when the htlc is outstanding and a new block comes in. p, err := newPeer( - conn, connReq, s, peerAddr, inbound, initFeatures, + s.cfg, conn, connReq, s, peerAddr, inbound, initFeatures, legacyFeatures, s.cfg.ChanEnableTimeout, lncfg.DefaultOutgoingCltvRejectDelta, errBuffer, ) From 50e86f88fe37e578381372bb0a46bce43aa7ba90 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 14 May 2020 14:33:01 +0200 Subject: [PATCH 19/20] rpcserver+subrpcserver: don't use global cfg --- rpcserver.go | 2 +- subrpcserver_config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rpcserver.go b/rpcserver.go index a5e0933b..520899ef 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -587,7 +587,7 @@ func newRPCServer(cfg *Config, s *server, macService *macaroons.Service, // the dependencies they need are properly populated within each sub // server configuration struct. err = subServerCgs.PopulateDependencies( - s.cc, cfg.networkDir, macService, atpl, invoiceRegistry, + cfg, s.cc, cfg.networkDir, macService, atpl, invoiceRegistry, s.htlcSwitch, activeNetParams.Params, s.chanRouter, routerBackend, s.nodeSigner, s.chanDB, s.sweeper, tower, s.towerClient, cfg.net.ResolveTCPAddr, genInvoiceFeatures, diff --git a/subrpcserver_config.go b/subrpcserver_config.go index af76d40a..0e98a928 100644 --- a/subrpcserver_config.go +++ b/subrpcserver_config.go @@ -80,7 +80,7 @@ type subRPCServerConfigs struct { // // NOTE: This MUST be called before any callers are permitted to execute the // FetchConfig method. -func (s *subRPCServerConfigs) PopulateDependencies(cc *chainControl, +func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config, cc *chainControl, networkDir string, macService *macaroons.Service, atpl *autopilot.Manager, invoiceRegistry *invoices.InvoiceRegistry, From 4261d3f5af16255bed96a99364d5a09053a47637 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 14 May 2020 14:35:11 +0200 Subject: [PATCH 20/20] lnd: remove global cfg variable --- chainregistry.go | 4 +++- lnd.go | 55 ++++++++++++++++++++++-------------------------- server_test.go | 7 +++++- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/chainregistry.go b/chainregistry.go index b5a38035..13f16e53 100644 --- a/chainregistry.go +++ b/chainregistry.go @@ -719,7 +719,9 @@ func (c *chainRegistry) NumActiveChains() uint32 { // initNeutrinoBackend inits a new instance of the neutrino light client // backend given a target chain directory to store the chain state. -func initNeutrinoBackend(chainDir string) (*neutrino.ChainService, func(), error) { +func initNeutrinoBackend(cfg *Config, chainDir string) (*neutrino.ChainService, + func(), error) { + // First we'll open the database file for neutrino, creating the // database if needed. We append the normalized network name here to // match the behavior of btcwallet. diff --git a/lnd.go b/lnd.go index ba59b3c5..840e684f 100644 --- a/lnd.go +++ b/lnd.go @@ -51,16 +51,12 @@ import ( "github.com/lightningnetwork/lnd/watchtower/wtdb" ) -var ( - cfg *Config -) - // WalletUnlockerAuthOptions returns a list of DialOptions that can be used to // authenticate with the wallet unlocker service. // // NOTE: This should only be called after the WalletUnlocker listener has // signaled it is ready. -func WalletUnlockerAuthOptions() ([]grpc.DialOption, error) { +func WalletUnlockerAuthOptions(cfg *Config) ([]grpc.DialOption, error) { creds, err := credentials.NewClientTLSFromFile(cfg.TLSCertPath, "") if err != nil { return nil, fmt.Errorf("unable to read TLS cert: %v", err) @@ -79,7 +75,7 @@ func WalletUnlockerAuthOptions() ([]grpc.DialOption, error) { // // NOTE: This should only be called after the RPCListener has signaled it is // ready. -func AdminAuthOptions() ([]grpc.DialOption, error) { +func AdminAuthOptions(cfg *Config) ([]grpc.DialOption, error) { creds, err := credentials.NewClientTLSFromFile(cfg.TLSCertPath, "") if err != nil { return nil, fmt.Errorf("unable to read TLS cert: %v", err) @@ -192,8 +188,7 @@ type rpcListeners func() ([]*ListenerWithSignal, func(), error) // validated main configuration struct and an optional listener config struct. // This function starts all main system components then blocks until a signal // is received on the shutdownChan at which point everything is shut down again. -func Main(config *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { - cfg = config +func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { defer func() { ltndLog.Info("Shutdown complete") err := RootLogWriter.Close() @@ -289,10 +284,7 @@ func Main(config *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) erro ctx, cancel := context.WithCancel(ctx) defer cancel() - tlsCfg, restCreds, restProxyDest, err := getTLSConfig( - cfg.TLSCertPath, cfg.TLSKeyPath, cfg.TLSExtraIPs, - cfg.TLSExtraDomains, cfg.RPCListeners, - ) + tlsCfg, restCreds, restProxyDest, err := getTLSConfig(cfg) if err != nil { err := fmt.Errorf("unable to load TLS credentials: %v", err) ltndLog.Error(err) @@ -324,7 +316,7 @@ func Main(config *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) erro var neutrinoCS *neutrino.ChainService if mainChain.Node == "neutrino" { neutrinoBackend, neutrinoCleanUp, err := initNeutrinoBackend( - mainChain.ChainDir, + cfg, mainChain.ChainDir, ) if err != nil { err := fmt.Errorf("unable to initialize neutrino "+ @@ -398,7 +390,7 @@ func Main(config *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) erro // for wallet encryption. if !cfg.NoSeedBackup { params, err := waitForWalletPassword( - cfg.RESTListeners, serverOpts, restDialOpts, + cfg, cfg.RESTListeners, serverOpts, restDialOpts, restProxyDest, tlsCfg, walletUnlockerListeners, ) if err != nil { @@ -773,16 +765,15 @@ func Main(config *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) erro // getTLSConfig returns a TLS configuration for the gRPC server and credentials // and a proxy destination for the REST reverse proxy. -func getTLSConfig(tlsCertPath string, tlsKeyPath string, tlsExtraIPs, - tlsExtraDomains []string, rpcListeners []net.Addr) (*tls.Config, - *credentials.TransportCredentials, string, error) { +func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials, + string, error) { // Ensure we create TLS key and certificate if they don't exist. - if !fileExists(tlsCertPath) && !fileExists(tlsKeyPath) { + if !fileExists(cfg.TLSCertPath) && !fileExists(cfg.TLSKeyPath) { rpcsLog.Infof("Generating TLS certificates...") err := cert.GenCertPair( - "lnd autogenerated cert", tlsCertPath, tlsKeyPath, - tlsExtraIPs, tlsExtraDomains, + "lnd autogenerated cert", cfg.TLSCertPath, + cfg.TLSKeyPath, cfg.TLSExtraIPs, cfg.TLSExtraDomains, cert.DefaultAutogenValidity, ) if err != nil { @@ -791,7 +782,9 @@ func getTLSConfig(tlsCertPath string, tlsKeyPath string, tlsExtraIPs, rpcsLog.Infof("Done generating TLS certificates") } - certData, parsedCert, err := cert.LoadCert(tlsCertPath, tlsKeyPath) + certData, parsedCert, err := cert.LoadCert( + cfg.TLSCertPath, cfg.TLSKeyPath, + ) if err != nil { return nil, nil, "", err } @@ -803,7 +796,7 @@ func getTLSConfig(tlsCertPath string, tlsKeyPath string, tlsExtraIPs, refresh := false if cfg.TLSAutoRefresh { refresh, err = cert.IsOutdated( - parsedCert, tlsExtraIPs, tlsExtraDomains, + parsedCert, cfg.TLSExtraIPs, cfg.TLSExtraDomains, ) if err != nil { return nil, nil, "", err @@ -816,20 +809,20 @@ func getTLSConfig(tlsCertPath string, tlsKeyPath string, tlsExtraIPs, ltndLog.Info("TLS certificate is expired or outdated, " + "generating a new one") - err := os.Remove(tlsCertPath) + err := os.Remove(cfg.TLSCertPath) if err != nil { return nil, nil, "", err } - err = os.Remove(tlsKeyPath) + err = os.Remove(cfg.TLSKeyPath) if err != nil { return nil, nil, "", err } rpcsLog.Infof("Renewing TLS certificates...") err = cert.GenCertPair( - "lnd autogenerated cert", tlsCertPath, tlsKeyPath, - tlsExtraIPs, tlsExtraDomains, + "lnd autogenerated cert", cfg.TLSCertPath, + cfg.TLSKeyPath, cfg.TLSExtraIPs, cfg.TLSExtraDomains, cert.DefaultAutogenValidity, ) if err != nil { @@ -838,19 +831,21 @@ func getTLSConfig(tlsCertPath string, tlsKeyPath string, tlsExtraIPs, rpcsLog.Infof("Done renewing TLS certificates") // Reload the certificate data. - certData, _, err = cert.LoadCert(tlsCertPath, tlsKeyPath) + certData, _, err = cert.LoadCert( + cfg.TLSCertPath, cfg.TLSKeyPath, + ) if err != nil { return nil, nil, "", err } } tlsCfg := cert.TLSConfFromCert(certData) - restCreds, err := credentials.NewClientTLSFromFile(tlsCertPath, "") + restCreds, err := credentials.NewClientTLSFromFile(cfg.TLSCertPath, "") if err != nil { return nil, nil, "", err } - restProxyDest := rpcListeners[0].String() + restProxyDest := cfg.RPCListeners[0].String() switch { case strings.Contains(restProxyDest, "0.0.0.0"): restProxyDest = strings.Replace( @@ -967,7 +962,7 @@ type WalletUnlockParams struct { // waitForWalletPassword will spin up gRPC and REST endpoints for the // WalletUnlocker server, and block until a password is provided by // the user to this RPC server. -func waitForWalletPassword(restEndpoints []net.Addr, +func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, serverOpts []grpc.ServerOption, restDialOpts []grpc.DialOption, restProxyDest string, tlsConf *tls.Config, getListeners rpcListeners) (*WalletUnlockParams, error) { diff --git a/server_test.go b/server_test.go index cb9034a0..d272fcf9 100644 --- a/server_test.go +++ b/server_test.go @@ -114,7 +114,12 @@ func TestTLSAutoRegeneration(t *testing.T) { // Now let's run getTLSConfig. If it works properly, it should delete // the cert and create a new one. - _, _, _, err = getTLSConfig(certPath, keyPath, nil, nil, rpcListeners) + cfg := &Config{ + TLSCertPath: certPath, + TLSKeyPath: keyPath, + RPCListeners: rpcListeners, + } + _, _, _, err = getTLSConfig(cfg) if err != nil { t.Fatalf("couldn't retrieve TLS config") }