chainreg+lnd: split chain control initialization

As a preparation for extracting the wallet related initialization code,
we first need to separate the purely configuration related chain control
initialization from the wallet related code. We do that by splitting the
chain control into a partial and full struct that is initialized in two
parts. This also allows us to create the wallet configuration itself
outside of the chain control package and we need to thread through fewer
parameters through the chain control config.
This commit is contained in:
Oliver Gugger
2021-09-23 16:54:38 +02:00
parent 47f1b81a51
commit 140d5a8086
2 changed files with 112 additions and 95 deletions

View File

@ -17,7 +17,6 @@ import (
"github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/chain" "github.com/btcsuite/btcwallet/chain"
"github.com/btcsuite/btcwallet/wallet"
"github.com/lightninglabs/neutrino" "github.com/lightninglabs/neutrino"
"github.com/lightningnetwork/lnd/blockcache" "github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
@ -80,24 +79,6 @@ type Config struct {
// BlockCache is the main cache for storing block information. // BlockCache is the main cache for storing block information.
BlockCache *blockcache.BlockCache BlockCache *blockcache.BlockCache
// PrivateWalletPw is the private wallet password to the underlying
// btcwallet instance.
PrivateWalletPw []byte
// PublicWalletPw is the public wallet password to the underlying btcwallet
// instance.
PublicWalletPw []byte
// Birthday specifies the time the wallet was initially created.
Birthday time.Time
// RecoveryWindow specifies the address look-ahead for which to scan when
// restoring a wallet.
RecoveryWindow uint32
// Wallet is a pointer to the backing wallet instance.
Wallet *wallet.Wallet
// NeutrinoCS is a pointer to a neutrino ChainService. Must be non-nil if // NeutrinoCS is a pointer to a neutrino ChainService. Must be non-nil if
// using neutrino. // using neutrino.
NeutrinoCS *neutrino.ChainService NeutrinoCS *neutrino.ChainService
@ -113,13 +94,6 @@ type Config struct {
// TCP connections to Bitcoin peers in the event of a pruned block being // TCP connections to Bitcoin peers in the event of a pruned block being
// requested. // requested.
Dialer chain.Dialer Dialer chain.Dialer
// LoaderOptions holds functional wallet db loader options.
LoaderOptions []btcwallet.LoaderOption
// CoinSelectionStrategy is the strategy that is used for selecting
// coins when funding a transaction.
CoinSelectionStrategy wallet.CoinSelectionStrategy
} }
const ( const (
@ -181,44 +155,30 @@ var DefaultLtcChannelConstraints = channeldb.ChannelConstraints{
MaxAcceptedHtlcs: input.MaxHTLCNumber / 2, MaxAcceptedHtlcs: input.MaxHTLCNumber / 2,
} }
// ChainControl couples the three primary interfaces lnd utilizes for a // PartialChainControl contains all the primary interfaces of the chain control
// particular chain together. A single ChainControl instance will exist for all // that can be purely constructed from the global configuration. No wallet
// the chains lnd is currently active on. // instance is required for constructing this partial state.
type ChainControl struct { type PartialChainControl struct {
// ChainIO represents an abstraction over a source that can query the blockchain. // Cfg is the configuration that was used to create the partial chain
ChainIO lnwallet.BlockChainIO // control.
Cfg *Config
// HealthCheck is a function which can be used to send a low-cost, fast // HealthCheck is a function which can be used to send a low-cost, fast
// query to the chain backend to ensure we still have access to our // query to the chain backend to ensure we still have access to our
// node. // node.
HealthCheck func() error HealthCheck func() error
// FeeEstimator is used to estimate an optimal fee for transactions important to us. // FeeEstimator is used to estimate an optimal fee for transactions
// important to us.
FeeEstimator chainfee.Estimator FeeEstimator chainfee.Estimator
// Signer is used to provide signatures over things like transactions. // ChainNotifier is used to receive blockchain events that we are
Signer input.Signer // interested in.
// KeyRing represents a set of keys that we have the private keys to.
KeyRing keychain.SecretKeyRing
// Wc is an abstraction over some basic wallet commands. This base set of commands
// will be provided to the Wallet *LightningWallet raw pointer below.
Wc lnwallet.WalletController
// MsgSigner is used to sign arbitrary messages.
MsgSigner lnwallet.MessageSigner
// ChainNotifier is used to receive blockchain events that we are interested in.
ChainNotifier chainntnfs.ChainNotifier ChainNotifier chainntnfs.ChainNotifier
// ChainView is used in the router for maintaining an up-to-date graph. // ChainView is used in the router for maintaining an up-to-date graph.
ChainView chainview.FilteredChainView ChainView chainview.FilteredChainView
// Wallet is our LightningWallet that also contains the abstract Wc above. This wallet
// handles all of the lightning operations.
Wallet *lnwallet.LightningWallet
// ChainSource is the primary chain interface. This is used to operate // ChainSource is the primary chain interface. This is used to operate
// the wallet and do things such as rescanning, sending transactions, // the wallet and do things such as rescanning, sending transactions,
// notifications for received funds, etc. // notifications for received funds, etc.
@ -235,8 +195,40 @@ type ChainControl struct {
ChannelConstraints channeldb.ChannelConstraints ChannelConstraints channeldb.ChannelConstraints
} }
// GenDefaultBtcChannelConstraints generates the default set of channel // ChainControl couples the three primary interfaces lnd utilizes for a
// constraints that are to be used when funding a Bitcoin channel. // particular chain together. A single ChainControl instance will exist for all
// the chains lnd is currently active on.
type ChainControl struct {
// PartialChainControl is the part of the chain control that was
// initialized purely from the configuration and doesn't contain any
// wallet related elements.
*PartialChainControl
// ChainIO represents an abstraction over a source that can query the
// blockchain.
ChainIO lnwallet.BlockChainIO
// Signer is used to provide signatures over things like transactions.
Signer input.Signer
// KeyRing represents a set of keys that we have the private keys to.
KeyRing keychain.SecretKeyRing
// Wc is an abstraction over some basic wallet commands. This base set
// of commands will be provided to the Wallet *LightningWallet raw
// pointer below.
Wc lnwallet.WalletController
// MsgSigner is used to sign arbitrary messages.
MsgSigner lnwallet.MessageSigner
// Wallet is our LightningWallet that also contains the abstract Wc
// above. This wallet handles all of the lightning operations.
Wallet *lnwallet.LightningWallet
}
// GenDefaultBtcConstraints generates the default set of channel constraints
// that are to be used when funding a Bitcoin channel.
func GenDefaultBtcConstraints() channeldb.ChannelConstraints { func GenDefaultBtcConstraints() channeldb.ChannelConstraints {
// We use the dust limit for the maximally sized witness program with // We use the dust limit for the maximally sized witness program with
// a 40-byte data push. // a 40-byte data push.
@ -248,23 +240,21 @@ func GenDefaultBtcConstraints() channeldb.ChannelConstraints {
} }
} }
// NewChainControl attempts to create a ChainControl instance according // NewPartialChainControl creates a new partial chain control that contains all
// to the parameters in the passed configuration. Currently three // the parts that can be purely constructed from the passed in global
// branches of ChainControl instances exist: one backed by a running btcd // configuration and doesn't need any wallet instance yet.
// full-node, another backed by a running bitcoind full-node, and the other func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) {
// backed by a running neutrino light client instance. When running with a
// neutrino light client instance, `neutrinoCS` must be non-nil.
func NewChainControl(cfg *Config) (*ChainControl, func(), error) {
// Set the RPC config from the "home" chain. Multi-chain isn't yet // 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. // active, so we'll restrict usage to a particular chain for now.
homeChainConfig := cfg.Bitcoin homeChainConfig := cfg.Bitcoin
if cfg.PrimaryChain() == LitecoinChain { if cfg.PrimaryChain() == LitecoinChain {
homeChainConfig = cfg.Litecoin homeChainConfig = cfg.Litecoin
} }
log.Infof("Primary chain is set to: %v", log.Infof("Primary chain is set to: %v", cfg.PrimaryChain())
cfg.PrimaryChain())
cc := &ChainControl{} cc := &PartialChainControl{
Cfg: cfg,
}
switch cfg.PrimaryChain() { switch cfg.PrimaryChain() {
case BitcoinChain: case BitcoinChain:
@ -296,7 +286,6 @@ func NewChainControl(cfg *Config) (*ChainControl, func(), error) {
} }
var err error var err error
heightHintCacheConfig := chainntnfs.CacheConfig{ heightHintCacheConfig := chainntnfs.CacheConfig{
QueryDisable: cfg.HeightHintCacheQueryDisable, QueryDisable: cfg.HeightHintCacheQueryDisable,
} }
@ -643,12 +632,6 @@ func NewChainControl(cfg *Config) (*ChainControl, func(), error) {
} }
ccCleanup := func() { ccCleanup := func() {
if cc.Wallet != nil {
if err := cc.Wallet.Shutdown(); err != nil {
log.Errorf("Failed to shutdown wallet: %v", err)
}
}
if cc.FeeEstimator != nil { if cc.FeeEstimator != nil {
if err := cc.FeeEstimator.Stop(); err != nil { if err := cc.FeeEstimator.Stop(); err != nil {
log.Errorf("Failed to stop feeEstimator: %v", err) log.Errorf("Failed to stop feeEstimator: %v", err)
@ -667,20 +650,31 @@ func NewChainControl(cfg *Config) (*ChainControl, func(), error) {
cc.ChannelConstraints = DefaultLtcChannelConstraints cc.ChannelConstraints = DefaultLtcChannelConstraints
} }
walletConfig := &btcwallet.Config{ return cc, ccCleanup, nil
PrivatePass: cfg.PrivateWalletPw, }
PublicPass: cfg.PublicWalletPw,
Birthday: cfg.Birthday, // NewChainControl attempts to create a ChainControl instance according
RecoveryWindow: cfg.RecoveryWindow, // to the parameters in the passed configuration. Currently three
NetParams: cfg.ActiveNetParams.Params, // branches of ChainControl instances exist: one backed by a running btcd
CoinType: cfg.ActiveNetParams.CoinType, // full-node, another backed by a running bitcoind full-node, and the other
Wallet: cfg.Wallet, // backed by a running neutrino light client instance. When running with a
LoaderOptions: cfg.LoaderOptions, // neutrino light client instance, `neutrinoCS` must be non-nil.
CoinSelectionStrategy: cfg.CoinSelectionStrategy, func NewChainControl(walletConfig *btcwallet.Config,
ChainSource: cc.ChainSource, pcc *PartialChainControl) (*ChainControl, func(), error) {
cc := &ChainControl{
PartialChainControl: pcc,
} }
wc, err := btcwallet.New(*walletConfig, cfg.BlockCache) ccCleanup := func() {
if cc.Wallet != nil {
if err := cc.Wallet.Shutdown(); err != nil {
log.Errorf("Failed to shutdown wallet: %v", err)
}
}
}
wc, err := btcwallet.New(*walletConfig, pcc.Cfg.BlockCache)
if err != nil { if err != nil {
fmt.Printf("unable to create wallet controller: %v\n", err) fmt.Printf("unable to create wallet controller: %v\n", err)
return nil, ccCleanup, err return nil, ccCleanup, err
@ -692,14 +686,14 @@ func NewChainControl(cfg *Config) (*ChainControl, func(), error) {
cc.Wc = wc cc.Wc = wc
keyRing := keychain.NewBtcWalletKeyRing( keyRing := keychain.NewBtcWalletKeyRing(
wc.InternalWallet(), cfg.ActiveNetParams.CoinType, wc.InternalWallet(), walletConfig.CoinType,
) )
cc.KeyRing = keyRing cc.KeyRing = keyRing
// Create, and start the lnwallet, which handles the core payment // Create, and start the lnwallet, which handles the core payment
// channel logic, and exposes control via proxy state machines. // channel logic, and exposes control via proxy state machines.
walletCfg := lnwallet.Config{ walletCfg := lnwallet.Config{
Database: cfg.ChanStateDB, Database: pcc.Cfg.ChanStateDB,
Notifier: cc.ChainNotifier, Notifier: cc.ChainNotifier,
WalletController: wc, WalletController: wc,
Signer: cc.Signer, Signer: cc.Signer,
@ -707,7 +701,7 @@ func NewChainControl(cfg *Config) (*ChainControl, func(), error) {
SecretKeyRing: keyRing, SecretKeyRing: keyRing,
ChainIO: cc.ChainIO, ChainIO: cc.ChainIO,
DefaultConstraints: cc.ChannelConstraints, DefaultConstraints: cc.ChannelConstraints,
NetParams: *cfg.ActiveNetParams.Params, NetParams: *walletConfig.NetParams,
} }
lnWallet, err := lnwallet.NewLightningWallet(walletCfg) lnWallet, err := lnwallet.NewLightningWallet(walletCfg)
if err != nil { if err != nil {

43
lnd.go
View File

@ -718,36 +718,59 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error
LtcdMode: cfg.LtcdMode, LtcdMode: cfg.LtcdMode,
HeightHintDB: dbs.heightHintDB, HeightHintDB: dbs.heightHintDB,
ChanStateDB: dbs.chanStateDB.ChannelStateDB(), ChanStateDB: dbs.chanStateDB.ChannelStateDB(),
PrivateWalletPw: privateWalletPw,
PublicWalletPw: publicWalletPw,
Birthday: walletInitParams.Birthday,
RecoveryWindow: walletInitParams.RecoveryWindow,
Wallet: walletInitParams.Wallet,
NeutrinoCS: neutrinoCS, NeutrinoCS: neutrinoCS,
ActiveNetParams: cfg.ActiveNetParams, ActiveNetParams: cfg.ActiveNetParams,
FeeURL: cfg.FeeURL, FeeURL: cfg.FeeURL,
Dialer: func(addr string) (net.Conn, error) { Dialer: func(addr string) (net.Conn, error) {
return cfg.net.Dial("tcp", addr, cfg.ConnectionTimeout) return cfg.net.Dial("tcp", addr, cfg.ConnectionTimeout)
}, },
BlockCache: blockCache, BlockCache: blockCache,
LoaderOptions: []btcwallet.LoaderOption{dbs.walletDB}, }
// Let's go ahead and create the partial chain control now that is only
// dependent on our configuration and doesn't require any wallet
// specific information.
partialChainControl, cleanup, err := chainreg.NewPartialChainControl(
chainControlCfg,
)
if cleanup != nil {
defer cleanup()
}
if err != nil {
err := fmt.Errorf("unable to create partial chain control: %v",
err)
ltndLog.Error(err)
return err
}
walletConfig := &btcwallet.Config{
PrivatePass: privateWalletPw,
PublicPass: publicWalletPw,
Birthday: walletInitParams.Birthday,
RecoveryWindow: walletInitParams.RecoveryWindow,
NetParams: cfg.ActiveNetParams.Params,
CoinType: cfg.ActiveNetParams.CoinType,
Wallet: walletInitParams.Wallet,
LoaderOptions: []btcwallet.LoaderOption{dbs.walletDB},
} }
// Parse coin selection strategy. // Parse coin selection strategy.
switch cfg.CoinSelectionStrategy { switch cfg.CoinSelectionStrategy {
case "largest": case "largest":
chainControlCfg.CoinSelectionStrategy = wallet.CoinSelectionLargest walletConfig.CoinSelectionStrategy = wallet.CoinSelectionLargest
case "random": case "random":
chainControlCfg.CoinSelectionStrategy = wallet.CoinSelectionRandom walletConfig.CoinSelectionStrategy = wallet.CoinSelectionRandom
default: default:
return fmt.Errorf("unknown coin selection strategy %v", return fmt.Errorf("unknown coin selection strategy %v",
cfg.CoinSelectionStrategy) cfg.CoinSelectionStrategy)
} }
// We've created the wallet configuration now, so we can finish
// initializing the main chain control.
activeChainControl, cleanup, err := chainreg.NewChainControl( activeChainControl, cleanup, err := chainreg.NewChainControl(
chainControlCfg, walletConfig, partialChainControl,
) )
if cleanup != nil { if cleanup != nil {
defer cleanup() defer cleanup()