Add sendouputs function to lnd rpc (#2858)

This commit is contained in:
benthecarman 2021-04-05 13:56:21 -05:00 committed by GitHub
parent d726c498d0
commit 825024fa1a
2 changed files with 95 additions and 4 deletions

View File

@ -2,6 +2,8 @@ package org.bitcoins.lnd.rpc
import org.bitcoins.asyncutil.AsyncUtil
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.protocol.transaction.TransactionOutput
import org.bitcoins.core.wallet.fee.SatoshisPerKW
import org.bitcoins.testkit.fixtures.DualLndFixture
import scala.concurrent.duration.DurationInt
@ -49,4 +51,30 @@ class LndRpcClientPairTest extends DualLndFixture {
_ <- lndA.monitorInvoice(invoice.rHash)
} yield succeed
}
it must "send outputs from one node to another" in { params =>
val (bitcoind, lndA, lndB) = params
val sendAmt = Satoshis(10000)
val feeRate = SatoshisPerKW.fromLong(1000)
for {
oldBalA <- lndA.walletBalance().map(_.balance)
oldBalB <- lndB.walletBalance().map(_.balance)
addr <- lndB.getNewAddress
output = TransactionOutput(sendAmt, addr.scriptPubKey)
tx <- lndA.sendOutputs(Vector(output), feeRate, spendUnconfirmed = false)
_ <- lndA.publishTransaction(tx)
_ <- bitcoind.getNewAddress.flatMap(bitcoind.generateToAddress(6, _))
newBalA <- lndA.walletBalance().map(_.balance)
newBalB <- lndB.walletBalance().map(_.balance)
} yield {
assert(newBalB == oldBalB + sendAmt)
// account for variance in fees
assert(newBalA === oldBalA - sendAmt - feeRate.calc(tx) +- Satoshis(6))
}
}
}

View File

@ -8,20 +8,28 @@ import lnrpc._
import org.bitcoins.commons.jsonmodels.lnd._
import org.bitcoins.core.currency._
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.ln.LnInvoice
import org.bitcoins.core.protocol.ln.currency.MilliSatoshis
import org.bitcoins.core.protocol.ln.node.NodeId
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.transaction.TransactionOutPoint
import org.bitcoins.core.protocol.transaction.{
TransactionOutPoint,
TransactionOutput
}
import org.bitcoins.core.protocol.{transaction, BitcoinAddress}
import org.bitcoins.core.util.StartStopAsync
import org.bitcoins.core.wallet.fee.SatoshisPerByte
import org.bitcoins.core.wallet.fee.{
SatoshisPerByte,
SatoshisPerKW,
SatoshisPerVirtualByte
}
import org.bitcoins.crypto.DoubleSha256DigestBE
import org.bitcoins.lnd.rpc.LndRpcClient._
import org.bitcoins.lnd.rpc.config.LndInstance
import org.bitcoins.rpc.util.NativeProcessFactory
import scodec.bits.ByteVector
import walletrpc.WalletKitClient
import signrpc.TxOut
import walletrpc.{SendOutputsRequest, WalletKitClient}
import java.io.{File, FileInputStream}
import java.net.InetSocketAddress
@ -386,6 +394,61 @@ class LndRpcClient(val instance: LndInstance, binary: Option[File] = None)(
.invoke(request)
}
def sendOutputs(
outputs: Vector[TransactionOutput],
feeRate: SatoshisPerVirtualByte,
spendUnconfirmed: Boolean): Future[transaction.Transaction] = {
sendOutputs(outputs, feeRate.toSatoshisPerKW, spendUnconfirmed)
}
def sendOutputs(
outputs: Vector[TransactionOutput],
feeRate: SatoshisPerKW,
spendUnconfirmed: Boolean): Future[transaction.Transaction] = {
val txOuts = outputs.map(out =>
TxOut(out.value.satoshis.toLong,
ByteString.copyFrom(out.scriptPubKey.asmBytes.toArray)))
val request = SendOutputsRequest(satPerKw = feeRate.toLong,
outputs = txOuts,
spendUnconfirmed = spendUnconfirmed)
sendOutputs(request)
}
def sendOutputs(
request: SendOutputsRequest): Future[transaction.Transaction] = {
logger.trace("lnd calling sendoutputs")
wallet
.sendOutputs()
.addHeader(macaroonKey, instance.macaroon)
.invoke(request)
.map { res =>
val bytes = ByteVector(res.rawTx.toByteArray)
transaction.Transaction(bytes)
}
}
/** Broadcasts the given transaction
* @return None if no error, otherwise the error string
*/
def publishTransaction(
tx: transaction.Transaction): Future[Option[String]] = {
logger.trace("lnd calling publishtransaction")
val request = walletrpc.Transaction(ByteString.copyFrom(tx.bytes.toArray))
wallet
.publishTransaction()
.addHeader(macaroonKey, instance.macaroon)
.invoke(request)
.map { res =>
if (res.publishError.isEmpty) None
else Some(res.publishError)
}
}
def monitorInvoice(
rHash: ByteVector,
interval: FiniteDuration = 1.second,