mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 01:40:55 +01:00
2019 09 05 v18 rpc (#734)
* This is for colin * Fix remaining errors on V18 RPC calls * remove unused code final formatting fix * implemented fixed listwalletdir rpc test * responding to code review and replacing flatmaps
This commit is contained in:
parent
3aec12df6b
commit
d4ccc2c441
@ -183,24 +183,6 @@ class TestRpcUtilTest extends BitcoindRpcTest {
|
||||
}
|
||||
}
|
||||
|
||||
it should "be able to wait for disconnected nodes" in {
|
||||
for {
|
||||
(first, second) <- BitcoindRpcTestUtil.createUnconnectedNodePair(
|
||||
clientAccum)
|
||||
_ <- first.addNode(second.instance.uri, AddNodeArgument.Add)
|
||||
_ <- BitcoindRpcTestUtil.awaitConnection(first, second)
|
||||
|
||||
peerInfo <- first.getPeerInfo
|
||||
_ = assert(peerInfo.length == 1)
|
||||
_ = assert(peerInfo.head.addnode)
|
||||
_ = assert(peerInfo.head.networkInfo.addr == second.instance.uri)
|
||||
|
||||
_ <- first.disconnectNode(peerInfo.head.networkInfo.addr)
|
||||
_ <- BitcoindRpcTestUtil.awaitDisconnected(first, second)
|
||||
newPeerInfo <- first.getPeerInfo
|
||||
} yield assert(newPeerInfo.isEmpty)
|
||||
}
|
||||
|
||||
it should "be able to find outputs of previous transactions" in {
|
||||
for {
|
||||
(first, second, _) <- clientsF
|
||||
|
@ -19,18 +19,6 @@ class P2PRpcTest extends BitcoindRpcTest {
|
||||
|
||||
behavior of "P2PRpcTest"
|
||||
|
||||
it should "be able to get peer info" in {
|
||||
for {
|
||||
(freshClient, otherFreshClient) <- clientPairF
|
||||
infoList <- freshClient.getPeerInfo
|
||||
} yield {
|
||||
assert(infoList.length >= 0)
|
||||
val info = infoList.head
|
||||
assert(info.addnode)
|
||||
assert(info.networkInfo.addr == otherFreshClient.getDaemon.uri)
|
||||
}
|
||||
}
|
||||
|
||||
it should "be able to get the added node info" in {
|
||||
for {
|
||||
|
||||
|
@ -70,8 +70,8 @@ class WalletRpcTest extends BitcoindRpcTest {
|
||||
} yield {
|
||||
|
||||
val expectedFileName =
|
||||
if (client.instance.getVersion == BitcoindVersion.V17) ""
|
||||
else "wallet.dat"
|
||||
if (client.instance.getVersion == BitcoindVersion.V16) "wallet.dat"
|
||||
else ""
|
||||
|
||||
assert(wallets == Vector(expectedFileName))
|
||||
}
|
||||
|
@ -26,7 +26,19 @@ class BitcoindV16RpcClientTest extends BitcoindRpcTest {
|
||||
lazy val clientsF: Future[(BitcoindV16RpcClient, BitcoindV16RpcClient)] =
|
||||
BitcoindRpcTestUtil.createNodePairV16(clientAccum)
|
||||
|
||||
behavior of "BitoindV16RpcClient"
|
||||
behavior of "BitcoindV16RpcClient"
|
||||
|
||||
it should "be able to get peer info" in {
|
||||
for {
|
||||
(freshClient, otherFreshClient) <- clientsF
|
||||
infoList <- freshClient.getPeerInfo
|
||||
} yield {
|
||||
assert(infoList.length >= 0)
|
||||
val info = infoList.head
|
||||
assert(info.addnode)
|
||||
assert(info.networkInfo.addr == otherFreshClient.getDaemon.uri)
|
||||
}
|
||||
}
|
||||
|
||||
it should "be able to start a V16 bitcoind" in {
|
||||
for {
|
||||
|
@ -29,6 +29,18 @@ class BitcoindV17RpcClientTest extends BitcoindRpcTest {
|
||||
|
||||
behavior of "BitcoindV17RpcClient"
|
||||
|
||||
it should "be able to get peer info" in {
|
||||
for {
|
||||
(freshClient, otherFreshClient) <- clientsF
|
||||
infoList <- freshClient.getPeerInfo
|
||||
} yield {
|
||||
assert(infoList.length >= 0)
|
||||
val info = infoList.head
|
||||
assert(info.addnode)
|
||||
assert(info.networkInfo.addr == otherFreshClient.getDaemon.uri)
|
||||
}
|
||||
}
|
||||
|
||||
it should "test mempool acceptance" in {
|
||||
for {
|
||||
(client, otherClient) <- clientsF
|
||||
|
@ -52,7 +52,8 @@ class PsbtRpcTest extends BitcoindRpcTest {
|
||||
processedPsbt <- client.walletProcessPsbt(psbt)
|
||||
decoded <- client.decodePsbt(processedPsbt.psbt)
|
||||
} yield {
|
||||
assert(decoded.inputs.exists(_.nonWitnessUtxo.isDefined))
|
||||
assert(decoded.inputs.exists(inputs =>
|
||||
inputs.nonWitnessUtxo.isDefined || inputs.witnessUtxo.isDefined))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,101 @@
|
||||
package org.bitcoins.rpc.v18
|
||||
import org.bitcoins.chain.models.BlockHeaderDbHelper
|
||||
import org.bitcoins.core.protocol.blockchain.RegTestNetChainParams
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion
|
||||
import org.bitcoins.rpc.client.common.RpcOpts.AddNodeArgument
|
||||
import org.bitcoins.rpc.client.v18.BitcoindV18RpcClient
|
||||
import org.bitcoins.testkit.chain.BlockHeaderHelper
|
||||
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
|
||||
import org.bitcoins.testkit.util.BitcoindRpcTest
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
class BitcoindV18RpcClientTest extends BitcoindRpcTest {
|
||||
lazy val clientF: Future[BitcoindV18RpcClient] = {
|
||||
val client = new BitcoindV18RpcClient(BitcoindRpcTestUtil.v18Instance())
|
||||
val clientIsStartedF = BitcoindRpcTestUtil.startServers(Vector(client))
|
||||
clientIsStartedF.map(_ => client)
|
||||
}
|
||||
lazy val clientPairF: Future[(BitcoindV18RpcClient, BitcoindV18RpcClient)] =
|
||||
BitcoindRpcTestUtil.createNodePairV18(clientAccum)
|
||||
|
||||
clientF.foreach(c => clientAccum.+=(c))
|
||||
|
||||
behavior of "BitcoindV18RpcClient"
|
||||
|
||||
it should "be able to start a V18 bitcoind instance" in {
|
||||
|
||||
clientF.map { client =>
|
||||
assert(client.version == BitcoindVersion.V18)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
it should "return active rpc commands" in {
|
||||
val generatedF = clientF.flatMap(client =>
|
||||
client.getNewAddress.flatMap(addr => client.generateToAddress(100, addr)))
|
||||
val rpcinfoF =
|
||||
generatedF.flatMap(_ => clientF.flatMap(client => client.getRpcInfo()))
|
||||
|
||||
rpcinfoF.map { result =>
|
||||
assert(result.active_commands.length == 1)
|
||||
}
|
||||
}
|
||||
|
||||
it should "return a list of wallets" in {
|
||||
for {
|
||||
client <- clientF
|
||||
_ <- client.createWallet("Suredbits")
|
||||
list <- client.listWalletDir()
|
||||
} yield {
|
||||
assert(list.wallets.exists(_.name.contains("Suredbits")))
|
||||
}
|
||||
}
|
||||
|
||||
it should "analyze a descriptor" in {
|
||||
|
||||
val descriptor =
|
||||
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)"
|
||||
|
||||
val descriptorF =
|
||||
clientF.flatMap(client => client.getDescriptorInfo(descriptor))
|
||||
|
||||
descriptorF.map { result =>
|
||||
assert(result.isrange.==(false))
|
||||
assert(result.issolvable.==(true))
|
||||
assert(result.hasprivatekeys.==(false))
|
||||
}
|
||||
}
|
||||
|
||||
it should "get node address given a null parameter" in {
|
||||
val nodeF = clientF.flatMap(client => client.getNodeAddresses())
|
||||
|
||||
nodeF.map { result =>
|
||||
assert(result.isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: currently the test doesn't work because of how known nodes work (remove ignore and implement test)
|
||||
it should "get node addresses given a count" ignore {
|
||||
for {
|
||||
(freshClient, otherFreshClient) <- clientPairF
|
||||
freshclientnode <- freshClient.addNode(freshClient.getDaemon.uri,
|
||||
AddNodeArgument.Add)
|
||||
nodeaddress <- freshClient.getNodeAddresses(1)
|
||||
} yield {
|
||||
assert(nodeaddress.head.address == otherFreshClient.instance.uri)
|
||||
assert(nodeaddress.head.services == 1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
it should "successfully submit a header" in {
|
||||
val genesisHeader = RegTestNetChainParams.genesisBlock.blockHeader
|
||||
val genesisHeaderDb =
|
||||
BlockHeaderDbHelper.fromBlockHeader(height = 1, genesisHeader)
|
||||
val nextHeader = BlockHeaderHelper.buildNextHeader(genesisHeaderDb)
|
||||
clientF.flatMap(client =>
|
||||
client.submitHeader(nextHeader.blockHeader).map(_ => succeed))
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package org.bitcoins.rpc.v18
|
||||
import org.bitcoins.core.currency.Bitcoins
|
||||
import org.bitcoins.rpc.client.v18.BitcoindV18RpcClient
|
||||
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
|
||||
import org.bitcoins.testkit.util.BitcoindRpcTest
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
/**Tests for PSBT for RPC calls specific to V18 new PSBT calls
|
||||
*@see https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#test-vectors
|
||||
*/
|
||||
class PsbtRpcTest extends BitcoindRpcTest {
|
||||
|
||||
lazy val clientF: Future[BitcoindV18RpcClient] = {
|
||||
val client = new BitcoindV18RpcClient(BitcoindRpcTestUtil.v18Instance())
|
||||
val clientIsStartedF = BitcoindRpcTestUtil.startServers(Vector(client))
|
||||
clientIsStartedF.map(_ => client)
|
||||
}
|
||||
|
||||
clientF.foreach(c => clientAccum.+=(c))
|
||||
|
||||
behavior of "PsbtRpc"
|
||||
|
||||
it should "return something when analyzePsbt is called" in {
|
||||
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=")
|
||||
resultF.map { result =>
|
||||
val inputs = result.inputs
|
||||
assert(inputs.nonEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
it should "analyze a PSBT and return a non-empty result" 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="
|
||||
val analyzedF = clientF.flatMap(client => client.analyzePsbt(psbt))
|
||||
|
||||
analyzedF.map { result =>
|
||||
assert(result.inputs.exists(_.next.isDefined))
|
||||
assert(result.inputs.exists(_.missing.head.pubkeys.head.nonEmpty))
|
||||
assert(result.inputs.exists(_.missing.head.signatures.isEmpty))
|
||||
assert(result.inputs.exists(_.missing.head.redeemscript.isEmpty))
|
||||
assert(result.inputs.exists(_.missing.head.witnessscript.isEmpty))
|
||||
assert(result.inputs.exists(_.is_final) == false)
|
||||
assert(result.estimated_feerate.isDefined == false)
|
||||
assert(result.estimated_vsize.isDefined == false)
|
||||
assert(result.fee.isDefined)
|
||||
assert(result.next.nonEmpty)
|
||||
}
|
||||
}
|
||||
it should "correctly analyze a psbt " in {
|
||||
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 expectedfee = Bitcoins(0.00090341)
|
||||
val expectedhasutxo = true
|
||||
val expectedisfinal = false
|
||||
val expectedrole = "updater"
|
||||
analyzedF.map { result =>
|
||||
assert(result.fee.get == expectedfee)
|
||||
assert(result.next == expectedrole)
|
||||
assert(result.inputs.head.has_utxo == expectedhasutxo)
|
||||
|
||||
assert(result.inputs.head.is_final == expectedisfinal)
|
||||
|
||||
assert(result.estimated_vsize.isEmpty)
|
||||
|
||||
assert(result.estimated_feerate.isEmpty)
|
||||
|
||||
assert(result.inputs.head.next.get == expectedrole)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//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=="
|
||||
val updatedF = clientF.flatMap(client => client.utxoUpdatePsbt(psbt))
|
||||
|
||||
updatedF.map { result =>
|
||||
assert(result.contains(psbt))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Join psbt looks at the characteristics of a vector of PSBTs and converts them into a singular PSBT.
|
||||
* This test takes test vectors from BIP 157 each missing some characteristic covered by the other. When joined
|
||||
* together the resulting PSBT represented as a string is very different so we can't just search for parts of either
|
||||
* PSBT.
|
||||
*/
|
||||
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 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 joinedF = clientF.flatMap(client => client.joinPsbts(seqofpsbts))
|
||||
|
||||
joinedF.map { result =>
|
||||
assert(
|
||||
result.contains(
|
||||
//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"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -22,21 +22,22 @@ TaskKeys.downloadBitcoind := {
|
||||
Files.createDirectories(binaryDir)
|
||||
}
|
||||
|
||||
val versions = List("0.17.0.1", "0.16.3")
|
||||
val versions = List("0.18.1", "0.17.0.1", "0.16.3")
|
||||
|
||||
logger.debug(
|
||||
s"(Maybe) downloading Bitcoin Core binaries for versions: ${versions.mkString(",")}")
|
||||
|
||||
val platform =
|
||||
if (Properties.isLinux) "x86_64-linux-gnu"
|
||||
else if (Properties.isMac) "osx64"
|
||||
val (platform, suffix) =
|
||||
if (Properties.isLinux) ("x86_64-linux-gnu", "tar.gz")
|
||||
else if (Properties.isMac) ("osx64", "tar.gz")
|
||||
else if (Properties.isWin) ("win64", "zip")
|
||||
else sys.error(s"Unsupported OS: ${Properties.osName}")
|
||||
|
||||
versions.foreach { version =>
|
||||
val versionDir = binaryDir resolve version
|
||||
val archiveLocation = binaryDir resolve s"$version.tar.gz"
|
||||
val archiveLocation = binaryDir resolve s"$version.$suffix"
|
||||
val location =
|
||||
s"https://bitcoincore.org/bin/bitcoin-core-$version/bitcoin-$version-$platform.tar.gz"
|
||||
s"https://bitcoincore.org/bin/bitcoin-core-$version/bitcoin-$version-$platform.$suffix"
|
||||
|
||||
val expectedEndLocation = binaryDir resolve s"bitcoin-$version"
|
||||
if (Files
|
||||
|
@ -86,8 +86,10 @@ sealed trait BitcoindVersion
|
||||
|
||||
object BitcoindVersion {
|
||||
|
||||
/** The newest `bitcoind` version supported by Bitcoin-S */
|
||||
val newest = V17
|
||||
|
||||
|
||||
/** The newest version of `bitcoind` we support */
|
||||
val newest = V18
|
||||
|
||||
case object V16 extends BitcoindVersion {
|
||||
override def toString: String = "v0.16"
|
||||
@ -97,6 +99,10 @@ object BitcoindVersion {
|
||||
override def toString: String = "v0.17"
|
||||
}
|
||||
|
||||
case object V18 extends BitcoindVersion {
|
||||
override def toString: String = "v0.18"
|
||||
}
|
||||
|
||||
case object Unknown extends BitcoindVersion {
|
||||
override def toString: String = "Unknown"
|
||||
}
|
||||
|
@ -49,14 +49,14 @@ trait P2PRpc { self: Client =>
|
||||
bitcoindCall[Int]("getconnectioncount")
|
||||
}
|
||||
|
||||
def getNetworkInfo: Future[GetNetworkInfoResult] = {
|
||||
bitcoindCall[GetNetworkInfoResult]("getnetworkinfo")
|
||||
}
|
||||
|
||||
def getNetTotals: Future[GetNetTotalsResult] = {
|
||||
bitcoindCall[GetNetTotalsResult]("getnettotals")
|
||||
}
|
||||
|
||||
def getNetworkInfo: Future[GetNetworkInfoResult] = {
|
||||
bitcoindCall[GetNetworkInfoResult]("getnetworkinfo")
|
||||
}
|
||||
|
||||
def getPeerInfo: Future[Vector[Peer]] = {
|
||||
bitcoindCall[Vector[Peer]]("getpeerinfo")
|
||||
}
|
||||
|
@ -7,9 +7,11 @@ import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput}
|
||||
import org.bitcoins.rpc.jsonmodels.{
|
||||
FundRawTransactionResult,
|
||||
GetRawTransactionResult,
|
||||
RpcTransaction
|
||||
RpcTransaction,
|
||||
SignRawTransactionResult
|
||||
}
|
||||
import org.bitcoins.rpc.serializers.JsonSerializers._
|
||||
import org.bitcoins.rpc.serializers.JsonWriters._
|
||||
import play.api.libs.json._
|
||||
|
||||
import scala.concurrent.Future
|
||||
@ -94,4 +96,5 @@ trait RawTransactionRpc { self: Client =>
|
||||
"sendrawtransaction",
|
||||
List(JsString(transaction.hex), JsBoolean(allowHighFees)))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -184,4 +184,17 @@ trait WalletRpc { self: Client =>
|
||||
"walletpassphrasechange",
|
||||
List(JsString(currentPassphrase), JsString(newPassphrase)))
|
||||
}
|
||||
|
||||
def createWallet(
|
||||
walletName: String,
|
||||
disablePrivateKeys: Boolean = false): Future[CreateWalletResult] = {
|
||||
bitcoindCall[CreateWalletResult](
|
||||
"createwallet",
|
||||
List(JsString(walletName), Json.toJson(disablePrivateKeys)))
|
||||
}
|
||||
|
||||
def getAddressInfo(address: BitcoinAddress): Future[AddressInfoResult] = {
|
||||
bitcoindCall[AddressInfoResult]("getaddressinfo",
|
||||
List(JsString(address.value)))
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package org.bitcoins.rpc.client.v17
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.core.crypto.ECPrivateKey
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.script.crypto.HashType
|
||||
import org.bitcoins.rpc.client.common.{
|
||||
@ -12,8 +11,6 @@ import org.bitcoins.rpc.client.common.{
|
||||
}
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
import org.bitcoins.rpc.jsonmodels.{
|
||||
AddressInfoResult,
|
||||
CreateWalletResult,
|
||||
SignRawTransactionResult,
|
||||
TestMempoolAcceptResult
|
||||
}
|
||||
@ -44,11 +41,6 @@ class BitcoindV17RpcClient(override val instance: BitcoindInstance)(
|
||||
|
||||
override def version: BitcoindVersion = BitcoindVersion.V17
|
||||
|
||||
def getAddressInfo(address: BitcoinAddress): Future[AddressInfoResult] = {
|
||||
bitcoindCall[AddressInfoResult]("getaddressinfo",
|
||||
List(JsString(address.value)))
|
||||
}
|
||||
|
||||
/**
|
||||
* $signRawTx
|
||||
*
|
||||
@ -93,14 +85,6 @@ class BitcoindV17RpcClient(override val instance: BitcoindInstance)(
|
||||
List(JsArray(Vector(Json.toJson(transaction))), JsBoolean(allowHighFees)))
|
||||
.map(_.head)
|
||||
}
|
||||
|
||||
def createWallet(
|
||||
walletName: String,
|
||||
disablePrivateKeys: Boolean = false): Future[CreateWalletResult] = {
|
||||
bitcoindCall[CreateWalletResult](
|
||||
"createwallet",
|
||||
List(JsString(walletName), Json.toJson(disablePrivateKeys)))
|
||||
}
|
||||
}
|
||||
|
||||
object BitcoindV17RpcClient {
|
||||
|
@ -0,0 +1,99 @@
|
||||
package org.bitcoins.rpc.client.v18
|
||||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
|
||||
import scala.util.Try
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.rpc.client.common.RpcOpts
|
||||
import org.bitcoins.core.crypto.ECPrivateKey
|
||||
import org.bitcoins.core.script.crypto.HashType
|
||||
import org.bitcoins.rpc.jsonmodels.SignRawTransactionResult
|
||||
import play.api.libs.json.Json
|
||||
import play.api.libs.json.JsString
|
||||
import scala.concurrent.Future
|
||||
import org.bitcoins.rpc.serializers.JsonSerializers._
|
||||
import org.bitcoins.rpc.serializers.JsonWriters._
|
||||
|
||||
/**
|
||||
* Class for creating a BitcoindV18 instance that can access RPCs
|
||||
* @param instance
|
||||
* @param actorSystem
|
||||
*/
|
||||
class BitcoindV18RpcClient(override val instance: BitcoindInstance)(
|
||||
implicit
|
||||
actorSystem: ActorSystem)
|
||||
extends BitcoindRpcClient(instance)
|
||||
with V18PsbtRpc
|
||||
with V18DescriptorRpc
|
||||
with V18AssortedRpc {
|
||||
|
||||
override lazy val version: BitcoindVersion = BitcoindVersion.V18
|
||||
|
||||
/**
|
||||
* $signRawTx
|
||||
*
|
||||
* This RPC call signs the raw transaction with keys found in
|
||||
* the Bitcoin Core wallet.
|
||||
*/
|
||||
def signRawTransactionWithWallet(
|
||||
transaction: Transaction,
|
||||
utxoDeps: Vector[RpcOpts.SignRawTransactionOutputParameter] = Vector.empty,
|
||||
sigHash: HashType = HashType.sigHashAll
|
||||
): Future[SignRawTransactionResult] =
|
||||
bitcoindCall[SignRawTransactionResult]("signrawtransactionwithwallet",
|
||||
List(JsString(transaction.hex),
|
||||
Json.toJson(utxoDeps),
|
||||
Json.toJson(sigHash)))
|
||||
|
||||
/**
|
||||
* $signRawTx
|
||||
*
|
||||
* This RPC call signs the raw transaction with keys provided
|
||||
* manually.
|
||||
*/
|
||||
def signRawTransactionWithKey(
|
||||
transaction: Transaction,
|
||||
keys: Vector[ECPrivateKey],
|
||||
utxoDeps: Vector[RpcOpts.SignRawTransactionOutputParameter] = Vector.empty,
|
||||
sigHash: HashType = HashType.sigHashAll
|
||||
): Future[SignRawTransactionResult] =
|
||||
bitcoindCall[SignRawTransactionResult]("signrawtransactionwithkey",
|
||||
List(JsString(transaction.hex),
|
||||
Json.toJson(keys),
|
||||
Json.toJson(utxoDeps),
|
||||
Json.toJson(sigHash)))
|
||||
|
||||
}
|
||||
|
||||
object BitcoindV18RpcClient {
|
||||
|
||||
/**
|
||||
* Creates an RPC client from the given instance.
|
||||
*
|
||||
* Behind the scenes, we create an actor system for
|
||||
* you. You can use `withActorSystem` if you want to
|
||||
* manually specify an actor system for the RPC client.
|
||||
*/
|
||||
def apply(instance: BitcoindInstance): BitcoindV18RpcClient = {
|
||||
implicit val system = ActorSystem.create(BitcoindRpcClient.ActorSystemName)
|
||||
withActorSystem(instance)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an RPC client from the given instance,
|
||||
* together with the given actor system. This is for
|
||||
* advanced users, wher you need fine grained control
|
||||
* over the RPC client.
|
||||
*/
|
||||
def withActorSystem(instance: BitcoindInstance)(
|
||||
implicit system: ActorSystem): BitcoindV18RpcClient =
|
||||
new BitcoindV18RpcClient(instance)(system)
|
||||
|
||||
def fromUnknownVersion(
|
||||
rpcClient: BitcoindRpcClient): Try[BitcoindV18RpcClient] =
|
||||
Try {
|
||||
new BitcoindV18RpcClient(rpcClient.instance)(rpcClient.system)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package org.bitcoins.rpc.client.v18
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||
import org.bitcoins.rpc.client.common.Client
|
||||
import org.bitcoins.rpc.jsonmodels.{
|
||||
GetNodeAddressesResult,
|
||||
GetRpcInfoResult,
|
||||
ListWalletDirResult
|
||||
}
|
||||
import org.bitcoins.rpc.serializers.JsonSerializers._
|
||||
import play.api.libs.json.{JsString, Json}
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
/**
|
||||
* Assorted Rpc calls for Bitcoin V18
|
||||
* @see [[https://bitcoincore.org/en/doc/0.18.0/rpc/network/getnodeaddresses/]]
|
||||
* @see [[https://bitcoincore.org/en/doc/0.18.0/rpc/wallet/listwalletdir/]]
|
||||
* @see [[https://github.com/bitcoin/bitcoin/commit/e82f6ad6f270f1f101d8853be32fd11eff4ddfb8]]
|
||||
* @see [[https://bitcoincore.org/en/doc/0.18.0/rpc/mining/submitheader/]]
|
||||
*/
|
||||
trait V18AssortedRpc {
|
||||
self: Client =>
|
||||
|
||||
private def getNodeAddresses(
|
||||
count: Option[Int]): Future[Vector[GetNodeAddressesResult]] = {
|
||||
bitcoindCall[Vector[GetNodeAddressesResult]]("getnodeaddresses",
|
||||
List(Json.toJson(count)))
|
||||
}
|
||||
|
||||
def getNodeAddresses(count: Int): Future[Vector[GetNodeAddressesResult]] =
|
||||
getNodeAddresses(Some(count))
|
||||
|
||||
def getNodeAddresses(): Future[Vector[GetNodeAddressesResult]] =
|
||||
getNodeAddresses(None)
|
||||
|
||||
def listWalletDir(): Future[ListWalletDirResult] = {
|
||||
bitcoindCall[ListWalletDirResult]("listwalletdir")
|
||||
}
|
||||
|
||||
def getRpcInfo(): Future[GetRpcInfoResult] = {
|
||||
bitcoindCall[GetRpcInfoResult]("getrpcinfo")
|
||||
}
|
||||
|
||||
def submitHeader(header: BlockHeader): Future[Unit] = {
|
||||
bitcoindCall[Unit]("submitheader", List(JsString(header.hex)))
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package org.bitcoins.rpc.client.v18
|
||||
import org.bitcoins.rpc.client.common.Client
|
||||
import org.bitcoins.rpc.jsonmodels.{
|
||||
DeriveAddressesResult,
|
||||
GetDescriptorInfoResult
|
||||
}
|
||||
import play.api.libs.json.{JsString, Json}
|
||||
import org.bitcoins.rpc.serializers.JsonSerializers._
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
/**
|
||||
* RPC calls in V18 that use descriptor to give us output information
|
||||
* @see [[https://bitcoincore.org/en/doc/0.18.0/rpc/util/deriveaddresses/]]
|
||||
* @see [[https://bitcoincore.org/en/doc/0.18.0/rpc/util/getdescriptorinfo/]]
|
||||
*/
|
||||
trait V18DescriptorRpc {
|
||||
self: Client =>
|
||||
|
||||
def deriveAddresses(
|
||||
descriptor: String,
|
||||
range: Option[Vector[Double]]): Future[DeriveAddressesResult] = {
|
||||
bitcoindCall[DeriveAddressesResult](
|
||||
"deriveaddresses",
|
||||
List(JsString(descriptor), Json.toJson(range)))
|
||||
}
|
||||
|
||||
def getDescriptorInfo(descriptor: String): Future[GetDescriptorInfoResult] = {
|
||||
bitcoindCall[GetDescriptorInfoResult]("getdescriptorinfo",
|
||||
List(JsString(descriptor)))
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package org.bitcoins.rpc.client.v18
|
||||
|
||||
import org.bitcoins.rpc.client.common.Client
|
||||
import org.bitcoins.rpc.jsonmodels.AnalyzePsbtResult
|
||||
import org.bitcoins.rpc.serializers.JsonSerializers._
|
||||
import play.api.libs.json._
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
/**
|
||||
* Set of utilities to analyze, join, and update existing PSBTs
|
||||
* @see [[https://bitcoincore.org/en/doc/0.18.0/rpc/rawtransactions/analyzepsbt/]]
|
||||
* @see [[https://bitcoincore.org/en/doc/0.18.0/rpc/rawtransactions/joinpsbts/]]
|
||||
* @see [[https://bitcoincore.org/en/doc/0.18.0/rpc/rawtransactions/utxoupdatepsbt/]]
|
||||
*/
|
||||
trait V18PsbtRpc {
|
||||
self: Client =>
|
||||
|
||||
def analyzePsbt(psbt: String): Future[AnalyzePsbtResult] = {
|
||||
bitcoindCall[AnalyzePsbtResult]("analyzepsbt", List(JsString(psbt)))
|
||||
}
|
||||
|
||||
def joinPsbts(txs: Seq[String]): Future[String] = {
|
||||
bitcoindCall[String]("joinpsbts", List(Json.toJson(txs)))
|
||||
}
|
||||
|
||||
def utxoUpdatePsbt(psbt: String): Future[String] = {
|
||||
bitcoindCall[String]("utxoupdatepsbt", List(JsString(psbt)))
|
||||
}
|
||||
|
||||
}
|
@ -9,7 +9,15 @@ import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion
|
||||
|
||||
import scala.sys.process._
|
||||
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.core.config.NetworkParameters
|
||||
|
||||
import scala.util.Properties
|
||||
import java.nio.file.Files
|
||||
|
||||
import scala.util.Properties
|
||||
|
||||
|
||||
/**
|
||||
* Created by chris on 4/29/17.
|
||||
@ -48,6 +56,8 @@ sealed trait BitcoindInstance extends BitcoinSLogger {
|
||||
BitcoindVersion.V16
|
||||
case _: String if foundVersion.startsWith(BitcoindVersion.V17.toString) =>
|
||||
BitcoindVersion.V17
|
||||
case _: String if foundVersion.startsWith(BitcoindVersion.V18.toString) =>
|
||||
BitcoindVersion.V18
|
||||
case _: String => BitcoindVersion.Unknown
|
||||
}
|
||||
}
|
||||
@ -95,6 +105,7 @@ object BitcoindInstance {
|
||||
|
||||
val path = cmd
|
||||
new File(path.trim)
|
||||
|
||||
}
|
||||
|
||||
/** Constructs a `bitcoind` instance from the given datadir, using the
|
||||
|
@ -4,6 +4,9 @@ import java.net.URI
|
||||
|
||||
import org.bitcoins.core.currency.Bitcoins
|
||||
import org.bitcoins.core.number.{UInt32, UInt64}
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerKiloByte
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
sealed abstract class NetworkResult
|
||||
|
||||
@ -72,7 +75,8 @@ case class Peer(
|
||||
inflight: Vector[Int],
|
||||
whitelisted: Boolean,
|
||||
bytessent_per_msg: Map[String, Int],
|
||||
bytesrecv_per_msg: Map[String, Int])
|
||||
bytesrecv_per_msg: Map[String, Int],
|
||||
minfeefilter: Option[SatoshisPerKiloByte])
|
||||
extends NetworkResult
|
||||
|
||||
case class PeerNetworkInfo(
|
||||
@ -98,3 +102,10 @@ case class NodeBan(
|
||||
ban_created: UInt32,
|
||||
ban_reason: String)
|
||||
extends NetworkResult
|
||||
|
||||
final case class GetNodeAddressesResult(
|
||||
time: FiniteDuration,
|
||||
services: Int,
|
||||
address: java.net.URI,
|
||||
port: Int
|
||||
) extends NetworkResult
|
||||
|
@ -9,6 +9,7 @@ import org.bitcoins.core.crypto.{
|
||||
import org.bitcoins.core.currency.Satoshis
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.wallet.fee.BitcoinFeeUnit
|
||||
@ -53,8 +54,8 @@ case class BlockTransaction(
|
||||
|
||||
case class GetMiningInfoResult(
|
||||
blocks: Int,
|
||||
currentblockweight: Int,
|
||||
currentblocktx: Int,
|
||||
currentblockweight: Option[Int],
|
||||
currentblocktx: Option[Int],
|
||||
difficulty: BigDecimal,
|
||||
networkhashps: BigDecimal,
|
||||
pooledtx: Int,
|
||||
@ -99,8 +100,6 @@ trait ValidateAddressResult {
|
||||
@deprecated("Use 'getaddressinfo' instead", since = "0.16")
|
||||
def hex: Option[String]
|
||||
|
||||
@deprecated("Use 'getaddressinfo' instead", since = "0.16")
|
||||
def addresses: Option[Vector[BitcoinAddress]]
|
||||
def sigsrequired: Option[Int]
|
||||
|
||||
@deprecated("Use 'getaddressinfo' instead", since = "0.16")
|
||||
@ -117,6 +116,15 @@ trait ValidateAddressResult {
|
||||
|
||||
@deprecated("Use 'getaddressinfo' instead", since = "0.16")
|
||||
def hdmasterkeyid: Option[Sha256Hash160Digest]
|
||||
|
||||
@deprecated("Use 'getaddressinfo' instead", since = "0.16")
|
||||
def ischange: Option[Boolean]
|
||||
|
||||
@deprecated("Use 'getaddressinfo' instead", since = "0.16")
|
||||
def solvable: Option[Boolean]
|
||||
|
||||
@deprecated("Use 'getaddressinfo' instead", since = "0.16")
|
||||
def desc: Option[String]
|
||||
}
|
||||
|
||||
case class ValidateAddressResultImpl(
|
||||
@ -128,13 +136,15 @@ case class ValidateAddressResultImpl(
|
||||
isscript: Option[Boolean],
|
||||
script: Option[String],
|
||||
hex: Option[String],
|
||||
addresses: Option[Vector[BitcoinAddress]],
|
||||
sigsrequired: Option[Int],
|
||||
pubkey: Option[ECPublicKey],
|
||||
iscompressed: Option[Boolean],
|
||||
account: Option[String],
|
||||
hdkeypath: Option[String],
|
||||
hdmasterkeyid: Option[Sha256Hash160Digest])
|
||||
hdmasterkeyid: Option[Sha256Hash160Digest],
|
||||
ischange: Option[Boolean],
|
||||
solvable: Option[Boolean],
|
||||
desc: Option[String])
|
||||
extends ValidateAddressResult
|
||||
|
||||
case class EstimateSmartFeeResult(
|
||||
@ -148,3 +158,15 @@ case class TestMempoolAcceptResult(
|
||||
allowed: Boolean,
|
||||
rejectReason: Option[String]
|
||||
)
|
||||
|
||||
final case class DeriveAddressesResult(addresses: Vector[BitcoinAddress])
|
||||
extends OtherResult
|
||||
|
||||
final case class GetDescriptorInfoResult(
|
||||
descriptor: String,
|
||||
isrange: Boolean,
|
||||
issolvable: Boolean,
|
||||
hasprivatekeys: Boolean
|
||||
) extends OtherResult
|
||||
|
||||
final case class SubmitHeaderResult(header: BlockHeader) extends OtherResult
|
||||
|
@ -8,6 +8,8 @@ import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput}
|
||||
import org.bitcoins.core.protocol.{BitcoinAddress, P2PKHAddress, P2SHAddress}
|
||||
import org.bitcoins.core.script.ScriptType
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
sealed abstract class RawTransactionResult
|
||||
|
||||
case class RpcTransaction(
|
||||
@ -91,3 +93,12 @@ case class SignRawTransactionError(
|
||||
sequence: UInt32,
|
||||
error: String)
|
||||
extends RawTransactionResult
|
||||
|
||||
final case class GetRpcInfoResult(
|
||||
active_commands: Vector[RpcCommands]
|
||||
) extends RawTransactionResult
|
||||
|
||||
final case class RpcCommands(
|
||||
method: String,
|
||||
duration: FiniteDuration //this time is in microseconds
|
||||
) extends RawTransactionResult
|
||||
|
@ -69,3 +69,23 @@ final case class WalletCreateFundedPsbtResult(
|
||||
fee: Bitcoins,
|
||||
changepos: Int
|
||||
) extends RpcPsbtResult
|
||||
|
||||
final case class AnalyzePsbtResult(
|
||||
inputs: Vector[AnalyzePsbtInput],
|
||||
estimated_vsize: Option[Double],
|
||||
estimated_feerate: Option[Double],
|
||||
fee: Option[Bitcoins],
|
||||
next: String
|
||||
) extends RpcPsbtResult
|
||||
final case class AnalyzePsbtInput(
|
||||
has_utxo: Boolean,
|
||||
is_final: Boolean,
|
||||
missing: Option[PsbtMissingData],
|
||||
next: Option[String]
|
||||
) extends RpcPsbtResult
|
||||
final case class PsbtMissingData(
|
||||
pubkeys: Option[Vector[ECPublicKey]],
|
||||
signatures: Option[Vector[ECDigitalSignature]],
|
||||
redeemscript: Option[RpcPsbtScript],
|
||||
witnessscript: Option[RpcPsbtScript]
|
||||
) extends RpcPsbtResult
|
||||
|
@ -72,7 +72,7 @@ case class GetWalletInfoResult(
|
||||
keypoolsize: Int,
|
||||
keypoolsize_hd_internal: Int,
|
||||
paytxfee: BitcoinFeeUnit,
|
||||
hdmasterkeyid: Sha256Hash160Digest,
|
||||
hdmasterkeyid: Option[Sha256Hash160Digest],
|
||||
unlocked_until: Option[Int])
|
||||
extends WalletResult
|
||||
|
||||
@ -225,6 +225,14 @@ case class EmbeddedResult(
|
||||
|
||||
case class LabelResult(name: String, purpose: LabelPurpose) extends WalletResult
|
||||
|
||||
final case class ListWalletDirResult(
|
||||
wallets: Vector[ArrayOfWalletsInput]
|
||||
) extends WalletResult
|
||||
|
||||
final case class ArrayOfWalletsInput(
|
||||
name: String
|
||||
) extends WalletResult
|
||||
|
||||
final case class CreateWalletResult(
|
||||
name: String,
|
||||
warning: String
|
||||
|
@ -6,7 +6,7 @@ import java.net.{InetAddress, URI}
|
||||
import org.bitcoins.core.crypto._
|
||||
import org.bitcoins.core.currency.{Bitcoins, Satoshis}
|
||||
import org.bitcoins.core.hd.BIP32Path
|
||||
import org.bitcoins.core.number.{Int32, UInt32, UInt64}
|
||||
import org.bitcoins.core.number.{Int32, Int64, UInt32, UInt64}
|
||||
import org.bitcoins.core.protocol.blockchain.{Block, BlockHeader, MerkleBlock}
|
||||
import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptSignature}
|
||||
import org.bitcoins.core.protocol.transaction.{
|
||||
@ -21,7 +21,7 @@ import org.bitcoins.core.protocol.{
|
||||
P2SHAddress
|
||||
}
|
||||
import org.bitcoins.core.script.ScriptType
|
||||
import org.bitcoins.core.wallet.fee.BitcoinFeeUnit
|
||||
import org.bitcoins.core.wallet.fee.{BitcoinFeeUnit, SatoshisPerKiloByte}
|
||||
import org.bitcoins.rpc.client.common.RpcOpts.AddressType
|
||||
import org.bitcoins.rpc.jsonmodels._
|
||||
import org.bitcoins.rpc.serializers.JsonReaders._
|
||||
@ -30,7 +30,10 @@ import java.time.LocalDateTime
|
||||
import play.api.libs.functional.syntax._
|
||||
import play.api.libs.json._
|
||||
|
||||
import scala.concurrent.duration.DurationLong
|
||||
|
||||
object JsonSerializers {
|
||||
|
||||
implicit val bigIntReads: Reads[BigInt] = BigIntReads
|
||||
implicit val localDateTimeReads: Reads[LocalDateTime] = LocalDateTimeReads
|
||||
|
||||
@ -145,8 +148,17 @@ object JsonSerializers {
|
||||
implicit val networkInfoReads: Reads[GetNetworkInfoResult] =
|
||||
Json.reads[GetNetworkInfoResult]
|
||||
|
||||
implicit val satsPerKbReads: Reads[SatoshisPerKiloByte] =
|
||||
new Reads[SatoshisPerKiloByte] {
|
||||
|
||||
def reads(json: JsValue): JsResult[SatoshisPerKiloByte] =
|
||||
SerializerUtil.processJsNumber(num =>
|
||||
SatoshisPerKiloByte(Satoshis(Int64(num.toBigInt))))(json)
|
||||
}
|
||||
|
||||
implicit val peerNetworkInfoReads: Reads[PeerNetworkInfo] =
|
||||
Json.reads[PeerNetworkInfo]
|
||||
|
||||
implicit val peerReads: Reads[Peer] = ((__ \ "id").read[Int] and
|
||||
__.read[PeerNetworkInfo] and
|
||||
(__ \ "version").read[Int] and
|
||||
@ -160,7 +172,8 @@ object JsonSerializers {
|
||||
(__ \ "inflight").read[Vector[Int]] and
|
||||
(__ \ "whitelisted").read[Boolean] and
|
||||
(__ \ "bytessent_per_msg").read[Map[String, Int]] and
|
||||
(__ \ "bytesrecv_per_msg").read[Map[String, Int]])(Peer)
|
||||
(__ \ "bytesrecv_per_msg").read[Map[String, Int]] and
|
||||
(__ \ "minfeefilter").readNullable[SatoshisPerKiloByte])(Peer)
|
||||
|
||||
implicit val nodeBanReads: Reads[NodeBan] = Json.reads[NodeBan]
|
||||
|
||||
@ -368,9 +381,54 @@ object JsonSerializers {
|
||||
|
||||
implicit val rpcPsbtInputReads: Reads[RpcPsbtInput] = RpcPsbtInputReads
|
||||
|
||||
implicit val decodePsbtResult: Reads[DecodePsbtResult] =
|
||||
implicit val decodePsbtResultReads: Reads[DecodePsbtResult] =
|
||||
Json.reads[DecodePsbtResult]
|
||||
|
||||
implicit val psbtMissingDataReads: Reads[PsbtMissingData] =
|
||||
Json.reads[PsbtMissingData]
|
||||
|
||||
implicit val analyzePsbtInputReads: Reads[AnalyzePsbtInput] =
|
||||
Json.reads[AnalyzePsbtInput]
|
||||
|
||||
implicit val analyzePsbtResultReads: Reads[AnalyzePsbtResult] =
|
||||
Json.reads[AnalyzePsbtResult]
|
||||
|
||||
implicit val getNodeAddressesReads: Reads[GetNodeAddressesResult] =
|
||||
Reads[GetNodeAddressesResult] { js =>
|
||||
for {
|
||||
time <- (js \ "time").validate[Long].map(_.seconds)
|
||||
services <- (js \ "services").validate[Int]
|
||||
address <- (js \ "address").validate[URI]
|
||||
port <- (js \ "port").validate[Int]
|
||||
} yield GetNodeAddressesResult(time, services, address, port)
|
||||
}
|
||||
|
||||
implicit val rgetpcCommandsReads: Reads[RpcCommands] = Reads[RpcCommands] {
|
||||
js =>
|
||||
for {
|
||||
method <- (js \ "method").validate[String]
|
||||
duration <- (js \ "duration").validate[Long].map(_.microseconds)
|
||||
} yield RpcCommands(method, duration)
|
||||
}
|
||||
|
||||
implicit val getRpcInfoResultReads: Reads[GetRpcInfoResult] =
|
||||
Json.reads[GetRpcInfoResult]
|
||||
|
||||
implicit val arrayOfWalletsInputReads: Reads[ArrayOfWalletsInput] =
|
||||
Json.reads[ArrayOfWalletsInput]
|
||||
|
||||
implicit val listWalletsDirResultReads: Reads[ListWalletDirResult] =
|
||||
Json.reads[ListWalletDirResult]
|
||||
|
||||
implicit val deriveAddressesResultReads: Reads[DeriveAddressesResult] =
|
||||
Json.reads[DeriveAddressesResult]
|
||||
|
||||
implicit val submitHeaderResultReads: Reads[SubmitHeaderResult] =
|
||||
Json.reads[SubmitHeaderResult]
|
||||
|
||||
implicit val getDescriptorInfoResultReads: Reads[GetDescriptorInfoResult] =
|
||||
Json.reads[GetDescriptorInfoResult]
|
||||
|
||||
implicit val walletCreateFundedPsbtResultReads: Reads[
|
||||
WalletCreateFundedPsbtResult] = Json.reads[WalletCreateFundedPsbtResult]
|
||||
|
||||
@ -400,4 +458,5 @@ object JsonSerializers {
|
||||
|
||||
implicit val outputMapWrites: Writes[Map[BitcoinAddress, Bitcoins]] =
|
||||
mapWrites[BitcoinAddress, Bitcoins](_.value)
|
||||
|
||||
}
|
||||
|
@ -16,8 +16,10 @@ import scala.util.{Failure, Success}
|
||||
object JsonReaders {
|
||||
import org.bitcoins.rpc.serializers.JsonReaders._
|
||||
|
||||
implicit val feeProportionalMillionthsReads: Reads[FeeProportionalMillionths] = Reads { js =>
|
||||
SerializerUtil.processJsNumberBigInt(FeeProportionalMillionths.fromBigInt)(js)
|
||||
implicit val feeProportionalMillionthsReads: Reads[
|
||||
FeeProportionalMillionths] = Reads { js =>
|
||||
SerializerUtil.processJsNumberBigInt(FeeProportionalMillionths.fromBigInt)(
|
||||
js)
|
||||
}
|
||||
|
||||
implicit val channelStateReads: Reads[ChannelState] = {
|
||||
@ -85,7 +87,8 @@ object JsonReaders {
|
||||
|
||||
implicit val shortChannelIdReads: Reads[ShortChannelId] = {
|
||||
Reads { jsValue =>
|
||||
SerializerUtil.processJsString(ShortChannelId.fromHumanReadableString)(jsValue)
|
||||
SerializerUtil.processJsString(ShortChannelId.fromHumanReadableString)(
|
||||
jsValue)
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,14 +123,13 @@ object JsonReaders {
|
||||
paymentHash <- (jsValue \ "paymentHash").validate[Sha256Digest]
|
||||
expiry <- (jsValue \ "expiry").validate[Long]
|
||||
} yield
|
||||
InvoiceResult(
|
||||
prefix,
|
||||
timestamp.seconds,
|
||||
nodeId,
|
||||
serialized,
|
||||
description,
|
||||
paymentHash,
|
||||
expiry.seconds)
|
||||
InvoiceResult(prefix,
|
||||
timestamp.seconds,
|
||||
nodeId,
|
||||
serialized,
|
||||
description,
|
||||
paymentHash,
|
||||
expiry.seconds)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ import org.bitcoins.rpc.client.common.{
|
||||
}
|
||||
import org.bitcoins.rpc.client.v16.BitcoindV16RpcClient
|
||||
import org.bitcoins.rpc.client.v17.BitcoindV17RpcClient
|
||||
import org.bitcoins.rpc.client.v18.BitcoindV18RpcClient
|
||||
import org.bitcoins.rpc.config.{
|
||||
BitcoindAuthCredentials,
|
||||
BitcoindInstance,
|
||||
@ -45,6 +46,7 @@ import org.bitcoins.util.ListUtil
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.immutable.Map
|
||||
import scala.collection.mutable
|
||||
import org.bitcoins.core.compat.JavaConverters._
|
||||
import scala.concurrent._
|
||||
import scala.concurrent.duration.{DurationInt, FiniteDuration}
|
||||
import scala.util._
|
||||
@ -57,9 +59,11 @@ import java.nio.file.Path
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion.Unknown
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion.V16
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion.V17
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion.V18
|
||||
import java.nio.file.Files
|
||||
|
||||
import org.bitcoins.testkit.util.FileUtil
|
||||
import org.bitcoins.rpc.BitcoindException
|
||||
|
||||
//noinspection AccessorLikeMethodIsEmptyParen
|
||||
trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
@ -171,8 +175,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
private def getBinary(version: BitcoindVersion): File = version match {
|
||||
// default to newest version
|
||||
case Unknown => getBinary(BitcoindVersion.newest)
|
||||
case known @ (V16 | V17) =>
|
||||
import org.bitcoins.core.compat.JavaConverters._
|
||||
case known @ (V16 | V17 | V18) =>
|
||||
val versionFolder = Files
|
||||
.list(binaryDirectory)
|
||||
.iterator()
|
||||
@ -194,7 +197,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
|
||||
versionFolder
|
||||
.resolve("bin")
|
||||
.resolve("bitcoind")
|
||||
.resolve(if (Properties.isWin) "bitcoind.exe" else "bitcoind")
|
||||
.toFile()
|
||||
}
|
||||
|
||||
@ -266,6 +269,18 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
pruneMode = pruneMode,
|
||||
versionOpt = Some(BitcoindVersion.V17))
|
||||
|
||||
def v18Instance(
|
||||
port: Int = RpcUtil.randomPort,
|
||||
rpcPort: Int = RpcUtil.randomPort,
|
||||
zmqPort: Int = RpcUtil.randomPort,
|
||||
pruneMode: Boolean = false
|
||||
): BitcoindInstance =
|
||||
instance(port = port,
|
||||
rpcPort = rpcPort,
|
||||
zmqPort = zmqPort,
|
||||
pruneMode = pruneMode,
|
||||
versionOpt = Some(BitcoindVersion.V18))
|
||||
|
||||
def startServers(servers: Vector[BitcoindRpcClient])(
|
||||
implicit ec: ExecutionContext): Future[Unit] = {
|
||||
val startedServers = servers.map(_.start())
|
||||
@ -391,7 +406,8 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
import system.dispatcher
|
||||
|
||||
for {
|
||||
hashes <- clients.head.generate(blocks)
|
||||
address <- clients.head.getNewAddress
|
||||
hashes <- clients.head.generateToAddress(blocks, address)
|
||||
_ <- {
|
||||
val pairs = ListUtil.uniquePairs(clients)
|
||||
val syncFuts = pairs.map {
|
||||
@ -455,11 +471,10 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
.getAddedNodeInfo(to.getDaemon.uri)
|
||||
.map(info => info.isEmpty || info.head.connected.contains(false))
|
||||
.recoverWith {
|
||||
case exception: Exception
|
||||
case exception: BitcoindException
|
||||
if exception.getMessage.contains("Node has not been added") =>
|
||||
from.getPeerInfo.map { peerInfo =>
|
||||
peerInfo.forall(_.networkInfo.addr != to.instance.uri)
|
||||
}
|
||||
from.getPeerInfo.map(
|
||||
_.forall(_.networkInfo.addr != to.instance.uri))
|
||||
}
|
||||
|
||||
}
|
||||
@ -554,6 +569,9 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
case BitcoindVersion.V17 =>
|
||||
BitcoindV17RpcClient.withActorSystem(
|
||||
BitcoindRpcTestUtil.v17Instance())
|
||||
case BitcoindVersion.V18 =>
|
||||
BitcoindV18RpcClient.withActorSystem(
|
||||
BitcoindRpcTestUtil.v18Instance())
|
||||
}
|
||||
|
||||
// this is safe as long as this method is never
|
||||
@ -619,6 +637,15 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
(BitcoindV17RpcClient, BitcoindV17RpcClient)] =
|
||||
createNodePairInternal(BitcoindVersion.V17, clientAccum)
|
||||
|
||||
/**
|
||||
* Returns a pair of [[org.bitcoins.rpc.client.v18.BitcoindV18RpcClient BitcoindV18RpcClient]]
|
||||
* that are connected with some blocks in the chain
|
||||
*/
|
||||
def createNodePairV18(clientAccum: RpcClientAccum = Vector.newBuilder)(
|
||||
implicit system: ActorSystem): Future[
|
||||
(BitcoindV18RpcClient, BitcoindV18RpcClient)] =
|
||||
createNodePairInternal(BitcoindVersion.V18, clientAccum)
|
||||
|
||||
/**
|
||||
* Returns a triple of [[org.bitcoins.rpc.client.common.BitcoindRpcClient BitcoindRpcClient]]
|
||||
* that are connected with some blocks in the chain
|
||||
@ -664,7 +691,8 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
amount: Bitcoins = Bitcoins(1))(
|
||||
implicit executionContext: ExecutionContext): Future[Transaction] = {
|
||||
for {
|
||||
blocks <- sender.generate(2)
|
||||
address <- sender.getNewAddress
|
||||
blocks <- sender.generateToAddress(2, address)
|
||||
block0 <- sender.getBlock(blocks(0))
|
||||
block1 <- sender.getBlock(blocks(1))
|
||||
transaction0 <- sender.getTransaction(block0.tx(0))
|
||||
@ -707,17 +735,18 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
case unknown: BitcoindRpcClient =>
|
||||
val v16T = BitcoindV16RpcClient.fromUnknownVersion(unknown)
|
||||
val v17T = BitcoindV17RpcClient.fromUnknownVersion(unknown)
|
||||
(v16T, v17T) match {
|
||||
case (Failure(_), Failure(_)) =>
|
||||
val v18T = BitcoindV18RpcClient.fromUnknownVersion(unknown)
|
||||
(v16T, v17T, v18T) match {
|
||||
case (Failure(_), Failure(_), Failure(_)) =>
|
||||
throw new RuntimeException(
|
||||
"Could not figure out version of provided bitcoind RPC client!")
|
||||
case (Success(_), Success(_)) =>
|
||||
throw new RuntimeException(
|
||||
"This should not happen, managed to construct different versioned RPC clients from one single client")
|
||||
case (Success(v16), Failure(_)) =>
|
||||
"Could not figure out version of provided bitcoind RPC client!" +
|
||||
"This should not happen, managed to construct different versioned RPC clients from one single client")
|
||||
case (Success(v16), _, _) =>
|
||||
v16.signRawTransaction(transaction, utxoDeps)
|
||||
case (Failure(_), Success(v17)) =>
|
||||
case (_, Success(v17), _) =>
|
||||
v17.signRawTransactionWithWallet(transaction, utxoDeps)
|
||||
case (_, _, Success(v18)) =>
|
||||
v18.signRawTransactionWithWallet(transaction, utxoDeps)
|
||||
}
|
||||
}
|
||||
|
||||
@ -752,19 +781,16 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
implicit val materializer: ActorMaterializer =
|
||||
ActorMaterializer.create(actorSystem)
|
||||
implicit val ec: ExecutionContextExecutor = materializer.executionContext
|
||||
createRawCoinbaseTransaction(sender, receiver, amount)
|
||||
.flatMap(signRawTransaction(sender, _))
|
||||
.flatMap { signedTransaction =>
|
||||
sender
|
||||
.generate(100)
|
||||
.flatMap { _ => // Can't spend coinbase until depth 100
|
||||
sender
|
||||
.sendRawTransaction(signedTransaction.hex, allowHighFees = true)
|
||||
.flatMap { transactionHash =>
|
||||
sender.getTransaction(transactionHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
for {
|
||||
rawcoinbasetx <- createRawCoinbaseTransaction(sender, receiver, amount)
|
||||
signedtx <- signRawTransaction(sender, rawcoinbasetx)
|
||||
addr <- sender.getNewAddress
|
||||
_ <- sender.generateToAddress(100, addr)
|
||||
// Can't spend coinbase until depth 100
|
||||
transactionHash <- sender.sendRawTransaction(signedtx.hex,
|
||||
allowHighFees = true)
|
||||
transaction <- sender.getTransaction(transactionHash)
|
||||
} yield transaction
|
||||
}
|
||||
|
||||
/**
|
||||
@ -792,7 +818,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
implicit val mat: ActorMaterializer = ActorMaterializer.create(system)
|
||||
implicit val ec: ExecutionContextExecutor = mat.executionContext
|
||||
fundMemPoolTransaction(sender, address, amount).flatMap { txid =>
|
||||
sender.generate(1).map { _ =>
|
||||
sender.getNewAddress.flatMap(sender.generateToAddress(1, _)).map { _ =>
|
||||
txid
|
||||
}
|
||||
}
|
||||
@ -872,7 +898,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
//fund the wallet by generating 102 blocks, need this to get over coinbase maturity
|
||||
val generatedF = startedF.flatMap { _ =>
|
||||
clientAccum += rpc
|
||||
rpc.generate(blocksToGenerate)
|
||||
rpc.getNewAddress.flatMap(rpc.generateToAddress(blocksToGenerate, _))
|
||||
}
|
||||
|
||||
def areBlocksGenerated(): Future[Boolean] = {
|
||||
|
Loading…
Reference in New Issue
Block a user