1
0
Fork 0
mirror of https://github.com/ACINQ/eclair.git synced 2025-02-22 14:22:39 +01:00

electrum: make docker tests run on windows/mac

Our electrumx docker container needs to contains to bitcoind that
    is running on the host.
    On linux we use the host network mode, which is not available on windows/osx
    On windows/osx we use host.docker.internal, which is not available on linux. This
    requires docker 18.03 or higher.

    electrum: change scripthash balance logging level to debug

    electrum: add a specific test with identical outputs

    electrum: rename wallet test

    electrum wallet: fix balance computation issue

    when different keys produced the exact same confirmed + unconfirmed balances, we
    would compute an invalid balance because these duplicates would be pruned.

    electrum: clean up tests, and add watcher docker tests

    add basic electrum wallet test

    our wallet connects to a dockerized electrumx server
This commit is contained in:
sstone 2018-06-26 23:12:48 +02:00
parent 2d829d40eb
commit eecddcb340
No known key found for this signature in database
GPG key ID: 56D98C3997DD1517
9 changed files with 350 additions and 433 deletions

View file

@ -1,4 +1,6 @@
sudo: required
services:
-docker
dist: trusty
language: scala
scala:

View file

@ -205,6 +205,18 @@
<version>${guava.version}</version>
</dependency>
<!-- TESTS -->
<dependency>
<groupId>com.whisk</groupId>
<artifactId>docker-testkit-scalatest_${scala.version.short}</artifactId>
<version>0.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.whisk</groupId>
<artifactId>docker-testkit-impl-spotify_${scala.version.short}</artifactId>
<version>0.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-testkit_${scala.version.short}</artifactId>

View file

@ -16,12 +16,8 @@
package fr.acinq.eclair.blockchain.bitcoind
import java.io.File
import java.nio.file.Files
import java.util.UUID
import akka.actor.ActorSystem
import akka.actor.Status.Failure
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.pattern.pipe
import akka.testkit.{TestKit, TestProbe}
import com.typesafe.config.ConfigFactory
@ -37,35 +33,20 @@ import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import org.scalatest.{BeforeAndAfterAll, FunSuiteLike}
import scala.collection.JavaConversions._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.sys.process.{Process, _}
import scala.util.{Random, Try}
import collection.JavaConversions._
@RunWith(classOf[JUnitRunner])
class BitcoinCoreWalletSpec extends TestKit(ActorSystem("test")) with FunSuiteLike with BeforeAndAfterAll with Logging {
val INTEGRATION_TMP_DIR = s"${System.getProperty("buildDirectory")}/bitcoinj-${UUID.randomUUID().toString}"
logger.info(s"using tmp dir: $INTEGRATION_TMP_DIR")
val PATH_BITCOIND = new File(System.getProperty("buildDirectory"), "bitcoin-0.16.0/bin/bitcoind")
val PATH_BITCOIND_DATADIR = new File(INTEGRATION_TMP_DIR, "datadir-bitcoin")
var bitcoind: Process = null
var bitcoinrpcclient: BasicBitcoinJsonRPCClient = null
var bitcoincli: ActorRef = null
class BitcoinCoreWalletSpec extends TestKit(ActorSystem("test")) with BitcoindService with FunSuiteLike with BeforeAndAfterAll with Logging {
val walletPassword = Random.alphanumeric.take(8).mkString
implicit val formats = DefaultFormats
case class BitcoinReq(method: String, params: Any*)
override def beforeAll(): Unit = {
Files.createDirectories(PATH_BITCOIND_DATADIR.toPath)
Files.copy(classOf[BitcoinCoreWalletSpec].getResourceAsStream("/integration/bitcoin.conf"), new File(PATH_BITCOIND_DATADIR.toString, "bitcoin.conf").toPath)
startBitcoind()
}
@ -178,40 +159,5 @@ class BitcoinCoreWalletSpec extends TestKit(ActorSystem("test")) with FunSuiteLi
wallet.getBalance.pipeTo(sender.ref)
assert(sender.expectMsgType[Satoshi] > Satoshi(0))
}
private def startBitcoind(): Unit = {
bitcoind = s"$PATH_BITCOIND -datadir=$PATH_BITCOIND_DATADIR".run()
bitcoinrpcclient = new BasicBitcoinJsonRPCClient(user = "foo", password = "bar", host = "localhost", port = 28332)
bitcoincli = system.actorOf(Props(new Actor {
override def receive: Receive = {
case BitcoinReq(method) => bitcoinrpcclient.invoke(method) pipeTo sender
case BitcoinReq(method, params) => bitcoinrpcclient.invoke(method, params) pipeTo sender
case BitcoinReq(method, param1, param2) => bitcoinrpcclient.invoke(method, param1, param2) pipeTo sender
}
}))
}
private def stopBitcoind(): Int = {
// gracefully stopping bitcoin will make it store its state cleanly to disk, which is good for later debugging
logger.info(s"stopping bitcoind")
val sender = TestProbe()
sender.send(bitcoincli, BitcoinReq("stop"))
sender.expectMsgType[JValue]
bitcoind.exitValue()
}
private def waitForBitcoindReady(): Unit = {
val sender = TestProbe()
logger.info(s"waiting for bitcoind to initialize...")
awaitCond({
sender.send(bitcoincli, BitcoinReq("getnetworkinfo"))
sender.receiveOne(5 second).isInstanceOf[JValue]
}, max = 30 seconds, interval = 500 millis)
logger.info(s"generating initial blocks...")
sender.send(bitcoincli, BitcoinReq("generate", 500))
sender.expectMsgType[JValue](30 seconds)
}
}

View file

