multi: split out LND connection from rpcProxy

Remove the responsibility of creating an LND connection from the
rpcProxy and instead let the main LightningTerminal struct handle it.
All the lnd-connection specific functions are also moved into their own
file.
This commit is contained in:
Elle Mouton 2022-10-31 13:26:36 +02:00 committed by positiveblue
parent 116322d72c
commit 37b24d59dd
No known key found for this signature in database
GPG key ID: 4FFF2510928804DC
3 changed files with 104 additions and 93 deletions

85
lnd_connection.go Normal file
View file

@ -0,0 +1,85 @@
package terminal
import (
"context"
"crypto/tls"
"fmt"
"net"
grpcProxy "github.com/mwitkow/grpc-proxy/proxy"
"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/test/bufconn"
)
// connectLND sets up LiT's LND connection.
func connectLND(cfg *Config, bufListener *bufconn.Listener) (*grpc.ClientConn,
error) {
if cfg.lndRemote {
host, _, tlsPath, _, _ := cfg.lndConnectParams()
return dialBackend("lnd", host, tlsPath)
}
// If LND is running in integrated mode, then we use a bufconn to
// connect to lnd in integrated mode.
return dialBufConnBackend(bufListener)
}
// dialBackend connects to a gRPC backend through the given address and uses the
// given TLS certificate to authenticate the connection.
func dialBackend(name, dialAddr, tlsCertPath string) (*grpc.ClientConn, error) {
tlsConfig, err := credentials.NewClientTLSFromFile(tlsCertPath, "")
if err != nil {
return nil, fmt.Errorf("could not read %s TLS cert %s: %v",
name, tlsCertPath, err)
}
opts := []grpc.DialOption{
// From the grpcProxy doc: This codec is *crucial* to the
// functioning of the proxy.
grpc.WithCodec(grpcProxy.Codec()), // nolint
grpc.WithTransportCredentials(tlsConfig),
grpc.WithDefaultCallOptions(maxMsgRecvSize),
grpc.WithConnectParams(grpc.ConnectParams{
Backoff: backoff.DefaultConfig,
MinConnectTimeout: defaultConnectTimeout,
}),
}
log.Infof("Dialing %s gRPC server at %s", name, dialAddr)
cc, err := grpc.Dial(dialAddr, opts...)
if err != nil {
return nil, fmt.Errorf("failed dialing %s backend: %v", name,
err)
}
return cc, nil
}
// dialBufConnBackend dials an in-memory connection to an RPC listener and
// ignores any TLS certificate mismatches.
func dialBufConnBackend(listener *bufconn.Listener) (*grpc.ClientConn, error) {
tlsConfig := credentials.NewTLS(&tls.Config{
InsecureSkipVerify: true,
})
opts := []grpc.DialOption{
grpc.WithContextDialer(
func(context.Context, string) (net.Conn, error) {
return listener.Dial()
},
),
// From the grpcProxy doc: This codec is *crucial* to the
// functioning of the proxy.
grpc.WithCodec(grpcProxy.Codec()), // nolint
grpc.WithTransportCredentials(tlsConfig),
grpc.WithDefaultCallOptions(maxMsgRecvSize),
grpc.WithConnectParams(grpc.ConnectParams{
Backoff: backoff.DefaultConfig,
MinConnectTimeout: defaultConnectTimeout,
}),
}
return grpc.Dial("", opts...)
}

View file

