Merge pull request #838 from cfromknecht/litecoind-backend

Litecoind Backend
This commit is contained in:
Olaoluwa Osuntokun 2018-03-14 18:17:27 -07:00 committed by GitHub
commit 695b09e32b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 189 additions and 73 deletions

@ -22,23 +22,46 @@ import (
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/chainview" "github.com/lightningnetwork/lnd/routing/chainview"
"github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/rpcclient" "github.com/roasbeef/btcd/rpcclient"
"github.com/roasbeef/btcutil"
"github.com/roasbeef/btcwallet/chain" "github.com/roasbeef/btcwallet/chain"
"github.com/roasbeef/btcwallet/walletdb" "github.com/roasbeef/btcwallet/walletdb"
) )
// defaultChannelConstraints is the default set of channel constraints that are const (
// meant to be used when initially funding a channel. defaultBitcoinMinHTLCMSat = lnwire.MilliSatoshi(1000)
defaultBitcoinBaseFeeMSat = lnwire.MilliSatoshi(1000)
defaultBitcoinFeeRate = lnwire.MilliSatoshi(1)
defaultBitcoinTimeLockDelta = 144
defaultBitcoinStaticFeeRate = lnwallet.SatPerVByte(50)
defaultLitecoinMinHTLCMSat = lnwire.MilliSatoshi(1000)
defaultLitecoinBaseFeeMSat = lnwire.MilliSatoshi(1000)
defaultLitecoinFeeRate = lnwire.MilliSatoshi(1)
defaultLitecoinTimeLockDelta = 576
defaultLitecoinStaticFeeRate = lnwallet.SatPerVByte(200)
defaultLitecoinMinRelayFee = btcutil.Amount(1000)
)
// defaultBtcChannelConstraints is the default set of channel constraints that are
// meant to be used when initially funding a Bitcoin channel.
// //
// TODO(roasbeef): have one for both chains
// TODO(halseth): make configurable at startup? // TODO(halseth): make configurable at startup?
var defaultChannelConstraints = channeldb.ChannelConstraints{ var defaultBtcChannelConstraints = channeldb.ChannelConstraints{
DustLimit: lnwallet.DefaultDustLimit(), DustLimit: lnwallet.DefaultDustLimit(),
MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2, MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2,
} }
// defaultLtcChannelConstraints is the default set of channel constraints that are
// meant to be used when initially funding a Litecoin channel.
var defaultLtcChannelConstraints = channeldb.ChannelConstraints{
DustLimit: defaultLitecoinMinRelayFee,
MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2,
}
// chainCode is an enum-like structure for keeping track of the chains // chainCode is an enum-like structure for keeping track of the chains
// currently supported within lnd. // currently supported within lnd.
type chainCode uint32 type chainCode uint32
@ -111,7 +134,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
TimeLockDelta: cfg.Bitcoin.TimeLockDelta, TimeLockDelta: cfg.Bitcoin.TimeLockDelta,
} }
cc.feeEstimator = lnwallet.StaticFeeEstimator{ cc.feeEstimator = lnwallet.StaticFeeEstimator{
FeeRate: 50, FeeRate: defaultBitcoinStaticFeeRate,
} }
case litecoinChain: case litecoinChain:
cc.routingPolicy = htlcswitch.ForwardingPolicy{ cc.routingPolicy = htlcswitch.ForwardingPolicy{
@ -121,7 +144,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
TimeLockDelta: cfg.Litecoin.TimeLockDelta, TimeLockDelta: cfg.Litecoin.TimeLockDelta,
} }
cc.feeEstimator = lnwallet.StaticFeeEstimator{ cc.feeEstimator = lnwallet.StaticFeeEstimator{
FeeRate: 100, FeeRate: defaultLitecoinStaticFeeRate,
} }
default: default:
return nil, nil, fmt.Errorf("Default routing policy for "+ return nil, nil, fmt.Errorf("Default routing policy for "+
@ -223,17 +246,24 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
// database. // database.
walletConfig.ChainSource = chain.NewNeutrinoClient(svc) walletConfig.ChainSource = chain.NewNeutrinoClient(svc)
cleanUp = func() { cleanUp = func() {
defer nodeDatabase.Close() nodeDatabase.Close()
}
case "bitcoind", "litecoind":
var bitcoindMode *bitcoindConfig
switch {
case cfg.Bitcoin.Active:
bitcoindMode = cfg.BitcoindMode
case cfg.Litecoin.Active:
bitcoindMode = cfg.LitecoindMode
} }
case "bitcoind":
// Otherwise, we'll be speaking directly via RPC and ZMQ to a // Otherwise, we'll be speaking directly via RPC and ZMQ to a
// bitcoind node. If the specified host for the btcd/ltcd RPC // bitcoind node. If the specified host for the btcd/ltcd RPC
// server already has a port specified, then we use that // server already has a port specified, then we use that
// directly. Otherwise, we assume the default port according to // directly. Otherwise, we assume the default port according to
// the selected chain parameters. // the selected chain parameters.
var bitcoindHost string var bitcoindHost string
if strings.Contains(cfg.BitcoindMode.RPCHost, ":") { if strings.Contains(bitcoindMode.RPCHost, ":") {
bitcoindHost = cfg.BitcoindMode.RPCHost bitcoindHost = bitcoindMode.RPCHost
} else { } else {
// The RPC ports specified in chainparams.go assume // The RPC ports specified in chainparams.go assume
// btcd, which picks a different port so that btcwallet // btcd, which picks a different port so that btcwallet
@ -245,13 +275,13 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
} }
rpcPort -= 2 rpcPort -= 2
bitcoindHost = fmt.Sprintf("%v:%d", bitcoindHost = fmt.Sprintf("%v:%d",
cfg.BitcoindMode.RPCHost, rpcPort) bitcoindMode.RPCHost, rpcPort)
if cfg.Bitcoin.RegTest { if cfg.Bitcoin.Active && cfg.Bitcoin.RegTest {
conn, err := net.Dial("tcp", bitcoindHost) conn, err := net.Dial("tcp", bitcoindHost)
if err != nil || conn == nil { if err != nil || conn == nil {
rpcPort = 18443 rpcPort = 18443
bitcoindHost = fmt.Sprintf("%v:%d", bitcoindHost = fmt.Sprintf("%v:%d",
cfg.BitcoindMode.RPCHost, bitcoindMode.RPCHost,
rpcPort) rpcPort)
} else { } else {
conn.Close() conn.Close()
@ -259,8 +289,8 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
} }
} }
bitcoindUser := cfg.BitcoindMode.RPCUser bitcoindUser := bitcoindMode.RPCUser
bitcoindPass := cfg.BitcoindMode.RPCPass bitcoindPass := bitcoindMode.RPCPass
rpcConfig := &rpcclient.ConnConfig{ rpcConfig := &rpcclient.ConnConfig{
Host: bitcoindHost, Host: bitcoindHost,
User: bitcoindUser, User: bitcoindUser,
@ -271,7 +301,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
HTTPPostMode: true, HTTPPostMode: true,
} }
cc.chainNotifier, err = bitcoindnotify.New(rpcConfig, cc.chainNotifier, err = bitcoindnotify.New(rpcConfig,
cfg.BitcoindMode.ZMQPath, *activeNetParams.Params) bitcoindMode.ZMQPath, *activeNetParams.Params)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -279,7 +309,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
// Next, we'll create an instance of the bitcoind chain view to // Next, we'll create an instance of the bitcoind chain view to
// be used within the routing layer. // be used within the routing layer.
cc.chainView, err = chainview.NewBitcoindFilteredChainView( cc.chainView, err = chainview.NewBitcoindFilteredChainView(
*rpcConfig, cfg.BitcoindMode.ZMQPath, *rpcConfig, bitcoindMode.ZMQPath,
*activeNetParams.Params) *activeNetParams.Params)
if err != nil { if err != nil {
srvrLog.Errorf("unable to create chain view: %v", err) srvrLog.Errorf("unable to create chain view: %v", err)
@ -290,7 +320,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
// used by the wallet for notifications, calls, etc. // used by the wallet for notifications, calls, etc.
bitcoindConn, err = chain.NewBitcoindClient( bitcoindConn, err = chain.NewBitcoindClient(
activeNetParams.Params, bitcoindHost, bitcoindUser, activeNetParams.Params, bitcoindHost, bitcoindUser,
bitcoindPass, cfg.BitcoindMode.ZMQPath, bitcoindPass, bitcoindMode.ZMQPath,
time.Millisecond*100) time.Millisecond*100)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -300,7 +330,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
// If we're not in regtest mode, then we'll attempt to use a // If we're not in regtest mode, then we'll attempt to use a
// proper fee estimator for testnet. // proper fee estimator for testnet.
if !cfg.Bitcoin.RegTest { if cfg.Bitcoin.Active && !cfg.Bitcoin.RegTest {
ltndLog.Infof("Initializing bitcoind backed fee estimator") ltndLog.Infof("Initializing bitcoind backed fee estimator")
// Finally, we'll re-initialize the fee estimator, as // Finally, we'll re-initialize the fee estimator, as
@ -317,8 +347,25 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
if err := cc.feeEstimator.Start(); err != nil { if err := cc.feeEstimator.Start(); err != nil {
return nil, nil, err return nil, nil, err
} }
} else if cfg.Litecoin.Active {
ltndLog.Infof("Initializing litecoind backed fee estimator")
// Finally, we'll re-initialize the fee estimator, as
// if we're using litecoind as a backend, then we can
// use live fee estimates, rather than a statically
// coded value.
fallBackFeeRate := lnwallet.SatPerVByte(25)
cc.feeEstimator, err = lnwallet.NewBitcoindFeeEstimator(
*rpcConfig, fallBackFeeRate,
)
if err != nil {
return nil, nil, err
} }
case "btcd": if err := cc.feeEstimator.Start(); err != nil {
return nil, nil, err
}
}
case "btcd", "ltcd":
// Otherwise, we'll be speaking directly via RPC to a node. // Otherwise, we'll be speaking directly via RPC to a node.
// //
// So first we'll load btcd/ltcd's TLS cert for the RPC // So first we'll load btcd/ltcd's TLS cert for the RPC
@ -437,6 +484,12 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
cc.signer = wc cc.signer = wc
cc.chainIO = wc cc.chainIO = wc
// Select the default channel constraints for the primary chain.
channelConstraints := defaultBtcChannelConstraints
if registeredChains.PrimaryChain() == litecoinChain {
channelConstraints = defaultLtcChannelConstraints
}
keyRing := keychain.NewBtcWalletKeyRing( keyRing := keychain.NewBtcWalletKeyRing(
wc.InternalWallet(), activeNetParams.CoinType, wc.InternalWallet(), activeNetParams.CoinType,
) )
@ -451,7 +504,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
FeeEstimator: cc.feeEstimator, FeeEstimator: cc.feeEstimator,
SecretKeyRing: keyRing, SecretKeyRing: keyRing,
ChainIO: cc.chainIO, ChainIO: cc.chainIO,
DefaultConstraints: defaultChannelConstraints, DefaultConstraints: channelConstraints,
NetParams: *activeNetParams.Params, NetParams: *activeNetParams.Params,
} }
wallet, err := lnwallet.NewLightningWallet(walletCfg) wallet, err := lnwallet.NewLightningWallet(walletCfg)

@ -52,16 +52,6 @@ const (
// HTLCs on our channels. // HTLCs on our channels.
minTimeLockDelta = 4 minTimeLockDelta = 4
defaultBitcoinMinHTLCMSat = 1000
defaultBitcoinBaseFeeMSat = 1000
defaultBitcoinFeeRate = 1
defaultBitcoinTimeLockDelta = 144
defaultLitecoinMinHTLCMSat = 1000
defaultLitecoinBaseFeeMSat = 1000
defaultLitecoinFeeRate = 1
defaultLitecoinTimeLockDelta = 576
defaultAlias = "" defaultAlias = ""
defaultColor = "#3399FF" defaultColor = "#3399FF"
) )
@ -83,13 +73,14 @@ var (
defaultLtcdRPCCertFile = filepath.Join(defaultLtcdDir, "rpc.cert") defaultLtcdRPCCertFile = filepath.Join(defaultLtcdDir, "rpc.cert")
defaultBitcoindDir = btcutil.AppDataDir("bitcoin", false) defaultBitcoindDir = btcutil.AppDataDir("bitcoin", false)
defaultLitecoindDir = btcutil.AppDataDir("litecoin", false)
) )
type chainConfig struct { type chainConfig struct {
Active bool `long:"active" description:"If the chain should be active or not."` 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."` 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"` Node string `long:"node" description:"The blockchain interface to use." choice:"btcd" choice:"bitcoind" choice:"neutrino" choice:"ltcd" choice:"litecoind"`
TestNet3 bool `long:"testnet" description:"Use the test network"` TestNet3 bool `long:"testnet" description:"Use the test network"`
SimNet bool `long:"simnet" description:"Use the simulation test network"` SimNet bool `long:"simnet" description:"Use the simulation test network"`
@ -184,6 +175,7 @@ type config struct {
Litecoin *chainConfig `group:"Litecoin" namespace:"litecoin"` Litecoin *chainConfig `group:"Litecoin" namespace:"litecoin"`
LtcdMode *btcdConfig `group:"ltcd" namespace:"ltcd"` LtcdMode *btcdConfig `group:"ltcd" namespace:"ltcd"`
LitecoindMode *bitcoindConfig `group:"litecoind" namespace:"litecoind"`
Autopilot *autoPilotConfig `group:"autopilot" namespace:"autopilot"` Autopilot *autoPilotConfig `group:"autopilot" namespace:"autopilot"`
@ -241,13 +233,17 @@ func loadConfig() (*config, error) {
BaseFee: defaultLitecoinBaseFeeMSat, BaseFee: defaultLitecoinBaseFeeMSat,
FeeRate: defaultLitecoinFeeRate, FeeRate: defaultLitecoinFeeRate,
TimeLockDelta: defaultLitecoinTimeLockDelta, TimeLockDelta: defaultLitecoinTimeLockDelta,
Node: "btcd", Node: "ltcd",
}, },
LtcdMode: &btcdConfig{ LtcdMode: &btcdConfig{
Dir: defaultLtcdDir, Dir: defaultLtcdDir,
RPCHost: defaultRPCHost, RPCHost: defaultRPCHost,
RPCCert: defaultLtcdRPCCertFile, RPCCert: defaultLtcdRPCCertFile,
}, },
LitecoindMode: &bitcoindConfig{
Dir: defaultLitecoindDir,
RPCHost: defaultRPCHost,
},
MaxPendingChannels: defaultMaxPendingChannels, MaxPendingChannels: defaultMaxPendingChannels,
NoEncryptWallet: defaultNoEncryptWallet, NoEncryptWallet: defaultNoEncryptWallet,
Autopilot: &autoPilotConfig{ Autopilot: &autoPilotConfig{
@ -333,6 +329,7 @@ func loadConfig() (*config, error) {
cfg.BtcdMode.Dir = cleanAndExpandPath(cfg.BtcdMode.Dir) cfg.BtcdMode.Dir = cleanAndExpandPath(cfg.BtcdMode.Dir)
cfg.LtcdMode.Dir = cleanAndExpandPath(cfg.LtcdMode.Dir) cfg.LtcdMode.Dir = cleanAndExpandPath(cfg.LtcdMode.Dir)
cfg.BitcoindMode.Dir = cleanAndExpandPath(cfg.BitcoindMode.Dir) cfg.BitcoindMode.Dir = cleanAndExpandPath(cfg.BitcoindMode.Dir)
cfg.LitecoindMode.Dir = cleanAndExpandPath(cfg.LitecoindMode.Dir)
// Setup dial and DNS resolution functions depending on the specified // Setup dial and DNS resolution functions depending on the specified
// options. The default is to use the standard golang "net" package // options. The default is to use the standard golang "net" package
@ -398,17 +395,16 @@ func loadConfig() (*config, error) {
str := "%s: simnet mode for litecoin not currently supported" str := "%s: simnet mode for litecoin not currently supported"
return nil, fmt.Errorf(str, funcName) return nil, fmt.Errorf(str, funcName)
} }
if cfg.Litecoin.RegTest {
str := "%s: regnet mode for litecoin not currently supported"
return nil, fmt.Errorf(str, funcName)
}
if cfg.Litecoin.TimeLockDelta < minTimeLockDelta { if cfg.Litecoin.TimeLockDelta < minTimeLockDelta {
return nil, fmt.Errorf("timelockdelta must be at least %v", return nil, fmt.Errorf("timelockdelta must be at least %v",
minTimeLockDelta) minTimeLockDelta)
} }
if cfg.Litecoin.Node != "btcd" {
str := "%s: only ltcd (`btcd`) mode supported for litecoin at this time"
return nil, fmt.Errorf(str, funcName)
}
// The litecoin chain is the current active chain. However // The litecoin chain is the current active chain. However
// throughout the codebase we required chaincfg.Params. So as a // throughout the codebase we required chaincfg.Params. So as a
// temporary hack, we'll mutate the default net params for // temporary hack, we'll mutate the default net params for
@ -417,13 +413,32 @@ func loadConfig() (*config, error) {
applyLitecoinParams(&paramCopy) applyLitecoinParams(&paramCopy)
activeNetParams = paramCopy activeNetParams = paramCopy
err := parseRPCParams(cfg.Litecoin, cfg.LtcdMode, litecoinChain, switch cfg.Litecoin.Node {
funcName) case "ltcd":
err := parseRPCParams(cfg.Litecoin, cfg.LtcdMode,
litecoinChain, funcName)
if err != nil { if err != nil {
err := fmt.Errorf("unable to load RPC credentials for "+ err := fmt.Errorf("unable to load RPC "+
"ltcd: %v", err) "credentials for ltcd: %v", err)
return nil, err return nil, err
} }
case "litecoind":
if cfg.Litecoin.SimNet {
return nil, fmt.Errorf("%s: litecoind does not "+
"support simnet", funcName)
}
err := parseRPCParams(cfg.Litecoin, cfg.LitecoindMode,
litecoinChain, funcName)
if err != nil {
err := fmt.Errorf("unable to load RPC "+
"credentials for litecoind: %v", err)
return nil, err
}
default:
str := "%s: only ltcd and litecoind mode supported for " +
"litecoin at this time"
return nil, fmt.Errorf(str, funcName)
}
cfg.Litecoin.ChainDir = filepath.Join(cfg.DataDir, cfg.Litecoin.ChainDir = filepath.Join(cfg.DataDir,
defaultChainSubDirname, defaultChainSubDirname,
@ -485,6 +500,10 @@ func loadConfig() (*config, error) {
} }
case "neutrino": case "neutrino":
// No need to get RPC parameters. // No need to get RPC parameters.
default:
str := "%s: only btcd, bitcoind, and neutrino mode " +
"supported for bitcoin at this time"
return nil, fmt.Errorf(str, funcName)
} }
cfg.Bitcoin.ChainDir = filepath.Join(cfg.DataDir, cfg.Bitcoin.ChainDir = filepath.Join(cfg.DataDir,
@ -762,9 +781,16 @@ func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode,
"bitcoind.zmqpath") "bitcoind.zmqpath")
} }
switch net {
case bitcoinChain:
daemonName = "bitcoind" daemonName = "bitcoind"
confDir = conf.Dir confDir = conf.Dir
confFile = "bitcoin" confFile = "bitcoin"
case litecoinChain:
daemonName = "litecoind"
confDir = conf.Dir
confFile = "litecoin"
}
} }
// If we're in simnet mode, then the running btcd instance won't read // If we're in simnet mode, then the running btcd instance won't read
@ -780,7 +806,7 @@ func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode,
confFile = filepath.Join(confDir, fmt.Sprintf("%v.conf", confFile)) confFile = filepath.Join(confDir, fmt.Sprintf("%v.conf", confFile))
switch cConfig.Node { switch cConfig.Node {
case "btcd": case "btcd", "ltcd":
nConf := nodeConfig.(*btcdConfig) nConf := nodeConfig.(*btcdConfig)
rpcUser, rpcPass, err := extractBtcdRPCParams(confFile) rpcUser, rpcPass, err := extractBtcdRPCParams(confFile)
if err != nil { if err != nil {
@ -789,7 +815,7 @@ func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode,
err) err)
} }
nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass
case "bitcoind": case "bitcoind", "litecoind":
nConf := nodeConfig.(*bitcoindConfig) nConf := nodeConfig.(*bitcoindConfig)
rpcUser, rpcPass, zmqPath, err := extractBitcoindRPCParams(confFile) rpcUser, rpcPass, zmqPath, err := extractBitcoindRPCParams(confFile)
if err != nil { if err != nil {

@ -178,17 +178,33 @@ installing `lnd` in preparation for the
lnd --bitcoin.active --bitcoin.testnet --debuglevel=debug --btcd.rpcuser=kek --btcd.rpcpass=kek --externalip=X.X.X.X lnd --bitcoin.active --bitcoin.testnet --debuglevel=debug --btcd.rpcuser=kek --btcd.rpcpass=kek --externalip=X.X.X.X
``` ```
#### Running lnd using the bitcoind backend #### Running lnd using the bitcoind or litecoind backend
To configure your bitcoind backend for use with lnd, first complete and verify the following: The configuration for bitcoind and litecoind are nearly identical, the following
steps can be mirrored with loss of generality to enable a litecoind backend.
Setup will be described in regards to `bitciond`, but note that `lnd` uses a
distinct `litecoin.node=litecoind` argument and analogous subconfigurations
prefixed by `litecoind`.
- The `bitcoind` instance must To configure your bitcoind backend for use with lnd, first complete and verify
be configured with `--txindex` just like `btcd` above the following:
- Additionally, since `lnd` uses [ZeroMQ](https://github.com/bitcoin/bitcoin/blob/master/doc/zmq.md) to interface with `bitcoind`, *your `bitcoind` installation must be compiled with ZMQ*. If you installed it from source, this is likely the case, but if you installed it via Homebrew in the past it may not be included ([this has now been fixed](https://github.com/Homebrew/homebrew-core/pull/23088) in the latest Homebrew recipe for bitcoin)
- Configure the `bitcoind` instance for ZMQ with `--zmqpubrawblock` and `--zmqpubrawtx` - The `bitcoind` instance must be configured with `--txindex` just like `btcd`
(the latter is optional but allows you to see unconfirmed transactions in your above
wallet). They must be combined in the same ZMQ socket address (e.g. `--zmqpubrawblock=tcp://127.0.0.1:28332` and `--zmqpubrawtx=tcp://127.0.0.1:28332`). - Additionally, since `lnd` uses
- Start `bitcoind` running against testnet, and let it complete a full sync with the testnet chain (alternatively, use `--bitcoind.regtest` instead). [ZeroMQ](https://github.com/bitcoin/bitcoin/blob/master/doc/zmq.md) to
interface with `bitcoind`, *your `bitcoind` installation must be compiled with
ZMQ*. If you installed it from source, this is likely the case, but if you
installed it via Homebrew in the past it may not be included ([this has now
been fixed](https://github.com/Homebrew/homebrew-core/pull/23088) in the
latest Homebrew recipe for bitcoin)
- Configure the `bitcoind` instance for ZMQ with `--zmqpubrawblock` and
`--zmqpubrawtx` (the latter is optional but allows you to see unconfirmed
transactions in your wallet). They must be combined in the same ZMQ socket
address (e.g. `--zmqpubrawblock=tcp://127.0.0.1:28332` and
`--zmqpubrawtx=tcp://127.0.0.1:28332`).
- Start `bitcoind` running against testnet, and let it complete a full sync with
the testnet chain (alternatively, use `--bitcoind.regtest` instead).
Here's a sample `bitcoin.conf` for use with lnd: Here's a sample `bitcoin.conf` for use with lnd:
``` ```
@ -268,8 +284,8 @@ Notice the `[Bitcoin]` section. This section houses the parameters for the
Bitcoin chain. `lnd` also supports Litecoin testnet4 (but not both BTC and LTC Bitcoin chain. `lnd` also supports Litecoin testnet4 (but not both BTC and LTC
at the same time), so when working with Litecoin be sure to set to parameters at the same time), so when working with Litecoin be sure to set to parameters
for Litecoin accordingly. For node configuration, the sections are called for Litecoin accordingly. For node configuration, the sections are called
`[Btcd]`, `[Bitcoind]`, `[Neutrino]`, and `[Ltcd]` depending on which chain `[Btcd]`, `[Bitcoind]`, `[Neutrino]`, `[Ltcd]`, and `[Litecoind]` depending on
and node type you're using. which chain and node type you're using.
# Accurate as of: # Accurate as of:
- _roasbeef/btcd commit:_ `f8c02aff4e7a807ba0c1349e2db03695d8e790e8` - _roasbeef/btcd commit:_ `f8c02aff4e7a807ba0c1349e2db03695d8e790e8`

@ -42,12 +42,19 @@ const (
// TODO(roasbeef): add command line param to modify // TODO(roasbeef): add command line param to modify
maxFundingAmount = btcutil.Amount(1 << 24) maxFundingAmount = btcutil.Amount(1 << 24)
// minRemoteDelay and maxRemoteDelay is the extremes of the CSV delay // minBtcRemoteDelay and maxBtcRemoteDelay is the extremes of the
// we will require the remote to use for its commitment transaction. // Bitcoin CSV delay we will require the remote to use for its
// The actual delay we will require will be somewhere between these // commitment transaction. The actual delay we will require will be
// values, depending on channel size. // somewhere between these values, depending on channel size.
minRemoteDelay = 144 minBtcRemoteDelay uint16 = 144
maxRemoteDelay = 2016 maxBtcRemoteDelay uint16 = 2016
// minLtcRemoteDelay and maxLtcRemoteDelay is the extremes of the
// Litecoin CSV delay we will require the remote to use for its
// commitment transaction. The actual delay we will require will be
// somewhere between these values, depending on channel size.
minLtcRemoteDelay uint16 = 576
maxLtcRemoteDelay uint16 = 8064
// maxWaitNumBlocksFundingConf is the maximum number of blocks to wait // maxWaitNumBlocksFundingConf is the maximum number of blocks to wait
// for the funding transaction to be confirmed before forgetting about // for the funding transaction to be confirmed before forgetting about

@ -166,7 +166,7 @@ func createTestWallet(cdb *channeldb.DB, netParams *chaincfg.Params,
ChainIO: bio, ChainIO: bio,
FeeEstimator: estimator, FeeEstimator: estimator,
NetParams: *netParams, NetParams: *netParams,
DefaultConstraints: defaultChannelConstraints, DefaultConstraints: defaultBtcChannelConstraints,
}) })
if err != nil { if err != nil {
return nil, err return nil, err

17
lnd.go

@ -238,6 +238,17 @@ func lndMain() error {
primaryChain := registeredChains.PrimaryChain() primaryChain := registeredChains.PrimaryChain()
registeredChains.RegisterChain(primaryChain, activeChainControl) registeredChains.RegisterChain(primaryChain, activeChainControl)
// Select the configuration and furnding parameters for Bitcoin or
// Litecoin, depending on the primary registered chain.
chainCfg := cfg.Bitcoin
minRemoteDelay := minBtcRemoteDelay
maxRemoteDelay := maxBtcRemoteDelay
if primaryChain == litecoinChain {
chainCfg = cfg.Litecoin
minRemoteDelay = minLtcRemoteDelay
maxRemoteDelay = maxLtcRemoteDelay
}
// TODO(roasbeef): add rotation // TODO(roasbeef): add rotation
idPrivKey, err := activeChainControl.wallet.DerivePrivKey(keychain.KeyDescriptor{ idPrivKey, err := activeChainControl.wallet.DerivePrivKey(keychain.KeyDescriptor{
KeyLocator: keychain.KeyLocator{ KeyLocator: keychain.KeyLocator{
@ -340,7 +351,7 @@ func lndMain() error {
// In case the user has explicitly specified // In case the user has explicitly specified
// a default value for the number of // a default value for the number of
// confirmations, we use it. // confirmations, we use it.
defaultConf := uint16(cfg.Bitcoin.DefaultNumChanConfs) defaultConf := uint16(chainCfg.DefaultNumChanConfs)
if defaultConf != 0 { if defaultConf != 0 {
return defaultConf return defaultConf
} }
@ -373,13 +384,13 @@ func lndMain() error {
// In case the user has explicitly specified // In case the user has explicitly specified
// a default value for the remote delay, we // a default value for the remote delay, we
// use it. // use it.
defaultDelay := uint16(cfg.Bitcoin.DefaultRemoteDelay) defaultDelay := uint16(chainCfg.DefaultRemoteDelay)
if defaultDelay > 0 { if defaultDelay > 0 {
return defaultDelay return defaultDelay
} }
// If not we scale according to channel size. // If not we scale according to channel size.
delay := uint16(maxRemoteDelay * delay := uint16(btcutil.Amount(maxRemoteDelay) *
chanAmt / maxFundingAmount) chanAmt / maxFundingAmount)
if delay < minRemoteDelay { if delay < minRemoteDelay {
delay = minRemoteDelay delay = minRemoteDelay

@ -2095,6 +2095,9 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
default: default:
// TODO(roasbeef): assumes set delta between versions // TODO(roasbeef): assumes set delta between versions
defaultDelta := cfg.Bitcoin.TimeLockDelta defaultDelta := cfg.Bitcoin.TimeLockDelta
if registeredChains.PrimaryChain() == litecoinChain {
defaultDelta = cfg.Litecoin.TimeLockDelta
}
options = append(options, zpay32.CLTVExpiry(uint64(defaultDelta))) options = append(options, zpay32.CLTVExpiry(uint64(defaultDelta)))
} }