Add conversion utils for LndRpcClient (#3660)

This commit is contained in:
benthecarman 2021-09-13 07:32:55 -05:00 committed by GitHub
parent 911fd3429d
commit 74777a9683
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 101 additions and 41 deletions

View File

@ -1,12 +1,16 @@
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.currency.{Bitcoins, Satoshis}
import org.bitcoins.core.number.{Int32, UInt32}
import org.bitcoins.core.protocol.script.EmptyScriptSignature
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.psbt.PSBT
import org.bitcoins.core.wallet.fee.SatoshisPerKW
import org.bitcoins.testkit.fixtures.DualLndFixture
import scala.concurrent.duration.DurationInt
import scala.util.{Failure, Success}
class LndRpcClientPairTest extends DualLndFixture {
@ -23,6 +27,40 @@ class LndRpcClientPairTest extends DualLndFixture {
}
}
it must "sign a transaction" in { param =>
val (bitcoind, lnd, _) = param
for {
addr <- lnd.getNewAddress
_ <- bitcoind.sendToAddress(addr, Bitcoins(1))
bitcoindAddr <- bitcoind.getNewAddress
utxo <- lnd.listUnspent.map(_.head)
prevOut = TransactionOutput(utxo.amount, utxo.spk)
input = TransactionInput(utxo.outPointOpt.get,
EmptyScriptSignature,
TransactionConstants.sequence)
output = TransactionOutput(Bitcoins(0.5), bitcoindAddr.scriptPubKey)
unsigned = BaseTransaction(Int32.two,
Vector(input),
Vector(output),
UInt32.zero)
(scriptSig, wit) <- lnd.computeInputScript(unsigned, 0, prevOut)
} yield {
val psbt = PSBT
.fromUnsignedTx(unsigned)
.addWitnessUTXOToInput(prevOut, 0)
.addFinalizedScriptWitnessToInput(scriptSig, wit, 0)
psbt.extractTransactionAndValidate match {
case Success(_) => succeed
case Failure(exception) => fail(exception)
}
}
}
it must "pay a invoice" in { param =>
val (_, lndA, lndB) = param

View File

@ -22,10 +22,12 @@ import org.bitcoins.core.protocol.transaction.{
Transaction => Tx
}
import org.bitcoins.core.psbt.PSBT
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.util.StartStopAsync
import org.bitcoins.core.wallet.fee.{SatoshisPerKW, SatoshisPerVirtualByte}
import org.bitcoins.crypto._
import org.bitcoins.lnd.rpc.LndRpcClient._
import org.bitcoins.lnd.rpc.LndUtils._
import org.bitcoins.lnd.rpc.config.{LndInstance, LndInstanceLocal}
import scodec.bits.ByteVector
import signrpc._
@ -145,8 +147,7 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
def lookupInvoice(rHash: PaymentHashTag): Future[Invoice] = {
logger.trace("lnd calling lookupinvoice")
val byteStr = ByteString.copyFrom(rHash.bytes.toArray)
val req: PaymentHash = PaymentHash(rHash = byteStr)
val req: PaymentHash = PaymentHash(rHash = rHash.bytes)
lnd.lookupInvoice(req)
}
@ -177,12 +178,11 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
lnd
.addInvoice(invoice)
.map { res =>
val paymentHashBytes = ByteVector(res.rHash.toByteArray)
AddInvoiceResult(
PaymentHashTag(Sha256Digest(paymentHashBytes)),
PaymentHashTag(Sha256Digest(res.rHash)),
LnInvoice.fromString(res.paymentRequest),
res.addIndex,
ByteVector(res.paymentAddr.toByteArray)
res.paymentAddr
)
}
}
@ -210,9 +210,11 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
TransactionOutPoint(txId, vout)
}
val spkBytes = ByteVector.fromValidHex(utxo.pkScript)
UTXOResult(BitcoinAddress.fromString(utxo.address),
Satoshis(utxo.amountSat),
ScriptPubKey(utxo.pkScript),
ScriptPubKey.fromAsmBytes(spkBytes),
outPointOpt,
utxo.confirmations)
@ -271,7 +273,7 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
satPerVByte: SatoshisPerVirtualByte,
privateChannel: Boolean): Future[Option[TransactionOutPoint]] = {
val request = OpenChannelRequest(
nodePubkey = ByteString.copyFrom(nodeId.bytes.toArray),
nodePubkey = nodeId.bytes,
localFundingAmount = fundingAmount.satoshis.toLong,
satPerVbyte = satPerVByte.toLong,
`private` = privateChannel
@ -287,7 +289,7 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
satPerVByte: SatoshisPerVirtualByte,
privateChannel: Boolean): Future[Option[TransactionOutPoint]] = {
val request = OpenChannelRequest(
nodePubkey = ByteString.copyFrom(nodeId.bytes.toArray),
nodePubkey = nodeId.bytes,
localFundingAmount = fundingAmount.satoshis.toLong,
pushSat = pushAmt.satoshis.toLong,
satPerVbyte = satPerVByte.toLong,
@ -305,8 +307,7 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
.openChannelSync(request)
.map { point =>
point.fundingTxid.fundingTxidBytes match {
case Some(bytesStr) =>
val bytes = ByteVector(bytesStr.toByteArray)
case Some(bytes) =>
val txId = DoubleSha256DigestBE(bytes)
Some(TransactionOutPoint(txId, UInt32(point.outputIndex)))
case None => None
@ -379,9 +380,8 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
def sendPayment(
nodeId: NodeId,
amount: CurrencyUnit): Future[SendResponse] = {
val request: SendRequest = SendRequest(
dest = ByteString.copyFrom(nodeId.bytes.toArray),
amt = amount.satoshis.toLong)
val request: SendRequest =
SendRequest(dest = nodeId.bytes, amt = amount.satoshis.toLong)
sendPayment(request)
}
@ -405,12 +405,8 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
feeRate: SatoshisPerKW,
spendUnconfirmed: Boolean): Future[Tx] = {
val txOuts = outputs.map(out =>
TxOut(out.value.satoshis.toLong,
ByteString.copyFrom(out.scriptPubKey.asmBytes.toArray)))
val request = SendOutputsRequest(satPerKw = feeRate.toLong,
outputs = txOuts,
outputs = outputs,
spendUnconfirmed = spendUnconfirmed)
sendOutputs(request)
}
@ -420,15 +416,11 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
wallet
.sendOutputs(request)
.map { res =>
val bytes = ByteVector(res.rawTx.toByteArray)
Tx(bytes)
}
.map(res => Tx(res.rawTx))
}
def finalizePSBT(psbt: PSBT): Future[PSBT] = {
val bytes = ByteString.copyFrom(psbt.bytes.toArray)
val request = FinalizePsbtRequest(bytes)
val request = FinalizePsbtRequest(psbt.bytes)
finalizePSBT(request)
}
@ -438,20 +430,17 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
wallet
.finalizePsbt(request)
.map { res =>
val bytes = ByteVector(res.signedPsbt.toByteArray)
PSBT(bytes)
}
.map(res => PSBT(res.signedPsbt))
}
def computeInputScript(
tx: Tx,
inputIdx: Int,
output: TransactionOutput): Future[(ScriptSignature, ScriptWitness)] = {
val spkBytes = ByteString.copyFrom(output.scriptPubKey.bytes.toArray)
val txOut = TxOut(value = output.value.satoshis.toLong, pkScript = spkBytes)
val signDescriptor =
SignDescriptor(output = Some(txOut), inputIndex = inputIdx)
SignDescriptor(output = Some(output),
sighash = HashType.sigHashAll.num.toInt,
inputIndex = inputIdx)
computeInputScript(tx, Vector(signDescriptor)).map(_.head)
}
@ -461,7 +450,7 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
signDescriptors: Vector[SignDescriptor]): Future[
Vector[(ScriptSignature, ScriptWitness)]] = {
val request: SignReq =
SignReq(ByteString.copyFrom(tx.bytes.toArray), signDescriptors)
SignReq(tx.bytes, signDescriptors)
computeInputScript(request)
}
@ -472,12 +461,9 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
signer.computeInputScript(request).map { res =>
res.inputScripts.map { script =>
val scriptSigByes = ByteVector(script.sigScript.toByteArray)
val scriptSig = ScriptSignature(scriptSigByes)
val scriptSig = ScriptSignature.fromAsmBytes(script.sigScript)
val witness = ScriptWitness(script.witness.reverse.toVector)
val witnessStackBytes =
script.witness.map(w => ByteVector(w.toByteArray))
val witness = ScriptWitness(witnessStackBytes)
(scriptSig, witness)
}.toVector
}
@ -489,7 +475,7 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
def publishTransaction(tx: Tx): Future[Option[String]] = {
logger.trace("lnd calling publishtransaction")
val request = walletrpc.Transaction(ByteString.copyFrom(tx.bytes.toArray))
val request = walletrpc.Transaction(tx.bytes)
wallet
.publishTransaction(request)
@ -578,7 +564,7 @@ class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
// complete the promise with an exception so the runnable will be canceled
p.failure(
new RuntimeException(
s"LndApi.monitorInvoice() [${instance}] too many attempts: ${attempts
s"LndApi.monitorInvoice() [$instance] too many attempts: ${attempts
.get()} for invoice=${rHash.hash.hex}"))
}
}

View File

@ -0,0 +1,36 @@
package org.bitcoins.lnd.rpc
import com.google.protobuf.ByteString
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.transaction.TransactionOutput
import scodec.bits._
import signrpc.TxOut
import scala.language.implicitConversions
object LndUtils {
implicit def byteVecToByteString(byteVector: ByteVector): ByteString =
ByteString.copyFrom(byteVector.toArray)
implicit def byteStringToByteVec(byteString: ByteString): ByteVector =
ByteVector(byteString.toByteArray)
implicit def outputToTxOut(output: TransactionOutput): TxOut =
TxOut(output.value.satoshis.toLong, output.scriptPubKey.asmBytes)
implicit def txOutToTxOutput(txOut: TxOut): TransactionOutput =
TransactionOutput(Satoshis(txOut.value),
ScriptPubKey.fromAsmBytes(txOut.pkScript))
// If other kinds of Iterables are needed, there's a fancy thing to do
// that is done all over the Seq code using params and an implicit CanBuildFrom
implicit def outputVecToTxOuts(
outputs: Vector[TransactionOutput]): Vector[TxOut] =
outputs.map(outputToTxOut)
implicit def byteStringVecToByteVecs(
byteStrings: Vector[ByteString]): Vector[ByteVector] =
byteStrings.map(byteStringToByteVec)
}