Add a `payment_metadata` field to `RecipientOnionFields`
authorMatt Corallo <git@bluematt.me>
Fri, 24 Mar 2023 01:31:14 +0000 (01:31 +0000)
committerMatt Corallo <git@bluematt.me>
Wed, 19 Apr 2023 02:57:19 +0000 (02:57 +0000)
This adds the new `payment_metadata` to `RecipientOnionFields`,
passing the metadata from BOLT11 invoices through the send pipeline
and finally copying them info the onion when sending HTLCs.

This completes send-side support for the new payment metadata
feature.

lightning-invoice/src/payment.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/onion_utils.rs
lightning/src/ln/outbound_payment.rs

index 00b17db7899d60e235d95057d45b3e97886eadf7..ce0d37ee399a1788ea5e4ee685288d1e6efd1ff0 100644 (file)
@@ -145,8 +145,10 @@ fn pay_invoice_using_amount<P: Deref>(
        payer: P
 ) -> Result<(), PaymentError> where P::Target: Payer {
        let payment_hash = PaymentHash((*invoice.payment_hash()).into_inner());
-       let payment_secret = Some(*invoice.payment_secret());
-       let recipient_onion = RecipientOnionFields { payment_secret };
+       let recipient_onion = RecipientOnionFields {
+               payment_secret: Some(*invoice.payment_secret()),
+               payment_metadata: invoice.payment_metadata().map(|v| v.clone()),
+       };
        let mut payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key(),
                invoice.min_final_cltv_expiry_delta() as u32)
                .with_expiry_time(expiry_time_from_unix_epoch(invoice).as_secs())
index 7590d6f2f22efaf699230aede503369f2f349746..4ca93418582e1da2d94fbeeff63959c5c3b06e5e 100644 (file)
@@ -7603,6 +7603,7 @@ where
                                                                                session_privs: [session_priv_bytes].iter().map(|a| *a).collect(),
                                                                                payment_hash: htlc.payment_hash,
                                                                                payment_secret: None, // only used for retries, and we'll never retry on startup
+                                                                               payment_metadata: None, // only used for retries, and we'll never retry on startup
                                                                                keysend_preimage: None, // only used for retries, and we'll never retry on startup
                                                                                pending_amt_msat: path_amt,
                                                                                pending_fee_msat: Some(path_fee),
index a751cb092daf1bf816f68924ada9464e72fdab0f..b9f36bbd8dadf94592c9082a303ef378879a4819 100644 (file)
@@ -170,7 +170,7 @@ pub(super) fn build_onion_payloads(path: &Vec<RouteHop>, total_msat: u64, mut re
                                                        total_msat,
                                                })
                                        } else { None },
-                                       payment_metadata: None,
+                                       payment_metadata: recipient_onion.payment_metadata.take(),
                                        keysend_preimage: *keysend_preimage,
                                }
                        } else {
index 3dde7f243870613107a69fcd94c9ba5cd01726f6..738c1d1952f133fe46ad94be85378427e09e84d1 100644 (file)
@@ -46,6 +46,7 @@ pub(crate) enum PendingOutboundPayment {
                session_privs: HashSet<[u8; 32]>,
                payment_hash: PaymentHash,
                payment_secret: Option<PaymentSecret>,
+               payment_metadata: Option<Vec<u8>>,
                keysend_preimage: Option<PaymentPreimage>,
                pending_amt_msat: u64,
                /// Used to track the fee paid. Only present if the payment was serialized on 0.0.103+.
@@ -422,6 +423,19 @@ pub struct RecipientOnionFields {
        /// receives, thus you should generally never be providing a secret here for spontaneous
        /// payments.
        pub payment_secret: Option<PaymentSecret>,
+       /// The payment metadata serves a similar purpose as [`Self::payment_secret`] but is of
+       /// arbitrary length. This gives recipients substantially more flexibility to receive
+       /// additional data.
+       ///
+       /// In LDK, while the [`Self::payment_secret`] is fixed based on an internal authentication
+       /// scheme to authenticate received payments against expected payments and invoices, this field
+       /// is not used in LDK for received payments, and can be used to store arbitrary data in
+       /// invoices which will be received with the payment.
+       ///
+       /// Note that this field was added to the lightning specification more recently than
+       /// [`Self::payment_secret`] and while nearly all lightning senders support secrets, metadata
+       /// may not be supported as universally.
+       pub payment_metadata: Option<Vec<u8>>,
 }
 
 impl RecipientOnionFields {
@@ -429,7 +443,7 @@ impl RecipientOnionFields {
        /// set of onion fields for today's BOLT11 invoices - most nodes require a [`PaymentSecret`]
        /// but do not require or provide any further data.
        pub fn secret_only(payment_secret: PaymentSecret) -> Self {
-               Self { payment_secret: Some(payment_secret) }
+               Self { payment_secret: Some(payment_secret), payment_metadata: None }
        }
 
        /// Creates a new [`RecipientOnionFields`] with no fields. This generally does not create
@@ -438,7 +452,7 @@ impl RecipientOnionFields {
        ///
        /// [`ChannelManager::send_spontaneous_payment`]: super::channelmanager::ChannelManager::send_spontaneous_payment
        pub fn spontaneous_empty() -> Self {
-               Self { payment_secret: None }
+               Self { payment_secret: None, payment_metadata: None }
        }
 }
 
@@ -719,7 +733,7 @@ impl OutboundPayments {
                                hash_map::Entry::Occupied(mut payment) => {
                                        let res = match payment.get() {
                                                PendingOutboundPayment::Retryable {
-                                                       total_msat, keysend_preimage, payment_secret, pending_amt_msat, ..
+                                                       total_msat, keysend_preimage, payment_secret, payment_metadata, pending_amt_msat, ..
                                                } => {
                                                        let retry_amt_msat: u64 = route.paths.iter().map(|path| path.last().unwrap().fee_msat).sum();
                                                        if retry_amt_msat + *pending_amt_msat > *total_msat * (100 + RETRY_OVERFLOW_PERCENTAGE) / 100 {
@@ -729,6 +743,7 @@ impl OutboundPayments {
                                                        }
                                                        (*total_msat, RecipientOnionFields {
                                                                        payment_secret: *payment_secret,
+                                                                       payment_metadata: payment_metadata.clone(),
                                                                }, *keysend_preimage)
                                                },
                                                PendingOutboundPayment::Legacy { .. } => {
@@ -912,6 +927,7 @@ impl OutboundPayments {
                                        pending_fee_msat: Some(0),
                                        payment_hash,
                                        payment_secret: recipient_onion.payment_secret,
+                                       payment_metadata: recipient_onion.payment_metadata,
                                        keysend_preimage,
                                        starting_block_height: best_block_height,
                                        total_msat: route.get_total_amount(),
@@ -1345,6 +1361,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
                (4, payment_secret, option),
                (5, keysend_preimage, option),
                (6, total_msat, required),
+               (7, payment_metadata, option),
                (8, pending_amt_msat, required),
                (10, starting_block_height, required),
                (not_written, retry_strategy, (static_value, None)),