From 9cad62a577d4e4c13c73ebf25e6e53accb81e43f Mon Sep 17 00:00:00 2001 From: Gregory Hill Date: Sat, 3 Oct 2020 12:07:19 +0100 Subject: [PATCH 1/2] make everything async Signed-off-by: Gregory Hill --- client/Cargo.toml | 6 +- client/examples/retry_client.rs | 19 +- client/examples/test_against_node.rs | 22 +- client/src/client.rs | 644 ++++++++++++++------- client/src/error.rs | 37 +- client/src/jsonrpc.rs | 41 ++ client/src/lib.rs | 5 +- client/src/queryable.rs | 31 +- integration_test/Cargo.toml | 2 + integration_test/src/main.rs | 824 +++++++++++++++++---------- 10 files changed, 1098 insertions(+), 533 deletions(-) create mode 100644 client/src/jsonrpc.rs diff --git a/client/Cargo.toml b/client/Cargo.toml index 60c7df2b..20a81863 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,10 @@ 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" +tokio = { version = "0.2.22", features = ["full"] } +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..8733726d 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 tokio::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> { - let mut args = [into_json(txid)?, into_json(vout)?, opt_into_json(include_mempool)?]; - opt_result(self.call("gettxout", handle_defaults(&mut args, &[null()]))?) + let mut args = [ + into_json(txid)?, + into_json(vout)?, + opt_into_json(include_mempool)?, + ]; + opt_result( + self.call("gettxout", handle_defaults(&mut args, &[null()])) + .await?, + ) } - fn get_tx_out_proof( + async fn get_tx_out_proof( &self, txids: &[bitcoin::Txid], block_hash: Option<&bitcoin::BlockHash>, ) -> Result> { let mut args = [into_json(txids)?, opt_into_json(block_hash)?]; - let hex: String = self.call("gettxoutproof", handle_defaults(&mut args, &[null()]))?; + let hex: String = self + .call("gettxoutproof", handle_defaults(&mut args, &[null()])) + .await?; Ok(FromHex::from_hex(&hex)?) } - fn import_public_key( + async fn import_public_key( &self, pubkey: &PublicKey, label: Option<&str>, rescan: Option, ) -> Result<()> { - let mut args = [pubkey.to_string().into(), opt_into_json(label)?, opt_into_json(rescan)?]; - self.call("importpubkey", handle_defaults(&mut args, &[into_json("")?, null()])) + let mut args = [ + pubkey.to_string().into(), + opt_into_json(label)?, + opt_into_json(rescan)?, + ]; + self.call( + "importpubkey", + handle_defaults(&mut args, &[into_json("")?, null()]), + ) + .await } - fn import_private_key( + async fn import_private_key( &self, privkey: &PrivateKey, label: Option<&str>, rescan: Option, ) -> Result<()> { - let mut args = [privkey.to_string().into(), opt_into_json(label)?, opt_into_json(rescan)?]; - self.call("importprivkey", handle_defaults(&mut args, &[into_json("")?, null()])) + let mut args = [ + privkey.to_string().into(), + opt_into_json(label)?, + opt_into_json(rescan)?, + ]; + self.call( + "importprivkey", + handle_defaults(&mut args, &[into_json("")?, null()]), + ) + .await } - fn import_address( + async fn import_address( &self, address: &Address, label: Option<&str>, rescan: Option, ) -> Result<()> { - let mut args = [address.to_string().into(), opt_into_json(label)?, opt_into_json(rescan)?]; - self.call("importaddress", handle_defaults(&mut args, &[into_json("")?, null()])) + let mut args = [ + address.to_string().into(), + opt_into_json(label)?, + opt_into_json(rescan)?, + ]; + self.call( + "importaddress", + handle_defaults(&mut args, &[into_json("")?, null()]), + ) + .await } - fn import_address_script( + async fn import_address_script( &self, script: &Script, label: Option<&str>, @@ -602,11 +696,12 @@ pub trait RpcApi: Sized { "importaddress", handle_defaults(&mut args, &[into_json("")?, true.into(), null()]), ) + .await } - fn import_multi( + async fn import_multi<'a>( &self, - requests: &[json::ImportMultiRequest], + requests: &'a [json::ImportMultiRequest<'a>], options: Option<&json::ImportMultiOptions>, ) -> Result> { let mut json_requests = Vec::with_capacity(requests.len()); @@ -615,18 +710,21 @@ pub trait RpcApi: Sized { } let mut args = [json_requests.into(), opt_into_json(options)?]; self.call("importmulti", handle_defaults(&mut args, &[null()])) + .await } - fn set_label(&self, address: &Address, label: &str) -> Result<()> { + async fn set_label(&self, address: &Address, label: &str) -> Result<()> { self.call("setlabel", &[address.to_string().into(), label.into()]) + .await } - fn key_pool_refill(&self, new_size: Option) -> Result<()> { + async fn key_pool_refill(&self, new_size: Option) -> Result<()> { let mut args = [opt_into_json(new_size)?]; self.call("keypoolrefill", handle_defaults(&mut args, &[null()])) + .await } - fn list_unspent( + async fn list_unspent( &self, minconf: Option, maxconf: Option, @@ -641,28 +739,37 @@ pub trait RpcApi: Sized { opt_into_json(include_unsafe)?, opt_into_json(query_options)?, ]; - let defaults = [into_json(0)?, into_json(9999999)?, empty_arr(), into_json(true)?, null()]; + let defaults = [ + into_json(0)?, + into_json(9999999)?, + empty_arr(), + into_json(true)?, + null(), + ]; self.call("listunspent", handle_defaults(&mut args, &defaults)) + .await } /// To unlock, use [unlock_unspent]. - fn lock_unspent(&self, outputs: &[OutPoint]) -> Result { + async fn lock_unspent(&self, outputs: &[OutPoint]) -> Result { let outputs: Vec<_> = outputs .into_iter() .map(|o| serde_json::to_value(JsonOutPoint::from(*o)).unwrap()) .collect(); self.call("lockunspent", &[false.into(), outputs.into()]) + .await } - fn unlock_unspent(&self, outputs: &[OutPoint]) -> Result { + async fn unlock_unspent(&self, outputs: &[OutPoint]) -> Result { let outputs: Vec<_> = outputs .into_iter() .map(|o| serde_json::to_value(JsonOutPoint::from(*o)).unwrap()) .collect(); self.call("lockunspent", &[true.into(), outputs.into()]) + .await } - fn list_received_by_address( + async fn list_received_by_address( &self, address_filter: Option<&Address>, minconf: Option, @@ -676,10 +783,14 @@ pub trait RpcApi: Sized { opt_into_json(address_filter)?, ]; let defaults = [1.into(), false.into(), false.into(), null()]; - self.call("listreceivedbyaddress", handle_defaults(&mut args, &defaults)) + self.call( + "listreceivedbyaddress", + handle_defaults(&mut args, &defaults), + ) + .await } - fn create_raw_transaction_hex( + async fn create_raw_transaction_hex( &self, utxos: &[json::CreateRawTransactionInput], outs: &HashMap, @@ -687,7 +798,8 @@ pub trait RpcApi: Sized { replaceable: Option, ) -> Result { let outs_converted = serde_json::Map::from_iter( - outs.iter().map(|(k, v)| (k.clone(), serde_json::Value::from(v.as_btc()))), + outs.iter() + .map(|(k, v)| (k.clone(), serde_json::Value::from(v.as_btc()))), ); let mut args = [ into_json(utxos)?, @@ -696,34 +808,45 @@ pub trait RpcApi: Sized { opt_into_json(replaceable)?, ]; let defaults = [into_json(0i64)?, null()]; - self.call("createrawtransaction", handle_defaults(&mut args, &defaults)) + self.call( + "createrawtransaction", + handle_defaults(&mut args, &defaults), + ) + .await } - fn create_raw_transaction( + async fn create_raw_transaction( &self, utxos: &[json::CreateRawTransactionInput], outs: &HashMap, locktime: Option, replaceable: Option, ) -> Result { - let hex: String = self.create_raw_transaction_hex(utxos, outs, locktime, replaceable)?; + let hex: String = self + .create_raw_transaction_hex(utxos, outs, locktime, replaceable) + .await?; let bytes: Vec = FromHex::from_hex(&hex)?; Ok(bitcoin::consensus::encode::deserialize(&bytes)?) } - fn fund_raw_transaction( + async fn fund_raw_transaction( &self, tx: R, options: Option<&json::FundRawTransactionOptions>, is_witness: Option, ) -> Result { - let mut args = [tx.raw_hex().into(), opt_into_json(options)?, opt_into_json(is_witness)?]; + let mut args = [ + tx.raw_hex().into(), + opt_into_json(options)?, + opt_into_json(is_witness)?, + ]; let defaults = [empty_obj(), null()]; self.call("fundrawtransaction", handle_defaults(&mut args, &defaults)) + .await } #[deprecated] - fn sign_raw_transaction( + async fn sign_raw_transaction( &self, tx: R, utxos: Option<&[json::SignRawTransactionInput]>, @@ -738,20 +861,29 @@ pub trait RpcApi: Sized { ]; let defaults = [empty_arr(), empty_arr(), null()]; self.call("signrawtransaction", handle_defaults(&mut args, &defaults)) + .await } - fn sign_raw_transaction_with_wallet( + async fn sign_raw_transaction_with_wallet( &self, tx: R, utxos: Option<&[json::SignRawTransactionInput]>, sighash_type: Option, ) -> Result { - let mut args = [tx.raw_hex().into(), opt_into_json(utxos)?, opt_into_json(sighash_type)?]; + let mut args = [ + tx.raw_hex().into(), + opt_into_json(utxos)?, + opt_into_json(sighash_type)?, + ]; let defaults = [empty_arr(), null()]; - self.call("signrawtransactionwithwallet", handle_defaults(&mut args, &defaults)) + self.call( + "signrawtransactionwithwallet", + handle_defaults(&mut args, &defaults), + ) + .await } - fn sign_raw_transaction_with_key( + async fn sign_raw_transaction_with_key( &self, tx: R, privkeys: &[PrivateKey], @@ -765,83 +897,110 @@ pub trait RpcApi: Sized { opt_into_json(sighash_type)?, ]; let defaults = [empty_arr(), null()]; - self.call("signrawtransactionwithkey", handle_defaults(&mut args, &defaults)) + self.call( + "signrawtransactionwithkey", + handle_defaults(&mut args, &defaults), + ) + .await } - fn test_mempool_accept( + async fn test_mempool_accept( &self, rawtxs: &[R], ) -> Result> { - let hexes: Vec = - rawtxs.to_vec().into_iter().map(|r| r.raw_hex().into()).collect(); - self.call("testmempoolaccept", &[hexes.into()]) + let hexes: Vec = rawtxs + .to_vec() + .into_iter() + .map(|r| r.raw_hex().into()) + .collect(); + self.call("testmempoolaccept", &[hexes.into()]).await } - fn stop(&self) -> Result { - self.call("stop", &[]) + async fn stop(&self) -> Result { + self.call("stop", &[]).await } - fn verify_message( + async fn verify_message( &self, address: &Address, signature: &Signature, message: &str, ) -> Result { - let args = [address.to_string().into(), signature.to_string().into(), into_json(message)?]; - self.call("verifymessage", &args) + let args = [ + address.to_string().into(), + signature.to_string().into(), + into_json(message)?, + ]; + self.call("verifymessage", &args).await } /// Generate new address under own control - fn get_new_address( + async fn get_new_address( &self, label: Option<&str>, address_type: Option, ) -> Result
{ - self.call("getnewaddress", &[opt_into_json(label)?, opt_into_json(address_type)?]) + self.call( + "getnewaddress", + &[opt_into_json(label)?, opt_into_json(address_type)?], + ) + .await } - fn get_address_info(&self, address: &Address) -> Result { + async fn get_address_info(&self, address: &Address) -> Result { self.call("getaddressinfo", &[address.to_string().into()]) + .await } /// Mine `block_num` blocks and pay coinbase to `address` /// /// Returns hashes of the generated blocks - fn generate_to_address( + async fn generate_to_address( &self, block_num: u64, address: &Address, ) -> Result> { - self.call("generatetoaddress", &[block_num.into(), address.to_string().into()]) + self.call( + "generatetoaddress", + &[block_num.into(), address.to_string().into()], + ) + .await } /// Mine up to block_num blocks immediately (before the RPC call returns) /// to an address in the wallet. - fn generate(&self, block_num: u64, maxtries: Option) -> Result> { + async fn generate( + &self, + block_num: u64, + maxtries: Option, + ) -> Result> { self.call("generate", &[block_num.into(), opt_into_json(maxtries)?]) + .await } /// Mark a block as invalid by `block_hash` - fn invalidate_block(&self, block_hash: &bitcoin::BlockHash) -> Result<()> { + async fn invalidate_block(&self, block_hash: &bitcoin::BlockHash) -> Result<()> { self.call("invalidateblock", &[into_json(block_hash)?]) + .await } /// Mark a block as valid by `block_hash` - fn reconsider_block(&self, block_hash: &bitcoin::BlockHash) -> Result<()> { + async fn reconsider_block(&self, block_hash: &bitcoin::BlockHash) -> Result<()> { self.call("reconsiderblock", &[into_json(block_hash)?]) + .await } /// Get txids of all transactions in a memory pool - fn get_raw_mempool(&self) -> Result> { - self.call("getrawmempool", &[]) + async fn get_raw_mempool(&self) -> Result> { + self.call("getrawmempool", &[]).await } /// Get mempool data for given transaction - fn get_mempool_entry(&self, txid: &bitcoin::Txid) -> Result { - self.call("getmempoolentry", &[into_json(txid)?]) + async fn get_mempool_entry(&self, txid: &bitcoin::Txid) -> Result { + self.call("getmempoolentry", &[into_json(txid)?]).await } - fn send_to_address( + async fn send_to_address( &self, address: &Address, amount: Amount, @@ -866,17 +1025,25 @@ pub trait RpcApi: Sized { "sendtoaddress", handle_defaults( &mut args, - &["".into(), "".into(), false.into(), false.into(), 6.into(), null()], + &[ + "".into(), + "".into(), + false.into(), + false.into(), + 6.into(), + null(), + ], ), ) + .await } /// Returns data about each connected network node as an array of /// [`PeerInfo`][] /// /// [`PeerInfo`]: net/struct.PeerInfo.html - fn get_peer_info(&self) -> Result> { - self.call("getpeerinfo", &[]) + async fn get_peer_info(&self) -> Result> { + self.call("getpeerinfo", &[]).await } /// Requests that a ping be sent to all other nodes, to measure ping @@ -887,21 +1054,23 @@ pub trait RpcApi: Sized { /// /// Ping command is handled in queue with all other commands, so it /// measures processing backlog, not just network ping. - fn ping(&self) -> Result<()> { - self.call("ping", &[]) + async fn ping(&self) -> Result<()> { + self.call("ping", &[]).await } - fn send_raw_transaction(&self, tx: R) -> Result { + async fn send_raw_transaction(&self, tx: R) -> Result { self.call("sendrawtransaction", &[tx.raw_hex().into()]) + .await } - fn estimate_smart_fee( + async fn estimate_smart_fee( &self, conf_target: u16, estimate_mode: Option, ) -> Result { let mut args = [into_json(conf_target)?, opt_into_json(estimate_mode)?]; self.call("estimatesmartfee", handle_defaults(&mut args, &[null()])) + .await } /// Waits for a specific new block and returns useful info about it. @@ -911,8 +1080,8 @@ pub trait RpcApi: Sized { /// /// 1. `timeout`: Time in milliseconds to wait for a response. 0 /// indicates no timeout. - fn wait_for_new_block(&self, timeout: u64) -> Result { - self.call("waitfornewblock", &[into_json(timeout)?]) + async fn wait_for_new_block(&self, timeout: u64) -> Result { + self.call("waitfornewblock", &[into_json(timeout)?]).await } /// Waits for a specific new block and returns useful info about it. @@ -923,16 +1092,16 @@ pub trait RpcApi: Sized { /// 1. `blockhash`: Block hash to wait for. /// 2. `timeout`: Time in milliseconds to wait for a response. 0 /// indicates no timeout. - fn wait_for_block( + async fn wait_for_block( &self, blockhash: &bitcoin::BlockHash, timeout: u64, ) -> Result { let args = [into_json(blockhash)?, into_json(timeout)?]; - self.call("waitforblock", &args) + self.call("waitforblock", &args).await } - fn wallet_create_funded_psbt( + async fn wallet_create_funded_psbt( &self, inputs: &[json::CreateRawTransactionInput], outputs: &HashMap, @@ -941,7 +1110,9 @@ pub trait RpcApi: Sized { bip32derivs: Option, ) -> Result { let outputs_converted = serde_json::Map::from_iter( - outputs.iter().map(|(k, v)| (k.clone(), serde_json::Value::from(v.as_btc()))), + outputs + .iter() + .map(|(k, v)| (k.clone(), serde_json::Value::from(v.as_btc()))), ); let mut args = [ into_json(inputs)?, @@ -952,29 +1123,44 @@ pub trait RpcApi: Sized { ]; self.call( "walletcreatefundedpsbt", - handle_defaults(&mut args, &[0.into(), serde_json::Map::new().into(), false.into()]), + handle_defaults( + &mut args, + &[0.into(), serde_json::Map::new().into(), false.into()], + ), ) + .await } - fn get_descriptor_info(&self, desc: &str) -> Result { + async fn get_descriptor_info(&self, desc: &str) -> Result { self.call("getdescriptorinfo", &[desc.to_string().into()]) + .await } - fn combine_psbt(&self, psbts: &[String]) -> Result { - self.call("combinepsbt", &[into_json(psbts)?]) + async fn combine_psbt(&self, psbts: &[String]) -> Result { + self.call("combinepsbt", &[into_json(psbts)?]).await } - fn finalize_psbt(&self, psbt: &str, extract: Option) -> Result { + async fn finalize_psbt( + &self, + psbt: &str, + extract: Option, + ) -> Result { let mut args = [into_json(psbt)?, opt_into_json(extract)?]; self.call("finalizepsbt", handle_defaults(&mut args, &[true.into()])) + .await } - fn derive_addresses(&self, descriptor: &str, range: Option<[u32; 2]>) -> Result> { + async fn derive_addresses( + &self, + descriptor: &str, + range: Option<[u32; 2]>, + ) -> Result> { let mut args = [into_json(descriptor)?, opt_into_json(range)?]; self.call("deriveaddresses", handle_defaults(&mut args, &[null()])) + .await } - fn rescan_blockchain( + async fn rescan_blockchain( &self, start_from: Option, stop_height: Option, @@ -986,46 +1172,56 @@ pub trait RpcApi: Sized { pub start_height: usize, pub stop_height: Option, } - let res: Response = - self.call("rescanblockchain", handle_defaults(&mut args, &[0.into(), null()]))?; + let res: Response = self + .call( + "rescanblockchain", + handle_defaults(&mut args, &[0.into(), null()]), + ) + .await?; Ok((res.start_height, res.stop_height)) } /// Returns statistics about the unspent transaction output set. /// This call may take some time. - fn get_tx_out_set_info(&self) -> Result { - self.call("gettxoutsetinfo", &[]) + async fn get_tx_out_set_info(&self) -> Result { + self.call("gettxoutsetinfo", &[]).await } /// Returns information about network traffic, including bytes in, bytes out, /// and current time. - fn get_net_totals(&self) -> Result { - self.call("getnettotals", &[]) + async fn get_net_totals(&self) -> Result { + self.call("getnettotals", &[]).await } /// Returns the estimated network hashes per second based on the last n blocks. - fn get_network_hash_ps(&self, nblocks: Option, height: Option) -> Result { + async fn get_network_hash_ps(&self, nblocks: Option, height: Option) -> Result { let mut args = [opt_into_json(nblocks)?, opt_into_json(height)?]; - self.call("getnetworkhashps", handle_defaults(&mut args, &[null(), null()])) + self.call( + "getnetworkhashps", + handle_defaults(&mut args, &[null(), null()]), + ) + .await } /// Returns the total uptime of the server in seconds - fn uptime(&self) -> Result { - self.call("uptime", &[]) + async fn uptime(&self) -> Result { + self.call("uptime", &[]).await } } /// Client implements a JSON-RPC client for the Bitcoin Core daemon or compatible APIs. pub struct Client { - client: jsonrpc::client::Client, + url: String, + client: reqwest::Client, + nonce: Arc>, } impl fmt::Debug for Client { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "bitcoincore_rpc::Client(jsonrpc::Client(last_nonce={}))", - self.client.last_nonce() + "bitcoincore_rpc::Client(jsonrpc::Client(url={}))", + self.url ) } } @@ -1035,61 +1231,111 @@ impl Client { /// /// Can only return [Err] when using cookie authentication. pub fn new(url: String, auth: Auth) -> Result { + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert( + reqwest::header::CONTENT_TYPE, + reqwest::header::HeaderValue::from_static("application/json"), + ); + let (user, pass) = auth.get_user_pass()?; + + // Set Authorization header + if let Some(ref user) = user { + let mut auth = user.clone(); + auth.push(':'); + if let Some(ref pass) = pass { + auth.push_str(&pass[..]); + } + let value = format!("Basic {}", &base64::encode(auth.as_bytes())); + headers.insert( + "Authorization", + reqwest::header::HeaderValue::from_str(&value).unwrap(), + ); + } + + let client = reqwest::Client::builder() + .default_headers(headers) + .build()?; + Ok(Client { - client: jsonrpc::client::Client::new(url, user, pass), + url, + client, + nonce: Arc::new(Mutex::new(0)), }) } /// Create a new Client. - pub fn from_jsonrpc(client: jsonrpc::client::Client) -> Client { + pub fn from_client(url: String, client: reqwest::Client) -> Client { Client { - client: client, + url, + client, + nonce: Arc::new(Mutex::new(0)), } } /// Get the underlying JSONRPC client. - pub fn get_jsonrpc_client(&self) -> &jsonrpc::client::Client { + pub fn get_client(&self) -> &reqwest::Client { &self.client } + + /// Get the last nonce. + pub async fn last_nonce(&self) -> u64 { + *self.nonce.lock().await + } } +#[async_trait] impl RpcApi for Client { /// Call an `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 { - let req = self.client.build_request(&cmd, &args); + let mut nonce = self.nonce.lock().await; + *nonce += 1; + + let req = JsonRpcRequest { + method: cmd, + params: args, + id: From::from(*nonce), + jsonrpc: Some("2.0"), + }; + if log_enabled!(Debug) { debug!(target: "bitcoincore_rpc", "JSON-RPC request: {} {}", cmd, serde_json::Value::from(args)); } - let resp = self.client.send_request(&req).map_err(Error::from); + let resp = self + .client + .post(&self.url) + .json(&req) + .send() + .await? + .json::() + .await?; + + if resp.jsonrpc != None && resp.jsonrpc != Some(From::from("2.0")) { + return Err(Error::VersionMismatch); + } + if resp.id != req.id { + return Err(Error::NonceMismatch); + } + log_response(cmd, &resp); - Ok(resp?.into_result()?) + Ok(resp.into_result()?) } } -fn log_response(cmd: &str, resp: &Result) { +fn log_response(cmd: &str, resp: &JsonRpcResponse) { if log_enabled!(Warn) || log_enabled!(Debug) || log_enabled!(Trace) { - match resp { - Err(ref e) => { - if log_enabled!(Debug) { - debug!(target: "bitcoincore_rpc", "JSON-RPC failed parsing reply of {}: {:?}", cmd, e); - } - } - Ok(ref resp) => { - if let Some(ref e) = resp.error { - if log_enabled!(Debug) { - debug!(target: "bitcoincore_rpc", "JSON-RPC error for {}: {:?}", cmd, e); - } - } else if log_enabled!(Trace) { - let result = resp.result.as_ref().unwrap_or(&serde_json::Value::Null); - trace!(target: "bitcoincore_rpc", "JSON-RPC response for {}: {}", cmd, result); - } + if let Some(ref e) = resp.error { + if log_enabled!(Debug) { + debug!(target: "bitcoincore_rpc", "JSON-RPC error for {}: {:?}", cmd, e); } + } else if log_enabled!(Trace) { + let result = resp.result.as_ref().unwrap_or(&serde_json::Value::Null); + trace!(target: "bitcoincore_rpc", "JSON-RPC response for {}: {}", cmd, result); } } } @@ -1100,16 +1346,22 @@ mod tests { use bitcoin; use serde_json; - #[test] - fn test_raw_tx() { + #[tokio::test] + async fn test_raw_tx() { use bitcoin::consensus::encode; let client = Client::new("http://localhost/".into(), Auth::None).unwrap(); let tx: bitcoin::Transaction = encode::deserialize(&Vec::::from_hex("0200000001586bd02815cf5faabfec986a4e50d25dbee089bd2758621e61c5fab06c334af0000000006b483045022100e85425f6d7c589972ee061413bcf08dc8c8e589ce37b217535a42af924f0e4d602205c9ba9cb14ef15513c9d946fa1c4b797883e748e8c32171bdf6166583946e35c012103dae30a4d7870cd87b45dd53e6012f71318fdd059c1c2623b8cc73f8af287bb2dfeffffff021dc4260c010000001976a914f602e88b2b5901d8aab15ebe4a97cf92ec6e03b388ac00e1f505000000001976a914687ffeffe8cf4e4c038da46a9b1d37db385a472d88acfd211500").unwrap()).unwrap(); - assert!(client.send_raw_transaction(&tx).is_err()); - assert!(client.send_raw_transaction(&encode::serialize(&tx)).is_err()); - assert!(client.send_raw_transaction("deadbeef").is_err()); - assert!(client.send_raw_transaction("deadbeef".to_owned()).is_err()); + assert!(client.send_raw_transaction(&tx).await.is_err()); + assert!(client + .send_raw_transaction(&encode::serialize(&tx)) + .await + .is_err()); + assert!(client.send_raw_transaction("deadbeef").await.is_err()); + assert!(client + .send_raw_transaction("deadbeef".to_owned()) + .await + .is_err()); } fn test_handle_defaults_inner() -> Result<()> { diff --git a/client/src/error.rs b/client/src/error.rs index e90980c9..acdb42cc 100644 --- a/client/src/error.rs +++ b/client/src/error.rs @@ -10,16 +10,17 @@ use std::{error, fmt, io}; -use bitcoin; -use bitcoin::hashes::hex; -use bitcoin::secp256k1; -use jsonrpc; +use crate::bitcoin; +use crate::bitcoin::hashes::hex; +use crate::bitcoin::secp256k1; +use serde::{Deserialize, Serialize}; use serde_json; /// The error type for errors produced in this library. #[derive(Debug)] pub enum Error { - JsonRpc(jsonrpc::error::Error), + Http(reqwest::Error), + JsonRpc(JsonRpcError), Hex(hex::Error), Json(serde_json::error::Error), BitcoinSerialization(bitcoin::consensus::encode::Error), @@ -29,11 +30,13 @@ pub enum Error { InvalidCookieFile, /// The JSON result had an unexpected structure. UnexpectedStructure, + VersionMismatch, + NonceMismatch, } -impl From for Error { - fn from(e: jsonrpc::error::Error) -> Error { - Error::JsonRpc(e) +impl From for Error { + fn from(e: reqwest::Error) -> Error { + Error::Http(e) } } @@ -76,7 +79,8 @@ impl From for Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Error::JsonRpc(ref e) => write!(f, "JSON-RPC error: {}", e), + Error::Http(ref e) => write!(f, "Reqwest error: {}", e), + Error::JsonRpc(ref e) => write!(f, "JSON-RPC error: {:?}", e), Error::Hex(ref e) => write!(f, "hex decode error: {}", e), Error::Json(ref e) => write!(f, "JSON error: {}", e), Error::BitcoinSerialization(ref e) => write!(f, "Bitcoin serialization error: {}", e), @@ -85,6 +89,8 @@ impl fmt::Display for Error { Error::InvalidAmount(ref e) => write!(f, "invalid amount: {}", e), Error::InvalidCookieFile => write!(f, "invalid cookie file"), Error::UnexpectedStructure => write!(f, "the JSON result had an unexpected structure"), + Error::NonceMismatch => write!(f, "Nonce of response did not match nonce of request"), + Error::VersionMismatch => write!(f, "`jsonrpc` field set to non-\"2.0\""), } } } @@ -96,7 +102,7 @@ impl error::Error for Error { fn cause(&self) -> Option<&error::Error> { match *self { - Error::JsonRpc(ref e) => Some(e), + Error::JsonRpc(ref e) => None, Error::Hex(ref e) => Some(e), Error::Json(ref e) => Some(e), Error::BitcoinSerialization(ref e) => Some(e), @@ -106,3 +112,14 @@ impl error::Error for Error { } } } + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +/// A JSONRPC error object +pub struct JsonRpcError { + /// The integer identifier of the error + pub code: i32, + /// A string describing the error + pub message: String, + /// Additional data specific to the error + pub data: Option, +} diff --git a/client/src/jsonrpc.rs b/client/src/jsonrpc.rs new file mode 100644 index 00000000..95eeb754 --- /dev/null +++ b/client/src/jsonrpc.rs @@ -0,0 +1,41 @@ +use crate::{Error, JsonRpcError}; +use serde::{Deserialize, Serialize}; +use serde_json; + +/// Types re-declared from [rust-jsonrpc](https://github.com/apoelstra/rust-jsonrpc/blob/master/src/lib.rs) + +#[derive(Debug, Clone, PartialEq, Serialize)] +/// A JSONRPC request object +pub struct JsonRpcRequest<'a, 'b> { + /// The name of the RPC call + pub method: &'a str, + /// Parameters to the RPC call + pub params: &'b [serde_json::Value], + /// Identifier for this Request, which should appear in the response + pub id: serde_json::Value, + /// jsonrpc field, MUST be "2.0" + pub jsonrpc: Option<&'a str>, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +/// A JSONRPC response object +pub struct JsonRpcResponse { + /// A result if there is one, or null + pub result: Option, + /// An error if there is one, or null + pub error: Option, + /// Identifier for this Request, which should match that of the request + pub id: serde_json::Value, + /// jsonrpc field, MUST be "2.0" + pub jsonrpc: Option, +} + +impl JsonRpcResponse { + pub fn into_result(self) -> Result { + if let Some(e) = self.error { + return Err(Error::JsonRpc(e)); + } + + serde_json::from_value(self.result.unwrap_or(serde_json::Value::Null)).map_err(Error::Json) + } +} diff --git a/client/src/lib.rs b/client/src/lib.rs index d1dd7b37..2ba704a4 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -23,7 +23,7 @@ extern crate log; extern crate serde; extern crate serde_json; -pub extern crate jsonrpc; +pub extern crate reqwest; pub extern crate bitcoincore_rpc_json; pub use bitcoincore_rpc_json as json; @@ -31,8 +31,9 @@ pub use json::bitcoin; mod client; mod error; +mod jsonrpc; mod queryable; pub use client::*; -pub use error::Error; +pub use error::{Error, JsonRpcError}; pub use queryable::*; diff --git a/client/src/queryable.rs b/client/src/queryable.rs index b221829e..260745ca 100644 --- a/client/src/queryable.rs +++ b/client/src/queryable.rs @@ -8,46 +8,51 @@ // If not, see . // -use bitcoin; +use crate::bitcoin; use serde_json; -use client::Result; -use client::RpcApi; +use crate::client::Result; +use crate::client::RpcApi; +use async_trait::async_trait; /// A type that can be queried from Bitcoin Core. +#[async_trait] pub trait Queryable: Sized { /// Type of the ID used to query the item. type Id; /// Query the item using `rpc` and convert to `Self`. - fn query(rpc: &C, id: &Self::Id) -> Result; + async fn query(rpc: &C, id: &Self::Id) -> Result; } -impl Queryable for bitcoin::blockdata::block::Block { +#[async_trait] +impl Queryable for bitcoin::blockdata::block::Block { type Id = bitcoin::BlockHash; - fn query(rpc: &C, id: &Self::Id) -> Result { + async fn query(rpc: &C, id: &Self::Id) -> Result { let rpc_name = "getblock"; - let hex: String = rpc.call(rpc_name, &[serde_json::to_value(id)?, 0.into()])?; + let hex: String = rpc.call(rpc_name, &[serde_json::to_value(id)?, 0.into()]).await?; let bytes: Vec = bitcoin::hashes::hex::FromHex::from_hex(&hex)?; Ok(bitcoin::consensus::encode::deserialize(&bytes)?) } } -impl Queryable for bitcoin::blockdata::transaction::Transaction { +#[async_trait] +impl Queryable for bitcoin::blockdata::transaction::Transaction { type Id = bitcoin::Txid; - fn query(rpc: &C, id: &Self::Id) -> Result { + async fn query(rpc: &C, id: &Self::Id) -> Result { let rpc_name = "getrawtransaction"; - let hex: String = rpc.call(rpc_name, &[serde_json::to_value(id)?])?; + let hex: String = rpc.call(rpc_name, &[serde_json::to_value(id)?]).await?; let bytes: Vec = bitcoin::hashes::hex::FromHex::from_hex(&hex)?; Ok(bitcoin::consensus::encode::deserialize(&bytes)?) } } -impl Queryable for Option<::json::GetTxOutResult> { +#[async_trait] +impl Queryable for Option { type Id = bitcoin::OutPoint; - fn query(rpc: &C, id: &Self::Id) -> Result { - rpc.get_tx_out(&id.txid, id.vout, Some(true)) + async fn query(rpc: &C, id: &Self::Id) -> Result { + rpc.get_tx_out(&id.txid, id.vout, Some(true)).await } } diff --git a/integration_test/Cargo.toml b/integration_test/Cargo.toml index 5561454e..dee772aa 100644 --- a/integration_test/Cargo.toml +++ b/integration_test/Cargo.toml @@ -2,8 +2,10 @@ name = "integration_test" version = "0.1.0" authors = ["Steven Roose "] +edition = "2018" [dependencies] bitcoincore-rpc = { path = "../client" } lazy_static = "1.4.0" bitcoin = { version = "0.23", features = [ "use-serde", "rand" ] } +tokio = { version = "0.2.22", features = ["full"] } diff --git a/integration_test/src/main.rs b/integration_test/src/main.rs index b786abc8..5a508dee 100644 --- a/integration_test/src/main.rs +++ b/integration_test/src/main.rs @@ -10,15 +10,15 @@ #![deny(unused)] -extern crate bitcoin; -extern crate bitcoincore_rpc; +use bitcoin; +use bitcoincore_rpc; #[macro_use] extern crate lazy_static; use std::collections::HashMap; use bitcoincore_rpc::json; -use bitcoincore_rpc::jsonrpc::error::Error as JsonRpcError; +// use bitcoincore_rpc::reqwest::Error as JsonRpcError; use bitcoincore_rpc::{Auth, Client, Error, RpcApi}; use bitcoin::consensus::encode::{deserialize, serialize}; @@ -43,9 +43,13 @@ lazy_static! { /// Assert that the call returns a "deprecated" error. macro_rules! assert_deprecated { ($call:expr) => { - match $call.unwrap_err() { - Error::JsonRpc(JsonRpcError::Rpc(e)) if e.code == -32 => {} - e => panic!("expected deprecated error for {}, got: {}", stringify!($call), e), + match $call.await.unwrap_err() { + Error::JsonRpc(e) if e.code == -32 => {} + e => panic!( + "expected deprecated error for {}, got: {}", + stringify!($call), + e + ), } }; } @@ -53,9 +57,13 @@ macro_rules! assert_deprecated { /// Assert that the call returns a "method not found" error. macro_rules! assert_not_found { ($call:expr) => { - match $call.unwrap_err() { - Error::JsonRpc(JsonRpcError::Rpc(e)) if e.code == -32601 => {} - e => panic!("expected method not found error for {}, got: {}", stringify!($call), e), + match $call.await.unwrap_err() { + Error::JsonRpc(e) if e.code == -32601 => {} + e => panic!( + "expected method not found error for {}, got: {}", + stringify!($call), + e + ), } }; } @@ -89,68 +97,69 @@ fn get_auth() -> bitcoincore_rpc::Auth { }; } -fn main() { +#[tokio::main] +async fn main() { let rpc_url = get_rpc_url(); let auth = get_auth(); let cl = Client::new(rpc_url, auth).unwrap(); - test_get_network_info(&cl); - unsafe { VERSION = cl.version().unwrap() }; + test_get_network_info(&cl).await; + unsafe { VERSION = cl.version().await.unwrap() }; println!("Version: {}", version()); - test_get_mining_info(&cl); - test_get_blockchain_info(&cl); - test_get_new_address(&cl); - test_dump_private_key(&cl); - test_generate(&cl); - test_get_balance_generate_to_address(&cl); - test_get_balances_generate_to_address(&cl); - test_get_best_block_hash(&cl); - test_get_block_count(&cl); - test_get_block_hash(&cl); - test_get_block(&cl); - test_get_block_header_get_block_header_info(&cl); - test_get_address_info(&cl); - test_set_label(&cl); - test_send_to_address(&cl); - test_get_received_by_address(&cl); - test_list_unspent(&cl); - test_get_difficulty(&cl); - test_get_connection_count(&cl); - test_get_raw_transaction(&cl); - test_get_raw_mempool(&cl); - test_get_transaction(&cl); - test_list_transactions(&cl); - test_list_since_block(&cl); - test_get_tx_out(&cl); - test_get_tx_out_proof(&cl); - test_get_mempool_entry(&cl); - test_lock_unspent_unlock_unspent(&cl); - test_get_block_filter(&cl); - test_sign_raw_transaction_with_send_raw_transaction(&cl); - test_invalidate_block_reconsider_block(&cl); - test_key_pool_refill(&cl); - test_create_raw_transaction(&cl); - test_fund_raw_transaction(&cl); - test_test_mempool_accept(&cl); - test_wallet_create_funded_psbt(&cl); - test_combine_psbt(&cl); - test_finalize_psbt(&cl); - test_list_received_by_address(&cl); - test_import_public_key(&cl); - test_import_priv_key(&cl); - test_import_address(&cl); - test_import_address_script(&cl); - test_estimate_smart_fee(&cl); - test_ping(&cl); - test_get_peer_info(&cl); - test_rescan_blockchain(&cl); - test_create_wallet(&cl); - test_get_tx_out_set_info(&cl); - test_get_net_totals(&cl); - test_get_network_hash_ps(&cl); - test_uptime(&cl); + test_get_mining_info(&cl).await; + test_get_blockchain_info(&cl).await; + test_get_new_address(&cl).await; + test_dump_private_key(&cl).await; + test_generate(&cl).await; + test_get_balance_generate_to_address(&cl).await; + test_get_balances_generate_to_address(&cl).await; + test_get_best_block_hash(&cl).await; + test_get_block_count(&cl).await; + test_get_block_hash(&cl).await; + test_get_block(&cl).await; + test_get_block_header_get_block_header_info(&cl).await; + test_get_address_info(&cl).await; + test_set_label(&cl).await; + test_send_to_address(&cl).await; + test_get_received_by_address(&cl).await; + test_list_unspent(&cl).await; + test_get_difficulty(&cl).await; + test_get_connection_count(&cl).await; + test_get_raw_transaction(&cl).await; + test_get_raw_mempool(&cl).await; + test_get_transaction(&cl).await; + test_list_transactions(&cl).await; + test_list_since_block(&cl).await; + test_get_tx_out(&cl).await; + test_get_tx_out_proof(&cl).await; + test_get_mempool_entry(&cl).await; + test_lock_unspent_unlock_unspent(&cl).await; + test_get_block_filter(&cl).await; + test_sign_raw_transaction_with_send_raw_transaction(&cl).await; + test_invalidate_block_reconsider_block(&cl).await; + test_key_pool_refill(&cl).await; + test_create_raw_transaction(&cl).await; + test_fund_raw_transaction(&cl).await; + test_test_mempool_accept(&cl).await; + test_wallet_create_funded_psbt(&cl).await; + test_combine_psbt(&cl).await; + test_finalize_psbt(&cl).await; + test_list_received_by_address(&cl).await; + test_import_public_key(&cl).await; + test_import_priv_key(&cl).await; + test_import_address(&cl).await; + test_import_address_script(&cl).await; + test_estimate_smart_fee(&cl).await; + test_ping(&cl).await; + test_get_peer_info(&cl).await; + test_rescan_blockchain(&cl).await; + test_create_wallet(&cl).await; + test_get_tx_out_set_info(&cl).await; + test_get_net_totals(&cl).await; + test_get_network_hash_ps(&cl).await; + test_uptime(&cl).await; //TODO import_multi( //TODO verify_message( //TODO wait_for_new_block(&self, timeout: u64) -> Result { @@ -163,44 +172,56 @@ fn main() { //TODO load_wallet(&self, wallet: &str) -> Result { //TODO unload_wallet(&self, wallet: Option<&str>) -> Result<()> { //TODO backup_wallet(&self, destination: Option<&str>) -> Result<()> { - test_stop(cl); + test_stop(cl).await; } -fn test_get_network_info(cl: &Client) { - let _ = cl.get_network_info().unwrap(); +async fn test_get_network_info(cl: &Client) { + let _ = cl.get_network_info().await.unwrap(); } -fn test_get_mining_info(cl: &Client) { - let _ = cl.get_mining_info().unwrap(); +async fn test_get_mining_info(cl: &Client) { + let _ = cl.get_mining_info().await.unwrap(); } -fn test_get_blockchain_info(cl: &Client) { - let info = cl.get_blockchain_info().unwrap(); +async fn test_get_blockchain_info(cl: &Client) { + let info = cl.get_blockchain_info().await.unwrap(); assert_eq!(&info.chain, "regtest"); } -fn test_get_new_address(cl: &Client) { - let addr = cl.get_new_address(None, Some(json::AddressType::Legacy)).unwrap(); +async fn test_get_new_address(cl: &Client) { + let addr = cl + .get_new_address(None, Some(json::AddressType::Legacy)) + .await + .unwrap(); assert_eq!(addr.address_type(), Some(bitcoin::AddressType::P2pkh)); - let addr = cl.get_new_address(None, Some(json::AddressType::Bech32)).unwrap(); + let addr = cl + .get_new_address(None, Some(json::AddressType::Bech32)) + .await + .unwrap(); assert_eq!(addr.address_type(), Some(bitcoin::AddressType::P2wpkh)); - let addr = cl.get_new_address(None, Some(json::AddressType::P2shSegwit)).unwrap(); + let addr = cl + .get_new_address(None, Some(json::AddressType::P2shSegwit)) + .await + .unwrap(); assert_eq!(addr.address_type(), Some(bitcoin::AddressType::P2sh)); } -fn test_dump_private_key(cl: &Client) { - let addr = cl.get_new_address(None, Some(json::AddressType::Bech32)).unwrap(); - let sk = cl.dump_private_key(&addr).unwrap(); +async fn test_dump_private_key(cl: &Client) { + let addr = cl + .get_new_address(None, Some(json::AddressType::Bech32)) + .await + .unwrap(); + let sk = cl.dump_private_key(&addr).await.unwrap(); assert_eq!(addr, Address::p2wpkh(&sk.public_key(&SECP), *NET)); } -fn test_generate(cl: &Client) { +async fn test_generate(cl: &Client) { if version() < 180000 { - let blocks = cl.generate(4, None).unwrap(); + let blocks = cl.generate(4, None).await.unwrap(); assert_eq!(blocks.len(), 4); - let blocks = cl.generate(6, Some(45)).unwrap(); + let blocks = cl.generate(6, Some(45)).await.unwrap(); assert_eq!(blocks.len(), 6); } else if version() < 190000 { assert_deprecated!(cl.generate(5, None)); @@ -209,55 +230,67 @@ fn test_generate(cl: &Client) { } } -fn test_get_balance_generate_to_address(cl: &Client) { - let initial = cl.get_balance(None, None).unwrap(); +async fn test_get_balance_generate_to_address(cl: &Client) { + let initial = cl.get_balance(None, None).await.unwrap(); - let blocks = cl.generate_to_address(500, &cl.get_new_address(None, None).unwrap()).unwrap(); + let blocks = cl + .generate_to_address(500, &cl.get_new_address(None, None).await.unwrap()) + .await + .unwrap(); assert_eq!(blocks.len(), 500); - assert_ne!(cl.get_balance(None, None).unwrap(), initial); + assert_ne!(cl.get_balance(None, None).await.unwrap(), initial); } -fn test_get_balances_generate_to_address(cl: &Client) { +async fn test_get_balances_generate_to_address(cl: &Client) { if version() >= 190000 { - let initial = cl.get_balances().unwrap(); + let initial = cl.get_balances().await.unwrap(); - let blocks = cl.generate_to_address(500, &cl.get_new_address(None, None).unwrap()).unwrap(); + let blocks = cl + .generate_to_address(500, &cl.get_new_address(None, None).await.unwrap()) + .await + .unwrap(); assert_eq!(blocks.len(), 500); - assert_ne!(cl.get_balances().unwrap(), initial); + assert_ne!(cl.get_balances().await.unwrap(), initial); } } -fn test_get_best_block_hash(cl: &Client) { - let _ = cl.get_best_block_hash().unwrap(); +async fn test_get_best_block_hash(cl: &Client) { + let _ = cl.get_best_block_hash().await.unwrap(); } -fn test_get_block_count(cl: &Client) { - let height = cl.get_block_count().unwrap(); +async fn test_get_block_count(cl: &Client) { + let height = cl.get_block_count().await.unwrap(); assert!(height > 0); } -fn test_get_block_hash(cl: &Client) { - let h = cl.get_block_count().unwrap(); - assert_eq!(cl.get_block_hash(h).unwrap(), cl.get_best_block_hash().unwrap()); +async fn test_get_block_hash(cl: &Client) { + let h = cl.get_block_count().await.unwrap(); + assert_eq!( + cl.get_block_hash(h).await.unwrap(), + cl.get_best_block_hash().await.unwrap() + ); } -fn test_get_block(cl: &Client) { - let tip = cl.get_best_block_hash().unwrap(); - let block = cl.get_block(&tip).unwrap(); - let hex = cl.get_block_hex(&tip).unwrap(); - assert_eq!(block, deserialize(&Vec::::from_hex(&hex).unwrap()).unwrap()); +async fn test_get_block(cl: &Client) { + let tip = cl.get_best_block_hash().await.unwrap(); + let block = cl.get_block(&tip).await.unwrap(); + let hex = cl.get_block_hex(&tip).await.unwrap(); + assert_eq!( + block, + deserialize(&Vec::::from_hex(&hex).unwrap()).unwrap() + ); assert_eq!(hex, serialize(&block).to_hex()); - let tip = cl.get_best_block_hash().unwrap(); - let info = cl.get_block_info(&tip).unwrap(); + let tip = cl.get_best_block_hash().await.unwrap(); + let info = cl.get_block_info(&tip).await.unwrap(); assert_eq!(info.hash, tip); assert_eq!(info.confirmations, 1); } -fn test_get_block_header_get_block_header_info(cl: &Client) { - let tip = cl.get_best_block_hash().unwrap(); - let header = cl.get_block_header(&tip).unwrap(); - let info = cl.get_block_header_info(&tip).unwrap(); +async fn test_get_block_header_get_block_header_info(cl: &Client) { + let tip = cl.get_best_block_hash().await.unwrap(); + let header = cl.get_block_header(&tip).await.unwrap(); + let info = cl.get_block_header_info(&tip).await.unwrap(); assert_eq!(header.bitcoin_hash(), info.hash); assert_eq!(header.version, info.version); assert_eq!(header.merkle_root, info.merkle_root); @@ -266,174 +299,277 @@ fn test_get_block_header_get_block_header_info(cl: &Client) { assert!(info.previous_block_hash.is_some()); } -fn test_get_address_info(cl: &Client) { - let addr = cl.get_new_address(None, Some(json::AddressType::Legacy)).unwrap(); - let info = cl.get_address_info(&addr).unwrap(); +async fn test_get_address_info(cl: &Client) { + let addr = cl + .get_new_address(None, Some(json::AddressType::Legacy)) + .await + .unwrap(); + let info = cl.get_address_info(&addr).await.unwrap(); assert!(!info.is_witness.unwrap()); - let addr = cl.get_new_address(None, Some(json::AddressType::Bech32)).unwrap(); - let info = cl.get_address_info(&addr).unwrap(); + let addr = cl + .get_new_address(None, Some(json::AddressType::Bech32)) + .await + .unwrap(); + let info = cl.get_address_info(&addr).await.unwrap(); assert!(!info.witness_program.unwrap().is_empty()); - let addr = cl.get_new_address(None, Some(json::AddressType::P2shSegwit)).unwrap(); - let info = cl.get_address_info(&addr).unwrap(); + let addr = cl + .get_new_address(None, Some(json::AddressType::P2shSegwit)) + .await + .unwrap(); + let info = cl.get_address_info(&addr).await.unwrap(); assert!(!info.hex.unwrap().is_empty()); } -fn test_set_label(cl: &Client) { - let addr = cl.get_new_address(Some("label"), None).unwrap(); - let info = cl.get_address_info(&addr).unwrap(); +async fn test_set_label(cl: &Client) { + let addr = cl.get_new_address(Some("label"), None).await.unwrap(); + let info = cl.get_address_info(&addr).await.unwrap(); assert_eq!(&info.label, "label"); assert_eq!(info.labels[0].name, "label"); - cl.set_label(&addr, "other").unwrap(); - let info = cl.get_address_info(&addr).unwrap(); + cl.set_label(&addr, "other").await.unwrap(); + let info = cl.get_address_info(&addr).await.unwrap(); assert_eq!(&info.label, "other"); assert_eq!(info.labels[0].name, "other"); } -fn test_send_to_address(cl: &Client) { - let addr = cl.get_new_address(None, None).unwrap(); +async fn test_send_to_address(cl: &Client) { + let addr = cl.get_new_address(None, None).await.unwrap(); let est = json::EstimateMode::Conservative; - let _ = cl.send_to_address(&addr, btc(1), Some("cc"), None, None, None, None, None).unwrap(); - let _ = cl.send_to_address(&addr, btc(1), None, Some("tt"), None, None, None, None).unwrap(); - let _ = cl.send_to_address(&addr, btc(1), None, None, Some(true), None, None, None).unwrap(); - let _ = cl.send_to_address(&addr, btc(1), None, None, None, Some(true), None, None).unwrap(); - let _ = cl.send_to_address(&addr, btc(1), None, None, None, None, Some(3), None).unwrap(); - let _ = cl.send_to_address(&addr, btc(1), None, None, None, None, None, Some(est)).unwrap(); -} - -fn test_get_received_by_address(cl: &Client) { - let addr = cl.get_new_address(None, None).unwrap(); - let _ = cl.send_to_address(&addr, btc(1), None, None, None, None, None, None).unwrap(); - assert_eq!(cl.get_received_by_address(&addr, Some(0)).unwrap(), btc(1)); - assert_eq!(cl.get_received_by_address(&addr, Some(1)).unwrap(), btc(0)); - let _ = cl.generate_to_address(7, &cl.get_new_address(None, None).unwrap()).unwrap(); - assert_eq!(cl.get_received_by_address(&addr, Some(6)).unwrap(), btc(1)); - assert_eq!(cl.get_received_by_address(&addr, None).unwrap(), btc(1)); -} - -fn test_list_unspent(cl: &Client) { - let addr = cl.get_new_address(None, None).unwrap(); - let txid = cl.send_to_address(&addr, btc(1), None, None, None, None, None, None).unwrap(); - let unspent = cl.list_unspent(Some(0), None, Some(&[&addr]), None, None).unwrap(); + let _ = cl + .send_to_address(&addr, btc(1), Some("cc"), None, None, None, None, None) + .await + .unwrap(); + let _ = cl + .send_to_address(&addr, btc(1), None, Some("tt"), None, None, None, None) + .await + .unwrap(); + let _ = cl + .send_to_address(&addr, btc(1), None, None, Some(true), None, None, None) + .await + .unwrap(); + let _ = cl + .send_to_address(&addr, btc(1), None, None, None, Some(true), None, None) + .await + .unwrap(); + let _ = cl + .send_to_address(&addr, btc(1), None, None, None, None, Some(3), None) + .await + .unwrap(); + let _ = cl + .send_to_address(&addr, btc(1), None, None, None, None, None, Some(est)) + .await + .unwrap(); +} + +async fn test_get_received_by_address(cl: &Client) { + let addr = cl.get_new_address(None, None).await.unwrap(); + let _ = cl + .send_to_address(&addr, btc(1), None, None, None, None, None, None) + .await + .unwrap(); + assert_eq!( + cl.get_received_by_address(&addr, Some(0)).await.unwrap(), + btc(1) + ); + assert_eq!( + cl.get_received_by_address(&addr, Some(1)).await.unwrap(), + btc(0) + ); + let _ = cl + .generate_to_address(7, &cl.get_new_address(None, None).await.unwrap()) + .await + .unwrap(); + assert_eq!( + cl.get_received_by_address(&addr, Some(6)).await.unwrap(), + btc(1) + ); + assert_eq!( + cl.get_received_by_address(&addr, None).await.unwrap(), + btc(1) + ); +} + +async fn test_list_unspent(cl: &Client) { + let addr = cl.get_new_address(None, None).await.unwrap(); + let txid = cl + .send_to_address(&addr, btc(1), None, None, None, None, None, None) + .await + .unwrap(); + let unspent = cl + .list_unspent(Some(0), None, Some(&[&addr]), None, None) + .await + .unwrap(); assert_eq!(unspent[0].txid, txid); assert_eq!(unspent[0].address.as_ref(), Some(&addr)); assert_eq!(unspent[0].amount, btc(1)); - let txid = cl.send_to_address(&addr, btc(7), None, None, None, None, None, None).unwrap(); + let txid = cl + .send_to_address(&addr, btc(7), None, None, None, None, None, None) + .await + .unwrap(); let options = json::ListUnspentQueryOptions { minimum_amount: Some(btc(7)), maximum_amount: Some(btc(7)), ..Default::default() }; - let unspent = cl.list_unspent(Some(0), None, Some(&[&addr]), None, Some(options)).unwrap(); + let unspent = cl + .list_unspent(Some(0), None, Some(&[&addr]), None, Some(options)) + .await + .unwrap(); assert_eq!(unspent.len(), 1); assert_eq!(unspent[0].txid, txid); assert_eq!(unspent[0].address.as_ref(), Some(&addr)); assert_eq!(unspent[0].amount, btc(7)); } -fn test_get_difficulty(cl: &Client) { - let _ = cl.get_difficulty().unwrap(); +async fn test_get_difficulty(cl: &Client) { + let _ = cl.get_difficulty().await.unwrap(); } -fn test_get_connection_count(cl: &Client) { - let _ = cl.get_connection_count().unwrap(); +async fn test_get_connection_count(cl: &Client) { + let _ = cl.get_connection_count().await.unwrap(); } -fn test_get_raw_transaction(cl: &Client) { - let addr = cl.get_new_address(None, None).unwrap(); - let txid = cl.send_to_address(&addr, btc(1), None, None, None, None, None, None).unwrap(); - let tx = cl.get_raw_transaction(&txid, None).unwrap(); - let hex = cl.get_raw_transaction_hex(&txid, None).unwrap(); - assert_eq!(tx, deserialize(&Vec::::from_hex(&hex).unwrap()).unwrap()); +async fn test_get_raw_transaction(cl: &Client) { + let addr = cl.get_new_address(None, None).await.unwrap(); + let txid = cl + .send_to_address(&addr, btc(1), None, None, None, None, None, None) + .await + .unwrap(); + let tx = cl.get_raw_transaction(&txid, None).await.unwrap(); + let hex = cl.get_raw_transaction_hex(&txid, None).await.unwrap(); + assert_eq!( + tx, + deserialize(&Vec::::from_hex(&hex).unwrap()).unwrap() + ); assert_eq!(hex, serialize(&tx).to_hex()); - let info = cl.get_raw_transaction_info(&txid, None).unwrap(); + let info = cl.get_raw_transaction_info(&txid, None).await.unwrap(); assert_eq!(info.txid, txid); - let blocks = cl.generate_to_address(7, &cl.get_new_address(None, None).unwrap()).unwrap(); - let _ = cl.get_raw_transaction_info(&txid, Some(&blocks[0])).unwrap(); + let blocks = cl + .generate_to_address(7, &cl.get_new_address(None, None).await.unwrap()) + .await + .unwrap(); + let _ = cl + .get_raw_transaction_info(&txid, Some(&blocks[0])) + .await + .unwrap(); } -fn test_get_raw_mempool(cl: &Client) { - let _ = cl.get_raw_mempool().unwrap(); +async fn test_get_raw_mempool(cl: &Client) { + let _ = cl.get_raw_mempool().await.unwrap(); } -fn test_get_transaction(cl: &Client) { - let txid = - cl.send_to_address(&RANDOM_ADDRESS, btc(1), None, None, None, None, None, None).unwrap(); - let tx = cl.get_transaction(&txid, None).unwrap(); +async fn test_get_transaction(cl: &Client) { + let txid = cl + .send_to_address(&RANDOM_ADDRESS, btc(1), None, None, None, None, None, None) + .await + .unwrap(); + let tx = cl.get_transaction(&txid, None).await.unwrap(); assert_eq!(tx.amount, sbtc(-1.0)); assert_eq!(tx.info.txid, txid); let fake = Txid::hash(&[1, 2]); - assert!(cl.get_transaction(&fake, Some(true)).is_err()); + assert!(cl.get_transaction(&fake, Some(true)).await.is_err()); } -fn test_list_transactions(cl: &Client) { - let _ = cl.list_transactions(None, None, None, None).unwrap(); - let _ = cl.list_transactions(Some("l"), None, None, None).unwrap(); - let _ = cl.list_transactions(None, Some(3), None, None).unwrap(); - let _ = cl.list_transactions(None, None, Some(3), None).unwrap(); - let _ = cl.list_transactions(None, None, None, Some(true)).unwrap(); +async fn test_list_transactions(cl: &Client) { + let _ = cl.list_transactions(None, None, None, None).await.unwrap(); + let _ = cl + .list_transactions(Some("l"), None, None, None) + .await + .unwrap(); + let _ = cl + .list_transactions(None, Some(3), None, None) + .await + .unwrap(); + let _ = cl + .list_transactions(None, None, Some(3), None) + .await + .unwrap(); + let _ = cl + .list_transactions(None, None, None, Some(true)) + .await + .unwrap(); } -fn test_list_since_block(cl: &Client) { - let r = cl.list_since_block(None, None, None, None).unwrap(); - assert_eq!(r.lastblock, cl.get_best_block_hash().unwrap()); +async fn test_list_since_block(cl: &Client) { + let r = cl.list_since_block(None, None, None, None).await.unwrap(); + assert_eq!(r.lastblock, cl.get_best_block_hash().await.unwrap()); assert!(!r.transactions.is_empty()); } -fn test_get_tx_out(cl: &Client) { - let txid = - cl.send_to_address(&RANDOM_ADDRESS, btc(1), None, None, None, None, None, None).unwrap(); - let out = cl.get_tx_out(&txid, 0, Some(false)).unwrap(); +async fn test_get_tx_out(cl: &Client) { + let txid = cl + .send_to_address(&RANDOM_ADDRESS, btc(1), None, None, None, None, None, None) + .await + .unwrap(); + let out = cl.get_tx_out(&txid, 0, Some(false)).await.unwrap(); assert!(out.is_none()); - let out = cl.get_tx_out(&txid, 0, Some(true)).unwrap(); + let out = cl.get_tx_out(&txid, 0, Some(true)).await.unwrap(); assert!(out.is_some()); - let _ = cl.get_tx_out(&txid, 0, None).unwrap(); + let _ = cl.get_tx_out(&txid, 0, None).await.unwrap(); } -fn test_get_tx_out_proof(cl: &Client) { - let txid1 = - cl.send_to_address(&RANDOM_ADDRESS, btc(1), None, None, None, None, None, None).unwrap(); - let txid2 = - cl.send_to_address(&RANDOM_ADDRESS, btc(1), None, None, None, None, None, None).unwrap(); - let blocks = cl.generate_to_address(7, &cl.get_new_address(None, None).unwrap()).unwrap(); - let proof = cl.get_tx_out_proof(&[txid1, txid2], Some(&blocks[0])).unwrap(); +async fn test_get_tx_out_proof(cl: &Client) { + let txid1 = cl + .send_to_address(&RANDOM_ADDRESS, btc(1), None, None, None, None, None, None) + .await + .unwrap(); + let txid2 = cl + .send_to_address(&RANDOM_ADDRESS, btc(1), None, None, None, None, None, None) + .await + .unwrap(); + let blocks = cl + .generate_to_address(7, &cl.get_new_address(None, None).await.unwrap()) + .await + .unwrap(); + let proof = cl + .get_tx_out_proof(&[txid1, txid2], Some(&blocks[0])) + .await + .unwrap(); assert!(!proof.is_empty()); } -fn test_get_mempool_entry(cl: &Client) { - let txid = - cl.send_to_address(&RANDOM_ADDRESS, btc(1), None, None, None, None, None, None).unwrap(); - let entry = cl.get_mempool_entry(&txid).unwrap(); +async fn test_get_mempool_entry(cl: &Client) { + let txid = cl + .send_to_address(&RANDOM_ADDRESS, btc(1), None, None, None, None, None, None) + .await + .unwrap(); + let entry = cl.get_mempool_entry(&txid).await.unwrap(); assert!(entry.spent_by.is_empty()); let fake = Txid::hash(&[1, 2]); - assert!(cl.get_mempool_entry(&fake).is_err()); + assert!(cl.get_mempool_entry(&fake).await.is_err()); } -fn test_lock_unspent_unlock_unspent(cl: &Client) { - let addr = cl.get_new_address(None, None).unwrap(); - let txid = cl.send_to_address(&addr, btc(1), None, None, None, None, None, None).unwrap(); +async fn test_lock_unspent_unlock_unspent(cl: &Client) { + let addr = cl.get_new_address(None, None).await.unwrap(); + let txid = cl + .send_to_address(&addr, btc(1), None, None, None, None, None, None) + .await + .unwrap(); - assert!(cl.lock_unspent(&[OutPoint::new(txid, 0)]).unwrap()); - assert!(cl.unlock_unspent(&[OutPoint::new(txid, 0)]).unwrap()); + assert!(cl.lock_unspent(&[OutPoint::new(txid, 0)]).await.unwrap()); + assert!(cl.unlock_unspent(&[OutPoint::new(txid, 0)]).await.unwrap()); } -fn test_get_block_filter(cl: &Client) { - let blocks = cl.generate_to_address(7, &cl.get_new_address(None, None).unwrap()).unwrap(); +async fn test_get_block_filter(cl: &Client) { + let blocks = cl + .generate_to_address(7, &cl.get_new_address(None, None).await.unwrap()) + .await + .unwrap(); if version() >= 190000 { - let _ = cl.get_block_filter(&blocks[0]).unwrap(); + let _ = cl.get_block_filter(&blocks[0]).await.unwrap(); } else { assert_not_found!(cl.get_block_filter(&blocks[0])); } } -fn test_sign_raw_transaction_with_send_raw_transaction(cl: &Client) { +async fn test_sign_raw_transaction_with_send_raw_transaction(cl: &Client) { let sk = PrivateKey { network: Network::Regtest, key: secp256k1::SecretKey::new(&mut secp256k1::rand::thread_rng()), @@ -445,7 +581,10 @@ fn test_sign_raw_transaction_with_send_raw_transaction(cl: &Client) { minimum_amount: Some(btc(2)), ..Default::default() }; - let unspent = cl.list_unspent(Some(6), None, None, None, Some(options)).unwrap(); + let unspent = cl + .list_unspent(Some(6), None, None, None, Some(options)) + .await + .unwrap(); let unspent = unspent.into_iter().nth(0).unwrap(); let tx = Transaction { @@ -473,9 +612,15 @@ fn test_sign_raw_transaction_with_send_raw_transaction(cl: &Client) { redeem_script: None, amount: Some(unspent.amount), }; - let res = cl.sign_raw_transaction_with_wallet(&tx, Some(&[input]), None).unwrap(); + let res = cl + .sign_raw_transaction_with_wallet(&tx, Some(&[input]), None) + .await + .unwrap(); assert!(res.complete); - let txid = cl.send_raw_transaction(&res.transaction().unwrap()).unwrap(); + let txid = cl + .send_raw_transaction(&res.transaction().unwrap()) + .await + .unwrap(); let tx = Transaction { version: 1, @@ -495,29 +640,37 @@ fn test_sign_raw_transaction_with_send_raw_transaction(cl: &Client) { }], }; - let res = - cl.sign_raw_transaction_with_key(&tx, &[sk], None, Some(SigHashType::All.into())).unwrap(); + let res = cl + .sign_raw_transaction_with_key(&tx, &[sk], None, Some(SigHashType::All.into())) + .await + .unwrap(); assert!(res.complete); - let _ = cl.send_raw_transaction(&res.transaction().unwrap()).unwrap(); + let _ = cl + .send_raw_transaction(&res.transaction().unwrap()) + .await + .unwrap(); } -fn test_invalidate_block_reconsider_block(cl: &Client) { - let hash = cl.get_best_block_hash().unwrap(); - cl.invalidate_block(&hash).unwrap(); - cl.reconsider_block(&hash).unwrap(); +async fn test_invalidate_block_reconsider_block(cl: &Client) { + let hash = cl.get_best_block_hash().await.unwrap(); + cl.invalidate_block(&hash).await.unwrap(); + cl.reconsider_block(&hash).await.unwrap(); } -fn test_key_pool_refill(cl: &Client) { - cl.key_pool_refill(Some(100)).unwrap(); - cl.key_pool_refill(None).unwrap(); +async fn test_key_pool_refill(cl: &Client) { + cl.key_pool_refill(Some(100)).await.unwrap(); + cl.key_pool_refill(None).await.unwrap(); } -fn test_create_raw_transaction(cl: &Client) { +async fn test_create_raw_transaction(cl: &Client) { let options = json::ListUnspentQueryOptions { minimum_amount: Some(btc(2)), ..Default::default() }; - let unspent = cl.list_unspent(Some(6), None, None, None, Some(options)).unwrap(); + let unspent = cl + .list_unspent(Some(6), None, None, None, Some(options)) + .await + .unwrap(); let unspent = unspent.into_iter().nth(0).unwrap(); let input = json::CreateRawTransactionInput { @@ -528,15 +681,23 @@ fn test_create_raw_transaction(cl: &Client) { let mut output = HashMap::new(); output.insert(RANDOM_ADDRESS.to_string(), btc(1)); - let tx = - cl.create_raw_transaction(&[input.clone()], &output, Some(500_000), Some(true)).unwrap(); - let hex = cl.create_raw_transaction_hex(&[input], &output, Some(500_000), Some(true)).unwrap(); - assert_eq!(tx, deserialize(&Vec::::from_hex(&hex).unwrap()).unwrap()); + let tx = cl + .create_raw_transaction(&[input.clone()], &output, Some(500_000), Some(true)) + .await + .unwrap(); + let hex = cl + .create_raw_transaction_hex(&[input], &output, Some(500_000), Some(true)) + .await + .unwrap(); + assert_eq!( + tx, + deserialize(&Vec::::from_hex(&hex).unwrap()).unwrap() + ); assert_eq!(hex, serialize(&tx).to_hex()); } -fn test_fund_raw_transaction(cl: &Client) { - let addr = cl.get_new_address(None, None).unwrap(); +async fn test_fund_raw_transaction(cl: &Client) { + let addr = cl.get_new_address(None, None).await.unwrap(); let mut output = HashMap::new(); output.insert(RANDOM_ADDRESS.to_string(), btc(1)); @@ -552,8 +713,14 @@ fn test_fund_raw_transaction(cl: &Client) { conf_target: None, estimate_mode: None, }; - let tx = cl.create_raw_transaction_hex(&[], &output, Some(500_000), Some(true)).unwrap(); - let funded = cl.fund_raw_transaction(tx, Some(&options), Some(false)).unwrap(); + let tx = cl + .create_raw_transaction_hex(&[], &output, Some(500_000), Some(true)) + .await + .unwrap(); + let funded = cl + .fund_raw_transaction(tx, Some(&options), Some(false)) + .await + .unwrap(); let _ = funded.transaction().unwrap(); let options = json::FundRawTransactionOptions { @@ -568,17 +735,26 @@ fn test_fund_raw_transaction(cl: &Client) { conf_target: Some(2), estimate_mode: Some(json::EstimateMode::Conservative), }; - let tx = cl.create_raw_transaction_hex(&[], &output, Some(500_000), Some(true)).unwrap(); - let funded = cl.fund_raw_transaction(tx, Some(&options), Some(false)).unwrap(); + let tx = cl + .create_raw_transaction_hex(&[], &output, Some(500_000), Some(true)) + .await + .unwrap(); + let funded = cl + .fund_raw_transaction(tx, Some(&options), Some(false)) + .await + .unwrap(); let _ = funded.transaction().unwrap(); } -fn test_test_mempool_accept(cl: &Client) { +async fn test_test_mempool_accept(cl: &Client) { let options = json::ListUnspentQueryOptions { minimum_amount: Some(btc(2)), ..Default::default() }; - let unspent = cl.list_unspent(Some(6), None, None, None, Some(options)).unwrap(); + let unspent = cl + .list_unspent(Some(6), None, None, None, Some(options)) + .await + .unwrap(); let unspent = unspent.into_iter().nth(0).unwrap(); let input = json::CreateRawTransactionInput { @@ -589,24 +765,33 @@ fn test_test_mempool_accept(cl: &Client) { let mut output = HashMap::new(); output.insert(RANDOM_ADDRESS.to_string(), unspent.amount - *FEE); - let tx = - cl.create_raw_transaction(&[input.clone()], &output, Some(500_000), Some(false)).unwrap(); - let res = cl.test_mempool_accept(&[&tx]).unwrap(); + let tx = cl + .create_raw_transaction(&[input.clone()], &output, Some(500_000), Some(false)) + .await + .unwrap(); + let res = cl.test_mempool_accept(&[&tx]).await.unwrap(); assert!(!res[0].allowed); assert!(res[0].reject_reason.is_some()); - let signed = - cl.sign_raw_transaction_with_wallet(&tx, None, None).unwrap().transaction().unwrap(); - let res = cl.test_mempool_accept(&[&signed]).unwrap(); + let signed = cl + .sign_raw_transaction_with_wallet(&tx, None, None) + .await + .unwrap() + .transaction() + .unwrap(); + let res = cl.test_mempool_accept(&[&signed]).await.unwrap(); assert!(res[0].allowed, "not allowed: {:?}", res[0].reject_reason); } -fn test_wallet_create_funded_psbt(cl: &Client) { - let addr = cl.get_new_address(None, None).unwrap(); +async fn test_wallet_create_funded_psbt(cl: &Client) { + let addr = cl.get_new_address(None, None).await.unwrap(); let options = json::ListUnspentQueryOptions { minimum_amount: Some(btc(2)), ..Default::default() }; - let unspent = cl.list_unspent(Some(6), None, None, None, Some(options)).unwrap(); + let unspent = cl + .list_unspent(Some(6), None, None, None, Some(options)) + .await + .unwrap(); let unspent = unspent.into_iter().nth(0).unwrap(); let input = json::CreateRawTransactionInput { @@ -637,6 +822,7 @@ fn test_wallet_create_funded_psbt(cl: &Client) { Some(options), Some(true), ) + .await .unwrap(); let options = json::WalletCreateFundedPsbtOptions { @@ -653,16 +839,20 @@ fn test_wallet_create_funded_psbt(cl: &Client) { }; let psbt = cl .wallet_create_funded_psbt(&[input], &output, Some(500_000), Some(options), Some(true)) + .await .unwrap(); assert!(!psbt.psbt.is_empty()); } -fn test_combine_psbt(cl: &Client) { +async fn test_combine_psbt(cl: &Client) { let options = json::ListUnspentQueryOptions { minimum_amount: Some(btc(2)), ..Default::default() }; - let unspent = cl.list_unspent(Some(6), None, None, None, Some(options)).unwrap(); + let unspent = cl + .list_unspent(Some(6), None, None, None, Some(options)) + .await + .unwrap(); let unspent = unspent.into_iter().nth(0).unwrap(); let input = json::CreateRawTransactionInput { txid: unspent.txid, @@ -673,18 +863,25 @@ fn test_combine_psbt(cl: &Client) { output.insert(RANDOM_ADDRESS.to_string(), btc(1)); let psbt1 = cl .wallet_create_funded_psbt(&[input.clone()], &output, Some(500_000), None, Some(true)) + .await .unwrap(); - let psbt = cl.combine_psbt(&[psbt1.psbt.clone(), psbt1.psbt]).unwrap(); + let psbt = cl + .combine_psbt(&[psbt1.psbt.clone(), psbt1.psbt]) + .await + .unwrap(); assert!(!psbt.is_empty()); } -fn test_finalize_psbt(cl: &Client) { +async fn test_finalize_psbt(cl: &Client) { let options = json::ListUnspentQueryOptions { minimum_amount: Some(btc(2)), ..Default::default() }; - let unspent = cl.list_unspent(Some(6), None, None, None, Some(options)).unwrap(); + let unspent = cl + .list_unspent(Some(6), None, None, None, Some(options)) + .await + .unwrap(); let unspent = unspent.into_iter().nth(0).unwrap(); let input = json::CreateRawTransactionInput { txid: unspent.txid, @@ -695,77 +892,110 @@ fn test_finalize_psbt(cl: &Client) { output.insert(RANDOM_ADDRESS.to_string(), btc(1)); let psbt = cl .wallet_create_funded_psbt(&[input.clone()], &output, Some(500_000), None, Some(true)) + .await .unwrap(); - let res = cl.finalize_psbt(&psbt.psbt, Some(true)).unwrap(); + let res = cl.finalize_psbt(&psbt.psbt, Some(true)).await.unwrap(); assert!(!res.complete); //TODO(stevenroose) add sign psbt and test hex field //assert!(res.hex.is_some()); } -fn test_list_received_by_address(cl: &Client) { - let addr = cl.get_new_address(None, None).unwrap(); - let txid = cl.send_to_address(&addr, btc(1), None, None, None, None, None, None).unwrap(); +async fn test_list_received_by_address(cl: &Client) { + let addr = cl.get_new_address(None, None).await.unwrap(); + let txid = cl + .send_to_address(&addr, btc(1), None, None, None, None, None, None) + .await + .unwrap(); - let _ = cl.list_received_by_address(Some(&addr), None, None, None).unwrap(); - let _ = cl.list_received_by_address(Some(&addr), None, Some(true), None).unwrap(); - let _ = cl.list_received_by_address(Some(&addr), None, None, Some(true)).unwrap(); - let _ = cl.list_received_by_address(None, Some(200), None, None).unwrap(); + let _ = cl + .list_received_by_address(Some(&addr), None, None, None) + .await + .unwrap(); + let _ = cl + .list_received_by_address(Some(&addr), None, Some(true), None) + .await + .unwrap(); + let _ = cl + .list_received_by_address(Some(&addr), None, None, Some(true)) + .await + .unwrap(); + let _ = cl + .list_received_by_address(None, Some(200), None, None) + .await + .unwrap(); - let res = cl.list_received_by_address(Some(&addr), Some(0), None, None).unwrap(); + let res = cl + .list_received_by_address(Some(&addr), Some(0), None, None) + .await + .unwrap(); assert_eq!(res[0].txids, vec![txid]); } -fn test_import_public_key(cl: &Client) { +async fn test_import_public_key(cl: &Client) { let sk = PrivateKey { network: Network::Regtest, key: secp256k1::SecretKey::new(&mut secp256k1::rand::thread_rng()), compressed: true, }; - cl.import_public_key(&sk.public_key(&SECP), None, None).unwrap(); - cl.import_public_key(&sk.public_key(&SECP), Some("l"), None).unwrap(); - cl.import_public_key(&sk.public_key(&SECP), None, Some(false)).unwrap(); + cl.import_public_key(&sk.public_key(&SECP), None, None) + .await + .unwrap(); + cl.import_public_key(&sk.public_key(&SECP), Some("l"), None) + .await + .unwrap(); + cl.import_public_key(&sk.public_key(&SECP), None, Some(false)) + .await + .unwrap(); } -fn test_import_priv_key(cl: &Client) { +async fn test_import_priv_key(cl: &Client) { let sk = PrivateKey { network: Network::Regtest, key: secp256k1::SecretKey::new(&mut secp256k1::rand::thread_rng()), compressed: true, }; - cl.import_private_key(&sk, None, None).unwrap(); - cl.import_private_key(&sk, Some("l"), None).unwrap(); - cl.import_private_key(&sk, None, Some(false)).unwrap(); + cl.import_private_key(&sk, None, None).await.unwrap(); + cl.import_private_key(&sk, Some("l"), None).await.unwrap(); + cl.import_private_key(&sk, None, Some(false)).await.unwrap(); } -fn test_import_address(cl: &Client) { +async fn test_import_address(cl: &Client) { let sk = PrivateKey { network: Network::Regtest, key: secp256k1::SecretKey::new(&mut secp256k1::rand::thread_rng()), compressed: true, }; let addr = Address::p2pkh(&sk.public_key(&SECP), Network::Regtest); - cl.import_address(&addr, None, None).unwrap(); - cl.import_address(&addr, Some("l"), None).unwrap(); - cl.import_address(&addr, None, Some(false)).unwrap(); + cl.import_address(&addr, None, None).await.unwrap(); + cl.import_address(&addr, Some("l"), None).await.unwrap(); + cl.import_address(&addr, None, Some(false)).await.unwrap(); } -fn test_import_address_script(cl: &Client) { +async fn test_import_address_script(cl: &Client) { let sk = PrivateKey { network: Network::Regtest, key: secp256k1::SecretKey::new(&mut secp256k1::rand::thread_rng()), compressed: true, }; let addr = Address::p2pkh(&sk.public_key(&SECP), Network::Regtest); - cl.import_address_script(&addr.script_pubkey(), None, None, None).unwrap(); - cl.import_address_script(&addr.script_pubkey(), Some("l"), None, None).unwrap(); - cl.import_address_script(&addr.script_pubkey(), None, Some(false), None).unwrap(); - cl.import_address_script(&addr.script_pubkey(), None, None, Some(true)).unwrap(); + cl.import_address_script(&addr.script_pubkey(), None, None, None) + .await + .unwrap(); + cl.import_address_script(&addr.script_pubkey(), Some("l"), None, None) + .await + .unwrap(); + cl.import_address_script(&addr.script_pubkey(), None, Some(false), None) + .await + .unwrap(); + cl.import_address_script(&addr.script_pubkey(), None, None, Some(true)) + .await + .unwrap(); } -fn test_estimate_smart_fee(cl: &Client) { +async fn test_estimate_smart_fee(cl: &Client) { let mode = json::EstimateMode::Unset; - let res = cl.estimate_smart_fee(3, Some(mode)).unwrap(); + let res = cl.estimate_smart_fee(3, Some(mode)).await.unwrap(); // With a fresh node, we can't get fee estimates. if let Some(errors) = res.errors { @@ -777,30 +1007,37 @@ fn test_estimate_smart_fee(cl: &Client) { } } - assert!(res.fee_rate.is_some(), "no fee estimate available: {:?}", res.errors); + assert!( + res.fee_rate.is_some(), + "no fee estimate available: {:?}", + res.errors + ); assert!(res.fee_rate.unwrap() >= btc(0)); } -fn test_ping(cl: &Client) { - let _ = cl.ping().unwrap(); +async fn test_ping(cl: &Client) { + let _ = cl.ping().await.unwrap(); } -fn test_get_peer_info(cl: &Client) { - let info = cl.get_peer_info().unwrap(); +async fn test_get_peer_info(cl: &Client) { + let info = cl.get_peer_info().await.unwrap(); if info.is_empty() { panic!("No peers are connected so we can't test get_peer_info"); } } -fn test_rescan_blockchain(cl: &Client) { - let count = cl.get_block_count().unwrap() as usize; +async fn test_rescan_blockchain(cl: &Client) { + let count = cl.get_block_count().await.unwrap() as usize; assert!(count > 21); - let (start, stop) = cl.rescan_blockchain(Some(count - 20), Some(count - 1)).unwrap(); + let (start, stop) = cl + .rescan_blockchain(Some(count - 20), Some(count - 1)) + .await + .unwrap(); assert_eq!(start, count - 20); assert_eq!(stop, Some(count - 1)); } -fn test_create_wallet(cl: &Client) { +async fn test_create_wallet(cl: &Client) { let wallet_names = vec!["alice", "bob", "carol", "denise", "emily"]; struct WalletParams<'a> { @@ -861,6 +1098,7 @@ fn test_create_wallet(cl: &Client) { wallet_param.passphrase, wallet_param.avoid_reuse, ) + .await .unwrap(); assert_eq!(result.name, wallet_param.name); @@ -874,7 +1112,7 @@ fn test_create_wallet(cl: &Client) { let wallet_client_url = format!("{}{}{}", get_rpc_url(), "/wallet/", wallet_param.name); let wallet_client = Client::new(wallet_client_url, get_auth()).unwrap(); - let wallet_info = wallet_client.get_wallet_info().unwrap(); + let wallet_info = wallet_client.get_wallet_info().await.unwrap(); assert_eq!(wallet_info.wallet_name, wallet_param.name); @@ -885,12 +1123,14 @@ fn test_create_wallet(cl: &Client) { let has_avoid_reuse = wallet_param.avoid_reuse.unwrap_or(false); assert_eq!(wallet_info.avoid_reuse.unwrap_or(false), has_avoid_reuse); assert_eq!( - wallet_info.scanning.unwrap_or(json::ScanningDetails::NotScanning(false)), + wallet_info + .scanning + .unwrap_or(json::ScanningDetails::NotScanning(false)), json::ScanningDetails::NotScanning(false) ); } - let mut wallet_list = cl.list_wallets().unwrap(); + let mut wallet_list = cl.list_wallets().await.unwrap(); wallet_list.sort(); @@ -901,22 +1141,22 @@ fn test_create_wallet(cl: &Client) { assert!(wallet_list.iter().zip(wallet_names).all(|(a, b)| a == b)); } -fn test_get_tx_out_set_info(cl: &Client) { - cl.get_tx_out_set_info().unwrap(); +async fn test_get_tx_out_set_info(cl: &Client) { + cl.get_tx_out_set_info().await.unwrap(); } -fn test_get_net_totals(cl: &Client) { - cl.get_net_totals().unwrap(); +async fn test_get_net_totals(cl: &Client) { + cl.get_net_totals().await.unwrap(); } -fn test_get_network_hash_ps(cl: &Client) { - cl.get_network_hash_ps(None, None).unwrap(); +async fn test_get_network_hash_ps(cl: &Client) { + cl.get_network_hash_ps(None, None).await.unwrap(); } -fn test_uptime(cl: &Client) { - cl.uptime().unwrap(); +async fn test_uptime(cl: &Client) { + cl.uptime().await.unwrap(); } -fn test_stop(cl: Client) { - println!("Stopping: '{}'", cl.stop().unwrap()); +async fn test_stop(cl: Client) { + println!("Stopping: '{}'", cl.stop().await.unwrap()); } From 9b8b32c44bc82cf3162cf252636ca441f12325d1 Mon Sep 17 00:00:00 2001 From: Gregory Hill Date: Tue, 6 Oct 2020 17:39:25 +0100 Subject: [PATCH 2/2] kick tokio dependency from client Signed-off-by: Gregory Hill --- client/Cargo.toml | 1 - client/src/client.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/Cargo.toml b/client/Cargo.toml index 20a81863..b2ff4203 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -24,7 +24,6 @@ bitcoincore-rpc-json = { version = "0.11.0", path = "../json"} log = "0.4.5" reqwest = { version = "0.10", features = ["json"] } async-trait = "0.1.40" -tokio = { version = "0.2.22", features = ["full"] } base64-compat = "1.0.0" # Used for deserialization of JSON. diff --git a/client/src/client.rs b/client/src/client.rs index 8733726d..6e4f0b33 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -30,7 +30,7 @@ use bitcoin::{ }; use log::Level::{Debug, Trace, Warn}; use serde::{Deserialize, Serialize}; -use tokio::sync::Mutex; +use std::sync::Mutex; use crate::error::*; use crate::json;