我需要使用比特币-rpc 协议向测试网上正在运行的比特币节点请求内存池中的交易列表。由于某些特定原因,我还需要 hyper(我需要一个低级库)。我可以使用 0.14.28 版本来实现,但我想使用最新版本,但我没有成功。作为免责声明,我的编码经验相对较短,因此我特别喜欢长而描述性的答案!我尝试的代码如下。它打印“A”但仍然卡住而不打印“B”,这让我认为服务器没有响应。
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,
}
我还注意到变量“req”中请求正文的表示如下
&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}",
),
},
}
如果我使用以前版本的 hyper (0.14.28),它可以工作,请求正文的表示方式略有不同。不知道是不是这个问题
&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}",
),
),
}
最后,我运行的比特币节点是最新的主节点,提交为4b1196a9855dcd188a24f393aa2fa21e2d61f061。比特币节点的配置如下
[test]
testnet=1
server=1
datadir=<path to your testnet blockchain>
rpcuser=username
rpcpassword=password
rpcport=18332
最终我依赖于
Client
的legacy
模块中的hyper_util
。这段代码应该可以工作
use base64::Engine;
use bytes::Bytes;
use http_body_util::{BodyExt, Full};
use hyper::header::{AUTHORIZATION, CONTENT_TYPE};
use hyper::Request;
use hyper_util::client::legacy::Client;
use hyper_util::rt::TokioExecutor;
use serde::{Deserialize, Serialize};
use serde_json::json;
#[tokio::main]
async fn main() {
let response = send_json_rpc_request("getrawmempool", json!([])).await;
match response {
Ok(result) => {
let response_deserialized: JsonRpcResult<Vec<String>> =
serde_json::from_str(&result).unwrap();
let result_inner = response_deserialized.result.unwrap();
println!("Transactions: {:?}", result_inner);
}
Err(error) => {
println!("Error: {:?}", error);
}
}
}
async fn send_json_rpc_request(
method: &str,
params: serde_json::Value,
) -> Result<String, JsonRpcError> {
let url = "http://127.0.0.1:18332".parse::<hyper::Uri>().unwrap();
let (username, password) = ("username", "password");
let client: Client<_, Full<Bytes>> = Client::builder(TokioExecutor::new()).build_http();
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();
let response = client.request(req).await.unwrap();
let status = response.status();
let body = response.into_body().collect().await.unwrap().to_bytes();
if status.is_success() {
Ok(String::from_utf8(body.to_vec()).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,
}