You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Hyper 1.x通过RPC调用Testnet比特币节点获取Mempool交易遇阻

Hyper升级后比特币Testnet RPC请求卡住

我需要通过Bitcoin-RPC协议向Testnet上的比特币节点请求内存池(Mempool)交易列表,因需求必须使用底层库Hyper。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,
}

请求体对比

最新Hyper版本中的请求体

&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}",
        ),
    ),
}

比特币节点配置

运行的比特币节点为最新master分支,提交哈希为4b1196a9855dcd188a24f393aa2fa21e2d61f061,配置如下:

[test]
testnet=1
server=1
datadir=<path to your testnet blockchain>
rpcuser=username
rpcpassword=password
rpcport=18332

问题原因及解决方案

原因

Hyper 1.x版本中,http1::handshake返回的conn是一个需要持续运行的Future,负责处理连接上的IO事件(包括接收响应)。旧版本Hyper会自动处理这个逻辑,但新版本需要显式驱动该Future,否则请求发送后无法接收服务器响应,导致代码卡在send_request处。

解决方案

在获取sender和conn后,使用tokio::spawn启动一个任务来运行conn的Future。修改send_json_rpc_request函数中的相关代码:

// 原代码
let (mut sender, _conn) = hyper::client::conn::http1::handshake(io).await.unwrap();

// 修改为:
let (mut sender, conn) = hyper::client::conn::http1::handshake(io).await.unwrap();
// 启动任务处理连接IO
tokio::spawn(async move {
    if let Err(e) = conn.await {
        eprintln!("Connection error: {}", e);
    }
});

此外,建议显式设置Host请求头(Hyper通常会自动添加,但显式设置更稳妥):

let req = Request::builder()
    // ... 其他配置
    .header("Host", host)
    // ... 其他配置
    .body(Full::<Bytes>::from(request_body))
    .unwrap();

完整修改后的核心函数片段

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();
    
    // 新增:启动任务处理连接
    tokio::spawn(async move {
        if let Err(e) = conn.await {
            eprintln!("Connection error: {}", e);
        }
    });
    
    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("Host", host)
        .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),
            }),
        }
    }
}

额外建议

  • 替换代码中的unwrap()为?或显式错误处理,这样能更清晰地定位连接、请求构建等环节的问题。
  • 查看比特币节点日志,确认是否收到请求以及是否有错误输出,辅助排查问题。

内容的提问来源于stack exchange,提问作者Lorban

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:22:04