mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-19 05:43:51 +01:00
Add conversion utils for LndRpcClient (#3660)
This commit is contained in:
parent
911fd3429d
commit
74777a9683
@ -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
|
||||
|
||||
|
@ -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}"))
|
||||
}
|
||||
}
|
||||
|
36
lnd-rpc/src/main/scala/org/bitcoins/lnd/rpc/LndUtils.scala
Normal file
36
lnd-rpc/src/main/scala/org/bitcoins/lnd/rpc/LndUtils.scala
Normal 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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user