multi: extract key ring creation from chain control

To make it possible to supply our own implementation of a secret key
ring, we extract that part from the chain control and split the whole
chain control creation into two parts.
This commit is contained in:
Oliver Gugger 2021-10-14 15:42:47 +02:00
parent 1309c6afea
commit 9fa9dd8e43
No known key found for this signature in database
GPG key ID: 8E4256593F177720
4 changed files with 103 additions and 68 deletions

View file

@ -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

View file

@ -1547,6 +1547,7 @@ func (c *Config) ImplementationConfig(
RestRegistrar: defaultImpl,
ExternalValidator: defaultImpl,
DatabaseBuilder: NewDefaultDatabaseBuilder(c, ltndLog),
WalletConfigBuilder: defaultImpl,
ChainControlBuilder: defaultImpl,
}
}

View file

@ -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
}

11
lnd.go
View file

@ -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)
}