diff --git a/chainregistry.go b/chainregistry.go index 7f420ccb..7a29111f 100644 --- a/chainregistry.go +++ b/chainregistry.go @@ -6,7 +6,6 @@ import ( "io/ioutil" "net" "os" - "path/filepath" "strconv" "strings" "sync" @@ -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" @@ -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. @@ -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 { diff --git a/lnd.go b/lnd.go index 96e5b9d6..329acf40 100644 --- a/lnd.go +++ b/lnd.go @@ -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" @@ -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 @@ -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)