Add LndInstanceRemote (#3710)

This commit is contained in:
benthecarman 2021-10-02 16:15:40 -05:00 committed by GitHub
parent 466de3e46a
commit f6169cc3af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 171 additions and 16 deletions

View file

@ -0,0 +1,119 @@
package org.bitcoins.lnd.rpc
import lnrpc.Invoice.InvoiceState
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.protocol.ln.LnInvoice
import org.bitcoins.core.protocol.ln.currency._
import org.bitcoins.core.protocol.script.P2WPKHWitnessSPKV0
import org.bitcoins.testkit.fixtures.RemoteLndFixture
import scala.concurrent.Future
class LndRemoteClientTest extends RemoteLndFixture {
it must "get info from lnd" in { lnd =>
for {
info <- lnd.getInfo
} yield assert(info.blockHeight >= 0)
}
it must "create an invoice using sats" in { lnd =>
val memo = "this is my memo"
val amount = Satoshis(1000)
for {
invoiceResult <- lnd.addInvoice(memo, amount, 1000)
} yield {
val invoice = invoiceResult.invoice
assert(invoice.lnTags.description.map(_.string).contains(memo))
assert(invoice.amount.map(_.toSatoshis).contains(amount))
}
}
it must "create an invoice using msats" in { lnd =>
val memo = "this is my memo"
val amount = MilliSatoshis(1000)
for {
invoiceResult <- lnd.addInvoice(memo, amount, 1000)
} yield {
val invoice = invoiceResult.invoice
assert(invoice.lnTags.description.map(_.string).contains(memo))
assert(invoice.amount.map(_.toMSat).contains(amount))
}
}
it must "get an on-chain address" in { lnd =>
for {
addr <- lnd.getNewAddress
} yield assert(addr.scriptPubKey.isInstanceOf[P2WPKHWitnessSPKV0])
}
it must "get unspent utxos" in { lnd =>
for {
utxos <- lnd.listUnspent
} yield assert(utxos.isEmpty)
}
it must "look up an invoice" in { lnd =>
val memo = "this is my memo"
val amount = MilliSatoshis(1000)
for {
invoiceResult <- lnd.addInvoice(memo, amount, 1000)
invoice = invoiceResult.invoice
_ = {
assert(invoice.lnTags.description.map(_.string).contains(memo))
assert(invoice.amount.map(_.toMSat).contains(amount))
}
lookupResult <- lnd.lookupInvoice(invoiceResult.rHash)
} yield {
val lookupInvoice = LnInvoice.fromString(lookupResult.paymentRequest)
assert(lookupInvoice.lnTags.description.map(_.string).contains(memo))
assert(lookupInvoice.amount.map(_.toMSat).contains(amount))
assert(lookupResult.state == InvoiceState.OPEN)
}
}
it must "get wallet balance" in { lnd =>
for {
balances <- lnd.walletBalance()
} yield {
assert(balances.balance == Satoshis.zero)
assert(balances.unconfirmedBalance == Satoshis.zero)
assert(balances.confirmedBalance == Satoshis.zero)
}
}
it must "get channel balance" in { lnd =>
for {
balances <- lnd.channelBalance()
} yield {
assert(balances.localBalance == Satoshis.zero)
assert(balances.remoteBalance == Satoshis.zero)
assert(balances.pendingOpenLocalBalance == Satoshis.zero)
assert(balances.pendingOpenRemoteBalance == Satoshis.zero)
assert(balances.unsettledLocalBalance == Satoshis.zero)
assert(balances.unsettledRemoteBalance == Satoshis.zero)
}
}
it must "lease and release an output" in { lnd =>
for {
utxos <- lnd.listUnspent
leaseFs = utxos.map(u => lnd.leaseOutput(u.outPointOpt.get, 100))
_ <- Future.sequence(leaseFs)
leases <- lnd.listLeases()
_ = assert(leases.size == utxos.size)
releaseFs = utxos.map(u => lnd.releaseOutput(u.outPointOpt.get))
_ <- Future.sequence(releaseFs)
leases <- lnd.listLeases()
} yield assert(leases.isEmpty)
}
}

View file

@ -31,7 +31,7 @@ import org.bitcoins.core.wallet.fee.{SatoshisPerKW, SatoshisPerVirtualByte}
import org.bitcoins.crypto._ import org.bitcoins.crypto._
import org.bitcoins.lnd.rpc.LndRpcClient._ import org.bitcoins.lnd.rpc.LndRpcClient._
import org.bitcoins.lnd.rpc.LndUtils._ import org.bitcoins.lnd.rpc.LndUtils._
import org.bitcoins.lnd.rpc.config.{LndInstance, LndInstanceLocal} import org.bitcoins.lnd.rpc.config._
import scodec.bits._ import scodec.bits._
import signrpc._ import signrpc._
import walletrpc.{ import walletrpc.{
@ -62,12 +62,14 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
case _: LndInstanceLocal => case _: LndInstanceLocal =>
require(binaryOpt.isDefined, require(binaryOpt.isDefined,
s"Binary must be defined with a local instance of lnd") s"Binary must be defined with a local instance of lnd")
case _: LndInstanceRemote => ()
} }
/** The command to start the daemon on the underlying OS */ /** The command to start the daemon on the underlying OS */
override def cmd: String = instance match { override def cmd: String = instance match {
case local: LndInstanceLocal => case local: LndInstanceLocal =>
s"${binaryOpt.get} --lnddir=${local.datadir.toAbsolutePath}" s"${binaryOpt.get} --lnddir=${local.datadir.toAbsolutePath}"
case _: LndInstanceRemote => ""
} }
implicit val executionContext: ExecutionContext = system.dispatcher implicit val executionContext: ExecutionContext = system.dispatcher

View file

@ -13,18 +13,8 @@ import java.nio.file._
import scala.util.Properties import scala.util.Properties
sealed trait LndInstance { sealed trait LndInstance {
def network: BitcoinNetwork
def listenBinding: URI
def restUri: URI
def rpcUri: URI def rpcUri: URI
def bitcoindAuthCredentials: PasswordBased
def bitcoindRpcUri: URI
def zmqConfig: ZmqConfig
def debugLevel: LogLevel
def macaroon: String def macaroon: String
def datadir: Path
def certFile: File def certFile: File
} }
@ -107,3 +97,6 @@ object LndInstanceLocal
config.lndInstance config.lndInstance
} }
} }
case class LndInstanceRemote(rpcUri: URI, macaroon: String, certFile: File)
extends LndInstance

