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:
Chris Stewart 2019-09-05 09:15:00 -05:00 committed by GitHub
parent 3aec12df6b
commit d4ccc2c441
27 changed files with 715 additions and 118 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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))
}

View File

@ -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 {

View File

@ -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

View File

@ -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))
}
}

View File

@ -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))
}
}

View File

@ -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"))
}
}
}

View File

@ -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

View File

@ -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"
}

View File

@ -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")
}

View File

@ -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)))
}
}

View File

@ -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)))
}
}

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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)))
}
}

View File

@ -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)))
}
}

View File

@ -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)))
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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,8 +123,7 @@ object JsonReaders {
paymentHash <- (jsValue \ "paymentHash").validate[Sha256Digest]
expiry <- (jsValue \ "expiry").validate[Long]
} yield
InvoiceResult(
prefix,
InvoiceResult(prefix,
timestamp.seconds,
nodeId,
serialized,

View File

@ -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(_)) =>
throw new RuntimeException(
"Could not figure out version of provided bitcoind RPC client!")
case (Success(_), Success(_)) =>
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!" +
"This should not happen, managed to construct different versioned RPC clients from one single client")
case (Success(v16), Failure(_)) =>
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] = {