Dorian ddf3f73df5 feat: wire bitcoin.getinfo and lnd.getinfo RPC routes + AIUI bitcoin context (Task 3)
Register bitcoin.rs and lnd.rs modules in mod.rs and add route entries
for bitcoin.getinfo and lnd.getinfo. Add bitcoinInfo ref and context
display to AIUI useArchy.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 13:56:29 +00:00

109 lines
3.1 KiB
Rust

use super::RpcHandler;
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize)]
struct BitcoinInfo {
block_height: u64,
sync_progress: f64,
chain: String,
difficulty: f64,
mempool_size: u64,
mempool_tx_count: u64,
verification_progress: f64,
}
#[derive(Debug, Deserialize)]
struct BitcoinRpcResponse<T> {
result: Option<T>,
error: Option<serde_json::Value>,
}
#[derive(Debug, Deserialize)]
struct BlockchainInfo {
chain: Option<String>,
blocks: Option<u64>,
difficulty: Option<f64>,
#[serde(rename = "verificationprogress")]
verification_progress: Option<f64>,
}
#[derive(Debug, Deserialize)]
struct MempoolInfo {
size: Option<u64>,
bytes: Option<u64>,
}
impl RpcHandler {
pub(super) async fn handle_bitcoin_getinfo(&self) -> Result<serde_json::Value> {
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(10))
.build()
.context("Failed to create HTTP client")?;
let blockchain_info = self
.bitcoin_rpc_call::<BlockchainInfo>(&client, "getblockchaininfo", &[])
.await
.context("Failed to query getblockchaininfo")?;
let mempool_info = self
.bitcoin_rpc_call::<MempoolInfo>(&client, "getmempoolinfo", &[])
.await
.unwrap_or(MempoolInfo {
size: Some(0),
bytes: Some(0),
});
let info = BitcoinInfo {
block_height: blockchain_info.blocks.unwrap_or(0),
sync_progress: blockchain_info
.verification_progress
.unwrap_or(0.0),
chain: blockchain_info.chain.unwrap_or_else(|| "unknown".into()),
difficulty: blockchain_info.difficulty.unwrap_or(0.0),
mempool_size: mempool_info.bytes.unwrap_or(0),
mempool_tx_count: mempool_info.size.unwrap_or(0),
verification_progress: blockchain_info
.verification_progress
.unwrap_or(0.0),
};
Ok(serde_json::to_value(info)?)
}
async fn bitcoin_rpc_call<T: serde::de::DeserializeOwned>(
&self,
client: &reqwest::Client,
method: &str,
params: &[serde_json::Value],
) -> Result<T> {
let body = serde_json::json!({
"jsonrpc": "1.0",
"id": "archy",
"method": method,
"params": params,
});
let resp = client
.post("http://127.0.0.1:8332/")
.basic_auth("archipelago", Some("archipelago123"))
.json(&body)
.send()
.await
.context("Bitcoin RPC connection failed")?;
let rpc_resp: BitcoinRpcResponse<T> = resp
.json()
.await
.context("Failed to parse Bitcoin RPC response")?;
if let Some(err) = rpc_resp.error {
anyhow::bail!("Bitcoin RPC error: {}", err);
}
rpc_resp
.result
.ok_or_else(|| anyhow::anyhow!("Bitcoin RPC returned null result"))
}
}