From 8bc9fa951faedf83f2ee18c0b47794da1197ceea Mon Sep 17 00:00:00 2001 From: michael1011 Date: Thu, 2 Jan 2025 17:57:05 +0100 Subject: [PATCH] chore: nicer CLN error parsing --- boltzr/src/lightning/cln/mod.rs | 50 ++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/boltzr/src/lightning/cln/mod.rs b/boltzr/src/lightning/cln/mod.rs index e149a499..c32d4f64 100644 --- a/boltzr/src/lightning/cln/mod.rs +++ b/boltzr/src/lightning/cln/mod.rs @@ -4,6 +4,7 @@ use crate::lightning::cln::cln_rpc::{ ListconfigsResponse, }; use alloy::hex; +use anyhow::anyhow; use async_trait::async_trait; use serde::{Deserialize, Serialize}; use std::fs; @@ -76,7 +77,8 @@ impl Cln { recurrence_label: None, recurrence_counter: None, }) - .await?; + .await + .map_err(Self::parse_error)?; Ok(res.into_inner().invoice) } @@ -93,6 +95,34 @@ impl Cln { .await?; Ok(res.into_inner()) } + + fn parse_error(status: tonic::Status) -> anyhow::Error { + let mut msg = status.message(); + + const MESSAGE_PREFIXES: &[&str] = &["message: \"", "message\":\"", "message\\\":\\\""]; + const MESSAGE_SUFFIXES: &[&str] = &["\", data:", "\\\"}", "\"}"]; + + 'outer: loop { + for prefix in MESSAGE_PREFIXES { + if let Some(start) = msg.find(prefix) { + for suffix in MESSAGE_SUFFIXES { + if let Some(end) = msg.rfind(suffix) { + msg = &msg[start + prefix.len()..end]; + continue 'outer; + } + } + } + } + + break; + } + + if msg.is_empty() { + return anyhow!("{}", status.code().to_string()); + } + + anyhow!(msg.to_owned()) + } } #[async_trait] @@ -137,3 +167,21 @@ impl BaseClient for Cln { Ok(()) } } + +#[cfg(test)] +mod test { + use super::*; + use rstest::*; + + #[rstest] + #[case("Error calling method Xpay: RpcError { code: Some(203), message: \"Destination said it doesn't know invoice: incorrect_or_unknown_payment_details\", data: None }", "Destination said it doesn't know invoice: incorrect_or_unknown_payment_details")] + #[case("Failed: could not route or connect directly to 02ead25af6e0271c5167d6fd05545d2d538995ce3e16ad780a1010fc4e91522202: {\"code\":400,\"message\":\"Unable to connect, no address known for peer\"}", "Unable to connect, no address known for peer")] + #[case("Error calling method FetchInvoice: RpcError { code: Some(1003), message: \"Failed: could not route or connect directly to 02ee337aec2b12fef609a57d1c32834fe3107d315acaf8f5206ccf94dc2a9baa8c: {\"code\":400,\"message\":\"Unable to connect, no address known for peer\"}\", data: None }", "Unable to connect, no address known for peer")] + #[case("Error calling method FetchInvoice: RpcError { code: Some(1003), message: \"Failed: could not route or connect directly to 02ee337aec2b12fef609a57d1c32834fe3107d315acaf8f5206ccf94dc2a9baa8c: {\\\"code\\\":400,\\\"message\\\":\\\"Unable to connect, no address known for peer\\\"}\", data: None }", "Unable to connect, no address known for peer")] + #[case("gRPC error", "gRPC error")] + #[case("", "The operation was cancelled")] + fn test_parse_error(#[case] msg: &str, #[case] expected: &str) { + let error = Cln::parse_error(tonic::Status::new(tonic::Code::Cancelled, msg)); + assert_eq!(error.to_string(), expected); + } +}