From f6a941244e78545041331164fd6514a4a326e32f Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 22 Apr 2020 14:12:17 +0200 Subject: [PATCH] lndclient: use readonly macaroon for startup check --- lndclient/lnd_services.go | 79 +++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 23 deletions(-) diff --git a/lndclient/lnd_services.go b/lndclient/lnd_services.go index c6009ac..14afa64 100644 --- a/lndclient/lnd_services.go +++ b/lndclient/lnd_services.go @@ -2,7 +2,6 @@ package lndclient import ( "context" - "errors" "fmt" "net" "path/filepath" @@ -92,13 +91,6 @@ func NewLndServicesWithDialer(dialer dialerFunc, lndAddress, network, } } - // Now that we've ensured our macaroon directory is set properly, we - // can retrieve our full macaroon pouch from the directory. - macaroons, err := newMacaroonPouch(macaroonDir) - if err != nil { - return nil, fmt.Errorf("unable to obtain macaroons: %v", err) - } - // Setup connection with lnd log.Infof("Creating lnd connection to %v", lndAddress) conn, err := getClientConn(dialer, lndAddress, tlsPath) @@ -113,25 +105,36 @@ func NewLndServicesWithDialer(dialer dialerFunc, lndAddress, network, return nil, err } - lightningClient := newLightningClient( - conn, chainParams, macaroons.adminMac, + // We are going to check that the connected lnd is on the same network + // and is a compatible version with all the required subservers enabled. + // For this, we make two calls, both of which only need the readonly + // macaroon. We don't use the pouch yet because if not all subservers + // are enabled, then not all macaroons might be there and the user would + // get a more cryptic error message. + readonlyMac, err := newSerializedMacaroon( + filepath.Join(macaroonDir, defaultReadonlyFilename), ) - - // With our macaroons obtained, we'll ensure that the network for lnd - // matches our expected network. - info, err := lightningClient.GetInfo(context.Background()) if err != nil { - conn.Close() - return nil, fmt.Errorf("unable to get info for lnd "+ - "node: %v", err) + return nil, err } - if network != info.Network { - conn.Close() - return nil, errors.New( - "network mismatch with connected lnd instance", - ) + err = checkLndCompatibility(conn, chainParams, readonlyMac, network) + if err != nil { + return nil, err + } + + // Now that we've ensured our macaroon directory is set properly, we + // can retrieve our full macaroon pouch from the directory. + macaroons, err := newMacaroonPouch(macaroonDir) + if err != nil { + return nil, fmt.Errorf("unable to obtain macaroons: %v", err) } + // With the macaroons loaded and the version checked, we can now create + // the real lightning client which uses the admin macaroon. + lightningClient := newLightningClient( + conn, chainParams, macaroons.adminMac, + ) + // With the network check passed, we'll now initialize the rest of the // sub-server connections, giving each of them their specific macaroon. notifierClient := newChainNotifierClient(conn, macaroons.chainMac) @@ -142,7 +145,10 @@ func NewLndServicesWithDialer(dialer dialerFunc, lndAddress, network, cleanup := func() { log.Debugf("Closing lnd connection") - conn.Close() + err := conn.Close() + if err != nil { + log.Errorf("Error closing client connection: %v", err) + } log.Debugf("Wait for client to finish") lightningClient.WaitForFinished() @@ -183,6 +189,32 @@ func (s *GrpcLndServices) Close() { log.Debugf("Lnd services finished") } +// checkLndCompatibility makes sure the connected lnd instance is running on the +// correct network. +func checkLndCompatibility(conn *grpc.ClientConn, chainParams *chaincfg.Params, + readonlyMac serializedMacaroon, network string) error { + + // We use our own client with a readonly macaroon here, because we know + // that's all we need for the checks. + lightningClient := newLightningClient(conn, chainParams, readonlyMac) + + // With our readonly macaroon obtained, we'll ensure that the network + // for lnd matches our expected network. + info, err := lightningClient.GetInfo(context.Background()) + if err != nil { + conn.Close() + return fmt.Errorf("unable to get info for lnd "+ + "node: %v", err) + } + if network != info.Network { + conn.Close() + return fmt.Errorf("network mismatch with connected lnd "+ + "node, got '%s', wanted '%s'", info.Network, network) + + } + return nil +} + var ( defaultRPCPort = "10009" defaultLndDir = btcutil.AppDataDir("lnd", false) @@ -199,6 +231,7 @@ var ( defaultWalletKitMacaroonFilename = "walletkit.macaroon" defaultRouterMacaroonFilename = "router.macaroon" defaultSignerFilename = "signer.macaroon" + defaultReadonlyFilename = "readonly.macaroon" // maxMsgRecvSize is the largest gRPC message our client will receive. // We set this to 200MiB.