Navigation Menu

Skip to content

Commit

Permalink
Create initial BitcoindV20RpcClient (#2060)
Browse files Browse the repository at this point in the history
  • Loading branch information
benthecarman committed Sep 25, 2020
1 parent 856f88f commit 0b10038
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 34 deletions.
Expand Up @@ -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,
Expand Down
Expand Up @@ -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] =
Expand Down
27 changes: 17 additions & 10 deletions bitcoind-rpc/bitcoind-rpc.sbt
@@ -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
Expand All @@ -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(",")}")
Expand All @@ -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
Expand Down
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 =>
Expand All @@ -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"
Expand All @@ -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"
}
Expand Down
Expand Up @@ -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")
}
}
Expand Down
Expand Up @@ -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)))
Expand Down Expand Up @@ -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)))
Expand All @@ -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 =>
Expand Down Expand Up @@ -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)))
Expand Down
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down
Expand Up @@ -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)
Expand Down
Expand Up @@ -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),
Expand All @@ -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)))
}
Expand Down
@@ -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)
}

}

0 comments on commit 0b10038

Please sign in to comment.