mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-18 21:34:39 +01:00
2024 04 24 rm v24 (#5549)
* WIP * Fix simulaterawtransaction test * Remove unneeded AdressInfoResult data strutures for versions of bitcoind pre-v21 * Remove v24 * Remove support for v24 bitcoind from bitcoind-rpc.sbt * Fix tests * Fix UtilRpcTest * Fix teardown of bitcoind * scalafmt
This commit is contained in:
parent
d23d7851b8
commit
5dc5cca9cf
@ -256,120 +256,6 @@ sealed trait AddressInfoResult extends WalletResult {
|
||||
def hdseedid: Option[RipeMd160Digest]
|
||||
}
|
||||
|
||||
case class AddressInfoResultPreV18(
|
||||
address: BitcoinAddress,
|
||||
scriptPubKey: ScriptPubKey,
|
||||
ismine: Boolean,
|
||||
iswatchonly: Boolean,
|
||||
isscript: Boolean,
|
||||
iswitness: Boolean,
|
||||
iscompressed: Option[Boolean],
|
||||
witness_version: Option[WitnessVersion],
|
||||
witness_program: Option[String],
|
||||
script: Option[ScriptType],
|
||||
hex: Option[ScriptPubKey],
|
||||
pubkeys: Option[Vector[ECPublicKey]],
|
||||
sigsrequired: Option[Int],
|
||||
pubkey: Option[ECPublicKey],
|
||||
embedded: Option[EmbeddedResult],
|
||||
label: String,
|
||||
timestamp: Option[ZonedDateTime],
|
||||
hdkeypath: Option[BIP32Path],
|
||||
hdseedid: Option[RipeMd160Digest],
|
||||
hdmasterkeyid: Option[RipeMd160Digest],
|
||||
labels: Vector[LabelResult]
|
||||
) extends AddressInfoResult
|
||||
|
||||
// The split into two case classes is to deal with the 22 param limit for case classes
|
||||
case class AddressInfoResultPostV18(
|
||||
address: BitcoinAddress,
|
||||
scriptPubKey: ScriptPubKey,
|
||||
isProps: AddressInfoResultPostV18.AddressInfoIsProps,
|
||||
desc: String,
|
||||
witness_version: Option[WitnessVersion],
|
||||
witness_program: Option[String],
|
||||
script: Option[ScriptType],
|
||||
hex: Option[ScriptPubKey],
|
||||
pubkeys: Option[Vector[ECPublicKey]],
|
||||
sigsrequired: Option[Int],
|
||||
pubkey: Option[ECPublicKey],
|
||||
embedded: Option[EmbeddedResult],
|
||||
label: String,
|
||||
ischange: Boolean,
|
||||
timestamp: Option[ZonedDateTime],
|
||||
hdkeypath: Option[BIP32Path],
|
||||
hdseedid: Option[RipeMd160Digest],
|
||||
hdmasterfingerprint: Option[String],
|
||||
labels: Vector[LabelResult]
|
||||
) extends AddressInfoResult {
|
||||
override def ismine: Boolean = isProps.ismine
|
||||
def solvable: Boolean = isProps.solvable
|
||||
override def iswatchonly: Boolean = isProps.iswatchonly
|
||||
override def isscript: Boolean = isProps.isscript
|
||||
override def iswitness: Boolean = isProps.iswitness
|
||||
override def iscompressed: Option[Boolean] = isProps.iscompressed
|
||||
}
|
||||
|
||||
object AddressInfoResultPostV18 {
|
||||
|
||||
case class AddressInfoIsProps(
|
||||
ismine: Boolean,
|
||||
solvable: Boolean,
|
||||
iswatchonly: Boolean,
|
||||
isscript: Boolean,
|
||||
iswitness: Boolean,
|
||||
iscompressed: Option[Boolean]
|
||||
)
|
||||
|
||||
case class AddressInfoResultPostV18WithoutIsProps(
|
||||
address: BitcoinAddress,
|
||||
scriptPubKey: ScriptPubKey,
|
||||
desc: String,
|
||||
witness_version: Option[WitnessVersion],
|
||||
witness_program: Option[String],
|
||||
script: Option[ScriptType],
|
||||
hex: Option[ScriptPubKey],
|
||||
pubkeys: Option[Vector[ECPublicKey]],
|
||||
sigsrequired: Option[Int],
|
||||
pubkey: Option[ECPublicKey],
|
||||
embedded: Option[EmbeddedResult],
|
||||
label: String,
|
||||
ischange: Boolean,
|
||||
timestamp: Option[ZonedDateTime],
|
||||
hdkeypath: Option[BIP32Path],
|
||||
hdseedid: Option[RipeMd160Digest],
|
||||
hdmasterfingerprint: Option[String],
|
||||
labels: Vector[LabelResult]
|
||||
)
|
||||
|
||||
def apply(
|
||||
info: AddressInfoResultPostV18WithoutIsProps,
|
||||
isProps: AddressInfoIsProps
|
||||
): AddressInfoResultPostV18 = {
|
||||
AddressInfoResultPostV18(
|
||||
address = info.address,
|
||||
scriptPubKey = info.scriptPubKey,
|
||||
isProps = isProps,
|
||||
desc = info.desc,
|
||||
witness_version = info.witness_version,
|
||||
witness_program = info.witness_program,
|
||||
script = info.script,
|
||||
hex = info.hex,
|
||||
pubkeys = info.pubkeys,
|
||||
sigsrequired = info.sigsrequired,
|
||||
pubkey = info.pubkey,
|
||||
embedded = info.embedded,
|
||||
label = info.label,
|
||||
ischange = info.ischange,
|
||||
timestamp = info.timestamp,
|
||||
hdkeypath = info.hdkeypath,
|
||||
hdseedid = info.hdseedid,
|
||||
hdmasterfingerprint = info.hdmasterfingerprint,
|
||||
labels = info.labels
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
case class AddressInfoResultPostV21(
|
||||
address: BitcoinAddress,
|
||||
scriptPubKey: ScriptPubKey,
|
||||
|
@ -515,27 +515,6 @@ object JsonSerializers {
|
||||
implicit val embeddedResultReads: Reads[EmbeddedResult] =
|
||||
Json.reads[EmbeddedResult]
|
||||
|
||||
implicit val addressInfoResultPreV18Reads: Reads[AddressInfoResultPreV18] =
|
||||
Json.reads[AddressInfoResultPreV18]
|
||||
|
||||
implicit val addressInfoResultPostV18Reads
|
||||
: Reads[AddressInfoResultPostV18] = {
|
||||
Reads[AddressInfoResultPostV18] { json =>
|
||||
for {
|
||||
isProps <-
|
||||
Json.reads[AddressInfoResultPostV18.AddressInfoIsProps].reads(json)
|
||||
infoWithoutProps <-
|
||||
Json
|
||||
.reads[
|
||||
AddressInfoResultPostV18.AddressInfoResultPostV18WithoutIsProps
|
||||
]
|
||||
.reads(json)
|
||||
} yield {
|
||||
AddressInfoResultPostV18(infoWithoutProps, isProps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
implicit val addressInfoResultPostV21Reads
|
||||
: Reads[AddressInfoResultPostV21] = {
|
||||
Reads[AddressInfoResultPostV21] { json =>
|
||||
|
@ -6,9 +6,9 @@ import org.bitcoins.testkit.util.BitcoindRpcTest
|
||||
class BitcoindVersionTest extends BitcoindRpcTest {
|
||||
behavior of "BitcoindVersion"
|
||||
|
||||
it should "return version 24" in {
|
||||
val version = BitcoindVersion.fromNetworkVersion(240100)
|
||||
assert(version.equals(BitcoindVersion.V24))
|
||||
it should "return version 25" in {
|
||||
val version = BitcoindVersion.fromNetworkVersion(250100)
|
||||
assert(version.equals(BitcoindVersion.V25))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package org.bitcoins.rpc.common
|
||||
|
||||
import org.bitcoins.core.currency.Bitcoins
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.script.ScriptSignature
|
||||
import org.bitcoins.core.protocol.transaction.{
|
||||
TransactionInput,
|
||||
@ -193,4 +194,15 @@ class MempoolRpcTest extends BitcoindFixturesCachedPairNewest {
|
||||
_ <- client.saveMemPool()
|
||||
} yield assert(regTest.list().contains("mempool.dat"))
|
||||
}
|
||||
|
||||
it should "get tx spending prev out" in { nodePair =>
|
||||
val client = nodePair.node1
|
||||
val junkAddress: BitcoinAddress =
|
||||
BitcoinAddress("2NFyxovf6MyxfHqtVjstGzs6HeLqv92Nq4U")
|
||||
for {
|
||||
txid <- client.sendToAddress(junkAddress, Bitcoins.one)
|
||||
tx <- client.getRawTransaction(txid).map(_.hex)
|
||||
spending <- client.getTxSpendingPrevOut(tx.inputs.head.previousOutput)
|
||||
} yield assert(spending.spendingtxid.contains(txid))
|
||||
}
|
||||
}
|
||||
|
@ -1,42 +0,0 @@
|
||||
package org.bitcoins.rpc.common
|
||||
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.AddressType
|
||||
import org.bitcoins.core.protocol.P2PKHAddress
|
||||
import org.bitcoins.crypto.ECPrivateKey
|
||||
import org.bitcoins.testkit.rpc.BitcoindFixturesCachedPairNewest
|
||||
|
||||
class MessageRpcTest extends BitcoindFixturesCachedPairNewest {
|
||||
|
||||
behavior of "MessageRpc"
|
||||
|
||||
it should "be able to sign a message and verify that signature" in {
|
||||
nodePair =>
|
||||
val client = nodePair.node1
|
||||
val message = "Never gonna give you up\nNever gonna let you down\n..."
|
||||
for {
|
||||
address <- client.getNewAddress(addressType = AddressType.Legacy)
|
||||
signature <-
|
||||
client.signMessage(address.asInstanceOf[P2PKHAddress], message)
|
||||
validity <-
|
||||
client
|
||||
.verifyMessage(address.asInstanceOf[P2PKHAddress],
|
||||
signature,
|
||||
message)
|
||||
} yield assert(validity)
|
||||
}
|
||||
|
||||
it should "be able to sign a message with a private key and verify that signature" in {
|
||||
nodePair =>
|
||||
val message = "Never gonna give you up\nNever gonna let you down\n..."
|
||||
val privKey = ECPrivateKey.freshPrivateKey
|
||||
val address = P2PKHAddress(privKey.publicKey, networkParam)
|
||||
val client = nodePair.node1
|
||||
for {
|
||||
signature <- client.signMessageWithPrivKey(
|
||||
privKey.toPrivateKeyBytes(),
|
||||
message
|
||||
)
|
||||
validity <- client.verifyMessage(address, signature, message)
|
||||
} yield assert(validity)
|
||||
}
|
||||
}
|
@ -4,11 +4,14 @@ import org.bitcoins.commons.jsonmodels.bitcoind.{
|
||||
GetBlockWithTransactionsResultV22,
|
||||
RpcOpts
|
||||
}
|
||||
import org.bitcoins.core.api.chain.db.BlockHeaderDbHelper
|
||||
import org.bitcoins.core.currency.Bitcoins
|
||||
import org.bitcoins.core.number._
|
||||
import org.bitcoins.core.protocol.blockchain.RegTestNetChainParams
|
||||
import org.bitcoins.core.protocol.script.ScriptSignature
|
||||
import org.bitcoins.core.protocol.transaction._
|
||||
import org.bitcoins.crypto.DoubleSha256DigestBE
|
||||
import org.bitcoins.testkit.chain.BlockHeaderHelper
|
||||
import org.bitcoins.testkit.rpc.{
|
||||
BitcoindFixturesCachedPairNewest,
|
||||
BitcoindRpcTestUtil
|
||||
@ -135,4 +138,13 @@ class MiningRpcTest extends BitcoindFixturesCachedPairNewest {
|
||||
hps <- client.getNetworkHashPS()
|
||||
} yield assert(hps > 0)
|
||||
}
|
||||
|
||||
it should "successfully submit a header" in { case nodePair =>
|
||||
val client = nodePair.node1
|
||||
val genesisHeader = RegTestNetChainParams.genesisBlock.blockHeader
|
||||
val genesisHeaderDb =
|
||||
BlockHeaderDbHelper.fromBlockHeader(height = 1, BigInt(0), genesisHeader)
|
||||
val nextHeader = BlockHeaderHelper.buildNextHeader(genesisHeaderDb)
|
||||
client.submitHeader(nextHeader.blockHeader).map(_ => succeed)
|
||||
}
|
||||
}
|
||||
|
@ -70,4 +70,21 @@ class NodeRpcTest extends BitcoindFixturesFundedCachedNewest {
|
||||
assert(!helpHelp.isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
it should "be able to get network info" in { freshClient =>
|
||||
for {
|
||||
info <- freshClient.getNetworkInfo
|
||||
} yield {
|
||||
assert(info.networkactive)
|
||||
assert(info.localrelay)
|
||||
}
|
||||
}
|
||||
|
||||
it should "get node address given a null parameter" in { client =>
|
||||
val nodeF = client.getNodeAddresses()
|
||||
|
||||
nodeF.map { result =>
|
||||
assert(result.isEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,126 @@
|
||||
package org.bitcoins.rpc.common
|
||||
|
||||
import org.bitcoins.asyncutil.AsyncUtil
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.AddressType
|
||||
import org.bitcoins.core.protocol.{BitcoinAddress, P2PKHAddress}
|
||||
import org.bitcoins.core.protocol.script.descriptor.Descriptor
|
||||
import org.bitcoins.crypto.ECPrivateKey
|
||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||
import org.bitcoins.testkit.rpc.BitcoindFixturesCachedPairNewest
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
class UtilRpcTest extends BitcoindFixturesCachedPairNewest {
|
||||
|
||||
behavior of "UtilRpc"
|
||||
|
||||
it should "be able to sign a message and verify that signature" in {
|
||||
nodePair =>
|
||||
val client = nodePair.node1
|
||||
val message = "Never gonna give you up\nNever gonna let you down\n..."
|
||||
for {
|
||||
address <- client.getNewAddress(addressType = AddressType.Legacy)
|
||||
signature <-
|
||||
client.signMessage(address.asInstanceOf[P2PKHAddress], message)
|
||||
validity <-
|
||||
client
|
||||
.verifyMessage(address.asInstanceOf[P2PKHAddress],
|
||||
signature,
|
||||
message)
|
||||
} yield assert(validity)
|
||||
}
|
||||
|
||||
it should "be able to sign a message with a private key and verify that signature" in {
|
||||
nodePair =>
|
||||
val message = "Never gonna give you up\nNever gonna let you down\n..."
|
||||
val privKey = ECPrivateKey.freshPrivateKey
|
||||
val address = P2PKHAddress(privKey.publicKey, networkParam)
|
||||
val client = nodePair.node1
|
||||
for {
|
||||
signature <- client.signMessageWithPrivKey(
|
||||
privKey.toPrivateKeyBytes(),
|
||||
message
|
||||
)
|
||||
validity <- client.verifyMessage(address, signature, message)
|
||||
} yield assert(validity)
|
||||
}
|
||||
|
||||
it should "get index info" in { nodePair =>
|
||||
val client = nodePair.node1
|
||||
|
||||
def indexSynced(client: BitcoindRpcClient): Future[Boolean] = {
|
||||
for {
|
||||
blockCount <- client.getBlockCount()
|
||||
indexes <- client.getIndexInfo
|
||||
} yield {
|
||||
indexes("txindex").best_block_height == blockCount && indexes(
|
||||
"basic block filter index"
|
||||
).best_block_height == blockCount
|
||||
}
|
||||
}
|
||||
for {
|
||||
_ <- AsyncUtil.retryUntilSatisfiedF(() => indexSynced(client))
|
||||
indexes <- client.getIndexInfo
|
||||
blockCount <- client.getBlockCount()
|
||||
} yield {
|
||||
val txIndexInfo = indexes("txindex")
|
||||
assert(txIndexInfo.synced)
|
||||
assert(txIndexInfo.best_block_height == blockCount)
|
||||
|
||||
val blockFilterIndexInfo = indexes("basic block filter index")
|
||||
assert(blockFilterIndexInfo.synced)
|
||||
assert(blockFilterIndexInfo.best_block_height == blockCount)
|
||||
}
|
||||
}
|
||||
|
||||
it should "return active rpc commands" in { nodePair =>
|
||||
val client = nodePair.node1
|
||||
val generatedF =
|
||||
client.getNewAddress.flatMap(addr => client.generateToAddress(100, addr))
|
||||
val rpcinfoF =
|
||||
generatedF.flatMap(_ => client.getRpcInfo())
|
||||
|
||||
rpcinfoF.map { result =>
|
||||
assert(result.active_commands.length == 1)
|
||||
}
|
||||
}
|
||||
|
||||
it should "derive addresses from a descriptor" in { case nodePair =>
|
||||
val client = nodePair.node1
|
||||
val str0 =
|
||||
"wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)#t6wfjs64"
|
||||
val descriptor0 = Descriptor.fromString(str0)
|
||||
assert(descriptor0.toString == str0)
|
||||
val addresses0F =
|
||||
client.deriveAddresses(descriptor0, None).map(_.addresses)
|
||||
val expected0 =
|
||||
Vector("bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5").map(
|
||||
BitcoinAddress.fromString
|
||||
)
|
||||
val assert0 = addresses0F.map { addresses =>
|
||||
assert(addresses == expected0)
|
||||
}
|
||||
|
||||
val str1 =
|
||||
"wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)#kft60nuy"
|
||||
|
||||
val descriptor1 = Descriptor.fromString(str1)
|
||||
assert(descriptor1.toString == str1)
|
||||
val addresses1F =
|
||||
client.deriveAddresses(descriptor1, Some(Vector(0, 2))).map(_.addresses)
|
||||
val expected1 =
|
||||
Vector(
|
||||
"bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5",
|
||||
"bcrt1qhku5rq7jz8ulufe2y6fkcpnlvpsta7rq4442dy",
|
||||
"bcrt1qpgptk2gvshyl0s9lqshsmx932l9ccsv265tvaq"
|
||||
)
|
||||
.map(BitcoinAddress.fromString)
|
||||
|
||||
val assert1 = assert0.flatMap(_ =>
|
||||
addresses1F.map { addresses =>
|
||||
assert(addresses == expected1)
|
||||
})
|
||||
|
||||
assert1
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.{
|
||||
WalletFlag
|
||||
}
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind.{
|
||||
AddressInfoResultPostV21,
|
||||
DecodeScriptResultV22,
|
||||
DescriptorsResult,
|
||||
GetWalletInfoResultPostV22
|
||||
@ -15,6 +16,7 @@ import org.bitcoins.core.currency.{Bitcoins, CurrencyUnit, Satoshis}
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.script._
|
||||
import org.bitcoins.core.protocol.script.descriptor.{
|
||||
Descriptor,
|
||||
P2SHDescriptor,
|
||||
P2WPKHDescriptor
|
||||
}
|
||||
@ -726,7 +728,7 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
|
||||
for {
|
||||
_ <- client.unloadWallet(BitcoindRpcClient.DEFAULT_WALLET_NAME)
|
||||
_ <- client.createWallet(walletName, descriptors = true)
|
||||
_ <- client.importDescriptor(imp, Some(walletName))
|
||||
_ <- client.importDescriptor(imp, walletName)
|
||||
decoded <- client.decodeScript(p2wpkh)
|
||||
_ <- client.unloadWallet(walletName)
|
||||
_ <- client.loadWallet(BitcoindRpcClient.DEFAULT_WALLET_NAME)
|
||||
@ -738,6 +740,96 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
|
||||
}
|
||||
}
|
||||
|
||||
it should "simulate a transaction" in { nodePair =>
|
||||
val client = nodePair.node1
|
||||
val junkAddress: BitcoinAddress =
|
||||
BitcoinAddress("2NFyxovf6MyxfHqtVjstGzs6HeLqv92Nq4U")
|
||||
for {
|
||||
txid <- client.sendToAddress(junkAddress, Bitcoins.one)
|
||||
tx <- client.getRawTransaction(txid).map(_.hex)
|
||||
change <- client.simulateRawTransaction(tx)
|
||||
} yield {
|
||||
assert(change <= -Bitcoins.one)
|
||||
} // 1 bitcoin + fees
|
||||
}
|
||||
|
||||
it should "be able to validate a bitcoin address" in { case nodePair =>
|
||||
val client = nodePair.node1
|
||||
for {
|
||||
address <- client.getNewAddress
|
||||
validation <- client.validateAddress(address)
|
||||
} yield assert(validation.isvalid)
|
||||
}
|
||||
|
||||
it should "have extra address information" in { case nodePair =>
|
||||
val client = nodePair.node1
|
||||
for {
|
||||
address <- client.getNewAddress
|
||||
info <- client.getAddressInfo(address)
|
||||
} yield {
|
||||
info match {
|
||||
case postV21Info: AddressInfoResultPostV21 =>
|
||||
assert(postV21Info.address == address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it should "analyze a descriptor" in { case nodePair =>
|
||||
val client = nodePair.node1
|
||||
val descriptor =
|
||||
Descriptor.fromString(
|
||||
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)#gn28ywm7"
|
||||
)
|
||||
|
||||
val descriptorF = client.getDescriptorInfo(descriptor)
|
||||
|
||||
descriptorF.map { result =>
|
||||
assert(result.descriptor == descriptor)
|
||||
assert(result.isrange.==(false))
|
||||
assert(result.issolvable.==(true))
|
||||
assert(result.hasprivatekeys.==(false))
|
||||
}
|
||||
}
|
||||
|
||||
it must "importdescriptors" in { nodePair =>
|
||||
val client = nodePair.node1
|
||||
val str1 =
|
||||
"wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)#kft60nuy"
|
||||
val descriptor = Descriptor.fromString(str1)
|
||||
val imp = DescriptorsResult(
|
||||
desc = descriptor,
|
||||
timestamp = Instant.now().getEpochSecond,
|
||||
active = true,
|
||||
internal = None,
|
||||
range = Some(Vector(0, 2)),
|
||||
next = None
|
||||
)
|
||||
|
||||
val resultF =
|
||||
client.importDescriptors(imports = Vector(imp))
|
||||
|
||||
for {
|
||||
result <- resultF
|
||||
_ = assert(result.forall(_.success))
|
||||
firstAddress <- client.getNewAddress
|
||||
secondAddress <- client.getNewAddress
|
||||
// check it by deriving addresses externally
|
||||
deriveAddresses <- client
|
||||
.deriveAddresses(descriptor, Some(Vector(0, 1)))
|
||||
.map(_.addresses)
|
||||
} yield {
|
||||
assert(Vector(firstAddress, secondAddress) == deriveAddresses)
|
||||
}
|
||||
}
|
||||
|
||||
it should "be able to get the address info for a given address" in {
|
||||
case nodePair =>
|
||||
val client = nodePair.node1
|
||||
for {
|
||||
addr <- client.getNewAddress
|
||||
info <- client.getAddressInfo(addr)
|
||||
} yield assert(info.address == addr)
|
||||
}
|
||||
def startClient(client: BitcoindRpcClient): Future[Unit] = {
|
||||
BitcoindRpcTestUtil.startServers(Vector(client))
|
||||
}
|
||||
|
@ -1,410 +0,0 @@
|
||||
package org.bitcoins.rpc.v24
|
||||
|
||||
import org.bitcoins.asyncutil.AsyncUtil
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.{
|
||||
AddressType,
|
||||
WalletFlag
|
||||
}
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind._
|
||||
import org.bitcoins.core.api.chain.db.BlockHeaderDbHelper
|
||||
import org.bitcoins.core.config.RegTest
|
||||
import org.bitcoins.core.currency._
|
||||
import org.bitcoins.core.gcs.{BlockFilter, FilterType}
|
||||
import org.bitcoins.core.protocol.{Bech32mAddress, BitcoinAddress}
|
||||
import org.bitcoins.core.protocol.blockchain.RegTestNetChainParams
|
||||
import org.bitcoins.core.protocol.script.descriptor.Descriptor
|
||||
import org.bitcoins.core.psbt.PSBT
|
||||
import org.bitcoins.crypto.ECPublicKey
|
||||
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
|
||||
import org.bitcoins.rpc.client.v24.BitcoindV24RpcClient
|
||||
import org.bitcoins.testkit.chain.BlockHeaderHelper
|
||||
import org.bitcoins.testkit.rpc.{
|
||||
BitcoindFixturesFundedCachedV24,
|
||||
BitcoindRpcTestUtil
|
||||
}
|
||||
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.time.Instant
|
||||
import scala.concurrent.Future
|
||||
|
||||
class BitcoindV24RpcClientTest extends BitcoindFixturesFundedCachedV24 {
|
||||
|
||||
val junkAddress: BitcoinAddress =
|
||||
BitcoinAddress("2NFyxovf6MyxfHqtVjstGzs6HeLqv92Nq4U")
|
||||
|
||||
behavior of "BitcoindV24RpcClient"
|
||||
|
||||
it should "get index info" in { client: BitcoindV24RpcClient =>
|
||||
def indexSynced(client: BitcoindRpcClient): Future[Boolean] = {
|
||||
client.getIndexInfo.map { indexes =>
|
||||
indexes("txindex").best_block_height == 101 && indexes(
|
||||
"basic block filter index"
|
||||
).best_block_height == 101
|
||||
}
|
||||
}
|
||||
for {
|
||||
_ <- AsyncUtil.retryUntilSatisfiedF(() => indexSynced(client))
|
||||
indexes <- client.getIndexInfo
|
||||
} yield {
|
||||
val txIndexInfo = indexes("txindex")
|
||||
assert(txIndexInfo.synced)
|
||||
assert(txIndexInfo.best_block_height == 101)
|
||||
|
||||
val blockFilterIndexInfo = indexes("basic block filter index")
|
||||
assert(blockFilterIndexInfo.synced)
|
||||
assert(blockFilterIndexInfo.best_block_height == 101)
|
||||
}
|
||||
}
|
||||
|
||||
it should "be able to start a V24 bitcoind instance" in {
|
||||
client: BitcoindV24RpcClient =>
|
||||
for {
|
||||
v <- client.version
|
||||
} yield assert(v == BitcoindVersion.V24)
|
||||
}
|
||||
|
||||
it should "be able to get network info" in {
|
||||
freshClient: BitcoindV24RpcClient =>
|
||||
for {
|
||||
info <- freshClient.getNetworkInfo
|
||||
} yield {
|
||||
assert(info.networkactive)
|
||||
assert(info.localrelay)
|
||||
}
|
||||
}
|
||||
|
||||
it should "generate a bech32m address" in { client: BitcoindV24RpcClient =>
|
||||
for {
|
||||
address <- client.getNewAddress(addressType = AddressType.Bech32m)
|
||||
} yield {
|
||||
assert(address.isInstanceOf[Bech32mAddress])
|
||||
}
|
||||
}
|
||||
|
||||
it should "be able to validate a bitcoin address" in { case client =>
|
||||
for {
|
||||
address <- client.getNewAddress
|
||||
validation <- client.validateAddress(address)
|
||||
} yield assert(validation.isvalid)
|
||||
}
|
||||
|
||||
it should "have extra address information" in { client =>
|
||||
for {
|
||||
address <- client.getNewAddress
|
||||
info <- client.getAddressInfo(address)
|
||||
} yield {
|
||||
info match {
|
||||
case _: AddressInfoResultPreV18 | _: AddressInfoResultPostV18 =>
|
||||
fail("Was expecting AddressInfoResultPostV21")
|
||||
case postV21Info: AddressInfoResultPostV21 =>
|
||||
assert(postV21Info.address == address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it should "return active rpc commands" in { client =>
|
||||
val generatedF =
|
||||
client.getNewAddress.flatMap(addr => client.generateToAddress(100, addr))
|
||||
val rpcinfoF =
|
||||
generatedF.flatMap(_ => client.getRpcInfo())
|
||||
|
||||
rpcinfoF.map { result =>
|
||||
assert(result.active_commands.length == 1)
|
||||
}
|
||||
}
|
||||
|
||||
it should "analyze a descriptor" in { client =>
|
||||
val descriptor =
|
||||
Descriptor.fromString(
|
||||
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)#gn28ywm7"
|
||||
)
|
||||
|
||||
val descriptorF = client.getDescriptorInfo(descriptor)
|
||||
|
||||
descriptorF.map { result =>
|
||||
assert(result.descriptor == descriptor)
|
||||
assert(result.isrange.==(false))
|
||||
assert(result.issolvable.==(true))
|
||||
assert(result.hasprivatekeys.==(false))
|
||||
}
|
||||
}
|
||||
|
||||
it should "get node address given a null parameter" in { client =>
|
||||
val nodeF = client.getNodeAddresses()
|
||||
|
||||
nodeF.map { result =>
|
||||
assert(result.isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
it should "successfully submit a header" in { client =>
|
||||
val genesisHeader = RegTestNetChainParams.genesisBlock.blockHeader
|
||||
val genesisHeaderDb =
|
||||
BlockHeaderDbHelper.fromBlockHeader(height = 1, BigInt(0), genesisHeader)
|
||||
val nextHeader = BlockHeaderHelper.buildNextHeader(genesisHeaderDb)
|
||||
client.submitHeader(nextHeader.blockHeader).map(_ => succeed)
|
||||
}
|
||||
|
||||
it should "simulate a transaction" in { client =>
|
||||
for {
|
||||
txid <- client.sendToAddress(junkAddress, Bitcoins.one)
|
||||
tx <- client.getRawTransaction(txid).map(_.hex)
|
||||
change <- client.simulateRawTransaction(tx)
|
||||
} yield assert(change <= -Bitcoins.one) // 1 bitcoin + fees
|
||||
}
|
||||
|
||||
it should "get tx spending prev out" in { client =>
|
||||
for {
|
||||
txid <- client.sendToAddress(junkAddress, Bitcoins.one)
|
||||
tx <- client.getRawTransaction(txid).map(_.hex)
|
||||
spending <- client.getTxSpendingPrevOut(tx.inputs.head.previousOutput)
|
||||
} yield assert(spending.spendingtxid.contains(txid))
|
||||
}
|
||||
|
||||
it should "derive addresses from a descriptor" in { client =>
|
||||
val str0 =
|
||||
"wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)#t6wfjs64"
|
||||
val descriptor0 = Descriptor.fromString(str0)
|
||||
assert(descriptor0.toString == str0)
|
||||
val addresses0F =
|
||||
client.deriveAddresses(descriptor0, None).map(_.addresses)
|
||||
val expected0 =
|
||||
Vector("bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5").map(
|
||||
BitcoinAddress.fromString
|
||||
)
|
||||
val assert0 = addresses0F.map { addresses =>
|
||||
assert(addresses == expected0)
|
||||
}
|
||||
|
||||
val str1 =
|
||||
"wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)#kft60nuy"
|
||||
|
||||
val descriptor1 = Descriptor.fromString(str1)
|
||||
assert(descriptor1.toString == str1)
|
||||
val addresses1F =
|
||||
client.deriveAddresses(descriptor1, Some(Vector(0, 2))).map(_.addresses)
|
||||
val expected1 =
|
||||
Vector(
|
||||
"bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5",
|
||||
"bcrt1qhku5rq7jz8ulufe2y6fkcpnlvpsta7rq4442dy",
|
||||
"bcrt1qpgptk2gvshyl0s9lqshsmx932l9ccsv265tvaq"
|
||||
)
|
||||
.map(BitcoinAddress.fromString)
|
||||
|
||||
val assert1 = assert0.flatMap(_ =>
|
||||
addresses1F.map { addresses =>
|
||||
assert(addresses == expected1)
|
||||
})
|
||||
|
||||
assert1
|
||||
}
|
||||
|
||||
it must "importdescriptors" in { client =>
|
||||
val str1 =
|
||||
"wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)#kft60nuy"
|
||||
val descriptor = Descriptor.fromString(str1)
|
||||
val imp = DescriptorsResult(
|
||||
desc = descriptor,
|
||||
timestamp = Instant.now().getEpochSecond,
|
||||
active = true,
|
||||
internal = None,
|
||||
range = Some(Vector(0, 2)),
|
||||
next = None
|
||||
)
|
||||
|
||||
val resultF =
|
||||
client.importDescriptors(imports = Vector(imp), walletNameOpt = None)
|
||||
|
||||
for {
|
||||
result <- resultF
|
||||
_ = assert(result.forall(_.success))
|
||||
firstAddress <- client.getNewAddress
|
||||
secondAddress <- client.getNewAddress
|
||||
// check it by deriving addresses externally
|
||||
deriveAddresses <- client
|
||||
.deriveAddresses(descriptor, Some(Vector(0, 1)))
|
||||
.map(_.addresses)
|
||||
} yield {
|
||||
assert(Vector(firstAddress, secondAddress) == deriveAddresses)
|
||||
}
|
||||
}
|
||||
|
||||
it should "be able to get the address info for a given address" in {
|
||||
client: BitcoindV24RpcClient =>
|
||||
for {
|
||||
addr <- client.getNewAddress
|
||||
info <- client.getAddressInfo(addr)
|
||||
} yield assert(info.address == addr)
|
||||
}
|
||||
|
||||
it should "get a block filter given a block hash" in {
|
||||
client: BitcoindV24RpcClient =>
|
||||
for {
|
||||
blocks <- client.generate(1)
|
||||
blockHashBE = blocks.head
|
||||
blockFilter <- client.getBlockFilter(blockHashBE, FilterType.Basic)
|
||||
block <- client.getBlockRaw(blockHashBE)
|
||||
prevOuts = block.transactions
|
||||
.filterNot(_.isCoinbase)
|
||||
.flatMap(_.inputs.map(_.previousOutput))
|
||||
fundingOutputs <- Future.traverse(prevOuts) { outpoint =>
|
||||
client
|
||||
.getTransaction(outpoint.txIdBE)
|
||||
.map(_.hex.outputs(outpoint.idx))
|
||||
}
|
||||
prevFilter <- client.getBlockFilter(
|
||||
block.blockHeader.previousBlockHashBE,
|
||||
FilterType.Basic
|
||||
)
|
||||
} yield {
|
||||
val pubKeys = fundingOutputs.map(_.scriptPubKey).toVector
|
||||
val filter = BlockFilter(block, pubKeys)
|
||||
assert(filter.hash == blockFilter.filter.hash)
|
||||
assert(
|
||||
blockFilter.header == filter
|
||||
.getHeader(prevFilter.header.flip)
|
||||
.hashBE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
it should "be able to get the balances" in { client: BitcoindV24RpcClient =>
|
||||
for {
|
||||
immatureBalance <- client.getBalances
|
||||
_ <- client.generate(1)
|
||||
newImmatureBalance <- client.getBalances
|
||||
} yield {
|
||||
val blockReward = 50
|
||||
assert(immatureBalance.mine.immature.toBigDecimal >= 0)
|
||||
assert(
|
||||
immatureBalance.mine.trusted.toBigDecimal + blockReward == newImmatureBalance.mine.trusted.toBigDecimal
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
it should "be able to get blockchain info" in {
|
||||
client: BitcoindV24RpcClient =>
|
||||
for {
|
||||
info <- client.getBlockChainInfo
|
||||
bestHash <- client.getBestBlockHash()
|
||||
} yield {
|
||||
assert(info.isInstanceOf[GetBlockChainInfoResultPostV23])
|
||||
val postV23Info = info.asInstanceOf[GetBlockChainInfoResultPostV23]
|
||||
assert(postV23Info.chain == RegTest)
|
||||
assert(postV23Info.bestblockhash == bestHash)
|
||||
}
|
||||
}
|
||||
|
||||
it should "be able to get a block with verbose transactions" in {
|
||||
client: BitcoindRpcClient =>
|
||||
for {
|
||||
blocks <- client.generate(2)
|
||||
block <- client.getBlockWithTransactions(blocks(1))
|
||||
} yield {
|
||||
assert(block.hash == blocks(1))
|
||||
assert(block.tx.length == 1)
|
||||
val tx = block.tx.head
|
||||
assert(tx.vout.head.n == 0)
|
||||
}
|
||||
}
|
||||
|
||||
it should "be able to set the wallet flag 'avoid_reuse'" in {
|
||||
client: BitcoindV24RpcClient =>
|
||||
for {
|
||||
unspentPre <- client.listUnspent
|
||||
result <- client.setWalletFlag(WalletFlag.AvoidReuse, value = true)
|
||||
unspentPost <- client.listUnspent
|
||||
} yield {
|
||||
assert(result.flag_name == "avoid_reuse")
|
||||
assert(result.flag_state)
|
||||
assert(unspentPre.forall(utxo => utxo.reused.isEmpty))
|
||||
assert(unspentPost.forall(utxo => utxo.reused.isDefined))
|
||||
}
|
||||
}
|
||||
|
||||
it should "create a wallet with a passphrase" in {
|
||||
client: BitcoindV24RpcClient =>
|
||||
for {
|
||||
_ <- client.createWallet("suredbits", passphrase = "stackingsats")
|
||||
wallets <- client.listWallets
|
||||
} yield {
|
||||
assert(wallets.contains("suredbits"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
it should "check to see if the utxoUpdate input has been updated" in {
|
||||
client: BitcoindV24RpcClient =>
|
||||
val descriptor =
|
||||
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)"
|
||||
|
||||
val psbt =
|
||||
PSBT.fromBase64(
|
||||
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA=="
|
||||
)
|
||||
|
||||
for {
|
||||
result <- client.utxoUpdatePsbt(psbt, Seq(descriptor))
|
||||
} yield {
|
||||
assert(result == psbt)
|
||||
}
|
||||
}
|
||||
|
||||
it should "correct create multisig and get its descriptor" in {
|
||||
client: BitcoindV24RpcClient =>
|
||||
val pubKey1 = ECPublicKey.freshPublicKey
|
||||
val pubKey2 = ECPublicKey.freshPublicKey
|
||||
|
||||
for {
|
||||
multiSigResult <- client.createMultiSig(
|
||||
2,
|
||||
Vector(pubKey1, pubKey2),
|
||||
AddressType.Bech32
|
||||
)
|
||||
} yield {
|
||||
// just validate we are able to receive a sane descriptor
|
||||
// no need to check checksum
|
||||
assert(
|
||||
multiSigResult.descriptor.startsWith(
|
||||
s"wsh(multi(2,${pubKey1.hex},${pubKey2.hex}))#"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
it should "correctly dump tx out set" in { client: BitcoindV24RpcClient =>
|
||||
for {
|
||||
hash <- client.getBestBlockHash()
|
||||
height <- client.getBestHashBlockHeight()
|
||||
result <- client.dumpTxOutSet(new File("utxo.dat").toPath)
|
||||
} yield {
|
||||
assert(Files.exists(result.path))
|
||||
// Mild clean up
|
||||
Files.delete(result.path)
|
||||
|
||||
assert(result.base_hash == hash)
|
||||
assert(result.base_height == height)
|
||||
assert(result.coins_written > 0)
|
||||
}
|
||||
}
|
||||
|
||||
it should "correct generate to a descriptor" in {
|
||||
client: BitcoindV24RpcClient =>
|
||||
// 2-of-2 multisig descriptor
|
||||
val descriptor =
|
||||
"sh(sortedmulti(2,023f720438186fbdfde0c0a403e770a0f32a2d198623a8a982c47b621f8b307640,03ed261094d609d5e02ba6553c2d91e4fd056006ce2fe64aace72b69cb5be3ab9c))#nj9wx7up"
|
||||
val numBlocks = 10
|
||||
for {
|
||||
hashes <- client.generateToDescriptor(numBlocks, descriptor)
|
||||
} yield assert(hashes.size == numBlocks)
|
||||
}
|
||||
|
||||
it should "be able to get utxo info" in { client: BitcoindRpcClient =>
|
||||
for {
|
||||
block <- BitcoindRpcTestUtil.getFirstBlock(client)
|
||||
info1 <- client.getTxOut(block.tx.head.txid, 0)
|
||||
} yield assert(info1.coinbase)
|
||||
}
|
||||
|
||||
}
|
@ -23,7 +23,7 @@ TaskKeys.downloadBitcoind := {
|
||||
}
|
||||
|
||||
val versions =
|
||||
List("25.2","24.2")
|
||||
List("25.2")
|
||||
|
||||
logger.debug(
|
||||
s"(Maybe) downloading Bitcoin Core binaries for versions: ${versions.mkString(",")}")
|
||||
@ -94,24 +94,18 @@ TaskKeys.downloadBitcoind := {
|
||||
val expectedHash =
|
||||
if (Properties.isLinux)
|
||||
Map(
|
||||
"25.2" -> "8d8c387e597e0edfc256f0bbace1dac3ad1ebf4a3c06da3e2975fda333817dea",
|
||||
"24.2" -> "7540d6e34c311e355af2fd76e5eee853b76c291978d6b5ebb555c7877e9de38d",
|
||||
"25.2" -> "8d8c387e597e0edfc256f0bbace1dac3ad1ebf4a3c06da3e2975fda333817dea"
|
||||
)
|
||||
else if (Properties.isMac)
|
||||
Map(
|
||||
"25.2" -> (if (System.getProperty("os.arch") == "aarch64")
|
||||
"f55b394eebaa11d4b717d68aad9f75b824aaf3a7841dac7c26b1ef3d6d2915f5"
|
||||
else
|
||||
"e06ba379f6039ca99bc32d3e7974d420a31363498936f88aac7bab6f239de0f5"),
|
||||
"24.2" -> (if (System.getProperty("os.arch") == "aarch64")
|
||||
"ae6f5f0cb4079005c32695711ef78b26a26c4c547ceb593b3626059626530a5d"
|
||||
else
|
||||
"b1b21455c339b2daf0998bfad17d0741d967c3c81db040bb5f73234168526d29")
|
||||
"e06ba379f6039ca99bc32d3e7974d420a31363498936f88aac7bab6f239de0f5")
|
||||
)
|
||||
else if (Properties.isWin)
|
||||
Map(
|
||||
"25.2" -> "c2ac84f55ee879caefd4414868d318a741c52a7286da190bf7233d86a2ffca69",
|
||||
"24.2" -> "544436bc9d5ce017e679bbccfe8a4928fbc840b414ee0240db8c3523ba54340a",
|
||||
"25.2" -> "c2ac84f55ee879caefd4414868d318a741c52a7286da190bf7233d86a2ffca69"
|
||||
)
|
||||
else sys.error(s"Unsupported OS: ${Properties.osName}")
|
||||
|
||||
|
@ -16,7 +16,6 @@ import org.bitcoins.core.wallet.fee.FeeUnit
|
||||
import org.bitcoins.crypto.{DoubleSha256DigestBE, StringFactory}
|
||||
import org.bitcoins.rpc.client.v18.V18AssortedRpc
|
||||
import org.bitcoins.rpc.client.v20.{V20AssortedRpc, V20MultisigRpc}
|
||||
import org.bitcoins.rpc.client.v24.BitcoindV24RpcClient
|
||||
import org.bitcoins.rpc.client.v25.BitcoindV25RpcClient
|
||||
import org.bitcoins.rpc.config._
|
||||
|
||||
@ -344,7 +343,6 @@ object BitcoindRpcClient {
|
||||
system: ActorSystem
|
||||
): BitcoindRpcClient = {
|
||||
val bitcoind = version match {
|
||||
case BitcoindVersion.V24 => BitcoindV24RpcClient.withActorSystem(instance)
|
||||
case BitcoindVersion.V25 => BitcoindV25RpcClient.withActorSystem(instance)
|
||||
case BitcoindVersion.Unknown =>
|
||||
sys.error(
|
||||
@ -372,14 +370,10 @@ object BitcoindVersion
|
||||
val newest: BitcoindVersion = V25
|
||||
|
||||
val standard: Vector[BitcoindVersion] =
|
||||
Vector(V25, V24)
|
||||
Vector(V25)
|
||||
|
||||
val known: Vector[BitcoindVersion] = standard
|
||||
|
||||
case object V24 extends BitcoindVersion {
|
||||
override def toString: String = "v24"
|
||||
}
|
||||
|
||||
case object V25 extends BitcoindVersion {
|
||||
override def toString: String = "v25"
|
||||
}
|
||||
@ -402,7 +396,7 @@ object BitcoindVersion
|
||||
def fromNetworkVersion(int: Int): BitcoindVersion = {
|
||||
// need to translate the int 210100 (as an example) to a BitcoindVersion
|
||||
int.toString.substring(0, 2) match {
|
||||
case "24" => V24
|
||||
case "25" => V25
|
||||
case _ =>
|
||||
logger.warn(
|
||||
s"Unsupported Bitcoin Core version: $int. The latest supported version is ${BitcoindVersion.newest}"
|
||||
|
@ -49,20 +49,20 @@ trait DescriptorRpc {
|
||||
*/
|
||||
def importDescriptors(
|
||||
imports: Vector[DescriptorsResult],
|
||||
walletNameOpt: Option[String]
|
||||
walletName: String = BitcoindRpcClient.DEFAULT_WALLET_NAME
|
||||
): Future[Vector[ImportDescriptorResult]] = {
|
||||
bitcoindCall[Vector[ImportDescriptorResult]](
|
||||
"importdescriptors",
|
||||
List(Json.toJson(imports)),
|
||||
uriExtensionOpt = walletNameOpt.map(walletExtension)
|
||||
uriExtensionOpt = Some(walletExtension(walletName))
|
||||
)
|
||||
}
|
||||
|
||||
def importDescriptor(
|
||||
imp: DescriptorsResult,
|
||||
walletNameOpt: Option[String]
|
||||
walletName: String = BitcoindRpcClient.DEFAULT_WALLET_NAME
|
||||
): Future[ImportDescriptorResult] = {
|
||||
importDescriptors(Vector(imp), walletNameOpt).map(_.head)
|
||||
importDescriptors(Vector(imp), walletName).map(_.head)
|
||||
}
|
||||
|
||||
def listDescriptors(): Future[ListDescriptorsResult] = {
|
||||
|
@ -3,10 +3,10 @@ package org.bitcoins.rpc.client.common
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind._
|
||||
import org.bitcoins.commons.serializers.JsonReaders._
|
||||
import org.bitcoins.commons.serializers.JsonSerializers._
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutPoint}
|
||||
import org.bitcoins.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
|
||||
import org.bitcoins.rpc.BitcoindException
|
||||
import play.api.libs.json.{JsBoolean, JsString, Json}
|
||||
import play.api.libs.json.{JsArray, JsBoolean, JsString, Json}
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
@ -138,4 +138,23 @@ trait MempoolRpc { self: Client =>
|
||||
List(Json.toJson(transaction), Json.toJson(maxFeeRate))
|
||||
)
|
||||
}
|
||||
|
||||
def getTxSpendingPrevOut(
|
||||
prevout: TransactionOutPoint
|
||||
): Future[GetTxSpendingPrevOutResult] = {
|
||||
getTxSpendingPrevOut(Vector(prevout)).map(_.head)
|
||||
}
|
||||
|
||||
def getTxSpendingPrevOut(
|
||||
prevouts: Vector[TransactionOutPoint]
|
||||
): Future[Vector[GetTxSpendingPrevOutResult]] = {
|
||||
val json = JsArray(prevouts.map { prev =>
|
||||
Json.obj("txid" -> prev.txIdBE.hex, "vout" -> prev.vout.toLong)
|
||||
})
|
||||
|
||||
bitcoindCall[Vector[GetTxSpendingPrevOutResult]](
|
||||
"gettxspendingprevout",
|
||||
List(json)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ trait UtilRpc { self: Client =>
|
||||
}
|
||||
|
||||
def decodeScript(script: ScriptPubKey): Future[DecodeScriptResult] = {
|
||||
self.version.flatMap { case V25 | V24 | Unknown =>
|
||||
self.version.flatMap { case V25 | Unknown =>
|
||||
bitcoindCall[DecodeScriptResultV22](
|
||||
"decodescript",
|
||||
List(Json.toJson(script))
|
||||
@ -34,18 +34,17 @@ trait UtilRpc { self: Client =>
|
||||
}
|
||||
|
||||
def getIndexInfo: Future[Map[String, IndexInfoResult]] = {
|
||||
version.flatMap { case V25 | V24 | Unknown =>
|
||||
version.flatMap { case V25 | Unknown =>
|
||||
bitcoindCall[Map[String, IndexInfoResult]]("getindexinfo")
|
||||
}
|
||||
}
|
||||
|
||||
def getIndexInfo(indexName: String): Future[IndexInfoResult] = {
|
||||
version.flatMap { case V25 | V24 | Unknown =>
|
||||
version.flatMap { case V25 | Unknown =>
|
||||
bitcoindCall[Map[String, IndexInfoResult]](
|
||||
"getindexinfo",
|
||||
List(JsString(indexName))
|
||||
).map(_.head._2)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -160,13 +160,11 @@ trait WalletRpc { self: Client =>
|
||||
def getWalletInfo(
|
||||
walletName: String
|
||||
): Future[GetWalletInfoResult] = {
|
||||
self.version.flatMap {
|
||||
case BitcoindVersion.V25 | BitcoindVersion.V24 |
|
||||
BitcoindVersion.Unknown =>
|
||||
bitcoindCall[GetWalletInfoResultPostV22](
|
||||
"getwalletinfo",
|
||||
uriExtensionOpt = Some(walletExtension(walletName))
|
||||
)
|
||||
self.version.flatMap { case BitcoindVersion.V25 | BitcoindVersion.Unknown =>
|
||||
bitcoindCall[GetWalletInfoResultPostV22](
|
||||
"getwalletinfo",
|
||||
uriExtensionOpt = Some(walletExtension(walletName))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,7 +292,7 @@ trait WalletRpc { self: Client =>
|
||||
}
|
||||
|
||||
def getBalances: Future[GetBalancesResult] = {
|
||||
bitcoindCall[GetBalancesResult]("getbalances")
|
||||
getBalances(BitcoindRpcClient.DEFAULT_WALLET_NAME)
|
||||
}
|
||||
|
||||
def getBalances(walletName: String): Future[GetBalancesResult] = {
|
||||
@ -383,7 +381,7 @@ trait WalletRpc { self: Client =>
|
||||
descriptors: Boolean = false
|
||||
): Future[CreateWalletResult] =
|
||||
self.version.flatMap {
|
||||
case V25 | V24 =>
|
||||
case V25 =>
|
||||
bitcoindCall[CreateWalletResult](
|
||||
"createwallet",
|
||||
List(
|
||||
@ -412,19 +410,12 @@ trait WalletRpc { self: Client =>
|
||||
address: BitcoinAddress,
|
||||
walletName: String = DEFAULT_WALLET
|
||||
): Future[AddressInfoResult] = {
|
||||
self.version.flatMap {
|
||||
case Unknown =>
|
||||
bitcoindCall[AddressInfoResultPostV18](
|
||||
"getaddressinfo",
|
||||
List(JsString(address.value)),
|
||||
uriExtensionOpt = Some(walletExtension(walletName))
|
||||
)
|
||||
case V25 | V24 =>
|
||||
bitcoindCall[AddressInfoResultPostV21](
|
||||
"getaddressinfo",
|
||||
List(JsString(address.value)),
|
||||
uriExtensionOpt = Some(walletExtension(walletName))
|
||||
)
|
||||
self.version.flatMap { case V25 | Unknown =>
|
||||
bitcoindCall[AddressInfoResultPostV21](
|
||||
"getaddressinfo",
|
||||
List(JsString(address.value)),
|
||||
uriExtensionOpt = Some(walletExtension(walletName))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -551,4 +542,27 @@ trait WalletRpc { self: Client =>
|
||||
Json.toJson(sigHash)
|
||||
)
|
||||
)
|
||||
|
||||
def simulateRawTransaction(
|
||||
tx: Transaction,
|
||||
includeWatchOnly: Boolean = true,
|
||||
walletName: String = BitcoindRpcClient.DEFAULT_WALLET_NAME
|
||||
): Future[CurrencyUnit] = {
|
||||
simulateRawTransactions(Vector(tx), includeWatchOnly, walletName)
|
||||
}
|
||||
|
||||
def simulateRawTransactions(
|
||||
txs: Vector[Transaction],
|
||||
includeWatchOnly: Boolean = true,
|
||||
walletName: String = BitcoindRpcClient.DEFAULT_WALLET_NAME
|
||||
): Future[CurrencyUnit] = {
|
||||
val txsJson = JsArray(txs.map(tx => JsString(tx.hex)))
|
||||
val options = Json.obj("include_watchonly" -> includeWatchOnly)
|
||||
|
||||
bitcoindCall[SimulateRawTransactionResult](
|
||||
"simulaterawtransaction",
|
||||
List(txsJson, options),
|
||||
uriExtensionOpt = Some(walletExtension(walletName))
|
||||
).map(_.balance_change)
|
||||
}
|
||||
}
|
||||
|
@ -1,97 +0,0 @@
|
||||
package org.bitcoins.rpc.client.v24
|
||||
|
||||
import org.apache.pekko.actor.ActorSystem
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind.{
|
||||
GetTxSpendingPrevOutResult,
|
||||
SimulateRawTransactionResult
|
||||
}
|
||||
import org.bitcoins.commons.serializers.JsonSerializers._
|
||||
import org.bitcoins.core.currency.CurrencyUnit
|
||||
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutPoint}
|
||||
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
import play.api.libs.json._
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.util.Try
|
||||
|
||||
/** Class for creating a BitcoindV24 instance that can access RPCs
|
||||
*/
|
||||
class BitcoindV24RpcClient(override val instance: BitcoindInstance)(implicit
|
||||
actorSystem: ActorSystem
|
||||
) extends BitcoindRpcClient(instance) {
|
||||
|
||||
override lazy val version: Future[BitcoindVersion] =
|
||||
Future.successful(BitcoindVersion.V24)
|
||||
|
||||
def getTxSpendingPrevOut(
|
||||
prevout: TransactionOutPoint
|
||||
): Future[GetTxSpendingPrevOutResult] = {
|
||||
getTxSpendingPrevOut(Vector(prevout)).map(_.head)
|
||||
}
|
||||
|
||||
def getTxSpendingPrevOut(
|
||||
prevouts: Vector[TransactionOutPoint]
|
||||
): Future[Vector[GetTxSpendingPrevOutResult]] = {
|
||||
val json = JsArray(prevouts.map { prev =>
|
||||
Json.obj("txid" -> prev.txIdBE.hex, "vout" -> prev.vout.toLong)
|
||||
})
|
||||
|
||||
bitcoindCall[Vector[GetTxSpendingPrevOutResult]](
|
||||
"gettxspendingprevout",
|
||||
List(json)
|
||||
)
|
||||
}
|
||||
|
||||
def simulateRawTransaction(
|
||||
tx: Transaction,
|
||||
includeWatchOnly: Boolean = true
|
||||
): Future[CurrencyUnit] = {
|
||||
simulateRawTransactions(Vector(tx), includeWatchOnly)
|
||||
}
|
||||
|
||||
def simulateRawTransactions(
|
||||
txs: Vector[Transaction],
|
||||
includeWatchOnly: Boolean = true
|
||||
): Future[CurrencyUnit] = {
|
||||
val txsJson = JsArray(txs.map(tx => JsString(tx.hex)))
|
||||
val options = Json.obj("include_watchonly" -> includeWatchOnly)
|
||||
|
||||
bitcoindCall[SimulateRawTransactionResult](
|
||||
"simulaterawtransaction",
|
||||
List(txsJson, options)
|
||||
).map(_.balance_change)
|
||||
}
|
||||
}
|
||||
|
||||
object BitcoindV24RpcClient {
|
||||
|
||||
/** 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): BitcoindV24RpcClient = {
|
||||
implicit val system: ActorSystem =
|
||||
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, where you need fine grained
|
||||
* control over the RPC client.
|
||||
*/
|
||||
def withActorSystem(instance: BitcoindInstance)(implicit
|
||||
system: ActorSystem
|
||||
): BitcoindV24RpcClient =
|
||||
new BitcoindV24RpcClient(instance)(system)
|
||||
|
||||
def fromUnknownVersion(
|
||||
rpcClient: BitcoindRpcClient
|
||||
): Try[BitcoindV24RpcClient] =
|
||||
Try {
|
||||
new BitcoindV24RpcClient(rpcClient.instance)(rpcClient.system)
|
||||
}
|
||||
|
||||
}
|
@ -59,9 +59,6 @@ sealed trait BitcoindInstanceLocal extends BitcoindInstance {
|
||||
.last
|
||||
|
||||
foundVersion match {
|
||||
case _: String
|
||||
if foundVersion.startsWith(BitcoindVersion.V24.toString) =>
|
||||
BitcoindVersion.V24
|
||||
case _: String
|
||||
if foundVersion.startsWith(BitcoindVersion.V25.toString) =>
|
||||
BitcoindVersion.V25
|
||||
|
@ -1090,5 +1090,5 @@ object EclairRpcClient {
|
||||
* @see
|
||||
* https://github.com/ACINQ/eclair/releases/tag/v0.8.0
|
||||
*/
|
||||
val bitcoindV: BitcoindVersion = BitcoindVersion.V24
|
||||
val bitcoindV: BitcoindVersion = BitcoindVersion.V25
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package org.bitcoins.testkit.rpc
|
||||
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.AddNodeArgument
|
||||
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
|
||||
import org.bitcoins.rpc.client.v24.BitcoindV24RpcClient
|
||||
import org.bitcoins.rpc.client.v25.BitcoindV25RpcClient
|
||||
import org.bitcoins.rpc.util.{NodePair, NodeTriple}
|
||||
import org.bitcoins.testkit.EmbeddedPg
|
||||
@ -49,39 +48,6 @@ trait BitcoindFixturesFundedCached extends BitcoindFixtures {
|
||||
}
|
||||
}
|
||||
|
||||
trait BitcoindFixturesFundedCachedV24
|
||||
extends BitcoinSAsyncFixtureTest
|
||||
with BitcoindFixturesFundedCached
|
||||
with CachedBitcoindV24 {
|
||||
override type FixtureParam = BitcoindV24RpcClient
|
||||
|
||||
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
|
||||
val f: Future[Outcome] = for {
|
||||
bitcoind <- cachedBitcoindWithFundsF
|
||||
futOutcome = withV24FundedBitcoindCached(test, bitcoind)
|
||||
fut <- futOutcome.toFuture
|
||||
} yield fut
|
||||
new FutureOutcome(f)
|
||||
}
|
||||
|
||||
def withV24FundedBitcoindCached(
|
||||
test: OneArgAsyncTest,
|
||||
bitcoind: BitcoindV24RpcClient
|
||||
): FutureOutcome = {
|
||||
makeDependentFixture[BitcoindV24RpcClient](
|
||||
() => Future.successful(bitcoind),
|
||||
{ _ =>
|
||||
Future.unit // don't want to destroy anything since it is cached
|
||||
}
|
||||
)(test)
|
||||
}
|
||||
|
||||
override def afterAll(): Unit = {
|
||||
super[CachedBitcoindV24].afterAll()
|
||||
super[BitcoinSAsyncFixtureTest].afterAll()
|
||||
}
|
||||
}
|
||||
|
||||
trait BitcoindFixturesFundedCachedNewest
|
||||
extends BitcoinSAsyncFixtureTest
|
||||
with BitcoindFixturesFundedCached
|
||||
|
@ -30,7 +30,6 @@ import org.bitcoins.crypto.{
|
||||
import org.bitcoins.rpc.BitcoindException
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion._
|
||||
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
|
||||
import org.bitcoins.rpc.client.v24.BitcoindV24RpcClient
|
||||
import org.bitcoins.rpc.client.v25.BitcoindV25RpcClient
|
||||
import org.bitcoins.rpc.config._
|
||||
import org.bitcoins.rpc.util.{NodePair, RpcUtil}
|
||||
@ -177,7 +176,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
version match {
|
||||
// default to newest version
|
||||
case Unknown => getBinary(BitcoindVersion.newest, binaryDirectory)
|
||||
case known @ (V24 | V25) =>
|
||||
case known @ (V25) =>
|
||||
val fileList = Files
|
||||
.list(binaryDirectory)
|
||||
.iterator()
|
||||
@ -241,22 +240,6 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
BitcoindInstanceLocal.fromConfig(conf, binary)
|
||||
}
|
||||
|
||||
def v24Instance(
|
||||
port: Int = RpcUtil.randomPort,
|
||||
rpcPort: Int = RpcUtil.randomPort,
|
||||
zmqConfig: ZmqConfig = RpcUtil.zmqConfig,
|
||||
pruneMode: Boolean = false,
|
||||
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
|
||||
)(implicit system: ActorSystem): BitcoindInstanceLocal =
|
||||
instance(
|
||||
port = port,
|
||||
rpcPort = rpcPort,
|
||||
zmqConfig = zmqConfig,
|
||||
pruneMode = pruneMode,
|
||||
versionOpt = Some(BitcoindVersion.V24),
|
||||
binaryDirectory = binaryDirectory
|
||||
)
|
||||
|
||||
def v25Instance(
|
||||
port: Int = RpcUtil.randomPort,
|
||||
rpcPort: Int = RpcUtil.randomPort,
|
||||
@ -283,14 +266,6 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
|
||||
)(implicit system: ActorSystem): BitcoindInstanceLocal = {
|
||||
bitcoindVersion match {
|
||||
case BitcoindVersion.V24 =>
|
||||
BitcoindRpcTestUtil.v24Instance(
|
||||
port,
|
||||
rpcPort,
|
||||
zmqConfig,
|
||||
pruneMode,
|
||||
binaryDirectory = binaryDirectory
|
||||
)
|
||||
case BitcoindVersion.V25 =>
|
||||
BitcoindRpcTestUtil.v25Instance(
|
||||
port,
|
||||
@ -311,9 +286,8 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
)(implicit ec: ExecutionContext): Future[Unit] = {
|
||||
val startedServersF = Future.traverse(servers) { server =>
|
||||
server.start().flatMap { res =>
|
||||
val descriptors = true
|
||||
val createWalletF = for {
|
||||
version <- server.version
|
||||
descriptors = true
|
||||
_ <- res.createWallet(BitcoindRpcClient.DEFAULT_WALLET_NAME,
|
||||
descriptors = descriptors)
|
||||
} yield res
|
||||
@ -638,10 +612,6 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
||||
val rpc = version match {
|
||||
case BitcoindVersion.Unknown =>
|
||||
BitcoindRpcClient.withActorSystem(BitcoindRpcTestUtil.instance())
|
||||
case BitcoindVersion.V24 =>
|
||||
BitcoindV24RpcClient.withActorSystem(
|
||||
BitcoindRpcTestUtil.v24Instance()
|
||||
)
|
||||
case BitcoindVersion.V25 =>
|
||||
BitcoindV25RpcClient.withActorSystem(
|
||||
BitcoindRpcTestUtil.v25Instance())
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.bitcoins.testkit.rpc
|
||||
|
||||
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
|
||||
import org.bitcoins.rpc.client.v24.BitcoindV24RpcClient
|
||||
import org.bitcoins.rpc.util.{NodePair, NodeTriple}
|
||||
import org.bitcoins.testkit.fixtures.BitcoinSFixture
|
||||
import org.bitcoins.testkit.util.BitcoinSPekkoAsyncTest
|
||||
@ -80,6 +79,7 @@ trait CachedBitcoindFunded[T <: BitcoindRpcClient] extends CachedBitcoind[T] {
|
||||
if (isBitcoindUsed.get()) {
|
||||
// if it was used, shut down the cached bitcoind
|
||||
val stoppedF = for {
|
||||
_ <- Future.unit
|
||||
cachedBitcoind <- cachedBitcoindWithFundsF
|
||||
_ <- BitcoindRpcTestUtil.stopServer(cachedBitcoind)
|
||||
} yield {
|
||||
@ -128,18 +128,6 @@ trait CachedBitcoindBlockFilterRpcNewest
|
||||
}
|
||||
}
|
||||
|
||||
trait CachedBitcoindV24 extends CachedBitcoindFunded[BitcoindV24RpcClient] {
|
||||
_: BitcoinSPekkoAsyncTest =>
|
||||
|
||||
override protected lazy val cachedBitcoindWithFundsF
|
||||
: Future[BitcoindV24RpcClient] = {
|
||||
val _ = isBitcoindUsed.set(true)
|
||||
BitcoinSFixture
|
||||
.createBitcoindWithFunds(Some(BitcoindVersion.V24))
|
||||
.map(_.asInstanceOf[BitcoindV24RpcClient])
|
||||
}
|
||||
}
|
||||
|
||||
trait CachedBitcoindCollection[T <: BitcoindRpcClient]
|
||||
extends CachedBitcoind[T] {
|
||||
_: BitcoinSPekkoAsyncTest =>
|
||||
|
Loading…
Reference in New Issue
Block a user