From c4562cba6b0491e2fd7f545afea0d9d25e19dbe0 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 2 May 2023 08:33:46 +0200 Subject: [PATCH 01/13] perms: remove dead code --- perms/manager.go | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/perms/manager.go b/perms/manager.go index c4383dfed..d5459b801 100644 --- a/perms/manager.go +++ b/perms/manager.go @@ -292,27 +292,3 @@ func (pm *Manager) isLndURI(uri string) bool { _, lndCall := pm.fixedPerms[lndPerms][uri] return lndCall || lndSubServerCall } - -// IsLoopURI returns true if the given URI belongs to an RPC of loopd. -func (pm *Manager) IsLoopURI(uri string) bool { - _, ok := pm.fixedPerms[loopPerms][uri] - return ok -} - -// IsFaradayURI returns true if the given URI belongs to an RPC of faraday. -func (pm *Manager) IsFaradayURI(uri string) bool { - _, ok := pm.fixedPerms[faradayPerms][uri] - return ok -} - -// IsPoolURI returns true if the given URI belongs to an RPC of poold. -func (pm *Manager) IsPoolURI(uri string) bool { - _, ok := pm.fixedPerms[poolPerms][uri] - return ok -} - -// IsLitURI returns true if the given URI belongs to an RPC of LiT. -func (pm *Manager) IsLitURI(uri string) bool { - _, ok := pm.fixedPerms[litPerms][uri] - return ok -} From 3ce40f54a1351b0c3c2988a15a20ae180ef8d684 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 2 May 2023 08:39:02 +0200 Subject: [PATCH 02/13] rpc_proxy: remote subserver remote conn creation Remove the code for creating remote connections to loop, faraday and pool subservers from the rpcProxy. This is now handled by the subserver manager. --- rpc_proxy.go | 105 ++++++++------------------------------------------- terminal.go | 4 +- 2 files changed, 18 insertions(+), 91 deletions(-) diff --git a/rpc_proxy.go b/rpc_proxy.go index 560f90b03..081e74501 100644 --- a/rpc_proxy.go +++ b/rpc_proxy.go @@ -61,7 +61,7 @@ func (e *proxyErr) Unwrap() error { // component. func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator, superMacValidator session.SuperMacaroonValidator, - permsMgr *perms.Manager) *rpcProxy { + permsMgr *perms.Manager, subServerMgr *subservers.Manager) *rpcProxy { // The gRPC web calls are protected by HTTP basic auth which is defined // by base64(username:password). Because we only have a password, we @@ -81,6 +81,7 @@ func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator, permsMgr: permsMgr, macValidator: validator, superMacValidator: superMacValidator, + subServerMgr: subServerMgr, } p.grpcServer = grpc.NewServer( // From the grpxProxy doc: This codec is *crucial* to the @@ -151,19 +152,17 @@ type rpcProxy struct { // must only ever be used atomically. started int32 - cfg *Config - basicAuth string - permsMgr *perms.Manager + cfg *Config + basicAuth string + permsMgr *perms.Manager + subServerMgr *subservers.Manager macValidator macaroons.MacaroonValidator superMacValidator session.SuperMacaroonValidator superMacaroon string - lndConn *grpc.ClientConn - faradayConn *grpc.ClientConn - loopConn *grpc.ClientConn - poolConn *grpc.ClientConn + lndConn *grpc.ClientConn grpcServer *grpc.Server grpcWebProxy *grpcweb.WrappedGrpcServer @@ -171,44 +170,8 @@ type rpcProxy struct { // Start creates initial connection to lnd. func (p *rpcProxy) Start(lndConn *grpc.ClientConn) error { - var err error p.lndConn = lndConn - // Make sure we can connect to all the daemons that are configured to be - // running in remote mode. - if p.cfg.faradayRemote { - p.faradayConn, err = dialBackend( - "faraday", p.cfg.Remote.Faraday.RPCServer, - lncfg.CleanAndExpandPath( - p.cfg.Remote.Faraday.TLSCertPath, - ), - ) - if err != nil { - return fmt.Errorf("could not dial remote faraday: %v", - err) - } - } - - if p.cfg.loopRemote { - p.loopConn, err = dialBackend( - "loop", p.cfg.Remote.Loop.RPCServer, - lncfg.CleanAndExpandPath(p.cfg.Remote.Loop.TLSCertPath), - ) - if err != nil { - return fmt.Errorf("could not dial remote loop: %v", err) - } - } - - if p.cfg.poolRemote { - p.poolConn, err = dialBackend( - "pool", p.cfg.Remote.Pool.RPCServer, - lncfg.CleanAndExpandPath(p.cfg.Remote.Pool.TLSCertPath), - ) - if err != nil { - return fmt.Errorf("could not dial remote pool: %v", err) - } - } - atomic.CompareAndSwapInt32(&p.started, 0, 1) return nil @@ -224,27 +187,6 @@ func (p *rpcProxy) hasStarted() bool { func (p *rpcProxy) Stop() error { p.grpcServer.Stop() - if p.faradayConn != nil { - if err := p.faradayConn.Close(); err != nil { - log.Errorf("Error closing faraday connection: %v", err) - return err - } - } - - if p.loopConn != nil { - if err := p.loopConn.Close(); err != nil { - log.Errorf("Error closing loop connection: %v", err) - return err - } - } - - if p.poolConn != nil { - if err := p.poolConn.Close(); err != nil { - log.Errorf("Error closing pool connection: %v", err) - return err - } - } - return nil } @@ -362,38 +304,21 @@ func (p *rpcProxy) makeDirector(allowLitRPC bool) func(ctx context.Context, // since it must either be an lnd call or something that'll be // handled by the integrated daemons that are hooking into lnd's // gRPC server. - isFaraday := p.permsMgr.IsSubServerURI( - subservers.FARADAY, requestURI, - ) - isLoop := p.permsMgr.IsSubServerURI( - subservers.LOOP, requestURI, - ) - isPool := p.permsMgr.IsSubServerURI( - subservers.POOL, requestURI, - ) - isLit := p.permsMgr.IsSubServerURI( - subservers.LIT, requestURI, - ) - switch { - case isFaraday && p.cfg.faradayRemote: - return outCtx, p.faradayConn, nil - - case isLoop && p.cfg.loopRemote: - return outCtx, p.loopConn, nil - - case isPool && p.cfg.poolRemote: - return outCtx, p.poolConn, nil + handled, conn := p.subServerMgr.GetRemoteConn(requestURI) + if handled { + return outCtx, conn, nil + } // Calls to LiT session RPC aren't allowed in some cases. - case isLit && !allowLitRPC: + isLit := p.permsMgr.IsSubServerURI(subservers.LIT, requestURI) + if isLit && !allowLitRPC { return outCtx, nil, status.Errorf( codes.Unimplemented, "unknown service %s", requestURI, ) - - default: - return outCtx, p.lndConn, nil } + + return outCtx, p.lndConn, nil } } diff --git a/terminal.go b/terminal.go index bcce089dd..e5b932ddf 100644 --- a/terminal.go +++ b/terminal.go @@ -236,7 +236,9 @@ func (g *LightningTerminal) Run() error { // Construct the rpcProxy. It must be initialised before the main web // server is started. - g.rpcProxy = newRpcProxy(g.cfg, g, g.validateSuperMacaroon, g.permsMgr) + g.rpcProxy = newRpcProxy( + g.cfg, g, g.validateSuperMacaroon, g.permsMgr, g.subServerMgr, + ) // Start the main web server that dispatches requests either to the // static UI file server or the RPC proxy. This makes it possible to From 0ab5cb750b3362246c3f23f1298120d012917942 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 2 May 2023 08:43:03 +0200 Subject: [PATCH 03/13] subservers: embed Subserver in subServerWrapper --- subservers/manager.go | 35 ++++++++++++++++------------------- subservers/subserver.go | 24 ++++++++++++------------ 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/subservers/manager.go b/subservers/manager.go index 25d887685..935af3d04 100644 --- a/subservers/manager.go +++ b/subservers/manager.go @@ -46,7 +46,7 @@ func (s *Manager) AddServer(ss SubServer) { defer s.mu.Unlock() s.servers = append(s.servers, &subServerWrapper{ - subServer: ss, + SubServer: ss, quit: make(chan struct{}), }) } @@ -60,7 +60,7 @@ func (s *Manager) StartIntegratedServers(lndClient lnrpc.LightningClient, defer s.mu.Unlock() for _, ss := range s.servers { - if ss.subServer.Remote() { + if ss.Remote() { continue } @@ -68,8 +68,8 @@ func (s *Manager) StartIntegratedServers(lndClient lnrpc.LightningClient, lndClient, lndGrpc, withMacaroonService, ) if err != nil { - return fmt.Errorf("Unable to start %v in integrated "+ - "mode: %v", ss.subServer.Name(), err) + return fmt.Errorf("unable to start %v in integrated "+ + "mode: %v", ss.Name(), err) } } @@ -83,14 +83,14 @@ func (s *Manager) ConnectRemoteSubServers() { defer s.mu.Unlock() for _, ss := range s.servers { - if !ss.subServer.Remote() { + if !ss.Remote() { continue } err := ss.connectRemote() if err != nil { log.Errorf("Failed to connect to remote %s: %v", - ss.subServer.Name(), err) + ss.Name(), err) continue } @@ -108,11 +108,11 @@ func (s *Manager) RegisterRPCServices(server grpc.ServiceRegistrar) { // a catch-all for any gRPC request that isn't known because we // didn't register any server for it. The director will then // forward the request to the remote service. - if ss.subServer.Remote() { + if ss.Remote() { continue } - ss.subServer.RegisterGrpcService(server) + ss.RegisterGrpcService(server) } } @@ -125,11 +125,11 @@ func (s *Manager) GetRemoteConn(uri string) (bool, *grpc.ClientConn) { defer s.mu.RUnlock() for _, ss := range s.servers { - if !s.permsMgr.IsSubServerURI(ss.subServer.Name(), uri) { + if !s.permsMgr.IsSubServerURI(ss.Name(), uri) { continue } - if !ss.subServer.Remote() { + if !ss.Remote() { return false, nil } @@ -151,7 +151,7 @@ func (s *Manager) ValidateMacaroon(ctx context.Context, defer s.mu.RUnlock() for _, ss := range s.servers { - if !s.permsMgr.IsSubServerURI(ss.subServer.Name(), uri) { + if !s.permsMgr.IsSubServerURI(ss.Name(), uri) { continue } @@ -159,7 +159,7 @@ func (s *Manager) ValidateMacaroon(ctx context.Context, // need to validate the macaroon here since the remote server // will do it when the request arrives. But we have handled the // request, as we were able to identify it. - if ss.subServer.Remote() { + if ss.Remote() { return true, nil } @@ -170,14 +170,12 @@ func (s *Manager) ValidateMacaroon(ctx context.Context, return true, fmt.Errorf("%s is not yet ready for "+ "requests, the subserver has not started or "+ "lnd still starting/syncing", - ss.subServer.Name()) + ss.Name()) } // Validate the macaroon with the integrated sub-server's own // validator. - err := ss.subServer.ValidateMacaroon( - ctx, requiredPermissions, uri, - ) + err := ss.ValidateMacaroon(ctx, requiredPermissions, uri) if err != nil { return true, fmt.Errorf("invalid macaroon: %v", err) } @@ -199,14 +197,13 @@ func (s *Manager) Stop() error { defer s.mu.RUnlock() for _, ss := range s.servers { - if ss.subServer.Remote() { + if ss.Remote() { continue } err := ss.stop() if err != nil { - log.Errorf("Error stopping %s: %v", ss.subServer.Name(), - err) + log.Errorf("Error stopping %s: %v", ss.Name(), err) returnErr = err } } diff --git a/subservers/subserver.go b/subservers/subserver.go index 796d9a0b4..7797a6026 100644 --- a/subservers/subserver.go +++ b/subservers/subserver.go @@ -21,13 +21,13 @@ const ( // subServerWrapper is a wrapper around the SubServer interface and is used by // the subServerMgr to manage a SubServer. type subServerWrapper struct { + SubServer + integratedStarted bool startedMu sync.RWMutex stopped sync.Once - subServer SubServer - remoteConn *grpc.ClientConn wg sync.WaitGroup @@ -66,7 +66,7 @@ func (s *subServerWrapper) stop() error { s.wg.Wait() // If running in remote mode, close the connection. - if s.subServer.Remote() && s.remoteConn != nil { + if s.Remote() && s.remoteConn != nil { err := s.remoteConn.Close() if err != nil { returnErr = fmt.Errorf("could not close "+ @@ -76,19 +76,19 @@ func (s *subServerWrapper) stop() error { } // Else, stop the integrated sub-server process. - err := s.subServer.Stop() + err := s.Stop() if err != nil { returnErr = fmt.Errorf("could not close "+ "integrated connection: %v", err) return } - if s.subServer.ServerErrChan() == nil { + if s.ServerErrChan() == nil { return } select { - case returnErr = <-s.subServer.ServerErrChan(): + case returnErr = <-s.ServerErrChan(): default: } }) @@ -100,13 +100,13 @@ func (s *subServerWrapper) stop() error { func (s *subServerWrapper) startIntegrated(lndClient lnrpc.LightningClient, lndGrpc *lndclient.GrpcLndServices, withMacaroonService bool) error { - err := s.subServer.Start(lndClient, lndGrpc, withMacaroonService) + err := s.Start(lndClient, lndGrpc, withMacaroonService) if err != nil { return err } s.setStarted(true) - if s.subServer.ServerErrChan() == nil { + if s.ServerErrChan() == nil { return nil } @@ -115,14 +115,14 @@ func (s *subServerWrapper) startIntegrated(lndClient lnrpc.LightningClient, defer s.wg.Done() select { - case err := <-s.subServer.ServerErrChan(): + case err := <-s.ServerErrChan(): // The sub server should shut itself down if an error // happens. We don't need to try to stop it again. s.setStarted(false) err = fmt.Errorf("received critical error from "+ "sub-server (%s), shutting down: %v", - s.subServer.Name(), err) + s.Name(), err) log.Error(err) @@ -135,9 +135,9 @@ func (s *subServerWrapper) startIntegrated(lndClient lnrpc.LightningClient, // connectRemote attempts to make a connection to the remote sub-server. func (s *subServerWrapper) connectRemote() error { - cfg := s.subServer.RemoteConfig() + cfg := s.RemoteConfig() certPath := lncfg.CleanAndExpandPath(cfg.TLSCertPath) - name := s.subServer.Name() + name := s.Name() conn, err := dialBackend(name, cfg.RPCServer, certPath) if err != nil { return fmt.Errorf("remote dial error: %v", err) From 9eb01518e69051444ad2cb6beec874e5dfc836c0 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 2 May 2023 08:46:30 +0200 Subject: [PATCH 04/13] multi: add MacaroonPath method to subserver manager --- rpc_proxy.go | 43 +++++++++---------------------------------- subservers/manager.go | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/rpc_proxy.go b/rpc_proxy.go index 081e74501..1766f583d 100644 --- a/rpc_proxy.go +++ b/rpc_proxy.go @@ -467,46 +467,21 @@ func (p *rpcProxy) basicAuthToMacaroon(basicAuth, requestURI string, return nil, ctxErr } - var ( - macPath string - macData []byte - ) - subserver, err := p.permsMgr.SubServerHandler(requestURI) - if err != nil { - return nil, err - } - - switch subserver { - case subservers.LND: - _, _, _, macPath, macData = p.cfg.lndConnectParams() + var macData []byte + handled, macPath := p.subServerMgr.MacaroonPath(requestURI) - case subservers.FARADAY: - if p.cfg.faradayRemote { - macPath = p.cfg.Remote.Faraday.MacaroonPath - } else { - macPath = p.cfg.Faraday.MacaroonPath - } - - case subservers.LOOP: - if p.cfg.loopRemote { - macPath = p.cfg.Remote.Loop.MacaroonPath - } else { - macPath = p.cfg.Loop.MacaroonPath - } + switch { + case handled: - case subservers.POOL: - if p.cfg.poolRemote { - macPath = p.cfg.Remote.Pool.MacaroonPath - } else { - macPath = p.cfg.Pool.MacaroonPath - } + case p.permsMgr.IsSubServerURI(subservers.LND, requestURI): + _, _, _, macPath, macData = p.cfg.lndConnectParams() - case subservers.LIT: + case p.permsMgr.IsSubServerURI(subservers.LIT, requestURI): macPath = p.cfg.MacaroonPath default: - return nil, fmt.Errorf("unknown subserver handler: %v", - subserver) + return nil, fmt.Errorf("unknown gRPC web request: %v", + requestURI) } switch { diff --git a/subservers/manager.go b/subservers/manager.go index 935af3d04..8bfb8d431 100644 --- a/subservers/manager.go +++ b/subservers/manager.go @@ -189,6 +189,27 @@ func (s *Manager) ValidateMacaroon(ctx context.Context, return false, nil } +// MacaroonPath checks if any of the manager's sub-servers owns the given uri +// and if so, the appropriate macaroon path is returned for that sub-server. +func (s *Manager) MacaroonPath(uri string) (bool, string) { + s.mu.RLock() + defer s.mu.RUnlock() + + for _, ss := range s.servers { + if !s.permsMgr.IsSubServerURI(ss.Name(), uri) { + continue + } + + if ss.Remote() { + return true, ss.RemoteConfig().MacaroonPath + } + + return true, ss.MacPath() + } + + return false, "" +} + // Stop stops all the manager's sub-servers func (s *Manager) Stop() error { var returnErr error From 05c52684052408ac2e9d547ae478024d5b5b8c95 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 2 May 2023 08:47:58 +0200 Subject: [PATCH 05/13] multi: add ReadRemoteMacaroon method to subserver manager --- rpc_proxy.go | 19 +++-------------- subservers/manager.go | 47 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/rpc_proxy.go b/rpc_proxy.go index 1766f583d..d60ed1343 100644 --- a/rpc_proxy.go +++ b/rpc_proxy.go @@ -553,22 +553,9 @@ func (p *rpcProxy) convertSuperMacaroon(ctx context.Context, macHex string, // Is this actually a request that goes to a daemon that is running // remotely? - subserver, err := p.permsMgr.SubServerHandler(fullMethod) - if err == nil { - switch { - case subserver == subservers.FARADAY && p.cfg.faradayRemote: - return readMacaroon(lncfg.CleanAndExpandPath( - p.cfg.Remote.Faraday.MacaroonPath, - )) - case subserver == subservers.LOOP && p.cfg.loopRemote: - return readMacaroon(lncfg.CleanAndExpandPath( - p.cfg.Remote.Loop.MacaroonPath, - )) - case subserver == subservers.POOL && p.cfg.poolRemote: - return readMacaroon(lncfg.CleanAndExpandPath( - p.cfg.Remote.Pool.MacaroonPath, - )) - } + handled, macBytes, err := p.subServerMgr.ReadRemoteMacaroon(fullMethod) + if handled { + return macBytes, err } return nil, nil diff --git a/subservers/manager.go b/subservers/manager.go index 8bfb8d431..0337cda7a 100644 --- a/subservers/manager.go +++ b/subservers/manager.go @@ -3,17 +3,20 @@ package subservers import ( "context" "fmt" + "io/ioutil" "sync" "time" "github.com/lightninglabs/lightning-terminal/perms" "github.com/lightninglabs/lndclient" + "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" grpcProxy "github.com/mwitkow/grpc-proxy/proxy" "google.golang.org/grpc" "google.golang.org/grpc/backoff" "google.golang.org/grpc/credentials" "gopkg.in/macaroon-bakery.v2/bakery" + "gopkg.in/macaroon.v2" ) var ( @@ -210,6 +213,32 @@ func (s *Manager) MacaroonPath(uri string) (bool, string) { return false, "" } +// ReadRemoteMacaroon checks if any of the manager's sub-servers running in +// remote mode owns the given uri and if so, the appropriate macaroon path is +// returned for that sub-server. +func (s *Manager) ReadRemoteMacaroon(uri string) (bool, []byte, error) { + s.mu.RLock() + defer s.mu.RUnlock() + + for _, ss := range s.servers { + if !s.permsMgr.IsSubServerURI(ss.Name(), uri) { + continue + } + + if !ss.Remote() { + return false, nil, nil + } + + macBytes, err := readMacaroon(lncfg.CleanAndExpandPath( + ss.RemoteConfig().MacaroonPath, + )) + + return true, macBytes, err + } + + return false, nil, nil +} + // Stop stops all the manager's sub-servers func (s *Manager) Stop() error { var returnErr error @@ -259,3 +288,21 @@ func dialBackend(name, dialAddr, tlsCertPath string) (*grpc.ClientConn, error) { } return cc, nil } + +// readMacaroon tries to read the macaroon file at the specified path. +func readMacaroon(macPath string) ([]byte, error) { + // Load the specified macaroon file. + macBytes, err := ioutil.ReadFile(macPath) + if err != nil { + return nil, fmt.Errorf("unable to read macaroon path: %v", err) + } + + // Make sure it actually is a macaroon by parsing it. + mac := &macaroon.Macaroon{} + if err := mac.UnmarshalBinary(macBytes); err != nil { + return nil, fmt.Errorf("unable to decode macaroon: %v", err) + } + + // It's a macaroon alright, let's return the binary data now. + return macBytes, nil +} From a3a5d636e634a6ead390e914908f6e23714c7bf0 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 2 May 2023 11:35:13 +0200 Subject: [PATCH 06/13] subservers+terminal: let subserver mgr handle REST registrations --- subservers/faraday.go | 16 ++++++++++++++++ subservers/interface.go | 8 ++++++++ subservers/loop.go | 16 ++++++++++++++++ subservers/manager.go | 28 ++++++++++++++++++++++++++++ subservers/pool.go | 16 ++++++++++++++++ terminal.go | 29 +++++------------------------ 6 files changed, 89 insertions(+), 24 deletions(-) diff --git a/subservers/faraday.go b/subservers/faraday.go index c55c7e182..d3f00292a 100644 --- a/subservers/faraday.go +++ b/subservers/faraday.go @@ -1,6 +1,9 @@ package subservers import ( + "context" + + restProxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/lightninglabs/faraday" "github.com/lightninglabs/faraday/frdrpc" "github.com/lightninglabs/faraday/frdrpcserver" @@ -76,6 +79,19 @@ func (f *faradaySubServer) RegisterGrpcService(service grpc.ServiceRegistrar) { frdrpc.RegisterFaradayServerServer(service, f) } +// RegisterRestService registers the sub-server's REST handlers with the given +// endpoint. +// +// NOTE: this is part of the SubServer interface. +func (f *faradaySubServer) RegisterRestService(ctx context.Context, + mux *restProxy.ServeMux, endpoint string, + dialOpts []grpc.DialOption) error { + + return frdrpc.RegisterFaradayServerHandlerFromEndpoint( + ctx, mux, endpoint, dialOpts, + ) +} + // ServerErrChan returns an error channel that should be listened on after // starting the sub-server to listen for any runtime errors. It is optional and // may be set to nil. This only applies in integrated mode. diff --git a/subservers/interface.go b/subservers/interface.go index e29c338fa..fe4104828 100644 --- a/subservers/interface.go +++ b/subservers/interface.go @@ -1,6 +1,9 @@ package subservers import ( + "context" + + restProxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/macaroons" @@ -36,6 +39,11 @@ type SubServer interface { // the given registrar. RegisterGrpcService(grpc.ServiceRegistrar) + // RegisterRestService registers the sub-server's REST handlers with the + // given endpoint. + RegisterRestService(context.Context, *restProxy.ServeMux, string, + []grpc.DialOption) error + // ServerErrChan returns an error channel that should be listened on // after starting the sub-server to listen for any runtime errors. It // is optional and may be set to nil. This only applies in integrated diff --git a/subservers/loop.go b/subservers/loop.go index 97de3bd03..9eab767ce 100644 --- a/subservers/loop.go +++ b/subservers/loop.go @@ -1,6 +1,9 @@ package subservers import ( + "context" + + restProxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/loopd" "github.com/lightninglabs/loop/looprpc" @@ -81,6 +84,19 @@ func (l *loopSubServer) RegisterGrpcService(registrar grpc.ServiceRegistrar) { looprpc.RegisterSwapClientServer(registrar, l) } +// RegisterRestService registers the sub-server's REST handlers with the given +// endpoint. +// +// NOTE: this is part of the SubServer interface. +func (l *loopSubServer) RegisterRestService(ctx context.Context, + mux *restProxy.ServeMux, endpoint string, + dialOpts []grpc.DialOption) error { + + return looprpc.RegisterSwapClientHandlerFromEndpoint( + ctx, mux, endpoint, dialOpts, + ) +} + // ServerErrChan returns an error channel that should be listened on after // starting the sub-server to listen for any runtime errors. It is optional and // may be set to nil. This only applies in integrated mode. diff --git a/subservers/manager.go b/subservers/manager.go index 0337cda7a..c13aeab31 100644 --- a/subservers/manager.go +++ b/subservers/manager.go @@ -7,6 +7,7 @@ import ( "sync" "time" + restProxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/lightninglabs/lightning-terminal/perms" "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/lncfg" @@ -119,6 +120,33 @@ func (s *Manager) RegisterRPCServices(server grpc.ServiceRegistrar) { } } +// RegisterRestServices registers all the manager's sub-servers REST handlers +// with the given endpoint. +func (s *Manager) RegisterRestServices(ctx context.Context, + mux *restProxy.ServeMux, endpoint string, + dialOpts []grpc.DialOption) error { + + s.mu.RLock() + defer s.mu.RUnlock() + + for _, ss := range s.servers { + // In remote mode the "director" of the RPC proxy will act as + // a catch-all for any gRPC request that isn't known because we + // didn't register any server for it. The director will then + // forward the request to the remote service. + if ss.Remote() { + continue + } + + err := ss.RegisterRestService(ctx, mux, endpoint, dialOpts) + if err != nil { + return err + } + } + + return nil +} + // GetRemoteConn checks if any of the manager's sub-servers owns the given uri // and if so, the remote connection to that sub-server is returned. The bool // return value indicates if the uri is managed by one of the sub-servers diff --git a/subservers/pool.go b/subservers/pool.go index da875f523..f98c0b99e 100644 --- a/subservers/pool.go +++ b/subservers/pool.go @@ -1,6 +1,9 @@ package subservers import ( + "context" + + restProxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/lightninglabs/lndclient" "github.com/lightninglabs/pool" "github.com/lightninglabs/pool/poolrpc" @@ -72,6 +75,19 @@ func (p *poolSubServer) RegisterGrpcService(registrar grpc.ServiceRegistrar) { poolrpc.RegisterTraderServer(registrar, p) } +// RegisterRestService registers the sub-server's REST handlers with the given +// endpoint. +// +// NOTE: this is part of the SubServer interface. +func (p *poolSubServer) RegisterRestService(ctx context.Context, + mux *restProxy.ServeMux, endpoint string, + dialOpts []grpc.DialOption) error { + + return poolrpc.RegisterTraderHandlerFromEndpoint( + ctx, mux, endpoint, dialOpts, + ) +} + // ServerErrChan returns an error channel that should be listened on after // starting the sub-server to listen for any runtime errors. It is optional and // may be set to nil. This only applies in integrated mode. diff --git a/terminal.go b/terminal.go index e5b932ddf..0225b0ab3 100644 --- a/terminal.go +++ b/terminal.go @@ -19,7 +19,6 @@ import ( restProxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/jessevdk/go-flags" - "github.com/lightninglabs/faraday/frdrpc" "github.com/lightninglabs/lightning-terminal/accounts" "github.com/lightninglabs/lightning-terminal/autopilotserver" "github.com/lightninglabs/lightning-terminal/firewall" @@ -33,9 +32,7 @@ import ( "github.com/lightninglabs/lightning-terminal/subservers" "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop" - "github.com/lightninglabs/loop/looprpc" "github.com/lightninglabs/pool" - "github.com/lightninglabs/pool/poolrpc" "github.com/lightningnetwork/lnd" "github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/chainreg" @@ -234,6 +231,10 @@ func (g *LightningTerminal) Run() error { // lnd once it's fully started. g.subServerMgr = subservers.NewManager(g.permsMgr) + // Register our sub-servers. This must be done before the REST proxy is + // set up so that the correct REST handlers are registered. + g.initSubServers() + // Construct the rpcProxy. It must be initialised before the main web // server is started. g.rpcProxy = newRpcProxy( @@ -293,10 +294,6 @@ func (g *LightningTerminal) Run() error { // up, these are considered non-fatal and will not result in an error being // returned. func (g *LightningTerminal) start() error { - // Create the instances of our subservers now so we can hook them up to - // lnd once it's fully started. - g.initSubServers() - var err error g.accountService, err = accounts.NewService( @@ -926,23 +923,7 @@ func (g *LightningTerminal) RegisterRestSubserver(ctx context.Context, return err } - err = frdrpc.RegisterFaradayServerHandlerFromEndpoint( - ctx, mux, endpoint, dialOpts, - ) - if err != nil { - return err - } - - err = looprpc.RegisterSwapClientHandlerFromEndpoint( - ctx, mux, endpoint, dialOpts, - ) - if err != nil { - return err - } - - return poolrpc.RegisterTraderHandlerFromEndpoint( - ctx, mux, endpoint, dialOpts, - ) + return g.subServerMgr.RegisterRestServices(ctx, mux, endpoint, dialOpts) } // ValidateMacaroon extracts the macaroon from the context's gRPC metadata, From 53d0d8432e99fb22773c58a87e7efe596eba6fbe Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 2 May 2023 11:43:52 +0200 Subject: [PATCH 07/13] subservers+terminal: move overwriting agent name --- subservers/loop.go | 5 +++++ subservers/pool.go | 4 ++++ terminal.go | 8 -------- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/subservers/loop.go b/subservers/loop.go index 9eab767ce..77a9440ee 100644 --- a/subservers/loop.go +++ b/subservers/loop.go @@ -5,6 +5,7 @@ import ( restProxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/loop" "github.com/lightninglabs/loop/loopd" "github.com/lightninglabs/loop/looprpc" "github.com/lightningnetwork/lnd/lnrpc" @@ -27,6 +28,10 @@ var _ SubServer = (*loopSubServer)(nil) func NewLoopSubServer(cfg *loopd.Config, remoteCfg *RemoteDaemonConfig, remote bool) SubServer { + // Overwrite the loop daemon's user agent name, so it sends "litd" + // instead of "loopd". + loop.AgentName = "litd" + return &loopSubServer{ Daemon: loopd.New(cfg, nil), cfg: cfg, diff --git a/subservers/pool.go b/subservers/pool.go index f98c0b99e..6433139f1 100644 --- a/subservers/pool.go +++ b/subservers/pool.go @@ -27,6 +27,10 @@ var _ SubServer = (*poolSubServer)(nil) func NewPoolSubServer(cfg *pool.Config, remoteCfg *RemoteDaemonConfig, remote bool) SubServer { + // Overwrite the pool daemon's user agent name, so it sends "litd" + // instead of and "poold". + pool.SetAgentName("litd") + return &poolSubServer{ Server: pool.NewServer(cfg), cfg: cfg, diff --git a/terminal.go b/terminal.go index 0225b0ab3..37eea4aca 100644 --- a/terminal.go +++ b/terminal.go @@ -31,8 +31,6 @@ import ( "github.com/lightninglabs/lightning-terminal/session" "github.com/lightninglabs/lightning-terminal/subservers" "github.com/lightninglabs/lndclient" - "github.com/lightninglabs/loop" - "github.com/lightninglabs/pool" "github.com/lightningnetwork/lnd" "github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/chainreg" @@ -1413,16 +1411,10 @@ func (g *LightningTerminal) initSubServers() { g.cfg.faradayRemote, )) - // Overwrite the loop daemon's user agent name so it sends "litd" - // instead of "loopd". - loop.AgentName = "litd" g.subServerMgr.AddServer(subservers.NewLoopSubServer( g.cfg.Loop, g.cfg.Remote.Loop, g.cfg.loopRemote, )) - // Overwrite the pool daemon's user agent name so it sends "litd" - // instead of and "poold". - pool.SetAgentName("litd") g.subServerMgr.AddServer(subservers.NewPoolSubServer( g.cfg.Pool, g.cfg.Remote.Pool, g.cfg.poolRemote, )) From ab8c022a5b3e60587ea60aa4675cee8e0dc6a94c Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 2 May 2023 08:52:28 +0200 Subject: [PATCH 08/13] subservers: add Permissions method to the interface --- subservers/faraday.go | 10 ++++++++++ subservers/interface.go | 5 +++++ subservers/loop.go | 10 ++++++++++ subservers/pool.go | 10 ++++++++++ 4 files changed, 35 insertions(+) diff --git a/subservers/faraday.go b/subservers/faraday.go index d3f00292a..798348b62 100644 --- a/subservers/faraday.go +++ b/subservers/faraday.go @@ -7,9 +7,11 @@ import ( "github.com/lightninglabs/faraday" "github.com/lightninglabs/faraday/frdrpc" "github.com/lightninglabs/faraday/frdrpcserver" + "github.com/lightninglabs/faraday/frdrpcserver/perms" "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/lnrpc" "google.golang.org/grpc" + "gopkg.in/macaroon-bakery.v2/bakery" ) // faradaySubServer implements the SubServer interface. @@ -108,3 +110,11 @@ func (f *faradaySubServer) ServerErrChan() chan error { func (f *faradaySubServer) MacPath() string { return f.cfg.MacaroonPath } + +// Permissions returns a map of all RPC methods and their required macaroon +// permissions to access the sub-server. +// +// NOTE: this is part of the SubServer interface. +func (f *faradaySubServer) Permissions() map[string][]bakery.Op { + return perms.RequiredPermissions +} diff --git a/subservers/interface.go b/subservers/interface.go index fe4104828..7064879ad 100644 --- a/subservers/interface.go +++ b/subservers/interface.go @@ -8,6 +8,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/macaroons" "google.golang.org/grpc" + "gopkg.in/macaroon-bakery.v2/bakery" ) // SubServer defines an interface that should be implemented by any sub-server @@ -53,4 +54,8 @@ type SubServer interface { // MacPath returns the path to the sub-server's macaroon if it is not // running in remote mode. MacPath() string + + // Permissions returns a map of all RPC methods and their required + // macaroon permissions to access the sub-server. + Permissions() map[string][]bakery.Op } diff --git a/subservers/loop.go b/subservers/loop.go index 77a9440ee..d6ae31deb 100644 --- a/subservers/loop.go +++ b/subservers/loop.go @@ -7,9 +7,11 @@ import ( "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop" "github.com/lightninglabs/loop/loopd" + "github.com/lightninglabs/loop/loopd/perms" "github.com/lightninglabs/loop/looprpc" "github.com/lightningnetwork/lnd/lnrpc" "google.golang.org/grpc" + "gopkg.in/macaroon-bakery.v2/bakery" ) // loopSubServer implements the SubServer interface. @@ -118,3 +120,11 @@ func (l *loopSubServer) ServerErrChan() chan error { func (l *loopSubServer) MacPath() string { return l.cfg.MacaroonPath } + +// Permissions returns a map of all RPC methods and their required macaroon +// permissions to access the sub-server. +// +// NOTE: this is part of the SubServer interface. +func (l *loopSubServer) Permissions() map[string][]bakery.Op { + return perms.RequiredPermissions +} diff --git a/subservers/pool.go b/subservers/pool.go index 6433139f1..8ddeeaa37 100644 --- a/subservers/pool.go +++ b/subservers/pool.go @@ -6,9 +6,11 @@ import ( restProxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/lightninglabs/lndclient" "github.com/lightninglabs/pool" + "github.com/lightninglabs/pool/perms" "github.com/lightninglabs/pool/poolrpc" "github.com/lightningnetwork/lnd/lnrpc" "google.golang.org/grpc" + "gopkg.in/macaroon-bakery.v2/bakery" ) // poolSubServer implements the SubServer interface. @@ -108,3 +110,11 @@ func (p *poolSubServer) ServerErrChan() chan error { func (p *poolSubServer) MacPath() string { return p.cfg.MacaroonPath } + +// Permissions returns a map of all RPC methods and their required macaroon +// permissions to access the sub-server. +// +// NOTE: this is part of the SubServer interface. +func (p *poolSubServer) Permissions() map[string][]bakery.Op { + return perms.RequiredPermissions +} From 06bb73f62d457105d9890247f32fbf03d2f5039d Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 2 May 2023 08:57:35 +0200 Subject: [PATCH 09/13] perms+subservers: let subserver register perms --- itest/litd_mode_integrated_test.go | 10 ++++ perms/manager.go | 76 ++++++++++-------------------- subservers/manager.go | 2 + 3 files changed, 37 insertions(+), 51 deletions(-) diff --git a/itest/litd_mode_integrated_test.go b/itest/litd_mode_integrated_test.go index 493d748ea..6d007bec4 100644 --- a/itest/litd_mode_integrated_test.go +++ b/itest/litd_mode_integrated_test.go @@ -18,12 +18,16 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/lightninglabs/faraday/frdrpc" + faraday "github.com/lightninglabs/faraday/frdrpcserver/perms" "github.com/lightninglabs/lightning-node-connect/mailbox" terminal "github.com/lightninglabs/lightning-terminal" "github.com/lightninglabs/lightning-terminal/litrpc" "github.com/lightninglabs/lightning-terminal/perms" "github.com/lightninglabs/lightning-terminal/session" + "github.com/lightninglabs/lightning-terminal/subservers" + loop "github.com/lightninglabs/loop/loopd/perms" "github.com/lightninglabs/loop/looprpc" + pool "github.com/lightninglabs/pool/perms" "github.com/lightninglabs/pool/poolrpc" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnrpc" @@ -1090,6 +1094,12 @@ func bakeSuperMacaroon(cfg *LitNodeConfig, readOnly bool) (string, error) { return "", err } + permsMgr.RegisterSubServer(subservers.LOOP, loop.RequiredPermissions) + permsMgr.RegisterSubServer(subservers.POOL, pool.RequiredPermissions) + permsMgr.RegisterSubServer( + subservers.FARADAY, faraday.RequiredPermissions, + ) + superMacPermissions := permsMgr.ActivePermissions(readOnly) nullID := [4]byte{} superMacHex, err := terminal.BakeSuperMacaroon( diff --git a/perms/manager.go b/perms/manager.go index d5459b801..34adf6cf1 100644 --- a/perms/manager.go +++ b/perms/manager.go @@ -1,25 +1,18 @@ package perms import ( - "fmt" "regexp" "strings" "sync" - faraday "github.com/lightninglabs/faraday/frdrpcserver/perms" - loop "github.com/lightninglabs/loop/loopd/perms" - pool "github.com/lightninglabs/pool/perms" "github.com/lightningnetwork/lnd" "github.com/lightningnetwork/lnd/lnrpc" "gopkg.in/macaroon-bakery.v2/bakery" ) const ( - poolPerms string = "pool" - loopPerms string = "loop" - faradayPerms string = "faraday" - litPerms string = "lit" - lndPerms string = "lnd" + litPerms string = "lit" + lndPerms string = "lnd" ) // Manager manages the permission lists that Lit requires. @@ -54,9 +47,6 @@ type Manager struct { // was compiled with and then only the corresponding permissions will be added. func NewManager(withAllSubServers bool) (*Manager, error) { permissions := make(map[string]map[string][]bakery.Op) - permissions[faradayPerms] = faraday.RequiredPermissions - permissions[loopPerms] = loop.RequiredPermissions - permissions[poolPerms] = pool.RequiredPermissions permissions[litPerms] = RequiredPermissions permissions[lndPerms] = lnd.MainRPCServerPermissions() for k, v := range whiteListedLNDMethods { @@ -106,6 +96,21 @@ func NewManager(withAllSubServers bool) (*Manager, error) { }, nil } +// RegisterSubServer adds the permissions of a given sub-server to the set +// managed by the Manager. +func (pm *Manager) RegisterSubServer(name string, + permissions map[string][]bakery.Op) { + + pm.permsMu.Lock() + defer pm.permsMu.Unlock() + + pm.fixedPerms[name] = permissions + + for uri, ops := range permissions { + pm.perms[uri] = ops + } +} + // OnLNDBuildTags should be called once a list of LND build tags has been // obtained. It then uses those build tags to decide which of the LND sub-server // permissions to add to the main permissions list. This method should only @@ -225,50 +230,19 @@ func (pm *Manager) ActivePermissions(readOnly bool) []bakery.Op { // _except_ for any LND permissions. In other words, this returns permissions // for which the external validator of Lit is responsible. func (pm *Manager) GetLitPerms() map[string][]bakery.Op { - mapSize := len(pm.fixedPerms[litPerms]) + - len(pm.fixedPerms[faradayPerms]) + - len(pm.fixedPerms[loopPerms]) + len(pm.fixedPerms[poolPerms]) + result := make(map[string][]bakery.Op) + for subserver, ops := range pm.fixedPerms { + if subserver == lndPerms { + continue + } - result := make(map[string][]bakery.Op, mapSize) - for key, value := range pm.fixedPerms[faradayPerms] { - result[key] = value - } - for key, value := range pm.fixedPerms[loopPerms] { - result[key] = value - } - for key, value := range pm.fixedPerms[poolPerms] { - result[key] = value - } - for key, value := range pm.fixedPerms[litPerms] { - result[key] = value + for key, value := range ops { + result[key] = value + } } return result } -// SubServerHandler returns the name of the subserver that should handle the -// given URI. -func (pm *Manager) SubServerHandler(uri string) (string, error) { - switch { - case pm.IsSubServerURI(lndPerms, uri): - return lndPerms, nil - - case pm.IsSubServerURI(faradayPerms, uri): - return faradayPerms, nil - - case pm.IsSubServerURI(loopPerms, uri): - return loopPerms, nil - - case pm.IsSubServerURI(poolPerms, uri): - return poolPerms, nil - - case pm.IsSubServerURI(litPerms, uri): - return litPerms, nil - - default: - return "", fmt.Errorf("unknown gRPC web request: %v", uri) - } -} - // IsSubServerURI if the given URI belongs to the RPC of the given server. func (pm *Manager) IsSubServerURI(name string, uri string) bool { if name == lndPerms { diff --git a/subservers/manager.go b/subservers/manager.go index c13aeab31..6349439a2 100644 --- a/subservers/manager.go +++ b/subservers/manager.go @@ -53,6 +53,8 @@ func (s *Manager) AddServer(ss SubServer) { SubServer: ss, quit: make(chan struct{}), }) + + s.permsMgr.RegisterSubServer(ss.Name(), ss.Permissions()) } // StartIntegratedServers starts all the manager's sub-servers that should be From daab70c0407f6f73d50d3b79e7b0b0283172dbaf Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 2 May 2023 12:21:46 +0200 Subject: [PATCH 10/13] config: register subserver loggers before validation Ensure that all of Lit's subserver loggers have been registered _before_ the config is validated. This will allow users to use the `--lnd.debuglevel` flag to set Lit specific subserever logger levels. --- config.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/config.go b/config.go index da793b5b4..9f6087c2d 100644 --- a/config.go +++ b/config.go @@ -328,6 +328,12 @@ func loadAndValidateConfig(interceptor signal.Interceptor) (*Config, error) { os.Exit(0) } + // Before we validate the config, we first hook up our own loggers. + // This must be done before the config is validated if LND is running + // in integrated mode so that the log levels for various non-LND related + // subsystems can be set via the `lnd.debuglevel` flag. + SetupLoggers(preCfg.Lnd.LogWriter, interceptor) + // Load the main configuration file and parse any command line options. // This function will also set up logging properly. cfg, err := loadConfigFile(preCfg, interceptor) @@ -335,10 +341,6 @@ func loadAndValidateConfig(interceptor signal.Interceptor) (*Config, error) { return nil, err } - // With the validated config obtained, we now know that the root logging - // system of lnd is initialized and we can hook up our own loggers now. - SetupLoggers(cfg.Lnd.LogWriter, interceptor) - // Translate the more user friendly string modes into the more developer // friendly internal bool variables now. cfg.lndRemote = cfg.LndMode == ModeRemote From 2a825f1009bef62e279a57a5965be2a8fa7d5c1d Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 3 May 2023 08:04:28 +0200 Subject: [PATCH 11/13] multi: return error if connecting to remote sub-server fails --- subservers/manager.go | 8 ++++---- terminal.go | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/subservers/manager.go b/subservers/manager.go index 6349439a2..3fcda9119 100644 --- a/subservers/manager.go +++ b/subservers/manager.go @@ -84,7 +84,7 @@ func (s *Manager) StartIntegratedServers(lndClient lnrpc.LightningClient, // ConnectRemoteSubServers creates connections to all the manager's sub-servers // that are running remotely. -func (s *Manager) ConnectRemoteSubServers() { +func (s *Manager) ConnectRemoteSubServers() error { s.mu.Lock() defer s.mu.Unlock() @@ -95,12 +95,12 @@ func (s *Manager) ConnectRemoteSubServers() { err := ss.connectRemote() if err != nil { - log.Errorf("Failed to connect to remote %s: %v", + return fmt.Errorf("failed to connect to remote %s: %v", ss.Name(), err) - - continue } } + + return nil } // RegisterRPCServices registers all the manager's sub-servers with the given diff --git a/terminal.go b/terminal.go index 37eea4aca..60adcacb2 100644 --- a/terminal.go +++ b/terminal.go @@ -491,7 +491,10 @@ func (g *LightningTerminal) start() error { // Initialise any connections to sub-servers that we are running in // remote mode. - g.subServerMgr.ConnectRemoteSubServers() + if err := g.subServerMgr.ConnectRemoteSubServers(); err != nil { + return fmt.Errorf("error connecting to remote sub-servers: %v", + err) + } // Now start the RPC proxy that will handle all incoming gRPC, grpc-web // and REST requests. From ba3193b341abdee8a2b6d74cd1e7610ff70f96aa Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 3 May 2023 10:26:11 +0200 Subject: [PATCH 12/13] rpc_proxy+subservers: error out of GetRemoteConn if not ready Due to the rpcProxy being started early now, it could be the case that a call is made to `GetRemoteConn` before the remote connection has actually been set up. This commit catches this case so that an error can be returned and a panic avoided. --- rpc_proxy.go | 7 ++++++- subservers/manager.go | 13 +++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/rpc_proxy.go b/rpc_proxy.go index d60ed1343..25eb3c0bd 100644 --- a/rpc_proxy.go +++ b/rpc_proxy.go @@ -304,7 +304,12 @@ func (p *rpcProxy) makeDirector(allowLitRPC bool) func(ctx context.Context, // since it must either be an lnd call or something that'll be // handled by the integrated daemons that are hooking into lnd's // gRPC server. - handled, conn := p.subServerMgr.GetRemoteConn(requestURI) + handled, conn, err := p.subServerMgr.GetRemoteConn(requestURI) + if err != nil { + return outCtx, nil, status.Errorf( + codes.Unavailable, err.Error(), + ) + } if handled { return outCtx, conn, nil } diff --git a/subservers/manager.go b/subservers/manager.go index 3fcda9119..78da300a1 100644 --- a/subservers/manager.go +++ b/subservers/manager.go @@ -153,7 +153,7 @@ func (s *Manager) RegisterRestServices(ctx context.Context, // and if so, the remote connection to that sub-server is returned. The bool // return value indicates if the uri is managed by one of the sub-servers // running in remote mode. -func (s *Manager) GetRemoteConn(uri string) (bool, *grpc.ClientConn) { +func (s *Manager) GetRemoteConn(uri string) (bool, *grpc.ClientConn, error) { s.mu.RLock() defer s.mu.RUnlock() @@ -163,13 +163,18 @@ func (s *Manager) GetRemoteConn(uri string) (bool, *grpc.ClientConn) { } if !ss.Remote() { - return false, nil + return false, nil, nil + } + + if ss.remoteConn == nil { + return true, nil, fmt.Errorf("not yet connected to "+ + "remote sub-server(%s)", ss.Name()) } - return true, ss.remoteConn + return true, ss.remoteConn, nil } - return false, nil + return false, nil, nil } // ValidateMacaroon checks if any of the manager's sub-servers owns the given From f725c3a9597a73a9f644ae700cf1a9123ec2f278 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 3 May 2023 11:01:17 +0200 Subject: [PATCH 13/13] rpc_proxy: check `hasStarted` before using lnd connection In the rpcProxy `makeDirector` method, we need to check if the rpcProxy has started before making use of the `lndConn`. --- rpc_proxy.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rpc_proxy.go b/rpc_proxy.go index 25eb3c0bd..54f35b32c 100644 --- a/rpc_proxy.go +++ b/rpc_proxy.go @@ -323,6 +323,12 @@ func (p *rpcProxy) makeDirector(allowLitRPC bool) func(ctx context.Context, ) } + // If the rpcProxy has not started yet, then the lnd connection + // will not be initialised yet. + if !p.hasStarted() { + return outCtx, nil, ErrWaitingToStart + } + return outCtx, p.lndConn, nil } }