diff --git a/chainreg/chainregistry.go b/chainreg/chainregistry.go index 2b3ec4bc5..0980b729e 100644 --- a/chainreg/chainregistry.go +++ b/chainreg/chainregistry.go @@ -30,7 +30,6 @@ import ( "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnwallet" - "github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/chainview" @@ -664,11 +663,17 @@ func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) { // 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 NewChainControl(walletConfig *btcwallet.Config, +func NewChainControl(walletConfig lnwallet.Config, + msgSigner lnwallet.MessageSigner, pcc *PartialChainControl) (*ChainControl, func(), error) { cc := &ChainControl{ PartialChainControl: pcc, + MsgSigner: msgSigner, + Signer: walletConfig.Signer, + ChainIO: walletConfig.ChainIO, + Wc: walletConfig.WalletController, + KeyRing: walletConfig.SecretKeyRing, } ccCleanup := func() { @@ -679,36 +684,7 @@ func NewChainControl(walletConfig *btcwallet.Config, } } - wc, err := btcwallet.New(*walletConfig, pcc.Cfg.BlockCache) - if err != nil { - fmt.Printf("unable to create wallet controller: %v\n", err) - return nil, ccCleanup, err - } - - cc.MsgSigner = wc - cc.Signer = wc - cc.ChainIO = wc - cc.Wc = wc - - keyRing := keychain.NewBtcWalletKeyRing( - wc.InternalWallet(), walletConfig.CoinType, - ) - cc.KeyRing = keyRing - - // Create, and start the lnwallet, which handles the core payment - // channel logic, and exposes control via proxy state machines. - walletCfg := lnwallet.Config{ - Database: pcc.Cfg.ChanStateDB, - Notifier: cc.ChainNotifier, - WalletController: wc, - Signer: cc.Signer, - FeeEstimator: cc.FeeEstimator, - SecretKeyRing: keyRing, - ChainIO: cc.ChainIO, - DefaultConstraints: cc.ChannelConstraints, - NetParams: *walletConfig.NetParams, - } - lnWallet, err := lnwallet.NewLightningWallet(walletCfg) + lnWallet, err := lnwallet.NewLightningWallet(walletConfig) if err != nil { fmt.Printf("unable to create wallet: %v\n", err) return nil, ccCleanup, err diff --git a/config.go b/config.go index 6044ae0c6..0fd4fd3e2 100644 --- a/config.go +++ b/config.go @@ -1547,6 +1547,7 @@ func (c *Config) ImplementationConfig( RestRegistrar: defaultImpl, ExternalValidator: defaultImpl, DatabaseBuilder: NewDefaultDatabaseBuilder(c, ltndLog), + WalletConfigBuilder: defaultImpl, ChainControlBuilder: defaultImpl, } } diff --git a/config_builder.go b/config_builder.go index 525e65f27..a70ece5a6 100644 --- a/config_builder.go +++ b/config_builder.go @@ -86,15 +86,24 @@ type DatabaseBuilder interface { BuildDatabase(ctx context.Context) (*DatabaseInstances, func(), error) } +// WalletConfigBuilder is an interface that must be satisfied by a custom wallet +// implementation. +type WalletConfigBuilder interface { + // BuildWalletConfig is responsible for creating or unlocking and then + // fully initializing a wallet. + BuildWalletConfig(context.Context, *DatabaseInstances, + *rpcperms.InterceptorChain, + []*ListenerWithSignal) (*chainreg.PartialChainControl, + *btcwallet.Config, func(), error) +} + // ChainControlBuilder is an interface that must be satisfied by a custom wallet // implementation. type ChainControlBuilder interface { - // BuildChainControl is responsible for creating or unlocking and then - // fully initializing a wallet and returning it as part of a fully - // populated chain control instance. - BuildChainControl(context.Context, *DatabaseInstances, - *rpcperms.InterceptorChain, - []*ListenerWithSignal) (*chainreg.ChainControl, func(), error) + // BuildChainControl is responsible for creating a fully populated chain + // control instance from a wallet. + BuildChainControl(*chainreg.PartialChainControl, + *btcwallet.Config) (*chainreg.ChainControl, func(), error) } // ImplementationCfg is a struct that holds all configuration items for @@ -116,6 +125,10 @@ type ImplementationCfg struct { // backend instances. DatabaseBuilder + // WalletConfigBuilder is a type that can provide a wallet configuration + // with a fully loaded and unlocked wallet. + WalletConfigBuilder + // ChainControlBuilder is a type that can provide a custom wallet // implementation. ChainControlBuilder @@ -195,15 +208,14 @@ func (d *DefaultWalletImpl) Permissions() map[string][]bakery.Op { return nil } -// BuildChainControl is responsible for creating or unlocking and then fully -// initializing a wallet and returning it as part of a fully populated chain -// control instance. +// BuildWalletConfig is responsible for creating or unlocking and then +// fully initializing a wallet. // -// NOTE: This is part of the ChainControlBuilder interface. -func (d *DefaultWalletImpl) BuildChainControl(ctx context.Context, +// NOTE: This is part of the WalletConfigBuilder interface. +func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context, dbs *DatabaseInstances, interceptorChain *rpcperms.InterceptorChain, - grpcListeners []*ListenerWithSignal) (*chainreg.ChainControl, func(), - error) { + grpcListeners []*ListenerWithSignal) (*chainreg.PartialChainControl, + *btcwallet.Config, func(), error) { // Keep track of our various cleanup functions. We use a defer function // as well to not repeat ourselves with every return statement. @@ -245,7 +257,7 @@ func (d *DefaultWalletImpl) BuildChainControl(ctx context.Context, err := fmt.Errorf("unable to initialize neutrino "+ "backend: %v", err) d.logger.Error(err) - return nil, nil, err + return nil, nil, nil, err } cleanUpTasks = append(cleanUpTasks, neutrinoCleanUp) neutrinoCS = neutrinoBackend @@ -270,7 +282,7 @@ func (d *DefaultWalletImpl) BuildChainControl(ctx context.Context, d.pwService.SetMacaroonDB(dbs.MacaroonDB) walletExists, err := d.pwService.WalletExists() if err != nil { - return nil, nil, err + return nil, nil, nil, err } if !walletExists { @@ -287,9 +299,9 @@ func (d *DefaultWalletImpl) BuildChainControl(ctx context.Context, if d.cfg.WalletUnlockPasswordFile != "" && !walletExists && !d.cfg.WalletUnlockAllowCreate { - return nil, nil, fmt.Errorf("wallet unlock password file was " + - "specified but wallet does not exist; initialize the " + - "wallet before using auto unlocking") + return nil, nil, nil, fmt.Errorf("wallet unlock password file " + + "was specified but wallet does not exist; initialize " + + "the wallet before using auto unlocking") } // What wallet mode are we running in? We've already made sure the no @@ -306,8 +318,8 @@ func (d *DefaultWalletImpl) BuildChainControl(ctx context.Context, "password provided in file") pwBytes, err := ioutil.ReadFile(d.cfg.WalletUnlockPasswordFile) if err != nil { - return nil, nil, fmt.Errorf("error reading password "+ - "from file %s: %v", + return nil, nil, nil, fmt.Errorf("error reading "+ + "password from file %s: %v", d.cfg.WalletUnlockPasswordFile, err) } @@ -322,8 +334,8 @@ func (d *DefaultWalletImpl) BuildChainControl(ctx context.Context, pwBytes, 0, ) if err != nil { - return nil, nil, fmt.Errorf("error unlocking wallet "+ - "with password from file: %v", err) + return nil, nil, nil, fmt.Errorf("error unlocking "+ + "wallet with password from file: %v", err) } cleanUpTasks = append(cleanUpTasks, func() { @@ -343,7 +355,7 @@ func (d *DefaultWalletImpl) BuildChainControl(ctx context.Context, // over RPC. default: if err := d.interceptor.Notifier.NotifyReady(false); err != nil { - return nil, nil, err + return nil, nil, nil, err } params, err := waitForWalletPassword( @@ -354,7 +366,7 @@ func (d *DefaultWalletImpl) BuildChainControl(ctx context.Context, err := fmt.Errorf("unable to set up wallet password "+ "listeners: %v", err) d.logger.Error(err) - return nil, nil, err + return nil, nil, nil, err } walletInitParams = *params @@ -386,7 +398,7 @@ func (d *DefaultWalletImpl) BuildChainControl(ctx context.Context, err := fmt.Errorf("unable to set up macaroon "+ "authentication: %v", err) d.logger.Error(err) - return nil, nil, err + return nil, nil, nil, err } cleanUpTasks = append(cleanUpTasks, func() { if err := macaroonService.Close(); err != nil { @@ -402,7 +414,7 @@ func (d *DefaultWalletImpl) BuildChainControl(ctx context.Context, if err != nil && err != macaroons.ErrAlreadyUnlocked { err := fmt.Errorf("unable to unlock macaroons: %v", err) d.logger.Error(err) - return nil, nil, err + return nil, nil, nil, err } // In case we actually needed to unlock the wallet, we now need @@ -415,7 +427,7 @@ func (d *DefaultWalletImpl) BuildChainControl(ctx context.Context, ctx, macaroonService, adminPermissions(), ) if err != nil { - return nil, nil, err + return nil, nil, nil, err } // The channel is buffered by one element so writing @@ -446,7 +458,7 @@ func (d *DefaultWalletImpl) BuildChainControl(ctx context.Context, err := fmt.Errorf("unable to create macaroons "+ "%v", err) d.logger.Error(err) - return nil, nil, err + return nil, nil, nil, err } } @@ -538,7 +550,7 @@ func (d *DefaultWalletImpl) BuildChainControl(ctx context.Context, err := fmt.Errorf("unable to create partial chain control: %v", err) d.logger.Error(err) - return nil, nil, err + return nil, nil, nil, err } walletConfig := &btcwallet.Config{ @@ -562,23 +574,60 @@ func (d *DefaultWalletImpl) BuildChainControl(ctx context.Context, walletConfig.CoinSelectionStrategy = wallet.CoinSelectionRandom default: - return nil, nil, fmt.Errorf("unknown coin selection strategy "+ - "%v", d.cfg.CoinSelectionStrategy) + return nil, nil, nil, fmt.Errorf("unknown coin selection "+ + "strategy %v", d.cfg.CoinSelectionStrategy) + } + + earlyExit = false + return partialChainControl, walletConfig, cleanUp, nil +} + +// BuildChainControl is responsible for creating a fully populated chain +// control instance from a wallet. +// +// NOTE: This is part of the ChainControlBuilder interface. +func (d *DefaultWalletImpl) BuildChainControl( + partialChainControl *chainreg.PartialChainControl, + walletConfig *btcwallet.Config) (*chainreg.ChainControl, func(), error) { + + walletController, err := btcwallet.New( + *walletConfig, partialChainControl.Cfg.BlockCache, + ) + if err != nil { + fmt.Printf("unable to create wallet controller: %v\n", err) + d.logger.Error(err) + return nil, nil, err + } + + keyRing := keychain.NewBtcWalletKeyRing( + walletController.InternalWallet(), walletConfig.CoinType, + ) + + // Create, and start the lnwallet, which handles the core payment + // channel logic, and exposes control via proxy state machines. + lnWalletConfig := lnwallet.Config{ + Database: partialChainControl.Cfg.ChanStateDB, + Notifier: partialChainControl.ChainNotifier, + WalletController: walletController, + Signer: walletController, + FeeEstimator: partialChainControl.FeeEstimator, + SecretKeyRing: keyRing, + ChainIO: walletController, + DefaultConstraints: partialChainControl.ChannelConstraints, + NetParams: *walletConfig.NetParams, } // We've created the wallet configuration now, so we can finish // initializing the main chain control. - activeChainControl, ccCleanup, err := chainreg.NewChainControl( - walletConfig, partialChainControl, + activeChainControl, cleanUp, err := chainreg.NewChainControl( + lnWalletConfig, walletController, partialChainControl, ) - cleanUpTasks = append(cleanUpTasks, ccCleanup) if err != nil { err := fmt.Errorf("unable to create chain control: %v", err) d.logger.Error(err) return nil, nil, err } - earlyExit = false return activeChainControl, cleanUp, nil } diff --git a/lnd.go b/lnd.go index 6ac66e3ac..83a23c846 100644 --- a/lnd.go +++ b/lnd.go @@ -361,9 +361,18 @@ func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg, defer cleanUp() - activeChainControl, cleanUp, err := implCfg.BuildChainControl( + partialChainControl, walletConfig, cleanUp, err := implCfg.BuildWalletConfig( ctx, dbs, interceptorChain, grpcListeners, ) + if err != nil { + return fmt.Errorf("error creating wallet config: %v", err) + } + + defer cleanUp() + + activeChainControl, cleanUp, err := implCfg.BuildChainControl( + partialChainControl, walletConfig, + ) if err != nil { return fmt.Errorf("error loading chain control: %v", err) }