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 (
"context"
"crypto/tls"
"encoding/base64"
"encoding/hex"
"fmt"
"io/ioutil"
"net"
"net/http"
"strings"
"sync/atomic"
@ -21,12 +19,9 @@ import (
"github.com/lightningnetwork/lnd/macaroons"
grpcProxy "github.com/mwitkow/grpc-proxy/proxy"
"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/grpc/test/bufconn"
"gopkg.in/macaroon.v2"
)
@ -65,7 +60,7 @@ func (e *proxyErr) Unwrap() error {
// component.
func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator,
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
// by base64(username:password). Because we only have a password, we
@ -85,7 +80,6 @@ func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator,
permsMgr: permsMgr,
macValidator: validator,
superMacValidator: superMacValidator,
bufListener: bufListener,
}
p.grpcServer = grpc.NewServer(
// From the grpxProxy doc: This codec is *crucial* to the
@ -162,7 +156,6 @@ type rpcProxy struct {
macValidator macaroons.MacaroonValidator
superMacValidator session.SuperMacaroonValidator
bufListener *bufconn.Listener
superMacaroon string
@ -176,21 +169,9 @@ type rpcProxy struct {
}
// Start creates initial connection to lnd.
func (p *rpcProxy) Start() error {
func (p *rpcProxy) Start(lndConn *grpc.ClientConn) error {
var err error
// 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)
}
p.lndConn = lndConn
// Make sure we can connect to all the daemons that are configured to be
// running in remote mode.
@ -242,13 +223,6 @@ func (p *rpcProxy) hasStarted() bool {
func (p *rpcProxy) Stop() error {
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 err := p.faradayConn.Close(); err != nil {
log.Errorf("Error closing faraday connection: %v", err)
@ -681,68 +655,6 @@ func (p *rpcProxy) convertSuperMacaroon(ctx context.Context, macHex string,
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
// gRPC dial options from it.
func readMacaroon(macPath string) ([]byte, error) {

View file

@ -160,6 +160,7 @@ type LightningTerminal struct {
wg sync.WaitGroup
errQueue *queue.ConcurrentQueue[error]
lndConn *grpc.ClientConn
lndClient *lndclient.GrpcLndServices
basicClient lnrpc.LightningClient
@ -244,7 +245,7 @@ func (g *LightningTerminal) Run() error {
g.loopServer = loopd.New(g.cfg.Loop, nil)
g.poolServer = pool.NewServer(g.cfg.Pool)
g.rpcProxy = newRpcProxy(
g.cfg, g, g.validateSuperMacaroon, g.permsMgr, bufRpcListener,
g.cfg, g, g.validateSuperMacaroon, g.permsMgr,
)
g.accountService, err = accounts.NewService(
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
// 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
// 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",
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 err := g.httpServer.Close(); err != nil {
log.Errorf("Error stopping UI server: %v", err)