depolymerization

wire gateway for Bitcoin/Ethereum
Log | Files | Refs | Submodules | README | LICENSE

commit 41bcc3e8faf18221eff61b83bcbd3f720fc0c408
parent 8fb1cc0dc0874eee68781c420a37a89db986fbd9
Author: Antoine A <>
Date:   Wed, 30 Jul 2025 15:26:23 +0200

bitcoin: improve rpc connect error

Diffstat:
MCargo.lock | 6+++---
Mdepolymerizer-bitcoin/src/rpc.rs | 34++++++++++++++++++++++++++--------
2 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -2829,7 +2829,7 @@ dependencies = [ [[package]] name = "taler-api" version = "0.0.0" -source = "git+https://git.taler.net/taler-rust.git/#a7021471084dbf2d6242517b147bdf94b0754da2" +source = "git+https://git.taler.net/taler-rust.git/#0725b22564cab84463159e4461b8acfc2121776b" dependencies = [ "axum", "base64", @@ -2852,7 +2852,7 @@ dependencies = [ [[package]] name = "taler-common" version = "0.0.0" -source = "git+https://git.taler.net/taler-rust.git/#a7021471084dbf2d6242517b147bdf94b0754da2" +source = "git+https://git.taler.net/taler-rust.git/#0725b22564cab84463159e4461b8acfc2121776b" dependencies = [ "anyhow", "clap", @@ -2877,7 +2877,7 @@ dependencies = [ [[package]] name = "taler-test-utils" version = "0.0.0" -source = "git+https://git.taler.net/taler-rust.git/#a7021471084dbf2d6242517b147bdf94b0754da2" +source = "git+https://git.taler.net/taler-rust.git/#0725b22564cab84463159e4461b8acfc2121776b" dependencies = [ "axum", "http-body-util", diff --git a/depolymerizer-bitcoin/src/rpc.rs b/depolymerizer-bitcoin/src/rpc.rs @@ -32,6 +32,7 @@ use serde_json::{Value, json}; use std::{ fmt::Debug, io::{ErrorKind, IoSlice, Write as _}, + net::SocketAddr, path::PathBuf, str::FromStr as _, time::Duration, @@ -94,6 +95,8 @@ pub enum Error { Bitcoin(String), #[error("JSON: {0}")] Json(#[from] serde_json::Error), + #[error("connect: {0}")] + Connect(#[from] RpcConnectErr), #[error("Null rpc, no result or error")] Null, } @@ -168,6 +171,16 @@ impl JsonSocket { } } +#[derive(Debug, thiserror::Error)] +pub enum RpcConnectErr { + #[error("failed to read cookie file at '{0}': {1}")] + Cookie(String, ErrorKind), + #[error("failed to connect {0}: {1}")] + Tcp(SocketAddr, ErrorKind), + #[error("failed to connect {0}: {1}")] + Elapsed(SocketAddr, tokio::time::error::Elapsed), +} + /// Bitcoin RPC connection pub struct Rpc { socket: JsonSocket, @@ -176,35 +189,41 @@ pub struct Rpc { impl Rpc { /// Start a RPC connection - pub async fn common(cfg: &RpcCfg) -> io::Result<Self> { + pub async fn common(cfg: &RpcCfg) -> std::result::Result<Self, RpcConnectErr> { Self::new(cfg, None).await } /// Start a wallet RPC connection - pub async fn wallet(cfg: &RpcCfg, wallet: &str) -> io::Result<Self> { + pub async fn wallet(cfg: &RpcCfg, wallet: &str) -> std::result::Result<Self, RpcConnectErr> { Self::new(cfg, Some(wallet)).await } - async fn new(cfg: &RpcCfg, wallet: Option<&str>) -> io::Result<Self> { + async fn new(cfg: &RpcCfg, wallet: Option<&str>) -> std::result::Result<Self, RpcConnectErr> { let path = if let Some(wallet) = wallet { format!("/wallet/{wallet}") } else { String::from("/") }; - // TODO error let token = match &cfg.auth { RpcAuth::Basic(s) => s.as_bytes().to_vec(), RpcAuth::Cookie(path) => match std::fs::read(path) { Ok(content) => content, Err(e) if e.kind() == ErrorKind::IsADirectory => { - std::fs::read(PathBuf::from_str(path).unwrap().join(".cookie"))? + let path = PathBuf::from_str(path).unwrap().join(".cookie"); + std::fs::read(&path).map_err(|e| { + RpcConnectErr::Cookie(path.to_string_lossy().to_string(), e.kind()) + })? } - Err(e) => return Err(e), + Err(e) => return Err(RpcConnectErr::Cookie(path.clone(), e.kind())), }, }; // Open connection - let sock = timeout(Duration::from_secs(5), TcpStream::connect(&cfg.addr)).await??; + let sock = timeout(Duration::from_secs(5), TcpStream::connect(&cfg.addr)) + .await + .map_err(|e| RpcConnectErr::Elapsed(cfg.addr, e))? + .map_err(|e| RpcConnectErr::Tcp(cfg.addr, e.kind()))?; + sock.set_nodelay(true).ok(); Ok(Self { @@ -273,7 +292,6 @@ impl Rpc { /// Unlock loaded wallet pub async fn unlock_wallet(&mut self, passwd: &str) -> Result<()> { - // TODO Capped at 3yrs, is it enough ? expect_null(self.call("walletpassphrase", &(passwd, 100000000)).await) }