@ -2,12 +2,10 @@ package terminal
import ( import (
"context" "context"
"crypto/tls"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net"
"net/http" "net/http"
"strings" "strings"
"sync/atomic" "sync/atomic"
@ -21,12 +19,9 @@ import (
"github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/macaroons"
grpcProxy "github.com/mwitkow/grpc-proxy/proxy" grpcProxy "github.com/mwitkow/grpc-proxy/proxy"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"google.golang.org/grpc/test/bufconn"
"gopkg.in/macaroon.v2" "gopkg.in/macaroon.v2"
) )
@ -65,7 +60,7 @@ func (e *proxyErr) Unwrap() error {
// component. // component.
func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator, func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator,
superMacValidator session.SuperMacaroonValidator, superMacValidator session.SuperMacaroonValidator,
permsMgr *perms.Manager, bufListener *bufconn.Listener) *rpcProxy { permsMgr *perms.Manager) *rpcProxy {
// The gRPC web calls are protected by HTTP basic auth which is defined // The gRPC web calls are protected by HTTP basic auth which is defined
// by base64(username:password). Because we only have a password, we // by base64(username:password). Because we only have a password, we
@ -85,7 +80,6 @@ func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator,
permsMgr: permsMgr, permsMgr: permsMgr,
macValidator: validator, macValidator: validator,
superMacValidator: superMacValidator, superMacValidator: superMacValidator,
bufListener: bufListener,
} }
p.grpcServer = grpc.NewServer( p.grpcServer = grpc.NewServer(
// From the grpxProxy doc: This codec is *crucial* to the // From the grpxProxy doc: This codec is *crucial* to the
@ -162,7 +156,6 @@ type rpcProxy struct {
macValidator macaroons.MacaroonValidator macValidator macaroons.MacaroonValidator
superMacValidator session.SuperMacaroonValidator superMacValidator session.SuperMacaroonValidator
bufListener *bufconn.Listener
superMacaroon string superMacaroon string
@ -176,21 +169,9 @@ type rpcProxy struct {
} }
// Start creates initial connection to lnd. // Start creates initial connection to lnd.
func (p *rpcProxy) Start() error { func (p *rpcProxy) Start(lndConn *grpc.ClientConn) error {
var err error var err error
p.lndConn = lndConn
// Setup the connection to lnd.
host, _, tlsPath, _, _ := p.cfg.lndConnectParams()
// We use a bufconn to connect to lnd in integrated mode.
if p.cfg.LndMode == ModeIntegrated {
p.lndConn, err = dialBufConnBackend(p.bufListener)
} else {
p.lndConn, err = dialBackend("lnd", host, tlsPath)
}
if err != nil {
return fmt.Errorf("could not dial lnd: %v", err)
}
// Make sure we can connect to all the daemons that are configured to be // Make sure we can connect to all the daemons that are configured to be
// running in remote mode. // running in remote mode.
@ -242,13 +223,6 @@ func (p *rpcProxy) hasStarted() bool {
func (p *rpcProxy) Stop() error { func (p *rpcProxy) Stop() error {
p.grpcServer.Stop() p.grpcServer.Stop()
if p.lndConn != nil {
if err := p.lndConn.Close(); err != nil {
log.Errorf("Error closing lnd connection: %v", err)
return err
}
}
if p.faradayConn != nil { if p.faradayConn != nil {
if err := p.faradayConn.Close(); err != nil { if err := p.faradayConn.Close(); err != nil {
log.Errorf("Error closing faraday connection: %v", err) log.Errorf("Error closing faraday connection: %v", err)
@ -681,68 +655,6 @@ func (p *rpcProxy) convertSuperMacaroon(ctx context.Context, macHex string,
return nil, nil return nil, nil
} }
// dialBufConnBackend dials an in-memory connection to an RPC listener and
// ignores any TLS certificate mismatches.
func dialBufConnBackend(listener *bufconn.Listener) (*grpc.ClientConn, error) {
tlsConfig := credentials.NewTLS(&tls.Config{
InsecureSkipVerify: true,
})
conn, err := grpc.Dial(
"",
grpc.WithContextDialer(
func(context.Context, string) (net.Conn, error) {
return listener.Dial()
},
),
grpc.WithTransportCredentials(tlsConfig),
// From the grpcProxy doc: This codec is *crucial* to the
// functioning of the proxy.
grpc.WithCodec(grpcProxy.Codec()), // nolint
grpc.WithTransportCredentials(tlsConfig),
grpc.WithDefaultCallOptions(maxMsgRecvSize),
grpc.WithConnectParams(grpc.ConnectParams{
Backoff: backoff.DefaultConfig,
MinConnectTimeout: defaultConnectTimeout,
}),
)
return conn, err
}
// dialBackend connects to a gRPC backend through the given address and uses the
// given TLS certificate to authenticate the connection.
func dialBackend(name, dialAddr, tlsCertPath string) (*grpc.ClientConn, error) {
var opts []grpc.DialOption
tlsConfig, err := credentials.NewClientTLSFromFile(tlsCertPath, "")
if err != nil {
return nil, fmt.Errorf("could not read %s TLS cert %s: %v",
name, tlsCertPath, err)
}
opts = append(
opts,
// From the grpcProxy doc: This codec is *crucial* to the
// functioning of the proxy.
grpc.WithCodec(grpcProxy.Codec()), // nolint
grpc.WithTransportCredentials(tlsConfig),
grpc.WithDefaultCallOptions(maxMsgRecvSize),
grpc.WithConnectParams(grpc.ConnectParams{
Backoff: backoff.DefaultConfig,
MinConnectTimeout: defaultConnectTimeout,
}),
)
log.Infof("Dialing %s gRPC server at %s", name, dialAddr)
cc, err := grpc.Dial(dialAddr, opts...)
if err != nil {
return nil, fmt.Errorf("failed dialing %s backend: %v", name,
err)
}
return cc, nil
}
// readMacaroon tries to read the macaroon file at the specified path and create // readMacaroon tries to read the macaroon file at the specified path and create
// gRPC dial options from it. // gRPC dial options from it.
func readMacaroon(macPath string) ([]byte, error) { func readMacaroon(macPath string) ([]byte, error) {

View file

@ -160,6 +160,7 @@ type LightningTerminal struct {
wg sync.WaitGroup wg sync.WaitGroup
errQueue *queue.ConcurrentQueue[error] errQueue *queue.ConcurrentQueue[error]
lndConn *grpc.ClientConn
lndClient *lndclient.GrpcLndServices lndClient *lndclient.GrpcLndServices
basicClient lnrpc.LightningClient basicClient lnrpc.LightningClient
@ -244,7 +245,7 @@ func (g *LightningTerminal) Run() error {
g.loopServer = loopd.New(g.cfg.Loop, nil) g.loopServer = loopd.New(g.cfg.Loop, nil)
g.poolServer = pool.NewServer(g.cfg.Pool) g.poolServer = pool.NewServer(g.cfg.Pool)
g.rpcProxy = newRpcProxy( g.rpcProxy = newRpcProxy(
g.cfg, g, g.validateSuperMacaroon, g.permsMgr, bufRpcListener, g.cfg, g, g.validateSuperMacaroon, g.permsMgr,
) )
g.accountService, err = accounts.NewService( g.accountService, err = accounts.NewService(
filepath.Dir(g.cfg.MacaroonPath), g.errQueue.ChanIn(), filepath.Dir(g.cfg.MacaroonPath), g.errQueue.ChanIn(),
@ -436,11 +437,17 @@ func (g *LightningTerminal) Run() error {
} }
}() }()
// Connect to LND.
g.lndConn, err = connectLND(g.cfg, bufRpcListener)
if err != nil {
return fmt.Errorf("could not connect to LND: %v", err)
}
// Now start the RPC proxy that will handle all incoming gRPC, grpc-web // Now start the RPC proxy that will handle all incoming gRPC, grpc-web
// and REST requests. We also start the main web server that dispatches // and REST requests. We also start the main web server that dispatches
// requests either to the static UI file server or the RPC proxy. This // requests either to the static UI file server or the RPC proxy. This
// makes it possible to unlock lnd through the UI. // makes it possible to unlock lnd through the UI.
if err := g.rpcProxy.Start(); err != nil { if err := g.rpcProxy.Start(g.lndConn); err != nil {
return fmt.Errorf("error starting lnd gRPC proxy server: %v", return fmt.Errorf("error starting lnd gRPC proxy server: %v",
err) err)
} }
@ -1178,6 +1185,13 @@ func (g *LightningTerminal) shutdown() error {
} }
} }
if g.lndConn != nil {
if err := g.lndConn.Close(); err != nil {
log.Errorf("Error closing lnd connection: %v", err)
returnErr = err
}
}
if g.httpServer != nil { if g.httpServer != nil {
if err := g.httpServer.Close(); err != nil { if err := g.httpServer.Close(); err != nil {
log.Errorf("Error stopping UI server: %v", err) log.Errorf("Error stopping UI server: %v", err)