Skip to content

Commit 689ea4a

Browse files
committed
Make corepc-client async
Edit the copy of the sync client created in the previous commit to be async. Update the readme and cargo.toml files. Add only small set of RPCs.
1 parent c9cd46d commit 689ea4a

23 files changed

Lines changed: 619 additions & 23 deletions

File tree

client/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ rustdoc-args = ["--cfg", "docsrs"]
1919
[features]
2020
# Enable this feature to get a blocking JSON-RPC client.
2121
client-sync = ["jsonrpc", "jsonrpc/bitreq_http"]
22+
# Enable this feature to get an async JSON-RPC client.
23+
client-async = ["jsonrpc", "jsonrpc/bitreq_http_async", "jsonrpc/client_async"]
2224

2325
[dependencies]
2426
bitcoin = { version = "0.32.0", default-features = false, features = ["std", "serde"] }

client/README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
# corepc-client
22

3-
Rust client for the Bitcoin Core daemon's JSON-RPC API. Currently this
4-
is only a blocking client and is intended to be used in integration testing.
3+
Rust client for the Bitcoin Core daemon's JSON-RPC API.
4+
5+
This crate provides:
6+
7+
- A blocking client intended for integration testing (`client-sync`).
8+
- An async client intended for production (`client-async`).
9+
10+
## Features
11+
12+
- `client-sync`: Blocking JSON-RPC client.
13+
- `client-async`: Async JSON-RPC client.
514

615
## Minimum Supported Rust Version (MSRV)
716

client/src/client_async/mod.rs

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-License-Identifier: CC0-1.0
22

3-
//! JSON-RPC clients for testing against specific versions of Bitcoin Core.
3+
//! Async JSON-RPC clients for specific versions of Bitcoin Core.
44
55
mod error;
66
pub mod v17;
@@ -22,7 +22,7 @@ use std::fs::File;
2222
use std::io::{BufRead, BufReader};
2323
use std::path::PathBuf;
2424

25-
pub use crate::client_sync::error::Error;
25+
pub use crate::client_async::error::Error;
2626

2727
/// Crate-specific Result type.
2828
///
@@ -55,38 +55,38 @@ impl Auth {
5555
}
5656
}
5757

