Add PaymentContext to payment::ReceiveTlvs
[rust-lightning] / lightning / src / blinded_path / payment.rs
index 106cd802caac2abb860a19ab0366eea69370bfd2..b722a4419190aa3f3cf0f6fa8ca6c5126fae094c 100644 (file)
@@ -12,10 +12,10 @@ use crate::ln::channelmanager::CounterpartyForwardingInfo;
 use crate::ln::features::BlindedHopFeatures;
 use crate::ln::msgs::DecodeError;
 use crate::offers::invoice::BlindedPayInfo;
-use crate::prelude::*;
-use crate::util::ser::{Readable, Writeable, Writer};
+use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, Writeable, Writer};
 
-use core::convert::TryFrom;
+#[allow(unused_imports)]
+use crate::prelude::*;
 
 /// An intermediate node, its outbound channel, and relay parameters.
 #[derive(Clone, Debug)]
@@ -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<CounterpartyForwardingInfo> for PaymentRelay {
        type Error = ();
 
@@ -120,11 +143,14 @@ impl TryFrom<CounterpartyForwardingInfo> for PaymentRelay {
 
 impl Writeable for ForwardTlvs {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               let features_opt =
+                       if self.features == BlindedHopFeatures::empty() { None }
+                       else { Some(&self.features) };
                encode_tlv_stream!(w, {
                        (2, self.short_channel_id, required),
                        (10, self.payment_relay, required),
                        (12, self.payment_constraints, required),
-                       (14, self.features, required)
+                       (14, features_opt, option)
                });
                Ok(())
        }
@@ -134,7 +160,8 @@ impl Writeable for ReceiveTlvs {
        fn write<W: Writer>(&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(())
        }
@@ -160,22 +187,26 @@ 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<utils::Padding> = _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)?,
                                payment_constraints: payment_constraints.0.unwrap(),
-                               features: features.ok_or(DecodeError::InvalidValue)?,
+                               features: features.unwrap_or_else(BlindedHopFeatures::empty),
                        }))
                } else {
                        if payment_relay.is_some() || features.is_some() { return Err(DecodeError::InvalidValue) }
                        Ok(BlindedPaymentTlvs::Receive(ReceiveTlvs {
                                payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?,
                                payment_constraints: payment_constraints.0.unwrap(),
+                               payment_context: payment_context.0.unwrap(),
                        }))
                }
        }
@@ -276,21 +307,57 @@ pub(super) fn compute_payinfo(
        })
 }
 
-impl_writeable_msg!(PaymentRelay, {
-       cltv_expiry_delta,
-       fee_proportional_millionths,
-       fee_base_msat
-}, {});
+impl Writeable for PaymentRelay {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               self.cltv_expiry_delta.write(w)?;
+               self.fee_proportional_millionths.write(w)?;
+               HighZeroBytesDroppedBigSize(self.fee_base_msat).write(w)
+       }
+}
+impl Readable for PaymentRelay {
+       fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
+               let cltv_expiry_delta: u16 = Readable::read(r)?;
+               let fee_proportional_millionths: u32 = Readable::read(r)?;
+               let fee_base_msat: HighZeroBytesDroppedBigSize<u32> = Readable::read(r)?;
+               Ok(Self { cltv_expiry_delta, fee_proportional_millionths, fee_base_msat: fee_base_msat.0 })
+       }
+}
+
+impl Writeable for PaymentConstraints {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               self.max_cltv_expiry.write(w)?;
+               HighZeroBytesDroppedBigSize(self.htlc_minimum_msat).write(w)
+       }
+}
+impl Readable for PaymentConstraints {
+       fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
+               let max_cltv_expiry: u32 = Readable::read(r)?;
+               let htlc_minimum_msat: HighZeroBytesDroppedBigSize<u64> = Readable::read(r)?;
+               Ok(Self { max_cltv_expiry, htlc_minimum_msat: htlc_minimum_msat.0 })
+       }
+}
 
-impl_writeable_msg!(PaymentConstraints, {
-       max_cltv_expiry,
-       htlc_minimum_msat
-}, {});
+impl_writeable_tlv_based_enum!(PaymentContext,
+       ;
+       (0, Unknown),
+);
+
+impl Writeable for UnknownPaymentContext {
+       fn write<W: Writer>(&self, _w: &mut W) -> Result<(), io::Error> {
+               Ok(())
+       }
+}
+
+impl Readable for UnknownPaymentContext {
+       fn read<R: io::Read>(_r: &mut R) -> Result<Self, DecodeError> {
+               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;
@@ -339,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();
@@ -357,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);
@@ -410,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();
@@ -460,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());
@@ -514,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();