Skip to content

Commit

Permalink
lnd+chainregistry: initialize neutrino light client before wallet setup
Browse files Browse the repository at this point in the history
In this commit, we slightly refactor the startup of lnd when running
with a Neutrino light client backend. We'll now begin syncing our
backend as soon as lnd starts and passes all configuration checks. Since
this is all done before lnd's wallet setup, the light client will be
syncing in the background while the user notes/inputs their wallet seed.
This is done in order to provide a better UX from the point of the user,
such that most of the chain will already be synced by the time they get
to deposit funds into the wallet.
  • Loading branch information
wpaulino authored and Roasbeef committed Mar 13, 2019
1 parent d075722 commit 1fe6599
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 78 deletions.
92 changes: 15 additions & 77 deletions chainregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"io/ioutil"
"net"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
Expand All @@ -17,7 +16,6 @@ import (
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/chain"
"github.com/btcsuite/btcwallet/wallet"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/lightninglabs/neutrino"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/chainntnfs/bitcoindnotify"
Expand Down Expand Up @@ -124,13 +122,15 @@ type chainControl struct {
}

// newChainControlFromConfig attempts to create a chainControl instance
// according to the parameters in the passed lnd configuration. Currently two
// according to the parameters in the passed lnd configuration. Currently three
// branches of chainControl instances exist: one backed by a running btcd
// full-node, and the other backed by a running neutrino light client instance.
// 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,
privateWalletPw, publicWalletPw []byte, birthday time.Time,
recoveryWindow uint32,
wallet *wallet.Wallet) (*chainControl, func(), error) {
recoveryWindow uint32, wallet *wallet.Wallet,
neutrinoCS *neutrino.ChainService) (*chainControl, func(), error) {

// 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.
Expand Down Expand Up @@ -198,83 +198,21 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
// of the selected chain.
switch homeChainConfig.Node {
case "neutrino":
// 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.
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")
nodeDatabase, err := walletdb.Create("bdb", dbName)
if err != nil {
return nil, nil, err
}

// With the database open, we can now create an instance of the
// neutrino light client. We pass in relevant configuration
// parameters required.
config := neutrino.Config{
DataDir: neutrinoDbPath,
Database: nodeDatabase,
ChainParams: *activeNetParams.Params,
AddPeers: cfg.NeutrinoMode.AddPeers,
ConnectPeers: cfg.NeutrinoMode.ConnectPeers,
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
},
}
neutrino.MaxPeers = 8
neutrino.BanDuration = 5 * time.Second
svc, err := neutrino.NewChainService(config)
if err != nil {
nodeDatabase.Close()
return nil, nil, fmt.Errorf("unable to create neutrino: %v", err)
}
svc.Start()
cleanUp = func() {
svc.Stop()
nodeDatabase.Close()
}

// Next we'll create the instances of the ChainNotifier and
// FilteredChainView interface which is backed by the neutrino
// light client.
cc.chainNotifier = neutrinonotify.New(svc, hintCache, hintCache)
cc.chainView, err = chainview.NewCfFilteredChainView(svc)
// We'll create ChainNotifier and FilteredChainView instances,
// along with the wallet's ChainSource, which are all backed by
// the neutrino light client.
cc.chainNotifier = neutrinonotify.New(
neutrinoCS, hintCache, hintCache,
)
cc.chainView, err = chainview.NewCfFilteredChainView(neutrinoCS)
if err != nil {
cleanUp()
return nil, nil, err
}

// Finally, we'll set the chain source for btcwallet, and
// create our clean up function which simply closes the
// database.
walletConfig.ChainSource = chain.NewNeutrinoClient(
activeNetParams.Params, svc,
activeNetParams.Params, neutrinoCS,
)

case "bitcoind", "litecoind":
var bitcoindMode *bitcoindConfig
switch {
Expand Down
79 changes: 78 additions & 1 deletion lnd.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ import (

"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcwallet/wallet"
"github.com/btcsuite/btcwallet/walletdb"
proxy "github.com/grpc-ecosystem/grpc-gateway/runtime"
flags "github.com/jessevdk/go-flags"
"github.com/lightninglabs/neutrino"

"github.com/lightningnetwork/lnd/autopilot"
"github.com/lightningnetwork/lnd/build"
Expand Down Expand Up @@ -196,6 +198,81 @@ func lndMain() error {
}
proxyOpts := []grpc.DialOption{grpc.WithTransportCredentials(cCreds)}

// Before starting the wallet, we'll create and start our Neutrino 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 {
mainChain = cfg.Litecoin
}
var neutrinoCS *neutrino.ChainService
if mainChain.Node == "neutrino" {
// 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.
dbPath := filepath.Join(
mainChain.ChainDir,
normalizeNetwork(activeNetParams.Name),
)

// Ensure that the neutrino db path exists.
if err := os.MkdirAll(dbPath, 0700); err != nil {
return err
}

dbName := filepath.Join(dbPath, "neutrino.db")
db, err := walletdb.Create("bdb", dbName)
if err != nil {
return fmt.Errorf("unable to create neutrino "+
"database: %v", err)
}
defer db.Close()

// With the database open, we can now create an instance of the
// neutrino light client. We pass in relevant configuration
// parameters required.
config := neutrino.Config{
DataDir: dbPath,
Database: db,
ChainParams: *activeNetParams.Params,
AddPeers: cfg.NeutrinoMode.AddPeers,
ConnectPeers: cfg.NeutrinoMode.ConnectPeers,
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
},
}

neutrino.MaxPeers = 8
neutrino.BanDuration = 5 * time.Second

neutrinoCS, err = neutrino.NewChainService(config)
if err != nil {
return fmt.Errorf("unable to create neutrino light "+
"client: %v", err)
}

neutrinoCS.Start()
defer neutrinoCS.Stop()
}

var (
privateWalletPw = lnwallet.DefaultPrivatePassphrase
publicWalletPw = lnwallet.DefaultPublicPassphrase
Expand Down Expand Up @@ -269,7 +346,7 @@ func lndMain() error {
// Lightning Network Daemon.
activeChainControl, chainCleanUp, err := newChainControlFromConfig(
cfg, chanDB, privateWalletPw, publicWalletPw, birthday,
recoveryWindow, unlockedWallet,
recoveryWindow, unlockedWallet, neutrinoCS,
)
if err != nil {
fmt.Printf("unable to create chain control: %v\n", err)
Expand Down

0 comments on commit 1fe6599

Please sign in to comment.