mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-22 06:31:55 +01:00
Create initial BitcoindV20RpcClient (#2060)
This commit is contained in:
parent
11e6b7d9eb
commit
e53317b409
11 changed files with 239 additions and 34 deletions
|
@ -99,12 +99,24 @@ case class PeerNetworkInfo(
|
|||
pingwait: Option[BigDecimal])
|
||||
extends NetworkResult
|
||||
|
||||
case class NodeBan(
|
||||
trait NodeBan extends NetworkResult {
|
||||
def address: URI
|
||||
def banned_until: UInt32
|
||||
def ban_created: UInt32
|
||||
}
|
||||
|
||||
case class NodeBanPreV20(
|
||||
address: URI,
|
||||
banned_until: UInt32,
|
||||
ban_created: UInt32,
|
||||
ban_reason: String)
|
||||
extends NetworkResult
|
||||
extends NodeBan
|
||||
|
||||
case class NodeBanPostV20(
|
||||
address: URI,
|
||||
banned_until: UInt32,
|
||||
ban_created: UInt32)
|
||||
extends NodeBan
|
||||
|
||||
final case class GetNodeAddressesResult(
|
||||
time: FiniteDuration,
|
||||
|
|
|
@ -204,7 +204,11 @@ object JsonSerializers {
|
|||
(__ \ "bytesrecv_per_msg").read[Map[String, Int]] and
|
||||
(__ \ "minfeefilter").readNullable[SatoshisPerKiloByte])(Peer)
|
||||
|
||||
implicit val nodeBanReads: Reads[NodeBan] = Json.reads[NodeBan]
|
||||
implicit val nodeBanPostV20Reads: Reads[NodeBanPostV20] =
|
||||
Json.reads[NodeBanPostV20]
|
||||
|
||||
implicit val nodeBanPreV20Reads: Reads[NodeBanPreV20] =
|
||||
Json.reads[NodeBanPreV20]
|
||||
|
||||
// Blockchain Models
|
||||
implicit val getBlockResultReads: Reads[GetBlockResult] =
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import scala.util.Properties
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.concurrent.{Future, Await}
|
||||
import scala.concurrent.{Await, Future}
|
||||
import scala.concurrent.duration.DurationInt
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
|
@ -19,16 +19,21 @@ TaskKeys.downloadBitcoind := {
|
|||
|
||||
val binaryDir = CommonSettings.binariesPath.resolve("bitcoind")
|
||||
|
||||
|
||||
if (Files.notExists(binaryDir)) {
|
||||
logger.info(s"Creating directory for bitcoind binaries: $binaryDir")
|
||||
Files.createDirectories(binaryDir)
|
||||
}
|
||||
|
||||
val experimentalVersion = "0.18.99" // TODO: change this when new version compiled on suredbits server
|
||||
val experimentalVersion =
|
||||
"0.18.99" // TODO: change this when new version compiled on suredbits server
|
||||
|
||||
val versions =
|
||||
List("0.19.0.1", "0.18.1", "0.17.0.1", "0.16.3", experimentalVersion)
|
||||
List("0.20.1",
|
||||
"0.19.0.1",
|
||||
"0.18.1",
|
||||
"0.17.0.1",
|
||||
"0.16.3",
|
||||
experimentalVersion)
|
||||
|
||||
logger.debug(
|
||||
s"(Maybe) downloading Bitcoin Core binaries for versions: ${versions.mkString(",")}")
|
||||
|
@ -51,12 +56,14 @@ TaskKeys.downloadBitcoind := {
|
|||
|
||||
val expectedEndLocation = binaryDir resolve s"bitcoin-$version"
|
||||
|
||||
if (Files
|
||||
.list(binaryDir)
|
||||
.iterator
|
||||
.asScala
|
||||
.map(_.toString)
|
||||
.exists(expectedEndLocation.toString.startsWith(_))) {
|
||||
if (
|
||||
Files
|
||||
.list(binaryDir)
|
||||
.iterator
|
||||
.asScala
|
||||
.map(_.toString)
|
||||
.exists(expectedEndLocation.toString.startsWith(_))
|
||||
) {
|
||||
logger.debug(
|
||||
s"Directory $expectedEndLocation already exists, skipping download of version $version")
|
||||
Future.unit
|
||||
|
|
|
@ -3,9 +3,9 @@ package org.bitcoins.rpc.client.common
|
|||
import java.io.File
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.core.api.node.NodeApi
|
||||
import org.bitcoins.core.api.chain.ChainQueryApi
|
||||
import org.bitcoins.core.api.feeprovider.FeeRateApi
|
||||
import org.bitcoins.core.api.node.NodeApi
|
||||
import org.bitcoins.core.protocol.BlockStamp
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.util.FutureUtil
|
||||
|
@ -15,6 +15,7 @@ import org.bitcoins.rpc.client.v16.BitcoindV16RpcClient
|
|||
import org.bitcoins.rpc.client.v17.BitcoindV17RpcClient
|
||||
import org.bitcoins.rpc.client.v18.BitcoindV18RpcClient
|
||||
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
||||
import org.bitcoins.rpc.client.v20.BitcoindV20RpcClient
|
||||
import org.bitcoins.rpc.config.{BitcoindConfig, BitcoindInstance}
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
@ -164,6 +165,7 @@ object BitcoindRpcClient {
|
|||
case BitcoindVersion.V17 => BitcoindV17RpcClient.withActorSystem(instance)
|
||||
case BitcoindVersion.V18 => BitcoindV18RpcClient.withActorSystem(instance)
|
||||
case BitcoindVersion.V19 => BitcoindV19RpcClient.withActorSystem(instance)
|
||||
case BitcoindVersion.V20 => BitcoindV20RpcClient.withActorSystem(instance)
|
||||
case BitcoindVersion.Experimental =>
|
||||
BitcoindV18RpcClient.withActorSystem(instance)
|
||||
case BitcoindVersion.Unknown =>
|
||||
|
@ -180,7 +182,7 @@ sealed trait BitcoindVersion
|
|||
object BitcoindVersion {
|
||||
|
||||
/** The newest version of `bitcoind` we support */
|
||||
val newest = V19
|
||||
val newest: BitcoindVersion = V20
|
||||
|
||||
case object V16 extends BitcoindVersion {
|
||||
override def toString: String = "v0.16"
|
||||
|
@ -198,6 +200,10 @@ object BitcoindVersion {
|
|||
override def toString: String = "v0.19"
|
||||
}
|
||||
|
||||
case object V20 extends BitcoindVersion {
|
||||
override def toString: String = "v0.20"
|
||||
}
|
||||
|
||||
case object Experimental extends BitcoindVersion {
|
||||
override def toString: String = "v0.18.99"
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ trait BlockchainRpc { self: Client =>
|
|||
self.version match {
|
||||
case BitcoindVersion.V16 | BitcoindVersion.V17 | BitcoindVersion.V18 =>
|
||||
bitcoindCall[GetBlockChainInfoResultPreV19]("getblockchaininfo")
|
||||
case BitcoindVersion.V19 | BitcoindVersion.Experimental |
|
||||
BitcoindVersion.Unknown =>
|
||||
case BitcoindVersion.V20 | BitcoindVersion.V19 |
|
||||
BitcoindVersion.Experimental | BitcoindVersion.Unknown =>
|
||||
bitcoindCall[GetBlockChainInfoResultPostV19]("getblockchaininfo")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ trait MempoolRpc { self: Client =>
|
|||
Map[DoubleSha256DigestBE, GetMemPoolResult]] = {
|
||||
|
||||
self.version match {
|
||||
case V19 | Experimental | Unknown =>
|
||||
case V20 | V19 | Experimental | Unknown =>
|
||||
bitcoindCall[Map[DoubleSha256DigestBE, GetMemPoolResultPostV19]](
|
||||
"getmempoolancestors",
|
||||
List(JsString(txid.hex), JsBoolean(true)))
|
||||
|
@ -64,7 +64,7 @@ trait MempoolRpc { self: Client =>
|
|||
def getMemPoolDescendantsVerbose(txid: DoubleSha256DigestBE): Future[
|
||||
Map[DoubleSha256DigestBE, GetMemPoolResult]] = {
|
||||
self.version match {
|
||||
case V19 | Experimental | Unknown =>
|
||||
case V20 | V19 | Experimental | Unknown =>
|
||||
bitcoindCall[Map[DoubleSha256DigestBE, GetMemPoolResultPostV19]](
|
||||
"getmempooldescendants",
|
||||
List(JsString(txid.hex), JsBoolean(true)))
|
||||
|
@ -84,7 +84,7 @@ trait MempoolRpc { self: Client =>
|
|||
txid: DoubleSha256DigestBE): Future[GetMemPoolEntryResult] = {
|
||||
|
||||
self.version match {
|
||||
case V19 | Experimental | Unknown =>
|
||||
case V20 | V19 | Experimental | Unknown =>
|
||||
bitcoindCall[GetMemPoolEntryResultPostV19]("getmempoolentry",
|
||||
List(JsString(txid.hex)))
|
||||
case V16 | V17 | V18 =>
|
||||
|
@ -127,7 +127,7 @@ trait MempoolRpc { self: Client =>
|
|||
Map[DoubleSha256DigestBE, GetMemPoolResult]] = {
|
||||
|
||||
self.version match {
|
||||
case V19 | Experimental | Unknown =>
|
||||
case V20 | V19 | Experimental | Unknown =>
|
||||
bitcoindCall[Map[DoubleSha256DigestBE, GetMemPoolResultPostV19]](
|
||||
"getrawmempool",
|
||||
List(JsBoolean(true)))
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.{
|
|||
import org.bitcoins.commons.jsonmodels.bitcoind._
|
||||
import org.bitcoins.commons.serializers.JsonSerializers._
|
||||
import org.bitcoins.core.protocol.blockchain.Block
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion._
|
||||
import play.api.libs.json.{JsBoolean, JsNumber, JsString}
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
@ -65,7 +66,13 @@ trait P2PRpc { self: Client =>
|
|||
}
|
||||
|
||||
def listBanned: Future[Vector[NodeBan]] = {
|
||||
bitcoindCall[Vector[NodeBan]]("listbanned")
|
||||
self.version match {
|
||||
case V20 | Unknown =>
|
||||
bitcoindCall[Vector[NodeBanPostV20]]("listbanned")
|
||||
case V16 | V17 | V18 | V19 | Experimental =>
|
||||
bitcoindCall[Vector[NodeBanPreV20]]("listbanned")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def setBan(
|
||||
|
|
|
@ -98,7 +98,7 @@ trait RawTransactionRpc { self: Client =>
|
|||
maxfeerate: Double = 0.10): Future[DoubleSha256DigestBE] = {
|
||||
|
||||
val feeParameter = self.version match {
|
||||
case V19 | Experimental | Unknown =>
|
||||
case V20 | V19 | Experimental | Unknown =>
|
||||
JsNumber(maxfeerate)
|
||||
case V16 | V17 | V18 =>
|
||||
JsBoolean(maxfeerate == 0)
|
||||
|
|
|
@ -208,7 +208,7 @@ trait WalletRpc { self: Client =>
|
|||
blank: Boolean = false,
|
||||
passphrase: String = ""): Future[CreateWalletResult] =
|
||||
self.version match {
|
||||
case V19 | Experimental | Unknown =>
|
||||
case V20 | V19 | Experimental | Unknown =>
|
||||
bitcoindCall[CreateWalletResult]("createwallet",
|
||||
List(JsString(walletName),
|
||||
JsBoolean(disablePrivateKeys),
|
||||
|
@ -227,7 +227,7 @@ trait WalletRpc { self: Client =>
|
|||
case V16 | V17 =>
|
||||
bitcoindCall[AddressInfoResultPreV18]("getaddressinfo",
|
||||
List(JsString(address.value)))
|
||||
case V18 | V19 | Experimental | Unknown =>
|
||||
case V18 | V19 | V20 | Experimental | Unknown =>
|
||||
bitcoindCall[AddressInfoResultPostV18]("getaddressinfo",
|
||||
List(JsString(address.value)))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
package org.bitcoins.rpc.client.v20
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.WalletFlag
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind.{
|
||||
GetBalancesResult,
|
||||
RpcOpts,
|
||||
SetWalletFlagResult,
|
||||
SignRawTransactionResult
|
||||
}
|
||||
import org.bitcoins.commons.serializers.JsonSerializers._
|
||||
import org.bitcoins.commons.serializers.JsonWriters._
|
||||
import org.bitcoins.core.api.chain.ChainQueryApi
|
||||
import org.bitcoins.core.api.chain.ChainQueryApi.FilterResponse
|
||||
import org.bitcoins.core.gcs.FilterType
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.script.crypto.HashType
|
||||
import org.bitcoins.core.util.FutureUtil
|
||||
import org.bitcoins.crypto.ECPrivateKey
|
||||
import org.bitcoins.rpc.client.common.{
|
||||
BitcoindRpcClient,
|
||||
BitcoindVersion,
|
||||
DescriptorRpc,
|
||||
PsbtRpc
|
||||
}
|
||||
import org.bitcoins.rpc.client.v19.V19BlockFilterRpc
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
import play.api.libs.json._
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.util.Try
|
||||
|
||||
/**
|
||||
* Class for creating a BitcoindV19 instance that can access RPCs
|
||||
*/
|
||||
class BitcoindV20RpcClient(override val instance: BitcoindInstance)(implicit
|
||||
actorSystem: ActorSystem)
|
||||
extends BitcoindRpcClient(instance)
|
||||
with DescriptorRpc
|
||||
with PsbtRpc
|
||||
with V19BlockFilterRpc {
|
||||
|
||||
override def getFiltersBetweenHeights(
|
||||
startHeight: Int,
|
||||
endHeight: Int): Future[Vector[ChainQueryApi.FilterResponse]] = {
|
||||
val allHeights = startHeight.to(endHeight)
|
||||
|
||||
def f(range: Vector[Int]): Future[Vector[FilterResponse]] = {
|
||||
val filterFs = range.map { height =>
|
||||
for {
|
||||
hash <- getBlockHash(height)
|
||||
filter <- getBlockFilter(hash, FilterType.Basic)
|
||||
} yield {
|
||||
FilterResponse(filter.filter, hash, height)
|
||||
}
|
||||
}
|
||||
Future.sequence(filterFs)
|
||||
}
|
||||
|
||||
FutureUtil.batchExecute(elements = allHeights.toVector,
|
||||
f = f,
|
||||
init = Vector.empty,
|
||||
batchSize = 25)
|
||||
}
|
||||
|
||||
override def getFilterCount: Future[Int] = getBlockCount
|
||||
|
||||
override lazy val version: BitcoindVersion = BitcoindVersion.V20
|
||||
|
||||
/**
|
||||
* $signRawTx
|
||||
*
|
||||
* This RPC call signs the raw transaction with keys found in
|
||||
* the Bitcoin Core wallet.
|
||||
*/
|
||||
def signRawTransactionWithWallet(
|
||||
transaction: Transaction,
|
||||
utxoDeps: Vector[RpcOpts.SignRawTransactionOutputParameter] =
|
||||
Vector.empty,
|
||||
sigHash: HashType = HashType.sigHashAll
|
||||
): Future[SignRawTransactionResult] =
|
||||
bitcoindCall[SignRawTransactionResult]("signrawtransactionwithwallet",
|
||||
List(JsString(transaction.hex),
|
||||
Json.toJson(utxoDeps),
|
||||
Json.toJson(sigHash)))
|
||||
|
||||
/**
|
||||
* $signRawTx
|
||||
*
|
||||
* This RPC call signs the raw transaction with keys provided
|
||||
* manually.
|
||||
*/
|
||||
def signRawTransactionWithKey(
|
||||
transaction: Transaction,
|
||||
keys: Vector[ECPrivateKey],
|
||||
utxoDeps: Vector[RpcOpts.SignRawTransactionOutputParameter] =
|
||||
Vector.empty,
|
||||
sigHash: HashType = HashType.sigHashAll
|
||||
): Future[SignRawTransactionResult] =
|
||||
bitcoindCall[SignRawTransactionResult]("signrawtransactionwithkey",
|
||||
List(JsString(transaction.hex),
|
||||
Json.toJson(keys),
|
||||
Json.toJson(utxoDeps),
|
||||
Json.toJson(sigHash)))
|
||||
|
||||
/**
|
||||
* Change the state of the given wallet flag for a wallet.
|
||||
*/
|
||||
def setWalletFlag(
|
||||
flag: WalletFlag,
|
||||
value: Boolean
|
||||
): Future[SetWalletFlagResult] =
|
||||
bitcoindCall[SetWalletFlagResult](
|
||||
"setwalletflag",
|
||||
List(JsString(flag.toString), Json.toJson(value)))
|
||||
|
||||
def getBalances: Future[GetBalancesResult] = {
|
||||
bitcoindCall[GetBalancesResult]("getbalances")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object BitcoindV20RpcClient {
|
||||
|
||||
/**
|
||||
* Creates an RPC client from the given instance.
|
||||
*
|
||||
* Behind the scenes, we create an actor system for
|
||||
* you. You can use `withActorSystem` if you want to
|
||||
* manually specify an actor system for the RPC client.
|
||||
*/
|
||||
def apply(instance: BitcoindInstance): BitcoindV20RpcClient = {
|
||||
implicit val system =
|
||||
ActorSystem.create(BitcoindRpcClient.ActorSystemName)
|
||||
withActorSystem(instance)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an RPC client from the given instance,
|
||||
* together with the given actor system. This is for
|
||||
* advanced users, where you need fine grained control
|
||||
* over the RPC client.
|
||||
*/
|
||||
def withActorSystem(instance: BitcoindInstance)(implicit
|
||||
system: ActorSystem): BitcoindV20RpcClient =
|
||||
new BitcoindV20RpcClient(instance)(system)
|
||||
|
||||
def fromUnknownVersion(
|
||||
rpcClient: BitcoindRpcClient): Try[BitcoindV20RpcClient] =
|
||||
Try {
|
||||
new BitcoindV20RpcClient(rpcClient.instance)(rpcClient.system)
|
||||
}
|
||||
|
||||
}
|
|
@ -106,6 +106,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
|||
|debug=1
|
||||
|walletbroadcast=1
|
||||
|peerbloomfilters=1
|
||||
|fallbackfee=0.0002
|
||||
|txindex=${if (pruneMode) 0
|
||||
else 1 /* pruning and txindex are not compatible */}
|
||||
|zmqpubhashtx=tcp://127.0.0.1:$zmqPort
|
||||
|
@ -157,7 +158,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
|||
version match {
|
||||
// default to newest version
|
||||
case Unknown => getBinary(BitcoindVersion.newest)
|
||||
case known @ (Experimental | V16 | V17 | V18 | V19) =>
|
||||
case known @ (Experimental | V16 | V17 | V18 | V19 | V20) =>
|
||||
val fileList = Files
|
||||
.list(binaryDirectory)
|
||||
.iterator()
|
||||
|
@ -201,9 +202,12 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
|||
versionOpt: Option[BitcoindVersion] = None): BitcoindInstance = {
|
||||
val uri = new URI("http://localhost:" + port)
|
||||
val rpcUri = new URI("http://localhost:" + rpcPort)
|
||||
val hasNeutrinoSupport =
|
||||
versionOpt.contains(BitcoindVersion.V19) || versionOpt
|
||||
.contains(BitcoindVersion.Experimental)
|
||||
val hasNeutrinoSupport = versionOpt match {
|
||||
case Some(V16) | Some(V17) | Some(V18) =>
|
||||
false
|
||||
case Some(V19) | Some(V20) | Some(Experimental) | Some(Unknown) | None =>
|
||||
true
|
||||
}
|
||||
val configFile =
|
||||
writtenConfig(uri,
|
||||
rpcUri,
|
||||
|
@ -215,11 +219,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
|||
val binary: File = versionOpt match {
|
||||
case Some(version) => getBinary(version)
|
||||
case None =>
|
||||
if (
|
||||
Files.exists(
|
||||
BitcoindRpcTestUtil.binaryDirectory
|
||||
)
|
||||
) {
|
||||
if (Files.exists(BitcoindRpcTestUtil.binaryDirectory)) {
|
||||
newestBitcoindBinary
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
|
@ -287,6 +287,18 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
|||
pruneMode = pruneMode,
|
||||
versionOpt = Some(BitcoindVersion.V19))
|
||||
|
||||
def v20Instance(
|
||||
port: Int = RpcUtil.randomPort,
|
||||
rpcPort: Int = RpcUtil.randomPort,
|
||||
zmqPort: Int = RpcUtil.randomPort,
|
||||
pruneMode: Boolean = false
|
||||
): BitcoindInstance =
|
||||
instance(port = port,
|
||||
rpcPort = rpcPort,
|
||||
zmqPort = zmqPort,
|
||||
pruneMode = pruneMode,
|
||||
versionOpt = Some(BitcoindVersion.V20))
|
||||
|
||||
def vExperimentalInstance(
|
||||
port: Int = RpcUtil.randomPort,
|
||||
rpcPort: Int = RpcUtil.randomPort,
|
||||
|
@ -593,6 +605,9 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
|||
case BitcoindVersion.V19 =>
|
||||
BitcoindV19RpcClient.withActorSystem(
|
||||
BitcoindRpcTestUtil.v19Instance())
|
||||
case BitcoindVersion.V20 =>
|
||||
BitcoindV19RpcClient.withActorSystem(
|
||||
BitcoindRpcTestUtil.v20Instance())
|
||||
case BitcoindVersion.Experimental =>
|
||||
BitcoindV19RpcClient.withActorSystem(
|
||||
BitcoindRpcTestUtil.vExperimentalInstance())
|
||||
|
|
Loading…
Add table
Reference in a new issue