+// 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::*;
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(()))
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;
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(())
}
}
+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;