Skip to content

Commit

Permalink
Store actual payer and accepted receiver amounts
Browse files Browse the repository at this point in the history
  • Loading branch information
danielgranhao committed Dec 30, 2024
1 parent ff1445f commit 86a099f
Show file tree
Hide file tree
Showing 14 changed files with 217 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ typedef void (*UniFfiRustFutureContinuation)(void * _Nonnull, int8_t);
// Scaffolding functions
void uniffi_breez_sdk_liquid_bindings_fn_free_bindingliquidsdk(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
);
void uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_accept_payment_proposed_fees(void*_Nonnull ptr, RustBuffer req, RustCallStatus *_Nonnull out_status
);
RustBuffer uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_add_event_listener(void*_Nonnull ptr, uint64_t listener, RustCallStatus *_Nonnull out_status
);
void uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_backup(void*_Nonnull ptr, RustBuffer req, RustCallStatus *_Nonnull out_status
Expand All @@ -81,6 +83,8 @@ RustBuffer uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_fetch_lig
);
RustBuffer uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_fetch_onchain_limits(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
);
RustBuffer uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_fetch_payment_proposed_fees(void*_Nonnull ptr, RustBuffer req, RustCallStatus *_Nonnull out_status
);
RustBuffer uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_get_info(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
);
RustBuffer uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_get_payment(void*_Nonnull ptr, RustBuffer req, RustCallStatus *_Nonnull out_status
Expand Down Expand Up @@ -279,6 +283,9 @@ uint16_t uniffi_breez_sdk_liquid_bindings_checksum_func_parse_invoice(void
);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_func_set_logger(void

);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_bindingliquidsdk_accept_payment_proposed_fees(void

);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_bindingliquidsdk_add_event_listener(void

Expand All @@ -303,6 +310,9 @@ uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_bindingliquidsdk_fetch
);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_bindingliquidsdk_fetch_onchain_limits(void

);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_bindingliquidsdk_fetch_payment_proposed_fees(void

);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_bindingliquidsdk_get_info(void

Expand Down
57 changes: 28 additions & 29 deletions lib/core/src/chain_swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,9 @@ impl ChainSwapHandler {
.map_err(|_| anyhow!("Invalid ChainSwapState for Chain Swap {id}: {status}"))?;

match swap_state {
// If the swap is not local (pulled from real-time sync) we do not:
// - claim twice
// - accept fees twice
// If the swap is not local (pulled from real-time sync) we do not claim twice
ChainSwapStates::TransactionServerMempool
| ChainSwapStates::TransactionServerConfirmed
| ChainSwapStates::TransactionLockupFailed => {
| ChainSwapStates::TransactionServerConfirmed => {
log::debug!("Received {swap_state:?} for non-local Chain swap {id} from status stream, skipping update.");
return Ok(());
}
Expand Down Expand Up @@ -331,7 +328,7 @@ impl ChainSwapHandler {
| ChainSwapStates::TransactionRefunded
| ChainSwapStates::SwapExpired => {
// Zero-amount Receive Chain Swaps also get to TransactionLockupFailed when user locks up funds
let is_zero_amount = swap.get_boltz_create_response()?.lockup_details.amount == 0;
let is_zero_amount = swap.payer_amount_sat == 0;
if matches!(swap_state, ChainSwapStates::TransactionLockupFailed) && is_zero_amount
{
match self.handle_amountless_update(swap).await {
Expand Down Expand Up @@ -395,26 +392,19 @@ impl ChainSwapHandler {
receiver_amount_sat,
} => {
debug!("Zero-amount swap validated. Auto-accepting...");
self.persister.update_zero_amount_swap_values(
&id,
user_lockup_amount_sat,
receiver_amount_sat,
)?;
self.persister
.update_actual_payer_amount(&id, user_lockup_amount_sat)?;
self.swapper
.accept_zero_amount_chain_swap_quote(&id, quote)
.map_err(Into::into)
.accept_zero_amount_chain_swap_quote(&id, quote)?;
self.persister
.update_accepted_receiver_amount(&id, receiver_amount_sat)
}
ValidateAmountlessSwapResult::RequiresUserAction {
user_lockup_amount_sat,
receiver_amount_sat_original_estimate,
} => {
debug!("Zero-amount swap validated. Fees are too high for automatic accepting. Moving to WaitingFeeAcceptance");
// While the user doesn't accept new fees, let's continue to show the original estimate
self.persister.update_zero_amount_swap_values(
&id,
user_lockup_amount_sat,
receiver_amount_sat_original_estimate,
)?;
self.persister
.update_actual_payer_amount(&id, user_lockup_amount_sat)?;
self.update_swap_info(&ChainSwapUpdate {
swap_id: id,
to_state: WaitingFeeAcceptance,
Expand Down Expand Up @@ -486,11 +476,8 @@ impl ChainSwapHandler {
);

if min_auto_accept_server_lockup_amount_sat > quote_server_lockup_amount_sat {
let receiver_amount_sat_original_estimate =
server_lockup_amount_estimate_sat - swap.claim_fees_sat;
Ok(ValidateAmountlessSwapResult::RequiresUserAction {
user_lockup_amount_sat,
receiver_amount_sat_original_estimate,
})
} else {
let receiver_amount_sat = quote_server_lockup_amount_sat - swap.claim_fees_sat;
Expand Down Expand Up @@ -1231,12 +1218,25 @@ impl ChainSwapHandler {
.unblind(&secp, liquid_swap_script.blinding_key.secret_key())?
.value;
}
if value < claim_details.amount {
return Err(anyhow!(
"Transaction value {value} sats is less than {} sats",
claim_details.amount
));
match chain_swap.accepted_receiver_amount_sat {
None => {
if value < claim_details.amount {
return Err(anyhow!(
"Transaction value {value} sats is less than {} sats",
claim_details.amount
));
}
}
Some(accepted_receiver_amount_sat) => {
if value < accepted_receiver_amount_sat - chain_swap.claim_fees_sat {
return Err(anyhow!(
"Transaction value {value} sats is less than accepted {} sats",
claim_details.amount
));
}
}
}

Ok(())
}

Expand Down Expand Up @@ -1360,7 +1360,6 @@ enum ValidateAmountlessSwapResult {
},
RequiresUserAction {
user_lockup_amount_sat: u64,
receiver_amount_sat_original_estimate: u64,
},
}

Expand Down
6 changes: 6 additions & 0 deletions lib/core/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -729,8 +729,14 @@ pub(crate) struct ChainSwap {
pub(crate) timeout_block_height: u32,
pub(crate) preimage: String,
pub(crate) description: Option<String>,
/// Payer amount defined at swap creation
pub(crate) payer_amount_sat: u64,
/// The actual payer amount in case it differs from `payer_amount_sat` (over/underpayment)
pub(crate) actual_payer_amount_sat: Option<u64>,
/// Receiver amount defined at swap creation
pub(crate) receiver_amount_sat: u64,
/// The final receiver amount, in case of an over/underpayment that has been accepted
pub(crate) accepted_receiver_amount_sat: Option<u64>,
pub(crate) claim_fees_sat: u64,
/// The [ChainPair] chosen on swap creation
pub(crate) pair_fees_json: String,
Expand Down
58 changes: 39 additions & 19 deletions lib/core/src/persist/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ impl Persister {
refund_tx_id,
created_at,
state,
pair_fees_json
pair_fees_json,
actual_payer_amount_sat,
accepted_receiver_amount_sat
FROM chain_swaps
{where_clause_str}
ORDER BY created_at
Expand Down Expand Up @@ -185,6 +187,8 @@ impl Persister {
created_at: row.get(18)?,
state: row.get(19)?,
pair_fees_json: row.get(20)?,
actual_payer_amount_sat: row.get(21)?,
accepted_receiver_amount_sat: row.get(22)?,
})
}

Expand Down Expand Up @@ -274,39 +278,55 @@ impl Persister {
Ok(())
}

/// Used for Zero-amount Receive Chain swaps, when we fetched the quote and we know how much
/// the sender locked up
pub(crate) fn update_zero_amount_swap_values(
/// Used for receive chain swaps, when the sender over/underpays
pub(crate) fn update_actual_payer_amount(
&self,
swap_id: &str,
payer_amount_sat: u64,
receiver_amount_sat: u64,
actual_payer_amount_sat: u64,
) -> Result<(), PaymentError> {
log::info!("Updating chain swap {swap_id}: payer_amount_sat = {payer_amount_sat}, receiver_amount_sat = {receiver_amount_sat}");
log::info!(
"Updating chain swap {swap_id}: actual_payer_amount_sat = {actual_payer_amount_sat}"
);
let con: Connection = self.get_connection()?;
con.execute(
"UPDATE chain_swaps
SET actual_payer_amount_sat = :actual_payer_amount_sat
WHERE id = :id",
named_params! {
":id": swap_id,
":actual_payer_amount_sat": actual_payer_amount_sat,
},
)?;

Ok(())
}

/// Used for receive chain swaps, when fees are accepted and thus the agreed received amount is known
pub(crate) fn update_accepted_receiver_amount(
&self,
swap_id: &str,
accepted_receiver_amount_sat: u64,
) -> Result<(), PaymentError> {
log::info!(
"Updating chain swap {swap_id}: accepted_receiver_amount_sat = {accepted_receiver_amount_sat}"
);
let mut con: Connection = self.get_connection()?;
let tx = con.transaction_with_behavior(TransactionBehavior::Immediate)?;

tx.execute(
"UPDATE chain_swaps
SET
payer_amount_sat = :payer_amount_sat,
receiver_amount_sat = :receiver_amount_sat
WHERE
id = :id",
"UPDATE chain_swaps
SET accepted_receiver_amount_sat = :accepted_receiver_amount_sat
WHERE id = :id",
named_params! {
":id": swap_id,
":payer_amount_sat": payer_amount_sat,
":receiver_amount_sat": receiver_amount_sat,
":accepted_receiver_amount_sat": accepted_receiver_amount_sat,
},
)?;
self.commit_outgoing(
&tx,
swap_id,
RecordType::Chain,
Some(vec![
"payer_amount_sat".to_string(),
"receiver_amount_sat".to_string(),
]),
Some(vec!["accepted_receiver_amount_sat".to_string()]),
)?;
tx.commit()?;
self.sync_trigger
Expand Down
2 changes: 2 additions & 0 deletions lib/core/src/persist/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,5 +214,7 @@ pub(crate) fn current_migrations() -> Vec<&'static str> {
) STRICT;",
"ALTER TABLE receive_swaps DROP COLUMN mrh_script_pubkey;",
"ALTER TABLE payment_details ADD COLUMN lnurl_info_json TEXT;",
"ALTER TABLE chain_swaps ADD COLUMN actual_payer_amount_sat INTEGER;",
"ALTER TABLE chain_swaps ADD COLUMN accepted_receiver_amount_sat INTEGER;",
]
}
28 changes: 21 additions & 7 deletions lib/core/src/persist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ impl Persister {
cs.claim_address,
cs.state,
cs.pair_fees_json,
cs.actual_payer_amount_sat,
cs.accepted_receiver_amount_sat,
rtx.amount_sat,
pd.destination,
pd.description,
Expand Down Expand Up @@ -473,12 +475,14 @@ impl Persister {
let maybe_chain_swap_pair_fees_json: Option<String> = row.get(38)?;
let maybe_chain_swap_pair_fees: Option<ChainPair> =
maybe_chain_swap_pair_fees_json.and_then(|pair| serde_json::from_str(&pair).ok());
let maybe_chain_swap_actual_payer_amount_sat: Option<u64> = row.get(39)?;
let maybe_chain_swap_accepted_receiver_amount_sat: Option<u64> = row.get(40)?;

let maybe_swap_refund_tx_amount_sat: Option<u64> = row.get(39)?;
let maybe_swap_refund_tx_amount_sat: Option<u64> = row.get(41)?;

let maybe_payment_details_destination: Option<String> = row.get(40)?;
let maybe_payment_details_description: Option<String> = row.get(41)?;
let maybe_payment_details_lnurl_info_json: Option<String> = row.get(42)?;
let maybe_payment_details_destination: Option<String> = row.get(42)?;
let maybe_payment_details_description: Option<String> = row.get(43)?;
let maybe_payment_details_lnurl_info_json: Option<String> = row.get(44)?;
let maybe_payment_details_lnurl_info: Option<LnUrlInfo> =
maybe_payment_details_lnurl_info_json.and_then(|info| serde_json::from_str(&info).ok());

Expand Down Expand Up @@ -545,7 +549,18 @@ impl Persister {
}
None => match maybe_chain_swap_id {
Some(chain_swap_id) => {
let payer_amount_sat = maybe_chain_swap_payer_amount_sat.unwrap_or(0);
let payer_amount_sat = match maybe_chain_swap_actual_payer_amount_sat {
Some(actual_payer_amount_sat) => actual_payer_amount_sat,
None => maybe_chain_swap_payer_amount_sat.unwrap_or(0),
};
let receiver_amount_sat =
match maybe_chain_swap_accepted_receiver_amount_sat {
Some(accepted_receiver_amount_sat) => accepted_receiver_amount_sat,
None => match maybe_chain_swap_actual_payer_amount_sat {
Some(_) => payer_amount_sat, // For over/underpaid chain swaps WaitingFeeAcceptance, show zero fees
None => maybe_chain_swap_receiver_amount_sat.unwrap_or(0),
},
};
let swapper_fees_sat = maybe_chain_swap_pair_fees
.map(|pair| pair.fees.percentage)
.map(|fr| ((fr / 100.0) * payer_amount_sat as f64).ceil() as u64)
Expand All @@ -563,8 +578,7 @@ impl Persister {
description: maybe_chain_swap_description
.unwrap_or("Bitcoin transfer".to_string()),
payer_amount_sat,
receiver_amount_sat: maybe_chain_swap_receiver_amount_sat
.unwrap_or(0),
receiver_amount_sat,
swapper_fees_sat,
refund_tx_id: maybe_chain_swap_refund_tx_id,
refund_tx_amount_sat: maybe_swap_refund_tx_amount_sat,
Expand Down
6 changes: 5 additions & 1 deletion lib/core/src/recover/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ impl RecoveredOnchainDataChainReceive {
&self,
min_lockup_amount_sat: u64,
is_expired: bool,
is_waiting_fee_acceptance: bool,
) -> Option<PaymentState> {
let is_refundable = self.btc_user_lockup_amount_sat > 0
&& (is_expired || self.btc_user_lockup_amount_sat < min_lockup_amount_sat);
Expand Down Expand Up @@ -232,7 +233,10 @@ impl RecoveredOnchainDataChainReceive {
}
(None, None) => match is_refundable {
true => Some(PaymentState::Refundable),
false => Some(PaymentState::Pending),
false => match is_waiting_fee_acceptance {
true => Some(PaymentState::WaitingFeeAcceptance),
false => Some(PaymentState::Pending),
},
},
},
None => match is_expired {
Expand Down
23 changes: 14 additions & 9 deletions lib/core/src/recover/recoverer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use lwk_wollet::WalletTx;
use tokio::sync::Mutex;

use super::model::*;
use crate::model::PaymentState;
use crate::prelude::{Direction, Swap};
use crate::wallet::OnchainWallet;
use crate::{
Expand Down Expand Up @@ -213,17 +212,23 @@ impl Recoverer {
log::warn!("Could not apply recovered data for incoming Chain swap {swap_id}: recovery data not found");
continue;
};
if chain_swap.receiver_amount_sat
!= recovered_data.btc_user_lockup_amount_sat
{
chain_swap.actual_payer_amount_sat =
Some(recovered_data.btc_user_lockup_amount_sat)
}
let is_expired = bitcoin_height >= chain_swap.timeout_block_height;
let min_lockup_amount_sat = chain_swap.payer_amount_sat;
let is_waiting_fee_acceptance =
chain_swap.state == PaymentState::WaitingFeeAcceptance;
if let Some(new_state) =
recovered_data.derive_partial_state(min_lockup_amount_sat, is_expired)
{
// When local state is WaitingFeeAcceptance do not change to Pending
if !(new_state == PaymentState::Pending && is_waiting_fee_acceptance) {
chain_swap.state = new_state;
}
chain_swap.actual_payer_amount_sat.is_some()
&& chain_swap.accepted_receiver_amount_sat.is_none();
if let Some(new_state) = recovered_data.derive_partial_state(
min_lockup_amount_sat,
is_expired,
is_waiting_fee_acceptance,
) {
chain_swap.state = new_state;
}
chain_swap.server_lockup_tx_id = recovered_data
.lbtc_server_lockup_tx_id
Expand Down
Loading

0 comments on commit 86a099f

Please sign in to comment.