diff --git a/client/Cargo.toml b/client/Cargo.toml
index 60c7df2b..b2ff4203 100644
--- a/client/Cargo.toml
+++ b/client/Cargo.toml
@@ -12,6 +12,7 @@ repository = "https://github.com/rust-bitcoin/rust-bitcoincore-rpc/"
description = "RPC client library for the Bitcoin Core JSON-RPC API."
keywords = [ "crypto", "bitcoin", "bitcoin-core", "rpc" ]
readme = "README.md"
+edition = "2018"
[lib]
name = "bitcoincore_rpc"
@@ -21,7 +22,9 @@ path = "src/lib.rs"
bitcoincore-rpc-json = { version = "0.11.0", path = "../json"}
log = "0.4.5"
-jsonrpc = "0.11"
+reqwest = { version = "0.10", features = ["json"] }
+async-trait = "0.1.40"
+base64-compat = "1.0.0"
# Used for deserialization of JSON.
serde = "1"
diff --git a/client/examples/retry_client.rs b/client/examples/retry_client.rs
index 10b2fed3..ed9c9871 100644
--- a/client/examples/retry_client.rs
+++ b/client/examples/retry_client.rs
@@ -8,10 +8,10 @@
// If not, see .
//
-extern crate bitcoincore_rpc;
-extern crate jsonrpc;
-extern crate serde;
-extern crate serde_json;
+use async_trait::async_trait;
+use bitcoincore_rpc;
+use serde;
+use serde_json;
use bitcoincore_rpc::{Client, Error, Result, RpcApi};
@@ -22,25 +22,24 @@ pub struct RetryClient {
const INTERVAL: u64 = 1000;
const RETRY_ATTEMPTS: u8 = 10;
+#[async_trait]
impl RpcApi for RetryClient {
- fn call serde::de::Deserialize<'a>>(
+ async fn call serde::de::Deserialize<'a>>(
&self,
cmd: &str,
args: &[serde_json::Value],
) -> Result {
for _ in 0..RETRY_ATTEMPTS {
- match self.client.call(cmd, args) {
+ match self.client.call(cmd, args).await {
Ok(ret) => return Ok(ret),
- Err(Error::JsonRpc(jsonrpc::error::Error::Rpc(ref rpcerr)))
- if rpcerr.code == -28 =>
- {
+ Err(Error::JsonRpc(ref rpcerr)) if rpcerr.code == -28 => {
::std::thread::sleep(::std::time::Duration::from_millis(INTERVAL));
continue;
}
Err(e) => return Err(e),
}
}
- self.client.call(cmd, args)
+ self.client.call(cmd, args).await
}
}
diff --git a/client/examples/test_against_node.rs b/client/examples/test_against_node.rs
index 595d854b..5a5cb131 100644
--- a/client/examples/test_against_node.rs
+++ b/client/examples/test_against_node.rs
@@ -10,11 +10,12 @@
//! A very simple example used as a self-test of this library against a Bitcoin
//! Core node.
-extern crate bitcoincore_rpc;
+use bitcoincore_rpc;
use bitcoincore_rpc::{bitcoin, Auth, Client, Error, RpcApi};
-fn main_result() -> Result<(), Error> {
+#[tokio::main]
+async fn main_result() -> Result<(), Error> {
let mut args = std::env::args();
let _exe_name = args.next().unwrap();
@@ -25,19 +26,22 @@ fn main_result() -> Result<(), Error> {
let rpc = Client::new(url, Auth::UserPass(user, pass)).unwrap();
- let _blockchain_info = rpc.get_blockchain_info()?;
+ let _blockchain_info = rpc.get_blockchain_info().await?;
- let best_block_hash = rpc.get_best_block_hash()?;
+ let best_block_hash = rpc.get_best_block_hash().await?;
println!("best block hash: {}", best_block_hash);
- let bestblockcount = rpc.get_block_count()?;
+ let bestblockcount = rpc.get_block_count().await?;
println!("best block height: {}", bestblockcount);
- let best_block_hash_by_height = rpc.get_block_hash(bestblockcount)?;
+ let best_block_hash_by_height = rpc.get_block_hash(bestblockcount).await?;
println!("best block hash by height: {}", best_block_hash_by_height);
assert_eq!(best_block_hash_by_height, best_block_hash);
- let bitcoin_block: bitcoin::Block = rpc.get_by_id(&best_block_hash)?;
- println!("best block hash by `get`: {}", bitcoin_block.header.prev_blockhash);
- let bitcoin_tx: bitcoin::Transaction = rpc.get_by_id(&bitcoin_block.txdata[0].txid())?;
+ let bitcoin_block: bitcoin::Block = rpc.get_by_id(&best_block_hash).await?;
+ println!(
+ "best block hash by `get`: {}",
+ bitcoin_block.header.prev_blockhash
+ );
+ let bitcoin_tx: bitcoin::Transaction = rpc.get_by_id(&bitcoin_block.txdata[0].txid()).await?;
println!("tx by `get`: {}", bitcoin_tx.txid());
Ok(())
diff --git a/client/src/client.rs b/client/src/client.rs
index 636e375d..6e4f0b33 100644
--- a/client/src/client.rs
+++ b/client/src/client.rs
@@ -12,13 +12,17 @@ use std::collections::HashMap;
use std::fs::File;
use std::iter::FromIterator;
use std::path::PathBuf;
+use std::sync::Arc;
use std::{fmt, result};
-use bitcoin;
-use jsonrpc;
+use crate::bitcoin;
+use crate::jsonrpc::{JsonRpcRequest, JsonRpcResponse};
+use reqwest;
use serde;
use serde_json;
+use async_trait::async_trait;
+use base64;
use bitcoin::hashes::hex::{FromHex, ToHex};
use bitcoin::secp256k1::Signature;
use bitcoin::{
@@ -26,10 +30,11 @@ use bitcoin::{
};
use log::Level::{Debug, Trace, Warn};
use serde::{Deserialize, Serialize};
+use std::sync::Mutex;
-use error::*;
-use json;
-use queryable;
+use crate::error::*;
+use crate::json;
+use crate::queryable;
/// Crate-specific Result type, shorthand for `std::result::Result` with our
/// crate-specific Error type;
@@ -216,39 +221,43 @@ impl Auth {
}
}
+#[async_trait]
pub trait RpcApi: Sized {
/// Call a `cmd` rpc with given `args` list
- fn call serde::de::Deserialize<'a>>(
+ async fn call serde::de::Deserialize<'a>>(
&self,
cmd: &str,
args: &[serde_json::Value],
) -> Result;
/// Query an object implementing `Querable` type
- fn get_by_id>(
+ async fn get_by_id>(
&self,
id: &>::Id,
- ) -> Result {
- T::query(&self, &id)
+ ) -> Result
+ where
+ >::Id: Sync,
+ {
+ T::query(&self, &id).await
}
- fn get_network_info(&self) -> Result {
- self.call("getnetworkinfo", &[])
+ async fn get_network_info(&self) -> Result {
+ self.call("getnetworkinfo", &[]).await
}
- fn version(&self) -> Result {
+ async fn version(&self) -> Result {
#[derive(Deserialize)]
struct Response {
pub version: usize,
}
- let res: Response = self.call("getnetworkinfo", &[])?;
+ let res: Response = self.call("getnetworkinfo", &[]).await?;
Ok(res.version)
}
- fn add_multisig_address(
+ async fn add_multisig_address<'a>(
&self,
nrequired: usize,
- keys: &[json::PubKeyOrAddress],
+ keys: &'a [json::PubKeyOrAddress<'a>],
label: Option<&str>,
address_type: Option,
) -> Result {
@@ -258,19 +267,24 @@ pub trait RpcApi: Sized {
opt_into_json(label)?,
opt_into_json(address_type)?,
];
- self.call("addmultisigaddress", handle_defaults(&mut args, &[into_json("")?, null()]))
+ self.call(
+ "addmultisigaddress",
+ handle_defaults(&mut args, &[into_json("")?, null()]),
+ )
+ .await
}
- fn load_wallet(&self, wallet: &str) -> Result {
- self.call("loadwallet", &[wallet.into()])
+ async fn load_wallet(&self, wallet: &str) -> Result {
+ self.call("loadwallet", &[wallet.into()]).await
}
- fn unload_wallet(&self, wallet: Option<&str>) -> Result<()> {
+ async fn unload_wallet(&self, wallet: Option<&str>) -> Result<()> {
let mut args = [opt_into_json(wallet)?];
self.call("unloadwallet", handle_defaults(&mut args, &[null()]))
+ .await
}
- fn create_wallet(
+ async fn create_wallet(
&self,
wallet: &str,
disable_private_keys: Option,
@@ -287,79 +301,88 @@ pub trait RpcApi: Sized {
];
self.call(
"createwallet",
- handle_defaults(&mut args, &[false.into(), false.into(), into_json("")?, false.into()]),
+ handle_defaults(
+ &mut args,
+ &[false.into(), false.into(), into_json("")?, false.into()],
+ ),
)
+ .await
}
- fn list_wallets(&self) -> Result> {
- self.call("listwallets", &[])
+ async fn list_wallets(&self) -> Result> {
+ self.call("listwallets", &[]).await
}
- fn get_wallet_info(&self) -> Result {
- self.call("getwalletinfo", &[])
+ async fn get_wallet_info(&self) -> Result {
+ self.call("getwalletinfo", &[]).await
}
- fn backup_wallet(&self, destination: Option<&str>) -> Result<()> {
+ async fn backup_wallet(&self, destination: Option<&str>) -> Result<()> {
let mut args = [opt_into_json(destination)?];
self.call("backupwallet", handle_defaults(&mut args, &[null()]))
+ .await
}
- fn dump_private_key(&self, address: &Address) -> Result {
+ async fn dump_private_key(&self, address: &Address) -> Result {
self.call("dumpprivkey", &[address.to_string().into()])
+ .await
}
- fn encrypt_wallet(&self, passphrase: &str) -> Result<()> {
- self.call("encryptwallet", &[into_json(passphrase)?])
+ async fn encrypt_wallet(&self, passphrase: &str) -> Result<()> {
+ self.call("encryptwallet", &[into_json(passphrase)?]).await
}
- fn get_difficulty(&self) -> Result {
- self.call("getdifficulty", &[])
+ async fn get_difficulty(&self) -> Result {
+ self.call("getdifficulty", &[]).await
}
- fn get_connection_count(&self) -> Result {
- self.call("getconnectioncount", &[])
+ async fn get_connection_count(&self) -> Result {
+ self.call("getconnectioncount", &[]).await
}
- fn get_block(&self, hash: &bitcoin::BlockHash) -> Result {
- let hex: String = self.call("getblock", &[into_json(hash)?, 0.into()])?;
+ async fn get_block(&self, hash: &bitcoin::BlockHash) -> Result {
+ let hex: String = self.call("getblock", &[into_json(hash)?, 0.into()]).await?;
let bytes: Vec = FromHex::from_hex(&hex)?;
Ok(bitcoin::consensus::encode::deserialize(&bytes)?)
}
- fn get_block_hex(&self, hash: &bitcoin::BlockHash) -> Result {
- self.call("getblock", &[into_json(hash)?, 0.into()])
+ async fn get_block_hex(&self, hash: &bitcoin::BlockHash) -> Result {
+ self.call("getblock", &[into_json(hash)?, 0.into()]).await
}
- fn get_block_info(&self, hash: &bitcoin::BlockHash) -> Result {
- self.call("getblock", &[into_json(hash)?, 1.into()])
+ async fn get_block_info(&self, hash: &bitcoin::BlockHash) -> Result {
+ self.call("getblock", &[into_json(hash)?, 1.into()]).await
}
//TODO(stevenroose) add getblock_txs
- fn get_block_header(&self, hash: &bitcoin::BlockHash) -> Result {
- let hex: String = self.call("getblockheader", &[into_json(hash)?, false.into()])?;
+ async fn get_block_header(&self, hash: &bitcoin::BlockHash) -> Result {
+ let hex: String = self
+ .call("getblockheader", &[into_json(hash)?, false.into()])
+ .await?;
let bytes: Vec = FromHex::from_hex(&hex)?;
Ok(bitcoin::consensus::encode::deserialize(&bytes)?)
}
- fn get_block_header_info(
+ async fn get_block_header_info(
&self,
hash: &bitcoin::BlockHash,
) -> Result {
self.call("getblockheader", &[into_json(hash)?, true.into()])
+ .await
}
- fn get_mining_info(&self) -> Result {
- self.call("getmininginfo", &[])
+ async fn get_mining_info(&self) -> Result {
+ self.call("getmininginfo", &[]).await
}
/// Returns a data structure containing various state info regarding
/// blockchain processing.
- fn get_blockchain_info(&self) -> Result {
- let mut raw: serde_json::Value = self.call("getblockchaininfo", &[])?;
+ async fn get_blockchain_info(&self) -> Result {
+ let mut raw: serde_json::Value = self.call("getblockchaininfo", &[]).await?;
// The softfork fields are not backwards compatible:
// - 0.18.x returns a "softforks" array and a "bip9_softforks" map.
// - 0.19.x returns a "softforks" map.
- Ok(if self.version()? < 190000 {
+ Ok(if self.version().await? < 190000 {
use Error::UnexpectedStructure as err;
// First, remove both incompatible softfork fields.
@@ -422,88 +445,121 @@ pub trait RpcApi: Sized {
}
/// Returns the numbers of block in the longest chain.
- fn get_block_count(&self) -> Result {
- self.call("getblockcount", &[])
+ async fn get_block_count(&self) -> Result {
+ self.call("getblockcount", &[]).await
}
/// Returns the hash of the best (tip) block in the longest blockchain.
- fn get_best_block_hash(&self) -> Result {
- self.call("getbestblockhash", &[])
+ async fn get_best_block_hash(&self) -> Result {
+ self.call("getbestblockhash", &[]).await
}
/// Get block hash at a given height
- fn get_block_hash(&self, height: u64) -> Result {
- self.call("getblockhash", &[height.into()])
+ async fn get_block_hash(&self, height: u64) -> Result {
+ self.call("getblockhash", &[height.into()]).await
}
- fn get_raw_transaction(
+ async fn get_raw_transaction(
&self,
txid: &bitcoin::Txid,
block_hash: Option<&bitcoin::BlockHash>,
) -> Result {
- let mut args = [into_json(txid)?, into_json(false)?, opt_into_json(block_hash)?];
- let hex: String = self.call("getrawtransaction", handle_defaults(&mut args, &[null()]))?;
+ let mut args = [
+ into_json(txid)?,
+ into_json(false)?,
+ opt_into_json(block_hash)?,
+ ];
+ let hex: String = self
+ .call("getrawtransaction", handle_defaults(&mut args, &[null()]))
+ .await?;
let bytes: Vec = FromHex::from_hex(&hex)?;
Ok(bitcoin::consensus::encode::deserialize(&bytes)?)
}
- fn get_raw_transaction_hex(
+ async fn get_raw_transaction_hex(
&self,
txid: &bitcoin::Txid,
block_hash: Option<&bitcoin::BlockHash>,
) -> Result {
- let mut args = [into_json(txid)?, into_json(false)?, opt_into_json(block_hash)?];
+ let mut args = [
+ into_json(txid)?,
+ into_json(false)?,
+ opt_into_json(block_hash)?,
+ ];
self.call("getrawtransaction", handle_defaults(&mut args, &[null()]))
+ .await
}
- fn get_raw_transaction_info(
+ async fn get_raw_transaction_info(
&self,
txid: &bitcoin::Txid,
block_hash: Option<&bitcoin::BlockHash>,
) -> Result {
- let mut args = [into_json(txid)?, into_json(true)?, opt_into_json(block_hash)?];
+ let mut args = [
+ into_json(txid)?,
+ into_json(true)?,
+ opt_into_json(block_hash)?,
+ ];
self.call("getrawtransaction", handle_defaults(&mut args, &[null()]))
+ .await
}
- fn get_block_filter(
+ async fn get_block_filter(
&self,
block_hash: &bitcoin::BlockHash,
) -> Result {
- self.call("getblockfilter", &[into_json(block_hash)?])
+ self.call("getblockfilter", &[into_json(block_hash)?]).await
}
- fn get_balance(
+ async fn get_balance(
&self,
minconf: Option,
include_watchonly: Option,
) -> Result {
- let mut args = ["*".into(), opt_into_json(minconf)?, opt_into_json(include_watchonly)?];
+ let mut args = [
+ "*".into(),
+ opt_into_json(minconf)?,
+ opt_into_json(include_watchonly)?,
+ ];
Ok(Amount::from_btc(
- self.call("getbalance", handle_defaults(&mut args, &[0.into(), null()]))?,
+ self.call(
+ "getbalance",
+ handle_defaults(&mut args, &[0.into(), null()]),
+ )
+ .await?,
)?)
}
- fn get_balances(&self) -> Result {
- Ok(self.call("getbalances", &[])?)
+ async fn get_balances(&self) -> Result {
+ Ok(self.call("getbalances", &[]).await?)
}
- fn get_received_by_address(&self, address: &Address, minconf: Option) -> Result {
+ async fn get_received_by_address(
+ &self,
+ address: &Address,
+ minconf: Option,
+ ) -> Result {
let mut args = [address.to_string().into(), opt_into_json(minconf)?];
Ok(Amount::from_btc(
- self.call("getreceivedbyaddress", handle_defaults(&mut args, &[null()]))?,
+ self.call(
+ "getreceivedbyaddress",
+ handle_defaults(&mut args, &[null()]),
+ )
+ .await?,
)?)
}
- fn get_transaction(
+ async fn get_transaction(
&self,
txid: &bitcoin::Txid,
include_watchonly: Option,
) -> Result {
let mut args = [into_json(txid)?, opt_into_json(include_watchonly)?];
self.call("gettransaction", handle_defaults(&mut args, &[null()]))
+ .await
}
- fn list_transactions(
+ async fn list_transactions(
&self,
label: Option<&str>,
count: Option,
@@ -516,10 +572,14 @@ pub trait RpcApi: Sized {
opt_into_json(skip)?,
opt_into_json(include_watchonly)?,
];
- self.call("listtransactions", handle_defaults(&mut args, &[10.into(), 0.into(), null()]))
+ self.call(
+ "listtransactions",
+ handle_defaults(&mut args, &[10.into(), 0.into(), null()]),
+ )
+ .await
}
- fn list_since_block(
+ async fn list_since_block(
&self,
blockhash: Option<&bitcoin::BlockHash>,
target_confirmations: Option,
@@ -533,59 +593,93 @@ pub trait RpcApi: Sized {
opt_into_json(include_removed)?,
];
self.call("listsinceblock", handle_defaults(&mut args, &[null()]))
+ .await
}
- fn get_tx_out(
+ async fn get_tx_out(
&self,
txid: &bitcoin::Txid,
vout: u32,
include_mempool: Option,
) -> Result