Skip to content

Commit

Permalink
Sync payment details
Browse files Browse the repository at this point in the history
  • Loading branch information
dangeross committed Dec 19, 2024
1 parent 2346465 commit 6f4284a
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 62 deletions.
6 changes: 3 additions & 3 deletions lib/core/src/chain_swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ impl ChainSwapHandler {
fees_sat: lockup_tx_fees_sat + swap.claim_fees_sat,
payment_type: PaymentType::Send,
is_confirmed: false,
}, None)?;
}, None, false)?;

self.update_swap_info(&ChainSwapUpdate {
swap_id: id,
Expand Down Expand Up @@ -840,6 +840,7 @@ impl ChainSwapHandler {
is_confirmed: false,
},
None,
false,
)?;
Some(claim_tx_id.clone())
}
Expand Down Expand Up @@ -1319,9 +1320,8 @@ impl ChainSwapHandler {

#[cfg(test)]
mod tests {
use std::collections::{HashMap, HashSet};

use anyhow::Result;
use std::collections::{HashMap, HashSet};

use crate::{
model::{
Expand Down
109 changes: 77 additions & 32 deletions lib/core/src/persist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,21 @@ pub(crate) mod send;
pub(crate) mod sync;

use std::collections::{HashMap, HashSet};
use std::ops::Not;
use std::{fs::create_dir_all, path::PathBuf, str::FromStr};

use crate::error::PaymentError;
use crate::lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription};
use crate::model::*;
use crate::sync::model::RecordType;
use crate::{get_invoice_description, utils};
use anyhow::{anyhow, Result};
use boltz_client::boltz::{ChainPair, ReversePair, SubmarinePair};
use lwk_wollet::WalletTx;
use migrations::current_migrations;
use model::PaymentTxDetails;
use rusqlite::{params, params_from_iter, Connection, OptionalExtension, Row, ToSql};
use rusqlite::{
params, params_from_iter, Connection, OptionalExtension, Row, ToSql, TransactionBehavior,
};
use rusqlite_migration::{Migrations, M};
use sdk_common::bitcoin::hashes::hex::ToHex;
use tokio::sync::mpsc::Sender;
Expand Down Expand Up @@ -98,10 +101,7 @@ impl Persister {
}
}

pub(crate) fn insert_or_update_payment_with_wallet_tx(
&self,
tx: &WalletTx,
) -> Result<(), PaymentError> {
pub(crate) fn insert_or_update_payment_with_wallet_tx(&self, tx: &WalletTx) -> Result<()> {
let tx_id = tx.txid.to_string();
let is_tx_confirmed = tx.height.is_some();
let amount_sat = tx.balance.values().sum::<i64>();
Expand All @@ -122,10 +122,12 @@ impl Persister {
},
is_confirmed: is_tx_confirmed,
},
Some(PaymentTxDetails {
destination: maybe_script_pubkey,
maybe_script_pubkey.map(|destination| PaymentTxDetails {
tx_id,
destination,
..Default::default()
}),
true,
)
}

Expand Down Expand Up @@ -161,9 +163,11 @@ impl Persister {
&self,
ptx: PaymentTxData,
payment_tx_details: Option<PaymentTxDetails>,
) -> Result<(), PaymentError> {
let con = self.get_connection()?;
con.execute(
from_wallet_tx_data: bool,
) -> Result<()> {
let mut con = self.get_connection()?;
let tx = con.transaction_with_behavior(TransactionBehavior::Immediate)?;
tx.execute(
"INSERT INTO payment_tx_data (
tx_id,
timestamp,
Expand All @@ -190,24 +194,48 @@ impl Persister {
),
)?;

if let Some(payment_details) = payment_tx_details {
// Only store the destination if there is no payment_details entry else
// the destination is overwritten by the tx script_pubkey
Self::insert_or_update_payment_details_inner(&con, &ptx.tx_id, payment_details)?;
let mut trigger_sync = false;
if let Some(ref payment_tx_details) = payment_tx_details {
// If the update comes from the wallet tx:
// - Skip updating the destination from the script_pubkey
// - Skip syncing the payment_tx_details
Self::insert_or_update_payment_details_inner(
&tx,
payment_tx_details,
from_wallet_tx_data,
)?;
if !from_wallet_tx_data {
self.commit_outgoing(
&tx,
&payment_tx_details.tx_id,
RecordType::PaymentDetails,
None,
)?;
trigger_sync = true;
}
}

tx.commit()?;

if trigger_sync {
self.sync_trigger.try_send(())?;
}

Ok(())
}

fn insert_or_update_payment_details_inner(
con: &Connection,
tx_id: &str,
payment_tx_details: PaymentTxDetails,
payment_tx_details: &PaymentTxDetails,
skip_destination_update: bool,
) -> Result<()> {
// Only store the destination if there is no payment_details entry else
// the destination is overwritten by the tx script_pubkey
let destination_update = skip_destination_update
.not()
.then_some("destination = excluded.destination,")
.unwrap_or_default();
con.execute(
"INSERT INTO payment_details (
&format!(
"INSERT INTO payment_details (
tx_id,
destination,
description,
Expand All @@ -216,15 +244,18 @@ impl Persister {
VALUES (?, ?, ?, ?)
ON CONFLICT (tx_id)
DO UPDATE SET
{destination_update}
description = COALESCE(excluded.description, description),
lnurl_info_json = excluded.lnurl_info_json
",
lnurl_info_json = COALESCE(excluded.lnurl_info_json, lnurl_info_json)
"
),
(
tx_id,
payment_tx_details.destination,
payment_tx_details.description,
&payment_tx_details.tx_id,
&payment_tx_details.destination,
&payment_tx_details.description,
payment_tx_details
.lnurl_info
.as_ref()
.map(|info| serde_json::to_string(&info).ok()),
),
)?;
Expand All @@ -233,11 +264,23 @@ impl Persister {

pub(crate) fn insert_or_update_payment_details(
&self,
tx_id: &str,
payment_tx_details: PaymentTxDetails,
) -> Result<()> {
let con = self.get_connection()?;
Self::insert_or_update_payment_details_inner(&con, tx_id, payment_tx_details)
let mut con = self.get_connection()?;
let tx = con.transaction_with_behavior(TransactionBehavior::Immediate)?;

Self::insert_or_update_payment_details_inner(&tx, &payment_tx_details, false)?;
self.commit_outgoing(
&tx,
&payment_tx_details.tx_id,
RecordType::PaymentDetails,
None,
)?;
tx.commit()?;

self.sync_trigger.try_send(())?;

Ok(())
}

pub(crate) fn get_payment_details(&self, tx_id: &str) -> Result<Option<PaymentTxDetails>> {
Expand All @@ -248,10 +291,11 @@ impl Persister {
WHERE tx_id = ?",
)?;
let res = stmt.query_row([tx_id], |row| {
let destination = row.get(1)?;
let description = row.get(2)?;
let maybe_lnurl_info_json: Option<String> = row.get(3)?;
let destination = row.get(0)?;
let description = row.get(1)?;
let maybe_lnurl_info_json: Option<String> = row.get(2)?;
Ok(PaymentTxDetails {
tx_id: tx_id.to_string(),
destination,
description,
lnurl_info: maybe_lnurl_info_json
Expand Down Expand Up @@ -754,9 +798,10 @@ mod tests {
storage.insert_or_update_payment(
payment_tx_data.clone(),
Some(PaymentTxDetails {
destination: Some("mock-address".to_string()),
destination: "mock-address".to_string(),
..Default::default()
}),
false,
)?;

assert!(storage
Expand Down
3 changes: 2 additions & 1 deletion lib/core/src/persist/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use super::LnUrlInfo;

#[derive(Clone, Debug, Default)]
pub(crate) struct PaymentTxDetails {
pub(crate) destination: Option<String>,
pub(crate) tx_id: String,
pub(crate) destination: String,
pub(crate) description: Option<String>,
pub(crate) lnurl_info: Option<LnUrlInfo>,
}
29 changes: 28 additions & 1 deletion lib/core/src/persist/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rusqlite::{
named_params, Connection, OptionalExtension, Row, Statement, Transaction, TransactionBehavior,
};

use super::{cache::KEY_LAST_DERIVATION_INDEX, Persister, Swap};
use super::{cache::KEY_LAST_DERIVATION_INDEX, PaymentTxDetails, Persister, Swap};
use crate::{
sync::model::{
data::LAST_DERIVATION_INDEX_DATA_ID, sync::Record, RecordType, SyncOutgoingChanges,
Expand Down Expand Up @@ -466,4 +466,31 @@ impl Persister {

Ok(())
}

pub(crate) fn commit_incoming_payment_details(
&self,
payment_tx_details: PaymentTxDetails,
sync_state: &SyncState,
last_commit_time: Option<u32>,
) -> Result<()> {
let mut con = self.get_connection()?;
let tx = con.transaction_with_behavior(TransactionBehavior::Immediate)?;

if let Some(last_commit_time) = last_commit_time {
Self::check_commit_update(&tx, &sync_state.record_id, last_commit_time)?;
}

Self::insert_or_update_payment_details_inner(&tx, &payment_tx_details, false)?;

Self::set_sync_state_stmt(&tx)?.execute(named_params! {
":data_id": &sync_state.data_id,
":record_id": &sync_state.record_id,
":record_revision": &sync_state.record_revision,
":is_local": &sync_state.is_local,
})?;

tx.commit()?;

Ok(())
}
}
1 change: 1 addition & 0 deletions lib/core/src/receive_swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ impl ReceiveSwapHandler {
is_confirmed: false,
},
None,
false,
)?;

info!("Successfully broadcast claim tx {claim_tx_id} for Receive Swap {swap_id}");
Expand Down
44 changes: 28 additions & 16 deletions lib/core/src/sdk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1223,10 +1223,12 @@ impl LiquidSdk {
self.persister.insert_or_update_payment(
tx_data.clone(),
Some(PaymentTxDetails {
destination: Some(destination.clone()),
tx_id: tx_id.clone(),
destination: destination.clone(),
description: description.clone(),
..Default::default()
}),
false,
)?;
self.emit_payment_updated(Some(tx_id)).await?; // Emit Pending event

Expand Down Expand Up @@ -2682,14 +2684,21 @@ impl LiquidSdk {
match sa {
// For AES, we decrypt the contents if the preimage is available
SuccessAction::Aes { data } => {
let PaymentDetails::Lightning { preimage, .. } = &payment.details else {
let PaymentDetails::Lightning {
swap_id, preimage, ..
} = &payment.details
else {
return Err(LnUrlPayError::Generic {
err: format!("Invalid payment type: expected type `PaymentDetails::Lightning`, got payment details {:?}.", payment.details),
});
};

match preimage {
Some(preimage_str) => {
debug!(
"Decrypting AES success action with preimage for Send Swap {}",
swap_id
);
let preimage =
sha256::Hash::from_str(preimage_str).map_err(|_| {
LnUrlPayError::Generic {
Expand All @@ -2705,7 +2714,10 @@ impl LiquidSdk {
};
Some(SuccessActionProcessed::Aes { result })
}
None => None,
None => {
debug!("Preimage not yet available to decrypt AES success action for Send Swap {}", swap_id);
None
}
}
}
SuccessAction::Message { data } => {
Expand All @@ -2721,11 +2733,13 @@ impl LiquidSdk {
Some(_) => None,
None => Some(prepare_response.data.domain),
};
if let Some(tx_id) = &payment.tx_id {
self.persister.insert_or_update_payment_details(
tx_id,
PaymentTxDetails {
destination: payment.destination.clone(),
if let (Some(tx_id), Some(destination)) =
(payment.tx_id.clone(), payment.destination.clone())
{
self.persister
.insert_or_update_payment_details(PaymentTxDetails {
tx_id,
destination,
description: prepare_response.comment.clone(),
lnurl_info: Some(LnUrlInfo {
ln_address: prepare_response.data.ln_address,
Expand All @@ -2736,8 +2750,7 @@ impl LiquidSdk {
lnurl_pay_unprocessed_success_action: prepare_response.success_action,
lnurl_withdraw_endpoint: None,
}),
},
)?;
})?;
}

Ok(LnUrlPayResult::EndpointSuccess {
Expand Down Expand Up @@ -2789,17 +2802,16 @@ impl LiquidSdk {
.persister
.fetch_receive_swap_by_invoice(&invoice.bolt11)?
{
self.persister.insert_or_update_payment_details(
&tx_id,
PaymentTxDetails {
destination: Some(receive_res.destination),
self.persister
.insert_or_update_payment_details(PaymentTxDetails {
tx_id,
destination: receive_res.destination,
description: req.description,
lnurl_info: Some(LnUrlInfo {
lnurl_withdraw_endpoint: Some(req.data.callback),
..Default::default()
}),
},
)?;
})?;
}
}
Ok(res)
Expand Down
Loading

0 comments on commit 6f4284a

Please sign in to comment.