diff --git a/chainregistry.go b/chainregistry.go index 38969ec0..c5947653 100644 --- a/chainregistry.go +++ b/chainregistry.go @@ -22,23 +22,46 @@ import ( "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet" + "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/chainview" "github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/rpcclient" + "github.com/roasbeef/btcutil" "github.com/roasbeef/btcwallet/chain" "github.com/roasbeef/btcwallet/walletdb" ) -// defaultChannelConstraints is the default set of channel constraints that are -// meant to be used when initially funding a channel. +const ( + 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? -var defaultChannelConstraints = channeldb.ChannelConstraints{ +var defaultBtcChannelConstraints = channeldb.ChannelConstraints{ DustLimit: lnwallet.DefaultDustLimit(), 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 // currently supported within lnd. type chainCode uint32 @@ -111,7 +134,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, TimeLockDelta: cfg.Bitcoin.TimeLockDelta, } cc.feeEstimator = lnwallet.StaticFeeEstimator{ - FeeRate: 50, + FeeRate: defaultBitcoinStaticFeeRate, } case litecoinChain: cc.routingPolicy = htlcswitch.ForwardingPolicy{ @@ -121,7 +144,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, TimeLockDelta: cfg.Litecoin.TimeLockDelta, } cc.feeEstimator = lnwallet.StaticFeeEstimator{ - FeeRate: 100, + FeeRate: defaultLitecoinStaticFeeRate, } default: return nil, nil, fmt.Errorf("Default routing policy for "+ @@ -223,17 +246,24 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, // database. walletConfig.ChainSource = chain.NewNeutrinoClient(svc) 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 // bitcoind node. If the specified host for the btcd/ltcd RPC // server already has a port specified, then we use that // directly. Otherwise, we assume the default port according to // the selected chain parameters. var bitcoindHost string - if strings.Contains(cfg.BitcoindMode.RPCHost, ":") { - bitcoindHost = cfg.BitcoindMode.RPCHost + if strings.Contains(bitcoindMode.RPCHost, ":") { + bitcoindHost = bitcoindMode.RPCHost } else { // The RPC ports specified in chainparams.go assume // btcd, which picks a different port so that btcwallet @@ -245,13 +275,13 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, } rpcPort -= 2 bitcoindHost = fmt.Sprintf("%v:%d", - cfg.BitcoindMode.RPCHost, rpcPort) - if cfg.Bitcoin.RegTest { + bitcoindMode.RPCHost, rpcPort) + if cfg.Bitcoin.Active && cfg.Bitcoin.RegTest { conn, err := net.Dial("tcp", bitcoindHost) if err != nil || conn == nil { rpcPort = 18443 bitcoindHost = fmt.Sprintf("%v:%d", - cfg.BitcoindMode.RPCHost, + bitcoindMode.RPCHost, rpcPort) } else { conn.Close() @@ -259,8 +289,8 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, } } - bitcoindUser := cfg.BitcoindMode.RPCUser - bitcoindPass := cfg.BitcoindMode.RPCPass + bitcoindUser := bitcoindMode.RPCUser + bitcoindPass := bitcoindMode.RPCPass rpcConfig := &rpcclient.ConnConfig{ Host: bitcoindHost, User: bitcoindUser, @@ -271,7 +301,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, HTTPPostMode: true, } cc.chainNotifier, err = bitcoindnotify.New(rpcConfig, - cfg.BitcoindMode.ZMQPath, *activeNetParams.Params) + bitcoindMode.ZMQPath, *activeNetParams.Params) if err != nil { 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 // be used within the routing layer. cc.chainView, err = chainview.NewBitcoindFilteredChainView( - *rpcConfig, cfg.BitcoindMode.ZMQPath, + *rpcConfig, bitcoindMode.ZMQPath, *activeNetParams.Params) if err != nil { 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. bitcoindConn, err = chain.NewBitcoindClient( activeNetParams.Params, bitcoindHost, bitcoindUser, - bitcoindPass, cfg.BitcoindMode.ZMQPath, + bitcoindPass, bitcoindMode.ZMQPath, time.Millisecond*100) if err != nil { 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 // proper fee estimator for testnet. - if !cfg.Bitcoin.RegTest { + if cfg.Bitcoin.Active && !cfg.Bitcoin.RegTest { ltndLog.Infof("Initializing bitcoind backed fee estimator") // 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 { 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 + } + if err := cc.feeEstimator.Start(); err != nil { + return nil, nil, err + } } - case "btcd": + case "btcd", "ltcd": // Otherwise, we'll be speaking directly via RPC to a node. // // 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.chainIO = wc + // Select the default channel constraints for the primary chain. + channelConstraints := defaultBtcChannelConstraints + if registeredChains.PrimaryChain() == litecoinChain { + channelConstraints = defaultLtcChannelConstraints + } + keyRing := keychain.NewBtcWalletKeyRing( wc.InternalWallet(), activeNetParams.CoinType, ) @@ -451,7 +504,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, FeeEstimator: cc.feeEstimator, SecretKeyRing: keyRing, ChainIO: cc.chainIO, - DefaultConstraints: defaultChannelConstraints, + DefaultConstraints: channelConstraints, NetParams: *activeNetParams.Params, } wallet, err := lnwallet.NewLightningWallet(walletCfg) diff --git a/config.go b/config.go index 594f14fc..3da18b27 100644 --- a/config.go +++ b/config.go @@ -52,16 +52,6 @@ const ( // HTLCs on our channels. minTimeLockDelta = 4 - defaultBitcoinMinHTLCMSat = 1000 - defaultBitcoinBaseFeeMSat = 1000 - defaultBitcoinFeeRate = 1 - defaultBitcoinTimeLockDelta = 144 - - defaultLitecoinMinHTLCMSat = 1000 - defaultLitecoinBaseFeeMSat = 1000 - defaultLitecoinFeeRate = 1 - defaultLitecoinTimeLockDelta = 576 - defaultAlias = "" defaultColor = "#3399FF" ) @@ -82,14 +72,15 @@ var ( defaultLtcdDir = btcutil.AppDataDir("ltcd", false) defaultLtcdRPCCertFile = filepath.Join(defaultLtcdDir, "rpc.cert") - defaultBitcoindDir = btcutil.AppDataDir("bitcoin", false) + defaultBitcoindDir = btcutil.AppDataDir("bitcoin", false) + defaultLitecoindDir = btcutil.AppDataDir("litecoin", false) ) 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"` + 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"` SimNet bool `long:"simnet" description:"Use the simulation test network"` @@ -182,8 +173,9 @@ type config struct { BitcoindMode *bitcoindConfig `group:"bitcoind" namespace:"bitcoind"` NeutrinoMode *neutrinoConfig `group:"neutrino" namespace:"neutrino"` - Litecoin *chainConfig `group:"Litecoin" namespace:"litecoin"` - LtcdMode *btcdConfig `group:"ltcd" namespace:"ltcd"` + Litecoin *chainConfig `group:"Litecoin" namespace:"litecoin"` + LtcdMode *btcdConfig `group:"ltcd" namespace:"ltcd"` + LitecoindMode *bitcoindConfig `group:"litecoind" namespace:"litecoind"` Autopilot *autoPilotConfig `group:"autopilot" namespace:"autopilot"` @@ -241,13 +233,17 @@ func loadConfig() (*config, error) { BaseFee: defaultLitecoinBaseFeeMSat, FeeRate: defaultLitecoinFeeRate, TimeLockDelta: defaultLitecoinTimeLockDelta, - Node: "btcd", + Node: "ltcd", }, LtcdMode: &btcdConfig{ Dir: defaultLtcdDir, RPCHost: defaultRPCHost, RPCCert: defaultLtcdRPCCertFile, }, + LitecoindMode: &bitcoindConfig{ + Dir: defaultLitecoindDir, + RPCHost: defaultRPCHost, + }, MaxPendingChannels: defaultMaxPendingChannels, NoEncryptWallet: defaultNoEncryptWallet, Autopilot: &autoPilotConfig{ @@ -333,6 +329,7 @@ func loadConfig() (*config, error) { 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) // Setup dial and DNS resolution functions depending on the specified // 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" 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 { return nil, fmt.Errorf("timelockdelta must be at least %v", 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 // throughout the codebase we required chaincfg.Params. So as a // temporary hack, we'll mutate the default net params for @@ -417,12 +413,31 @@ func loadConfig() (*config, error) { applyLitecoinParams(¶mCopy) activeNetParams = paramCopy - err := parseRPCParams(cfg.Litecoin, cfg.LtcdMode, litecoinChain, - funcName) - if err != nil { - err := fmt.Errorf("unable to load RPC credentials for "+ - "ltcd: %v", err) - return nil, err + switch cfg.Litecoin.Node { + case "ltcd": + err := parseRPCParams(cfg.Litecoin, cfg.LtcdMode, + litecoinChain, funcName) + if err != nil { + err := fmt.Errorf("unable to load RPC "+ + "credentials for ltcd: %v", 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, @@ -485,6 +500,10 @@ func loadConfig() (*config, error) { } case "neutrino": // 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, @@ -762,9 +781,16 @@ func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode, "bitcoind.zmqpath") } - daemonName = "bitcoind" - confDir = conf.Dir - confFile = "bitcoin" + switch net { + case bitcoinChain: + daemonName = "bitcoind" + confDir = conf.Dir + 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 @@ -780,7 +806,7 @@ func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode, confFile = filepath.Join(confDir, fmt.Sprintf("%v.conf", confFile)) switch cConfig.Node { - case "btcd": + case "btcd", "ltcd": nConf := nodeConfig.(*btcdConfig) rpcUser, rpcPass, err := extractBtcdRPCParams(confFile) if err != nil { @@ -789,7 +815,7 @@ func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode, err) } nConf.RPCUser, nConf.RPCPass = rpcUser, rpcPass - case "bitcoind": + case "bitcoind", "litecoind": nConf := nodeConfig.(*bitcoindConfig) rpcUser, rpcPass, zmqPath, err := extractBitcoindRPCParams(confFile) if err != nil { diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 7e76319f..6111ccc5 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -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 ``` -#### 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 -be configured with `--txindex` just like `btcd` above -- 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 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). +To configure your bitcoind backend for use with lnd, first complete and verify +the following: + +- The `bitcoind` instance must be configured with `--txindex` just like `btcd` + above +- 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 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: ``` @@ -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 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 -`[Btcd]`, `[Bitcoind]`, `[Neutrino]`, and `[Ltcd]` depending on which chain -and node type you're using. +`[Btcd]`, `[Bitcoind]`, `[Neutrino]`, `[Ltcd]`, and `[Litecoind]` depending on +which chain and node type you're using. # Accurate as of: - _roasbeef/btcd commit:_ `f8c02aff4e7a807ba0c1349e2db03695d8e790e8` diff --git a/fundingmanager.go b/fundingmanager.go index d27087ed..607a91e7 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -42,12 +42,19 @@ const ( // TODO(roasbeef): add command line param to modify maxFundingAmount = btcutil.Amount(1 << 24) - // minRemoteDelay and maxRemoteDelay is the extremes of the 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. - minRemoteDelay = 144 - maxRemoteDelay = 2016 + // minBtcRemoteDelay and maxBtcRemoteDelay is the extremes of the + // Bitcoin 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. + minBtcRemoteDelay uint16 = 144 + 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 // for the funding transaction to be confirmed before forgetting about diff --git a/fundingmanager_test.go b/fundingmanager_test.go index 8ed26302..012b7d32 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -166,7 +166,7 @@ func createTestWallet(cdb *channeldb.DB, netParams *chaincfg.Params, ChainIO: bio, FeeEstimator: estimator, NetParams: *netParams, - DefaultConstraints: defaultChannelConstraints, + DefaultConstraints: defaultBtcChannelConstraints, }) if err != nil { return nil, err diff --git a/lnd.go b/lnd.go index 9b2f5088..f12b6010 100644 --- a/lnd.go +++ b/lnd.go @@ -238,6 +238,17 @@ func lndMain() error { primaryChain := registeredChains.PrimaryChain() 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 idPrivKey, err := activeChainControl.wallet.DerivePrivKey(keychain.KeyDescriptor{ KeyLocator: keychain.KeyLocator{ @@ -340,7 +351,7 @@ func lndMain() error { // In case the user has explicitly specified // a default value for the number of // confirmations, we use it. - defaultConf := uint16(cfg.Bitcoin.DefaultNumChanConfs) + defaultConf := uint16(chainCfg.DefaultNumChanConfs) if defaultConf != 0 { return defaultConf } @@ -373,13 +384,13 @@ func lndMain() error { // In case the user has explicitly specified // a default value for the remote delay, we // use it. - defaultDelay := uint16(cfg.Bitcoin.DefaultRemoteDelay) + defaultDelay := uint16(chainCfg.DefaultRemoteDelay) if defaultDelay > 0 { return defaultDelay } // If not we scale according to channel size. - delay := uint16(maxRemoteDelay * + delay := uint16(btcutil.Amount(maxRemoteDelay) * chanAmt / maxFundingAmount) if delay < minRemoteDelay { delay = minRemoteDelay diff --git a/rpcserver.go b/rpcserver.go index 7d380fd3..be6bbc9d 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2095,6 +2095,9 @@ func (r *rpcServer) AddInvoice(ctx context.Context, default: // TODO(roasbeef): assumes set delta between versions defaultDelta := cfg.Bitcoin.TimeLockDelta + if registeredChains.PrimaryChain() == litecoinChain { + defaultDelta = cfg.Litecoin.TimeLockDelta + } options = append(options, zpay32.CLTVExpiry(uint64(defaultDelta))) }