2017-05-03 05:16:37 +03:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2017-05-18 21:49:32 +03:00
|
|
|
"encoding/hex"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
2017-12-22 08:23:24 +03:00
|
|
|
"net"
|
2017-05-18 21:49:32 +03:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2017-12-22 08:23:24 +03:00
|
|
|
"strconv"
|
2017-05-18 21:49:32 +03:00
|
|
|
"strings"
|
2017-05-03 05:16:37 +03:00
|
|
|
"sync"
|
2017-05-18 21:49:32 +03:00
|
|
|
"time"
|
2017-05-03 05:16:37 +03:00
|
|
|
|
2017-05-18 21:49:32 +03:00
|
|
|
"github.com/lightninglabs/neutrino"
|
2017-05-03 05:16:37 +03:00
|
|
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
2017-12-22 08:23:24 +03:00
|
|
|
"github.com/lightningnetwork/lnd/chainntnfs/bitcoindnotify"
|
2017-05-18 21:49:32 +03:00
|
|
|
"github.com/lightningnetwork/lnd/chainntnfs/btcdnotify"
|
2017-05-25 03:46:37 +03:00
|
|
|
"github.com/lightningnetwork/lnd/chainntnfs/neutrinonotify"
|
2017-05-18 21:49:32 +03:00
|
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
2017-06-17 01:12:34 +03:00
|
|
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
2018-02-18 04:30:27 +03:00
|
|
|
"github.com/lightningnetwork/lnd/keychain"
|
2017-05-03 05:16:37 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
2017-05-18 21:49:32 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
|
2018-03-14 07:15:20 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
2017-05-18 21:49:32 +03:00
|
|
|
"github.com/lightningnetwork/lnd/routing/chainview"
|
2017-05-03 05:16:37 +03:00
|
|
|
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
2017-08-25 04:54:17 +03:00
|
|
|
"github.com/roasbeef/btcd/rpcclient"
|
2018-03-14 07:15:20 +03:00
|
|
|
"github.com/roasbeef/btcutil"
|
2017-05-25 03:46:37 +03:00
|
|
|
"github.com/roasbeef/btcwallet/chain"
|
2017-05-18 21:49:32 +03:00
|
|
|
"github.com/roasbeef/btcwallet/walletdb"
|
2017-05-03 05:16:37 +03:00
|
|
|
)
|
|
|
|
|
2018-03-14 07:15:20 +03:00
|
|
|
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)
|
2018-03-28 10:47:33 +03:00
|
|
|
defaultLitecoinDustLimit = btcutil.Amount(54600)
|
2018-03-14 07:15:20 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// defaultBtcChannelConstraints is the default set of channel constraints that are
|
|
|
|
// meant to be used when initially funding a Bitcoin channel.
|
2017-07-31 02:01:44 +03:00
|
|
|
//
|
2017-12-17 16:33:49 +03:00
|
|
|
// TODO(halseth): make configurable at startup?
|
2018-03-14 07:15:20 +03:00
|
|
|
var defaultBtcChannelConstraints = channeldb.ChannelConstraints{
|
2017-07-31 02:01:44 +03:00
|
|
|
DustLimit: lnwallet.DefaultDustLimit(),
|
|
|
|
MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2,
|
|
|
|
}
|
|
|
|
|
2018-03-14 07:15:20 +03:00
|
|
|
// defaultLtcChannelConstraints is the default set of channel constraints that are
|
|
|
|
// meant to be used when initially funding a Litecoin channel.
|
|
|
|
var defaultLtcChannelConstraints = channeldb.ChannelConstraints{
|
2018-03-28 10:47:33 +03:00
|
|
|
DustLimit: defaultLitecoinDustLimit,
|
2018-03-14 07:15:20 +03:00
|
|
|
MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2,
|
|
|
|
}
|
|
|
|
|
2017-09-04 02:53:04 +03:00
|
|
|
// chainCode is an enum-like structure for keeping track of the chains
|
|
|
|
// currently supported within lnd.
|
2017-05-03 05:16:37 +03:00
|
|
|
type chainCode uint32
|
|
|
|
|
|
|
|
const (
|
|
|
|
// bitcoinChain is Bitcoin's testnet chain.
|
|
|
|
bitcoinChain chainCode = iota
|
|
|
|
|
|
|
|
// litecoinChain is Litecoin's testnet chain.
|
|
|
|
litecoinChain
|
|
|
|
)
|
|
|
|
|
|
|
|
// String returns a string representation of the target chainCode.
|
|
|
|
func (c chainCode) String() string {
|
|
|
|
switch c {
|
|
|
|
case bitcoinChain:
|
|
|
|
return "bitcoin"
|
|
|
|
case litecoinChain:
|
|
|
|
return "litecoin"
|
|
|
|
default:
|
|
|
|
return "kekcoin"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// chainControl couples the three primary interfaces lnd utilizes for a
|
|
|
|
// particular chain together. A single chainControl instance will exist for all
|
|
|
|
// the chains lnd is currently active on.
|
|
|
|
type chainControl struct {
|
2017-05-18 21:49:32 +03:00
|
|
|
chainIO lnwallet.BlockChainIO
|
|
|
|
|
|
|
|
feeEstimator lnwallet.FeeEstimator
|
|
|
|
|
|
|
|
signer lnwallet.Signer
|
|
|
|
|
|
|
|
msgSigner lnwallet.MessageSigner
|
|
|
|
|
2017-05-03 05:16:37 +03:00
|
|
|
chainNotifier chainntnfs.ChainNotifier
|
|
|
|
|
2017-05-18 21:49:32 +03:00
|
|
|
chainView chainview.FilteredChainView
|
|
|
|
|
2017-05-03 05:16:37 +03:00
|
|
|
wallet *lnwallet.LightningWallet
|
2017-06-17 01:12:34 +03:00
|
|
|
|
|
|
|
routingPolicy htlcswitch.ForwardingPolicy
|
2017-05-03 05:16:37 +03:00
|
|
|
}
|
|
|
|
|
2017-05-25 03:46:37 +03:00
|
|
|
// newChainControlFromConfig attempts to create a chainControl instance
|
|
|
|
// according to the parameters in the passed lnd configuration. Currently two
|
|
|
|
// branches of chainControl instances exist: one backed by a running btcd
|
|
|
|
// full-node, and the other backed by a running neutrino light client instance.
|
2017-10-12 12:22:33 +03:00
|
|
|
func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
|
2017-10-20 05:53:19 +03:00
|
|
|
privateWalletPw, publicWalletPw []byte) (*chainControl, func(), error) {
|
|
|
|
|
2017-05-18 21:49:32 +03:00
|
|
|
// 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 {
|
|
|
|
homeChainConfig = cfg.Litecoin
|
|
|
|
}
|
|
|
|
ltndLog.Infof("Primary chain is set to: %v",
|
|
|
|
registeredChains.PrimaryChain())
|
|
|
|
|
2017-09-04 02:53:04 +03:00
|
|
|
cc := &chainControl{}
|
2017-05-18 21:49:32 +03:00
|
|
|
|
2017-06-17 01:12:34 +03:00
|
|
|
switch registeredChains.PrimaryChain() {
|
|
|
|
case bitcoinChain:
|
2017-12-17 16:33:49 +03:00
|
|
|
cc.routingPolicy = htlcswitch.ForwardingPolicy{
|
|
|
|
MinHTLC: cfg.Bitcoin.MinHTLC,
|
|
|
|
BaseFee: cfg.Bitcoin.BaseFee,
|
|
|
|
FeeRate: cfg.Bitcoin.FeeRate,
|
|
|
|
TimeLockDelta: cfg.Bitcoin.TimeLockDelta,
|
|
|
|
}
|
2017-09-04 02:53:04 +03:00
|
|
|
cc.feeEstimator = lnwallet.StaticFeeEstimator{
|
2018-03-14 07:15:20 +03:00
|
|
|
FeeRate: defaultBitcoinStaticFeeRate,
|
2017-09-04 02:53:04 +03:00
|
|
|
}
|
2017-06-17 01:12:34 +03:00
|
|
|
case litecoinChain:
|
2017-12-17 16:33:49 +03:00
|
|
|
cc.routingPolicy = htlcswitch.ForwardingPolicy{
|
|
|
|
MinHTLC: cfg.Litecoin.MinHTLC,
|
|
|
|
BaseFee: cfg.Litecoin.BaseFee,
|
|
|
|
FeeRate: cfg.Litecoin.FeeRate,
|
|
|
|
TimeLockDelta: cfg.Litecoin.TimeLockDelta,
|
|
|
|
}
|
2017-09-04 02:53:04 +03:00
|
|
|
cc.feeEstimator = lnwallet.StaticFeeEstimator{
|
2018-03-14 07:15:20 +03:00
|
|
|
FeeRate: defaultLitecoinStaticFeeRate,
|
2017-09-04 02:53:04 +03:00
|
|
|
}
|
2017-06-17 01:12:34 +03:00
|
|
|
default:
|
|
|
|
return nil, nil, fmt.Errorf("Default routing policy for "+
|
|
|
|
"chain %v is unknown", registeredChains.PrimaryChain())
|
|
|
|
}
|
|
|
|
|
2017-09-04 02:53:04 +03:00
|
|
|
walletConfig := &btcwallet.Config{
|
2017-10-20 05:53:19 +03:00
|
|
|
PrivatePass: privateWalletPw,
|
|
|
|
PublicPass: publicWalletPw,
|
2017-09-04 02:53:04 +03:00
|
|
|
DataDir: homeChainConfig.ChainDir,
|
|
|
|
NetParams: activeNetParams.Params,
|
|
|
|
FeeEstimator: cc.feeEstimator,
|
2018-03-13 02:31:31 +03:00
|
|
|
CoinType: activeNetParams.CoinType,
|
2017-09-04 02:53:04 +03:00
|
|
|
}
|
|
|
|
|
2017-05-18 21:49:32 +03:00
|
|
|
var (
|
2017-12-22 08:23:24 +03:00
|
|
|
err error
|
|
|
|
cleanUp func()
|
|
|
|
bitcoindConn *chain.BitcoindClient
|
2017-05-18 21:49:32 +03:00
|
|
|
)
|
|
|
|
|
2017-09-04 02:53:04 +03:00
|
|
|
// If spv mode is active, then we'll be using a distinct set of
|
2017-05-18 21:49:32 +03:00
|
|
|
// chainControl interfaces that interface directly with the p2p network
|
|
|
|
// of the selected chain.
|
2017-12-22 08:23:24 +03:00
|
|
|
switch homeChainConfig.Node {
|
|
|
|
case "neutrino":
|
2017-05-25 03:46:37 +03:00
|
|
|
// First we'll open the database file for neutrino, creating
|
2018-01-06 00:42:55 +03:00
|
|
|
// the database if needed. We append the normalized network name
|
|
|
|
// here to match the behavior of btcwallet.
|
|
|
|
neutrinoDbPath := filepath.Join(homeChainConfig.ChainDir,
|
|
|
|
normalizeNetwork(activeNetParams.Name))
|
|
|
|
|
|
|
|
// Ensure that the neutrino db path exists.
|
|
|
|
if err := os.MkdirAll(neutrinoDbPath, 0700); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
dbName := filepath.Join(neutrinoDbPath, "neutrino.db")
|
2017-05-18 21:49:32 +03:00
|
|
|
nodeDatabase, err := walletdb.Create("bdb", dbName)
|
|
|
|
if err != nil {
|
2017-05-25 03:46:37 +03:00
|
|
|
return nil, nil, err
|
2017-05-18 21:49:32 +03:00
|
|
|
}
|
|
|
|
|
2017-05-25 03:46:37 +03:00
|
|
|
// With the database open, we can now create an instance of the
|
|
|
|
// neutrino light client. We pass in relevant configuration
|
|
|
|
// parameters required.
|
2017-05-18 21:49:32 +03:00
|
|
|
config := neutrino.Config{
|
2018-01-06 00:42:55 +03:00
|
|
|
DataDir: neutrinoDbPath,
|
2017-05-18 21:49:32 +03:00
|
|
|
Database: nodeDatabase,
|
|
|
|
ChainParams: *activeNetParams.Params,
|
2017-06-06 01:44:18 +03:00
|
|
|
AddPeers: cfg.NeutrinoMode.AddPeers,
|
|
|
|
ConnectPeers: cfg.NeutrinoMode.ConnectPeers,
|
2018-02-06 05:36:11 +03:00
|
|
|
Dialer: func(addr net.Addr) (net.Conn, error) {
|
|
|
|
return cfg.net.Dial(addr.Network(), addr.String())
|
|
|
|
},
|
|
|
|
NameResolver: func(host string) ([]net.IP, error) {
|
|
|
|
addrs, err := cfg.net.LookupHost(host)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ips := make([]net.IP, 0, len(addrs))
|
|
|
|
for _, strIP := range addrs {
|
|
|
|
ip := net.ParseIP(strIP)
|
|
|
|
if ip == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
ips = append(ips, ip)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ips, nil
|
|
|
|
},
|
2017-05-18 21:49:32 +03:00
|
|
|
}
|
|
|
|
neutrino.WaitForMoreCFHeaders = time.Second * 1
|
|
|
|
neutrino.MaxPeers = 8
|
|
|
|
neutrino.BanDuration = 5 * time.Second
|
|
|
|
svc, err := neutrino.NewChainService(config)
|
|
|
|
if err != nil {
|
2017-05-25 03:46:37 +03:00
|
|
|
return nil, nil, fmt.Errorf("unable to create neutrino: %v", err)
|
2017-05-18 21:49:32 +03:00
|
|
|
}
|
|
|
|
svc.Start()
|
|
|
|
|
2017-05-25 03:46:37 +03:00
|
|
|
// Next we'll create the instances of the ChainNotifier and
|
|
|
|
// FilteredChainView interface which is backed by the neutrino
|
|
|
|
// light client.
|
|
|
|
cc.chainNotifier, err = neutrinonotify.New(svc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
cc.chainView, err = chainview.NewCfFilteredChainView(svc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2017-05-18 21:49:32 +03:00
|
|
|
|
2017-05-25 03:46:37 +03:00
|
|
|
// Finally, we'll set the chain source for btcwallet, and
|
|
|
|
// create our clean up function which simply closes the
|
|
|
|
// database.
|
2017-06-05 07:06:52 +03:00
|
|
|
walletConfig.ChainSource = chain.NewNeutrinoClient(svc)
|
2017-05-25 03:46:37 +03:00
|
|
|
cleanUp = func() {
|
2018-03-29 01:18:07 +03:00
|
|
|
svc.Stop()
|
2018-03-14 07:15:20 +03:00
|
|
|
nodeDatabase.Close()
|
|
|
|
}
|
|
|
|
case "bitcoind", "litecoind":
|
|
|
|
var bitcoindMode *bitcoindConfig
|
|
|
|
switch {
|
|
|
|
case cfg.Bitcoin.Active:
|
|
|
|
bitcoindMode = cfg.BitcoindMode
|
|
|
|
case cfg.Litecoin.Active:
|
|
|
|
bitcoindMode = cfg.LitecoindMode
|
2017-05-25 03:46:37 +03:00
|
|
|
}
|
2017-12-22 08:23:24 +03:00
|
|
|
// 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
|
2018-03-14 07:15:20 +03:00
|
|
|
if strings.Contains(bitcoindMode.RPCHost, ":") {
|
|
|
|
bitcoindHost = bitcoindMode.RPCHost
|
2017-12-22 08:23:24 +03:00
|
|
|
} else {
|
|
|
|
// The RPC ports specified in chainparams.go assume
|
|
|
|
// btcd, which picks a different port so that btcwallet
|
|
|
|
// can use the same RPC port as bitcoind. We convert
|
|
|
|
// this back to the btcwallet/bitcoind port.
|
|
|
|
rpcPort, err := strconv.Atoi(activeNetParams.rpcPort)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
rpcPort -= 2
|
|
|
|
bitcoindHost = fmt.Sprintf("%v:%d",
|
2018-03-14 07:15:20 +03:00
|
|
|
bitcoindMode.RPCHost, rpcPort)
|
|
|
|
if cfg.Bitcoin.Active && cfg.Bitcoin.RegTest {
|
2017-12-22 08:23:24 +03:00
|
|
|
conn, err := net.Dial("tcp", bitcoindHost)
|
|
|
|
if err != nil || conn == nil {
|
|
|
|
rpcPort = 18443
|
|
|
|
bitcoindHost = fmt.Sprintf("%v:%d",
|
2018-03-14 07:15:20 +03:00
|
|
|
bitcoindMode.RPCHost,
|
2017-12-22 08:23:24 +03:00
|
|
|
rpcPort)
|
|
|
|
} else {
|
|
|
|
conn.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-14 07:15:20 +03:00
|
|
|
bitcoindUser := bitcoindMode.RPCUser
|
|
|
|
bitcoindPass := bitcoindMode.RPCPass
|
2017-12-22 08:23:24 +03:00
|
|
|
rpcConfig := &rpcclient.ConnConfig{
|
|
|
|
Host: bitcoindHost,
|
|
|
|
User: bitcoindUser,
|
|
|
|
Pass: bitcoindPass,
|
|
|
|
DisableConnectOnNew: true,
|
|
|
|
DisableAutoReconnect: false,
|
|
|
|
DisableTLS: true,
|
|
|
|
HTTPPostMode: true,
|
|
|
|
}
|
|
|
|
cc.chainNotifier, err = bitcoindnotify.New(rpcConfig,
|
2018-03-14 07:15:20 +03:00
|
|
|
bitcoindMode.ZMQPath, *activeNetParams.Params)
|
2017-12-22 08:23:24 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next, we'll create an instance of the bitcoind chain view to
|
|
|
|
// be used within the routing layer.
|
|
|
|
cc.chainView, err = chainview.NewBitcoindFilteredChainView(
|
2018-03-14 07:15:20 +03:00
|
|
|
*rpcConfig, bitcoindMode.ZMQPath,
|
2017-12-22 08:23:24 +03:00
|
|
|
*activeNetParams.Params)
|
|
|
|
if err != nil {
|
|
|
|
srvrLog.Errorf("unable to create chain view: %v", err)
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a special rpc+ZMQ client for bitcoind which will be
|
|
|
|
// used by the wallet for notifications, calls, etc.
|
|
|
|
bitcoindConn, err = chain.NewBitcoindClient(
|
|
|
|
activeNetParams.Params, bitcoindHost, bitcoindUser,
|
2018-03-14 07:15:20 +03:00
|
|
|
bitcoindPass, bitcoindMode.ZMQPath,
|
2017-12-22 08:23:24 +03:00
|
|
|
time.Millisecond*100)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
walletConfig.ChainSource = bitcoindConn
|
|
|
|
|
|
|
|
// If we're not in regtest mode, then we'll attempt to use a
|
|
|
|
// proper fee estimator for testnet.
|
2018-03-14 07:15:20 +03:00
|
|
|
if cfg.Bitcoin.Active && !cfg.Bitcoin.RegTest {
|
2017-12-22 08:23:24 +03:00
|
|
|
ltndLog.Infof("Initializing bitcoind backed fee estimator")
|
|
|
|
|
|
|
|
// Finally, we'll re-initialize the fee estimator, as
|
|
|
|
// if we're using bitcoind as a backend, then we can
|
|
|
|
// use live fee estimates, rather than a statically
|
|
|
|
// coded value.
|
2018-02-13 17:00:47 +03:00
|
|
|
fallBackFeeRate := lnwallet.SatPerVByte(25)
|
2017-12-22 08:23:24 +03:00
|
|
|
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
|
|
|
|
}
|
2018-03-14 07:15:20 +03:00
|
|
|
} 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
|
|
|
|
}
|
2017-12-22 08:23:24 +03:00
|
|
|
}
|
2018-03-14 07:15:20 +03:00
|
|
|
case "btcd", "ltcd":
|
2017-05-18 21:49:32 +03:00
|
|
|
// Otherwise, we'll be speaking directly via RPC to a node.
|
|
|
|
//
|
|
|
|
// So first we'll load btcd/ltcd's TLS cert for the RPC
|
|
|
|
// 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.
|
2017-12-22 08:23:24 +03:00
|
|
|
var btcdMode *btcdConfig
|
|
|
|
switch {
|
|
|
|
case cfg.Bitcoin.Active:
|
|
|
|
btcdMode = cfg.BtcdMode
|
|
|
|
case cfg.Litecoin.Active:
|
|
|
|
btcdMode = cfg.LtcdMode
|
|
|
|
}
|
2017-05-18 21:49:32 +03:00
|
|
|
var rpcCert []byte
|
2017-12-22 08:23:24 +03:00
|
|
|
if btcdMode.RawRPCCert != "" {
|
|
|
|
rpcCert, err = hex.DecodeString(btcdMode.RawRPCCert)
|
2017-05-18 21:49:32 +03:00
|
|
|
if err != nil {
|
2017-05-25 03:46:37 +03:00
|
|
|
return nil, nil, err
|
2017-05-18 21:49:32 +03:00
|
|
|
}
|
|
|
|
} else {
|
2017-12-22 08:23:24 +03:00
|
|
|
certFile, err := os.Open(btcdMode.RPCCert)
|
2017-05-18 21:49:32 +03:00
|
|
|
if err != nil {
|
2017-05-25 03:46:37 +03:00
|
|
|
return nil, nil, err
|
2017-05-18 21:49:32 +03:00
|
|
|
}
|
|
|
|
rpcCert, err = ioutil.ReadAll(certFile)
|
|
|
|
if err != nil {
|
2017-05-25 03:46:37 +03:00
|
|
|
return nil, nil, err
|
2017-05-18 21:49:32 +03:00
|
|
|
}
|
|
|
|
if err := certFile.Close(); err != nil {
|
2017-05-25 03:46:37 +03:00
|
|
|
return nil, nil, err
|
2017-05-18 21:49:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 btcdHost string
|
2017-12-22 08:23:24 +03:00
|
|
|
if strings.Contains(btcdMode.RPCHost, ":") {
|
|
|
|
btcdHost = btcdMode.RPCHost
|
2017-05-18 21:49:32 +03:00
|
|
|
} else {
|
2017-12-22 08:23:24 +03:00
|
|
|
btcdHost = fmt.Sprintf("%v:%v", btcdMode.RPCHost,
|
2017-05-18 21:49:32 +03:00
|
|
|
activeNetParams.rpcPort)
|
|
|
|
}
|
|
|
|
|
2017-12-22 08:23:24 +03:00
|
|
|
btcdUser := btcdMode.RPCUser
|
|
|
|
btcdPass := btcdMode.RPCPass
|
2017-08-25 04:54:17 +03:00
|
|
|
rpcConfig := &rpcclient.ConnConfig{
|
2017-05-18 21:49:32 +03:00
|
|
|
Host: btcdHost,
|
|
|
|
Endpoint: "ws",
|
|
|
|
User: btcdUser,
|
|
|
|
Pass: btcdPass,
|
|
|
|
Certificates: rpcCert,
|
|
|
|
DisableTLS: false,
|
|
|
|
DisableConnectOnNew: true,
|
|
|
|
DisableAutoReconnect: false,
|
|
|
|
}
|
|
|
|
cc.chainNotifier, err = btcdnotify.New(rpcConfig)
|
|
|
|
if err != nil {
|
2017-05-25 03:46:37 +03:00
|
|
|
return nil, nil, err
|
2017-05-18 21:49:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, we'll create an instance of the default chain view to be
|
|
|
|
// used within the routing layer.
|
|
|
|
cc.chainView, err = chainview.NewBtcdFilteredChainView(*rpcConfig)
|
|
|
|
if err != nil {
|
|
|
|
srvrLog.Errorf("unable to create chain view: %v", err)
|
2017-05-25 03:46:37 +03:00
|
|
|
return nil, nil, err
|
2017-05-18 21:49:32 +03:00
|
|
|
}
|
2017-05-25 03:46:37 +03:00
|
|
|
|
|
|
|
// Create a special websockets rpc client for btcd which will be used
|
|
|
|
// by the wallet for notifications, calls, etc.
|
2017-06-06 05:44:54 +03:00
|
|
|
chainRPC, err := chain.NewRPCClient(activeNetParams.Params, btcdHost,
|
2017-05-25 03:46:37 +03:00
|
|
|
btcdUser, btcdPass, rpcCert, false, 20)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2017-06-06 05:44:54 +03:00
|
|
|
walletConfig.ChainSource = chainRPC
|
2017-11-23 22:36:12 +03:00
|
|
|
|
2017-11-30 03:15:00 +03:00
|
|
|
// If we're not in simnet or regtest mode, then we'll attempt
|
|
|
|
// to use a proper fee estimator for testnet.
|
|
|
|
if !cfg.Bitcoin.SimNet && !cfg.Litecoin.SimNet &&
|
|
|
|
!cfg.Bitcoin.RegTest && !cfg.Litecoin.RegTest {
|
|
|
|
|
2017-11-23 22:36:12 +03:00
|
|
|
ltndLog.Infof("Initializing btcd backed fee estimator")
|
|
|
|
|
|
|
|
// Finally, we'll re-initialize the fee estimator, as
|
|
|
|
// if we're using btcd as a backend, then we can use
|
|
|
|
// live fee estimates, rather than a statically coded
|
|
|
|
// value.
|
2018-02-13 17:00:47 +03:00
|
|
|
fallBackFeeRate := lnwallet.SatPerVByte(25)
|
2017-11-23 22:36:12 +03:00
|
|
|
cc.feeEstimator, err = lnwallet.NewBtcdFeeEstimator(
|
|
|
|
*rpcConfig, fallBackFeeRate,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
if err := cc.feeEstimator.Start(); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
2017-12-22 08:23:24 +03:00
|
|
|
default:
|
|
|
|
return nil, nil, fmt.Errorf("unknown node type: %s",
|
|
|
|
homeChainConfig.Node)
|
2017-05-18 21:49:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
wc, err := btcwallet.New(*walletConfig)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("unable to create wallet controller: %v\n", err)
|
2017-05-25 03:46:37 +03:00
|
|
|
return nil, nil, err
|
2017-05-18 21:49:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
cc.msgSigner = wc
|
|
|
|
cc.signer = wc
|
|
|
|
cc.chainIO = wc
|
|
|
|
|
2018-03-14 07:15:20 +03:00
|
|
|
// Select the default channel constraints for the primary chain.
|
|
|
|
channelConstraints := defaultBtcChannelConstraints
|
|
|
|
if registeredChains.PrimaryChain() == litecoinChain {
|
|
|
|
channelConstraints = defaultLtcChannelConstraints
|
|
|
|
}
|
|
|
|
|
2018-03-13 02:31:31 +03:00
|
|
|
keyRing := keychain.NewBtcWalletKeyRing(
|
|
|
|
wc.InternalWallet(), activeNetParams.CoinType,
|
|
|
|
)
|
|
|
|
|
2017-05-18 21:49:32 +03:00
|
|
|
// Create, and start the lnwallet, which handles the core payment
|
|
|
|
// channel logic, and exposes control via proxy state machines.
|
2017-07-31 02:01:44 +03:00
|
|
|
walletCfg := lnwallet.Config{
|
|
|
|
Database: chanDB,
|
|
|
|
Notifier: cc.chainNotifier,
|
|
|
|
WalletController: wc,
|
|
|
|
Signer: cc.signer,
|
|
|
|
FeeEstimator: cc.feeEstimator,
|
2018-03-13 02:31:31 +03:00
|
|
|
SecretKeyRing: keyRing,
|
2017-07-31 02:01:44 +03:00
|
|
|
ChainIO: cc.chainIO,
|
2018-03-14 07:15:20 +03:00
|
|
|
DefaultConstraints: channelConstraints,
|
2017-07-31 02:01:44 +03:00
|
|
|
NetParams: *activeNetParams.Params,
|
|
|
|
}
|
|
|
|
wallet, err := lnwallet.NewLightningWallet(walletCfg)
|
2017-05-18 21:49:32 +03:00
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("unable to create wallet: %v\n", err)
|
2017-05-25 03:46:37 +03:00
|
|
|
return nil, nil, err
|
2017-05-18 21:49:32 +03:00
|
|
|
}
|
|
|
|
if err := wallet.Startup(); err != nil {
|
|
|
|
fmt.Printf("unable to start wallet: %v\n", err)
|
2017-05-25 03:46:37 +03:00
|
|
|
return nil, nil, err
|
2017-05-18 21:49:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ltndLog.Info("LightningWallet opened")
|
|
|
|
|
|
|
|
cc.wallet = wallet
|
|
|
|
|
2017-05-25 03:46:37 +03:00
|
|
|
return cc, cleanUp, nil
|
2017-05-18 21:49:32 +03:00
|
|
|
}
|
|
|
|
|
2017-05-03 05:16:37 +03:00
|
|
|
var (
|
2018-03-15 03:03:22 +03:00
|
|
|
// bitcoinTestnetGenesis is the genesis hash of Bitcoin's testnet
|
|
|
|
// chain.
|
|
|
|
bitcoinTestnetGenesis = chainhash.Hash([chainhash.HashSize]byte{
|
2017-12-03 05:04:03 +03:00
|
|
|
0x43, 0x49, 0x7f, 0xd7, 0xf8, 0x26, 0x95, 0x71,
|
|
|
|
0x08, 0xf4, 0xa3, 0x0f, 0xd9, 0xce, 0xc3, 0xae,
|
|
|
|
0xba, 0x79, 0x97, 0x20, 0x84, 0xe9, 0x0e, 0xad,
|
|
|
|
0x01, 0xea, 0x33, 0x09, 0x00, 0x00, 0x00, 0x00,
|
2017-05-03 05:16:37 +03:00
|
|
|
})
|
|
|
|
|
2018-03-15 11:58:00 +03:00
|
|
|
// bitcoinMainnetGenesis is the genesis hash of Bitcoin's main chain.
|
|
|
|
bitcoinMainnetGenesis = chainhash.Hash([chainhash.HashSize]byte{
|
|
|
|
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
|
|
|
|
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
|
|
|
|
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
|
|
|
|
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
})
|
|
|
|
|
2018-03-15 03:03:22 +03:00
|
|
|
// litecoinTestnetGenesis is the genesis hash of Litecoin's testnet4
|
|
|
|
// chain.
|
|
|
|
litecoinTestnetGenesis = chainhash.Hash([chainhash.HashSize]byte{
|
2017-05-03 05:16:37 +03:00
|
|
|
0xa0, 0x29, 0x3e, 0x4e, 0xeb, 0x3d, 0xa6, 0xe6,
|
|
|
|
0xf5, 0x6f, 0x81, 0xed, 0x59, 0x5f, 0x57, 0x88,
|
|
|
|
0x0d, 0x1a, 0x21, 0x56, 0x9e, 0x13, 0xee, 0xfd,
|
|
|
|
0xd9, 0x51, 0x28, 0x4b, 0x5a, 0x62, 0x66, 0x49,
|
|
|
|
})
|
|
|
|
|
2018-03-15 11:58:00 +03:00
|
|
|
// litecoinMainnetGenesis is the genesis hash of Litecoin's main chain.
|
|
|
|
litecoinMainnetGenesis = chainhash.Hash([chainhash.HashSize]byte{
|
|
|
|
0xe2, 0xbf, 0x04, 0x7e, 0x7e, 0x5a, 0x19, 0x1a,
|
|
|
|
0xa4, 0xef, 0x34, 0xd3, 0x14, 0x97, 0x9d, 0xc9,
|
|
|
|
0x98, 0x6e, 0x0f, 0x19, 0x25, 0x1e, 0xda, 0xba,
|
|
|
|
0x59, 0x40, 0xfd, 0x1f, 0xe3, 0x65, 0xa7, 0x12,
|
|
|
|
})
|
|
|
|
|
2017-05-03 05:16:37 +03:00
|
|
|
// chainMap is a simple index that maps a chain's genesis hash to the
|
|
|
|
// chainCode enum for that chain.
|
|
|
|
chainMap = map[chainhash.Hash]chainCode{
|
2018-03-15 03:03:22 +03:00
|
|
|
bitcoinTestnetGenesis: bitcoinChain,
|
|
|
|
litecoinTestnetGenesis: litecoinChain,
|
2017-05-03 05:16:37 +03:00
|
|
|
|
2018-03-15 11:58:00 +03:00
|
|
|
bitcoinMainnetGenesis: bitcoinChain,
|
|
|
|
litecoinMainnetGenesis: litecoinChain,
|
2017-05-03 05:16:37 +03:00
|
|
|
}
|
2017-09-04 02:53:28 +03:00
|
|
|
|
|
|
|
// chainDNSSeeds is a map of a chain's hash to the set of DNS seeds
|
|
|
|
// that will be use to bootstrap peers upon first startup.
|
|
|
|
//
|
2017-10-31 05:02:48 +03:00
|
|
|
// The first item in the array is the primary host we'll use to attempt
|
|
|
|
// the SRV lookup we require. If we're unable to receive a response
|
|
|
|
// over UDP, then we'll fall back to manual TCP resolution. The second
|
|
|
|
// item in the array is a special A record that we'll query in order to
|
|
|
|
// receive the IP address of the current authoritative DNS server for
|
|
|
|
// the network seed.
|
|
|
|
//
|
2017-09-04 02:53:28 +03:00
|
|
|
// TODO(roasbeef): extend and collapse these and chainparams.go into
|
|
|
|
// struct like chaincfg.Params
|
2017-10-31 05:02:48 +03:00
|
|
|
chainDNSSeeds = map[chainhash.Hash][][2]string{
|
2018-03-15 12:00:14 +03:00
|
|
|
bitcoinMainnetGenesis: {
|
|
|
|
{
|
|
|
|
"nodes.lightning.directory",
|
|
|
|
"soa.nodes.lightning.directory",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2018-03-15 03:04:45 +03:00
|
|
|
bitcoinTestnetGenesis: {
|
2017-10-31 05:02:48 +03:00
|
|
|
{
|
2018-03-15 03:04:45 +03:00
|
|
|
"test.nodes.lightning.directory",
|
2017-10-31 05:02:48 +03:00
|
|
|
"soa.nodes.lightning.directory",
|
|
|
|
},
|
2017-09-04 02:53:28 +03:00
|
|
|
},
|
2018-03-15 12:00:14 +03:00
|
|
|
|
|
|
|
litecoinMainnetGenesis: {
|
|
|
|
{
|
|
|
|
"ltc.nodes.lightning.directory",
|
|
|
|
"soa.nodes.lightning.directory",
|
|
|
|
},
|
|
|
|
},
|
2017-09-04 02:53:28 +03:00
|
|
|
}
|
2017-05-03 05:16:37 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// chainRegistry keeps track of the current chains
|
|
|
|
type chainRegistry struct {
|
|
|
|
sync.RWMutex
|
|
|
|
|
|
|
|
activeChains map[chainCode]*chainControl
|
|
|
|
netParams map[chainCode]*bitcoinNetParams
|
|
|
|
|
|
|
|
primaryChain chainCode
|
|
|
|
}
|
|
|
|
|
|
|
|
// newChainRegistry creates a new chainRegistry.
|
|
|
|
func newChainRegistry() *chainRegistry {
|
|
|
|
return &chainRegistry{
|
|
|
|
activeChains: make(map[chainCode]*chainControl),
|
|
|
|
netParams: make(map[chainCode]*bitcoinNetParams),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// RegisterChain assigns an active chainControl instance to a target chain
|
|
|
|
// identified by its chainCode.
|
|
|
|
func (c *chainRegistry) RegisterChain(newChain chainCode, cc *chainControl) {
|
|
|
|
c.Lock()
|
|
|
|
c.activeChains[newChain] = cc
|
|
|
|
c.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// LookupChain attempts to lookup an active chainControl instance for the
|
|
|
|
// target chain.
|
|
|
|
func (c *chainRegistry) LookupChain(targetChain chainCode) (*chainControl, bool) {
|
|
|
|
c.RLock()
|
|
|
|
cc, ok := c.activeChains[targetChain]
|
|
|
|
c.RUnlock()
|
|
|
|
return cc, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// LookupChainByHash attempts to look up an active chainControl which
|
|
|
|
// corresponds to the passed genesis hash.
|
|
|
|
func (c *chainRegistry) LookupChainByHash(chainHash chainhash.Hash) (*chainControl, bool) {
|
|
|
|
c.RLock()
|
|
|
|
defer c.RUnlock()
|
|
|
|
|
|
|
|
targetChain, ok := chainMap[chainHash]
|
|
|
|
if !ok {
|
|
|
|
return nil, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
cc, ok := c.activeChains[targetChain]
|
|
|
|
return cc, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// RegisterPrimaryChain sets a target chain as the "home chain" for lnd.
|
|
|
|
func (c *chainRegistry) RegisterPrimaryChain(cc chainCode) {
|
|
|
|
c.Lock()
|
|
|
|
defer c.Unlock()
|
|
|
|
|
|
|
|
c.primaryChain = cc
|
|
|
|
}
|
|
|
|
|
|
|
|
// PrimaryChain returns the primary chain for this running lnd instance. The
|
|
|
|
// primary chain is considered the "home base" while the other registered
|
|
|
|
// chains are treated as secondary chains.
|
|
|
|
func (c *chainRegistry) PrimaryChain() chainCode {
|
|
|
|
c.RLock()
|
|
|
|
defer c.RUnlock()
|
|
|
|
|
|
|
|
return c.primaryChain
|
|
|
|
}
|
|
|
|
|
|
|
|
// ActiveChains returns the total number of active chains.
|
|
|
|
func (c *chainRegistry) ActiveChains() []chainCode {
|
|
|
|
c.RLock()
|
|
|
|
defer c.RUnlock()
|
|
|
|
|
|
|
|
chains := make([]chainCode, 0, len(c.activeChains))
|
|
|
|
for activeChain := range c.activeChains {
|
|
|
|
chains = append(chains, activeChain)
|
|
|
|
}
|
|
|
|
|
|
|
|
return chains
|
|
|
|
}
|
|
|
|
|
|
|
|
// NumActiveChains returns the total number of active chains.
|
|
|
|
func (c *chainRegistry) NumActiveChains() uint32 {
|
|
|
|
c.RLock()
|
|
|
|
defer c.RUnlock()
|
|
|
|
|
|
|
|
return uint32(len(c.activeChains))
|
|
|
|
}
|