mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-13 19:37:30 +01:00
Use PSBT type in bitcoind calls (#2242)
This commit is contained in:
parent
e4194220c1
commit
e830547773
13 changed files with 158 additions and 153 deletions
|
@ -4,18 +4,19 @@ import org.bitcoins.core.currency.Bitcoins
|
|||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.psbt.PSBT
|
||||
import org.bitcoins.core.script.ScriptType
|
||||
import org.bitcoins.core.script.crypto.HashType
|
||||
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey}
|
||||
|
||||
sealed abstract class RpcPsbtResult
|
||||
|
||||
final case class WalletProcessPsbtResult(psbt: String, complete: Boolean)
|
||||
final case class WalletProcessPsbtResult(psbt: PSBT, complete: Boolean)
|
||||
extends RpcPsbtResult
|
||||
|
||||
sealed abstract class FinalizePsbtResult extends RpcPsbtResult
|
||||
final case class FinalizedPsbt(hex: Transaction) extends FinalizePsbtResult
|
||||
final case class NonFinalizedPsbt(psbt: String) extends FinalizePsbtResult
|
||||
final case class NonFinalizedPsbt(psbt: PSBT) extends FinalizePsbtResult
|
||||
|
||||
final case class DecodePsbtResult(
|
||||
tx: RpcTransaction,
|
||||
|
@ -67,7 +68,7 @@ final case class RpcPsbtOutput(
|
|||
) extends RpcPsbtResult
|
||||
|
||||
final case class WalletCreateFundedPsbtResult(
|
||||
psbt: String, // todo change me
|
||||
psbt: PSBT,
|
||||
fee: Bitcoins,
|
||||
changepos: Int
|
||||
) extends RpcPsbtResult
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.bitcoins.core.protocol.{
|
|||
P2PKHAddress,
|
||||
P2SHAddress
|
||||
}
|
||||
import org.bitcoins.core.psbt.PSBT
|
||||
import org.bitcoins.core.script.ScriptType
|
||||
import org.bitcoins.core.script.crypto.HashType
|
||||
import org.bitcoins.core.wallet.fee.{BitcoinFeeUnit, SatoshisPerByte}
|
||||
|
@ -394,6 +395,12 @@ object JsonReaders {
|
|||
SerializerUtil.processJsString[Transaction](Transaction.fromHex)(json)
|
||||
}
|
||||
|
||||
implicit object PSBTReads extends Reads[PSBT] {
|
||||
|
||||
override def reads(json: JsValue): JsResult[PSBT] =
|
||||
SerializerUtil.processJsString[PSBT](PSBT.fromString)(json)
|
||||
}
|
||||
|
||||
implicit object TransactionOutPointReads extends Reads[TransactionOutPoint] {
|
||||
private case class OutPoint(txid: DoubleSha256DigestBE, vout: UInt32)
|
||||
|
||||
|
@ -479,7 +486,7 @@ object JsonReaders {
|
|||
if ((json \ "hex").isDefined) {
|
||||
JsError("PSBT was submitted as a serialized hex transaction!")
|
||||
} else {
|
||||
(json \ "psbt").validate[String].map(NonFinalizedPsbt)
|
||||
(json \ "psbt").validate[PSBT].map(NonFinalizedPsbt)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.bitcoins.core.psbt.{
|
|||
OutputPSBTRecord
|
||||
}
|
||||
import org.bitcoins.core.script.constant.ScriptToken
|
||||
import org.bitcoins.core.psbt.PSBT
|
||||
import org.bitcoins.crypto._
|
||||
import play.api.libs.functional.syntax._
|
||||
import play.api.libs.json._
|
||||
|
@ -75,6 +76,7 @@ object JsonSerializers {
|
|||
implicit val bitcoinAddressReads: Reads[BitcoinAddress] = BitcoinAddressReads
|
||||
implicit val merkleBlockReads: Reads[MerkleBlock] = MerkleBlockReads
|
||||
implicit val transactionReads: Reads[Transaction] = TransactionReads
|
||||
implicit val psbtReads: Reads[PSBT] = PSBTReads
|
||||
|
||||
implicit val transactionOutPointReads: Reads[TransactionOutPoint] =
|
||||
TransactionOutPointReads
|
||||
|
|
|
@ -10,11 +10,7 @@ import org.bitcoins.core.protocol.BitcoinAddress
|
|||
import org.bitcoins.core.protocol.ln.currency.MilliSatoshis
|
||||
import org.bitcoins.core.protocol.script.{ScriptPubKey, WitnessScriptPubKey}
|
||||
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput}
|
||||
import org.bitcoins.core.psbt.{
|
||||
GlobalPSBTRecord,
|
||||
InputPSBTRecord,
|
||||
OutputPSBTRecord
|
||||
}
|
||||
import org.bitcoins.core.psbt._
|
||||
import org.bitcoins.core.script.crypto._
|
||||
import org.bitcoins.core.util.BytesUtil
|
||||
import org.bitcoins.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
|
||||
|
@ -90,6 +86,10 @@ object JsonWriters {
|
|||
override def writes(o: Transaction): JsValue = JsString(o.hex)
|
||||
}
|
||||
|
||||
implicit object PSBTWrites extends Writes[PSBT] {
|
||||
override def writes(o: PSBT): JsValue = JsString(o.base64)
|
||||
}
|
||||
|
||||
implicit def mapWrites[K, V](keyString: K => String)(implicit
|
||||
vWrites: Writes[V]): Writes[Map[K, V]] =
|
||||
new Writes[Map[K, V]] {
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.bitcoins.core.protocol.transaction.{
|
|||
TransactionInput,
|
||||
TransactionOutPoint
|
||||
}
|
||||
import org.bitcoins.core.psbt.PSBT
|
||||
import org.bitcoins.rpc.client.v17.BitcoindV17RpcClient
|
||||
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
|
||||
import org.bitcoins.testkit.util.BitcoindRpcTest
|
||||
|
@ -37,7 +38,7 @@ class PsbtRpcTest extends BitcoindRpcTest {
|
|||
"cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA=",
|
||||
"cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA=",
|
||||
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==" // this one is from Core
|
||||
)
|
||||
).map(PSBT.fromBase64)
|
||||
|
||||
for {
|
||||
(client, _, _) <- clientsF
|
||||
|
@ -53,7 +54,7 @@ class PsbtRpcTest extends BitcoindRpcTest {
|
|||
client.createRawTransaction(Vector.empty, Map(address -> Bitcoins.one))
|
||||
fundedRawTx <- client.fundRawTransaction(rawTx)
|
||||
psbt <- client.convertToPsbt(fundedRawTx.hex)
|
||||
processedPsbt <- client.walletProcessPsbt(psbt)
|
||||
processedPsbt <- client.walletProcessPSBT(psbt)
|
||||
decoded <- client.decodePsbt(processedPsbt.psbt)
|
||||
} yield {
|
||||
assert(decoded.inputs.exists(inputs =>
|
||||
|
@ -75,7 +76,7 @@ class PsbtRpcTest extends BitcoindRpcTest {
|
|||
psbt <-
|
||||
client.createPsbt(Vector(TransactionInput.fromTxidAndVout(txid, vout)),
|
||||
Map(newAddr -> Bitcoins(0.5)))
|
||||
processed <- client.walletProcessPsbt(psbt)
|
||||
processed <- client.walletProcessPSBT(psbt)
|
||||
finalized <- client.finalizePsbt(processed.psbt)
|
||||
} yield finalized match {
|
||||
case _: FinalizedPsbt => succeed
|
||||
|
@ -119,11 +120,11 @@ class PsbtRpcTest extends BitcoindRpcTest {
|
|||
thirdClient.createPsbt(inputs, Map(newAddr -> Bitcoins(1.5)))
|
||||
}
|
||||
// Update psbts, should only have data for one input and not the other
|
||||
clientProcessedPsbt <- client.walletProcessPsbt(psbt).map(_.psbt)
|
||||
clientProcessedPsbt <- client.walletProcessPSBT(psbt).map(_.psbt)
|
||||
|
||||
otherClientProcessedPsbt <-
|
||||
otherClient
|
||||
.walletProcessPsbt(psbt)
|
||||
.walletProcessPSBT(psbt)
|
||||
.map(_.psbt)
|
||||
|
||||
// Combine and finalize the psbts
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.bitcoins.rpc.v18
|
||||
|
||||
import org.bitcoins.core.currency.Bitcoins
|
||||
import org.bitcoins.core.psbt.PSBT
|
||||
import org.bitcoins.rpc.client.v18.BitcoindV18RpcClient
|
||||
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
|
||||
import org.bitcoins.testkit.util.BitcoindRpcTest
|
||||
|
@ -23,10 +24,12 @@ class PsbtRpcTest extends BitcoindRpcTest {
|
|||
behavior of "PsbtRpc"
|
||||
|
||||
it should "return something when analyzePsbt is called" in {
|
||||
//PSBT with one P2PKH input and one P2SH-P2WPKH input both with non-final scriptSigs. P2SH-P2WPKH input's redeemScript is available. Outputs filled.
|
||||
val psbt =
|
||||
"cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA="
|
||||
|
||||
clientF.flatMap { client =>
|
||||
val resultF = client.analyzePsbt(
|
||||
//PSBT with one P2PKH input and one P2SH-P2WPKH input both with non-final scriptSigs. P2SH-P2WPKH input's redeemScript is available. Outputs filled.
|
||||
"cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=")
|
||||
val resultF = client.analyzePsbt(PSBT.fromBase64(psbt))
|
||||
resultF.map { result =>
|
||||
val inputs = result.inputs
|
||||
assert(inputs.nonEmpty)
|
||||
|
@ -38,7 +41,8 @@ class PsbtRpcTest extends BitcoindRpcTest {
|
|||
|
||||
val psbt =
|
||||
"cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA="
|
||||
val analyzedF = clientF.flatMap(client => client.analyzePsbt(psbt))
|
||||
val analyzedF =
|
||||
clientF.flatMap(client => client.analyzePsbt(PSBT.fromBase64(psbt)))
|
||||
|
||||
analyzedF.map { result =>
|
||||
assert(result.inputs.exists(_.next.isDefined))
|
||||
|
@ -57,7 +61,8 @@ class PsbtRpcTest extends BitcoindRpcTest {
|
|||
val psbt =
|
||||
//PSBT with one P2PKH input and one P2SH-P2WPKH input both with non-final scriptSigs. P2SH-P2WPKH input's redeemScript is available. Outputs filled.
|
||||
"cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA="
|
||||
val analyzedF = clientF.flatMap(client => client.analyzePsbt(psbt))
|
||||
val analyzedF =
|
||||
clientF.flatMap(client => client.analyzePsbt(PSBT.fromBase64(psbt)))
|
||||
val expectedfee = Bitcoins(0.00090341)
|
||||
val expectedhasutxo = true
|
||||
val expectedisfinal = false
|
||||
|
@ -81,11 +86,12 @@ class PsbtRpcTest extends BitcoindRpcTest {
|
|||
//Todo: figure out how to implement a test here
|
||||
it should "check to see if the utxoUpdate input has been updated" in {
|
||||
val psbt =
|
||||
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA=="
|
||||
PSBT.fromBase64(
|
||||
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==")
|
||||
val updatedF = clientF.flatMap(client => client.utxoUpdatePsbt(psbt))
|
||||
|
||||
updatedF.map { result =>
|
||||
assert(result.contains(psbt))
|
||||
assert(result == psbt)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,18 +103,22 @@ class PsbtRpcTest extends BitcoindRpcTest {
|
|||
*/
|
||||
it should "joinpsbts " in {
|
||||
val seqofpsbts = Vector(
|
||||
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA",
|
||||
PSBT.fromBase64(
|
||||
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA"),
|
||||
//PSBT with one P2PKH input and one P2SH-P2WPKH input both with non-final scriptSigs. P2SH-P2WPKH input's redeemScript is available. Outputs filled.
|
||||
"cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA="
|
||||
PSBT.fromBase64(
|
||||
"cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA="
|
||||
)
|
||||
)
|
||||
|
||||
val joinedF = clientF.flatMap(client => client.joinPsbts(seqofpsbts))
|
||||
|
||||
joinedF.map { result =>
|
||||
assert(
|
||||
result.contains(
|
||||
result ==
|
||||
//the expected joined version of these 2 psbts
|
||||
"cHNidP8BAP0LAQIAAAADJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAAP7///+rCUmgjFr3xJuCEvQX4vFas/XDPc8VOCGoE5+Helt75AAAAAAA/v///6sJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAQAAAAD+////BNPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh2A76gsAAAAAGXapFHaKQLvXQMvoHZiOcd4qTVxxOWsdiKyOJAAAAAAAABl2qRRvRiC1U/oJXnIbnuDv6foDnMpFl4isAAAAAAABAMwBAAAAAomjxx6rTSDgNxu7pMxpj6KVyUY6+i45f4UzzLYvlWflAQAAABcWABS+GNFSqbASA52vPafeT1M0nuy5hf////+G+KpDpx3/FEiJOlMKcjfva0YIu7LdLQFx5jrsakiQtAEAAAAXFgAU/j6e8adF6XTZAsQ1WUOryzS9U1P/////AgDC6wsAAAAAGXapFIXP8Ql/2eAIuzSvcJxiGXs4l4pIiKxy/vhOLAAAABepFDOXJboh79Yqx1OpvNBn1semo50FhwAAAAAAAQDfAgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAABqRzBEAiBwsiRRI+a/R01gxbUMBD1MaRpdJDXwmjSnZiqdwlF5CgIgATKcqdrPKAvfMHQOwDkEIkIsgctFg5RXrrdvwS7dlbMBIQJlfRGNM1e44PTCzUbbezn22cONmnCry5st5dyNv+TOMf7///8C09/1BQAAAAAZdqkU0MWZA8W6woaHYOkP1SGkZlqnZSCIrADh9QUAAAAAF6kUNUXm4zuDLEcFDyTT7rk8nAOUi8eHsy4TAAABASAA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHhwEEFgAUhdE1N/LiZUBaNNuvqePdoB+4IwgAAAAiAgLq1ZZofKgGBD7cPeEWzfKdXpJXwZbNBVz2mMjQK/JOmRC0prpnAAAAgAAAAIACAACAACICA5T2K+nfGZUsVYd2iut2mAYa0sSiXIlPR9jBYrTXIT0FELSmumcAAACAAQAAgAIAAIAA"))
|
||||
PSBT.fromBase64(
|
||||
"cHNidP8BAP0LAQIAAAADJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAAP7///+rCUmgjFr3xJuCEvQX4vFas/XDPc8VOCGoE5+Helt75AAAAAAA/v///6sJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAQAAAAD+////BNPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh2A76gsAAAAAGXapFHaKQLvXQMvoHZiOcd4qTVxxOWsdiKyOJAAAAAAAABl2qRRvRiC1U/oJXnIbnuDv6foDnMpFl4isAAAAAAABAMwBAAAAAomjxx6rTSDgNxu7pMxpj6KVyUY6+i45f4UzzLYvlWflAQAAABcWABS+GNFSqbASA52vPafeT1M0nuy5hf////+G+KpDpx3/FEiJOlMKcjfva0YIu7LdLQFx5jrsakiQtAEAAAAXFgAU/j6e8adF6XTZAsQ1WUOryzS9U1P/////AgDC6wsAAAAAGXapFIXP8Ql/2eAIuzSvcJxiGXs4l4pIiKxy/vhOLAAAABepFDOXJboh79Yqx1OpvNBn1semo50FhwAAAAAAAQDfAgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAABqRzBEAiBwsiRRI+a/R01gxbUMBD1MaRpdJDXwmjSnZiqdwlF5CgIgATKcqdrPKAvfMHQOwDkEIkIsgctFg5RXrrdvwS7dlbMBIQJlfRGNM1e44PTCzUbbezn22cONmnCry5st5dyNv+TOMf7///8C09/1BQAAAAAZdqkU0MWZA8W6woaHYOkP1SGkZlqnZSCIrADh9QUAAAAAF6kUNUXm4zuDLEcFDyTT7rk8nAOUi8eHsy4TAAABASAA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHhwEEFgAUhdE1N/LiZUBaNNuvqePdoB+4IwgAAAAiAgLq1ZZofKgGBD7cPeEWzfKdXpJXwZbNBVz2mMjQK/JOmRC0prpnAAAAgAAAAIACAACAACICA5T2K+nfGZUsVYd2iut2mAYa0sSiXIlPR9jBYrTXIT0FELSmumcAAACAAQAAgAIAAIAA"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.bitcoins.commons.jsonmodels.bitcoind.{
|
|||
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.WalletFlag
|
||||
import org.bitcoins.core.config.RegTest
|
||||
import org.bitcoins.core.gcs.{BlockFilter, FilterType}
|
||||
import org.bitcoins.core.psbt.PSBT
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion
|
||||
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
||||
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
|
||||
|
@ -124,13 +125,14 @@ class BitcoindV19RpcClientTest extends BitcoindRpcTest {
|
|||
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)"
|
||||
|
||||
val psbt =
|
||||
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA=="
|
||||
PSBT.fromBase64(
|
||||
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==")
|
||||
|
||||
for {
|
||||
(client, _) <- clientPairF
|
||||
result <- client.utxoUpdatePsbt(psbt, Seq(descriptor))
|
||||
} yield {
|
||||
assert(result.contains(psbt))
|
||||
assert(result == psbt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.bitcoins.commons.jsonmodels.bitcoind._
|
|||
import org.bitcoins.core.config.RegTest
|
||||
import org.bitcoins.core.gcs.{BlockFilter, FilterType}
|
||||
import org.bitcoins.core.protocol.transaction.EmptyTransaction
|
||||
import org.bitcoins.core.psbt.PSBT
|
||||
import org.bitcoins.crypto.ECPublicKey
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion
|
||||
import org.bitcoins.rpc.client.v20.BitcoindV20RpcClient
|
||||
|
@ -120,13 +121,14 @@ class BitcoindV20RpcClientTest extends BitcoindRpcTest {
|
|||
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)"
|
||||
|
||||
val psbt =
|
||||
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA=="
|
||||
PSBT.fromBase64(
|
||||
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==")
|
||||
|
||||
for {
|
||||
(client, _) <- clientPairF
|
||||
result <- client.utxoUpdatePsbt(psbt, Seq(descriptor))
|
||||
} yield {
|
||||
assert(result.contains(psbt))
|
||||
assert(result == psbt)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ class BitcoindRpcClient(val instance: BitcoindInstance)(implicit
|
|||
with TransactionRpc
|
||||
with UTXORpc
|
||||
with WalletRpc
|
||||
with PsbtRpc
|
||||
with UtilRpc {
|
||||
|
||||
override def version: BitcoindVersion = BitcoindVersion.Unknown
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
package org.bitcoins.rpc.client.common
|
||||
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind.AnalyzePsbtResult
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind.{
|
||||
AnalyzePsbtResult,
|
||||
DecodePsbtResult,
|
||||
FinalizePsbtResult
|
||||
}
|
||||
import org.bitcoins.commons.serializers.JsonSerializers._
|
||||
import play.api.libs.json.{JsString, Json}
|
||||
import org.bitcoins.commons.serializers.JsonWriters._
|
||||
import org.bitcoins.core.currency.{Bitcoins, CurrencyUnit}
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput}
|
||||
import org.bitcoins.core.psbt.PSBT
|
||||
import play.api.libs.json._
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
|
@ -16,21 +25,64 @@ import scala.concurrent.Future
|
|||
trait PsbtRpc {
|
||||
self: Client =>
|
||||
|
||||
def analyzePsbt(psbt: String): Future[AnalyzePsbtResult] = {
|
||||
bitcoindCall[AnalyzePsbtResult]("analyzepsbt", List(JsString(psbt)))
|
||||
def analyzePsbt(psbt: PSBT): Future[AnalyzePsbtResult] = {
|
||||
bitcoindCall[AnalyzePsbtResult]("analyzepsbt", List(JsString(psbt.base64)))
|
||||
}
|
||||
|
||||
def joinPsbts(txs: Seq[String]): Future[String] = {
|
||||
bitcoindCall[String]("joinpsbts", List(Json.toJson(txs)))
|
||||
def joinPsbts(psbts: Seq[PSBT]): Future[PSBT] = {
|
||||
bitcoindCall[PSBT]("joinpsbts", List(Json.toJson(psbts)))
|
||||
}
|
||||
|
||||
def utxoUpdatePsbt(psbt: String): Future[String] = {
|
||||
bitcoindCall[String]("utxoupdatepsbt", List(JsString(psbt)))
|
||||
def utxoUpdatePsbt(psbt: PSBT): Future[PSBT] = {
|
||||
bitcoindCall[PSBT]("utxoupdatepsbt", List(JsString(psbt.base64)))
|
||||
}
|
||||
|
||||
def utxoUpdatePsbt(psbt: String, descriptors: Seq[String]): Future[String] = {
|
||||
bitcoindCall[String]("utxoupdatepsbt",
|
||||
List(JsString(psbt), Json.toJson(descriptors)))
|
||||
def utxoUpdatePsbt(psbt: PSBT, descriptors: Seq[String]): Future[PSBT] = {
|
||||
bitcoindCall[PSBT]("utxoupdatepsbt",
|
||||
List(JsString(psbt.base64), Json.toJson(descriptors)))
|
||||
}
|
||||
|
||||
def convertToPsbt(
|
||||
rawTx: Transaction,
|
||||
permitSigData: Boolean = false,
|
||||
isWitness: Option[Boolean] = None): Future[PSBT] = {
|
||||
val firstArgs: List[JsValue] =
|
||||
List(Json.toJson(rawTx), JsBoolean(permitSigData))
|
||||
val args: List[JsValue] = firstArgs ++ isWitness.map(Json.toJson(_)).toList
|
||||
bitcoindCall[PSBT]("converttopsbt", args)
|
||||
}
|
||||
|
||||
def createPsbt(
|
||||
inputs: Vector[TransactionInput],
|
||||
outputs: Map[BitcoinAddress, CurrencyUnit],
|
||||
locktime: Int = 0,
|
||||
replacable: Boolean = false): Future[PSBT] = {
|
||||
val outputsJson =
|
||||
Json.toJson {
|
||||
outputs.map {
|
||||
case (addr, curr) => addr -> Bitcoins(curr.satoshis)
|
||||
}
|
||||
}
|
||||
bitcoindCall[PSBT]("createpsbt",
|
||||
List(Json.toJson(inputs),
|
||||
outputsJson,
|
||||
JsNumber(locktime),
|
||||
JsBoolean(replacable)))
|
||||
}
|
||||
|
||||
def combinePsbt(psbts: Vector[PSBT]): Future[PSBT] = {
|
||||
bitcoindCall[PSBT]("combinepsbt", List(Json.toJson(psbts)))
|
||||
}
|
||||
|
||||
def finalizePsbt(
|
||||
psbt: PSBT,
|
||||
extract: Boolean = true): Future[FinalizePsbtResult] = {
|
||||
bitcoindCall[FinalizePsbtResult](
|
||||
"finalizepsbt",
|
||||
List(JsString(psbt.base64), JsBoolean(extract)))
|
||||
}
|
||||
|
||||
def decodePsbt(psbt: PSBT): Future[DecodePsbtResult] =
|
||||
bitcoindCall[DecodePsbtResult]("decodepsbt", List(Json.toJson(psbt)))
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.bitcoins.rpc.client.common
|
|||
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.{
|
||||
AddressType,
|
||||
WalletCreateFundedPsbtOptions,
|
||||
WalletFlag
|
||||
}
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind._
|
||||
|
@ -11,7 +12,9 @@ import org.bitcoins.core.crypto.ECPrivateKeyUtil
|
|||
import org.bitcoins.core.currency.{Bitcoins, CurrencyUnit}
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.blockchain.MerkleBlock
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput}
|
||||
import org.bitcoins.core.psbt.PSBT
|
||||
import org.bitcoins.core.script.crypto.HashType
|
||||
import org.bitcoins.crypto.{
|
||||
DoubleSha256Digest,
|
||||
DoubleSha256DigestBE,
|
||||
|
@ -475,4 +478,39 @@ trait WalletRpc { self: Client =>
|
|||
uriExtensionOpt = walletNameOpt.map(walletExtension)
|
||||
)
|
||||
}
|
||||
|
||||
def walletProcessPSBT(
|
||||
psbt: PSBT,
|
||||
sign: Boolean = true,
|
||||
sigHashType: HashType = HashType.sigHashAll,
|
||||
walletNameOpt: Option[String] = None): Future[WalletProcessPsbtResult] = {
|
||||
bitcoindCall[WalletProcessPsbtResult](
|
||||
"walletprocesspsbt",
|
||||
List(JsString(psbt.base64), JsBoolean(sign), Json.toJson(sigHashType)),
|
||||
uriExtensionOpt = walletNameOpt.map(walletExtension)
|
||||
)
|
||||
}
|
||||
|
||||
def walletCreateFundedPsbt(
|
||||
inputs: Vector[TransactionInput],
|
||||
outputs: Map[BitcoinAddress, CurrencyUnit],
|
||||
locktime: Int = 0,
|
||||
options: WalletCreateFundedPsbtOptions = WalletCreateFundedPsbtOptions(),
|
||||
bip32derivs: Boolean = false,
|
||||
walletNameOpt: Option[String] = None
|
||||
): Future[WalletCreateFundedPsbtResult] = {
|
||||
val jsonOutputs =
|
||||
Json.toJson {
|
||||
outputs.map { case (addr, curr) => addr -> Bitcoins(curr.satoshis) }
|
||||
}
|
||||
bitcoindCall[WalletCreateFundedPsbtResult](
|
||||
"walletcreatefundedpsbt",
|
||||
List(Json.toJson(inputs),
|
||||
jsonOutputs,
|
||||
JsNumber(locktime),
|
||||
Json.toJson(options),
|
||||
Json.toJson(bip32derivs)),
|
||||
uriExtensionOpt = walletNameOpt.map(walletExtension)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,8 +34,7 @@ import scala.util.Try
|
|||
class BitcoindV17RpcClient(override val instance: BitcoindInstance)(implicit
|
||||
actorSystem: ActorSystem)
|
||||
extends BitcoindRpcClient(instance)
|
||||
with V17LabelRpc
|
||||
with V17PsbtRpc {
|
||||
with V17LabelRpc {
|
||||
|
||||
override def version: BitcoindVersion = BitcoindVersion.V17
|
||||
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
package org.bitcoins.rpc.client.v17
|
||||
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.WalletCreateFundedPsbtOptions
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind.{
|
||||
DecodePsbtResult,
|
||||
FinalizePsbtResult,
|
||||
WalletCreateFundedPsbtResult,
|
||||
WalletProcessPsbtResult
|
||||
}
|
||||
import org.bitcoins.commons.serializers.JsonSerializers._
|
||||
import org.bitcoins.commons.serializers.JsonWriters._
|
||||
import org.bitcoins.core.currency.{Bitcoins, CurrencyUnit}
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput}
|
||||
import org.bitcoins.core.script.crypto.HashType
|
||||
import org.bitcoins.rpc.client.common.Client
|
||||
import play.api.libs.json._
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
/**
|
||||
* RPC calls related to PSBT (partially signed bitcoin transactions)
|
||||
* handling in Bitcoin Core.
|
||||
*
|
||||
* @note The PSBT format is currently not supported by Bitcoin-S.
|
||||
* Therefore raw strings are returned in several of these
|
||||
* RPCs.
|
||||
*
|
||||
* @see [[https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki BIP174]] on PSBTs
|
||||
* and [[https://github.com/bitcoin/bitcoin/blob/master/doc/psbt.md PSBT Howto for Bitcoin Core]]
|
||||
*/
|
||||
trait V17PsbtRpc { self: Client =>
|
||||
|
||||
def convertToPsbt(
|
||||
rawTx: Transaction,
|
||||
permitSigData: Boolean = false,
|
||||
isWitness: Option[Boolean] = None): Future[String] = {
|
||||
val firstArgs: List[JsValue] =
|
||||
List(Json.toJson(rawTx), JsBoolean(permitSigData))
|
||||
val args: List[JsValue] = firstArgs ++ isWitness.map(Json.toJson(_)).toList
|
||||
bitcoindCall[String]("converttopsbt", args)
|
||||
}
|
||||
|
||||
def createPsbt(
|
||||
inputs: Vector[TransactionInput],
|
||||
outputs: Map[BitcoinAddress, CurrencyUnit],
|
||||
locktime: Int = 0,
|
||||
replacable: Boolean = false): Future[String] = {
|
||||
val outputsJson =
|
||||
Json.toJson {
|
||||
outputs.map {
|
||||
case (addr, curr) => addr -> Bitcoins(curr.satoshis)
|
||||
}
|
||||
}
|
||||
bitcoindCall[String]("createpsbt",
|
||||
List(Json.toJson(inputs),
|
||||
outputsJson,
|
||||
JsNumber(locktime),
|
||||
JsBoolean(replacable)))
|
||||
}
|
||||
|
||||
def combinePsbt(psbts: Vector[String]): Future[String] = {
|
||||
bitcoindCall[String]("combinepsbt", List(Json.toJson(psbts)))
|
||||
}
|
||||
|
||||
def finalizePsbt(
|
||||
psbt: String,
|
||||
extract: Boolean = true): Future[FinalizePsbtResult] = {
|
||||
bitcoindCall[FinalizePsbtResult]("finalizepsbt",
|
||||
List(JsString(psbt), JsBoolean(extract)))
|
||||
}
|
||||
|
||||
def walletCreateFundedPsbt(
|
||||
inputs: Vector[TransactionInput],
|
||||
outputs: Map[BitcoinAddress, CurrencyUnit],
|
||||
locktime: Int = 0,
|
||||
options: WalletCreateFundedPsbtOptions = WalletCreateFundedPsbtOptions(),
|
||||
bip32derivs: Boolean = false
|
||||
): Future[WalletCreateFundedPsbtResult] = {
|
||||
val jsonOutputs =
|
||||
Json.toJson {
|
||||
outputs.map { case (addr, curr) => addr -> Bitcoins(curr.satoshis) }
|
||||
}
|
||||
bitcoindCall[WalletCreateFundedPsbtResult](
|
||||
"walletcreatefundedpsbt",
|
||||
List(Json.toJson(inputs),
|
||||
jsonOutputs,
|
||||
JsNumber(locktime),
|
||||
Json.toJson(options),
|
||||
Json.toJson(bip32derivs))
|
||||
)
|
||||
}
|
||||
|
||||
def walletProcessPsbt(
|
||||
psbt: String,
|
||||
sign: Boolean = true,
|
||||
sighashType: HashType = HashType.sigHashAll,
|
||||
bip32Derivs: Boolean = false
|
||||
): Future[WalletProcessPsbtResult] = {
|
||||
val args = List(JsString(psbt),
|
||||
JsBoolean(sign),
|
||||
Json.toJson(sighashType),
|
||||
JsBoolean(bip32Derivs))
|
||||
|
||||
bitcoindCall[WalletProcessPsbtResult]("walletprocesspsbt", args)
|
||||
}
|
||||
|
||||
def decodePsbt(psbt: String): Future[DecodePsbtResult] =
|
||||
bitcoindCall[DecodePsbtResult]("decodepsbt", List(Json.toJson(psbt)))
|
||||
}
|
Loading…
Add table
Reference in a new issue