diff --git a/lnd.go b/lnd.go index 60e3d962..f1aedc5b 100644 --- a/lnd.go +++ b/lnd.go @@ -45,6 +45,8 @@ import ( "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/macaroons" + "github.com/lightningnetwork/lnd/monitoring" + "github.com/lightningnetwork/lnd/rpcperms" "github.com/lightningnetwork/lnd/signal" "github.com/lightningnetwork/lnd/tor" "github.com/lightningnetwork/lnd/walletunlocker" @@ -162,14 +164,6 @@ type ListenerWithSignal struct { // Ready will be closed by the server listening on Listener. Ready chan struct{} - - // ExternalRPCSubserverCfg is optional and specifies the registration - // callback and permissions to register external gRPC subservers. - ExternalRPCSubserverCfg *RPCSubserverConfig - - // ExternalRestRegistrar is optional and specifies the registration - // callback to register external REST subservers. - ExternalRestRegistrar RestRegistrar } // ListenerCfg is a wrapper around custom listeners that can be passed to lnd @@ -182,14 +176,15 @@ type ListenerCfg struct { // RPCListener can be set to the listener to use for the RPC server. If // nil a regular network listener will be created. RPCListener *ListenerWithSignal -} -// rpcListeners is a function type used for closures that fetches a set of RPC -// listeners for the current configuration. If no custom listeners are present, -// this should return normal listeners from the RPC endpoints defined in the -// config. The second return value us a closure that will close the fetched -// listeners. -type rpcListeners func() ([]*ListenerWithSignal, func(), error) + // ExternalRPCSubserverCfg is optional and specifies the registration + // callback and permissions to register external gRPC subservers. + ExternalRPCSubserverCfg *RPCSubserverConfig + + // ExternalRestRegistrar is optional and specifies the registration + // callback to register external REST subservers. + ExternalRestRegistrar RestRegistrar +} // Main is the true entry point for lnd. It accepts a fully populated and // validated main configuration struct and an optional listener config struct. @@ -279,22 +274,6 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { defer cleanUp() - // We use the first RPC listener as the destination for our REST proxy. - // If the listener is set to listen on all interfaces, we replace it - // with localhost, as we cannot dial it directly. - restProxyDest := cfg.RPCListeners[0].String() - switch { - case strings.Contains(restProxyDest, "0.0.0.0"): - restProxyDest = strings.Replace( - restProxyDest, "0.0.0.0", "127.0.0.1", 1, - ) - - case strings.Contains(restProxyDest, "[::]"): - restProxyDest = strings.Replace( - restProxyDest, "[::]", "[::1]", 1, - ) - } - // Before starting the wallet, we'll create and start our Neutrino // light client instance, if enabled, in order to allow it to sync // while the rest of the daemon continues startup. @@ -319,7 +298,6 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { var ( walletInitParams WalletUnlockParams - shutdownUnlocker = func() {} privateWalletPw = lnwallet.DefaultPrivatePassphrase publicWalletPw = lnwallet.DefaultPublicPassphrase ) @@ -329,11 +307,14 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { // this information. walletInitParams.Birthday = time.Now() - // getListeners is a closure that creates listeners from the - // RPCListeners defined in the config. It also returns a cleanup - // closure and the server options to use for the GRPC server. - getListeners := func() ([]*ListenerWithSignal, func(), error) { - var grpcListeners []*ListenerWithSignal + // If we have chosen to start with a dedicated listener for the + // rpc server, we set it directly. + var grpcListeners []*ListenerWithSignal + if lisCfg.RPCListener != nil { + grpcListeners = []*ListenerWithSignal{lisCfg.RPCListener} + } else { + // Otherwise we create listeners from the RPCListeners defined + // in the config. for _, grpcEndpoint := range cfg.RPCListeners { // Start a gRPC server listening for HTTP/2 // connections. @@ -341,48 +322,76 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { if err != nil { ltndLog.Errorf("unable to listen on %s", grpcEndpoint) - return nil, nil, err + return err } + defer lis.Close() + grpcListeners = append( grpcListeners, &ListenerWithSignal{ Listener: lis, Ready: make(chan struct{}), }) } - - cleanup := func() { - for _, lis := range grpcListeners { - lis.Close() - } - } - return grpcListeners, cleanup, nil } - // walletUnlockerListeners is a closure we'll hand to the wallet - // unlocker, that will be called when it needs listeners for its GPRC - // server. - walletUnlockerListeners := func() ([]*ListenerWithSignal, func(), - error) { - - // If we have chosen to start with a dedicated listener for the - // wallet unlocker, we return it directly. - if lisCfg.WalletUnlocker != nil { - return []*ListenerWithSignal{lisCfg.WalletUnlocker}, - func() {}, nil - } - - // Otherwise we'll return the regular listeners. - return getListeners() + // We'll create the WalletUnlockerService and check whether the wallet + // already exists. + pwService := createWalletUnlockerService(cfg) + walletExists, err := pwService.WalletExists() + if err != nil { + return err } + // Create a new RPC interceptor that we'll add to the GRPC server. This + // will be used to log the API calls invoked on the GRPC server. + interceptorChain := rpcperms.NewInterceptorChain( + rpcsLog, cfg.NoMacaroons, walletExists, + ) + rpcServerOpts := interceptorChain.CreateServerOpts() + serverOpts = append(serverOpts, rpcServerOpts...) + + grpcServer := grpc.NewServer(serverOpts...) + defer grpcServer.Stop() + + // Register the WalletUnlockerService with the GRPC server. + lnrpc.RegisterWalletUnlockerServer(grpcServer, pwService) + + // Initialize, and register our implementation of the gRPC interface + // exported by the rpcServer. + rpcServer := newRPCServer( + cfg, interceptorChain, lisCfg.ExternalRPCSubserverCfg, + lisCfg.ExternalRestRegistrar, + ) + + err = rpcServer.RegisterWithGrpcServer(grpcServer) + if err != nil { + return err + } + + // Now that both the WalletUnlocker and LightningService have been + // registered with the GRPC server, we can start listening. + err = startGrpcListen(cfg, grpcServer, grpcListeners) + if err != nil { + return err + } + + // Now start the REST proxy for our gRPC server above. We'll ensure + // we direct LND to connect to its loopback address rather than a + // wildcard to prevent certificate issues when accessing the proxy + // externally. + stopProxy, err := startRestProxy( + cfg, rpcServer, restDialOpts, restListen, + ) + if err != nil { + return err + } + defer stopProxy() + // We wait until the user provides a password over RPC. In case lnd is // started with the --noseedbackup flag, we use the default password // for wallet encryption. if !cfg.NoSeedBackup { - params, shutdown, err := waitForWalletPassword( - cfg, cfg.RESTListeners, serverOpts, restDialOpts, - restProxyDest, restListen, walletUnlockerListeners, - ) + params, err := waitForWalletPassword(cfg, pwService) if err != nil { err := fmt.Errorf("unable to set up wallet password "+ "listeners: %v", err) @@ -391,7 +400,6 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { } walletInitParams = *params - shutdownUnlocker = shutdown privateWalletPw = walletInitParams.Password publicWalletPw = walletInitParams.Password defer func() { @@ -407,6 +415,10 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { } } + // Now that the wallet password has been provided, transition the RPC + // state into Unlocked. + interceptorChain.SetWalletUnlocked() + var macaroonService *macaroons.Service if !cfg.NoMacaroons { // Create the macaroon authentication/authorization service. @@ -492,11 +504,12 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { ltndLog.Warnf(msg, "invoice", cfg.InvoiceMacPath) } } - } - // Now we're definitely done with the unlocker, shut it down so we can - // start the main RPC service later. - shutdownUnlocker() + // We add the macaroon service to our RPC interceptor. This + // will start checking macaroons against permissions on every + // RPC invocation. + interceptorChain.AddMacaroonService(macaroonService) + } // With the information parsed from the configuration, create valid // instances of the pertinent interfaces required to operate the @@ -717,29 +730,14 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { } defer atplManager.Stop() - // rpcListeners is a closure we'll hand to the rpc server, that will be - // called when it needs listeners for its GPRC server. - rpcListeners := func() ([]*ListenerWithSignal, func(), error) { - // If we have chosen to start with a dedicated listener for the - // rpc server, we return it directly. - if lisCfg.RPCListener != nil { - return []*ListenerWithSignal{lisCfg.RPCListener}, - func() {}, nil - } - - // Otherwise we'll return the regular listeners. - return getListeners() - } - - // Initialize, and register our implementation of the gRPC interface - // exported by the rpcServer. - rpcServer, err := newRPCServer( - cfg, server, macaroonService, cfg.SubRPCServers, serverOpts, - restDialOpts, restProxyDest, atplManager, server.invoices, - tower, restListen, rpcListeners, chainedAcceptor, + // Now we have created all dependencies necessary to populate and + // start the RPC server. + err = rpcServer.addDeps( + server, macaroonService, cfg.SubRPCServers, atplManager, + server.invoices, tower, chainedAcceptor, ) if err != nil { - err := fmt.Errorf("unable to create RPC server: %v", err) + err := fmt.Errorf("unable to add deps to RPC server: %v", err) ltndLog.Error(err) return err } @@ -750,6 +748,9 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error { } defer rpcServer.Stop() + // We transition the RPC state to Active, as the RPC server is up. + interceptorChain.SetRPCActive() + // If we're not in regtest or simnet mode, We'll wait until we're fully // synced to continue the start up of the remainder of the daemon. This // ensures that we don't accept any possibly invalid state transitions, or @@ -1124,14 +1125,9 @@ type WalletUnlockParams struct { MacResponseChan chan []byte } -// waitForWalletPassword will spin up gRPC and REST endpoints for the -// WalletUnlocker server, and block until a password is provided by -// the user to this RPC server. -func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, - serverOpts []grpc.ServerOption, restDialOpts []grpc.DialOption, - restProxyDest string, restListen func(net.Addr) (net.Listener, error), - getListeners rpcListeners) (*WalletUnlockParams, func(), error) { - +// createWalletUnlockerService creates a WalletUnlockerService from the passed +// config. +func createWalletUnlockerService(cfg *Config) *walletunlocker.UnlockerService { chainConfig := cfg.Bitcoin if cfg.registeredChains.PrimaryChain() == chainreg.LitecoinChain { chainConfig = cfg.Litecoin @@ -1143,36 +1139,16 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, macaroonFiles := []string{ cfg.AdminMacPath, cfg.ReadMacPath, cfg.InvoiceMacPath, } - pwService := walletunlocker.New( + return walletunlocker.New( chainConfig.ChainDir, cfg.ActiveNetParams.Params, !cfg.SyncFreelist, macaroonFiles, cfg.DB.Bolt.DBTimeout, cfg.ResetWalletTransactions, ) +} - // Set up a new PasswordService, which will listen for passwords - // provided over RPC. - grpcServer := grpc.NewServer(serverOpts...) - lnrpc.RegisterWalletUnlockerServer(grpcServer, pwService) - - var shutdownFuncs []func() - shutdown := func() { - // Make sure nothing blocks on reading on the macaroon channel, - // otherwise the GracefulStop below will never return. - close(pwService.MacResponseChan) - - 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, shutdown, err - } - shutdownFuncs = append(shutdownFuncs, cleanup) +// startGrpcListen starts the GRPC server on the passed listeners. +func startGrpcListen(cfg *Config, grpcServer *grpc.Server, + listeners []*ListenerWithSignal) error { // 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. @@ -1181,8 +1157,7 @@ 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", - lis.Addr()) + rpcsLog.Infof("RPC server listening on %s", lis.Addr()) // Close the ready chan to indicate we are listening. close(lis.Ready) @@ -1192,29 +1167,102 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, }(lis) } - // Start a REST proxy for our gRPC server above. + // If Prometheus monitoring is enabled, start the Prometheus exporter. + if cfg.Prometheus.Enabled() { + err := monitoring.ExportPrometheusMetrics( + grpcServer, cfg.Prometheus, + ) + if err != nil { + return err + } + } + + // Wait for gRPC servers to be up running. + wg.Wait() + + return nil +} + +// startRestProxy starts the given REST proxy on the listeners found in the +// config. +func startRestProxy(cfg *Config, rpcServer *rpcServer, restDialOpts []grpc.DialOption, + restListen func(net.Addr) (net.Listener, error)) (func(), error) { + + // We use the first RPC listener as the destination for our REST proxy. + // If the listener is set to listen on all interfaces, we replace it + // with localhost, as we cannot dial it directly. + restProxyDest := cfg.RPCListeners[0].String() + switch { + case strings.Contains(restProxyDest, "0.0.0.0"): + restProxyDest = strings.Replace( + restProxyDest, "0.0.0.0", "127.0.0.1", 1, + ) + + case strings.Contains(restProxyDest, "[::]"): + restProxyDest = strings.Replace( + restProxyDest, "[::]", "[::1]", 1, + ) + } + + var shutdownFuncs []func() + shutdown := func() { + for _, shutdownFn := range shutdownFuncs { + shutdownFn() + } + } + + // Start a REST proxy for our gRPC server. ctx := context.Background() ctx, cancel := context.WithCancel(ctx) shutdownFuncs = append(shutdownFuncs, cancel) - mux := proxy.NewServeMux() + // We'll set up a proxy that will forward REST calls to the GRPC + // server. + // + // The default JSON marshaler of the REST proxy only sets OrigName to + // true, which instructs it to use the same field names as specified in + // the proto file and not switch to camel case. What we also want is + // that the marshaler prints all values, even if they are falsey. + customMarshalerOption := proxy.WithMarshalerOption( + proxy.MIMEWildcard, &proxy.JSONPb{ + OrigName: true, + EmitDefaults: true, + }, + ) + mux := proxy.NewServeMux(customMarshalerOption) - err = lnrpc.RegisterWalletUnlockerHandlerFromEndpoint( + // Register both services with the REST proxy. + err := lnrpc.RegisterWalletUnlockerHandlerFromEndpoint( ctx, mux, restProxyDest, restDialOpts, ) if err != nil { - return nil, shutdown, err + return nil, err } - srv := &http.Server{Handler: allowCORS(mux, cfg.RestCORS)} + err = rpcServer.RegisterWithRestProxy( + ctx, mux, restDialOpts, restProxyDest, + ) + if err != nil { + return nil, err + } - for _, restEndpoint := range restEndpoints { + // Wrap the default grpc-gateway handler with the WebSocket handler. + restHandler := lnrpc.NewWebSocketProxy(mux, rpcsLog) + + // 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. + var wg sync.WaitGroup + + // Now spin up a network listener for each requested port and start a + // goroutine that serves REST with the created mux there. + for _, restEndpoint := range cfg.RESTListeners { lis, err := restListen(restEndpoint) if err != nil { - ltndLog.Errorf("Password gRPC proxy unable to listen "+ - "on %s", restEndpoint) - return nil, shutdown, err + ltndLog.Errorf("gRPC proxy unable to listen on %s", + restEndpoint) + return nil, err } + shutdownFuncs = append(shutdownFuncs, func() { err := lis.Close() if err != nil { @@ -1225,16 +1273,38 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, wg.Add(1) go func() { - rpcsLog.Infof("Password gRPC proxy started at %s", - lis.Addr()) + rpcsLog.Infof("gRPC proxy started at %s", lis.Addr()) + + // Create our proxy chain now. A request will pass + // through the following chain: + // req ---> CORS handler --> WS proxy ---> + // REST proxy --> gRPC endpoint + corsHandler := allowCORS(restHandler, cfg.RestCORS) + wg.Done() - _ = srv.Serve(lis) + err := http.Serve(lis, corsHandler) + if err != nil && !lnrpc.IsClosedConnError(err) { + rpcsLog.Error(err) + } }() } - // Wait for gRPC and REST servers to be up running. + // Wait for REST servers to be up running. wg.Wait() + return shutdown, nil +} + +// waitForWalletPassword blocks until a password is provided by the user to +// this RPC server. +func waitForWalletPassword(cfg *Config, + pwService *walletunlocker.UnlockerService) (*WalletUnlockParams, error) { + + chainConfig := cfg.Bitcoin + if cfg.registeredChains.PrimaryChain() == chainreg.LitecoinChain { + chainConfig = cfg.Litecoin + } + // Wait for user to provide the password. ltndLog.Infof("Waiting for wallet encryption password. Use `lncli " + "create` to create a wallet, `lncli unlock` to unlock an " + @@ -1259,7 +1329,7 @@ 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, shutdown, fmt.Errorf("invalid internal "+ + return nil, fmt.Errorf("invalid internal "+ "seed version %v, current version is %v", cipherSeed.InternalVersion, keychain.KeyDerivationVersion) @@ -1286,7 +1356,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, ltndLog.Errorf("Could not unload new "+ "wallet: %v", err) } - return nil, shutdown, err + return nil, err } // For new wallets, the ResetWalletTransactions flag is a no-op. @@ -1304,7 +1374,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, UnloadWallet: loader.UnloadWallet, StatelessInit: initMsg.StatelessInit, MacResponseChan: pwService.MacResponseChan, - }, shutdown, nil + }, nil // The wallet has already been created in the past, and is simply being // unlocked. So we'll just return these passphrases. @@ -1328,10 +1398,10 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, UnloadWallet: unlockMsg.UnloadWallet, StatelessInit: unlockMsg.StatelessInit, MacResponseChan: pwService.MacResponseChan, - }, shutdown, nil + }, nil case <-signal.ShutdownChannel(): - return nil, shutdown, fmt.Errorf("shutting down") + return nil, fmt.Errorf("shutting down") } } diff --git a/lnrpc/autopilotrpc/autopilot_server.go b/lnrpc/autopilotrpc/autopilot_server.go index fd9649ad..b3ece6cb 100644 --- a/lnrpc/autopilotrpc/autopilot_server.go +++ b/lnrpc/autopilotrpc/autopilot_server.go @@ -51,6 +51,13 @@ var ( } ) +// ServerShell is a shell struct holding a reference to the actual sub-server. +// It is used to register the gRPC sub-server with the root server before we +// have the necessary dependencies to populate the actual sub-server. +type ServerShell struct { + AutopilotServer +} + // Server is a sub-server of the main RPC server: the autopilot RPC. This sub // RPC server allows external callers to access the status of the autopilot // currently active within lnd, as well as configuring it at runtime. @@ -118,11 +125,11 @@ func (s *Server) Name() string { // is called, each sub-server won't be able to have // requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error { // We make sure that we register it with the main gRPC server to ensure // all our methods are routed properly. - RegisterAutopilotServer(grpcServer, s) + RegisterAutopilotServer(grpcServer, r) log.Debugf("Autopilot RPC server successfully register with root " + "gRPC server") @@ -134,8 +141,8 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { // RPC server to register itself with the main REST mux server. Until this is // called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (s *Server) RegisterWithRestServer(ctx context.Context, +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRestServer(ctx context.Context, mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { // We make sure that we register it with the main REST server to ensure @@ -152,6 +159,25 @@ func (s *Server) RegisterWithRestServer(ctx context.Context, return nil } +// CreateSubServer populates the subserver's dependencies using the passed +// SubServerConfigDispatcher. This method should fully initialize the +// sub-server instance, making it ready for action. It returns the macaroon +// permissions that the sub-server wishes to pass on to the root server for all +// methods routed towards it. +// +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( + lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + + subServer, macPermissions, err := createNewSubServer(configRegistry) + if err != nil { + return nil, nil, err + } + + r.AutopilotServer = subServer + return subServer, macPermissions, nil +} + // Status returns the current status of the autopilot agent. // // NOTE: Part of the AutopilotServer interface. diff --git a/lnrpc/autopilotrpc/driver.go b/lnrpc/autopilotrpc/driver.go index 7615e242..8f849c3e 100644 --- a/lnrpc/autopilotrpc/driver.go +++ b/lnrpc/autopilotrpc/driver.go @@ -13,7 +13,7 @@ import ( // that is meant for us in the config dispatcher, then we'll exit with an // error. func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( - lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + *Server, lnrpc.MacaroonPerms, error) { // We'll attempt to look up the config that we expect, according to our // subServerName name. If we can't find this, then we'll exit with an @@ -48,9 +48,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( func init() { subServer := &lnrpc.SubServerDriver{ SubServerName: subServerName, - New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, - lnrpc.MacaroonPerms, error) { - return createNewSubServer(c) + NewGrpcHandler: func() lnrpc.GrpcHandler { + return &ServerShell{} }, } diff --git a/lnrpc/chainrpc/chainnotifier_server.go b/lnrpc/chainrpc/chainnotifier_server.go index 3e35ab60..80f3c0ea 100644 --- a/lnrpc/chainrpc/chainnotifier_server.go +++ b/lnrpc/chainrpc/chainnotifier_server.go @@ -72,6 +72,13 @@ var ( "still in the process of starting") ) +// ServerShell is a shell struct holding a reference to the actual sub-server. +// It is used to register the gRPC sub-server with the root server before we +// have the necessary dependencies to populate the actual sub-server. +type ServerShell struct { + ChainNotifierServer +} + // Server is a sub-server of the main RPC server: the chain notifier RPC. This // RPC sub-server allows external callers to access the full chain notifier // capabilities of lnd. This allows callers to create custom protocols, external @@ -172,11 +179,11 @@ func (s *Server) Name() string { // sub-server to register itself with the main gRPC root server. Until this is // called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error { // We make sure that we register it with the main gRPC server to ensure // all our methods are routed properly. - RegisterChainNotifierServer(grpcServer, s) + RegisterChainNotifierServer(grpcServer, r) log.Debug("ChainNotifier RPC server successfully register with root " + "gRPC server") @@ -188,8 +195,8 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { // RPC server to register itself with the main REST mux server. Until this is // called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (s *Server) RegisterWithRestServer(ctx context.Context, +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRestServer(ctx context.Context, mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { // We make sure that we register it with the main REST server to ensure @@ -206,6 +213,25 @@ func (s *Server) RegisterWithRestServer(ctx context.Context, return nil } +// CreateSubServer populates the subserver's dependencies using the passed +// SubServerConfigDispatcher. This method should fully initialize the +// sub-server instance, making it ready for action. It returns the macaroon +// permissions that the sub-server wishes to pass on to the root server for all +// methods routed towards it. +// +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( + lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + + subServer, macPermissions, err := createNewSubServer(configRegistry) + if err != nil { + return nil, nil, err + } + + r.ChainNotifierServer = subServer + return subServer, macPermissions, nil +} + // RegisterConfirmationsNtfn is a synchronous response-streaming RPC that // registers an intent for a client to be notified once a confirmation request // has reached its required number of confirmations on-chain. diff --git a/lnrpc/chainrpc/driver.go b/lnrpc/chainrpc/driver.go index 0a932fb6..be307f82 100644 --- a/lnrpc/chainrpc/driver.go +++ b/lnrpc/chainrpc/driver.go @@ -13,7 +13,7 @@ import ( // the config that is meant for us in the config dispatcher, then we'll exit // with an error. func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( - lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + *Server, lnrpc.MacaroonPerms, error) { // We'll attempt to look up the config that we expect, according to our // subServerName name. If we can't find this, then we'll exit with an @@ -55,10 +55,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( func init() { subServer := &lnrpc.SubServerDriver{ SubServerName: subServerName, - New: func(c lnrpc.SubServerConfigDispatcher) ( - lnrpc.SubServer, lnrpc.MacaroonPerms, error) { - - return createNewSubServer(c) + NewGrpcHandler: func() lnrpc.GrpcHandler { + return &ServerShell{} }, } diff --git a/lnrpc/invoicesrpc/driver.go b/lnrpc/invoicesrpc/driver.go index 54ad1fc1..5183c878 100644 --- a/lnrpc/invoicesrpc/driver.go +++ b/lnrpc/invoicesrpc/driver.go @@ -13,7 +13,7 @@ import ( // that is meant for us in the config dispatcher, then we'll exit with an // error. func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( - lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + *Server, lnrpc.MacaroonPerms, error) { // We'll attempt to look up the config that we expect, according to our // subServerName name. If we can't find this, then we'll exit with an @@ -40,9 +40,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( func init() { subServer := &lnrpc.SubServerDriver{ SubServerName: subServerName, - New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, - lnrpc.MacaroonPerms, error) { - return createNewSubServer(c) + NewGrpcHandler: func() lnrpc.GrpcHandler { + return &ServerShell{} }, } diff --git a/lnrpc/invoicesrpc/invoices_server.go b/lnrpc/invoicesrpc/invoices_server.go index 3f053956..9c092e57 100644 --- a/lnrpc/invoicesrpc/invoices_server.go +++ b/lnrpc/invoicesrpc/invoices_server.go @@ -66,6 +66,13 @@ var ( DefaultInvoicesMacFilename = "invoices.macaroon" ) +// ServerShell is a shell struct holding a reference to the actual sub-server. +// It is used to register the gRPC sub-server with the root server before we +// have the necessary dependencies to populate the actual sub-server. +type ServerShell struct { + InvoicesServer +} + // Server is a sub-server of the main RPC server: the invoices RPC. This sub // RPC server allows external callers to access the status of the invoices // currently active within lnd, as well as configuring it at runtime. @@ -157,11 +164,11 @@ func (s *Server) Name() string { // RPC server to register itself with the main gRPC root server. Until this is // called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error { // We make sure that we register it with the main gRPC server to ensure // all our methods are routed properly. - RegisterInvoicesServer(grpcServer, s) + RegisterInvoicesServer(grpcServer, r) log.Debugf("Invoices RPC server successfully registered with root " + "gRPC server") @@ -173,8 +180,8 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { // RPC server to register itself with the main REST mux server. Until this is // called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (s *Server) RegisterWithRestServer(ctx context.Context, +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRestServer(ctx context.Context, mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { // We make sure that we register it with the main REST server to ensure @@ -191,6 +198,25 @@ func (s *Server) RegisterWithRestServer(ctx context.Context, return nil } +// CreateSubServer populates the subserver's dependencies using the passed +// SubServerConfigDispatcher. This method should fully initialize the +// sub-server instance, making it ready for action. It returns the macaroon +// permissions that the sub-server wishes to pass on to the root server for all +// methods routed towards it. +// +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( + lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + + subServer, macPermissions, err := createNewSubServer(configRegistry) + if err != nil { + return nil, nil, err + } + + r.InvoicesServer = subServer + return subServer, macPermissions, nil +} + // SubscribeSingleInvoice returns a uni-directional stream (server -> client) // for notifying the client of state changes for a specified invoice. func (s *Server) SubscribeSingleInvoice(req *SubscribeSingleInvoiceRequest, diff --git a/lnrpc/routerrpc/driver.go b/lnrpc/routerrpc/driver.go index e174e222..0899464c 100644 --- a/lnrpc/routerrpc/driver.go +++ b/lnrpc/routerrpc/driver.go @@ -11,7 +11,7 @@ import ( // config that is meant for us in the config dispatcher, then we'll exit with // an error. func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( - lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + *Server, lnrpc.MacaroonPerms, error) { // We'll attempt to look up the config that we expect, according to our // subServerName name. If we can't find this, then we'll exit with an @@ -46,8 +46,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( func init() { subServer := &lnrpc.SubServerDriver{ SubServerName: subServerName, - New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, lnrpc.MacaroonPerms, error) { - return createNewSubServer(c) + NewGrpcHandler: func() lnrpc.GrpcHandler { + return &ServerShell{} }, } diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index bc9d7514..02192ddc 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -129,6 +129,13 @@ var ( DefaultRouterMacFilename = "router.macaroon" ) +// ServerShell a is shell struct holding a reference to the actual sub-server. +// It is used to register the gRPC sub-server with the root server before we +// have the necessary dependencies to populate the actual sub-server. +type ServerShell struct { + RouterServer +} + // Server is a stand alone sub RPC server which exposes functionality that // allows clients to route arbitrary payment through the Lightning Network. type Server struct { @@ -233,11 +240,11 @@ func (s *Server) Name() string { // sub RPC server to register itself with the main gRPC root server. Until this // is called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error { // We make sure that we register it with the main gRPC server to ensure // all our methods are routed properly. - RegisterRouterServer(grpcServer, s) + RegisterRouterServer(grpcServer, r) log.Debugf("Router RPC server successfully register with root gRPC " + "server") @@ -249,8 +256,8 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { // RPC server to register itself with the main REST mux server. Until this is // called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (s *Server) RegisterWithRestServer(ctx context.Context, +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRestServer(ctx context.Context, mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { // We make sure that we register it with the main REST server to ensure @@ -267,6 +274,25 @@ func (s *Server) RegisterWithRestServer(ctx context.Context, return nil } +// CreateSubServer populates the subserver's dependencies using the passed +// SubServerConfigDispatcher. This method should fully initialize the +// sub-server instance, making it ready for action. It returns the macaroon +// permissions that the sub-server wishes to pass on to the root server for all +// methods routed towards it. +// +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( + lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + + subServer, macPermissions, err := createNewSubServer(configRegistry) + if err != nil { + return nil, nil, err + } + + r.RouterServer = subServer + return subServer, macPermissions, nil +} + // SendPaymentV2 attempts to route a payment described by the passed // PaymentRequest to the final destination. If we are unable to route the // payment, or cannot find a route that satisfies the constraints in the diff --git a/lnrpc/signrpc/driver.go b/lnrpc/signrpc/driver.go index b598c33c..cd130d5e 100644 --- a/lnrpc/signrpc/driver.go +++ b/lnrpc/signrpc/driver.go @@ -13,7 +13,7 @@ import ( // config that is meant for us in the config dispatcher, then we'll exit with // an error. func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( - lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + *Server, lnrpc.MacaroonPerms, error) { // We'll attempt to look up the config that we expect, according to our // subServerName name. If we can't find this, then we'll exit with an @@ -55,10 +55,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( func init() { subServer := &lnrpc.SubServerDriver{ SubServerName: subServerName, - New: func(c lnrpc.SubServerConfigDispatcher) ( - lnrpc.SubServer, lnrpc.MacaroonPerms, error) { - - return createNewSubServer(c) + NewGrpcHandler: func() lnrpc.GrpcHandler { + return &ServerShell{} }, } diff --git a/lnrpc/signrpc/signer_server.go b/lnrpc/signrpc/signer_server.go index fdeb3f58..c48c36e1 100644 --- a/lnrpc/signrpc/signer_server.go +++ b/lnrpc/signrpc/signer_server.go @@ -76,6 +76,13 @@ var ( DefaultSignerMacFilename = "signer.macaroon" ) +// ServerShell is a shell struct holding a reference to the actual sub-server. +// It is used to register the gRPC sub-server with the root server before we +// have the necessary dependencies to populate the actual sub-server. +type ServerShell struct { + SignerServer +} + // Server is a sub-server of the main RPC server: the signer RPC. This sub RPC // server allows external callers to access the full signing capabilities of // lnd. This allows callers to create custom protocols, external to lnd, even @@ -167,11 +174,11 @@ func (s *Server) Name() string { // is called, each sub-server won't be able to have // requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error { // We make sure that we register it with the main gRPC server to ensure // all our methods are routed properly. - RegisterSignerServer(grpcServer, s) + RegisterSignerServer(grpcServer, r) log.Debugf("Signer RPC server successfully register with root gRPC " + "server") @@ -183,8 +190,8 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { // RPC server to register itself with the main REST mux server. Until this is // called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (s *Server) RegisterWithRestServer(ctx context.Context, +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRestServer(ctx context.Context, mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { // We make sure that we register it with the main REST server to ensure @@ -201,6 +208,25 @@ func (s *Server) RegisterWithRestServer(ctx context.Context, return nil } +// CreateSubServer populates the subserver's dependencies using the passed +// SubServerConfigDispatcher. This method should fully initialize the +// sub-server instance, making it ready for action. It returns the macaroon +// permissions that the sub-server wishes to pass on to the root server for all +// methods routed towards it. +// +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( + lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + + subServer, macPermissions, err := createNewSubServer(configRegistry) + if err != nil { + return nil, nil, err + } + + r.SignerServer = subServer + return subServer, macPermissions, nil +} + // SignOutputRaw generates a signature for the passed transaction according to // the data within the passed SignReq. If we're unable to find the keys that // correspond to the KeyLocators in the SignReq then we'll return an error. diff --git a/lnrpc/sub_server.go b/lnrpc/sub_server.go index 7970273a..23033abb 100644 --- a/lnrpc/sub_server.go +++ b/lnrpc/sub_server.go @@ -32,7 +32,14 @@ type SubServer interface { // Name returns a unique string representation of the sub-server. This // can be used to identify the sub-server and also de-duplicate them. Name() string +} +// GrpcHandler is the interface that should be registered with the root gRPC +// server, and is the interface that implements the subserver's defined RPC. +// Before the actual sub server has been created, this will be an empty shell +// allowing us to start the gRPC server before we have all the dependencies +// needed to create the subserver itself. +type GrpcHandler interface { // RegisterWithRootServer will be called by the root gRPC server to // direct a sub RPC server to register itself with the main gRPC root // server. Until this is called, each sub-server won't be able to have @@ -45,6 +52,14 @@ type SubServer interface { // routed towards it. RegisterWithRestServer(context.Context, *runtime.ServeMux, string, []grpc.DialOption) error + + // CreateSubServer populates the subserver's dependencies using the + // passed SubServerConfigDispatcher. This method should fully + // initialize the sub-server instance, making it ready for action. It + // returns the macaroon permissions that the sub-server wishes to pass + // on to the root server for all methods routed towards it. + CreateSubServer(subCfgs SubServerConfigDispatcher) (SubServer, + MacaroonPerms, error) } // SubServerConfigDispatcher is an interface that all sub-servers will use to @@ -60,22 +75,18 @@ type SubServerConfigDispatcher interface { } // SubServerDriver is a template struct that allows the root server to create a -// sub-server with minimal knowledge. The root server only need a fully -// populated SubServerConfigDispatcher and with the aide of the -// RegisterSubServers method, it's able to create and initialize all -// sub-servers. +// sub-server gRPC handler with minimal knowledge. type SubServerDriver struct { // SubServerName is the full name of a sub-sever. // // NOTE: This MUST be unique. SubServerName string - // New creates, and fully initializes a new sub-server instance with - // the aide of the SubServerConfigDispatcher. This closure should - // return the SubServer, ready for action, along with the set of - // macaroon permissions that the sub-server wishes to pass on to the - // root server for all methods routed towards it. - New func(subCfgs SubServerConfigDispatcher) (SubServer, MacaroonPerms, error) + // NewGrpcHandler creates a a new sub-server gRPC interface that can be + // registered with the root gRPC server. It is not expected that the + // SubServer is ready for operation before its CreateSubServer and + // Start methods have been called. + NewGrpcHandler func() GrpcHandler } var ( diff --git a/lnrpc/verrpc/driver.go b/lnrpc/verrpc/driver.go index db250f7d..49977f69 100644 --- a/lnrpc/verrpc/driver.go +++ b/lnrpc/verrpc/driver.go @@ -9,10 +9,8 @@ import ( func init() { subServer := &lnrpc.SubServerDriver{ SubServerName: subServerName, - New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, - lnrpc.MacaroonPerms, error) { - - return &Server{}, macPermissions, nil + NewGrpcHandler: func() lnrpc.GrpcHandler { + return &ServerShell{} }, } diff --git a/lnrpc/verrpc/server.go b/lnrpc/verrpc/server.go index ecbd29c1..31c34899 100644 --- a/lnrpc/verrpc/server.go +++ b/lnrpc/verrpc/server.go @@ -5,6 +5,7 @@ import ( "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/lightningnetwork/lnd/build" + "github.com/lightningnetwork/lnd/lnrpc" "google.golang.org/grpc" "gopkg.in/macaroon-bakery.v2/bakery" ) @@ -18,6 +19,13 @@ var macPermissions = map[string][]bakery.Op{ }}, } +// ServerShell is a shell struct holding a reference to the actual sub-server. +// It is used to register the gRPC sub-server with the root server before we +// have the necessary dependencies to populate the actual sub-server. +type ServerShell struct { + VersionerServer +} + // Server is an rpc server that supports querying for information about the // running binary. type Server struct{} @@ -48,9 +56,9 @@ func (s *Server) Name() string { // sub RPC server to register itself with the main gRPC root server. Until this // is called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { - RegisterVersionerServer(grpcServer, s) +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error { + RegisterVersionerServer(grpcServer, r) log.Debugf("Versioner RPC server successfully registered with root " + "gRPC server") @@ -62,8 +70,8 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { // RPC server to register itself with the main REST mux server. Until this is // called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (s *Server) RegisterWithRestServer(ctx context.Context, +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRestServer(ctx context.Context, mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { // We make sure that we register it with the main REST server to ensure @@ -80,6 +88,21 @@ func (s *Server) RegisterWithRestServer(ctx context.Context, return nil } +// CreateSubServer populates the subserver's dependencies using the passed +// SubServerConfigDispatcher. This method should fully initialize the +// sub-server instance, making it ready for action. It returns the macaroon +// permissions that the sub-server wishes to pass on to the root server for all +// methods routed towards it. +// +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) CreateSubServer(_ lnrpc.SubServerConfigDispatcher) ( + lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + + subServer := &Server{} + r.VersionerServer = subServer + return subServer, macPermissions, nil +} + // GetVersion returns information about the compiled binary. func (s *Server) GetVersion(_ context.Context, _ *VersionRequest) (*Version, error) { diff --git a/lnrpc/walletrpc/driver.go b/lnrpc/walletrpc/driver.go index deb02a6e..649445a2 100644 --- a/lnrpc/walletrpc/driver.go +++ b/lnrpc/walletrpc/driver.go @@ -12,7 +12,9 @@ import ( // sub server given the main config dispatcher method. If we're unable to find // the config that is meant for us in the config dispatcher, then we'll exit // with an error. -func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, lnrpc.MacaroonPerms, error) { +func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( + *WalletKit, lnrpc.MacaroonPerms, error) { + // We'll attempt to look up the config that we expect, according to our // subServerName name. If we can't find this, then we'll exit with an // error, as we're unable to properly initialize ourselves without this @@ -67,8 +69,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (lnrpc.S func init() { subServer := &lnrpc.SubServerDriver{ SubServerName: subServerName, - New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, lnrpc.MacaroonPerms, error) { - return createNewSubServer(c) + NewGrpcHandler: func() lnrpc.GrpcHandler { + return &ServerShell{} }, } diff --git a/lnrpc/walletrpc/walletkit_server.go b/lnrpc/walletrpc/walletkit_server.go index 04fafc25..392c6f30 100644 --- a/lnrpc/walletrpc/walletkit_server.go +++ b/lnrpc/walletrpc/walletkit_server.go @@ -148,6 +148,13 @@ var ( // an empty label. var ErrZeroLabel = errors.New("cannot label transaction with empty label") +// ServerShell is a shell struct holding a reference to the actual sub-server. +// It is used to register the gRPC sub-server with the root server before we +// have the necessary dependencies to populate the actual sub-server. +type ServerShell struct { + WalletKitServer +} + // WalletKit is a sub-RPC server that exposes a tool kit which allows clients // to execute common wallet operations. This includes requesting new addresses, // keys (for contracts!), and publishing transactions. @@ -233,11 +240,11 @@ func (w *WalletKit) Name() string { // sub RPC server to register itself with the main gRPC root server. Until this // is called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (w *WalletKit) RegisterWithRootServer(grpcServer *grpc.Server) error { +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error { // We make sure that we register it with the main gRPC server to ensure // all our methods are routed properly. - RegisterWalletKitServer(grpcServer, w) + RegisterWalletKitServer(grpcServer, r) log.Debugf("WalletKit RPC server successfully registered with " + "root gRPC server") @@ -249,8 +256,8 @@ func (w *WalletKit) RegisterWithRootServer(grpcServer *grpc.Server) error { // RPC server to register itself with the main REST mux server. Until this is // called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (w *WalletKit) RegisterWithRestServer(ctx context.Context, +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRestServer(ctx context.Context, mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { // We make sure that we register it with the main REST server to ensure @@ -267,6 +274,25 @@ func (w *WalletKit) RegisterWithRestServer(ctx context.Context, return nil } +// CreateSubServer populates the subserver's dependencies using the passed +// SubServerConfigDispatcher. This method should fully initialize the +// sub-server instance, making it ready for action. It returns the macaroon +// permissions that the sub-server wishes to pass on to the root server for all +// methods routed towards it. +// +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( + lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + + subServer, macPermissions, err := createNewSubServer(configRegistry) + if err != nil { + return nil, nil, err + } + + r.WalletKitServer = subServer + return subServer, macPermissions, nil +} + // ListUnspent returns useful information about each unspent output owned by the // wallet, as reported by the underlying `ListUnspentWitness`; the information // returned is: outpoint, amount in satoshis, address, address type, diff --git a/lnrpc/watchtowerrpc/driver.go b/lnrpc/watchtowerrpc/driver.go index dc9cecce..3fa5f1d2 100644 --- a/lnrpc/watchtowerrpc/driver.go +++ b/lnrpc/watchtowerrpc/driver.go @@ -13,7 +13,7 @@ import ( // that is meant for us in the config dispatcher, then we'll exit with an // error. func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( - lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + *Handler, lnrpc.MacaroonPerms, error) { // We'll attempt to look up the config that we expect, according to our // subServerName name. If we can't find this, then we'll exit with an @@ -40,9 +40,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( func init() { subServer := &lnrpc.SubServerDriver{ SubServerName: subServerName, - New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, - lnrpc.MacaroonPerms, error) { - return createNewSubServer(c) + NewGrpcHandler: func() lnrpc.GrpcHandler { + return &ServerShell{} }, } diff --git a/lnrpc/watchtowerrpc/handler.go b/lnrpc/watchtowerrpc/handler.go index d4124708..88add9d0 100644 --- a/lnrpc/watchtowerrpc/handler.go +++ b/lnrpc/watchtowerrpc/handler.go @@ -5,7 +5,7 @@ package watchtowerrpc import ( "context" "errors" - fmt "fmt" + "fmt" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/lightningnetwork/lnd/lnrpc" @@ -35,6 +35,13 @@ var ( ErrTowerNotActive = errors.New("watchtower not active") ) +// ServerShell is a shell struct holding a reference to the actual sub-server. +// It is used to register the gRPC sub-server with the root server before we +// have the necessary dependencies to populate the actual sub-server. +type ServerShell struct { + WatchtowerServer +} + // Handler is the RPC server we'll use to interact with the backing active // watchtower. type Handler struct { @@ -80,11 +87,11 @@ func (c *Handler) Name() string { // RPC server to register itself with the main gRPC root server. Until this is // called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (c *Handler) RegisterWithRootServer(grpcServer *grpc.Server) error { +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error { // We make sure that we register it with the main gRPC server to ensure // all our methods are routed properly. - RegisterWatchtowerServer(grpcServer, c) + RegisterWatchtowerServer(grpcServer, r) log.Debugf("Watchtower RPC server successfully register with root " + "gRPC server") @@ -96,8 +103,8 @@ func (c *Handler) RegisterWithRootServer(grpcServer *grpc.Server) error { // RPC server to register itself with the main REST mux server. Until this is // called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (c *Handler) RegisterWithRestServer(ctx context.Context, +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRestServer(ctx context.Context, mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { // We make sure that we register it with the main REST server to ensure @@ -114,6 +121,25 @@ func (c *Handler) RegisterWithRestServer(ctx context.Context, return nil } +// CreateSubServer populates the subserver's dependencies using the passed +// SubServerConfigDispatcher. This method should fully initialize the +// sub-server instance, making it ready for action. It returns the macaroon +// permissions that the sub-server wishes to pass on to the root server for all +// methods routed towards it. +// +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( + lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + + subServer, macPermissions, err := createNewSubServer(configRegistry) + if err != nil { + return nil, nil, err + } + + r.WatchtowerServer = subServer + return subServer, macPermissions, nil +} + // AddTower adds a new watchtower reachable at the given address and considers // it for new sessions. If the watchtower already exists, then any new addresses // included will be considered when dialing it for session negotiations and diff --git a/lnrpc/wtclientrpc/driver.go b/lnrpc/wtclientrpc/driver.go index 78e39976..cecbbf43 100644 --- a/lnrpc/wtclientrpc/driver.go +++ b/lnrpc/wtclientrpc/driver.go @@ -12,7 +12,7 @@ import ( // that is meant for us in the config dispatcher, then we'll exit with an // error. func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( - lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + *WatchtowerClient, lnrpc.MacaroonPerms, error) { // We'll attempt to look up the config that we expect, according to our // subServerName name. If we can't find this, then we'll exit with an @@ -46,9 +46,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( func init() { subServer := &lnrpc.SubServerDriver{ SubServerName: subServerName, - New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, - lnrpc.MacaroonPerms, error) { - return createNewSubServer(c) + NewGrpcHandler: func() lnrpc.GrpcHandler { + return &ServerShell{} }, } diff --git a/lnrpc/wtclientrpc/wtclient.go b/lnrpc/wtclientrpc/wtclient.go index 4d55e468..fa3c640d 100644 --- a/lnrpc/wtclientrpc/wtclient.go +++ b/lnrpc/wtclientrpc/wtclient.go @@ -64,6 +64,13 @@ var ( ErrWtclientNotActive = errors.New("watchtower client not active") ) +// ServerShell is a shell struct holding a reference to the actual sub-server. +// It is used to register the gRPC sub-server with the root server before we +// have the necessary dependencies to populate the actual sub-server. +type ServerShell struct { + WatchtowerClientServer +} + // WatchtowerClient is the RPC server we'll use to interact with the backing // active watchtower client. // @@ -112,14 +119,11 @@ func (c *WatchtowerClient) Name() string { // RPC server to register itself with the main gRPC root server. Until this is // called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (c *WatchtowerClient) RegisterWithRootServer(grpcServer *grpc.Server) error { +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error { // We make sure that we register it with the main gRPC server to ensure // all our methods are routed properly. - RegisterWatchtowerClientServer(grpcServer, c) - - c.cfg.Log.Debugf("WatchtowerClient RPC server successfully registered " + - "with root gRPC server") + RegisterWatchtowerClientServer(grpcServer, r) return nil } @@ -128,8 +132,8 @@ func (c *WatchtowerClient) RegisterWithRootServer(grpcServer *grpc.Server) error // RPC server to register itself with the main REST mux server. Until this is // called, each sub-server won't be able to have requests routed towards it. // -// NOTE: This is part of the lnrpc.SubServer interface. -func (c *WatchtowerClient) RegisterWithRestServer(ctx context.Context, +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) RegisterWithRestServer(ctx context.Context, mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { // We make sure that we register it with the main REST server to ensure @@ -142,6 +146,25 @@ func (c *WatchtowerClient) RegisterWithRestServer(ctx context.Context, return nil } +// CreateSubServer populates the subserver's dependencies using the passed +// SubServerConfigDispatcher. This method should fully initialize the +// sub-server instance, making it ready for action. It returns the macaroon +// permissions that the sub-server wishes to pass on to the root server for all +// methods routed towards it. +// +// NOTE: This is part of the lnrpc.GrpcHandler interface. +func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( + lnrpc.SubServer, lnrpc.MacaroonPerms, error) { + + subServer, macPermissions, err := createNewSubServer(configRegistry) + if err != nil { + return nil, nil, err + } + + r.WatchtowerClientServer = subServer + return subServer, macPermissions, nil +} + // isActive returns nil if the watchtower client is initialized so that we can // process RPC requests. func (c *WatchtowerClient) isActive() error { diff --git a/lntest/harness.go b/lntest/harness.go index bf8dd450..b54234da 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -300,8 +300,12 @@ func (n *NetworkHarness) NewNodeWithSeed(name string, extraArgs []string, ctxt, cancel := context.WithTimeout(ctxb, DefaultTimeout) defer cancel() - genSeedResp, err := node.GenSeed(ctxt, genSeedReq) - if err != nil { + + var genSeedResp *lnrpc.GenSeedResponse + if err := wait.NoError(func() error { + genSeedResp, err = node.GenSeed(ctxt, genSeedReq) + return err + }, DefaultTimeout); err != nil { return nil, nil, nil, err } diff --git a/lntest/itest/log_error_whitelist.txt b/lntest/itest/log_error_whitelist.txt index bf2d06ea..49ee52f3 100644 --- a/lntest/itest/log_error_whitelist.txt +++ b/lntest/itest/log_error_whitelist.txt @@ -232,6 +232,15 @@