diff --git a/libs/sdk-bindings/src/breez_sdk.udl b/libs/sdk-bindings/src/breez_sdk.udl index 4fbccd192..7d6970658 100644 --- a/libs/sdk-bindings/src/breez_sdk.udl +++ b/libs/sdk-bindings/src/breez_sdk.udl @@ -11,6 +11,7 @@ dictionary RouteHintHop { enum ServiceErrorCode { "BackupFailed", "ClientError", + "CryptographicError", "DerivationFailed", "Generic", "InitFailed", @@ -18,7 +19,6 @@ enum ServiceErrorCode { "PersistenceFailed", "RequestFailed", "ShutdownFailed", - "SignatureError", "StreamAlreadyCreated", "TransportError", }; @@ -55,7 +55,7 @@ enum OnchainErrorCode { enum ValueErrorCode { "ConversionFailed", - "DecodingFailed", + "InvalidInvoice", "InvalidMnemonic", "InvalidSignature", "InvalidTimestamp", diff --git a/libs/sdk-bindings/src/uniffi_binding.rs b/libs/sdk-bindings/src/uniffi_binding.rs index 2def0d3ea..e7bf0e474 100644 --- a/libs/sdk-bindings/src/uniffi_binding.rs +++ b/libs/sdk-bindings/src/uniffi_binding.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use anyhow::{anyhow, Result}; use log::{Level, LevelFilter, Metadata, Record}; use once_cell::sync::{Lazy, OnceCell}; @@ -95,10 +94,10 @@ pub fn connect( } /// If used, this must be called before `connect` -pub fn set_log_stream(log_stream: Box) -> Result<()> { +pub fn set_log_stream(log_stream: Box) -> SdkResult<()> { LOG_INIT .set(true) - .map_err(|_| anyhow!("log stream already created"))?; + .map_err(|_| SdkError::init_failed("log stream already created"))?; BindingLogger::init(log_stream); Ok(()) } diff --git a/libs/sdk-core/src/breez_services.rs b/libs/sdk-core/src/breez_services.rs index b2a0bef99..cf81995c8 100644 --- a/libs/sdk-core/src/breez_services.rs +++ b/libs/sdk-core/src/breez_services.rs @@ -15,6 +15,7 @@ use serde_json::json; use tokio::sync::{mpsc, watch, Mutex}; use tokio::time::sleep; use tonic::codegen::InterceptedService; +use tonic::metadata::errors::InvalidMetadataValue; use tonic::metadata::{Ascii, MetadataValue}; use tonic::service::Interceptor; use tonic::transport::{Channel, Uri}; @@ -218,7 +219,9 @@ impl BreezServices { "BreezServices is not running", )); } - self.shutdown_sender.send(()).map_err(anyhow::Error::msg)?; + self.shutdown_sender.send(()).map_err(|e| { + SdkError::service_error(ServiceErrorCode::ShutdownFailed, &e.to_string()) + })?; *started = false; Ok(()) } @@ -324,7 +327,9 @@ impl BreezServices { })?; let preimage_arr: [u8; 32] = preimage.into_inner(); - let decrypted = (data, &preimage_arr).try_into()?; + let decrypted = (data, &preimage_arr).try_into().map_err(|e| { + SdkError::generic(&format!("Error decrypting data: {e}")) + })?; SuccessActionProcessed::Aes { data: decrypted } } SuccessAction::Message(data) => { @@ -442,7 +447,7 @@ impl BreezServices { .sign_message(&request.message) .await .map(|signature| SignMessageResponse { signature }) - .map_err(|e| SdkError::signature_error(&format!("Failed to sign message: {e}"))) + .map_err(|e| SdkError::cryptographic_error(&format!("Failed to sign message: {e}"))) } /// Check whether given message was signed by the private key or the given @@ -455,7 +460,7 @@ impl BreezServices { .check_message(&request.message, &request.pubkey, &request.signature) .await .map(|is_valid| CheckMessageResponse { is_valid }) - .map_err(|e| SdkError::signature_error(&format!("Failed to check message: {e}"))) + .map_err(|e| SdkError::cryptographic_error(&format!("Failed to check message: {e}"))) } /// Retrieve the node up to date BackupStatus @@ -798,7 +803,7 @@ impl BreezServices { } /// Connects to the selected LSP, if any - async fn connect_lsp_peer(&self) -> Result<()> { + async fn connect_lsp_peer(&self) -> SdkResult<()> { if let Ok(lsp_info) = self.lsp_info().await { let node_id = lsp_info.pubkey; let address = lsp_info.host; @@ -848,12 +853,12 @@ impl BreezServices { } } - async fn on_event(&self, e: BreezEvent) -> Result<()> { + async fn on_event(&self, e: BreezEvent) -> SdkResult<()> { debug!("breez services got event {:?}", e); self.notify_event_listeners(e.clone()).await } - async fn notify_event_listeners(&self, e: BreezEvent) -> Result<()> { + async fn notify_event_listeners(&self, e: BreezEvent) -> SdkResult<()> { if let Err(err) = self.btc_receive_swapper.on_event(e.clone()).await { debug!( "btc_receive_swapper failed to process event {:?}: {:?}", @@ -935,7 +940,7 @@ impl BreezServices { /// Starts the BreezServices background threads. /// /// Internal method. Should only be used as part of [BreezServices::start] - async fn start_background_tasks(self: &Arc) -> Result<()> { + async fn start_background_tasks(self: &Arc) -> SdkResult<()> { // start the signer let (shutdown_signer_sender, signer_signer_receiver) = mpsc::channel(1); self.start_signer(signer_signer_receiver).await; @@ -1246,8 +1251,11 @@ impl BreezServices { async fn closed_channel_to_transaction( &self, channel: crate::models::Channel, - ) -> Result { - let now_epoch_sec = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(); + ) -> SdkResult { + let now_epoch_sec = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|e| SdkError::generic(&format!("System time error: {e}")))? + .as_secs(); let channel_closed_at = match channel.closed_at { Some(closed_at) => closed_at, @@ -1589,12 +1597,19 @@ impl BreezServer { pub(crate) async fn get_channel_opener_client( &self, - ) -> Result>> { + ) -> SdkResult>> { let s = self.server_url.clone(); - let channel = Channel::from_shared(s)?.connect().await?; + let channel = Channel::from_shared(s) + .map_err(|e| SdkError::invalid_url(&e.to_string()))? + .connect() + .await?; let api_key_metadata: Option> = match &self.api_key { - Some(key) => Some(format!("Bearer {key}").parse()?), + Some(key) => Some(format!("Bearer {key}").parse().map_err( + |e: InvalidMetadataValue| { + SdkError::service_error(ServiceErrorCode::ClientError, &e.to_string()) + }, + )?), _ => None, }; let client = diff --git a/libs/sdk-core/src/error.rs b/libs/sdk-core/src/error.rs index c2a49c44b..dffb95534 100644 --- a/libs/sdk-core/src/error.rs +++ b/libs/sdk-core/src/error.rs @@ -21,7 +21,7 @@ pub enum ServiceErrorCode { PersistenceFailed, RequestFailed, ShutdownFailed, - SignatureError, + CryptographicError, StreamAlreadyCreated, TransportError, } @@ -90,7 +90,7 @@ impl fmt::Display for OnchainErrorCode { #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] pub enum ValueErrorCode { ConversionFailed, - DecodingFailed, + InvalidInvoice, InvalidMnemonic, InvalidSignature, InvalidTimestamp, @@ -142,8 +142,19 @@ impl SdkError { ) } + pub fn conversion_failed(err: &str) -> Self { + Self::value_error(ValueErrorCode::ConversionFailed, err) + } + + pub fn cryptographic_error(err: &str) -> Self { + Self::service_error(ServiceErrorCode::CryptographicError, err) + } + pub fn decoding_failed(err: &str) -> Self { - Self::value_error(ValueErrorCode::DecodingFailed, err) + Self::value_error( + ValueErrorCode::ConversionFailed, + &format!("Decoding failed: {err}"), + ) } pub fn derive_encryption_key_failed(e: Error) -> Self { @@ -153,6 +164,13 @@ impl SdkError { ) } + pub fn encoding_failed(err: &str) -> Self { + Self::value_error( + ValueErrorCode::ConversionFailed, + &format!("Encoding failed: {err}"), + ) + } + pub fn generic(err: &str) -> Self { Self::service_error(ServiceErrorCode::Generic, err) } @@ -245,10 +263,6 @@ impl SdkError { } } - pub fn signature_error(err: &str) -> Self { - Self::service_error(ServiceErrorCode::SignatureError, err) - } - pub fn swap_provider_error(err: &str) -> Self { Self::onchain_error(OnchainErrorCode::SwapProviderError, err) } @@ -301,6 +315,12 @@ impl From for SdkError { } } +impl From for SdkError { + fn from(value: base64::DecodeError) -> Self { + Self::decoding_failed(&value.to_string()) + } +} + impl From for SdkError { fn from(value: bitcoin::hashes::hex::Error) -> Self { Self::decoding_failed(&value.to_string()) @@ -315,7 +335,7 @@ impl From for SdkError { impl From for SdkError { fn from(value: bitcoin::util::sighash::Error) -> Self { - Self::signature_error(&value.to_string()) + Self::cryptographic_error(&value.to_string()) } } @@ -331,15 +351,24 @@ impl From for SdkError { } } +impl From for SdkError { + fn from(value: std::num::ParseIntError) -> Self { + Self::value_error( + ValueErrorCode::UnknownValue, + &format!("Parsing error: {value}"), + ) + } +} + impl From for SdkError { fn from(value: secp256k1::Error) -> Self { - Self::signature_error(&value.to_string()) + Self::cryptographic_error(&value.to_string()) } } impl From for SdkError { fn from(value: tonic::transport::Error) -> Self { - Self::signature_error(&value.to_string()) + Self::service_error(ServiceErrorCode::TransportError, &value.to_string()) } } @@ -349,13 +378,6 @@ impl From for SdkError { } } -// TODO This won't be necessary when all service methods return SdkResult -impl From for SdkError { - fn from(value: anyhow::Error) -> Self { - Self::generic(&value.to_string()) - } -} - #[macro_export] macro_rules! ensure_sdk { ($cond:expr, $err:expr) => { diff --git a/libs/sdk-core/src/greenlight/error.rs b/libs/sdk-core/src/greenlight/error.rs index bb1988a27..72c75cf17 100644 --- a/libs/sdk-core/src/greenlight/error.rs +++ b/libs/sdk-core/src/greenlight/error.rs @@ -106,6 +106,7 @@ pub(crate) enum JsonRpcErrCode { WaitTimeout = 2000, } +#[allow(clippy::invalid_regex)] pub(crate) fn parse_cln_error(err: Error) -> Result> { let re = Regex::new(r"Some\((?-?\d+)\)")?; re.captures(&err.to_string()).map_or(Ok(None), |caps| { diff --git a/libs/sdk-core/src/greenlight/node_api.rs b/libs/sdk-core/src/greenlight/node_api.rs index 4c37eb48e..1b95d4649 100644 --- a/libs/sdk-core/src/greenlight/node_api.rs +++ b/libs/sdk-core/src/greenlight/node_api.rs @@ -283,7 +283,7 @@ impl Greenlight { async fn fetch_channels_and_balance( &self, - ) -> Result<( + ) -> SdkResult<( Vec, Vec, Vec, @@ -341,7 +341,7 @@ impl NodeAPI for Greenlight { use_description_hash: Option, expiry: Option, cltv: Option, - ) -> Result { + ) -> SdkResult { let mut client = self.get_node_client().await?; let request = InvoiceRequest { amount_msat: Some(AmountOrAny { @@ -353,7 +353,10 @@ impl NodeAPI for Greenlight { }), label: format!( "breez-{}", - SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|e| SdkError::generic(&format!("System time error: {e}")))? + .as_millis() ), description, preimage, @@ -372,7 +375,7 @@ impl NodeAPI for Greenlight { &self, since_timestamp: u64, balance_changed: bool, - ) -> Result { + ) -> SdkResult { info!("pull changed since {}", since_timestamp); let mut node_client = self.get_node_client().await?; @@ -437,7 +440,9 @@ impl NodeAPI for Greenlight { let mut all_channel_models: Vec = all_channels.clone().into_iter().map(|c| c.into()).collect(); - all_channel_models.extend(forgotten_closed_channels?); + all_channel_models.extend(forgotten_closed_channels.map_err(|e| { + SdkError::generic(&format!("Error mapping forgotten closed channels: {e}")) + })?); // calculate onchain balance let onchain_balance = onchain_funds.iter().fold(0, |a, b| { @@ -466,22 +471,25 @@ impl NodeAPI for Greenlight { // calculate payment limits and inbound liquidity let mut max_payable: u64 = 0; let mut max_receivable_single_channel: u64 = 0; - opened_channels.iter().try_for_each(|c| -> Result<()> { - max_payable += c - .spendable_msat - .as_ref() - .map(|a| a.msat) - .unwrap_or_default(); - let receivable_amount = c - .receivable_msat - .as_ref() - .map(|a| a.msat) - .unwrap_or_default(); - if receivable_amount > max_receivable_single_channel { - max_receivable_single_channel = receivable_amount; - } - Ok(()) - })?; + opened_channels + .iter() + .try_for_each(|c| -> Result<()> { + max_payable += c + .spendable_msat + .as_ref() + .map(|a| a.msat) + .unwrap_or_default(); + let receivable_amount = c + .receivable_msat + .as_ref() + .map(|a| a.msat) + .unwrap_or_default(); + if receivable_amount > max_receivable_single_channel { + max_receivable_single_channel = receivable_amount; + } + Ok(()) + }) + .map_err(|e| SdkError::generic(&format!("Error calculating channel liquidity: {e}")))?; let max_allowed_to_receive_msats = max(MAX_INBOUND_LIQUIDITY_MSAT - channels_balance, 0); let node_pubkey = hex::encode(node_info.id); @@ -563,7 +571,7 @@ impl NodeAPI for Greenlight { client.key_send(request).await?.into_inner().try_into() } - async fn start(&self) -> Result<()> { + async fn start(&self) -> SdkResult<()> { self.get_node_client() .await? .getinfo(pb::cln::GetinfoRequest {}) @@ -599,7 +607,7 @@ impl NodeAPI for Greenlight { } } - async fn list_peers(&self) -> Result> { + async fn list_peers(&self) -> SdkResult> { let mut client = self.get_node_client().await?; let res: cln::ListpeersResponse = client @@ -634,17 +642,23 @@ impl NodeAPI for Greenlight { Ok(verify(message.as_bytes(), signature, &pk)) } - fn sign_invoice(&self, invoice: RawInvoice) -> Result { + fn sign_invoice(&self, invoice: RawInvoice) -> SdkResult { let hrp_bytes = invoice.hrp.to_string().as_bytes().to_vec(); let data_bytes = invoice.data.to_base32(); // create the message for the signer let msg_type: u16 = 8; - let data_len: u16 = data_bytes.len().try_into()?; + let data_len: u16 = data_bytes + .len() + .try_into() + .map_err(|e: std::num::TryFromIntError| SdkError::conversion_failed(&e.to_string()))?; let mut data_len_bytes = data_len.to_be_bytes().to_vec(); let mut data_buf = data_bytes.iter().copied().map(u5::to_u8).collect(); - let hrp_len: u16 = hrp_bytes.len().try_into()?; + let hrp_len: u16 = hrp_bytes + .len() + .try_into() + .map_err(|e: std::num::TryFromIntError| SdkError::conversion_failed(&e.to_string()))?; let mut hrp_len_bytes = hrp_len.to_be_bytes().to_vec(); let mut hrp_buf = hrp_bytes.to_vec(); @@ -654,7 +668,10 @@ impl NodeAPI for Greenlight { buf.append(&mut hrp_len_bytes); buf.append(&mut hrp_buf); // Sign the invoice using the signer - let raw_result = self.signer.sign_invoice(buf)?; + let raw_result = self + .signer + .sign_invoice(buf) + .map_err(|e| SdkError::generic(&e.to_string()))?; info!( "recover id: {:?} raw = {:?}", raw_result, raw_result[64] as i32 @@ -662,14 +679,21 @@ impl NodeAPI for Greenlight { // contruct the RecoveryId let rid = RecoveryId::from_i32(raw_result[64] as i32).expect("recovery ID"); let sig = &raw_result[0..64]; - let recoverable_sig = - RecoverableSignature::from_compact(sig, rid).map_err(|e| anyhow!(e))?; + let recoverable_sig = RecoverableSignature::from_compact(sig, rid) + .map_err(|e| SdkError::conversion_failed(&e.to_string()))?; let signed_invoice: Result = invoice.sign(|_| Ok(recoverable_sig)); - Ok(signed_invoice?.to_string()) + Ok(signed_invoice + .map_err(|e| { + SdkError::value_error( + ValueErrorCode::InvalidInvoice, + &format!("Error signing invoice: {e}"), + ) + })? + .to_string()) } - async fn close_peer_channels(&self, node_id: String) -> Result> { + async fn close_peer_channels(&self, node_id: String) -> SdkResult> { let mut client = self.get_node_client().await?; let closed_channels = client .list_peer_channels(ListpeerchannelsRequest { @@ -695,7 +719,9 @@ impl NodeAPI for Greenlight { } if should_close { - let chan_id = channel.channel_id.ok_or(anyhow!("empty channel id"))?; + let chan_id = channel + .channel_id + .ok_or(SdkError::generic("Empty channel id"))?; let response = client .close(CloseRequest { id: hex::encode(chan_id), @@ -712,12 +738,10 @@ impl NodeAPI for Greenlight { tx_ids.push(hex::encode( res.into_inner() .txid - .ok_or(anyhow!("empty txid in close response"))?, + .ok_or(SdkError::generic("Empty txid in close response"))?, )); } - Err(e) => { - error!("error closing channel: {}", e); - } + Err(e) => Err(SdkError::generic(&format!("Empty closing channel: {e}")))?, }; } } @@ -742,7 +766,7 @@ impl NodeAPI for Greenlight { Ok(stream) } - async fn static_backup(&self) -> Result> { + async fn static_backup(&self) -> SdkResult> { let mut client = self.get_node_client().await?; let res = client .static_backup(StaticbackupRequest {}) diff --git a/libs/sdk-core/src/input_parser.rs b/libs/sdk-core/src/input_parser.rs index 16bd7b16f..bc8775883 100644 --- a/libs/sdk-core/src/input_parser.rs +++ b/libs/sdk-core/src/input_parser.rs @@ -206,7 +206,8 @@ pub async fn parse(input: &str) -> SdkResult { }); } - lnurl_endpoint = maybe_replace_host_with_mockito_test_host(lnurl_endpoint)?; + lnurl_endpoint = maybe_replace_host_with_mockito_test_host(lnurl_endpoint) + .map_err(|e| SdkError::generic(&e.to_string()))?; let lnurl_data: LnUrlRequestData = get_parse_and_log_response(&lnurl_endpoint).await?; let temp = lnurl_data.into(); let temp = match temp { @@ -236,7 +237,7 @@ pub async fn parse(input: &str) -> SdkResult { /// Makes a GET request to the specified `url` and logs on DEBUG: /// - the URL /// - the raw response body -pub(crate) async fn get_and_log_response(url: &str) -> Result { +pub(crate) async fn get_and_log_response(url: &str) -> SdkResult { debug!("Making GET request to: {url}"); let raw_body = reqwest::get(url).await?.text().await?; @@ -250,7 +251,9 @@ pub(crate) async fn get_parse_and_log_response(url: &str) -> SdkResult where for<'a> T: serde::de::Deserialize<'a>, { - let raw_body = get_and_log_response(url).await?; + let raw_body = get_and_log_response(url) + .await + .map_err(|e| SdkError::request_failed(&e.to_string()))?; serde_json::from_str(&raw_body).map_err(|e| { SdkError::value_error( diff --git a/libs/sdk-core/src/invoice.rs b/libs/sdk-core/src/invoice.rs index 72462e305..2f7973b5a 100644 --- a/libs/sdk-core/src/invoice.rs +++ b/libs/sdk-core/src/invoice.rs @@ -1,4 +1,3 @@ -use anyhow::{anyhow, Result}; use bitcoin::secp256k1::PublicKey; use hex::ToHex; use lightning::routing::gossip::RoutingFees; @@ -51,7 +50,7 @@ pub struct RouteHint { } impl RouteHint { - pub fn to_ldk_hint(&self) -> Result { + pub fn to_ldk_hint(&self) -> SdkResult { let mut hops = Vec::new(); for hop in self.hops.iter() { let pubkey_res = PublicKey::from_str(&hop.src_node_id)?; @@ -96,9 +95,19 @@ pub fn add_lsp_routing_hints( invoice: String, lsp_hint: Option, new_amount_msats: u64, -) -> Result { - let signed = invoice.parse::()?; - let invoice = Invoice::from_signed(signed)?; +) -> SdkResult { + let signed = invoice.parse::().map_err(|e| { + SdkError::value_error( + ValueErrorCode::InvalidInvoice, + &format!("Parsing error: {e}"), + ) + })?; + let invoice = Invoice::from_signed(signed).map_err(|e| { + SdkError::value_error( + ValueErrorCode::InvalidInvoice, + &format!("Semantic error: {e}"), + ) + })?; let mut invoice_builder = InvoiceBuilder::new(invoice.currency()) .invoice_description(invoice.description()) @@ -139,12 +148,12 @@ pub fn add_lsp_routing_hints( invoice_builder = invoice_builder.private_route(hint); } - let invoice_builder = invoice_builder.build_raw(); - - match invoice_builder { - Ok(invoice) => Ok(invoice), - Err(err) => Err(anyhow!(err)), - } + invoice_builder.build_raw().map_err(|e| { + SdkError::value_error( + ValueErrorCode::InvalidInvoice, + &format!("Error building invoice: {e}"), + ) + }) } /// Parse a BOLT11 payment request and return a structure contains the parsed fields. @@ -154,12 +163,8 @@ pub fn parse_invoice(bolt11: &str) -> SdkResult { .unwrap_or(bolt11) .parse::() .map_err(|e| SdkError::decoding_failed(&format!("Failed to parse invoice: {e}")))?; - let invoice = Invoice::from_signed(signed).map_err(|e| { - SdkError::value_error( - ValueErrorCode::ConversionFailed, - &format!("Failed to convert raw invoice: {e}"), - ) - })?; + let invoice = Invoice::from_signed(signed) + .map_err(|e| SdkError::conversion_failed(&format!("Failed to convert raw invoice: {e}")))?; let since_the_epoch = invoice .timestamp() diff --git a/libs/sdk-core/src/lnurl/auth.rs b/libs/sdk-core/src/lnurl/auth.rs index 0a5b29e09..dda9adca9 100644 --- a/libs/sdk-core/src/lnurl/auth.rs +++ b/libs/sdk-core/src/lnurl/auth.rs @@ -1,4 +1,4 @@ -use crate::error::{SdkError, SdkResult, ValueErrorCode}; +use crate::error::{SdkError, SdkResult, ServiceErrorCode, ValueErrorCode}; use crate::input_parser::get_parse_and_log_response; use crate::{LnUrlAuthRequestData, LnUrlCallbackStatus, NodeAPI}; use anyhow::{anyhow, Result}; @@ -19,7 +19,12 @@ pub(crate) async fn perform_lnurl_auth( req_data: LnUrlAuthRequestData, ) -> SdkResult { let url = Url::from_str(&req_data.url).map_err(|e| SdkError::invalid_url(&e.to_string()))?; - let linking_keys = derive_linking_keys(node_api, url)?; + let linking_keys = derive_linking_keys(node_api, url).map_err(|e| { + SdkError::service_error( + ServiceErrorCode::DerivationFailed, + &format!("Error deriving linking keys: {e}"), + ) + })?; let k1_to_sign = Message::from_slice(&hex::decode(req_data.k1)?)?; let sig = Secp256k1::new().sign_ecdsa(&k1_to_sign, &linking_keys.secret_key()); diff --git a/libs/sdk-core/src/lnurl/mod.rs b/libs/sdk-core/src/lnurl/mod.rs index 38361a386..2c2d4c0ac 100644 --- a/libs/sdk-core/src/lnurl/mod.rs +++ b/libs/sdk-core/src/lnurl/mod.rs @@ -2,18 +2,26 @@ pub(crate) mod auth; pub(crate) mod pay; pub(crate) mod withdraw; -use anyhow::Result; +use crate::error::SdkResult; /// Replaces the scheme, host and port with a local mockito host. Preserves the rest of the path. #[cfg(test)] -pub(crate) fn maybe_replace_host_with_mockito_test_host(lnurl_endpoint: String) -> Result { +pub(crate) fn maybe_replace_host_with_mockito_test_host( + lnurl_endpoint: String, +) -> SdkResult { // During tests, the mockito test URL chooses a free port. This cannot be known in advance, // so the URL has to be adjusted dynamically. + + use crate::error::SdkError; let server = crate::input_parser::tests::MOCK_HTTP_SERVER.lock().unwrap(); - let mockito_endpoint_url = reqwest::Url::parse(&server.url())?; - let mut parsed_lnurl_endpoint = reqwest::Url::parse(&lnurl_endpoint)?; + let mockito_endpoint_url = + reqwest::Url::parse(&server.url()).map_err(|e| SdkError::invalid_url(&e.to_string()))?; + let mut parsed_lnurl_endpoint = + reqwest::Url::parse(&lnurl_endpoint).map_err(|e| SdkError::invalid_url(&e.to_string()))?; - parsed_lnurl_endpoint.set_host(mockito_endpoint_url.host_str())?; + parsed_lnurl_endpoint + .set_host(mockito_endpoint_url.host_str()) + .map_err(|e| SdkError::invalid_url(&e.to_string()))?; let _ = parsed_lnurl_endpoint.set_scheme(mockito_endpoint_url.scheme()); let _ = parsed_lnurl_endpoint.set_port(mockito_endpoint_url.port()); @@ -21,7 +29,9 @@ pub(crate) fn maybe_replace_host_with_mockito_test_host(lnurl_endpoint: String) } #[cfg(not(test))] -pub(crate) fn maybe_replace_host_with_mockito_test_host(lnurl_endpoint: String) -> Result { +pub(crate) fn maybe_replace_host_with_mockito_test_host( + lnurl_endpoint: String, +) -> SdkResult { // When not called from a test, we fallback to keeping the URL intact Ok(lnurl_endpoint) } diff --git a/libs/sdk-core/src/lnurl/pay.rs b/libs/sdk-core/src/lnurl/pay.rs index 03f70cc8a..611791dd5 100644 --- a/libs/sdk-core/src/lnurl/pay.rs +++ b/libs/sdk-core/src/lnurl/pay.rs @@ -1,9 +1,9 @@ -use crate::input_parser::*; +use crate::error::{PaymentErrorCode, SdkError, SdkResult, ValueErrorCode}; use crate::invoice::parse_invoice; use crate::lnurl::maybe_replace_host_with_mockito_test_host; use crate::lnurl::pay::model::{CallbackResponse, SuccessAction, ValidatedCallbackResponse}; use crate::LnUrlErrorData; -use anyhow::{anyhow, Result}; +use crate::{ensure_sdk, input_parser::*}; use bitcoin::hashes::{sha256, Hash}; use std::str::FromStr; @@ -18,7 +18,7 @@ pub(crate) async fn validate_lnurl_pay( user_amount_sat: u64, comment: Option, req_data: LnUrlPayRequestData, -) -> Result { +) -> SdkResult { validate_user_input( user_amount_sat * 1000, &comment, @@ -53,9 +53,10 @@ fn build_pay_callback_url( user_amount_sat: u64, user_comment: &Option, req_data: &LnUrlPayRequestData, -) -> Result { +) -> SdkResult { let amount_msat = (user_amount_sat * 1000).to_string(); - let mut url = reqwest::Url::from_str(&req_data.callback)?; + let mut url = reqwest::Url::from_str(&req_data.callback) + .map_err(|e| SdkError::invalid_url(&e.to_string()))?; url.query_pairs_mut().append_pair("amount", &amount_msat); if let Some(comment) = user_comment { @@ -73,21 +74,30 @@ fn validate_user_input( condition_min_amount_msat: u64, condition_max_amount_msat: u64, condition_max_comment_len: u16, -) -> Result<()> { - if user_amount_msat < condition_min_amount_msat { - return Err(anyhow!("Amount is smaller than the minimum allowed")); - } +) -> SdkResult<()> { + ensure_sdk!( + user_amount_msat >= condition_min_amount_msat, + SdkError::payment_error( + PaymentErrorCode::InvalidAmount, + "Amount is smaller than the minimum allowed" + ) + ); - if user_amount_msat > condition_max_amount_msat { - return Err(anyhow!("Amount is bigger than the maximum allowed")); - } + ensure_sdk!( + user_amount_msat <= condition_max_amount_msat, + SdkError::payment_error( + PaymentErrorCode::InvalidAmount, + "Amount is bigger than the maximum allowed" + ) + ); match comment { None => Ok(()), Some(msg) => match msg.len() <= condition_max_comment_len as usize { true => Ok(()), - false => Err(anyhow!( - "Comment is longer than the maximum allowed comment length" + false => Err(SdkError::value_error( + ValueErrorCode::UnexpectedLength, + "Comment is longer than the maximum allowed comment length", )), }, } @@ -97,37 +107,50 @@ fn validate_invoice( user_amount_sat: u64, bolt11: &str, req_data: &LnUrlPayRequestData, -) -> Result<()> { +) -> SdkResult<()> { let invoice = parse_invoice(bolt11)?; match invoice.description_hash { - None => return Err(anyhow!("Invoice is missing description hash")), + None => { + return Err(SdkError::value_error( + ValueErrorCode::MissingParameters, + "Invoice is missing description hash", + )) + } Some(received_hash) => { // The hash is calculated from the exact metadata string, as received from the LNURL endpoint let calculated_hash = sha256::Hash::hash(req_data.metadata_str.as_bytes()); if received_hash != calculated_hash.to_string() { - return Err(anyhow!("Invoice has an invalid description hash")); + return Err(SdkError::value_error( + ValueErrorCode::UnknownValue, + "Invoice has an invalid description hash", + )); } } } match invoice.amount_msat { - None => Err(anyhow!("Amount is bigger than the maximum allowed")), + None => Err(SdkError::payment_error( + PaymentErrorCode::InvalidAmount, + "Amount is bigger than the maximum allowed", + )), Some(invoice_amount_msat) => match invoice_amount_msat == (user_amount_sat * 1000) { true => Ok(()), - false => Err(anyhow!( - "Invoice amount is different than the user chosen amount" + false => Err(SdkError::payment_error( + PaymentErrorCode::InvalidAmount, + "Invoice amount is different than the user chosen amount", )), }, } } pub(crate) mod model { - use crate::input_parser::*; + use crate::error::{SdkError, SdkResult, ValueErrorCode}; use crate::lnurl::pay::{Aes256CbcDec, Aes256CbcEnc}; + use crate::{ensure_sdk, input_parser::*}; use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit}; - use anyhow::{anyhow, Result}; + use anyhow::Result; use serde::{Deserialize, Serialize}; pub(crate) enum ValidatedCallbackResponse { @@ -233,23 +256,33 @@ pub(crate) mod model { impl AesSuccessActionData { /// Validates the fields, but does not decrypt and validate the ciphertext. - pub fn validate(&self) -> Result<()> { - if self.description.len() > 144 { - return Err(anyhow!( + pub fn validate(&self) -> SdkResult<()> { + ensure_sdk!( + self.description.len() <= 144, + SdkError::value_error( + ValueErrorCode::UnexpectedLength, "AES action description length is larger than the maximum allowed" - )); - } + ) + ); - if self.ciphertext.len() > 4096 { - return Err(anyhow!( + ensure_sdk!( + self.ciphertext.len() <= 4096, + SdkError::value_error( + ValueErrorCode::UnexpectedLength, "AES action ciphertext length is larger than the maximum allowed" - )); - } + ) + ); + base64::decode(&self.ciphertext)?; - if self.iv.len() != 24 { - return Err(anyhow!("AES action iv has unexpected length")); - } + ensure_sdk!( + self.iv.len() == 24, + SdkError::value_error( + ValueErrorCode::UnexpectedLength, + "AES action iv has unexpected length" + ) + ); + base64::decode(&self.iv)?; Ok(()) @@ -290,39 +323,43 @@ pub(crate) mod model { } impl MessageSuccessActionData { - pub fn validate(&self) -> Result<()> { + pub fn validate(&self) -> SdkResult<()> { match self.message.len() <= 144 { true => Ok(()), - false => Err(anyhow!( - "Success action message is longer than the maximum allowed length" + false => Err(SdkError::value_error( + ValueErrorCode::UnexpectedLength, + "Success action message is longer than the maximum allowed length", )), } } } impl UrlSuccessActionData { - pub fn validate(&self, req_data: &LnUrlPayRequestData) -> Result<()> { + pub fn validate(&self, req_data: &LnUrlPayRequestData) -> SdkResult<()> { match self.description.len() <= 144 { true => Ok(()), - false => Err(anyhow!( - "Success action description is longer than the maximum allowed length" + false => Err(SdkError::value_error( + ValueErrorCode::UnexpectedLength, + "Success action description is longer than the maximum allowed length", )), } .and_then(|_| { - let req_url = reqwest::Url::parse(&req_data.callback)?; + let req_url = reqwest::Url::parse(&req_data.callback) + .map_err(|e| SdkError::invalid_url(&e.to_string()))?; let req_domain = req_url .domain() - .ok_or_else(|| anyhow!("Could not determine callback domain"))?; + .ok_or_else(|| SdkError::invalid_url("Could not determine callback domain"))?; - let action_res_url = reqwest::Url::parse(&self.url)?; - let action_res_domain = action_res_url - .domain() - .ok_or_else(|| anyhow!("Could not determine Success Action URL domain"))?; + let action_res_url = reqwest::Url::parse(&self.url) + .map_err(|e| SdkError::invalid_url(&e.to_string()))?; + let action_res_domain = action_res_url.domain().ok_or_else(|| { + SdkError::invalid_url("Could not determine Success Action URL domain") + })?; match req_domain == action_res_domain { true => Ok(()), - false => Err(anyhow!( - "Success Action URL has different domain than the callback domain" + false => Err(SdkError::invalid_url( + "Success Action URL has different domain than the callback domain", )), } }) diff --git a/libs/sdk-core/src/lnurl/withdraw.rs b/libs/sdk-core/src/lnurl/withdraw.rs index 1ba48cd0e..caee4b809 100644 --- a/libs/sdk-core/src/lnurl/withdraw.rs +++ b/libs/sdk-core/src/lnurl/withdraw.rs @@ -38,7 +38,8 @@ pub(crate) async fn validate_lnurl_withdraw( ); // Send invoice to the LNURL-w endpoint via the callback - let callback_url = build_withdraw_callback_url(&req_data, &invoice)?; + let callback_url = build_withdraw_callback_url(&req_data, &invoice) + .map_err(|e| SdkError::generic(&format!("Error building callback url: {e}")))?; let callback_res: LnUrlCallbackStatus = get_parse_and_log_response(&callback_url).await?; let withdraw_status = match callback_res { LnUrlCallbackStatus::Ok => LnUrlWithdrawResult::Ok { diff --git a/libs/sdk-core/src/lsp.rs b/libs/sdk-core/src/lsp.rs index 0560c93d0..780f1ce2a 100644 --- a/libs/sdk-core/src/lsp.rs +++ b/libs/sdk-core/src/lsp.rs @@ -99,7 +99,7 @@ impl LspInformation { #[tonic::async_trait] impl LspAPI for BreezServer { - async fn list_lsps(&self, pubkey: String) -> Result> { + async fn list_lsps(&self, pubkey: String) -> SdkResult> { let mut client = self.get_channel_opener_client().await?; let request = Request::new(LspListRequest { pubkey }); @@ -120,16 +120,19 @@ impl LspAPI for BreezServer { lsp_id: String, lsp_pubkey: Vec, payment_info: PaymentInformation, - ) -> Result { + ) -> SdkResult { let mut client = self.get_channel_opener_client().await?; let mut buf = Vec::new(); buf.reserve(payment_info.encoded_len()); - payment_info.encode(&mut buf)?; + payment_info + .encode(&mut buf) + .map_err(|e| SdkError::encoding_failed(&e.to_string()))?; let request = Request::new(RegisterPaymentRequest { lsp_id, - blob: encrypt(lsp_pubkey, buf)?, + blob: encrypt(lsp_pubkey, buf) + .map_err(|e| SdkError::cryptographic_error(&e.to_string()))?, }); let response = client.register_payment(request).await?; diff --git a/libs/sdk-core/src/models.rs b/libs/sdk-core/src/models.rs index 19578c0f2..d1b09d8ae 100644 --- a/libs/sdk-core/src/models.rs +++ b/libs/sdk-core/src/models.rs @@ -69,12 +69,12 @@ pub trait NodeAPI: Send + Sync { use_description_hash: Option, expiry: Option, cltv: Option, - ) -> Result; + ) -> SdkResult; async fn pull_changed( &self, since_timestamp: u64, balance_changed: bool, - ) -> Result; + ) -> SdkResult; /// As per the `pb::PayRequest` docs, `amount_sats` is only needed when the invoice doesn't specify an amount async fn send_payment( &self, @@ -86,16 +86,16 @@ pub trait NodeAPI: Send + Sync { node_id: String, amount_sats: u64, ) -> Result; - async fn start(&self) -> Result<()>; + async fn start(&self) -> SdkResult<()>; async fn sweep(&self, to_address: String, fee_rate_sats_per_vbyte: u32) -> SdkResult>; async fn start_signer(&self, shutdown: mpsc::Receiver<()>); - async fn list_peers(&self) -> Result>; + async fn list_peers(&self) -> SdkResult>; async fn connect_peer(&self, node_id: String, addr: String) -> Result<()>; - fn sign_invoice(&self, invoice: RawInvoice) -> Result; - async fn close_peer_channels(&self, node_id: String) -> Result>; + fn sign_invoice(&self, invoice: RawInvoice) -> SdkResult; + async fn close_peer_channels(&self, node_id: String) -> SdkResult>; async fn stream_incoming_payments(&self) -> Result>; async fn stream_log_messages(&self) -> Result>; - async fn static_backup(&self) -> Result>; + async fn static_backup(&self) -> SdkResult>; async fn execute_command(&self, command: String) -> SdkResult; async fn sign_message(&self, message: &str) -> Result; async fn check_message(&self, message: &str, pubkey: &str, signature: &str) -> Result; @@ -112,13 +112,13 @@ pub trait NodeAPI: Send + Sync { /// Trait covering LSP-related functionality #[tonic::async_trait] pub trait LspAPI: Send + Sync { - async fn list_lsps(&self, node_pubkey: String) -> Result>; + async fn list_lsps(&self, node_pubkey: String) -> SdkResult>; async fn register_payment( &self, lsp_id: String, lsp_pubkey: Vec, payment_info: PaymentInformation, - ) -> Result; + ) -> SdkResult; } /// Trait covering fiat-related functionality @@ -150,7 +150,7 @@ pub trait SwapperAPI: Send + Sync { hash: Vec, payer_pubkey: Vec, node_pubkey: String, - ) -> Result; + ) -> SdkResult; async fn complete_swap(&self, bolt11: String) -> Result<()>; } @@ -341,7 +341,7 @@ impl FullReverseSwapInfo { } /// Derives the lockup address from the redeem script - pub(crate) fn get_lockup_address(&self, network: Network) -> Result
{ + pub(crate) fn get_lockup_address(&self, network: Network) -> SdkResult
{ let redeem_script = Script::from_hex(&self.redeem_script)?; Ok(Address::p2wsh(&redeem_script, network.into())) } @@ -1107,7 +1107,7 @@ impl SwapInfo { } } -pub(crate) fn parse_short_channel_id(id_str: &str) -> Result { +pub(crate) fn parse_short_channel_id(id_str: &str) -> SdkResult { let parts: Vec<&str> = id_str.split('x').collect(); if parts.len() != 3 { return Ok(0); diff --git a/libs/sdk-core/src/moonpay.rs b/libs/sdk-core/src/moonpay.rs index 368035b86..819f12d51 100644 --- a/libs/sdk-core/src/moonpay.rs +++ b/libs/sdk-core/src/moonpay.rs @@ -1,7 +1,7 @@ -use anyhow::Result; use reqwest::Url; use crate::breez_services::BreezServer; +use crate::error::{SdkError, SdkResult}; use crate::grpc::SignUrlRequest; use crate::SwapInfo; @@ -28,7 +28,7 @@ pub(crate) fn moonpay_config() -> MoonPayConfig { } } -async fn create_moonpay_url(wallet_address: &str, max_amount: &str) -> Result { +async fn create_moonpay_url(wallet_address: &str, max_amount: &str) -> SdkResult { let config = moonpay_config(); let url = Url::parse_with_params( &config.base_url, @@ -41,18 +41,19 @@ async fn create_moonpay_url(wallet_address: &str, max_amount: &str) -> Result Result; + async fn buy_bitcoin_url(&self, swap_info: &SwapInfo) -> SdkResult; } #[tonic::async_trait] impl MoonPayApi for BreezServer { - async fn buy_bitcoin_url(&self, swap_info: &SwapInfo) -> Result { + async fn buy_bitcoin_url(&self, swap_info: &SwapInfo) -> SdkResult { let config = moonpay_config(); let url = create_moonpay_url( swap_info.bitcoin_address.as_str(), diff --git a/libs/sdk-core/src/persist/cache.rs b/libs/sdk-core/src/persist/cache.rs index bb06330cd..357fbb890 100644 --- a/libs/sdk-core/src/persist/cache.rs +++ b/libs/sdk-core/src/persist/cache.rs @@ -1,10 +1,9 @@ -use crate::models::NodeState; +use crate::{error::SdkResult, models::NodeState}; use super::db::SqliteStorage; -use anyhow::Result; impl SqliteStorage { - pub fn get_cached_item(&self, key: String) -> Result> { + pub fn get_cached_item(&self, key: String) -> SdkResult> { let res = self.get_connection()?.query_row( "SELECT value FROM cached_items WHERE key = ?1", [key], @@ -13,7 +12,7 @@ impl SqliteStorage { Ok(res.ok()) } - pub fn update_cached_item(&self, key: String, value: String) -> Result<()> { + pub fn update_cached_item(&self, key: String, value: String) -> SdkResult<()> { self.get_connection()?.execute( "INSERT OR REPLACE INTO cached_items (key, value) VALUES (?1,?2)", (key, value), @@ -22,19 +21,19 @@ impl SqliteStorage { } #[allow(dead_code)] - pub fn delete_cached_item(&self, key: String) -> Result<()> { + pub fn delete_cached_item(&self, key: String) -> SdkResult<()> { self.get_connection()? .execute("DELETE FROM cached_items WHERE key = ?1", [key])?; Ok(()) } - pub fn set_node_state(&self, state: &NodeState) -> Result<()> { + pub fn set_node_state(&self, state: &NodeState) -> SdkResult<()> { let serialized_state = serde_json::to_string(state)?; self.update_cached_item("node_state".to_string(), serialized_state)?; Ok(()) } - pub fn get_node_state(&self) -> Result> { + pub fn get_node_state(&self) -> SdkResult> { let state_str = self.get_cached_item("node_state".to_string())?; Ok(match state_str { Some(str) => serde_json::from_str(str.as_str())?, @@ -42,37 +41,37 @@ impl SqliteStorage { }) } - pub fn set_last_backup_time(&self, t: u64) -> Result<()> { + pub fn set_last_backup_time(&self, t: u64) -> SdkResult<()> { self.update_cached_item("last_backup_time".to_string(), t.to_string())?; Ok(()) } - pub fn get_last_backup_time(&self) -> Result> { + pub fn get_last_backup_time(&self) -> SdkResult> { let state_str = self.get_cached_item("last_backup_time".to_string())?; Ok(match state_str { Some(str) => str.as_str().parse::().ok(), None => None, }) } - pub fn set_gl_credentials(&self, creds: Vec) -> Result<()> { + pub fn set_gl_credentials(&self, creds: Vec) -> SdkResult<()> { self.update_cached_item("gl_credentials".to_string(), hex::encode(creds))?; Ok(()) } - pub fn get_gl_credentials(&self) -> Result>> { + pub fn get_gl_credentials(&self) -> SdkResult>> { match self.get_cached_item("gl_credentials".to_string())? { Some(str) => Ok(Some(hex::decode(str)?)), None => Ok(None), } } - pub fn set_static_backup(&self, backup: Vec) -> Result<()> { + pub fn set_static_backup(&self, backup: Vec) -> SdkResult<()> { let serialized_state = serde_json::to_string(&backup)?; self.update_cached_item("static_backup".to_string(), serialized_state)?; Ok(()) } - pub fn get_static_backup(&self) -> Result>> { + pub fn get_static_backup(&self) -> SdkResult>> { let backup_str = self.get_cached_item("static_backup".to_string())?; Ok(match backup_str { Some(str) => serde_json::from_str(str.as_str())?, diff --git a/libs/sdk-core/src/persist/channels.rs b/libs/sdk-core/src/persist/channels.rs index 74f484631..8064d3b19 100644 --- a/libs/sdk-core/src/persist/channels.rs +++ b/libs/sdk-core/src/persist/channels.rs @@ -1,11 +1,10 @@ -use crate::models::*; +use crate::{error::SdkResult, models::*}; use super::db::SqliteStorage; -use anyhow::Result; use std::str::FromStr; impl SqliteStorage { - pub(crate) fn update_channels(&self, channels: &[Channel]) -> Result<()> { + pub(crate) fn update_channels(&self, channels: &[Channel]) -> SdkResult<()> { // insert all channels for c in channels.iter().cloned() { self.insert_or_update_channel(c)? @@ -36,7 +35,7 @@ impl SqliteStorage { Ok(()) } - pub(crate) fn list_channels(&self) -> Result> { + pub(crate) fn list_channels(&self) -> SdkResult> { let con = self.get_connection()?; let mut stmt = con.prepare( " @@ -75,7 +74,7 @@ impl SqliteStorage { Ok(channels) } - pub(crate) fn insert_or_update_channel(&self, c: Channel) -> Result<()> { + pub(crate) fn insert_or_update_channel(&self, c: Channel) -> SdkResult<()> { self.get_connection()?.execute( "INSERT OR REPLACE INTO channels ( funding_txid, diff --git a/libs/sdk-core/src/persist/swap.rs b/libs/sdk-core/src/persist/swap.rs index 0d9751ee7..0bc91d0d3 100644 --- a/libs/sdk-core/src/persist/swap.rs +++ b/libs/sdk-core/src/persist/swap.rs @@ -1,15 +1,15 @@ use crate::{ - error::SdkResult, + error::{SdkError, SdkResult}, models::{SwapInfo, SwapStatus}, }; use super::db::{SqliteStorage, StringArray}; use crate::OpeningFeeParams; -use anyhow::{anyhow, Result}; +use anyhow::Result; use rusqlite::{named_params, OptionalExtension, Params, Row, Transaction}; impl SqliteStorage { - pub(crate) fn insert_swap(&self, swap_info: SwapInfo) -> Result<()> { + pub(crate) fn insert_swap(&self, swap_info: SwapInfo) -> SdkResult<()> { let mut con = self.get_connection()?; let tx = con.transaction()?; @@ -70,9 +70,9 @@ impl SqliteStorage { Self::insert_swaps_fees( &tx, swap_info.bitcoin_address, - swap_info - .channel_opening_fees - .ok_or_else(|| anyhow!("Dynamic fees must be set when creating a new swap"))?, + swap_info.channel_opening_fees.ok_or_else(|| { + SdkError::generic("Dynamic fees must be set when creating a new swap") + })?, )?; tx.commit()?; @@ -127,7 +127,7 @@ impl SqliteStorage { tx: &Transaction, bitcoin_address: String, channel_opening_fees: OpeningFeeParams, - ) -> Result<()> { + ) -> SdkResult<()> { tx.execute( "INSERT OR REPLACE INTO sync.swaps_fees (bitcoin_address, created_at, channel_opening_fees) VALUES(:bitcoin_address, CURRENT_TIMESTAMP, :channel_opening_fees)", named_params! { @@ -144,7 +144,7 @@ impl SqliteStorage { &self, bitcoin_address: String, channel_opening_fees: OpeningFeeParams, - ) -> Result<()> { + ) -> SdkResult<()> { let mut con = self.get_connection()?; let tx = con.transaction()?; @@ -228,23 +228,23 @@ impl SqliteStorage { ) } - fn select_single_swap

(&self, where_clause: &str, params: P) -> Result> + fn select_single_swap

(&self, where_clause: &str, params: P) -> SdkResult> where P: Params, { - self.get_connection()? + Ok(self + .get_connection()? .query_row(&self.select_swap_query(where_clause), params, |row| { self.sql_row_to_swap(row) }) - .optional() - .map_err(|e| anyhow!(e)) + .optional()?) } - pub(crate) fn get_swap_info_by_hash(&self, hash: &Vec) -> Result> { + pub(crate) fn get_swap_info_by_hash(&self, hash: &Vec) -> SdkResult> { self.select_single_swap("payment_hash = ?1", [hash]) } - pub(crate) fn get_swap_info_by_address(&self, address: String) -> Result> { + pub(crate) fn get_swap_info_by_address(&self, address: String) -> SdkResult> { self.select_single_swap("swaps.bitcoin_address = ?1", [address]) } diff --git a/libs/sdk-core/src/persist/sync.rs b/libs/sdk-core/src/persist/sync.rs index 297118e07..e1bd4c295 100644 --- a/libs/sdk-core/src/persist/sync.rs +++ b/libs/sdk-core/src/persist/sync.rs @@ -1,4 +1,4 @@ -use crate::ReverseSwapStatus; +use crate::{error::SdkResult, ReverseSwapStatus}; use super::db::SqliteStorage; use anyhow::Result; @@ -66,13 +66,13 @@ impl SqliteStorage { Ok(version) } - pub fn get_last_sync_request(&self) -> Result> { + pub fn get_last_sync_request(&self) -> SdkResult> { let res: rusqlite::Result> = self.get_connection()? .query_row("SELECT max(id) FROM sync.sync_requests", [], |row| { row.get::>(0) }); - res.map_err(anyhow::Error::msg) + Ok(res?) } pub(crate) fn delete_sync_requests_up_to(&self, request_id: u64) -> Result<()> { diff --git a/libs/sdk-core/src/persist/transactions.rs b/libs/sdk-core/src/persist/transactions.rs index 724825db7..7eb43e13c 100644 --- a/libs/sdk-core/src/persist/transactions.rs +++ b/libs/sdk-core/src/persist/transactions.rs @@ -99,7 +99,7 @@ impl SqliteStorage { &self, payment_hash: &str, payer_amount_msat: u64, - ) -> Result<()> { + ) -> SdkResult<()> { let con = self.get_connection()?; let mut prep_statement = con.prepare( " diff --git a/libs/sdk-core/src/reverseswap.rs b/libs/sdk-core/src/reverseswap.rs index 1eb8f0cda..4f677409f 100644 --- a/libs/sdk-core/src/reverseswap.rs +++ b/libs/sdk-core/src/reverseswap.rs @@ -213,7 +213,10 @@ impl BTCSendSwap { .create_reverse_swap_on_remote( amount_sat, reverse_swap_keys.preimage_hash_bytes().to_hex(), - reverse_swap_keys.public_key()?.to_hex(), + reverse_swap_keys + .public_key() + .map_err(|e| SdkError::generic(&e.to_string()))? + .to_hex(), pair_hash.clone(), routing_node, ) diff --git a/libs/sdk-core/src/swap.rs b/libs/sdk-core/src/swap.rs index 3bea68ea8..7c68156bf 100644 --- a/libs/sdk-core/src/swap.rs +++ b/libs/sdk-core/src/swap.rs @@ -30,7 +30,7 @@ impl SwapperAPI for BreezServer { hash: Vec, payer_pubkey: Vec, node_id: String, - ) -> Result { + ) -> SdkResult { let mut fund_client = self.get_fund_manager_client().await?; let request = AddFundInitRequest { hash: hash.clone(), @@ -152,7 +152,9 @@ impl BTCReceiveSwap { // create swap keys let swap_keys = create_swap_keys()?; - let pubkey = swap_keys.public_key_bytes()?; + let pubkey = swap_keys + .public_key_bytes() + .map_err(|e| SdkError::generic(&e.to_string()))?; let hash = swap_keys.preimage_hash_bytes(); // use swap API to fetch a new swap address @@ -263,7 +265,7 @@ impl BTCReceiveSwap { } #[allow(dead_code)] - pub(crate) fn get_swap_info(&self, address: String) -> Result> { + pub(crate) fn get_swap_info(&self, address: String) -> SdkResult> { self.persister.get_swap_info_by_address(address) } @@ -524,7 +526,7 @@ impl SwapKeys { } } -pub(crate) fn create_swap_keys() -> Result { +pub(crate) fn create_swap_keys() -> SdkResult { let priv_key = rand::thread_rng().gen::<[u8; 32]>().to_vec(); let preimage = rand::thread_rng().gen::<[u8; 32]>().to_vec(); Ok(SwapKeys { priv_key, preimage }) @@ -535,7 +537,7 @@ pub(crate) fn create_submarine_swap_script( swapper_pub_key: Vec, payer_pub_key: Vec, lock_height: i64, -) -> Result