lightningdevkit / ldk-node

A ready-to-go node implementation built using LDK.
Other
140 stars 72 forks source link

Allow to manually register and claim payments #308

Closed tnull closed 2 months ago

tnull commented 3 months ago

Fixes #280. Fixes #57.

Previously, we wouldn't allow invoice generation for third-party payment hashes. However, as some users require this feature, we here allow them to generate invoices for which they supply the payment hashes, allowing them to retrieve the preimage out-of-bound.

To this end, we add receive_for_hash and receive_variable_amount_for_hash variants to the Bolt11Payment handler. Then, when we receive the payment, we emit a new PaymentClaimable event that users need to call claim_manually for with the correct preimage. Note that this currently only implements the BOLT11 flow as for BOLT12 additions to LDK will be necessary that are pending the next release.

We also add a commit resolving #57, i.e., adding a latest_update_timestamp field to PaymentDetails which allows to filter and sort payment based on how recent they are.

tnull commented 2 months ago

Go ahead and squash. I'll take a final look in the morning.

Squashed without additional changes.

tnull commented 2 months ago

Force-pushed with the following changes:

> git diff-tree -U2 32fb2a8 a678c5e
diff --git a/src/event.rs b/src/event.rs
index a0363ef..838df42 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -87,7 +87,7 @@ pub enum Event {
        /// A payment for a previously-registered payment hash has been received.
        ///
-       /// This needs to be manually claimed by supplying the correct pre-image to [`claim_for_hash`].
+       /// This needs to be manually claimed by supplying the correct preimage to [`claim_for_hash`].
        ///
-       /// If the the provided parameters don't match the expectations or the pre-image can't be
+       /// If the 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`].
        ///
diff --git a/src/payment/bolt11.rs b/src/payment/bolt11.rs
index a543101..e8d030b 100644
--- a/src/payment/bolt11.rs
+++ b/src/payment/bolt11.rs
@@ -266,5 +266,5 @@ impl Bolt11Payment {
        /// been registered via [`receive_for_hash`] or [`receive_variable_amount_for_hash`].
        ///
-       /// This should be called in reponse to a [`PaymentClaimable`] event as soon as the pre-image is
+       /// This should be called in reponse to a [`PaymentClaimable`] event as soon as the preimage is
        /// available.
        ///
@@ -323,5 +323,5 @@ impl Bolt11Payment {
        ///
        /// This should be called in reponse to a [`PaymentClaimable`] event if the payment needs to be
-       /// failed back, e.g., if the correct pre-image can't be retrieved in time before the claim
+       /// failed back, e.g., if the correct preimage can't be retrieved in time before the claim
        /// deadline has been reached.
        ///
@@ -473,5 +473,5 @@ impl Bolt11Payment {
                                .get_payment_preimage(payment_hash, payment_secret.clone())
                                .ok();
-                       debug_assert!(res.is_some(), "We just let ChannelManager create an inbound payment, it can't have forgotten the pre-image by now.");
+                       debug_assert!(res.is_some(), "We just let ChannelManager create an inbound payment, it can't have forgotten the preimage by now.");
                        res
                } else {
diff --git a/tests/common/mod.rs b/tests/common/mod.rs
index f587b0c..5959bd5 100644
--- a/tests/common/mod.rs
+++ b/tests/common/mod.rs
@@ -609,5 +609,5 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
        assert!(matches!(node_b.payment(&payment_id).unwrap().kind, PaymentKind::Bolt11 { .. }));

-       // Test manually registered/claimed payments.
+       // Test claiming manually registered payments.
        let invoice_amount_3_msat = 5_532_000;
        let manual_preimage = PaymentPreimage([42u8; 32]);
@@ -646,4 +646,50 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
        assert!(matches!(node_b.payment(&manual_payment_id).unwrap().kind, PaymentKind::Bolt11 { .. }));

+       // Test failing manually registered payments.
+       let invoice_amount_4_msat = 5_532_000;
+       let manual_fail_preimage = PaymentPreimage([43u8; 32]);
+       let manual_fail_payment_hash =
+               PaymentHash(Sha256::hash(&manual_fail_preimage.0).to_byte_array());
+       let manual_fail_invoice = node_b
+               .bolt11_payment()
+               .receive_for_hash(invoice_amount_3_msat, &"asdf", 9217, manual_fail_payment_hash)
+               .unwrap();
+       let manual_fail_payment_id = node_a.bolt11_payment().send(&manual_fail_invoice).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();
+       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 { .. }
+       ));
+
        // Test spontaneous/keysend payments
        println!("\nA send_spontaneous_payment");
@@ -677,6 +723,6 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
                PaymentKind::Spontaneous { .. }
        ));
-       assert_eq!(node_a.list_payments().len(), 5);
-       assert_eq!(node_b.list_payments().len(), 6);
+       assert_eq!(node_a.list_payments().len(), 6);
+       assert_eq!(node_b.list_payments().len(), 7);

        println!("\nB close_channel (force: {})", force_close);