From 155301fc1d289c8533ff379d1b4ea8d77853e881 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Sat, 20 Nov 2021 19:59:46 -0500 Subject: [PATCH] Allow lnd remote to work with certificate string (#3840) --- .../org/bitcoins/lnd/rpc/LndRpcClient.scala | 33 +++++++++++++++---- .../bitcoins/lnd/rpc/config/LndInstance.scala | 29 ++++++++++++++-- .../testkit/fixtures/LndFixture.scala | 14 +++++++- 3 files changed, 67 insertions(+), 9 deletions(-) diff --git a/lnd-rpc/src/main/scala/org/bitcoins/lnd/rpc/LndRpcClient.scala b/lnd-rpc/src/main/scala/org/bitcoins/lnd/rpc/LndRpcClient.scala index 86a3d8d797..f34501f33e 100644 --- a/lnd-rpc/src/main/scala/org/bitcoins/lnd/rpc/LndRpcClient.scala +++ b/lnd-rpc/src/main/scala/org/bitcoins/lnd/rpc/LndRpcClient.scala @@ -43,7 +43,7 @@ import walletrpc.{ _ } -import java.io.{File, FileInputStream} +import java.io._ import java.net.InetSocketAddress import java.util.concurrent.Executor import java.util.concurrent.atomic.AtomicInteger @@ -76,8 +76,19 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)( // These need to be lazy so we don't try and fetch // the tls certificate before it is generated - - private[this] lazy val certStream = new FileInputStream(instance.certFile) + private[this] lazy val certStreamOpt: Option[InputStream] = { + instance.certFileOpt match { + case Some(file) => Some(new FileInputStream(file)) + case None => + instance.certificateOpt match { + case Some(cert) => + Some( + new ByteArrayInputStream( + cert.getBytes(java.nio.charset.StandardCharsets.UTF_8.name))) + case None => None + } + } + } private lazy val callCredentials = new CallCredentials { @@ -99,12 +110,22 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)( } // Configure the client - private lazy val clientSettings: GrpcClientSettings = - GrpcClientSettings + private lazy val clientSettings: GrpcClientSettings = { + val trustManagerOpt = certStreamOpt match { + case Some(stream) => Some(SSLContextUtils.trustManagerFromStream(stream)) + case None => None + } + + val client = GrpcClientSettings .connectToServiceAt(instance.rpcUri.getHost, instance.rpcUri.getPort) - .withTrustManager(SSLContextUtils.trustManagerFromStream(certStream)) .withCallCredentials(callCredentials) + trustManagerOpt match { + case Some(trustManager) => client.withTrustManager(trustManager) + case None => client + } + } + // Create a client-side stub for the services lazy val lnd: LightningClient = LightningClient(clientSettings) lazy val wallet: WalletKitClient = WalletKitClient(clientSettings) diff --git a/lnd-rpc/src/main/scala/org/bitcoins/lnd/rpc/config/LndInstance.scala b/lnd-rpc/src/main/scala/org/bitcoins/lnd/rpc/config/LndInstance.scala index acf8097c37..6c01af7983 100644 --- a/lnd-rpc/src/main/scala/org/bitcoins/lnd/rpc/config/LndInstance.scala +++ b/lnd-rpc/src/main/scala/org/bitcoins/lnd/rpc/config/LndInstance.scala @@ -15,7 +15,8 @@ import scala.util.Properties sealed trait LndInstance { def rpcUri: URI def macaroon: String - def certFile: File + def certFileOpt: Option[File] + def certificateOpt: Option[String] } case class LndInstanceLocal( @@ -30,6 +31,9 @@ case class LndInstanceLocal( debugLevel: LogLevel) extends LndInstance { + override val certificateOpt: Option[String] = None + override val certFileOpt: Option[File] = Some(certFile) + private var macaroonOpt: Option[String] = None override def macaroon: String = { @@ -98,5 +102,26 @@ object LndInstanceLocal } } -case class LndInstanceRemote(rpcUri: URI, macaroon: String, certFile: File) +case class LndInstanceRemote( + rpcUri: URI, + macaroon: String, + certFileOpt: Option[File], + certificateOpt: Option[String]) extends LndInstance + +object LndInstanceRemote { + + def apply( + rpcUri: URI, + macaroon: String, + certFile: File): LndInstanceRemote = { + LndInstanceRemote(rpcUri, macaroon, Some(certFile), None) + } + + def apply( + rpcUri: URI, + macaroon: String, + certificate: String): LndInstanceRemote = { + LndInstanceRemote(rpcUri, macaroon, None, Some(certificate)) + } +} diff --git a/testkit/src/main/scala/org/bitcoins/testkit/fixtures/LndFixture.scala b/testkit/src/main/scala/org/bitcoins/testkit/fixtures/LndFixture.scala index b9f565f778..ef0b34c69f 100644 --- a/testkit/src/main/scala/org/bitcoins/testkit/fixtures/LndFixture.scala +++ b/testkit/src/main/scala/org/bitcoins/testkit/fixtures/LndFixture.scala @@ -7,6 +7,8 @@ import org.bitcoins.testkit.lnd._ import org.bitcoins.testkit.rpc._ import org.scalatest.FutureOutcome +import scala.io.Source + /** A trait that is useful if you need Lnd fixtures for your test suite */ trait LndFixture extends BitcoinSFixture with CachedBitcoindV21 { @@ -82,10 +84,20 @@ trait RemoteLndFixture extends BitcoinSFixture with CachedBitcoindV21 { client = LndRpcTestClient.fromSbtDownload(Some(bitcoind)) lnd <- client.start() + // get certificate as a string + cert = { + val file = lnd.instance.certFileOpt.get + val source = Source.fromFile(file) + val str = source.getLines().toVector.mkString("\n") + source.close() + + str + } + // create a remote instance and client remoteInstance = LndInstanceRemote(lnd.instance.rpcUri, lnd.instance.macaroon, - lnd.instance.certFile) + cert) remoteLnd = LndRpcClient(remoteInstance) } yield remoteLnd },