mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-18 21:34:39 +01:00
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:
parent
b0f7d6f26b
commit
2287c6ced9
2
.github/workflows/Mac_2.13_RPC_Tests.yml
vendored
2
.github/workflows/Mac_2.13_RPC_Tests.yml
vendored
@ -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
|
||||
|
@ -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
|
@ -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"
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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"))
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
12
build.sbt
12
build.sbt
@ -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)
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 { _ =>
|
||||
|
@ -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
|
||||
|
@ -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 =>
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -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(
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 =
|
||||
|
@ -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))
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 =>
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user