mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-20 13:44:59 +01:00
BitcoindRpcClient Improvements (#3600)
* Changes in bitcoind-rpc module * Get things compiling * Implement BitcoindInstanceRemote & BitcoindInstanceLocal * Fix doc compile * bitcoind remote instance test * rename BitcoindInstanceRemote * Scalafmt * Applied requested changes(except one) * implemented fromConfigFile and fromDataDir for BitcoindInstanceRemote * implemented fromNetworkVersion * BitcoindInstanceRemote changes * Get things compiling * Add implict type parameter to InstanceFactory * Fix recursive reference of version <-> getNetworkInfo * bug fix * bug fix 2 Co-authored-by: Chris Stewart <stewart.chris1234@gmail.com>
This commit is contained in:
parent
2c8a2b0e32
commit
d53f164478
50 changed files with 794 additions and 348 deletions
|
@ -10,8 +10,6 @@ import scalafx.scene.control._
|
|||
import scalafx.scene.layout._
|
||||
import scalafx.scene.text.{Font, TextAlignment}
|
||||
|
||||
import scala.util.Try
|
||||
|
||||
class BitcoindConfigPane(
|
||||
appConfig: BitcoinSAppConfig,
|
||||
model: LandingPaneModel) {
|
||||
|
@ -39,12 +37,12 @@ class BitcoindConfigPane(
|
|||
GUIUtil.setNumericInput(portTF)
|
||||
|
||||
private val rpcUserTF: TextField = new TextField {
|
||||
text = Try(appConfig.rpcUser).getOrElse("")
|
||||
text = appConfig.rpcUser.getOrElse("")
|
||||
minWidth = 300
|
||||
}
|
||||
|
||||
private val rpcPasswordTF: PasswordField = new PasswordField {
|
||||
text = Try(appConfig.rpcPassword).getOrElse("")
|
||||
text = appConfig.rpcPassword.getOrElse("")
|
||||
minWidth = 300
|
||||
}
|
||||
|
||||
|
|
|
@ -6,11 +6,13 @@ import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
|||
import org.bitcoins.rpc.config.{
|
||||
BitcoindAuthCredentials,
|
||||
BitcoindConfig,
|
||||
BitcoindInstance
|
||||
BitcoindInstanceLocal,
|
||||
BitcoindInstanceRemote
|
||||
}
|
||||
import org.bitcoins.rpc.util.RpcUtil
|
||||
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
|
||||
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil.newestBitcoindBinary
|
||||
|
||||
import org.bitcoins.testkit.util.{BitcoindRpcTest, FileUtil}
|
||||
import org.scalatest.compatible.Assertion
|
||||
|
||||
|
@ -66,7 +68,7 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
|
|||
""".stripMargin
|
||||
|
||||
val conf = BitcoindConfig(confStr, FileUtil.tmpDir())
|
||||
val instance = BitcoindInstance.fromConfig(conf, newestBitcoindBinary)
|
||||
val instance = BitcoindInstanceLocal.fromConfig(conf, newestBitcoindBinary)
|
||||
assert(
|
||||
instance.authCredentials
|
||||
.isInstanceOf[BitcoindAuthCredentials.CookieBased])
|
||||
|
@ -86,7 +88,7 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
|
|||
""".stripMargin
|
||||
|
||||
val conf = BitcoindConfig(confStr, FileUtil.tmpDir())
|
||||
val instance = BitcoindInstance.fromConfig(conf, newestBitcoindBinary)
|
||||
val instance = BitcoindInstanceLocal.fromConfig(conf, newestBitcoindBinary)
|
||||
assert(
|
||||
instance.authCredentials
|
||||
.isInstanceOf[BitcoindAuthCredentials.PasswordBased])
|
||||
|
@ -94,7 +96,7 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
|
|||
}
|
||||
|
||||
// the values in this conf was generated by executing
|
||||
// rpcauth.py from Bicoin Core like this:
|
||||
// rpcauth.py from Bitcoin Core like this:
|
||||
//
|
||||
// ❯ ./rpcauth.py bitcoin-s strong_password
|
||||
// String to be appended to bitcoin.conf:
|
||||
|
@ -117,7 +119,7 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
|
|||
BitcoindAuthCredentials.PasswordBased(username = "bitcoin-s",
|
||||
password = "strong_password")
|
||||
val instance =
|
||||
BitcoindInstance(
|
||||
BitcoindInstanceLocal(
|
||||
network = RegTest,
|
||||
uri = new URI(s"http://localhost:$port"),
|
||||
rpcUri = new URI(s"http://localhost:$rpcPort"),
|
||||
|
@ -131,7 +133,7 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
|
|||
|
||||
it should "parse a bitcoin.conf file, start bitcoind, mine some blocks and quit" in {
|
||||
val instance =
|
||||
BitcoindInstance.fromDatadir(datadir.toFile, newestBitcoindBinary)
|
||||
BitcoindInstanceLocal.fromDatadir(datadir.toFile, newestBitcoindBinary)
|
||||
val client = BitcoindRpcClient.withActorSystem(instance)
|
||||
|
||||
for {
|
||||
|
@ -152,4 +154,53 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
|
|||
|
||||
}
|
||||
|
||||
it should "connect us to a remote bitcoind instance and ping it successfully" in {
|
||||
val port = RpcUtil.randomPort
|
||||
val rpcPort = RpcUtil.randomPort
|
||||
val confStr = s"""
|
||||
|daemon=1
|
||||
|rpcauth=bitcoin-s:6d7580be1deb4ae52bc4249871845b09$$82b282e7c6493f6982a5a7af9fbb1b671bab702e2f31bbb1c016bb0ea1cc27ca
|
||||
|regtest=1
|
||||
|port=${RpcUtil.randomPort}
|
||||
|rpcport=${RpcUtil.randomPort}
|
||||
""".stripMargin
|
||||
|
||||
val conf = BitcoindConfig(confStr, FileUtil.tmpDir())
|
||||
val authCredentials =
|
||||
BitcoindAuthCredentials.PasswordBased(username = "bitcoin-s",
|
||||
password = "strong_password")
|
||||
val instance =
|
||||
BitcoindInstanceLocal(
|
||||
network = RegTest,
|
||||
uri = new URI(s"http://localhost:$port"),
|
||||
rpcUri = new URI(s"http://localhost:$rpcPort"),
|
||||
authCredentials = authCredentials,
|
||||
datadir = conf.datadir,
|
||||
binary = newestBitcoindBinary
|
||||
)
|
||||
|
||||
val client = BitcoindRpcClient.withActorSystem(instance)
|
||||
for {
|
||||
_ <- startClient(client)
|
||||
remoteInstance = BitcoindInstanceRemote(
|
||||
network = instance.network,
|
||||
uri = instance.uri,
|
||||
rpcUri = instance.rpcUri,
|
||||
authCredentials = instance.authCredentials,
|
||||
zmqConfig = instance.zmqConfig,
|
||||
proxyParams = None
|
||||
)
|
||||
remoteClient = BitcoindRpcClient.withActorSystem(remoteInstance)
|
||||
_ <- remoteClient.isStartedF.map {
|
||||
case false =>
|
||||
client.stop()
|
||||
fail("Couldn't ping remote instance")
|
||||
case true =>
|
||||
}
|
||||
_ <- remoteClient.stop()
|
||||
|
||||
} yield succeed
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package org.bitcoins.rpc.common
|
||||
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion.{V19, V20, V21}
|
||||
import org.bitcoins.testkit.util.BitcoindRpcTest
|
||||
|
||||
class BitcoindVersionTest extends BitcoindRpcTest {
|
||||
behavior of "BitcoindVersion"
|
||||
|
||||
it should "return version 21" in {
|
||||
val version = BitcoindVersion.fromNetworkVersion(210100)
|
||||
assert(version.equals(V21))
|
||||
}
|
||||
it should "return version 20" in {
|
||||
val version = BitcoindVersion.fromNetworkVersion(200309)
|
||||
assert(version.equals(V20))
|
||||
}
|
||||
it should "return version 19" in {
|
||||
val version = BitcoindVersion.fromNetworkVersion(190100)
|
||||
assert(version.equals(V19))
|
||||
}
|
||||
|
||||
}
|
|
@ -9,6 +9,7 @@ import org.bitcoins.core.protocol.transaction.{
|
|||
}
|
||||
import org.bitcoins.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.rpc.BitcoindException
|
||||
import org.bitcoins.rpc.config.{BitcoindInstanceLocal, BitcoindInstanceRemote}
|
||||
import org.bitcoins.testkit.rpc.{
|
||||
BitcoindFixturesCachedPairV21,
|
||||
BitcoindRpcTestUtil
|
||||
|
@ -180,8 +181,13 @@ class MempoolRpcTest extends BitcoindFixturesCachedPairV21 {
|
|||
it should "be able to save the mem pool to disk" in {
|
||||
nodePair: FixtureParam =>
|
||||
val client = nodePair.node1
|
||||
val localInstance = client.getDaemon match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
sys.error(s"Cannot have remote bitcoind instance in tests")
|
||||
case local: BitcoindInstanceLocal => local
|
||||
}
|
||||
val regTest =
|
||||
new File(client.getDaemon.datadir.getAbsolutePath + "/regtest")
|
||||
new File(localInstance.datadir.getAbsolutePath + "/regtest")
|
||||
assert(regTest.isDirectory)
|
||||
assert(!regTest.list().contains("mempool.dat"))
|
||||
for {
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.bitcoins.crypto.{ECPrivateKey, ECPublicKey}
|
|||
import org.bitcoins.rpc._
|
||||
import org.bitcoins.rpc.client.common._
|
||||
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
||||
import org.bitcoins.rpc.config.{BitcoindInstanceLocal, BitcoindInstanceRemote}
|
||||
import org.bitcoins.rpc.util.{NodePair, RpcUtil}
|
||||
import org.bitcoins.testkit.rpc.{
|
||||
BitcoindFixturesCachedPairV19,
|
||||
|
@ -105,7 +106,13 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairV19 {
|
|||
|
||||
it should "be able to backup the wallet" in { nodePair =>
|
||||
val walletClient = nodePair.node2
|
||||
val datadir = walletClient.getDaemon.datadir.getAbsolutePath
|
||||
val localInstance = walletClient.getDaemon match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
sys.error(s"Cannot use remote bitcoind instance in test cases")
|
||||
case local: BitcoindInstanceLocal =>
|
||||
local
|
||||
}
|
||||
val datadir = localInstance.datadir.getAbsolutePath
|
||||
for {
|
||||
_ <- walletClient.backupWallet(datadir + "/backup.dat", Some(walletName))
|
||||
} yield {
|
||||
|
@ -286,6 +293,12 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairV19 {
|
|||
val publicKey = ecPrivateKey.publicKey
|
||||
val address = P2PKHAddress(publicKey, networkParam)
|
||||
|
||||
val localInstance = client.getDaemon match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
sys.error(s"Cannot use remote bitcoind instance in test cases")
|
||||
case local: BitcoindInstanceLocal =>
|
||||
local
|
||||
}
|
||||
for {
|
||||
_ <- client.importPrivKey(ecPrivateKey.toPrivateKeyBytes(),
|
||||
rescan = false,
|
||||
|
@ -294,7 +307,7 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairV19 {
|
|||
result <-
|
||||
client
|
||||
.dumpWallet(
|
||||
client.getDaemon.datadir.getAbsolutePath + "/wallet_dump.dat",
|
||||
localInstance.datadir.getAbsolutePath + "/wallet_dump.dat",
|
||||
Some(walletName))
|
||||
} yield {
|
||||
assert(key.toPrivateKey == ecPrivateKey)
|
||||
|
@ -359,10 +372,16 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairV19 {
|
|||
it should "be able to import a wallet" in { nodePair =>
|
||||
val client = nodePair.node2
|
||||
val walletClient = client
|
||||
val localInstance = client.getDaemon match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
sys.error(s"Cannot use remote bitcoind instance in test cases")
|
||||
case local: BitcoindInstanceLocal =>
|
||||
local
|
||||
}
|
||||
for {
|
||||
address <- client.getNewAddress(Some(walletName))
|
||||
walletFile =
|
||||
client.getDaemon.datadir.getAbsolutePath + "/client_wallet.dat"
|
||||
localInstance.datadir.getAbsolutePath + "/client_wallet.dat"
|
||||
|
||||
fileResult <-
|
||||
client.dumpWallet(walletFile, walletNameOpt = Some(walletName))
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.bitcoins.core.wallet.signer.BitcoinSigner
|
|||
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.config.{BitcoindInstanceLocal, BitcoindInstanceRemote}
|
||||
import org.bitcoins.rpc.util.RpcUtil
|
||||
import org.bitcoins.testkit.rpc.{
|
||||
BitcoindFixturesCachedPairV21,
|
||||
|
@ -71,9 +72,15 @@ class WalletRpcTest extends BitcoindFixturesCachedPairV21 {
|
|||
|
||||
it should "be able to dump the wallet" in { nodePair: FixtureParam =>
|
||||
val client = nodePair.node1
|
||||
val localInstance = client.getDaemon match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
sys.error(s"Cannot use remote bitcoind instance in test cases")
|
||||
case local: BitcoindInstanceLocal =>
|
||||
local
|
||||
}
|
||||
for {
|
||||
result <- {
|
||||
val datadir = client.getDaemon.datadir.getAbsolutePath
|
||||
val datadir = localInstance.datadir.getAbsolutePath
|
||||
client.dumpWallet(datadir + "/test.dat")
|
||||
}
|
||||
} yield {
|
||||
|
@ -84,12 +91,18 @@ class WalletRpcTest extends BitcoindFixturesCachedPairV21 {
|
|||
|
||||
it should "be able to list wallets" in { nodePair: FixtureParam =>
|
||||
val client = nodePair.node1
|
||||
val localInstance = client.getDaemon match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
sys.error(s"Cannot use remote bitcoind instance in test cases")
|
||||
case local: BitcoindInstanceLocal =>
|
||||
local
|
||||
}
|
||||
for {
|
||||
wallets <- client.listWallets
|
||||
} yield {
|
||||
|
||||
val expectedFileName =
|
||||
if (client.instance.getVersion == BitcoindVersion.V16) "wallet.dat"
|
||||
if (localInstance.getVersion == BitcoindVersion.V16) "wallet.dat"
|
||||
else ""
|
||||
|
||||
assert(wallets == Vector(expectedFileName))
|
||||
|
@ -98,13 +111,19 @@ class WalletRpcTest extends BitcoindFixturesCachedPairV21 {
|
|||
|
||||
it should "be able to backup the wallet" in { nodePair: FixtureParam =>
|
||||
val client = nodePair.node1
|
||||
val localInstance = client.getDaemon match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
sys.error(s"Cannot use remote bitcoind instance in test cases")
|
||||
case local: BitcoindInstanceLocal =>
|
||||
local
|
||||
}
|
||||
for {
|
||||
_ <- {
|
||||
val datadir = client.getDaemon.datadir.getAbsolutePath
|
||||
val datadir = localInstance.datadir.getAbsolutePath
|
||||
client.backupWallet(datadir + "/backup.dat")
|
||||
}
|
||||
} yield {
|
||||
val datadir = client.getDaemon.datadir.getAbsolutePath
|
||||
val datadir = localInstance.datadir.getAbsolutePath
|
||||
val file = new File(datadir + "/backup.dat")
|
||||
assert(file.exists)
|
||||
assert(file.isFile)
|
||||
|
@ -397,7 +416,12 @@ class WalletRpcTest extends BitcoindFixturesCachedPairV21 {
|
|||
val ecPrivateKey = ECPrivateKey.freshPrivateKey
|
||||
val publicKey = ecPrivateKey.publicKey
|
||||
val address = P2PKHAddress(publicKey, networkParam)
|
||||
|
||||
val localInstance = client.getDaemon match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
sys.error(s"Cannot use remote bitcoind instance in test cases")
|
||||
case local: BitcoindInstanceLocal =>
|
||||
local
|
||||
}
|
||||
for {
|
||||
_ <- client.importPrivKey(ecPrivateKey.toPrivateKeyBytes(),
|
||||
rescan = false)
|
||||
|
@ -405,7 +429,7 @@ class WalletRpcTest extends BitcoindFixturesCachedPairV21 {
|
|||
result <-
|
||||
client
|
||||
.dumpWallet(
|
||||
client.getDaemon.datadir.getAbsolutePath + "/wallet_dump.dat")
|
||||
localInstance.datadir.getAbsolutePath + "/wallet_dump.dat")
|
||||
} yield {
|
||||
assert(key.toPrivateKey == ecPrivateKey)
|
||||
val reader = new Scanner(result.filename)
|
||||
|
@ -465,11 +489,17 @@ class WalletRpcTest extends BitcoindFixturesCachedPairV21 {
|
|||
|
||||
it should "be able to import a wallet" in { nodePair: FixtureParam =>
|
||||
val client = nodePair.node1
|
||||
val localInstance = client.getDaemon match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
sys.error(s"Cannot use remote bitcoind instance in test cases")
|
||||
case local: BitcoindInstanceLocal =>
|
||||
local
|
||||
}
|
||||
for {
|
||||
walletClient <- walletClientF
|
||||
address <- client.getNewAddress
|
||||
walletFile =
|
||||
client.getDaemon.datadir.getAbsolutePath + "/client_wallet.dat"
|
||||
localInstance.datadir.getAbsolutePath + "/client_wallet.dat"
|
||||
|
||||
fileResult <- client.dumpWallet(walletFile)
|
||||
_ <- walletClient.walletPassphrase(password, 1000)
|
||||
|
@ -482,11 +512,16 @@ class WalletRpcTest extends BitcoindFixturesCachedPairV21 {
|
|||
it should "be able to load a wallet" in { nodePair: FixtureParam =>
|
||||
val client = nodePair.node1
|
||||
val name = "tmp_wallet"
|
||||
|
||||
val localInstance = client.getDaemon match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
sys.error(s"Cannot use remote bitcoind instance in test cases")
|
||||
case local: BitcoindInstanceLocal =>
|
||||
local
|
||||
}
|
||||
for {
|
||||
walletClient <- walletClientF
|
||||
walletFile =
|
||||
client.getDaemon.datadir.getAbsolutePath + s"/regtest/wallets/$name"
|
||||
localInstance.datadir.getAbsolutePath + s"/regtest/wallets/$name"
|
||||
|
||||
_ <- client.createWallet(walletFile)
|
||||
_ <- client.unloadWallet(walletFile)
|
||||
|
|
|
@ -41,8 +41,13 @@ class BitcoindV16RpcClientTest extends BitcoindFixturesCachedPairV16 {
|
|||
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)
|
||||
for {
|
||||
v <- client.version
|
||||
v1 <- otherClient.version
|
||||
} yield {
|
||||
assert(v == BitcoindVersion.V16)
|
||||
assert(v1 == BitcoindVersion.V16)
|
||||
}
|
||||
}
|
||||
|
||||
it should "be able to sign a raw transaction" in { nodePair: FixtureParam =>
|
||||
|
|
|
@ -30,7 +30,9 @@ class BitcoindV18RpcClientTest extends BitcoindFixturesFundedCachedV18 {
|
|||
}
|
||||
|
||||
it should "be able to start a V18 bitcoind instance" in { client =>
|
||||
assert(client.version == BitcoindVersion.V18)
|
||||
for {
|
||||
v <- client.version
|
||||
} yield assert(v == BitcoindVersion.V18)
|
||||
}
|
||||
|
||||
it should "return active rpc commands" in { client =>
|
||||
|
|
|
@ -20,7 +20,11 @@ class BitcoindV19RpcClientTest extends BitcoindFixturesFundedCachedV19 {
|
|||
|
||||
it should "be able to start a V19 bitcoind instance" in {
|
||||
client: BitcoindV19RpcClient =>
|
||||
assert(client.version == BitcoindVersion.V19)
|
||||
for {
|
||||
v <- client.version
|
||||
} yield {
|
||||
assert(v == BitcoindVersion.V19)
|
||||
}
|
||||
}
|
||||
|
||||
it should "get a block filter given a block hash" in {
|
||||
|
|
|
@ -20,7 +20,9 @@ class BitcoindV20RpcClientTest extends BitcoindFixturesFundedCachedV20 {
|
|||
|
||||
it should "be able to start a V20 bitcoind instance" in {
|
||||
client: BitcoindV20RpcClient =>
|
||||
assert(client.version == BitcoindVersion.V20)
|
||||
for {
|
||||
v <- client.version
|
||||
} yield assert(v == BitcoindVersion.V20)
|
||||
}
|
||||
|
||||
it should "get a block filter given a block hash" in {
|
||||
|
|
|
@ -21,7 +21,9 @@ class BitcoindV21RpcClientTest extends BitcoindFixturesFundedCachedV21 {
|
|||
|
||||
it should "be able to start a V21 bitcoind instance" in {
|
||||
client: BitcoindV21RpcClient =>
|
||||
assert(client.version == BitcoindVersion.V21)
|
||||
for {
|
||||
v <- client.version
|
||||
} yield assert(v == BitcoindVersion.V21)
|
||||
}
|
||||
|
||||
it should "be able to get network info" in {
|
||||
|
|
|
@ -27,7 +27,12 @@ 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.config.{BitcoindConfig, BitcoindInstance}
|
||||
import org.bitcoins.rpc.config.{
|
||||
BitcoindConfig,
|
||||
BitcoindInstance,
|
||||
BitcoindInstanceLocal,
|
||||
BitcoindInstanceRemote
|
||||
}
|
||||
|
||||
import java.io.File
|
||||
import scala.concurrent.Future
|
||||
|
@ -45,7 +50,7 @@ import scala.concurrent.Future
|
|||
* This is a sealed abstract class, so you can pattern match easily
|
||||
* on the errors, and handle them as you see fit.
|
||||
*/
|
||||
class BitcoindRpcClient(val instance: BitcoindInstance)(implicit
|
||||
class BitcoindRpcClient(override val instance: BitcoindInstance)(implicit
|
||||
override val system: ActorSystem)
|
||||
extends Client
|
||||
with FeeRateApi
|
||||
|
@ -65,12 +70,15 @@ class BitcoindRpcClient(val instance: BitcoindInstance)(implicit
|
|||
with PsbtRpc
|
||||
with UtilRpc {
|
||||
|
||||
override def version: BitcoindVersion = instance.getVersion
|
||||
|
||||
require(
|
||||
instance.isRemote ||
|
||||
version == BitcoindVersion.Unknown || version == instance.getVersion,
|
||||
s"bitcoind version must be $version, got ${instance.getVersion}")
|
||||
override lazy val version: Future[BitcoindVersion] = {
|
||||
instance match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
getNetworkInfo.map(info =>
|
||||
BitcoindVersion.fromNetworkVersion(info.version))
|
||||
case local: BitcoindInstanceLocal =>
|
||||
Future.successful(local.getVersion)
|
||||
}
|
||||
}
|
||||
|
||||
// Fee Rate Provider
|
||||
|
||||
|
@ -209,14 +217,17 @@ class BitcoindRpcClient(val instance: BitcoindInstance)(implicit
|
|||
height: Int): Future[Vector[CompactFilterDb]] = filterHeadersUnsupported
|
||||
|
||||
protected def filtersUnsupported: Future[Nothing] = {
|
||||
Future.failed(
|
||||
new UnsupportedOperationException(
|
||||
s"bitcoind ${instance.getVersion} does not support block filters"))
|
||||
version.map { v =>
|
||||
throw new UnsupportedOperationException(
|
||||
s"bitcoind $v does not support block filters")
|
||||
}
|
||||
}
|
||||
|
||||
protected def filterHeadersUnsupported: Future[Nothing] = {
|
||||
Future.failed(new UnsupportedOperationException(
|
||||
s"bitcoind ${instance.getVersion} does not support block filters headers through the rpc"))
|
||||
version.map { v =>
|
||||
throw new UnsupportedOperationException(
|
||||
s"bitcoind $v does not support block filters headers through the rpc")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,12 +260,14 @@ object BitcoindRpcClient {
|
|||
new BitcoindRpcClient(instance)
|
||||
|
||||
/** Constructs a RPC client from the given datadir, or
|
||||
* the default datadir if no directory is provided
|
||||
* the default datadir if no directory is provided.
|
||||
* This is always a [[BitcoindInstanceLocal]] since a binary
|
||||
* is passed into this method
|
||||
*/
|
||||
def fromDatadir(
|
||||
datadir: File = BitcoindConfig.DEFAULT_DATADIR,
|
||||
binary: File): BitcoindRpcClient = {
|
||||
val instance = BitcoindInstance.fromDatadir(datadir, binary)
|
||||
val instance = BitcoindInstanceLocal.fromDatadir(datadir, binary)
|
||||
val cli = BitcoindRpcClient(instance)
|
||||
cli
|
||||
}
|
||||
|
@ -336,4 +349,24 @@ object BitcoindVersion extends StringFactory[BitcoindVersion] {
|
|||
override def fromString(string: String): BitcoindVersion = {
|
||||
fromStringOpt(string).get
|
||||
}
|
||||
|
||||
/** Gets the bitcoind version from the 'getnetworkresult' bitcoind rpc
|
||||
* An example for 210100 for the 21.1.0 release of bitcoin core
|
||||
*/
|
||||
def fromNetworkVersion(int: Int): BitcoindVersion = {
|
||||
//need to translate the int 210100 (as an example) to a BitcoindVersion
|
||||
int.toString.substring(0, 2) match {
|
||||
case "16" => V16
|
||||
case "17" => V17
|
||||
case "18" =>
|
||||
int.toString.substring(2, 4) match {
|
||||
case "99" => Experimental
|
||||
case _ => V18
|
||||
}
|
||||
case "19" => V19
|
||||
case "20" => V20
|
||||
case "21" => V21
|
||||
case _ => Unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ trait BlockchainRpc { self: Client =>
|
|||
}
|
||||
|
||||
def getBlockChainInfo: Future[GetBlockChainInfoResult] = {
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V16 | V17 | V18 =>
|
||||
bitcoindCall[GetBlockChainInfoResultPreV19]("getblockchaininfo")
|
||||
case V21 | V20 | V19 | Experimental | Unknown =>
|
||||
|
|
|
@ -22,7 +22,12 @@ import org.bitcoins.rpc.config.BitcoindAuthCredentials.{
|
|||
CookieBased,
|
||||
PasswordBased
|
||||
}
|
||||
import org.bitcoins.rpc.config.{BitcoindAuthCredentials, BitcoindInstance}
|
||||
import org.bitcoins.rpc.config.{
|
||||
BitcoindAuthCredentials,
|
||||
BitcoindInstance,
|
||||
BitcoindInstanceLocal,
|
||||
BitcoindInstanceRemote
|
||||
}
|
||||
import org.bitcoins.tor.Socks5ClientTransport
|
||||
import play.api.libs.json._
|
||||
|
||||
|
@ -45,28 +50,46 @@ trait Client
|
|||
extends Logging
|
||||
with StartStopAsync[BitcoindRpcClient]
|
||||
with NativeProcessFactory {
|
||||
def version: BitcoindVersion
|
||||
def version: Future[BitcoindVersion]
|
||||
protected val instance: BitcoindInstance
|
||||
|
||||
protected def walletExtension(walletName: String): String =
|
||||
s"/wallet/$walletName"
|
||||
|
||||
/** The log file of the Bitcoin Core daemon
|
||||
/** The log file of the Bitcoin Core daemon.
|
||||
* This returns the log file if the underlying instance is
|
||||
* [[org.bitcoins.rpc.config.BitcoindInstanceLocal]], and
|
||||
* None if the underlying instance is [[BitcoindInstanceRemote]]
|
||||
*/
|
||||
lazy val logFile: Path = {
|
||||
|
||||
val prefix = instance.network match {
|
||||
case MainNet => ""
|
||||
case TestNet3 => "testnet"
|
||||
case RegTest => "regtest"
|
||||
case SigNet => "signet"
|
||||
lazy val logFileOpt: Option[Path] = {
|
||||
instance match {
|
||||
case _: BitcoindInstanceRemote => None
|
||||
case local: BitcoindInstanceLocal =>
|
||||
val prefix = instance.network match {
|
||||
case MainNet => ""
|
||||
case TestNet3 => "testnet"
|
||||
case RegTest => "regtest"
|
||||
case SigNet => "signet"
|
||||
}
|
||||
val path = local.datadir.toPath.resolve(prefix).resolve("debug.log")
|
||||
Some(path)
|
||||
}
|
||||
instance.datadir.toPath.resolve(prefix).resolve("debug.log")
|
||||
}
|
||||
|
||||
/** The configuration file of the Bitcoin Core daemon */
|
||||
lazy val confFile: Path =
|
||||
instance.datadir.toPath.resolve("bitcoin.conf")
|
||||
/** The configuration file of the Bitcoin Core daemon
|
||||
* This returns the conf file is the underlying instance is
|
||||
* [[BitcoindInstanceLocal]] and None if the underlying
|
||||
* instance is [[BitcoindInstanceRemote]]
|
||||
*/
|
||||
lazy val confFileOpt: Option[Path] = {
|
||||
instance match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
None
|
||||
case local: BitcoindInstanceLocal =>
|
||||
val path = local.datadir.toPath.resolve("bitcoin.conf")
|
||||
Some(path)
|
||||
}
|
||||
}
|
||||
|
||||
implicit protected val system: ActorSystem
|
||||
|
||||
|
@ -108,15 +131,23 @@ trait Client
|
|||
def getDaemon: BitcoindInstance = instance
|
||||
|
||||
override lazy val cmd: String = {
|
||||
val binaryPath = instance.binary.getAbsolutePath
|
||||
val cmd = List(binaryPath,
|
||||
"-datadir=" + instance.datadir,
|
||||
"-rpcport=" + instance.rpcUri.getPort,
|
||||
"-port=" + instance.uri.getPort)
|
||||
logger.debug(
|
||||
s"starting bitcoind with datadir ${instance.datadir} and binary path $binaryPath")
|
||||
instance match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
logger.warn(
|
||||
s"Cannot start remote instance with local binary command. You've likely misconfigured something")
|
||||
""
|
||||
case local: BitcoindInstanceLocal =>
|
||||
val binaryPath = local.binary.getAbsolutePath
|
||||
val cmd = List(binaryPath,
|
||||
"-datadir=" + local.datadir,
|
||||
"-rpcport=" + instance.rpcUri.getPort,
|
||||
"-port=" + instance.uri.getPort)
|
||||
logger.debug(
|
||||
s"starting bitcoind with datadir ${local.datadir} and binary path $binaryPath")
|
||||
|
||||
cmd.mkString(" ")
|
||||
cmd.mkString(" ")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Starts bitcoind on the local system.
|
||||
|
@ -125,15 +156,6 @@ trait Client
|
|||
* cannot be started
|
||||
*/
|
||||
override def start(): Future[BitcoindRpcClient] = {
|
||||
if (version != BitcoindVersion.Unknown) {
|
||||
val foundVersion = instance.getVersion
|
||||
if (foundVersion != version) {
|
||||
throw new RuntimeException(
|
||||
s"Wrong version for bitcoind RPC client! Expected $version, got $foundVersion")
|
||||
}
|
||||
}
|
||||
|
||||
val startedF = startBinary()
|
||||
|
||||
// if we're doing cookie based authentication, we might attempt
|
||||
// to read the cookie file before it's written. this ensures
|
||||
|
@ -151,16 +173,41 @@ trait Client
|
|||
case _: PasswordBased => Future.successful(())
|
||||
|
||||
}
|
||||
val isAlreadyStarted: Future[Boolean] = isStartedF
|
||||
|
||||
val started: Future[BitcoindRpcClient] = {
|
||||
for {
|
||||
_ <- startedF
|
||||
_ <- awaitCookie(instance.authCredentials)
|
||||
_ = isStartedFlag.set(true)
|
||||
_ <- AsyncUtil.retryUntilSatisfiedF(() => isStartedF,
|
||||
interval = 1.seconds,
|
||||
maxTries = 120)
|
||||
} yield this.asInstanceOf[BitcoindRpcClient]
|
||||
val started: Future[Future[BitcoindRpcClient]] = isAlreadyStarted.map {
|
||||
case false =>
|
||||
instance match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
sys.error(
|
||||
s"Cannot start a remote instance, it needs to be started on the remote host machine")
|
||||
case local: BitcoindInstanceLocal =>
|
||||
val versionCheckF = version.map { v =>
|
||||
if (v != BitcoindVersion.Unknown) {
|
||||
val foundVersion = local.getVersion
|
||||
if (foundVersion != v) {
|
||||
throw new RuntimeException(
|
||||
s"Wrong version for bitcoind RPC client! Expected $version, got $foundVersion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val startedF = versionCheckF.flatMap(_ => startBinary())
|
||||
|
||||
for {
|
||||
_ <- startedF
|
||||
_ <- awaitCookie(instance.authCredentials)
|
||||
_ = isStartedFlag.set(true)
|
||||
_ <- AsyncUtil.retryUntilSatisfiedF(() => isStartedF,
|
||||
interval = 1.seconds,
|
||||
maxTries = 120)
|
||||
} yield this.asInstanceOf[BitcoindRpcClient]
|
||||
}
|
||||
case true =>
|
||||
for {
|
||||
_ <- awaitCookie(instance.authCredentials)
|
||||
_ = isStartedFlag.set(true)
|
||||
} yield this.asInstanceOf[BitcoindRpcClient]
|
||||
}
|
||||
|
||||
started.onComplete {
|
||||
|
@ -176,19 +223,27 @@ trait Client
|
|||
// of our instances. We don't want to do this on mainnet,
|
||||
// as both the logs and conf file most likely contain sensitive
|
||||
// information
|
||||
if (network != MainNet) {
|
||||
val tempfile = Files.createTempFile("bitcoind-log-", ".dump")
|
||||
val logfile = Files.readAllBytes(logFile)
|
||||
Files.write(tempfile, logfile)
|
||||
logger.info(s"Dumped debug.log to $tempfile")
|
||||
|
||||
val otherTempfile = Files.createTempFile("bitcoin-conf-", ".dump")
|
||||
val conffile = Files.readAllBytes(confFile)
|
||||
Files.write(otherTempfile, conffile)
|
||||
logger.info(s"Dumped bitcoin.conf to $otherTempfile")
|
||||
instance match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
()
|
||||
case _: BitcoindInstanceLocal =>
|
||||
if (network != MainNet) {
|
||||
val tempfile = Files.createTempFile("bitcoind-log-", ".dump")
|
||||
val logfile = Files.readAllBytes(logFileOpt.get)
|
||||
Files.write(tempfile, logfile)
|
||||
logger.info(s"Dumped debug.log to $tempfile")
|
||||
|
||||
val otherTempfile = Files.createTempFile("bitcoin-conf-", ".dump")
|
||||
val conffile = Files.readAllBytes(confFileOpt.get)
|
||||
Files.write(otherTempfile, conffile)
|
||||
logger.info(s"Dumped bitcoin.conf to $otherTempfile")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
started
|
||||
|
||||
started.flatten
|
||||
}
|
||||
|
||||
private def tryPing(): Future[Boolean] = {
|
||||
|
@ -227,11 +282,7 @@ trait Client
|
|||
// if the cookie file doesn't exist we're not started
|
||||
Future.successful(false)
|
||||
case (CookieBased(_, _) | PasswordBased(_, _)) =>
|
||||
if (isStartedFlag.get) {
|
||||
tryPing()
|
||||
} else {
|
||||
Future.successful(false)
|
||||
}
|
||||
tryPing()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,9 +378,16 @@ trait Client
|
|||
/** Cached http client to send requests to bitcoind with */
|
||||
private lazy val httpClient: HttpExt = Http(system)
|
||||
|
||||
private lazy val httpConnectionPoolSettings: ConnectionPoolSettings =
|
||||
Socks5ClientTransport.createConnectionPoolSettings(instance.rpcUri,
|
||||
instance.proxyParams)
|
||||
private lazy val httpConnectionPoolSettings: ConnectionPoolSettings = {
|
||||
instance match {
|
||||
case remote: BitcoindInstanceRemote =>
|
||||
Socks5ClientTransport.createConnectionPoolSettings(instance.rpcUri,
|
||||
remote.proxyParams)
|
||||
case _: BitcoindInstanceLocal =>
|
||||
Socks5ClientTransport.createConnectionPoolSettings(instance.rpcUri,
|
||||
None)
|
||||
}
|
||||
}
|
||||
|
||||
protected def sendRequest(req: HttpRequest): Future[HttpResponse] = {
|
||||
httpClient.singleRequest(req, settings = httpConnectionPoolSettings)
|
||||
|
|
|
@ -31,7 +31,7 @@ trait MempoolRpc { self: Client =>
|
|||
def getMemPoolAncestorsVerbose(txid: DoubleSha256DigestBE): Future[
|
||||
Map[DoubleSha256DigestBE, GetMemPoolResult]] = {
|
||||
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V21 | V20 | V19 | Experimental | Unknown =>
|
||||
bitcoindCall[Map[DoubleSha256DigestBE, GetMemPoolResultPostV19]](
|
||||
"getmempoolancestors",
|
||||
|
@ -62,7 +62,7 @@ trait MempoolRpc { self: Client =>
|
|||
|
||||
def getMemPoolDescendantsVerbose(txid: DoubleSha256DigestBE): Future[
|
||||
Map[DoubleSha256DigestBE, GetMemPoolResult]] = {
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V21 | V20 | V19 | Experimental | Unknown =>
|
||||
bitcoindCall[Map[DoubleSha256DigestBE, GetMemPoolResultPostV19]](
|
||||
"getmempooldescendants",
|
||||
|
@ -82,7 +82,7 @@ trait MempoolRpc { self: Client =>
|
|||
def getMemPoolEntry(
|
||||
txid: DoubleSha256DigestBE): Future[GetMemPoolEntryResult] = {
|
||||
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V21 | V20 | V19 | Experimental | Unknown =>
|
||||
bitcoindCall[GetMemPoolEntryResultPostV19]("getmempoolentry",
|
||||
List(JsString(txid.hex)))
|
||||
|
@ -124,7 +124,7 @@ trait MempoolRpc { self: Client =>
|
|||
def getRawMemPoolWithTransactions: Future[
|
||||
Map[DoubleSha256DigestBE, GetMemPoolResult]] = {
|
||||
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V21 | V20 | V19 | Experimental | Unknown =>
|
||||
bitcoindCall[Map[DoubleSha256DigestBE, GetMemPoolResultPostV19]](
|
||||
"getrawmempool",
|
||||
|
|
|
@ -40,7 +40,7 @@ trait MultisigRpc { self: Client =>
|
|||
JsArray(keys.map(keyToString)),
|
||||
JsString(account)) ++ addressType.map(Json.toJson(_)).toList
|
||||
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V21 | V20 | Unknown =>
|
||||
bitcoindCall[MultiSigResultPostV20](
|
||||
"addmultisigaddress",
|
||||
|
@ -82,7 +82,7 @@ trait MultisigRpc { self: Client =>
|
|||
minSignatures: Int,
|
||||
keys: Vector[ECPublicKey],
|
||||
walletNameOpt: Option[String] = None): Future[MultiSigResult] = {
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V21 | V20 | Unknown =>
|
||||
bitcoindCall[MultiSigResultPostV20](
|
||||
"createmultisig",
|
||||
|
|
|
@ -56,16 +56,14 @@ trait P2PRpc { self: Client =>
|
|||
}
|
||||
|
||||
def getNetworkInfo: Future[GetNetworkInfoResult] = {
|
||||
version match {
|
||||
case V21 | Unknown =>
|
||||
bitcoindCall[GetNetworkInfoResultPostV21]("getnetworkinfo")
|
||||
case V16 | V17 | V18 | V19 | V20 | Experimental =>
|
||||
bitcoindCall[GetNetworkInfoResultPostV21]("getnetworkinfo")
|
||||
.recoverWith { case _ =>
|
||||
bitcoindCall[GetNetworkInfoResultPreV21]("getnetworkinfo")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getPeerInfo: Future[Vector[Peer]] = {
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V21 | Unknown =>
|
||||
bitcoindCall[Vector[PeerPostV21]]("getpeerinfo")
|
||||
case V20 =>
|
||||
|
@ -76,7 +74,7 @@ trait P2PRpc { self: Client =>
|
|||
}
|
||||
|
||||
def listBanned: Future[Vector[NodeBan]] = {
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V21 | V20 | Unknown =>
|
||||
bitcoindCall[Vector[NodeBanPostV20]]("listbanned")
|
||||
case V16 | V17 | V18 | V19 | Experimental =>
|
||||
|
|
|
@ -110,16 +110,18 @@ trait RawTransactionRpc { self: Client =>
|
|||
transaction: Transaction,
|
||||
maxfeerate: Double = 0.10): Future[DoubleSha256DigestBE] = {
|
||||
|
||||
val feeParameter = self.version match {
|
||||
val feeParameterF = self.version.map {
|
||||
case V21 | V20 | V19 | Experimental | Unknown =>
|
||||
JsNumber(maxfeerate)
|
||||
case V16 | V17 | V18 =>
|
||||
JsBoolean(maxfeerate == 0)
|
||||
}
|
||||
|
||||
bitcoindCall[DoubleSha256DigestBE](
|
||||
"sendrawtransaction",
|
||||
List(JsString(transaction.hex), feeParameter))
|
||||
feeParameterF.flatMap { feeParameter =>
|
||||
bitcoindCall[DoubleSha256DigestBE](
|
||||
"sendrawtransaction",
|
||||
List(JsString(transaction.hex), feeParameter))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ trait UtilRpc { self: Client =>
|
|||
}
|
||||
|
||||
def getIndexInfo: Future[Map[String, IndexInfoResult]] = {
|
||||
version match {
|
||||
version.flatMap {
|
||||
case V21 | Unknown =>
|
||||
bitcoindCall[Map[String, IndexInfoResult]]("getindexinfo")
|
||||
case V16 | V17 | V18 | V19 | V20 | Experimental =>
|
||||
|
@ -36,7 +36,7 @@ trait UtilRpc { self: Client =>
|
|||
}
|
||||
|
||||
def getIndexInfo(indexName: String): Future[IndexInfoResult] = {
|
||||
version match {
|
||||
version.flatMap {
|
||||
case V21 | Unknown =>
|
||||
bitcoindCall[Map[String, IndexInfoResult]](
|
||||
"getindexinfo",
|
||||
|
|
|
@ -306,7 +306,7 @@ trait WalletRpc { self: Client =>
|
|||
walletNameOpt: Option[String] = None
|
||||
): Future[SetWalletFlagResult] = {
|
||||
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V21 | V20 | V19 | Experimental | Unknown =>
|
||||
bitcoindCall[SetWalletFlagResult](
|
||||
"setwalletflag",
|
||||
|
@ -320,7 +320,7 @@ trait WalletRpc { self: Client =>
|
|||
}
|
||||
|
||||
def getBalances: Future[GetBalancesResult] = {
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V21 | V20 | V19 | Experimental | Unknown =>
|
||||
bitcoindCall[GetBalancesResult]("getbalances")
|
||||
case V16 | V17 | V18 =>
|
||||
|
@ -331,7 +331,7 @@ trait WalletRpc { self: Client =>
|
|||
}
|
||||
|
||||
def getBalances(walletName: String): Future[GetBalancesResult] = {
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V21 | V20 | V19 | Experimental | Unknown =>
|
||||
bitcoindCall[GetBalancesResult]("getbalances",
|
||||
uriExtensionOpt =
|
||||
|
@ -404,7 +404,7 @@ trait WalletRpc { self: Client =>
|
|||
disablePrivateKeys: Boolean = false,
|
||||
blank: Boolean = false,
|
||||
passphrase: String = ""): Future[CreateWalletResult] =
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V21 | V20 | V19 | Experimental | Unknown =>
|
||||
bitcoindCall[CreateWalletResult]("createwallet",
|
||||
List(JsString(walletName),
|
||||
|
@ -422,7 +422,7 @@ trait WalletRpc { self: Client =>
|
|||
def getAddressInfo(
|
||||
address: BitcoinAddress,
|
||||
walletNameOpt: Option[String] = None): Future[AddressInfoResult] = {
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V16 | V17 =>
|
||||
bitcoindCall[AddressInfoResultPreV18](
|
||||
"getaddressinfo",
|
||||
|
|
|
@ -29,7 +29,8 @@ class BitcoindV16RpcClient(override val instance: BitcoindInstance)(implicit
|
|||
with V16AccountRpc
|
||||
with V16SendRpc {
|
||||
|
||||
override def version: BitcoindVersion = BitcoindVersion.V16
|
||||
override lazy val version: Future[BitcoindVersion] =
|
||||
Future.successful(BitcoindVersion.V16)
|
||||
|
||||
override def getFilterCount(): Future[Int] = filtersUnsupported
|
||||
|
||||
|
|
|
@ -35,7 +35,8 @@ class BitcoindV17RpcClient(override val instance: BitcoindInstance)(implicit
|
|||
extends BitcoindRpcClient(instance)
|
||||
with V17LabelRpc {
|
||||
|
||||
override def version: BitcoindVersion = BitcoindVersion.V17
|
||||
override lazy val version: Future[BitcoindVersion.V17.type] =
|
||||
Future.successful(BitcoindVersion.V17)
|
||||
|
||||
override def getFilterCount(): Future[Int] = filtersUnsupported
|
||||
|
||||
|
|
|
@ -35,7 +35,8 @@ class BitcoindV18RpcClient(override val instance: BitcoindInstance)(implicit
|
|||
with PsbtRpc
|
||||
with V18AssortedRpc {
|
||||
|
||||
override lazy val version: BitcoindVersion = BitcoindVersion.V18
|
||||
override lazy val version: Future[BitcoindVersion.V18.type] =
|
||||
Future.successful(BitcoindVersion.V18)
|
||||
|
||||
override def getFilterCount(): Future[Int] = filtersUnsupported
|
||||
|
||||
|
|
|
@ -78,7 +78,8 @@ class BitcoindV19RpcClient(override val instance: BitcoindInstance)(implicit
|
|||
} yield Vector(filter.filterDb(height))
|
||||
}
|
||||
|
||||
override lazy val version: BitcoindVersion = BitcoindVersion.V19
|
||||
override lazy val version: Future[BitcoindVersion.V19.type] =
|
||||
Future.successful(BitcoindVersion.V19)
|
||||
|
||||
/** $signRawTx
|
||||
*
|
||||
|
|
|
@ -78,7 +78,8 @@ class BitcoindV20RpcClient(override val instance: BitcoindInstance)(implicit
|
|||
} yield Vector(filter.filterDb(height))
|
||||
}
|
||||
|
||||
override lazy val version: BitcoindVersion = BitcoindVersion.V20
|
||||
override lazy val version: Future[BitcoindVersion.V20.type] =
|
||||
Future.successful(BitcoindVersion.V20)
|
||||
|
||||
/** $signRawTx
|
||||
*
|
||||
|
|
|
@ -36,7 +36,7 @@ trait V20MultisigRpc extends MultisigRpc { self: Client =>
|
|||
JsArray(keys.map(keyToString)),
|
||||
JsString(account)) ++ addressType.map(Json.toJson(_)).toList
|
||||
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V20 | V21 | Unknown =>
|
||||
bitcoindCall[MultiSigResultPostV20]("addmultisigaddress", params)
|
||||
case version @ (V16 | V17 | V18 | V19 | Experimental) =>
|
||||
|
@ -74,7 +74,7 @@ trait V20MultisigRpc extends MultisigRpc { self: Client =>
|
|||
minSignatures: Int,
|
||||
keys: Vector[ECPublicKey],
|
||||
walletNameOpt: Option[String] = None): Future[MultiSigResultPostV20] = {
|
||||
self.version match {
|
||||
self.version.flatMap {
|
||||
case V20 | V21 | Unknown =>
|
||||
bitcoindCall[MultiSigResultPostV20](
|
||||
"createmultisig",
|
||||
|
|
|
@ -79,7 +79,8 @@ class BitcoindV21RpcClient(override val instance: BitcoindInstance)(implicit
|
|||
} yield Vector(filter.filterDb(height))
|
||||
}
|
||||
|
||||
override lazy val version: BitcoindVersion = BitcoindVersion.V21
|
||||
override lazy val version: Future[BitcoindVersion.V21.type] =
|
||||
Future.successful(BitcoindVersion.V21)
|
||||
|
||||
/** $signRawTx
|
||||
*
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
package org.bitcoins.rpc.config
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import grizzled.slf4j.Logging
|
||||
import org.bitcoins.commons.util.NativeProcessFactory
|
||||
import org.bitcoins.core.api.commons.InstanceFactory
|
||||
import org.bitcoins.core.api.commons.{InstanceFactory, InstanceFactoryLocal}
|
||||
import org.bitcoins.core.config.NetworkParameters
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion
|
||||
import org.bitcoins.tor.Socks5ProxyParams
|
||||
|
||||
import java.io.{File, FileNotFoundException}
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.nio.file.{Files, Path, Paths}
|
||||
import scala.sys.process._
|
||||
|
@ -17,25 +19,31 @@ import scala.util.Properties
|
|||
*/
|
||||
sealed trait BitcoindInstance extends Logging {
|
||||
|
||||
require(binary.exists || isRemote,
|
||||
s"bitcoind binary path (${binary.getAbsolutePath}) does not exist!")
|
||||
|
||||
// would like to check .canExecute as well, but we've run into issues on some machines
|
||||
require(binary.isFile || isRemote,
|
||||
s"bitcoind binary path (${binary.getAbsolutePath}) must be a file")
|
||||
|
||||
/** The binary file that should get executed to start Bitcoin Core */
|
||||
def binary: File
|
||||
|
||||
def datadir: File
|
||||
|
||||
def network: NetworkParameters
|
||||
def uri: URI
|
||||
def rpcUri: URI
|
||||
def authCredentials: BitcoindAuthCredentials
|
||||
def zmqConfig: ZmqConfig
|
||||
|
||||
def isRemote: Boolean
|
||||
def p2pPort: Int = uri.getPort
|
||||
implicit def system: ActorSystem
|
||||
|
||||
}
|
||||
|
||||
/** Represents a bitcoind instance that is running locally on the same host */
|
||||
sealed trait BitcoindInstanceLocal extends BitcoindInstance {
|
||||
|
||||
/** The binary file that should get executed to start Bitcoin Core */
|
||||
def binary: File
|
||||
|
||||
def datadir: File
|
||||
|
||||
// would like to check .canExecute as well, but we've run into issues on some machines
|
||||
require(binary.isFile,
|
||||
s"bitcoind binary path (${binary.getAbsolutePath}) must be a file")
|
||||
|
||||
require(binary.exists,
|
||||
s"bitcoind binary path (${binary.getAbsolutePath}) does not exist!")
|
||||
|
||||
def getVersion: BitcoindVersion = {
|
||||
|
||||
|
@ -65,25 +73,26 @@ sealed trait BitcoindInstance extends Logging {
|
|||
case _: String => BitcoindVersion.Unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def p2pPort: Int = uri.getPort
|
||||
|
||||
/** Refers to a bitcoind instance that is running remotely on another machine */
|
||||
sealed trait BitcoindInstanceRemote extends BitcoindInstance {
|
||||
def proxyParams: Option[Socks5ProxyParams]
|
||||
}
|
||||
|
||||
object BitcoindInstance extends InstanceFactory[BitcoindInstance] {
|
||||
object BitcoindInstanceLocal
|
||||
extends InstanceFactoryLocal[BitcoindInstanceLocal, ActorSystem] {
|
||||
|
||||
private case class BitcoindInstanceImpl(
|
||||
private case class BitcoindInstanceLocalImpl(
|
||||
network: NetworkParameters,
|
||||
uri: URI,
|
||||
rpcUri: URI,
|
||||
authCredentials: BitcoindAuthCredentials,
|
||||
zmqConfig: ZmqConfig,
|
||||
binary: File,
|
||||
datadir: File,
|
||||
isRemote: Boolean,
|
||||
proxyParams: Option[Socks5ProxyParams]
|
||||
) extends BitcoindInstance
|
||||
datadir: File
|
||||
)(implicit override val system: ActorSystem)
|
||||
extends BitcoindInstanceLocal
|
||||
|
||||
def apply(
|
||||
network: NetworkParameters,
|
||||
|
@ -91,23 +100,22 @@ object BitcoindInstance extends InstanceFactory[BitcoindInstance] {
|
|||
rpcUri: URI,
|
||||
authCredentials: BitcoindAuthCredentials,
|
||||
zmqConfig: ZmqConfig = ZmqConfig(),
|
||||
binary: File = DEFAULT_BITCOIND_LOCATION,
|
||||
datadir: File = BitcoindConfig.DEFAULT_DATADIR,
|
||||
isRemote: Boolean = false,
|
||||
proxyParams: Option[Socks5ProxyParams] = None
|
||||
): BitcoindInstance = {
|
||||
BitcoindInstanceImpl(network,
|
||||
uri,
|
||||
rpcUri,
|
||||
authCredentials,
|
||||
zmqConfig = zmqConfig,
|
||||
binary = binary,
|
||||
datadir = datadir,
|
||||
isRemote = isRemote,
|
||||
proxyParams = proxyParams)
|
||||
binary: File = DEFAULT_BITCOIND_LOCATION match {
|
||||
case Some(file) => file
|
||||
case None => bitcoindLocationFromConfigFile
|
||||
},
|
||||
datadir: File = BitcoindConfig.DEFAULT_DATADIR
|
||||
)(implicit system: ActorSystem): BitcoindInstanceLocal = {
|
||||
BitcoindInstanceLocalImpl(network,
|
||||
uri,
|
||||
rpcUri,
|
||||
authCredentials,
|
||||
zmqConfig = zmqConfig,
|
||||
binary = binary,
|
||||
datadir = datadir)
|
||||
}
|
||||
|
||||
lazy val DEFAULT_BITCOIND_LOCATION: File = {
|
||||
lazy val DEFAULT_BITCOIND_LOCATION: Option[File] = {
|
||||
val cmd =
|
||||
if (Properties.isWin) {
|
||||
NativeProcessFactory.findExecutableOnPath("bitcoind.exe")
|
||||
|
@ -115,14 +123,21 @@ object BitcoindInstance extends InstanceFactory[BitcoindInstance] {
|
|||
NativeProcessFactory.findExecutableOnPath("bitcoind")
|
||||
}
|
||||
|
||||
cmd.getOrElse(
|
||||
throw new FileNotFoundException("Cannot find a path to bitcoind"))
|
||||
cmd
|
||||
}
|
||||
|
||||
lazy val remoteFilePath: File = {
|
||||
Files.createTempFile("dummy", "").toFile
|
||||
}
|
||||
|
||||
def bitcoindLocationFromConfigFile: File = {
|
||||
val homeVar = sys.env("HOME");
|
||||
val config = ConfigFactory
|
||||
.parseFile(new File(homeVar + "/.bitcoin-s/bitcoin-s.conf"))
|
||||
.resolve()
|
||||
new File(config.getString("bitcoin-s.bitcoind-rpc.binary"))
|
||||
}
|
||||
|
||||
/** Constructs a `bitcoind` instance from the given datadir, using the
|
||||
* `bitcoin.conf` found within (if any)
|
||||
*
|
||||
|
@ -130,8 +145,11 @@ object BitcoindInstance extends InstanceFactory[BitcoindInstance] {
|
|||
*/
|
||||
def fromDatadir(
|
||||
datadir: File = BitcoindConfig.DEFAULT_DATADIR,
|
||||
binary: File = DEFAULT_BITCOIND_LOCATION
|
||||
): BitcoindInstance = {
|
||||
binary: File = DEFAULT_BITCOIND_LOCATION match {
|
||||
case Some(file) => file
|
||||
case None => bitcoindLocationFromConfigFile
|
||||
}
|
||||
)(implicit system: ActorSystem): BitcoindInstanceLocal = {
|
||||
require(datadir.exists, s"${datadir.getPath} does not exist!")
|
||||
require(datadir.isDirectory, s"${datadir.getPath} is not a directory!")
|
||||
|
||||
|
@ -145,8 +163,15 @@ object BitcoindInstance extends InstanceFactory[BitcoindInstance] {
|
|||
}
|
||||
}
|
||||
|
||||
override def fromDataDir(dir: File): BitcoindInstance = {
|
||||
fromDatadir(dir, DEFAULT_BITCOIND_LOCATION)
|
||||
def fromDataDir(dir: File)(implicit
|
||||
system: ActorSystem): BitcoindInstanceLocal = {
|
||||
fromDatadir(
|
||||
dir,
|
||||
DEFAULT_BITCOIND_LOCATION match {
|
||||
case Some(file) => file
|
||||
case None => bitcoindLocationFromConfigFile
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/** Construct a `bitcoind` from the given config file. If no `datadir` setting
|
||||
|
@ -156,8 +181,11 @@ object BitcoindInstance extends InstanceFactory[BitcoindInstance] {
|
|||
*/
|
||||
def fromConfFile(
|
||||
file: File = BitcoindConfig.DEFAULT_CONF_FILE,
|
||||
binary: File = DEFAULT_BITCOIND_LOCATION
|
||||
): BitcoindInstance = {
|
||||
binary: File = DEFAULT_BITCOIND_LOCATION match {
|
||||
case Some(file) => file
|
||||
case None => bitcoindLocationFromConfigFile
|
||||
}
|
||||
)(implicit system: ActorSystem): BitcoindInstanceLocal = {
|
||||
require(file.exists, s"${file.getPath} does not exist!")
|
||||
require(file.isFile, s"${file.getPath} is not a file!")
|
||||
|
||||
|
@ -166,27 +194,104 @@ object BitcoindInstance extends InstanceFactory[BitcoindInstance] {
|
|||
fromConfig(conf, binary)
|
||||
}
|
||||
|
||||
override def fromConfigFile(file: File): BitcoindInstance = {
|
||||
fromConfFile(file, DEFAULT_BITCOIND_LOCATION)
|
||||
def fromConfigFile(file: File)(implicit
|
||||
system: ActorSystem): BitcoindInstanceLocal = {
|
||||
fromConfFile(
|
||||
file,
|
||||
DEFAULT_BITCOIND_LOCATION match {
|
||||
case Some(file) => file
|
||||
case None => bitcoindLocationFromConfigFile
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/** Constructs a `bitcoind` instance from the given config */
|
||||
def fromConfig(
|
||||
config: BitcoindConfig,
|
||||
binary: File = DEFAULT_BITCOIND_LOCATION
|
||||
): BitcoindInstance = {
|
||||
binary: File = DEFAULT_BITCOIND_LOCATION match {
|
||||
case Some(file) => file
|
||||
case None => bitcoindLocationFromConfigFile
|
||||
}
|
||||
)(implicit system: ActorSystem): BitcoindInstanceLocal = {
|
||||
|
||||
val authCredentials = BitcoindAuthCredentials.fromConfig(config)
|
||||
BitcoindInstance(config.network,
|
||||
config.uri,
|
||||
config.rpcUri,
|
||||
authCredentials,
|
||||
zmqConfig = ZmqConfig.fromConfig(config),
|
||||
binary = binary,
|
||||
datadir = config.datadir)
|
||||
BitcoindInstanceLocalImpl(config.network,
|
||||
config.uri,
|
||||
config.rpcUri,
|
||||
authCredentials,
|
||||
zmqConfig = ZmqConfig.fromConfig(config),
|
||||
binary = binary,
|
||||
datadir = config.datadir)
|
||||
}
|
||||
|
||||
override val DEFAULT_DATADIR: Path = BitcoindConfig.DEFAULT_DATADIR.toPath
|
||||
|
||||
override val DEFAULT_CONF_FILE: Path = BitcoindConfig.DEFAULT_CONF_FILE.toPath
|
||||
}
|
||||
|
||||
object BitcoindInstanceRemote
|
||||
extends InstanceFactory[BitcoindInstanceRemote, ActorSystem] {
|
||||
|
||||
private case class BitcoindInstanceRemoteImpl(
|
||||
network: NetworkParameters,
|
||||
uri: URI,
|
||||
rpcUri: URI,
|
||||
authCredentials: BitcoindAuthCredentials,
|
||||
zmqConfig: ZmqConfig,
|
||||
proxyParams: Option[Socks5ProxyParams]
|
||||
)(implicit override val system: ActorSystem)
|
||||
extends BitcoindInstanceRemote
|
||||
|
||||
def apply(
|
||||
network: NetworkParameters,
|
||||
uri: URI,
|
||||
rpcUri: URI,
|
||||
authCredentials: BitcoindAuthCredentials,
|
||||
zmqConfig: ZmqConfig = ZmqConfig(),
|
||||
proxyParams: Option[Socks5ProxyParams] = None
|
||||
)(implicit system: ActorSystem): BitcoindInstanceRemote = {
|
||||
BitcoindInstanceRemoteImpl(network,
|
||||
uri,
|
||||
rpcUri,
|
||||
authCredentials,
|
||||
zmqConfig = zmqConfig,
|
||||
proxyParams = proxyParams)
|
||||
}
|
||||
|
||||
def fromConfFile(
|
||||
file: File
|
||||
)(implicit system: ActorSystem): BitcoindInstanceRemote = {
|
||||
require(file.exists, s"${file.getPath} does not exist!")
|
||||
require(file.isFile, s"${file.getPath} is not a file!")
|
||||
|
||||
val conf = BitcoindRpcAppConfig(file.toPath)
|
||||
fromConfig(conf)
|
||||
}
|
||||
|
||||
override def fromConfigFile(file: File)(implicit
|
||||
system: ActorSystem): BitcoindInstanceRemote = {
|
||||
fromConfFile(
|
||||
file
|
||||
)
|
||||
}
|
||||
|
||||
def fromConfig(
|
||||
config: BitcoindRpcAppConfig
|
||||
)(implicit system: ActorSystem): BitcoindInstanceRemote = {
|
||||
|
||||
BitcoindInstanceRemoteImpl(config.network,
|
||||
config.uri,
|
||||
config.rpcUri,
|
||||
config.authCredentials,
|
||||
zmqConfig = config.zmqConfig,
|
||||
proxyParams = None)
|
||||
}
|
||||
|
||||
override def fromDataDir(dir: File)(implicit
|
||||
system: ActorSystem): BitcoindInstanceRemote = {
|
||||
require(dir.exists, s"${dir.getPath} does not exist!")
|
||||
require(dir.isDirectory, s"${dir.getPath} is not a directory!")
|
||||
val conf = BitcoindRpcAppConfig(dir.toPath)
|
||||
fromConfig(conf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,11 +21,14 @@ case class BitcoindRpcAppConfig(
|
|||
private val directory: Path,
|
||||
private val confs: Config*)(implicit val system: ActorSystem)
|
||||
extends AppConfig {
|
||||
|
||||
import system.dispatcher
|
||||
|
||||
override protected[bitcoins] def configOverrides: List[Config] = confs.toList
|
||||
|
||||
override protected[bitcoins] def moduleName: String =
|
||||
BitcoindRpcAppConfig.moduleName
|
||||
|
||||
override protected[bitcoins] type ConfigType = BitcoindRpcAppConfig
|
||||
|
||||
override protected[bitcoins] def newConfigOfType(
|
||||
|
@ -38,8 +41,8 @@ case class BitcoindRpcAppConfig(
|
|||
|
||||
override def stop(): Future[Unit] = Future.unit
|
||||
|
||||
lazy val DEFAULT_BINARY_PATH: File =
|
||||
BitcoindInstance.DEFAULT_BITCOIND_LOCATION
|
||||
lazy val DEFAULT_BINARY_PATH: Option[File] =
|
||||
BitcoindInstanceLocal.DEFAULT_BITCOIND_LOCATION
|
||||
|
||||
lazy val binaryOpt: Option[File] =
|
||||
config.getStringOrNone("bitcoin-s.bitcoind-rpc.binary").map(new File(_))
|
||||
|
@ -72,10 +75,11 @@ case class BitcoindRpcAppConfig(
|
|||
|
||||
lazy val rpcUri: URI = new URI(s"$rpcBind:$rpcPort")
|
||||
|
||||
lazy val rpcUser: String = config.getString("bitcoin-s.bitcoind-rpc.rpcuser")
|
||||
lazy val rpcUser: Option[String] =
|
||||
config.getStringOrNone("bitcoin-s.bitcoind-rpc.rpcuser")
|
||||
|
||||
lazy val rpcPassword: String =
|
||||
config.getString("bitcoin-s.bitcoind-rpc.rpcpassword")
|
||||
lazy val rpcPassword: Option[String] =
|
||||
config.getStringOrNone("bitcoin-s.bitcoind-rpc.rpcpassword")
|
||||
|
||||
lazy val torConf: TorAppConfig =
|
||||
TorAppConfig(directory, confs: _*)
|
||||
|
@ -91,8 +95,17 @@ case class BitcoindRpcAppConfig(
|
|||
lazy val isRemote: Boolean =
|
||||
config.getBooleanOrElse("bitcoin-s.bitcoind-rpc.isRemote", default = false)
|
||||
|
||||
lazy val authCredentials: BitcoindAuthCredentials =
|
||||
BitcoindAuthCredentials.PasswordBased(rpcUser, rpcPassword)
|
||||
lazy val authCredentials: BitcoindAuthCredentials = rpcUser match {
|
||||
case Some(rpcUser) => {
|
||||
rpcPassword match {
|
||||
case Some(rpcPassword) =>
|
||||
BitcoindAuthCredentials.PasswordBased(rpcUser, rpcPassword)
|
||||
case None =>
|
||||
BitcoindAuthCredentials.CookieBased(network)
|
||||
}
|
||||
}
|
||||
case None => BitcoindAuthCredentials.CookieBased(network)
|
||||
}
|
||||
|
||||
lazy val zmqRawBlock: Option[InetSocketAddress] =
|
||||
config.getStringOrNone("bitcoin-s.bitcoind-rpc.zmqpubrawblock").map { str =>
|
||||
|
@ -121,40 +134,49 @@ case class BitcoindRpcAppConfig(
|
|||
lazy val zmqConfig: ZmqConfig =
|
||||
ZmqConfig(zmqHashBlock, zmqRawBlock, zmqHashTx, zmqRawTx)
|
||||
|
||||
lazy val bitcoindInstance: BitcoindInstance = {
|
||||
val fallbackBinary =
|
||||
if (isRemote) BitcoindInstance.remoteFilePath else DEFAULT_BINARY_PATH
|
||||
lazy val bitcoindInstance = binaryOpt match {
|
||||
case Some(file) =>
|
||||
BitcoindInstanceLocal(
|
||||
network = network,
|
||||
uri = uri,
|
||||
rpcUri = rpcUri,
|
||||
authCredentials = authCredentials,
|
||||
zmqConfig = zmqConfig,
|
||||
binary = file,
|
||||
datadir = bitcoindDataDir
|
||||
)
|
||||
|
||||
BitcoindInstance(
|
||||
network = network,
|
||||
uri = uri,
|
||||
rpcUri = rpcUri,
|
||||
authCredentials = authCredentials,
|
||||
zmqConfig = zmqConfig,
|
||||
binary = binaryOpt.getOrElse(fallbackBinary),
|
||||
datadir = bitcoindDataDir,
|
||||
isRemote = isRemote,
|
||||
proxyParams = socks5ProxyParams
|
||||
)
|
||||
case None =>
|
||||
BitcoindInstanceRemote(network = network,
|
||||
uri = uri,
|
||||
rpcUri = rpcUri,
|
||||
authCredentials = authCredentials,
|
||||
zmqConfig = zmqConfig,
|
||||
proxyParams = socks5ProxyParams)
|
||||
}
|
||||
|
||||
lazy val client: BitcoindRpcClient = {
|
||||
val version = versionOpt.getOrElse(bitcoindInstance.getVersion)
|
||||
implicit val system: ActorSystem =
|
||||
ActorSystem.create("bitcoind-rpc-client-created-by-bitcoin-s", config)
|
||||
BitcoindRpcClient.fromVersion(version, bitcoindInstance)
|
||||
bitcoindInstance match {
|
||||
case local: BitcoindInstanceLocal =>
|
||||
val version = versionOpt.getOrElse(local.getVersion)
|
||||
BitcoindRpcClient.fromVersion(version, bitcoindInstance)
|
||||
case _: BitcoindInstanceRemote =>
|
||||
new BitcoindRpcClient(bitcoindInstance)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object BitcoindRpcAppConfig
|
||||
extends AppConfigFactoryActorSystem[BitcoindRpcAppConfig] {
|
||||
|
||||
override val moduleName: String = "bitcoind"
|
||||
|
||||
/** Constructs a node configuration from the default Bitcoin-S
|
||||
* data directory and given list of configuration overrides.
|
||||
*/
|
||||
|
||||
override def fromDatadir(datadir: Path, confs: Vector[Config])(implicit
|
||||
system: ActorSystem): BitcoindRpcAppConfig =
|
||||
BitcoindRpcAppConfig(datadir, confs: _*)
|
||||
|
||||
}
|
||||
|
|
|
@ -3,11 +3,17 @@ package org.bitcoins.core.api.commons
|
|||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
|
||||
trait InstanceFactory[+T] {
|
||||
/** A factory to create things like bitcoind instances or eclair instances
|
||||
* @tparam the type of the instance (i.e. BitcoindInstance)
|
||||
* @tparam the type of hte implicit parameter, this can be an execution context or ActorSystem
|
||||
*/
|
||||
trait InstanceFactory[+T, I] {
|
||||
def fromConfigFile(file: File)(implicit i: I): T
|
||||
|
||||
def fromDataDir(dir: File)(implicit i: I): T
|
||||
}
|
||||
|
||||
trait InstanceFactoryLocal[+T, I] extends InstanceFactory[T, I] {
|
||||
def DEFAULT_DATADIR: Path
|
||||
def DEFAULT_CONF_FILE: Path
|
||||
|
||||
def fromConfigFile(file: File = DEFAULT_CONF_FILE.toFile): T
|
||||
|
||||
def fromDataDir(dir: File = DEFAULT_DATADIR.toFile): T
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import org.bitcoins.feeprovider._
|
|||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
||||
import org.bitcoins.node._
|
||||
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
import org.bitcoins.rpc.config._
|
||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||
import org.bitcoins.wallet.Wallet
|
||||
|
@ -77,7 +77,8 @@ implicit val walletConf: WalletAppConfig =
|
|||
|
||||
// let's use a helper method to get a v19 bitcoind
|
||||
// and a ChainApi
|
||||
val bitcoind = BitcoindV19RpcClient(BitcoindInstance.fromConfigFile())
|
||||
val instance = BitcoindInstanceLocal.fromConfigFile(BitcoindConfig.DEFAULT_CONF_FILE)
|
||||
val bitcoind = BitcoindV19RpcClient(instance)
|
||||
val nodeApi = BitcoinSWalletTest.MockNodeApi
|
||||
|
||||
// Create our key manager
|
||||
|
|
|
@ -21,10 +21,10 @@ import org.bitcoins.chain.blockchain._
|
|||
import org.bitcoins.chain.blockchain.sync._
|
||||
import org.bitcoins.chain.models._
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
import org.bitcoins.rpc.config.BitcoindInstanceLocal
|
||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||
import org.bitcoins.testkit.chain._
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import scala.concurrent._
|
||||
import java.nio.file.Files
|
||||
```
|
||||
|
@ -32,12 +32,12 @@ import java.nio.file.Files
|
|||
```scala mdoc:compile-only
|
||||
|
||||
implicit val ec = ExecutionContext.global
|
||||
|
||||
implicit val system = ActorSystem("System")
|
||||
// We are assuming that a `bitcoind` regtest node is running the background.
|
||||
// You can see our `bitcoind` guides to see how to connect
|
||||
// to a local or remote `bitcoind` node.
|
||||
|
||||
val bitcoindInstance = BitcoindInstance.fromDatadir()
|
||||
val bitcoindInstance = BitcoindInstanceLocal.fromDatadir()
|
||||
val rpcCli = BitcoindRpcClient(bitcoindInstance)
|
||||
|
||||
// Next, we need to create a way to monitor the chain:
|
||||
|
|
|
@ -14,7 +14,7 @@ import org.bitcoins.feeprovider._
|
|||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
||||
import org.bitcoins.node._
|
||||
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
import org.bitcoins.rpc.config._
|
||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||
import org.bitcoins.wallet.Wallet
|
||||
|
@ -55,7 +55,8 @@ implicit val walletConf: WalletAppConfig =
|
|||
|
||||
// let's use a helper method to get a v19 bitcoind
|
||||
// and a ChainApi
|
||||
val bitcoind = BitcoindV19RpcClient(BitcoindInstance.fromConfigFile())
|
||||
val instance = BitcoindInstanceLocal.fromConfigFile(BitcoindConfig.DEFAULT_CONF_FILE)
|
||||
val bitcoind = BitcoindV19RpcClient(instance)
|
||||
val chainApi = BitcoinSWalletTest.MockChainQueryApi
|
||||
val aesPasswordOpt = Some(AesPassword.fromString("password"))
|
||||
|
||||
|
|
|
@ -67,7 +67,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, Vector.newBuilder)
|
||||
val bitcoindF = BitcoindRpcTestUtil.startedBitcoindRpcClient(Some(instance), Vector.newBuilder)
|
||||
|
||||
//contains information on how to connect to bitcoin's p2p info
|
||||
val peerF = bitcoindF.flatMap(b => NodeUnitTest.createPeer(b))
|
||||
|
|
|
@ -42,13 +42,13 @@ import org.bitcoins.rpc.BitcoindWalletException
|
|||
import org.bitcoins.crypto._
|
||||
import org.bitcoins.core.protocol._
|
||||
import org.bitcoins.core.currency._
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
```
|
||||
|
||||
```scala mdoc:compile-only
|
||||
|
||||
implicit val ec: ExecutionContext = ExecutionContext.global
|
||||
|
||||
implicit val system = ActorSystem("System")
|
||||
// this reads authentication credentials and
|
||||
// connection details from the default data
|
||||
// directory on your platform
|
||||
|
@ -68,7 +68,7 @@ To do so the wallet rpc functions have an optional `walletName` parameter.
|
|||
```scala mdoc:compile-only
|
||||
|
||||
implicit val ec: ExecutionContext = ExecutionContext.global
|
||||
|
||||
implicit val system = ActorSystem("System")
|
||||
val client = BitcoindRpcClient.fromDatadir(binary=new File("/path/to/bitcoind"), datadir=new File("/path/to/bitcoind-datadir"))
|
||||
|
||||
for {
|
||||
|
@ -96,7 +96,7 @@ ready to create the connection with our RPC client
|
|||
```scala mdoc:compile-only
|
||||
|
||||
implicit val ec: ExecutionContext = ExecutionContext.global
|
||||
|
||||
implicit val system = ActorSystem("System")
|
||||
val username = "FILL_ME_IN" //this username comes from 'rpcuser' in your bitcoin.conf file
|
||||
val password = "FILL_ME_IN" //this password comes from your 'rpcpassword' in your bitcoin.conf file
|
||||
|
||||
|
@ -106,7 +106,7 @@ val authCredentials = BitcoindAuthCredentials.PasswordBased(
|
|||
)
|
||||
|
||||
val bitcoindInstance = {
|
||||
BitcoindInstance (
|
||||
BitcoindInstanceLocal(
|
||||
network = MainNet,
|
||||
uri = new URI(s"http://localhost:${MainNet.port}"),
|
||||
rpcUri = new URI(s"http://localhost:${MainNet.rpcPort}"),
|
||||
|
|
|
@ -36,7 +36,7 @@ Here is an example of how to start eclair:
|
|||
```scala mdoc:invisible
|
||||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.eclair.rpc.client.EclairRpcClient
|
||||
import org.bitcoins.eclair.rpc.config.EclairInstance
|
||||
import org.bitcoins.eclair.rpc.config.EclairInstanceLocal
|
||||
import java.nio.file.Paths
|
||||
```
|
||||
|
||||
|
@ -47,7 +47,7 @@ implicit val ec = system.dispatcher
|
|||
|
||||
val datadirPath = Paths.get("path", "to", "datadir")
|
||||
val binaryPath = Paths.get("path", "to", "eclair-node-0.5.0-ac08560", "bin", "eclair-node.sh")
|
||||
val instance = EclairInstance.fromDatadir(datadirPath.toFile, logbackXml = None, proxyParams = None)
|
||||
val instance = EclairInstanceLocal.fromDatadir(datadirPath.toFile, logbackXml = None, proxyParams = None)
|
||||
val client = new EclairRpcClient(instance, Some(binaryPath.toFile))
|
||||
|
||||
val startedF = client.start()
|
||||
|
|
|
@ -43,7 +43,7 @@ implicit val ec = system.dispatcher
|
|||
|
||||
val datadirPath = Paths.get("path", "to", "datadir")
|
||||
val binaryPath = Paths.get("path", "to", "lnd-linux-amd64-v0.13.1-beta", "lnd")
|
||||
val instance = LndInstance.fromDataDir(datadirPath.toFile)
|
||||
val instance = LndInstanceLocal.fromDataDir(datadirPath.toFile)
|
||||
val client = new LndRpcClient(instance, Some(binaryPath.toFile))
|
||||
|
||||
val startedF = client.start()
|
||||
|
|
|
@ -48,7 +48,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, Vector.newBuilder)
|
||||
val bitcoindRpcClientF = BitcoindRpcTestUtil.startedBitcoindRpcClient(Some(instance), Vector.newBuilder)
|
||||
|
||||
//yay! it's started. Now you can run tests against this.
|
||||
//let's just grab the block count for an example
|
||||
|
|
|
@ -29,7 +29,7 @@ import org.bitcoins.core.wallet.fee._
|
|||
import org.bitcoins.feeprovider._
|
||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
||||
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
import org.bitcoins.rpc.config.BitcoindInstanceLocal
|
||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||
import org.bitcoins.wallet._
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
|
@ -46,7 +46,8 @@ implicit val walletConf: WalletAppConfig =
|
|||
|
||||
// let's use a helper method to get a v19 bitcoind
|
||||
// and a ChainApi
|
||||
val bitcoind = BitcoindV19RpcClient(BitcoindInstance.fromConfigFile())
|
||||
|
||||
val bitcoind = BitcoindV19RpcClient(BitcoindInstanceLocal.fromConfFile())
|
||||
val aesPasswordOpt = Some(AesPassword.fromString("password"))
|
||||
|
||||
// Create our key manager
|
||||
|
|
|
@ -62,7 +62,7 @@ import org.bitcoins.core.wallet.fee._
|
|||
import org.bitcoins.feeprovider._
|
||||
import org.bitcoins.keymanager.bip39._
|
||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
import org.bitcoins.rpc.config._
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import org.bitcoins.core.api.wallet.WalletApi
|
||||
import org.bitcoins.wallet.Wallet
|
||||
|
@ -71,6 +71,7 @@ import com.typesafe.config.ConfigFactory
|
|||
import java.nio.file.Files
|
||||
import java.time.Instant
|
||||
import scala.concurrent._
|
||||
import akka.actor.ActorSystem
|
||||
|
||||
val chainApi = new ChainQueryApi {
|
||||
override def epochSecondToBlockHeight(time: Long): Future[Int] = Future.successful(0)
|
||||
|
@ -85,7 +86,7 @@ val chainApi = new ChainQueryApi {
|
|||
|
||||
```scala mdoc:compile-only
|
||||
implicit val ec = scala.concurrent.ExecutionContext.global
|
||||
|
||||
implicit val system = ActorSystem("System")
|
||||
|
||||
val config = ConfigFactory.parseString {
|
||||
"""
|
||||
|
@ -113,7 +114,7 @@ val configF: Future[Unit] = for {
|
|||
_ <- chainConfig.start()
|
||||
} yield ()
|
||||
|
||||
val bitcoindInstance = BitcoindInstance.fromDatadir()
|
||||
val bitcoindInstance = BitcoindInstanceLocal.fromDatadir()
|
||||
|
||||
val bitcoind = BitcoindRpcClient(bitcoindInstance)
|
||||
|
||||
|
|
|
@ -21,7 +21,10 @@ import org.bitcoins.core.protocol.ln.{
|
|||
}
|
||||
import org.bitcoins.eclair.rpc.api._
|
||||
import org.bitcoins.eclair.rpc.client.EclairRpcClient
|
||||
import org.bitcoins.eclair.rpc.config.{EclairAuthCredentials, EclairInstance}
|
||||
import org.bitcoins.eclair.rpc.config.{
|
||||
EclairAuthCredentials,
|
||||
EclairInstanceLocal
|
||||
}
|
||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||
import org.bitcoins.testkit.async.TestAsyncUtil
|
||||
import org.bitcoins.testkit.eclair.rpc.{EclairNodes4, EclairRpcTestUtil}
|
||||
|
@ -512,7 +515,7 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
|
|||
|
||||
val badInstanceF = badCredentialsF.flatMap { badCredentials =>
|
||||
val getBadInstance = (client: EclairRpcClient, _: EclairRpcClient) => {
|
||||
val instance = EclairInstance(
|
||||
val instance = EclairInstanceLocal(
|
||||
network = client.instance.network,
|
||||
uri = client.instance.uri,
|
||||
rpcUri = client.instance.rpcUri,
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package org.bitcoins.eclair.rpc.config
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import com.typesafe.config.{Config, ConfigFactory}
|
||||
import org.bitcoins.core.api.commons.InstanceFactory
|
||||
import org.bitcoins.core.api.commons.InstanceFactoryLocal
|
||||
import org.bitcoins.core.config.{MainNet, NetworkParameters, RegTest, TestNet3}
|
||||
import org.bitcoins.core.protocol.ln.LnPolicy
|
||||
import org.bitcoins.core.util.NetworkUtil
|
||||
|
@ -25,15 +26,20 @@ sealed trait EclairInstance {
|
|||
def proxyParams: Option[Socks5ProxyParams]
|
||||
}
|
||||
|
||||
sealed trait EclairInstanceLocal extends EclairInstance
|
||||
|
||||
sealed trait EclairInstanceRemote extends EclairInstance
|
||||
|
||||
/** @define fromConfigDoc
|
||||
* Parses a [[com.typesafe.config.Config Config]] in the format of this
|
||||
* [[https://github.com/ACINQ/eclair/blob/master/eclair-core/src/main/resources/reference.conf sample reference.conf]]
|
||||
* file to a
|
||||
* [[org.bitcoins.eclair.rpc.config.EclairInstance EclairInstance]]
|
||||
*/
|
||||
object EclairInstance extends InstanceFactory[EclairInstance] {
|
||||
object EclairInstanceLocal
|
||||
extends InstanceFactoryLocal[EclairInstanceLocal, ActorSystem] {
|
||||
|
||||
private case class EclairInstanceImpl(
|
||||
private case class EclairInstanceLocalImpl(
|
||||
network: NetworkParameters,
|
||||
uri: URI,
|
||||
rpcUri: URI,
|
||||
|
@ -43,7 +49,7 @@ object EclairInstance extends InstanceFactory[EclairInstance] {
|
|||
bitcoindAuthCredentials: Option[BitcoindAuthCredentials],
|
||||
zmqConfig: Option[ZmqConfig],
|
||||
proxyParams: Option[Socks5ProxyParams])
|
||||
extends EclairInstance
|
||||
extends EclairInstanceLocal
|
||||
|
||||
def apply(
|
||||
network: NetworkParameters,
|
||||
|
@ -56,15 +62,15 @@ object EclairInstance extends InstanceFactory[EclairInstance] {
|
|||
bitcoindAuthCredentials: Option[BitcoindAuthCredentials] = None,
|
||||
zmqConfig: Option[ZmqConfig] = None
|
||||
): EclairInstance = {
|
||||
EclairInstanceImpl(network,
|
||||
uri,
|
||||
rpcUri,
|
||||
authCredentials,
|
||||
logbackXmlPath,
|
||||
bitcoindRpcUri,
|
||||
bitcoindAuthCredentials,
|
||||
zmqConfig,
|
||||
proxyParams)
|
||||
EclairInstanceLocalImpl(network,
|
||||
uri,
|
||||
rpcUri,
|
||||
authCredentials,
|
||||
logbackXmlPath,
|
||||
bitcoindRpcUri,
|
||||
bitcoindAuthCredentials,
|
||||
zmqConfig,
|
||||
proxyParams)
|
||||
}
|
||||
|
||||
override val DEFAULT_DATADIR: Path = Paths.get(Properties.userHome, ".eclair")
|
||||
|
@ -80,7 +86,7 @@ object EclairInstance extends InstanceFactory[EclairInstance] {
|
|||
def fromDatadir(
|
||||
datadir: File = DEFAULT_DATADIR.toFile,
|
||||
logbackXml: Option[String],
|
||||
proxyParams: Option[Socks5ProxyParams]): EclairInstance = {
|
||||
proxyParams: Option[Socks5ProxyParams]): EclairInstanceLocal = {
|
||||
require(datadir.exists, s"${datadir.getPath} does not exist!")
|
||||
require(datadir.isDirectory, s"${datadir.getPath} is not a directory!")
|
||||
|
||||
|
@ -90,14 +96,14 @@ object EclairInstance extends InstanceFactory[EclairInstance] {
|
|||
|
||||
}
|
||||
|
||||
override def fromConfigFile(
|
||||
file: File = DEFAULT_CONF_FILE.toFile): EclairInstance =
|
||||
override def fromConfigFile(file: File = DEFAULT_CONF_FILE.toFile)(implicit
|
||||
system: ActorSystem): EclairInstanceLocal =
|
||||
fromConfFile(file, None, None)
|
||||
|
||||
def fromConfFile(
|
||||
file: File = DEFAULT_CONF_FILE.toFile,
|
||||
logbackXml: Option[String],
|
||||
proxyParams: Option[Socks5ProxyParams]): EclairInstance = {
|
||||
proxyParams: Option[Socks5ProxyParams]): EclairInstanceLocal = {
|
||||
require(file.exists, s"${file.getPath} does not exist!")
|
||||
require(file.isFile, s"${file.getPath} is not a file!")
|
||||
|
||||
|
@ -106,8 +112,8 @@ object EclairInstance extends InstanceFactory[EclairInstance] {
|
|||
fromConfig(config, file.getParentFile, logbackXml, proxyParams)
|
||||
}
|
||||
|
||||
override def fromDataDir(
|
||||
dir: File = DEFAULT_DATADIR.toFile): EclairInstance = {
|
||||
override def fromDataDir(dir: File = DEFAULT_DATADIR.toFile)(implicit
|
||||
system: ActorSystem): EclairInstanceLocal = {
|
||||
require(dir.exists, s"${dir.getPath} does not exist!")
|
||||
require(dir.isDirectory, s"${dir.getPath} is not a directory!")
|
||||
|
||||
|
@ -121,7 +127,7 @@ object EclairInstance extends InstanceFactory[EclairInstance] {
|
|||
config: Config,
|
||||
datadir: File,
|
||||
logbackXml: Option[String],
|
||||
proxyParams: Option[Socks5ProxyParams]): EclairInstance = {
|
||||
proxyParams: Option[Socks5ProxyParams]): EclairInstanceLocal = {
|
||||
fromConfig(config, Some(datadir), logbackXml, proxyParams)
|
||||
}
|
||||
|
||||
|
@ -135,7 +141,7 @@ object EclairInstance extends InstanceFactory[EclairInstance] {
|
|||
config: Config,
|
||||
datadir: Option[File],
|
||||
logbackXml: Option[String],
|
||||
proxyParams: Option[Socks5ProxyParams]): EclairInstance = {
|
||||
proxyParams: Option[Socks5ProxyParams]): EclairInstanceLocal = {
|
||||
val chain = ConfigUtil.getStringOrElse(config, "eclair.chain", "testnet")
|
||||
|
||||
// default conf: https://github.com/ACINQ/eclair/blob/master/eclair-core/src/main/resources/reference.conf
|
||||
|
@ -193,7 +199,7 @@ object EclairInstance extends InstanceFactory[EclairInstance] {
|
|||
|
||||
val zmqConfig = ZmqConfig(rawBlock = rawBlock, rawTx = rawTx)
|
||||
|
||||
EclairInstance(
|
||||
EclairInstanceLocalImpl(
|
||||
network = np,
|
||||
uri = uri,
|
||||
rpcUri = rpcUri,
|
||||
|
|
|
@ -25,7 +25,7 @@ import org.bitcoins.core.util.StartStopAsync
|
|||
import org.bitcoins.core.wallet.fee.{SatoshisPerKW, SatoshisPerVirtualByte}
|
||||
import org.bitcoins.crypto._
|
||||
import org.bitcoins.lnd.rpc.LndRpcClient._
|
||||
import org.bitcoins.lnd.rpc.config.LndInstance
|
||||
import org.bitcoins.lnd.rpc.config.{LndInstance, LndInstanceLocal}
|
||||
import scodec.bits.ByteVector
|
||||
import signrpc.TxOut
|
||||
import walletrpc.{SendOutputsRequest, WalletKitClient}
|
||||
|
@ -40,17 +40,21 @@ import scala.util.{Failure, Success, Try}
|
|||
|
||||
/** @param binary Path to lnd executable
|
||||
*/
|
||||
class LndRpcClient(val instance: LndInstance, binary: Option[File] = None)(
|
||||
class LndRpcClient(val instance: LndInstance, binaryOpt: Option[File] = None)(
|
||||
implicit system: ActorSystem)
|
||||
extends NativeProcessFactory
|
||||
with StartStopAsync[LndRpcClient]
|
||||
with Logging {
|
||||
instance match {
|
||||
case _: LndInstanceLocal =>
|
||||
require(binaryOpt.isDefined,
|
||||
s"Binary must be defined with a local instance of lnd")
|
||||
}
|
||||
|
||||
/** The command to start the daemon on the underlying OS */
|
||||
override def cmd: String = binary match {
|
||||
case Some(file) =>
|
||||
s"$file --lnddir=${instance.datadir.toAbsolutePath}"
|
||||
case None => ""
|
||||
override def cmd: String = instance match {
|
||||
case local: LndInstanceLocal =>
|
||||
s"${binaryOpt.get} --lnddir=${local.datadir.toAbsolutePath}"
|
||||
}
|
||||
|
||||
implicit val executionContext: ExecutionContext = system.dispatcher
|
||||
|
@ -513,9 +517,10 @@ class LndRpcClient(val instance: LndInstance, binary: Option[File] = None)(
|
|||
// too many tries to get info about a payment
|
||||
// either Lnd is down or the payment is still in PENDING state for some reason
|
||||
// complete the promise with an exception so the runnable will be canceled
|
||||
p.failure(new RuntimeException(
|
||||
s"LndApi.monitorInvoice() [${instance.datadir}] too many attempts: ${attempts
|
||||
.get()} for invoice=${rHash.hash.hex}"))
|
||||
p.failure(
|
||||
new RuntimeException(
|
||||
s"LndApi.monitorInvoice() [${instance}] too many attempts: ${attempts
|
||||
.get()} for invoice=${rHash.hash.hex}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,7 +153,7 @@ case class LndConfig(private[bitcoins] val lines: Seq[String], datadir: File)
|
|||
LndConfig(lines, newDatadir)
|
||||
}
|
||||
|
||||
lazy val lndInstance: LndInstance = LndInstance(
|
||||
lazy val lndInstance: LndInstanceLocal = LndInstanceLocal(
|
||||
datadir.toPath,
|
||||
network,
|
||||
listenBinding,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.bitcoins.lnd.rpc.config
|
||||
|
||||
import org.bitcoins.core.api.commons.InstanceFactory
|
||||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.core.api.commons.InstanceFactoryLocal
|
||||
import org.bitcoins.core.config._
|
||||
import org.bitcoins.rpc.config.BitcoindAuthCredentials._
|
||||
import org.bitcoins.rpc.config._
|
||||
|
@ -11,7 +12,23 @@ import java.net._
|
|||
import java.nio.file._
|
||||
import scala.util.Properties
|
||||
|
||||
case class LndInstance(
|
||||
sealed trait LndInstance {
|
||||
def network: BitcoinNetwork
|
||||
def listenBinding: URI
|
||||
def restUri: URI
|
||||
def rpcUri: URI
|
||||
def bitcoindAuthCredentials: PasswordBased
|
||||
def bitcoindRpcUri: URI
|
||||
def zmqConfig: ZmqConfig
|
||||
def debugLevel: LogLevel
|
||||
def macaroon: String
|
||||
|
||||
def datadir: Path
|
||||
|
||||
def certFile: File
|
||||
}
|
||||
|
||||
case class LndInstanceLocal(
|
||||
datadir: Path,
|
||||
network: BitcoinNetwork,
|
||||
listenBinding: URI,
|
||||
|
@ -20,14 +37,12 @@ case class LndInstance(
|
|||
bitcoindAuthCredentials: PasswordBased,
|
||||
bitcoindRpcUri: URI,
|
||||
zmqConfig: ZmqConfig,
|
||||
debugLevel: LogLevel) {
|
||||
|
||||
lazy val certFile: File =
|
||||
datadir.resolve("tls.cert").toFile
|
||||
debugLevel: LogLevel)
|
||||
extends LndInstance {
|
||||
|
||||
private var macaroonOpt: Option[String] = None
|
||||
|
||||
def macaroon: String = {
|
||||
override def macaroon: String = {
|
||||
macaroonOpt match {
|
||||
case Some(value) => value
|
||||
case None =>
|
||||
|
@ -36,7 +51,7 @@ case class LndInstance(
|
|||
.resolve("data")
|
||||
.resolve("chain")
|
||||
.resolve("bitcoin")
|
||||
.resolve(LndInstance.getNetworkDirName(network))
|
||||
.resolve(LndInstanceLocal.getNetworkDirName(network))
|
||||
.resolve("admin.macaroon")
|
||||
|
||||
val bytes = Files.readAllBytes(path)
|
||||
|
@ -46,9 +61,13 @@ case class LndInstance(
|
|||
hex
|
||||
}
|
||||
}
|
||||
|
||||
lazy val certFile: File =
|
||||
datadir.resolve("tls.cert").toFile
|
||||
}
|
||||
|
||||
object LndInstance extends InstanceFactory[LndInstance] {
|
||||
object LndInstanceLocal
|
||||
extends InstanceFactoryLocal[LndInstanceLocal, ActorSystem] {
|
||||
|
||||
override val DEFAULT_DATADIR: Path = Paths.get(Properties.userHome, ".lnd")
|
||||
|
||||
|
@ -63,8 +82,8 @@ object LndInstance extends InstanceFactory[LndInstance] {
|
|||
}
|
||||
}
|
||||
|
||||
override def fromConfigFile(
|
||||
file: File = DEFAULT_CONF_FILE.toFile): LndInstance = {
|
||||
override def fromConfigFile(file: File = DEFAULT_CONF_FILE.toFile)(implicit
|
||||
system: ActorSystem): LndInstanceLocal = {
|
||||
require(file.exists, s"${file.getPath} does not exist!")
|
||||
require(file.isFile, s"${file.getPath} is not a file!")
|
||||
|
||||
|
@ -73,7 +92,8 @@ object LndInstance extends InstanceFactory[LndInstance] {
|
|||
fromConfig(config)
|
||||
}
|
||||
|
||||
override def fromDataDir(dir: File = DEFAULT_DATADIR.toFile): LndInstance = {
|
||||
override def fromDataDir(dir: File = DEFAULT_DATADIR.toFile)(implicit
|
||||
system: ActorSystem): LndInstanceLocal = {
|
||||
require(dir.exists, s"${dir.getPath} does not exist!")
|
||||
require(dir.isDirectory, s"${dir.getPath} is not a directory!")
|
||||
|
||||
|
@ -83,7 +103,7 @@ object LndInstance extends InstanceFactory[LndInstance] {
|
|||
fromConfig(config)
|
||||
}
|
||||
|
||||
def fromConfig(config: LndConfig): LndInstance = {
|
||||
def fromConfig(config: LndConfig): LndInstanceLocal = {
|
||||
config.lndInstance
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,11 +21,12 @@ import org.bitcoins.core.protocol.ln.node.NodeId
|
|||
import org.bitcoins.crypto.Sha256Digest
|
||||
import org.bitcoins.eclair.rpc.api._
|
||||
import org.bitcoins.eclair.rpc.client.EclairRpcClient
|
||||
import org.bitcoins.eclair.rpc.config.EclairInstance
|
||||
import org.bitcoins.eclair.rpc.config.EclairInstanceLocal
|
||||
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
|
||||
import org.bitcoins.rpc.config.{
|
||||
BitcoindAuthCredentials,
|
||||
BitcoindInstance,
|
||||
BitcoindInstanceLocal,
|
||||
ZmqConfig
|
||||
}
|
||||
import org.bitcoins.rpc.util.RpcUtil
|
||||
|
@ -59,10 +60,13 @@ trait EclairRpcTestUtil extends Logging {
|
|||
|
||||
/** Makes a best effort to get a 0.16 bitcoind instance
|
||||
*/
|
||||
def startedBitcoindRpcClient(instance: BitcoindInstance = bitcoindInstance())(
|
||||
implicit actorSystem: ActorSystem): Future[BitcoindRpcClient] = {
|
||||
def startedBitcoindRpcClient(
|
||||
instanceOpt: Option[BitcoindInstanceLocal] = None)(implicit
|
||||
actorSystem: ActorSystem): Future[BitcoindRpcClient] = {
|
||||
//need to do something with the Vector.newBuilder presumably?
|
||||
BitcoindRpcTestUtil.startedBitcoindRpcClient(instance, Vector.newBuilder)
|
||||
val instance = instanceOpt.getOrElse(bitcoindInstance())
|
||||
BitcoindRpcTestUtil.startedBitcoindRpcClient(Some(instance),
|
||||
Vector.newBuilder)
|
||||
}
|
||||
|
||||
/** Creates a bitcoind instance with the given parameters */
|
||||
|
@ -70,8 +74,8 @@ trait EclairRpcTestUtil extends Logging {
|
|||
port: Int = RpcUtil.randomPort,
|
||||
rpcPort: Int = RpcUtil.randomPort,
|
||||
zmqConfig: ZmqConfig = RpcUtil.zmqConfig,
|
||||
bitcoindV: BitcoindVersion =
|
||||
EclairRpcClient.bitcoindV): BitcoindInstance = {
|
||||
bitcoindV: BitcoindVersion = EclairRpcClient.bitcoindV)(implicit
|
||||
system: ActorSystem): BitcoindInstanceLocal = {
|
||||
BitcoindRpcTestUtil.getInstance(bitcoindVersion = bitcoindV,
|
||||
port = port,
|
||||
rpcPort = rpcPort,
|
||||
|
@ -147,29 +151,29 @@ trait EclairRpcTestUtil extends Logging {
|
|||
|
||||
/** Assumes bitcoind is running already and you have specified correct bindings in eclair.conf */
|
||||
def cannonicalEclairInstance(
|
||||
logbackXml: Option[String] = None): EclairInstance = {
|
||||
logbackXml: Option[String] = None): EclairInstanceLocal = {
|
||||
val datadir = cannonicalDatadir
|
||||
eclairInstance(datadir, logbackXml)
|
||||
}
|
||||
|
||||
def eclairInstance(
|
||||
datadir: File,
|
||||
logbackXml: Option[String]): EclairInstance = {
|
||||
val instance = EclairInstance.fromDatadir(datadir, logbackXml, None)
|
||||
logbackXml: Option[String]): EclairInstanceLocal = {
|
||||
val instance = EclairInstanceLocal.fromDatadir(datadir, logbackXml, None)
|
||||
instance
|
||||
}
|
||||
|
||||
/** Starts the given bitcoind instance and then starts the eclair instance */
|
||||
def eclairInstance(
|
||||
bitcoindRpc: BitcoindRpcClient,
|
||||
logbackXml: Option[String] = None): EclairInstance = {
|
||||
logbackXml: Option[String] = None): EclairInstanceLocal = {
|
||||
val datadir = eclairDataDir(bitcoindRpc, false)
|
||||
eclairInstance(datadir, logbackXml)
|
||||
}
|
||||
|
||||
def randomEclairInstance(
|
||||
bitcoindRpc: BitcoindRpcClient,
|
||||
logbackXml: Option[String] = None): EclairInstance = {
|
||||
logbackXml: Option[String] = None): EclairInstanceLocal = {
|
||||
val datadir = eclairDataDir(bitcoindRpc, false)
|
||||
eclairInstance(datadir, logbackXml)
|
||||
}
|
||||
|
@ -682,7 +686,7 @@ trait EclairRpcTestUtil extends Logging {
|
|||
val bitcoindRpc = {
|
||||
val instance = eclairRpcClient.instance
|
||||
val auth = instance.authCredentials
|
||||
val bitcoindInstance = BitcoindInstance(
|
||||
val bitcoindInstance = BitcoindInstanceLocal(
|
||||
network = instance.network,
|
||||
uri = new URI("http://localhost:18333"),
|
||||
rpcUri = auth.bitcoindRpcUri,
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package org.bitcoins.testkit.fixtures
|
||||
|
||||
import com.typesafe.config.{Config, ConfigFactory}
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
import org.bitcoins.rpc.config.{
|
||||
BitcoindInstance,
|
||||
BitcoindInstanceLocal,
|
||||
BitcoindInstanceRemote
|
||||
}
|
||||
import org.bitcoins.rpc.util.RpcUtil
|
||||
import org.bitcoins.server.BitcoinSAppConfig
|
||||
import org.bitcoins.testkit.rpc.CachedBitcoindNewest
|
||||
|
@ -35,7 +39,11 @@ trait BitcoinSAppConfigBitcoinFixtureNotStarted
|
|||
val builder: () => Future[BitcoinSAppConfig] = () => {
|
||||
for {
|
||||
bitcoind <- cachedBitcoindWithFundsF
|
||||
datadir = bitcoind.instance.datadir
|
||||
datadir = bitcoind.instance match {
|
||||
case local: BitcoindInstanceLocal => local.datadir
|
||||
case _: BitcoindInstanceRemote =>
|
||||
sys.error("Remote instance should not be used in tests")
|
||||
}
|
||||
conf = buildConfig(bitcoind.instance)
|
||||
bitcoinSAppConfig = BitcoinSAppConfig(datadir.toPath, conf)
|
||||
} yield bitcoinSAppConfig
|
||||
|
@ -56,6 +64,11 @@ trait BitcoinSAppConfigBitcoinFixtureNotStarted
|
|||
* and sets tor config
|
||||
*/
|
||||
private def buildConfig(instance: BitcoindInstance): Config = {
|
||||
val version = instance match {
|
||||
case local: BitcoindInstanceLocal => local.getVersion
|
||||
case _: BitcoindInstanceRemote =>
|
||||
sys.error("Remote instance should not be used in tests")
|
||||
}
|
||||
val configStr =
|
||||
s"""
|
||||
|bitcoin-s.bitcoind-rpc.rpcuser="${instance.authCredentials.username}"
|
||||
|
@ -63,7 +76,7 @@ trait BitcoinSAppConfigBitcoinFixtureNotStarted
|
|||
|bitcoin-s.bitcoind-rpc.rpcbind="${instance.rpcUri.getHost}"
|
||||
|bitcoin-s.bitcoind-rpc.rpcport="${instance.rpcUri.getPort}"
|
||||
|bitcoin-s.bitcoind-rpc.isRemote=true
|
||||
|bitcoin-s.bitcoind-rpc.version="${instance.getVersion}"
|
||||
|bitcoin-s.bitcoind-rpc.version="${version}"
|
||||
|bitcoin-s.node.mode=bitcoind
|
||||
|bitcoin-s.tor.enabled=${TorUtil.torEnabled}
|
||||
|bitcoin-s.proxy.enabled=${TorUtil.torEnabled}
|
||||
|
|
|
@ -8,11 +8,12 @@ import org.bitcoins.core.protocol.ln.node.NodeId
|
|||
import org.bitcoins.core.protocol.transaction.TransactionOutPoint
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
||||
import org.bitcoins.lnd.rpc.LndRpcClient
|
||||
import org.bitcoins.lnd.rpc.config.LndInstance
|
||||
import org.bitcoins.lnd.rpc.config.LndInstanceLocal
|
||||
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
|
||||
import org.bitcoins.rpc.config.{
|
||||
BitcoindAuthCredentials,
|
||||
BitcoindInstance,
|
||||
BitcoindInstanceLocal,
|
||||
ZmqConfig
|
||||
}
|
||||
import org.bitcoins.rpc.util.RpcUtil
|
||||
|
@ -39,10 +40,11 @@ trait LndRpcTestUtil extends Logging {
|
|||
|
||||
/** Makes a best effort to get a 0.21 bitcoind instance
|
||||
*/
|
||||
def startedBitcoindRpcClient(instance: BitcoindInstance = bitcoindInstance())(
|
||||
implicit actorSystem: ActorSystem): Future[BitcoindRpcClient] = {
|
||||
def startedBitcoindRpcClient(
|
||||
instanceOpt: Option[BitcoindInstanceLocal] = None)(implicit
|
||||
actorSystem: ActorSystem): Future[BitcoindRpcClient] = {
|
||||
//need to do something with the Vector.newBuilder presumably?
|
||||
BitcoindRpcTestUtil.startedBitcoindRpcClient(instance, Vector.newBuilder)
|
||||
BitcoindRpcTestUtil.startedBitcoindRpcClient(instanceOpt, Vector.newBuilder)
|
||||
}
|
||||
|
||||
/** Creates a bitcoind instance with the given parameters */
|
||||
|
@ -50,7 +52,8 @@ trait LndRpcTestUtil extends Logging {
|
|||
port: Int = RpcUtil.randomPort,
|
||||
rpcPort: Int = RpcUtil.randomPort,
|
||||
zmqConfig: ZmqConfig = RpcUtil.zmqConfig,
|
||||
bitcoindV: BitcoindVersion = BitcoindVersion.V21): BitcoindInstance = {
|
||||
bitcoindV: BitcoindVersion = BitcoindVersion.V21)(implicit
|
||||
system: ActorSystem): BitcoindInstanceLocal = {
|
||||
BitcoindRpcTestUtil.getInstance(bitcoindVersion = bitcoindV,
|
||||
port = port,
|
||||
rpcPort = rpcPort,
|
||||
|
@ -107,13 +110,15 @@ trait LndRpcTestUtil extends Logging {
|
|||
}
|
||||
}
|
||||
|
||||
def lndInstance(bitcoindRpc: BitcoindRpcClient): LndInstance = {
|
||||
def lndInstance(bitcoindRpc: BitcoindRpcClient)(implicit
|
||||
system: ActorSystem): LndInstanceLocal = {
|
||||
val datadir = lndDataDir(bitcoindRpc, isCannonical = false)
|
||||
lndInstance(datadir)
|
||||
}
|
||||
|
||||
def lndInstance(datadir: File): LndInstance = {
|
||||
LndInstance.fromDataDir(datadir)
|
||||
def lndInstance(datadir: File)(implicit
|
||||
system: ActorSystem): LndInstanceLocal = {
|
||||
LndInstanceLocal.fromDataDir(datadir)
|
||||
}
|
||||
|
||||
/** Returns a `Future` that is completed when both lnd and bitcoind have the same block height
|
||||
|
|
|
@ -210,8 +210,8 @@ trait BitcoindRpcTestUtil extends Logging {
|
|||
zmqConfig: ZmqConfig = RpcUtil.zmqConfig,
|
||||
pruneMode: Boolean = false,
|
||||
versionOpt: Option[BitcoindVersion] = None,
|
||||
binaryDirectory: Path =
|
||||
BitcoindRpcTestClient.sbtBinaryDirectory): BitcoindInstance = {
|
||||
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory)(implicit
|
||||
system: ActorSystem): BitcoindInstanceLocal = {
|
||||
val uri = new URI("http://localhost:" + port)
|
||||
val rpcUri = new URI("http://localhost:" + rpcPort)
|
||||
val hasNeutrinoSupport = versionOpt match {
|
||||
|
@ -241,7 +241,7 @@ trait BitcoindRpcTestUtil extends Logging {
|
|||
|
||||
}
|
||||
|
||||
BitcoindInstance.fromConfig(conf, binary)
|
||||
BitcoindInstanceLocal.fromConfig(conf, binary)
|
||||
}
|
||||
|
||||
def v16Instance(
|
||||
|
@ -250,7 +250,7 @@ trait BitcoindRpcTestUtil extends Logging {
|
|||
zmqConfig: ZmqConfig = RpcUtil.zmqConfig,
|
||||
pruneMode: Boolean = false,
|
||||
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
|
||||
): BitcoindInstance =
|
||||
)(implicit system: ActorSystem): BitcoindInstanceLocal =
|
||||
instance(port = port,
|
||||
rpcPort = rpcPort,
|
||||
zmqConfig = zmqConfig,
|
||||
|
@ -264,7 +264,7 @@ trait BitcoindRpcTestUtil extends Logging {
|
|||
zmqConfig: ZmqConfig = RpcUtil.zmqConfig,
|
||||
pruneMode: Boolean = false,
|
||||
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
|
||||
): BitcoindInstance =
|
||||
)(implicit system: ActorSystem): BitcoindInstanceLocal =
|
||||
instance(port = port,
|
||||
rpcPort = rpcPort,
|
||||
zmqConfig = zmqConfig,
|
||||
|
@ -278,7 +278,7 @@ trait BitcoindRpcTestUtil extends Logging {
|
|||
zmqConfig: ZmqConfig = RpcUtil.zmqConfig,
|
||||
pruneMode: Boolean = false,
|
||||
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
|
||||
): BitcoindInstance =
|
||||
)(implicit system: ActorSystem): BitcoindInstanceLocal =
|
||||
instance(port = port,
|
||||
rpcPort = rpcPort,
|
||||
zmqConfig = zmqConfig,
|
||||
|
@ -292,7 +292,7 @@ trait BitcoindRpcTestUtil extends Logging {
|
|||
zmqConfig: ZmqConfig = RpcUtil.zmqConfig,
|
||||
pruneMode: Boolean = false,
|
||||
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
|
||||
): BitcoindInstance =
|
||||
)(implicit system: ActorSystem): BitcoindInstanceLocal =
|
||||
instance(port = port,
|
||||
rpcPort = rpcPort,
|
||||
zmqConfig = zmqConfig,
|
||||
|
@ -306,7 +306,7 @@ trait BitcoindRpcTestUtil extends Logging {
|
|||
zmqConfig: ZmqConfig = RpcUtil.zmqConfig,
|
||||
pruneMode: Boolean = false,
|
||||
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
|
||||
): BitcoindInstance =
|
||||
)(implicit system: ActorSystem): BitcoindInstanceLocal =
|
||||
instance(port = port,
|
||||
rpcPort = rpcPort,
|
||||
zmqConfig = zmqConfig,
|
||||
|
@ -320,7 +320,7 @@ trait BitcoindRpcTestUtil extends Logging {
|
|||
zmqConfig: ZmqConfig = RpcUtil.zmqConfig,
|
||||
pruneMode: Boolean = false,
|
||||
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
|
||||
): BitcoindInstance =
|
||||
)(implicit system: ActorSystem): BitcoindInstanceLocal =
|
||||
instance(port = port,
|
||||
rpcPort = rpcPort,
|
||||
zmqConfig = zmqConfig,
|
||||
|
@ -334,7 +334,7 @@ trait BitcoindRpcTestUtil extends Logging {
|
|||
zmqConfig: ZmqConfig = RpcUtil.zmqConfig,
|
||||
pruneMode: Boolean = false,
|
||||
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
|
||||
): BitcoindInstance =
|
||||
)(implicit system: ActorSystem): BitcoindInstanceLocal =
|
||||
instance(port = port,
|
||||
rpcPort = rpcPort,
|
||||
zmqConfig = zmqConfig,
|
||||
|
@ -349,8 +349,8 @@ trait BitcoindRpcTestUtil extends Logging {
|
|||
rpcPort: Int = RpcUtil.randomPort,
|
||||
zmqConfig: ZmqConfig = RpcUtil.zmqConfig,
|
||||
pruneMode: Boolean = false,
|
||||
binaryDirectory: Path =
|
||||
BitcoindRpcTestClient.sbtBinaryDirectory): BitcoindInstance = {
|
||||
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory)(implicit
|
||||
system: ActorSystem): BitcoindInstanceLocal = {
|
||||
bitcoindVersion match {
|
||||
case BitcoindVersion.V16 =>
|
||||
BitcoindRpcTestUtil.v16Instance(port,
|
||||
|
@ -624,15 +624,21 @@ trait BitcoindRpcTestUtil extends Logging {
|
|||
maxTries: Int = 50)(implicit system: ActorSystem): Future[Unit] = {
|
||||
implicit val ec = system.dispatcher
|
||||
AsyncUtil
|
||||
.retryUntilSatisfiedF(conditionF = { () =>
|
||||
Future {
|
||||
val dir = client.getDaemon.datadir
|
||||
FileUtil.deleteTmpDir(dir)
|
||||
!dir.exists()
|
||||
}
|
||||
},
|
||||
interval = interval,
|
||||
maxTries = maxTries)
|
||||
.retryUntilSatisfiedF(
|
||||
conditionF = { () =>
|
||||
Future {
|
||||
val dir = client.getDaemon match {
|
||||
case _: BitcoindInstanceRemote =>
|
||||
sys.error(s"Cannot have remote bitcoind instance in testkit")
|
||||
case local: BitcoindInstanceLocal => local.datadir
|
||||
}
|
||||
FileUtil.deleteTmpDir(dir)
|
||||
!dir.exists()
|
||||
}
|
||||
},
|
||||
interval = interval,
|
||||
maxTries = maxTries
|
||||
)
|
||||
}
|
||||
|
||||
/** Returns a pair of unconnected
|
||||
|
@ -982,13 +988,13 @@ trait BitcoindRpcTestUtil extends Logging {
|
|||
case v16: BitcoindV16RpcClient =>
|
||||
v16.getAddressInfo(address).map(_.pubkey)
|
||||
case other: BitcoindRpcClient =>
|
||||
if (
|
||||
other.instance.getVersion.toString >= BitcoindVersion.V17.toString
|
||||
) {
|
||||
val v17 = new BitcoindV17RpcClient(other.instance)
|
||||
v17.getAddressInfo(address).map(_.pubkey)
|
||||
} else {
|
||||
other.getAddressInfo(address).map(_.pubkey)
|
||||
other.version.flatMap { v =>
|
||||
if (v.toString >= BitcoindVersion.V17.toString) {
|
||||
val v17 = new BitcoindV17RpcClient(other.instance)
|
||||
v17.getAddressInfo(address).map(_.pubkey)
|
||||
} else {
|
||||
other.getAddressInfo(address).map(_.pubkey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1121,10 +1127,13 @@ trait BitcoindRpcTestUtil extends Logging {
|
|||
* this vectorbuilder.
|
||||
*/
|
||||
def startedBitcoindRpcClient(
|
||||
instance: BitcoindInstance = BitcoindRpcTestUtil.instance(),
|
||||
instanceOpt: Option[BitcoindInstanceLocal] = None,
|
||||
clientAccum: RpcClientAccum)(implicit
|
||||
system: ActorSystem): Future[BitcoindRpcClient] = {
|
||||
implicit val ec: ExecutionContextExecutor = system.dispatcher
|
||||
|
||||
val instance = instanceOpt.getOrElse(BitcoindRpcTestUtil.instance())
|
||||
|
||||
require(
|
||||
instance.datadir.getPath.startsWith(Properties.tmpDir),
|
||||
s"${instance.datadir} is not in user temp dir! This could lead to bad things happening.")
|
||||
|
|
|
@ -2,7 +2,7 @@ package org.bitcoins.testkit.util
|
|||
|
||||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
import org.bitcoins.rpc.config.BitcoindInstanceLocal
|
||||
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
|
@ -17,7 +17,7 @@ case class BitcoindRpcTestClient(
|
|||
s"Path did not exist! got=${binary.toAbsolutePath.toString}")
|
||||
import system.dispatcher
|
||||
|
||||
private lazy val bitcoindInstance: BitcoindInstance = {
|
||||
private lazy val bitcoindInstance: BitcoindInstanceLocal = {
|
||||
BitcoindRpcTestUtil.getInstance(bitcoindVersion = version,
|
||||
binaryDirectory = binaryDirectory)
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ case class BitcoindRpcTestClient(
|
|||
case Some(client) => Future.successful(client)
|
||||
case None =>
|
||||
val clientF =
|
||||
BitcoindRpcTestUtil.startedBitcoindRpcClient(bitcoindInstance,
|
||||
BitcoindRpcTestUtil.startedBitcoindRpcClient(Some(bitcoindInstance),
|
||||
clientAccum =
|
||||
Vector.newBuilder)
|
||||
clientF.map { c =>
|
||||
|
|
Loading…
Add table
Reference in a new issue