Merge pull request #4985 from halseth/walletunlocker-unify-jan21

[gRPC] WalletUnlocker and RPCServer unification
This commit is contained in:
Johan T. Halseth authored and GitHub committed 2021-03-11 14:00:00 +01:00
commit fd76b879a2
29 files changed
+1136 -640

No files matched your search

+213 -143
View File
@@ -45,6 +45,8 @@ import (
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/monitoring"
"github.com/lightningnetwork/lnd/rpcperms"
"github.com/lightningnetwork/lnd/signal" "github.com/lightningnetwork/lnd/signal"
"github.com/lightningnetwork/lnd/tor" "github.com/lightningnetwork/lnd/tor"
"github.com/lightningnetwork/lnd/walletunlocker" "github.com/lightningnetwork/lnd/walletunlocker"
@@ -162,14 +164,6 @@ type ListenerWithSignal struct {
// Ready will be closed by the server listening on Listener. // Ready will be closed by the server listening on Listener.
Ready chan struct{} 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 // 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 // RPCListener can be set to the listener to use for the RPC server. If
// nil a regular network listener will be created. // nil a regular network listener will be created.
RPCListener *ListenerWithSignal RPCListener *ListenerWithSignal
}
// rpcListeners is a function type used for closures that fetches a set of RPC // ExternalRPCSubserverCfg is optional and specifies the registration
// listeners for the current configuration. If no custom listeners are present, // callback and permissions to register external gRPC subservers.
// this should return normal listeners from the RPC endpoints defined in the ExternalRPCSubserverCfg *RPCSubserverConfig
// config. The second return value us a closure that will close the fetched
// listeners. // ExternalRestRegistrar is optional and specifies the registration
type rpcListeners func() ([]*ListenerWithSignal, func(), error) // callback to register external REST subservers.
ExternalRestRegistrar RestRegistrar
}
// Main is the true entry point for lnd. It accepts a fully populated and // Main is the true entry point for lnd. It accepts a fully populated and
// validated main configuration struct and an optional listener config struct. // 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() 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 // Before starting the wallet, we'll create and start our Neutrino
// light client instance, if enabled, in order to allow it to sync // light client instance, if enabled, in order to allow it to sync
// while the rest of the daemon continues startup. // while the rest of the daemon continues startup.
@@ -319,7 +298,6 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
var ( var (
walletInitParams WalletUnlockParams walletInitParams WalletUnlockParams
shutdownUnlocker = func() {}
privateWalletPw = lnwallet.DefaultPrivatePassphrase privateWalletPw = lnwallet.DefaultPrivatePassphrase
publicWalletPw = lnwallet.DefaultPublicPassphrase publicWalletPw = lnwallet.DefaultPublicPassphrase
) )
@@ -329,11 +307,14 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
// this information. // this information.
walletInitParams.Birthday = time.Now() walletInitParams.Birthday = time.Now()
// getListeners is a closure that creates listeners from the // If we have chosen to start with a dedicated listener for the
// RPCListeners defined in the config. It also returns a cleanup // rpc server, we set it directly.
// closure and the server options to use for the GRPC server. var grpcListeners []*ListenerWithSignal
getListeners := func() ([]*ListenerWithSignal, func(), error) { if lisCfg.RPCListener != nil {
var grpcListeners []*ListenerWithSignal grpcListeners = []*ListenerWithSignal{lisCfg.RPCListener}
} else {
// Otherwise we create listeners from the RPCListeners defined
// in the config.
for _, grpcEndpoint := range cfg.RPCListeners { for _, grpcEndpoint := range cfg.RPCListeners {
// Start a gRPC server listening for HTTP/2 // Start a gRPC server listening for HTTP/2
// connections. // connections.
@@ -341,48 +322,76 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
if err != nil { if err != nil {
ltndLog.Errorf("unable to listen on %s", ltndLog.Errorf("unable to listen on %s",
grpcEndpoint) grpcEndpoint)
return nil, nil, err return err
} }
defer lis.Close()
grpcListeners = append( grpcListeners = append(
grpcListeners, &ListenerWithSignal{ grpcListeners, &ListenerWithSignal{
Listener: lis, Listener: lis,
Ready: make(chan struct{}), 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 // We'll create the WalletUnlockerService and check whether the wallet
// unlocker, that will be called when it needs listeners for its GPRC // already exists.
// server. pwService := createWalletUnlockerService(cfg)
walletUnlockerListeners := func() ([]*ListenerWithSignal, func(), walletExists, err := pwService.WalletExists()
error) { if err != nil {
return err
// 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()
} }
// 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 // We wait until the user provides a password over RPC. In case lnd is
// started with the --noseedbackup flag, we use the default password // started with the --noseedbackup flag, we use the default password
// for wallet encryption. // for wallet encryption.
if !cfg.NoSeedBackup { if !cfg.NoSeedBackup {
params, shutdown, err := waitForWalletPassword( params, err := waitForWalletPassword(cfg, pwService)
cfg, cfg.RESTListeners, serverOpts, restDialOpts,
restProxyDest, restListen, walletUnlockerListeners,
)
if err != nil { if err != nil {
err := fmt.Errorf("unable to set up wallet password "+ err := fmt.Errorf("unable to set up wallet password "+
"listeners: %v", err) "listeners: %v", err)
@@ -391,7 +400,6 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
} }
walletInitParams = *params walletInitParams = *params
shutdownUnlocker = shutdown
privateWalletPw = walletInitParams.Password privateWalletPw = walletInitParams.Password
publicWalletPw = walletInitParams.Password publicWalletPw = walletInitParams.Password
defer func() { defer func() {
@@ -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 var macaroonService *macaroons.Service
if !cfg.NoMacaroons { if !cfg.NoMacaroons {
// Create the macaroon authentication/authorization service. // 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) ltndLog.Warnf(msg, "invoice", cfg.InvoiceMacPath)
} }
} }
}
// Now we're definitely done with the unlocker, shut it down so we can // We add the macaroon service to our RPC interceptor. This
// start the main RPC service later. // will start checking macaroons against permissions on every
shutdownUnlocker() // RPC invocation.
interceptorChain.AddMacaroonService(macaroonService)
}
// With the information parsed from the configuration, create valid // With the information parsed from the configuration, create valid
// instances of the pertinent interfaces required to operate the // instances of the pertinent interfaces required to operate the
@@ -717,29 +730,14 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
} }
defer atplManager.Stop() defer atplManager.Stop()
// rpcListeners is a closure we'll hand to the rpc server, that will be // Now we have created all dependencies necessary to populate and
// called when it needs listeners for its GPRC server. // start the RPC server.
rpcListeners := func() ([]*ListenerWithSignal, func(), error) { err = rpcServer.addDeps(
// If we have chosen to start with a dedicated listener for the server, macaroonService, cfg.SubRPCServers, atplManager,
// rpc server, we return it directly. server.invoices, tower, chainedAcceptor,
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,
) )
if err != nil { 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) ltndLog.Error(err)
return err return err
} }
@@ -750,6 +748,9 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
} }
defer rpcServer.Stop() 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 // 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 // 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 // ensures that we don't accept any possibly invalid state transitions, or
@@ -1124,14 +1125,9 @@ type WalletUnlockParams struct {
MacResponseChan chan []byte MacResponseChan chan []byte
} }
// waitForWalletPassword will spin up gRPC and REST endpoints for the // createWalletUnlockerService creates a WalletUnlockerService from the passed
// WalletUnlocker server, and block until a password is provided by // config.
// the user to this RPC server. func createWalletUnlockerService(cfg *Config) *walletunlocker.UnlockerService {
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) {
chainConfig := cfg.Bitcoin chainConfig := cfg.Bitcoin
if cfg.registeredChains.PrimaryChain() == chainreg.LitecoinChain { if cfg.registeredChains.PrimaryChain() == chainreg.LitecoinChain {
chainConfig = cfg.Litecoin chainConfig = cfg.Litecoin
@@ -1143,36 +1139,16 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
macaroonFiles := []string{ macaroonFiles := []string{
cfg.AdminMacPath, cfg.ReadMacPath, cfg.InvoiceMacPath, cfg.AdminMacPath, cfg.ReadMacPath, cfg.InvoiceMacPath,
} }
pwService := walletunlocker.New( return walletunlocker.New(
chainConfig.ChainDir, cfg.ActiveNetParams.Params, chainConfig.ChainDir, cfg.ActiveNetParams.Params,
!cfg.SyncFreelist, macaroonFiles, cfg.DB.Bolt.DBTimeout, !cfg.SyncFreelist, macaroonFiles, cfg.DB.Bolt.DBTimeout,
cfg.ResetWalletTransactions, cfg.ResetWalletTransactions,
) )
}
// Set up a new PasswordService, which will listen for passwords // startGrpcListen starts the GRPC server on the passed listeners.
// provided over RPC. func startGrpcListen(cfg *Config, grpcServer *grpc.Server,
grpcServer := grpc.NewServer(serverOpts...) listeners []*ListenerWithSignal) error {
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)
// Use a WaitGroup so we can be sure the instructions on how to input the // Use a WaitGroup so we can be sure the instructions on how to input the
// password is the last thing to be printed to the console. // password is the last thing to be printed to the console.
@@ -1181,8 +1157,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
for _, lis := range listeners { for _, lis := range listeners {
wg.Add(1) wg.Add(1)
go func(lis *ListenerWithSignal) { go func(lis *ListenerWithSignal) {
rpcsLog.Infof("Password RPC server listening on %s", rpcsLog.Infof("RPC server listening on %s", lis.Addr())
lis.Addr())
// Close the ready chan to indicate we are listening. // Close the ready chan to indicate we are listening.
close(lis.Ready) close(lis.Ready)
@@ -1192,29 +1167,102 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
}(lis) }(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 := context.Background()
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
shutdownFuncs = append(shutdownFuncs, cancel) 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, ctx, mux, restProxyDest, restDialOpts,
) )
if err != nil { 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) lis, err := restListen(restEndpoint)
if err != nil { if err != nil {
ltndLog.Errorf("Password gRPC proxy unable to listen "+ ltndLog.Errorf("gRPC proxy unable to listen on %s",
"on %s", restEndpoint) restEndpoint)
return nil, shutdown, err return nil, err
} }
shutdownFuncs = append(shutdownFuncs, func() { shutdownFuncs = append(shutdownFuncs, func() {
err := lis.Close() err := lis.Close()
if err != nil { if err != nil {
@@ -1225,16 +1273,38 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
wg.Add(1) wg.Add(1)
go func() { go func() {
rpcsLog.Infof("Password gRPC proxy started at %s", rpcsLog.Infof("gRPC proxy started at %s", lis.Addr())
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() 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() 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. // Wait for user to provide the password.
ltndLog.Infof("Waiting for wallet encryption password. Use `lncli " + ltndLog.Infof("Waiting for wallet encryption password. Use `lncli " +
"create` to create a wallet, `lncli unlock` to unlock an " + "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 // version, then we'll return an error as we don't understand
// this. // this.
if cipherSeed.InternalVersion != keychain.KeyDerivationVersion { if cipherSeed.InternalVersion != keychain.KeyDerivationVersion {
return nil, shutdown, fmt.Errorf("invalid internal "+ return nil, fmt.Errorf("invalid internal "+
"seed version %v, current version is %v", "seed version %v, current version is %v",
cipherSeed.InternalVersion, cipherSeed.InternalVersion,
keychain.KeyDerivationVersion) keychain.KeyDerivationVersion)
@@ -1286,7 +1356,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
ltndLog.Errorf("Could not unload new "+ ltndLog.Errorf("Could not unload new "+
"wallet: %v", err) "wallet: %v", err)
} }
return nil, shutdown, err return nil, err
} }
// For new wallets, the ResetWalletTransactions flag is a no-op. // For new wallets, the ResetWalletTransactions flag is a no-op.
@@ -1304,7 +1374,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
UnloadWallet: loader.UnloadWallet, UnloadWallet: loader.UnloadWallet,
StatelessInit: initMsg.StatelessInit, StatelessInit: initMsg.StatelessInit,
MacResponseChan: pwService.MacResponseChan, MacResponseChan: pwService.MacResponseChan,
}, shutdown, nil }, nil
// The wallet has already been created in the past, and is simply being // The wallet has already been created in the past, and is simply being
// unlocked. So we'll just return these passphrases. // unlocked. So we'll just return these passphrases.
@@ -1328,10 +1398,10 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
UnloadWallet: unlockMsg.UnloadWallet, UnloadWallet: unlockMsg.UnloadWallet,
StatelessInit: unlockMsg.StatelessInit, StatelessInit: unlockMsg.StatelessInit,
MacResponseChan: pwService.MacResponseChan, MacResponseChan: pwService.MacResponseChan,
}, shutdown, nil }, nil
case <-signal.ShutdownChannel(): case <-signal.ShutdownChannel():
return nil, shutdown, fmt.Errorf("shutting down") return nil, fmt.Errorf("shutting down")
} }
} }
+31 -5
View File
@@ -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 // 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 // RPC server allows external callers to access the status of the autopilot
// currently active within lnd, as well as configuring it at runtime. // 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 // is called, each sub-server won't be able to have
// requests routed towards it. // requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
// We make sure that we register it with the main gRPC server to ensure // We make sure that we register it with the main gRPC server to ensure
// all our methods are routed properly. // all our methods are routed properly.
RegisterAutopilotServer(grpcServer, s) RegisterAutopilotServer(grpcServer, r)
log.Debugf("Autopilot RPC server successfully register with root " + log.Debugf("Autopilot RPC server successfully register with root " +
"gRPC server") "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 // 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. // called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (s *Server) RegisterWithRestServer(ctx context.Context, func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
// We make sure that we register it with the main REST server to ensure // 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 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. // Status returns the current status of the autopilot agent.
// //
// NOTE: Part of the AutopilotServer interface. // NOTE: Part of the AutopilotServer interface.
+3 -4
View File
@@ -13,7 +13,7 @@ import (
// that is meant for us in the config dispatcher, then we'll exit with an // that is meant for us in the config dispatcher, then we'll exit with an
// error. // error.
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( 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 // 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 // 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() { func init() {
subServer := &lnrpc.SubServerDriver{ subServer := &lnrpc.SubServerDriver{
SubServerName: subServerName, SubServerName: subServerName,
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, NewGrpcHandler: func() lnrpc.GrpcHandler {
lnrpc.MacaroonPerms, error) { return &ServerShell{}
return createNewSubServer(c)
}, },
} }
+31 -5
View File
@@ -72,6 +72,13 @@ var (
"still in the process of starting") "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 // 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 // RPC sub-server allows external callers to access the full chain notifier
// capabilities of lnd. This allows callers to create custom protocols, external // 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 // 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. // called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
// We make sure that we register it with the main gRPC server to ensure // We make sure that we register it with the main gRPC server to ensure
// all our methods are routed properly. // all our methods are routed properly.
RegisterChainNotifierServer(grpcServer, s) RegisterChainNotifierServer(grpcServer, r)
log.Debug("ChainNotifier RPC server successfully register with root " + log.Debug("ChainNotifier RPC server successfully register with root " +
"gRPC server") "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 // 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. // called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (s *Server) RegisterWithRestServer(ctx context.Context, func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
// We make sure that we register it with the main REST server to ensure // 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 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 // RegisterConfirmationsNtfn is a synchronous response-streaming RPC that
// registers an intent for a client to be notified once a confirmation request // registers an intent for a client to be notified once a confirmation request
// has reached its required number of confirmations on-chain. // has reached its required number of confirmations on-chain.
+3 -5
View File
@@ -13,7 +13,7 @@ import (
// the config that is meant for us in the config dispatcher, then we'll exit // the config that is meant for us in the config dispatcher, then we'll exit
// with an error. // with an error.
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( 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 // 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 // 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() { func init() {
subServer := &lnrpc.SubServerDriver{ subServer := &lnrpc.SubServerDriver{
SubServerName: subServerName, SubServerName: subServerName,
New: func(c lnrpc.SubServerConfigDispatcher) ( NewGrpcHandler: func() lnrpc.GrpcHandler {
lnrpc.SubServer, lnrpc.MacaroonPerms, error) { return &ServerShell{}
return createNewSubServer(c)
}, },
} }
+3 -4
View File
@@ -13,7 +13,7 @@ import (
// that is meant for us in the config dispatcher, then we'll exit with an // that is meant for us in the config dispatcher, then we'll exit with an
// error. // error.
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( 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 // 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 // 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() { func init() {
subServer := &lnrpc.SubServerDriver{ subServer := &lnrpc.SubServerDriver{
SubServerName: subServerName, SubServerName: subServerName,
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, NewGrpcHandler: func() lnrpc.GrpcHandler {
lnrpc.MacaroonPerms, error) { return &ServerShell{}
return createNewSubServer(c)
}, },
} }
+31 -5
View File
@@ -66,6 +66,13 @@ var (
DefaultInvoicesMacFilename = "invoices.macaroon" 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 // 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 // RPC server allows external callers to access the status of the invoices
// currently active within lnd, as well as configuring it at runtime. // 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 // 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. // called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
// We make sure that we register it with the main gRPC server to ensure // We make sure that we register it with the main gRPC server to ensure
// all our methods are routed properly. // all our methods are routed properly.
RegisterInvoicesServer(grpcServer, s) RegisterInvoicesServer(grpcServer, r)
log.Debugf("Invoices RPC server successfully registered with root " + log.Debugf("Invoices RPC server successfully registered with root " +
"gRPC server") "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 // 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. // called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (s *Server) RegisterWithRestServer(ctx context.Context, func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
// We make sure that we register it with the main REST server to ensure // 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 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) // SubscribeSingleInvoice returns a uni-directional stream (server -> client)
// for notifying the client of state changes for a specified invoice. // for notifying the client of state changes for a specified invoice.
func (s *Server) SubscribeSingleInvoice(req *SubscribeSingleInvoiceRequest, func (s *Server) SubscribeSingleInvoice(req *SubscribeSingleInvoiceRequest,
+3 -3
View File
@@ -11,7 +11,7 @@ import (
// config that is meant for us in the config dispatcher, then we'll exit with // config that is meant for us in the config dispatcher, then we'll exit with
// an error. // an error.
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( 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 // 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 // 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() { func init() {
subServer := &lnrpc.SubServerDriver{ subServer := &lnrpc.SubServerDriver{
SubServerName: subServerName, SubServerName: subServerName,
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, lnrpc.MacaroonPerms, error) { NewGrpcHandler: func() lnrpc.GrpcHandler {
return createNewSubServer(c) return &ServerShell{}
}, },
} }
+31 -5
View File
@@ -129,6 +129,13 @@ var (
DefaultRouterMacFilename = "router.macaroon" 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 // Server is a stand alone sub RPC server which exposes functionality that
// allows clients to route arbitrary payment through the Lightning Network. // allows clients to route arbitrary payment through the Lightning Network.
type Server struct { 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 // 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. // is called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
// We make sure that we register it with the main gRPC server to ensure // We make sure that we register it with the main gRPC server to ensure
// all our methods are routed properly. // all our methods are routed properly.
RegisterRouterServer(grpcServer, s) RegisterRouterServer(grpcServer, r)
log.Debugf("Router RPC server successfully register with root gRPC " + log.Debugf("Router RPC server successfully register with root gRPC " +
"server") "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 // 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. // called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (s *Server) RegisterWithRestServer(ctx context.Context, func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
// We make sure that we register it with the main REST server to ensure // 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 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 // SendPaymentV2 attempts to route a payment described by the passed
// PaymentRequest to the final destination. If we are unable to route the // PaymentRequest to the final destination. If we are unable to route the
// payment, or cannot find a route that satisfies the constraints in the // payment, or cannot find a route that satisfies the constraints in the
+3 -5
View File
@@ -13,7 +13,7 @@ import (
// config that is meant for us in the config dispatcher, then we'll exit with // config that is meant for us in the config dispatcher, then we'll exit with
// an error. // an error.
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( 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 // 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 // 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() { func init() {
subServer := &lnrpc.SubServerDriver{ subServer := &lnrpc.SubServerDriver{
SubServerName: subServerName, SubServerName: subServerName,
New: func(c lnrpc.SubServerConfigDispatcher) ( NewGrpcHandler: func() lnrpc.GrpcHandler {
lnrpc.SubServer, lnrpc.MacaroonPerms, error) { return &ServerShell{}
return createNewSubServer(c)
}, },
} }
+31 -5
View File
@@ -76,6 +76,13 @@ var (
DefaultSignerMacFilename = "signer.macaroon" 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 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 // server allows external callers to access the full signing capabilities of
// lnd. This allows callers to create custom protocols, external to lnd, even // 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 // is called, each sub-server won't be able to have
// requests routed towards it. // requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
// We make sure that we register it with the main gRPC server to ensure // We make sure that we register it with the main gRPC server to ensure
// all our methods are routed properly. // all our methods are routed properly.
RegisterSignerServer(grpcServer, s) RegisterSignerServer(grpcServer, r)
log.Debugf("Signer RPC server successfully register with root gRPC " + log.Debugf("Signer RPC server successfully register with root gRPC " +
"server") "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 // 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. // called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (s *Server) RegisterWithRestServer(ctx context.Context, func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
// We make sure that we register it with the main REST server to ensure // 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 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 // 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 // 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. // correspond to the KeyLocators in the SignReq then we'll return an error.
+21 -10
View File
@@ -32,7 +32,14 @@ type SubServer interface {
// Name returns a unique string representation of the sub-server. This // Name returns a unique string representation of the sub-server. This
// can be used to identify the sub-server and also de-duplicate them. // can be used to identify the sub-server and also de-duplicate them.
Name() string 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 // RegisterWithRootServer will be called by the root gRPC server to
// direct a sub RPC server to register itself with the main gRPC root // 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 // server. Until this is called, each sub-server won't be able to have
@@ -45,6 +52,14 @@ type SubServer interface {
// routed towards it. // routed towards it.
RegisterWithRestServer(context.Context, *runtime.ServeMux, string, RegisterWithRestServer(context.Context, *runtime.ServeMux, string,
[]grpc.DialOption) error []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 // 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 // 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 // sub-server gRPC handler with minimal knowledge.
// populated SubServerConfigDispatcher and with the aide of the
// RegisterSubServers method, it's able to create and initialize all
// sub-servers.
type SubServerDriver struct { type SubServerDriver struct {
// SubServerName is the full name of a sub-sever. // SubServerName is the full name of a sub-sever.
// //
// NOTE: This MUST be unique. // NOTE: This MUST be unique.
SubServerName string SubServerName string
// New creates, and fully initializes a new sub-server instance with // NewGrpcHandler creates a a new sub-server gRPC interface that can be
// the aide of the SubServerConfigDispatcher. This closure should // registered with the root gRPC server. It is not expected that the
// return the SubServer, ready for action, along with the set of // SubServer is ready for operation before its CreateSubServer and
// macaroon permissions that the sub-server wishes to pass on to the // Start methods have been called.
// root server for all methods routed towards it. NewGrpcHandler func() GrpcHandler
New func(subCfgs SubServerConfigDispatcher) (SubServer, MacaroonPerms, error)
} }
var ( var (
+2 -4
View File
@@ -9,10 +9,8 @@ import (
func init() { func init() {
subServer := &lnrpc.SubServerDriver{ subServer := &lnrpc.SubServerDriver{
SubServerName: subServerName, SubServerName: subServerName,
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, NewGrpcHandler: func() lnrpc.GrpcHandler {
lnrpc.MacaroonPerms, error) { return &ServerShell{}
return &Server{}, macPermissions, nil
}, },
} }
+28 -5
View File
@@ -5,6 +5,7 @@ import (
"github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/build"
"github.com/lightningnetwork/lnd/lnrpc"
"google.golang.org/grpc" "google.golang.org/grpc"
"gopkg.in/macaroon-bakery.v2/bakery" "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 // Server is an rpc server that supports querying for information about the
// running binary. // running binary.
type Server struct{} 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 // 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. // is called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
RegisterVersionerServer(grpcServer, s) RegisterVersionerServer(grpcServer, r)
log.Debugf("Versioner RPC server successfully registered with root " + log.Debugf("Versioner RPC server successfully registered with root " +
"gRPC server") "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 // 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. // called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (s *Server) RegisterWithRestServer(ctx context.Context, func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
// We make sure that we register it with the main REST server to ensure // 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 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. // GetVersion returns information about the compiled binary.
func (s *Server) GetVersion(_ context.Context, func (s *Server) GetVersion(_ context.Context,
_ *VersionRequest) (*Version, error) { _ *VersionRequest) (*Version, error) {
+5 -3
View File
@@ -12,7 +12,9 @@ import (
// sub server given the main config dispatcher method. If we're unable to find // 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 // the config that is meant for us in the config dispatcher, then we'll exit
// with an error. // 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 // 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 // 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 // error, as we're unable to properly initialize ourselves without this
@@ -67,8 +69,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (lnrpc.S
func init() { func init() {
subServer := &lnrpc.SubServerDriver{ subServer := &lnrpc.SubServerDriver{
SubServerName: subServerName, SubServerName: subServerName,
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, lnrpc.MacaroonPerms, error) { NewGrpcHandler: func() lnrpc.GrpcHandler {
return createNewSubServer(c) return &ServerShell{}
}, },
} }
+31 -5
View File
@@ -148,6 +148,13 @@ var (
// an empty label. // an empty label.
var ErrZeroLabel = errors.New("cannot label transaction with 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 // WalletKit is a sub-RPC server that exposes a tool kit which allows clients
// to execute common wallet operations. This includes requesting new addresses, // to execute common wallet operations. This includes requesting new addresses,
// keys (for contracts!), and publishing transactions. // 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 // 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. // is called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (w *WalletKit) RegisterWithRootServer(grpcServer *grpc.Server) error { func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
// We make sure that we register it with the main gRPC server to ensure // We make sure that we register it with the main gRPC server to ensure
// all our methods are routed properly. // all our methods are routed properly.
RegisterWalletKitServer(grpcServer, w) RegisterWalletKitServer(grpcServer, r)
log.Debugf("WalletKit RPC server successfully registered with " + log.Debugf("WalletKit RPC server successfully registered with " +
"root gRPC server") "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 // 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. // called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (w *WalletKit) RegisterWithRestServer(ctx context.Context, func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
// We make sure that we register it with the main REST server to ensure // 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 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 // ListUnspent returns useful information about each unspent output owned by the
// wallet, as reported by the underlying `ListUnspentWitness`; the information // wallet, as reported by the underlying `ListUnspentWitness`; the information
// returned is: outpoint, amount in satoshis, address, address type, // returned is: outpoint, amount in satoshis, address, address type,
+3 -4
View File
@@ -13,7 +13,7 @@ import (
// that is meant for us in the config dispatcher, then we'll exit with an // that is meant for us in the config dispatcher, then we'll exit with an
// error. // error.
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( 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 // 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 // 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() { func init() {
subServer := &lnrpc.SubServerDriver{ subServer := &lnrpc.SubServerDriver{
SubServerName: subServerName, SubServerName: subServerName,
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, NewGrpcHandler: func() lnrpc.GrpcHandler {
lnrpc.MacaroonPerms, error) { return &ServerShell{}
return createNewSubServer(c)
}, },
} }
+32 -6
View File
@@ -5,7 +5,7 @@ package watchtowerrpc
import ( import (
"context" "context"
"errors" "errors"
fmt "fmt" "fmt"
"github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
@@ -35,6 +35,13 @@ var (
ErrTowerNotActive = errors.New("watchtower not active") 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 // Handler is the RPC server we'll use to interact with the backing active
// watchtower. // watchtower.
type Handler struct { 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 // 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. // called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (c *Handler) RegisterWithRootServer(grpcServer *grpc.Server) error { func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
// We make sure that we register it with the main gRPC server to ensure // We make sure that we register it with the main gRPC server to ensure
// all our methods are routed properly. // all our methods are routed properly.
RegisterWatchtowerServer(grpcServer, c) RegisterWatchtowerServer(grpcServer, r)
log.Debugf("Watchtower RPC server successfully register with root " + log.Debugf("Watchtower RPC server successfully register with root " +
"gRPC server") "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 // 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. // called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (c *Handler) RegisterWithRestServer(ctx context.Context, func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
// We make sure that we register it with the main REST server to ensure // 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 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 // 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 // it for new sessions. If the watchtower already exists, then any new addresses
// included will be considered when dialing it for session negotiations and // included will be considered when dialing it for session negotiations and
+3 -4
View File
@@ -12,7 +12,7 @@ import (
// that is meant for us in the config dispatcher, then we'll exit with an // that is meant for us in the config dispatcher, then we'll exit with an
// error. // error.
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( 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 // 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 // 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() { func init() {
subServer := &lnrpc.SubServerDriver{ subServer := &lnrpc.SubServerDriver{
SubServerName: subServerName, SubServerName: subServerName,
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, NewGrpcHandler: func() lnrpc.GrpcHandler {
lnrpc.MacaroonPerms, error) { return &ServerShell{}
return createNewSubServer(c)
}, },
} }
+31 -8
View File
@@ -64,6 +64,13 @@ var (
ErrWtclientNotActive = errors.New("watchtower client not active") 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 // WatchtowerClient is the RPC server we'll use to interact with the backing
// active watchtower client. // 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 // 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. // called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (c *WatchtowerClient) RegisterWithRootServer(grpcServer *grpc.Server) error { func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
// We make sure that we register it with the main gRPC server to ensure // We make sure that we register it with the main gRPC server to ensure
// all our methods are routed properly. // all our methods are routed properly.
RegisterWatchtowerClientServer(grpcServer, c) RegisterWatchtowerClientServer(grpcServer, r)
c.cfg.Log.Debugf("WatchtowerClient RPC server successfully registered " +
"with root gRPC server")
return nil 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 // 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. // called, each sub-server won't be able to have requests routed towards it.
// //
// NOTE: This is part of the lnrpc.SubServer interface. // NOTE: This is part of the lnrpc.GrpcHandler interface.
func (c *WatchtowerClient) RegisterWithRestServer(ctx context.Context, func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
// We make sure that we register it with the main REST server to ensure // 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 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 // isActive returns nil if the watchtower client is initialized so that we can
// process RPC requests. // process RPC requests.
func (c *WatchtowerClient) isActive() error { func (c *WatchtowerClient) isActive() error {
+6 -2
View File
@@ -300,8 +300,12 @@ func (n *NetworkHarness) NewNodeWithSeed(name string, extraArgs []string,
ctxt, cancel := context.WithTimeout(ctxb, DefaultTimeout) ctxt, cancel := context.WithTimeout(ctxb, DefaultTimeout)
defer cancel() 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 return nil, nil, nil, err
} }
+9
View File
@@ -232,6 +232,15 @@
<time> [ERR] RPCS: [/signrpc.Signer/DeriveSharedKey]: use either key_desc or key_loc <time> [ERR] RPCS: [/signrpc.Signer/DeriveSharedKey]: use either key_desc or key_loc
<time> [ERR] RPCS: [/signrpc.Signer/DeriveSharedKey]: use either raw_key_bytes or key_index <time> [ERR] RPCS: [/signrpc.Signer/DeriveSharedKey]: use either raw_key_bytes or key_index
<time> [ERR] RPCS: [/signrpc.Signer/DeriveSharedKey]: when setting key_desc the field key_desc.key_loc must also be set <time> [ERR] RPCS: [/signrpc.Signer/DeriveSharedKey]: when setting key_desc the field key_desc.key_loc must also be set
<time> [ERR] RPCS: [/lnrpc.Lightning/BakeMacaroon]: permission denied
<time> [ERR] RPCS: [/lnrpc.Lightning/GetInfo]: cannot retrieve macaroon: cannot get macaroon: root key with id doesn't exist
<time> [ERR] RPCS: [/lnrpc.Lightning/GetInfo]: caveat "ipaddr 1.1.1.1" not satisfied: macaroon locked to different IP address
<time> [ERR] RPCS: [/lnrpc.Lightning/GetInfo]: caveat "time-before <time>" not satisfied: macaroon has expired
<time> [ERR] RPCS: [/lnrpc.Lightning/GetInfo]: expected 1 macaroon, got 0
<time> [ERR] RPCS: [/lnrpc.Lightning/GetInfo]: permission denied
<time> [ERR] RPCS: [/lnrpc.Lightning/GetInfo]: the RPC server is in the process of starting up, but not yet ready to accept calls
<time> [ERR] RPCS: [/lnrpc.Lightning/ListMacaroonIDs]: cannot retrieve macaroon: cannot get macaroon: root key with id 1 doesn't exist
<time> [ERR] RPCS: [/lnrpc.Lightning/NewAddress]: permission denied
<time> [ERR] RPCS: unable to open channel to NodeKey(<hex>): received funding error from <hex>: chan_id=<hex>, err=channel too large <time> [ERR] RPCS: unable to open channel to NodeKey(<hex>): received funding error from <hex>: chan_id=<hex>, err=channel too large
<time> [ERR] RPCS: unable to open channel to NodeKey(<hex>): received funding error from <hex>: chan_id=<hex>, err=chan size of 0.16777216 BTC exceeds maximum chan size of 0.16777215 BTC <time> [ERR] RPCS: unable to open channel to NodeKey(<hex>): received funding error from <hex>: chan_id=<hex>, err=chan size of 0.16777216 BTC exceeds maximum chan size of 0.16777215 BTC
<time> [ERR] RPCS: unable to open channel to NodeKey(<hex>): received funding error from <hex>: chan_id=<hex>, err=chan size of 10.00000001 BTC exceeds maximum chan size of 0.16777215 BTC <time> [ERR] RPCS: unable to open channel to NodeKey(<hex>): received funding error from <hex>: chan_id=<hex>, err=chan size of 10.00000001 BTC exceeds maximum chan size of 0.16777215 BTC
+2 -1
View File
@@ -3,6 +3,7 @@ s/short_chan_id=[[:digit:]]+/short_chan_id=<cid>/g
s/[0-9a-f]{16,}/<hex>/g s/[0-9a-f]{16,}/<hex>/g
s/[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\:[[:digit:]]+/<ip>/g s/[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\:[[:digit:]]+/<ip>/g
s/[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2} [[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\.[[:digit:]]{3}/<time>/g s/[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2} [[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\.[[:digit:]]{3}/<time>/g
s/[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}T[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\.[[:digit:]]{1,18}Z/<time>/g
s/[[:digit:]]+\:[[:digit:]]+\:[[:digit:]]+/<chan>/g s/[[:digit:]]+\:[[:digit:]]+\:[[:digit:]]+/<chan>/g
s/[[:digit:]]+ mSAT/<amt>/g s/[[:digit:]]+ mSAT/<amt>/g
s/HTLC ID = [[:digit:]]+/HTLC ID = <id>/g s/HTLC ID = [[:digit:]]+/HTLC ID = <id>/g
@@ -19,4 +20,4 @@ s/sync failed: remote believes our tail height is [[:digit:]]+, while we have [[
s/sync failed: remote's next commit height is [[:digit:]]+, while we believe it is [[:digit:]]+/sync failed: remote's next commit height is <height>, while we believe it is <height>/g s/sync failed: remote's next commit height is [[:digit:]]+, while we believe it is [[:digit:]]+/sync failed: remote's next commit height is <height>, while we believe it is <height>/g
s/replacement transaction <hex> has an insufficient absolute fee: needs [[:digit:]]+, has [[:digit:]]+/replacement transaction <hex> has an insufficient absolute fee: needs <amt>, has <amt>/g s/replacement transaction <hex> has an insufficient absolute fee: needs [[:digit:]]+, has [[:digit:]]+/replacement transaction <hex> has an insufficient absolute fee: needs <amt>, has <amt>/g
s/Rescan for [[:digit:]]+ addresses/Rescan for <num> addresses/g s/Rescan for [[:digit:]]+ addresses/Rescan for <num> addresses/g
s/unable to get hash from block with height [[:digit:]]+/unable to get hash from block with height <height>/g s/unable to get hash from block with height [[:digit:]]+/unable to get hash from block with height <height>/g
+4 -3
View File
@@ -801,8 +801,9 @@ func (hn *HarnessNode) initLightningClient(conn *grpc.ClientConn) error {
hn.SignerClient = signrpc.NewSignerClient(conn) hn.SignerClient = signrpc.NewSignerClient(conn)
// Set the harness node's pubkey to what the node claims in GetInfo. // Set the harness node's pubkey to what the node claims in GetInfo.
err := hn.FetchNodeInfo() // Since the RPC might not be immediately active, we wrap the call in a
if err != nil { // wait.NoError.
if err := wait.NoError(hn.FetchNodeInfo, DefaultTimeout); err != nil {
return err return err
} }
@@ -812,7 +813,7 @@ func (hn *HarnessNode) initLightningClient(conn *grpc.ClientConn) error {
// until then, we'll create a dummy subscription to ensure we can do so // until then, we'll create a dummy subscription to ensure we can do so
// successfully before proceeding. We use a dummy subscription in order // successfully before proceeding. We use a dummy subscription in order
// to not consume an update from the real one. // to not consume an update from the real one.
err = wait.NoError(func() error { err := wait.NoError(func() error {
req := &lnrpc.GraphTopologySubscription{} req := &lnrpc.GraphTopologySubscription{}
ctx, cancelFunc := context.WithCancel(context.Background()) ctx, cancelFunc := context.WithCancel(context.Background())
topologyClient, err := hn.SubscribeChannelGraph(ctx, req) topologyClient, err := hn.SubscribeChannelGraph(ctx, req)
-36
View File
@@ -1,8 +1,6 @@
package lnd package lnd
import ( import (
"context"
"github.com/btcsuite/btcd/connmgr" "github.com/btcsuite/btcd/connmgr"
"github.com/btcsuite/btclog" "github.com/btcsuite/btclog"
"github.com/lightninglabs/neutrino" "github.com/lightninglabs/neutrino"
@@ -42,7 +40,6 @@ import (
"github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/sweep"
"github.com/lightningnetwork/lnd/watchtower" "github.com/lightningnetwork/lnd/watchtower"
"github.com/lightningnetwork/lnd/watchtower/wtclient" "github.com/lightningnetwork/lnd/watchtower/wtclient"
"google.golang.org/grpc"
) )
// replaceableLogger is a thin wrapper around a logger that is used so the // replaceableLogger is a thin wrapper around a logger that is used so the
@@ -175,36 +172,3 @@ func (c logClosure) String() string {
func newLogClosure(c func() string) logClosure { func newLogClosure(c func() string) logClosure {
return logClosure(c) return logClosure(c)
} }
// errorLogUnaryServerInterceptor is a simple UnaryServerInterceptor that will
// automatically log any errors that occur when serving a client's unary
// request.
func errorLogUnaryServerInterceptor(logger btclog.Logger) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (interface{}, error) {
resp, err := handler(ctx, req)
if err != nil {
// TODO(roasbeef): also log request details?
logger.Errorf("[%v]: %v", info.FullMethod, err)
}
return resp, err
}
}
// errorLogStreamServerInterceptor is a simple StreamServerInterceptor that
// will log any errors that occur while processing a client or server streaming
// RPC.
func errorLogStreamServerInterceptor(logger btclog.Logger) grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream,
info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
err := handler(srv, ss)
if err != nil {
logger.Errorf("[%v]: %v", info.FullMethod, err)
}
return err
}
}
+5 -73
View File
@@ -9,7 +9,6 @@ import (
"time" "time"
"github.com/lightningnetwork/lnd/channeldb/kvdb" "github.com/lightningnetwork/lnd/channeldb/kvdb"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"gopkg.in/macaroon-bakery.v2/bakery" "gopkg.in/macaroon-bakery.v2/bakery"
@@ -58,11 +57,11 @@ type Service struct {
rks *RootKeyStorage rks *RootKeyStorage
// externalValidators is a map between an absolute gRPC URIs and the // ExternalValidators is a map between an absolute gRPC URIs and the
// corresponding external macaroon validator to be used for that URI. // corresponding external macaroon validator to be used for that URI.
// If no external validator for an URI is specified, the service will // If no external validator for an URI is specified, the service will
// use the internal validator. // use the internal validator.
externalValidators map[string]MacaroonValidator ExternalValidators map[string]MacaroonValidator
// StatelessInit denotes if the service was initialized in the stateless // StatelessInit denotes if the service was initialized in the stateless
// mode where no macaroon files should be created on disk. // mode where no macaroon files should be created on disk.
@@ -125,7 +124,7 @@ func NewService(dir, location string, statelessInit bool,
return &Service{ return &Service{
Bakery: *svc, Bakery: *svc,
rks: rootKeyStore, rks: rootKeyStore,
externalValidators: make(map[string]MacaroonValidator), ExternalValidators: make(map[string]MacaroonValidator),
StatelessInit: statelessInit, StatelessInit: statelessInit,
}, nil }, nil
} }
@@ -159,83 +158,16 @@ func (svc *Service) RegisterExternalValidator(fullMethod string,
return fmt.Errorf("validator cannot be nil") return fmt.Errorf("validator cannot be nil")
} }
_, ok := svc.externalValidators[fullMethod] _, ok := svc.ExternalValidators[fullMethod]
if ok { if ok {
return fmt.Errorf("external validator for method %s already "+ return fmt.Errorf("external validator for method %s already "+
"registered", fullMethod) "registered", fullMethod)
} }
svc.externalValidators[fullMethod] = validator svc.ExternalValidators[fullMethod] = validator
return nil return nil
} }
// UnaryServerInterceptor is a GRPC interceptor that checks whether the
// request is authorized by the included macaroons.
func (svc *Service) UnaryServerInterceptor(
permissionMap map[string][]bakery.Op) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (interface{}, error) {
uriPermissions, ok := permissionMap[info.FullMethod]
if !ok {
return nil, fmt.Errorf("%s: unknown permissions "+
"required for method", info.FullMethod)
}
// Find out if there is an external validator registered for
// this method. Fall back to the internal one if there isn't.
validator, ok := svc.externalValidators[info.FullMethod]
if !ok {
validator = svc
}
// Now that we know what validator to use, let it do its work.
err := validator.ValidateMacaroon(
ctx, uriPermissions, info.FullMethod,
)
if err != nil {
return nil, err
}
return handler(ctx, req)
}
}
// StreamServerInterceptor is a GRPC interceptor that checks whether the
// request is authorized by the included macaroons.
func (svc *Service) StreamServerInterceptor(
permissionMap map[string][]bakery.Op) grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream,
info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
uriPermissions, ok := permissionMap[info.FullMethod]
if !ok {
return fmt.Errorf("%s: unknown permissions required "+
"for method", info.FullMethod)
}
// Find out if there is an external validator registered for
// this method. Fall back to the internal one if there isn't.
validator, ok := svc.externalValidators[info.FullMethod]
if !ok {
validator = svc
}
// Now that we know what validator to use, let it do its work.
err := validator.ValidateMacaroon(
ss.Context(), uriPermissions, info.FullMethod,
)
if err != nil {
return err
}
return handler(srv, ss)
}
}
// ValidateMacaroon validates the capabilities of a given request given a // ValidateMacaroon validates the capabilities of a given request given a
// bakery service, context, and uri. Within the passed context.Context, we // bakery service, context, and uri. Within the passed context.Context, we
// expect a macaroon to be encoded as request metadata using the key // expect a macaroon to be encoded as request metadata using the key
+410
View File
@@ -0,0 +1,410 @@
package rpcperms
import (
"context"
"fmt"
"sync"
"github.com/btcsuite/btclog"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/monitoring"
"google.golang.org/grpc"
"gopkg.in/macaroon-bakery.v2/bakery"
)
// rpcState is an enum that we use to keep track of the current RPC service
// state. This will transition as we go from startup to unlocking the wallet,
// and finally fully active.
type rpcState uint8
const (
// walletNotCreated is the starting state if the RPC server is active,
// but the wallet is not yet created. In this state we'll only allow
// calls to the WalletUnlockerService.
walletNotCreated rpcState = iota
// walletLocked indicates the RPC server is active, but the wallet is
// locked. In this state we'll only allow calls to the
// WalletUnlockerService.
walletLocked
// walletUnlocked means that the wallet has been unlocked, but the full
// RPC server is not yeat ready.
walletUnlocked
// rpcActive means that the RPC server is ready to accept calls.
rpcActive
)
var (
// ErrNoWallet is returned if the wallet does not exist.
ErrNoWallet = fmt.Errorf("wallet not created, create one to enable " +
"full RPC access")
// ErrWalletLocked is returned if the wallet is locked and any service
// other than the WalletUnlocker is called.
ErrWalletLocked = fmt.Errorf("wallet locked, unlock it to enable " +
"full RPC access")
// ErrWalletUnlocked is returned if the WalletUnlocker service is
// called when the wallet already has been unlocked.
ErrWalletUnlocked = fmt.Errorf("wallet already unlocked, " +
"WalletUnlocker service is no longer available")
// ErrRPCStarting is returned if the wallet has been unlocked but the
// RPC server is not yet ready to accept calls.
ErrRPCStarting = fmt.Errorf("the RPC server is in the process of " +
"starting up, but not yet ready to accept calls")
// macaroonWhitelist defines methods that we don't require macaroons to
// access.
macaroonWhitelist = map[string]struct{}{
// We allow all calls to the WalletUnlocker without macaroons.
"/lnrpc.WalletUnlocker/GenSeed": {},
"/lnrpc.WalletUnlocker/InitWallet": {},
"/lnrpc.WalletUnlocker/UnlockWallet": {},
"/lnrpc.WalletUnlocker/ChangePassword": {},
}
)
// InterceptorChain is a struct that can be added to the running GRPC server,
// intercepting API calls. This is useful for logging, enforcing permissions
// etc.
type InterceptorChain struct {
// state is the current RPC state of our RPC server.
state rpcState
// noMacaroons should be set true if we don't want to check macaroons.
noMacaroons bool
// svc is the macaroon service used to enforce permissions in case
// macaroons are used.
svc *macaroons.Service
// permissionMap is the permissions to enforce if macaroons are used.
permissionMap map[string][]bakery.Op
// rpcsLog is the logger used to log calles to the RPCs intercepted.
rpcsLog btclog.Logger
sync.RWMutex
}
// NewInterceptorChain creates a new InterceptorChain.
func NewInterceptorChain(log btclog.Logger, noMacaroons,
walletExists bool) *InterceptorChain {
startState := walletNotCreated
if walletExists {
startState = walletLocked
}
return &InterceptorChain{
state: startState,
noMacaroons: noMacaroons,
permissionMap: make(map[string][]bakery.Op),
rpcsLog: log,
}
}
// SetWalletUnlocked moves the RPC state from either walletNotCreated or
// walletLocked to walletUnlocked.
func (r *InterceptorChain) SetWalletUnlocked() {
r.Lock()
defer r.Unlock()
r.state = walletUnlocked
}
// SetRPCActive moves the RPC state from walletUnlocked to rpcActive.
func (r *InterceptorChain) SetRPCActive() {
r.Lock()
defer r.Unlock()
r.state = rpcActive
}
// AddMacaroonService adds a macaroon service to the interceptor. After this is
// done every RPC call made will have to pass a valid macaroon to be accepted.
func (r *InterceptorChain) AddMacaroonService(svc *macaroons.Service) {
r.Lock()
defer r.Unlock()
r.svc = svc
}
// AddPermission adds a new macaroon rule for the given method.
func (r *InterceptorChain) AddPermission(method string, ops []bakery.Op) error {
r.Lock()
defer r.Unlock()
if _, ok := r.permissionMap[method]; ok {
return fmt.Errorf("detected duplicate macaroon constraints "+
"for path: %v", method)
}
r.permissionMap[method] = ops
return nil
}
// Permissions returns the current set of macaroon permissions.
func (r *InterceptorChain) Permissions() map[string][]bakery.Op {
r.RLock()
defer r.RUnlock()
// Make a copy under the read lock to avoid races.
c := make(map[string][]bakery.Op)
for k, v := range r.permissionMap {
s := make([]bakery.Op, len(v))
copy(s, v)
c[k] = s
}
return c
}
// CreateServerOpts creates the GRPC server options that can be added to a GRPC
// server in order to add this InterceptorChain.
func (r *InterceptorChain) CreateServerOpts() []grpc.ServerOption {
var unaryInterceptors []grpc.UnaryServerInterceptor
var strmInterceptors []grpc.StreamServerInterceptor
// The first interceptors we'll add to the chain is our logging
// interceptors, so we can automatically log all errors that happen
// during RPC calls.
unaryInterceptors = append(
unaryInterceptors, errorLogUnaryServerInterceptor(r.rpcsLog),
)
strmInterceptors = append(
strmInterceptors, errorLogStreamServerInterceptor(r.rpcsLog),
)
// Next we'll add our RPC state check interceptors, that will check
// whether the attempted call is allowed in the current state.
unaryInterceptors = append(
unaryInterceptors, r.rpcStateUnaryServerInterceptor(),
)
strmInterceptors = append(
strmInterceptors, r.rpcStateStreamServerInterceptor(),
)
// We'll add the macaroon interceptors. If macaroons aren't disabled,
// then these interceptors will enforce macaroon authentication.
unaryInterceptors = append(
unaryInterceptors, r.MacaroonUnaryServerInterceptor(),
)
strmInterceptors = append(
strmInterceptors, r.MacaroonStreamServerInterceptor(),
)
// Get interceptors for Prometheus to gather gRPC performance metrics.
// If monitoring is disabled, GetPromInterceptors() will return empty
// slices.
promUnaryInterceptors, promStrmInterceptors :=
monitoring.GetPromInterceptors()
// Concatenate the slices of unary and stream interceptors respectively.
unaryInterceptors = append(unaryInterceptors, promUnaryInterceptors...)
strmInterceptors = append(strmInterceptors, promStrmInterceptors...)
// Create server options from the interceptors we just set up.
chainedUnary := grpc_middleware.WithUnaryServerChain(
unaryInterceptors...,
)
chainedStream := grpc_middleware.WithStreamServerChain(
strmInterceptors...,
)
serverOpts := []grpc.ServerOption{chainedUnary, chainedStream}
return serverOpts
}
// errorLogUnaryServerInterceptor is a simple UnaryServerInterceptor that will
// automatically log any errors that occur when serving a client's unary
// request.
func errorLogUnaryServerInterceptor(logger btclog.Logger) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (interface{}, error) {
resp, err := handler(ctx, req)
if err != nil {
// TODO(roasbeef): also log request details?
logger.Errorf("[%v]: %v", info.FullMethod, err)
}
return resp, err
}
}
// errorLogStreamServerInterceptor is a simple StreamServerInterceptor that
// will log any errors that occur while processing a client or server streaming
// RPC.
func errorLogStreamServerInterceptor(logger btclog.Logger) grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream,
info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
err := handler(srv, ss)
if err != nil {
logger.Errorf("[%v]: %v", info.FullMethod, err)
}
return err
}
}
// checkMacaroon validates that the context contains the macaroon needed to
// invoke the given RPC method.
func (r *InterceptorChain) checkMacaroon(ctx context.Context,
fullMethod string) error {
// If noMacaroons is set, we'll always allow the call.
if r.noMacaroons {
return nil
}
// Check whether the method is whitelisted, if so we'll allow it
// regardless of macaroons.
_, ok := macaroonWhitelist[fullMethod]
if ok {
return nil
}
r.RLock()
svc := r.svc
r.RUnlock()
// If the macaroon service is not yet active, we cannot allow
// the call.
if svc == nil {
return fmt.Errorf("unable to determine macaroon permissions")
}
r.RLock()
uriPermissions, ok := r.permissionMap[fullMethod]
r.RUnlock()
if !ok {
return fmt.Errorf("%s: unknown permissions required for method",
fullMethod)
}
// Find out if there is an external validator registered for
// this method. Fall back to the internal one if there isn't.
validator, ok := svc.ExternalValidators[fullMethod]
if !ok {
validator = svc
}
// Now that we know what validator to use, let it do its work.
return validator.ValidateMacaroon(ctx, uriPermissions, fullMethod)
}
// MacaroonUnaryServerInterceptor is a GRPC interceptor that checks whether the
// request is authorized by the included macaroons.
func (r *InterceptorChain) MacaroonUnaryServerInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (interface{}, error) {
// Check macaroons.
if err := r.checkMacaroon(ctx, info.FullMethod); err != nil {
return nil, err
}
return handler(ctx, req)
}
}
// MacaroonStreamServerInterceptor is a GRPC interceptor that checks whether
// the request is authorized by the included macaroons.
func (r *InterceptorChain) MacaroonStreamServerInterceptor() grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream,
info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
// Check macaroons.
err := r.checkMacaroon(ss.Context(), info.FullMethod)
if err != nil {
return err
}
return handler(srv, ss)
}
}
// checkRPCState checks whether a call to the given server is allowed in the
// current RPC state.
func (r *InterceptorChain) checkRPCState(srv interface{}) error {
r.RLock()
state := r.state
r.RUnlock()
switch state {
// If the wallet does not exists, only calls to the WalletUnlocker are
// accepted.
case walletNotCreated:
_, ok := srv.(lnrpc.WalletUnlockerServer)
if !ok {
return ErrNoWallet
}
// If the wallet is locked, only calls to the WalletUnlocker are
// accepted.
case walletLocked:
_, ok := srv.(lnrpc.WalletUnlockerServer)
if !ok {
return ErrWalletLocked
}
// If the wallet is unlocked, but the RPC not yet active, we reject.
case walletUnlocked:
_, ok := srv.(lnrpc.WalletUnlockerServer)
if ok {
return ErrWalletUnlocked
}
return ErrRPCStarting
// If the RPC is active, we allow calls to any service except the
// WalletUnlocker.
case rpcActive:
_, ok := srv.(lnrpc.WalletUnlockerServer)
if ok {
return ErrWalletUnlocked
}
default:
return fmt.Errorf("unknown RPC state: %v", state)
}
return nil
}
// rpcStateUnaryServerInterceptor is a GRPC interceptor that checks whether
// calls to the given gGRPC server is allowed in the current rpc state.
func (r *InterceptorChain) rpcStateUnaryServerInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (interface{}, error) {
if err := r.checkRPCState(info.Server); err != nil {
return nil, err
}
return handler(ctx, req)
}
}
// rpcStateStreamServerInterceptor is a GRPC interceptor that checks whether
// calls to the given gGRPC server is allowed in the current rpc state.
func (r *InterceptorChain) rpcStateStreamServerInterceptor() grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream,
info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
if err := r.checkRPCState(srv); err != nil {
return err
}
return handler(srv, ss)
}
}
+150 -287
View File
@@ -8,7 +8,6 @@ import (
"fmt" "fmt"
"io" "io"
"math" "math"
"net"
"net/http" "net/http"
"runtime" "runtime"
"sort" "sort"
@@ -28,7 +27,6 @@ import (
"github.com/btcsuite/btcutil/psbt" "github.com/btcsuite/btcutil/psbt"
"github.com/btcsuite/btcwallet/wallet/txauthor" "github.com/btcsuite/btcwallet/wallet/txauthor"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
proxy "github.com/grpc-ecosystem/grpc-gateway/runtime" proxy "github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/lightningnetwork/lnd/autopilot" "github.com/lightningnetwork/lnd/autopilot"
"github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/build"
@@ -61,12 +59,12 @@ import (
"github.com/lightningnetwork/lnd/lnwallet/chanfunding" "github.com/lightningnetwork/lnd/lnwallet/chanfunding"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/monitoring"
"github.com/lightningnetwork/lnd/peer" "github.com/lightningnetwork/lnd/peer"
"github.com/lightningnetwork/lnd/peernotifier" "github.com/lightningnetwork/lnd/peernotifier"
"github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/record"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/rpcperms"
"github.com/lightningnetwork/lnd/signal" "github.com/lightningnetwork/lnd/signal"
"github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/sweep"
"github.com/lightningnetwork/lnd/watchtower" "github.com/lightningnetwork/lnd/watchtower"
@@ -492,34 +490,8 @@ type rpcServer struct {
// own independent service. This allows us to expose a set of // own independent service. This allows us to expose a set of
// micro-service like abstractions to the outside world for users to // micro-service like abstractions to the outside world for users to
// consume. // consume.
subServers []lnrpc.SubServer subServers []lnrpc.SubServer
subGrpcHandlers []lnrpc.GrpcHandler
// grpcServer is the main gRPC server that this RPC server, and all the
// sub-servers will use to register themselves and accept client
// requests from.
grpcServer *grpc.Server
// listeners is a list of listeners to use when starting the grpc
// server. We make it configurable such that the grpc server can listen
// on custom interfaces.
listeners []*ListenerWithSignal
// listenerCleanUp are a set of closures functions that will allow this
// main RPC server to clean up all the listening socket created for the
// server.
listenerCleanUp []func()
// restDialOpts are a set of gRPC dial options that the REST server
// proxy will use to connect to the main gRPC server.
restDialOpts []grpc.DialOption
// restProxyDest is the address to forward REST requests to.
restProxyDest string
// restListen is a function closure that allows the REST server proxy to
// connect to the main gRPC server to proxy all incoming requests,
// applying the current TLS configuration, if any.
restListen func(net.Addr) (net.Listener, error)
// routerBackend contains the backend implementation of the router // routerBackend contains the backend implementation of the router
// rpc sub server. // rpc sub server.
@@ -538,34 +510,64 @@ type rpcServer struct {
// selfNode is our own pubkey. // selfNode is our own pubkey.
selfNode route.Vertex selfNode route.Vertex
// allPermissions is a map of all registered gRPC URIs (including // interceptorChain is the the interceptor added to our gRPC server.
// internal and external subservers) to the permissions they require. interceptorChain *rpcperms.InterceptorChain
allPermissions map[string][]bakery.Op
// extSubserverCfg is optional and specifies the registration
// callback and permissions to register external gRPC subservers.
extSubserverCfg *RPCSubserverConfig
// extRestRegistrar is optional and specifies the registration
// callback to register external REST subservers.
extRestRegistrar RestRegistrar
} }
// A compile time check to ensure that rpcServer fully implements the // A compile time check to ensure that rpcServer fully implements the
// LightningServer gRPC service. // LightningServer gRPC service.
var _ lnrpc.LightningServer = (*rpcServer)(nil) var _ lnrpc.LightningServer = (*rpcServer)(nil)
// newRPCServer creates and returns a new instance of the rpcServer. The // newRPCServer creates and returns a new instance of the rpcServer. Before
// rpcServer will handle creating all listening sockets needed by it, and any // dependencies are added, this will be an non-functioning RPC server only to
// of the sub-servers that it maintains. The set of serverOpts should be the // be used to register the LightningService with the gRPC server.
// base level options passed to the grPC server. This typically includes things func newRPCServer(cfg *Config, interceptorChain *rpcperms.InterceptorChain,
// like requiring TLS, etc. extSubserverCfg *RPCSubserverConfig,
func newRPCServer(cfg *Config, s *server, macService *macaroons.Service, extRestRegistrar RestRegistrar) *rpcServer {
subServerCgs *subRPCServerConfigs, serverOpts []grpc.ServerOption,
restDialOpts []grpc.DialOption, restProxyDest string, // We go trhough the list of registered sub-servers, and create a gRPC
atpl *autopilot.Manager, invoiceRegistry *invoices.InvoiceRegistry, // handler for each. These are used to register with the gRPC server
tower *watchtower.Standalone, // before all dependencies are available.
restListen func(net.Addr) (net.Listener, error), registeredSubServers := lnrpc.RegisteredSubServers()
getListeners rpcListeners,
chanPredicate *chanacceptor.ChainedAcceptor) (*rpcServer, error) { var subServerHandlers []lnrpc.GrpcHandler
for _, subServer := range registeredSubServers {
subServerHandlers = append(
subServerHandlers, subServer.NewGrpcHandler(),
)
}
return &rpcServer{
cfg: cfg,
subGrpcHandlers: subServerHandlers,
interceptorChain: interceptorChain,
extSubserverCfg: extSubserverCfg,
extRestRegistrar: extRestRegistrar,
quit: make(chan struct{}, 1),
}
}
// addDeps populates all dependencies needed by the RPC server, and any
// of the sub-servers that it maintains. When this is done, the RPC server can
// be started, and start accepting RPC calls.
func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
subServerCgs *subRPCServerConfigs, atpl *autopilot.Manager,
invoiceRegistry *invoices.InvoiceRegistry, tower *watchtower.Standalone,
chanPredicate *chanacceptor.ChainedAcceptor) error {
// Set up router rpc backend. // Set up router rpc backend.
channelGraph := s.localChanDB.ChannelGraph() channelGraph := s.localChanDB.ChannelGraph()
selfNode, err := channelGraph.SourceNode() selfNode, err := channelGraph.SourceNode()
if err != nil { if err != nil {
return nil, err return err
} }
graph := s.localChanDB.ChannelGraph() graph := s.localChanDB.ChannelGraph()
routerBackend := &routerrpc.RouterBackend{ routerBackend := &routerrpc.RouterBackend{
@@ -596,10 +598,10 @@ func newRPCServer(cfg *Config, s *server, macService *macaroons.Service,
}, },
FindRoute: s.chanRouter.FindRoute, FindRoute: s.chanRouter.FindRoute,
MissionControl: s.missionControl, MissionControl: s.missionControl,
ActiveNetParams: cfg.ActiveNetParams.Params, ActiveNetParams: r.cfg.ActiveNetParams.Params,
Tower: s.controlTower, Tower: s.controlTower,
MaxTotalTimelock: cfg.MaxOutgoingCltvExpiry, MaxTotalTimelock: r.cfg.MaxOutgoingCltvExpiry,
DefaultFinalCltvDelta: uint16(cfg.Bitcoin.TimeLockDelta), DefaultFinalCltvDelta: uint16(r.cfg.Bitcoin.TimeLockDelta),
SubscribeHtlcEvents: s.htlcNotifier.SubscribeHtlcEvents, SubscribeHtlcEvents: s.htlcNotifier.SubscribeHtlcEvents,
InterceptableForwarder: s.interceptableSwitch, InterceptableForwarder: s.interceptableSwitch,
SetChannelEnabled: func(outpoint wire.OutPoint) error { SetChannelEnabled: func(outpoint wire.OutPoint) error {
@@ -626,172 +628,125 @@ func newRPCServer(cfg *Config, s *server, macService *macaroons.Service,
// //
// TODO(roasbeef): extend sub-sever config to have both (local vs remote) DB // TODO(roasbeef): extend sub-sever config to have both (local vs remote) DB
err = subServerCgs.PopulateDependencies( err = subServerCgs.PopulateDependencies(
cfg, s.cc, cfg.networkDir, macService, atpl, invoiceRegistry, r.cfg, s.cc, r.cfg.networkDir, macService, atpl, invoiceRegistry,
s.htlcSwitch, cfg.ActiveNetParams.Params, s.chanRouter, s.htlcSwitch, r.cfg.ActiveNetParams.Params, s.chanRouter,
routerBackend, s.nodeSigner, s.localChanDB, s.remoteChanDB, routerBackend, s.nodeSigner, s.localChanDB, s.remoteChanDB,
s.sweeper, tower, s.towerClient, s.anchorTowerClient, s.sweeper, tower, s.towerClient, s.anchorTowerClient,
cfg.net.ResolveTCPAddr, genInvoiceFeatures, rpcsLog, r.cfg.net.ResolveTCPAddr, genInvoiceFeatures, rpcsLog,
) )
if err != nil { if err != nil {
return nil, err return err
} }
// Now that the sub-servers have all their dependencies in place, we // Now that the sub-servers have all their dependencies in place, we
// can create each sub-server! // can create each sub-server!
registeredSubServers := lnrpc.RegisteredSubServers() for _, subServerInstance := range r.subGrpcHandlers {
for _, subServer := range registeredSubServers { subServer, macPerms, err := subServerInstance.CreateSubServer(
subServerInstance, macPerms, err := subServer.New(subServerCgs) subServerCgs,
)
if err != nil { if err != nil {
return nil, err return err
} }
// We'll collect the sub-server, and also the set of // We'll collect the sub-server, and also the set of
// permissions it needs for macaroons so we can apply the // permissions it needs for macaroons so we can apply the
// interceptors below. // interceptors below.
subServers = append(subServers, subServerInstance) subServers = append(subServers, subServer)
subServerPerms = append(subServerPerms, macPerms) subServerPerms = append(subServerPerms, macPerms)
} }
// Next, we need to merge the set of sub server macaroon permissions // Next, we need to merge the set of sub server macaroon permissions
// with the main RPC server permissions so we can unite them under a // with the main RPC server permissions so we can unite them under a
// single set of interceptors. // single set of interceptors.
permissions := MainRPCServerPermissions() for m, ops := range MainRPCServerPermissions() {
for _, subServerPerm := range subServerPerms { err := r.interceptorChain.AddPermission(m, ops)
for method, ops := range subServerPerm { if err != nil {
// For each new method:ops combo, we also ensure that return err
// non of the sub-servers try to override each other.
if _, ok := permissions[method]; ok {
return nil, fmt.Errorf("detected duplicate "+
"macaroon constraints for path: %v",
method)
}
permissions[method] = ops
} }
} }
// Get the listeners and server options to use for this rpc server. for _, subServerPerm := range subServerPerms {
listeners, cleanup, err := getListeners() for method, ops := range subServerPerm {
if err != nil { err := r.interceptorChain.AddPermission(method, ops)
return nil, err if err != nil {
return err
}
}
} }
// External subserver possibly need to register their own permissions // External subserver possibly need to register their own permissions
// and macaroon validator. // and macaroon validator.
for _, lis := range listeners { if r.extSubserverCfg != nil {
extSubserver := lis.ExternalRPCSubserverCfg macValidator := r.extSubserverCfg.MacaroonValidator
if extSubserver != nil { for method, ops := range r.extSubserverCfg.Permissions {
macValidator := extSubserver.MacaroonValidator err := r.interceptorChain.AddPermission(method, ops)
for method, ops := range extSubserver.Permissions { if err != nil {
// For each new method:ops combo, we also ensure return err
// that non of the sub-servers try to override }
// each other.
if _, ok := permissions[method]; ok {
return nil, fmt.Errorf("detected "+
"duplicate macaroon "+
"constraints for path: %v",
method)
}
permissions[method] = ops // Give the external subservers the possibility
// to also use their own validator to check any
// Give the external subservers the possibility // macaroons attached to calls to this method.
// to also use their own validator to check any // This allows them to have their own root key
// macaroons attached to calls to this method. // ID database and permission entities.
// This allows them to have their own root key if macValidator != nil {
// ID database and permission entities. err := macService.RegisterExternalValidator(
if macValidator != nil { method, macValidator,
err := macService.RegisterExternalValidator( )
method, macValidator, if err != nil {
) return fmt.Errorf("could "+
if err != nil { "not register "+
return nil, fmt.Errorf("could "+ "external macaroon "+
"not register "+ "validator: %v", err)
"external macaroon "+
"validator: %v", err)
}
} }
} }
} }
} }
// If macaroons aren't disabled (a non-nil service), then we'll set up // Finally, with all the set up complete, add the last dependencies to
// our set of interceptors which will allow us to handle the macaroon // the rpc server.
// authentication in a single location. r.server = s
macUnaryInterceptors := []grpc.UnaryServerInterceptor{} r.subServers = subServers
macStrmInterceptors := []grpc.StreamServerInterceptor{} r.routerBackend = routerBackend
if macService != nil { r.chanPredicate = chanPredicate
unaryInterceptor := macService.UnaryServerInterceptor(permissions) r.macService = macService
macUnaryInterceptors = append(macUnaryInterceptors, unaryInterceptor) r.selfNode = selfNode.PubKeyBytes
return nil
}
strmInterceptor := macService.StreamServerInterceptor(permissions) // RegisterWithGrpcServer registers the rpcServer and any subservers with the
macStrmInterceptors = append(macStrmInterceptors, strmInterceptor) // root gRPC server.
} func (r *rpcServer) RegisterWithGrpcServer(grpcServer *grpc.Server) error {
// Register the main RPC server.
// Get interceptors for Prometheus to gather gRPC performance metrics. lnrpc.RegisterLightningServer(grpcServer, r)
// If monitoring is disabled, GetPromInterceptors() will return empty
// slices.
promUnaryInterceptors, promStrmInterceptors := monitoring.GetPromInterceptors()
// Concatenate the slices of unary and stream interceptors respectively.
unaryInterceptors := append(macUnaryInterceptors, promUnaryInterceptors...)
strmInterceptors := append(macStrmInterceptors, promStrmInterceptors...)
// We'll also add our logging interceptors as well, so we can
// automatically log all errors that happen during RPC calls.
unaryInterceptors = append(
unaryInterceptors, errorLogUnaryServerInterceptor(rpcsLog),
)
strmInterceptors = append(
strmInterceptors, errorLogStreamServerInterceptor(rpcsLog),
)
// If any interceptors have been set up, add them to the server options.
if len(unaryInterceptors) != 0 && len(strmInterceptors) != 0 {
chainedUnary := grpc_middleware.WithUnaryServerChain(
unaryInterceptors...,
)
chainedStream := grpc_middleware.WithStreamServerChain(
strmInterceptors...,
)
serverOpts = append(serverOpts, chainedUnary, chainedStream)
}
// Finally, with all the pre-set up complete, we can create the main
// gRPC server, and register the main lnrpc server along side.
grpcServer := grpc.NewServer(serverOpts...)
rootRPCServer := &rpcServer{
cfg: cfg,
restDialOpts: restDialOpts,
listeners: listeners,
listenerCleanUp: []func(){cleanup},
restProxyDest: restProxyDest,
subServers: subServers,
restListen: restListen,
grpcServer: grpcServer,
server: s,
routerBackend: routerBackend,
chanPredicate: chanPredicate,
quit: make(chan struct{}, 1),
macService: macService,
selfNode: selfNode.PubKeyBytes,
allPermissions: permissions,
}
lnrpc.RegisterLightningServer(grpcServer, rootRPCServer)
// Now the main RPC server has been registered, we'll iterate through // Now the main RPC server has been registered, we'll iterate through
// all the sub-RPC servers and register them to ensure that requests // all the sub-RPC servers and register them to ensure that requests
// are properly routed towards them. // are properly routed towards them.
for _, subServer := range subServers { for _, subServer := range r.subGrpcHandlers {
err := subServer.RegisterWithRootServer(grpcServer) err := subServer.RegisterWithRootServer(grpcServer)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to register "+ return fmt.Errorf("unable to register "+
"sub-server %v with root: %v", "sub-server with root: %v", err)
subServer.Name(), err)
} }
} }
return rootRPCServer, nil // Before actually listening on the gRPC listener, give external
// subservers the chance to register to our gRPC server. Those external
// subservers (think GrUB) are responsible for starting/stopping on
// their own, we just let them register their services to the same
// server instance so all of them can be exposed on the same
// port/listener.
if r.extSubserverCfg != nil && r.extSubserverCfg.Registrar != nil {
registerer := r.extSubserverCfg.Registrar
err := registerer.RegisterGrpcSubserver(grpcServer)
if err != nil {
rpcsLog.Errorf("error registering external gRPC "+
"subserver: %v", err)
}
}
return nil
} }
// Start launches any helper goroutines required for the rpcServer to function. // Start launches any helper goroutines required for the rpcServer to function.
@@ -813,133 +768,46 @@ func (r *rpcServer) Start() error {
} }
} }
// With all the sub-servers started, we'll spin up the listeners for return nil
// the main RPC server itself. }
for _, lis := range r.listeners {
go func(lis *ListenerWithSignal) {
rpcsLog.Infof("RPC server listening on %s", lis.Addr())
// Before actually listening on the gRPC listener, give // RegisterWithRestProxy registers the RPC server and any subservers with the
// external subservers the chance to register to our // given REST proxy.
// gRPC server. Those external subservers (think GrUB) func (r *rpcServer) RegisterWithRestProxy(restCtx context.Context,
// are responsible for starting/stopping on their own, restMux *proxy.ServeMux, restDialOpts []grpc.DialOption,
// we just let them register their services to the same restProxyDest string) error {
// server instance so all of them can be exposed on the
// same port/listener.
extSubCfg := lis.ExternalRPCSubserverCfg
if extSubCfg != nil && extSubCfg.Registrar != nil {
registerer := extSubCfg.Registrar
err := registerer.RegisterGrpcSubserver(
r.grpcServer,
)
if err != nil {
rpcsLog.Errorf("error registering "+
"external gRPC subserver: %v",
err)
}
}
// Close the ready chan to indicate we are listening.
close(lis.Ready)
_ = r.grpcServer.Serve(lis)
}(lis)
}
// If Prometheus monitoring is enabled, start the Prometheus exporter.
if r.cfg.Prometheus.Enabled() {
err := monitoring.ExportPrometheusMetrics(
r.grpcServer, r.cfg.Prometheus,
)
if err != nil {
return err
}
}
// 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,
},
)
// 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.
restMux := proxy.NewServeMux(customMarshalerOption)
restCtx, restCancel := context.WithCancel(context.Background())
r.listenerCleanUp = append(r.listenerCleanUp, restCancel)
// Wrap the default grpc-gateway handler with the WebSocket handler.
restHandler := lnrpc.NewWebSocketProxy(restMux, rpcsLog)
// With our custom REST proxy mux created, register our main RPC and // With our custom REST proxy mux created, register our main RPC and
// give all subservers a chance to register as well. // give all subservers a chance to register as well.
err := lnrpc.RegisterLightningHandlerFromEndpoint( err := lnrpc.RegisterLightningHandlerFromEndpoint(
restCtx, restMux, r.restProxyDest, r.restDialOpts, restCtx, restMux, restProxyDest, restDialOpts,
) )
if err != nil { if err != nil {
return err return err
} }
for _, subServer := range r.subServers {
for _, subServer := range r.subGrpcHandlers {
err := subServer.RegisterWithRestServer( err := subServer.RegisterWithRestServer(
restCtx, restMux, r.restProxyDest, r.restDialOpts, restCtx, restMux, restProxyDest, restDialOpts,
) )
if err != nil { if err != nil {
return fmt.Errorf("unable to register REST sub-server "+ return fmt.Errorf("unable to register REST sub-server "+
"%v with root: %v", subServer.Name(), err) "with root: %v", err)
} }
} }
// Before listening on any of the interfaces, we also want to give the // Before listening on any of the interfaces, we also want to give the
// external subservers a chance to register their own REST proxy stub // external subservers a chance to register their own REST proxy stub
// with our mux instance. // with our mux instance.
for _, lis := range r.listeners { if r.extRestRegistrar != nil {
if lis.ExternalRestRegistrar != nil { err := r.extRestRegistrar.RegisterRestSubserver(
err := lis.ExternalRestRegistrar.RegisterRestSubserver( restCtx, restMux, restProxyDest, restDialOpts,
restCtx, restMux, r.restProxyDest, )
r.restDialOpts,
)
if err != nil {
rpcsLog.Errorf("error registering "+
"external REST subserver: %v", err)
}
}
}
// 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 r.cfg.RESTListeners {
lis, err := r.restListen(restEndpoint)
if err != nil { if err != nil {
ltndLog.Errorf("gRPC proxy unable to listen on %s", rpcsLog.Errorf("error registering "+
restEndpoint) "external REST subserver: %v", err)
return err
} }
r.listenerCleanUp = append(r.listenerCleanUp, func() {
_ = lis.Close()
})
go func() {
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, r.cfg.RestCORS)
err := http.Serve(lis, corsHandler)
if err != nil && !lnrpc.IsClosedConnError(err) {
rpcsLog.Error(err)
}
}()
} }
return nil return nil
} }
@@ -967,12 +835,6 @@ func (r *rpcServer) Stop() error {
} }
} }
// Finally, we can clean up all the listening sockets to ensure that we
// give the file descriptors back to the OS.
for _, cleanUp := range r.listenerCleanUp {
cleanUp()
}
return nil return nil
} }
@@ -6542,7 +6404,8 @@ func (r *rpcServer) BakeMacaroon(ctx context.Context,
// Either we have the special entity "uri" which specifies a // Either we have the special entity "uri" which specifies a
// full gRPC URI or we have one of the pre-defined actions. // full gRPC URI or we have one of the pre-defined actions.
if op.Entity == macaroons.PermissionEntityCustomURI { if op.Entity == macaroons.PermissionEntityCustomURI {
_, ok := r.allPermissions[op.Action] allPermissions := r.interceptorChain.Permissions()
_, ok := allPermissions[op.Action]
if !ok { if !ok {
return nil, fmt.Errorf("invalid permission " + return nil, fmt.Errorf("invalid permission " +
"action, must be an existing URI in " + "action, must be an existing URI in " +
@@ -6657,7 +6520,7 @@ func (r *rpcServer) ListPermissions(_ context.Context,
rpcsLog.Debugf("[listpermissions]") rpcsLog.Debugf("[listpermissions]")
permissionMap := make(map[string]*lnrpc.MacaroonPermissionList) permissionMap := make(map[string]*lnrpc.MacaroonPermissionList)
for uri, perms := range r.allPermissions { for uri, perms := range r.interceptorChain.Permissions() {
rpcPerms := make([]*lnrpc.MacaroonPermission, len(perms)) rpcPerms := make([]*lnrpc.MacaroonPermission, len(perms))
for idx, perm := range perms { for idx, perm := range perms {
rpcPerms[idx] = &lnrpc.MacaroonPermission{ rpcPerms[idx] = &lnrpc.MacaroonPermission{
+11
View File
@@ -160,6 +160,17 @@ func New(chainDir string, params *chaincfg.Params, noFreelistSync bool,
} }
} }
// WalletExists returns whether a wallet exists on the file path the
// UnlockerService is using.
func (u *UnlockerService) WalletExists() (bool, error) {
netDir := btcwallet.NetworkDir(u.chainDir, u.netParams)
loader := wallet.NewLoader(
u.netParams, netDir, u.noFreelistSync, u.dbTimeout, 0,
)
return loader.WalletExists()
}
// GenSeed is the first method that should be used to instantiate a new lnd // GenSeed is the first method that should be used to instantiate a new lnd
// instance. This method allows a caller to generate a new aezeed cipher seed // instance. This method allows a caller to generate a new aezeed cipher seed
// given an optional passphrase. If provided, the passphrase will be necessary // given an optional passphrase. If provided, the passphrase will be necessary