58-
/// Defines a `jsonrpc::Client` using `bitreq`.
58+
/// Defines a async `jsonrpc::Client` using `bitreq`.
5959
#[macro_export]
60-
macro_rules! define_jsonrpc_bitreq_client {
60+
macro_rules! define_jsonrpc_bitreq_async_client {
6161
($version:literal) => {
6262
use std::fmt;
63+
use $crate::client_async::{log_response, Auth, Result};
64+
use $crate::client_async::error::Error;
6365

64-
use $crate::client_sync::{log_response, Auth, Result};
65-
use $crate::client_sync::error::Error;
66-
67-
/// Client implements a JSON-RPC client for the Bitcoin Core daemon or compatible APIs.
66+
/// Client implements an async JSON-RPC client for the Bitcoin Core daemon or compatible APIs.
6867
pub struct Client {
69-
inner: jsonrpc::client::Client,
68+
inner: jsonrpc::AsyncClient,
7069
}
7170

7271
impl fmt::Debug for Client {
7372
fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
7473
write!(
7574
f,
76-
"corepc_client::client_sync::{}::Client({:?})", $version, self.inner
75+
"corepc_client::client_async::{}::Client({:?})",
76+
$version, self.inner
7777
)
7878
}
7979
}
8080

8181
impl Client {
8282
/// Creates a client to a bitcoind JSON-RPC server without authentication.
8383
pub fn new(url: &str) -> Self {
84-
let transport = jsonrpc::http::bitreq_http::Builder::new()
84+
let transport = jsonrpc::bitreq_http_async::Builder::new()
8585
.url(url)
8686
.expect("jsonrpc v0.19, this function does not error")
8787
.timeout(std::time::Duration::from_secs(60))
8888
.build();
89-
let inner = jsonrpc::client::Client::with_transport(transport);
89+
let inner = jsonrpc::AsyncClient::with_transport(transport);
9090

9191
Self { inner }
9292
}
@@ -97,20 +97,19 @@ macro_rules! define_jsonrpc_bitreq_client {
9797
return Err(Error::MissingUserPassword);
9898
}
9999
let (user, pass) = auth.get_user_pass()?;
100-
101-
let transport = jsonrpc::http::bitreq_http::Builder::new()
100+
let transport = jsonrpc::bitreq_http_async::Builder::new()
102101
.url(url)
103102
.expect("jsonrpc v0.19, this function does not error")
104103
.timeout(std::time::Duration::from_secs(60))
105104
.basic_auth(user.unwrap(), pass)
106105
.build();
107-
let inner = jsonrpc::client::Client::with_transport(transport);
106+
let inner = jsonrpc::AsyncClient::with_transport(transport);
108107

109108
Ok(Self { inner })
110109
}
111110

112111
/// Call an RPC `method` with given `args` list.
113-
pub fn call<T: for<'a> serde::de::Deserialize<'a>>(
112+
pub async fn call<T: for<'a> serde::de::Deserialize<'a>>(
114113
&self,
115114
method: &str,
116115
args: &[serde_json::Value],
@@ -121,7 +120,7 @@ macro_rules! define_jsonrpc_bitreq_client {
121120
log::debug!(target: "corepc", "request: {} {}", method, serde_json::Value::from(args));
122121
}
123122

124-
let resp = self.inner.send_request(req).map_err(Error::from);
123+
let resp = self.inner.send_request(req).await.map_err(Error::from);
125124
log_response(method, &resp);
126125
Ok(resp?.result()?)
127126
}
@@ -138,14 +137,14 @@ macro_rules! define_jsonrpc_bitreq_client {
138137
///
139138
/// - `$expected_versions`: An vector of expected server versions e.g., `[230100, 230200]`.
140139
#[macro_export]
141-
macro_rules! impl_client_check_expected_server_version {
140+
macro_rules! impl_async_client_check_expected_server_version {
142141
($expected_versions:expr) => {
143142
impl Client {
144143
/// Checks that the JSON-RPC endpoint is for a `bitcoind` instance with the expected version.
145-
pub fn check_expected_server_version(&self) -> Result<()> {
146-
let server_version = self.server_version()?;
144+
pub async fn check_expected_server_version(&self) -> Result<()> {
145+
let server_version = self.server_version().await?;
147146
if !$expected_versions.contains(&server_version) {
148-
return Err($crate::client_sync::error::UnexpectedServerVersionError {
147+
return Err($crate::client_async::error::UnexpectedServerVersionError {
149148
got: server_version,
150149
expected: $expected_versions.to_vec(),
151150
})?;
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! Macros for implementing async JSON-RPC methods on a client.
4+
//!
5+
//! Specifically this is methods found under the `== Blockchain ==` section of the
6+
//! API docs of Bitcoin Core `v0.17`.
7+
//!
8+
//! All macros require `Client` to be in scope.
9+
//!
10+
//! See or use the `define_jsonrpc_bitreq_async_client!` macro to define a `Client`.
11+
12+
/// Implements Bitcoin Core JSON-RPC API method `getblock`.
13+
#[macro_export]
14+
macro_rules! impl_async_client_v17__get_block {
15+
() => {
16+
impl Client {
17+
/// Gets a block by blockhash.
18+
pub async fn get_block(&self, hash: BlockHash) -> Result<Block> {
19+
let json = self.get_block_verbose_zero(hash).await?;
20+
Ok(json.block()?)
21+
}
22+
23+
/// Gets a block by blockhash with verbose set to 0.
24+
pub async fn get_block_verbose_zero(
25+
&self,
26+
hash: BlockHash,
27+
) -> Result<GetBlockVerboseZero> {
28+
self.call("getblock", &[into_json(hash)?, 0.into()]).await
29+
}
30+
31+
/// Gets a block by blockhash with verbose set to 1.
32+
pub async fn get_block_verbose_one(
33+
&self,
34+
hash: BlockHash,
35+
) -> Result<GetBlockVerboseOne> {
36+
self.call("getblock", &[into_json(hash)?, 1.into()]).await
37+
}
38+
39+
/// Alias for getblock verbosity 1, matching bitcoincore-rpc naming.
40+
pub async fn get_block_info(&self, hash: BlockHash) -> Result<GetBlockVerboseOne> {
41+
self.get_block_verbose_one(hash).await
42+
}
43+
}
44+
};
45+
}
46+
47+
/// Implements Bitcoin Core JSON-RPC API method `getblockcount`.
48+
#[macro_export]
49+
macro_rules! impl_async_client_v17__get_block_count {
50+
() => {
51+
impl Client {
52+
pub async fn get_block_count(&self) -> Result<GetBlockCount> {
53+
self.call("getblockcount", &[]).await
54+
}
55+
}
56+
};
57+
}
58+
59+
/// Implements Bitcoin Core JSON-RPC API method `getblockhash`.
60+
#[macro_export]
61+
macro_rules! impl_async_client_v17__get_block_hash {
62+
() => {
63+
impl Client {
64+
pub async fn get_block_hash(&self, height: u64) -> Result<GetBlockHash> {
65+
self.call("getblockhash", &[into_json(height)?]).await
66+
}
67+
}
68+
};
69+
}
70+
71+
/// Implements Bitcoin Core JSON-RPC API method `getblockheader`.
72+
#[macro_export]
73+
macro_rules! impl_async_client_v17__get_block_header {
74+
() => {
75+
impl Client {
76+
pub async fn get_block_header(&self, hash: &BlockHash) -> Result<GetBlockHeader> {
77+
self.call("getblockheader", &[into_json(hash)?, into_json(false)?]).await
78+
}
79+
80+
// This is the same as calling getblockheader with verbose==true.
81+
pub async fn get_block_header_verbose(
82+
&self,
83+
hash: &BlockHash,
84+
) -> Result<GetBlockHeaderVerbose> {
85+
self.call("getblockheader", &[into_json(hash)?]).await
86+
}
87+
88+
/// Alias for getblockheader with verbose true.
89+
pub async fn get_block_header_info(
90+
&self,
91+
hash: &BlockHash,
92+
) -> Result<GetBlockHeaderVerbose> {
93+
self.get_block_header_verbose(hash).await
94+
}
95+
}
96+
};
97+
}
98+
99+
/// Implements Bitcoin Core JSON-RPC API method `getrawmempool`.
100+
#[macro_export]
101+
macro_rules! impl_async_client_v17__get_raw_mempool {
102+
() => {
103+
impl Client {
104+
pub async fn get_raw_mempool(&self) -> Result<GetRawMempool> {
105+
// Equivalent to self.call("getrawmempool", &[into_json(false)?])
106+
self.call("getrawmempool", &[]).await
107+
}
108+
109+
pub async fn get_raw_mempool_verbose(&self) -> Result<GetRawMempoolVerbose> {
110+
self.call("getrawmempool", &[into_json(true)?]).await
111+
}
112+
}
113+
};
114+
}

client/src/client_async/v17/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! An async JSON-RPC client for Bitcoin Core `v0.17`.
4+
5+
pub mod blockchain;
6+
pub mod network;
7+
pub mod raw_transactions;
8+
9+
use bitcoin::{Block, BlockHash, Txid};
10+
11+
use crate::client_async::into_json;
12+
use crate::types::v17::*;
13+
14+
crate::define_jsonrpc_bitreq_async_client!("v17");
15+
crate::impl_async_client_check_expected_server_version!({ [170200] });
16+
17+
// == Blockchain ==
18+
crate::impl_async_client_v17__get_block!();
19+
crate::impl_async_client_v17__get_block_count!();
20+
crate::impl_async_client_v17__get_block_hash!();
21+
crate::impl_async_client_v17__get_block_header!();
22+
crate::impl_async_client_v17__get_raw_mempool!();
23+
24+
// == Network ==
25+
crate::impl_async_client_v17__get_network_info!();
26+
27+
// == Rawtransactions ==
28+
crate::impl_async_client_v17__get_raw_transaction!();
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! Macros for implementing async JSON-RPC methods on a client.
4+
//!
5+
//! Specifically this is methods found under the `== Network ==` section of the
6+
//! API docs of Bitcoin Core `v0.17`.
7+
//!
8+
//! All macros require `Client` to be in scope.
9+
//!
10+
//! See or use the `define_jsonrpc_bitreq_async_client!` macro to define a `Client`.
11+
12+
/// Implements Bitcoin Core JSON-RPC API method `getnetworkinfo`.
13+
#[macro_export]
14+
macro_rules! impl_async_client_v17__get_network_info {
15+
() => {
16+
impl Client {
17+
/// Returns the server version field of `GetNetworkInfo`.
18+
pub async fn server_version(&self) -> Result<usize> {
19+
let info = self.get_network_info().await?;
20+
Ok(info.version)
21+
}
22+
23+
pub async fn get_network_info(&self) -> Result<GetNetworkInfo> {
24+
self.call("getnetworkinfo", &[]).await
25+
}
26+
}
27+
};
28+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! Macros for implementing async JSON-RPC methods on a client.
4+
//!
5+
//! Specifically this is methods found under the `== Rawtransactions ==` section of the
6+
//! API docs of Bitcoin Core `v0.17`.
7+
//!
8+
//! All macros require `Client` to be in scope.
9+
//!
10+
//! See or use the `define_jsonrpc_bitreq_async_client!` macro to define a `Client`.
11+
12+
/// Implements Bitcoin Core JSON-RPC API method `getrawtransaction`.
13+
#[macro_export]
14+
macro_rules! impl_async_client_v17__get_raw_transaction {
15+
() => {
16+
impl Client {
17+
pub async fn get_raw_transaction(
18+
&self,
19+
txid: bitcoin::Txid,
20+
) -> Result<GetRawTransaction> {
21+
self.call("getrawtransaction", &[into_json(&txid)?, false.into()]).await
22+
}
23+
24+
pub async fn get_raw_transaction_verbose(
25+
&self,
26+
txid: Txid,
27+
) -> Result<GetRawTransactionVerbose> {
28+
self.call("getrawtransaction", &[into_json(&txid)?, true.into()]).await
29+
}
30+
}
31+
};
32+
}

client/src/client_async/v18/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! An async JSON-RPC client for Bitcoin Core `v0.18`.
4+
5+
use bitcoin::{Block, BlockHash, Txid};
6+
7+
use crate::client_async::into_json;
8+
use crate::types::v18::*;
9+
10+
crate::define_jsonrpc_bitreq_async_client!("v18");
11+
crate::impl_async_client_check_expected_server_version!({ [180100] });
12+
13+
// == Blockchain ==
14+
crate::impl_async_client_v17__get_block!();
15+
crate::impl_async_client_v17__get_block_count!();
16+
crate::impl_async_client_v17__get_block_hash!();
17+
crate::impl_async_client_v17__get_block_header!();
18+
crate::impl_async_client_v17__get_raw_mempool!();
19+
20+
// == Network ==
21+
crate::impl_async_client_v17__get_network_info!();
22+
23+
// == Rawtransactions ==
24+
crate::impl_async_client_v17__get_raw_transaction!();
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! Macros for implementing async JSON-RPC methods on a client.
4+
//!
5+
//! Specifically this is methods found under the `== Blockchain ==` section of the
6+
//! API docs of Bitcoin Core `v0.19`.
7+
//!
8+
//! All macros require `Client` to be in scope.
9+
//!
10+
//! See or use the `define_jsonrpc_bitreq_async_client!` macro to define a `Client`.
11+
12+
/// Implements Bitcoin Core JSON-RPC API method `getblockfilter`.
13+
#[macro_export]
14+
macro_rules! impl_async_client_v19__get_block_filter {
15+
() => {
16+
impl Client {
17+
pub async fn get_block_filter(&self, block: BlockHash) -> Result<GetBlockFilter> {
18+
self.call("getblockfilter", &[into_json(block)?]).await
19+
}
20+
}
21+
};
22+
}

0 commit comments

Comments
 (0)