lnd: shutdown wallet unlocker after macaroon creation

Because we'll need to return the macaroon through the wallet unlocker
we cannot shut down its service before we have done so, otherwise
we'll end up in a deadlock. That's why we collect all shutdown
tasks and return them as a function that can be called after we've
initialized the macaroon service.
This commit is contained in:
Oliver Gugger 2020-10-06 17:23:36 +02:00
parent 71ba2a8e60
commit b685a97fcd
No known key found for this signature in database
GPG Key ID: 8E4256593F177720

67
lnd.go
View File

@ -318,6 +318,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
var ( var (
walletInitParams WalletUnlockParams walletInitParams WalletUnlockParams
shutdownUnlocker = func() {}
privateWalletPw = lnwallet.DefaultPrivatePassphrase privateWalletPw = lnwallet.DefaultPrivatePassphrase
publicWalletPw = lnwallet.DefaultPublicPassphrase publicWalletPw = lnwallet.DefaultPublicPassphrase
) )
@ -377,7 +378,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
// started with the --noseedbackup flag, we use the default password // started with the --noseedbackup flag, we use the default password
// for wallet encryption. // for wallet encryption.
if !cfg.NoSeedBackup { if !cfg.NoSeedBackup {
params, err := waitForWalletPassword( params, shutdown, err := waitForWalletPassword(
cfg, cfg.RESTListeners, serverOpts, restDialOpts, cfg, cfg.RESTListeners, serverOpts, restDialOpts,
restProxyDest, tlsCfg, walletUnlockerListeners, restProxyDest, tlsCfg, walletUnlockerListeners,
) )
@ -389,6 +390,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
} }
walletInitParams = *params walletInitParams = *params
shutdownUnlocker = shutdown
privateWalletPw = walletInitParams.Password privateWalletPw = walletInitParams.Password
publicWalletPw = walletInitParams.Password publicWalletPw = walletInitParams.Password
defer func() { defer func() {
@ -443,6 +445,10 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
} }
} }
// Now we're definitely done with the unlocker, shut it down so we can
// start the main RPC service later.
shutdownUnlocker()
// With the information parsed from the configuration, create valid // With the information parsed from the configuration, create valid
// instances of the pertinent interfaces required to operate the // instances of the pertinent interfaces required to operate the
// Lightning Network Daemon. // Lightning Network Daemon.
@ -1041,7 +1047,7 @@ type WalletUnlockParams struct {
func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
serverOpts []grpc.ServerOption, restDialOpts []grpc.DialOption, serverOpts []grpc.ServerOption, restDialOpts []grpc.DialOption,
restProxyDest string, tlsConf *tls.Config, restProxyDest string, tlsConf *tls.Config,
getListeners rpcListeners) (*WalletUnlockParams, error) { getListeners rpcListeners) (*WalletUnlockParams, func(), error) {
chainConfig := cfg.Bitcoin chainConfig := cfg.Bitcoin
if cfg.registeredChains.PrimaryChain() == chainreg.LitecoinChain { if cfg.registeredChains.PrimaryChain() == chainreg.LitecoinChain {
@ -1064,16 +1070,23 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
// Set up a new PasswordService, which will listen for passwords // Set up a new PasswordService, which will listen for passwords
// provided over RPC. // provided over RPC.
grpcServer := grpc.NewServer(serverOpts...) grpcServer := grpc.NewServer(serverOpts...)
defer grpcServer.GracefulStop()
lnrpc.RegisterWalletUnlockerServer(grpcServer, pwService) lnrpc.RegisterWalletUnlockerServer(grpcServer, pwService)
var shutdownFuncs []func()
shutdown := func() {
for _, shutdownFn := range shutdownFuncs {
shutdownFn()
}
}
shutdownFuncs = append(shutdownFuncs, grpcServer.GracefulStop)
// Start a gRPC server listening for HTTP/2 connections, solely used // Start a gRPC server listening for HTTP/2 connections, solely used
// for getting the encryption password from the client. // for getting the encryption password from the client.
listeners, cleanup, err := getListeners() listeners, cleanup, err := getListeners()
if err != nil { if err != nil {
return nil, err return nil, shutdown, err
} }
defer cleanup() shutdownFuncs = append(shutdownFuncs, cleanup)
// Use a WaitGroup so we can be sure the instructions on how to input the // Use a WaitGroup so we can be sure the instructions on how to input the
// password is the last thing to be printed to the console. // password is the last thing to be printed to the console.
@ -1082,21 +1095,21 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
for _, lis := range listeners { for _, lis := range listeners {
wg.Add(1) wg.Add(1)
go func(lis *ListenerWithSignal) { go func(lis *ListenerWithSignal) {
rpcsLog.Infof("password RPC server listening on %s", rpcsLog.Infof("Password RPC server listening on %s",
lis.Addr()) lis.Addr())
// Close the ready chan to indicate we are listening. // Close the ready chan to indicate we are listening.
close(lis.Ready) close(lis.Ready)
wg.Done() wg.Done()
grpcServer.Serve(lis) _ = grpcServer.Serve(lis)
}(lis) }(lis)
} }
// Start a REST proxy for our gRPC server above. // Start a REST proxy for our gRPC server above.
ctx := context.Background() ctx := context.Background()
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
defer cancel() shutdownFuncs = append(shutdownFuncs, cancel)
mux := proxy.NewServeMux() mux := proxy.NewServeMux()
@ -1104,7 +1117,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
ctx, mux, restProxyDest, restDialOpts, ctx, mux, restProxyDest, restDialOpts,
) )
if err != nil { if err != nil {
return nil, err return nil, shutdown, err
} }
srv := &http.Server{Handler: allowCORS(mux, cfg.RestCORS)} srv := &http.Server{Handler: allowCORS(mux, cfg.RestCORS)}
@ -1112,22 +1125,24 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
for _, restEndpoint := range restEndpoints { for _, restEndpoint := range restEndpoints {
lis, err := lncfg.TLSListenOnAddress(restEndpoint, tlsConf) lis, err := lncfg.TLSListenOnAddress(restEndpoint, tlsConf)
if err != nil { if err != nil {
ltndLog.Errorf( ltndLog.Errorf("Password gRPC proxy unable to listen "+
"password gRPC proxy unable to listen on %s", "on %s", restEndpoint)
restEndpoint, return nil, shutdown, err
)
return nil, err
} }
defer lis.Close() shutdownFuncs = append(shutdownFuncs, func() {
err := lis.Close()
if err != nil {
rpcsLog.Errorf("Error closing listener: %v",
err)
}
})
wg.Add(1) wg.Add(1)
go func() { go func() {
rpcsLog.Infof( rpcsLog.Infof("Password gRPC proxy started at %s",
"password gRPC proxy started at %s", lis.Addr())
lis.Addr(),
)
wg.Done() wg.Done()
srv.Serve(lis) _ = srv.Serve(lis)
}() }()
} }
@ -1158,8 +1173,8 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
// version, then we'll return an error as we don't understand // version, then we'll return an error as we don't understand
// this. // this.
if cipherSeed.InternalVersion != keychain.KeyDerivationVersion { if cipherSeed.InternalVersion != keychain.KeyDerivationVersion {
return nil, fmt.Errorf("invalid internal seed version "+ return nil, shutdown, fmt.Errorf("invalid internal "+
"%v, current version is %v", "seed version %v, current version is %v",
cipherSeed.InternalVersion, cipherSeed.InternalVersion,
keychain.KeyDerivationVersion) keychain.KeyDerivationVersion)
} }
@ -1185,7 +1200,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
ltndLog.Errorf("Could not unload new "+ ltndLog.Errorf("Could not unload new "+
"wallet: %v", err) "wallet: %v", err)
} }
return nil, err return nil, shutdown, err
} }
return &WalletUnlockParams{ return &WalletUnlockParams{
@ -1195,7 +1210,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
Wallet: newWallet, Wallet: newWallet,
ChansToRestore: initMsg.ChanBackups, ChansToRestore: initMsg.ChanBackups,
UnloadWallet: loader.UnloadWallet, UnloadWallet: loader.UnloadWallet,
}, nil }, shutdown, nil
// The wallet has already been created in the past, and is simply being // The wallet has already been created in the past, and is simply being
// unlocked. So we'll just return these passphrases. // unlocked. So we'll just return these passphrases.
@ -1206,10 +1221,10 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
Wallet: unlockMsg.Wallet, Wallet: unlockMsg.Wallet,
ChansToRestore: unlockMsg.ChanBackups, ChansToRestore: unlockMsg.ChanBackups,
UnloadWallet: unlockMsg.UnloadWallet, UnloadWallet: unlockMsg.UnloadWallet,
}, nil }, shutdown, nil
case <-signal.ShutdownChannel(): case <-signal.ShutdownChannel():
return nil, fmt.Errorf("shutting down") return nil, shutdown, fmt.Errorf("shutting down")
} }
} }