From c8815384787e6b0837a51944555357623d4ffa4e Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 26 Mar 2024 09:48:03 -0500 Subject: [PATCH] Add PaymentContext to payment::ReceiveTlvs Providing LDK-specific data in a BlindedPath allows for the data to be received with the corresponding payment. This is useful as it can then be surfaced in PaymentClaimable events where the user may correlated with an Offer, for instance. Define a PaymentContext enum for including various context (e.g., offer, refund, static invoice). --- lightning/src/blinded_path/payment.rs | 56 +++++++++++++++++++++-- lightning/src/ln/blinded_payment_tests.rs | 6 ++- lightning/src/ln/channelmanager.rs | 3 +- lightning/src/ln/msgs.rs | 2 +- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/lightning/src/blinded_path/payment.rs b/lightning/src/blinded_path/payment.rs index 3d56020a6..b722a4419 100644 --- a/lightning/src/blinded_path/payment.rs +++ b/lightning/src/blinded_path/payment.rs @@ -53,6 +53,8 @@ pub struct ReceiveTlvs { pub payment_secret: PaymentSecret, /// Constraints for the receiver of this payment. pub payment_constraints: PaymentConstraints, + /// Context for the receiver of this payment. + pub payment_context: PaymentContext, } /// Data to construct a [`BlindedHop`] for sending a payment over. @@ -97,6 +99,27 @@ pub struct PaymentConstraints { pub htlc_minimum_msat: u64, } +/// The context of an inbound payment, which is included in a [`BlindedPath`] via [`ReceiveTlvs`] +/// and surfaced in [`PaymentPurpose`]. +/// +/// [`BlindedPath`]: crate::blinded_path::BlindedPath +/// [`PaymentPurpose`]: crate::events::PaymentPurpose +#[derive(Clone, Debug)] +pub enum PaymentContext { + /// The payment context was unknown. + Unknown(UnknownPaymentContext), +} + +/// An unknown payment context. +#[derive(Clone, Debug)] +pub struct UnknownPaymentContext(()); + +impl PaymentContext { + pub(crate) fn unknown() -> Self { + PaymentContext::Unknown(UnknownPaymentContext(())) + } +} + impl TryFrom for PaymentRelay { type Error = (); @@ -137,7 +160,8 @@ impl Writeable for ReceiveTlvs { fn write(&self, w: &mut W) -> Result<(), io::Error> { encode_tlv_stream!(w, { (12, self.payment_constraints, required), - (65536, self.payment_secret, required) + (65536, self.payment_secret, required), + (65537, self.payment_context, required) }); Ok(()) } @@ -163,11 +187,14 @@ impl Readable for BlindedPaymentTlvs { (12, payment_constraints, required), (14, features, option), (65536, payment_secret, option), + (65537, payment_context, (default_value, PaymentContext::unknown())), }); let _padding: Option = _padding; if let Some(short_channel_id) = scid { - if payment_secret.is_some() { return Err(DecodeError::InvalidValue) } + if payment_secret.is_some() { + return Err(DecodeError::InvalidValue) + } Ok(BlindedPaymentTlvs::Forward(ForwardTlvs { short_channel_id, payment_relay: payment_relay.ok_or(DecodeError::InvalidValue)?, @@ -179,6 +206,7 @@ impl Readable for BlindedPaymentTlvs { Ok(BlindedPaymentTlvs::Receive(ReceiveTlvs { payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?, payment_constraints: payment_constraints.0.unwrap(), + payment_context: payment_context.0.unwrap(), })) } } @@ -309,10 +337,27 @@ impl Readable for PaymentConstraints { } } +impl_writeable_tlv_based_enum!(PaymentContext, + ; + (0, Unknown), +); + +impl Writeable for UnknownPaymentContext { + fn write(&self, _w: &mut W) -> Result<(), io::Error> { + Ok(()) + } +} + +impl Readable for UnknownPaymentContext { + fn read(_r: &mut R) -> Result { + Ok(UnknownPaymentContext(())) + } +} + #[cfg(test)] mod tests { use bitcoin::secp256k1::PublicKey; - use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, ReceiveTlvs, PaymentConstraints, PaymentRelay}; + use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, ReceiveTlvs, PaymentConstraints, PaymentContext, PaymentRelay}; use crate::ln::PaymentSecret; use crate::ln::features::BlindedHopFeatures; use crate::ln::functional_test_utils::TEST_FINAL_CLTV; @@ -361,6 +406,7 @@ mod tests { max_cltv_expiry: 0, htlc_minimum_msat: 1, }, + payment_context: PaymentContext::unknown(), }; let htlc_maximum_msat = 100_000; let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat, 12).unwrap(); @@ -379,6 +425,7 @@ mod tests { max_cltv_expiry: 0, htlc_minimum_msat: 1, }, + payment_context: PaymentContext::unknown(), }; let blinded_payinfo = super::compute_payinfo(&[], &recv_tlvs, 4242, TEST_FINAL_CLTV as u16).unwrap(); assert_eq!(blinded_payinfo.fee_base_msat, 0); @@ -432,6 +479,7 @@ mod tests { max_cltv_expiry: 0, htlc_minimum_msat: 3, }, + payment_context: PaymentContext::unknown(), }; let htlc_maximum_msat = 100_000; let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat, TEST_FINAL_CLTV as u16).unwrap(); @@ -482,6 +530,7 @@ mod tests { max_cltv_expiry: 0, htlc_minimum_msat: 1, }, + payment_context: PaymentContext::unknown(), }; let htlc_minimum_msat = 3798; assert!(super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_minimum_msat - 1, TEST_FINAL_CLTV as u16).is_err()); @@ -536,6 +585,7 @@ mod tests { max_cltv_expiry: 0, htlc_minimum_msat: 1, }, + payment_context: PaymentContext::unknown(), }; let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, 10_000, TEST_FINAL_CLTV as u16).unwrap(); diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index eb31be9ee..a0438b244 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -9,7 +9,7 @@ use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; use crate::blinded_path::BlindedPath; -use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, PaymentConstraints, PaymentRelay, ReceiveTlvs}; +use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentRelay, ReceiveTlvs}; use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PaymentFailureReason}; use crate::ln::PaymentSecret; use crate::ln::channelmanager; @@ -63,6 +63,7 @@ fn blinded_payment_path( htlc_minimum_msat: intro_node_min_htlc_opt.unwrap_or_else(|| channel_upds.last().unwrap().htlc_minimum_msat), }, + payment_context: PaymentContext::unknown(), }; let mut secp_ctx = Secp256k1::new(); BlindedPath::new_for_payment( @@ -108,6 +109,7 @@ fn do_one_hop_blinded_path(success: bool) { max_cltv_expiry: u32::max_value(), htlc_minimum_msat: chan_upd.htlc_minimum_msat, }, + payment_context: PaymentContext::unknown(), }; let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPath::one_hop_for_payment( @@ -151,6 +153,7 @@ fn mpp_to_one_hop_blinded_path() { max_cltv_expiry: u32::max_value(), htlc_minimum_msat: chan_upd_1_3.htlc_minimum_msat, }, + payment_context: PaymentContext::unknown(), }; let blinded_path = BlindedPath::one_hop_for_payment( nodes[3].node.get_our_node_id(), payee_tlvs, TEST_FINAL_CLTV as u16, @@ -1281,6 +1284,7 @@ fn custom_tlvs_to_blinded_path() { max_cltv_expiry: u32::max_value(), htlc_minimum_msat: chan_upd.htlc_minimum_msat, }, + payment_context: PaymentContext::unknown(), }; let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPath::one_hop_for_payment( diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 553ff853e..6a33b2d54 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -32,7 +32,7 @@ use bitcoin::secp256k1::Secp256k1; use bitcoin::{secp256k1, Sequence}; use crate::blinded_path::{BlindedPath, NodeIdLookUp}; -use crate::blinded_path::payment::{PaymentConstraints, ReceiveTlvs}; +use crate::blinded_path::payment::{PaymentConstraints, PaymentContext, ReceiveTlvs}; use crate::chain; use crate::chain::{Confirm, ChannelMonitorUpdateStatus, Watch, BestBlock}; use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator}; @@ -8995,6 +8995,7 @@ where max_cltv_expiry, htlc_minimum_msat: 1, }, + payment_context: PaymentContext::unknown(), }; self.router.create_blinded_payment_paths( payee_node_id, first_hops, payee_tlvs, amount_msats, secp_ctx diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 8040d8c42..4d1f6985f 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -2713,7 +2713,7 @@ impl ReadableArgs<(Option, &NS)> for InboundOnionPayload w }) }, ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Receive(ReceiveTlvs { - payment_secret, payment_constraints + payment_secret, payment_constraints, payment_context: _ })} => { if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) } Ok(Self::BlindedReceive { -- 2.39.5