From b685a97fcde00fbab6eff7a03d36812693aca38e Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 6 Oct 2020 17:23:36 +0200 Subject: [PATCH] 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. --- lnd.go | 67 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/lnd.go b/lnd.go index c7986dad..238ad535 100644 --- a/lnd.go +++ b/lnd.go @@ -318,6 +318,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { var ( walletInitParams WalletUnlockParams + shutdownUnlocker = func() {} privateWalletPw = lnwallet.DefaultPrivatePassphrase 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 // for wallet encryption. if !cfg.NoSeedBackup { - params, err := waitForWalletPassword( + params, shutdown, err := waitForWalletPassword( cfg, cfg.RESTListeners, serverOpts, restDialOpts, restProxyDest, tlsCfg, walletUnlockerListeners, ) @@ -389,6 +390,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { } walletInitParams = *params + shutdownUnlocker = shutdown privateWalletPw = walletInitParams.Password publicWalletPw = walletInitParams.Password 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 // instances of the pertinent interfaces required to operate the // Lightning Network Daemon. @@ -1041,7 +1047,7 @@ type WalletUnlockParams struct { func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, serverOpts []grpc.ServerOption, restDialOpts []grpc.DialOption, restProxyDest string, tlsConf *tls.Config, - getListeners rpcListeners) (*WalletUnlockParams, error) { + getListeners rpcListeners) (*WalletUnlockParams, func(), error) { chainConfig := cfg.Bitcoin 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 // provided over RPC. grpcServer := grpc.NewServer(serverOpts...) - defer grpcServer.GracefulStop() 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 // for getting the encryption password from the client. listeners, cleanup, err := getListeners() 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 // 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 { wg.Add(1) go func(lis *ListenerWithSignal) { - rpcsLog.Infof("password RPC server listening on %s", + rpcsLog.Infof("Password RPC server listening on %s", lis.Addr()) // Close the ready chan to indicate we are listening. close(lis.Ready) wg.Done() - grpcServer.Serve(lis) + _ = grpcServer.Serve(lis) }(lis) } // Start a REST proxy for our gRPC server above. ctx := context.Background() ctx, cancel := context.WithCancel(ctx) - defer cancel() + shutdownFuncs = append(shutdownFuncs, cancel) mux := proxy.NewServeMux() @@ -1104,7 +1117,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, ctx, mux, restProxyDest, restDialOpts, ) if err != nil { - return nil, err + return nil, shutdown, err } srv := &http.Server{Handler: allowCORS(mux, cfg.RestCORS)} @@ -1112,22 +1125,24 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, for _, restEndpoint := range restEndpoints { lis, err := lncfg.TLSListenOnAddress(restEndpoint, tlsConf) if err != nil { - ltndLog.Errorf( - "password gRPC proxy unable to listen on %s", - restEndpoint, - ) - return nil, err + ltndLog.Errorf("Password gRPC proxy unable to listen "+ + "on %s", restEndpoint) + return nil, shutdown, err } - defer lis.Close() + shutdownFuncs = append(shutdownFuncs, func() { + err := lis.Close() + if err != nil { + rpcsLog.Errorf("Error closing listener: %v", + err) + } + }) wg.Add(1) go func() { - rpcsLog.Infof( - "password gRPC proxy started at %s", - lis.Addr(), - ) + rpcsLog.Infof("Password gRPC proxy started at %s", + lis.Addr()) 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 // this. if cipherSeed.InternalVersion != keychain.KeyDerivationVersion { - return nil, fmt.Errorf("invalid internal seed version "+ - "%v, current version is %v", + return nil, shutdown, fmt.Errorf("invalid internal "+ + "seed version %v, current version is %v", cipherSeed.InternalVersion, keychain.KeyDerivationVersion) } @@ -1185,7 +1200,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, ltndLog.Errorf("Could not unload new "+ "wallet: %v", err) } - return nil, err + return nil, shutdown, err } return &WalletUnlockParams{ @@ -1195,7 +1210,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, Wallet: newWallet, ChansToRestore: initMsg.ChanBackups, UnloadWallet: loader.UnloadWallet, - }, nil + }, shutdown, nil // The wallet has already been created in the past, and is simply being // unlocked. So we'll just return these passphrases. @@ -1206,10 +1221,10 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, Wallet: unlockMsg.Wallet, ChansToRestore: unlockMsg.ChanBackups, UnloadWallet: unlockMsg.UnloadWallet, - }, nil + }, shutdown, nil case <-signal.ShutdownChannel(): - return nil, fmt.Errorf("shutting down") + return nil, shutdown, fmt.Errorf("shutting down") } }