Implement caching of bitcoind in the walletTest,nodeTest, and partially bitcoindRpcTest project (#2792)

* Create CachedBitcoind, implement it in FundTransactionHandlingTest

* Add BaseWalletTest, extend it with BitcoinSWalletTest & BitcoinSWalletTestCachedBitcoind, add CachedBitcoinV19 and use it RescanHandlingTest

* Make ProcessBlockTest work with cached bitcoind

* Make trait for CachedBitcoindNewest for the newest version of bitcoind

* Make UTXOLifeCycleTest use cached bitcoind

* Add WalletBloom, WalletSyncTest to use cached bitcoinds

* Add WalletIntegrationTest

* Rework beforeAll() and afterAll() into the super trait like BaseWalletTest

* Add standlone BitcoindFixtures, use it in BitcoindBackendTest

* Use new BitcoindFixtures in BitcoindBlockPollingTest

* Introduce BaseNodeTest, start implementing the usage of cached bitcoinds in the nodeTest project

* Use cached bitcoind's with SpvNodeTest & SpvNodeWithWalletTest

* Fix bug on postgres with reusing database, upsert the genesis header rather than create it

* Get NeutrinoNode tests workign with cached bitcoinds

* Fix NeutrinoNodeWithWallet by destroying wallet state for Postgres

* Add teardown helper method for bitcoind

* Teardown chain project when using node fixtures since node is dependent upon the chain project.

* Turn off parallelExecution again

* Switch the parallelExecution flag to only be set on CI, so we can get better performance when running locally

* Start implementing BitcoindFixtures, use BitcoindFixturesCachedTriple on TestUtilRpcTest

* Fix compiler errors, begin implementing NodePair

* Refactor TestRpcUtilTest to use 2 bitcoinds rather than 2

* Reduce the number of bitcoinds that MultiWalletRpcTest needs from 3 -> 1

* Reduce number of bitcoinds used in WalletRpcTest from 3 -> 2

* Add some documentation

* Try to re-add parallelExecution

* Reduce the number of bitcoinds used in PsbtRpcTest from 3 -> 2

* Disable parallelExecution in Test again

* Make BitcoindV21RpcClientTest & BitcoindV20RpcClientTest reduce bitcoind usage from 2 -> 1

* Make BitcoindV19RpcClienttest reduce bitcoind usage from 2 -> 1

* Rework MempoolRpcTest to use fixtures, add BitcoindVersion to CachedBitcoindCollection

* Make sure clientAccumm has to be specified as a paramter now rather than filling in by default

* Begin parameterizing NodePair/NodeTriple to retain type information for the specific version of bitcoind that was used

* Don't implement version in super trait

* Fix docs

* Fix async issue in V21 test suite

* Append to vectors in CachedBitcoinCollection rather than replace

* Fix rebase issues

* Add scaladocs

* Fix BitcoindV18RpcClient address info test

* Implement fixtures in BitcoindV17RpcClientTest fixtures

* Cleanup v17 PsbtRpcTest

* Reduce bitcoind usage from 3 -> 1 in BitcoindV18RpcClientTest

* Remove abandon transaction test, this allows us to reduce the number of bitcoind's used in MempoolRpcTest from 3 -> 2

* Remove the requirement to inject  BitcoinSAsyncFixtureTest, add it in the test traits explicitly to make things easier. Also add explicit afterAll() method to tear down both the CachedBitcoind & BitcoinSAsyncFixtureTest

* Fix missing Await.result() in BitcoindRpcTest.afterAll()

* Rework MultiWalletRpcTest to use a NodePair

* Rework BlockchainRpcTest to use fixtures

* Rework Client start()/stop() methods. Now use an AtomicBoolean to indicate when a user has requested a client to start/stop rather than sending pings to bitcoind that can fail because the conneciton pool has been shutdown in test cases

* Try my luck with turning on parallelExecution in CI again

* Revert parallelExecution, now testes do not run in parallel on CI

* Only turn off parallelExecution for bitcoindRpcTest

* Adjust build to only have bitcoindRpcTest NOT in run parallel on mac, reduce number of blocks used in BitcoindRpcTestUtil.createNodeSequence

* Run less tests in the rpc test suite as that takes the longest, move them over to node/wallet/dlc test suite on mac osx CI

* Don't run eclair tests in parallel either

* Remove CachedBitcoind from BitcoinSWalletTest

* Fix async bug in test case

* Push to github to force re-run of CI

* Push to github to force re-run of CI

* Push to github to force re-run of CI
This commit is contained in:
Chris Stewart 2021-03-19 06:37:53 -05:00 committed by GitHub
parent b0f7d6f26b
commit 2287c6ced9
56 changed files with 3046 additions and 2084 deletions

View File

@ -27,4 +27,4 @@ jobs:
~/.bitcoin-s/binaries
key: ${{ runner.os }}-cache
- name: run tests
run: sbt ++2.13.5 downloadBitcoind downloadEclair coverage cryptoTestJVM/test coreTestJVM/test appCommonsTest/test bitcoindRpcTest/test bitcoindRpc/coverageReport bitcoindRpc/coverageAggregate bitcoindRpc/coveralls eclairRpcTest/test eclairRpc/coverageReport eclairRpc/coverageAggregate eclairRpc/coveralls
run: sbt ++2.13.5 downloadBitcoind downloadEclair coverage bitcoindRpcTest/test bitcoindRpc/coverageReport bitcoindRpc/coverageAggregate bitcoindRpc/coveralls eclairRpcTest/test eclairRpc/coverageReport eclairRpc/coverageAggregate eclairRpc/coveralls

View File

@ -25,4 +25,4 @@ jobs:
~/.bitcoin-s/binaries
key: ${{ runner.os }}-cache
- name: run tests
run: sbt ++2.13.5 downloadBitcoind coverage walletTest/test wallet/coverageReport wallet/coverageAggregate wallet/coveralls nodeTest/test node/coverageReport node/coverageAggregate node/coveralls dlcOracleTest/test asyncUtilsTestJVM/test dlcOracle/coverageReport dlcOracle/coveralls
run: sbt ++2.13.5 downloadBitcoind coverage cryptoTestJVM/test coreTestJVM/test appCommonsTest/test walletTest/test wallet/coverageReport wallet/coverageAggregate wallet/coveralls nodeTest/test node/coverageReport node/coverageAggregate node/coveralls dlcOracleTest/test asyncUtilsTestJVM/test dlcOracle/coverageReport dlcOracle/coveralls

View File

@ -40,19 +40,20 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
/** Tests that the client can call the isStartedF method
* without throwing and then start
*/
private def testClientStart(client: BitcoindRpcClient): Future[Assertion] = {
clientAccum += client
for {
firstStarted <- client.isStartedF
_ <- startClient(client)
secondStarted <- client.isStartedF
private def testClientStart(client: BitcoindRpcClient): Future[Assertion] =
synchronized {
clientAccum += client
for {
firstStarted <- client.isStartedF
_ <- startClient(client)
secondStarted <- client.isStartedF
_ <- client.getBalance
} yield {
assert(!firstStarted)
assert(secondStarted)
_ <- client.getBalance
} yield {
assert(!firstStarted)
assert(secondStarted)
}
}
}
behavior of "BitcoindInstance"

View File

@ -1,110 +1,94 @@
package org.bitcoins.rpc
import org.bitcoins.core.currency.Bitcoins
import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.rpc.util.RpcUtil
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.{BitcoindRpcTest, FileUtil}
import org.bitcoins.rpc.client.v21.BitcoindV21RpcClient
import org.bitcoins.rpc.util.{NodePair, RpcUtil}
import org.bitcoins.testkit.rpc.{
BitcoindFixturesCachedPairNewest,
BitcoindRpcTestUtil
}
import org.bitcoins.testkit.util.FileUtil
import org.scalatest.{FutureOutcome, Outcome}
import java.io.File
import scala.concurrent.Future
class TestRpcUtilTest extends BitcoindRpcTest {
class TestRpcUtilTest extends BitcoindFixturesCachedPairNewest {
private lazy val clientsF =
BitcoindRpcTestUtil.createNodeTriple(clientAccum = clientAccum)
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val outcomeF: Future[Outcome] = for {
clients <- clientsF
outcome = with2BitcoindsCached(test, clients)
fut <- outcome.toFuture
} yield fut
new FutureOutcome(outcomeF)
}
behavior of "BitcoindRpcUtil"
it should "create a temp bitcoin directory when creating a DaemonInstance, and then delete it" in {
val instance =
BitcoindRpcTestUtil.instance(RpcUtil.randomPort, RpcUtil.randomPort)
val dir = instance.datadir
assert(dir.isDirectory)
assert(dir.getPath().startsWith(scala.util.Properties.tmpDir))
assert(
dir.listFiles.contains(new File(dir.getAbsolutePath + "/bitcoin.conf")))
FileUtil.deleteTmpDir(dir)
assert(!dir.exists)
}
it should "be able to create a single node, wait for it to start and then delete it" in {
val instance = BitcoindRpcTestUtil.instance()
val client = BitcoindRpcClient.withActorSystem(instance)
val startedF = client.start()
startedF.map { _ =>
client.stop()
succeed
}
}
it should "be able to create a connected node pair with more than 100 blocks and then delete them" in {
for {
(client1, client2) <- BitcoindRpcTestUtil.createNodePair()
_ = assert(client1.getDaemon.datadir.isDirectory)
_ = assert(client2.getDaemon.datadir.isDirectory)
nodes <- client1.getAddedNodeInfo(client2.getDaemon.uri)
_ = assert(nodes.nonEmpty)
count1 <- client1.getBlockCount
count2 <- client2.getBlockCount
_ = assert(count1 > 100)
_ = assert(count2 > 100)
_ <- BitcoindRpcTestUtil.deleteNodePair(client1, client2)
} yield {
assert(!client1.getDaemon.datadir.exists)
assert(!client2.getDaemon.datadir.exists)
}
_: NodePair[BitcoindV21RpcClient] =>
val instance =
BitcoindRpcTestUtil.instance(RpcUtil.randomPort, RpcUtil.randomPort)
val dir = instance.datadir
assert(dir.isDirectory)
assert(dir.getPath().startsWith(scala.util.Properties.tmpDir))
assert(
dir.listFiles.contains(new File(dir.getAbsolutePath + "/bitcoin.conf")))
FileUtil.deleteTmpDir(dir)
assert(!dir.exists)
}
it should "be able to generate and sync blocks" in {
for {
(first, second, third) <- clientsF
address <- second.getNewAddress
txid <- first.sendToAddress(address, Bitcoins.one)
_ <- BitcoindRpcTestUtil.generateAndSync(Vector(first, second, third))
tx <- first.getTransaction(txid)
_ = assert(tx.confirmations > 0)
rawTx <- second.getRawTransaction(txid)
_ = assert(rawTx.confirmations.exists(_ > 0))
firstBlock <- first.getBestBlockHash
secondBlock <- second.getBestBlockHash
} yield assert(firstBlock == secondBlock)
nodes: NodePair[BitcoindV21RpcClient] =>
val NodePair(first, second) = nodes
for {
address <- second.getNewAddress
txid <- first.sendToAddress(address, Bitcoins.one)
_ <- BitcoindRpcTestUtil.generateAndSync(Vector(first, second))
tx <- first.getTransaction(txid)
_ = assert(tx.confirmations > 0)
rawTx <- second.getRawTransaction(txid)
_ = assert(rawTx.confirmations.exists(_ > 0))
firstBlock <- first.getBestBlockHash
secondBlock <- second.getBestBlockHash
} yield assert(firstBlock == secondBlock)
}
it should "ble able to generate blocks with multiple clients and sync inbetween" in {
val blocksToGenerate = 10
for {
(first, second, third) <- clientsF
allClients = Vector(first, second, third)
heightPreGeneration <- first.getBlockCount
_ <- BitcoindRpcTestUtil.generateAllAndSync(allClients,
blocks = blocksToGenerate)
firstHash <- first.getBestBlockHash
secondHash <- second.getBestBlockHash
heightPostGeneration <- first.getBlockCount
} yield {
assert(firstHash == secondHash)
assert(
heightPostGeneration - heightPreGeneration == blocksToGenerate * allClients.length)
}
nodes: NodePair[BitcoindV21RpcClient] =>
val blocksToGenerate = 10
val NodePair(first, second) = nodes
val allClients = nodes.toVector
for {
heightPreGeneration <- first.getBlockCount
_ <- BitcoindRpcTestUtil.generateAllAndSync(allClients,
blocks = blocksToGenerate)
firstHash <- first.getBestBlockHash
secondHash <- second.getBestBlockHash
heightPostGeneration <- first.getBlockCount
} yield {
assert(firstHash == secondHash)
assert(
heightPostGeneration - heightPreGeneration == blocksToGenerate * allClients.length)
}
}
it should "be able to find outputs of previous transactions" in {
for {
(first, second, _) <- clientsF
address <- second.getNewAddress
txid <- first.sendToAddress(address, Bitcoins.one)
hashes <- BitcoindRpcTestUtil.generateAndSync(Vector(first, second))
vout <- BitcoindRpcTestUtil.findOutput(first,
txid,
Bitcoins.one,
Some(hashes.head))
tx <- first.getRawTransaction(txid, Some(hashes.head))
} yield {
assert(tx.vout(vout.toInt).value == Bitcoins.one)
}
nodes: NodePair[BitcoindV21RpcClient] =>
val NodePair(first, second) = nodes
for {
address <- second.getNewAddress
txid <- first.sendToAddress(address, Bitcoins.one)
hashes <- BitcoindRpcTestUtil.generateAndSync(Vector(first, second))
vout <- BitcoindRpcTestUtil.findOutput(first,
txid,
Bitcoins.one,
Some(hashes.head))
tx <- first.getRawTransaction(txid, Some(hashes.head))
} yield {
assert(tx.vout(vout.toInt).value == Bitcoins.one)
}
}
}

View File

@ -1,65 +1,38 @@
package org.bitcoins.rpc.common
import org.bitcoins.asyncutil.AsyncUtil
import org.bitcoins.commons.jsonmodels.bitcoind.GetBlockChainInfoResultPreV19
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.{
AddNodeArgument,
AddressType
}
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.AddressType
import org.bitcoins.core.config.RegTest
import org.bitcoins.core.currency.Bitcoins
import org.bitcoins.core.number.UInt32
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.BitcoindRpcTest
import org.bitcoins.testkit.rpc.{
BitcoindFixturesCachedPairV17,
BitcoindRpcTestUtil
}
import scala.concurrent.Future
import scala.concurrent.{Await, Future}
class BlockchainRpcTest extends BitcoindRpcTest {
class BlockchainRpcTest extends BitcoindFixturesCachedPairV17 {
lazy val clientsF: Future[(BitcoindRpcClient, BitcoindRpcClient)] =
BitcoindRpcTestUtil.createNodePairV17(clientAccum = clientAccum)
lazy val pruneClientF: Future[BitcoindRpcClient] = {
val pruneClient =
BitcoindRpcClient.withActorSystem(
BitcoindRpcTestUtil
.instance(pruneMode = true, versionOpt = Some(BitcoindVersion.V17)))
lazy val pruneClientF: Future[BitcoindRpcClient] = clientsF.flatMap {
case (_, _) =>
val pruneClient =
BitcoindRpcClient.withActorSystem(
BitcoindRpcTestUtil
.instance(pruneMode = true, versionOpt = Some(BitcoindVersion.V17)))
clientAccum += pruneClient
for {
_ <- pruneClient.start()
_ <- pruneClient.getNewAddress.flatMap(
pruneClient.generateToAddress(1000, _))
} yield pruneClient
for {
_ <- pruneClient.start()
_ <- pruneClient.getNewAddress.flatMap(
pruneClient.generateToAddress(1000, _))
} yield pruneClient
}
behavior of "BlockchainRpc"
it should "be able to get the block count" in {
it should "be able to get the first block" in { nodePair =>
val client = nodePair.node1
for {
(client, otherClient) <- clientsF
// kick off both futures at the same time to avoid
// one of them generating new blocks in between
clientCountF = client.getBlockCount
otherClientCountF = otherClient.getBlockCount
List(clientCount, otherClientCount) <- {
val countsF = List(clientCountF, otherClientCountF)
Future.sequence(countsF)
}
} yield {
assert(clientCount >= 0)
assert(clientCount == otherClientCount)
}
}
it should "be able to get the first block" in {
for {
(client, _) <- clientsF
block <- BitcoindRpcTestUtil.getFirstBlock(client)
} yield {
assert(block.tx.nonEmpty)
@ -67,19 +40,9 @@ class BlockchainRpcTest extends BitcoindRpcTest {
}
}
it should "be able to prune the blockchain" in {
it should "be able to get blockchain info" in { nodePair =>
val client = nodePair.node1
for {
pruneClient <- pruneClientF
count <- pruneClient.getBlockCount
pruned <- pruneClient.pruneBlockChain(count)
} yield {
assert(pruned > 0)
}
}
it should "be able to get blockchain info" in {
for {
(client, _) <- clientsF
info <- client.getBlockChainInfo
bestHash <- client.getBestBlockHash
} yield {
@ -92,9 +55,10 @@ class BlockchainRpcTest extends BitcoindRpcTest {
}
}
it should "be able to invalidate a block" in {
it should "be able to invalidate a block" in { nodePair =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
(client, otherClient) <- clientsF
address <- otherClient.getNewAddress(addressType = AddressType.P2SHSegwit)
txid <-
BitcoindRpcTestUtil
@ -116,9 +80,9 @@ class BlockchainRpcTest extends BitcoindRpcTest {
}
}
it should "be able to get block hash by height" in {
it should "be able to get block hash by height" in { nodePair =>
val client = nodePair.node1
for {
(client, _) <- clientsF
blocks <- client.getNewAddress.flatMap(client.generateToAddress(2, _))
count <- client.getBlockCount
hash <- client.getBlockHash(count)
@ -129,38 +93,9 @@ class BlockchainRpcTest extends BitcoindRpcTest {
}
}
it should "be able to mark a block as precious" in {
it should "be able to get tx out proof and verify it" in { nodePair =>
val client = nodePair.node1
for {
(freshClient, otherFreshClient) <-
BitcoindRpcTestUtil.createNodePair(clientAccum)
_ <- freshClient.disconnectNode(otherFreshClient.getDaemon.uri)
_ <- BitcoindRpcTestUtil.awaitDisconnected(freshClient, otherFreshClient)
blocks1 <-
freshClient.getNewAddress.flatMap(freshClient.generateToAddress(1, _))
blocks2 <- otherFreshClient.getNewAddress.flatMap(
otherFreshClient.generateToAddress(1, _))
bestHash1 <- freshClient.getBestBlockHash
_ = assert(bestHash1 == blocks1.head)
bestHash2 <- otherFreshClient.getBestBlockHash
_ = assert(bestHash2 == blocks2.head)
_ <-
freshClient
.addNode(otherFreshClient.getDaemon.uri, AddNodeArgument.OneTry)
_ <- AsyncUtil.retryUntilSatisfiedF(() =>
BitcoindRpcTestUtil.hasSeenBlock(otherFreshClient, bestHash1))
_ <- otherFreshClient.preciousBlock(bestHash1)
newBestHash <- otherFreshClient.getBestBlockHash
} yield assert(newBestHash == bestHash1)
}
it should "be able to get tx out proof and verify it" in {
for {
(client, _) <- clientsF
block <- BitcoindRpcTestUtil.getFirstBlock(client)
merkle <- client.getTxOutProof(Vector(block.tx.head.txid))
txids <- client.verifyTxOutProof(merkle)
@ -172,9 +107,9 @@ class BlockchainRpcTest extends BitcoindRpcTest {
}
}
it should "be able to rescan the blockchain" in {
it should "be able to rescan the blockchain" in { nodePair =>
val client = nodePair.node1
for {
(client, _) <- clientsF
result <- client.rescanBlockChain()
count <- client.getBlockCount
} yield {
@ -183,9 +118,9 @@ class BlockchainRpcTest extends BitcoindRpcTest {
}
}
it should "be able to get the chain tx stats" in {
it should "be able to get the chain tx stats" in { nodePair =>
val client = nodePair.node1
for {
(client, _) <- clientsF
stats <- client.getChainTxStats
} yield {
assert(stats.txcount > 0)
@ -193,18 +128,18 @@ class BlockchainRpcTest extends BitcoindRpcTest {
}
}
it should "be able to get a raw block" in {
it should "be able to get a raw block" in { nodePair =>
val client = nodePair.node1
for {
(client, _) <- clientsF
blocks <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
block <- client.getBlockRaw(blocks.head)
blockHeader <- client.getBlockHeaderRaw(blocks.head)
} yield assert(block.blockHeader == blockHeader)
}
it should "be able to get a block" in {
it should "be able to get a block" in { nodePair =>
val client = nodePair.node1
for {
(client, _) <- clientsF
blocks <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
block <- client.getBlock(blocks.head)
} yield {
@ -217,9 +152,9 @@ class BlockchainRpcTest extends BitcoindRpcTest {
}
}
it should "be able to get a transaction" in {
it should "be able to get a transaction" in { nodePair =>
val client = nodePair.node1
for {
(client, _) <- clientsF
block <- BitcoindRpcTestUtil.getFirstBlock(client)
tx <- client.getTransaction(block.tx.head.txid)
count <- client.getBlockCount
@ -233,9 +168,9 @@ class BlockchainRpcTest extends BitcoindRpcTest {
}
}
it should "be able to get a block with verbose transactions" in {
it should "be able to get a block with verbose transactions" in { nodePair =>
val client = nodePair.node1
for {
(client, _) <- clientsF
blocks <- client.getNewAddress.flatMap(client.generateToAddress(2, _))
block <- client.getBlockWithTransactions(blocks(1))
} yield {
@ -246,23 +181,23 @@ class BlockchainRpcTest extends BitcoindRpcTest {
}
}
it should "be able to get the chain tips" in {
it should "be able to get the chain tips" in { nodePair =>
val client = nodePair.node1
for {
(client, _) <- clientsF
_ <- client.getChainTips
} yield succeed
}
it should "be able to get the best block hash" in {
it should "be able to get the best block hash" in { nodePair =>
val client = nodePair.node1
for {
(client, _) <- clientsF
_ <- client.getBestBlockHash
} yield succeed
}
it should "be able to list all blocks since a given block" in {
it should "be able to list all blocks since a given block" in { nodePair =>
val client = nodePair.node1
for {
(client, _) <- clientsF
blocks <- client.getNewAddress.flatMap(client.generateToAddress(3, _))
list <- client.listSinceBlock(blocks(0))
} yield {
@ -272,16 +207,16 @@ class BlockchainRpcTest extends BitcoindRpcTest {
}
}
it should "be able to verify the chain" in {
it should "be able to verify the chain" in { nodePair =>
val client = nodePair.node1
for {
(client, _) <- clientsF
valid <- client.verifyChain(blocks = 0)
} yield assert(valid)
}
it should "be able to get the tx outset info" in {
it should "be able to get the tx outset info" in { nodePair =>
val client = nodePair.node1
for {
(client, _) <- clientsF
info <- client.getTxOutSetInfo
count <- client.getBlockCount
hash <- client.getBestBlockHash
@ -291,15 +226,19 @@ class BlockchainRpcTest extends BitcoindRpcTest {
}
}
it should "be able to list transactions in a given range" in { // Assumes 30 transactions
it should "be able to prune the blockchain" in { _ =>
for {
(client, _) <- clientsF
list1 <- client.listTransactions()
list2 <- client.listTransactions(count = 20)
list3 <- client.listTransactions(count = 20, skip = 10)
pruneClient <- pruneClientF
count <- pruneClient.getBlockCount
pruned <- pruneClient.pruneBlockChain(count)
} yield {
assert(list2.takeRight(10) == list1)
assert(list2.splitAt(10)._1 == list3.takeRight(10))
assert(pruned > 0)
}
}
override def afterAll(): Unit = {
val stoppedF = pruneClientF.flatMap(BitcoindRpcTestUtil.stopServer)
val _ = Await.result(stoppedF, duration)
super.afterAll()
}
}

View File

@ -1,7 +1,5 @@
package org.bitcoins.rpc.common
import java.io.File
import org.bitcoins.core.currency.Bitcoins
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script.ScriptSignature
@ -11,90 +9,72 @@ import org.bitcoins.core.protocol.transaction.{
}
import org.bitcoins.crypto.DoubleSha256Digest
import org.bitcoins.rpc.BitcoindException
import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.rpc.client.common.BitcoindVersion.V18
import org.bitcoins.rpc.config.BitcoindInstance
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.BitcoindRpcTest
import org.bitcoins.testkit.rpc.{
BitcoindFixturesCachedPairNewest,
BitcoindRpcTestUtil
}
import org.scalatest.{FutureOutcome, Outcome}
import java.io.File
import scala.concurrent.Future
class MempoolRpcTest extends BitcoindRpcTest {
class MempoolRpcTest extends BitcoindFixturesCachedPairNewest {
lazy val clientsF: Future[(BitcoindRpcClient, BitcoindRpcClient)] =
BitcoindRpcTestUtil.createNodePairV18(clientAccum = clientAccum)
lazy val clientWithoutBroadcastF: Future[BitcoindRpcClient] = {
val defaultConfig = BitcoindRpcTestUtil.standardConfig
val configNoBroadcast =
defaultConfig
.withOption("walletbroadcast", 0.toString)
val instanceWithoutBroadcast =
BitcoindInstance.fromConfig(configNoBroadcast,
BitcoindRpcTestUtil.getBinary(V18))
val clientWithoutBroadcast =
BitcoindRpcClient.withActorSystem(instanceWithoutBroadcast)
val clientWithoutBroadcastF = clientWithoutBroadcast.start()
for {
(client, otherClient) <- clientsF
clientWithoutBroadcast <- clientWithoutBroadcastF
_ = {
clientAccum += clientWithoutBroadcast
}
pairs = Vector(client -> clientWithoutBroadcast,
otherClient -> clientWithoutBroadcast)
_ <- BitcoindRpcTestUtil.connectPairs(pairs)
_ <- BitcoindRpcTestUtil.syncPairs(pairs)
_ <- BitcoindRpcTestUtil.generateAndSync(
Vector(clientWithoutBroadcast, client, otherClient),
blocks = 200)
} yield clientWithoutBroadcast
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val futOutcome: Future[Outcome] = for {
nodePair <- clientsF
futOutcome = with2BitcoindsCached(test, nodePair)
f <- futOutcome.toFuture
} yield f
new FutureOutcome(futOutcome)
}
behavior of "MempoolRpc"
it should "be able to find a transaction sent to the mem pool" in {
for {
(client, otherClient) <- clientsF
transaction <-
BitcoindRpcTestUtil.sendCoinbaseTransaction(client, otherClient)
mempool <- client.getRawMemPool
} yield {
assert(mempool.length == 1)
assert(mempool.head == transaction.txid)
}
nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
transaction <-
BitcoindRpcTestUtil.sendCoinbaseTransaction(client, otherClient)
mempool <- client.getRawMemPool
} yield {
assert(mempool.length == 1)
assert(mempool.head == transaction.txid)
}
}
it should "be able to find a verbose transaction in the mem pool" in {
for {
(client, otherClient) <- clientsF
transaction <-
BitcoindRpcTestUtil.sendCoinbaseTransaction(client, otherClient)
mempool <- client.getRawMemPoolWithTransactions
} yield {
val txid = mempool.keySet.head
assert(txid == transaction.txid)
assert(mempool(txid).size > 0)
}
nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
transaction <-
BitcoindRpcTestUtil.sendCoinbaseTransaction(client, otherClient)
mempool <- client.getRawMemPoolWithTransactions
} yield {
val txid = mempool.keySet.head
assert(txid == transaction.txid)
assert(mempool(txid).size > 0)
}
}
it should "be able to find a mem pool entry" in {
it should "be able to find a mem pool entry" in { nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
(client, otherClient) <- clientsF
transaction <-
BitcoindRpcTestUtil.sendCoinbaseTransaction(client, otherClient)
_ <- client.getMemPoolEntry(transaction.txid)
} yield succeed
}
it must "fail to find a mempool entry" in {
it must "fail to find a mempool entry" in { nodePair: FixtureParam =>
val client = nodePair.node1
val txid = DoubleSha256Digest.empty
val resultF = for {
(client, _) <- clientsF
txid = DoubleSha256Digest.empty
result <- client.getMemPoolEntry(txid)
} yield {
result
@ -104,18 +84,20 @@ class MempoolRpcTest extends BitcoindRpcTest {
}
it must "fail to find a mempool entry and return None" in {
val resultF = for {
(client, _) <- clientsF
txid = DoubleSha256Digest.empty
result <- client.getMemPoolEntryOpt(txid)
} yield {
assert(result.isEmpty)
}
resultF
nodePair: FixtureParam =>
val client = nodePair.node1
val txid = DoubleSha256Digest.empty
val resultF = for {
result <- client.getMemPoolEntryOpt(txid)
} yield {
assert(result.isEmpty)
}
resultF
}
it should "be able to get mem pool info" in {
it should "be able to get mem pool info" in { nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
(client, otherClient) <- clientsF
_ <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
info <- client.getMemPoolInfo
_ <-
@ -129,91 +111,81 @@ class MempoolRpcTest extends BitcoindRpcTest {
}
it should "be able to prioritise a mem pool transaction" in {
for {
(client, otherClient) <- clientsF
address <- otherClient.getNewAddress
txid <-
BitcoindRpcTestUtil
.fundMemPoolTransaction(client, address, Bitcoins(3.2))
entry <- client.getMemPoolEntry(txid)
tt <- client.prioritiseTransaction(txid, Bitcoins(1).satoshis)
newEntry <- client.getMemPoolEntry(txid)
} yield {
assert(entry.fee == entry.modifiedfee)
assert(tt)
assert(newEntry.fee == entry.fee)
assert(newEntry.modifiedfee == newEntry.fee + Bitcoins(1))
}
nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
address <- otherClient.getNewAddress
txid <-
BitcoindRpcTestUtil
.fundMemPoolTransaction(client, address, Bitcoins(3.2))
entry <- client.getMemPoolEntry(txid)
tt <- client.prioritiseTransaction(txid, Bitcoins(1).satoshis)
newEntry <- client.getMemPoolEntry(txid)
} yield {
assert(entry.fee == entry.modifiedfee)
assert(tt)
assert(newEntry.fee == entry.fee)
assert(newEntry.modifiedfee == newEntry.fee + Bitcoins(1))
}
}
it should "be able to find mem pool ancestors and descendants" in {
for {
(client, _) <- clientsF
_ <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
address1 <- client.getNewAddress
txid1 <- BitcoindRpcTestUtil.fundMemPoolTransaction(client,
address1,
Bitcoins(2))
mempool <- client.getRawMemPool
address2 <- client.getNewAddress
nodePair: FixtureParam =>
val client = nodePair.node1
for {
_ <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
address1 <- client.getNewAddress
txid1 <- BitcoindRpcTestUtil.fundMemPoolTransaction(client,
address1,
Bitcoins(2))
mempool <- client.getRawMemPool
address2 <- client.getNewAddress
createdTx <- {
val input: TransactionInput =
TransactionInput(TransactionOutPoint(txid1.flip, UInt32.zero),
ScriptSignature.empty,
UInt32.max - UInt32.one)
client
.createRawTransaction(Vector(input), Map(address2 -> Bitcoins.one))
createdTx <- {
val input: TransactionInput =
TransactionInput(TransactionOutPoint(txid1.flip, UInt32.zero),
ScriptSignature.empty,
UInt32.max - UInt32.one)
client
.createRawTransaction(Vector(input), Map(address2 -> Bitcoins.one))
}
signedTx <- BitcoindRpcTestUtil.signRawTransaction(client, createdTx)
txid2 <- client.sendRawTransaction(signedTx.hex, maxfeerate = 0)
descendantsTxid1 <- client.getMemPoolDescendants(txid1)
verboseDescendantsTxid1 <- client.getMemPoolDescendantsVerbose(txid1)
_ = {
assert(descendantsTxid1.head == txid2)
val (txid, mempoolresults) = verboseDescendantsTxid1.head
assert(txid == txid2)
assert(mempoolresults.ancestorcount == 2)
}
ancestorsTxid2 <- client.getMemPoolAncestors(txid2)
verboseAncestorsTxid2 <- client.getMemPoolAncestorsVerbose(txid2)
_ = {
assert(ancestorsTxid2.head == txid1)
val (txid, mempoolresults) = verboseAncestorsTxid2.head
assert(txid == txid1)
assert(mempoolresults.descendantcount == 2)
}
} yield {
assert(mempool.head == txid1)
assert(signedTx.complete)
}
signedTx <- BitcoindRpcTestUtil.signRawTransaction(client, createdTx)
txid2 <- client.sendRawTransaction(signedTx.hex, maxfeerate = 0)
descendantsTxid1 <- client.getMemPoolDescendants(txid1)
verboseDescendantsTxid1 <- client.getMemPoolDescendantsVerbose(txid1)
_ = {
assert(descendantsTxid1.head == txid2)
val (txid, mempoolresults) = verboseDescendantsTxid1.head
assert(txid == txid2)
assert(mempoolresults.ancestorcount == 2)
}
ancestorsTxid2 <- client.getMemPoolAncestors(txid2)
verboseAncestorsTxid2 <- client.getMemPoolAncestorsVerbose(txid2)
_ = {
assert(ancestorsTxid2.head == txid1)
val (txid, mempoolresults) = verboseAncestorsTxid2.head
assert(txid == txid1)
assert(mempoolresults.descendantcount == 2)
}
} yield {
assert(mempool.head == txid1)
assert(signedTx.complete)
}
}
it should "be able to abandon a transaction" in {
for {
(_, otherClient) <- clientsF
clientWithoutBroadcast <- clientWithoutBroadcastF
recipient <- otherClient.getNewAddress
txid <- clientWithoutBroadcast.sendToAddress(recipient, Bitcoins(1))
_ <- clientWithoutBroadcast.abandonTransaction(txid)
maybeAbandoned <- clientWithoutBroadcast.getTransaction(txid)
} yield assert(maybeAbandoned.details.head.abandoned.contains(true))
}
it should "be able to save the mem pool to disk" in {
for {
(client, _) <- clientsF
regTest = {
val regTest =
new File(client.getDaemon.datadir.getAbsolutePath + "/regtest")
assert(regTest.isDirectory)
assert(!regTest.list().contains("mempool.dat"))
regTest
}
_ <- client.saveMemPool()
} yield assert(regTest.list().contains("mempool.dat"))
nodePair: FixtureParam =>
val client = nodePair.node1
val regTest =
new File(client.getDaemon.datadir.getAbsolutePath + "/regtest")
assert(regTest.isDirectory)
assert(!regTest.list().contains("mempool.dat"))
for {
_ <- client.saveMemPool()
} yield assert(regTest.list().contains("mempool.dat"))
}
}

View File

@ -12,10 +12,8 @@ import scala.concurrent.Future
class MessageRpcTest extends BitcoindRpcTest {
val clientF: Future[BitcoindRpcClient] =
BitcoindRpcTestUtil.startedBitcoindRpcClient().map { client =>
clientAccum += client
client
}
BitcoindRpcTestUtil
.startedBitcoindRpcClient(clientAccum = clientAccum)
behavior of "MessageRpc"

View File

@ -1,7 +1,5 @@
package org.bitcoins.rpc.common
import java.io.File
import java.util.Scanner
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.AddressType
import org.bitcoins.core.crypto.ECPrivateKeyUtil
@ -13,100 +11,116 @@ import org.bitcoins.core.wallet.fee.SatoshisPerByte
import org.bitcoins.crypto.{ECPrivateKey, ECPublicKey}
import org.bitcoins.rpc._
import org.bitcoins.rpc.client.common._
import org.bitcoins.rpc.util.RpcUtil
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.{AkkaUtil, BitcoindRpcTest}
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
import org.bitcoins.rpc.util.{NodePair, RpcUtil}
import org.bitcoins.testkit.rpc.{
BitcoindFixturesCachedPair,
BitcoindRpcTestUtil
}
import org.bitcoins.testkit.util.{AkkaUtil, BitcoinSAsyncFixtureTest}
import org.scalatest.{FutureOutcome, Outcome}
import java.io.File
import java.util.Scanner
import scala.concurrent.Future
import scala.concurrent.duration.DurationInt
/** These tests are all copied over from WalletRpcTest and changed to be for multi-wallet */
class MultiWalletRpcTest extends BitcoindRpcTest {
class MultiWalletRpcTest
extends BitcoinSAsyncFixtureTest
with BitcoindFixturesCachedPair[BitcoindV19RpcClient] {
override val version = BitcoindVersion.V19
override type FixtureParam = NodePair[BitcoindV19RpcClient]
val walletName = "other"
var password = "password"
lazy val clientsF: Future[
(BitcoindRpcClient, BitcoindRpcClient, BitcoindRpcClient)] =
BitcoindRpcTestUtil.createNodeTripleV19(clientAccum = clientAccum)
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val f: Future[Outcome] = for {
bitcoind <- cachedSetupClientsF
futOutcome = with2BitcoindsCached(test, bitcoind)
fut <- futOutcome.toFuture
} yield fut
new FutureOutcome(f)
}
lazy val walletClientF: Future[BitcoindRpcClient] = clientsF.flatMap {
clients =>
val walletClient =
BitcoindRpcClient.withActorSystem(BitcoindRpcTestUtil.instance())
clientAccum += walletClient
private val cachedSetupClientsF: Future[NodePair[BitcoindV19RpcClient]] = {
clientsF.flatMap(setupWalletClient)
}
for {
_ <- startClient(walletClient)
_ <- walletClient.createWallet(walletName)
_ <- walletClient.encryptWallet(password, Some(walletName))
_ <-
walletClient
.getNewAddress(Some(walletName))
.flatMap(walletClient.generateToAddress(101, _))
_ <- clients._1.createWallet(walletName)
/** We need to test bitcoin core's wallet specific features, so we need to set that up */
private def setupWalletClient(pair: NodePair[BitcoindV19RpcClient]): Future[
NodePair[BitcoindV19RpcClient]] = {
val NodePair(client: BitcoindV19RpcClient,
walletClient: BitcoindV19RpcClient) = pair
for {
_ <- walletClient.createWallet(walletName)
_ <- walletClient.encryptWallet(password, Some(walletName))
_ <-
walletClient
.getNewAddress(Some(walletName))
.flatMap(walletClient.generateToAddress(101, _))
_ <- client.createWallet(walletName)
// Restart so wallet is encrypted
_ <- walletClient.stop()
_ <- RpcUtil.awaitServerShutdown(walletClient)
// Very rarely we are prevented from starting the client again because Core
// hasn't released its locks on the datadir. This is prevent that.
_ <- AkkaUtil.nonBlockingSleep(1.second)
_ <- walletClient.start()
_ <- walletClient.loadWallet(walletName)
// Restart so wallet is encrypted
_ <- walletClient.stop()
_ <- RpcUtil.awaitServerShutdown(walletClient)
// Very rarely we are prevented from starting the client again because Core
// hasn't released its locks on the datadir. This is prevent that.
_ <- AkkaUtil.nonBlockingSleep(1.second)
started <- walletClient.start()
_ <- walletClient.loadWallet(walletName)
wallets <- walletClient.listWallets
wallets2 <- clients._1.listWallets
_ = require(wallets.size == 2)
_ = require(wallets2.size == 2)
} yield walletClient
wallets <- walletClient.listWallets
wallets2 <- client.listWallets
_ = require(wallets.size == 2)
_ = require(wallets2.size == 2)
} yield NodePair[BitcoindV19RpcClient](
client,
started.asInstanceOf[BitcoindV19RpcClient])
}
behavior of "WalletRpc"
it must "setup correctly" in {
it must "setup correctly" in { nodePair =>
val walletClient = nodePair.node2
for {
walletClient <- walletClientF
wallets <- walletClient.listWallets
} yield assert(wallets.size == 2)
}
it must "fail when no wallet is set" in {
it must "fail when no wallet is set" in { nodePair =>
val walletClient = nodePair.node2
recoverToSucceededIf[BitcoindWalletException](for {
walletClient <- walletClientF
_ <- walletClient.getBalance
} yield ())
}
it must "get balance" in {
it must "get balance" in { nodePair =>
val walletClient = nodePair.node2
for {
walletClient <- walletClientF
balance <- walletClient.getBalance(walletName)
} yield {
// Has one mature coinbase
assert(balance == Bitcoins(50))
assert(balance == Bitcoins(25))
}
}
it should "be able to backup the wallet" in {
it should "be able to backup the wallet" in { nodePair =>
val walletClient = nodePair.node2
val datadir = walletClient.getDaemon.datadir.getAbsolutePath
for {
client <- walletClientF
_ <- {
val datadir = client.getDaemon.datadir.getAbsolutePath
client.backupWallet(datadir + "/backup.dat", Some(walletName))
}
_ <- walletClient.backupWallet(datadir + "/backup.dat", Some(walletName))
} yield {
val datadir = client.getDaemon.datadir.getAbsolutePath
val file = new File(datadir + "/backup.dat")
assert(file.exists)
assert(file.isFile)
}
}
it should "be able to lock and unlock the wallet" in {
it should "be able to lock and unlock the wallet" in { nodePair =>
val walletClient = nodePair.node2
for {
walletClient <- walletClientF
_ <- walletClient.walletLock(walletName)
_ <- walletClient.walletPassphrase(password, 1000, Some(walletName))
@ -120,58 +134,54 @@ class MultiWalletRpcTest extends BitcoindRpcTest {
} yield assert(newInfo.unlocked_until.contains(0))
}
it should "be able to get an address from bitcoind" in {
for {
client <- walletClientF
_ <- {
val addrFuts =
List(
client.getNewAddress("bech32", AddressType.Bech32, walletName),
client.getNewAddress("p2sh", AddressType.P2SHSegwit, walletName),
client.getNewAddress("legacy", AddressType.Legacy, walletName)
)
Future.sequence(addrFuts)
}
} yield succeed
it should "be able to get an address from bitcoind" in { nodePair =>
val client = nodePair.node2
val addrFuts =
List(
client.getNewAddress("bech32", AddressType.Bech32, walletName),
client.getNewAddress("p2sh", AddressType.P2SHSegwit, walletName),
client.getNewAddress("legacy", AddressType.Legacy, walletName)
)
Future
.sequence(addrFuts)
.map(_ => succeed)
}
it should "be able to get a new raw change address" in {
for {
client <- walletClientF
_ <- {
val addrFuts =
List(
client.getRawChangeAddress(walletName),
client.getRawChangeAddress(AddressType.Bech32, walletName),
client.getRawChangeAddress(AddressType.P2SHSegwit, walletName),
client.getRawChangeAddress(AddressType.Legacy, walletName)
)
Future.sequence(addrFuts)
}
} yield succeed
it should "be able to get a new raw change address" in { nodePair =>
val client = nodePair.node2
val addrFuts =
List(
client.getRawChangeAddress(walletName),
client.getRawChangeAddress(AddressType.Bech32, walletName),
client.getRawChangeAddress(AddressType.P2SHSegwit, walletName),
client.getRawChangeAddress(AddressType.Legacy, walletName)
)
Future.sequence(addrFuts).map(_ => succeed)
}
it should "be able to get the amount recieved by some address" in {
for {
client <- walletClientF
address <- client.getNewAddress(Some(walletName))
amount <-
client.getReceivedByAddress(address, walletNameOpt = Some(walletName))
} yield assert(amount == Bitcoins(0))
nodePair =>
val client = nodePair.node2
for {
address <- client.getNewAddress(Some(walletName))
amount <-
client.getReceivedByAddress(address, walletNameOpt = Some(walletName))
} yield assert(amount == Bitcoins(0))
}
it should "be able to get the unconfirmed balance" in {
it should "be able to get the unconfirmed balance" in { nodePair =>
val client = nodePair.node2
for {
client <- walletClientF
balance <- client.getUnconfirmedBalance(walletName)
} yield {
assert(balance == Bitcoins(0))
}
}
it should "be able to get the wallet info" in {
it should "be able to get the wallet info" in { nodePair =>
val client = nodePair.node2
for {
client <- walletClientF
info <- client.getWalletInfo(walletName)
} yield {
assert(info.balance.toBigDecimal > 0)
@ -181,9 +191,9 @@ class MultiWalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to refill the keypool" in {
it should "be able to refill the keypool" in { nodePair =>
val client = nodePair.node2
for {
client <- walletClientF
_ <- client.walletPassphrase(password, 1000, Some(walletName))
info <- client.getWalletInfo(walletName)
_ <- client.keyPoolRefill(info.keypoolsize + 1, Some(walletName))
@ -191,11 +201,11 @@ class MultiWalletRpcTest extends BitcoindRpcTest {
} yield assert(newInfo.keypoolsize == info.keypoolsize + 1)
}
it should "be able to change the wallet password" in {
it should "be able to change the wallet password" in { nodePair =>
val walletClient = nodePair.node2
val newPass = "new_password"
for {
walletClient <- walletClientF
_ <- walletClient.walletLock(walletName)
_ <-
walletClient.walletPassphraseChange(password, newPass, Some(walletName))
@ -213,10 +223,10 @@ class MultiWalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to send to an address" in {
it should "be able to send to an address" in { nodePair =>
val otherClient = nodePair.node1
val client = nodePair.node2
for {
client <- walletClientF
(otherClient, _, _) <- clientsF
address <- otherClient.getNewAddress(Some(walletName))
_ <- client.walletPassphrase(password, 1000, Some(walletName))
txid <- client.sendToAddress(address,
@ -230,10 +240,10 @@ class MultiWalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to send btc to many addresses" in {
it should "be able to send btc to many addresses" in { nodePair =>
val otherClient = nodePair.node1
val client = nodePair.node2
for {
client <- walletClientF
(otherClient, _, _) <- clientsF
address1 <- otherClient.getNewAddress(Some(walletName))
address2 <- otherClient.getNewAddress(Some(walletName))
_ <- client.walletPassphrase(password, 1000, Some(walletName))
@ -250,9 +260,9 @@ class MultiWalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to get the balance" in {
it should "be able to get the balance" in { nodePair =>
val client = nodePair.node2
for {
client <- walletClientF
balance <- client.getBalance(walletName)
_ <-
client
@ -265,21 +275,21 @@ class MultiWalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to dump a private key" in {
it should "be able to dump a private key" in { nodePair =>
val client = nodePair.node2
for {
client <- walletClientF
address <- client.getNewAddress(Some(walletName))
_ <- client.dumpPrivKey(address, Some(walletName))
} yield succeed
}
it should "be able to import a private key" in {
it should "be able to import a private key" in { nodePair =>
val client = nodePair.node2
val ecPrivateKey = ECPrivateKey.freshPrivateKey
val publicKey = ecPrivateKey.publicKey
val address = P2PKHAddress(publicKey, networkParam)
for {
client <- walletClientF
_ <- client.importPrivKey(ecPrivateKey,
rescan = false,
walletNameOpt = Some(walletName))
@ -302,52 +312,53 @@ class MultiWalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to import a public key" in {
it should "be able to import a public key" in { nodePair =>
val client = nodePair.node2
val pubKey = ECPublicKey.freshPublicKey
for {
client <- walletClientF
_ <- client.importPubKey(pubKey, walletNameOpt = Some(walletName))
} yield succeed
}
it should "be able to import multiple addresses with importMulti" in {
val privKey = ECPrivateKey.freshPrivateKey
val address1 = P2PKHAddress(privKey.publicKey, networkParam)
nodePair =>
val client = nodePair.node2
val privKey = ECPrivateKey.freshPrivateKey
val address1 = P2PKHAddress(privKey.publicKey, networkParam)
val privKey1 = ECPrivateKey.freshPrivateKey
val privKey2 = ECPrivateKey.freshPrivateKey
val privKey1 = ECPrivateKey.freshPrivateKey
val privKey2 = ECPrivateKey.freshPrivateKey
for {
client <- walletClientF
firstResult <-
client
.createMultiSig(2,
Vector(privKey1.publicKey, privKey2.publicKey),
walletNameOpt = Some(walletName))
address2 = firstResult.address
for {
firstResult <-
client
.createMultiSig(2,
Vector(privKey1.publicKey, privKey2.publicKey),
walletNameOpt = Some(walletName))
address2 = firstResult.address
secondResult <-
client
.importMulti(
Vector(
RpcOpts.ImportMultiRequest(RpcOpts.ImportMultiAddress(address1),
UInt32(0)),
RpcOpts.ImportMultiRequest(RpcOpts.ImportMultiAddress(address2),
UInt32(0))),
rescan = false,
walletNameOpt = Some(walletName)
)
} yield {
assert(secondResult.length == 2)
assert(secondResult(0).success)
assert(secondResult(1).success)
}
secondResult <-
client
.importMulti(
Vector(
RpcOpts.ImportMultiRequest(RpcOpts.ImportMultiAddress(address1),
UInt32(0)),
RpcOpts.ImportMultiRequest(RpcOpts.ImportMultiAddress(address2),
UInt32(0))),
rescan = false,
walletNameOpt = Some(walletName)
)
} yield {
assert(secondResult.length == 2)
assert(secondResult(0).success)
assert(secondResult(1).success)
}
}
it should "be able to import a wallet" in {
it should "be able to import a wallet" in { nodePair =>
val client = nodePair.node2
val walletClient = client
for {
client <- walletClientF
walletClient <- walletClientF
address <- client.getNewAddress(Some(walletName))
walletFile =
client.getDaemon.datadir.getAbsolutePath + "/client_wallet.dat"
@ -361,9 +372,9 @@ class MultiWalletRpcTest extends BitcoindRpcTest {
}
it should "be able to set the tx fee" in {
it should "be able to set the tx fee" in { nodePair =>
val client = nodePair.node2
for {
client <- walletClientF
success <- client.setTxFee(Bitcoins(0.01), Some(walletName))
info <- client.getWalletInfo(walletName)
} yield {
@ -372,10 +383,10 @@ class MultiWalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to sign a raw transaction with the wallet" in {
it should "be able to sign a raw transaction with the wallet" in { nodePair =>
val otherClient = nodePair.node1
val client = nodePair.node2
for {
client <- walletClientF
(otherClient, _, _) <- clientsF
address <- otherClient.getNewAddress(Some(walletName))
transactionWithoutFunds <-
client
@ -397,4 +408,8 @@ class MultiWalletRpcTest extends BitcoindRpcTest {
TransactionOutput(Bitcoins(1), address.scriptPubKey)))
}
}
def startClient(client: BitcoindRpcClient): Future[Unit] = {
BitcoindRpcTestUtil.startServers(Vector(client))
}
}

View File

@ -0,0 +1,41 @@
package org.bitcoins.rpc.common
import org.bitcoins.asyncutil.AsyncUtil
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.AddNodeArgument
import org.bitcoins.testkit.rpc.{
BitcoindFixturesCachedPairV17,
BitcoindRpcTestUtil
}
class PreciousBlockRpcTest extends BitcoindFixturesCachedPairV17 {
it should "be able to mark a block as precious" in { nodePair =>
val freshClient = nodePair.node1
val otherFreshClient = nodePair.node2
for {
_ <- freshClient.disconnectNode(otherFreshClient.getDaemon.uri)
_ <- BitcoindRpcTestUtil.awaitDisconnected(freshClient, otherFreshClient)
blocks1 <-
freshClient.getNewAddress.flatMap(freshClient.generateToAddress(1, _))
blocks2 <- otherFreshClient.getNewAddress.flatMap(
otherFreshClient.generateToAddress(1, _))
bestHash1 <- freshClient.getBestBlockHash
_ = assert(bestHash1 == blocks1.head)
bestHash2 <- otherFreshClient.getBestBlockHash
_ = assert(bestHash2 == blocks2.head)
_ <-
freshClient
.addNode(otherFreshClient.getDaemon.uri, AddNodeArgument.OneTry)
_ <- AsyncUtil.retryUntilSatisfiedF(() =>
BitcoindRpcTestUtil.hasSeenBlock(otherFreshClient, bestHash1))
_ <- otherFreshClient.preciousBlock(bestHash1)
newBestHash <- otherFreshClient.getBestBlockHash
} yield assert(newBestHash == bestHash1)
}
}

View File

@ -21,29 +21,35 @@ import org.bitcoins.core.wallet.utxo.{ECSignatureParams, P2WPKHV0InputInfo}
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPrivateKey, ECPublicKey}
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
import org.bitcoins.rpc.util.RpcUtil
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.{AkkaUtil, BitcoindRpcTest}
import org.bitcoins.testkit.rpc.{
BitcoindFixturesCachedPairNewest,
BitcoindRpcTestUtil
}
import org.bitcoins.testkit.util.AkkaUtil
import org.scalatest.{FutureOutcome, Outcome}
import java.io.File
import java.util.Scanner
import scala.annotation.nowarn
import scala.async.Async.{async, await}
import scala.concurrent.duration.DurationInt
import scala.concurrent.Future
import scala.concurrent.{Await, Future}
import scala.reflect.io.Directory
@nowarn
class WalletRpcTest extends BitcoindRpcTest {
class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
lazy val clientsF: Future[
(BitcoindRpcClient, BitcoindRpcClient, BitcoindRpcClient)] =
BitcoindRpcTestUtil.createNodeTripleV19(clientAccum = clientAccum)
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val f: Future[Outcome] = for {
clients <- clientsF
futOutcome = with2BitcoindsCached(test, clients)
fut <- futOutcome.toFuture
} yield fut
new FutureOutcome(f)
}
// This client's wallet is encrypted
lazy val walletClientF: Future[BitcoindRpcClient] = clientsF.flatMap { _ =>
val walletClient =
BitcoindRpcClient.withActorSystem(BitcoindRpcTestUtil.instance())
clientAccum += walletClient
for {
_ <- startClient(walletClient)
@ -63,9 +69,9 @@ class WalletRpcTest extends BitcoindRpcTest {
behavior of "WalletRpc"
it should "be able to dump the wallet" in {
it should "be able to dump the wallet" in { nodePair: FixtureParam =>
val client = nodePair.node1
for {
(client, _, _) <- clientsF
result <- {
val datadir = client.getDaemon.datadir.getAbsolutePath
client.dumpWallet(datadir + "/test.dat")
@ -76,9 +82,9 @@ class WalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to list wallets" in {
it should "be able to list wallets" in { nodePair: FixtureParam =>
val client = nodePair.node1
for {
(client, _, _) <- clientsF
wallets <- client.listWallets
} yield {
@ -90,9 +96,9 @@ class WalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to backup the wallet" in {
it should "be able to backup the wallet" in { nodePair: FixtureParam =>
val client = nodePair.node1
for {
(client, _, _) <- clientsF
_ <- {
val datadir = client.getDaemon.datadir.getAbsolutePath
client.backupWallet(datadir + "/backup.dat")
@ -105,7 +111,7 @@ class WalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to lock and unlock the wallet" in {
it should "be able to lock and unlock the wallet" in { _: FixtureParam =>
for {
walletClient <- walletClientF
_ <- walletClient.walletLock()
@ -122,58 +128,63 @@ class WalletRpcTest extends BitcoindRpcTest {
}
it should "be able to get an address from bitcoind" in {
for {
(client, _, _) <- clientsF
_ <- {
val addrFuts =
List(client.getNewAddress,
client.getNewAddress(AddressType.Bech32),
client.getNewAddress(AddressType.P2SHSegwit),
client.getNewAddress(AddressType.Legacy))
Future.sequence(addrFuts)
}
} yield succeed
nodePair: FixtureParam =>
val client = nodePair.node1
for {
_ <- {
val addrFuts =
List(client.getNewAddress,
client.getNewAddress(AddressType.Bech32),
client.getNewAddress(AddressType.P2SHSegwit),
client.getNewAddress(AddressType.Legacy))
Future.sequence(addrFuts)
}
} yield succeed
}
it should "be able to get a new raw change address" in {
for {
(client, _, _) <- clientsF
_ <- {
val addrFuts =
List(
client.getRawChangeAddress,
client.getRawChangeAddress(AddressType.Legacy),
client.getRawChangeAddress(AddressType.Bech32),
client.getRawChangeAddress(AddressType.P2SHSegwit)
)
Future.sequence(addrFuts)
}
} yield succeed
nodePair: FixtureParam =>
val client = nodePair.node1
for {
_ <- {
val addrFuts =
List(
client.getRawChangeAddress,
client.getRawChangeAddress(AddressType.Legacy),
client.getRawChangeAddress(AddressType.Bech32),
client.getRawChangeAddress(AddressType.P2SHSegwit)
)
Future.sequence(addrFuts)
}
} yield succeed
}
it should "be able to get the amount recieved by some address" in {
for {
(client, _, _) <- clientsF
address <- client.getNewAddress
amount <- client.getReceivedByAddress(address)
} yield assert(amount == Bitcoins(0))
nodePair: FixtureParam =>
val client = nodePair.node1
for {
address <- client.getNewAddress
amount <- client.getReceivedByAddress(address)
} yield assert(amount == Bitcoins(0))
}
it should "be able to get the unconfirmed balance" in {
for {
(client, _, _) <- clientsF
balance <- client.getUnconfirmedBalance
transaction <- BitcoindRpcTestUtil.sendCoinbaseTransaction(client, client)
newBalance <- client.getUnconfirmedBalance
} yield {
assert(balance == Bitcoins(0))
assert(newBalance == transaction.amount)
}
nodePair: FixtureParam =>
val client = nodePair.node1
for {
balance <- client.getUnconfirmedBalance
transaction <- BitcoindRpcTestUtil.sendCoinbaseTransaction(client,
client)
newBalance <- client.getUnconfirmedBalance
} yield {
assert(balance == Bitcoins(0))
assert(newBalance == transaction.amount)
}
}
it should "be able to get the wallet info" in {
it should "be able to get the wallet info" in { nodePair: FixtureParam =>
val client = nodePair.node1
for {
(client, _, _) <- clientsF
info <- client.getWalletInfo
} yield {
assert(info.balance.toBigDecimal > 0)
@ -183,16 +194,16 @@ class WalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to refill the keypool" in {
it should "be able to refill the keypool" in { nodePair: FixtureParam =>
val client = nodePair.node1
for {
(client, _, _) <- clientsF
info <- client.getWalletInfo
_ <- client.keyPoolRefill(info.keypoolsize + 1)
newInfo <- client.getWalletInfo
} yield assert(newInfo.keypoolsize == info.keypoolsize + 1)
}
it should "be able to change the wallet password" in {
it should "be able to change the wallet password" in { _: FixtureParam =>
val newPass = "new_password"
for {
@ -215,44 +226,9 @@ class WalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to import funds without rescan and then remove them" in async {
val (client, otherClient, thirdClient) = await(clientsF)
val address = await(thirdClient.getNewAddress)
val privKey = await(thirdClient.dumpPrivKey(address))
val txidF =
BitcoindRpcTestUtil
.fundBlockChainTransaction(client, thirdClient, address, Bitcoins(1.5))
val txid = await(txidF)
await(client.getNewAddress.flatMap(client.generateToAddress(1, _)))
val tx = await(client.getTransaction(txid))
val proof = await(client.getTxOutProof(Vector(txid)))
val balanceBefore = await(otherClient.getBalance)
await(otherClient.importPrivKey(privKey, rescan = false))
await(otherClient.importPrunedFunds(tx.hex, proof))
val balanceAfter = await(otherClient.getBalance)
assert(balanceAfter == balanceBefore + Bitcoins(1.5))
val addressInfo = await(otherClient.validateAddress(address))
if (otherClient.instance.getVersion == BitcoindVersion.V16) {
assert(addressInfo.ismine.contains(true))
}
await(otherClient.removePrunedFunds(txid))
val balance = await(otherClient.getBalance)
assert(balance == balanceBefore)
}
it should "be able to list address groupings" in {
it should "be able to list address groupings" in { nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
val amount = Bitcoins(1.25)
def getChangeAddressAndAmount(
@ -274,7 +250,6 @@ class WalletRpcTest extends BitcoindRpcTest {
}
for {
(client, otherClient, _) <- clientsF
groupingsBefore <- client.listAddressGroupings
address <- client.getNewAddress
@ -316,9 +291,10 @@ class WalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to send to an address" in {
it should "be able to send to an address" in { nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
(client, otherClient, _) <- clientsF
address <- otherClient.getNewAddress
txid <- client.sendToAddress(address, Bitcoins(1))
transaction <- client.getTransaction(txid)
@ -329,51 +305,55 @@ class WalletRpcTest extends BitcoindRpcTest {
}
it should "be able to send btc to many addresses" in {
for {
(client, otherClient, _) <- clientsF
address1 <- otherClient.getNewAddress
address2 <- otherClient.getNewAddress
txid <-
client
.sendMany(Map(address1 -> Bitcoins(1), address2 -> Bitcoins(2)))
transaction <- client.getTransaction(txid)
} yield {
assert(transaction.amount == Bitcoins(-3))
assert(transaction.details.exists(_.address.contains(address1)))
assert(transaction.details.exists(_.address.contains(address2)))
}
nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
address1 <- otherClient.getNewAddress
address2 <- otherClient.getNewAddress
txid <-
client
.sendMany(Map(address1 -> Bitcoins(1), address2 -> Bitcoins(2)))
transaction <- client.getTransaction(txid)
} yield {
assert(transaction.amount == Bitcoins(-3))
assert(transaction.details.exists(_.address.contains(address1)))
assert(transaction.details.exists(_.address.contains(address2)))
}
}
it should "be able to list transactions by receiving addresses" in {
for {
(client, otherClient, _) <- clientsF
address <- otherClient.getNewAddress
txid <-
BitcoindRpcTestUtil
.fundBlockChainTransaction(client,
otherClient,
address,
Bitcoins(1.5))
receivedList <- otherClient.listReceivedByAddress()
} yield {
val entryList =
receivedList.filter(entry => entry.address == address)
assert(entryList.length == 1)
val entry = entryList.head
assert(entry.txids.head == txid)
assert(entry.address == address)
assert(entry.amount == Bitcoins(1.5))
assert(entry.confirmations == 1)
}
nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
address <- otherClient.getNewAddress
txid <-
BitcoindRpcTestUtil
.fundBlockChainTransaction(client,
otherClient,
address,
Bitcoins(1.5))
receivedList <- otherClient.listReceivedByAddress()
} yield {
val entryList =
receivedList.filter(entry => entry.address == address)
assert(entryList.length == 1)
val entry = entryList.head
assert(entry.txids.head == txid)
assert(entry.address == address)
assert(entry.amount == Bitcoins(1.5))
assert(entry.confirmations == 1)
}
}
it should "be able to import an address" in {
it should "be able to import an address" in { nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
val address = Bech32Address
.fromString("bcrt1q9h9wkz6ad49szfl035wh3qdacuslkp6j9pfp4j")
for {
(client, otherClient, _) <- clientsF
_ <- otherClient.importAddress(address)
txid <- BitcoindRpcTestUtil.fundBlockChainTransaction(client,
otherClient,
@ -392,9 +372,9 @@ class WalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to get the balance" in {
it should "be able to get the balance" in { nodePair: FixtureParam =>
val client = nodePair.node1
for {
(client, _, _) <- clientsF
balance <- client.getBalance
_ <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
newBalance <- client.getBalance
@ -404,21 +384,21 @@ class WalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to dump a private key" in {
it should "be able to dump a private key" in { nodePair: FixtureParam =>
val client = nodePair.node1
for {
(client, _, _) <- clientsF
address <- client.getNewAddress
_ <- client.dumpPrivKey(address)
} yield succeed
}
it should "be able to import a private key" in {
it should "be able to import a private key" in { nodePair: FixtureParam =>
val client = nodePair.node1
val ecPrivateKey = ECPrivateKey.freshPrivateKey
val publicKey = ecPrivateKey.publicKey
val address = P2PKHAddress(publicKey, networkParam)
for {
(client, _, _) <- clientsF
_ <- client.importPrivKey(ecPrivateKey, rescan = false)
key <- client.dumpPrivKey(address)
result <-
@ -438,48 +418,49 @@ class WalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to import a public key" in {
it should "be able to import a public key" in { nodePair: FixtureParam =>
val client = nodePair.node1
val pubKey = ECPublicKey.freshPublicKey
for {
(client, _, _) <- clientsF
_ <- client.importPubKey(pubKey)
} yield succeed
}
it should "be able to import multiple addresses with importMulti" in {
val privKey = ECPrivateKey.freshPrivateKey
val address1 = P2PKHAddress(privKey.publicKey, networkParam)
nodePair: FixtureParam =>
val client = nodePair.node1
val privKey = ECPrivateKey.freshPrivateKey
val address1 = P2PKHAddress(privKey.publicKey, networkParam)
val privKey1 = ECPrivateKey.freshPrivateKey
val privKey2 = ECPrivateKey.freshPrivateKey
val privKey1 = ECPrivateKey.freshPrivateKey
val privKey2 = ECPrivateKey.freshPrivateKey
for {
(client, _, _) <- clientsF
firstResult <-
client
.createMultiSig(2, Vector(privKey1.publicKey, privKey2.publicKey))
address2 = firstResult.address
for {
firstResult <-
client
.createMultiSig(2, Vector(privKey1.publicKey, privKey2.publicKey))
address2 = firstResult.address
secondResult <-
client
.importMulti(
Vector(
RpcOpts.ImportMultiRequest(RpcOpts.ImportMultiAddress(address1),
UInt32(0)),
RpcOpts.ImportMultiRequest(RpcOpts.ImportMultiAddress(address2),
UInt32(0))),
rescan = false
)
} yield {
assert(secondResult.length == 2)
assert(secondResult(0).success)
assert(secondResult(1).success)
}
secondResult <-
client
.importMulti(
Vector(
RpcOpts.ImportMultiRequest(RpcOpts.ImportMultiAddress(address1),
UInt32(0)),
RpcOpts.ImportMultiRequest(RpcOpts.ImportMultiAddress(address2),
UInt32(0))),
rescan = false
)
} yield {
assert(secondResult.length == 2)
assert(secondResult(0).success)
assert(secondResult(1).success)
}
}
it should "be able to import a wallet" in {
it should "be able to import a wallet" in { nodePair: FixtureParam =>
val client = nodePair.node1
for {
(client, _, _) <- clientsF
walletClient <- walletClientF
address <- client.getNewAddress
walletFile =
@ -493,11 +474,11 @@ class WalletRpcTest extends BitcoindRpcTest {
}
it should "be able to load a wallet" in {
it should "be able to load a wallet" in { nodePair: FixtureParam =>
val client = nodePair.node1
val name = "tmp_wallet"
for {
(client, _, _) <- clientsF
walletClient <- walletClientF
walletFile =
client.getDaemon.datadir.getAbsolutePath + s"/regtest/wallets/$name"
@ -514,9 +495,9 @@ class WalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to set the tx fee" in {
it should "be able to set the tx fee" in { nodePair: FixtureParam =>
val client = nodePair.node1
for {
(client, _, _) <- clientsF
success <- client.setTxFee(Bitcoins(0.01))
info <- client.getWalletInfo
} yield {
@ -525,9 +506,10 @@ class WalletRpcTest extends BitcoindRpcTest {
}
}
it should "be able to bump a mem pool tx fee" in {
it should "be able to bump a mem pool tx fee" in { nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
(client, otherClient, _) <- clientsF
address <- otherClient.getNewAddress
unspent <- client.listUnspent
changeAddress <- client.getRawChangeAddress
@ -555,65 +537,79 @@ class WalletRpcTest extends BitcoindRpcTest {
}
it should "be able to sign a raw transaction with the wallet" in {
for {
(client, otherClient, _) <- clientsF
address <- otherClient.getNewAddress
transactionWithoutFunds <-
client
.createRawTransaction(Vector.empty, Map(address -> Bitcoins(1)))
transactionResult <- client.fundRawTransaction(transactionWithoutFunds)
transaction = transactionResult.hex
singedTx <- client.signRawTransactionWithWallet(transaction).map(_.hex)
nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
address <- otherClient.getNewAddress
transactionWithoutFunds <-
client
.createRawTransaction(Vector.empty, Map(address -> Bitcoins(1)))
transactionResult <- client.fundRawTransaction(transactionWithoutFunds)
transaction = transactionResult.hex
singedTx <- client.signRawTransactionWithWallet(transaction).map(_.hex)
// Will throw error if invalid
_ <- client.sendRawTransaction(singedTx)
} yield {
assert(transaction.inputs.length == 1)
assert(
transaction.outputs.contains(
TransactionOutput(Bitcoins(1), address.scriptPubKey)))
}
// Will throw error if invalid
_ <- client.sendRawTransaction(singedTx)
} yield {
assert(transaction.inputs.length == 2)
assert(
transaction.outputs.contains(
TransactionOutput(Bitcoins(1), address.scriptPubKey)))
}
}
it should "generate the same (low R) signatures as bitcoin-s" in {
for {
(client, otherClient, _) <- clientsF
address <- otherClient.getNewAddress
transactionWithoutFunds <-
client
.createRawTransaction(Vector.empty, Map(address -> Bitcoins(1)))
transactionResult <- client.fundRawTransaction(transactionWithoutFunds)
transaction = transactionResult.hex
signedTx <- client.signRawTransactionWithWallet(transaction).map(_.hex)
nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
address <- otherClient.getNewAddress
transactionWithoutFunds <-
client
.createRawTransaction(Vector.empty, Map(address -> Bitcoins(1)))
transactionResult <- client.fundRawTransaction(transactionWithoutFunds)
transaction = transactionResult.hex
signedTx <- client.signRawTransactionWithWallet(transaction).map(_.hex)
// Validate signature against bitcoin-s generated one
outPoint = transaction.inputs.head.previousOutput
prevTx <- client.getRawTransactionRaw(outPoint.txIdBE)
output = prevTx.outputs(outPoint.vout.toInt)
privKey <- client.dumpPrivKey(
BitcoinAddress.fromScriptPubKey(output.scriptPubKey, RegTest))
partialSig <- BitcoinSigner.signSingle(
ECSignatureParams(
P2WPKHV0InputInfo(outPoint, output.value, privKey.publicKey),
prevTx,
privKey,
HashType.sigHashAll),
transaction,
isDummySignature = false)
} yield {
signedTx match {
case btx: NonWitnessTransaction =>
assert(
btx.inputs.head.scriptSignature.signatures.head == partialSig.signature)
case wtx: WitnessTransaction =>
wtx.witness.head match {
case p2wpkh: P2WPKHWitnessV0 =>
assert(p2wpkh.pubKey == partialSig.pubKey)
assert(p2wpkh.signature == partialSig.signature)
case _: P2WSHWitnessV0 | EmptyScriptWitness =>
fail("Expected P2WPKH")
}
// Validate signature against bitcoin-s generated one
outPoint = transaction.inputs.head.previousOutput
prevTx <- client.getRawTransactionRaw(outPoint.txIdBE)
output = prevTx.outputs(outPoint.vout.toInt)
privKey <- client.dumpPrivKey(
BitcoinAddress.fromScriptPubKey(output.scriptPubKey, RegTest))
partialSig <- BitcoinSigner.signSingle(
ECSignatureParams(
P2WPKHV0InputInfo(outPoint, output.value, privKey.publicKey),
prevTx,
privKey,
HashType.sigHashAll),
transaction,
isDummySignature = false)
} yield {
signedTx match {
case btx: NonWitnessTransaction =>
assert(
btx.inputs.head.scriptSignature.signatures.head == partialSig.signature)
case wtx: WitnessTransaction =>
wtx.witness.head match {
case p2wpkh: P2WPKHWitnessV0 =>
assert(p2wpkh.pubKey == partialSig.pubKey)
assert(p2wpkh.signature == partialSig.signature)
case _: P2WSHWitnessV0 | EmptyScriptWitness =>
fail("Expected P2WPKH")
}
}
}
}
}
def startClient(client: BitcoindRpcClient): Future[Unit] = {
BitcoindRpcTestUtil.startServers(Vector(client))
}
override def afterAll(): Unit = {
val stopF = walletClientF.map(BitcoindRpcTestUtil.stopServer)
Await.result(stopF, duration)
super.afterAll()
}
}

View File

@ -14,25 +14,21 @@ import org.bitcoins.core.protocol.transaction.{
}
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPrivateKey}
import org.bitcoins.rpc.client.common.BitcoindVersion
import org.bitcoins.rpc.client.v16.BitcoindV16RpcClient
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.{AkkaUtil, BitcoindRpcTest}
import org.bitcoins.testkit.rpc.{
BitcoindFixturesCachedPairV16,
BitcoindRpcTestUtil
}
import scala.async.Async.{async, await}
import scala.concurrent.Future
import scala.concurrent.duration.DurationInt
import scala.util.Properties
class BitcoindV16RpcClientTest extends BitcoindRpcTest {
lazy val clientsF: Future[(BitcoindV16RpcClient, BitcoindV16RpcClient)] =
BitcoindRpcTestUtil.createNodePairV16(clientAccum)
class BitcoindV16RpcClientTest extends BitcoindFixturesCachedPairV16 {
behavior of "BitcoindV16RpcClient"
it should "be able to get peer info" in {
it should "be able to get peer info" in { nodePair: FixtureParam =>
val freshClient = nodePair.node1
val otherFreshClient = nodePair.node2
for {
(freshClient, otherFreshClient) <- clientsF
infoList <- freshClient.getPeerInfo
} yield {
assert(infoList.length >= 0)
@ -42,18 +38,17 @@ class BitcoindV16RpcClientTest extends BitcoindRpcTest {
}
}
it should "be able to start a V16 bitcoind" in {
for {
(client, otherClient) <- clientsF
} yield {
assert(client.version == BitcoindVersion.V16)
assert(otherClient.version == BitcoindVersion.V16)
}
it should "be able to start a V16 bitcoind" in { nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
assert(client.version == BitcoindVersion.V16)
assert(otherClient.version == BitcoindVersion.V16)
}
it should "be able to sign a raw transaction" in {
it should "be able to sign a raw transaction" in { nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
(client, otherClient) <- clientsF
addr <- client.getNewAddress
_ <- otherClient.sendToAddress(addr, Bitcoins.one)
_ <-
@ -88,154 +83,106 @@ class BitcoindV16RpcClientTest extends BitcoindRpcTest {
// copied form the equivalent test in BitcoindV17RpcClientTest
it should "be able to sign a raw transaction with private keys" in {
val privkeys: List[ECPrivateKey] =
List("cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N",
"cVKpPfVKSJxKqVpE9awvXNWuLHCa5j5tiE7K6zbUSptFpTEtiFrA")
.map(ECPrivateKeyUtil.fromWIFToPrivateKey)
nodePair: FixtureParam =>
val client = nodePair.node1
val privkeys: List[ECPrivateKey] =
List("cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N",
"cVKpPfVKSJxKqVpE9awvXNWuLHCa5j5tiE7K6zbUSptFpTEtiFrA")
.map(ECPrivateKeyUtil.fromWIFToPrivateKey)
val txids =
List("9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71",
"83a4f6a6b73660e13ee6cb3c6063fa3759c50c9b7521d0536022961898f4fb02")
.map(DoubleSha256DigestBE.fromHex)
val txids =
List("9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71",
"83a4f6a6b73660e13ee6cb3c6063fa3759c50c9b7521d0536022961898f4fb02")
.map(DoubleSha256DigestBE.fromHex)
val vouts = List(0, 0)
val vouts = List(0, 0)
val inputs: Vector[TransactionInput] = txids
.zip(vouts)
.map { case (txid, vout) =>
TransactionInput.fromTxidAndVout(txid, UInt32(vout))
val inputs: Vector[TransactionInput] = txids
.zip(vouts)
.map { case (txid, vout) =>
TransactionInput.fromTxidAndVout(txid, UInt32(vout))
}
.toVector
val address =
BitcoinAddress.fromString("mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB")
val outputs: Map[BitcoinAddress, Bitcoins] =
Map(address -> Bitcoins(0.1))
val scriptPubKeys =
List("76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac",
"76a914669b857c03a5ed269d5d85a1ffac9ed5d663072788ac")
.map(ScriptPubKey.fromAsmHex)
val utxoDeps = inputs.zip(scriptPubKeys).map { case (input, pubKey) =>
SignRawTransactionOutputParameter.fromTransactionInput(input, pubKey)
}
.toVector
val address =
BitcoinAddress.fromString("mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB")
val outputs: Map[BitcoinAddress, Bitcoins] =
Map(address -> Bitcoins(0.1))
val scriptPubKeys =
List("76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac",
"76a914669b857c03a5ed269d5d85a1ffac9ed5d663072788ac")
.map(ScriptPubKey.fromAsmHex)
val utxoDeps = inputs.zip(scriptPubKeys).map { case (input, pubKey) =>
SignRawTransactionOutputParameter.fromTransactionInput(input, pubKey)
}
for {
(client, _) <- clientsF
rawTx <- client.createRawTransaction(inputs, outputs)
signed <- client.signRawTransaction(rawTx, utxoDeps, privkeys.toVector)
} yield assert(signed.complete)
for {
rawTx <- client.createRawTransaction(inputs, outputs)
signed <- client.signRawTransaction(rawTx, utxoDeps, privkeys.toVector)
} yield assert(signed.complete)
}
it should "be able to send from an account to an addresss" in {
for {
(client, otherClient) <- clientsF
address <- otherClient.getNewAddress
txid <- client.sendFrom("", address, Bitcoins(1))
transaction <- client.getTransaction(txid)
} yield {
assert(transaction.amount == Bitcoins(-1))
assert(transaction.details.head.address.contains(address))
}
}
it should "be able to get the amount received by an account and list amounts received by all accounts" in async {
val (client, otherClient) = await(clientsF)
val ourAccount = "another_new_account"
val emptyAccount = "empty_account"
val ourAccountAddress = await(client.getNewAddress(ourAccount))
await(
BitcoindRpcTestUtil
.fundBlockChainTransaction(otherClient,
client,
ourAccountAddress,
Bitcoins(1.5)))
val accountlessAddress = await(client.getNewAddress)
val sendAmt = Bitcoins(1.5)
val _ = await(
BitcoindRpcTestUtil
.fundBlockChainTransaction(otherClient,
client,
accountlessAddress,
sendAmt))
if (Properties.isMac) await(AkkaUtil.nonBlockingSleep(10.seconds))
val ourAccountAmount = await(client.getReceivedByAccount(ourAccount))
assert(ourAccountAmount == sendAmt)
val receivedByAccount = await(client.listReceivedByAccount())
val ourAccountOpt =
receivedByAccount
.find(_.account == ourAccount)
assert(ourAccountOpt.isDefined)
assert(ourAccountOpt.get.amount == Bitcoins(1.5))
val accountLessOpt =
receivedByAccount
.find(_.account == "")
assert(accountLessOpt.isDefined)
assert(accountLessOpt.get.amount > Bitcoins(0))
assert(!receivedByAccount.exists(_.account == emptyAccount))
val accounts = await(client.listAccounts())
assert(accounts(ourAccount) == Bitcoins(1.5))
assert(accounts("") > Bitcoins(0))
assert(!accounts.keySet.contains(emptyAccount))
nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
address <- otherClient.getNewAddress
txid <- client.sendFrom("", address, Bitcoins(1))
transaction <- client.getTransaction(txid)
} yield {
assert(transaction.amount == Bitcoins(-1))
assert(transaction.details.head.address.contains(address))
}
}
it should "be able to get and set the account for a given address" in {
val account1 = "account_1"
val account2 = "account_2"
for {
(client, _) <- clientsF
address <- client.getNewAddress(account1)
acc1 <- client.getAccount(address)
_ <- client.setAccount(address, account2)
acc2 <- client.getAccount(address)
} yield {
assert(acc1 == account1)
assert(acc2 == account2)
}
nodePair: FixtureParam =>
val client = nodePair.node1
val account1 = "account_1"
val account2 = "account_2"
for {
address <- client.getNewAddress(account1)
acc1 <- client.getAccount(address)
_ <- client.setAccount(address, account2)
acc2 <- client.getAccount(address)
} yield {
assert(acc1 == account1)
assert(acc2 == account2)
}
}
it should "be able to get all addresses belonging to an account" in {
for {
(client, _) <- clientsF
address <- client.getNewAddress
addresses <- client.getAddressesByAccount("")
} yield assert(addresses.contains(address))
nodePair: FixtureParam =>
val client = nodePair.node1
for {
address <- client.getNewAddress
addresses <- client.getAddressesByAccount("")
} yield assert(addresses.contains(address))
}
it should "be able to get an account's address" in {
it should "be able to get an account's address" in { nodePair: FixtureParam =>
val client = nodePair.node1
val account = "a_new_account"
for {
(client, _) <- clientsF
address <- client.getAccountAddress(account)
result <- client.getAccount(address)
} yield assert(result == account)
}
it should "be able to move funds from one account to another" in {
val account = "move_account"
for {
(client, _) <- clientsF
success <- client.move("", account, Bitcoins(1))
map <- client.listAccounts()
} yield {
assert(success)
assert(map(account) == Bitcoins(1))
}
nodePair: FixtureParam =>
val client = nodePair.node1
val account = "move_account"
for {
success <- client.move("", account, Bitcoins(1))
map <- client.listAccounts()
} yield {
assert(success)
assert(map(account) == Bitcoins(1))
}
}
}

View File

@ -14,24 +14,23 @@ import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.transaction.TransactionInput
import org.bitcoins.crypto.DoubleSha256DigestBE
import org.bitcoins.rpc.client.v17.BitcoindV17RpcClient
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.BitcoindRpcTest
import org.bitcoins.rpc.util.NodePair
import org.bitcoins.testkit.rpc.{
BitcoindFixturesCachedPairV17,
BitcoindRpcTestUtil
}
import scala.concurrent.Future
class BitcoindV17RpcClientTest extends BitcoindRpcTest {
class BitcoindV17RpcClientTest extends BitcoindFixturesCachedPairV17 {
val usedLabel = "used_label"
val unusedLabel = "unused_label"
val clientsF: Future[(BitcoindV17RpcClient, BitcoindV17RpcClient)] =
BitcoindRpcTestUtil.createNodePairV17(clientAccum)
behavior of "BitcoindV17RpcClient"
it should "be able to get peer info" in {
it should "be able to get peer info" in { nodePair: FixtureParam =>
val NodePair(freshClient, otherFreshClient) = nodePair
for {
(freshClient, otherFreshClient) <- clientsF
infoList <- freshClient.getPeerInfo
} yield {
assert(infoList.length >= 0)
@ -41,9 +40,16 @@ class BitcoindV17RpcClientTest extends BitcoindRpcTest {
}
}
it should "test mempool acceptance" in {
it must "have our BitcoindRpcClient work with .hashCode() and equals" in {
nodePair =>
val NodePair(client1, client2) = nodePair
assert(client1 != client2)
assert(client1.hashCode() != client2.hashCode())
}
it should "test mempool acceptance" in { nodePair: FixtureParam =>
val NodePair(client, otherClient) = nodePair
for {
(client, otherClient) <- clientsF
tx <-
BitcoindRpcTestUtil.createRawCoinbaseTransaction(client, otherClient)
acceptance <- client.testMempoolAccept(tx)
@ -53,149 +59,154 @@ class BitcoindV17RpcClientTest extends BitcoindRpcTest {
}
it should "sign a raw transaction with wallet keys" in {
for {
(client, otherClient) <- clientsF
rawTx <-
BitcoindRpcTestUtil.createRawCoinbaseTransaction(client, otherClient)
signedTx <- client.signRawTransactionWithWallet(rawTx)
} yield assert(signedTx.complete)
nodePair: FixtureParam =>
val NodePair(client, otherClient) = nodePair
for {
rawTx <-
BitcoindRpcTestUtil.createRawCoinbaseTransaction(client, otherClient)
signedTx <- client.signRawTransactionWithWallet(rawTx)
} yield assert(signedTx.complete)
}
// copied from Bitcoin Core: https://github.com/bitcoin/bitcoin/blob/fa6180188b8ab89af97860e6497716405a48bab6/test/functional/rpc_signrawtransaction.py
it should "sign a raw transaction with private keys" in {
val privkeys =
List("cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N",
"cVKpPfVKSJxKqVpE9awvXNWuLHCa5j5tiE7K6zbUSptFpTEtiFrA")
.map(ECPrivateKeyUtil.fromWIFToPrivateKey)
nodePair: FixtureParam =>
val NodePair(client, _) = nodePair
val privkeys =
List("cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N",
"cVKpPfVKSJxKqVpE9awvXNWuLHCa5j5tiE7K6zbUSptFpTEtiFrA")
.map(ECPrivateKeyUtil.fromWIFToPrivateKey)
val txids =
List("9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71",
"83a4f6a6b73660e13ee6cb3c6063fa3759c50c9b7521d0536022961898f4fb02")
.map(DoubleSha256DigestBE.fromHex)
val txids =
List("9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71",
"83a4f6a6b73660e13ee6cb3c6063fa3759c50c9b7521d0536022961898f4fb02")
.map(DoubleSha256DigestBE.fromHex)
val vouts = List(0, 0)
val vouts = List(0, 0)
val inputs: Vector[TransactionInput] = txids
.zip(vouts)
.map { case (txid, vout) =>
TransactionInput.fromTxidAndVout(txid, UInt32(vout))
val inputs: Vector[TransactionInput] = txids
.zip(vouts)
.map { case (txid, vout) =>
TransactionInput.fromTxidAndVout(txid, UInt32(vout))
}
.toVector
val address =
BitcoinAddress.fromString("mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB")
val outputs: Map[BitcoinAddress, Bitcoins] =
Map(address -> Bitcoins(0.1))
val scriptPubKeys =
List("76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac",
"76a914669b857c03a5ed269d5d85a1ffac9ed5d663072788ac")
.map(ScriptPubKey.fromAsmHex)
val utxoDeps = inputs.zip(scriptPubKeys).map { case (input, pubKey) =>
SignRawTransactionOutputParameter.fromTransactionInput(input, pubKey)
}
.toVector
val address =
BitcoinAddress.fromString("mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB")
val outputs: Map[BitcoinAddress, Bitcoins] =
Map(address -> Bitcoins(0.1))
val scriptPubKeys =
List("76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac",
"76a914669b857c03a5ed269d5d85a1ffac9ed5d663072788ac")
.map(ScriptPubKey.fromAsmHex)
val utxoDeps = inputs.zip(scriptPubKeys).map { case (input, pubKey) =>
SignRawTransactionOutputParameter.fromTransactionInput(input, pubKey)
}
for {
(client, _) <- clientsF
rawTx <- client.createRawTransaction(inputs, outputs)
signed <-
client.signRawTransactionWithKey(rawTx, privkeys.toVector, utxoDeps)
} yield assert(signed.complete)
for {
rawTx <- client.createRawTransaction(inputs, outputs)
signed <-
client.signRawTransactionWithKey(rawTx, privkeys.toVector, utxoDeps)
} yield assert(signed.complete)
}
it should "be able to get the address info for a given address" in {
for {
(client, _) <- clientsF
addr <- client.getNewAddress
info <- client.getAddressInfo(addr)
} yield assert(info.address == addr)
nodePair: FixtureParam =>
val NodePair(client, _) = nodePair
for {
addr <- client.getNewAddress
info <- client.getAddressInfo(addr)
} yield assert(info.address == addr)
}
it should "be able to get the address info for a given P2SHSegwit address" in {
for {
(client, _) <- clientsF
addr <- client.getNewAddress(addressType = AddressType.P2SHSegwit)
info <- client.getAddressInfo(addr)
} yield assert(info.address == addr)
nodePair: FixtureParam =>
val NodePair(client, _) = nodePair
for {
addr <- client.getNewAddress(addressType = AddressType.P2SHSegwit)
info <- client.getAddressInfo(addr)
} yield assert(info.address == addr)
}
it should "be able to get the address info for a given Legacy address" in {
for {
(client, _) <- clientsF
addr <- client.getNewAddress(addressType = AddressType.Legacy)
info <- client.getAddressInfo(addr)
} yield assert(info.address == addr)
nodePair: FixtureParam =>
val NodePair(client, _) = nodePair
for {
addr <- client.getNewAddress(addressType = AddressType.Legacy)
info <- client.getAddressInfo(addr)
} yield assert(info.address == addr)
}
// needs #360 to be merged
it should "be able to get the address info for a given Bech32 address" in {
for {
(client, _) <- clientsF
addr <- client.getNewAddress(AddressType.Bech32)
info <- client.getAddressInfo(addr)
} yield {
assert(info.address.networkParameters == RegTest)
assert(info.address == addr)
}
nodePair: FixtureParam =>
val NodePair(client, _) = nodePair
for {
addr <- client.getNewAddress(AddressType.Bech32)
info <- client.getAddressInfo(addr)
} yield {
assert(info.address.networkParameters == RegTest)
assert(info.address == addr)
}
}
it should "be able to get the amount received by a label" in {
for {
(client, otherClient) <- clientsF
address <- client.getNewAddress(usedLabel)
_ <-
BitcoindRpcTestUtil
.fundBlockChainTransaction(client,
otherClient,
address,
Bitcoins(1.5))
nodePair: FixtureParam =>
val NodePair(client, otherClient) = nodePair
for {
address <- client.getNewAddress(usedLabel)
_ <-
BitcoindRpcTestUtil
.fundBlockChainTransaction(client,
otherClient,
address,
Bitcoins(1.5))
amount <- client.getReceivedByLabel(usedLabel)
} yield assert(amount == Bitcoins(1.5))
amount <- client.getReceivedByLabel(usedLabel)
} yield assert(amount == Bitcoins(1.5))
}
it should "list all labels" in {
it should "list all labels" in { nodePair: FixtureParam =>
val NodePair(client, _) = nodePair
for {
(client, _) <- clientsF
_ <- client.listLabels()
} yield succeed
}
it should "list all labels with purposes" in {
clientsF.flatMap { case (client, otherClient) =>
val sendLabel = "sendLabel"
it should "list all labels with purposes" in { nodePair: FixtureParam =>
val NodePair(client, otherClient) = nodePair
val sendLabel = "sendLabel"
val isImportDone = () =>
client.ping().map(_ => true).recover {
case exc if exc.getMessage.contains("rescanning") => false
case exc =>
logger.error(s"throwing $exc")
throw exc
}
def importTx(n: Int): Future[Unit] =
for {
address <- otherClient.getNewAddress
_ <- client.importAddress(address, sendLabel + n)
_ <- AsyncUtil.retryUntilSatisfiedF(isImportDone)
} yield ()
val isImportDone = () =>
client.ping().map(_ => true).recover {
case exc if exc.getMessage.contains("rescanning") => false
case exc =>
logger.error(s"throwing $exc")
throw exc
}
def importTx(n: Int): Future[Unit] =
for {
_ <- importTx(0)
_ <- importTx(1)
receiveLabels <- client.listLabels(Some(LabelPurpose.Receive))
sendLabels <- client.listLabels(Some(LabelPurpose.Send))
} yield assert(receiveLabels != sendLabels)
}
address <- otherClient.getNewAddress
_ <- client.importAddress(address, sendLabel + n)
_ <- AsyncUtil.retryUntilSatisfiedF(isImportDone)
} yield ()
for {
_ <- importTx(0)
_ <- importTx(1)
receiveLabels <- client.listLabels(Some(LabelPurpose.Receive))
sendLabels <- client.listLabels(Some(LabelPurpose.Send))
} yield assert(receiveLabels != sendLabels)
}
it should "set labels" in {
it should "set labels" in { nodePair: FixtureParam =>
val NodePair(client, otherClient) = nodePair
val l = "setLabel"
val btc = Bitcoins(1)
for {
(client, otherClient) <- clientsF
addr <- client.getNewAddress
_ <- BitcoindRpcTestUtil.fundBlockChainTransaction(otherClient,
client,
@ -214,9 +225,9 @@ class BitcoindV17RpcClientTest extends BitcoindRpcTest {
} yield assert(newAmount == btc)
}
it should "list amounts received by all labels" in {
it should "list amounts received by all labels" in { nodePair: FixtureParam =>
val NodePair(client, otherClient) = nodePair
for {
(client, otherClient) <- clientsF
addressWithLabel <- client.getNewAddress(usedLabel)
addressNoLabel <- client.getNewAddress
_ <- otherClient.sendToAddress(addressNoLabel, Bitcoins.one)
@ -244,9 +255,9 @@ class BitcoindV17RpcClientTest extends BitcoindRpcTest {
}
}
it should "create a wallet" in {
it should "create a wallet" in { nodePair: FixtureParam =>
val NodePair(client, _) = nodePair
for {
(client, _) <- clientsF
_ <- client.createWallet("suredbits")
wallets <- client.listWallets
} yield {

View File

@ -14,58 +14,60 @@ import org.bitcoins.core.protocol.transaction.{
}
import org.bitcoins.core.psbt.PSBT
import org.bitcoins.rpc.client.v17.BitcoindV17RpcClient
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.BitcoindRpcTest
import org.bitcoins.rpc.util.NodePair
import org.bitcoins.testkit.rpc.{
BitcoindFixturesCachedPairV17,
BitcoindRpcTestUtil
}
import scala.concurrent.Future
class PsbtRpcTest extends BitcoindRpcTest {
lazy val clientsF: Future[
(BitcoindV17RpcClient, BitcoindV17RpcClient, BitcoindV17RpcClient)] = {
BitcoindRpcTestUtil.createNodeTripleV17(clientAccum)
}
class PsbtRpcTest extends BitcoindFixturesCachedPairV17 {
behavior of "PsbtRpc"
// https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#Test_Vectors
it should "decode all the BIP174 example PSBTs" in {
val psbts = Vector(
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA",
"cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA",
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQMEAQAAAAAAAA==",
"cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=",
"cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA=",
"cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA=",
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==" // this one is from Core
).map(PSBT.fromBase64)
nodePair: NodePair[BitcoindV17RpcClient] =>
val client = nodePair.node1
val psbts = Vector(
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA",
"cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA",
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQMEAQAAAAAAAA==",
"cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=",
"cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA=",
"cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA=",
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==" // this one is from Core
).map(PSBT.fromBase64)
for {
(client, _, _) <- clientsF
_ <- Future.sequence(psbts.map(client.decodePsbt))
} yield succeed
for {
_ <- Future.sequence(psbts.map(client.decodePsbt))
} yield succeed
}
it should "convert raw TXs to PSBTs, process them and then decode them" in {
for {
(client, _, _) <- clientsF
address <- client.getNewAddress
rawTx <-
client.createRawTransaction(Vector.empty, Map(address -> Bitcoins.one))
fundedRawTx <- client.fundRawTransaction(rawTx)
psbt <- client.convertToPsbt(fundedRawTx.hex)
processedPsbt <- client.walletProcessPSBT(psbt)
decoded <- client.decodePsbt(processedPsbt.psbt)
} yield {
assert(decoded.inputs.exists(inputs =>
inputs.nonWitnessUtxo.isDefined || inputs.witnessUtxo.isDefined))
}
nodePair: FixtureParam =>
val client = nodePair.node1
for {
address <- client.getNewAddress
rawTx <-
client.createRawTransaction(Vector.empty,
Map(address -> Bitcoins.one))
fundedRawTx <- client.fundRawTransaction(rawTx)
psbt <- client.convertToPsbt(fundedRawTx.hex)
processedPsbt <- client.walletProcessPSBT(psbt)
decoded <- client.decodePsbt(processedPsbt.psbt)
} yield {
assert(decoded.inputs.exists(inputs =>
inputs.nonWitnessUtxo.isDefined || inputs.witnessUtxo.isDefined))
}
}
it should "finalize a simple PSBT" in {
it should "finalize a simple PSBT" in { nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
(client, otherClient, _) <- clientsF
addr <- client.getNewAddress
txid <- BitcoindRpcTestUtil.fundBlockChainTransaction(client,
otherClient,
@ -85,52 +87,39 @@ class PsbtRpcTest extends BitcoindRpcTest {
}
// copies this test from Core: https://github.com/bitcoin/bitcoin/blob/master/test/functional/rpc_psbt.py#L158
it should "combine PSBTs from multiple sources" in {
it should "combine PSBTs from multiple sources" in { nodePair: FixtureParam =>
val client = nodePair.node1
val otherClient = nodePair.node2
for {
(client, otherClient, thirdClient) <- clientsF
// create outputs for transaction
clientAddr <- client.getNewAddress
otherClientAddr <- otherClient.getNewAddress
clientTxid <- thirdClient.sendToAddress(clientAddr, Bitcoins.one)
otherClientTxid <-
thirdClient.sendToAddress(otherClientAddr, Bitcoins.one)
clientTxid <- otherClient.sendToAddress(clientAddr, Bitcoins.one)
_ <- BitcoindRpcTestUtil.generateAndSync(
Vector(thirdClient, client, otherClient))
_ <- BitcoindRpcTestUtil.generateAndSync(Vector(otherClient, client))
rawClientTx <- client.getRawTransaction(clientTxid)
_ = assert(rawClientTx.confirmations.exists(_ > 0))
clientVout <-
BitcoindRpcTestUtil.findOutput(client, clientTxid, Bitcoins.one)
otherClientVout <- BitcoindRpcTestUtil.findOutput(otherClient,
otherClientTxid,
Bitcoins.one)
// create a psbt spending outputs generated above
newAddr <- thirdClient.getNewAddress
newAddr <- otherClient.getNewAddress
psbt <- {
val inputs =
Vector(
TransactionInput
.fromTxidAndVout(clientTxid, clientVout),
TransactionInput.fromTxidAndVout(otherClientTxid, otherClientVout)
.fromTxidAndVout(clientTxid, clientVout)
)
thirdClient.createPsbt(inputs, Map(newAddr -> Bitcoins(1.5)))
otherClient.createPsbt(inputs, Map(newAddr -> Bitcoins(1.5)))
}
// Update psbts, should only have data for one input and not the other
clientProcessedPsbt <- client.walletProcessPSBT(psbt).map(_.psbt)
otherClientProcessedPsbt <-
otherClient
.walletProcessPSBT(psbt)
.map(_.psbt)
// Combine and finalize the psbts
combined <- thirdClient.combinePsbt(
Vector(clientProcessedPsbt, otherClientProcessedPsbt))
finalized <- thirdClient.finalizePsbt(combined)
combined <- otherClient.combinePsbt(Vector(clientProcessedPsbt, psbt))
finalized <- otherClient.finalizePsbt(combined)
} yield {
finalized match {
case _: FinalizedPsbt => succeed
@ -139,9 +128,9 @@ class PsbtRpcTest extends BitcoindRpcTest {
}
}
it should "create a PSBT and then decode it" in {
it should "create a PSBT and then decode it" in { nodePair: FixtureParam =>
val client = nodePair.node1
for {
(client, _, _) <- clientsF
address <- client.getNewAddress
input <- client.listUnspent.map(_.filter(_.spendable).head)
psbt <- {
@ -162,23 +151,25 @@ class PsbtRpcTest extends BitcoindRpcTest {
}
it should "create a funded wallet PSBT and then decode it" in {
for {
(client, _, _) <- clientsF
address <- client.getNewAddress
input <- client.listUnspent.map(_.filter(_.spendable).head)
psbt <- {
val outpoint = TransactionOutPoint(input.txid.flip, UInt32(input.vout))
val ourInput = TransactionInput(outpoint,
ScriptSignature.empty,
TransactionConstants.sequence)
client.walletCreateFundedPsbt(
Vector(ourInput),
Map(address -> Bitcoins(input.amount.toBigDecimal / 2)))
nodePair: FixtureParam =>
val client = nodePair.node1
for {
address <- client.getNewAddress
input <- client.listUnspent.map(_.filter(_.spendable).head)
psbt <- {
val outpoint =
TransactionOutPoint(input.txid.flip, UInt32(input.vout))
val ourInput = TransactionInput(outpoint,
ScriptSignature.empty,
TransactionConstants.sequence)
client.walletCreateFundedPsbt(
Vector(ourInput),
Map(address -> Bitcoins(input.amount.toBigDecimal / 2)))
}
_ <- client.decodePsbt(psbt.psbt)
} yield {
succeed
}
_ <- client.decodePsbt(psbt.psbt)
} yield {
succeed
}
}
}

View File

@ -1,6 +1,5 @@
package org.bitcoins.rpc.v18
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.AddNodeArgument
import org.bitcoins.commons.jsonmodels.bitcoind.{
AddressInfoResultPostV18,
AddressInfoResultPostV21,
@ -9,125 +8,15 @@ import org.bitcoins.commons.jsonmodels.bitcoind.{
import org.bitcoins.core.api.chain.db.BlockHeaderDbHelper
import org.bitcoins.core.protocol.blockchain.RegTestNetChainParams
import org.bitcoins.rpc.client.common.BitcoindVersion
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 org.bitcoins.testkit.rpc.BitcoindFixturesFundedCachedV18
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))
class BitcoindV18RpcClientTest extends BitcoindFixturesFundedCachedV18 {
behavior of "BitcoindV18RpcClient"
it should "be able to get peer info" in {
it should "have extra address information" in { client =>
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 must "have our BitcoindRpcClient work with .hashCode() and equals" in {
for {
(client1, client2) <- clientPairF
} yield {
assert(client1 != client2)
assert(client1.hashCode() != client2.hashCode())
}
}
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
_ <- 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, BigInt(0), genesisHeader)
val nextHeader = BlockHeaderHelper.buildNextHeader(genesisHeaderDb)
clientF.flatMap(client =>
client.submitHeader(nextHeader.blockHeader).map(_ => succeed))
}
it should "have extra address information" in {
for {
(client, _) <- clientPairF
address <- client.getNewAddress
info <- client.getAddressInfo(address)
} yield {
@ -140,4 +29,56 @@ class BitcoindV18RpcClientTest extends BitcoindRpcTest {
}
}
it should "be able to start a V18 bitcoind instance" in { client =>
assert(client.version == BitcoindVersion.V18)
}
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 "return a list of wallets" in { client =>
for {
_ <- client.createWallet("Suredbits")
list <- client.listWalletDir()
} yield {
assert(list.wallets.exists(_.name.contains("Suredbits")))
}
}
it should "analyze a descriptor" in { client =>
val descriptor =
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)"
val descriptorF = 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 { 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)
}
}

View File

@ -3,66 +3,53 @@ package org.bitcoins.rpc.v18
import org.bitcoins.core.currency.Bitcoins
import org.bitcoins.core.psbt.PSBT
import org.bitcoins.rpc.client.v18.BitcoindV18RpcClient
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.BitcoindRpcTest
import scala.concurrent.Future
import org.bitcoins.testkit.rpc.BitcoindFixturesFundedCachedV18
/** 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))
class PsbtRpcTest extends BitcoindFixturesFundedCachedV18 {
behavior of "PsbtRpc"
it should "return something when analyzePsbt is called" in {
//PSBT with one P2PKH input and one P2SH-P2WPKH input both with non-final scriptSigs. P2SH-P2WPKH input's redeemScript is available. Outputs filled.
val psbt =
"cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA="
client: BitcoindV18RpcClient =>
//PSBT with one P2PKH input and one P2SH-P2WPKH input both with non-final scriptSigs. P2SH-P2WPKH input's redeemScript is available. Outputs filled.
val psbt =
"cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA="
clientF.flatMap { client =>
val resultF = client.analyzePsbt(PSBT.fromBase64(psbt))
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.
client: BitcoindV18RpcClient =>
//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.fromBase64(psbt)))
val psbt =
"cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA="
val analyzedF = client.analyzePsbt(PSBT.fromBase64(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)
}
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 {
it should "correctly analyze a psbt " in { client: BitcoindV18RpcClient =>
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.fromBase64(psbt)))
val analyzedF = client.analyzePsbt(PSBT.fromBase64(psbt))
val expectedfee = Bitcoins(0.00090341)
val expectedhasutxo = true
val expectedisfinal = false
@ -85,14 +72,15 @@ class PsbtRpcTest extends BitcoindRpcTest {
//Todo: figure out how to implement a test here
it should "check to see if the utxoUpdate input has been updated" in {
val psbt =
PSBT.fromBase64(
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==")
val updatedF = clientF.flatMap(client => client.utxoUpdatePsbt(psbt))
client: BitcoindV18RpcClient =>
val psbt =
PSBT.fromBase64(
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==")
val updatedF = client.utxoUpdatePsbt(psbt)
updatedF.map { result =>
assert(result == psbt)
}
updatedF.map { result =>
assert(result == psbt)
}
}
/** Join psbt looks at the characteristics of a vector of PSBTs and converts them into a singular PSBT.
@ -100,7 +88,7 @@ class PsbtRpcTest extends BitcoindRpcTest {
* 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 {
it should "joinpsbts " in { client: BitcoindV18RpcClient =>
val seqofpsbts = Vector(
PSBT.fromBase64(
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA"),
@ -110,7 +98,7 @@ class PsbtRpcTest extends BitcoindRpcTest {
)
)
val joinedF = clientF.flatMap(client => client.joinPsbts(seqofpsbts))
val joinedF = client.joinPsbts(seqofpsbts)
joinedF.map { result =>
assert(

View File

@ -1,149 +1,123 @@
package org.bitcoins.rpc.v19
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.WalletFlag
import org.bitcoins.commons.jsonmodels.bitcoind.{
Bip9SoftforkPostV19,
GetBlockChainInfoResultPostV19
}
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.WalletFlag
import org.bitcoins.core.config.RegTest
import org.bitcoins.core.gcs.{BlockFilter, FilterType}
import org.bitcoins.core.psbt.PSBT
import org.bitcoins.rpc.client.common.BitcoindVersion
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.BitcoindRpcTest
import org.bitcoins.testkit.rpc.BitcoindFixturesFundedCachedV19
import scala.concurrent.Future
class BitcoindV19RpcClientTest extends BitcoindRpcTest {
lazy val clientF: Future[BitcoindV19RpcClient] = {
val client = new BitcoindV19RpcClient(BitcoindRpcTestUtil.v19Instance())
val clientIsStartedF = BitcoindRpcTestUtil.startServers(Vector(client))
clientIsStartedF.map(_ => client)
}
lazy val clientPairF: Future[(BitcoindV19RpcClient, BitcoindV19RpcClient)] =
BitcoindRpcTestUtil.createNodePairV19(clientAccum)
clientF.foreach(c => clientAccum.+=(c))
class BitcoindV19RpcClientTest extends BitcoindFixturesFundedCachedV19 {
behavior of "BitcoindV19RpcClient"
it should "be able to start a V19 bitcoind instance" in {
clientF.map { client =>
client: BitcoindV19RpcClient =>
assert(client.version == BitcoindVersion.V19)
}
}
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 "get a block filter given a block hash" in {
for {
(client, _) <- clientPairF
blocks <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
blockFilter <- client.getBlockFilter(blocks.head, FilterType.Basic)
client: BitcoindV19RpcClient =>
for {
blocks <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
blockFilter <- client.getBlockFilter(blocks.head, FilterType.Basic)
block <- client.getBlockRaw(blocks.head)
txs <- Future.sequence(
block.transactions
.filterNot(_.isCoinbase)
.map(tx => client.getTransaction(tx.txIdBE)))
block <- client.getBlockRaw(blocks.head)
txs <- Future.sequence(
block.transactions
.filterNot(_.isCoinbase)
.map(tx => client.getTransaction(tx.txIdBE)))
prevFilter <- client.getBlockFilter(block.blockHeader.previousBlockHashBE,
FilterType.Basic)
} yield {
val pubKeys = txs.flatMap(_.hex.outputs.map(_.scriptPubKey)).toVector
val filter = BlockFilter(block, pubKeys)
assert(filter.hash == blockFilter.filter.hash)
assert(
blockFilter.header == filter
.getHeader(prevFilter.header.flip)
.hash
.flip)
}
prevFilter <- client.getBlockFilter(
block.blockHeader.previousBlockHashBE,
FilterType.Basic)
} yield {
val pubKeys = txs.flatMap(_.hex.outputs.map(_.scriptPubKey)).toVector
val filter = BlockFilter(block, pubKeys)
assert(filter.hash == blockFilter.filter.hash)
assert(
blockFilter.header == filter
.getHeader(prevFilter.header.flip)
.hash
.flip)
}
}
it should "be able to get the balances" in {
it should "be able to get the balances" in { client: BitcoindV19RpcClient =>
for {
(client, _) <- clientPairF
immatureBalance <- client.getBalances
_ <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
newImmatureBalance <- client.getBalances
} yield {
val blockReward = 12.5
val blockReward = 50
assert(immatureBalance.mine.immature.toBigDecimal >= 0)
assert(
immatureBalance.mine.immature.toBigDecimal + blockReward == newImmatureBalance.mine.immature.toBigDecimal)
immatureBalance.mine.trusted.toBigDecimal + blockReward == newImmatureBalance.mine.trusted.toBigDecimal)
}
}
it should "be able to get blockchain info" in {
for {
(client, _) <- clientPairF
info <- client.getBlockChainInfo
bestHash <- client.getBestBlockHash
} yield {
assert(info.isInstanceOf[GetBlockChainInfoResultPostV19])
val preV19Info = info.asInstanceOf[GetBlockChainInfoResultPostV19]
assert(preV19Info.chain == RegTest)
assert(preV19Info.softforks.size >= 5)
assert(
preV19Info.softforks.values.exists(_.isInstanceOf[Bip9SoftforkPostV19]))
assert(preV19Info.bestblockhash == bestHash)
}
client: BitcoindV19RpcClient =>
for {
info <- client.getBlockChainInfo
bestHash <- client.getBestBlockHash
} yield {
assert(info.isInstanceOf[GetBlockChainInfoResultPostV19])
val preV19Info = info.asInstanceOf[GetBlockChainInfoResultPostV19]
assert(preV19Info.chain == RegTest)
assert(preV19Info.softforks.size >= 5)
assert(
preV19Info.softforks.values.exists(
_.isInstanceOf[Bip9SoftforkPostV19]))
assert(preV19Info.bestblockhash == bestHash)
}
}
it should "be able to set the wallet flag 'avoid_reuse'" in {
for {
(client, _) <- clientPairF
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))
}
client: BitcoindV19RpcClient =>
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 {
for {
(client, _) <- clientPairF
_ <- client.createWallet("suredbits", passphrase = "stackingsats")
wallets <- client.listWallets
} yield {
assert(wallets.contains("suredbits"))
}
client: BitcoindV19RpcClient =>
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: BitcoindV19RpcClient =>
val descriptor =
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)"
val descriptor =
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)"
val psbt =
PSBT.fromBase64(
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==")
val psbt =
PSBT.fromBase64(
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==")
for {
(client, _) <- clientPairF
result <- client.utxoUpdatePsbt(psbt, Seq(descriptor))
} yield {
assert(result == psbt)
}
for {
result <- client.utxoUpdatePsbt(psbt, Seq(descriptor))
} yield {
assert(result == psbt)
}
}
}

View File

@ -1,8 +1,5 @@
package org.bitcoins.rpc.v20
import java.io.File
import java.nio.file.Files
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.WalletFlag
import org.bitcoins.commons.jsonmodels.bitcoind._
import org.bitcoins.core.config.RegTest
@ -11,157 +8,137 @@ import org.bitcoins.core.psbt.PSBT
import org.bitcoins.crypto.ECPublicKey
import org.bitcoins.rpc.client.common.BitcoindVersion
import org.bitcoins.rpc.client.v20.BitcoindV20RpcClient
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.BitcoindRpcTest
import org.bitcoins.testkit.rpc.BitcoindFixturesFundedCachedV20
import java.io.File
import java.nio.file.Files
import scala.concurrent.Future
class BitcoindV20RpcClientTest extends BitcoindRpcTest {
lazy val clientPairF: Future[(BitcoindV20RpcClient, BitcoindV20RpcClient)] =
BitcoindRpcTestUtil.createNodePairV20(clientAccum)
lazy val clientF: Future[BitcoindV20RpcClient] = clientPairF.map(_._1)
clientF.foreach(c => clientAccum.+=(c))
class BitcoindV20RpcClientTest extends BitcoindFixturesFundedCachedV20 {
behavior of "BitcoindV20RpcClient"
it should "be able to start a V20 bitcoind instance" in {
clientF.map { client =>
client: BitcoindV20RpcClient =>
assert(client.version == BitcoindVersion.V20)
}
}
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 "get a block filter given a block hash" in {
for {
(client, _) <- clientPairF
blocks <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
blockFilter <- client.getBlockFilter(blocks.head, FilterType.Basic)
client: BitcoindV20RpcClient =>
for {
blocks <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
blockFilter <- client.getBlockFilter(blocks.head, FilterType.Basic)
block <- client.getBlockRaw(blocks.head)
txs <- Future.sequence(
block.transactions
.filterNot(_.isCoinbase)
.map(tx => client.getTransaction(tx.txIdBE)))
block <- client.getBlockRaw(blocks.head)
txs <- Future.sequence(
block.transactions
.filterNot(_.isCoinbase)
.map(tx => client.getTransaction(tx.txIdBE)))
prevFilter <- client.getBlockFilter(block.blockHeader.previousBlockHashBE,
FilterType.Basic)
} yield {
val pubKeys = txs.flatMap(_.hex.outputs.map(_.scriptPubKey)).toVector
val filter = BlockFilter(block, pubKeys)
assert(filter.hash == blockFilter.filter.hash)
assert(
blockFilter.header == filter
.getHeader(prevFilter.header.flip)
.hash
.flip)
}
prevFilter <- client.getBlockFilter(
block.blockHeader.previousBlockHashBE,
FilterType.Basic)
} yield {
val pubKeys = txs.flatMap(_.hex.outputs.map(_.scriptPubKey)).toVector
val filter = BlockFilter(block, pubKeys)
assert(filter.hash == blockFilter.filter.hash)
assert(
blockFilter.header == filter
.getHeader(prevFilter.header.flip)
.hash
.flip)
}
}
it should "be able to get the balances" in {
it should "be able to get the balances" in { client: BitcoindV20RpcClient =>
for {
(client, _) <- clientPairF
immatureBalance <- client.getBalances
_ <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
newImmatureBalance <- client.getBalances
} yield {
val blockReward = 12.5
val blockReward = 50
assert(immatureBalance.mine.immature.toBigDecimal >= 0)
assert(
immatureBalance.mine.immature.toBigDecimal + blockReward == newImmatureBalance.mine.immature.toBigDecimal)
immatureBalance.mine.trusted.toBigDecimal + blockReward == newImmatureBalance.mine.trusted.toBigDecimal)
}
}
it should "be able to get blockchain info" in {
for {
(client, _) <- clientPairF
info <- client.getBlockChainInfo
bestHash <- client.getBestBlockHash
} yield {
assert(info.isInstanceOf[GetBlockChainInfoResultPostV19])
val preV19Info = info.asInstanceOf[GetBlockChainInfoResultPostV19]
assert(preV19Info.chain == RegTest)
assert(preV19Info.softforks.size >= 5)
assert(
preV19Info.softforks.values.exists(_.isInstanceOf[Bip9SoftforkPostV19]))
assert(preV19Info.bestblockhash == bestHash)
}
client: BitcoindV20RpcClient =>
for {
info <- client.getBlockChainInfo
bestHash <- client.getBestBlockHash
} yield {
assert(info.isInstanceOf[GetBlockChainInfoResultPostV19])
val preV19Info = info.asInstanceOf[GetBlockChainInfoResultPostV19]
assert(preV19Info.chain == RegTest)
assert(preV19Info.softforks.size >= 5)
assert(
preV19Info.softforks.values.exists(
_.isInstanceOf[Bip9SoftforkPostV19]))
assert(preV19Info.bestblockhash == bestHash)
}
}
it should "be able to set the wallet flag 'avoid_reuse'" in {
for {
(client, _) <- clientPairF
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))
}
client: BitcoindV20RpcClient =>
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 {
for {
(client, _) <- clientPairF
_ <- client.createWallet("suredbits", passphrase = "stackingsats")
wallets <- client.listWallets
} yield {
assert(wallets.contains("suredbits"))
}
client: BitcoindV20RpcClient =>
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: BitcoindV20RpcClient =>
val descriptor =
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)"
val descriptor =
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)"
val psbt =
PSBT.fromBase64(
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==")
val psbt =
PSBT.fromBase64(
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==")
for {
(client, _) <- clientPairF
result <- client.utxoUpdatePsbt(psbt, Seq(descriptor))
} yield {
assert(result == psbt)
}
for {
result <- client.utxoUpdatePsbt(psbt, Seq(descriptor))
} yield {
assert(result == psbt)
}
}
it should "correct create multisig and get its descriptor" in {
val pubKey1 = ECPublicKey.freshPublicKey
val pubKey2 = ECPublicKey.freshPublicKey
client: BitcoindV20RpcClient =>
val pubKey1 = ECPublicKey.freshPublicKey
val pubKey2 = ECPublicKey.freshPublicKey
for {
client <- clientF
multiSigResult <- client.createMultiSig(2, Vector(pubKey1, pubKey2))
} yield {
// just validate we are able to receive a sane descriptor
// no need to check checksum
assert(
multiSigResult.descriptor.startsWith(
s"sh(multi(2,${pubKey1.hex},${pubKey2.hex}))#"))
}
for {
multiSigResult <- client.createMultiSig(2, Vector(pubKey1, pubKey2))
} yield {
// just validate we are able to receive a sane descriptor
// no need to check checksum
assert(
multiSigResult.descriptor.startsWith(
s"sh(multi(2,${pubKey1.hex},${pubKey2.hex}))#"))
}
}
it should "correctly dump tx out set" in {
it should "correctly dump tx out set" in { client: BitcoindV20RpcClient =>
for {
client <- clientF
hash <- client.getBestBlockHash
height <- client.getBestHashBlockHeight()
result <- client.dumpTxOutSet(new File("utxo.dat").toPath)
@ -177,13 +154,13 @@ class BitcoindV20RpcClientTest extends BitcoindRpcTest {
}
it should "correct generate to a descriptor" in {
// 2-of-2 multisig descriptor
val descriptor =
"sh(sortedmulti(2,023f720438186fbdfde0c0a403e770a0f32a2d198623a8a982c47b621f8b307640,03ed261094d609d5e02ba6553c2d91e4fd056006ce2fe64aace72b69cb5be3ab9c))#nj9wx7up"
val numBlocks = 10
for {
client <- clientF
hashes <- client.generateToDescriptor(numBlocks, descriptor)
} yield assert(hashes.size == numBlocks)
client: BitcoindV20RpcClient =>
// 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)
}
}

View File

@ -1,200 +1,181 @@
package org.bitcoins.rpc.v21
import org.bitcoins.asyncutil.AsyncUtil
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.WalletFlag
import org.bitcoins.commons.jsonmodels.bitcoind._
import org.bitcoins.core.config.RegTest
import org.bitcoins.core.gcs.{BlockFilter, FilterType}
import org.bitcoins.core.psbt.PSBT
import org.bitcoins.crypto.ECPublicKey
import org.bitcoins.rpc.client.common.BitcoindVersion
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
import org.bitcoins.rpc.client.v21.BitcoindV21RpcClient
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.BitcoindRpcTest
import org.bitcoins.testkit.rpc.BitcoindFixturesFundedCachedV21
import java.io.File
import java.nio.file.Files
import scala.concurrent.Future
class BitcoindV21RpcClientTest extends BitcoindRpcTest {
lazy val clientPairF: Future[(BitcoindV21RpcClient, BitcoindV21RpcClient)] =
BitcoindRpcTestUtil.createNodePairV21(clientAccum)
lazy val clientF: Future[BitcoindV21RpcClient] = clientPairF.map(_._1)
clientF.foreach(c => clientAccum.+=(c))
class BitcoindV21RpcClientTest extends BitcoindFixturesFundedCachedV21 {
behavior of "BitcoindV21RpcClient"
it should "be able to start a V21 bitcoind instance" in {
clientF.map { client =>
client: BitcoindV21RpcClient =>
assert(client.version == BitcoindVersion.V21)
}
}
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 network info" in {
for {
(freshClient, _) <- clientPairF
info <- freshClient.getNetworkInfo
} yield {
assert(info.networkactive)
assert(info.localrelay)
assert(info.connections == 1)
}
freshClient: BitcoindV21RpcClient =>
for {
info <- freshClient.getNetworkInfo
} yield {
assert(info.networkactive)
assert(info.localrelay)
}
}
it should "get index info" in {
it should "get index info" in { client: BitcoindV21RpcClient =>
def isHeight101(client: BitcoindRpcClient): Future[Boolean] = {
client.getBlockCount.map(_ == 101)
}
for {
(client, _) <- clientPairF
_ <- AsyncUtil.retryUntilSatisfiedF(() => isHeight101(client))
indexes <- client.getIndexInfo
} yield {
val txIndexInfo = indexes("txindex")
assert(txIndexInfo.synced)
assert(txIndexInfo.best_block_height == 400)
assert(txIndexInfo.best_block_height == 101)
val blockFilterIndexInfo = indexes("basic block filter index")
assert(blockFilterIndexInfo.synced)
assert(blockFilterIndexInfo.best_block_height == 400)
assert(blockFilterIndexInfo.best_block_height == 101)
}
}
it should "be able to get the address info for a given address" in {
for {
(client, _) <- clientPairF
addr <- client.getNewAddress
info <- client.getAddressInfo(addr)
} yield assert(info.address == addr)
client: BitcoindV21RpcClient =>
for {
addr <- client.getNewAddress
info <- client.getAddressInfo(addr)
} yield assert(info.address == addr)
}
it should "get a block filter given a block hash" in {
for {
(client, _) <- clientPairF
blocks <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
blockFilter <- client.getBlockFilter(blocks.head, FilterType.Basic)
client: BitcoindV21RpcClient =>
for {
blocks <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
blockFilter <- client.getBlockFilter(blocks.head, FilterType.Basic)
block <- client.getBlockRaw(blocks.head)
txs <- Future.sequence(
block.transactions
.filterNot(_.isCoinbase)
.map(tx => client.getTransaction(tx.txIdBE)))
block <- client.getBlockRaw(blocks.head)
txs <- Future.sequence(
block.transactions
.filterNot(_.isCoinbase)
.map(tx => client.getTransaction(tx.txIdBE)))
prevFilter <- client.getBlockFilter(block.blockHeader.previousBlockHashBE,
FilterType.Basic)
} yield {
val pubKeys = txs.flatMap(_.hex.outputs.map(_.scriptPubKey)).toVector
val filter = BlockFilter(block, pubKeys)
assert(filter.hash == blockFilter.filter.hash)
assert(
blockFilter.header == filter
.getHeader(prevFilter.header.flip)
.hash
.flip)
}
prevFilter <- client.getBlockFilter(
block.blockHeader.previousBlockHashBE,
FilterType.Basic)
} yield {
val pubKeys = txs.flatMap(_.hex.outputs.map(_.scriptPubKey)).toVector
val filter = BlockFilter(block, pubKeys)
assert(filter.hash == blockFilter.filter.hash)
assert(
blockFilter.header == filter
.getHeader(prevFilter.header.flip)
.hash
.flip)
}
}
it should "be able to get the balances" in {
it should "be able to get the balances" in { client: BitcoindV21RpcClient =>
for {
(client, _) <- clientPairF
immatureBalance <- client.getBalances
_ <- client.getNewAddress.flatMap(client.generateToAddress(1, _))
newImmatureBalance <- client.getBalances
} yield {
val blockReward = 12.5
val blockReward = 50
assert(immatureBalance.mine.immature.toBigDecimal >= 0)
assert(
immatureBalance.mine.immature.toBigDecimal + blockReward == newImmatureBalance.mine.immature.toBigDecimal)
immatureBalance.mine.trusted.toBigDecimal + blockReward == newImmatureBalance.mine.trusted.toBigDecimal)
}
}
it should "be able to get blockchain info" in {
for {
(client, _) <- clientPairF
info <- client.getBlockChainInfo
bestHash <- client.getBestBlockHash
} yield {
assert(info.isInstanceOf[GetBlockChainInfoResultPostV19])
val preV19Info = info.asInstanceOf[GetBlockChainInfoResultPostV19]
assert(preV19Info.chain == RegTest)
assert(preV19Info.softforks.size >= 5)
assert(
preV19Info.softforks.values.exists(_.isInstanceOf[Bip9SoftforkPostV19]))
assert(preV19Info.bestblockhash == bestHash)
}
client: BitcoindV21RpcClient =>
for {
info <- client.getBlockChainInfo
bestHash <- client.getBestBlockHash
} yield {
assert(info.isInstanceOf[GetBlockChainInfoResultPostV19])
val preV19Info = info.asInstanceOf[GetBlockChainInfoResultPostV19]
assert(preV19Info.chain == RegTest)
assert(preV19Info.softforks.size >= 5)
assert(
preV19Info.softforks.values.exists(
_.isInstanceOf[Bip9SoftforkPostV19]))
assert(preV19Info.bestblockhash == bestHash)
}
}
it should "be able to set the wallet flag 'avoid_reuse'" in {
for {
(client, _) <- clientPairF
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))
}
client: BitcoindV21RpcClient =>
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 {
for {
(client, _) <- clientPairF
_ <- client.createWallet("suredbits", passphrase = "stackingsats")
wallets <- client.listWallets
} yield {
assert(wallets.contains("suredbits"))
}
client: BitcoindV21RpcClient =>
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: BitcoindV21RpcClient =>
val descriptor =
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)"
val descriptor =
"pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)"
val psbt =
PSBT.fromBase64(
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==")
val psbt =
PSBT.fromBase64(
"cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAA==")
for {
(client, _) <- clientPairF
result <- client.utxoUpdatePsbt(psbt, Seq(descriptor))
} yield {
assert(result == psbt)
}
for {
result <- client.utxoUpdatePsbt(psbt, Seq(descriptor))
} yield {
assert(result == psbt)
}
}
it should "correct create multisig and get its descriptor" in {
val pubKey1 = ECPublicKey.freshPublicKey
val pubKey2 = ECPublicKey.freshPublicKey
client: BitcoindV21RpcClient =>
val pubKey1 = ECPublicKey.freshPublicKey
val pubKey2 = ECPublicKey.freshPublicKey
for {
client <- clientF
multiSigResult <- client.createMultiSig(2, Vector(pubKey1, pubKey2))
} yield {
// just validate we are able to receive a sane descriptor
// no need to check checksum
assert(
multiSigResult.descriptor.startsWith(
s"sh(multi(2,${pubKey1.hex},${pubKey2.hex}))#"))
}
for {
multiSigResult <- client.createMultiSig(2, Vector(pubKey1, pubKey2))
} yield {
// just validate we are able to receive a sane descriptor
// no need to check checksum
assert(
multiSigResult.descriptor.startsWith(
s"sh(multi(2,${pubKey1.hex},${pubKey2.hex}))#"))
}
}
it should "correctly dump tx out set" in {
it should "correctly dump tx out set" in { client: BitcoindV21RpcClient =>
for {
client <- clientF
hash <- client.getBestBlockHash
height <- client.getBestHashBlockHeight()
result <- client.dumpTxOutSet(new File("utxo.dat").toPath)
@ -210,13 +191,13 @@ class BitcoindV21RpcClientTest extends BitcoindRpcTest {
}
it should "correct generate to a descriptor" in {
// 2-of-2 multisig descriptor
val descriptor =
"sh(sortedmulti(2,023f720438186fbdfde0c0a403e770a0f32a2d198623a8a982c47b621f8b307640,03ed261094d609d5e02ba6553c2d91e4fd056006ce2fe64aace72b69cb5be3ab9c))#nj9wx7up"
val numBlocks = 10
for {
client <- clientF
hashes <- client.generateToDescriptor(numBlocks, descriptor)
} yield assert(hashes.size == numBlocks)
client: BitcoindV21RpcClient =>
// 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)
}
}

View File

@ -31,6 +31,7 @@ import org.bitcoins.rpc.config.{BitcoindAuthCredentials, BitcoindInstance}
import org.bitcoins.rpc.util.NativeProcessFactory
import play.api.libs.json._
import java.util.concurrent.atomic.AtomicBoolean
import scala.concurrent._
import scala.concurrent.duration.DurationInt
import scala.util.control.NonFatal
@ -97,7 +98,7 @@ trait Client
def getDaemon: BitcoindInstance = instance
override def cmd: String = {
override lazy val cmd: String = {
val binaryPath = instance.binary.getAbsolutePath
val cmd = List(binaryPath,
"-datadir=" + instance.datadir,
@ -124,17 +125,6 @@ trait Client
}
val startedF = startBinary()
def isStartedF: Future[Boolean] = {
val started: Promise[Boolean] = Promise()
val pingF = bitcoindCall[Unit]("ping", printError = false)
pingF.onComplete {
case Success(_) => started.success(true)
case Failure(_) => started.success(false)
}
started.future
}
// if we're doing cookie based authentication, we might attempt
// to read the cookie file before it's written. this ensures
@ -157,6 +147,7 @@ trait Client
for {
_ <- startedF
_ <- awaitCookie(instance.authCredentials)
_ = isStartedFlag.set(true)
_ <- AsyncUtil.retryUntilSatisfiedF(() => isStartedF,
interval = 1.seconds,
maxTries = 60)
@ -191,39 +182,47 @@ trait Client
started
}
private def tryPing(): Future[Boolean] = {
val request = buildRequest(instance, "ping", JsArray.empty)
val responseF = sendRequest(request)
val payloadF: Future[JsValue] =
responseF.flatMap(getPayload(_))
// Ping successful if no error can be parsed from the payload
val parsedF = payloadF.map { payload =>
(payload \ errorKey).validate[BitcoindException] match {
case _: JsSuccess[BitcoindException] => false
case _: JsError => true
}
}
parsedF.recover {
case exc: StreamTcpException
if exc.getMessage.contains("Connection refused") =>
false
case _: JsonParseException =>
//see https://github.com/bitcoin-s/bitcoin-s/issues/527
false
}
}
private val isStartedFlag: AtomicBoolean = new AtomicBoolean(false)
/** Checks whether the underlying bitcoind daemon is running
*/
def isStartedF: Future[Boolean] = {
def tryPing: Future[Boolean] = {
val request = buildRequest(instance, "ping", JsArray.empty)
val responseF = sendRequest(request)
val payloadF: Future[JsValue] =
responseF.flatMap(getPayload(_))
// Ping successful if no error can be parsed from the payload
val parsedF = payloadF.map { payload =>
(payload \ errorKey).validate[BitcoindException] match {
case _: JsSuccess[BitcoindException] => false
case _: JsError => true
}
}
parsedF.recover {
case exc: StreamTcpException
if exc.getMessage.contains("Connection refused") =>
false
case _: JsonParseException =>
//see https://github.com/bitcoin-s/bitcoin-s/issues/527
false
}
}
instance.authCredentials match {
case cookie: CookieBased if Files.notExists(cookie.cookiePath) =>
// if the cookie file doesn't exist we're not started
Future.successful(false)
case (CookieBased(_, _) | PasswordBased(_, _)) => tryPing
case (CookieBased(_, _) | PasswordBased(_, _)) =>
if (isStartedFlag.get) {
tryPing()
} else {
Future.successful(false)
}
}
}
@ -233,6 +232,7 @@ trait Client
def stop(): Future[BitcoindRpcClient] = {
for {
_ <- bitcoindCall[String]("stop")
_ = isStartedFlag.set(false)
//do we want to call this right away?
//i think bitcoind stops asynchronously
//so it returns fast from the 'stop' rpc command

View File

@ -0,0 +1,14 @@
package org.bitcoins.rpc.util
import org.bitcoins.rpc.client.common.BitcoindRpcClient
case class NodePair[T <: BitcoindRpcClient](node1: T, node2: T) {
val toVector: Vector[T] = Vector(node1, node2)
}
object NodePair {
def fromTuple[T <: BitcoindRpcClient](pair: (T, T)): NodePair[T] = {
NodePair(pair._1, pair._2)
}
}

View File

@ -0,0 +1,14 @@
package org.bitcoins.rpc.util
import org.bitcoins.rpc.client.common.BitcoindRpcClient
case class NodeTriple[T <: BitcoindRpcClient](node1: T, node2: T, node3: T) {
val toVector: Vector[T] = Vector(node1, node2, node3)
}
object NodeTriple {
def fromTuple[T <: BitcoindRpcClient](nodes: (T, T, T)): NodeTriple[T] = {
NodeTriple(nodes._1, nodes._2, nodes._3)
}
}

View File

@ -566,11 +566,18 @@ lazy val zmq = project
coreJVM % testAndCompile
)
def isCI = {
Properties
.envOrNone("CI")
.isDefined
}
lazy val bitcoindRpcTest = project
.in(file("bitcoind-rpc-test"))
.settings(CommonSettings.testSettings: _*)
.settings(name := "bitcoin-s-bitcoind-rpc-test",
libraryDependencies ++= Deps.bitcoindRpcTest.value)
libraryDependencies ++= Deps.bitcoindRpcTest.value,
parallelExecution := !(isCI && Properties.isMac))
.dependsOn(coreJVM % testAndCompile, testkit)
lazy val bench = project
@ -588,7 +595,8 @@ lazy val eclairRpcTest = project
.settings(CommonSettings.testSettings: _*)
.settings(
libraryDependencies ++= Deps.eclairRpcTest.value,
name := "bitcoin-s-eclair-rpc-test"
name := "bitcoin-s-eclair-rpc-test",
parallelExecution := !(isCI && Properties.isMac)
)
.dependsOn(coreJVM % testAndCompile, testkit)

View File

@ -66,7 +66,7 @@ implicit val ec = system.dispatcher
//so let's start one (make sure you ran 'sbt downloadBitcoind')
val instance = BitcoindRpcTestUtil.instance(versionOpt = Some(BitcoindVersion.Experimental))
val p2pPort = instance.p2pPort
val bitcoindF = BitcoindRpcTestUtil.startedBitcoindRpcClient(instance)
val bitcoindF = BitcoindRpcTestUtil.startedBitcoindRpcClient(instance, Vector.newBuilder)
//contains information on how to connect to bitcoin's p2p info
val peerF = bitcoindF.map(b => NodeUnitTest.createPeer(b))

View File

@ -51,7 +51,7 @@ val bitcoindV = BitcoindVersion.V19
val instance = BitcoindRpcTestUtil.instance(versionOpt = Some(bitcoindV))
//now let's create an rpc client off of that instance
val bitcoindRpcClientF = BitcoindRpcTestUtil.startedBitcoindRpcClient(instance)
val bitcoindRpcClientF = BitcoindRpcTestUtil.startedBitcoindRpcClient(instance, Vector.newBuilder)
//yay! it's started. Now you can run tests against this.
//let's just grab the block count for an example

View File

@ -7,15 +7,15 @@ import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.testkit.BitcoinSTestAppConfig
import org.bitcoins.testkit.async.TestAsyncUtil
import org.bitcoins.testkit.node.NodeUnitTest
import org.bitcoins.testkit.node.NodeTestWithCachedBitcoindV19
import org.bitcoins.testkit.node.fixture.SpvNodeConnectedWithBitcoindV19
import org.scalatest.FutureOutcome
import org.scalatest.{FutureOutcome, Outcome}
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.control.NonFatal
class BroadcastTransactionTest extends NodeUnitTest {
class BroadcastTransactionTest extends NodeTestWithCachedBitcoindV19 {
/** Wallet config with data directory set to user temp directory */
implicit override protected def getFreshConfig: BitcoinSAppConfig =
@ -23,8 +23,16 @@ class BroadcastTransactionTest extends NodeUnitTest {
override type FixtureParam = SpvNodeConnectedWithBitcoindV19
def withFixture(test: OneArgAsyncTest): FutureOutcome =
withSpvNodeConnectedToBitcoindV19(test)(system, getFreshConfig)
def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val outcome: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
outcome = withSpvNodeConnectedToBitcoindV19Cached(test, bitcoind)(
system,
getFreshConfig)
f <- outcome.toFuture
} yield f
new FutureOutcome(outcome)
}
private val sendAmount = 1.bitcoin

View File

@ -2,17 +2,19 @@ package org.bitcoins.node
import akka.actor.Cancellable
import org.bitcoins.crypto.DoubleSha256DigestBE
import org.bitcoins.rpc.client.common.BitcoindVersion
import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.testkit.BitcoinSTestAppConfig
import org.bitcoins.testkit.fixtures.UsesExperimentalBitcoind
import org.bitcoins.testkit.node.fixture.NeutrinoNodeConnectedWithBitcoind
import org.bitcoins.testkit.node.{NodeTestUtil, NodeUnitTest}
import org.scalatest.FutureOutcome
import org.bitcoins.testkit.node.{
NodeTestUtil,
NodeTestWithCachedBitcoindNewest
}
import org.scalatest.{FutureOutcome, Outcome}
import scala.concurrent.Future
class NeutrinoNodeTest extends NodeUnitTest {
class NeutrinoNodeTest extends NodeTestWithCachedBitcoindNewest {
/** Wallet config with data directory set to user temp directory */
implicit override protected def getFreshConfig: BitcoinSAppConfig =
@ -20,8 +22,14 @@ class NeutrinoNodeTest extends NodeUnitTest {
override type FixtureParam = NeutrinoNodeConnectedWithBitcoind
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
withNeutrinoNodeConnectedToBitcoind(test, Some(BitcoindVersion.V21))
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val outcomeF: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
outcome = withNeutrinoNodeConnectedToBitcoind(test, bitcoind)
f <- outcome.toFuture
} yield f
new FutureOutcome(outcomeF)
}
behavior of "NeutrinoNode"

View File

@ -6,20 +6,19 @@ import org.bitcoins.core.protocol.script.MultiSignatureScriptPubKey
import org.bitcoins.core.protocol.transaction.TransactionOutput
import org.bitcoins.core.wallet.fee.SatoshisPerByte
import org.bitcoins.crypto.ECPublicKey
import org.bitcoins.rpc.client.common.BitcoindVersion
import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.testkit.BitcoinSTestAppConfig
import org.bitcoins.testkit.node.{
NeutrinoNodeFundedWalletBitcoind,
NodeTestUtil,
NodeUnitTest
NodeTestWithCachedBitcoindNewest
}
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
import org.scalatest.FutureOutcome
import org.scalatest.{FutureOutcome, Outcome}
import scala.concurrent.Future
class NeutrinoNodeWithWalletTest extends NodeUnitTest {
class NeutrinoNodeWithWalletTest extends NodeTestWithCachedBitcoindNewest {
/** Wallet config with data directory set to user temp directory */
implicit override protected def getFreshConfig: BitcoinSAppConfig =
@ -28,11 +27,17 @@ class NeutrinoNodeWithWalletTest extends NodeUnitTest {
override type FixtureParam = NeutrinoNodeFundedWalletBitcoind
def withFixture(test: OneArgAsyncTest): FutureOutcome = {
withNeutrinoNodeFundedWalletBitcoind(
test = test,
bip39PasswordOpt = getBIP39PasswordOpt(),
versionOpt = Some(BitcoindVersion.V21)
)(system, getFreshConfig)
val outcomeF: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
outcome = withNeutrinoNodeFundedWalletBitcoind(
test = test,
bip39PasswordOpt = getBIP39PasswordOpt(),
bitcoind = bitcoind
)(system, getFreshConfig)
f <- outcome.toFuture
} yield f
new FutureOutcome(outcomeF)
}
val TestAmount = 1.bitcoin

View File

@ -6,13 +6,16 @@ import org.bitcoins.rpc.util.RpcUtil
import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.testkit.BitcoinSTestAppConfig
import org.bitcoins.testkit.node.fixture.SpvNodeConnectedWithBitcoind
import org.bitcoins.testkit.node.{NodeTestUtil, NodeUnitTest}
import org.scalatest.FutureOutcome
import org.bitcoins.testkit.node.{
NodeTestUtil,
NodeTestWithCachedBitcoindNewest
}
import org.scalatest.{FutureOutcome, Outcome}
import scala.concurrent.Future
import scala.concurrent.duration.DurationInt
class SpvNodeTest extends NodeUnitTest {
class SpvNodeTest extends NodeTestWithCachedBitcoindNewest {
/** Wallet config with data directory set to user temp directory */
implicit override protected def getFreshConfig: BitcoinSAppConfig =
@ -20,8 +23,14 @@ class SpvNodeTest extends NodeUnitTest {
override type FixtureParam = SpvNodeConnectedWithBitcoind
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
withSpvNodeConnectedToBitcoind(test)
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val outcomeF: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
outcome = withSpvNodeConnectedToBitcoindCached(test, bitcoind)
f <- outcome.toFuture
} yield f
new FutureOutcome(outcomeF)
}
behavior of "SpvNode"
@ -89,7 +98,11 @@ class SpvNodeTest extends NodeUnitTest {
//the send headers message.
val has6BlocksF = RpcUtil.retryUntilSatisfiedF(
conditionF = () =>
spvNode.chainApiFromDb().flatMap(_.getBlockCount().map(_ == 6)),
spvNode
.chainApiFromDb()
.flatMap(_.getBlockCount().map { count =>
count == 108
}),
interval = 250.millis)
has6BlocksF.map { _ =>

View File

@ -7,15 +7,15 @@ import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.testkit.BitcoinSTestAppConfig
import org.bitcoins.testkit.node.{
NodeTestUtil,
NodeUnitTest,
NodeTestWithCachedBitcoindNewest,
SpvNodeFundedWalletBitcoind
}
import org.bitcoins.wallet.Wallet
import org.scalatest.FutureOutcome
import org.scalatest.{FutureOutcome, Outcome}
import scala.concurrent.Future
class SpvNodeWithWalletTest extends NodeUnitTest {
class SpvNodeWithWalletTest extends NodeTestWithCachedBitcoindNewest {
/** Wallet config with data directory set to user temp directory */
implicit override protected def getFreshConfig: BitcoinSAppConfig =
@ -24,7 +24,14 @@ class SpvNodeWithWalletTest extends NodeUnitTest {
override type FixtureParam = SpvNodeFundedWalletBitcoind
def withFixture(test: OneArgAsyncTest): FutureOutcome = {
withSpvNodeFundedWalletBitcoind(test, getBIP39PasswordOpt())
val outcomeF: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
outcome = withSpvNodeFundedWalletBitcoindCached(test,
getBIP39PasswordOpt(),
bitcoind)
f <- outcome.toFuture
} yield f
new FutureOutcome(outcomeF)
}
val amountFromBitcoind = 1.bitcoin

View File

@ -5,12 +5,16 @@ import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.testkit.BitcoinSTestAppConfig
import org.bitcoins.testkit.node.{
NodeTestUtil,
NodeUnitTest,
NodeTestWithCachedBitcoindNewest,
SpvNodeFundedWalletBitcoind
}
import org.scalatest.{BeforeAndAfter, FutureOutcome}
import org.scalatest.{BeforeAndAfter, FutureOutcome, Outcome}
class UpdateBloomFilterTest extends NodeUnitTest with BeforeAndAfter {
import scala.concurrent.Future
class UpdateBloomFilterTest
extends NodeTestWithCachedBitcoindNewest
with BeforeAndAfter {
/** Wallet config with data directory set to user temp directory */
implicit override protected def getFreshConfig: BitcoinSAppConfig =
@ -19,7 +23,14 @@ class UpdateBloomFilterTest extends NodeUnitTest with BeforeAndAfter {
override type FixtureParam = SpvNodeFundedWalletBitcoind
def withFixture(test: OneArgAsyncTest): FutureOutcome = {
withSpvNodeFundedWalletBitcoind(test, None)
val outcome: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
outcome = withSpvNodeFundedWalletBitcoindCached(test,
getBIP39PasswordOpt(),
bitcoind)
f <- outcome.toFuture
} yield f
new FutureOutcome(outcome)
}
it must "update the bloom filter with a TX" in { param =>

View File

@ -1,26 +1,39 @@
package org.bitcoins.node.networking.peer
import org.bitcoins.chain.config.ChainAppConfig
import org.bitcoins.node.models.Peer
import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.testkit.BitcoinSTestAppConfig
import org.bitcoins.testkit.async.TestAsyncUtil
import org.bitcoins.testkit.node.{CachedBitcoinSAppConfig, NodeUnitTest}
import org.scalatest.FutureOutcome
import org.bitcoins.testkit.node.{
CachedBitcoinSAppConfig,
NodeTestWithCachedBitcoindNewest,
NodeUnitTest
}
import org.scalatest.{FutureOutcome, Outcome}
import scala.concurrent.Future
import scala.concurrent.duration.DurationInt
/** Created by chris on 7/1/16.
*/
class PeerMessageHandlerTest extends NodeUnitTest with CachedBitcoinSAppConfig {
class PeerMessageHandlerTest
extends NodeTestWithCachedBitcoindNewest
with CachedBitcoinSAppConfig {
/** Wallet config with data directory set to user temp directory */
implicit override protected def getFreshConfig: BitcoinSAppConfig =
BitcoinSTestAppConfig.getSpvTestConfig()
override type FixtureParam = Unit
override type FixtureParam = Peer
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
test(())
val outcomeF: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
outcome = withBitcoindPeer(test, bitcoind)
f <- outcome.toFuture
} yield f
new FutureOutcome(outcomeF)
}
implicit protected lazy val chainConfig: ChainAppConfig =
@ -28,15 +41,12 @@ class PeerMessageHandlerTest extends NodeUnitTest with CachedBitcoinSAppConfig {
behavior of "PeerHandler"
it must "be able to fully initialize a PeerMessageReceiver" in { _ =>
val peerHandlerF =
bitcoindPeerF.flatMap(p => NodeUnitTest.buildPeerHandler(p))
it must "be able to fully initialize a PeerMessageReceiver" in { peer =>
val peerHandlerF = NodeUnitTest.buildPeerHandler(peer)
val peerMsgSenderF = peerHandlerF.map(_.peerMsgSender)
val p2pClientF = peerHandlerF.map(_.p2pClient)
val _ =
bitcoindPeerF.flatMap(_ => peerHandlerF.map(_.peerMsgSender.connect()))
val _ = peerHandlerF.map(_.peerMsgSender.connect())
val isConnectedF = TestAsyncUtil.retryUntilSatisfiedF(
() => p2pClientF.flatMap(_.isConnected()),
interval = 500.millis
@ -196,9 +206,4 @@ class PeerMessageHandlerTest extends NodeUnitTest with CachedBitcoinSAppConfig {
peerMsgHandler ! Tcp.Close
probe.expectMsg(Tcp.Closed)
}*/
override def afterAll(): Unit = {
startedBitcoindF.flatMap(_.stop())
super.afterAll()
}
}

View File

@ -20,9 +20,9 @@ import scala.util.Properties
object CommonSettings {
private val isCI = {
sys.props
.get("CI")
private def isCI = {
Properties
.envOrNone("CI")
.isDefined
}
@ -150,8 +150,7 @@ object CommonSettings {
//show full stack trace (-oF) of failed tests and duration of tests (-oD)
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-oDF"),
logBuffered in Test := false,
skip.in(publish) := true,
parallelExecution.in(Test) := false
skip.in(publish) := true
) ++ settings
lazy val prodSettings: Seq[Setting[_]] = settings

View File

@ -647,7 +647,7 @@ object ChainUnitTest extends ChainVerificationLogger {
val chainHandlerF = makeChainHandler()
for {
chainHandler <- chainHandlerF
genHeader <- chainHandler.blockHeaderDAO.create(genesisHeaderDb)
genHeader <- chainHandler.blockHeaderDAO.upsert(genesisHeaderDb)
} yield genHeader
}

View File

@ -57,7 +57,8 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
*/
def startedBitcoindRpcClient(instance: BitcoindInstance = bitcoindInstance())(
implicit actorSystem: ActorSystem): Future[BitcoindRpcClient] = {
BitcoindRpcTestUtil.startedBitcoindRpcClient(instance)
//need to do something with the Vector.newBuilder presumably?
BitcoindRpcTestUtil.startedBitcoindRpcClient(instance, Vector.newBuilder)
}
/** Creates a bitcoind instance with the given parameters */

View File

@ -0,0 +1,165 @@
package org.bitcoins.testkit.node
import akka.actor.{ActorSystem, Cancellable}
import org.bitcoins.core.api.chain.{ChainApi, ChainQueryApi, FilterSyncMarker}
import org.bitcoins.core.api.chain.db.{
BlockHeaderDb,
CompactFilterDb,
CompactFilterHeaderDb
}
import org.bitcoins.core.config.NetworkParameters
import org.bitcoins.core.gcs.FilterHeader
import org.bitcoins.core.p2p.CompactFilterMessage
import org.bitcoins.core.protocol.blockchain.BlockHeader
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
import org.bitcoins.crypto.DoubleSha256DigestBE
import org.bitcoins.db.AppConfig
import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.testkit.EmbeddedPg
import org.bitcoins.testkit.chain.ChainUnitTest
import org.bitcoins.testkit.fixtures.BitcoinSFixture
import org.bitcoins.testkit.keymanager.KeyManagerTestUtil
import scala.concurrent.Future
import scala.concurrent.duration.DurationInt
/** A base test trait for all the tests in our nodeTest module */
trait BaseNodeTest extends BitcoinSFixture with EmbeddedPg {
/** Wallet config with data directory set to user temp directory */
implicit protected def getFreshConfig: BitcoinSAppConfig
implicit override lazy val np: NetworkParameters =
getFreshConfig.nodeConf.network
override def beforeAll(): Unit = {
AppConfig.throwIfDefaultDatadir(getFreshConfig.nodeConf)
super[EmbeddedPg].beforeAll()
}
override def afterAll(): Unit = {
super[EmbeddedPg].afterAll()
}
lazy val junkAddress: BitcoinAddress =
BitcoinAddress("2NFyxovf6MyxfHqtVjstGzs6HeLqv92Nq4U")
/** Helper method to generate blocks every interval
* @return a cancellable that will stop generating blocks
*/
def genBlockInterval(bitcoind: BitcoindRpcClient)(implicit
system: ActorSystem): Cancellable = {
var counter = 0
val desiredBlocks = 5
val interval = 500.millis
val genBlock = new Runnable {
override def run(): Unit = {
if (counter < desiredBlocks) {
bitcoind.getNewAddress.flatMap(bitcoind.generateToAddress(1, _))
counter = counter + 1
}
}
}
system.scheduler.scheduleAtFixedRate(2.second, interval)(genBlock)
}
def getBIP39PasswordOpt(): Option[String] =
KeyManagerTestUtil.bip39PasswordOpt
val genesisChainApi: ChainApi = new ChainApi {
override def processHeaders(
headers: Vector[BlockHeader]): Future[ChainApi] =
Future.successful(this)
override def getHeader(
hash: DoubleSha256DigestBE): Future[Option[BlockHeaderDb]] =
Future.successful(None)
override def getHeadersAtHeight(
height: Int): Future[Vector[BlockHeaderDb]] =
Future.successful(Vector.empty)
override def getBlockCount(): Future[Int] = Future.successful(0)
override def getBestBlockHeader(): Future[BlockHeaderDb] =
Future.successful(ChainUnitTest.genesisHeaderDb)
override def processFilterHeaders(
filterHeaders: Vector[FilterHeader],
stopHash: DoubleSha256DigestBE): Future[ChainApi] =
Future.successful(this)
override def nextBlockHeaderBatchRange(
stopHash: DoubleSha256DigestBE,
batchSize: Int): Future[Option[FilterSyncMarker]] =
Future.successful(None)
override def nextFilterHeaderBatchRange(
startHeight: Int,
batchSize: Int): Future[Option[FilterSyncMarker]] =
Future.successful(None)
override def processFilters(
message: Vector[CompactFilterMessage]): Future[ChainApi] =
Future.successful(this)
override def processCheckpoints(
checkpoints: Vector[DoubleSha256DigestBE],
blockHash: DoubleSha256DigestBE): Future[ChainApi] =
Future.successful(this)
override def getFilterHeaderCount(): Future[Int] = Future.successful(0)
override def getFilterHeadersAtHeight(
height: Int): Future[Vector[CompactFilterHeaderDb]] =
Future.successful(Vector.empty)
override def getBestFilterHeader(): Future[Option[CompactFilterHeaderDb]] =
Future.successful(None)
override def getFilterHeader(blockHash: DoubleSha256DigestBE): Future[
Option[CompactFilterHeaderDb]] = Future.successful(None)
override def getFilter(
hash: DoubleSha256DigestBE): Future[Option[CompactFilterDb]] =
Future.successful(None)
override def getFilterCount(): Future[Int] = Future.successful(0)
override def getFiltersAtHeight(
height: Int): Future[Vector[CompactFilterDb]] =
Future.successful(Vector.empty)
override def getHeightByBlockStamp(blockStamp: BlockStamp): Future[Int] =
Future.successful(0)
override def getHeadersBetween(
from: BlockHeaderDb,
to: BlockHeaderDb): Future[Vector[BlockHeaderDb]] =
Future.successful(Vector.empty)
override def getBlockHeight(
blockHash: DoubleSha256DigestBE): Future[Option[Int]] =
Future.successful(None)
override def getBestBlockHash(): Future[DoubleSha256DigestBE] =
Future.successful(DoubleSha256DigestBE.empty)
override def getNumberOfConfirmations(
blockHashOpt: DoubleSha256DigestBE): Future[Option[Int]] =
Future.successful(None)
override def getFiltersBetweenHeights(
startHeight: Int,
endHeight: Int): Future[Vector[ChainQueryApi.FilterResponse]] =
Future.successful(Vector.empty)
override def epochSecondToBlockHeight(time: Long): Future[Int] =
Future.successful(0)
}
}

View File

@ -0,0 +1,188 @@
package org.bitcoins.testkit.node
import akka.actor.ActorSystem
import org.bitcoins.node.{Node, NodeType}
import org.bitcoins.node.models.Peer
import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.testkit.chain.ChainUnitTest
import org.bitcoins.testkit.node.NodeUnitTest.{createPeer, syncNeutrinoNode}
import org.bitcoins.testkit.node.fixture.{
NeutrinoNodeConnectedWithBitcoind,
SpvNodeConnectedWithBitcoind,
SpvNodeConnectedWithBitcoindV19
}
import org.bitcoins.testkit.rpc.{
CachedBitcoind,
CachedBitcoindNewest,
CachedBitcoindV19
}
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
import org.bitcoins.wallet.WalletCallbacks
import org.scalatest.FutureOutcome
import scala.concurrent.Future
/** Test trait for using a bitcoin-s [[Node]] that requires a cached bitcoind.
* The cached bitcoind will be share across tests in the test suite that extends
* this trait.
*/
trait NodeTestWithCachedBitcoind extends BaseNodeTest { _: CachedBitcoind[_] =>
def withSpvNodeFundedWalletBitcoindCached(
test: OneArgAsyncTest,
bip39PasswordOpt: Option[String],
bitcoind: BitcoindRpcClient)(implicit
system: ActorSystem,
appConfig: BitcoinSAppConfig): FutureOutcome = {
makeDependentFixture[SpvNodeFundedWalletBitcoind](
build = () =>
NodeUnitTest.createSpvNodeFundedWalletFromBitcoind(
walletCallbacks = WalletCallbacks.empty,
bip39PasswordOpt = bip39PasswordOpt,
bitcoind = bitcoind)(
system, // Force V18 because Spv is disabled on versions after
appConfig),
{ case x: SpvNodeFundedWalletBitcoind =>
tearDownNodeWithBitcoind(x)
}
)(test)
}
def withSpvNodeConnectedToBitcoindCached(
test: OneArgAsyncTest,
bitcoind: BitcoindRpcClient)(implicit
system: ActorSystem,
appConfig: BitcoinSAppConfig): FutureOutcome = {
val nodeWithBitcoindBuilder: () => Future[SpvNodeConnectedWithBitcoind] = {
() =>
require(appConfig.nodeType == NodeType.SpvNode)
for {
node <- NodeUnitTest.createSpvNode(createPeer(bitcoind))(
system,
appConfig.chainConf,
appConfig.nodeConf)
started <- node.start()
_ <- NodeUnitTest.syncSpvNode(started, bitcoind)
} yield SpvNodeConnectedWithBitcoind(node, bitcoind)
}
makeDependentFixture[SpvNodeConnectedWithBitcoind](
build = nodeWithBitcoindBuilder,
{ case x: SpvNodeConnectedWithBitcoind =>
NodeUnitTest.destroyNode(x.node)
})(test)
}
def withNeutrinoNodeConnectedToBitcoind(
test: OneArgAsyncTest,
bitcoind: BitcoindRpcClient)(implicit
system: ActorSystem,
appConfig: BitcoinSAppConfig): FutureOutcome = {
val nodeWithBitcoindBuilder: () => Future[
NeutrinoNodeConnectedWithBitcoind] = { () =>
require(appConfig.nodeType == NodeType.NeutrinoNode)
for {
node <- NodeUnitTest.createNeutrinoNode(bitcoind)(system,
appConfig.chainConf,
appConfig.nodeConf)
startedNode <- node.start()
syncedNode <- syncNeutrinoNode(startedNode, bitcoind)
} yield NeutrinoNodeConnectedWithBitcoind(syncedNode, bitcoind)
}
makeDependentFixture[NeutrinoNodeConnectedWithBitcoind](
build = nodeWithBitcoindBuilder,
{ case x: NeutrinoNodeConnectedWithBitcoind =>
tearDownNode(x.node)
})(test)
}
def withNeutrinoNodeFundedWalletBitcoind(
test: OneArgAsyncTest,
bip39PasswordOpt: Option[String],
bitcoind: BitcoindRpcClient,
walletCallbacks: WalletCallbacks = WalletCallbacks.empty)(implicit
system: ActorSystem,
appConfig: BitcoinSAppConfig): FutureOutcome = {
makeDependentFixture[NeutrinoNodeFundedWalletBitcoind](
build = () =>
NodeUnitTest
.createNeutrinoNodeFundedWalletFromBitcoind(
bip39PasswordOpt = bip39PasswordOpt,
bitcoind,
walletCallbacks = walletCallbacks)(system, appConfig),
{ case x: NeutrinoNodeFundedWalletBitcoind =>
tearDownNodeWithBitcoind(x)
}
)(test)
}
def withBitcoindPeer(
test: OneArgAsyncTest,
bitcoind: BitcoindRpcClient): FutureOutcome = {
val peer = NodeTestUtil.getBitcoindPeer(bitcoind)
makeDependentFixture[Peer](
() => Future.successful(peer),
_ => Future.unit
)(test)
}
private def tearDownNodeWithBitcoind(
nodeWithBitcoind: NodeFundedWalletBitcoind): Future[Unit] = {
val node = nodeWithBitcoind.node
val destroyNodeF = tearDownNode(node)
val destroyWalletF =
BitcoinSWalletTest.destroyWallet(nodeWithBitcoind.wallet)
for {
_ <- destroyNodeF
_ <- destroyWalletF
} yield ()
}
private def tearDownNode(node: Node): Future[Unit] = {
val destroyNodeF = NodeUnitTest.destroyNode(node)
for {
_ <- destroyNodeF
_ <- ChainUnitTest.destroyAllTables()(node.chainAppConfig,
system.dispatcher)
} yield ()
}
}
trait NodeTestWithCachedBitcoindNewest
extends NodeTestWithCachedBitcoind
with CachedBitcoindNewest
trait NodeTestWithCachedBitcoindV19
extends NodeTestWithCachedBitcoind
with CachedBitcoindV19 {
def withSpvNodeConnectedToBitcoindV19Cached(
test: OneArgAsyncTest,
bitcoind: BitcoindV19RpcClient)(implicit
system: ActorSystem,
appConfig: BitcoinSAppConfig): FutureOutcome = {
val nodeWithBitcoindBuilder: () => Future[
SpvNodeConnectedWithBitcoindV19] = { () =>
require(appConfig.nodeType == NodeType.SpvNode)
for {
node <- NodeUnitTest.createSpvNode(createPeer(bitcoind))(
system,
appConfig.chainConf,
appConfig.nodeConf)
started <- node.start()
_ <- NodeUnitTest.syncSpvNode(started, bitcoind)
} yield SpvNodeConnectedWithBitcoindV19(node, bitcoind)
}
makeDependentFixture[SpvNodeConnectedWithBitcoindV19](
build = nodeWithBitcoindBuilder,
{ case x: SpvNodeConnectedWithBitcoindV19 =>
NodeUnitTest.destroyNode(x.node)
}
)(test)
}
}

View File

@ -1,21 +1,8 @@
package org.bitcoins.testkit.node
import java.net.InetSocketAddress
import akka.actor.{ActorSystem, Cancellable}
import akka.actor.ActorSystem
import org.bitcoins.chain.config.ChainAppConfig
import org.bitcoins.core.api.chain.{ChainApi, ChainQueryApi, FilterSyncMarker}
import org.bitcoins.core.api.chain.db.{
BlockHeaderDb,
CompactFilterDb,
CompactFilterHeaderDb
}
import org.bitcoins.core.config.NetworkParameters
import org.bitcoins.core.gcs.FilterHeader
import org.bitcoins.core.p2p.CompactFilterMessage
import org.bitcoins.core.protocol.blockchain.BlockHeader
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
import org.bitcoins.crypto.DoubleSha256DigestBE
import org.bitcoins.db.AppConfig
import org.bitcoins.core.api.chain.ChainApi
import org.bitcoins.node._
import org.bitcoins.node.config.NodeAppConfig
import org.bitcoins.node.models.Peer
@ -31,10 +18,8 @@ import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
import org.bitcoins.rpc.util.RpcUtil
import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.server.BitcoinSAppConfig._
import org.bitcoins.testkit.EmbeddedPg
import org.bitcoins.testkit.chain.ChainUnitTest
import org.bitcoins.testkit.fixtures.BitcoinSFixture
import org.bitcoins.testkit.keymanager.KeyManagerTestUtil
import org.bitcoins.testkit.node.NodeUnitTest.{
createPeer,
emptyPeer,
@ -46,131 +31,16 @@ import org.bitcoins.testkit.node.fixture.{
SpvNodeConnectedWithBitcoind,
SpvNodeConnectedWithBitcoindV19
}
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.wallet.{BitcoinSWalletTest, WalletWithBitcoindRpc}
import org.bitcoins.testkitcore.node.P2PMessageTestUtil
import org.bitcoins.wallet.WalletCallbacks
import org.scalatest.FutureOutcome
import scala.concurrent.duration._
import java.net.InetSocketAddress
import scala.concurrent.{ExecutionContext, Future}
trait NodeUnitTest extends BitcoinSFixture with EmbeddedPg {
override def beforeAll(): Unit = {
AppConfig.throwIfDefaultDatadir(getFreshConfig.nodeConf)
super[EmbeddedPg].beforeAll()
}
override def afterAll(): Unit = {
super[EmbeddedPg].afterAll()
}
/** Wallet config with data directory set to user temp directory */
implicit protected def getFreshConfig: BitcoinSAppConfig
implicit override lazy val np: NetworkParameters =
getFreshConfig.nodeConf.network
lazy val startedBitcoindF = BitcoindRpcTestUtil.startedBitcoindRpcClient()
lazy val bitcoindPeerF = startedBitcoindF.map(NodeTestUtil.getBitcoindPeer)
lazy val junkAddress: BitcoinAddress =
BitcoinAddress("2NFyxovf6MyxfHqtVjstGzs6HeLqv92Nq4U")
val genesisChainApi: ChainApi = new ChainApi {
override def processHeaders(
headers: Vector[BlockHeader]): Future[ChainApi] =
Future.successful(this)
override def getHeader(
hash: DoubleSha256DigestBE): Future[Option[BlockHeaderDb]] =
Future.successful(None)
override def getHeadersAtHeight(
height: Int): Future[Vector[BlockHeaderDb]] =
Future.successful(Vector.empty)
override def getBlockCount(): Future[Int] = Future.successful(0)
override def getBestBlockHeader(): Future[BlockHeaderDb] =
Future.successful(ChainUnitTest.genesisHeaderDb)
override def processFilterHeaders(
filterHeaders: Vector[FilterHeader],
stopHash: DoubleSha256DigestBE): Future[ChainApi] =
Future.successful(this)
override def nextBlockHeaderBatchRange(
stopHash: DoubleSha256DigestBE,
batchSize: Int): Future[Option[FilterSyncMarker]] =
Future.successful(None)
override def nextFilterHeaderBatchRange(
startHeight: Int,
batchSize: Int): Future[Option[FilterSyncMarker]] =
Future.successful(None)
override def processFilters(
message: Vector[CompactFilterMessage]): Future[ChainApi] =
Future.successful(this)
override def processCheckpoints(
checkpoints: Vector[DoubleSha256DigestBE],
blockHash: DoubleSha256DigestBE): Future[ChainApi] =
Future.successful(this)
override def getFilterHeaderCount(): Future[Int] = Future.successful(0)
override def getFilterHeadersAtHeight(
height: Int): Future[Vector[CompactFilterHeaderDb]] =
Future.successful(Vector.empty)
override def getBestFilterHeader(): Future[Option[CompactFilterHeaderDb]] =
Future.successful(None)
override def getFilterHeader(blockHash: DoubleSha256DigestBE): Future[
Option[CompactFilterHeaderDb]] = Future.successful(None)
override def getFilter(
hash: DoubleSha256DigestBE): Future[Option[CompactFilterDb]] =
Future.successful(None)
override def getFilterCount(): Future[Int] = Future.successful(0)
override def getFiltersAtHeight(
height: Int): Future[Vector[CompactFilterDb]] =
Future.successful(Vector.empty)
override def getHeightByBlockStamp(blockStamp: BlockStamp): Future[Int] =
Future.successful(0)
override def getHeadersBetween(
from: BlockHeaderDb,
to: BlockHeaderDb): Future[Vector[BlockHeaderDb]] =
Future.successful(Vector.empty)
override def getBlockHeight(
blockHash: DoubleSha256DigestBE): Future[Option[Int]] =
Future.successful(None)
override def getBestBlockHash(): Future[DoubleSha256DigestBE] =
Future.successful(DoubleSha256DigestBE.empty)
override def getNumberOfConfirmations(
blockHashOpt: DoubleSha256DigestBE): Future[Option[Int]] =
Future.successful(None)
override def getFiltersBetweenHeights(
startHeight: Int,
endHeight: Int): Future[Vector[ChainQueryApi.FilterResponse]] =
Future.successful(Vector.empty)
override def epochSecondToBlockHeight(time: Long): Future[Int] =
Future.successful(0)
}
trait NodeUnitTest extends BaseNodeTest {
def withDisconnectedSpvNode(test: OneArgAsyncTest)(implicit
system: ActorSystem,
@ -316,31 +186,6 @@ trait NodeUnitTest extends BitcoinSFixture with EmbeddedPg {
_: NodeFundedWalletBitcoind)(system, appConfig)
)(test)
}
/** Helper method to generate blocks every interval
* @return a cancellable that will stop generating blocks
*/
def genBlockInterval(bitcoind: BitcoindRpcClient)(implicit
system: ActorSystem): Cancellable = {
var counter = 0
val desiredBlocks = 5
val interval = 500.millis
val genBlock = new Runnable {
override def run(): Unit = {
if (counter < desiredBlocks) {
bitcoind.getNewAddress.flatMap(bitcoind.generateToAddress(1, _))
counter = counter + 1
}
}
}
system.scheduler.scheduleAtFixedRate(2.second, interval)(genBlock)
}
def getBIP39PasswordOpt(): Option[String] =
KeyManagerTestUtil.bip39PasswordOpt
}
object NodeUnitTest extends P2PLogger {
@ -380,7 +225,9 @@ object NodeUnitTest extends P2PLogger {
def destroyNode(node: Node)(implicit ec: ExecutionContext): Future[Unit] = {
for {
_ <- node.stop()
} yield ()
} yield {
()
}
}
def destroyNodeConnectedWithBitcoind(
@ -415,6 +262,25 @@ object NodeUnitTest extends P2PLogger {
require(appConfig.nodeType == NodeType.SpvNode)
for {
bitcoind <- BitcoinSFixture.createBitcoindWithFunds(versionOpt)
spvNodeWithBitcoind <- createSpvNodeFundedWalletFromBitcoind(
walletCallbacks,
bip39PasswordOpt,
bitcoind)
} yield {
spvNodeWithBitcoind
}
}
/** Creates a spv node & funded wallet with the given bitcoind */
def createSpvNodeFundedWalletFromBitcoind(
walletCallbacks: WalletCallbacks,
bip39PasswordOpt: Option[String],
bitcoind: BitcoindRpcClient)(implicit
system: ActorSystem,
appConfig: BitcoinSAppConfig): Future[SpvNodeFundedWalletBitcoind] = {
import system.dispatcher
require(appConfig.nodeType == NodeType.SpvNode)
for {
node <- createSpvNode(createPeer(bitcoind))
fundedWallet <- BitcoinSWalletTest.fundedWalletAndBitcoind(
bitcoind,
@ -472,6 +338,36 @@ object NodeUnitTest extends P2PLogger {
}
}
def createNeutrinoNodeFundedWalletFromBitcoind(
bip39PasswordOpt: Option[String],
bitcoind: BitcoindRpcClient,
walletCallbacks: WalletCallbacks)(implicit
system: ActorSystem,
appConfig: BitcoinSAppConfig): Future[
NeutrinoNodeFundedWalletBitcoind] = {
import system.dispatcher
require(appConfig.nodeType == NodeType.NeutrinoNode)
for {
node <- createNeutrinoNode(bitcoind)
fundedWallet <- BitcoinSWalletTest.fundedWalletAndBitcoind(
bitcoindRpcClient = bitcoind,
nodeApi = node,
chainQueryApi = node,
bip39PasswordOpt = bip39PasswordOpt,
walletCallbacks = walletCallbacks)
startedNode <- node.start()
syncedNode <- syncNeutrinoNode(startedNode, bitcoind)
//callbacks are executed asynchronously, which is how we fund the wallet
//so we need to wait until the wallet balances are correct
_ <- BitcoinSWalletTest.awaitWalletBalances(fundedWallet)
} yield {
NeutrinoNodeFundedWalletBitcoind(node = syncedNode,
wallet = fundedWallet.wallet,
bitcoindRpc = fundedWallet.bitcoind,
bip39PasswordOpt = bip39PasswordOpt)
}
}
def destroyNodeFundedWalletBitcoind(
fundedWalletBitcoind: NodeFundedWalletBitcoind)(implicit
system: ActorSystem,

View File

@ -0,0 +1,318 @@
package org.bitcoins.testkit.rpc
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
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.client.v19.BitcoindV19RpcClient
import org.bitcoins.rpc.client.v20.BitcoindV20RpcClient
import org.bitcoins.rpc.client.v21.BitcoindV21RpcClient
import org.bitcoins.rpc.util.{NodePair, NodeTriple}
import org.bitcoins.testkit.EmbeddedPg
import org.bitcoins.testkit.fixtures.BitcoinSFixture
import org.bitcoins.testkit.util.BitcoinSAsyncFixtureTest
import org.scalatest.{FutureOutcome, Outcome}
import scala.concurrent.Future
/** A trait that is useful if you need bitcoind fixtures for your test suite */
trait BitcoindFixtures extends BitcoinSFixture with EmbeddedPg {
_: BitcoinSAsyncFixtureTest =>
def withNewestFundedBitcoindCached(
test: OneArgAsyncTest,
bitcoind: BitcoindRpcClient): FutureOutcome = {
makeDependentFixture[BitcoindRpcClient](
() => Future.successful(bitcoind),
{ case _ =>
Future.unit // don't want to destroy anything since it is cached
})(test)
}
}
/** Bitcoind fixtures with a cached a bitcoind instance */
trait BitcoindFixturesCached extends BitcoindFixtures {
_: BitcoinSAsyncFixtureTest with CachedBitcoind[_] =>
}
/** Bitcoind fixtures with a cached a bitcoind instance that is funded */
trait BitcoindFixturesFundedCached extends BitcoindFixtures {
_: BitcoinSAsyncFixtureTest with CachedBitcoindFunded[_] =>
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val f: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
futOutcome = withNewestFundedBitcoindCached(test, bitcoind)
fut <- futOutcome.toFuture
} yield fut
new FutureOutcome(f)
}
}
/** Test trait that caches a [[BitcoindV18RpcClient]] that is funded
* and available to use with fixtures
*/
trait BitcoindFixturesFundedCachedV18
extends BitcoinSAsyncFixtureTest
with BitcoindFixturesFundedCached
with CachedBitcoindV18 {
override type FixtureParam = BitcoindV18RpcClient
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val f: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
futOutcome = withV18FundedBitcoindCached(test, bitcoind)
fut <- futOutcome.toFuture
} yield fut
new FutureOutcome(f)
}
def withV18FundedBitcoindCached(
test: OneArgAsyncTest,
bitcoind: BitcoindV18RpcClient): FutureOutcome = {
makeDependentFixture[BitcoindV18RpcClient](
() => Future.successful(bitcoind),
{ case _ =>
Future.unit // don't want to destroy anything since it is cached
})(test)
}
override def afterAll(): Unit = {
super[CachedBitcoindV18].afterAll()
super[BitcoinSAsyncFixtureTest].afterAll()
}
}
/** Test trait that caches a [[BitcoindV19RpcClient]] that is funded
* and available to use with fixtures
*/
trait BitcoindFixturesFundedCachedV19
extends BitcoinSAsyncFixtureTest
with BitcoindFixturesFundedCached
with CachedBitcoindV19 {
override type FixtureParam = BitcoindV19RpcClient
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val f: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
futOutcome = withV19FundedBitcoindCached(test, bitcoind)
fut <- futOutcome.toFuture
} yield fut
new FutureOutcome(f)
}
def withV19FundedBitcoindCached(
test: OneArgAsyncTest,
bitcoind: BitcoindV19RpcClient): FutureOutcome = {
makeDependentFixture[BitcoindV19RpcClient](
() => Future.successful(bitcoind),
{ case _ =>
Future.unit // don't want to destroy anything since it is cached
})(test)
}
override def afterAll(): Unit = {
super[CachedBitcoindV19].afterAll()
super[BitcoinSAsyncFixtureTest].afterAll()
}
}
/** Test trait that caches a [[BitcoindV20RpcClient]] that is funded
* and available to use with fixtures
*/
trait BitcoindFixturesFundedCachedV20
extends BitcoinSAsyncFixtureTest
with BitcoindFixturesFundedCached
with CachedBitcoindV20 {
override type FixtureParam = BitcoindV20RpcClient
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val f: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
futOutcome = withV20FundedBitcoindCached(test, bitcoind)
fut <- futOutcome.toFuture
} yield fut
new FutureOutcome(f)
}
def withV20FundedBitcoindCached(
test: OneArgAsyncTest,
bitcoind: BitcoindV20RpcClient): FutureOutcome = {
makeDependentFixture[BitcoindV20RpcClient](
() => Future.successful(bitcoind),
{ case _ =>
Future.unit // don't want to destroy anything since it is cached
})(test)
}
override def afterAll(): Unit = {
super[CachedBitcoindV20].afterAll()
super[BitcoinSAsyncFixtureTest].afterAll()
}
}
/** Test trait that caches a [[BitcoindV21RpcClient]] that is funded
* and available to use with fixtures
*/
trait BitcoindFixturesFundedCachedV21
extends BitcoinSAsyncFixtureTest
with BitcoindFixturesFundedCached
with CachedBitcoindV21 {
override type FixtureParam = BitcoindV21RpcClient
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val f: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
futOutcome = withV21FundedBitcoindCached(test, bitcoind)
fut <- futOutcome.toFuture
} yield fut
new FutureOutcome(f)
}
def withV21FundedBitcoindCached(
test: OneArgAsyncTest,
bitcoind: BitcoindV21RpcClient): FutureOutcome = {
makeDependentFixture[BitcoindV21RpcClient](
() => Future.successful(bitcoind),
{ case _ =>
Future.unit // don't want to destroy anything since it is cached
})(test)
}
override def afterAll(): Unit = {
super[CachedBitcoindV21].afterAll()
super[BitcoinSAsyncFixtureTest].afterAll()
}
}
/** Bitcoind fixtures with two cached bitcoind that are connected via p2p */
trait BitcoindFixturesCachedPair[T <: BitcoindRpcClient]
extends BitcoindFixturesCached
with CachedBitcoindPair[T] {
_: BitcoinSAsyncFixtureTest =>
def with2BitcoindsCached(
test: OneArgAsyncTest,
bitcoinds: NodePair[T]): FutureOutcome = {
makeDependentFixture[NodePair[T]](
() => Future.successful(bitcoinds),
destroy = { case _: NodePair[T] =>
//do nothing since we are caching bitcoinds
//the test trait may want to re-use them
Future.unit
}
)(test)
}
}
/** Bitcoind fixtures with two cached BitcoindV16RpcClient that are connected via p2p */
trait BitcoindFixturesCachedPairV16
extends BitcoinSAsyncFixtureTest
with BitcoindFixturesCachedPair[BitcoindV16RpcClient] {
override type FixtureParam = NodePair[BitcoindV16RpcClient]
override val version: BitcoindVersion = BitcoindVersion.V16
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val futOutcome = for {
pair <- clientsF
futOutcome = with2BitcoindsCached(test, pair)
f <- futOutcome.toFuture
} yield f
new FutureOutcome(futOutcome)
}
override def afterAll(): Unit = {
super[BitcoindFixturesCachedPair].afterAll()
super[BitcoinSAsyncFixtureTest].afterAll()
}
}
/** Bitcoind fixtures with two cached [[BitcoindV17RpcClient]] that are connected via p2p */
trait BitcoindFixturesCachedPairV17
extends BitcoinSAsyncFixtureTest
with BitcoindFixturesCachedPair[BitcoindV17RpcClient] {
override type FixtureParam = NodePair[BitcoindV17RpcClient]
override val version: BitcoindVersion = BitcoindVersion.V17
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val futOutcome = for {
pair <- clientsF
futOutcome = with2BitcoindsCached(test, pair)
f <- futOutcome.toFuture
} yield f
new FutureOutcome(futOutcome)
}
override def afterAll(): Unit = {
super[BitcoindFixturesCachedPair].afterAll()
super[BitcoinSAsyncFixtureTest].afterAll()
}
}
/** Bitcoind fixtures with two cached [[BitcoindV18RpcClient]] that are connected via p2p */
trait BitcoindFixturesCachedPairV18
extends BitcoinSAsyncFixtureTest
with BitcoindFixturesCachedPair[BitcoindV18RpcClient] {
override type FixtureParam = NodePair[BitcoindV18RpcClient]
override val version: BitcoindVersion = BitcoindVersion.V18
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val futOutcome = for {
pair <- clientsF
futOutcome = with2BitcoindsCached(test, pair)
f <- futOutcome.toFuture
} yield f
new FutureOutcome(futOutcome)
}
override def afterAll(): Unit = {
super[BitcoindFixturesCachedPair].afterAll()
super[BitcoinSAsyncFixtureTest].afterAll()
}
}
/** Bitcoind fixtures with two cached bitcoind rpc clients that are [[BitcoindVersion.newest]] that are connected via p2p */
trait BitcoindFixturesCachedPairNewest
extends BitcoinSAsyncFixtureTest
with BitcoindFixturesCachedPair[BitcoindV21RpcClient] {
override type FixtureParam = NodePair[BitcoindV21RpcClient]
override val version: BitcoindVersion = BitcoindVersion.newest
override def afterAll(): Unit = {
super[BitcoindFixturesCachedPair].afterAll()
super[BitcoinSAsyncFixtureTest].afterAll()
}
}
/** Bitcoind fixtures with three cached bitcoind that are connected via p2p */
trait BitcoindFixturesCachedTriple[T <: BitcoindRpcClient]
extends BitcoinSAsyncFixtureTest
with BitcoindFixturesCached
with CachedBitcoindTriple[T] {
def with3BitcoindsCached(
test: OneArgAsyncTest,
bitcoinds: NodeTriple[T]): FutureOutcome = {
makeDependentFixture[NodeTriple[T]](
() => Future.successful(bitcoinds),
destroy = { case _: NodeTriple[T] =>
//do nothing since we are caching bitcoinds
//the test trait may want to re-use them
Future.unit
}
)(test)
}
override def afterAll(): Unit = {
super[CachedBitcoindTriple].afterAll()
super[BitcoinSAsyncFixtureTest].afterAll()
}
}

View File

@ -482,7 +482,8 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
system: ActorSystem): Future[Vector[Vector[DoubleSha256DigestBE]]] = {
import system.dispatcher
val sliding = ListUtil.rotateHead(clients)
val sliding: Vector[Vector[BitcoindRpcClient]] =
ListUtil.rotateHead(clients)
val initF = Future.successful(Vector.empty[Vector[DoubleSha256DigestBE]])
@ -666,13 +667,9 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
connectedPairsF.map(_ => ())
}
/** Generates a vector of connected and started RPC clients. They all have
* spenable money in their wallet.
*/
private def createNodeSequence[T <: BitcoindRpcClient](
numNodes: Int,
version: BitcoindVersion,
clientAccum: RpcClientAccum)(implicit
version: BitcoindVersion)(implicit
system: ActorSystem): Future[Vector[T]] = {
import system.dispatcher
@ -708,8 +705,6 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
// methods calling this make sure that the version
// arg and the type arg matches up
val rpcT = rpc.asInstanceOf[T]
clientAccum += rpcT
rpcT
}.toVector
@ -722,7 +717,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
for {
pairs <- pairsF
_ <- connectPairs(pairs)
_ <- BitcoindRpcTestUtil.generateAllAndSync(clients, blocks = 200)
_ <- BitcoindRpcTestUtil.generateAllAndSync(clients, blocks = 101)
} yield clients
}
@ -732,7 +727,18 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
system: ActorSystem): Future[(T, T)] = {
import system.dispatcher
createNodeSequence[T](numNodes = 2, version, clientAccum).map {
createNodePairInternal[T](version).map { pair =>
clientAccum.++=(Vector(pair._1, pair._2))
pair
}
}
private def createNodePairInternal[T <: BitcoindRpcClient](
version: BitcoindVersion)(implicit
system: ActorSystem): Future[(T, T)] = {
import system.dispatcher
createNodeSequence[T](numNodes = 2, version).map {
case first +: second +: _ => (first, second)
case _: Vector[BitcoindRpcClient] =>
throw new RuntimeException("Did not get two clients!")
@ -742,55 +748,58 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
/** Returns a pair of [[org.bitcoins.rpc.client.common.BitcoindRpcClient BitcoindRpcClient]]
* that are connected with some blocks in the chain
*/
def createNodePair(clientAccum: RpcClientAccum = Vector.newBuilder)(implicit
def createNodePair[T <: BitcoindRpcClient](
clientAccum: RpcClientAccum = Vector.newBuilder)(implicit
system: ActorSystem): Future[(BitcoindRpcClient, BitcoindRpcClient)] =
createNodePairInternal(BitcoindVersion.Unknown, clientAccum)
createNodePair[T](BitcoindVersion.newest).map { pair =>
clientAccum.++=(Vector(pair._1, pair._2))
pair
}(system.dispatcher)
def createNodePair[T <: BitcoindRpcClient](version: BitcoindVersion)(implicit
system: ActorSystem): Future[(T, T)] = {
createNodePairInternal(version)
}
/** Returns a pair of [[org.bitcoins.rpc.client.v16.BitcoindV16RpcClient BitcoindV16RpcClient]]
* that are connected with some blocks in the chain
*/
def createNodePairV16(
clientAccum: RpcClientAccum = Vector.newBuilder)(implicit
def createNodePairV16(clientAccum: RpcClientAccum)(implicit
system: ActorSystem): Future[(BitcoindV16RpcClient, BitcoindV16RpcClient)] =
createNodePairInternal(BitcoindVersion.V16, clientAccum)
/** Returns a pair of [[org.bitcoins.rpc.client.v17.BitcoindV17RpcClient BitcoindV17RpcClient]]
* that are connected with some blocks in the chain
*/
def createNodePairV17(
clientAccum: RpcClientAccum = Vector.newBuilder)(implicit
def createNodePairV17(clientAccum: RpcClientAccum)(implicit
system: ActorSystem): Future[(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
def createNodePairV18(clientAccum: RpcClientAccum)(implicit
system: ActorSystem): Future[(BitcoindV18RpcClient, BitcoindV18RpcClient)] =
createNodePairInternal(BitcoindVersion.V18, clientAccum)
/** Returns a pair of [[org.bitcoins.rpc.client.v19.BitcoindV19RpcClient BitcoindV19RpcClient]]
* that are connected with some blocks in the chain
*/
def createNodePairV19(
clientAccum: RpcClientAccum = Vector.newBuilder)(implicit
def createNodePairV19(clientAccum: RpcClientAccum)(implicit
system: ActorSystem): Future[(BitcoindV19RpcClient, BitcoindV19RpcClient)] =
createNodePairInternal(BitcoindVersion.V19, clientAccum)
/** Returns a pair of [[org.bitcoins.rpc.client.v20.BitcoindV20RpcClient BitcoindV20RpcClient]]
* that are connected with some blocks in the chain
*/
def createNodePairV20(
clientAccum: RpcClientAccum = Vector.newBuilder)(implicit
def createNodePairV20(clientAccum: RpcClientAccum)(implicit
system: ActorSystem): Future[(BitcoindV20RpcClient, BitcoindV20RpcClient)] =
createNodePairInternal(BitcoindVersion.V20, clientAccum)
/** Returns a pair of [[org.bitcoins.rpc.client.v21.BitcoindV21RpcClient BitcoindV21RpcClient]]
* that are connected with some blocks in the chain
*/
def createNodePairV21(
clientAccum: RpcClientAccum = Vector.newBuilder)(implicit
def createNodePairV21(clientAccum: RpcClientAccum)(implicit
system: ActorSystem): Future[(BitcoindV21RpcClient, BitcoindV21RpcClient)] =
createNodePairInternal(BitcoindVersion.V21, clientAccum)
@ -803,7 +812,23 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
)(implicit system: ActorSystem): Future[(T, T, T)] = {
import system.dispatcher
createNodeSequence[T](numNodes = 3, version, clientAccum).map {
createNodeTripleInternal[T](version).map { nodes =>
clientAccum.+=(nodes._1)
clientAccum.+=(nodes._2)
clientAccum.+=(nodes._3)
nodes
}
}
/** Returns a triple of [[org.bitcoins.rpc.client.common.BitcoindRpcClient BitcoindRpcClient]]
* that are connected with some blocks in the chain
*/
private def createNodeTripleInternal[T <: BitcoindRpcClient](
version: BitcoindVersion
)(implicit system: ActorSystem): Future[(T, T, T)] = {
import system.dispatcher
createNodeSequence[T](numNodes = 3, version).map {
case first +: second +: third +: _ => (first, second, third)
case _: Vector[T] =>
throw new RuntimeException("Did not get three clients!")
@ -814,31 +839,39 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
* that are connected with some blocks in the chain
*/
def createNodeTriple(
clientAccum: RpcClientAccum = Vector.newBuilder
clientAccum: RpcClientAccum
)(implicit system: ActorSystem): Future[
(BitcoindRpcClient, BitcoindRpcClient, BitcoindRpcClient)] = {
createNodeTripleInternal(BitcoindVersion.Unknown, clientAccum)
}
/** Returns a triple of org.bitcoins.rpc.client.common.BitcoindRpcClient BitcoindRpcClient
* that are connected with some blocks in the chain
*/
def createNodeTriple[T <: BitcoindRpcClient](version: BitcoindVersion)(
implicit system: ActorSystem): Future[(T, T, T)] = {
createNodeTripleInternal(version)
}
/** @return a triple of [[org.bitcoins.rpc.client.v17.BitcoindV17RpcClient BitcoindV17RpcClient]]
* that are connected with some blocks in the chain
*/
def createNodeTripleV17(
clientAccum: RpcClientAccum = Vector.newBuilder
clientAccum: RpcClientAccum
)(implicit system: ActorSystem): Future[
(BitcoindV17RpcClient, BitcoindV17RpcClient, BitcoindV17RpcClient)] = {
createNodeTripleInternal(BitcoindVersion.V17, clientAccum)
}
def createNodeTripleV18(
clientAccum: RpcClientAccum = Vector.newBuilder
clientAccum: RpcClientAccum
)(implicit system: ActorSystem): Future[
(BitcoindV18RpcClient, BitcoindV18RpcClient, BitcoindV18RpcClient)] = {
createNodeTripleInternal(BitcoindVersion.V18, clientAccum)
}
def createNodeTripleV19(
clientAccum: RpcClientAccum = Vector.newBuilder
clientAccum: RpcClientAccum
)(implicit system: ActorSystem): Future[
(BitcoindV19RpcClient, BitcoindV19RpcClient, BitcoindV19RpcClient)] = {
createNodeTripleInternal(BitcoindVersion.V19, clientAccum)
@ -890,6 +923,10 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
v17.signRawTransactionWithWallet(transaction, utxoDeps)
case v16: BitcoindV16RpcClient =>
v16.signRawTransaction(transaction, utxoDeps)
case v20: BitcoindV20RpcClient =>
v20.signRawTransactionWithWallet(transaction)
case v21: BitcoindV21RpcClient =>
v21.signRawTransactionWithWallet(transaction)
case unknown: BitcoindRpcClient =>
val v16T = BitcoindV16RpcClient.fromUnknownVersion(unknown)
val v17T = BitcoindV17RpcClient.fromUnknownVersion(unknown)
@ -1064,7 +1101,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
*/
def startedBitcoindRpcClient(
instance: BitcoindInstance = BitcoindRpcTestUtil.instance(),
clientAccum: RpcClientAccum = Vector.newBuilder)(implicit
clientAccum: RpcClientAccum)(implicit
system: ActorSystem): Future[BitcoindRpcClient] = {
implicit val ec: ExecutionContextExecutor = system.dispatcher
require(

View File

@ -0,0 +1,208 @@
package org.bitcoins.testkit.rpc
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
import org.bitcoins.rpc.client.v17.BitcoindV17RpcClient
import org.bitcoins.rpc.client.v18.BitcoindV18RpcClient
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
import org.bitcoins.rpc.client.v20.BitcoindV20RpcClient
import org.bitcoins.rpc.client.v21.BitcoindV21RpcClient
import org.bitcoins.rpc.util.{NodePair, NodeTriple}
import org.bitcoins.testkit.fixtures.BitcoinSFixture
import org.bitcoins.testkit.util.BitcoinSAkkaAsyncTest
import java.util.concurrent.atomic.{AtomicBoolean, AtomicReference}
import scala.concurrent.{Await, Future}
/** A trait that holds a cached instance of a [[org.bitcoins.rpc.client.common.BitcoindRpcClient]]
* This is useful for using with fixtures to avoid creating a new bitcoind everytime a
* new test is run.
*
* The idea is our wallet/chain/node can just use the cached bitcoind rather than a fresh one.
* This does mean that test cases have to be written in such a way where assertions
* are not dependent on specific bitcoind state.
*/
trait CachedBitcoind[T <: BitcoindRpcClient] { _: BitcoinSAkkaAsyncTest => }
trait CachedBitcoindFunded[T <: BitcoindRpcClient] extends CachedBitcoind[T] {
_: BitcoinSAkkaAsyncTest =>
/** Flag to indicate if the bitcoind was used
*
* If we don't have this, we have no way
* to know if we should cleanup the cached bitcoind
* in the [[afterAll()]] method or not.
*
* We do not want to accidentally create a bitcoind
* inside of [[afterAll()]] just to have it
* cleaned up in the same method.
*/
protected val isBitcoindUsed: AtomicBoolean = new AtomicBoolean(false)
/** The bitcoind instance, lazyily created */
protected lazy val cachedBitcoindWithFundsF: Future[BitcoindRpcClient] = {
val _ = isBitcoindUsed.set(true)
BitcoinSFixture
.createBitcoindWithFunds(None)
}
override def afterAll(): Unit = {
if (isBitcoindUsed.get()) {
//if it was used, shut down the cached bitcoind
val stoppedF = for {
cachedBitcoind <- cachedBitcoindWithFundsF
_ <- BitcoindRpcTestUtil.stopServer(cachedBitcoind)
} yield ()
Await.result(stoppedF, duration)
} else {
//do nothing since bitcoind wasn't used
}
}
}
trait CachedBitcoindNewest extends CachedBitcoindFunded[BitcoindRpcClient] {
_: BitcoinSAkkaAsyncTest =>
override protected lazy val cachedBitcoindWithFundsF: Future[
BitcoindRpcClient] = {
val _ = isBitcoindUsed.set(true)
BitcoinSFixture
.createBitcoindWithFunds(Some(BitcoindVersion.newest))
}
}
trait CachedBitcoindV17 extends CachedBitcoindFunded[BitcoindV17RpcClient] {
_: BitcoinSAkkaAsyncTest =>
override protected lazy val cachedBitcoindWithFundsF: Future[
BitcoindV17RpcClient] = {
val _ = isBitcoindUsed.set(true)
BitcoinSFixture
.createBitcoindWithFunds(Some(BitcoindVersion.V17))
.map(_.asInstanceOf[BitcoindV17RpcClient])
}
}
trait CachedBitcoindV18 extends CachedBitcoindFunded[BitcoindV18RpcClient] {
_: BitcoinSAkkaAsyncTest =>
override protected lazy val cachedBitcoindWithFundsF: Future[
BitcoindV18RpcClient] = {
val _ = isBitcoindUsed.set(true)
BitcoinSFixture
.createBitcoindWithFunds(Some(BitcoindVersion.V18))
.map(_.asInstanceOf[BitcoindV18RpcClient])
}
}
trait CachedBitcoindV19 extends CachedBitcoindFunded[BitcoindV19RpcClient] {
_: BitcoinSAkkaAsyncTest =>
override protected lazy val cachedBitcoindWithFundsF: Future[
BitcoindV19RpcClient] = {
val _ = isBitcoindUsed.set(true)
BitcoinSFixture
.createBitcoindWithFunds(Some(BitcoindVersion.V19))
.map(_.asInstanceOf[BitcoindV19RpcClient])
}
}
trait CachedBitcoindV20 extends CachedBitcoindFunded[BitcoindV20RpcClient] {
_: BitcoinSAkkaAsyncTest =>
override protected lazy val cachedBitcoindWithFundsF: Future[
BitcoindV20RpcClient] = {
val _ = isBitcoindUsed.set(true)
BitcoinSFixture
.createBitcoindWithFunds(Some(BitcoindVersion.V20))
.map(_.asInstanceOf[BitcoindV20RpcClient])
}
}
trait CachedBitcoindV21 extends CachedBitcoindFunded[BitcoindV21RpcClient] {
_: BitcoinSAkkaAsyncTest =>
override protected lazy val cachedBitcoindWithFundsF: Future[
BitcoindV21RpcClient] = {
val _ = isBitcoindUsed.set(true)
BitcoinSFixture
.createBitcoindWithFunds(Some(BitcoindVersion.V21))
.map(_.asInstanceOf[BitcoindV21RpcClient])
}
}
trait CachedBitcoindCollection[T <: BitcoindRpcClient]
extends CachedBitcoind[T] {
_: BitcoinSAkkaAsyncTest =>
/** The version of bitcoind we are creating in the collection
* By default, we just use the newest version of bitcoind
*/
def version: BitcoindVersion
/** Flag to indicate if the bitcoinds were used
*
* If we don't have this, we have no way
* to know if we should cleanup the cached bitcoind
* in the [[afterAll()]] method or not.
*
* We do not want to accidentally create a bitcoind
* inside of [[afterAll()]] just to have it
* cleaned up in the same method.
*/
protected val isClientsUsed: AtomicBoolean = new AtomicBoolean(false)
protected lazy val cachedClients: AtomicReference[
Vector[BitcoindRpcClient]] = {
new AtomicReference[Vector[BitcoindRpcClient]](Vector.empty)
}
override def afterAll(): Unit = {
if (isClientsUsed.get()) {
//if it was used, shut down the cached bitcoind
val clients = cachedClients.get()
val stoppedF = for {
_ <- BitcoindRpcTestUtil.stopServers(clients)
} yield ()
Await.result(stoppedF, duration)
} else {
//do nothing since bitcoind wasn't used
}
}
}
trait CachedBitcoindPair[T <: BitcoindRpcClient]
extends CachedBitcoindCollection[T] {
_: BitcoinSAkkaAsyncTest =>
lazy val clientsF: Future[NodePair[T]] = {
BitcoindRpcTestUtil
.createNodePair[T](version)
.map(NodePair.fromTuple)
.map { tuple =>
isClientsUsed.set(true)
val clients = cachedClients.get()
cachedClients.set(clients ++ tuple.toVector)
tuple
}
}
}
trait CachedBitcoindTriple[T <: BitcoindRpcClient]
extends CachedBitcoindCollection[T] {
_: BitcoinSAkkaAsyncTest =>
lazy val clientsF: Future[NodeTriple[T]] = {
BitcoindRpcTestUtil
.createNodeTriple[T](version)
.map(NodeTriple.fromTuple)
.map { triple =>
isClientsUsed.set(true)
val clients = cachedClients.get()
cachedClients.set(clients ++ triple.toVector)
triple
}
}
}

View File

@ -3,6 +3,8 @@ package org.bitcoins.testkit.util
import akka.actor.ActorSystem
import akka.testkit.TestKit
import akka.util.Timeout
import org.bitcoins.core.config.NetworkParameters
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkitcore.util.BaseAsyncTest
import org.scalatest._
import org.scalatest.flatspec.{AsyncFlatSpec, FixtureAsyncFlatSpec}
@ -18,6 +20,7 @@ trait BitcoinSAkkaAsyncTest extends BaseAsyncTest { this: AsyncTestSuite =>
implicit val system: ActorSystem = {
ActorSystem(s"${getClass.getSimpleName}-${System.currentTimeMillis()}")
}
implicit val networkParam: NetworkParameters = BitcoindRpcTestUtil.network
/** Needed because the default execution context will become overloaded
* if we do not specify a unique execution context for each suite

View File

@ -1,12 +1,11 @@
package org.bitcoins.testkit.util
import java.nio.file.Files
import org.bitcoins.core.config.NetworkParameters
import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import scala.collection.mutable
import scala.concurrent.Future
import scala.concurrent.{Await, Future}
abstract class BitcoindRpcTest extends BitcoinSAsyncTest {
@ -30,8 +29,6 @@ abstract class BitcoindRpcTest extends BitcoinSAsyncTest {
}
}
implicit val networkParam: NetworkParameters = BitcoindRpcTestUtil.network
/** Bitcoind RPC clients can be added to this builder
* as they are created in tests. After tests have
* stopped running (either by succeeding or failing)
@ -42,7 +39,8 @@ abstract class BitcoindRpcTest extends BitcoinSAsyncTest {
Vector[BitcoindRpcClient]] = Vector.newBuilder
override def afterAll(): Unit = {
BitcoindRpcTestUtil.stopServers(clientAccum.result())
val stopF = BitcoindRpcTestUtil.stopServers(clientAccum.result())
Await.result(stopF, duration)
super.afterAll()
}

View File

@ -32,7 +32,9 @@ case class BitcoindRpcTestClient(
case Some(client) => Future.successful(client)
case None =>
val clientF =
BitcoindRpcTestUtil.startedBitcoindRpcClient(bitcoindInstance)
BitcoindRpcTestUtil.startedBitcoindRpcClient(bitcoindInstance,
clientAccum =
Vector.newBuilder)
clientF.map { c =>
clientOpt = Some(c)
c

View File

@ -0,0 +1,125 @@
package org.bitcoins.testkit.wallet
import com.typesafe.config.{Config, ConfigFactory}
import org.bitcoins.core.api.chain.ChainQueryApi
import org.bitcoins.core.api.chain.ChainQueryApi.FilterResponse
import org.bitcoins.core.gcs.BlockFilter
import org.bitcoins.core.protocol.BlockStamp
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.crypto.DoubleSha256DigestBE
import org.bitcoins.db.AppConfig
import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.testkit.keymanager.KeyManagerTestUtil
import org.bitcoins.testkit.{BitcoinSTestAppConfig, EmbeddedPg}
import org.bitcoins.wallet.config.WalletAppConfig
import org.scalatest.AsyncTestSuite
import scala.concurrent.Future
/** Base test trait for all the tests in our walletTest module */
trait BaseWalletTest extends EmbeddedPg { _: AsyncTestSuite =>
override def beforeAll(): Unit = {
AppConfig.throwIfDefaultDatadir(getFreshConfig.walletConf)
super[EmbeddedPg].beforeAll()
}
override def afterAll(): Unit = {
super[EmbeddedPg].afterAll()
}
val legacyWalletConf: Config =
ConfigFactory.parseString("bitcoin-s.wallet.defaultAccountType = legacy")
val segwitWalletConf: Config =
ConfigFactory.parseString("bitcoin-s.wallet.defaultAccountType = segwit")
// This is a random block on testnet
val testBlockHash: DoubleSha256DigestBE = DoubleSha256DigestBE.fromHex(
"00000000496dcc754fabd97f3e2df0a7337eab417d75537fecf97a7ebb0e7c75")
/** Wallet config with data directory set to user temp directory */
implicit protected def getFreshConfig: BitcoinSAppConfig =
BitcoinSTestAppConfig.getSpvWithEmbeddedDbTestConfig(pgUrl)
implicit protected def getFreshWalletAppConfig: WalletAppConfig = {
getFreshConfig.walletConf
}
def getBIP39PasswordOpt(): Option[String] =
KeyManagerTestUtil.bip39PasswordOpt
def chainQueryApi: ChainQueryApi =
new ChainQueryApi {
/** Gets the height of the given block */
override def getBlockHeight(
blockHash: DoubleSha256DigestBE): Future[Option[Int]] =
if (blockHash == testBlockHash)
Future.successful(Some(1))
else FutureUtil.none
/** Gets the hash of the block that is what we consider "best" */
override def getBestBlockHash(): Future[DoubleSha256DigestBE] =
Future.successful(testBlockHash)
/** Gets number of confirmations for the given block hash */
override def getNumberOfConfirmations(
blockHash: DoubleSha256DigestBE): Future[Option[Int]] =
if (blockHash == testBlockHash)
Future.successful(Some(6))
else FutureUtil.none
/** Gets the number of compact filters in the database */
override def getFilterCount(): Future[Int] = Future.successful(1)
/** Returns the block height of the given block stamp */
override def getHeightByBlockStamp(blockStamp: BlockStamp): Future[Int] =
Future.successful(1)
override def getFiltersBetweenHeights(
startHeight: Int,
endHeight: Int): Future[Vector[FilterResponse]] =
Future.successful {
import scodec.bits._
// This is a filter for the random block on testnet
val filterBytes: ByteVector =
hex"fd2701f0ed169ad16107a8a74609b9e4de3c6133c564f79923ca228805d3" ++
hex"8e3efc796c4b35034cb573b10b759cdda5efd19e1cdb4d343afcb06455fa" ++
hex"820b06eca828ad61d3377fa464f3bd06ff4432310a363f667e13d09ba993" ++
hex"264c703a0aa668b33eaa555bd3e93ac85dfde380ab723aafd407dfa13ffe" ++
hex"2e7ddf6f452bd0d977617c4ab2dc3b38c26810023984ad57890e3cf34cfc" ++
hex"2d4a6973b9430ede26bfd9f5bb24e043d48483d84b9025d0a940b15f13fc" ++
hex"0a1e77abd7626869f417c7710e9a6315477691d7c4e2c50f0e776755a62a" ++
hex"b6f0e8eb7a3be8d1a8c3d9dd4602efc5146f0d431d1669378d7afa03c7b9" ++
hex"84d9b0b78007abb6e7c036156e5186d1d79a2f37daecfcbe8821cf42851c" ++
hex"b10ef0c359307d54e53078eb631f02c067a474dceb484da20bc0e7c5451a" ++
hex"b957f46b306caa82938b19bb34fd76c5cc07e048932524704dec8f72c91c" ++
hex"d5ee1f4648de839047a0bea0d4d4d66c19cfccc2b5f285a84af18114f608" ++
hex"f144391648aedfb5ffcccbb51272512d6ba9a2e19a47cebe5b50a8a7073a" ++
hex"1c24059440444047a41bdbab16f61bc4b0ee8987de82fd25cc62abc86e2b" ++
hex"577fc55175be138680df7253a8bcae9d9954391d3bed806ce5a6869b4553" ++
hex"0f214486b1b7f0347efcfde58ca0882f059f7b1541c74506930897c78e23" ++
hex"a6c94b49856369606ed652b8c7402a49f289cb5d1098bb999112225327e0" ++
hex"a32efd2bcd192a2ffbd1997c6a3b7d1a9445bc31fb57485ebe0c431e482b" ++
hex"04e509e557cff107cee08a45c22aa3cbdcb9d305bd95c919e90239e0ec29" ++
hex"2a5418a6151f431e8ab82278b3d816ecd483f43d3d657dae9996cc523fdd" ++
hex"242c4e01935db91a2936e9398ff7278b8a3430eed99ad25fc2a41afc0b4a" ++
hex"e417f6c1785414607cfa13f04173740333a5b58655c74a51deddb38cf8c3" ++
hex"d50b7d2ccf380cad34a5c341e7155494cc4560dff3b19bf88b4d73e9ce76" ++
hex"cbeff573fe93674e4a752d06d5321ff00a4582d62683fb4986d36eaec825" ++
hex"c14d41b2d5aefaf539e989f7fa097eac657c70b975c56e26b73fb9401ce3" ++
hex"81502f0883d52c6a3bcc956e0ea1787f0717d0205fecfe55b01edb1ac0"
Vector(
FilterResponse(compactFilter = BlockFilter
.fromBytes(filterBytes, testBlockHash.flip),
blockHash = testBlockHash,
blockHeight = 1))
}
override def epochSecondToBlockHeight(time: Long): Future[Int] =
Future.successful(0)
}
}

View File

@ -8,8 +8,7 @@ import org.bitcoins.core.api.chain.ChainQueryApi.FilterResponse
import org.bitcoins.core.api.feeprovider.FeeRateApi
import org.bitcoins.core.api.node.NodeApi
import org.bitcoins.core.currency._
import org.bitcoins.core.gcs.BlockFilter
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
import org.bitcoins.core.protocol.BlockStamp
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.core.wallet.fee._
@ -26,14 +25,14 @@ import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.server.BitcoinSAppConfig._
import org.bitcoins.testkitcore.Implicits.GeneratorOps
import org.bitcoins.testkit.EmbeddedPg
import org.bitcoins.testkit.chain.SyncUtil
import org.bitcoins.testkitcore.gen._
import org.bitcoins.testkit.fixtures.BitcoinSFixture
import org.bitcoins.testkit.keymanager.KeyManagerTestUtil
import org.bitcoins.testkit.util.FileUtil
import org.bitcoins.testkit.wallet.FundWalletUtil.FundedWallet
import org.bitcoins.testkit.{BitcoinSTestAppConfig, EmbeddedPg}
import org.bitcoins.testkitcore.Implicits.GeneratorOps
import org.bitcoins.testkitcore.gen._
import org.bitcoins.wallet.config.WalletAppConfig
import org.bitcoins.wallet.{Wallet, WalletCallbacks, WalletLogger}
import org.scalatest._
@ -41,117 +40,21 @@ import org.scalatest._
import scala.concurrent._
import scala.concurrent.duration._
trait BitcoinSWalletTest extends BitcoinSFixture with EmbeddedPg {
trait BitcoinSWalletTest
extends BitcoinSFixture
with BaseWalletTest
with EmbeddedPg {
import BitcoinSWalletTest._
/** Wallet config with data directory set to user temp directory */
implicit protected def getFreshConfig: BitcoinSAppConfig =
BitcoinSTestAppConfig.getSpvWithEmbeddedDbTestConfig(pgUrl)
implicit protected def getFreshWalletAppConfig: WalletAppConfig = {
getFreshConfig.walletConf
}
override def beforeAll(): Unit = {
AppConfig.throwIfDefaultDatadir(getFreshConfig.walletConf)
super[EmbeddedPg].beforeAll()
}
override def afterAll(): Unit = {
Await.result(getFreshConfig.chainConf.stop(), 1.minute)
Await.result(getFreshConfig.nodeConf.stop(), 1.minute)
Await.result(getFreshConfig.walletConf.stop(), 1.minute)
super[EmbeddedPg].afterAll()
super.afterAll()
}
def nodeApi: NodeApi = MockNodeApi
val testAddr: BitcoinAddress =
BitcoinAddress.fromString("bcrt1qlhctylgvdsvaanv539rg7hyn0sjkdm23y70kgq")
val legacyWalletConf: Config =
ConfigFactory.parseString("bitcoin-s.wallet.defaultAccountType = legacy")
val segwitWalletConf: Config =
ConfigFactory.parseString("bitcoin-s.wallet.defaultAccountType = segwit")
// This is a random block on testnet
val testBlockHash: DoubleSha256DigestBE = DoubleSha256DigestBE.fromHex(
"00000000496dcc754fabd97f3e2df0a7337eab417d75537fecf97a7ebb0e7c75")
def chainQueryApi: ChainQueryApi =
new ChainQueryApi {
/** Gets the height of the given block */
override def getBlockHeight(
blockHash: DoubleSha256DigestBE): Future[Option[Int]] =
if (blockHash == testBlockHash)
Future.successful(Some(1))
else FutureUtil.none
/** Gets the hash of the block that is what we consider "best" */
override def getBestBlockHash(): Future[DoubleSha256DigestBE] =
Future.successful(testBlockHash)
/** Gets number of confirmations for the given block hash */
override def getNumberOfConfirmations(
blockHash: DoubleSha256DigestBE): Future[Option[Int]] =
if (blockHash == testBlockHash)
Future.successful(Some(6))
else FutureUtil.none
/** Gets the number of compact filters in the database */
override def getFilterCount(): Future[Int] = Future.successful(1)
/** Returns the block height of the given block stamp */
override def getHeightByBlockStamp(blockStamp: BlockStamp): Future[Int] =
Future.successful(1)
override def getFiltersBetweenHeights(
startHeight: Int,
endHeight: Int): Future[Vector[FilterResponse]] =
Future.successful {
import scodec.bits._
// This is a filter for the random block on testnet
val filterBytes: ByteVector =
hex"fd2701f0ed169ad16107a8a74609b9e4de3c6133c564f79923ca228805d3" ++
hex"8e3efc796c4b35034cb573b10b759cdda5efd19e1cdb4d343afcb06455fa" ++
hex"820b06eca828ad61d3377fa464f3bd06ff4432310a363f667e13d09ba993" ++
hex"264c703a0aa668b33eaa555bd3e93ac85dfde380ab723aafd407dfa13ffe" ++
hex"2e7ddf6f452bd0d977617c4ab2dc3b38c26810023984ad57890e3cf34cfc" ++
hex"2d4a6973b9430ede26bfd9f5bb24e043d48483d84b9025d0a940b15f13fc" ++
hex"0a1e77abd7626869f417c7710e9a6315477691d7c4e2c50f0e776755a62a" ++
hex"b6f0e8eb7a3be8d1a8c3d9dd4602efc5146f0d431d1669378d7afa03c7b9" ++
hex"84d9b0b78007abb6e7c036156e5186d1d79a2f37daecfcbe8821cf42851c" ++
hex"b10ef0c359307d54e53078eb631f02c067a474dceb484da20bc0e7c5451a" ++
hex"b957f46b306caa82938b19bb34fd76c5cc07e048932524704dec8f72c91c" ++
hex"d5ee1f4648de839047a0bea0d4d4d66c19cfccc2b5f285a84af18114f608" ++
hex"f144391648aedfb5ffcccbb51272512d6ba9a2e19a47cebe5b50a8a7073a" ++
hex"1c24059440444047a41bdbab16f61bc4b0ee8987de82fd25cc62abc86e2b" ++
hex"577fc55175be138680df7253a8bcae9d9954391d3bed806ce5a6869b4553" ++
hex"0f214486b1b7f0347efcfde58ca0882f059f7b1541c74506930897c78e23" ++
hex"a6c94b49856369606ed652b8c7402a49f289cb5d1098bb999112225327e0" ++
hex"a32efd2bcd192a2ffbd1997c6a3b7d1a9445bc31fb57485ebe0c431e482b" ++
hex"04e509e557cff107cee08a45c22aa3cbdcb9d305bd95c919e90239e0ec29" ++
hex"2a5418a6151f431e8ab82278b3d816ecd483f43d3d657dae9996cc523fdd" ++
hex"242c4e01935db91a2936e9398ff7278b8a3430eed99ad25fc2a41afc0b4a" ++
hex"e417f6c1785414607cfa13f04173740333a5b58655c74a51deddb38cf8c3" ++
hex"d50b7d2ccf380cad34a5c341e7155494cc4560dff3b19bf88b4d73e9ce76" ++
hex"cbeff573fe93674e4a752d06d5321ff00a4582d62683fb4986d36eaec825" ++
hex"c14d41b2d5aefaf539e989f7fa097eac657c70b975c56e26b73fb9401ce3" ++
hex"81502f0883d52c6a3bcc956e0ea1787f0717d0205fecfe55b01edb1ac0"
Vector(
FilterResponse(compactFilter = BlockFilter
.fromBytes(filterBytes, testBlockHash.flip),
blockHash = testBlockHash,
blockHeight = 1))
}
override def epochSecondToBlockHeight(time: Long): Future[Int] =
Future.successful(0)
}
/** Lets you customize the parameters for the created wallet */
val withNewConfiguredWallet: Config => OneArgAsyncTest => FutureOutcome = {
walletConfig =>
@ -265,27 +168,6 @@ trait BitcoinSWalletTest extends BitcoinSFixture with EmbeddedPg {
makeDependentFixture(builder, destroy = destroyWalletWithBitcoind)(test)
}
def withFundedWalletAndBitcoind(
test: OneArgAsyncTest,
bip39PasswordOpt: Option[String]): FutureOutcome = {
val builder: () => Future[WalletWithBitcoind] = { () =>
for {
bitcoind <-
BitcoinSFixture
.createBitcoindWithFunds(None)
walletWithBitcoind <- createWalletWithBitcoindCallbacks(
bitcoind = bitcoind,
bip39PasswordOpt = bip39PasswordOpt)
fundedWallet <- fundWalletWithBitcoind(walletWithBitcoind)
_ <- SyncUtil.syncWalletFullBlocks(wallet = fundedWallet.wallet,
bitcoind = bitcoind)
_ <- BitcoinSWalletTest.awaitWalletBalances(fundedWallet)
} yield fundedWallet
}
makeDependentFixture(builder, destroy = destroyWalletWithBitcoind)(test)
}
def withFundedWalletAndBitcoindV19(
test: OneArgAsyncTest,
bip39PasswordOpt: Option[String]): FutureOutcome = {
@ -337,9 +219,6 @@ trait BitcoinSWalletTest extends BitcoinSFixture with EmbeddedPg {
}
makeDependentFixture(builder, destroy = destroy)(test)
}
def getBIP39PasswordOpt(): Option[String] =
KeyManagerTestUtil.bip39PasswordOpt
}
object BitcoinSWalletTest extends WalletLogger {
@ -586,10 +465,20 @@ object BitcoinSWalletTest extends WalletLogger {
import system.dispatcher
for {
created <- createWalletWithBitcoindCallbacks(bitcoind, bip39PasswordOpt)
} yield WalletWithBitcoindV19(created.wallet, bitcoind)
}
def createWalletWithBitcoind(
bitcoind: BitcoindRpcClient,
bip39PasswordOpt: Option[String])(implicit
system: ActorSystem,
config: BitcoinSAppConfig): Future[WalletWithBitcoindRpc] = {
import system.dispatcher
for {
created <- createWalletWithBitcoindCallbacks(bitcoind, bip39PasswordOpt)
} yield WalletWithBitcoindRpc(created.wallet, bitcoind)
}
def createWalletWithBitcoind(
wallet: Wallet,
bitcoindRpcClient: BitcoindRpcClient

View File

@ -0,0 +1,211 @@
package org.bitcoins.testkit.wallet
import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
import org.bitcoins.testkit.EmbeddedPg
import org.bitcoins.testkit.chain.SyncUtil
import org.bitcoins.testkit.fixtures.BitcoinSFixture
import org.bitcoins.testkit.rpc.{
BitcoindRpcTestUtil,
CachedBitcoind,
CachedBitcoindNewest,
CachedBitcoindV19
}
import org.bitcoins.testkit.wallet.BitcoinSWalletTest.{
createWalletWithBitcoind,
createWalletWithBitcoindCallbacks,
destroyWallet,
fundWalletWithBitcoind
}
import org.scalatest.{FutureOutcome, Outcome}
import scala.concurrent.Future
import scala.util.{Failure, Success}
/** Bitcoin-s wallet test trait that uses cached bitcoinds
* rather than fresh bitcoinds.
*
* This should be used by default unless there is a reason your
* test suite needs fresh bitcoin's for every unit test inside of it
*/
trait BitcoinSWalletTestCachedBitcoind
extends BitcoinSFixture
with BaseWalletTest
with EmbeddedPg { _: CachedBitcoind[_] =>
/** Creates a funded wallet fixture with bitcoind
* This is different than [[withFundedWalletAndBitcoind()]]
* in the sense that it does NOT destroy the given bitcoind.
* It is the responsibility of the caller of this method to
* do that, if needed.
*/
def withFundedWalletAndBitcoindCached(
test: OneArgAsyncTest,
bip39PasswordOpt: Option[String],
bitcoind: BitcoindRpcClient): FutureOutcome = {
val builder: () => Future[WalletWithBitcoind] = { () =>
for {
walletWithBitcoind <- createWalletWithBitcoindCallbacks(
bitcoind = bitcoind,
bip39PasswordOpt = bip39PasswordOpt)
fundedWallet <- fundWalletWithBitcoind(walletWithBitcoind)
_ <- SyncUtil.syncWalletFullBlocks(wallet = fundedWallet.wallet,
bitcoind = bitcoind)
_ <- BitcoinSWalletTest.awaitWalletBalances(fundedWallet)
} yield fundedWallet
}
makeDependentFixture[WalletWithBitcoind](
builder,
{ case walletWithBitcoind: WalletWithBitcoind =>
destroyWallet(walletWithBitcoind.wallet)
})(test)
}
def withNewWalletAndBitcoindCached(
test: OneArgAsyncTest,
bip39PasswordOpt: Option[String],
bitcoind: BitcoindRpcClient): FutureOutcome = {
val builder: () => Future[WalletWithBitcoind] = composeBuildersAndWrap(
builder = { () =>
Future.successful(bitcoind)
},
dependentBuilder = { (bitcoind: BitcoindRpcClient) =>
createWalletWithBitcoind(bitcoind, bip39PasswordOpt)
},
wrap = (_: BitcoindRpcClient, walletWithBitcoind: WalletWithBitcoind) =>
walletWithBitcoind
)
makeDependentFixture[WalletWithBitcoind](
builder,
{ case walletWithBitcoind: WalletWithBitcoind =>
destroyWallet(walletWithBitcoind.wallet)
})(test)
}
def withFundedWalletAndBitcoind(
test: OneArgAsyncTest,
bip39PasswordOpt: Option[String]): FutureOutcome = {
val bitcoindF = BitcoinSFixture
.createBitcoindWithFunds(None)
//create a bitcoind, then pretend that it is cached
//so we can re-use code in withFundedWalletBitcoindCached
val resultF = for {
bitcoind <- bitcoindF
outcome = withFundedWalletAndBitcoindCached(test,
bip39PasswordOpt,
bitcoind)
f <- outcome.toFuture
} yield f
//since we aren't actually caching the bitcoind, we need
//to shut it down now
val stoppedBitcoind: Future[Outcome] = resultF.transformWith({
case Success(outcome) =>
stopBitcoind(bitcoindF)
.map(_ => outcome)
case Failure(err) =>
stopBitcoind(bitcoindF)
.flatMap(_ => Future.failed(err))
})
val futureOutcome = new FutureOutcome(stoppedBitcoind)
futureOutcome
}
/** Helper method to stop a Future[BitcoindRpcClient] */
private def stopBitcoind(
bitcoindF: Future[BitcoindRpcClient]): Future[Unit] = {
for {
bitcoind <- bitcoindF
_ <- BitcoindRpcTestUtil.stopServer(bitcoind)
} yield ()
}
}
trait BitcoinSWalletTestCachedBitcoindNewest
extends BitcoinSWalletTestCachedBitcoind
with CachedBitcoindNewest {
override def afterAll(): Unit = {
super[CachedBitcoindNewest].afterAll()
}
}
trait BitcoinSWalletTestCachedBitcoinV19
extends BitcoinSWalletTestCachedBitcoind
with CachedBitcoindV19 {
override def afterAll(): Unit = {
super[CachedBitcoindV19].afterAll()
}
/** Creates a funded wallet fixture with bitcoind
* This is different than [[withFundedWalletAndBitcoind()]]
* in the sense that it does NOT destroy the given bitcoind.
* It is the responsibility of the caller of this method to
* do that, if needed.
*/
def withFundedWalletAndBitcoindCachedV19(
test: OneArgAsyncTest,
bip39PasswordOpt: Option[String],
bitcoind: BitcoindV19RpcClient): FutureOutcome = {
val builder: () => Future[WalletWithBitcoindV19] = { () =>
for {
walletWithBitcoind <- createWalletWithBitcoindCallbacks(
bitcoind = bitcoind,
bip39PasswordOpt = bip39PasswordOpt)
walletWithBitcoindV19 = WalletWithBitcoindV19(walletWithBitcoind.wallet,
bitcoind)
fundedWallet <- fundWalletWithBitcoind[WalletWithBitcoindV19](
walletWithBitcoindV19)
_ <- SyncUtil.syncWalletFullBlocks(wallet = fundedWallet.wallet,
bitcoind = bitcoind)
_ <- BitcoinSWalletTest.awaitWalletBalances(fundedWallet)
} yield fundedWallet
}
makeDependentFixture[WalletWithBitcoind](
builder,
destroy = { case walletWithBitcoind: WalletWithBitcoind =>
destroyWallet(walletWithBitcoind.wallet)
})(test)
}
def withNewWalletAndBitcoindCachedV19(
test: OneArgAsyncTest,
bip39PasswordOpt: Option[String],
bitcoind: BitcoindV19RpcClient): FutureOutcome = {
val builder: () => Future[WalletWithBitcoind] = composeBuildersAndWrap(
builder = { () =>
Future.successful(bitcoind)
},
dependentBuilder = { (bitcoind: BitcoindV19RpcClient) =>
BitcoinSWalletTest.createWalletWithBitcoindV19(bitcoind,
bip39PasswordOpt)
},
wrap =
(_: BitcoindRpcClient, walletWithBitcoind: WalletWithBitcoindV19) =>
walletWithBitcoind
)
makeDependentFixture[WalletWithBitcoind](
builder,
{ case walletWithBitcoind: WalletWithBitcoind =>
destroyWallet(walletWithBitcoind.wallet)
})(test)
}
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val f: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
futOutcome = withFundedWalletAndBitcoindCachedV19(test,
getBIP39PasswordOpt(),
bitcoind)
fut <- futOutcome.toFuture
} yield fut
new FutureOutcome(f)
}
}

View File

@ -3,9 +3,13 @@ package org.bitcoins.wallet
import org.bitcoins.commons.jsonmodels.wallet.SyncHeightDescriptor
import org.bitcoins.core.currency._
import org.bitcoins.db.AppConfig
import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.server.{BitcoinSAppConfig, BitcoindRpcBackendUtil}
import org.bitcoins.testkit.fixtures.BitcoinSFixture
import org.bitcoins.testkit.util.BitcoinSAsyncTest
import org.bitcoins.testkit.rpc.{
BitcoindFixturesFundedCached,
CachedBitcoindNewest
}
import org.bitcoins.testkit.util.BitcoinSAsyncFixtureTest
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
import org.bitcoins.testkit.{BitcoinSTestAppConfig, EmbeddedPg}
import org.bitcoins.wallet.config.WalletAppConfig
@ -13,9 +17,16 @@ import org.bitcoins.wallet.config.WalletAppConfig
import scala.concurrent.Await
import scala.concurrent.duration.DurationInt
class BitcoindBackendTest extends BitcoinSAsyncTest with EmbeddedPg {
class BitcoindBackendTest
extends BitcoinSAsyncFixtureTest
with BitcoindFixturesFundedCached
with CachedBitcoindNewest
with EmbeddedPg {
override type FixtureParam = BitcoindRpcClient
/** Wallet config with data directory set to user temp directory */
implicit protected def config: BitcoinSAppConfig =
BitcoinSTestAppConfig.getNeutrinoWithEmbeddedDbTestConfig(pgUrl)
@ -32,15 +43,13 @@ class BitcoindBackendTest extends BitcoinSAsyncTest with EmbeddedPg {
Await.result(config.chainConf.stop(), 1.minute)
Await.result(config.nodeConf.stop(), 1.minute)
Await.result(config.walletConf.stop(), 1.minute)
super[EmbeddedPg].afterAll()
super.afterAll()
}
it must "correctly catch up to bitcoind" in {
it must "correctly catch up to bitcoind" in { bitcoind =>
val amountToSend = Bitcoins.one
for {
// Setup bitcoind
bitcoind <- BitcoinSFixture.createBitcoindWithFunds()
header <- bitcoind.getBestBlockHeader()
// Setup wallet

View File

@ -3,9 +3,13 @@ package org.bitcoins.wallet
import org.bitcoins.asyncutil.AsyncUtil
import org.bitcoins.core.currency._
import org.bitcoins.db.AppConfig
import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.server.{BitcoinSAppConfig, BitcoindRpcBackendUtil}
import org.bitcoins.testkit.fixtures.BitcoinSFixture
import org.bitcoins.testkit.util.BitcoinSAsyncTest
import org.bitcoins.testkit.rpc.{
BitcoindFixturesFundedCached,
CachedBitcoindNewest
}
import org.bitcoins.testkit.util.BitcoinSAsyncFixtureTest
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
import org.bitcoins.testkit.{BitcoinSTestAppConfig, EmbeddedPg}
import org.bitcoins.wallet.config.WalletAppConfig
@ -13,7 +17,13 @@ import org.bitcoins.wallet.config.WalletAppConfig
import scala.concurrent.Await
import scala.concurrent.duration.DurationInt
class BitcoindBlockPollingTest extends BitcoinSAsyncTest with EmbeddedPg {
class BitcoindBlockPollingTest
extends BitcoinSAsyncFixtureTest
with BitcoindFixturesFundedCached
with CachedBitcoindNewest
with EmbeddedPg {
override type FixtureParam = BitcoindRpcClient
/** Wallet config with data directory set to user temp directory */
implicit protected def config: BitcoinSAppConfig =
@ -32,15 +42,13 @@ class BitcoindBlockPollingTest extends BitcoinSAsyncTest with EmbeddedPg {
Await.result(config.chainConf.stop(), 1.minute)
Await.result(config.nodeConf.stop(), 1.minute)
Await.result(config.walletConf.stop(), 1.minute)
super[EmbeddedPg].afterAll()
super.afterAll()
}
it must "properly setup and poll blocks from bitcoind" in {
it must "properly setup and poll blocks from bitcoind" in { bitcoind =>
val amountToSend = Bitcoins.one
for {
// Setup bitcoind
bitcoind <- BitcoinSFixture.createBitcoindWithFunds()
blockCount <- bitcoind.getBlockCount
// Setup wallet

View File

@ -6,21 +6,29 @@ import org.bitcoins.core.wallet.builder.RawTxSigner
import org.bitcoins.core.wallet.utxo.StorageLocationTag.HotStorage
import org.bitcoins.core.wallet.utxo._
import org.bitcoins.testkit.wallet.{
BitcoinSWalletTest,
BitcoinSWalletTestCachedBitcoindNewest,
WalletTestUtil,
WalletWithBitcoind
}
import org.bitcoins.testkitcore.util.TestUtil
import org.scalatest.{Assertion, FutureOutcome}
import org.scalatest.{Assertion, FutureOutcome, Outcome}
import scala.concurrent.Future
class FundTransactionHandlingTest extends BitcoinSWalletTest {
class FundTransactionHandlingTest
extends BitcoinSWalletTestCachedBitcoindNewest {
override type FixtureParam = WalletWithBitcoind
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
withFundedWalletAndBitcoind(test, getBIP39PasswordOpt())
val f: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
futOutcome = withFundedWalletAndBitcoindCached(test,
getBIP39PasswordOpt(),
bitcoind)
fut <- futOutcome.toFuture
} yield fut
new FutureOutcome(f)
}
val destination: TransactionOutput =

View File

@ -5,16 +5,32 @@ import org.bitcoins.core.currency._
import org.bitcoins.core.gcs.FilterType
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.core.wallet.utxo.TxoState
import org.bitcoins.testkit.wallet.{BitcoinSWalletTest, WalletWithBitcoindV19}
import org.scalatest.FutureOutcome
import org.bitcoins.testkit.rpc.CachedBitcoindV19
import org.bitcoins.testkit.wallet.{
BitcoinSWalletTestCachedBitcoinV19,
WalletWithBitcoindV19
}
import org.scalatest.{FutureOutcome, Outcome}
class ProcessBlockTest extends BitcoinSWalletTest {
import scala.concurrent.Future
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
withNewWalletAndBitcoindV19(test, getBIP39PasswordOpt())
class ProcessBlockTest
extends BitcoinSWalletTestCachedBitcoinV19
with CachedBitcoindV19 {
override type FixtureParam = WalletWithBitcoindV19
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val f: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
futOutcome = withNewWalletAndBitcoindCachedV19(test,
getBIP39PasswordOpt(),
bitcoind)
fut <- futOutcome.toFuture
} yield fut
new FutureOutcome(f)
}
it must "process a block" in { param =>
val WalletWithBitcoindV19(wallet, bitcoind) = param
@ -83,7 +99,7 @@ class ProcessBlockTest extends BitcoinSWalletTest {
_ = assert(startingUtxos.isEmpty)
_ = assert(startingBalance == Satoshis.zero)
addr <- wallet.getNewAddress()
hashes <- bitcoind.generateToAddress(101, addr)
hashes <- bitcoind.generateToAddress(102, addr)
filters <- FutureUtil.sequentially(hashes)(
bitcoind.getBlockFilter(_, FilterType.Basic))
filtersWithBlockHash = hashes.map(_.flip).zip(filters.map(_.filter))

View File

@ -13,7 +13,7 @@ import scala.concurrent.Future
class ProcessTransactionTest extends BitcoinSWalletTest {
override type FixtureParam = WalletApi
def withFixture(test: OneArgAsyncTest): FutureOutcome = {
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
withNewWallet(test, getBIP39PasswordOpt())
}

View File

@ -7,15 +7,14 @@ import org.bitcoins.core.util.FutureUtil
import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.testkit.BitcoinSTestAppConfig
import org.bitcoins.testkit.wallet.{
BitcoinSWalletTest,
BitcoinSWalletTestCachedBitcoinV19,
WalletWithBitcoind,
WalletWithBitcoindV19
}
import org.scalatest.FutureOutcome
import scala.concurrent.Future
class RescanHandlingTest extends BitcoinSWalletTest {
class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoinV19 {
/** Wallet config with data directory set to user temp directory */
implicit override protected def getFreshConfig: BitcoinSAppConfig =
@ -23,10 +22,6 @@ class RescanHandlingTest extends BitcoinSWalletTest {
override type FixtureParam = WalletWithBitcoind
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
withFundedWalletAndBitcoindV19(test, getBIP39PasswordOpt())
}
behavior of "Wallet rescans"
it must "properly clear utxos and address for an account" in {

View File

@ -2,6 +2,7 @@ package org.bitcoins.wallet
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.number._
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.wallet.fee.SatoshisPerByte
@ -9,22 +10,35 @@ import org.bitcoins.core.wallet.utxo.TxoState
import org.bitcoins.core.wallet.utxo.TxoState._
import org.bitcoins.crypto.ECPublicKey
import org.bitcoins.testkit.wallet.{
BitcoinSWalletTest,
BitcoinSWalletTestCachedBitcoindNewest,
WalletWithBitcoind,
WalletWithBitcoindRpc
}
import org.scalatest.FutureOutcome
import org.scalatest.{FutureOutcome, Outcome}
class UTXOLifeCycleTest extends BitcoinSWalletTest {
import scala.concurrent.Future
class UTXOLifeCycleTest extends BitcoinSWalletTestCachedBitcoindNewest {
behavior of "Wallet Txo States"
override type FixtureParam = WalletWithBitcoind
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
withFundedWalletAndBitcoind(test, getBIP39PasswordOpt())
val f: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
futOutcome = withFundedWalletAndBitcoindCached(test,
getBIP39PasswordOpt(),
bitcoind)
fut <- futOutcome.toFuture
} yield fut
new FutureOutcome(f)
}
val testAddr: BitcoinAddress =
BitcoinAddress
.fromString("bcrt1qlhctylgvdsvaanv539rg7hyn0sjkdm23y70kgq")
it should "track a utxo state change to broadcast spent" in { param =>
val WalletWithBitcoindRpc(wallet, _) = param

View File

@ -3,19 +3,29 @@ package org.bitcoins.wallet
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.testkitcore.Implicits._
import org.bitcoins.testkit.wallet.{
BitcoinSWalletTest,
BitcoinSWalletTestCachedBitcoindNewest,
WalletWithBitcoind,
WalletWithBitcoindRpc
}
import org.scalatest.FutureOutcome
import org.scalatest.{FutureOutcome, Outcome}
class WalletBloomTest extends BitcoinSWalletTest {
import scala.concurrent.Future
class WalletBloomTest extends BitcoinSWalletTestCachedBitcoindNewest {
behavior of "Wallet bloom filter"
override type FixtureParam = WalletWithBitcoind
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
withFundedWalletAndBitcoind(test, getBIP39PasswordOpt())
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val f: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
futOutcome = withFundedWalletAndBitcoindCached(test,
getBIP39PasswordOpt(),
bitcoind)
fut <- futOutcome.toFuture
} yield fut
new FutureOutcome(f)
}
it should "generate a bloom filter that matches the pubkeys in our wallet" in {
param =>

View File

@ -10,15 +10,30 @@ import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.core.wallet.utxo.TxoState
import org.bitcoins.rpc.BitcoindException.InvalidAddressOrKey
import org.bitcoins.testkit.wallet.BitcoinSWalletTest.RandomFeeProvider
import org.bitcoins.testkit.wallet._
import org.scalatest.FutureOutcome
import org.bitcoins.testkit.wallet.{
BitcoinSWalletTestCachedBitcoindNewest,
WalletTestUtil,
WalletWithBitcoind,
WalletWithBitcoindRpc
}
import org.scalatest.{FutureOutcome, Outcome}
class WalletIntegrationTest extends BitcoinSWalletTest {
import scala.concurrent.Future
class WalletIntegrationTest extends BitcoinSWalletTestCachedBitcoindNewest {
override type FixtureParam = WalletWithBitcoind
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
withNewWalletAndBitcoind(test)
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val f: Future[Outcome] = for {
bitcoind <- cachedBitcoindWithFundsF
futOutcome = withNewWalletAndBitcoindCached(test,
getBIP39PasswordOpt(),
bitcoind)
fut <- futOutcome.toFuture
} yield fut
new FutureOutcome(f)
}
behavior of "Wallet - integration test"
@ -338,7 +353,6 @@ class WalletIntegrationTest extends BitcoinSWalletTest {
// Assert spending tx valid to bitcoind
oldBalance <- bitcoind.getBalance
_ = assert(oldBalance == Satoshis(510000000000L))
_ <- bitcoind.sendRawTransaction(signedTx)
_ <- bitcoind.generateToAddress(1, bitcoindAddr)

View File

@ -1,18 +1,17 @@
package org.bitcoins.wallet.sync
import org.bitcoins.testkit.chain.SyncUtil
import org.bitcoins.testkit.wallet.{BitcoinSWalletTest, WalletWithBitcoindV19}
import org.scalatest.FutureOutcome
import org.bitcoins.testkit.wallet.{
BitcoinSWalletTestCachedBitcoinV19,
WalletWithBitcoindV19
}
class WalletSyncTest extends BitcoinSWalletTest {
class WalletSyncTest extends BitcoinSWalletTestCachedBitcoinV19 {
behavior of "WalletSync"
override type FixtureParam = WalletWithBitcoindV19
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
withNewWalletAndBitcoindV19(test, getBIP39PasswordOpt())
it must "sync a wallet with bitcoind" in { param =>
val wallet = param.wallet
val bitcoind = param.bitcoind