Merge pull request #4985 from halseth/walletunlocker-unify-jan21
[gRPC] WalletUnlocker and RPCServer unification
This commit is contained in:
commit
fd76b879a2
356
lnd.go
356
lnd.go
@ -45,6 +45,8 @@ import (
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
|
||||
"github.com/lightningnetwork/lnd/macaroons"
|
||||
"github.com/lightningnetwork/lnd/monitoring"
|
||||
"github.com/lightningnetwork/lnd/rpcperms"
|
||||
"github.com/lightningnetwork/lnd/signal"
|
||||
"github.com/lightningnetwork/lnd/tor"
|
||||
"github.com/lightningnetwork/lnd/walletunlocker"
|
||||
@ -162,14 +164,6 @@ type ListenerWithSignal struct {
|
||||
|
||||
// Ready will be closed by the server listening on Listener.
|
||||
Ready chan struct{}
|
||||
|
||||
// ExternalRPCSubserverCfg is optional and specifies the registration
|
||||
// callback and permissions to register external gRPC subservers.
|
||||
ExternalRPCSubserverCfg *RPCSubserverConfig
|
||||
|
||||
// ExternalRestRegistrar is optional and specifies the registration
|
||||
// callback to register external REST subservers.
|
||||
ExternalRestRegistrar RestRegistrar
|
||||
}
|
||||
|
||||
// ListenerCfg is a wrapper around custom listeners that can be passed to lnd
|
||||
@ -182,14 +176,15 @@ type ListenerCfg struct {
|
||||
// RPCListener can be set to the listener to use for the RPC server. If
|
||||
// nil a regular network listener will be created.
|
||||
RPCListener *ListenerWithSignal
|
||||
}
|
||||
|
||||
// rpcListeners is a function type used for closures that fetches a set of RPC
|
||||
// listeners for the current configuration. If no custom listeners are present,
|
||||
// this should return normal listeners from the RPC endpoints defined in the
|
||||
// config. The second return value us a closure that will close the fetched
|
||||
// listeners.
|
||||
type rpcListeners func() ([]*ListenerWithSignal, func(), error)
|
||||
// ExternalRPCSubserverCfg is optional and specifies the registration
|
||||
// callback and permissions to register external gRPC subservers.
|
||||
ExternalRPCSubserverCfg *RPCSubserverConfig
|
||||
|
||||
// ExternalRestRegistrar is optional and specifies the registration
|
||||
// callback to register external REST subservers.
|
||||
ExternalRestRegistrar RestRegistrar
|
||||
}
|
||||
|
||||
// Main is the true entry point for lnd. It accepts a fully populated and
|
||||
// validated main configuration struct and an optional listener config struct.
|
||||
@ -279,22 +274,6 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
||||
|
||||
defer cleanUp()
|
||||
|
||||
// We use the first RPC listener as the destination for our REST proxy.
|
||||
// If the listener is set to listen on all interfaces, we replace it
|
||||
// with localhost, as we cannot dial it directly.
|
||||
restProxyDest := cfg.RPCListeners[0].String()
|
||||
switch {
|
||||
case strings.Contains(restProxyDest, "0.0.0.0"):
|
||||
restProxyDest = strings.Replace(
|
||||
restProxyDest, "0.0.0.0", "127.0.0.1", 1,
|
||||
)
|
||||
|
||||
case strings.Contains(restProxyDest, "[::]"):
|
||||
restProxyDest = strings.Replace(
|
||||
restProxyDest, "[::]", "[::1]", 1,
|
||||
)
|
||||
}
|
||||
|
||||
// Before starting the wallet, we'll create and start our Neutrino
|
||||
// light client instance, if enabled, in order to allow it to sync
|
||||
// while the rest of the daemon continues startup.
|
||||
@ -319,7 +298,6 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
||||
|
||||
var (
|
||||
walletInitParams WalletUnlockParams
|
||||
shutdownUnlocker = func() {}
|
||||
privateWalletPw = lnwallet.DefaultPrivatePassphrase
|
||||
publicWalletPw = lnwallet.DefaultPublicPassphrase
|
||||
)
|
||||
@ -329,11 +307,14 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
||||
// this information.
|
||||
walletInitParams.Birthday = time.Now()
|
||||
|
||||
// getListeners is a closure that creates listeners from the
|
||||
// RPCListeners defined in the config. It also returns a cleanup
|
||||
// closure and the server options to use for the GRPC server.
|
||||
getListeners := func() ([]*ListenerWithSignal, func(), error) {
|
||||
var grpcListeners []*ListenerWithSignal
|
||||
// If we have chosen to start with a dedicated listener for the
|
||||
// rpc server, we set it directly.
|
||||
var grpcListeners []*ListenerWithSignal
|
||||
if lisCfg.RPCListener != nil {
|
||||
grpcListeners = []*ListenerWithSignal{lisCfg.RPCListener}
|
||||
} else {
|
||||
// Otherwise we create listeners from the RPCListeners defined
|
||||
// in the config.
|
||||
for _, grpcEndpoint := range cfg.RPCListeners {
|
||||
// Start a gRPC server listening for HTTP/2
|
||||
// connections.
|
||||
@ -341,48 +322,76 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
||||
if err != nil {
|
||||
ltndLog.Errorf("unable to listen on %s",
|
||||
grpcEndpoint)
|
||||
return nil, nil, err
|
||||
return err
|
||||
}
|
||||
defer lis.Close()
|
||||
|
||||
grpcListeners = append(
|
||||
grpcListeners, &ListenerWithSignal{
|
||||
Listener: lis,
|
||||
Ready: make(chan struct{}),
|
||||
})
|
||||
}
|
||||
|
||||
cleanup := func() {
|
||||
for _, lis := range grpcListeners {
|
||||
lis.Close()
|
||||
}
|
||||
}
|
||||
return grpcListeners, cleanup, nil
|
||||
}
|
||||
|
||||
// walletUnlockerListeners is a closure we'll hand to the wallet
|
||||
// unlocker, that will be called when it needs listeners for its GPRC
|
||||
// server.
|
||||
walletUnlockerListeners := func() ([]*ListenerWithSignal, func(),
|
||||
error) {
|
||||
|
||||
// If we have chosen to start with a dedicated listener for the
|
||||
// wallet unlocker, we return it directly.
|
||||
if lisCfg.WalletUnlocker != nil {
|
||||
return []*ListenerWithSignal{lisCfg.WalletUnlocker},
|
||||
func() {}, nil
|
||||
}
|
||||
|
||||
// Otherwise we'll return the regular listeners.
|
||||
return getListeners()
|
||||
// We'll create the WalletUnlockerService and check whether the wallet
|
||||
// already exists.
|
||||
pwService := createWalletUnlockerService(cfg)
|
||||
walletExists, err := pwService.WalletExists()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a new RPC interceptor that we'll add to the GRPC server. This
|
||||
// will be used to log the API calls invoked on the GRPC server.
|
||||
interceptorChain := rpcperms.NewInterceptorChain(
|
||||
rpcsLog, cfg.NoMacaroons, walletExists,
|
||||
)
|
||||
rpcServerOpts := interceptorChain.CreateServerOpts()
|
||||
serverOpts = append(serverOpts, rpcServerOpts...)
|
||||
|
||||
grpcServer := grpc.NewServer(serverOpts...)
|
||||
defer grpcServer.Stop()
|
||||
|
||||
// Register the WalletUnlockerService with the GRPC server.
|
||||
lnrpc.RegisterWalletUnlockerServer(grpcServer, pwService)
|
||||
|
||||
// Initialize, and register our implementation of the gRPC interface
|
||||
// exported by the rpcServer.
|
||||
rpcServer := newRPCServer(
|
||||
cfg, interceptorChain, lisCfg.ExternalRPCSubserverCfg,
|
||||
lisCfg.ExternalRestRegistrar,
|
||||
)
|
||||
|
||||
err = rpcServer.RegisterWithGrpcServer(grpcServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Now that both the WalletUnlocker and LightningService have been
|
||||
// registered with the GRPC server, we can start listening.
|
||||
err = startGrpcListen(cfg, grpcServer, grpcListeners)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Now start the REST proxy for our gRPC server above. We'll ensure
|
||||
// we direct LND to connect to its loopback address rather than a
|
||||
// wildcard to prevent certificate issues when accessing the proxy
|
||||
// externally.
|
||||
stopProxy, err := startRestProxy(
|
||||
cfg, rpcServer, restDialOpts, restListen,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer stopProxy()
|
||||
|
||||
// We wait until the user provides a password over RPC. In case lnd is
|
||||
// started with the --noseedbackup flag, we use the default password
|
||||
// for wallet encryption.
|
||||
if !cfg.NoSeedBackup {
|
||||
params, shutdown, err := waitForWalletPassword(
|
||||
cfg, cfg.RESTListeners, serverOpts, restDialOpts,
|
||||
restProxyDest, restListen, walletUnlockerListeners,
|
||||
)
|
||||
params, err := waitForWalletPassword(cfg, pwService)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("unable to set up wallet password "+
|
||||
"listeners: %v", err)
|
||||
@ -391,7 +400,6 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
||||
}
|
||||
|
||||
walletInitParams = *params
|
||||
shutdownUnlocker = shutdown
|
||||
privateWalletPw = walletInitParams.Password
|
||||
publicWalletPw = walletInitParams.Password
|
||||
defer func() {
|
||||
@ -407,6 +415,10 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Now that the wallet password has been provided, transition the RPC
|
||||
// state into Unlocked.
|
||||
interceptorChain.SetWalletUnlocked()
|
||||
|
||||
var macaroonService *macaroons.Service
|
||||
if !cfg.NoMacaroons {
|
||||
// Create the macaroon authentication/authorization service.
|
||||
@ -492,11 +504,12 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
||||
ltndLog.Warnf(msg, "invoice", cfg.InvoiceMacPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we're definitely done with the unlocker, shut it down so we can
|
||||
// start the main RPC service later.
|
||||
shutdownUnlocker()
|
||||
// We add the macaroon service to our RPC interceptor. This
|
||||
// will start checking macaroons against permissions on every
|
||||
// RPC invocation.
|
||||
interceptorChain.AddMacaroonService(macaroonService)
|
||||
}
|
||||
|
||||
// With the information parsed from the configuration, create valid
|
||||
// instances of the pertinent interfaces required to operate the
|
||||
@ -717,29 +730,14 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
||||
}
|
||||
defer atplManager.Stop()
|
||||
|
||||
// rpcListeners is a closure we'll hand to the rpc server, that will be
|
||||
// called when it needs listeners for its GPRC server.
|
||||
rpcListeners := func() ([]*ListenerWithSignal, func(), error) {
|
||||
// If we have chosen to start with a dedicated listener for the
|
||||
// rpc server, we return it directly.
|
||||
if lisCfg.RPCListener != nil {
|
||||
return []*ListenerWithSignal{lisCfg.RPCListener},
|
||||
func() {}, nil
|
||||
}
|
||||
|
||||
// Otherwise we'll return the regular listeners.
|
||||
return getListeners()
|
||||
}
|
||||
|
||||
// Initialize, and register our implementation of the gRPC interface
|
||||
// exported by the rpcServer.
|
||||
rpcServer, err := newRPCServer(
|
||||
cfg, server, macaroonService, cfg.SubRPCServers, serverOpts,
|
||||
restDialOpts, restProxyDest, atplManager, server.invoices,
|
||||
tower, restListen, rpcListeners, chainedAcceptor,
|
||||
// Now we have created all dependencies necessary to populate and
|
||||
// start the RPC server.
|
||||
err = rpcServer.addDeps(
|
||||
server, macaroonService, cfg.SubRPCServers, atplManager,
|
||||
server.invoices, tower, chainedAcceptor,
|
||||
)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("unable to create RPC server: %v", err)
|
||||
err := fmt.Errorf("unable to add deps to RPC server: %v", err)
|
||||
ltndLog.Error(err)
|
||||
return err
|
||||
}
|
||||
@ -750,6 +748,9 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
||||
}
|
||||
defer rpcServer.Stop()
|
||||
|
||||
// We transition the RPC state to Active, as the RPC server is up.
|
||||
interceptorChain.SetRPCActive()
|
||||
|
||||
// If we're not in regtest or simnet mode, We'll wait until we're fully
|
||||
// synced to continue the start up of the remainder of the daemon. This
|
||||
// ensures that we don't accept any possibly invalid state transitions, or
|
||||
@ -1124,14 +1125,9 @@ type WalletUnlockParams struct {
|
||||
MacResponseChan chan []byte
|
||||
}
|
||||
|
||||
// waitForWalletPassword will spin up gRPC and REST endpoints for the
|
||||
// WalletUnlocker server, and block until a password is provided by
|
||||
// the user to this RPC server.
|
||||
func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
|
||||
serverOpts []grpc.ServerOption, restDialOpts []grpc.DialOption,
|
||||
restProxyDest string, restListen func(net.Addr) (net.Listener, error),
|
||||
getListeners rpcListeners) (*WalletUnlockParams, func(), error) {
|
||||
|
||||
// createWalletUnlockerService creates a WalletUnlockerService from the passed
|
||||
// config.
|
||||
func createWalletUnlockerService(cfg *Config) *walletunlocker.UnlockerService {
|
||||
chainConfig := cfg.Bitcoin
|
||||
if cfg.registeredChains.PrimaryChain() == chainreg.LitecoinChain {
|
||||
chainConfig = cfg.Litecoin
|
||||
@ -1143,36 +1139,16 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
|
||||
macaroonFiles := []string{
|
||||
cfg.AdminMacPath, cfg.ReadMacPath, cfg.InvoiceMacPath,
|
||||
}
|
||||
pwService := walletunlocker.New(
|
||||
return walletunlocker.New(
|
||||
chainConfig.ChainDir, cfg.ActiveNetParams.Params,
|
||||
!cfg.SyncFreelist, macaroonFiles, cfg.DB.Bolt.DBTimeout,
|
||||
cfg.ResetWalletTransactions,
|
||||
)
|
||||
}
|
||||
|
||||
// Set up a new PasswordService, which will listen for passwords
|
||||
// provided over RPC.
|
||||
grpcServer := grpc.NewServer(serverOpts...)
|
||||
lnrpc.RegisterWalletUnlockerServer(grpcServer, pwService)
|
||||
|
||||
var shutdownFuncs []func()
|
||||
shutdown := func() {
|
||||
// Make sure nothing blocks on reading on the macaroon channel,
|
||||
// otherwise the GracefulStop below will never return.
|
||||
close(pwService.MacResponseChan)
|
||||
|
||||
for _, shutdownFn := range shutdownFuncs {
|
||||
shutdownFn()
|
||||
}
|
||||
}
|
||||
shutdownFuncs = append(shutdownFuncs, grpcServer.GracefulStop)
|
||||
|
||||
// Start a gRPC server listening for HTTP/2 connections, solely used
|
||||
// for getting the encryption password from the client.
|
||||
listeners, cleanup, err := getListeners()
|
||||
if err != nil {
|
||||
return nil, shutdown, err
|
||||
}
|
||||
shutdownFuncs = append(shutdownFuncs, cleanup)
|
||||
// startGrpcListen starts the GRPC server on the passed listeners.
|
||||
func startGrpcListen(cfg *Config, grpcServer *grpc.Server,
|
||||
listeners []*ListenerWithSignal) error {
|
||||
|
||||
// Use a WaitGroup so we can be sure the instructions on how to input the
|
||||
// password is the last thing to be printed to the console.
|
||||
@ -1181,8 +1157,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
|
||||
for _, lis := range listeners {
|
||||
wg.Add(1)
|
||||
go func(lis *ListenerWithSignal) {
|
||||
rpcsLog.Infof("Password RPC server listening on %s",
|
||||
lis.Addr())
|
||||
rpcsLog.Infof("RPC server listening on %s", lis.Addr())
|
||||
|
||||
// Close the ready chan to indicate we are listening.
|
||||
close(lis.Ready)
|
||||
@ -1192,29 +1167,102 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
|
||||
}(lis)
|
||||
}
|
||||
|
||||
// Start a REST proxy for our gRPC server above.
|
||||
// If Prometheus monitoring is enabled, start the Prometheus exporter.
|
||||
if cfg.Prometheus.Enabled() {
|
||||
err := monitoring.ExportPrometheusMetrics(
|
||||
grpcServer, cfg.Prometheus,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for gRPC servers to be up running.
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// startRestProxy starts the given REST proxy on the listeners found in the
|
||||
// config.
|
||||
func startRestProxy(cfg *Config, rpcServer *rpcServer, restDialOpts []grpc.DialOption,
|
||||
restListen func(net.Addr) (net.Listener, error)) (func(), error) {
|
||||
|
||||
// We use the first RPC listener as the destination for our REST proxy.
|
||||
// If the listener is set to listen on all interfaces, we replace it
|
||||
// with localhost, as we cannot dial it directly.
|
||||
restProxyDest := cfg.RPCListeners[0].String()
|
||||
switch {
|
||||
case strings.Contains(restProxyDest, "0.0.0.0"):
|
||||
restProxyDest = strings.Replace(
|
||||
restProxyDest, "0.0.0.0", "127.0.0.1", 1,
|
||||
)
|
||||
|
||||
case strings.Contains(restProxyDest, "[::]"):
|
||||
restProxyDest = strings.Replace(
|
||||
restProxyDest, "[::]", "[::1]", 1,
|
||||
)
|
||||
}
|
||||
|
||||
var shutdownFuncs []func()
|
||||
shutdown := func() {
|
||||
for _, shutdownFn := range shutdownFuncs {
|
||||
shutdownFn()
|
||||
}
|
||||
}
|
||||
|
||||
// Start a REST proxy for our gRPC server.
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
shutdownFuncs = append(shutdownFuncs, cancel)
|
||||
|
||||
mux := proxy.NewServeMux()
|
||||
// We'll set up a proxy that will forward REST calls to the GRPC
|
||||
// server.
|
||||
//
|
||||
// The default JSON marshaler of the REST proxy only sets OrigName to
|
||||
// true, which instructs it to use the same field names as specified in
|
||||
// the proto file and not switch to camel case. What we also want is
|
||||
// that the marshaler prints all values, even if they are falsey.
|
||||
customMarshalerOption := proxy.WithMarshalerOption(
|
||||
proxy.MIMEWildcard, &proxy.JSONPb{
|
||||
OrigName: true,
|
||||
EmitDefaults: true,
|
||||
},
|
||||
)
|
||||
mux := proxy.NewServeMux(customMarshalerOption)
|
||||
|
||||
err = lnrpc.RegisterWalletUnlockerHandlerFromEndpoint(
|
||||
// Register both services with the REST proxy.
|
||||
err := lnrpc.RegisterWalletUnlockerHandlerFromEndpoint(
|
||||
ctx, mux, restProxyDest, restDialOpts,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, shutdown, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
srv := &http.Server{Handler: allowCORS(mux, cfg.RestCORS)}
|
||||
err = rpcServer.RegisterWithRestProxy(
|
||||
ctx, mux, restDialOpts, restProxyDest,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, restEndpoint := range restEndpoints {
|
||||
// Wrap the default grpc-gateway handler with the WebSocket handler.
|
||||
restHandler := lnrpc.NewWebSocketProxy(mux, rpcsLog)
|
||||
|
||||
// Use a WaitGroup so we can be sure the instructions on how to input the
|
||||
// password is the last thing to be printed to the console.
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Now spin up a network listener for each requested port and start a
|
||||
// goroutine that serves REST with the created mux there.
|
||||
for _, restEndpoint := range cfg.RESTListeners {
|
||||
lis, err := restListen(restEndpoint)
|
||||
if err != nil {
|
||||
ltndLog.Errorf("Password gRPC proxy unable to listen "+
|
||||
"on %s", restEndpoint)
|
||||
return nil, shutdown, err
|
||||
ltndLog.Errorf("gRPC proxy unable to listen on %s",
|
||||
restEndpoint)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
shutdownFuncs = append(shutdownFuncs, func() {
|
||||
err := lis.Close()
|
||||
if err != nil {
|
||||
@ -1225,16 +1273,38 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
rpcsLog.Infof("Password gRPC proxy started at %s",
|
||||
lis.Addr())
|
||||
rpcsLog.Infof("gRPC proxy started at %s", lis.Addr())
|
||||
|
||||
// Create our proxy chain now. A request will pass
|
||||
// through the following chain:
|
||||
// req ---> CORS handler --> WS proxy --->
|
||||
// REST proxy --> gRPC endpoint
|
||||
corsHandler := allowCORS(restHandler, cfg.RestCORS)
|
||||
|
||||
wg.Done()
|
||||
_ = srv.Serve(lis)
|
||||
err := http.Serve(lis, corsHandler)
|
||||
if err != nil && !lnrpc.IsClosedConnError(err) {
|
||||
rpcsLog.Error(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Wait for gRPC and REST servers to be up running.
|
||||
// Wait for REST servers to be up running.
|
||||
wg.Wait()
|
||||
|
||||
return shutdown, nil
|
||||
}
|
||||
|
||||
// waitForWalletPassword blocks until a password is provided by the user to
|
||||
// this RPC server.
|
||||
func waitForWalletPassword(cfg *Config,
|
||||
pwService *walletunlocker.UnlockerService) (*WalletUnlockParams, error) {
|
||||
|
||||
chainConfig := cfg.Bitcoin
|
||||
if cfg.registeredChains.PrimaryChain() == chainreg.LitecoinChain {
|
||||
chainConfig = cfg.Litecoin
|
||||
}
|
||||
|
||||
// Wait for user to provide the password.
|
||||
ltndLog.Infof("Waiting for wallet encryption password. Use `lncli " +
|
||||
"create` to create a wallet, `lncli unlock` to unlock an " +
|
||||
@ -1259,7 +1329,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
|
||||
// version, then we'll return an error as we don't understand
|
||||
// this.
|
||||
if cipherSeed.InternalVersion != keychain.KeyDerivationVersion {
|
||||
return nil, shutdown, fmt.Errorf("invalid internal "+
|
||||
return nil, fmt.Errorf("invalid internal "+
|
||||
"seed version %v, current version is %v",
|
||||
cipherSeed.InternalVersion,
|
||||
keychain.KeyDerivationVersion)
|
||||
@ -1286,7 +1356,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
|
||||
ltndLog.Errorf("Could not unload new "+
|
||||
"wallet: %v", err)
|
||||
}
|
||||
return nil, shutdown, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// For new wallets, the ResetWalletTransactions flag is a no-op.
|
||||
@ -1304,7 +1374,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
|
||||
UnloadWallet: loader.UnloadWallet,
|
||||
StatelessInit: initMsg.StatelessInit,
|
||||
MacResponseChan: pwService.MacResponseChan,
|
||||
}, shutdown, nil
|
||||
}, nil
|
||||
|
||||
// The wallet has already been created in the past, and is simply being
|
||||
// unlocked. So we'll just return these passphrases.
|
||||
@ -1328,10 +1398,10 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
|
||||
UnloadWallet: unlockMsg.UnloadWallet,
|
||||
StatelessInit: unlockMsg.StatelessInit,
|
||||
MacResponseChan: pwService.MacResponseChan,
|
||||
}, shutdown, nil
|
||||
}, nil
|
||||
|
||||
case <-signal.ShutdownChannel():
|
||||
return nil, shutdown, fmt.Errorf("shutting down")
|
||||
return nil, fmt.Errorf("shutting down")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,13 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// ServerShell is a shell struct holding a reference to the actual sub-server.
|
||||
// It is used to register the gRPC sub-server with the root server before we
|
||||
// have the necessary dependencies to populate the actual sub-server.
|
||||
type ServerShell struct {
|
||||
AutopilotServer
|
||||
}
|
||||
|
||||
// Server is a sub-server of the main RPC server: the autopilot RPC. This sub
|
||||
// RPC server allows external callers to access the status of the autopilot
|
||||
// currently active within lnd, as well as configuring it at runtime.
|
||||
@ -118,11 +125,11 @@ func (s *Server) Name() string {
|
||||
// is called, each sub-server won't be able to have
|
||||
// requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// We make sure that we register it with the main gRPC server to ensure
|
||||
// all our methods are routed properly.
|
||||
RegisterAutopilotServer(grpcServer, s)
|
||||
RegisterAutopilotServer(grpcServer, r)
|
||||
|
||||
log.Debugf("Autopilot RPC server successfully register with root " +
|
||||
"gRPC server")
|
||||
@ -134,8 +141,8 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// RPC server to register itself with the main REST mux server. Until this is
|
||||
// called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (s *Server) RegisterWithRestServer(ctx context.Context,
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
|
||||
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
|
||||
|
||||
// We make sure that we register it with the main REST server to ensure
|
||||
@ -152,6 +159,25 @@ func (s *Server) RegisterWithRestServer(ctx context.Context,
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSubServer populates the subserver's dependencies using the passed
|
||||
// SubServerConfigDispatcher. This method should fully initialize the
|
||||
// sub-server instance, making it ready for action. It returns the macaroon
|
||||
// permissions that the sub-server wishes to pass on to the root server for all
|
||||
// methods routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
subServer, macPermissions, err := createNewSubServer(configRegistry)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
r.AutopilotServer = subServer
|
||||
return subServer, macPermissions, nil
|
||||
}
|
||||
|
||||
// Status returns the current status of the autopilot agent.
|
||||
//
|
||||
// NOTE: Part of the AutopilotServer interface.
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
// that is meant for us in the config dispatcher, then we'll exit with an
|
||||
// error.
|
||||
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
*Server, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
// We'll attempt to look up the config that we expect, according to our
|
||||
// subServerName name. If we can't find this, then we'll exit with an
|
||||
@ -48,9 +48,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
func init() {
|
||||
subServer := &lnrpc.SubServerDriver{
|
||||
SubServerName: subServerName,
|
||||
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer,
|
||||
lnrpc.MacaroonPerms, error) {
|
||||
return createNewSubServer(c)
|
||||
NewGrpcHandler: func() lnrpc.GrpcHandler {
|
||||
return &ServerShell{}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -72,6 +72,13 @@ var (
|
||||
"still in the process of starting")
|
||||
)
|
||||
|
||||
// ServerShell is a shell struct holding a reference to the actual sub-server.
|
||||
// It is used to register the gRPC sub-server with the root server before we
|
||||
// have the necessary dependencies to populate the actual sub-server.
|
||||
type ServerShell struct {
|
||||
ChainNotifierServer
|
||||
}
|
||||
|
||||
// Server is a sub-server of the main RPC server: the chain notifier RPC. This
|
||||
// RPC sub-server allows external callers to access the full chain notifier
|
||||
// capabilities of lnd. This allows callers to create custom protocols, external
|
||||
@ -172,11 +179,11 @@ func (s *Server) Name() string {
|
||||
// sub-server to register itself with the main gRPC root server. Until this is
|
||||
// called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// We make sure that we register it with the main gRPC server to ensure
|
||||
// all our methods are routed properly.
|
||||
RegisterChainNotifierServer(grpcServer, s)
|
||||
RegisterChainNotifierServer(grpcServer, r)
|
||||
|
||||
log.Debug("ChainNotifier RPC server successfully register with root " +
|
||||
"gRPC server")
|
||||
@ -188,8 +195,8 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// RPC server to register itself with the main REST mux server. Until this is
|
||||
// called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (s *Server) RegisterWithRestServer(ctx context.Context,
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
|
||||
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
|
||||
|
||||
// We make sure that we register it with the main REST server to ensure
|
||||
@ -206,6 +213,25 @@ func (s *Server) RegisterWithRestServer(ctx context.Context,
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSubServer populates the subserver's dependencies using the passed
|
||||
// SubServerConfigDispatcher. This method should fully initialize the
|
||||
// sub-server instance, making it ready for action. It returns the macaroon
|
||||
// permissions that the sub-server wishes to pass on to the root server for all
|
||||
// methods routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
subServer, macPermissions, err := createNewSubServer(configRegistry)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
r.ChainNotifierServer = subServer
|
||||
return subServer, macPermissions, nil
|
||||
}
|
||||
|
||||
// RegisterConfirmationsNtfn is a synchronous response-streaming RPC that
|
||||
// registers an intent for a client to be notified once a confirmation request
|
||||
// has reached its required number of confirmations on-chain.
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
// the config that is meant for us in the config dispatcher, then we'll exit
|
||||
// with an error.
|
||||
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
*Server, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
// We'll attempt to look up the config that we expect, according to our
|
||||
// subServerName name. If we can't find this, then we'll exit with an
|
||||
@ -55,10 +55,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
func init() {
|
||||
subServer := &lnrpc.SubServerDriver{
|
||||
SubServerName: subServerName,
|
||||
New: func(c lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
return createNewSubServer(c)
|
||||
NewGrpcHandler: func() lnrpc.GrpcHandler {
|
||||
return &ServerShell{}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
// that is meant for us in the config dispatcher, then we'll exit with an
|
||||
// error.
|
||||
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
*Server, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
// We'll attempt to look up the config that we expect, according to our
|
||||
// subServerName name. If we can't find this, then we'll exit with an
|
||||
@ -40,9 +40,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
func init() {
|
||||
subServer := &lnrpc.SubServerDriver{
|
||||
SubServerName: subServerName,
|
||||
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer,
|
||||
lnrpc.MacaroonPerms, error) {
|
||||
return createNewSubServer(c)
|
||||
NewGrpcHandler: func() lnrpc.GrpcHandler {
|
||||
return &ServerShell{}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,13 @@ var (
|
||||
DefaultInvoicesMacFilename = "invoices.macaroon"
|
||||
)
|
||||
|
||||
// ServerShell is a shell struct holding a reference to the actual sub-server.
|
||||
// It is used to register the gRPC sub-server with the root server before we
|
||||
// have the necessary dependencies to populate the actual sub-server.
|
||||
type ServerShell struct {
|
||||
InvoicesServer
|
||||
}
|
||||
|
||||
// Server is a sub-server of the main RPC server: the invoices RPC. This sub
|
||||
// RPC server allows external callers to access the status of the invoices
|
||||
// currently active within lnd, as well as configuring it at runtime.
|
||||
@ -157,11 +164,11 @@ func (s *Server) Name() string {
|
||||
// RPC server to register itself with the main gRPC root server. Until this is
|
||||
// called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// We make sure that we register it with the main gRPC server to ensure
|
||||
// all our methods are routed properly.
|
||||
RegisterInvoicesServer(grpcServer, s)
|
||||
RegisterInvoicesServer(grpcServer, r)
|
||||
|
||||
log.Debugf("Invoices RPC server successfully registered with root " +
|
||||
"gRPC server")
|
||||
@ -173,8 +180,8 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// RPC server to register itself with the main REST mux server. Until this is
|
||||
// called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (s *Server) RegisterWithRestServer(ctx context.Context,
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
|
||||
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
|
||||
|
||||
// We make sure that we register it with the main REST server to ensure
|
||||
@ -191,6 +198,25 @@ func (s *Server) RegisterWithRestServer(ctx context.Context,
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSubServer populates the subserver's dependencies using the passed
|
||||
// SubServerConfigDispatcher. This method should fully initialize the
|
||||
// sub-server instance, making it ready for action. It returns the macaroon
|
||||
// permissions that the sub-server wishes to pass on to the root server for all
|
||||
// methods routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
subServer, macPermissions, err := createNewSubServer(configRegistry)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
r.InvoicesServer = subServer
|
||||
return subServer, macPermissions, nil
|
||||
}
|
||||
|
||||
// SubscribeSingleInvoice returns a uni-directional stream (server -> client)
|
||||
// for notifying the client of state changes for a specified invoice.
|
||||
func (s *Server) SubscribeSingleInvoice(req *SubscribeSingleInvoiceRequest,
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
// config that is meant for us in the config dispatcher, then we'll exit with
|
||||
// an error.
|
||||
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
*Server, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
// We'll attempt to look up the config that we expect, according to our
|
||||
// subServerName name. If we can't find this, then we'll exit with an
|
||||
@ -46,8 +46,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
func init() {
|
||||
subServer := &lnrpc.SubServerDriver{
|
||||
SubServerName: subServerName,
|
||||
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
return createNewSubServer(c)
|
||||
NewGrpcHandler: func() lnrpc.GrpcHandler {
|
||||
return &ServerShell{}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -129,6 +129,13 @@ var (
|
||||
DefaultRouterMacFilename = "router.macaroon"
|
||||
)
|
||||
|
||||
// ServerShell a is shell struct holding a reference to the actual sub-server.
|
||||
// It is used to register the gRPC sub-server with the root server before we
|
||||
// have the necessary dependencies to populate the actual sub-server.
|
||||
type ServerShell struct {
|
||||
RouterServer
|
||||
}
|
||||
|
||||
// Server is a stand alone sub RPC server which exposes functionality that
|
||||
// allows clients to route arbitrary payment through the Lightning Network.
|
||||
type Server struct {
|
||||
@ -233,11 +240,11 @@ func (s *Server) Name() string {
|
||||
// sub RPC server to register itself with the main gRPC root server. Until this
|
||||
// is called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// We make sure that we register it with the main gRPC server to ensure
|
||||
// all our methods are routed properly.
|
||||
RegisterRouterServer(grpcServer, s)
|
||||
RegisterRouterServer(grpcServer, r)
|
||||
|
||||
log.Debugf("Router RPC server successfully register with root gRPC " +
|
||||
"server")
|
||||
@ -249,8 +256,8 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// RPC server to register itself with the main REST mux server. Until this is
|
||||
// called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (s *Server) RegisterWithRestServer(ctx context.Context,
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
|
||||
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
|
||||
|
||||
// We make sure that we register it with the main REST server to ensure
|
||||
@ -267,6 +274,25 @@ func (s *Server) RegisterWithRestServer(ctx context.Context,
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSubServer populates the subserver's dependencies using the passed
|
||||
// SubServerConfigDispatcher. This method should fully initialize the
|
||||
// sub-server instance, making it ready for action. It returns the macaroon
|
||||
// permissions that the sub-server wishes to pass on to the root server for all
|
||||
// methods routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
subServer, macPermissions, err := createNewSubServer(configRegistry)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
r.RouterServer = subServer
|
||||
return subServer, macPermissions, nil
|
||||
}
|
||||
|
||||
// SendPaymentV2 attempts to route a payment described by the passed
|
||||
// PaymentRequest to the final destination. If we are unable to route the
|
||||
// payment, or cannot find a route that satisfies the constraints in the
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
// config that is meant for us in the config dispatcher, then we'll exit with
|
||||
// an error.
|
||||
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
*Server, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
// We'll attempt to look up the config that we expect, according to our
|
||||
// subServerName name. If we can't find this, then we'll exit with an
|
||||
@ -55,10 +55,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
func init() {
|
||||
subServer := &lnrpc.SubServerDriver{
|
||||
SubServerName: subServerName,
|
||||
New: func(c lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
return createNewSubServer(c)
|
||||
NewGrpcHandler: func() lnrpc.GrpcHandler {
|
||||
return &ServerShell{}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -76,6 +76,13 @@ var (
|
||||
DefaultSignerMacFilename = "signer.macaroon"
|
||||
)
|
||||
|
||||
// ServerShell is a shell struct holding a reference to the actual sub-server.
|
||||
// It is used to register the gRPC sub-server with the root server before we
|
||||
// have the necessary dependencies to populate the actual sub-server.
|
||||
type ServerShell struct {
|
||||
SignerServer
|
||||
}
|
||||
|
||||
// Server is a sub-server of the main RPC server: the signer RPC. This sub RPC
|
||||
// server allows external callers to access the full signing capabilities of
|
||||
// lnd. This allows callers to create custom protocols, external to lnd, even
|
||||
@ -167,11 +174,11 @@ func (s *Server) Name() string {
|
||||
// is called, each sub-server won't be able to have
|
||||
// requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// We make sure that we register it with the main gRPC server to ensure
|
||||
// all our methods are routed properly.
|
||||
RegisterSignerServer(grpcServer, s)
|
||||
RegisterSignerServer(grpcServer, r)
|
||||
|
||||
log.Debugf("Signer RPC server successfully register with root gRPC " +
|
||||
"server")
|
||||
@ -183,8 +190,8 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// RPC server to register itself with the main REST mux server. Until this is
|
||||
// called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (s *Server) RegisterWithRestServer(ctx context.Context,
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
|
||||
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
|
||||
|
||||
// We make sure that we register it with the main REST server to ensure
|
||||
@ -201,6 +208,25 @@ func (s *Server) RegisterWithRestServer(ctx context.Context,
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSubServer populates the subserver's dependencies using the passed
|
||||
// SubServerConfigDispatcher. This method should fully initialize the
|
||||
// sub-server instance, making it ready for action. It returns the macaroon
|
||||
// permissions that the sub-server wishes to pass on to the root server for all
|
||||
// methods routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
subServer, macPermissions, err := createNewSubServer(configRegistry)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
r.SignerServer = subServer
|
||||
return subServer, macPermissions, nil
|
||||
}
|
||||
|
||||
// SignOutputRaw generates a signature for the passed transaction according to
|
||||
// the data within the passed SignReq. If we're unable to find the keys that
|
||||
// correspond to the KeyLocators in the SignReq then we'll return an error.
|
||||
|
@ -32,7 +32,14 @@ type SubServer interface {
|
||||
// Name returns a unique string representation of the sub-server. This
|
||||
// can be used to identify the sub-server and also de-duplicate them.
|
||||
Name() string
|
||||
}
|
||||
|
||||
// GrpcHandler is the interface that should be registered with the root gRPC
|
||||
// server, and is the interface that implements the subserver's defined RPC.
|
||||
// Before the actual sub server has been created, this will be an empty shell
|
||||
// allowing us to start the gRPC server before we have all the dependencies
|
||||
// needed to create the subserver itself.
|
||||
type GrpcHandler interface {
|
||||
// RegisterWithRootServer will be called by the root gRPC server to
|
||||
// direct a sub RPC server to register itself with the main gRPC root
|
||||
// server. Until this is called, each sub-server won't be able to have
|
||||
@ -45,6 +52,14 @@ type SubServer interface {
|
||||
// routed towards it.
|
||||
RegisterWithRestServer(context.Context, *runtime.ServeMux, string,
|
||||
[]grpc.DialOption) error
|
||||
|
||||
// CreateSubServer populates the subserver's dependencies using the
|
||||
// passed SubServerConfigDispatcher. This method should fully
|
||||
// initialize the sub-server instance, making it ready for action. It
|
||||
// returns the macaroon permissions that the sub-server wishes to pass
|
||||
// on to the root server for all methods routed towards it.
|
||||
CreateSubServer(subCfgs SubServerConfigDispatcher) (SubServer,
|
||||
MacaroonPerms, error)
|
||||
}
|
||||
|
||||
// SubServerConfigDispatcher is an interface that all sub-servers will use to
|
||||
@ -60,22 +75,18 @@ type SubServerConfigDispatcher interface {
|
||||
}
|
||||
|
||||
// SubServerDriver is a template struct that allows the root server to create a
|
||||
// sub-server with minimal knowledge. The root server only need a fully
|
||||
// populated SubServerConfigDispatcher and with the aide of the
|
||||
// RegisterSubServers method, it's able to create and initialize all
|
||||
// sub-servers.
|
||||
// sub-server gRPC handler with minimal knowledge.
|
||||
type SubServerDriver struct {
|
||||
// SubServerName is the full name of a sub-sever.
|
||||
//
|
||||
// NOTE: This MUST be unique.
|
||||
SubServerName string
|
||||
|
||||
// New creates, and fully initializes a new sub-server instance with
|
||||
// the aide of the SubServerConfigDispatcher. This closure should
|
||||
// return the SubServer, ready for action, along with the set of
|
||||
// macaroon permissions that the sub-server wishes to pass on to the
|
||||
// root server for all methods routed towards it.
|
||||
New func(subCfgs SubServerConfigDispatcher) (SubServer, MacaroonPerms, error)
|
||||
// NewGrpcHandler creates a a new sub-server gRPC interface that can be
|
||||
// registered with the root gRPC server. It is not expected that the
|
||||
// SubServer is ready for operation before its CreateSubServer and
|
||||
// Start methods have been called.
|
||||
NewGrpcHandler func() GrpcHandler
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -9,10 +9,8 @@ import (
|
||||
func init() {
|
||||
subServer := &lnrpc.SubServerDriver{
|
||||
SubServerName: subServerName,
|
||||
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer,
|
||||
lnrpc.MacaroonPerms, error) {
|
||||
|
||||
return &Server{}, macPermissions, nil
|
||||
NewGrpcHandler: func() lnrpc.GrpcHandler {
|
||||
return &ServerShell{}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
"github.com/lightningnetwork/lnd/build"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"google.golang.org/grpc"
|
||||
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||
)
|
||||
@ -18,6 +19,13 @@ var macPermissions = map[string][]bakery.Op{
|
||||
}},
|
||||
}
|
||||
|
||||
// ServerShell is a shell struct holding a reference to the actual sub-server.
|
||||
// It is used to register the gRPC sub-server with the root server before we
|
||||
// have the necessary dependencies to populate the actual sub-server.
|
||||
type ServerShell struct {
|
||||
VersionerServer
|
||||
}
|
||||
|
||||
// Server is an rpc server that supports querying for information about the
|
||||
// running binary.
|
||||
type Server struct{}
|
||||
@ -48,9 +56,9 @@ func (s *Server) Name() string {
|
||||
// sub RPC server to register itself with the main gRPC root server. Until this
|
||||
// is called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
RegisterVersionerServer(grpcServer, s)
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
RegisterVersionerServer(grpcServer, r)
|
||||
|
||||
log.Debugf("Versioner RPC server successfully registered with root " +
|
||||
"gRPC server")
|
||||
@ -62,8 +70,8 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// RPC server to register itself with the main REST mux server. Until this is
|
||||
// called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (s *Server) RegisterWithRestServer(ctx context.Context,
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
|
||||
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
|
||||
|
||||
// We make sure that we register it with the main REST server to ensure
|
||||
@ -80,6 +88,21 @@ func (s *Server) RegisterWithRestServer(ctx context.Context,
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSubServer populates the subserver's dependencies using the passed
|
||||
// SubServerConfigDispatcher. This method should fully initialize the
|
||||
// sub-server instance, making it ready for action. It returns the macaroon
|
||||
// permissions that the sub-server wishes to pass on to the root server for all
|
||||
// methods routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) CreateSubServer(_ lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
subServer := &Server{}
|
||||
r.VersionerServer = subServer
|
||||
return subServer, macPermissions, nil
|
||||
}
|
||||
|
||||
// GetVersion returns information about the compiled binary.
|
||||
func (s *Server) GetVersion(_ context.Context,
|
||||
_ *VersionRequest) (*Version, error) {
|
||||
|
@ -12,7 +12,9 @@ import (
|
||||
// sub server given the main config dispatcher method. If we're unable to find
|
||||
// the config that is meant for us in the config dispatcher, then we'll exit
|
||||
// with an error.
|
||||
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
*WalletKit, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
// We'll attempt to look up the config that we expect, according to our
|
||||
// subServerName name. If we can't find this, then we'll exit with an
|
||||
// error, as we're unable to properly initialize ourselves without this
|
||||
@ -67,8 +69,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (lnrpc.S
|
||||
func init() {
|
||||
subServer := &lnrpc.SubServerDriver{
|
||||
SubServerName: subServerName,
|
||||
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
return createNewSubServer(c)
|
||||
NewGrpcHandler: func() lnrpc.GrpcHandler {
|
||||
return &ServerShell{}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -148,6 +148,13 @@ var (
|
||||
// an empty label.
|
||||
var ErrZeroLabel = errors.New("cannot label transaction with empty label")
|
||||
|
||||
// ServerShell is a shell struct holding a reference to the actual sub-server.
|
||||
// It is used to register the gRPC sub-server with the root server before we
|
||||
// have the necessary dependencies to populate the actual sub-server.
|
||||
type ServerShell struct {
|
||||
WalletKitServer
|
||||
}
|
||||
|
||||
// WalletKit is a sub-RPC server that exposes a tool kit which allows clients
|
||||
// to execute common wallet operations. This includes requesting new addresses,
|
||||
// keys (for contracts!), and publishing transactions.
|
||||
@ -233,11 +240,11 @@ func (w *WalletKit) Name() string {
|
||||
// sub RPC server to register itself with the main gRPC root server. Until this
|
||||
// is called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (w *WalletKit) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// We make sure that we register it with the main gRPC server to ensure
|
||||
// all our methods are routed properly.
|
||||
RegisterWalletKitServer(grpcServer, w)
|
||||
RegisterWalletKitServer(grpcServer, r)
|
||||
|
||||
log.Debugf("WalletKit RPC server successfully registered with " +
|
||||
"root gRPC server")
|
||||
@ -249,8 +256,8 @@ func (w *WalletKit) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// RPC server to register itself with the main REST mux server. Until this is
|
||||
// called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (w *WalletKit) RegisterWithRestServer(ctx context.Context,
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
|
||||
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
|
||||
|
||||
// We make sure that we register it with the main REST server to ensure
|
||||
@ -267,6 +274,25 @@ func (w *WalletKit) RegisterWithRestServer(ctx context.Context,
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSubServer populates the subserver's dependencies using the passed
|
||||
// SubServerConfigDispatcher. This method should fully initialize the
|
||||
// sub-server instance, making it ready for action. It returns the macaroon
|
||||
// permissions that the sub-server wishes to pass on to the root server for all
|
||||
// methods routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
subServer, macPermissions, err := createNewSubServer(configRegistry)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
r.WalletKitServer = subServer
|
||||
return subServer, macPermissions, nil
|
||||
}
|
||||
|
||||
// ListUnspent returns useful information about each unspent output owned by the
|
||||
// wallet, as reported by the underlying `ListUnspentWitness`; the information
|
||||
// returned is: outpoint, amount in satoshis, address, address type,
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
// that is meant for us in the config dispatcher, then we'll exit with an
|
||||
// error.
|
||||
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
*Handler, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
// We'll attempt to look up the config that we expect, according to our
|
||||
// subServerName name. If we can't find this, then we'll exit with an
|
||||
@ -40,9 +40,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
func init() {
|
||||
subServer := &lnrpc.SubServerDriver{
|
||||
SubServerName: subServerName,
|
||||
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer,
|
||||
lnrpc.MacaroonPerms, error) {
|
||||
return createNewSubServer(c)
|
||||
NewGrpcHandler: func() lnrpc.GrpcHandler {
|
||||
return &ServerShell{}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ package watchtowerrpc
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
fmt "fmt"
|
||||
"fmt"
|
||||
|
||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
@ -35,6 +35,13 @@ var (
|
||||
ErrTowerNotActive = errors.New("watchtower not active")
|
||||
)
|
||||
|
||||
// ServerShell is a shell struct holding a reference to the actual sub-server.
|
||||
// It is used to register the gRPC sub-server with the root server before we
|
||||
// have the necessary dependencies to populate the actual sub-server.
|
||||
type ServerShell struct {
|
||||
WatchtowerServer
|
||||
}
|
||||
|
||||
// Handler is the RPC server we'll use to interact with the backing active
|
||||
// watchtower.
|
||||
type Handler struct {
|
||||
@ -80,11 +87,11 @@ func (c *Handler) Name() string {
|
||||
// RPC server to register itself with the main gRPC root server. Until this is
|
||||
// called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (c *Handler) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// We make sure that we register it with the main gRPC server to ensure
|
||||
// all our methods are routed properly.
|
||||
RegisterWatchtowerServer(grpcServer, c)
|
||||
RegisterWatchtowerServer(grpcServer, r)
|
||||
|
||||
log.Debugf("Watchtower RPC server successfully register with root " +
|
||||
"gRPC server")
|
||||
@ -96,8 +103,8 @@ func (c *Handler) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// RPC server to register itself with the main REST mux server. Until this is
|
||||
// called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (c *Handler) RegisterWithRestServer(ctx context.Context,
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
|
||||
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
|
||||
|
||||
// We make sure that we register it with the main REST server to ensure
|
||||
@ -114,6 +121,25 @@ func (c *Handler) RegisterWithRestServer(ctx context.Context,
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSubServer populates the subserver's dependencies using the passed
|
||||
// SubServerConfigDispatcher. This method should fully initialize the
|
||||
// sub-server instance, making it ready for action. It returns the macaroon
|
||||
// permissions that the sub-server wishes to pass on to the root server for all
|
||||
// methods routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
subServer, macPermissions, err := createNewSubServer(configRegistry)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
r.WatchtowerServer = subServer
|
||||
return subServer, macPermissions, nil
|
||||
}
|
||||
|
||||
// AddTower adds a new watchtower reachable at the given address and considers
|
||||
// it for new sessions. If the watchtower already exists, then any new addresses
|
||||
// included will be considered when dialing it for session negotiations and
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
// that is meant for us in the config dispatcher, then we'll exit with an
|
||||
// error.
|
||||
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
*WatchtowerClient, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
// We'll attempt to look up the config that we expect, according to our
|
||||
// subServerName name. If we can't find this, then we'll exit with an
|
||||
@ -46,9 +46,8 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
func init() {
|
||||
subServer := &lnrpc.SubServerDriver{
|
||||
SubServerName: subServerName,
|
||||
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer,
|
||||
lnrpc.MacaroonPerms, error) {
|
||||
return createNewSubServer(c)
|
||||
NewGrpcHandler: func() lnrpc.GrpcHandler {
|
||||
return &ServerShell{}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -64,6 +64,13 @@ var (
|
||||
ErrWtclientNotActive = errors.New("watchtower client not active")
|
||||
)
|
||||
|
||||
// ServerShell is a shell struct holding a reference to the actual sub-server.
|
||||
// It is used to register the gRPC sub-server with the root server before we
|
||||
// have the necessary dependencies to populate the actual sub-server.
|
||||
type ServerShell struct {
|
||||
WatchtowerClientServer
|
||||
}
|
||||
|
||||
// WatchtowerClient is the RPC server we'll use to interact with the backing
|
||||
// active watchtower client.
|
||||
//
|
||||
@ -112,14 +119,11 @@ func (c *WatchtowerClient) Name() string {
|
||||
// RPC server to register itself with the main gRPC root server. Until this is
|
||||
// called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (c *WatchtowerClient) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
|
||||
// We make sure that we register it with the main gRPC server to ensure
|
||||
// all our methods are routed properly.
|
||||
RegisterWatchtowerClientServer(grpcServer, c)
|
||||
|
||||
c.cfg.Log.Debugf("WatchtowerClient RPC server successfully registered " +
|
||||
"with root gRPC server")
|
||||
RegisterWatchtowerClientServer(grpcServer, r)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -128,8 +132,8 @@ func (c *WatchtowerClient) RegisterWithRootServer(grpcServer *grpc.Server) error
|
||||
// RPC server to register itself with the main REST mux server. Until this is
|
||||
// called, each sub-server won't be able to have requests routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||
func (c *WatchtowerClient) RegisterWithRestServer(ctx context.Context,
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
|
||||
mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
|
||||
|
||||
// We make sure that we register it with the main REST server to ensure
|
||||
@ -142,6 +146,25 @@ func (c *WatchtowerClient) RegisterWithRestServer(ctx context.Context,
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSubServer populates the subserver's dependencies using the passed
|
||||
// SubServerConfigDispatcher. This method should fully initialize the
|
||||
// sub-server instance, making it ready for action. It returns the macaroon
|
||||
// permissions that the sub-server wishes to pass on to the root server for all
|
||||
// methods routed towards it.
|
||||
//
|
||||
// NOTE: This is part of the lnrpc.GrpcHandler interface.
|
||||
func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||
|
||||
subServer, macPermissions, err := createNewSubServer(configRegistry)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
r.WatchtowerClientServer = subServer
|
||||
return subServer, macPermissions, nil
|
||||
}
|
||||
|
||||
// isActive returns nil if the watchtower client is initialized so that we can
|
||||
// process RPC requests.
|
||||
func (c *WatchtowerClient) isActive() error {
|
||||
|
@ -300,8 +300,12 @@ func (n *NetworkHarness) NewNodeWithSeed(name string, extraArgs []string,
|
||||
|
||||
ctxt, cancel := context.WithTimeout(ctxb, DefaultTimeout)
|
||||
defer cancel()
|
||||
genSeedResp, err := node.GenSeed(ctxt, genSeedReq)
|
||||
if err != nil {
|
||||
|
||||
var genSeedResp *lnrpc.GenSeedResponse
|
||||
if err := wait.NoError(func() error {
|
||||
genSeedResp, err = node.GenSeed(ctxt, genSeedReq)
|
||||
return err
|
||||
}, DefaultTimeout); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
|
@ -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 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: [/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=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
|
||||
|
@ -3,6 +3,7 @@ s/short_chan_id=[[:digit:]]+/short_chan_id=<cid>/g
|
||||
s/[0-9a-f]{16,}/<hex>/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}T[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\.[[:digit:]]{1,18}Z/<time>/g
|
||||
s/[[:digit:]]+\:[[:digit:]]+\:[[:digit:]]+/<chan>/g
|
||||
s/[[:digit:]]+ mSAT/<amt>/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/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/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
|
||||
|
@ -801,8 +801,9 @@ func (hn *HarnessNode) initLightningClient(conn *grpc.ClientConn) error {
|
||||
hn.SignerClient = signrpc.NewSignerClient(conn)
|
||||
|
||||
// Set the harness node's pubkey to what the node claims in GetInfo.
|
||||
err := hn.FetchNodeInfo()
|
||||
if err != nil {
|
||||
// Since the RPC might not be immediately active, we wrap the call in a
|
||||
// wait.NoError.
|
||||
if err := wait.NoError(hn.FetchNodeInfo, DefaultTimeout); err != nil {
|
||||
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
|
||||
// successfully before proceeding. We use a dummy subscription in order
|
||||
// to not consume an update from the real one.
|
||||
err = wait.NoError(func() error {
|
||||
err := wait.NoError(func() error {
|
||||
req := &lnrpc.GraphTopologySubscription{}
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
topologyClient, err := hn.SubscribeChannelGraph(ctx, req)
|
||||
|
36
log.go
36
log.go
@ -1,8 +1,6 @@
|
||||
package lnd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/btcsuite/btcd/connmgr"
|
||||
"github.com/btcsuite/btclog"
|
||||
"github.com/lightninglabs/neutrino"
|
||||
@ -42,7 +40,6 @@ import (
|
||||
"github.com/lightningnetwork/lnd/sweep"
|
||||
"github.com/lightningnetwork/lnd/watchtower"
|
||||
"github.com/lightningnetwork/lnd/watchtower/wtclient"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lnd/channeldb/kvdb"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||
@ -58,11 +57,11 @@ type Service struct {
|
||||
|
||||
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.
|
||||
// If no external validator for an URI is specified, the service will
|
||||
// use the internal validator.
|
||||
externalValidators map[string]MacaroonValidator
|
||||
ExternalValidators map[string]MacaroonValidator
|
||||
|
||||
// StatelessInit denotes if the service was initialized in the stateless
|
||||
// mode where no macaroon files should be created on disk.
|
||||
@ -125,7 +124,7 @@ func NewService(dir, location string, statelessInit bool,
|
||||
return &Service{
|
||||
Bakery: *svc,
|
||||
rks: rootKeyStore,
|
||||
externalValidators: make(map[string]MacaroonValidator),
|
||||
ExternalValidators: make(map[string]MacaroonValidator),
|
||||
StatelessInit: statelessInit,
|
||||
}, nil
|
||||
}
|
||||
@ -159,83 +158,16 @@ func (svc *Service) RegisterExternalValidator(fullMethod string,
|
||||
return fmt.Errorf("validator cannot be nil")
|
||||
}
|
||||
|
||||
_, ok := svc.externalValidators[fullMethod]
|
||||
_, ok := svc.ExternalValidators[fullMethod]
|
||||
if ok {
|
||||
return fmt.Errorf("external validator for method %s already "+
|
||||
"registered", fullMethod)
|
||||
}
|
||||
|
||||
svc.externalValidators[fullMethod] = validator
|
||||
svc.ExternalValidators[fullMethod] = validator
|
||||
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
|
||||
// bakery service, context, and uri. Within the passed context.Context, we
|
||||
// expect a macaroon to be encoded as request metadata using the key
|
||||
|
410
rpcperms/interceptor.go
Normal file
410
rpcperms/interceptor.go
Normal 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)
|
||||
}
|
||||
}
|
437
rpcserver.go
437
rpcserver.go
@ -8,7 +8,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"sort"
|
||||
@ -28,7 +27,6 @@ import (
|
||||
"github.com/btcsuite/btcutil/psbt"
|
||||
"github.com/btcsuite/btcwallet/wallet/txauthor"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
proxy "github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
"github.com/lightningnetwork/lnd/autopilot"
|
||||
"github.com/lightningnetwork/lnd/build"
|
||||
@ -61,12 +59,12 @@ import (
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chanfunding"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/macaroons"
|
||||
"github.com/lightningnetwork/lnd/monitoring"
|
||||
"github.com/lightningnetwork/lnd/peer"
|
||||
"github.com/lightningnetwork/lnd/peernotifier"
|
||||
"github.com/lightningnetwork/lnd/record"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/lightningnetwork/lnd/rpcperms"
|
||||
"github.com/lightningnetwork/lnd/signal"
|
||||
"github.com/lightningnetwork/lnd/sweep"
|
||||
"github.com/lightningnetwork/lnd/watchtower"
|
||||
@ -492,34 +490,8 @@ type rpcServer struct {
|
||||
// own independent service. This allows us to expose a set of
|
||||
// micro-service like abstractions to the outside world for users to
|
||||
// consume.
|
||||
subServers []lnrpc.SubServer
|
||||
|
||||
// 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)
|
||||
subServers []lnrpc.SubServer
|
||||
subGrpcHandlers []lnrpc.GrpcHandler
|
||||
|
||||
// routerBackend contains the backend implementation of the router
|
||||
// rpc sub server.
|
||||
@ -538,34 +510,64 @@ type rpcServer struct {
|
||||
// selfNode is our own pubkey.
|
||||
selfNode route.Vertex
|
||||
|
||||
// allPermissions is a map of all registered gRPC URIs (including
|
||||
// internal and external subservers) to the permissions they require.
|
||||
allPermissions map[string][]bakery.Op
|
||||
// interceptorChain is the the interceptor added to our gRPC server.
|
||||
interceptorChain *rpcperms.InterceptorChain
|
||||
|
||||
// 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
|
||||
// LightningServer gRPC service.
|
||||
var _ lnrpc.LightningServer = (*rpcServer)(nil)
|
||||
|
||||
// newRPCServer creates and returns a new instance of the rpcServer. The
|
||||
// rpcServer will handle creating all listening sockets needed by it, and any
|
||||
// of the sub-servers that it maintains. The set of serverOpts should be the
|
||||
// base level options passed to the grPC server. This typically includes things
|
||||
// like requiring TLS, etc.
|
||||
func newRPCServer(cfg *Config, s *server, macService *macaroons.Service,
|
||||
subServerCgs *subRPCServerConfigs, serverOpts []grpc.ServerOption,
|
||||
restDialOpts []grpc.DialOption, restProxyDest string,
|
||||
atpl *autopilot.Manager, invoiceRegistry *invoices.InvoiceRegistry,
|
||||
tower *watchtower.Standalone,
|
||||
restListen func(net.Addr) (net.Listener, error),
|
||||
getListeners rpcListeners,
|
||||
chanPredicate *chanacceptor.ChainedAcceptor) (*rpcServer, error) {
|
||||
// newRPCServer creates and returns a new instance of the rpcServer. Before
|
||||
// dependencies are added, this will be an non-functioning RPC server only to
|
||||
// be used to register the LightningService with the gRPC server.
|
||||
func newRPCServer(cfg *Config, interceptorChain *rpcperms.InterceptorChain,
|
||||
extSubserverCfg *RPCSubserverConfig,
|
||||
extRestRegistrar RestRegistrar) *rpcServer {
|
||||
|
||||
// We go trhough the list of registered sub-servers, and create a gRPC
|
||||
// handler for each. These are used to register with the gRPC server
|
||||
// before all dependencies are available.
|
||||
registeredSubServers := lnrpc.RegisteredSubServers()
|
||||
|
||||
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.
|
||||
channelGraph := s.localChanDB.ChannelGraph()
|
||||
selfNode, err := channelGraph.SourceNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
graph := s.localChanDB.ChannelGraph()
|
||||
routerBackend := &routerrpc.RouterBackend{
|
||||
@ -596,10 +598,10 @@ func newRPCServer(cfg *Config, s *server, macService *macaroons.Service,
|
||||
},
|
||||
FindRoute: s.chanRouter.FindRoute,
|
||||
MissionControl: s.missionControl,
|
||||
ActiveNetParams: cfg.ActiveNetParams.Params,
|
||||
ActiveNetParams: r.cfg.ActiveNetParams.Params,
|
||||
Tower: s.controlTower,
|
||||
MaxTotalTimelock: cfg.MaxOutgoingCltvExpiry,
|
||||
DefaultFinalCltvDelta: uint16(cfg.Bitcoin.TimeLockDelta),
|
||||
MaxTotalTimelock: r.cfg.MaxOutgoingCltvExpiry,
|
||||
DefaultFinalCltvDelta: uint16(r.cfg.Bitcoin.TimeLockDelta),
|
||||
SubscribeHtlcEvents: s.htlcNotifier.SubscribeHtlcEvents,
|
||||
InterceptableForwarder: s.interceptableSwitch,
|
||||
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
|
||||
err = subServerCgs.PopulateDependencies(
|
||||
cfg, s.cc, cfg.networkDir, macService, atpl, invoiceRegistry,
|
||||
s.htlcSwitch, cfg.ActiveNetParams.Params, s.chanRouter,
|
||||
r.cfg, s.cc, r.cfg.networkDir, macService, atpl, invoiceRegistry,
|
||||
s.htlcSwitch, r.cfg.ActiveNetParams.Params, s.chanRouter,
|
||||
routerBackend, s.nodeSigner, s.localChanDB, s.remoteChanDB,
|
||||
s.sweeper, tower, s.towerClient, s.anchorTowerClient,
|
||||
cfg.net.ResolveTCPAddr, genInvoiceFeatures, rpcsLog,
|
||||
r.cfg.net.ResolveTCPAddr, genInvoiceFeatures, rpcsLog,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
// Now that the sub-servers have all their dependencies in place, we
|
||||
// can create each sub-server!
|
||||
registeredSubServers := lnrpc.RegisteredSubServers()
|
||||
for _, subServer := range registeredSubServers {
|
||||
subServerInstance, macPerms, err := subServer.New(subServerCgs)
|
||||
for _, subServerInstance := range r.subGrpcHandlers {
|
||||
subServer, macPerms, err := subServerInstance.CreateSubServer(
|
||||
subServerCgs,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
// We'll collect the sub-server, and also the set of
|
||||
// permissions it needs for macaroons so we can apply the
|
||||
// interceptors below.
|
||||
subServers = append(subServers, subServerInstance)
|
||||
subServers = append(subServers, subServer)
|
||||
subServerPerms = append(subServerPerms, macPerms)
|
||||
}
|
||||
|
||||
// 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
|
||||
// single set of interceptors.
|
||||
permissions := MainRPCServerPermissions()
|
||||
for _, subServerPerm := range subServerPerms {
|
||||
for method, ops := range subServerPerm {
|
||||
// For each new method:ops combo, we also ensure 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
|
||||
for m, ops := range MainRPCServerPermissions() {
|
||||
err := r.interceptorChain.AddPermission(m, ops)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Get the listeners and server options to use for this rpc server.
|
||||
listeners, cleanup, err := getListeners()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for _, subServerPerm := range subServerPerms {
|
||||
for method, ops := range subServerPerm {
|
||||
err := r.interceptorChain.AddPermission(method, ops)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// External subserver possibly need to register their own permissions
|
||||
// and macaroon validator.
|
||||
for _, lis := range listeners {
|
||||
extSubserver := lis.ExternalRPCSubserverCfg
|
||||
if extSubserver != nil {
|
||||
macValidator := extSubserver.MacaroonValidator
|
||||
for method, ops := range extSubserver.Permissions {
|
||||
// For each new method:ops combo, we also ensure
|
||||
// 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)
|
||||
}
|
||||
if r.extSubserverCfg != nil {
|
||||
macValidator := r.extSubserverCfg.MacaroonValidator
|
||||
for method, ops := range r.extSubserverCfg.Permissions {
|
||||
err := r.interceptorChain.AddPermission(method, ops)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
permissions[method] = ops
|
||||
|
||||
// Give the external subservers the possibility
|
||||
// to also use their own validator to check any
|
||||
// macaroons attached to calls to this method.
|
||||
// This allows them to have their own root key
|
||||
// ID database and permission entities.
|
||||
if macValidator != nil {
|
||||
err := macService.RegisterExternalValidator(
|
||||
method, macValidator,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could "+
|
||||
"not register "+
|
||||
"external macaroon "+
|
||||
"validator: %v", err)
|
||||
}
|
||||
// Give the external subservers the possibility
|
||||
// to also use their own validator to check any
|
||||
// macaroons attached to calls to this method.
|
||||
// This allows them to have their own root key
|
||||
// ID database and permission entities.
|
||||
if macValidator != nil {
|
||||
err := macService.RegisterExternalValidator(
|
||||
method, macValidator,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could "+
|
||||
"not register "+
|
||||
"external macaroon "+
|
||||
"validator: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If macaroons aren't disabled (a non-nil service), then we'll set up
|
||||
// our set of interceptors which will allow us to handle the macaroon
|
||||
// authentication in a single location.
|
||||
macUnaryInterceptors := []grpc.UnaryServerInterceptor{}
|
||||
macStrmInterceptors := []grpc.StreamServerInterceptor{}
|
||||
if macService != nil {
|
||||
unaryInterceptor := macService.UnaryServerInterceptor(permissions)
|
||||
macUnaryInterceptors = append(macUnaryInterceptors, unaryInterceptor)
|
||||
// Finally, with all the set up complete, add the last dependencies to
|
||||
// the rpc server.
|
||||
r.server = s
|
||||
r.subServers = subServers
|
||||
r.routerBackend = routerBackend
|
||||
r.chanPredicate = chanPredicate
|
||||
r.macService = macService
|
||||
r.selfNode = selfNode.PubKeyBytes
|
||||
return nil
|
||||
}
|
||||
|
||||
strmInterceptor := macService.StreamServerInterceptor(permissions)
|
||||
macStrmInterceptors = append(macStrmInterceptors, strmInterceptor)
|
||||
}
|
||||
|
||||
// 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(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)
|
||||
// RegisterWithGrpcServer registers the rpcServer and any subservers with the
|
||||
// root gRPC server.
|
||||
func (r *rpcServer) RegisterWithGrpcServer(grpcServer *grpc.Server) error {
|
||||
// Register the main RPC server.
|
||||
lnrpc.RegisterLightningServer(grpcServer, r)
|
||||
|
||||
// Now the main RPC server has been registered, we'll iterate through
|
||||
// all the sub-RPC servers and register them to ensure that requests
|
||||
// are properly routed towards them.
|
||||
for _, subServer := range subServers {
|
||||
for _, subServer := range r.subGrpcHandlers {
|
||||
err := subServer.RegisterWithRootServer(grpcServer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to register "+
|
||||
"sub-server %v with root: %v",
|
||||
subServer.Name(), err)
|
||||
return fmt.Errorf("unable to register "+
|
||||
"sub-server with root: %v", 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.
|
||||
@ -813,133 +768,46 @@ func (r *rpcServer) Start() error {
|
||||
}
|
||||
}
|
||||
|
||||
// With all the sub-servers started, we'll spin up the listeners for
|
||||
// the main RPC server itself.
|
||||
for _, lis := range r.listeners {
|
||||
go func(lis *ListenerWithSignal) {
|
||||
rpcsLog.Infof("RPC server listening on %s", lis.Addr())
|
||||
return 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.
|
||||
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)
|
||||
// RegisterWithRestProxy registers the RPC server and any subservers with the
|
||||
// given REST proxy.
|
||||
func (r *rpcServer) RegisterWithRestProxy(restCtx context.Context,
|
||||
restMux *proxy.ServeMux, restDialOpts []grpc.DialOption,
|
||||
restProxyDest string) error {
|
||||
|
||||
// With our custom REST proxy mux created, register our main RPC and
|
||||
// give all subservers a chance to register as well.
|
||||
err := lnrpc.RegisterLightningHandlerFromEndpoint(
|
||||
restCtx, restMux, r.restProxyDest, r.restDialOpts,
|
||||
restCtx, restMux, restProxyDest, restDialOpts,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, subServer := range r.subServers {
|
||||
|
||||
for _, subServer := range r.subGrpcHandlers {
|
||||
err := subServer.RegisterWithRestServer(
|
||||
restCtx, restMux, r.restProxyDest, r.restDialOpts,
|
||||
restCtx, restMux, restProxyDest, restDialOpts,
|
||||
)
|
||||
if err != nil {
|
||||
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
|
||||
// external subservers a chance to register their own REST proxy stub
|
||||
// with our mux instance.
|
||||
for _, lis := range r.listeners {
|
||||
if lis.ExternalRestRegistrar != nil {
|
||||
err := lis.ExternalRestRegistrar.RegisterRestSubserver(
|
||||
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 r.extRestRegistrar != nil {
|
||||
err := r.extRestRegistrar.RegisterRestSubserver(
|
||||
restCtx, restMux, restProxyDest, restDialOpts,
|
||||
)
|
||||
if err != nil {
|
||||
ltndLog.Errorf("gRPC proxy unable to listen on %s",
|
||||
restEndpoint)
|
||||
return err
|
||||
rpcsLog.Errorf("error registering "+
|
||||
"external REST subserver: %v", 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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -6542,7 +6404,8 @@ func (r *rpcServer) BakeMacaroon(ctx context.Context,
|
||||
// Either we have the special entity "uri" which specifies a
|
||||
// full gRPC URI or we have one of the pre-defined actions.
|
||||
if op.Entity == macaroons.PermissionEntityCustomURI {
|
||||
_, ok := r.allPermissions[op.Action]
|
||||
allPermissions := r.interceptorChain.Permissions()
|
||||
_, ok := allPermissions[op.Action]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid permission " +
|
||||
"action, must be an existing URI in " +
|
||||
@ -6657,7 +6520,7 @@ func (r *rpcServer) ListPermissions(_ context.Context,
|
||||
rpcsLog.Debugf("[listpermissions]")
|
||||
|
||||
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))
|
||||
for idx, perm := range perms {
|
||||
rpcPerms[idx] = &lnrpc.MacaroonPermission{
|
||||
|
@ -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
|
||||
// instance. This method allows a caller to generate a new aezeed cipher seed
|
||||
// given an optional passphrase. If provided, the passphrase will be necessary
|
||||
|
Loading…
Reference in New Issue
Block a user