diff --git a/CHANGELOG.md b/CHANGELOG.md index b231e8d1c4..5fafb0fe12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ a persisted `ChannelClosed` event. - Users of the VSS storage backend must upgrade their VSS server to at least version `v0.1.0-alpha.0` before upgrading LDK Node. +- The `payment_id` field on the `PaymentSuccessful`, `PaymentFailed`, and + `PaymentReceived` events is now a required (non-optional) `PaymentId`. Events + persisted by LDK Node v0.2.1 or earlier (which stored `payment_id` as + optional) will fail to deserialize on read; users upgrading from those + versions need to drain pending events before the upgrade. +- Applications using the BOLT 11 manual-claiming receive APIs + (`receive_*_for_hash`) now need to set + `Config::manually_claim_unknown_bolt11_payments` to `true`. Otherwise + matching inbound HTLCs will be failed back instead of emitting + `Event::PaymentClaimable`. ## Feature and API updates - The Bitcoin Core RPC and REST chain-source builder methods now accept an optional diff --git a/benches/payments.rs b/benches/payments.rs index 926dc5dade..91de0bfa77 100644 --- a/benches/payments.rs +++ b/benches/payments.rs @@ -75,12 +75,8 @@ async fn send_payments(node_a: Arc, node_b: Arc) -> std::time::Durat while success_count < total_payments { match node_a.next_event_async().await { Event::PaymentSuccessful { payment_id, payment_hash, .. } => { - if let Some(id) = payment_id { - success_count += 1; - println!("{}: Payment with id {:?} completed", payment_hash.0.as_hex(), id); - } else { - println!("Payment completed (no payment_id)"); - } + success_count += 1; + println!("{}: Payment with id {:?} completed", payment_hash.0.as_hex(), payment_id); }, Event::PaymentFailed { payment_id, payment_hash, .. } => { println!("{}: Payment {:?} failed", payment_hash.unwrap().0.as_hex(), payment_id); diff --git a/bindings/python/src/ldk_node/test_ldk_node.py b/bindings/python/src/ldk_node/test_ldk_node.py index 304caf9c04..063c95f700 100644 --- a/bindings/python/src/ldk_node/test_ldk_node.py +++ b/bindings/python/src/ldk_node/test_ldk_node.py @@ -235,7 +235,7 @@ def test_spontaneous_payment(self): self.assertEqual(received_event.custom_records, custom_tlvs) sender_payment = node_1.payment(keysend_payment_id) - receiver_payment = node_2.payment(keysend_payment_id) + receiver_payment = node_2.payment(received_event.payment_id) self.assertIsNotNone(sender_payment) self.assertIsNotNone(receiver_payment) diff --git a/src/config.rs b/src/config.rs index 772c4bd806..16a8caa265 100644 --- a/src/config.rs +++ b/src/config.rs @@ -155,6 +155,7 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_ /// | `route_parameters` | None | /// | `tor_config` | None | /// | `hrn_config` | HumanReadableNamesConfig::default() | +/// | `manually_claim_unknown_bolt11_payments` | false | /// /// See [`AnchorChannelsConfig`] and [`RouteParametersConfig`] for more information regarding their /// respective default values. @@ -219,6 +220,17 @@ pub struct Config { /// /// [BIP 353]: https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki pub hrn_config: HumanReadableNamesConfig, + /// Whether to emit [`Event::PaymentClaimable`] for unknown BOLT 11 payments that were created + /// with a user-provided payment hash and therefore need to be manually claimed. + /// + /// If disabled, such payments are failed back without being added to the payment store. + /// + /// **Warning:** Enabling this may let any holder of a valid invoice generated by this node tie + /// up inbound HTLC slots and grow the persisted event queue until the payment is claimed, + /// failed, or times out. + /// + /// [`Event::PaymentClaimable`]: crate::Event::PaymentClaimable + pub manually_claim_unknown_bolt11_payments: bool, } impl Default for Config { @@ -235,6 +247,7 @@ impl Default for Config { route_parameters: None, node_alias: None, hrn_config: HumanReadableNamesConfig::default(), + manually_claim_unknown_bolt11_payments: false, } } } diff --git a/src/event.rs b/src/event.rs index b8ca735198..d00c1746a4 100644 --- a/src/event.rs +++ b/src/event.rs @@ -105,9 +105,7 @@ pub enum Event { /// A sent payment was successful. PaymentSuccessful { /// A local identifier used to track the payment. - /// - /// Will only be `None` for events serialized with LDK Node v0.2.1 or prior. - payment_id: Option, + payment_id: PaymentId, /// The hash of the payment. payment_hash: PaymentHash, /// The preimage to the `payment_hash`. @@ -133,9 +131,7 @@ pub enum Event { /// A sent payment has failed. PaymentFailed { /// A local identifier used to track the payment. - /// - /// Will only be `None` for events serialized with LDK Node v0.2.1 or prior. - payment_id: Option, + payment_id: PaymentId, /// The hash of the payment. /// /// This will be `None` if the payment failed before receiving an invoice when paying a @@ -151,9 +147,7 @@ pub enum Event { /// A payment has been received. PaymentReceived { /// A local identifier used to track the payment. - /// - /// Will only be `None` for events serialized with LDK Node v0.2.1 or prior. - payment_id: Option, + payment_id: PaymentId, /// The hash of the payment. payment_hash: PaymentHash, /// The value, in thousandths of a satoshi, that has been received. @@ -200,17 +194,23 @@ pub enum Event { /// The caveat described above the `total_fee_earned_msat` field applies here as well. outbound_amount_forwarded_msat: Option, }, - /// A payment for a previously-registered payment hash has been received. + /// A payment for a BOLT 11 invoice with a user-provided payment hash has been received. /// - /// This needs to be manually claimed by supplying the correct preimage to [`claim_for_hash`]. + /// This needs to be manually claimed by supplying the correct preimage to [`claim_for_id`]. /// /// If the provided parameters don't match the expectations or the preimage can't be - /// retrieved in time, should be failed-back via [`fail_for_hash`]. + /// retrieved in time, should be failed-back via [`fail_for_id`]. /// /// Note claiming will necessarily fail after the `claim_deadline` has been reached. /// - /// [`claim_for_hash`]: crate::payment::Bolt11Payment::claim_for_hash - /// [`fail_for_hash`]: crate::payment::Bolt11Payment::fail_for_hash + /// This event is only emitted for unknown BOLT 11 payments if + /// [`Config::manually_claim_unknown_bolt11_payments`] is enabled. Enabling this may let any + /// holder of a valid invoice generated by this node tie up inbound HTLC slots and grow the + /// persisted event queue until the payment is claimed, failed, or times out. + /// + /// [`claim_for_id`]: crate::payment::Bolt11Payment::claim_for_id + /// [`fail_for_id`]: crate::payment::Bolt11Payment::fail_for_id + /// [`Config::manually_claim_unknown_bolt11_payments`]: crate::config::Config::manually_claim_unknown_bolt11_payments PaymentClaimable { /// A local identifier used to track the payment. payment_id: PaymentId, @@ -298,18 +298,18 @@ impl_writeable_tlv_based_enum!(Event, (0, PaymentSuccessful) => { (0, payment_hash, required), (1, fee_paid_msat, option), - (3, payment_id, option), + (3, payment_id, required), (5, payment_preimage, option), (7, bolt12_invoice, option), }, (1, PaymentFailed) => { (0, payment_hash, option), (1, reason, upgradable_option), - (3, payment_id, option), + (3, payment_id, required), }, (2, PaymentReceived) => { (0, payment_hash, required), - (1, payment_id, option), + (1, payment_id, required), (2, amount_msat, required), (3, custom_records, optional_vec), }, @@ -675,6 +675,25 @@ where }) } + fn resolve_inbound_payment_id( + &self, event_payment_id: Option, payment_hash: &PaymentHash, + ) -> (PaymentId, Option) { + let legacy_id = PaymentId(payment_hash.0); + let payment_id = event_payment_id.unwrap_or(legacy_id); + + if let Some(info) = self.payment_store.get(&payment_id) { + return (payment_id, Some(info)); + } + + if legacy_id != payment_id { + if let Some(info) = self.payment_store.get(&legacy_id) { + return (legacy_id, Some(info)); + } + } + + (payment_id, None) + } + pub async fn handle_event(&self, event: LdkEvent) -> Result<(), ReplayEvent> { match event { LdkEvent::FundingGenerationReady { @@ -782,6 +801,7 @@ where .lsps2_funding_tx_broadcast_safe(user_channel_id, counterparty_node_id); }, LdkEvent::PaymentClaimable { + payment_id, payment_hash, purpose, amount_msat, @@ -790,8 +810,8 @@ where counterparty_skimmed_fee_msat, .. } => { - let payment_id = PaymentId(payment_hash.0); - let payment_info = self.payment_store.get(&payment_id); + let (payment_id, mut payment_info) = + self.resolve_inbound_payment_id(payment_id, &payment_hash); if let Some(info) = payment_info.as_ref() { if info.direction == PaymentDirection::Outbound { log_info!( @@ -912,10 +932,73 @@ where } } + if payment_info.is_none() { + if let PaymentPurpose::Bolt11InvoicePayment { + payment_preimage: None, + payment_secret, + .. + } = &purpose + { + if !self.config.manually_claim_unknown_bolt11_payments { + log_info!( + self.logger, + "Refusing unknown BOLT11 payment with hash {} of {}msat", + hex_utils::to_string(&payment_hash.0), + amount_msat, + ); + self.channel_manager.fail_htlc_backwards(&payment_hash); + return Ok(()); + } + + let invoice_amount_msat = + amount_msat.saturating_add(counterparty_skimmed_fee_msat); + let kind = PaymentKind::Bolt11 { + hash: payment_hash, + preimage: None, + secret: Some(*payment_secret), + counterparty_skimmed_fee_msat: if counterparty_skimmed_fee_msat > 0 { + Some(counterparty_skimmed_fee_msat) + } else { + None + }, + }; + let payment = PaymentDetails::new( + payment_id, + kind, + Some(invoice_amount_msat), + None, + PaymentDirection::Inbound, + PaymentStatus::Pending, + ); + + match self.payment_store.insert(payment.clone()).await { + Ok(false) => (), + Ok(true) => { + log_error!( + self.logger, + "Bolt11InvoicePayment with ID {} was previously known", + payment_id, + ); + debug_assert!(false); + }, + Err(e) => { + log_error!( + self.logger, + "Failed to insert payment with ID {}: {}", + payment_id, + e + ); + return Err(ReplayEvent()); + }, + } + payment_info = Some(payment); + } + } + + let should_insert_payment = payment_info.is_none(); if let Some(info) = payment_info { // If this is known by the store but ChannelManager doesn't know the preimage, - // the payment has been registered via `_for_hash` variants and needs to be manually claimed via - // user interaction. + // the payment needs to be manually claimed via user interaction. match info.kind { PaymentKind::Bolt11 { preimage, .. } => { if purpose.preimage().is_none() { @@ -960,7 +1043,57 @@ where amount_msat, ); let payment_preimage = match purpose { - PaymentPurpose::Bolt11InvoicePayment { payment_preimage, .. } => { + PaymentPurpose::Bolt11InvoicePayment { + payment_preimage, + payment_secret, + .. + } => { + if should_insert_payment { + let invoice_amount_msat = + amount_msat.saturating_add(counterparty_skimmed_fee_msat); + let kind = PaymentKind::Bolt11 { + hash: payment_hash, + preimage: payment_preimage, + secret: Some(payment_secret), + counterparty_skimmed_fee_msat: if counterparty_skimmed_fee_msat > 0 + { + Some(counterparty_skimmed_fee_msat) + } else { + None + }, + }; + + let payment = PaymentDetails::new( + payment_id, + kind, + Some(invoice_amount_msat), + None, + PaymentDirection::Inbound, + PaymentStatus::Pending, + ); + + match self.payment_store.insert(payment).await { + Ok(false) => (), + Ok(true) => { + log_error!( + self.logger, + "Bolt11InvoicePayment with ID {} was previously known", + payment_id, + ); + debug_assert!(false); + }, + Err(e) => { + log_error!( + self.logger, + "Failed to insert payment with ID {}: {}", + payment_id, + e + ); + return Err(ReplayEvent()); + }, + } + } + payment_preimage }, PaymentPurpose::Bolt12OfferPayment { @@ -972,84 +1105,130 @@ where let payer_note = payment_context.invoice_request.payer_note_truncated; let offer_id = payment_context.offer_id; let quantity = payment_context.invoice_request.quantity; - let kind = PaymentKind::Bolt12Offer { - hash: Some(payment_hash), - preimage: payment_preimage, - secret: Some(payment_secret), - offer_id, - payer_note, - quantity, - }; - - let payment = PaymentDetails::new( - payment_id, - kind, - Some(amount_msat), - None, - PaymentDirection::Inbound, - PaymentStatus::Pending, - ); + if should_insert_payment { + let kind = PaymentKind::Bolt12Offer { + hash: Some(payment_hash), + preimage: payment_preimage, + secret: Some(payment_secret), + offer_id, + payer_note, + quantity, + }; + + let payment = PaymentDetails::new( + payment_id, + kind, + Some(amount_msat), + None, + PaymentDirection::Inbound, + PaymentStatus::Pending, + ); - match self.payment_store.insert(payment).await { - Ok(false) => (), - Ok(true) => { - log_error!( - self.logger, - "Bolt12OfferPayment with ID {} was previously known", - payment_id, - ); - debug_assert!(false); - }, - Err(e) => { - log_error!( - self.logger, - "Failed to insert payment with ID {}: {}", - payment_id, - e - ); - debug_assert!(false); - }, + match self.payment_store.insert(payment).await { + Ok(false) => (), + Ok(true) => { + log_error!( + self.logger, + "Bolt12OfferPayment with ID {} was previously known", + payment_id, + ); + debug_assert!(false); + }, + Err(e) => { + log_error!( + self.logger, + "Failed to insert payment with ID {}: {}", + payment_id, + e + ); + return Err(ReplayEvent()); + }, + } } payment_preimage }, - PaymentPurpose::Bolt12RefundPayment { payment_preimage, .. } => { + PaymentPurpose::Bolt12RefundPayment { + payment_preimage, + payment_secret, + .. + } => { + if should_insert_payment { + let kind = PaymentKind::Bolt12Refund { + hash: Some(payment_hash), + preimage: payment_preimage, + secret: Some(payment_secret), + payer_note: None, + quantity: None, + }; + + let payment = PaymentDetails::new( + payment_id, + kind, + Some(amount_msat), + None, + PaymentDirection::Inbound, + PaymentStatus::Pending, + ); + + match self.payment_store.insert(payment).await { + Ok(false) => (), + Ok(true) => { + log_error!( + self.logger, + "Bolt12RefundPayment with ID {} was previously known", + payment_id, + ); + debug_assert!(false); + }, + Err(e) => { + log_error!( + self.logger, + "Failed to insert payment with ID {}: {}", + payment_id, + e + ); + return Err(ReplayEvent()); + }, + } + } payment_preimage }, PaymentPurpose::SpontaneousPayment(preimage) => { - // Since it's spontaneous, we insert it now into our store. - let kind = PaymentKind::Spontaneous { - hash: payment_hash, - preimage: Some(preimage), - }; - - let payment = PaymentDetails::new( - payment_id, - kind, - Some(amount_msat), - None, - PaymentDirection::Inbound, - PaymentStatus::Pending, - ); + if should_insert_payment { + let kind = PaymentKind::Spontaneous { + hash: payment_hash, + preimage: Some(preimage), + }; + + let payment = PaymentDetails::new( + payment_id, + kind, + Some(amount_msat), + None, + PaymentDirection::Inbound, + PaymentStatus::Pending, + ); - match self.payment_store.insert(payment).await { - Ok(false) => (), - Ok(true) => { - log_error!( - self.logger, - "Spontaneous payment with ID {} was previously known", - payment_id, - ); - debug_assert!(false); - }, - Err(e) => { - log_error!( - self.logger, - "Failed to insert payment with ID {}: {}", - payment_id, - e - ); - debug_assert!(false); - }, + match self.payment_store.insert(payment).await { + Ok(false) => (), + Ok(true) => { + log_error!( + self.logger, + "Spontaneous payment with ID {} was previously known", + payment_id, + ); + debug_assert!(false); + }, + Err(e) => { + log_error!( + self.logger, + "Failed to insert payment with ID {}: {}", + payment_id, + e + ); + return Err(ReplayEvent()); + }, + } } Some(preimage) @@ -1081,6 +1260,7 @@ where } }, LdkEvent::PaymentClaimed { + payment_id, payment_hash, purpose, amount_msat, @@ -1088,9 +1268,8 @@ where htlcs: _, sender_intended_total_msat: _, onion_fields, - payment_id: _, } => { - let payment_id = PaymentId(payment_hash.0); + let (payment_id, _) = self.resolve_inbound_payment_id(payment_id, &payment_hash); log_info!( self.logger, "Claimed payment with ID {} from payment hash {} of {}msat.", @@ -1099,43 +1278,83 @@ where amount_msat, ); - let update = match purpose { + let (update, kind_for_insert) = match purpose { PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. - } => PaymentDetailsUpdate { - preimage: Some(payment_preimage), - secret: Some(Some(payment_secret)), - amount_msat: Some(Some(amount_msat)), - status: Some(PaymentStatus::Succeeded), - ..PaymentDetailsUpdate::new(payment_id) + } => { + let kind = PaymentKind::Bolt11 { + hash: payment_hash, + preimage: payment_preimage, + secret: Some(payment_secret), + counterparty_skimmed_fee_msat: None, + }; + let update = PaymentDetailsUpdate { + preimage: Some(payment_preimage), + secret: Some(Some(payment_secret)), + amount_msat: Some(Some(amount_msat)), + status: Some(PaymentStatus::Succeeded), + ..PaymentDetailsUpdate::new(payment_id) + }; + (update, kind) }, PaymentPurpose::Bolt12OfferPayment { - payment_preimage, payment_secret, .. - } => PaymentDetailsUpdate { - preimage: Some(payment_preimage), - secret: Some(Some(payment_secret)), - amount_msat: Some(Some(amount_msat)), - status: Some(PaymentStatus::Succeeded), - ..PaymentDetailsUpdate::new(payment_id) + payment_preimage, + payment_secret, + payment_context, + .. + } => { + let kind = PaymentKind::Bolt12Offer { + hash: Some(payment_hash), + preimage: payment_preimage, + secret: Some(payment_secret), + offer_id: payment_context.offer_id, + payer_note: payment_context.invoice_request.payer_note_truncated, + quantity: payment_context.invoice_request.quantity, + }; + let update = PaymentDetailsUpdate { + preimage: Some(payment_preimage), + secret: Some(Some(payment_secret)), + amount_msat: Some(Some(amount_msat)), + status: Some(PaymentStatus::Succeeded), + ..PaymentDetailsUpdate::new(payment_id) + }; + (update, kind) }, PaymentPurpose::Bolt12RefundPayment { payment_preimage, payment_secret, .. - } => PaymentDetailsUpdate { - preimage: Some(payment_preimage), - secret: Some(Some(payment_secret)), - amount_msat: Some(Some(amount_msat)), - status: Some(PaymentStatus::Succeeded), - ..PaymentDetailsUpdate::new(payment_id) + } => { + let kind = PaymentKind::Bolt12Refund { + hash: Some(payment_hash), + preimage: payment_preimage, + secret: Some(payment_secret), + payer_note: None, + quantity: None, + }; + let update = PaymentDetailsUpdate { + preimage: Some(payment_preimage), + secret: Some(Some(payment_secret)), + amount_msat: Some(Some(amount_msat)), + status: Some(PaymentStatus::Succeeded), + ..PaymentDetailsUpdate::new(payment_id) + }; + (update, kind) }, - PaymentPurpose::SpontaneousPayment(preimage) => PaymentDetailsUpdate { - preimage: Some(Some(preimage)), - amount_msat: Some(Some(amount_msat)), - status: Some(PaymentStatus::Succeeded), - ..PaymentDetailsUpdate::new(payment_id) + PaymentPurpose::SpontaneousPayment(preimage) => { + let kind = PaymentKind::Spontaneous { + hash: payment_hash, + preimage: Some(preimage), + }; + let update = PaymentDetailsUpdate { + preimage: Some(Some(preimage)), + amount_msat: Some(Some(amount_msat)), + status: Some(PaymentStatus::Succeeded), + ..PaymentDetailsUpdate::new(payment_id) + }; + (update, kind) }, }; @@ -1145,11 +1364,23 @@ where // be the result of a replayed event. ), Ok(DataStoreUpdateResult::NotFound) => { - log_error!( - self.logger, - "Claimed payment with ID {} couldn't be found in store", + let payment = PaymentDetails::new( payment_id, + kind_for_insert, + Some(amount_msat), + None, + PaymentDirection::Inbound, + PaymentStatus::Succeeded, ); + if let Err(e) = self.payment_store.insert(payment).await { + log_error!( + self.logger, + "Failed to insert payment with ID {}: {}", + payment_id, + e + ); + return Err(ReplayEvent()); + } }, Err(e) => { log_error!( @@ -1163,7 +1394,7 @@ where } let event = Event::PaymentReceived { - payment_id: Some(payment_id), + payment_id, payment_hash, amount_msat, custom_records: onion_fields @@ -1228,7 +1459,7 @@ where ); }); let event = Event::PaymentSuccessful { - payment_id: Some(payment_id), + payment_id, payment_hash, payment_preimage: Some(payment_preimage), fee_paid_msat, @@ -1264,8 +1495,7 @@ where }, }; - let event = - Event::PaymentFailed { payment_id: Some(payment_id), payment_hash, reason }; + let event = Event::PaymentFailed { payment_id, payment_hash, reason }; match self.event_queue.add_event(event).await { Ok(_) => return Ok(()), Err(e) => { diff --git a/src/lib.rs b/src/lib.rs index b22c1538cc..e8c69dec09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1016,6 +1016,7 @@ impl Node { Bolt11Payment::new( Arc::clone(&self.runtime), Arc::clone(&self.channel_manager), + Arc::clone(&self.keys_manager), Arc::clone(&self.connection_manager), Arc::clone(&self.liquidity_source), Arc::clone(&self.payment_store), @@ -1034,6 +1035,7 @@ impl Node { Arc::new(Bolt11Payment::new( Arc::clone(&self.runtime), Arc::clone(&self.channel_manager), + Arc::clone(&self.keys_manager), Arc::clone(&self.connection_manager), Arc::clone(&self.liquidity_source), Arc::clone(&self.payment_store), diff --git a/src/payment/bolt11.rs b/src/payment/bolt11.rs index 4503dfa061..f4596a7646 100644 --- a/src/payment/bolt11.rs +++ b/src/payment/bolt11.rs @@ -19,6 +19,7 @@ use lightning::ln::channelmanager::{ }; use lightning::ln::outbound_payment::{Bolt11PaymentError, Retry, RetryableSendFailure}; use lightning::routing::router::{PaymentParameters, RouteParameters, RouteParametersConfig}; +use lightning::sign::EntropySource; use lightning_invoice::{ Bolt11Invoice as LdkBolt11Invoice, Bolt11InvoiceDescription as LdkBolt11InvoiceDescription, }; @@ -37,7 +38,7 @@ use crate::payment::store::{ }; use crate::peer_store::{PeerInfo, PeerStore}; use crate::runtime::Runtime; -use crate::types::{ChannelManager, PaymentStore}; +use crate::types::{ChannelManager, KeysManager, PaymentStore}; #[cfg(not(feature = "uniffi"))] type Bolt11Invoice = LdkBolt11Invoice; @@ -69,6 +70,7 @@ impl_writeable_tlv_based!(PaymentMetadata, { pub struct Bolt11Payment { runtime: Arc, channel_manager: Arc, + keys_manager: Arc, connection_manager: Arc>>, liquidity_source: Arc>>, payment_store: Arc, @@ -81,7 +83,7 @@ pub struct Bolt11Payment { impl Bolt11Payment { pub(crate) fn new( runtime: Arc, channel_manager: Arc, - connection_manager: Arc>>, + keys_manager: Arc, connection_manager: Arc>>, liquidity_source: Arc>>, payment_store: Arc, peer_store: Arc>>, config: Arc, is_running: Arc>, logger: Arc, @@ -89,6 +91,7 @@ impl Bolt11Payment { Self { runtime, channel_manager, + keys_manager, connection_manager, liquidity_source, payment_store, @@ -103,64 +106,24 @@ impl Bolt11Payment { &self, amount_msat: Option, invoice_description: &LdkBolt11InvoiceDescription, expiry_secs: u32, manual_claim_payment_hash: Option, ) -> Result { - let invoice = { - let invoice_params = Bolt11InvoiceParameters { - amount_msats: amount_msat, - description: invoice_description.clone(), - invoice_expiry_delta_secs: Some(expiry_secs), - payment_hash: manual_claim_payment_hash, - ..Default::default() - }; - - match self.channel_manager.create_bolt11_invoice(invoice_params) { - Ok(inv) => { - log_info!(self.logger, "Invoice created: {}", inv); - inv - }, - Err(e) => { - log_error!(self.logger, "Failed to create invoice: {}", e); - return Err(Error::InvoiceCreationFailed); - }, - } + let invoice_params = Bolt11InvoiceParameters { + amount_msats: amount_msat, + description: invoice_description.clone(), + invoice_expiry_delta_secs: Some(expiry_secs), + payment_hash: manual_claim_payment_hash, + ..Default::default() }; - let payment_hash = invoice.payment_hash(); - let payment_secret = invoice.payment_secret(); - let id = PaymentId(payment_hash.0); - let preimage = if manual_claim_payment_hash.is_none() { - // If the user hasn't registered a custom payment hash, we're positive ChannelManager - // will know the preimage at this point. - let mut payment_metadata = invoice.payment_metadata().cloned(); - let res = self - .channel_manager - .get_payment_preimage_decrypt_metadata( - payment_hash, - payment_secret.clone(), - payment_metadata.as_deref_mut(), - ) - .ok(); - debug_assert!(res.is_some(), "We just let ChannelManager create an inbound payment, it can't have forgotten the preimage by now."); - res - } else { - None - }; - let kind = PaymentKind::Bolt11 { - hash: payment_hash, - preimage, - secret: Some(payment_secret.clone()), - counterparty_skimmed_fee_msat: None, - }; - let payment = PaymentDetails::new( - id, - kind, - amount_msat, - None, - PaymentDirection::Inbound, - PaymentStatus::Pending, - ); - self.runtime.block_on(self.payment_store.insert(payment))?; - - Ok(invoice) + match self.channel_manager.create_bolt11_invoice(invoice_params) { + Ok(inv) => { + log_info!(self.logger, "Invoice created: {}", inv); + Ok(inv) + }, + Err(e) => { + log_error!(self.logger, "Failed to create invoice: {}", e); + Err(Error::InvoiceCreationFailed) + }, + } } fn receive_via_jit_channel_inner( @@ -169,7 +132,7 @@ impl Bolt11Payment { max_proportional_lsp_fee_limit_ppm_msat: Option, payment_hash: Option, ) -> Result { let connection_manager = Arc::clone(&self.connection_manager); - let (invoice, chosen_lsp) = self.runtime.block_on(async move { + let res = self.runtime.block_on(async move { if let Some(amount_msat) = amount_msat { self.liquidity_source .lsps2_client() @@ -194,36 +157,8 @@ impl Bolt11Payment { ) .await } - })?; - - // Register payment in payment store. - let payment_hash = invoice.payment_hash(); - let payment_secret = invoice.payment_secret(); - let id = PaymentId(payment_hash.0); - let mut payment_metadata = invoice.payment_metadata().cloned(); - let preimage = self - .channel_manager - .get_payment_preimage_decrypt_metadata( - payment_hash, - payment_secret.clone(), - payment_metadata.as_deref_mut(), - ) - .ok(); - let kind = PaymentKind::Bolt11 { - hash: payment_hash, - preimage, - secret: Some(payment_secret.clone()), - counterparty_skimmed_fee_msat: None, - }; - let payment = PaymentDetails::new( - id, - kind, - amount_msat, - None, - PaymentDirection::Inbound, - PaymentStatus::Pending, - ); - self.runtime.block_on(self.payment_store.insert(payment))?; + }); + let (invoice, chosen_lsp) = res?; // Persist the chosen LSP peer to make sure we reconnect on restart. let peer_info = PeerInfo { node_id: chosen_lsp.node_id, address: chosen_lsp.address }; @@ -275,15 +210,7 @@ impl Bolt11Payment { } let payment_hash = invoice.payment_hash(); - let payment_id = PaymentId(invoice.payment_hash().0); - if let Some(payment) = self.payment_store.get(&payment_id) { - if payment.status == PaymentStatus::Pending - || payment.status == PaymentStatus::Succeeded - { - log_error!(self.logger, "Payment error: an invoice must not be paid twice."); - return Err(Error::DuplicatePayment); - } - } + let payment_id = PaymentId(self.keys_manager.get_secure_random_bytes()); let route_params_config = route_parameters.or(self.config.route_parameters).unwrap_or_default(); @@ -474,10 +401,10 @@ impl Bolt11Payment { ) } - /// Allows to attempt manually claiming payments with the given preimage that have previously - /// been registered via [`receive_for_hash`] or [`receive_variable_amount_for_hash`]. + /// Allows to attempt manually claiming a payment with the given preimage after a + /// [`PaymentClaimable`] event has been emitted. /// - /// This should be called in reponse to a [`PaymentClaimable`] event as soon as the preimage is + /// This should be called in response to a [`PaymentClaimable`] event as soon as the preimage is /// available. /// /// Will check that the payment is known, and that the given preimage and claimable amount @@ -486,17 +413,33 @@ impl Bolt11Payment { /// /// When claiming the payment has succeeded, a [`PaymentReceived`] event will be emitted. /// - /// [`receive_for_hash`]: Self::receive_for_hash - /// [`receive_variable_amount_for_hash`]: Self::receive_variable_amount_for_hash /// [`PaymentClaimable`]: crate::Event::PaymentClaimable /// [`PaymentReceived`]: crate::Event::PaymentReceived - pub fn claim_for_hash( - &self, payment_hash: PaymentHash, claimable_amount_msat: u64, preimage: PaymentPreimage, + pub fn claim_for_id( + &self, payment_id: PaymentId, claimable_amount_msat: u64, preimage: PaymentPreimage, ) -> Result<(), Error> { - let payment_id = PaymentId(payment_hash.0); + let details = self.payment_store.get(&payment_id).ok_or_else(|| { + log_error!( + self.logger, + "Failed to manually claim unknown payment with ID: {}", + payment_id + ); + Error::InvalidPaymentId + })?; - let expected_payment_hash = PaymentHash(Sha256::hash(&preimage.0).to_byte_array()); + let payment_hash = match details.kind { + PaymentKind::Bolt11 { hash, .. } => hash, + _ => { + log_error!( + self.logger, + "Failed to manually claim payment with ID {} of unsupported kind", + payment_id + ); + return Err(Error::InvalidPaymentId); + }, + }; + let expected_payment_hash = PaymentHash(Sha256::hash(&preimage.0).to_byte_array()); if expected_payment_hash != payment_hash { log_error!( self.logger, @@ -506,54 +449,60 @@ impl Bolt11Payment { return Err(Error::InvalidPaymentPreimage); } - if let Some(details) = self.payment_store.get(&payment_id) { - // For payments requested via `receive*_via_jit_channel_for_hash()` - // `skimmed_fee_msat` held by LSP must be taken into account. - let skimmed_fee_msat = match details.kind { - PaymentKind::Bolt11 { - counterparty_skimmed_fee_msat: Some(skimmed_fee_msat), - .. - } => skimmed_fee_msat, - _ => 0, - }; - if let Some(invoice_amount_msat) = details.amount_msat { - if claimable_amount_msat < invoice_amount_msat.saturating_sub(skimmed_fee_msat) { - log_error!( - self.logger, - "Failed to manually claim payment {} as the claimable amount is less than expected", - payment_id - ); - return Err(Error::InvalidAmount); - } + // For payments requested via `receive*_via_jit_channel_for_hash()` + // `skimmed_fee_msat` held by LSP must be taken into account. + let skimmed_fee_msat = match details.kind { + PaymentKind::Bolt11 { + counterparty_skimmed_fee_msat: Some(skimmed_fee_msat), .. + } => skimmed_fee_msat, + _ => 0, + }; + if let Some(invoice_amount_msat) = details.amount_msat { + if claimable_amount_msat < invoice_amount_msat.saturating_sub(skimmed_fee_msat) { + log_error!( + self.logger, + "Failed to manually claim payment {} as the claimable amount is less than expected", + payment_id + ); + return Err(Error::InvalidAmount); } - } else { - log_error!( - self.logger, - "Failed to manually claim unknown payment with hash: {}", - payment_hash - ); - return Err(Error::InvalidPaymentHash); } self.channel_manager.claim_funds(preimage); Ok(()) } - /// Allows to manually fail payments with the given hash that have previously - /// been registered via [`receive_for_hash`] or [`receive_variable_amount_for_hash`]. + /// Allows to manually fail a payment after a [`PaymentClaimable`] event has been emitted. /// - /// This should be called in reponse to a [`PaymentClaimable`] event if the payment needs to be + /// This should be called in response to a [`PaymentClaimable`] event if the payment needs to be /// failed back, e.g., if the correct preimage can't be retrieved in time before the claim /// deadline has been reached. /// /// Will check that the payment is known before failing the payment, and will return an error /// otherwise. /// - /// [`receive_for_hash`]: Self::receive_for_hash - /// [`receive_variable_amount_for_hash`]: Self::receive_variable_amount_for_hash /// [`PaymentClaimable`]: crate::Event::PaymentClaimable - pub fn fail_for_hash(&self, payment_hash: PaymentHash) -> Result<(), Error> { - let payment_id = PaymentId(payment_hash.0); + pub fn fail_for_id(&self, payment_id: PaymentId) -> Result<(), Error> { + let details = self.payment_store.get(&payment_id).ok_or_else(|| { + log_error!( + self.logger, + "Failed to manually fail unknown payment with ID {}", + payment_id, + ); + Error::InvalidPaymentId + })?; + + let payment_hash = match details.kind { + PaymentKind::Bolt11 { hash, .. } => hash, + _ => { + log_error!( + self.logger, + "Failed to manually fail payment with ID {} of unsupported kind", + payment_id + ); + return Err(Error::InvalidPaymentId); + }, + }; let update = PaymentDetailsUpdate { status: Some(PaymentStatus::Failed), @@ -565,10 +514,10 @@ impl Bolt11Payment { Ok(DataStoreUpdateResult::NotFound) => { log_error!( self.logger, - "Failed to manually fail unknown payment with hash {}", - payment_hash, + "Failed to manually fail unknown payment with ID {}", + payment_id, ); - return Err(Error::InvalidPaymentHash); + return Err(Error::InvalidPaymentId); }, Err(e) => { log_error!( @@ -600,17 +549,23 @@ impl Bolt11Payment { /// Returns a payable invoice that can be used to request a payment of the amount /// given for the given payment hash. /// - /// We will register the given payment hash and emit a [`PaymentClaimable`] event once - /// the inbound payment arrives. + /// The inbound payment will only be accepted if + /// [`Config::manually_claim_unknown_bolt11_payments`] is enabled. We will then emit a + /// [`PaymentClaimable`] event once the inbound payment arrives. + /// + /// **Warning:** it is the user's responsibility to never reuse the same payment hash. + /// Reusing a payment hash is unsafe and can lead to loss of funds. We do not detect or reject + /// duplicates for this API. /// /// **Note:** users *MUST* handle this event and claim the payment manually via - /// [`claim_for_hash`] as soon as they have obtained access to the preimage of the given - /// payment hash. If they're unable to obtain the preimage, they *MUST* immediately fail the payment via - /// [`fail_for_hash`]. + /// [`claim_for_id`] as soon as they have obtained access to the preimage of the given + /// payment hash. If they're unable to obtain the preimage, they *MUST* immediately fail the + /// payment via [`fail_for_id`]. /// /// [`PaymentClaimable`]: crate::Event::PaymentClaimable - /// [`claim_for_hash`]: Self::claim_for_hash - /// [`fail_for_hash`]: Self::fail_for_hash + /// [`Config::manually_claim_unknown_bolt11_payments`]: crate::config::Config::manually_claim_unknown_bolt11_payments + /// [`claim_for_id`]: Self::claim_for_id + /// [`fail_for_id`]: Self::fail_for_id pub fn receive_for_hash( &self, amount_msat: u64, description: &Bolt11InvoiceDescription, expiry_secs: u32, payment_hash: PaymentHash, @@ -636,17 +591,23 @@ impl Bolt11Payment { /// Returns a payable invoice that can be used to request a payment for the given payment hash /// and the amount to be determined by the user, also known as a "zero-amount" invoice. /// - /// We will register the given payment hash and emit a [`PaymentClaimable`] event once - /// the inbound payment arrives. + /// The inbound payment will only be accepted if + /// [`Config::manually_claim_unknown_bolt11_payments`] is enabled. We will then emit a + /// [`PaymentClaimable`] event once the inbound payment arrives. + /// + /// **Warning:** it is the user's responsibility to never reuse the same payment hash. + /// Reusing a payment hash is unsafe and can lead to loss of funds. We do not detect or reject + /// duplicates for this API. /// /// **Note:** users *MUST* handle this event and claim the payment manually via - /// [`claim_for_hash`] as soon as they have obtained access to the preimage of the given - /// payment hash. If they're unable to obtain the preimage, they *MUST* immediately fail the payment via - /// [`fail_for_hash`]. + /// [`claim_for_id`] as soon as they have obtained access to the preimage of the given + /// payment hash. If they're unable to obtain the preimage, they *MUST* immediately fail the + /// payment via [`fail_for_id`]. /// /// [`PaymentClaimable`]: crate::Event::PaymentClaimable - /// [`claim_for_hash`]: Self::claim_for_hash - /// [`fail_for_hash`]: Self::fail_for_hash + /// [`Config::manually_claim_unknown_bolt11_payments`]: crate::config::Config::manually_claim_unknown_bolt11_payments + /// [`claim_for_id`]: Self::claim_for_id + /// [`fail_for_id`]: Self::fail_for_id pub fn receive_variable_amount_for_hash( &self, description: &Bolt11InvoiceDescription, expiry_secs: u32, payment_hash: PaymentHash, ) -> Result { @@ -690,19 +651,25 @@ impl Bolt11Payment { /// If set, `max_total_lsp_fee_limit_msat` will limit how much fee we allow the LSP to take for opening the /// channel to us. We'll use its cheapest offer otherwise. /// - /// We will register the given payment hash and emit a [`PaymentClaimable`] event once - /// the inbound payment arrives. The check that [`counterparty_skimmed_fee_msat`] is within the limits - /// is performed *before* emitting the event. + /// The inbound payment will only be accepted if + /// [`Config::manually_claim_unknown_bolt11_payments`] is enabled. We will then emit a + /// [`PaymentClaimable`] event once the inbound payment arrives. The check that + /// [`counterparty_skimmed_fee_msat`] is within the limits is performed *before* emitting the event. + /// + /// **Warning:** it is the user's responsibility to never reuse the same payment hash. + /// Reusing a payment hash is unsafe and can lead to loss of funds. We do not detect or reject + /// duplicates for this API. /// /// **Note:** users *MUST* handle this event and claim the payment manually via - /// [`claim_for_hash`] as soon as they have obtained access to the preimage of the given - /// payment hash. If they're unable to obtain the preimage, they *MUST* immediately fail the payment via - /// [`fail_for_hash`]. + /// [`claim_for_id`] as soon as they have obtained access to the preimage of the given + /// payment hash. If they're unable to obtain the preimage, they *MUST* immediately fail the + /// payment via [`fail_for_id`]. /// /// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md /// [`PaymentClaimable`]: crate::Event::PaymentClaimable - /// [`claim_for_hash`]: Self::claim_for_hash - /// [`fail_for_hash`]: Self::fail_for_hash + /// [`Config::manually_claim_unknown_bolt11_payments`]: crate::config::Config::manually_claim_unknown_bolt11_payments + /// [`claim_for_id`]: Self::claim_for_id + /// [`fail_for_id`]: Self::fail_for_id /// [`counterparty_skimmed_fee_msat`]: crate::payment::PaymentKind::Bolt11::counterparty_skimmed_fee_msat pub fn receive_via_jit_channel_for_hash( &self, amount_msat: u64, description: &Bolt11InvoiceDescription, expiry_secs: u32, @@ -757,19 +724,25 @@ impl Bolt11Payment { /// parts-per-million millisatoshis, we allow the LSP to take for opening the channel to us. /// We'll use its cheapest offer otherwise. /// - /// We will register the given payment hash and emit a [`PaymentClaimable`] event once - /// the inbound payment arrives. The check that [`counterparty_skimmed_fee_msat`] is within the limits - /// is performed *before* emitting the event. + /// The inbound payment will only be accepted if + /// [`Config::manually_claim_unknown_bolt11_payments`] is enabled. We will then emit a + /// [`PaymentClaimable`] event once the inbound payment arrives. The check that + /// [`counterparty_skimmed_fee_msat`] is within the limits is performed *before* emitting the event. + /// + /// **Warning:** it is the user's responsibility to never reuse the same payment hash. + /// Reusing a payment hash is unsafe and can lead to loss of funds. We do not detect or reject + /// duplicates for this API. /// /// **Note:** users *MUST* handle this event and claim the payment manually via - /// [`claim_for_hash`] as soon as they have obtained access to the preimage of the given - /// payment hash. If they're unable to obtain the preimage, they *MUST* immediately fail the payment via - /// [`fail_for_hash`]. + /// [`claim_for_id`] as soon as they have obtained access to the preimage of the given + /// payment hash. If they're unable to obtain the preimage, they *MUST* immediately fail the + /// payment via [`fail_for_id`]. /// /// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md /// [`PaymentClaimable`]: crate::Event::PaymentClaimable - /// [`claim_for_hash`]: Self::claim_for_hash - /// [`fail_for_hash`]: Self::fail_for_hash + /// [`Config::manually_claim_unknown_bolt11_payments`]: crate::config::Config::manually_claim_unknown_bolt11_payments + /// [`claim_for_id`]: Self::claim_for_id + /// [`fail_for_id`]: Self::fail_for_id /// [`counterparty_skimmed_fee_msat`]: crate::payment::PaymentKind::Bolt11::counterparty_skimmed_fee_msat pub fn receive_variable_amount_via_jit_channel_for_hash( &self, description: &Bolt11InvoiceDescription, expiry_secs: u32, diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 50e2b993c8..c2fce6d2f1 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -224,7 +224,7 @@ macro_rules! expect_payment_received_event { ref e @ Event::PaymentReceived { payment_id, amount_msat, .. } => { println!("{} got event {:?}", $node.node_id(), e); assert_eq!(amount_msat, $amount_msat); - let payment = $node.payment(&payment_id.unwrap()).unwrap(); + let payment = $node.payment(&payment_id).unwrap(); if !matches!(payment.kind, ldk_node::payment::PaymentKind::Onchain { .. }) { assert_eq!(payment.fee_paid_msat, None); } @@ -241,6 +241,36 @@ macro_rules! expect_payment_received_event { pub(crate) use expect_payment_received_event; macro_rules! expect_payment_claimable_event { + ($node:expr, $payment_hash:expr, $claimable_amount_msat:expr) => {{ + let event = tokio::time::timeout( + std::time::Duration::from_secs(crate::common::INTEROP_TIMEOUT_SECS), + $node.next_event_async(), + ) + .await + .unwrap_or_else(|_| { + panic!( + "{} timed out waiting for PaymentClaimable event after 60s", + std::stringify!($node) + ) + }); + match event { + ref e @ Event::PaymentClaimable { + payment_id, + payment_hash, + claimable_amount_msat, + .. + } => { + println!("{} got event {:?}", std::stringify!($node), e); + assert_eq!(payment_hash, $payment_hash); + assert_eq!(claimable_amount_msat, $claimable_amount_msat); + $node.event_handled().unwrap(); + (payment_id, claimable_amount_msat) + }, + ref e => { + panic!("{} got unexpected event!: {:?}", std::stringify!($node), e); + }, + } + }}; ($node:expr, $payment_id:expr, $payment_hash:expr, $claimable_amount_msat:expr) => {{ let event = tokio::time::timeout( std::time::Duration::from_secs(crate::common::INTEROP_TIMEOUT_SECS), @@ -292,7 +322,7 @@ macro_rules! expect_payment_successful_event { if let Some(fee_msat) = $fee_paid_msat { assert_eq!(fee_paid_msat, fee_msat); } - let payment = $node.payment(&$payment_id.unwrap()).unwrap(); + let payment = $node.payment(&$payment_id).unwrap(); assert_eq!(payment.fee_paid_msat, fee_paid_msat); assert_eq!(payment_id, $payment_id); $node.event_handled().unwrap(); @@ -623,6 +653,7 @@ pub(crate) fn setup_two_nodes_with_store( println!("\n== Node B =="); let mut config_b = random_config(); config_b.store_type = store_type; + config_b.node_config.manually_claim_unknown_bolt11_payments = true; if cfg!(hrn_tests) { config_b.node_config.hrn_config = HumanReadableNamesConfig { @@ -1240,10 +1271,9 @@ pub(crate) async fn do_channel_full_cycle( .unwrap(); println!("\nA send"); - let payment_id = node_a.bolt11_payment().send(&invoice, None).unwrap(); - assert_eq!(node_a.bolt11_payment().send(&invoice, None), Err(NodeError::DuplicatePayment)); + let outbound_payment_id = node_a.bolt11_payment().send(&invoice, None).unwrap(); - assert!(!node_a.list_payments_with_filter(|p| p.id == payment_id).is_empty()); + assert!(!node_a.list_payments_with_filter(|p| p.id == outbound_payment_id).is_empty()); let outbound_payments_a = node_a.list_payments_with_filter(|p| { p.direction == PaymentDirection::Outbound && matches!(p.kind, PaymentKind::Bolt11 { .. }) @@ -1263,7 +1293,7 @@ pub(crate) async fn do_channel_full_cycle( let inbound_payments_b = node_b.list_payments_with_filter(|p| { p.direction == PaymentDirection::Inbound && matches!(p.kind, PaymentKind::Bolt11 { .. }) }); - assert_eq!(inbound_payments_b.len(), 1); + assert_eq!(inbound_payments_b.len(), 0); // Verify bolt12_invoice is None for BOLT11 payments match node_a.next_event_async().await { @@ -1276,24 +1306,17 @@ pub(crate) async fn do_channel_full_cycle( panic!("{} got unexpected event!: {:?}", std::stringify!(node_a), e); }, } - expect_event!(node_b, PaymentReceived); - assert_eq!(node_a.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_a.payment(&payment_id).unwrap().direction, PaymentDirection::Outbound); - assert_eq!(node_a.payment(&payment_id).unwrap().amount_msat, Some(invoice_amount_1_msat)); - assert!(matches!(node_a.payment(&payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); - assert_eq!(node_b.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_b.payment(&payment_id).unwrap().direction, PaymentDirection::Inbound); - assert_eq!(node_b.payment(&payment_id).unwrap().amount_msat, Some(invoice_amount_1_msat)); - assert!(matches!(node_b.payment(&payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); - - // Assert we fail duplicate outbound payments and check the status hasn't changed. - assert_eq!(Err(NodeError::DuplicatePayment), node_a.bolt11_payment().send(&invoice, None)); - assert_eq!(node_a.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_a.payment(&payment_id).unwrap().direction, PaymentDirection::Outbound); - assert_eq!(node_a.payment(&payment_id).unwrap().amount_msat, Some(invoice_amount_1_msat)); - assert_eq!(node_b.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_b.payment(&payment_id).unwrap().direction, PaymentDirection::Inbound); - assert_eq!(node_b.payment(&payment_id).unwrap().amount_msat, Some(invoice_amount_1_msat)); + let inbound_payment_id = expect_payment_received_event!(node_b, invoice_amount_1_msat); + let outbound_payment = node_a.payment(&outbound_payment_id).unwrap(); + assert_eq!(outbound_payment.status, PaymentStatus::Succeeded); + assert_eq!(outbound_payment.direction, PaymentDirection::Outbound); + assert_eq!(outbound_payment.amount_msat, Some(invoice_amount_1_msat)); + assert!(matches!(&outbound_payment.kind, PaymentKind::Bolt11 { .. })); + let inbound_payment = node_b.payment(&inbound_payment_id).unwrap(); + assert_eq!(inbound_payment.status, PaymentStatus::Succeeded); + assert_eq!(inbound_payment.direction, PaymentDirection::Inbound); + assert_eq!(inbound_payment.amount_msat, Some(invoice_amount_1_msat)); + assert!(matches!(&inbound_payment.kind, PaymentKind::Bolt11 { .. })); // Test under-/overpayment let invoice_amount_2_msat = 2500_000; @@ -1316,28 +1339,30 @@ pub(crate) async fn do_channel_full_cycle( let overpaid_amount_msat = invoice_amount_2_msat + 100; println!("\nA overpaid send"); - let payment_id = + let outbound_payment_id = node_a.bolt11_payment().send_using_amount(&invoice, overpaid_amount_msat, None).unwrap(); expect_event!(node_a, PaymentSuccessful); - let received_amount = match node_b.next_event_async().await { - ref e @ Event::PaymentReceived { amount_msat, .. } => { + let (inbound_payment_id, received_amount) = match node_b.next_event_async().await { + ref e @ Event::PaymentReceived { payment_id, amount_msat, .. } => { println!("{} got event {:?}", std::stringify!(node_b), e); node_b.event_handled().unwrap(); - amount_msat + (payment_id, amount_msat) }, ref e => { panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e); }, }; assert_eq!(received_amount, overpaid_amount_msat); - assert_eq!(node_a.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_a.payment(&payment_id).unwrap().direction, PaymentDirection::Outbound); - assert_eq!(node_a.payment(&payment_id).unwrap().amount_msat, Some(overpaid_amount_msat)); - assert!(matches!(node_a.payment(&payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); - assert_eq!(node_b.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_b.payment(&payment_id).unwrap().direction, PaymentDirection::Inbound); - assert_eq!(node_b.payment(&payment_id).unwrap().amount_msat, Some(overpaid_amount_msat)); - assert!(matches!(node_b.payment(&payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); + let outbound_payment = node_a.payment(&outbound_payment_id).unwrap(); + assert_eq!(outbound_payment.status, PaymentStatus::Succeeded); + assert_eq!(outbound_payment.direction, PaymentDirection::Outbound); + assert_eq!(outbound_payment.amount_msat, Some(overpaid_amount_msat)); + assert!(matches!(&outbound_payment.kind, PaymentKind::Bolt11 { .. })); + let inbound_payment = node_b.payment(&inbound_payment_id).unwrap(); + assert_eq!(inbound_payment.status, PaymentStatus::Succeeded); + assert_eq!(inbound_payment.direction, PaymentDirection::Inbound); + assert_eq!(inbound_payment.amount_msat, Some(overpaid_amount_msat)); + assert!(matches!(&inbound_payment.kind, PaymentKind::Bolt11 { .. })); // Test "zero-amount" invoice payment println!("\nB receive_variable_amount_payment"); @@ -1351,31 +1376,33 @@ pub(crate) async fn do_channel_full_cycle( node_a.bolt11_payment().send(&variable_amount_invoice, None) ); println!("\nA send_using_amount"); - let payment_id = node_a + let outbound_payment_id = node_a .bolt11_payment() .send_using_amount(&variable_amount_invoice, determined_amount_msat, None) .unwrap(); expect_event!(node_a, PaymentSuccessful); - let received_amount = match node_b.next_event_async().await { - ref e @ Event::PaymentReceived { amount_msat, .. } => { + let (inbound_payment_id, received_amount) = match node_b.next_event_async().await { + ref e @ Event::PaymentReceived { payment_id, amount_msat, .. } => { println!("{} got event {:?}", std::stringify!(node_b), e); node_b.event_handled().unwrap(); - amount_msat + (payment_id, amount_msat) }, ref e => { panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e); }, }; assert_eq!(received_amount, determined_amount_msat); - assert_eq!(node_a.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_a.payment(&payment_id).unwrap().direction, PaymentDirection::Outbound); - assert_eq!(node_a.payment(&payment_id).unwrap().amount_msat, Some(determined_amount_msat)); - assert!(matches!(node_a.payment(&payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); - assert_eq!(node_b.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_b.payment(&payment_id).unwrap().direction, PaymentDirection::Inbound); - assert_eq!(node_b.payment(&payment_id).unwrap().amount_msat, Some(determined_amount_msat)); - assert!(matches!(node_b.payment(&payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); + let outbound_payment = node_a.payment(&outbound_payment_id).unwrap(); + assert_eq!(outbound_payment.status, PaymentStatus::Succeeded); + assert_eq!(outbound_payment.direction, PaymentDirection::Outbound); + assert_eq!(outbound_payment.amount_msat, Some(determined_amount_msat)); + assert!(matches!(&outbound_payment.kind, PaymentKind::Bolt11 { .. })); + let inbound_payment = node_b.payment(&inbound_payment_id).unwrap(); + assert_eq!(inbound_payment.status, PaymentStatus::Succeeded); + assert_eq!(inbound_payment.direction, PaymentDirection::Inbound); + assert_eq!(inbound_payment.amount_msat, Some(determined_amount_msat)); + assert!(matches!(&inbound_payment.kind, PaymentKind::Bolt11 { .. })); // Test claiming manually registered payments. let invoice_amount_3_msat = 5_532_000; @@ -1390,34 +1417,28 @@ pub(crate) async fn do_channel_full_cycle( manual_payment_hash, ) .unwrap(); - let manual_payment_id = node_a.bolt11_payment().send(&manual_invoice, None).unwrap(); + let outbound_manual_payment_id = node_a.bolt11_payment().send(&manual_invoice, None).unwrap(); - let claimable_amount_msat = expect_payment_claimable_event!( - node_b, - manual_payment_id, - manual_payment_hash, - invoice_amount_3_msat - ); + let (manual_payment_id, claimable_amount_msat) = + expect_payment_claimable_event!(node_b, manual_payment_hash, invoice_amount_3_msat); + assert_ne!(manual_payment_id.0, manual_payment_hash.0); node_b .bolt11_payment() - .claim_for_hash(manual_payment_hash, claimable_amount_msat, manual_preimage) + .claim_for_id(manual_payment_id, claimable_amount_msat, manual_preimage) .unwrap(); - expect_payment_received_event!(node_b, claimable_amount_msat); - expect_payment_successful_event!(node_a, Some(manual_payment_id), None); - assert_eq!(node_a.payment(&manual_payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_a.payment(&manual_payment_id).unwrap().direction, PaymentDirection::Outbound); - assert_eq!( - node_a.payment(&manual_payment_id).unwrap().amount_msat, - Some(invoice_amount_3_msat) - ); - assert!(matches!(node_a.payment(&manual_payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); - assert_eq!(node_b.payment(&manual_payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_b.payment(&manual_payment_id).unwrap().direction, PaymentDirection::Inbound); - assert_eq!( - node_b.payment(&manual_payment_id).unwrap().amount_msat, - Some(invoice_amount_3_msat) - ); - assert!(matches!(node_b.payment(&manual_payment_id).unwrap().kind, PaymentKind::Bolt11 { .. })); + let received_payment_id = expect_payment_received_event!(node_b, claimable_amount_msat); + assert_eq!(received_payment_id, manual_payment_id); + expect_payment_successful_event!(node_a, outbound_manual_payment_id, None); + let outbound_manual_payment = node_a.payment(&outbound_manual_payment_id).unwrap(); + assert_eq!(outbound_manual_payment.status, PaymentStatus::Succeeded); + assert_eq!(outbound_manual_payment.direction, PaymentDirection::Outbound); + assert_eq!(outbound_manual_payment.amount_msat, Some(invoice_amount_3_msat)); + assert!(matches!(&outbound_manual_payment.kind, PaymentKind::Bolt11 { .. })); + let manual_payment = node_b.payment(&manual_payment_id).unwrap(); + assert_eq!(manual_payment.status, PaymentStatus::Succeeded); + assert_eq!(manual_payment.direction, PaymentDirection::Inbound); + assert_eq!(manual_payment.amount_msat, Some(invoice_amount_3_msat)); + assert!(matches!(&manual_payment.kind, PaymentKind::Bolt11 { .. })); // Test failing manually registered payments. let invoice_amount_4_msat = 5_532_000; @@ -1433,42 +1454,24 @@ pub(crate) async fn do_channel_full_cycle( manual_fail_payment_hash, ) .unwrap(); - let manual_fail_payment_id = node_a.bolt11_payment().send(&manual_fail_invoice, None).unwrap(); + let outbound_manual_fail_payment_id = + node_a.bolt11_payment().send(&manual_fail_invoice, None).unwrap(); - expect_payment_claimable_event!( - node_b, - manual_fail_payment_id, - manual_fail_payment_hash, - invoice_amount_4_msat - ); - node_b.bolt11_payment().fail_for_hash(manual_fail_payment_hash).unwrap(); + let (manual_fail_payment_id, _) = + expect_payment_claimable_event!(node_b, manual_fail_payment_hash, invoice_amount_4_msat); + assert_ne!(manual_fail_payment_id.0, manual_fail_payment_hash.0); + node_b.bolt11_payment().fail_for_id(manual_fail_payment_id).unwrap(); expect_event!(node_a, PaymentFailed); - assert_eq!(node_a.payment(&manual_fail_payment_id).unwrap().status, PaymentStatus::Failed); - assert_eq!( - node_a.payment(&manual_fail_payment_id).unwrap().direction, - PaymentDirection::Outbound - ); - assert_eq!( - node_a.payment(&manual_fail_payment_id).unwrap().amount_msat, - Some(invoice_amount_4_msat) - ); - assert!(matches!( - node_a.payment(&manual_fail_payment_id).unwrap().kind, - PaymentKind::Bolt11 { .. } - )); - assert_eq!(node_b.payment(&manual_fail_payment_id).unwrap().status, PaymentStatus::Failed); - assert_eq!( - node_b.payment(&manual_fail_payment_id).unwrap().direction, - PaymentDirection::Inbound - ); - assert_eq!( - node_b.payment(&manual_fail_payment_id).unwrap().amount_msat, - Some(invoice_amount_4_msat) - ); - assert!(matches!( - node_b.payment(&manual_fail_payment_id).unwrap().kind, - PaymentKind::Bolt11 { .. } - )); + let outbound_manual_fail_payment = node_a.payment(&outbound_manual_fail_payment_id).unwrap(); + assert_eq!(outbound_manual_fail_payment.status, PaymentStatus::Failed); + assert_eq!(outbound_manual_fail_payment.direction, PaymentDirection::Outbound); + assert_eq!(outbound_manual_fail_payment.amount_msat, Some(invoice_amount_4_msat)); + assert!(matches!(&outbound_manual_fail_payment.kind, PaymentKind::Bolt11 { .. })); + let manual_fail_payment = node_b.payment(&manual_fail_payment_id).unwrap(); + assert_eq!(manual_fail_payment.status, PaymentStatus::Failed); + assert_eq!(manual_fail_payment.direction, PaymentDirection::Inbound); + assert_eq!(manual_fail_payment.amount_msat, Some(invoice_amount_4_msat)); + assert!(matches!(&manual_fail_payment.kind, PaymentKind::Bolt11 { .. })); // Test spontaneous/keysend payments println!("\nA send_spontaneous_payment"); @@ -1480,39 +1483,38 @@ pub(crate) async fn do_channel_full_cycle( .unwrap(); expect_event!(node_a, PaymentSuccessful); let next_event = node_b.next_event_async().await; - let (received_keysend_amount, received_custom_records) = match next_event { - ref e @ Event::PaymentReceived { amount_msat, ref custom_records, .. } => { - println!("{} got event {:?}", std::stringify!(node_b), e); - node_b.event_handled().unwrap(); - (amount_msat, custom_records) - }, - ref e => { - panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e); - }, - }; + let (received_keysend_payment_id, received_keysend_amount, received_custom_records) = + match next_event { + ref e @ Event::PaymentReceived { + payment_id, amount_msat, ref custom_records, .. + } => { + println!("{} got event {:?}", std::stringify!(node_b), e); + node_b.event_handled().unwrap(); + (payment_id, amount_msat, custom_records) + }, + ref e => { + panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e); + }, + }; assert_eq!(received_keysend_amount, keysend_amount_msat); - assert_eq!(node_a.payment(&keysend_payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_a.payment(&keysend_payment_id).unwrap().direction, PaymentDirection::Outbound); - assert_eq!(node_a.payment(&keysend_payment_id).unwrap().amount_msat, Some(keysend_amount_msat)); - assert!(matches!( - node_a.payment(&keysend_payment_id).unwrap().kind, - PaymentKind::Spontaneous { .. } - )); + let keysend_payment = node_a.payment(&keysend_payment_id).unwrap(); + assert_eq!(keysend_payment.status, PaymentStatus::Succeeded); + assert_eq!(keysend_payment.direction, PaymentDirection::Outbound); + assert_eq!(keysend_payment.amount_msat, Some(keysend_amount_msat)); + assert!(matches!(&keysend_payment.kind, PaymentKind::Spontaneous { .. })); assert_eq!(received_custom_records, &custom_tlvs); - assert_eq!(node_b.payment(&keysend_payment_id).unwrap().status, PaymentStatus::Succeeded); - assert_eq!(node_b.payment(&keysend_payment_id).unwrap().direction, PaymentDirection::Inbound); - assert_eq!(node_b.payment(&keysend_payment_id).unwrap().amount_msat, Some(keysend_amount_msat)); - assert!(matches!( - node_b.payment(&keysend_payment_id).unwrap().kind, - PaymentKind::Spontaneous { .. } - )); + let received_keysend_payment = node_b.payment(&received_keysend_payment_id).unwrap(); + assert_eq!(received_keysend_payment.status, PaymentStatus::Succeeded); + assert_eq!(received_keysend_payment.direction, PaymentDirection::Inbound); + assert_eq!(received_keysend_payment.amount_msat, Some(keysend_amount_msat)); + assert!(matches!(&received_keysend_payment.kind, PaymentKind::Spontaneous { .. })); assert_eq!( node_a.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Bolt11 { .. })).len(), 5 ); assert_eq!( node_b.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Bolt11 { .. })).len(), - 6 + 5 ); assert_eq!( node_a diff --git a/tests/integration_tests_hrn.rs b/tests/integration_tests_hrn.rs index 6e758105a2..d61604798c 100644 --- a/tests/integration_tests_hrn.rs +++ b/tests/integration_tests_hrn.rs @@ -79,5 +79,5 @@ async fn unified_send_to_hrn() { }, }; - expect_payment_successful_event!(node_a, Some(offer_payment_id), None); + expect_payment_successful_event!(node_a, offer_payment_id, None); } diff --git a/tests/integration_tests_migration.rs b/tests/integration_tests_migration.rs index 7e5767dca6..53f60d19f7 100644 --- a/tests/integration_tests_migration.rs +++ b/tests/integration_tests_migration.rs @@ -203,7 +203,7 @@ async fn migrate_node_across_all_backends() { Bolt11InvoiceDescription::Direct(Description::new("ln send".to_string()).unwrap()); let invoice = node_b.bolt11_payment().receive(10_000, &description.into(), 3600).unwrap(); let ln_send_id = node.bolt11_payment().send(&invoice, None).unwrap(); - expect_payment_successful_event!(node, Some(ln_send_id), None); + expect_payment_successful_event!(node, ln_send_id, None); expect_payment_received_event!(node_b, 10_000); // Lightning receive: node B -> node. @@ -211,7 +211,7 @@ async fn migrate_node_across_all_backends() { Bolt11InvoiceDescription::Direct(Description::new("ln receive".to_string()).unwrap()); let invoice = node.bolt11_payment().receive(5_000, &description.into(), 3600).unwrap(); let ln_receive_id = node_b.bolt11_payment().send(&invoice, None).unwrap(); - expect_payment_successful_event!(node_b, Some(ln_receive_id), None); + expect_payment_successful_event!(node_b, ln_receive_id, None); expect_payment_received_event!(node, 5_000); // On-chain send: node -> a foreign address. diff --git a/tests/integration_tests_postgres.rs b/tests/integration_tests_postgres.rs index 889d681ba4..d71e36ca6d 100644 --- a/tests/integration_tests_postgres.rs +++ b/tests/integration_tests_postgres.rs @@ -37,7 +37,8 @@ async fn channel_full_cycle_with_postgres_store() { node_a.start().unwrap(); println!("\n== Node B =="); - let config_b = common::random_config(); + let mut config_b = common::random_config(); + config_b.node_config.manually_claim_unknown_bolt11_payments = true; let mut builder_b = Builder::from_config(config_b.node_config); builder_b.set_chain_source_esplora(esplora_url.clone(), None); let node_b = builder_b diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index e401c82189..a5defd6574 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -495,7 +495,7 @@ async fn multi_hop_sending() { .bolt11_payment() .receive(2_500_000, &invoice_description.clone().into(), 9217) .unwrap(); - nodes[0].bolt11_payment().send(&invoice, Some(route_params)).unwrap(); + let outbound_payment_id = nodes[0].bolt11_payment().send(&invoice, Some(route_params)).unwrap(); expect_event!(nodes[1], PaymentForwarded); @@ -504,9 +504,9 @@ async fn multi_hop_sending() { let node_3_fwd_event = matches!(nodes[3].next_event(), Some(Event::PaymentForwarded { .. })); assert!(node_2_fwd_event || node_3_fwd_event); - let payment_id = expect_payment_received_event!(&nodes[4], 2_500_000); + expect_payment_received_event!(&nodes[4], 2_500_000); let fee_paid_msat = Some(2000); - expect_payment_successful_event!(nodes[0], payment_id, Some(fee_paid_msat)); + expect_payment_successful_event!(nodes[0], outbound_payment_id, Some(fee_paid_msat)); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] @@ -585,13 +585,11 @@ async fn split_underpaid_bolt11_payment() { .unwrap(); let receiver_payment_id = expect_payment_received_event!(node_c, amount_msat); - assert_eq!(receiver_payment_id, Some(PaymentId(invoice.payment_hash().0))); - expect_payment_successful_event!(node_a, Some(payment_id_a), None); - expect_payment_successful_event!(node_b, Some(payment_id_b), None); + expect_payment_successful_event!(node_a, payment_id_a, None); + expect_payment_successful_event!(node_b, payment_id_b, None); // The receiver records the full invoice amount; each payer records only its own half. - let receiver_payments = - node_c.list_payments_with_filter(|p| p.id == receiver_payment_id.unwrap()); + let receiver_payments = node_c.list_payments_with_filter(|p| p.id == receiver_payment_id); assert_eq!(receiver_payments.len(), 1); assert_eq!(receiver_payments.first().unwrap().amount_msat, Some(amount_msat)); @@ -1793,7 +1791,7 @@ async fn splice_channel() { let payment_id = node_b.spontaneous_payment().send(amount_msat, node_a.node_id(), None).unwrap(); - expect_payment_successful_event!(node_b, Some(payment_id), None); + expect_payment_successful_event!(node_b, payment_id, None); expect_payment_received_event!(node_a, amount_msat); // Mine a block to give time for the HTLC to resolve @@ -2315,7 +2313,7 @@ async fn simple_bolt12_send_receive() { match event { ref e @ Event::PaymentSuccessful { payment_id: ref evt_id, ref bolt12_invoice, .. } => { println!("{} got event {:?}", node_a.node_id(), e); - assert_eq!(*evt_id, Some(payment_id)); + assert_eq!(*evt_id, payment_id); assert!( bolt12_invoice.is_some(), "bolt12_invoice should be present for BOLT12 payments" @@ -2389,12 +2387,12 @@ async fn simple_bolt12_send_receive() { ) .unwrap(); - expect_payment_successful_event!(node_a, Some(payment_id), None); + expect_payment_successful_event!(node_a, payment_id, None); let node_a_payments = node_a.list_payments_with_filter(|p| { matches!(p.kind, PaymentKind::Bolt12Offer { .. }) && p.id == payment_id }); assert_eq!(node_a_payments.len(), 1); - let payment_hash = match node_a_payments.first().unwrap().kind { + match node_a_payments.first().unwrap().kind { PaymentKind::Bolt12Offer { hash, preimage, @@ -2410,7 +2408,6 @@ async fn simple_bolt12_send_receive() { assert_eq!(expected_payer_note.unwrap(), note.clone().unwrap().0); // TODO: We should eventually set and assert the secret sender-side, too, but the BOLT12 // API currently doesn't allow to do that. - hash.unwrap() }, _ => { panic!("Unexpected payment kind"); @@ -2418,8 +2415,7 @@ async fn simple_bolt12_send_receive() { }; assert_eq!(node_a_payments.first().unwrap().amount_msat, Some(expected_amount_msat)); - expect_payment_received_event!(node_b, expected_amount_msat); - let node_b_payment_id = PaymentId(payment_hash.0); + let node_b_payment_id = expect_payment_received_event!(node_b, expected_amount_msat); let node_b_payments = node_b.list_payments_with_filter(|p| { matches!(p.kind, PaymentKind::Bolt12Offer { .. }) && p.id == node_b_payment_id }); @@ -2451,8 +2447,8 @@ async fn simple_bolt12_send_receive() { None, ) .unwrap(); - let invoice = node_a.bolt12_payment().request_refund_payment(&refund).unwrap(); - expect_payment_received_event!(node_a, overpaid_amount); + let _invoice = node_a.bolt12_payment().request_refund_payment(&refund).unwrap(); + let node_a_payment_id = expect_payment_received_event!(node_a, overpaid_amount); let node_b_payment_id = node_b .list_payments_with_filter(|p| { @@ -2462,7 +2458,7 @@ async fn simple_bolt12_send_receive() { .first() .unwrap() .id; - expect_payment_successful_event!(node_b, Some(node_b_payment_id), None); + expect_payment_successful_event!(node_b, node_b_payment_id, None); let node_b_payments = node_b.list_payments_with_filter(|p| { matches!(p.kind, PaymentKind::Bolt12Refund { .. }) && p.id == node_b_payment_id @@ -2489,7 +2485,6 @@ async fn simple_bolt12_send_receive() { } assert_eq!(node_b_payments.first().unwrap().amount_msat, Some(overpaid_amount)); - let node_a_payment_id = PaymentId(invoice.payment_hash().0); let node_a_payments = node_a.list_payments_with_filter(|p| { matches!(p.kind, PaymentKind::Bolt12Refund { .. }) && p.id == node_a_payment_id }); @@ -2636,7 +2631,7 @@ async fn async_payment() { node_receiver.start().unwrap(); - expect_payment_successful_event!(node_sender, Some(payment_id), None); + expect_payment_successful_event!(node_sender, payment_id, None); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] @@ -2862,7 +2857,7 @@ async fn unified_send_receive_bip21_uri() { }, }; - expect_payment_successful_event!(node_a, Some(offer_payment_id), None); + expect_payment_successful_event!(node_a, offer_payment_id, None); // Cut off the BOLT12 part to fallback to BOLT11. let uri_str_without_offer = uri_str.split("&lno=").next().unwrap(); @@ -2882,7 +2877,7 @@ async fn unified_send_receive_bip21_uri() { panic!("Expected Bolt11 payment but got error: {:?}", e); }, }; - expect_payment_successful_event!(node_a, Some(invoice_payment_id), None); + expect_payment_successful_event!(node_a, invoice_payment_id, None); let expect_onchain_amount_sats = 800_000; let onchain_uni_payment = @@ -2956,7 +2951,8 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { let service_node_id = service_node.node_id(); let service_addr = service_node.listening_addresses().unwrap().first().unwrap().clone(); - let client_config = random_config(); + let mut client_config = random_config(); + client_config.node_config.manually_claim_unknown_bolt11_payments = true; setup_builder!(client_builder, client_config.node_config); client_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); client_builder.add_liquidity_source(service_node_id, service_addr, None, true); @@ -3008,7 +3004,7 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { // Have the payer_node pay the invoice, therby triggering channel open service_node -> client_node. println!("Paying JIT invoice!"); - let payment_id = payer_node.bolt11_payment().send(&jit_invoice, None).unwrap(); + let payer_payment_id = payer_node.bolt11_payment().send(&jit_invoice, None).unwrap(); expect_channel_pending_event!(service_node, client_node.node_id()); expect_channel_ready_event!(service_node, client_node.node_id()); expect_event!(service_node, PaymentForwarded); @@ -3017,9 +3013,9 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { let service_fee_msat = (jit_amount_msat * channel_opening_fee_ppm as u64) / 1_000_000; let expected_received_amount_msat = jit_amount_msat - service_fee_msat; - expect_payment_successful_event!(payer_node, Some(payment_id), None); + expect_payment_successful_event!(payer_node, payer_payment_id, None); let client_payment_id = - expect_payment_received_event!(client_node, expected_received_amount_msat).unwrap(); + expect_payment_received_event!(client_node, expected_received_amount_msat); let client_payment = client_node.payment(&client_payment_id).unwrap(); match client_payment.kind { PaymentKind::Bolt11 { counterparty_skimmed_fee_msat, .. } => { @@ -3046,12 +3042,12 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { // are working as expected. println!("Paying regular invoice!"); let payment_id = payer_node.bolt11_payment().send(&invoice, None).unwrap(); - expect_payment_successful_event!(payer_node, Some(payment_id), None); + expect_payment_successful_event!(payer_node, payment_id, None); expect_event!(service_node, PaymentForwarded); expect_payment_received_event!(client_node, amount_msat); //////////////////////////////////////////////////////////////////////////// - // receive_via_jit_channel_for_hash and claim_for_hash + // receive_via_jit_channel_for_hash and claim_for_id //////////////////////////////////////////////////////////////////////////// println!("Generating JIT invoice!"); // Increase the amount to make sure it does not fit into the existing channels. @@ -3071,7 +3067,7 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { // Have the payer_node pay the invoice, therby triggering channel open service_node -> client_node. println!("Paying JIT invoice!"); - let payment_id = payer_node.bolt11_payment().send(&jit_invoice, None).unwrap(); + let payer_payment_id = payer_node.bolt11_payment().send(&jit_invoice, None).unwrap(); expect_channel_pending_event!(service_node, client_node.node_id()); expect_channel_ready_event!(service_node, client_node.node_id()); expect_channel_pending_event!(client_node, service_node.node_id()); @@ -3079,22 +3075,24 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { let service_fee_msat = (jit_amount_msat * channel_opening_fee_ppm as u64) / 1_000_000; let expected_received_amount_msat = jit_amount_msat - service_fee_msat; - let claimable_amount_msat = expect_payment_claimable_event!( + let (client_payment_id, claimable_amount_msat) = expect_payment_claimable_event!( client_node, - payment_id, manual_payment_hash, expected_received_amount_msat ); + assert_ne!(client_payment_id.0, manual_payment_hash.0); + assert_eq!(client_node.payment(&client_payment_id).unwrap().amount_msat, Some(jit_amount_msat)); println!("Claiming payment!"); client_node .bolt11_payment() - .claim_for_hash(manual_payment_hash, claimable_amount_msat, manual_preimage) + .claim_for_id(client_payment_id, claimable_amount_msat, manual_preimage) .unwrap(); expect_event!(service_node, PaymentForwarded); - expect_payment_successful_event!(payer_node, Some(payment_id), None); - let client_payment_id = - expect_payment_received_event!(client_node, expected_received_amount_msat).unwrap(); + expect_payment_successful_event!(payer_node, payer_payment_id, None); + let received_payment_id = + expect_payment_received_event!(client_node, expected_received_amount_msat); + assert_eq!(received_payment_id, client_payment_id); let client_payment = client_node.payment(&client_payment_id).unwrap(); match client_payment.kind { PaymentKind::Bolt11 { counterparty_skimmed_fee_msat, .. } => { @@ -3104,7 +3102,7 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { } //////////////////////////////////////////////////////////////////////////// - // receive_via_jit_channel_for_hash and fail_for_hash + // receive_via_jit_channel_for_hash and fail_for_id //////////////////////////////////////////////////////////////////////////// println!("Generating JIT invoice!"); // Increase the amount to make sure it does not fit into the existing channels. @@ -3124,7 +3122,7 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { // Have the payer_node pay the invoice, therby triggering channel open service_node -> client_node. println!("Paying JIT invoice!"); - let payment_id = payer_node.bolt11_payment().send(&jit_invoice, None).unwrap(); + let _payer_payment_id = payer_node.bolt11_payment().send(&jit_invoice, None).unwrap(); expect_channel_pending_event!(service_node, client_node.node_id()); expect_channel_ready_event!(service_node, client_node.node_id()); expect_channel_pending_event!(client_node, service_node.node_id()); @@ -3132,17 +3130,17 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) { let service_fee_msat = (jit_amount_msat * channel_opening_fee_ppm as u64) / 1_000_000; let expected_received_amount_msat = jit_amount_msat - service_fee_msat; - expect_payment_claimable_event!( + let (client_payment_id, _) = expect_payment_claimable_event!( client_node, - payment_id, manual_payment_hash, expected_received_amount_msat ); + assert_ne!(client_payment_id.0, manual_payment_hash.0); println!("Failing payment!"); - client_node.bolt11_payment().fail_for_hash(manual_payment_hash).unwrap(); + client_node.bolt11_payment().fail_for_id(client_payment_id).unwrap(); expect_event!(payer_node, PaymentFailed); - assert_eq!(client_node.payment(&payment_id).unwrap().status, PaymentStatus::Failed); + assert_eq!(client_node.payment(&client_payment_id).unwrap().status, PaymentStatus::Failed); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] @@ -3199,7 +3197,7 @@ async fn spontaneous_send_with_custom_preimage() { .unwrap(); // check payment status and verify stored preimage - expect_payment_successful_event!(node_a, Some(payment_id), None); + expect_payment_successful_event!(node_a, payment_id, None); let details: PaymentDetails = node_a.list_payments_with_filter(|p| p.id == payment_id).first().unwrap().clone(); assert_eq!(details.status, PaymentStatus::Succeeded); @@ -3274,7 +3272,8 @@ async fn lsps2_client_trusts_lsp() { let service_node_id = service_node.node_id(); let service_addr = service_node.listening_addresses().unwrap().first().unwrap().clone(); - let client_config = random_config(); + let mut client_config = random_config(); + client_config.node_config.manually_claim_unknown_bolt11_payments = true; setup_builder!(client_builder, client_config.node_config); client_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); client_builder.add_liquidity_source(service_node_id, service_addr.clone(), None, true); @@ -3334,8 +3333,8 @@ async fn lsps2_client_trusts_lsp() { // Have the payer_node pay the invoice, therby triggering channel open service_node -> client_node. println!("Paying JIT invoice!"); - let payment_id = payer_node.bolt11_payment().send(&res, None).unwrap(); - println!("Payment ID: {:?}", payment_id); + let payer_payment_id = payer_node.bolt11_payment().send(&res, None).unwrap(); + println!("Payment ID: {:?}", payer_payment_id); let funding_txo = expect_channel_pending_event!(service_node, client_node.node_id()); expect_channel_ready_event!(service_node, client_node.node_id()); expect_channel_pending_event!(client_node, service_node.node_id()); @@ -3373,21 +3372,22 @@ async fn lsps2_client_trusts_lsp() { let service_fee_msat = (jit_amount_msat * channel_opening_fee_ppm as u64) / 1_000_000; let expected_received_amount_msat = jit_amount_msat - service_fee_msat; - let _ = expect_payment_claimable_event!( + let (client_payment_id, _) = expect_payment_claimable_event!( client_node, - payment_id, manual_payment_hash, expected_received_amount_msat ); client_node .bolt11_payment() - .claim_for_hash(manual_payment_hash, jit_amount_msat, manual_preimage) + .claim_for_id(client_payment_id, jit_amount_msat, manual_preimage) .unwrap(); - expect_payment_successful_event!(payer_node, Some(payment_id), None); + expect_payment_successful_event!(payer_node, payer_payment_id, None); - let _ = expect_payment_received_event!(client_node, expected_received_amount_msat).unwrap(); + let received_payment_id = + expect_payment_received_event!(client_node, expected_received_amount_msat); + assert_eq!(received_payment_id, client_payment_id); // Check the nodes pick up on the confirmed funding tx now. wait_for_tx(&electrsd.client, funding_txo.txid).await; @@ -3450,7 +3450,8 @@ async fn lsps2_lsp_trusts_client_but_client_does_not_claim() { let service_node_id = service_node.node_id(); let service_addr = service_node.listening_addresses().unwrap().first().unwrap().clone(); - let client_config = random_config(); + let mut client_config = random_config(); + client_config.node_config.manually_claim_unknown_bolt11_payments = true; setup_builder!(client_builder, client_config.node_config); client_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); client_builder.add_liquidity_source(service_node_id, service_addr.clone(), None, true); diff --git a/tests/integration_tests_vss.rs b/tests/integration_tests_vss.rs index f0838585f7..3dd69905e7 100644 --- a/tests/integration_tests_vss.rs +++ b/tests/integration_tests_vss.rs @@ -35,7 +35,8 @@ async fn channel_full_cycle_with_vss_store() { node_a.start().unwrap(); println!("\n== Node B =="); - let config_b = common::random_config(); + let mut config_b = common::random_config(); + config_b.node_config.manually_claim_unknown_bolt11_payments = true; let mut builder_b = Builder::from_config(config_b.node_config); builder_b.set_chain_source_esplora(esplora_url.clone(), None); let node_b = builder_b diff --git a/tests/upgrade_downgrade_tests.rs b/tests/upgrade_downgrade_tests.rs index de5bef96e8..1b9a92a1a3 100644 --- a/tests/upgrade_downgrade_tests.rs +++ b/tests/upgrade_downgrade_tests.rs @@ -304,7 +304,7 @@ // ) { // match next_current_event(node).await { // ldk_node::Event::PaymentSuccessful { payment_id, .. } => { -// assert_eq!(payment_id.as_ref(), Some(expected_payment_id)); +// assert_eq!(&payment_id, expected_payment_id); // node.event_handled().unwrap(); // }, // event => panic!("{} got unexpected event: {:?}", node.node_id(), event), @@ -313,9 +313,8 @@ // // async fn expect_current_payment_received(node: &CurrentNode, expected_amount_msat: u64) { // match next_current_event(node).await { -// ldk_node::Event::PaymentReceived { amount_msat, payment_id, .. } => { +// ldk_node::Event::PaymentReceived { amount_msat, .. } => { // assert_eq!(amount_msat, expected_amount_msat); -// assert!(payment_id.is_some()); // node.event_handled().unwrap(); // }, // event => panic!("{} got unexpected event: {:?}", node.node_id(), event),