通过 rpc 向测试网上的比特币节点询问内存池并使用 rust 库 hyper 1.1.0

问题描述 投票:0回答:1

我需要使用比特币-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
json-rpc hyper bitcoin-testnet
1个回答
0
投票

最终我依赖于

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,
}
© www.soinside.com 2019 - 2024. All rights reserved.