I need to use bitcoin-rpc protocol to request the list of the transactions in the mempool to a running bitcoin node on testnet. I also need hyper for some specific reasons (I need a low level library). I could make it with the version 0.14.28, but I want to use the latest release, with which I am not succeeding. As a disclaimer, I have a relatively short experience with coding, so I particularly enjoy long and describing answers! The code I tried is the following. It prints "A" but remain stuck without printing "B", that makes me think that the server is not responding.
use base64::Engine;
use bytes::Bytes;
use http_body_util::{BodyExt, Full};
use hyper::header::{AUTHORIZATION, CONTENT_TYPE};
use hyper::Request;
use hyper_util::rt::TokioIo;
use serde::{Deserialize, Serialize};
use serde_json::json;
use tokio::net::TcpStream;
#[tokio::main]
async fn main() {
let response = send_json_rpc_request("getrawmempool", json!([])).await;
match response {
Ok(result) => {
let response_: Result<Vec<String>, ()> =
match serde_json::from_value(result.result.unwrap()) {
Ok(response_inner) => response_inner,
Err(_) => {
println!("Deserialization error");
Err(())
}
};
println!("The mempool is: {:?}", response_.unwrap());
}
Err(error) => {
println!("Error: {:?}", error);
}
}
}
async fn send_json_rpc_request<T: for<'de> Deserialize<'de> + std::fmt::Debug>(
method: &str,
params: serde_json::Value,
) -> Result<JsonRpcResult<T>, JsonRpcError> {
let url = "http://127.0.0.1:18332".parse::<hyper::Uri>().unwrap();
let host = url.host().expect("uri has no host");
let port = url.port_u16().unwrap_or(18332);
let address = format!("{}:{}", host, port);
let stream = TcpStream::connect(address).await.unwrap();
let io = TokioIo::new(stream);
let (mut sender, _conn) = hyper::client::conn::http1::handshake(io).await.unwrap();
let (username, password) = ("username", "password");
let request = JsonRpcRequest {
jsonrpc: "2.0".to_string(),
method: method.to_string(),
params,
id: 1,
};
let request_body = serde_json::to_string(&request).unwrap();
let req = Request::builder()
.method("POST")
.uri(url)
.header(CONTENT_TYPE, "application/json")
.header(
AUTHORIZATION,
format!(
"Basic {}",
base64::engine::general_purpose::STANDARD
.encode(format!("{}:{}", username, password))
),
)
.body(Full::<Bytes>::from(request_body))
.unwrap();
println!("A");
let response = sender.send_request(req).await.unwrap();
println!("B");
let status = response.status();
let body = response.into_body().collect().await.unwrap().to_bytes();
if status.is_success() {
serde_json::from_slice(&body).unwrap()
} else {
match serde_json::from_slice(&body) {
Ok(error_response) => Err(error_response),
Err(e) => Err(JsonRpcError {
code: -1,
message: format!("Deserialization error {:?}", e),
}),
}
}
}
#[derive(Debug, Serialize)]
struct JsonRpcRequest {
jsonrpc: String,
method: String,
params: serde_json::Value,
id: u64,
}
#[derive(Debug, Deserialize)]
pub struct JsonRpcResult<T> {
result: Option<T>,
error: Option<JsonRpcError>,
id: u64,
}
#[derive(Debug, Deserialize, Clone)]
pub struct JsonRpcError {
code: i32,
message: String,
}
I also note that the representation of the body of request in variable "req" is the follfowing
&req = Request {
method: POST,
uri: http://127.0.0.1:18332/,
version: HTTP/1.1,
headers: {
"content-type": "application/json",
"authorization": "Basic dXNlcm5hbWU6cGFzc3dvcmQ=",
},
body: Full {
data: Some(
b"{\"jsonrpc\":\"2.0\",\"method\":\"getrawmempool\",\"params\":[],\"id\":1}",
),
},
}
and if I use the previous version of hyper (0.14.28), with which it worked, the request body is represented slightly differently. I don't know if this is the issue.
&req = Request {
method: POST,
uri: http://127.0.0.1:18332/,
version: HTTP/1.1,
headers: {
"content-type": "application/json",
"authorization": "Basic dXNlcm5hbWU6cGFzc3dvcmQ=",
},
body: Body(
Full(
b"{\"jsonrpc\":\"2.0\",\"method\":\"getrawmempool\",\"params\":[],\"id\":1}",
),
),
}
Finally, the bitcoin node that I am running is the latest master, with commit 4b1196a9855dcd188a24f393aa2fa21e2d61f061. The configuration of the bitcoin node is the following
[test]
testnet=1
server=1
datadir=<path to your testnet blockchain>
rpcuser=username
rpcpassword=password
rpcport=18332
Eventually I relied on the
Client
inlegacy
module ofhyper_util
. This code should work