]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Support verifying payment_secrets for inbound static invoice payments
authorValentine Wallace <vwallace@protonmail.com>
Fri, 30 Aug 2024 22:41:36 +0000 (18:41 -0400)
committerValentine Wallace <vwallace@protonmail.com>
Fri, 8 Nov 2024 15:28:53 +0000 (10:28 -0500)
Add a new payment type for this, because normally the payment hash is factored
into the payment secrets we create for invoices, but static invoices don't have
a payment hash since they are paid via keysend.

lightning/src/ln/inbound_payment.rs

index d3cdae61691cd580605403c6199b8836f22dba3b..5b8d48e1c74976c81a7f59e275cca04cfc4b97a3 100644 (file)
@@ -98,11 +98,13 @@ impl ExpandedKey {
        }
 }
 
+/// We currently set aside 3 bits for the `Method` in the `PaymentSecret`.
 enum Method {
        LdkPaymentHash = 0,
        UserPaymentHash = 1,
        LdkPaymentHashCustomFinalCltv = 2,
        UserPaymentHashCustomFinalCltv = 3,
+       SpontaneousPayment = 4,
 }
 
 impl Method {
@@ -112,6 +114,7 @@ impl Method {
                        bits if bits == Method::UserPaymentHash as u8 => Ok(Method::UserPaymentHash),
                        bits if bits == Method::LdkPaymentHashCustomFinalCltv as u8 => Ok(Method::LdkPaymentHashCustomFinalCltv),
                        bits if bits == Method::UserPaymentHashCustomFinalCltv as u8 => Ok(Method::UserPaymentHashCustomFinalCltv),
+                       bits if bits == Method::SpontaneousPayment as u8 => Ok(Method::SpontaneousPayment),
                        unknown => Err(unknown),
                }
        }
@@ -191,6 +194,26 @@ pub fn create_from_hash(keys: &ExpandedKey, min_value_msat: Option<u64>, payment
        Ok(construct_payment_secret(&iv_bytes, &metadata_bytes, &keys.metadata_key))
 }
 
+#[cfg(async_payments)]
+pub(super) fn create_for_spontaneous_payment(
+       keys: &ExpandedKey, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32,
+       current_time: u64, min_final_cltv_expiry_delta: Option<u16>
+) -> Result<PaymentSecret, ()> {
+       let metadata_bytes = construct_metadata_bytes(
+               min_value_msat, Method::SpontaneousPayment, invoice_expiry_delta_secs, current_time,
+               min_final_cltv_expiry_delta
+       )?;
+
+       let mut hmac = HmacEngine::<Sha256>::new(&keys.spontaneous_pmt_key);
+       hmac.input(&metadata_bytes);
+       let hmac_bytes = Hmac::from_engine(hmac).to_byte_array();
+
+       let mut iv_bytes = [0 as u8; IV_LEN];
+       iv_bytes.copy_from_slice(&hmac_bytes[..IV_LEN]);
+
+       Ok(construct_payment_secret(&iv_bytes, &metadata_bytes, &keys.metadata_key))
+}
+
 fn construct_metadata_bytes(min_value_msat: Option<u64>, payment_type: Method,
        invoice_expiry_delta_secs: u32, highest_seen_timestamp: u64, min_final_cltv_expiry_delta: Option<u16>) -> Result<[u8; METADATA_LEN], ()> {
        if min_value_msat.is_some() && min_value_msat.unwrap() > MAX_VALUE_MSAT {
@@ -320,6 +343,14 @@ pub(super) fn verify<L: Deref>(payment_hash: PaymentHash, payment_data: &msgs::F
                                }
                        }
                },
+               Ok(Method::SpontaneousPayment) => {
+                       let mut hmac = HmacEngine::<Sha256>::new(&keys.spontaneous_pmt_key);
+                       hmac.input(&metadata_bytes[..]);
+                       if !fixed_time_eq(&iv_bytes, &Hmac::from_engine(hmac).to_byte_array().split_at_mut(IV_LEN).0) {
+                               log_trace!(logger, "Failing async payment HTLC with sender-generated payment_hash {}: unexpected payment_secret", &payment_hash);
+                               return Err(())
+                       }
+               },
                Err(unknown_bits) => {
                        log_trace!(logger, "Failing HTLC with payment hash {} due to unknown payment type {}", &payment_hash, unknown_bits);
                        return Err(());
@@ -365,6 +396,9 @@ pub(super) fn get_payment_preimage(payment_hash: PaymentHash, payment_secret: Pa
                Ok(Method::UserPaymentHash) | Ok(Method::UserPaymentHashCustomFinalCltv) => Err(APIError::APIMisuseError {
                        err: "Expected payment type to be LdkPaymentHash, instead got UserPaymentHash".to_string()
                }),
+               Ok(Method::SpontaneousPayment) => Err(APIError::APIMisuseError {
+                       err: "Can't extract payment preimage for spontaneous payments".to_string()
+               }),
                Err(other) => Err(APIError::APIMisuseError { err: format!("Unknown payment type: {}", other) }),
        }
 }