From 4c9e3b085b805ca4719ca1ae232aae1419a01573 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Fri, 3 Nov 2023 05:45:31 -0400 Subject: [PATCH] Add a String to Monero ConnectionErrors debugging the issue We're reaching this in CI so there must be some issue present. --- coins/monero/src/bin/reserialize_chain.rs | 16 ++++---- coins/monero/src/rpc/http.rs | 45 ++++++++++++++--------- coins/monero/src/rpc/mod.rs | 4 +- processor/src/networks/monero.rs | 8 +++- 4 files changed, 45 insertions(+), 28 deletions(-) diff --git a/coins/monero/src/bin/reserialize_chain.rs b/coins/monero/src/bin/reserialize_chain.rs index 88b49fcd..b23f73c1 100644 --- a/coins/monero/src/bin/reserialize_chain.rs +++ b/coins/monero/src/bin/reserialize_chain.rs @@ -26,8 +26,8 @@ mod binaries { let hash = loop { match rpc.get_block_hash(block_i).await { Ok(hash) => break hash, - Err(RpcError::ConnectionError) => { - println!("get_block_hash ConnectionError"); + Err(RpcError::ConnectionError(e)) => { + println!("get_block_hash ConnectionError: {e}"); continue; } Err(e) => panic!("couldn't get block {block_i}'s hash: {e:?}"), @@ -42,8 +42,8 @@ mod binaries { let res: BlockResponse = loop { match rpc.json_rpc_call("get_block", Some(json!({ "hash": hex::encode(hash) }))).await { Ok(res) => break res, - Err(RpcError::ConnectionError) => { - println!("get_block ConnectionError"); + Err(RpcError::ConnectionError(e)) => { + println!("get_block ConnectionError: {e}"); continue; } Err(e) => panic!("couldn't get block {block_i} via block.hash(): {e:?}"), @@ -85,8 +85,8 @@ mod binaries { .await { Ok(txs) => break txs, - Err(RpcError::ConnectionError) => { - println!("get_transactions ConnectionError"); + Err(RpcError::ConnectionError(e)) => { + println!("get_transactions ConnectionError: {e}"); continue; } Err(e) => panic!("couldn't call get_transactions: {e:?}"), @@ -190,8 +190,8 @@ mod binaries { .await { Ok(outs) => break outs, - Err(RpcError::ConnectionError) => { - println!("get_outs ConnectionError"); + Err(RpcError::ConnectionError(e)) => { + println!("get_outs ConnectionError: {e}"); continue; } Err(e) => panic!("couldn't connect to RPC to get outs: {e:?}"), diff --git a/coins/monero/src/rpc/http.rs b/coins/monero/src/rpc/http.rs index b19c1da6..803e35c5 100644 --- a/coins/monero/src/rpc/http.rs +++ b/coins/monero/src/rpc/http.rs @@ -1,7 +1,11 @@ +use core::str::FromStr; + use async_trait::async_trait; use digest_auth::AuthContext; -use hyper::{header::HeaderValue, Request, service::Service, client::connect::HttpConnector, Client}; +use hyper::{ + Uri, header::HeaderValue, Request, service::Service, client::connect::HttpConnector, Client, +}; use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder}; use crate::rpc::{RpcError, RpcConnection, Rpc}; @@ -41,7 +45,7 @@ impl HttpRpc { let url_clone = url; let split_url = url_clone.split('@').collect::>(); if split_url.len() != 2 { - Err(RpcError::ConnectionError)?; + Err(RpcError::ConnectionError("invalid amount of login specifications".to_string()))?; } let mut userpass = split_url[0]; url = split_url[1].to_string(); @@ -50,20 +54,20 @@ impl HttpRpc { if userpass.contains("://") { let split_userpass = userpass.split("://").collect::>(); if split_userpass.len() != 2 { - Err(RpcError::ConnectionError)?; + Err(RpcError::ConnectionError("invalid amount of protocol specifications".to_string()))?; } url = split_userpass[0].to_string() + "://" + &url; userpass = split_userpass[1]; } let split_userpass = userpass.split(':').collect::>(); - if split_userpass.len() != 2 { - Err(RpcError::ConnectionError)?; + if split_userpass.len() > 2 { + Err(RpcError::ConnectionError("invalid amount of passwords".to_string()))?; } Authentication::Authenticated( https_builder, split_userpass[0].to_string(), - split_userpass[1].to_string(), + split_userpass.get(1).unwrap_or(&"").to_string(), ) } else { Authentication::Unauthenticated(Client::builder().build(https_builder)) @@ -93,23 +97,28 @@ impl HttpRpc { Authentication::Unauthenticated(client) => client .request(request(self.url.clone() + "/" + route)) .await - .map_err(|_| RpcError::ConnectionError)?, + .map_err(|e| RpcError::ConnectionError(e.to_string()))?, Authentication::Authenticated(https_builder, user, pass) => { let connection = https_builder .clone() - .call(self.url.parse().map_err(|_| RpcError::ConnectionError)?) + .call( + self + .url + .parse() + .map_err(|e: ::Err| RpcError::ConnectionError(e.to_string()))?, + ) .await - .map_err(|_| RpcError::ConnectionError)?; + .map_err(|e| RpcError::ConnectionError(e.to_string()))?; let (mut requester, connection) = hyper::client::conn::http1::handshake(connection) .await - .map_err(|_| RpcError::ConnectionError)?; + .map_err(|e| RpcError::ConnectionError(e.to_string()))?; let connection_task = tokio::spawn(connection); connection_task_handle = Some(connection_task.abort_handle()); let mut response = requester .send_request(request("/".to_string() + route)) .await - .map_err(|_| RpcError::ConnectionError)?; + .map_err(|e| RpcError::ConnectionError(e.to_string()))?; // Only provide authentication if this daemon actually expects it if let Some(header) = response.headers().get("www-authenticate") { let mut request = request("/".to_string() + route); @@ -135,11 +144,13 @@ impl HttpRpc { ); // Wait for the connection to be ready again - requester.ready().await.map_err(|_| RpcError::ConnectionError)?; + requester.ready().await.map_err(|e| RpcError::ConnectionError(e.to_string()))?; // Make the request with the response challenge - response = - requester.send_request(request).await.map_err(|_| RpcError::ConnectionError)?; + response = requester + .send_request(request) + .await + .map_err(|e| RpcError::ConnectionError(e.to_string()))?; } response @@ -164,13 +175,13 @@ impl HttpRpc { let mut body = response.into_body(); while res.len() < length { let Some(data) = body.data().await else { break }; - res.extend(data.map_err(|_| RpcError::ConnectionError)?.as_ref()); + res.extend(data.map_err(|e| RpcError::ConnectionError(e.to_string()))?.as_ref()); } */ let res = hyper::body::to_bytes(response.into_body()) .await - .map_err(|_| RpcError::ConnectionError)? + .map_err(|e| RpcError::ConnectionError(e.to_string()))? .to_vec(); if let Some(connection_task) = connection_task_handle { @@ -188,6 +199,6 @@ impl RpcConnection for HttpRpc { // TODO: Make this timeout configurable tokio::time::timeout(core::time::Duration::from_secs(30), self.inner_post(route, body)) .await - .map_err(|_| RpcError::ConnectionError)? + .map_err(|e| RpcError::ConnectionError(e.to_string()))? } } diff --git a/coins/monero/src/rpc/mod.rs b/coins/monero/src/rpc/mod.rs index 20d6089d..c4002a99 100644 --- a/coins/monero/src/rpc/mod.rs +++ b/coins/monero/src/rpc/mod.rs @@ -57,8 +57,8 @@ struct TransactionsResponse { pub enum RpcError { #[cfg_attr(feature = "std", error("internal error ({0})"))] InternalError(&'static str), - #[cfg_attr(feature = "std", error("connection error"))] - ConnectionError, + #[cfg_attr(feature = "std", error("connection error ({0})"))] + ConnectionError(String), #[cfg_attr(feature = "std", error("invalid node ({0})"))] InvalidNode(&'static str), #[cfg_attr(feature = "std", error("unsupported protocol version ({0})"))] diff --git a/processor/src/networks/monero.rs b/processor/src/networks/monero.rs index 836772e4..94d74649 100644 --- a/processor/src/networks/monero.rs +++ b/processor/src/networks/monero.rs @@ -182,6 +182,9 @@ impl PartialEq for Monero { impl Eq for Monero {} fn map_rpc_err(err: RpcError) -> NetworkError { + if let RpcError::ConnectionError(e) = &err { + log::debug!("Monero ConnectionError: {e}"); + } if let RpcError::InvalidNode(reason) = &err { log::error!("Monero RpcError::InvalidNode({reason})"); } @@ -599,7 +602,10 @@ impl Network for Monero { async fn publish_transaction(&self, tx: &Self::Transaction) -> Result<(), NetworkError> { match self.rpc.publish_transaction(tx).await { Ok(_) => Ok(()), - Err(RpcError::ConnectionError) => Err(NetworkError::ConnectionError)?, + Err(RpcError::ConnectionError(e)) => { + log::debug!("Monero ConnectionError: {e}"); + Err(NetworkError::ConnectionError)? + } // TODO: Distinguish already in pool vs double spend (other signing attempt succeeded) vs // invalid transaction Err(e) => panic!("failed to publish TX {}: {e}", hex::encode(tx.hash())),