@ -0,0 +1,90 @@
/*
* Copyright 2018 ACINQ SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fr.acinq.eclair.blockchain.bitcoind
import java.io.File
import java.nio.file.{Files, StandardCopyOption}
import java.util.UUID
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.pattern.pipe
import akka.testkit.{TestKitBase, TestProbe}
import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, BitcoinJsonRPCClient}
import fr.acinq.eclair.integration.IntegrationSpec
import grizzled.slf4j.Logging
import org.json4s.JsonAST.JValue
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
trait BitcoindService extends Logging {
self: TestKitBase =>
implicit val system: ActorSystem
import scala.sys.process._
val INTEGRATION_TMP_DIR = s"${System.getProperty("buildDirectory")}/integration-${UUID.randomUUID().toString}"
logger.info(s"using tmp dir: $INTEGRATION_TMP_DIR")
val PATH_BITCOIND = new File(System.getProperty("buildDirectory"), "bitcoin-0.16.0/bin/bitcoind")
val PATH_BITCOIND_DATADIR = new File(INTEGRATION_TMP_DIR, "datadir-bitcoin")
var bitcoind: Process = null
var bitcoinrpcclient: BitcoinJsonRPCClient = null
var bitcoincli: ActorRef = null
case class BitcoinReq(method: String, params: Any*)
def startBitcoind(): Unit = {
Files.createDirectories(PATH_BITCOIND_DATADIR.toPath)
if (!Files.exists(new File(PATH_BITCOIND_DATADIR.toString, "bitcoin.conf").toPath)) {
Files.copy(classOf[IntegrationSpec].getResourceAsStream("/integration/bitcoin.conf"), new File(PATH_BITCOIND_DATADIR.toString, "bitcoin.conf").toPath, StandardCopyOption.REPLACE_EXISTING)
}
bitcoind = s"$PATH_BITCOIND -datadir=$PATH_BITCOIND_DATADIR".run()
bitcoinrpcclient = new BasicBitcoinJsonRPCClient(user = "foo", password = "bar", host = "localhost", port = 28332)
bitcoincli = system.actorOf(Props(new Actor {
override def receive: Receive = {
case BitcoinReq(method) => bitcoinrpcclient.invoke(method) pipeTo sender
case BitcoinReq(method, params) => bitcoinrpcclient.invoke(method, params) pipeTo sender
case BitcoinReq(method, param1, param2) => bitcoinrpcclient.invoke(method, param1, param2) pipeTo sender
}
}))
}
def stopBitcoind(): Unit = {
// gracefully stopping bitcoin will make it store its state cleanly to disk, which is good for later debugging
val sender = TestProbe()
sender.send(bitcoincli, BitcoinReq("stop"))
sender.expectMsgType[JValue]
bitcoind.exitValue()
}
def waitForBitcoindReady(): Unit = {
val sender = TestProbe()
logger.info(s"waiting for bitcoind to initialize...")
awaitCond({
sender.send(bitcoincli, BitcoinReq("getnetworkinfo"))
sender.receiveOne(5 second).isInstanceOf[JValue]
}, max = 30 seconds, interval = 500 millis)
logger.info(s"generating initial blocks...")
sender.send(bitcoincli, BitcoinReq("generate", 500))
sender.expectMsgType[JValue](30 seconds)
}
}

View file

@ -1,140 +0,0 @@
/*
* Copyright 2018 ACINQ SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fr.acinq.eclair.blockchain.electrum
import java.io.File
import java.net.InetSocketAddress
import java.nio.file.Files
import java.util.UUID
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.pattern.pipe
import akka.testkit.{TestKit, TestProbe}
import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, BitcoinJsonRPCClient}
import grizzled.slf4j.Logging
import org.json4s.DefaultFormats
import org.json4s.JsonAST.{JInt, JValue}
import org.json4s.jackson.JsonMethods
import org.junit.Ignore
import org.scalatest.{BeforeAndAfterAll, FunSuiteLike}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.sys.process._
import scala.util.{Success, Try}
@Ignore
class IntegrationSpec extends TestKit(ActorSystem("test")) with FunSuiteLike with BeforeAndAfterAll with Logging {
implicit val formats = DefaultFormats
require(System.getProperty("buildDirectory") != null, "please define system property buildDirectory")
require(System.getProperty("electrumxPath") != null, "please define system property electrumxPath")
val INTEGRATION_TMP_DIR = s"${System.getProperty("buildDirectory")}/integration-${UUID.randomUUID().toString}"
logger.info(s"using tmp dir: $INTEGRATION_TMP_DIR")
val PATH_BITCOIND = new File(System.getProperty("buildDirectory"), "bitcoin-0.16.0/bin/bitcoind")
val PATH_BITCOIND_DATADIR = new File(INTEGRATION_TMP_DIR, "datadir-bitcoin")
val PATH_ELECTRUMX_DBDIR = new File(INTEGRATION_TMP_DIR, "electrumx-db")
val PATH_ELECTRUMX = new File(System.getProperty("electrumxPath"))
var bitcoind: Process = _
var bitcoinrpcclient: BitcoinJsonRPCClient = _
var bitcoincli: ActorRef = _
var elecxtrumx: Process = _
var electrumClient: ActorRef = _
case class BitcoinReq(method: String, params: Seq[Any] = Nil)
override protected def beforeAll(): Unit = {
Files.createDirectories(PATH_BITCOIND_DATADIR.toPath)
Files.copy(classOf[IntegrationSpec].getResourceAsStream("/integration/bitcoin.conf"), new File(PATH_BITCOIND_DATADIR.toString, "bitcoin.conf").toPath)
bitcoinrpcclient = new BasicBitcoinJsonRPCClient(user = "foo", password = "bar", host = "localhost", port = 28332)
bitcoincli = system.actorOf(Props(new Actor {
override def receive: Receive = {
case BitcoinReq(method, Nil) =>
bitcoinrpcclient.invoke(method) pipeTo sender
case BitcoinReq(method, params) =>
bitcoinrpcclient.invoke(method, params: _*) pipeTo sender
}
}))
Files.createDirectories(PATH_ELECTRUMX_DBDIR.toPath)
startBitcoind
logger.info(s"generating initial blocks...")
val sender = TestProbe()
sender.send(bitcoincli, BitcoinReq("generate", 500 :: Nil))
sender.expectMsgType[JValue](10 seconds)
startElectrum
// FIXME use docker
electrumClient = system.actorOf(Props(new ElectrumClient(new InetSocketAddress("localhost", 51001))))
sender.send(electrumClient, ElectrumClient.AddStatusListener(sender.ref))
sender.expectMsg(3 seconds, ElectrumClient.ElectrumReady)
}
override protected def afterAll(): Unit = {
logger.info(s"stopping bitcoind")
stopBitcoind
bitcoind.destroy()
logger.info(s"stopping electrumx")
elecxtrumx.destroy()
}
def startBitcoind: Unit = {
bitcoind = s"$PATH_BITCOIND -datadir=$PATH_BITCOIND_DATADIR".run()
val sender = TestProbe()
logger.info(s"waiting for bitcoind to initialize...")
awaitCond({
sender.send(bitcoincli, BitcoinReq("getnetworkinfo"))
sender.receiveOne(5 second).isInstanceOf[JValue]
}, max = 30 seconds, interval = 500 millis)
logger.info(s"bitcoind is ready")
}
def stopBitcoind: Unit = {
// gracefully stopping bitcoin will make it store its state cleanly to disk, which is good for later debugging
val sender = TestProbe()
sender.send(bitcoincli, BitcoinReq("stop"))
bitcoind.exitValue()
}
def restartBitcoind: Unit = {
stopBitcoind
startBitcoind
}
def startElectrum: Unit = {
elecxtrumx = Process(s"$PATH_ELECTRUMX/electrumx_server.py",
None,
"DB_DIRECTORY" -> PATH_ELECTRUMX_DBDIR.getAbsolutePath,
"DAEMON_URL" -> "foo:bar@localhost:28332",
"COIN" -> "BitcoinSegwit",
"NET" -> "regtest",
"TCP_PORT" -> "51001").run()
logger.info(s"waiting for electrumx to initialize...")
awaitCond({
val result = s"$PATH_ELECTRUMX/electrumx_rpc.py getinfo".!!
Try(JsonMethods.parse(result) \ "daemon_height") match {
case Success(JInt(value)) if value.intValue() == 500 => true
case _ => false
}
}, max = 30 seconds, interval = 500 millis)
}
}

View file

@ -16,16 +16,32 @@
package fr.acinq.eclair.blockchain.electrum
import akka.actor.{ActorRef, Props}
import akka.testkit.TestProbe
import fr.acinq.bitcoin._
import fr.acinq.eclair.blockchain.electrum.ElectrumClient.{AddStatusListener, BroadcastTransaction, BroadcastTransactionResponse}
import org.json4s.JsonAST._
import java.net.InetSocketAddress
import akka.actor.{ActorRef, ActorSystem, Props}
import akka.testkit.{TestKit, TestProbe}
import com.whisk.docker.DockerReadyChecker
import fr.acinq.bitcoin.{BinaryData, Block, Btc, DeterministicWallet, MnemonicCode, Satoshi, Transaction, TxOut}
import fr.acinq.eclair.blockchain.bitcoind.BitcoinCoreWallet.{FundTransactionResponse, SignTransactionResponse}
import fr.acinq.eclair.blockchain.bitcoind.{BitcoinCoreWallet, BitcoindService}
import fr.acinq.eclair.blockchain.electrum.ElectrumClient.{BroadcastTransaction, BroadcastTransactionResponse}
import grizzled.slf4j.Logging
import org.json4s.JsonAST.{JDouble, JString, JValue}
import org.junit.experimental.categories.Category
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import org.scalatest.{BeforeAndAfterAll, FunSuiteLike}
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.sys.process._
class ElectrumWalletSpec extends IntegrationSpec {
trait DockerTest {}
@RunWith(classOf[JUnitRunner])
@Category(Array(classOf[DockerTest]))
class ElectrumWalletSpec extends TestKit(ActorSystem("test")) with FunSuiteLike with BitcoindService with ElectrumxService with BeforeAndAfterAll with Logging {
import ElectrumWallet._
@ -35,8 +51,45 @@ class ElectrumWalletSpec extends IntegrationSpec {
logger.info(s"mnemonic codes for our wallet: $mnemonics")
val master = DeterministicWallet.generate(seed)
var wallet: ActorRef = _
var electrumClient: ActorRef = _
override def beforeAll(): Unit = {
logger.info("starting bitcoind")
startBitcoind()
super.beforeAll()
}
override def afterAll(): Unit = {
logger.info("stopping bitcoind")
stopBitcoind()
super.afterAll()
}
def getCurrentAddress(probe: TestProbe) = {
probe.send(wallet, GetCurrentReceiveAddress)
probe.expectMsgType[GetCurrentReceiveAddressResponse]
}
def getBalance(probe: TestProbe) = {
probe.send(wallet, GetBalance)
probe.expectMsgType[GetBalanceResponse]
}
test("generate 500 blocks") {
val sender = TestProbe()
logger.info(s"waiting for bitcoind to initialize...")
awaitCond({
sender.send(bitcoincli, BitcoinReq("getnetworkinfo"))
sender.receiveOne(5 second).isInstanceOf[JValue]
}, max = 30 seconds, interval = 500 millis)
logger.info(s"generating initial blocks...")
sender.send(bitcoincli, BitcoinReq("generate", 500))
sender.expectMsgType[JValue](30 seconds)
DockerReadyChecker.LogLineContains("INFO:BlockProcessor:height: 501").looped(attempts = 15, delay = 1 second)
}
test("wait until wallet is ready") {
electrumClient = system.actorOf(Props(new ElectrumClientPool(Set(new InetSocketAddress("localhost", 50001)))))
wallet = system.actorOf(Props(new ElectrumWallet(seed, electrumClient, WalletParameters(Block.RegtestGenesisBlock.hash, minimumFee = Satoshi(5000)))), "wallet")
val probe = TestProbe()
awaitCond({
@ -47,78 +100,114 @@ class ElectrumWalletSpec extends IntegrationSpec {
logger.info(s"wallet is ready")
}
ignore("receive funds") {
test("receive funds") {
val probe = TestProbe()
val GetBalanceResponse(confirmed, unconfirmed) = getBalance(probe)
logger.info(s"initial balance: $confirmed $unconfirmed")
probe.send(wallet, GetCurrentReceiveAddress)
val GetCurrentReceiveAddressResponse(address) = probe.expectMsgType[GetCurrentReceiveAddressResponse]
// send money to our wallet
val GetCurrentReceiveAddressResponse(address) = getCurrentAddress(probe)
logger.info(s"sending 1 btc to $address")
probe.send(bitcoincli, BitcoinReq("sendtoaddress", address :: 1.0 :: Nil))
probe.send(bitcoincli, BitcoinReq("sendtoaddress", address, 1.0))
probe.expectMsgType[JValue]
awaitCond({
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed, unconfirmed) = probe.expectMsgType[GetBalanceResponse]
unconfirmed == Satoshi(100000000L)
val GetBalanceResponse(confirmed1, unconfirmed1) = getBalance(probe)
unconfirmed1 == unconfirmed + Satoshi(100000000L)
}, max = 30 seconds, interval = 1 second)
probe.send(bitcoincli, BitcoinReq("generate", 1 :: Nil))
// confirm our tx
probe.send(bitcoincli, BitcoinReq("generate", 1))
probe.expectMsgType[JValue]
awaitCond({
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed, unconfirmed) = probe.expectMsgType[GetBalanceResponse]
confirmed == Satoshi(100000000L)
val GetBalanceResponse(confirmed1, unconfirmed1) = getBalance(probe)
confirmed1 == confirmed + Satoshi(100000000L)
}, max = 30 seconds, interval = 1 second)
probe.send(wallet, GetCurrentReceiveAddress)
val GetCurrentReceiveAddressResponse(address1) = probe.expectMsgType[GetCurrentReceiveAddressResponse]
val GetCurrentReceiveAddressResponse(address1) = getCurrentAddress(probe)
logger.info(s"sending 1 btc to $address1")
probe.send(bitcoincli, BitcoinReq("sendtoaddress", address1 :: 1.0 :: Nil))
probe.send(bitcoincli, BitcoinReq("sendtoaddress", address1, 1.0))
probe.expectMsgType[JValue]
logger.info(s"sending 0.5 btc to $address1")
probe.send(bitcoincli, BitcoinReq("sendtoaddress", address1 :: 0.5 :: Nil))
probe.send(bitcoincli, BitcoinReq("sendtoaddress", address1, 0.5))
probe.expectMsgType[JValue]
probe.send(bitcoincli, BitcoinReq("generate", 1 :: Nil))
probe.send(bitcoincli, BitcoinReq("generate", 1))
probe.expectMsgType[JValue]
awaitCond({
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed, unconfirmed) = probe.expectMsgType[GetBalanceResponse]
confirmed == Satoshi(250000000L)
val GetBalanceResponse(confirmed1, unconfirmed1) = getBalance(probe)
confirmed1 == confirmed + Satoshi(250000000L)
}, max = 30 seconds, interval = 1 second)
}
test("handle transactions with identical outputs to us") {
val probe = TestProbe()
val GetBalanceResponse(confirmed, unconfirmed) = getBalance(probe)
logger.info(s"initial balance: $confirmed $unconfirmed")
// send money to our wallet
val amount = Satoshi(750000)
val GetCurrentReceiveAddressResponse(address) = getCurrentAddress(probe)
val tx = Transaction(version = 2,
txIn = Nil,
txOut = Seq(
TxOut(amount, fr.acinq.eclair.addressToPublicKeyScript(address, Block.RegtestGenesisBlock.hash)),
TxOut(amount, fr.acinq.eclair.addressToPublicKeyScript(address, Block.RegtestGenesisBlock.hash))
), lockTime = 0L)
val btcWallet = new BitcoinCoreWallet(bitcoinrpcclient)
val future = for {
FundTransactionResponse(tx1, pos, fee) <- btcWallet.fundTransaction(tx, false)
SignTransactionResponse(tx2, true) <- btcWallet.signTransaction(tx1)
txid <- btcWallet.publishTransaction(tx2)
} yield txid
val txid = Await.result(future, 10 seconds)
awaitCond({
val GetBalanceResponse(confirmed1, unconfirmed1) = getBalance(probe)
unconfirmed1 == unconfirmed + amount + amount
}, max = 30 seconds, interval = 1 second)
probe.send(bitcoincli, BitcoinReq("generate", 1))
probe.expectMsgType[JValue]
awaitCond({
val GetBalanceResponse(confirmed1, unconfirmed1) = getBalance(probe)
confirmed1 == confirmed + amount + amount
}, max = 30 seconds, interval = 1 second)
}
test("receive 'confidence changed' notification") {
val probe = TestProbe()
val listener = TestProbe()
system.eventStream.subscribe(listener.ref, classOf[WalletEvent])
listener.send(wallet, AddStatusListener(listener.ref))
probe.send(wallet, GetCurrentReceiveAddress)
val GetCurrentReceiveAddressResponse(address) = probe.expectMsgType[GetCurrentReceiveAddressResponse]
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed, unconfirmed) = probe.expectMsgType[GetBalanceResponse]
val GetCurrentReceiveAddressResponse(address) = getCurrentAddress(probe)
val GetBalanceResponse(confirmed, unconfirmed) = getBalance(probe)
logger.info(s"initial balance $confirmed $unconfirmed")
logger.info(s"sending 1 btc to $address")
probe.send(bitcoincli, BitcoinReq("sendtoaddress", address :: 1.0 :: Nil))
probe.send(bitcoincli, BitcoinReq("sendtoaddress", address, 1.0))
val JString(txid) = probe.expectMsgType[JValue]
logger.info(s"$txid send 1 btc to us at $address")
logger.info(s"$txid sent 1 btc to us at $address")
awaitCond({
val GetBalanceResponse(confirmed1, unconfirmed1) = getBalance(probe)
unconfirmed1 - unconfirmed == Satoshi(100000000L)
}, max = 30 seconds, interval = 1 second)
val TransactionReceived(tx, 0, received, sent, _) = listener.receiveOne(5 seconds)
assert(tx.txid === BinaryData(txid))
assert(received === Satoshi(100000000))
probe.send(bitcoincli, BitcoinReq("generate", 1 :: Nil))
logger.info("generating a new block")
probe.send(bitcoincli, BitcoinReq("generate", 1))
probe.expectMsgType[JValue]
awaitCond({
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed1, unconfirmed1) = probe.expectMsgType[GetBalanceResponse]
val GetBalanceResponse(confirmed1, unconfirmed1) = getBalance(probe)
confirmed1 - confirmed == Satoshi(100000000L)
}, max = 30 seconds, interval = 1 second)
@ -130,15 +219,12 @@ class ElectrumWalletSpec extends IntegrationSpec {
test("send money to someone else (we broadcast)") {
val probe = TestProbe()
probe.send(bitcoincli, BitcoinReq("getnewaddress"))
val JString(address) = probe.expectMsgType[JValue]
val (Base58.Prefix.PubkeyAddressTestnet, pubKeyHash) = Base58Check.decode(address)
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed, unconfirmed) = probe.expectMsgType[GetBalanceResponse]
val GetBalanceResponse(confirmed, unconfirmed) = getBalance(probe)
// create a tx that sends money to Bitcoin Core's address
val tx = Transaction(version = 2, txIn = Nil, txOut = TxOut(1 btc, Script.pay2pkh(pubKeyHash)) :: Nil, lockTime = 0L)
probe.send(bitcoincli, BitcoinReq("getnewaddress"))
val JString(address) = probe.expectMsgType[JValue]
val tx = Transaction(version = 2, txIn = Nil, txOut = TxOut(Btc(1), fr.acinq.eclair.addressToPublicKeyScript(address, Block.RegtestGenesisBlock.hash)) :: Nil, lockTime = 0L)
probe.send(wallet, CompleteTransaction(tx, 20000))
val CompleteTransactionResponse(tx1, None) = probe.expectMsgType[CompleteTransactionResponse]
@ -147,18 +233,17 @@ class ElectrumWalletSpec extends IntegrationSpec {
probe.send(wallet, BroadcastTransaction(tx1))
val BroadcastTransactionResponse(_, None) = probe.expectMsgType[BroadcastTransactionResponse]
probe.send(bitcoincli, BitcoinReq("generate", 1 :: Nil))
probe.send(bitcoincli, BitcoinReq("generate", 1))
probe.expectMsgType[JValue]
awaitCond({
probe.send(bitcoincli, BitcoinReq("getreceivedbyaddress", address :: Nil))
probe.send(bitcoincli, BitcoinReq("getreceivedbyaddress", address))
val JDouble(value) = probe.expectMsgType[JValue]
value == 1.0
}, max = 30 seconds, interval = 1 second)
awaitCond({
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed1, unconfirmed1) = probe.expectMsgType[GetBalanceResponse]
val GetBalanceResponse(confirmed1, unconfirmed1) = getBalance(probe)
logger.debug(s"current balance is $confirmed1")
confirmed1 < confirmed - Btc(1) && confirmed1 > confirmed - Btc(1) - Satoshi(50000)
}, max = 30 seconds, interval = 1 second)
@ -166,15 +251,13 @@ class ElectrumWalletSpec extends IntegrationSpec {
test("send money to ourselves (we broadcast)") {
val probe = TestProbe()
probe.send(wallet, GetCurrentReceiveAddress)
val GetCurrentReceiveAddressResponse(address) = probe.expectMsgType[GetCurrentReceiveAddressResponse]
val (Base58.Prefix.ScriptAddressTestnet, scriptHash) = Base58Check.decode(address)
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed, unconfirmed) = probe.expectMsgType[GetBalanceResponse]
val GetBalanceResponse(confirmed, unconfirmed) = getBalance(probe)
logger.info(s"current balance is $confirmed $unconfirmed")
// create a tx that sends money to Bitcoin Core's address
val tx = Transaction(version = 2, txIn = Nil, txOut = TxOut(1 btc, OP_HASH160 :: OP_PUSHDATA(scriptHash) :: OP_EQUAL :: Nil) :: Nil, lockTime = 0L)
probe.send(bitcoincli, BitcoinReq("getnewaddress"))
val JString(address) = probe.expectMsgType[JValue]
val tx = Transaction(version = 2, txIn = Nil, txOut = TxOut(Btc(1), fr.acinq.eclair.addressToPublicKeyScript(address, Block.RegtestGenesisBlock.hash)) :: Nil, lockTime = 0L)
probe.send(wallet, CompleteTransaction(tx, 20000))
val CompleteTransactionResponse(tx1, None) = probe.expectMsgType[CompleteTransactionResponse]
@ -183,134 +266,13 @@ class ElectrumWalletSpec extends IntegrationSpec {
probe.send(wallet, BroadcastTransaction(tx1))
val BroadcastTransactionResponse(_, None) = probe.expectMsgType[BroadcastTransactionResponse]
probe.send(bitcoincli, BitcoinReq("generate", 1 :: Nil))
probe.send(bitcoincli, BitcoinReq("generate", 1))
probe.expectMsgType[JValue]
awaitCond({
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed1, unconfirmed1) = probe.expectMsgType[GetBalanceResponse]
logger.debug(s"current balance is $confirmed1")
confirmed1 < confirmed && confirmed1 > confirmed - Satoshi(50000)
val GetBalanceResponse(confirmed1, unconfirmed1) = getBalance(probe)
logger.info(s"current balance is $confirmed $unconfirmed")
confirmed1 < confirmed - Btc(1) && confirmed1 > confirmed - Btc(1) - Satoshi(50000)
}, max = 30 seconds, interval = 1 second)
}
ignore("handle reorgs (pending receive)") {
val probe = TestProbe()
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed, unconfirmed) = probe.expectMsgType[GetBalanceResponse]
probe.send(wallet, GetCurrentReceiveAddress)
val GetCurrentReceiveAddressResponse(address) = probe.expectMsgType[GetCurrentReceiveAddressResponse]
// send money to our receive address
logger.info(s"sending 0.7 btc to $address")
probe.send(bitcoincli, BitcoinReq("sendtoaddress", address :: 0.7 :: Nil))
probe.expectMsgType[JValue]
// generate 1 block
probe.send(bitcoincli, BitcoinReq("generate", 1 :: Nil))
val JArray(List(JString(blockId))) = probe.expectMsgType[JValue]
// wait until our balance has been updated
awaitCond({
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed1, unconfirmed1) = probe.expectMsgType[GetBalanceResponse]
logger.debug(s"current balance is $confirmed1")
confirmed1 == confirmed + Btc(0.7)
}, max = 30 seconds, interval = 1 second)
// now invalidate the last block
probe.send(bitcoincli, BitcoinReq("invalidateblock", blockId :: Nil))
probe.expectMsgType[JValue]
// and restart bitcoind, which should remove pending wallet txs
// bitcoind was started with -zapwallettxes=2
stopBitcoind
Thread.sleep(2000)
startBitcoind
Thread.sleep(2000)
// generate 2 new blocks. the tx that sent us money is no longer there,
// the corresponding utxo should have been removed and our balance should
// be back to what it was before
probe.send(bitcoincli, BitcoinReq("generate", 2 :: Nil))
probe.expectMsgType[JValue]
val reorg = s"$PATH_ELECTRUMX/electrumx_rpc.py reorg 2".!!
awaitCond({
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed1, unconfirmed1) = probe.expectMsgType[GetBalanceResponse]
logger.debug(s"current balance is $confirmed1")
confirmed1 == confirmed
}, max = 30 seconds, interval = 1 second)
}
ignore("handle reorgs (pending send)") {
val probe = TestProbe()
probe.send(bitcoincli, BitcoinReq("getnewaddress"))
val JString(address) = probe.expectMsgType[JValue]
val (Base58.Prefix.PubkeyAddressTestnet, pubKeyHash) = Base58Check.decode(address)
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed, unconfirmed) = probe.expectMsgType[GetBalanceResponse]
logger.debug(s"we start with a balance of $confirmed")
// create a tx that sends money to Bitcoin Core's address
val amount = 0.5 btc
val tx = Transaction(version = 2, txIn = Nil, txOut = TxOut(amount, Script.pay2pkh(pubKeyHash)) :: Nil, lockTime = 0L)
probe.send(wallet, CompleteTransaction(tx, 20000))
val CompleteTransactionResponse(tx1, None) = probe.expectMsgType[CompleteTransactionResponse]
// send it ourselves
logger.info(s"sending $amount to $address with tx ${tx1.txid}")
probe.send(wallet, BroadcastTransaction(tx1))
val BroadcastTransactionResponse(_, None) = probe.expectMsgType[BroadcastTransactionResponse]
probe.send(bitcoincli, BitcoinReq("generate", 1 :: Nil))
val JArray(List(JString(blockId))) = probe.expectMsgType[JValue]
awaitCond({
probe.send(bitcoincli, BitcoinReq("getreceivedbyaddress", address :: Nil))
val JDouble(value) = probe.expectMsgType[JValue]
value == amount.amount.toDouble
}, max = 30 seconds, interval = 1 second)
awaitCond({
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed1, unconfirmed1) = probe.expectMsgType[GetBalanceResponse]
logger.debug(s"current balance is $confirmed1")
confirmed1 < confirmed - amount && confirmed1 > confirmed - amount - Satoshi(50000)
}, max = 30 seconds, interval = 1 second)
// now invalidate the last block
probe.send(bitcoincli, BitcoinReq("getblockcount"))
val JInt(count) = probe.expectMsgType[JValue]
probe.send(bitcoincli, BitcoinReq("invalidateblock", blockId :: Nil))
val foo = probe.expectMsgType[JValue]
probe.send(bitcoincli, BitcoinReq("getblockcount"))
val JInt(count1) = probe.expectMsgType[JValue]
// and restart bitcoind, which should remove pending wallet txs
// bitcoind was started with -zapwallettxes=2
stopBitcoind
Thread.sleep(2000)
startBitcoind
Thread.sleep(2000)
// generate 2 new blocks. the tx that sent us money is no longer there,
// the corresponding utxo should have been removed and our balance should
// be back to what it was before
probe.send(bitcoincli, BitcoinReq("generate", 2 :: Nil))
probe.expectMsgType[JValue]
val reorg = s"$PATH_ELECTRUMX/electrumx_rpc.py reorg 2".!!
awaitCond({
probe.send(wallet, GetBalance)
val GetBalanceResponse(confirmed1, unconfirmed1) = probe.expectMsgType[GetBalanceResponse]
logger.debug(s"current balance is $confirmed1")
confirmed1 == confirmed
}, max = 30 seconds, interval = 1 second)
}
}
}

View file

@ -16,70 +16,96 @@
package fr.acinq.eclair.blockchain.electrum
import akka.actor.Props
import akka.testkit.TestProbe
import java.net.InetSocketAddress
import akka.actor.{ActorSystem, Props}
import akka.testkit.{TestKit, TestProbe}
import fr.acinq.bitcoin.Crypto.PrivateKey
import fr.acinq.bitcoin.{Base58, OP_PUSHDATA, OutPoint, SIGHASH_ALL, Satoshi, Script, ScriptFlags, SigVersion, Transaction, TxIn, TxOut}
import fr.acinq.bitcoin.{Base58, OutPoint, SIGHASH_ALL, Satoshi, Script, ScriptFlags, ScriptWitness, SigVersion, Transaction, TxIn, TxOut}
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService
import fr.acinq.eclair.blockchain.{WatchConfirmed, WatchEventConfirmed, WatchEventSpent, WatchSpent}
import fr.acinq.eclair.channel.{BITCOIN_FUNDING_DEPTHOK, BITCOIN_FUNDING_SPENT}
import grizzled.slf4j.Logging
import org.json4s.JsonAST.{JArray, JString, JValue}
import org.junit.experimental.categories.Category
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import org.scalatest.{BeforeAndAfterAll, FunSuiteLike}
import scala.concurrent.duration._
class ElectrumWatcherSpec extends IntegrationSpec {
@RunWith(classOf[JUnitRunner])
@Category(Array(classOf[DockerTest]))
class ElectrumWatcherSpec extends TestKit(ActorSystem("test")) with FunSuiteLike with BitcoindService with ElectrumxService with BeforeAndAfterAll with Logging {
override def beforeAll(): Unit = {
logger.info("starting bitcoind")
startBitcoind()
waitForBitcoindReady()
super.beforeAll()
}
override def afterAll(): Unit = {
logger.info("stopping bitcoind")
stopBitcoind()
super.afterAll()
}
test("watch for confirmed transactions") {
val probe = TestProbe()
val electrumClient = system.actorOf(Props(new ElectrumClientPool(Set(new InetSocketAddress("localhost", 50001)))))
val watcher = system.actorOf(Props(new ElectrumWatcher(electrumClient)))
probe.send(bitcoincli, BitcoinReq("getnewaddress"))
val JString(address) = probe.expectMsgType[JValue]
println(address)
probe.send(bitcoincli, BitcoinReq("sendtoaddress", address :: 1.0 :: Nil))
probe.send(bitcoincli, BitcoinReq("sendtoaddress", address, 1.0))
val JString(txid) = probe.expectMsgType[JValue](3000 seconds)
probe.send(bitcoincli, BitcoinReq("getrawtransaction", txid :: Nil))
probe.send(bitcoincli, BitcoinReq("getrawtransaction", txid))
val JString(hex) = probe.expectMsgType[JValue]
val tx = Transaction.read(hex)
val listener = TestProbe()
probe.send(watcher, WatchConfirmed(listener.ref, tx.txid, tx.txOut(0).publicKeyScript, 4, BITCOIN_FUNDING_DEPTHOK))
probe.send(bitcoincli, BitcoinReq("generate", 3 :: Nil))
probe.send(bitcoincli, BitcoinReq("generate", 3))
listener.expectNoMsg(1 second)
probe.send(bitcoincli, BitcoinReq("generate", 2 :: Nil))
probe.send(bitcoincli, BitcoinReq("generate", 2))
val confirmed = listener.expectMsgType[WatchEventConfirmed](20 seconds)
system.stop(watcher)
}
test("watch for spent transactions") {
val probe = TestProbe()
val electrumClient = system.actorOf(Props(new ElectrumClientPool(Set(new InetSocketAddress("localhost", 50001)))))
val watcher = system.actorOf(Props(new ElectrumWatcher(electrumClient)))
probe.send(bitcoincli, BitcoinReq("getnewaddress"))
val JString(address) = probe.expectMsgType[JValue]
println(address)
probe.send(bitcoincli, BitcoinReq("dumpprivkey", address :: Nil))
probe.send(bitcoincli, BitcoinReq("dumpprivkey", address))
val JString(wif) = probe.expectMsgType[JValue]
val priv = PrivateKey.fromBase58(wif, Base58.Prefix.SecretKeyTestnet)
probe.send(bitcoincli, BitcoinReq("sendtoaddress", address :: 1.0 :: Nil))
val JString(txid) = probe.expectMsgType[JValue](3000 seconds)
probe.send(bitcoincli, BitcoinReq("sendtoaddress", address, 1.0))
val JString(txid) = probe.expectMsgType[JValue](30 seconds)
probe.send(bitcoincli, BitcoinReq("getrawtransaction", txid :: Nil))
probe.send(bitcoincli, BitcoinReq("getrawtransaction", txid))
val JString(hex) = probe.expectMsgType[JValue]
val tx = Transaction.read(hex)
// find the output for the address we generated and create a tx that spends it
val pos = tx.txOut.indexWhere(_.publicKeyScript == Script.write(Script.pay2pkh(priv.publicKey)))
val pos = tx.txOut.indexWhere(_.publicKeyScript == Script.write(Script.pay2wpkh(priv.publicKey)))
assert(pos != -1)
val spendingTx = {
val tmp = Transaction(version = 2,
txIn = TxIn(OutPoint(tx, pos), signatureScript = Nil, sequence = TxIn.SEQUENCE_FINAL) :: Nil,
txOut = TxOut(tx.txOut(pos).amount - Satoshi(1000), publicKeyScript = Script.pay2pkh(priv.publicKey)) :: Nil,
txOut = TxOut(tx.txOut(pos).amount - Satoshi(1000), publicKeyScript = Script.pay2wpkh(priv.publicKey)) :: Nil,
lockTime = 0)
val sig = Transaction.signInput(tmp, 0, tx.txOut(pos).publicKeyScript, SIGHASH_ALL, tx.txOut(pos).amount, SigVersion.SIGVERSION_BASE, priv)
val signedTx = tmp.updateSigScript(0, OP_PUSHDATA(sig) :: OP_PUSHDATA(priv.publicKey.toBin) :: Nil)
val sig = Transaction.signInput(tmp, 0, Script.pay2pkh(priv.publicKey), SIGHASH_ALL, tx.txOut(pos).amount, SigVersion.SIGVERSION_WITNESS_V0, priv)
val signedTx = tmp.updateWitness(0, ScriptWitness(sig :: priv.publicKey.toBin :: Nil))
Transaction.correctlySpends(signedTx, Seq(tx), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
signedTx
}
@ -87,9 +113,9 @@ class ElectrumWatcherSpec extends IntegrationSpec {
val listener = TestProbe()
probe.send(watcher, WatchSpent(listener.ref, tx.txid, pos, tx.txOut(pos).publicKeyScript, BITCOIN_FUNDING_SPENT))
listener.expectNoMsg(1 second)
probe.send(bitcoincli, BitcoinReq("sendrawtransaction", spendingTx.toString :: Nil))
probe.send(bitcoincli, BitcoinReq("sendrawtransaction", spendingTx.toString))
probe.expectMsgType[JValue]
probe.send(bitcoincli, BitcoinReq("generate", 2 :: Nil))
probe.send(bitcoincli, BitcoinReq("generate", 2))
val blocks = probe.expectMsgType[JValue]
val JArray(List(JString(block1), JString(block2))) = blocks
val spent = listener.expectMsgType[WatchEventSpent](20 seconds)

View file

@ -0,0 +1,48 @@
/*
* Copyright 2018 ACINQ SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fr.acinq.eclair.blockchain.electrum
import com.spotify.docker.client.{DefaultDockerClient, DockerClient}
import com.whisk.docker.impl.spotify.SpotifyDockerFactory
import com.whisk.docker.scalatest.DockerTestKit
import com.whisk.docker.{DockerContainer, DockerFactory, LogLineReceiver}
import org.scalatest.Suite
trait ElectrumxService extends DockerTestKit {
self: Suite =>
val electrumxContainer = if (System.getProperty("os.name").startsWith("Linux")) {
// "host" mode will let the container access the host network on linux
DockerContainer("lukechilds/electrumx")
.withNetworkMode("host")
.withEnv("DAEMON_URL=http://foo:bar@localhost:28332", "COIN=BitcoinSegwit", "NET=regtest")
.withLogLineReceiver(LogLineReceiver(true, println))
} else {
// on windows or oxs, host mode is not available, but from docker 18.03 on host.docker.internal can be used instead
// host.docker.internal is not (yet ?) available on linux though
DockerContainer("lukechilds/electrumx")
.withPorts(50001 -> Some(50001))
.withEnv("DAEMON_URL=http://foo:bar@host.docker.internal:28332", "COIN=BitcoinSegwit", "NET=regtest", "TCP_PORT=50001")
.withLogLineReceiver(LogLineReceiver(true, println))
}
override def dockerContainers: List[DockerContainer] = electrumxContainer :: super.dockerContainers
private val client: DockerClient = DefaultDockerClient.fromEnv().build()
override implicit val dockerFactory: DockerFactory = new SpotifyDockerFactory(client)
}

View file

@ -17,17 +17,16 @@
package fr.acinq.eclair.integration
import java.io.{File, PrintWriter}
import java.nio.file.Files
import java.util.{Properties, UUID}
import java.util.Properties
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.pattern.pipe
import akka.actor.{ActorRef, ActorSystem}
import akka.testkit.{TestKit, TestProbe}
import com.google.common.net.HostAndPort
import com.typesafe.config.{Config, ConfigFactory}
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.bitcoin.{Base58, Base58Check, Bech32, BinaryData, Block, Crypto, MilliSatoshi, OP_0, OP_CHECKSIG, OP_DUP, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160, OP_PUSHDATA, Satoshi, Script, ScriptFlags, Transaction}
import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, BitcoinJsonRPCClient, ExtendedBitcoinClient}
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService
import fr.acinq.eclair.blockchain.bitcoind.rpc.ExtendedBitcoinClient
import fr.acinq.eclair.blockchain.{Watch, WatchConfirmed}
import fr.acinq.eclair.channel.Register.Forward
import fr.acinq.eclair.channel._
@ -51,58 +50,30 @@ import org.scalatest.{BeforeAndAfterAll, FunSuiteLike}
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.sys.process._
/**
* Created by PM on 15/03/2017.
*/
@RunWith(classOf[JUnitRunner])
class IntegrationSpec extends TestKit(ActorSystem("test")) with FunSuiteLike with BeforeAndAfterAll with Logging {
class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService with FunSuiteLike with BeforeAndAfterAll with Logging {
val INTEGRATION_TMP_DIR = s"${System.getProperty("buildDirectory")}/integration-${UUID.randomUUID().toString}"
logger.info(s"using tmp dir: $INTEGRATION_TMP_DIR")
val PATH_BITCOIND = new File(System.getProperty("buildDirectory"), "bitcoin-0.16.0/bin/bitcoind")
val PATH_BITCOIND_DATADIR = new File(INTEGRATION_TMP_DIR, "datadir-bitcoin")
var bitcoind: Process = null
var bitcoinrpcclient: BitcoinJsonRPCClient = null
var bitcoincli: ActorRef = null
var nodes: Map[String, Kit] = Map()
implicit val formats = DefaultFormats
case class BitcoinReq(method: String, params: Any*)
override def beforeAll(): Unit = {
Files.createDirectories(PATH_BITCOIND_DATADIR.toPath)
Files.copy(classOf[IntegrationSpec].getResourceAsStream("/integration/bitcoin.conf"), new File(PATH_BITCOIND_DATADIR.toString, "bitcoin.conf").toPath)
bitcoind = s"$PATH_BITCOIND -datadir=$PATH_BITCOIND_DATADIR".run()
bitcoinrpcclient = new BasicBitcoinJsonRPCClient(user = "foo", password = "bar", host = "localhost", port = 28332)
bitcoincli = system.actorOf(Props(new Actor {
override def receive: Receive = {
case BitcoinReq(method) => bitcoinrpcclient.invoke(method) pipeTo sender
case BitcoinReq(method, params) => bitcoinrpcclient.invoke(method, params) pipeTo sender
}
}))
startBitcoind()
}
override def afterAll(): Unit = {
// gracefully stopping bitcoin will make it store its state cleanly to disk, which is good for later debugging
logger.info(s"stopping bitcoind")
val sender = TestProbe()
sender.send(bitcoincli, BitcoinReq("stop"))
sender.expectMsgType[JValue]
bitcoind.exitValue()
stopBitcoind()
nodes.foreach {
case (name, setup) =>
logger.info(s"stopping node $name")
setup.system.terminate()
}
// logger.warn(s"starting bitcoin-qt")
// val PATH_BITCOINQT = new File(System.getProperty("buildDirectory"), "bitcoin-0.14.0/bin/bitcoin-qt").toPath
// bitcoind = s"$PATH_BITCOINQT -datadir=$PATH_BITCOIND_DATADIR".run()
}
test("wait bitcoind ready") {