Create initial BitcoindV20RpcClient (#2060)

This commit is contained in:
Ben Carman 2020-09-25 13:29:22 -05:00 committed by GitHub
parent 11e6b7d9eb
commit e53317b409
11 changed files with 239 additions and 34 deletions

View file

@ -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,

View file

@ -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] =

View file

@ -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

View file

@ -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"
}

View file

@ -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")
}
}

View file

@ -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)))

View file

@ -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(

View file

@ -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)

View file

@ -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)))
}

View file

@ -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)
}
}

View file

@ -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())