diff --git a/build.sbt b/build.sbt index b0c81d93df..77daf6686c 100644 --- a/build.sbt +++ b/build.sbt @@ -6,7 +6,8 @@ lazy val root = project core, coreGen, coreTest, - zmq + zmq, + rpc ) lazy val secp256k1jni = project @@ -42,4 +43,12 @@ lazy val zmq = project core ) +lazy val rpc = project + .in(file("rpc")) + .enablePlugins() + .dependsOn( + core, + coreGen % "test->test" + ) + publishArtifact in root := false diff --git a/core/build.sbt b/core/build.sbt index a17c44fc8c..f461c6290f 100644 --- a/core/build.sbt +++ b/core/build.sbt @@ -1,9 +1,10 @@ - name := "bitcoin-s-core" libraryDependencies ++= Deps.core -testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "2") +testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, + "-verbosity", + "2") //test in assembly := {} @@ -17,4 +18,5 @@ coverageMinimum := 90 coverageFailOnMinimum := true -assemblyOption in assembly := (assemblyOption in assembly).value.copy(includeScala = false) +assemblyOption in assembly := (assemblyOption in assembly).value + .copy(includeScala = false) diff --git a/project/Deps.scala b/project/Deps.scala index 17073e528b..18aa0de202 100644 --- a/project/Deps.scala +++ b/project/Deps.scala @@ -9,12 +9,18 @@ object Deps { val slf4j = "1.7.5" val spray = "1.3.2" val zeromq = "0.4.3" + val akkav = "10.1.1" + val akkaStreamv = "2.5.12" + val playv = "2.6.9" } object Compile { val bouncycastle = "org.bouncycastle" % "bcprov-jdk15on" % V.bouncyCastle val slf4j = "org.slf4j" % "slf4j-api" % V.slf4j % "provided" val zeromq = "org.zeromq" % "jeromq" % V.zeromq + val akkaHttp = "com.typesafe.akka" %% "akka-http" % V.akkav + val akkaStream = "com.typesafe.akka" %% "akka-stream" % V.akkaStreamv + val playJson = "com.typesafe.play" %% "play-json" % V.playv } object Test { @@ -24,6 +30,8 @@ object Deps { val scalacheck = "org.scalacheck" %% "scalacheck" % V.scalacheck % "test" withSources() withJavadoc() val scalaTest = "org.scalatest" %% "scalatest" % V.scalaTest % "test" val spray = "io.spray" %% "spray-json" % V.spray % "test" + val akkaHttp = "com.typesafe.akka" %% "akka-http-testkit" % V.akkav % "test" + val akkaStream = "com.typesafe.akka" %% "akka-stream-testkit" % V.akkaStreamv % "test" } val core = List( @@ -51,4 +59,16 @@ object Deps { Test.scalacheck, Test.scalaTest ) + + val rpc = List( + Compile.akkaHttp, + Compile.akkaStream, + Compile.playJson, + Compile.slf4j, + Test.akkaHttp, + Test.akkaStream, + Test.logback, + Test.scalaTest, + Test.scalacheck + ) } diff --git a/project/plugins.sbt b/project/plugins.sbt index 77dd3b4c84..7ccd440bc7 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -8,4 +8,5 @@ addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.3.5") addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.1.0") -addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.2") + +addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.2") \ No newline at end of file diff --git a/rpc/src/main/scala/org/bitcoins/rpc/config/AuthCredentials.scala b/rpc/src/main/scala/org/bitcoins/rpc/config/AuthCredentials.scala new file mode 100644 index 0000000000..99a9887662 --- /dev/null +++ b/rpc/src/main/scala/org/bitcoins/rpc/config/AuthCredentials.scala @@ -0,0 +1,38 @@ +package org.bitcoins.rpc.config + +import java.io.File + +/** + * Created by chris on 5/2/17. + */ +sealed trait AuthCredentials { + + /** The directory where our bitcoin.conf file is located */ + def datadir: File + + /** rpcusername field in our bitcoin.conf file */ + def username: String + + /** rpcpassword field in our bitcoin.conf file */ + def password: String +} + +object AuthCredentials { + private case class AuthCredentialsImpl( + username: String, + password: String, + datadir: File) + extends AuthCredentials + + def apply(username: String, password: String): AuthCredentials = { + val defaultDataDir = new File(System.getProperty("user.home") + "/.bitcoin") + AuthCredentials(username, password, defaultDataDir) + } + + def apply( + username: String, + password: String, + datadir: File): AuthCredentials = { + AuthCredentialsImpl(username, password, datadir) + } +} diff --git a/rpc/src/main/scala/org/bitcoins/rpc/config/DaemonInstance.scala b/rpc/src/main/scala/org/bitcoins/rpc/config/DaemonInstance.scala new file mode 100644 index 0000000000..56ec1b461f --- /dev/null +++ b/rpc/src/main/scala/org/bitcoins/rpc/config/DaemonInstance.scala @@ -0,0 +1,33 @@ +package org.bitcoins.rpc.config + +import java.net.URI + +import org.bitcoins.core.config.NetworkParameters + +/** + * Created by chris on 4/29/17. + */ +sealed trait DaemonInstance { + + def network: NetworkParameters + def uri: URI + def rpcUri: URI + def authCredentials: AuthCredentials +} + +object DaemonInstance { + private case class DaemonInstanceImpl( + network: NetworkParameters, + uri: URI, + rpcUri: URI, + authCredentials: AuthCredentials) + extends DaemonInstance + + def apply( + network: NetworkParameters, + uri: URI, + rpcUri: URI, + authCredentials: AuthCredentials): DaemonInstance = { + DaemonInstanceImpl(network, uri, rpcUri, authCredentials) + } +} diff --git a/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/BlockchainResult.scala b/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/BlockchainResult.scala new file mode 100644 index 0000000000..d6819882ed --- /dev/null +++ b/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/BlockchainResult.scala @@ -0,0 +1,182 @@ +package org.bitcoins.rpc.jsonmodels + +import org.bitcoins.core.crypto.DoubleSha256Digest +import org.bitcoins.core.currency.Bitcoins +import org.bitcoins.core.number.{Int32, UInt32} +import org.bitcoins.core.wallet.fee.BitcoinFeeUnit + +sealed abstract class BlockchainResult + +case class GetBlockResult( + hash: DoubleSha256Digest, + confirmations: Int, + strippedsize: Int, + size: Int, + weight: Int, + height: Int, + version: Int, + versionHex: Int32, + merkleroot: DoubleSha256Digest, + tx: Vector[DoubleSha256Digest], + time: UInt32, + mediantime: UInt32, + nonce: UInt32, + bits: UInt32, + difficulty: BigDecimal, + chainwork: String, + previousblockhash: Option[DoubleSha256Digest], + nextblockhash: Option[DoubleSha256Digest]) + extends BlockchainResult + +case class GetBlockWithTransactionsResult( + hash: DoubleSha256Digest, + confirmations: Int, + strippedsize: Int, + size: Int, + weight: Int, + height: Int, + version: Int, + versionHex: Int32, + merkleroot: DoubleSha256Digest, + tx: Vector[RpcTransaction], + time: UInt32, + mediantime: UInt32, + nonce: UInt32, + bits: UInt32, + difficulty: BigDecimal, + chainwork: String, + previousblockhash: Option[DoubleSha256Digest], + nextblockhash: Option[DoubleSha256Digest]) + extends BlockchainResult + +case class GetBlockChainInfoResult( + chain: String, + blocks: Int, + headers: Int, + bestblockhash: DoubleSha256Digest, + difficulty: BigDecimal, + mediantime: Int, + verificationprogress: BigDecimal, + initialblockdownload: Boolean, + chainwork: String, // How should this be handled? + size_on_disk: Int, + pruned: Boolean, + pruneheight: Option[Int], + softforks: Vector[Softfork], + bip9_softforks: Map[String, Bip9Softfork], + warnings: String) + extends BlockchainResult + +case class Softfork( + id: String, + version: Int, + enforce: Option[Map[String, SoftforkProgress]], + reject: SoftforkProgress) + extends BlockchainResult + +case class SoftforkProgress( + status: Option[Boolean], + found: Option[Int], + required: Option[Int], + window: Option[Int]) + extends BlockchainResult + +case class Bip9Softfork( + status: String, + bit: Option[Int], + startTime: Int, + timeout: BigInt, + since: Int) + extends BlockchainResult + +case class GetBlockHeaderResult( + hash: DoubleSha256Digest, + confirmations: Int, + height: Int, + version: Int, + versionHex: Int32, + merkleroot: DoubleSha256Digest, + time: UInt32, + mediantime: UInt32, + nonce: UInt32, + bits: UInt32, + difficulty: BigDecimal, + chainwork: String, + previousblockhash: Option[DoubleSha256Digest], + nextblockhash: Option[DoubleSha256Digest]) + extends BlockchainResult + +case class ChainTip( + height: Int, + hash: DoubleSha256Digest, + branchlen: Int, + status: String) + extends BlockchainResult + +case class GetChainTxStatsResult( + time: UInt32, + txcount: Int, + window_block_count: Int, + window_tx_count: Option[Int], + window_interval: Option[UInt32], + txrate: Option[BigDecimal]) + extends BlockchainResult + +case class GetMemPoolResult( + size: Int, + fee: Option[Bitcoins], + modifiedfee: Option[Bitcoins], + time: UInt32, + height: Int, + descendantcount: Int, + descendantsize: Int, + descendantfees: Option[Bitcoins], + ancestorcount: Int, + ancestorsize: Int, + ancestorfees: Option[Bitcoins], + wtxid: DoubleSha256Digest, + depends: Vector[DoubleSha256Digest]) + extends BlockchainResult + +case class GetMemPoolEntryResult( + size: Int, + fee: Bitcoins, + modifiedfee: Bitcoins, + time: UInt32, + height: Int, + descendantcount: Int, + descendantsize: Int, + descendantfees: Bitcoins, // Should be BitcoinFeeUnit + ancestorcount: Int, + ancestorsize: Int, + ancestorfees: Bitcoins, // Should be BitcoinFeeUnit + depends: Option[Vector[DoubleSha256Digest]]) + extends BlockchainResult + +case class GetMemPoolInfoResult( + size: Int, + bytes: Int, + usage: Int, + maxmempool: Int, + mempoolminfee: BitcoinFeeUnit, + minrelaytxfee: Bitcoins) + extends BlockchainResult + +case class GetTxOutResult( + bestblock: DoubleSha256Digest, + confirmations: Int, + value: Bitcoins, + scriptPubKey: RpcScriptPubKey, + coinbase: Boolean) + extends BlockchainResult + +case class GetTxOutSetInfoResult( + height: Int, + bestblock: DoubleSha256Digest, + transactions: Int, + txouts: Int, + bogosize: Int, + hash_serialized_2: DoubleSha256Digest, + disk_size: Int, + total_amount: Bitcoins) + extends BlockchainResult diff --git a/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/NetworkResult.scala b/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/NetworkResult.scala new file mode 100644 index 0000000000..0d212460dd --- /dev/null +++ b/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/NetworkResult.scala @@ -0,0 +1,100 @@ +package org.bitcoins.rpc.jsonmodels + +import java.net.URI + +import org.bitcoins.core.currency.Bitcoins +import org.bitcoins.core.number.{UInt32, UInt64} + +sealed abstract class NetworkResult + +case class Node( + addednode: URI, + connected: Option[Boolean], + addresses: Option[Vector[NodeAddress]]) + extends NetworkResult + +case class NodeAddress(address: URI, connected: String) extends NetworkResult + +case class GetNetTotalsResult( + totalbytesrecv: Int, + totalbytessent: Int, + timemillis: UInt64, + uploadtarget: NetTarget) + extends NetworkResult + +case class NetTarget( + timeframe: UInt32, + target: Int, + target_reached: Boolean, + serve_historical_blocks: Boolean, + bytes_left_in_cycle: Int, + time_left_in_cycle: UInt32) + extends NetworkResult + +case class GetNetworkInfoResult( + version: Int, + subversion: String, + protocolversion: Int, + localservices: String, + localrelay: Boolean, + timeoffset: Int, + networkactive: Boolean, + connections: Int, + networks: Vector[Network], + relayfee: Bitcoins, + incrementalfee: Bitcoins, + localadresses: Option[Vector[NetworkAddress]], + warnings: String) + extends NetworkResult + +case class Network( + name: String, + limited: Boolean, + reachable: Boolean, + proxy: String, + proxy_randomize_credentials: Boolean) + extends NetworkResult + +case class NetworkAddress(address: String, port: Int, score: Int) + extends NetworkResult + +case class Peer( + id: Int, + networkInfo: PeerNetworkInfo, + version: Int, + subver: String, + inbound: Boolean, + addnode: Boolean, + startingheight: Int, + banscore: Int, + synced_headers: Int, + synced_blocks: Int, + inflight: Vector[Int], + whitelisted: Boolean, + bytessent_per_msg: Map[String, Int], + bytesrecv_per_msg: Map[String, Int]) + extends NetworkResult + +case class PeerNetworkInfo( + addr: URI, + addrbind: URI, + addrlocal: Option[URI], + services: String, + relaytxes: Boolean, + lastsend: UInt32, + lastrecv: UInt32, + bytessent: Int, + bytesrecv: Int, + conntime: UInt32, + timeoffset: Int, + pingtime: Option[BigDecimal], + minping: Option[BigDecimal], + pingwait: Option[BigDecimal]) + extends NetworkResult + +case class NodeBan( + address: URI, + banned_until: UInt32, + ban_created: UInt32, + ban_reason: String) + extends NetworkResult diff --git a/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/OtherResult.scala b/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/OtherResult.scala new file mode 100644 index 0000000000..b462e06bab --- /dev/null +++ b/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/OtherResult.scala @@ -0,0 +1,97 @@ +package org.bitcoins.rpc.jsonmodels + +import org.bitcoins.core.crypto.{ + DoubleSha256Digest, + ECPublicKey, + Sha256Hash160Digest +} +import org.bitcoins.core.currency.Satoshis +import org.bitcoins.core.number.UInt32 +import org.bitcoins.core.protocol.BitcoinAddress +import org.bitcoins.core.protocol.script.ScriptPubKey +import org.bitcoins.core.protocol.transaction.Transaction +import org.bitcoins.core.wallet.fee.BitcoinFeeUnit +import play.api.libs.json.JsObject + +sealed abstract class OtherResult + +case class GetBlockTemplateResult( + capabilities: Vector[String], + version: Int, + rules: Vector[String], + vbavailable: Map[String, Int], // Is this Int or BigDecimal? + vbrequired: Int, + previousblockhash: DoubleSha256Digest, + transactions: Vector[BlockTransaction], + coinbaseaux: Map[String, String], + coinbasevalue: Satoshis, + longpollid: String, // What is this? + coinbasetxn: Option[JsObject], + target: String, // What should this be? + mintime: UInt32, + mutable: Vector[String], + noncerange: String, + sigoplimit: Int, + sizelimit: Int, + weightlimit: Int, + curtime: UInt32, + bits: String, // What should this be? + height: Int) + extends OtherResult + +case class BlockTransaction( + data: Transaction, + txid: DoubleSha256Digest, + hash: DoubleSha256Digest, + depends: Vector[Int], + fee: Satoshis, + sigops: Int, + weight: Int, + required: Option[Boolean]) + extends OtherResult + +case class GetMiningInfoResult( + blocks: Int, + currentblockweight: Int, + currentblocktx: Int, + difficulty: BigDecimal, + networkhashps: BigDecimal, + pooledtx: Int, + chain: String, + warnings: String) + extends OtherResult + +case class GetMemoryInfoResult(locked: MemoryManager) extends OtherResult + +case class MemoryManager( + used: Int, + free: Int, + total: Int, + locked: Int, + chunks_used: Int, + chunks_free: Int) + extends OtherResult + +case class ValidateAddressResult( + isvalid: Boolean, + address: Option[BitcoinAddress], + scriptPubKey: Option[ScriptPubKey], + ismine: Option[Boolean], + iswatchonly: Option[Boolean], + isscript: Option[Boolean], + script: Option[ScriptPubKey], + hex: Option[String], + addresses: Option[Vector[BitcoinAddress]], + sigrequired: Option[Int], + pubkey: Option[ECPublicKey], + iscompressed: Option[Boolean], + account: Option[String], + hdkeypath: Option[String], + hdmasterkeyid: Option[Sha256Hash160Digest]) + extends OtherResult + +case class EstimateSmartFeeResult( + feerate: Option[BitcoinFeeUnit], + errors: Option[Vector[String]], + blocks: Int) + extends OtherResult diff --git a/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/RawTransactionResult.scala b/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/RawTransactionResult.scala new file mode 100644 index 0000000000..297e6e6d0b --- /dev/null +++ b/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/RawTransactionResult.scala @@ -0,0 +1,92 @@ +package org.bitcoins.rpc.jsonmodels + +import org.bitcoins.core.crypto.DoubleSha256Digest +import org.bitcoins.core.currency.Bitcoins +import org.bitcoins.core.number.UInt32 +import org.bitcoins.core.protocol.{BitcoinAddress, P2PKHAddress, P2SHAddress} +import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptSignature} +import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput} + +sealed abstract class RawTransactionResult + +case class RpcTransaction( + txid: DoubleSha256Digest, + hash: DoubleSha256Digest, + version: Int, + size: Int, + vsize: Int, + locktime: UInt32, + vin: Vector[TransactionInput], + vout: Vector[RpcTransactionOutput], + hex: Option[Transaction]) + extends RawTransactionResult + +case class RpcTransactionOutput( + value: Bitcoins, + n: Int, + scriptPubKey: RpcScriptPubKey) + extends RawTransactionResult + +case class RpcScriptPubKey( + asm: String, + hex: String, + reqSigs: Option[Int], + scriptType: String, + addresses: Option[Vector[BitcoinAddress]]) + extends RawTransactionResult + +case class DecodeScriptResult( + asm: String, + typeOfScript: Option[String], + reqSigs: Option[Int], + addresses: Option[Vector[P2PKHAddress]], + p2sh: P2SHAddress) + extends RawTransactionResult + +case class FundRawTransactionResult( + hex: Transaction, + fee: Bitcoins, + changepos: Int) + extends RawTransactionResult + +case class GetRawTransactionResult( + in_active_blockchain: Option[Boolean], + hex: Transaction, + txid: DoubleSha256Digest, + hash: DoubleSha256Digest, + size: Int, + vsize: Int, + version: Int, + locktime: UInt32, + vin: Vector[GetRawTransactionVin], + vout: Vector[RpcTransactionOutput], + blockhash: DoubleSha256Digest, + confirmations: Int, + time: UInt32, + blocktime: UInt32) + extends RawTransactionResult + +case class GetRawTransactionVin( + txid: Option[DoubleSha256Digest], + vout: Option[Int], + scriptSig: Option[GetRawTransactionScriptSig], + sequence: Option[BigDecimal], + txinwitness: Option[Vector[String]] // Should be TransactionWitness? +) extends RawTransactionResult + +case class GetRawTransactionScriptSig(asm: String, hex: ScriptSignature) + extends RawTransactionResult + +case class SignRawTransactionResult( + hex: Transaction, + complete: Boolean, + errors: Option[Vector[SignRawTransactionError]]) + extends RawTransactionResult + +case class SignRawTransactionError( + txid: DoubleSha256Digest, + vout: Int, + scriptSig: ScriptPubKey, + sequence: UInt32, + error: String) + extends RawTransactionResult diff --git a/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/WalletResult.scala b/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/WalletResult.scala new file mode 100644 index 0000000000..730d564108 --- /dev/null +++ b/rpc/src/main/scala/org/bitcoins/rpc/jsonmodels/WalletResult.scala @@ -0,0 +1,174 @@ +package org.bitcoins.rpc.jsonmodels + +import java.io.File + +import org.bitcoins.core.crypto.{DoubleSha256Digest, Sha256Hash160Digest} +import org.bitcoins.core.currency.Bitcoins +import org.bitcoins.core.number.UInt32 +import org.bitcoins.core.protocol.BitcoinAddress +import org.bitcoins.core.protocol.script.ScriptPubKey +import org.bitcoins.core.protocol.transaction.Transaction +import org.bitcoins.core.wallet.fee.BitcoinFeeUnit + +sealed abstract class WalletResult + +case class MultiSigResult(address: BitcoinAddress, redeemScript: ScriptPubKey) + extends WalletResult + +case class BumpFeeResult( + txid: DoubleSha256Digest, + origfee: Bitcoins, + fee: Bitcoins, // TODO: Should be BitcoinFeeUnit + errors: Vector[String]) + extends WalletResult + +case class GetTransactionResult( + amount: Bitcoins, + fee: Option[Bitcoins], + confirmations: Int, + generated: Option[Boolean], + blockhash: Option[DoubleSha256Digest], + blockindex: Option[Int], + blocktime: Option[UInt32], + txid: DoubleSha256Digest, + walletconflicts: Vector[DoubleSha256Digest], + time: UInt32, + timereceived: UInt32, + bip125_replaceable: String, + comment: Option[String], + to: Option[String], + details: Vector[TransactionDetails], + hex: Transaction) + extends WalletResult + +case class TransactionDetails( + involvesWatchonly: Option[Boolean], + account: Option[String], + address: Option[BitcoinAddress], + category: String, + amount: Bitcoins, + vout: Int, + fee: Option[Bitcoins], + abandoned: Option[Boolean]) + extends WalletResult + +case class GetWalletInfoResult( + walletname: String, + walletversion: Int, + balance: Bitcoins, + unconfirmed_balance: Bitcoins, + immature_balance: Bitcoins, + txcount: Int, + keypoololdest: UInt32, + keypoolsize: Int, + keypoolsize_hd_internal: Int, + paytxfee: BitcoinFeeUnit, + hdmasterkeyid: Sha256Hash160Digest, + unlocked_until: Option[Int]) + extends WalletResult + +case class ImportMultiResult(success: Boolean, error: Option[ImportMultiError]) + extends WalletResult + +case class ImportMultiError(code: Int, message: String) extends WalletResult + +case class RpcAddress( + address: BitcoinAddress, + balance: Bitcoins, + account: Option[String]) + extends WalletResult + +case class RpcAccount( + involvesWatchonly: Boolean, + account: String, + amount: Bitcoins, + confirmations: Int) + extends WalletResult + +case class DumpWalletResult(filename: File) + +case class RescanBlockChainResult(start_height: Int, stop_height: Int) + extends WalletResult + +case class ReceivedAddress( + involvesWatchonly: Option[Boolean], + address: BitcoinAddress, + account: String, + amount: Bitcoins, + confirmations: Int, + label: String, + txids: Vector[DoubleSha256Digest]) + extends WalletResult + +case class ReceivedAccount( + involvesWatchonly: Option[Boolean], + account: String, + amount: Bitcoins, + confirmations: Int, + lable: Option[String]) + extends WalletResult + +case class ListSinceBlockResult( + transactions: Vector[Payment], + lastblock: DoubleSha256Digest) + extends WalletResult + +case class Payment( + involvesWatchonly: Option[Boolean], + account: Option[String], + address: Option[BitcoinAddress], + category: String, + amount: Bitcoins, + vout: Int, + fee: Option[Bitcoins], + confirmations: Int, + generated: Option[Boolean], + blockhash: Option[DoubleSha256Digest], + blockindex: Option[Int], + blocktime: Option[UInt32], + txid: DoubleSha256Digest, + walletconflicts: Vector[DoubleSha256Digest], + time: UInt32, + timereceived: UInt32, + bip125_replaceable: String, + comment: Option[String], + to: Option[String]) + extends WalletResult + +case class ListTransactionsResult( + account: Option[String], + address: Option[BitcoinAddress], + category: String, + amount: Bitcoins, + label: Option[String], + vout: Option[Int], + fee: Option[Bitcoins], + confirmations: Option[Int], + trusted: Option[Boolean], + generated: Option[Boolean], + blockhash: Option[DoubleSha256Digest], + blockindex: Option[Int], + blocktime: Option[UInt32], + txid: Option[DoubleSha256Digest], + walletconflicts: Option[Vector[DoubleSha256Digest]], + time: UInt32, + timereceived: Option[UInt32], + comment: Option[String], + to: Option[String], + otheraccount: Option[String], + bip125_replaceable: String, + abandoned: Option[Boolean]) + extends WalletResult + +case class UnspentOutput( + txid: DoubleSha256Digest, + vout: Int, + address: Option[BitcoinAddress], + account: Option[String], + scriptPubKey: Option[ScriptPubKey], + reedemScript: Option[ScriptPubKey], + amount: Bitcoins, + confirmations: Int, + spendable: Boolean, + solvable: Boolean) + extends WalletResult diff --git a/rpc/src/main/scala/org/bitcoins/rpc/serializers/JsonSerializers.scala b/rpc/src/main/scala/org/bitcoins/rpc/serializers/JsonSerializers.scala new file mode 100644 index 0000000000..dcd8b97132 --- /dev/null +++ b/rpc/src/main/scala/org/bitcoins/rpc/serializers/JsonSerializers.scala @@ -0,0 +1,311 @@ +package org.bitcoins.rpc.serializers + +import java.io.File +import java.net.{InetAddress, URI} + +import org.bitcoins.core.crypto.{ + DoubleSha256Digest, + ECPublicKey, + Sha256Hash160Digest +} +import org.bitcoins.core.currency.{Bitcoins, Satoshis} +import org.bitcoins.core.number.{Int32, UInt32, UInt64} +import org.bitcoins.core.protocol.{ + Address, + BitcoinAddress, + P2PKHAddress, + P2SHAddress +} +import org.bitcoins.core.protocol.blockchain.{Block, BlockHeader, MerkleBlock} +import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptSignature} +import org.bitcoins.core.protocol.transaction.{ + Transaction, + TransactionInput, + TransactionOutPoint +} +import org.bitcoins.core.wallet.fee.BitcoinFeeUnit +import org.bitcoins.rpc.jsonmodels._ +import org.bitcoins.rpc.serializers.JsonReaders._ +import org.bitcoins.rpc.serializers.JsonWriters._ +import play.api.libs.json._ +import play.api.libs.functional.syntax._ + +object JsonSerializers { + implicit val bigIntReads: Reads[BigInt] = BigIntReads + + // Internal Types + implicit val doubleSha256DigestReads: Reads[DoubleSha256Digest] = + DoubleSha256DigestReads + implicit val bitcoinsReads: Reads[Bitcoins] = BitcoinsReads + implicit val satoshisReads: Reads[Satoshis] = SatoshisReads + implicit val blockHeaderReads: Reads[BlockHeader] = BlockHeaderReads + implicit val int32Reads: Reads[Int32] = Int32Reads + implicit val uInt32Reads: Reads[UInt32] = UInt32Reads + implicit val uInt64Reads: Reads[UInt64] = UInt64Reads + implicit val addressReads: Reads[Address] = AddressReads + implicit val unitReads: Reads[Unit] = UnitReads + implicit val inetAddressReads: Reads[InetAddress] = InetAddressReads + implicit val scriptPubKeyReads: Reads[ScriptPubKey] = ScriptPubKeyReads + implicit val blockReads: Reads[Block] = BlockReads + implicit val sha256Hash160DigestReads: Reads[Sha256Hash160Digest] = + Sha256Hash160DigestReads + implicit val eCPublicKeyReads: Reads[ECPublicKey] = ECPublicKeyReads + implicit val p2PKHAddressReads: Reads[P2PKHAddress] = P2PKHAddressReads + implicit val p2SHAddressReads: Reads[P2SHAddress] = P2SHAddressReads + implicit val transactionInputReads: Reads[TransactionInput] = + TransactionInputReads + implicit val bitcoinAddressReads: Reads[BitcoinAddress] = BitcoinAddressReads + implicit val merkleBlockReads: Reads[MerkleBlock] = MerkleBlockReads + implicit val transactionReads: Reads[Transaction] = TransactionReads + implicit val transactionOutPointReads: Reads[TransactionOutPoint] = + TransactionOutPointReads + implicit val bitcoinFeeUnitReads: Reads[BitcoinFeeUnit] = BitcoinFeeUnitReads + implicit val fileReads: Reads[File] = FileReads + implicit val uRIReads: Reads[URI] = URIReads + implicit val scriptSignatureReads: Reads[ScriptSignature] = + ScriptSignatureReads + + implicit val bitcoinsWrites: Writes[Bitcoins] = BitcoinsWrites + implicit val bitcoinAddressWrites: Writes[BitcoinAddress] = + BitcoinAddressWrites + implicit val doubleSha256DigestWrites: Writes[DoubleSha256Digest] = + DoubleSha256DigestWrites + implicit val scriptPubKeyWrites: Writes[ScriptPubKey] = ScriptPubKeyWrites + implicit val transactionInputWrites: Writes[TransactionInput] = + TransactionInputWrites + implicit val uInt32Writes: Writes[UInt32] = UInt32Writes + implicit val transactionWrites: Writes[Transaction] = TransactionWrites + + // Transaction Models + implicit val rpcScriptPubKeyReads: Reads[RpcScriptPubKey] = + ((__ \ "asm").read[String] and + (__ \ "hex").read[String] and + (__ \ "reqSigs").readNullable[Int] and + (__ \ "type").read[String] and + (__ \ "addresses").readNullable[Vector[BitcoinAddress]])(RpcScriptPubKey) + implicit val rpcTransactionOutputReads: Reads[RpcTransactionOutput] = + Json.reads[RpcTransactionOutput] + implicit val rpcTransactionReads: Reads[RpcTransaction] = + Json.reads[RpcTransaction] + + implicit val decodeScriptResultReads: Reads[DecodeScriptResult] = + ((__ \ "asm").read[String] and + (__ \ "type").readNullable[String] and + (__ \ "reqSigs").readNullable[Int] and + (__ \ "addresses").readNullable[Vector[P2PKHAddress]] and + (__ \ "p2sh").read[P2SHAddress])(DecodeScriptResult) + + implicit val fundRawTransactionResultReads: Reads[FundRawTransactionResult] = + Json.reads[FundRawTransactionResult] + + implicit val getRawTransactionScriptSigReads: Reads[ + GetRawTransactionScriptSig] = Json.reads[GetRawTransactionScriptSig] + implicit val getRawTransactionVinReads: Reads[GetRawTransactionVin] = + Json.reads[GetRawTransactionVin] + implicit val getRawTransactionResultReads: Reads[GetRawTransactionResult] = + Json.reads[GetRawTransactionResult] + + implicit val signRawTransactionErrorReads: Reads[SignRawTransactionError] = + Json.reads[SignRawTransactionError] + implicit val signRawTransactionResultReads: Reads[SignRawTransactionResult] = + Json.reads[SignRawTransactionResult] + + // Network Models + implicit val nodeAddressReads: Reads[NodeAddress] = Json.reads[NodeAddress] + implicit val nodeReads: Reads[Node] = Json.reads[Node] + + implicit val netTargetReads: Reads[NetTarget] = Json.reads[NetTarget] + implicit val getNetTotalsResultReads: Reads[GetNetTotalsResult] = + Json.reads[GetNetTotalsResult] + + implicit val networkReads: Reads[Network] = Json.reads[Network] + implicit val networkAddressReads: Reads[NetworkAddress] = + Json.reads[NetworkAddress] + implicit val networkInfoReads: Reads[GetNetworkInfoResult] = + Json.reads[GetNetworkInfoResult] + + implicit val peerNetworkInfoReads: Reads[PeerNetworkInfo] = + Json.reads[PeerNetworkInfo] + implicit val peerReads: Reads[Peer] = ((__ \ "id").read[Int] and + __.read[PeerNetworkInfo] and + (__ \ "version").read[Int] and + (__ \ "subver").read[String] and + (__ \ "inbound").read[Boolean] and + (__ \ "addnode").read[Boolean] and + (__ \ "startingheight").read[Int] and + (__ \ "banscore").read[Int] and + (__ \ "synced_headers").read[Int] and + (__ \ "synced_blocks").read[Int] and + (__ \ "inflight").read[Vector[Int]] and + (__ \ "whitelisted").read[Boolean] and + (__ \ "bytessent_per_msg").read[Map[String, Int]] and + (__ \ "bytesrecv_per_msg").read[Map[String, Int]])(Peer) + + implicit val nodeBanReads: Reads[NodeBan] = Json.reads[NodeBan] + + // Blockchain Models + implicit val getBlockResultReads: Reads[GetBlockResult] = + Json.reads[GetBlockResult] + + implicit val getBlockWithTransactionsResultReads: Reads[ + GetBlockWithTransactionsResult] = Json.reads[GetBlockWithTransactionsResult] + + implicit val softforkProgressReads: Reads[SoftforkProgress] = + Json.reads[SoftforkProgress] + implicit val softforkReads: Reads[Softfork] = Json.reads[Softfork] + implicit val bip9SoftforkReads: Reads[Bip9Softfork] = Json.reads[Bip9Softfork] + implicit val getBlockChainInfoResultReads: Reads[GetBlockChainInfoResult] = + Json.reads[GetBlockChainInfoResult] + + implicit val blockHeaderFormattedReads: Reads[GetBlockHeaderResult] = + Json.reads[GetBlockHeaderResult] + + implicit val chainTipReads: Reads[ChainTip] = Json.reads[ChainTip] + + implicit val getChainTxStatsResultReads: Reads[GetChainTxStatsResult] = + Json.reads[GetChainTxStatsResult] + + implicit val getMemPoolResultReads: Reads[GetMemPoolResult] = + Json.reads[GetMemPoolResult] + + implicit val getMemPoolEntryResultReads: Reads[GetMemPoolEntryResult] = + Json.reads[GetMemPoolEntryResult] + + implicit val getMemPoolInfoResultReads: Reads[GetMemPoolInfoResult] = + Json.reads[GetMemPoolInfoResult] + + implicit val getTxOutResultReads: Reads[GetTxOutResult] = + Json.reads[GetTxOutResult] + + implicit val getTxOutSetInfoResultReads: Reads[GetTxOutSetInfoResult] = + Json.reads[GetTxOutSetInfoResult] + + // Wallet Models + implicit val multiSigReads: Reads[MultiSigResult] = + Json.reads[MultiSigResult] + + implicit val bumpFeeReads: Reads[BumpFeeResult] = Json.reads[BumpFeeResult] + + implicit val TransactionDetailsReads: Reads[TransactionDetails] = + Json.reads[TransactionDetails] + implicit val getTransactionResultReads: Reads[GetTransactionResult] = + ((__ \ "amount").read[Bitcoins] and + (__ \ "fee").readNullable[Bitcoins] and + (__ \ "confirmations").read[Int] and + (__ \ "generated").readNullable[Boolean] and + (__ \ "blockhash").readNullable[DoubleSha256Digest] and + (__ \ "blockindex").readNullable[Int] and + (__ \ "blocktime").readNullable[UInt32] and + (__ \ "txid").read[DoubleSha256Digest] and + (__ \ "walletconflicts").read[Vector[DoubleSha256Digest]] and + (__ \ "time").read[UInt32] and + (__ \ "timereceived").read[UInt32] and + (__ \ "bip125-replaceable").read[String] and + (__ \ "comment").readNullable[String] and + (__ \ "to").readNullable[String] and + (__ \ "details").read[Vector[TransactionDetails]] and + (__ \ "hex").read[Transaction])(GetTransactionResult) + + implicit val getWalletInfoResultReads: Reads[GetWalletInfoResult] = + Json.reads[GetWalletInfoResult] + + implicit val importMultiErrorReads: Reads[ImportMultiError] = + Json.reads[ImportMultiError] + implicit val importMultiResultReads: Reads[ImportMultiResult] = + Json.reads[ImportMultiResult] + + implicit val rpcAddressReads: Reads[RpcAddress] = RpcAddressReads + + implicit val rpcAccoutReads: Reads[RpcAccount] = Json.reads[RpcAccount] + + implicit val dumpWalletResultReads: Reads[DumpWalletResult] = + Json.reads[DumpWalletResult] + + implicit val rescanBlockChainResultReads: Reads[RescanBlockChainResult] = + Json.reads[RescanBlockChainResult] + + implicit val receivedAddressReads: Reads[ReceivedAddress] = + Json.reads[ReceivedAddress] + + implicit val receivedAccountReads: Reads[ReceivedAccount] = + Json.reads[ReceivedAccount] + + implicit val paymentReads: Reads[Payment] = + ((__ \ "involvesWatchonly").readNullable[Boolean] and + (__ \ "account").readNullable[String] and + (__ \ "address").readNullable[BitcoinAddress] and + (__ \ "category").read[String] and + (__ \ "amount").read[Bitcoins] and + (__ \ "vout").read[Int] and + (__ \ "fee").readNullable[Bitcoins] and + (__ \ "confirmations").read[Int] and + (__ \ "generated").readNullable[Boolean] and + (__ \ "blockhash").readNullable[DoubleSha256Digest] and + (__ \ "blockindex").readNullable[Int] and + (__ \ "blocktime").readNullable[UInt32] and + (__ \ "txid").read[DoubleSha256Digest] and + (__ \ "walletconflicts").read[Vector[DoubleSha256Digest]] and + (__ \ "time").read[UInt32] and + (__ \ "timereceived").read[UInt32] and + (__ \ "bip125-replaceable").read[String] and + (__ \ "comment").readNullable[String] and + (__ \ "to").readNullable[String])(Payment) + implicit val listSinceBlockResultReads: Reads[ListSinceBlockResult] = + Json.reads[ListSinceBlockResult] + + implicit val listTransactionsResultReads: Reads[ListTransactionsResult] = + ((__ \ "account").readNullable[String] and + (__ \ "address").readNullable[BitcoinAddress] and + (__ \ "category").read[String] and + (__ \ "amount").read[Bitcoins] and + (__ \ "label").readNullable[String] and + (__ \ "vout").readNullable[Int] and + (__ \ "fee").readNullable[Bitcoins] and + (__ \ "confirmations").readNullable[Int] and + (__ \ "trusted").readNullable[Boolean] and + (__ \ "generated").readNullable[Boolean] and + (__ \ "blockhash").readNullable[DoubleSha256Digest] and + (__ \ "blockindex").readNullable[Int] and + (__ \ "blocktime").readNullable[UInt32] and + (__ \ "txid").readNullable[DoubleSha256Digest] and + (__ \ "walletconflicts").readNullable[Vector[DoubleSha256Digest]] and + (__ \ "time").read[UInt32] and + (__ \ "timereceived").readNullable[UInt32] and + (__ \ "comment").readNullable[String] and + (__ \ "to").readNullable[String] and + (__ \ "otheraccount").readNullable[String] and + (__ \ "bip125-replaceable").read[String] and + (__ \ "abandoned").readNullable[Boolean])(ListTransactionsResult) + + implicit val unspentOutputReads: Reads[UnspentOutput] = + Json.reads[UnspentOutput] + + // Other Models + implicit val blockTransactionReads: Reads[BlockTransaction] = + Json.reads[BlockTransaction] + implicit val getBlockTemplateResultReads: Reads[GetBlockTemplateResult] = + Json.reads[GetBlockTemplateResult] + + implicit val miningInfoReads: Reads[GetMiningInfoResult] = + Json.reads[GetMiningInfoResult] + + implicit val memoryManagerReads: Reads[MemoryManager] = + Json.reads[MemoryManager] + implicit val getMemoryInfoResultReads: Reads[GetMemoryInfoResult] = + Json.reads[GetMemoryInfoResult] + + implicit val validateAddressResultReads: Reads[ValidateAddressResult] = + Json.reads[ValidateAddressResult] + + implicit val estimateSmartFeeResultReads: Reads[EstimateSmartFeeResult] = + Json.reads[EstimateSmartFeeResult] + + // Map stuff + implicit def mapDoubleSha256DigestReads: Reads[ + Map[DoubleSha256Digest, GetMemPoolResult]] = + Reads.mapReads[DoubleSha256Digest, GetMemPoolResult](s => + JsSuccess(DoubleSha256Digest.fromHex(s))) + + implicit val outputMapWrites: Writes[Map[BitcoinAddress, Bitcoins]] = + mapWrites[BitcoinAddress, Bitcoins](_.value) +} diff --git a/rpc/src/main/scala/org/bitcoins/rpc/serializers/JsonWriters.scala b/rpc/src/main/scala/org/bitcoins/rpc/serializers/JsonWriters.scala new file mode 100644 index 0000000000..bf7d8ebbe7 --- /dev/null +++ b/rpc/src/main/scala/org/bitcoins/rpc/serializers/JsonWriters.scala @@ -0,0 +1,53 @@ +package org.bitcoins.rpc.serializers + +import org.bitcoins.core.crypto.DoubleSha256Digest +import org.bitcoins.core.currency.Bitcoins +import org.bitcoins.core.number.UInt32 +import org.bitcoins.core.protocol.BitcoinAddress +import org.bitcoins.core.protocol.script.ScriptPubKey +import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput} +import org.bitcoins.core.util.BitcoinSUtil +import play.api.libs.json._ + +object JsonWriters { + implicit object BitcoinsWrites extends Writes[Bitcoins] { + override def writes(o: Bitcoins): JsValue = JsNumber(o.toBigDecimal) + } + + implicit object BitcoinAddressWrites extends Writes[BitcoinAddress] { + override def writes(o: BitcoinAddress): JsValue = JsString(o.value) + } + + implicit object DoubleSha256DigestWrites extends Writes[DoubleSha256Digest] { + override def writes(o: DoubleSha256Digest): JsValue = JsString(o.hex) + } + + implicit object ScriptPubKeyWrites extends Writes[ScriptPubKey] { + override def writes(o: ScriptPubKey): JsValue = + JsString(BitcoinSUtil.encodeHex(o.asmBytes)) + } + + implicit object TransactionInputWrites extends Writes[TransactionInput] { + override def writes(o: TransactionInput): JsValue = + JsObject( + Seq(("txid", JsString(o.previousOutput.txId.flip.hex)), + ("vout", JsNumber(o.previousOutput.vout.toLong)), + ("sequence", JsNumber(o.sequence.toLong)))) + } + + implicit object UInt32Writes extends Writes[UInt32] { + override def writes(o: UInt32): JsValue = JsNumber(o.toLong) + } + + implicit object TransactionWrites extends Writes[Transaction] { + override def writes(o: Transaction): JsValue = JsString(o.hex) + } + + implicit def mapWrites[K, V](keyString: K => String)( + implicit + vWrites: Writes[V]): Writes[Map[K, V]] = new Writes[Map[K, V]] { + override def writes(o: Map[K, V]): JsValue = { + Json.toJson(o.map { case (k, v) => (keyString(k), v) }) + } + } +}