From f0b625c1e042a96129e1bd3f32c00b6b9b161232 Mon Sep 17 00:00:00 2001 From: Andrei <92177534+andrei-21@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:59:34 +0100 Subject: [PATCH] Update breez-sdk mock to the new pay on-chain API (#1111) * Bump uniffi to 0.28.0 (#1108) * Move `ParseError` to errors (#1105) * Migrate to the new pay on-chain API (#1102) * Update breez-sdk mock to the new pay on-chain API --- Cargo.lock | 61 ++++++--------- Cargo.toml | 4 +- build.rs | 4 +- examples/node/cli.rs | 12 +-- mock/breez-sdk/src/lib.rs | 70 +++++++++++------ src/errors.rs | 26 ++++++ src/lib.rs | 152 +++++++++++++++++------------------- src/lipalightninglib.udl | 21 ++++- src/symmetric_encryption.rs | 7 +- 9 files changed, 201 insertions(+), 156 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a661bc8ca..d1965c657 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1697,7 +1697,7 @@ checksum = "5e27ed0c2cf0c0cc52c6bcf3b45c907f433015e580879d14005386251842fb0a" dependencies = [ "graphql-introspection-query", "graphql-parser", - "heck", + "heck 0.4.1", "lazy_static", "proc-macro2", "quote", @@ -1777,6 +1777,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -2587,12 +2593,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d11de466f4a3006fe8a5e7ec84e93b79c70cb992ae0aa0eb631ad2df8abfe2" -[[package]] -name = "oneshot-uniffi" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c548d5c78976f6955d72d0ced18c48ca07030f7a1d4024529fedd7c1c01b29c" - [[package]] name = "opaque-debug" version = "0.3.1" @@ -2994,7 +2994,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes", - "heck", + "heck 0.4.1", "itertools", "lazy_static", "log", @@ -3879,7 +3879,7 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -3966,8 +3966,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" dependencies = [ "smawk", - "unicode-linebreak", - "unicode-width", ] [[package]] @@ -4362,12 +4360,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - [[package]] name = "unicode-normalization" version = "0.1.22" @@ -4397,9 +4389,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "uniffi" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5566fae48a5cb017005bf9cd622af5236b2a203a13fb548afde3506d3c68277" +checksum = "f31bff6daf87277a9014bcdefbc2842b0553392919d1096843c5aad899ca4588" dependencies = [ "anyhow", "uniffi_core", @@ -4408,9 +4400,9 @@ dependencies = [ [[package]] name = "uniffi_bindgen" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a77bb514bcd4bf27c9bd404d7c3f2a6a8131b957eba9c22cfeb7751c4278e09" +checksum = "96061d7e01b185aa405f7c9b134741ab3e50cc6796a47d6fd8ab9a5364b5feed" dependencies = [ "anyhow", "askama", @@ -4419,7 +4411,7 @@ dependencies = [ "fs-err", "glob", "goblin", - "heck", + "heck 0.5.0", "once_cell", "paste", "serde", @@ -4432,9 +4424,9 @@ dependencies = [ [[package]] name = "uniffi_checksum_derive" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae7e5a6c33b1dec3f255f57ec0b6af0f0b2bb3021868be1d5eec7a38e2905ebc" +checksum = "2fcfa22f55829d3aaa7acfb1c5150224188fe0f27c59a8a3eddcaa24d1ffbe58" dependencies = [ "quote", "syn 2.0.58", @@ -4442,25 +4434,24 @@ dependencies = [ [[package]] name = "uniffi_core" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea3eb5474d50fc149b7e4d86b9c5bd4a61dcc167f0683902bf18ae7bbb3deef" +checksum = "3210d57d6ab6065ab47a2898dacdb7c606fd6a4156196831fa3bf82e34ac58a6" dependencies = [ "anyhow", "bytes", "camino", "log", "once_cell", - "oneshot-uniffi", "paste", "static_assertions", ] [[package]] name = "uniffi_macros" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18331d35003f46f0d04047fbe4227291815b83a937a8c32bc057f990962182c4" +checksum = "b58691741080935437dc862122e68d7414432a11824ac1137868de46181a0bd2" dependencies = [ "bincode", "camino", @@ -4476,9 +4467,9 @@ dependencies = [ [[package]] name = "uniffi_meta" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7224422c4cfd181c7ca9fca2154abca4d21db962f926f270f996edd38b0c4b8" +checksum = "7663eacdbd9fbf4a88907ddcfe2e6fa85838eb6dc2418a7d91eebb3786f8e20b" dependencies = [ "anyhow", "bytes", @@ -4488,9 +4479,9 @@ dependencies = [ [[package]] name = "uniffi_testing" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ce878d0bdfc288b58797044eaaedf748526c56eef3575380bb4d4b19d69eee" +checksum = "f922465f7566f25f8fe766920205fdfa9a3fcdc209c6bfb7557f0b5bf45b04dd" dependencies = [ "anyhow", "camino", @@ -4501,9 +4492,9 @@ dependencies = [ [[package]] name = "uniffi_udl" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c43c9ed40a8d20a5c3eae2d23031092db6b96dc8e571beb449ba9757484cea0" +checksum = "cef408229a3a407fafa4c36dc4f6ece78a6fb258ab28d2b64bddd49c8cb680f6" dependencies = [ "anyhow", "textwrap", diff --git a/Cargo.toml b/Cargo.toml index bbb44911d..5f6c6b8bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ sha2 = { version = "0.10.8", features = [] } simplelog = { version = "0.12.2" } thiserror = "1.0.61" tokio = { version = "1.38.0", features = ["rt-multi-thread", "time", "sync"] } -uniffi = "0.27.0" +uniffi = "0.28.0" uuid = { version = "1.8.0", features = ["v5"] } [features] @@ -99,7 +99,7 @@ lazy_static = "1.4.0" [build-dependencies] camino = "1.1.7" -uniffi_bindgen = "0.27.0" +uniffi_bindgen = "0.28.0" # Use some of the binary size reduction strategies from https://github.com/johnthagen/min-sized-rust [profile.release] diff --git a/build.rs b/build.rs index df80bb66d..27647cdfc 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,7 @@ use camino::Utf8Path; use std::env; -use uniffi_bindgen::bindings::kotlin::gen_kotlin::KotlinBindingGenerator; -use uniffi_bindgen::bindings::swift::gen_swift::SwiftBindingGenerator; +use uniffi_bindgen::bindings::KotlinBindingGenerator; +use uniffi_bindgen::bindings::SwiftBindingGenerator; fn main() { let udl_file = Utf8Path::new("src/lipalightninglib.udl"); diff --git a/examples/node/cli.rs b/examples/node/cli.rs index efb2d0dac..ea6032a63 100644 --- a/examples/node/cli.rs +++ b/examples/node/cli.rs @@ -22,7 +22,7 @@ use uniffi_lipalightninglib::{ ExchangeRate, FailedSwapInfo, FiatValue, IncomingPaymentInfo, InvoiceCreationMetadata, InvoiceDetails, LightningNode, LiquidityLimit, LnUrlPayDetails, LnUrlWithdrawDetails, MaxRoutingFeeMode, OfferInfo, OfferKind, OutgoingPaymentInfo, PaymentInfo, PaymentMetadata, - Recipient, TzConfig, + RangeHit, Recipient, TzConfig, }; pub(crate) fn poll_for_user_input(node: &LightningNode, log_file_path: &str) { @@ -1346,11 +1346,11 @@ fn sweep(node: &LightningNode, address: String) -> Result { } fn clear_wallet_info(node: &LightningNode) -> Result<()> { - ensure!( - node.is_clear_wallet_feasible()?, - "Clearing the wallet isn't feasible at the moment due to the available funds being \ - either too low or too high" - ); + match node.check_clear_wallet_feasibility()? { + RangeHit::Below { min } => bail!("Balance is below min: {}", amount_to_string(&min)), + RangeHit::In => (), + RangeHit::Above { max } => bail!("Balance is above max: {}", amount_to_string(&max)), + }; let clear_wallet_info = node.prepare_clear_wallet()?; diff --git a/mock/breez-sdk/src/lib.rs b/mock/breez-sdk/src/lib.rs index 5378c7082..44167501f 100644 --- a/mock/breez-sdk/src/lib.rs +++ b/mock/breez-sdk/src/lib.rs @@ -32,7 +32,7 @@ const OPENING_FEE_PARAMS_PROMISE: &str = "promite"; const SWAP_MIN_AMOUNT_SAT: u64 = 1_000; const SWAP_MAX_AMOUNT_SAT: u64 = 1_000_000; -const SWAP_FEE_SAT: u64 = 1_500; +const SWAPPER_ROUTING_FEE_SAT: u64 = 150; const SWAP_TX_WEIGHT: u64 = 800; const SAT_PER_VBYTE: u64 = 12; const SWAP_FEE_PERCENTAGE: f64 = 0.5; @@ -53,19 +53,20 @@ pub use breez_sdk_core::{ InputType, InvoicePaidDetails, LNInvoice, ListPaymentsRequest, LnPaymentDetails, LnUrlPayRequest, LnUrlPayRequestData, LnUrlPayResult, LnUrlWithdrawRequest, LnUrlWithdrawRequestData, LnUrlWithdrawResult, MetadataItem, Network, NodeConfig, - OpenChannelFeeRequest, OpeningFeeParams, OpeningFeeParamsMenu, Payment, PaymentDetails, - PaymentFailedData, PaymentStatus, PaymentType, PaymentTypeFilter, + OnchainPaymentLimitsResponse, OpenChannelFeeRequest, OpeningFeeParams, OpeningFeeParamsMenu, + PayOnchainRequest, Payment, PaymentDetails, PaymentFailedData, PaymentStatus, PaymentType, + PaymentTypeFilter, PrepareOnchainPaymentRequest, PrepareOnchainPaymentResponse, PrepareRedeemOnchainFundsRequest, PrepareRefundRequest, ReceiveOnchainRequest, ReceivePaymentRequest, ReceivePaymentResponse, RedeemOnchainFundsRequest, RefundRequest, ReportIssueRequest, ReportPaymentFailureDetails, ReverseSwapFeesRequest, SendOnchainRequest, - SendPaymentRequest, SignMessageRequest, SwapStatus, UnspentTransactionOutput, + SendPaymentRequest, SignMessageRequest, SwapAmountType, SwapStatus, UnspentTransactionOutput, }; use breez_sdk_core::{ - ChannelState, Config, LspInformation, MaxReverseSwapAmountResponse, NodeState, - OpenChannelFeeResponse, PrepareRedeemOnchainFundsResponse, PrepareRefundResponse, - RecommendedFees, RedeemOnchainFundsResponse, RefundResponse, ReverseSwapInfo, - ReverseSwapPairInfo, ReverseSwapStatus, SendOnchainResponse, SendPaymentResponse, - ServiceHealthCheckResponse, SignMessageResponse, SwapInfo, + ChannelState, Config, LspInformation, NodeState, OpenChannelFeeResponse, PayOnchainResponse, + PrepareRedeemOnchainFundsResponse, PrepareRefundResponse, RecommendedFees, + RedeemOnchainFundsResponse, RefundResponse, ReverseSwapInfo, ReverseSwapPairInfo, + ReverseSwapStatus, SendPaymentResponse, ServiceHealthCheckResponse, SignMessageResponse, + SwapInfo, }; use chrono::Utc; use hex::FromHex; @@ -820,22 +821,45 @@ impl BreezServices { }) } - pub async fn max_reverse_swap_amount(&self) -> SdkResult { + pub async fn onchain_payment_limits(&self) -> SdkResult { let balance_sat = get_balance_msat() / 1000; - let total_sat = if balance_sat > SWAP_FEE_SAT { - balance_sat - SWAP_FEE_SAT + let (min_sat, max_sat) = if balance_sat > SWAPPER_ROUTING_FEE_SAT { + ( + SWAPPER_ROUTING_FEE_SAT, + balance_sat - SWAPPER_ROUTING_FEE_SAT, + ) } else { - 0 + (0, 0) }; + Ok(OnchainPaymentLimitsResponse { min_sat, max_sat }) + } - Ok(MaxReverseSwapAmountResponse { total_sat }) + pub async fn prepare_onchain_payment( + &self, + req: PrepareOnchainPaymentRequest, + ) -> Result { + let fees_lockup = 500; + let fees_claim = 500; + let total_fees = ((req.amount_sat as f64) / 100.0 * SWAP_FEE_PERCENTAGE) as u64 + + fees_lockup + + fees_claim; + let recipient_amount_sat = req.amount_sat - total_fees; + Ok(PrepareOnchainPaymentResponse { + fees_hash: "this-should-be-a-hash.dummy".to_string(), + fees_percentage: SWAP_FEE_PERCENTAGE, + fees_lockup, + fees_claim, + sender_amount_sat: req.amount_sat, + recipient_amount_sat, + total_fees, + }) } - pub async fn send_onchain( + pub async fn pay_onchain( &self, - req: SendOnchainRequest, - ) -> Result { - if req.amount_sat < SWAP_MIN_AMOUNT_SAT { + req: PayOnchainRequest, + ) -> Result { + if req.prepare_res.sender_amount_sat < SWAP_MIN_AMOUNT_SAT { return Err(SendOnchainError::PaymentFailed { err: "Insufficient funds".to_string(), }); @@ -843,17 +867,17 @@ impl BreezServices { let now = Utc::now().timestamp(); - let amount_msat = req.amount_sat * 1_000; + let amount_msat = req.prepare_res.sender_amount_sat * 1_000; - let routing_fee_msat = rand::thread_rng().gen_range(1000..4000); + let routing_fee_msat = SWAPPER_ROUTING_FEE_SAT * 1000; send_payment_mock_channels(amount_msat + routing_fee_msat); let reverse_swap_info = ReverseSwapInfo { id: now.to_string(), - claim_pubkey: req.onchain_recipient_address, + claim_pubkey: req.recipient_address, lockup_txid: Some("LOCKUP-TXID-DUMMY".to_string()), claim_txid: Some("CLAIM-TXID-DUMMY".to_string()), - onchain_amount_sat: req.amount_sat, + onchain_amount_sat: req.prepare_res.sender_amount_sat, status: ReverseSwapStatus::Initial, }; @@ -880,7 +904,7 @@ impl BreezServices { self.event_listener .on_event(BreezEvent::PaymentSucceed { details: payment }); - Ok(SendOnchainResponse { reverse_swap_info }) + Ok(PayOnchainResponse { reverse_swap_info }) } pub async fn list_refundables(&self) -> SdkResult> { diff --git a/src/errors.rs b/src/errors.rs index af19b4110..2c2ec7dd5 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -333,6 +333,32 @@ impl NotificationHandlingErrorCode { } } +/// Enum representing possible errors why parsing could fail. +#[derive(Debug, thiserror::Error)] +pub enum ParseError { + /// Parsing failed because parsed string was not complete. + /// Additional characters are needed to make the string valid. + /// It makes parsed string a valid prefix of a valid string. + #[error("Incomplete")] + Incomplete, + + /// Parsing failed because an unexpected character at position `at` was met. + /// The character **has to be removed**. + #[error("InvalidCharacter at {at}")] + InvalidCharacter { at: u32 }, +} + +impl From for ParseError { + fn from(error: parser::ParseError) -> Self { + match error { + parser::ParseError::Incomplete => ParseError::Incomplete, + parser::ParseError::UnexpectedCharacter(at) | parser::ParseError::ExcessSuffix(at) => { + ParseError::InvalidCharacter { at: at as u32 } + } + } + } +} + #[derive(Debug, PartialEq, Eq, thiserror::Error)] pub enum ParsePhoneNumberPrefixError { #[error("Incomplete")] diff --git a/src/lib.rs b/src/lib.rs index 34b20ba06..d13b87569 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,9 +62,9 @@ use crate::errors::{ }; pub use crate::errors::{ DecodeDataError, Error as LnError, LnUrlPayError, LnUrlPayErrorCode, LnUrlPayResult, - MnemonicError, NotificationHandlingError, NotificationHandlingErrorCode, ParsePhoneNumberError, - ParsePhoneNumberPrefixError, PayError, PayErrorCode, PayResult, Result, RuntimeErrorCode, - SimpleError, UnsupportedDataType, + MnemonicError, NotificationHandlingError, NotificationHandlingErrorCode, ParseError, + ParsePhoneNumberError, ParsePhoneNumberPrefixError, PayError, PayErrorCode, PayResult, Result, + RuntimeErrorCode, SimpleError, UnsupportedDataType, }; use crate::event::LipaEventListener; pub use crate::exchange_rate_provider::ExchangeRate; @@ -102,10 +102,11 @@ use breez_sdk_core::{ ConnectRequest, EventListener, GreenlightCredentials, GreenlightNodeConfig, InputType, ListPaymentsRequest, LnUrlPayRequest, LnUrlPayRequestData, LnUrlWithdrawRequest, LnUrlWithdrawRequestData, Network, NodeConfig, OpenChannelFeeRequest, OpeningFeeParams, - PaymentDetails, PaymentStatus, PaymentTypeFilter, PrepareRedeemOnchainFundsRequest, + PayOnchainRequest, PaymentDetails, PaymentStatus, PaymentTypeFilter, + PrepareOnchainPaymentRequest, PrepareOnchainPaymentResponse, PrepareRedeemOnchainFundsRequest, PrepareRefundRequest, ReceiveOnchainRequest, RedeemOnchainFundsRequest, RefundRequest, - ReportIssueRequest, ReportPaymentFailureDetails, ReverseSwapFeesRequest, SendOnchainRequest, - SendPaymentRequest, SignMessageRequest, UnspentTransactionOutput, + ReportIssueRequest, ReportPaymentFailureDetails, ReverseSwapFeesRequest, SendPaymentRequest, + SignMessageRequest, UnspentTransactionOutput, }; use crow::{CountryCode, LanguageCode, OfferManager, TopupError, TopupInfo}; pub use crow::{PermanentFailureCode, TemporaryFailureCode}; @@ -137,6 +138,16 @@ const CLN_DUST_LIMIT_SAT: u64 = 546; pub(crate) const DB_FILENAME: &str = "db2.db3"; +/// Represent the result of comparision of a value with a given range. +pub enum RangeHit { + /// The value is below the left side of the range. + Below { min: Amount }, + /// The value is whithin the range. + In, + /// The value is above the right side of the range. + Above { max: Amount }, +} + /// The fee charged by the Lightning Service Provider (LSP) for opening a channel with the node. /// This fee is being charged at the time of the channel creation. /// The LSP simply subtracts this fee from an incoming payment (if this incoming payment leads to a channel creation). @@ -249,8 +260,7 @@ pub struct ClearWalletInfo { pub onchain_fee: Amount, /// Estimate for the fee paid to the swap service. pub swap_fee: Amount, - /// Hash that shouldn't be altered. - pub fees_hash: String, + prepare_response: PrepareOnchainPaymentResponse, } const MAX_FEE_PERMYRIAD: u16 = 150; @@ -2344,23 +2354,11 @@ impl LightningNode { /// Check if clearing the wallet is feasible. /// - /// If not feasible, it means the balance is either too high or too low for a reverse-swap to - /// be used. + /// Meaning that the balance is within the range of what can be reverse-swapped. /// /// Requires network: **yes** - pub fn is_clear_wallet_feasible(&self) -> Result { - #[allow(deprecated)] - let amount_sat = self - .rt - .handle() - .block_on(self.sdk.max_reverse_swap_amount()) - .map_to_runtime_error( - RuntimeErrorCode::NodeUnavailable, - "Failed to get max reverse swap amount", - )? - .total_sat; - - let reverse_swap_info = self + pub fn check_clear_wallet_feasibility(&self) -> Result { + let limits = self .rt .handle() .block_on( @@ -2371,59 +2369,78 @@ impl LightningNode { RuntimeErrorCode::NodeUnavailable, "Failed to fetch reverse swap fees", )?; - - Ok(amount_sat >= reverse_swap_info.min && amount_sat <= reverse_swap_info.max) + let balance_sat = self + .sdk + .node_info() + .map_to_runtime_error( + RuntimeErrorCode::NodeUnavailable, + "Failed to read node info", + )? + .channels_balance_msat + .as_msats() + .sats_round_down(); + let exchange_rate = self.get_exchange_rate(); + let range_hit = match balance_sat { + balance_sat if balance_sat < limits.min => RangeHit::Below { + min: limits.min.as_sats().to_amount_up(&exchange_rate), + }, + balance_sat if balance_sat <= limits.max => RangeHit::In, + balance_sat if limits.max < balance_sat => RangeHit::Above { + max: limits.max.as_sats().to_amount_down(&exchange_rate), + }, + _ => permanent_failure!("Unreachable code in check_clear_wallet_feasibility()"), + }; + Ok(range_hit) } /// Prepares a reverse swap that sends all funds in LN channels. This is possible because the /// route to the swap service is known, so fees can be known in advance. /// - /// The return includes fee estimates and must be provided to [`LightningNode::clear_wallet`] in - /// order to execute the clear operation. - /// /// This can fail if the balance is either too low or too high for it to be reverse-swapped. - /// The method [`LightningNode::is_clear_wallet_feasible`] can be used to check if the balance + /// The method [`LightningNode::check_clear_wallet_feasibility`] can be used to check if the balance /// is within the required range. /// /// Requires network: **yes** pub fn prepare_clear_wallet(&self) -> Result { - #[allow(deprecated)] - let amount_sat = self + let claim_tx_feerate = self.query_onchain_fee_rate()?; + let limits = self .rt .handle() - .block_on(self.sdk.max_reverse_swap_amount()) + .block_on(self.sdk.onchain_payment_limits()) .map_to_runtime_error( RuntimeErrorCode::NodeUnavailable, - "Failed to get max reverse swap amount", - )? - .total_sat; - - let reverse_swap_info = self + "Failed to get on-chain payment limits", + )?; + let prepare_response = self .rt .handle() - .block_on(self.sdk.fetch_reverse_swap_fees(ReverseSwapFeesRequest { - send_amount_sat: Some(amount_sat), - claim_tx_feerate: None, - })) + .block_on( + self.sdk + .prepare_onchain_payment(PrepareOnchainPaymentRequest { + amount_sat: limits.max_sat, + amount_type: breez_sdk_core::SwapAmountType::Send, + claim_tx_feerate, + }), + ) .map_to_runtime_error( RuntimeErrorCode::NodeUnavailable, - "Failed to fetch reverse swap fees", + "Failed to prepare on-chain payment", )?; - let total_fees_sat = reverse_swap_info.total_fees.ok_or_permanent_failure( - "No total reverse swap fee estimation provided when amount was present", - )?; - let onchain_fee_sat = reverse_swap_info.fees_claim + reverse_swap_info.fees_lockup; - let swap_fee_sat = - ((amount_sat as f64) * reverse_swap_info.fees_percentage / 100_f64) as u64; + let total_fees_sat = prepare_response.total_fees; + let onchain_fee_sat = prepare_response.fees_claim + prepare_response.fees_lockup; + let swap_fee_sat = total_fees_sat - onchain_fee_sat; let exchange_rate = self.get_exchange_rate(); Ok(ClearWalletInfo { - clear_amount: amount_sat.as_sats().to_amount_up(&exchange_rate), + clear_amount: prepare_response + .sender_amount_sat + .as_sats() + .to_amount_up(&exchange_rate), total_estimated_fees: total_fees_sat.as_sats().to_amount_up(&exchange_rate), onchain_fee: onchain_fee_sat.as_sats().to_amount_up(&exchange_rate), swap_fee: swap_fee_sat.as_sats().to_amount_up(&exchange_rate), - fees_hash: reverse_swap_info.fees_hash, + prepare_response, }) } @@ -2441,14 +2458,11 @@ impl LightningNode { clear_wallet_info: ClearWalletInfo, destination_onchain_address_data: BitcoinAddressData, ) -> Result<()> { - #[allow(deprecated)] self.rt .handle() - .block_on(self.sdk.send_onchain(SendOnchainRequest { - amount_sat: clear_wallet_info.clear_amount.sats, - onchain_recipient_address: destination_onchain_address_data.address, - pair_hash: clear_wallet_info.fees_hash, - sat_per_vbyte: self.query_onchain_fee_rate()?, + .block_on(self.sdk.pay_onchain(PayOnchainRequest { + recipient_address: destination_onchain_address_data.address, + prepare_res: clear_wallet_info.prepare_response, })) .map_to_runtime_error( RuntimeErrorCode::NodeUnavailable, @@ -2675,32 +2689,6 @@ pub fn accept_terms_and_conditions( .map_runtime_error_to(RuntimeErrorCode::AuthServiceUnavailable) } -/// Enum representing possible errors why parsing could fail. -#[derive(Debug, thiserror::Error)] -pub enum ParseError { - /// Parsing failed because parsed string was not complete. - /// Additional characters are needed to make the string valid. - /// It makes parsed string a valid prefix of a valid string. - #[error("Incomplete")] - Incomplete, - - /// Parsing failed because an unexpected character at position `at` was met. - /// The character **has to be removed**. - #[error("InvalidCharacter at {at}")] - InvalidCharacter { at: u32 }, -} - -impl From for ParseError { - fn from(error: parser::ParseError) -> Self { - match error { - parser::ParseError::Incomplete => ParseError::Incomplete, - parser::ParseError::UnexpectedCharacter(at) | parser::ParseError::ExcessSuffix(at) => { - ParseError::InvalidCharacter { at: at as u32 } - } - } - } -} - /// Try to parse the provided string as a lightning address, return [`ParseError`] /// precisely indicating why parsing failed. /// diff --git a/src/lipalightninglib.udl b/src/lipalightninglib.udl index 504f3b30b..30c33c0e1 100644 --- a/src/lipalightninglib.udl +++ b/src/lipalightninglib.udl @@ -147,7 +147,7 @@ interface LightningNode { BreezHealthCheckStatus get_health_status(); [Throws=LnError] - boolean is_clear_wallet_feasible(); + RangeHit check_clear_wallet_feasibility(); [Throws=LnError] ClearWalletInfo prepare_clear_wallet(); @@ -207,6 +207,13 @@ enum Level { "Trace", }; +[Enum] +interface RangeHit { + Below(Amount min); + In(); + Above(Amount max); +}; + callback interface EventsCallback { void payment_received(string payment_hash); void payment_sent(string payment_hash, string payment_preimage); @@ -581,12 +588,22 @@ enum InvoiceAffordability { "Affordable", }; +dictionary PrepareOnchainPaymentResponse { + string fees_hash; + f64 fees_percentage; + u64 fees_lockup; + u64 fees_claim; + u64 sender_amount_sat; + u64 recipient_amount_sat; + u64 total_fees; +}; + dictionary ClearWalletInfo { Amount clear_amount; Amount total_estimated_fees; Amount onchain_fee; Amount swap_fee; - string fees_hash; + PrepareOnchainPaymentResponse prepare_response; }; dictionary ChannelCloseResolvingFees { diff --git a/src/symmetric_encryption.rs b/src/symmetric_encryption.rs index f688e4a88..3ff05860d 100644 --- a/src/symmetric_encryption.rs +++ b/src/symmetric_encryption.rs @@ -1,13 +1,12 @@ use crate::errors::{Error, Result}; use crate::random; -use aes_gcm::Aes256Gcm; -use aes_gcm::{aead::Aead, Nonce as AesNonce}; +use aes_gcm::aead::Aead; +use aes_gcm::{Aes256Gcm, Nonce as AesNonce}; use cipher::consts::U12; use cipher::{KeyInit, Unsigned}; use perro::MapToError; -use sha2::Digest; -use sha2::Sha256; +use sha2::{Digest, Sha256}; type NonceLength = U12; type Nonce = AesNonce;