mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 09:52:09 +01:00
* Address issue #673, also refactor fixture stuff to be in companion objects rather than traits so they can be re-used without extending the trait itself. This allows for more modularity with our fixtures * fix bugs in unit tests, address #675 to make MerkleBufferTests faster * Refactor test timeout in UpdateBloomFitlerTest * fix callback logic bug on matching unrelated txs/addresses in UpdateBloomFilterTest, re-order broadcasting of things to avoid async bugs hopefully * Make a def in NodeUnitTest, this keeps tests in the same suite from using the same config. This allows us to write multiple tests per suite, instead of just one. This also adds implicit parameters to our fixture constructors/destructors to properly create and destroy this config. The long term goal here also should be getting rid of config.initialize() we are calling everywhere as this is a anti-pattern, fixtures should take care of construction and destruction of things * Broadcast tx earlier in test case for UpdateBloomFilterTest * Rework NodeWithWalletTest to use the new fixtures we have in the node project, now use SpvNodeFundedWalletBitcoind
This commit is contained in:
parent
4d1b509ae7
commit
c934d8efc2
@ -125,6 +125,8 @@ object Blockchain extends ChainVerificationLogger {
|
||||
*/
|
||||
private def parseSuccessOrFailure(nested: Vector[Future[BlockchainUpdate]])(
|
||||
implicit ec: ExecutionContext): Future[BlockchainUpdate] = {
|
||||
require(nested.nonEmpty,
|
||||
s"Cannot parse success or failure if we don't have any updates!")
|
||||
val successfulTipOptF: Future[Option[BlockchainUpdate]] = {
|
||||
Future.find(nested) {
|
||||
case update: BlockchainUpdate =>
|
||||
|
@ -14,9 +14,11 @@ import org.bitcoins.rpc.util.AsyncUtil
|
||||
import org.bitcoins.rpc.BitcoindException
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.scalactic.Bool
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.duration._
|
||||
import org.bitcoins.testkit.async.TestAsyncUtil
|
||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest.WalletWithBitcoind
|
||||
|
||||
class BroadcastTransactionTest extends BitcoinSWalletTest {
|
||||
|
||||
|
@ -1,122 +1,96 @@
|
||||
package org.bitcoins.node
|
||||
|
||||
import org.bitcoins.core.currency._
|
||||
import org.bitcoins.chain.blockchain.ChainHandler
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.chain.models.BlockHeaderDAO
|
||||
import org.bitcoins.node.config.NodeAppConfig
|
||||
import org.bitcoins.node.models.Peer
|
||||
import org.scalatest.FutureOutcome
|
||||
import org.bitcoins.server.BitcoinSAppConfig
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||
|
||||
import scala.concurrent.Future
|
||||
import org.bitcoins.node.networking.peer.DataMessageHandler
|
||||
import scala.concurrent.Promise
|
||||
import scala.concurrent.duration._
|
||||
import org.scalatest.compatible.Assertion
|
||||
import org.scalatest.exceptions.TestFailedException
|
||||
import org.bitcoins.core.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.rpc.util.AsyncUtil
|
||||
import org.bitcoins.testkit.node.NodeTestUtil
|
||||
import akka.actor.Cancellable
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
||||
import scala.util.Try
|
||||
import scala.util.Failure
|
||||
import scala.util.Success
|
||||
import scala.concurrent.Await
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.bloom.BloomFilter
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.core.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
|
||||
import org.bitcoins.core.currency._
|
||||
import org.bitcoins.node.config.NodeAppConfig
|
||||
import org.bitcoins.node.networking.peer.DataMessageHandler
|
||||
import org.bitcoins.testkit.node.NodeUnitTest.SpvNodeFundedWalletBitcoind
|
||||
import org.bitcoins.testkit.node.{NodeTestUtil, NodeUnitTest}
|
||||
import org.bitcoins.wallet.api.UnlockedWalletApi
|
||||
import org.scalatest.FutureOutcome
|
||||
import org.scalatest.exceptions.TestFailedException
|
||||
|
||||
class NodeWithWalletTest extends BitcoinSWalletTest {
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{Future, Promise}
|
||||
|
||||
override type FixtureParam = WalletWithBitcoind
|
||||
class NodeWithWalletTest extends NodeUnitTest {
|
||||
|
||||
def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
||||
withNewWalletAndBitcoind(test)
|
||||
override type FixtureParam = SpvNodeFundedWalletBitcoind
|
||||
|
||||
it must "load a bloom filter and receive information about received payments" in {
|
||||
param =>
|
||||
val WalletWithBitcoind(wallet, rpc) = param
|
||||
def withFixture(test: OneArgAsyncTest): FutureOutcome = {
|
||||
withSpvNodeFundedWalletBitcoind(test, callbacks)
|
||||
}
|
||||
|
||||
/**
|
||||
* This is not ideal, how do we get one implicit value (`config`)
|
||||
* to resolve to multiple implicit parameters?
|
||||
*/
|
||||
implicit val nodeConfig: NodeAppConfig = config
|
||||
implicit val chainConfig: ChainAppConfig = config
|
||||
private val assertionP: Promise[Boolean] = Promise()
|
||||
|
||||
var expectedTxId: Option[DoubleSha256Digest] = None
|
||||
var cancellable: Option[Cancellable] = None
|
||||
private val expectedTxIdP: Promise[DoubleSha256Digest] = Promise()
|
||||
private val expectedTxIdF: Future[DoubleSha256Digest] = expectedTxIdP.future
|
||||
|
||||
val completionP = Promise[Assertion]
|
||||
private val walletP: Promise[UnlockedWalletApi] = Promise()
|
||||
private val walletF: Future[UnlockedWalletApi] = walletP.future
|
||||
|
||||
val amountFromBitcoind = 1.bitcoin
|
||||
|
||||
val callbacks = {
|
||||
def callbacks: SpvNodeCallbacks = {
|
||||
val onTx: DataMessageHandler.OnTxReceived = { tx =>
|
||||
if (expectedTxId.contains(tx.txId)) {
|
||||
logger.debug(s"Cancelling timeout we set earlier")
|
||||
cancellable.map(_.cancel())
|
||||
|
||||
for {
|
||||
expectedTxId <- expectedTxIdF
|
||||
wallet <- walletF
|
||||
} yield {
|
||||
if (expectedTxId == tx.txId) {
|
||||
for {
|
||||
prevBalance <- wallet.getUnconfirmedBalance()
|
||||
_ <- wallet.processTransaction(tx, confirmations = 0)
|
||||
balance <- wallet.getUnconfirmedBalance()
|
||||
} completionP.complete {
|
||||
Try {
|
||||
assert(balance == prevBalance + amountFromBitcoind)
|
||||
} yield {
|
||||
val result = balance == prevBalance + amountFromBitcoind
|
||||
assertionP.success(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpvNodeCallbacks(
|
||||
onTxReceived = Seq(onTx)
|
||||
)
|
||||
}
|
||||
|
||||
def processWalletTx(tx: DoubleSha256DigestBE) = {
|
||||
expectedTxId = Some(tx.flip)
|
||||
it must "load a bloom filter and receive information about received payments" in {
|
||||
param =>
|
||||
val SpvNodeFundedWalletBitcoind(initSpv, wallet, rpc) = param
|
||||
|
||||
walletP.success(wallet)
|
||||
|
||||
var cancellable: Option[Cancellable] = None
|
||||
|
||||
def processWalletTx(tx: DoubleSha256DigestBE): DoubleSha256DigestBE = {
|
||||
expectedTxIdP.success(tx.flip)
|
||||
// how long we're waiting for a tx notify before failing the test
|
||||
val delay = 15.seconds
|
||||
|
||||
val failTest: Runnable = new Runnable {
|
||||
override def run = {
|
||||
if (!assertionP.isCompleted) {
|
||||
val msg =
|
||||
s"Did not receive sent transaction within $delay"
|
||||
logger.error(msg)
|
||||
completionP.failure(new TestFailedException(msg, 0))
|
||||
assertionP.failure(new TestFailedException(msg, 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug(s"Setting timeout for receiving TX through node")
|
||||
cancellable = Some(actorSystem.scheduler.scheduleOnce(delay, failTest))
|
||||
cancellable = Some(system.scheduler.scheduleOnce(delay, failTest))
|
||||
tx
|
||||
}
|
||||
|
||||
for {
|
||||
_ <- config.initialize()
|
||||
|
||||
address <- wallet.getNewAddress()
|
||||
bloom <- wallet.getBloomFilter()
|
||||
|
||||
spv <- {
|
||||
val peer = Peer.fromBitcoind(rpc.instance)
|
||||
val chainHandler = {
|
||||
val bhDao = BlockHeaderDAO()
|
||||
ChainHandler(bhDao)
|
||||
}
|
||||
|
||||
val spv =
|
||||
SpvNode(peer,
|
||||
chainHandler,
|
||||
bloomFilter = bloom,
|
||||
callbacks = callbacks)
|
||||
spv.start()
|
||||
}
|
||||
address <- wallet.getNewAddress()
|
||||
spv <- initSpv.start()
|
||||
updatedBloom = spv.updateBloomFilter(address).bloomFilter
|
||||
_ <- spv.sync()
|
||||
_ <- NodeTestUtil.awaitSync(spv, rpc)
|
||||
|
||||
@ -125,10 +99,10 @@ class NodeWithWalletTest extends BitcoinSWalletTest {
|
||||
.map(processWalletTx)
|
||||
|
||||
ourTx <- rpc.getTransaction(ourTxid)
|
||||
_ = assert(bloom.isRelevant(ourTx.hex))
|
||||
_ = assert(updatedBloom.isRelevant(ourTx.hex))
|
||||
|
||||
assertion <- completionP.future
|
||||
} yield assertion
|
||||
result <- assertionP.future
|
||||
} yield assert(result)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,176 +1,149 @@
|
||||
package org.bitcoins.node
|
||||
|
||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||
import org.scalatest.FutureOutcome
|
||||
import org.bitcoins.node.models.Peer
|
||||
import org.bitcoins.chain.models.BlockHeaderDAO
|
||||
import org.bitcoins.chain.blockchain.ChainHandler
|
||||
import org.bitcoins.testkit.node.NodeTestUtil
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.node.config.NodeAppConfig
|
||||
import org.bitcoins.node.networking.peer.DataMessageHandler
|
||||
import akka.actor.Cancellable
|
||||
import org.bitcoins.core.currency._
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.blockchain.MerkleBlock
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerByte
|
||||
import org.bitcoins.node.networking.peer.DataMessageHandler
|
||||
import org.bitcoins.testkit.node.NodeUnitTest.SpvNodeFundedWalletBitcoind
|
||||
import org.bitcoins.testkit.node.{NodeTestUtil, NodeUnitTest}
|
||||
import org.scalatest.exceptions.TestFailedException
|
||||
import org.scalatest.{BeforeAndAfter, FutureOutcome}
|
||||
|
||||
import scala.concurrent._
|
||||
import scala.concurrent.duration._
|
||||
import org.scalatest.compatible.Assertion
|
||||
import org.bitcoins.core.currency._
|
||||
import scala.util.Try
|
||||
import akka.actor.Cancellable
|
||||
import org.scalatest.run
|
||||
import org.scalatest.exceptions.TestFailedException
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerByte
|
||||
|
||||
class UpdateBloomFilterTest extends BitcoinSWalletTest {
|
||||
override type FixtureParam = WalletWithBitcoind
|
||||
class UpdateBloomFilterTest extends NodeUnitTest with BeforeAndAfter {
|
||||
override type FixtureParam = SpvNodeFundedWalletBitcoind
|
||||
|
||||
def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
||||
withFundedWalletAndBitcoind(test)
|
||||
def withFixture(test: OneArgAsyncTest): FutureOutcome = {
|
||||
withSpvNodeFundedWalletBitcoind(test, callbacks)
|
||||
}
|
||||
|
||||
val testTimeout = 30.seconds
|
||||
private var assertionP: Promise[Boolean] = Promise()
|
||||
after {
|
||||
//reset assertion after a test runs, because we
|
||||
//are doing mutation to work around our callback
|
||||
//limitations, we can't currently modify callbacks
|
||||
//after a SpvNode is constructed :-(
|
||||
assertionP = Promise()
|
||||
}
|
||||
|
||||
/** The address we expect to receive funds at */
|
||||
private val addressFromWalletP: Promise[BitcoinAddress] = Promise()
|
||||
|
||||
// the TX we sent from our wallet to bitcoind,
|
||||
// we expect to get notified once this is
|
||||
// confirmed
|
||||
private val txFromWalletP: Promise[Transaction] = Promise()
|
||||
|
||||
def addressCallback: DataMessageHandler.OnTxReceived = { tx: Transaction =>
|
||||
// we check if any of the addresses in the TX
|
||||
// pays to our wallet address
|
||||
val _ = for {
|
||||
addressFromWallet <- addressFromWalletP.future
|
||||
result = tx.outputs.exists(
|
||||
_.scriptPubKey == addressFromWallet.scriptPubKey)
|
||||
} yield {
|
||||
if (result) {
|
||||
assertionP.success(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def txCallback: DataMessageHandler.OnMerkleBlockReceived = {
|
||||
(_: MerkleBlock, txs: Vector[Transaction]) =>
|
||||
{
|
||||
txFromWalletP.future
|
||||
.map { tx =>
|
||||
if (txs.contains(tx)) {
|
||||
assertionP.success(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def callbacks: SpvNodeCallbacks = {
|
||||
SpvNodeCallbacks(onTxReceived = Vector(addressCallback),
|
||||
onMerkleBlockReceived = Vector(txCallback))
|
||||
}
|
||||
|
||||
it must "update the bloom filter with an address" in { param =>
|
||||
val WalletWithBitcoind(wallet, rpc) = param
|
||||
implicit val chainConf: ChainAppConfig = config
|
||||
implicit val nodeConf: NodeAppConfig = config
|
||||
|
||||
val assertionP = Promise[Assertion]
|
||||
val assertionF = assertionP.future
|
||||
val SpvNodeFundedWalletBitcoind(initSpv, wallet, rpc) = param
|
||||
|
||||
// we want to schedule a runnable that aborts
|
||||
// the test after a timeout, but then
|
||||
// we need to cancel that runnable once
|
||||
// we get a result
|
||||
var cancelable: Option[Cancellable] = None
|
||||
val timeout = 15.seconds
|
||||
|
||||
for {
|
||||
_ <- config.initialize()
|
||||
|
||||
firstBloom <- wallet.getBloomFilter()
|
||||
|
||||
// this has to be generated after our bloom filter
|
||||
// is calculated
|
||||
addressFromWallet <- wallet.getNewAddress()
|
||||
|
||||
spv <- {
|
||||
val callback = SpvNodeCallbacks.onTxReceived { tx =>
|
||||
rpc.getRawTransaction(tx.txIdBE).foreach { res =>
|
||||
val paysToOurAddress =
|
||||
// we check if any of the addresses in the TX
|
||||
// pays to our wallet address
|
||||
res.vout.exists(_.scriptPubKey.addresses match {
|
||||
case None => false
|
||||
case Some(addresses) => addresses.exists(_ == addressFromWallet)
|
||||
})
|
||||
cancelable.forall(_.cancel())
|
||||
assertionP.complete {
|
||||
Try {
|
||||
assert(paysToOurAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val peer = Peer.fromBitcoind(rpc.instance)
|
||||
val chain = {
|
||||
val dao = BlockHeaderDAO()
|
||||
ChainHandler(dao)
|
||||
}
|
||||
val spv =
|
||||
SpvNode(peer, chain, bloomFilter = firstBloom, callbacks = callback)
|
||||
spv.start()
|
||||
}
|
||||
_ = addressFromWalletP.success(addressFromWallet)
|
||||
spv <- initSpv.start()
|
||||
_ = spv.updateBloomFilter(addressFromWallet)
|
||||
_ <- spv.sync()
|
||||
_ <- rpc.sendToAddress(addressFromWallet, 1.bitcoin)
|
||||
_ <- NodeTestUtil.awaitSync(spv, rpc)
|
||||
|
||||
_ = spv.updateBloomFilter(addressFromWallet)
|
||||
_ = {
|
||||
val runnable = new Runnable {
|
||||
cancelable = Some {
|
||||
system.scheduler.scheduleOnce(
|
||||
testTimeout,
|
||||
new Runnable {
|
||||
override def run: Unit = {
|
||||
assertionP.failure(
|
||||
new TestFailedException(
|
||||
s"Did not receive a TX message after $timeout!",
|
||||
if (!assertionP.isCompleted)
|
||||
assertionP.failure(new TestFailedException(
|
||||
s"Did not receive a merkle block message after $testTimeout!",
|
||||
failedCodeStackDepth = 0))
|
||||
}
|
||||
}
|
||||
cancelable = Some {
|
||||
actorSystem.scheduler.scheduleOnce(timeout, runnable)
|
||||
)
|
||||
}
|
||||
}
|
||||
_ <- rpc.sendToAddress(addressFromWallet, 1.bitcoin)
|
||||
assertion <- assertionF
|
||||
} yield assertion
|
||||
result <- assertionP.future
|
||||
} yield assert(result)
|
||||
}
|
||||
|
||||
it must "update the bloom filter with a TX" in { param =>
|
||||
val WalletWithBitcoind(wallet, rpc) = param
|
||||
implicit val chainConf: ChainAppConfig = config
|
||||
implicit val nodeConf: NodeAppConfig = config
|
||||
|
||||
val assertionP = Promise[Assertion]
|
||||
val assertionF = assertionP.future
|
||||
|
||||
val SpvNodeFundedWalletBitcoind(initSpv, wallet, rpc) = param
|
||||
// we want to schedule a runnable that aborts
|
||||
// the test after a timeout, but then
|
||||
// we need to cancel that runnable once
|
||||
// we get a result
|
||||
var cancelable: Option[Cancellable] = None
|
||||
|
||||
// the TX we sent from our wallet to bitcoind,
|
||||
// we expect to get notified once this is
|
||||
// confirmed
|
||||
var txFromWallet: Option[Transaction] = None
|
||||
val timeout = 15.seconds
|
||||
|
||||
for {
|
||||
_ <- config.initialize()
|
||||
|
||||
firstBloom <- wallet.getBloomFilter()
|
||||
|
||||
spv <- {
|
||||
val callback = SpvNodeCallbacks.onMerkleBlockReceived { (block, txs) =>
|
||||
val isFromOurWallet = txFromWallet.exists(tx => txs.contains(tx))
|
||||
// we might receive more merkle blocks than just the
|
||||
// one for our TX
|
||||
if (isFromOurWallet) {
|
||||
assertionP.success(assert(isFromOurWallet))
|
||||
}
|
||||
}
|
||||
|
||||
val peer = Peer.fromBitcoind(rpc.instance)
|
||||
val chain = {
|
||||
val dao = BlockHeaderDAO()
|
||||
ChainHandler(dao)
|
||||
}
|
||||
val spv =
|
||||
SpvNode(peer, chain, bloomFilter = firstBloom, callbacks = callback)
|
||||
spv.start()
|
||||
}
|
||||
_ <- spv.sync()
|
||||
_ <- NodeTestUtil.awaitSync(spv, rpc)
|
||||
|
||||
spv <- initSpv.start()
|
||||
addressFromBitcoind <- rpc.getNewAddress
|
||||
tx <- wallet
|
||||
.sendToAddress(addressFromBitcoind,
|
||||
5.bitcoin,
|
||||
SatoshisPerByte(100.sats))
|
||||
.map { tx =>
|
||||
txFromWallet = Some(tx)
|
||||
tx
|
||||
}
|
||||
|
||||
_ = txFromWalletP.success(tx)
|
||||
spvNewBloom = spv.updateBloomFilter(tx)
|
||||
_ = spv.broadcastTransaction(tx)
|
||||
_ <- spv.sync()
|
||||
_ <- NodeTestUtil.awaitSync(spv, rpc)
|
||||
_ = assert(spvNewBloom.bloomFilter.contains(tx.txId))
|
||||
_ = {
|
||||
val _ = spv.broadcastTransaction(tx)
|
||||
val SpvNode(_, _, newBloom, _) = spv.updateBloomFilter(tx)
|
||||
assert(newBloom.contains(tx.txId))
|
||||
|
||||
cancelable = Some {
|
||||
actorSystem.scheduler.scheduleOnce(
|
||||
timeout,
|
||||
system.scheduler.scheduleOnce(
|
||||
testTimeout,
|
||||
new Runnable {
|
||||
override def run: Unit = {
|
||||
if (!assertionP.isCompleted)
|
||||
assertionP.failure(
|
||||
new TestFailedException(
|
||||
s"Did not receive a merkle block message after $timeout!",
|
||||
assertionP.failure(new TestFailedException(
|
||||
s"Did not receive a merkle block message after $testTimeout!",
|
||||
failedCodeStackDepth = 0))
|
||||
}
|
||||
}
|
||||
@ -182,8 +155,8 @@ class UpdateBloomFilterTest extends BitcoinSWalletTest {
|
||||
// we should get notified about the block
|
||||
_ <- rpc.getNewAddress.flatMap(rpc.generateToAddress(1, _))
|
||||
|
||||
assertion <- assertionF
|
||||
} yield assertion
|
||||
result <- assertionP.future
|
||||
} yield assert(result)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -17,18 +17,11 @@ import scala.util.Failure
|
||||
class MerkleBuffersTest extends BitcoinSUnitTest {
|
||||
behavior of "MerkleBuffers"
|
||||
|
||||
/** Generating blocks and transactions take a little while,
|
||||
* this is to prevent the test from taking a _really_ long
|
||||
* time
|
||||
*/
|
||||
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
|
||||
customGenDrivenConfig(executions = 3)
|
||||
|
||||
it must "match a merkle block with its corresponding transactions" in {
|
||||
|
||||
val txsAndBlockGen: Gen[(Seq[Transaction], Seq[Transaction], Block)] = for {
|
||||
txs <- Gen.nonEmptyListOf(TransactionGenerators.transaction)
|
||||
otherTxs <- Gen.nonEmptyListOf(TransactionGenerators.transaction)
|
||||
txs <- TransactionGenerators.nonEmptySmallTransactions
|
||||
otherTxs <- TransactionGenerators.nonEmptySmallTransactions
|
||||
block <- BlockchainElementsGenerator.block(txs)
|
||||
} yield (txs, otherTxs, block)
|
||||
|
||||
|
@ -17,12 +17,12 @@ class PeerMessageHandlerTest extends NodeUnitTest {
|
||||
test(())
|
||||
}
|
||||
|
||||
private implicit val akkaTimeout = Timeout(timeout)
|
||||
implicit private val akkaTimeout = Timeout(timeout)
|
||||
|
||||
behavior of "PeerHandler"
|
||||
|
||||
it must "be able to fully initialize a PeerMessageReceiver" in { _ =>
|
||||
val peerHandlerF = buildPeerHandler()
|
||||
val peerHandlerF = bitcoindPeerF.map(p => NodeUnitTest.buildPeerHandler(p))
|
||||
val peerMsgSenderF = peerHandlerF.map(_.peerMsgSender)
|
||||
val peerMsgRecvF = peerHandlerF.map(_.peerMsgRecv)
|
||||
|
||||
|
@ -232,7 +232,7 @@ trait ChainUnitTest
|
||||
}
|
||||
|
||||
def createBitcoindChainHandlerViaZmq(): Future[BitcoindChainHandlerViaZmq] = {
|
||||
composeBuildersAndWrap(() => createBitcoind,
|
||||
composeBuildersAndWrap(() => BitcoinSFixture.createBitcoind,
|
||||
createChainHandlerWithBitcoindZmq,
|
||||
BitcoindChainHandlerViaZmq.apply)()
|
||||
}
|
||||
@ -269,7 +269,7 @@ trait ChainUnitTest
|
||||
def withBitcoindChainHandlerViaZmq(test: OneArgAsyncTest)(
|
||||
implicit system: ActorSystem): FutureOutcome = {
|
||||
val builder: () => Future[BitcoindChainHandlerViaZmq] =
|
||||
composeBuildersAndWrap(builder = () => createBitcoind,
|
||||
composeBuildersAndWrap(builder = () => BitcoinSFixture.createBitcoind,
|
||||
dependentBuilder =
|
||||
createChainHandlerWithBitcoindZmq,
|
||||
wrap = BitcoindChainHandlerViaZmq.apply)
|
||||
@ -280,7 +280,7 @@ trait ChainUnitTest
|
||||
def withBitcoindChainHandlerViaRpc(test: OneArgAsyncTest)(
|
||||
implicit system: ActorSystem): FutureOutcome = {
|
||||
val builder: () => Future[BitcoindChainHandlerViaRpc] = { () =>
|
||||
createBitcoind().flatMap(createChainApiWithBitcoindRpc(_))
|
||||
BitcoinSFixture.createBitcoind().flatMap(createChainApiWithBitcoindRpc(_))
|
||||
}
|
||||
|
||||
makeDependentFixture(builder, destroyBitcoindChainApiViaRpc)(test)
|
||||
|
@ -128,6 +128,10 @@ object TransactionGenerators extends BitcoinSLogger {
|
||||
def smallTransactions: Gen[Seq[Transaction]] =
|
||||
Gen.choose(0, 10).flatMap(i => Gen.listOfN(i, transaction))
|
||||
|
||||
def nonEmptySmallTransactions: Gen[Seq[Transaction]] = {
|
||||
Gen.choose(1, 10).flatMap(i => Gen.listOfN(i, transaction))
|
||||
}
|
||||
|
||||
def transaction: Gen[Transaction] =
|
||||
Gen.oneOf(baseTransaction, witnessTransaction)
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.bitcoins.testkit.fixtures
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
|
||||
import org.scalatest._
|
||||
@ -8,7 +9,7 @@ import org.scalatest._
|
||||
import scala.concurrent.{Future, Promise}
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
trait BitcoinSFixture extends fixture.AsyncFlatSpec {
|
||||
trait BitcoinSFixture extends fixture.AsyncFlatSpec with BitcoinSLogger {
|
||||
|
||||
/**
|
||||
* Given functions to build and destroy a fixture, returns a OneArgAsyncTest => FutureOutcome
|
||||
@ -33,7 +34,9 @@ trait BitcoinSFixture extends fixture.AsyncFlatSpec {
|
||||
fixtureF.foreach { fixture =>
|
||||
destroy(fixture).onComplete {
|
||||
case Success(_) => destroyP.success(())
|
||||
case Failure(err) => destroyP.failure(err)
|
||||
case Failure(err) =>
|
||||
logger.error(s"Failed to destroy fixture with err=${err}")
|
||||
destroyP.failure(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -130,8 +133,13 @@ trait BitcoinSFixture extends fixture.AsyncFlatSpec {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object BitcoinSFixture {
|
||||
|
||||
def createBitcoindWithFunds()(
|
||||
implicit system: ActorSystem): Future[BitcoindRpcClient] = {
|
||||
import system.dispatcher
|
||||
for {
|
||||
bitcoind <- createBitcoind()
|
||||
address <- bitcoind.getNewAddress
|
||||
@ -142,6 +150,7 @@ trait BitcoinSFixture extends fixture.AsyncFlatSpec {
|
||||
/** Creates a new bitcoind instance */
|
||||
def createBitcoind()(
|
||||
implicit system: ActorSystem): Future[BitcoindRpcClient] = {
|
||||
import system.dispatcher
|
||||
val instance = BitcoindRpcTestUtil.instance()
|
||||
val bitcoind = new BitcoindRpcClient(instance)
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
package org.bitcoins.testkit.fixtures
|
||||
|
||||
import org.bitcoins.node.models.BroadcastAbleTransactionDAO
|
||||
import org.scalatest._
|
||||
import org.bitcoins.testkit.node.NodeUnitTest
|
||||
import slick.jdbc.SQLiteProfile
|
||||
import org.bitcoins.node.db.NodeDbManagement
|
||||
import org.bitcoins.node.config.NodeAppConfig
|
||||
import org.bitcoins.node.models.BroadcastAbleTransactionDAO
|
||||
import org.bitcoins.testkit.node.NodeUnitTest
|
||||
import org.scalatest._
|
||||
import slick.jdbc.SQLiteProfile
|
||||
|
||||
case class NodeDAOs(txDAO: BroadcastAbleTransactionDAO)
|
||||
|
||||
@ -18,9 +17,8 @@ trait NodeDAOFixture extends fixture.AsyncFlatSpec with NodeUnitTest {
|
||||
|
||||
final override type FixtureParam = NodeDAOs
|
||||
|
||||
implicit private val nodeConfig: NodeAppConfig = config
|
||||
|
||||
def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
||||
makeFixture(build = () => NodeDbManagement.createAll().map(_ => daos),
|
||||
destroy = () => NodeDbManagement.dropAll())(test)
|
||||
makeFixture(
|
||||
build = () => NodeDbManagement.createAll()(nodeConfig, ec).map(_ => daos),
|
||||
destroy = () => NodeDbManagement.dropAll()(nodeConfig, ec))(test)
|
||||
}
|
||||
|
@ -3,10 +3,14 @@ package org.bitcoins.testkit.node
|
||||
import java.net.InetSocketAddress
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.chain.blockchain.ChainHandler
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.chain.models.BlockHeaderDAO
|
||||
import org.bitcoins.core.config.NetworkParameters
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.db.AppConfig
|
||||
import org.bitcoins.node.SpvNode
|
||||
import org.bitcoins.node.{SpvNode, SpvNodeCallbacks}
|
||||
import org.bitcoins.node.config.NodeAppConfig
|
||||
import org.bitcoins.node.models.Peer
|
||||
import org.bitcoins.node.networking.peer.{
|
||||
PeerHandler,
|
||||
@ -16,10 +20,14 @@ import org.bitcoins.node.networking.peer.{
|
||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||
import org.bitcoins.server.BitcoinSAppConfig
|
||||
import org.bitcoins.server.BitcoinSAppConfig._
|
||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||
import org.bitcoins.testkit.chain.ChainUnitTest
|
||||
import org.bitcoins.testkit.fixtures.BitcoinSFixture
|
||||
import org.bitcoins.testkit.node.NodeUnitTest.SpvNodeFundedWalletBitcoind
|
||||
import org.bitcoins.testkit.node.fixture.SpvNodeConnectedWithBitcoind
|
||||
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
|
||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||
import org.bitcoins.wallet.api.UnlockedWalletApi
|
||||
import org.scalatest.{
|
||||
BeforeAndAfter,
|
||||
BeforeAndAfterAll,
|
||||
@ -29,10 +37,6 @@ import org.scalatest.{
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||
import org.bitcoins.chain.blockchain.ChainHandler
|
||||
import org.bitcoins.chain.models.BlockHeaderDAO
|
||||
import org.bitcoins.node.SpvNodeCallbacks
|
||||
|
||||
trait NodeUnitTest
|
||||
extends BitcoinSFixture
|
||||
@ -60,83 +64,55 @@ trait NodeUnitTest
|
||||
val timeout: FiniteDuration = 10.seconds
|
||||
|
||||
/** Wallet config with data directory set to user temp directory */
|
||||
implicit protected lazy val config: BitcoinSAppConfig =
|
||||
implicit protected def config: BitcoinSAppConfig =
|
||||
BitcoinSTestAppConfig.getTestConfig()
|
||||
|
||||
implicit protected lazy val chainConfig: ChainAppConfig = config.chainConf
|
||||
|
||||
implicit protected lazy val nodeConfig: NodeAppConfig = config.nodeConf
|
||||
|
||||
implicit lazy val np: NetworkParameters = config.nodeConf.network
|
||||
|
||||
lazy val startedBitcoindF = BitcoindRpcTestUtil.startedBitcoindRpcClient()
|
||||
|
||||
lazy val bitcoindPeerF = startedBitcoindF.map(NodeTestUtil.getBitcoindPeer)
|
||||
|
||||
def buildPeerMessageReceiver(): PeerMessageReceiver = {
|
||||
|
||||
val dao = BlockHeaderDAO()
|
||||
val chainHandler = ChainHandler(dao)
|
||||
val receiver =
|
||||
PeerMessageReceiver.newReceiver(chainHandler, SpvNodeCallbacks.empty)
|
||||
receiver
|
||||
}
|
||||
|
||||
def buildPeerHandler(): Future[PeerHandler] = {
|
||||
bitcoindPeerF.map { peer =>
|
||||
val peerMsgReceiver = buildPeerMessageReceiver()
|
||||
//the problem here is the 'self', this needs to be an ordinary peer message handler
|
||||
//that can handle the handshake
|
||||
val peerMsgSender: PeerMessageSender = {
|
||||
val client = NodeTestUtil.client(peer, peerMsgReceiver)
|
||||
PeerMessageSender(client)
|
||||
}
|
||||
|
||||
PeerHandler(peerMsgReceiver, peerMsgSender)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def peerSocketAddress(
|
||||
bitcoindRpcClient: BitcoindRpcClient): InetSocketAddress = {
|
||||
NodeTestUtil.getBitcoindSocketAddress(bitcoindRpcClient)
|
||||
}
|
||||
|
||||
def createPeer(bitcoind: BitcoindRpcClient): Peer = {
|
||||
val socket = peerSocketAddress(bitcoind)
|
||||
Peer(id = None, socket = socket)
|
||||
}
|
||||
|
||||
def createSpvNode(bitcoind: BitcoindRpcClient): Future[SpvNode] = {
|
||||
val chainApiF = ChainUnitTest.createChainHandler()
|
||||
val peer = createPeer(bitcoind)
|
||||
for {
|
||||
chainApi <- chainApiF
|
||||
} yield
|
||||
SpvNode(peer = peer,
|
||||
chainApi = chainApi,
|
||||
bloomFilter = NodeTestUtil.emptyBloomFilter)
|
||||
}
|
||||
|
||||
def withSpvNode(test: OneArgAsyncTest)(
|
||||
implicit system: ActorSystem): FutureOutcome = {
|
||||
implicit system: ActorSystem,
|
||||
appConfig: BitcoinSAppConfig): FutureOutcome = {
|
||||
|
||||
val spvBuilder: () => Future[SpvNode] = { () =>
|
||||
val bitcoindF = createBitcoind()
|
||||
val bitcoindF = BitcoinSFixture.createBitcoind()
|
||||
bitcoindF.flatMap { bitcoind =>
|
||||
createSpvNode(bitcoind).flatMap(_.start())
|
||||
NodeUnitTest
|
||||
.createSpvNode(bitcoind, SpvNodeCallbacks.empty)(system,
|
||||
appConfig.chainConf,
|
||||
appConfig.nodeConf)
|
||||
.flatMap(_.start())
|
||||
}
|
||||
}
|
||||
|
||||
makeDependentFixture(
|
||||
build = spvBuilder,
|
||||
destroy = NodeUnitTest.destroySpvNode
|
||||
destroy =
|
||||
NodeUnitTest.destroySpvNode(_: SpvNode)(appConfig, system.dispatcher)
|
||||
)(test)
|
||||
}
|
||||
|
||||
def withSpvNodeConnectedToBitcoind(test: OneArgAsyncTest)(
|
||||
implicit system: ActorSystem): FutureOutcome = {
|
||||
implicit system: ActorSystem,
|
||||
appConfig: BitcoinSAppConfig): FutureOutcome = {
|
||||
val spvWithBitcoindBuilder: () => Future[SpvNodeConnectedWithBitcoind] = {
|
||||
() =>
|
||||
val bitcoindF = createBitcoind()
|
||||
val bitcoindF = BitcoinSFixture.createBitcoind()
|
||||
bitcoindF.flatMap { bitcoind =>
|
||||
val startedSpv = createSpvNode(bitcoind).flatMap(_.start())
|
||||
val spvNode = NodeUnitTest
|
||||
.createSpvNode(bitcoind, SpvNodeCallbacks.empty)(
|
||||
system,
|
||||
appConfig.chainConf,
|
||||
appConfig.nodeConf)
|
||||
val startedSpv = spvNode
|
||||
.flatMap(_.start())
|
||||
|
||||
startedSpv.map(spv => SpvNodeConnectedWithBitcoind(spv, bitcoind))
|
||||
}
|
||||
@ -144,13 +120,39 @@ trait NodeUnitTest
|
||||
|
||||
makeDependentFixture(
|
||||
build = spvWithBitcoindBuilder,
|
||||
destroy = NodeUnitTest.destorySpvNodeConnectedWithBitcoind
|
||||
destroy = NodeUnitTest.destorySpvNodeConnectedWithBitcoind(
|
||||
_: SpvNodeConnectedWithBitcoind)(system, appConfig)
|
||||
)(test)
|
||||
}
|
||||
|
||||
def withSpvNodeFundedWalletBitcoind(
|
||||
test: OneArgAsyncTest,
|
||||
callbacks: SpvNodeCallbacks)(
|
||||
implicit system: ActorSystem,
|
||||
appConfig: BitcoinSAppConfig): FutureOutcome = {
|
||||
|
||||
makeDependentFixture(
|
||||
build = () =>
|
||||
NodeUnitTest.createSpvNodeFundedWalletBitcoind(callbacks)(system,
|
||||
appConfig),
|
||||
destroy = NodeUnitTest.destroySpvNodeFundedWalletBitcoind(
|
||||
_: SpvNodeFundedWalletBitcoind)(system, appConfig)
|
||||
)(test)
|
||||
}
|
||||
}
|
||||
|
||||
object NodeUnitTest {
|
||||
object NodeUnitTest extends BitcoinSLogger {
|
||||
|
||||
/**
|
||||
* Creates
|
||||
* 1. a funded bitcoind wallet
|
||||
* 2. a funded bitcoin-s wallet
|
||||
* 3. a chain handler with the appropriate tables created
|
||||
* 4. a spv node that is connected to the bitcoin instance -- but not started! */
|
||||
case class SpvNodeFundedWalletBitcoind(
|
||||
spvNode: SpvNode,
|
||||
wallet: UnlockedWalletApi,
|
||||
bitcoindRpc: BitcoindRpcClient)
|
||||
|
||||
def destroySpvNode(spvNode: SpvNode)(
|
||||
implicit config: BitcoinSAppConfig,
|
||||
@ -174,4 +176,93 @@ object NodeUnitTest {
|
||||
_ <- bitcoindDestroyF
|
||||
} yield ()
|
||||
}
|
||||
|
||||
/** Creates a spv node, a funded bitcoin-s wallet, all of which are connected to bitcoind */
|
||||
def createSpvNodeFundedWalletBitcoind(callbacks: SpvNodeCallbacks)(
|
||||
implicit system: ActorSystem,
|
||||
appConfig: BitcoinSAppConfig): Future[SpvNodeFundedWalletBitcoind] = {
|
||||
import system.dispatcher
|
||||
val fundedWalletF = BitcoinSWalletTest.fundedWalletAndBitcoind()
|
||||
for {
|
||||
fundedWallet <- fundedWalletF
|
||||
spvNode <- createSpvNode(fundedWallet.bitcoind, callbacks)
|
||||
} yield {
|
||||
SpvNodeFundedWalletBitcoind(spvNode = spvNode,
|
||||
wallet = fundedWallet.wallet,
|
||||
bitcoindRpc = fundedWallet.bitcoind)
|
||||
}
|
||||
}
|
||||
|
||||
def destroySpvNodeFundedWalletBitcoind(
|
||||
fundedWalletBitcoind: SpvNodeFundedWalletBitcoind)(
|
||||
implicit system: ActorSystem,
|
||||
appConfig: BitcoinSAppConfig): Future[Unit] = {
|
||||
import system.dispatcher
|
||||
val walletWithBitcoind = {
|
||||
BitcoinSWalletTest.WalletWithBitcoind(fundedWalletBitcoind.wallet,
|
||||
fundedWalletBitcoind.bitcoindRpc)
|
||||
}
|
||||
val destroyedF = for {
|
||||
_ <- destroySpvNode(fundedWalletBitcoind.spvNode)
|
||||
_ <- BitcoinSWalletTest.destroyWalletWithBitcoind(walletWithBitcoind)
|
||||
} yield ()
|
||||
|
||||
destroyedF
|
||||
|
||||
}
|
||||
|
||||
def buildPeerMessageReceiver()(
|
||||
implicit system: ActorSystem,
|
||||
chainAppConfig: ChainAppConfig,
|
||||
nodeAppConfig: NodeAppConfig): PeerMessageReceiver = {
|
||||
import system.dispatcher
|
||||
val dao = BlockHeaderDAO()
|
||||
val chainHandler = ChainHandler(dao)
|
||||
val receiver =
|
||||
PeerMessageReceiver.newReceiver(chainHandler, SpvNodeCallbacks.empty)
|
||||
receiver
|
||||
}
|
||||
|
||||
def buildPeerHandler(peer: Peer)(
|
||||
implicit system: ActorSystem,
|
||||
chainAppConfig: ChainAppConfig,
|
||||
nodeAppConfig: NodeAppConfig): PeerHandler = {
|
||||
val peerMsgReceiver = buildPeerMessageReceiver()
|
||||
//the problem here is the 'self', this needs to be an ordinary peer message handler
|
||||
//that can handle the handshake
|
||||
val peerMsgSender: PeerMessageSender = {
|
||||
val client = NodeTestUtil.client(peer, peerMsgReceiver)
|
||||
PeerMessageSender(client)
|
||||
}
|
||||
PeerHandler(peerMsgReceiver, peerMsgSender)
|
||||
|
||||
}
|
||||
|
||||
def peerSocketAddress(
|
||||
bitcoindRpcClient: BitcoindRpcClient): InetSocketAddress = {
|
||||
NodeTestUtil.getBitcoindSocketAddress(bitcoindRpcClient)
|
||||
}
|
||||
|
||||
def createPeer(bitcoind: BitcoindRpcClient): Peer = {
|
||||
val socket = peerSocketAddress(bitcoind)
|
||||
Peer(id = None, socket = socket)
|
||||
}
|
||||
|
||||
def createSpvNode(bitcoind: BitcoindRpcClient, callbacks: SpvNodeCallbacks)(
|
||||
implicit system: ActorSystem,
|
||||
chainAppConfig: ChainAppConfig,
|
||||
nodeAppConfig: NodeAppConfig): Future[SpvNode] = {
|
||||
import system.dispatcher
|
||||
val chainApiF = ChainUnitTest.createChainHandler()
|
||||
val peer = createPeer(bitcoind)
|
||||
for {
|
||||
chainApi <- chainApiF
|
||||
} yield {
|
||||
SpvNode(peer = peer,
|
||||
chainApi = chainApi,
|
||||
bloomFilter = NodeTestUtil.emptyBloomFilter,
|
||||
callbacks = callbacks)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ trait BitcoinSWalletTest
|
||||
with BitcoinSFixture
|
||||
with BeforeAndAfterAll
|
||||
with BitcoinSLogger {
|
||||
import BitcoinSWalletTest._
|
||||
implicit val actorSystem: ActorSystem = ActorSystem(getClass.getSimpleName)
|
||||
implicit val ec: ExecutionContext = actorSystem.dispatcher
|
||||
|
||||
@ -52,48 +53,6 @@ trait BitcoinSWalletTest
|
||||
AppConfig.throwIfDefaultDatadir(config.walletConf)
|
||||
}
|
||||
|
||||
def destroyWallet(wallet: UnlockedWalletApi): Future[Unit] = {
|
||||
WalletDbManagement
|
||||
.dropAll()(config = wallet.walletConfig,
|
||||
ec = implicitly[ExecutionContext])
|
||||
.map(_ => ())
|
||||
}
|
||||
|
||||
/** Returns a function that can be used to create a wallet fixture.
|
||||
* If you pass in a configuration to this method that configuration
|
||||
* is given to the wallet as user-provided overrides. You could for
|
||||
* example use this to override the default data directory, network
|
||||
* or account type.
|
||||
*/
|
||||
private def createNewWallet(
|
||||
extraConfig: Option[Config]): () => Future[UnlockedWalletApi] =
|
||||
() => {
|
||||
val defaultConf = config.walletConf
|
||||
val walletConfig = extraConfig match {
|
||||
case None => defaultConf
|
||||
case Some(c) => defaultConf.withOverrides(c)
|
||||
}
|
||||
|
||||
// we want to check we're not overwriting
|
||||
// any user data
|
||||
AppConfig.throwIfDefaultDatadir(walletConfig)
|
||||
|
||||
walletConfig.initialize().flatMap { _ =>
|
||||
Wallet
|
||||
.initialize()(implicitly[ExecutionContext], walletConfig)
|
||||
.map {
|
||||
case InitializeWalletSuccess(wallet) => wallet
|
||||
case err: InitializeWalletError =>
|
||||
logger.error(s"Could not initialize wallet: $err")
|
||||
fail(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a wallet with the default configuration */
|
||||
private def createDefaultWallet(): Future[UnlockedWalletApi] =
|
||||
createNewWallet(None)() // get the standard config
|
||||
|
||||
/** Lets you customize the parameters for the created wallet */
|
||||
val withNewConfiguredWallet: Config => OneArgAsyncTest => FutureOutcome =
|
||||
walletConfig =>
|
||||
@ -118,27 +77,6 @@ trait BitcoinSWalletTest
|
||||
makeDependentFixture(build = createDefaultWallet _,
|
||||
destroy = destroyWallet)(test)
|
||||
|
||||
case class WalletWithBitcoind(
|
||||
wallet: UnlockedWalletApi,
|
||||
bitcoind: BitcoindRpcClient)
|
||||
|
||||
def createWalletWithBitcoind(
|
||||
wallet: UnlockedWalletApi): Future[WalletWithBitcoind] = {
|
||||
val bitcoindF = createBitcoindWithFunds()
|
||||
bitcoindF.map(WalletWithBitcoind(wallet, _))
|
||||
}
|
||||
|
||||
def destroyWalletWithBitcoind(
|
||||
walletWithBitcoind: WalletWithBitcoind): Future[Unit] = {
|
||||
val WalletWithBitcoind(wallet, bitcoind) = walletWithBitcoind
|
||||
val stopF = bitcoind.stop()
|
||||
val destroyWalletF = destroyWallet(wallet)
|
||||
for {
|
||||
_ <- stopF
|
||||
_ <- destroyWalletF
|
||||
} yield ()
|
||||
}
|
||||
|
||||
def withNewWalletAndBitcoind(test: OneArgAsyncTest): FutureOutcome = {
|
||||
val builder: () => Future[WalletWithBitcoind] = composeBuildersAndWrap(
|
||||
builder = createDefaultWallet _,
|
||||
@ -151,9 +89,97 @@ trait BitcoinSWalletTest
|
||||
|
||||
}
|
||||
|
||||
def withFundedWalletAndBitcoind(test: OneArgAsyncTest): FutureOutcome = {
|
||||
val builder: () => Future[WalletWithBitcoind] =
|
||||
composeBuildersAndWrapFuture(
|
||||
builder = createDefaultWallet _,
|
||||
dependentBuilder = createWalletWithBitcoind,
|
||||
processResult = (_: UnlockedWalletApi, pair: WalletWithBitcoind) =>
|
||||
fundWalletWithBitcoind(pair)
|
||||
)
|
||||
|
||||
makeDependentFixture(builder, destroy = destroyWalletWithBitcoind)(test)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object BitcoinSWalletTest extends BitcoinSLogger {
|
||||
|
||||
case class WalletWithBitcoind(
|
||||
wallet: UnlockedWalletApi,
|
||||
bitcoind: BitcoindRpcClient)
|
||||
|
||||
/** Returns a function that can be used to create a wallet fixture.
|
||||
* If you pass in a configuration to this method that configuration
|
||||
* is given to the wallet as user-provided overrides. You could for
|
||||
* example use this to override the default data directory, network
|
||||
* or account type.
|
||||
*/
|
||||
private def createNewWallet(extraConfig: Option[Config])(
|
||||
implicit config: BitcoinSAppConfig,
|
||||
ec: ExecutionContext): () => Future[UnlockedWalletApi] =
|
||||
() => {
|
||||
val defaultConf = config.walletConf
|
||||
val walletConfig = extraConfig match {
|
||||
case None => defaultConf
|
||||
case Some(c) => defaultConf.withOverrides(c)
|
||||
}
|
||||
|
||||
// we want to check we're not overwriting
|
||||
// any user data
|
||||
AppConfig.throwIfDefaultDatadir(walletConfig)
|
||||
|
||||
walletConfig.initialize().flatMap { _ =>
|
||||
Wallet
|
||||
.initialize()(implicitly[ExecutionContext], walletConfig)
|
||||
.map {
|
||||
case InitializeWalletSuccess(wallet) => wallet
|
||||
case err: InitializeWalletError =>
|
||||
logger.error(s"Could not initialize wallet: $err")
|
||||
throw new RuntimeException(
|
||||
s"Failed to intialize wallet in fixture with err=${err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a wallet with the default configuration */
|
||||
private def createDefaultWallet()(
|
||||
implicit config: BitcoinSAppConfig,
|
||||
ec: ExecutionContext): Future[UnlockedWalletApi] =
|
||||
createNewWallet(None)(config, ec)() // get the standard config
|
||||
|
||||
/** Pairs the given wallet with a bitcoind instance that has money in the bitcoind wallet */
|
||||
def createWalletWithBitcoind(wallet: UnlockedWalletApi)(
|
||||
implicit system: ActorSystem): Future[WalletWithBitcoind] = {
|
||||
import system.dispatcher
|
||||
val bitcoindF = BitcoinSFixture.createBitcoindWithFunds()
|
||||
bitcoindF.map(WalletWithBitcoind(wallet, _))
|
||||
}
|
||||
|
||||
/** Creates a default wallet, and then pairs it with a bitcoind instance that has money in the bitcoind wallet */
|
||||
def createWalletWithBitcoind()(
|
||||
implicit system: ActorSystem,
|
||||
config: BitcoinSAppConfig): Future[WalletWithBitcoind] = {
|
||||
import system.dispatcher
|
||||
val unlockedWalletApiF = createDefaultWallet()
|
||||
unlockedWalletApiF.flatMap(u => createWalletWithBitcoind(u))
|
||||
}
|
||||
|
||||
/** Gives us a funded bitcoin-s wallet and the bitcoind instance that funded that wallet */
|
||||
def fundedWalletAndBitcoind()(
|
||||
implicit config: BitcoinSAppConfig,
|
||||
system: ActorSystem): Future[WalletWithBitcoind] = {
|
||||
import system.dispatcher
|
||||
for {
|
||||
wallet <- createDefaultWallet()
|
||||
withBitcoind <- createWalletWithBitcoind(wallet)
|
||||
funded <- fundWalletWithBitcoind(withBitcoind)
|
||||
} yield funded
|
||||
}
|
||||
|
||||
/** Funds the given wallet with money from the given bitcoind */
|
||||
def fundWalletWithBitcoind(
|
||||
pair: WalletWithBitcoind): Future[WalletWithBitcoind] = {
|
||||
def fundWalletWithBitcoind(pair: WalletWithBitcoind)(
|
||||
implicit ec: ExecutionContext): Future[WalletWithBitcoind] = {
|
||||
val WalletWithBitcoind(wallet, bitcoind) = pair
|
||||
for {
|
||||
addr <- wallet.getNewAddress()
|
||||
@ -171,16 +197,22 @@ trait BitcoinSWalletTest
|
||||
}
|
||||
}
|
||||
|
||||
def withFundedWalletAndBitcoind(test: OneArgAsyncTest): FutureOutcome = {
|
||||
val builder: () => Future[WalletWithBitcoind] =
|
||||
composeBuildersAndWrapFuture(
|
||||
builder = createDefaultWallet _,
|
||||
dependentBuilder = createWalletWithBitcoind,
|
||||
processResult = (_: UnlockedWalletApi, pair: WalletWithBitcoind) =>
|
||||
fundWalletWithBitcoind(pair)
|
||||
)
|
||||
def destroyWalletWithBitcoind(walletWithBitcoind: WalletWithBitcoind)(
|
||||
implicit ec: ExecutionContext): Future[Unit] = {
|
||||
val WalletWithBitcoind(wallet, bitcoind) = walletWithBitcoind
|
||||
val stopF = bitcoind.stop()
|
||||
val destroyWalletF = destroyWallet(wallet)
|
||||
for {
|
||||
_ <- stopF
|
||||
_ <- destroyWalletF
|
||||
} yield ()
|
||||
}
|
||||
|
||||
makeDependentFixture(builder, destroy = destroyWalletWithBitcoind)(test)
|
||||
def destroyWallet(wallet: UnlockedWalletApi)(
|
||||
implicit ec: ExecutionContext): Future[Unit] = {
|
||||
WalletDbManagement
|
||||
.dropAll()(config = wallet.walletConfig, ec = ec)
|
||||
.map(_ => ())
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import org.bitcoins.wallet.api.UnlockWalletSuccess
|
||||
import org.bitcoins.core.util.FutureUtil
|
||||
import org.bitcoins.core.currency._
|
||||
import org.bitcoins.testkit.Implicits._
|
||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest.WalletWithBitcoind
|
||||
|
||||
class WalletBloomTest extends BitcoinSWalletTest {
|
||||
behavior of "Wallet bloom filter"
|
||||
|
@ -10,6 +10,7 @@ import org.scalatest.FutureOutcome
|
||||
|
||||
import scala.concurrent.Future
|
||||
import org.bitcoins.core.hd.HDChainType
|
||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest.WalletWithBitcoind
|
||||
|
||||
class WalletIntegrationTest extends BitcoinSWalletTest {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user