mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 18:47:38 +01:00
Add LndInstanceRemote (#3710)
This commit is contained in:
parent
466de3e46a
commit
f6169cc3af
5 changed files with 171 additions and 16 deletions
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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] = {
|
||||||
|
|
Loading…
Add table
Reference in a new issue