Skip to content

Commit 78c5f44

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 78c5f44

23 files changed

Lines changed: 613 additions & 27 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: 23 additions & 25 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
///
@@ -44,49 +44,48 @@ impl Auth {
4444
Auth::None => Ok((None, None)),
4545
Auth::UserPass(u, p) => Ok((Some(u), Some(p))),
4646
Auth::CookieFile(path) => {
47-
let line = BufReader::new(File::open(path)?)
48-
.lines()
49-
.next()
50-
.ok_or(Error::InvalidCookieFile)??;
47+
let line = BufReader::new(File::open(path)?).lines().next().ok_or(
48+
Error::InvalidCookieFile,
49+
)??;
5150
let colon = line.find(':').ok_or(Error::InvalidCookieFile)?;
5251
Ok((Some(line[..colon].into()), Some(line[colon + 1..].into())))
5352
}
5453
}
5554
}
5655
}
5756

58-
/// Defines a `jsonrpc::Client` using `bitreq`.
57+
/// Defines a async `jsonrpc::Client` using `bitreq`.
5958
#[macro_export]
60-
macro_rules! define_jsonrpc_bitreq_client {
59+
macro_rules! define_jsonrpc_bitreq_async_client {
6160
($version:literal) => {
6261
use std::fmt;
62+
use $crate::client_async::{log_response, Auth, Result};
63+
use $crate::client_async::error::Error;
6364

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.
65+
/// Client implements an async JSON-RPC client for the Bitcoin Core daemon or compatible APIs.
6866
pub struct Client {
69-
inner: jsonrpc::client::Client,
67+
inner: jsonrpc::AsyncClient,
7068
}
7169

7270
impl fmt::Debug for Client {
7371
fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
7472
write!(
7573
f,
76-
"corepc_client::client_sync::{}::Client({:?})", $version, self.inner
74+
"corepc_client::client_async::{}::Client({})",
75+
$version, self.url
7776
)
7877
}
7978
}
8079

8180
impl Client {
8281
/// Creates a client to a bitcoind JSON-RPC server without authentication.
8382
pub fn new(url: &str) -> Self {
84-
let transport = jsonrpc::http::bitreq_http::Builder::new()
83+
let transport = jsonrpc::bitreq_http_async::Builder::new()
8584
.url(url)
8685
.expect("jsonrpc v0.19, this function does not error")
8786
.timeout(std::time::Duration::from_secs(60))
8887
.build();
89-
let inner = jsonrpc::client::Client::with_transport(transport);
88+
let inner = jsonrpc::AsyncClient::with_transport(transport);
9089

9190
Self { inner }
9291
}
@@ -97,20 +96,19 @@ macro_rules! define_jsonrpc_bitreq_client {
9796
return Err(Error::MissingUserPassword);
9897
}
9998
let (user, pass) = auth.get_user_pass()?;
100-
101-
let transport = jsonrpc::http::bitreq_http::Builder::new()
99+
let transport = jsonrpc::bitreq_http_async::Builder::new()
102100
.url(url)
103101
.expect("jsonrpc v0.19, this function does not error")
104102
.timeout(std::time::Duration::from_secs(60))
105103
.basic_auth(user.unwrap(), pass)
106104
.build();
107-
let inner = jsonrpc::client::Client::with_transport(transport);
105+
let inner = jsonrpc::AsyncClient::with_transport(transport);
108106

109107
Ok(Self { inner })
110108
}
111109

112110
/// Call an RPC `method` with given `args` list.
113-
pub fn call<T: for<'a> serde::de::Deserialize<'a>>(
111+
pub async fn call<T: for<'a> serde::de::Deserialize<'a>>(
114112
&self,
115113
method: &str,
116114
args: &[serde_json::Value],
@@ -121,7 +119,7 @@ macro_rules! define_jsonrpc_bitreq_client {
121119
log::debug!(target: "corepc", "request: {} {}", method, serde_json::Value::from(args));
122120
}
123121

124-
let resp = self.inner.send_request(req).map_err(Error::from);
122+
let resp = self.inner.send_request(req).await.map_err(Error::from);
125123
log_response(method, &resp);
126124
Ok(resp?.result()?)
127125
}
@@ -138,14 +136,14 @@ macro_rules! define_jsonrpc_bitreq_client {
138136
///
139137
/// - `$expected_versions`: An vector of expected server versions e.g., `[230100, 230200]`.
140138
#[macro_export]
141-
macro_rules! impl_client_check_expected_server_version {
139+
macro_rules! impl_async_client_check_expected_server_version {
142140
($expected_versions:expr) => {
143141
impl Client {
144142
/// 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()?;
143+
pub async fn check_expected_server_version(&self) -> Result<()> {
144+
let server_version = self.server_version().await?;
147145
if !$expected_versions.contains(&server_version) {
148-
return Err($crate::client_sync::error::UnexpectedServerVersionError {
146+
return Err($crate::client_async::error::UnexpectedServerVersionError {
149147
got: server_version,
150148
expected: $expected_versions.to_vec(),
151149
})?;
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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(&self, hash: BlockHash) -> Result<GetBlockVerboseZero> {
25+
self.call("getblock", &[into_json(hash)?, 0.into()]).await
26+
}
27+
28+
/// Gets a block by blockhash with verbose set to 1.
29+
pub async fn get_block_verbose_one(&self, hash: BlockHash) -> Result<GetBlockVerboseOne> {
30+
self.call("getblock", &[into_json(hash)?, 1.into()]).await
31+
}
32+
33+
/// Alias for getblock verbosity 1, matching bitcoincore-rpc naming.
34+
pub async fn get_block_info(&self, hash: BlockHash) -> Result<GetBlockVerboseOne> {
35+
self.get_block_verbose_one(hash).await
36+
}
37+
}
38+
};
39+
}
40+
41+
/// Implements Bitcoin Core JSON-RPC API method `getblockcount`.
42+
#[macro_export]
43+
macro_rules! impl_async_client_v17__get_block_count {
44+
() => {
45+
impl Client {
46+
pub async fn get_block_count(&self) -> Result<GetBlockCount> {
47+
self.call("getblockcount", &[]).await
48+
}
49+
}
50+
};
51+
}
52+
53+
/// Implements Bitcoin Core JSON-RPC API method `getblockhash`.
54+
#[macro_export]
55+
macro_rules! impl_async_client_v17__get_block_hash {
56+
() => {
57+
impl Client {
58+
pub async fn get_block_hash(&self, height: u64) -> Result<GetBlockHash> {
59+
self.call("getblockhash", &[into_json(height)?]).await
60+
}
61+
}
62+
};
63+
}
64+
65+
/// Implements Bitcoin Core JSON-RPC API method `getblockheader`.
66+
#[macro_export]
67+
macro_rules! impl_async_client_v17__get_block_header {
68+
() => {
69+
impl Client {
70+
pub async fn get_block_header(&self, hash: &BlockHash) -> Result<GetBlockHeader> {
71+
self.call("getblockheader", &[into_json(hash)?, into_json(false)?]).await
72+
}
73+
74+
// This is the same as calling getblockheader with verbose==true.
75+
pub async fn get_block_header_verbose(
76+
&self,
77+
hash: &BlockHash,
78+
) -> Result<GetBlockHeaderVerbose> {
79+
self.call("getblockheader", &[into_json(hash)?]).await
80+
}
81+
82+
/// Alias for getblockheader with verbose true.
83+
pub async fn get_block_header_info(
84+
&self,
85+
hash: &BlockHash,
86+
) -> Result<GetBlockHeaderVerbose> {
87+
self.get_block_header_verbose(hash).await
88+
}
89+
}
90+
};
91+
}
92+
93+
/// Implements Bitcoin Core JSON-RPC API method `getrawmempool`.
94+
#[macro_export]
95+
macro_rules! impl_async_client_v17__get_raw_mempool {
96+
() => {
97+
impl Client {
98+
pub async fn get_raw_mempool(&self) -> Result<GetRawMempool> {
99+
// Equivalent to self.call("getrawmempool", &[into_json(false)?])
100+
self.call("getrawmempool", &[]).await
101+
}
102+
103+
pub async fn get_raw_mempool_verbose(&self) -> Result<GetRawMempoolVerbose> {
104+
self.call("getrawmempool", &[into_json(true)?]).await
105+
}
106+
}
107+
};
108+
}

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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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(&self, txid: bitcoin::Txid) -> Result<GetRawTransaction> {
18+
self.call("getrawtransaction", &[into_json(&txid)?, false.into()]).await
19+
}
20+
21+
pub async fn get_raw_transaction_verbose(
22+
&self,
23+
txid: Txid,
24+
) -> Result<GetRawTransactionVerbose> {
25+
self.call("getrawtransaction", &[into_json(&txid)?, true.into()]).await
26+
}
27+
}
28+
};
29+
}

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!();

0 commit comments

Comments
 (0)