View file

@ -1,6 +1,7 @@
package org.bitcoins.testkit.fixtures package org.bitcoins.testkit.fixtures
import org.bitcoins.lnd.rpc.LndRpcClient import org.bitcoins.lnd.rpc.LndRpcClient
import org.bitcoins.lnd.rpc.config.LndInstanceRemote
import org.bitcoins.rpc.client.common.BitcoindRpcClient import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.testkit.lnd._ import org.bitcoins.testkit.lnd._
import org.bitcoins.testkit.rpc._ import org.bitcoins.testkit.rpc._
@ -62,3 +63,37 @@ trait DualLndFixture extends BitcoinSFixture with CachedBitcoindV21 {
)(test) )(test)
} }
} }
trait RemoteLndFixture extends BitcoinSFixture with CachedBitcoindV21 {
override type FixtureParam = LndRpcClient
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
withLnd(test)
}
def withLnd(test: OneArgAsyncTest): FutureOutcome = {
makeDependentFixture[LndRpcClient](
() => {
for {
bitcoind <- cachedBitcoindWithFundsF
// start and initialize a lnd
client = LndRpcTestClient.fromSbtDownload(Some(bitcoind))
lnd <- client.start()
// create a remote instance and client
remoteInstance = LndInstanceRemote(lnd.instance.rpcUri,
lnd.instance.macaroon,
lnd.instance.certFile)
remoteLnd = LndRpcClient(remoteInstance)
} yield remoteLnd
},
{ lnd =>
for {
_ <- lnd.stop()
} yield ()
}
)(test)
}
}

View file

@ -8,7 +8,7 @@ import org.bitcoins.core.protocol.ln.node.NodeId
import org.bitcoins.core.protocol.transaction.TransactionOutPoint import org.bitcoins.core.protocol.transaction.TransactionOutPoint
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.lnd.rpc.LndRpcClient import org.bitcoins.lnd.rpc.LndRpcClient
import org.bitcoins.lnd.rpc.config.LndInstanceLocal import org.bitcoins.lnd.rpc.config.{LndInstanceLocal, LndInstanceRemote}
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion} import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
import org.bitcoins.rpc.config.{ import org.bitcoins.rpc.config.{
BitcoindAuthCredentials, BitcoindAuthCredentials,
@ -22,7 +22,7 @@ import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.{FileUtil, TestkitBinaries} import org.bitcoins.testkit.util.{FileUtil, TestkitBinaries}
import java.io.{File, PrintWriter} import java.io.{File, PrintWriter}
import java.net.InetSocketAddress import java.net.{InetSocketAddress, URI}
import java.nio.file.Path import java.nio.file.Path
import scala.concurrent.duration.DurationInt import scala.concurrent.duration.DurationInt
import scala.concurrent.{ExecutionContext, Future} import scala.concurrent.{ExecutionContext, Future}
@ -75,6 +75,7 @@ trait LndRpcTestUtil extends Logging {
|debuglevel=critical |debuglevel=critical
|listen=127.0.0.1:$port |listen=127.0.0.1:$port
|rpclisten=127.0.0.1:$rpcPort |rpclisten=127.0.0.1:$rpcPort
|externalip=127.0.0.1
|bitcoind.rpcuser = ${bitcoindInstance.authCredentials |bitcoind.rpcuser = ${bitcoindInstance.authCredentials
.asInstanceOf[BitcoindAuthCredentials.PasswordBased] .asInstanceOf[BitcoindAuthCredentials.PasswordBased]
.username} .username}
@ -164,9 +165,14 @@ trait LndRpcTestUtil extends Logging {
val infoF = otherClient.getInfo val infoF = otherClient.getInfo
val nodeIdF = client.getInfo.map(_.identityPubkey) val nodeIdF = client.getInfo.map(_.identityPubkey)
val connectionF: Future[Unit] = infoF.flatMap { info => val connectionF: Future[Unit] = infoF.flatMap { info =>
val uri = otherClient.instance.listenBinding val uriF: Future[URI] = otherClient.instance match {
case local: LndInstanceLocal => Future.successful(local.listenBinding)
case _: LndInstanceRemote =>
otherClient.getInfo.map(info => new URI(info.uris.head))
}
uriF.flatMap(uri =>
client.connectPeer(NodeId(info.identityPubkey), client.connectPeer(NodeId(info.identityPubkey),
new InetSocketAddress(uri.getHost, uri.getPort)) new InetSocketAddress(uri.getHost, uri.getPort)))
} }
def isConnected: Future[Boolean] = { def isConnected: Future[Boolean] = {