Merge pull request #2966 from G8XSU/2647-distribute
[rust-lightning] / lightning / src / blinded_path / payment.rs
index 39f7c7f11088c51bdb5a55393084274d96631820..7df7d1c63edd1ed8222b2b9e470b235671d14d82 100644 (file)
@@ -1,18 +1,36 @@
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
 //! Data structures and methods for constructing [`BlindedPath`]s to send a payment over.
 //!
 //! [`BlindedPath`]: crate::blinded_path::BlindedPath
 
 use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
 
-use crate::blinded_path::BlindedHop;
+use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode, NodeIdLookUp};
 use crate::blinded_path::utils;
+use crate::crypto::streams::ChaChaPolyReadAdapter;
 use crate::io;
-use crate::ln::PaymentSecret;
-use crate::ln::channelmanager::CounterpartyForwardingInfo;
+use crate::io::Cursor;
+use crate::ln::types::PaymentSecret;
+use crate::ln::channel_state::CounterpartyForwardingInfo;
 use crate::ln::features::BlindedHopFeatures;
 use crate::ln::msgs::DecodeError;
+use crate::ln::onion_utils;
 use crate::offers::invoice::BlindedPayInfo;
-use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, Writeable, Writer};
+use crate::offers::invoice_request::InvoiceRequestFields;
+use crate::offers::offer::OfferId;
+use crate::sign::{NodeSigner, Recipient};
+use crate::util::ser::{FixedLengthReader, LengthReadableArgs, HighZeroBytesDroppedBigSize, Readable, Writeable, Writer};
+
+use core::mem;
+use core::ops::Deref;
 
 #[allow(unused_imports)]
 use crate::prelude::*;
@@ -108,12 +126,51 @@ pub struct PaymentConstraints {
 pub enum PaymentContext {
        /// The payment context was unknown.
        Unknown(UnknownPaymentContext),
+
+       /// The payment was made for an invoice requested from a BOLT 12 [`Offer`].
+       ///
+       /// [`Offer`]: crate::offers::offer::Offer
+       Bolt12Offer(Bolt12OfferContext),
+
+       /// The payment was made for an invoice sent for a BOLT 12 [`Refund`].
+       ///
+       /// [`Refund`]: crate::offers::refund::Refund
+       Bolt12Refund(Bolt12RefundContext),
+}
+
+// Used when writing PaymentContext in Event::PaymentClaimable to avoid cloning.
+pub(crate) enum PaymentContextRef<'a> {
+       Bolt12Offer(&'a Bolt12OfferContext),
+       Bolt12Refund(&'a Bolt12RefundContext),
 }
 
 /// An unknown payment context.
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct UnknownPaymentContext(());
 
+/// The context of a payment made for an invoice requested from a BOLT 12 [`Offer`].
+///
+/// [`Offer`]: crate::offers::offer::Offer
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Bolt12OfferContext {
+       /// The identifier of the [`Offer`].
+       ///
+       /// [`Offer`]: crate::offers::offer::Offer
+       pub offer_id: OfferId,
+
+       /// Fields from an [`InvoiceRequest`] sent for a [`Bolt12Invoice`].
+       ///
+       /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+       /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
+       pub invoice_request: InvoiceRequestFields,
+}
+
+/// The context of a payment made for an invoice sent for a BOLT 12 [`Refund`].
+///
+/// [`Refund`]: crate::offers::refund::Refund
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Bolt12RefundContext {}
+
 impl PaymentContext {
        pub(crate) fn unknown() -> Self {
                PaymentContext::Unknown(UnknownPaymentContext(()))
@@ -224,6 +281,43 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
        utils::construct_blinded_hops(secp_ctx, pks, tlvs, session_priv)
 }
 
+// Advance the blinded onion payment path by one hop, so make the second hop into the new
+// introduction node.
+//
+// Will only modify `path` when returning `Ok`.
+pub(crate) fn advance_path_by_one<NS: Deref, NL: Deref, T>(
+       path: &mut BlindedPath, node_signer: &NS, node_id_lookup: &NL, secp_ctx: &Secp256k1<T>
+) -> Result<(), ()>
+where
+       NS::Target: NodeSigner,
+       NL::Target: NodeIdLookUp,
+       T: secp256k1::Signing + secp256k1::Verification,
+{
+       let control_tlvs_ss = node_signer.ecdh(Recipient::Node, &path.blinding_point, None)?;
+       let rho = onion_utils::gen_rho_from_shared_secret(&control_tlvs_ss.secret_bytes());
+       let encrypted_control_tlvs = &path.blinded_hops.get(0).ok_or(())?.encrypted_payload;
+       let mut s = Cursor::new(encrypted_control_tlvs);
+       let mut reader = FixedLengthReader::new(&mut s, encrypted_control_tlvs.len() as u64);
+       match ChaChaPolyReadAdapter::read(&mut reader, rho) {
+               Ok(ChaChaPolyReadAdapter {
+                       readable: BlindedPaymentTlvs::Forward(ForwardTlvs { short_channel_id, .. })
+               }) => {
+                       let next_node_id = match node_id_lookup.next_node_id(short_channel_id) {
+                               Some(node_id) => node_id,
+                               None => return Err(()),
+                       };
+                       let mut new_blinding_point = onion_utils::next_hop_pubkey(
+                               secp_ctx, path.blinding_point, control_tlvs_ss.as_ref()
+                       ).map_err(|_| ())?;
+                       mem::swap(&mut path.blinding_point, &mut new_blinding_point);
+                       path.introduction_node = IntroductionNode::NodeId(next_node_id);
+                       path.blinded_hops.remove(0);
+                       Ok(())
+               },
+               _ => Err(())
+       }
+}
+
 /// `None` if underflow occurs.
 pub(crate) fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
        let inbound_amt = inbound_amt_msat as u128;
@@ -340,8 +434,27 @@ impl Readable for PaymentConstraints {
 impl_writeable_tlv_based_enum!(PaymentContext,
        ;
        (0, Unknown),
+       (1, Bolt12Offer),
+       (2, Bolt12Refund),
 );
 
+impl<'a> Writeable for PaymentContextRef<'a> {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               match self {
+                       PaymentContextRef::Bolt12Offer(context) => {
+                               1u8.write(w)?;
+                               context.write(w)?;
+                       },
+                       PaymentContextRef::Bolt12Refund(context) => {
+                               2u8.write(w)?;
+                               context.write(w)?;
+                       },
+               }
+
+               Ok(())
+       }
+}
+
 impl Writeable for UnknownPaymentContext {
        fn write<W: Writer>(&self, _w: &mut W) -> Result<(), io::Error> {
                Ok(())
@@ -354,11 +467,18 @@ impl Readable for UnknownPaymentContext {
        }
 }
 
+impl_writeable_tlv_based!(Bolt12OfferContext, {
+       (0, offer_id, required),
+       (2, invoice_request, required),
+});
+
+impl_writeable_tlv_based!(Bolt12RefundContext, {});
+
 #[cfg(test)]
 mod tests {
        use bitcoin::secp256k1::PublicKey;
        use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, ReceiveTlvs, PaymentConstraints, PaymentContext, PaymentRelay};
-       use crate::ln::PaymentSecret;
+       use crate::ln::types::PaymentSecret;
        use crate::ln::features::BlindedHopFeatures;
        use crate::ln::functional_test_utils::TEST_FINAL_CLTV;