Skip to content

Commit

Permalink
Merge pull request breez#674 from breez/payment-timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
ubbabeck authored Jan 3, 2024
2 parents 94e234f + c57b6b2 commit e6323f5
Show file tree
Hide file tree
Showing 15 changed files with 138 additions and 17 deletions.
1 change: 1 addition & 0 deletions libs/sdk-bindings/src/breez_sdk.udl
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ dictionary LnPaymentDetails {
string? ln_address;
string? lnurl_withdraw_endpoint;
SwapInfo? swap_info;
u32? pending_expiration_block;
};

dictionary ClosedChannelPaymentDetails {
Expand Down
6 changes: 5 additions & 1 deletion libs/sdk-core/src/breez_services.rs
Original file line number Diff line number Diff line change
Expand Up @@ -919,7 +919,6 @@ impl BreezServices {
let mut payments = closed_channel_payments;
payments.extend(new_data.payments.clone());
self.persister.insert_or_update_payments(&payments, true)?;

let duration = start.elapsed();
info!("Sync duration: {:?}", duration);

Expand Down Expand Up @@ -989,6 +988,7 @@ impl BreezServices {
lnurl_metadata: None,
lnurl_withdraw_endpoint: None,
swap_info: None,
pending_expiration_block: None,
},
},
}],
Expand Down Expand Up @@ -2278,6 +2278,7 @@ pub(crate) mod tests {
ln_address: None,
lnurl_withdraw_endpoint: None,
swap_info: None,
pending_expiration_block: None,
},
},
},
Expand All @@ -2303,6 +2304,7 @@ pub(crate) mod tests {
ln_address: None,
lnurl_withdraw_endpoint: Some(test_lnurl_withdraw_endpoint.to_string()),
swap_info: None,
pending_expiration_block: None,
},
},
},
Expand All @@ -2328,6 +2330,7 @@ pub(crate) mod tests {
ln_address: Some(test_ln_address.to_string()),
lnurl_withdraw_endpoint: None,
swap_info: None,
pending_expiration_block: None,
},
},
},
Expand All @@ -2353,6 +2356,7 @@ pub(crate) mod tests {
ln_address: None,
lnurl_withdraw_endpoint: None,
swap_info: Some(swap_info.clone()),
pending_expiration_block: None,
},
},
},
Expand Down
1 change: 1 addition & 0 deletions libs/sdk-core/src/bridge_generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1251,6 +1251,7 @@ impl support::IntoDart for LnPaymentDetails {
self.lnurl_metadata.into_dart(),
self.lnurl_withdraw_endpoint.into_dart(),
self.swap_info.into_dart(),
self.pending_expiration_block.into_dart(),
]
.into_dart()
}
Expand Down
6 changes: 3 additions & 3 deletions libs/sdk-core/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ pub(crate) fn get_utxos(address: String, transactions: Vec<OnchainTx>) -> Result
// Calculate confirmed amount associated with this address
let mut spent_outputs: Vec<OutPoint> = Vec::new();
let mut utxos: Vec<Utxo> = Vec::new();
for (_, tx) in transactions.iter().enumerate() {
for (_, vin) in tx.vin.iter().enumerate() {
for tx in transactions.iter() {
for vin in tx.vin.iter() {
if vin.prevout.scriptpubkey_address == address.clone() {
spent_outputs.push(OutPoint {
txid: Txid::from_hex(vin.txid.as_str())?,
Expand All @@ -89,7 +89,7 @@ pub(crate) fn get_utxos(address: String, transactions: Vec<OnchainTx>) -> Result
}
}

for (_i, tx) in transactions.iter().enumerate() {
for tx in transactions.iter() {
for (index, vout) in tx.vout.iter().enumerate() {
if vout.scriptpubkey_address == address {
let outpoint = OutPoint {
Expand Down
53 changes: 50 additions & 3 deletions libs/sdk-core/src/greenlight/node_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use bitcoin::secp256k1::Secp256k1;
use bitcoin::util::bip32::{ChildNumber, ExtendedPrivKey};
use bitcoin::{Address, OutPoint, Script, Sequence, Transaction, TxIn, TxOut, Txid, Witness};
use ecies::symmetric::{sym_decrypt, sym_encrypt};
use futures::Stream;
use gl_client::node::ClnClient;
use gl_client::pb::cln::listinvoices_invoices::ListinvoicesInvoicesStatus;
use gl_client::pb::cln::listpays_pays::ListpaysPaysStatus;
Expand All @@ -36,12 +37,13 @@ use serde::{Deserialize, Serialize};
use strum_macros::{Display, EnumString};
use tokio::sync::{mpsc, Mutex};
use tokio::time::sleep;
use tokio_stream::{Stream, StreamExt};
use tokio_stream::StreamExt;
use tonic::Streaming;

use crate::invoice::{parse_invoice, validate_network, InvoiceError, RouteHintHop};
use crate::models::*;
use crate::node_api::{NodeAPI, NodeError, NodeResult};

use crate::persist::db::SqliteStorage;
use crate::{
Channel, ChannelState, NodeConfig, PrepareRedeemOnchainFundsRequest,
Expand Down Expand Up @@ -733,7 +735,6 @@ impl NodeAPI for Greenlight {
let max_allowed_to_receive_msats =
MAX_INBOUND_LIQUIDITY_MSAT.saturating_sub(channels_balance);
let node_pubkey = hex::encode(node_info.id);

// construct the node state
let node_state = NodeState {
id: node_pubkey.clone(),
Expand All @@ -748,10 +749,14 @@ impl NodeAPI for Greenlight {
connected_peers,
inbound_liquidity_msats: max_receivable_single_channel,
};
let mut htlc_list: Vec<Htlc> = Vec::new();
for channel in all_channel_models.clone() {
htlc_list.extend(channel.htlcs);
}

Ok(SyncResponse {
node_state,
payments: pull_transactions(since_timestamp, node_client.clone()).await?,
payments: pull_transactions(since_timestamp, node_client.clone(), htlc_list).await?,
channels: all_channel_models,
})
}
Expand Down Expand Up @@ -1419,6 +1424,7 @@ enum NodeCommand {
async fn pull_transactions(
since_timestamp: u64,
client: node::ClnClient,
htlc_list: Vec<Htlc>,
) -> NodeResult<Vec<Payment>> {
let mut c = client.clone();

Expand Down Expand Up @@ -1452,13 +1458,43 @@ async fn pull_transactions(
.map(TryInto::try_into)
.collect();

let outbound_transactions: NodeResult<Vec<Payment>> =
update_payment_expirations(outbound_transactions?, htlc_list);

let mut transactions: Vec<Payment> = Vec::new();
transactions.extend(received_transactions?);
transactions.extend(outbound_transactions?);

Ok(transactions)
}

fn update_payment_expirations(
payments: Vec<Payment>,
htlc_list: Vec<Htlc>,
) -> NodeResult<Vec<Payment>> {
let mut payments_res: Vec<Payment> = Vec::new();
if htlc_list.is_empty() {
return Ok(payments_res);
}

for mut payment in payments.clone() {
let new_data = payment.clone().details;
if let PaymentDetails::Ln { data } = new_data {
for htlc in &htlc_list {
let payment_hash = hex::encode(htlc.clone().payment_hash);
if payment_hash == data.payment_hash
&& data.pending_expiration_block < Some(htlc.expiry)
{
payment.details.add_pending_expiration_block(htlc.clone())
}
}
}
payments_res.push(payment);
}
info!("pending htlc payments {:?}", payments_res);
Ok(payments_res)
}

//pub(crate) fn offchain_payment_to_transaction
impl TryFrom<OffChainPayment> for Payment {
type Error = NodeError;
Expand Down Expand Up @@ -1487,6 +1523,7 @@ impl TryFrom<OffChainPayment> for Payment {
ln_address: None,
lnurl_withdraw_endpoint: None,
swap_info: None,
pending_expiration_block: None,
},
},
})
Expand Down Expand Up @@ -1526,6 +1563,7 @@ impl TryFrom<gl_client::signer::model::greenlight::Invoice> for Payment {
ln_address: None,
lnurl_withdraw_endpoint: None,
swap_info: None,
pending_expiration_block: None,
},
},
})
Expand Down Expand Up @@ -1580,6 +1618,7 @@ impl TryFrom<gl_client::signer::model::greenlight::Payment> for Payment {
ln_address: None,
lnurl_withdraw_endpoint: None,
swap_info: None,
pending_expiration_block: None,
},
},
})
Expand Down Expand Up @@ -1621,6 +1660,7 @@ impl TryFrom<cln::ListinvoicesInvoices> for Payment {
ln_address: None,
lnurl_withdraw_endpoint: None,
swap_info: None,
pending_expiration_block: None,
},
},
})
Expand Down Expand Up @@ -1685,6 +1725,7 @@ impl TryFrom<cln::ListpaysPays> for Payment {
ln_address: None,
lnurl_withdraw_endpoint: None,
swap_info: None,
pending_expiration_block: None,
},
},
})
Expand Down Expand Up @@ -1772,6 +1813,11 @@ impl From<cln::ListpeersPeersChannels> for Channel {
alias_remote,
alias_local,
closing_txid: None,
htlcs: c
.htlcs
.into_iter()
.map(|c| Htlc::from(c.expiry, c.payment_hash))
.collect(),
}
}
}
Expand Down Expand Up @@ -1846,6 +1892,7 @@ impl TryFrom<ListclosedchannelsClosedchannels> for Channel {
alias_remote,
alias_local,
closing_txid: None,
htlcs: Vec::new(),
})
}
}
Expand Down
12 changes: 6 additions & 6 deletions libs/sdk-core/src/input_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1165,11 +1165,11 @@ pub(crate) mod tests {

assert_eq!(pd.metadata_vec()?.len(), 3);
assert_eq!(
pd.metadata_vec()?.get(0).ok_or("Key not found")?.key,
pd.metadata_vec()?.first().ok_or("Key not found")?.key,
"text/plain"
);
assert_eq!(
pd.metadata_vec()?.get(0).ok_or("Key not found")?.value,
pd.metadata_vec()?.first().ok_or("Key not found")?.value,
"WRhtV"
);
assert_eq!(
Expand Down Expand Up @@ -1207,11 +1207,11 @@ pub(crate) mod tests {

assert_eq!(pd.metadata_vec()?.len(), 3);
assert_eq!(
pd.metadata_vec()?.get(0).ok_or("Key not found")?.key,
pd.metadata_vec()?.first().ok_or("Key not found")?.key,
"text/plain"
);
assert_eq!(
pd.metadata_vec()?.get(0).ok_or("Key not found")?.value,
pd.metadata_vec()?.first().ok_or("Key not found")?.value,
"WRhtV"
);
assert_eq!(
Expand Down Expand Up @@ -1404,11 +1404,11 @@ pub(crate) mod tests {

assert_eq!(pd.metadata_vec()?.len(), 3);
assert_eq!(
pd.metadata_vec()?.get(0).ok_or("Key not found")?.key,
pd.metadata_vec()?.first().ok_or("Key not found")?.key,
"text/plain"
);
assert_eq!(
pd.metadata_vec()?.get(0).ok_or("Key not found")?.value,
pd.metadata_vec()?.first().ok_or("Key not found")?.value,
"WRhtV"
);
assert_eq!(
Expand Down
28 changes: 28 additions & 0 deletions libs/sdk-core/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,14 @@ pub enum PaymentDetails {
},
}

impl PaymentDetails {
pub fn add_pending_expiration_block(&mut self, htlc: Htlc) {
if let PaymentDetails::Ln { data } = self {
data.pending_expiration_block = Some(htlc.expiry)
}
}
}

/// Details of a LN payment, as included in a [Payment]
#[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)]
pub struct LnPaymentDetails {
Expand All @@ -700,6 +708,9 @@ pub struct LnPaymentDetails {

/// Only set for [PaymentType::Received] payments that were received in the context of a swap
pub swap_info: Option<SwapInfo>,

/// Only set for [PaymentType::Pending] payments that are inflight.
pub pending_expiration_block: Option<u32>,
}

/// Represents the funds that were on the user side of the channel at the time it was closed.
Expand Down Expand Up @@ -1098,6 +1109,23 @@ pub struct Channel {
///
/// This may be empty for older closed channels, if it was not possible to retrieve the closing txid.
pub closing_txid: Option<String>,

pub htlcs: Vec<Htlc>,
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Htlc {
pub expiry: u32,
pub payment_hash: Vec<u8>,
}

impl Htlc {
pub fn from(expiry: u32, payment_hash: Vec<u8>) -> Self {
Htlc {
expiry,
payment_hash,
}
}
}

/// State of a Lightning channel
Expand Down
7 changes: 7 additions & 0 deletions libs/sdk-core/src/persist/channels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ impl SqliteStorage {
alias_local: row.get(7)?,
alias_remote: row.get(8)?,
closing_txid: row.get(9)?,
htlcs: Vec::new(),
})
})?
.map(|i| i.unwrap())
Expand Down Expand Up @@ -158,6 +159,7 @@ fn test_simple_sync_channels() {
alias_local: None,
alias_remote: None,
closing_txid: None,
htlcs: Vec::new(),
},
Channel {
funding_txid: "456".to_string(),
Expand All @@ -170,6 +172,7 @@ fn test_simple_sync_channels() {
alias_local: None,
alias_remote: None,
closing_txid: None,
htlcs: Vec::new(),
},
];

Expand Down Expand Up @@ -201,6 +204,7 @@ fn test_sync_closed_channels() {
alias_local: None,
alias_remote: None,
closing_txid: None,
htlcs: Vec::new(),
},
// Simulate closed channel that was persisted with closed_at and closing_txid
Channel {
Expand All @@ -214,6 +218,7 @@ fn test_sync_closed_channels() {
alias_local: None,
alias_remote: None,
closing_txid: Some("a".into()),
htlcs: Vec::new(),
},
];

Expand Down Expand Up @@ -243,6 +248,7 @@ fn test_sync_closed_channels() {
alias_local: None,
alias_remote: None,
closing_txid: None,
htlcs: Vec::new(),
},
Channel {
funding_txid: "456".to_string(),
Expand All @@ -255,6 +261,7 @@ fn test_sync_closed_channels() {
alias_local: None,
alias_remote: None,
closing_txid: None,
htlcs: Vec::new(),
},
];
assert_eq!(expected.len(), queried_channels.len());
Expand Down
2 changes: 1 addition & 1 deletion libs/sdk-core/src/persist/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ pub(crate) fn current_migrations() -> Vec<&'static str> {
confirmed_tx_ids TEXT NOT NULL,
min_allowed_deposit INTEGER NOT NULL,
max_allowed_deposit INTEGER NOT NULL,
last_redeem_error TEXT
last_redeem_error TEXT
) STRICT;
INSERT INTO swaps
Expand Down
Loading

0 comments on commit e6323f5

Please sign in to comment.