OffersMessage::Invoice(invoice) => panic!("Unexpected invoice: {:?}", invoice),
OffersMessage::InvoiceError(error) => panic!("Unexpected invoice_error: {:?}", error),
},
+ ParsedOnionMessageContents::AsyncPayments(message) => panic!("Unexpected async payments message: {:?}", message),
ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
},
Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
OffersMessage::Invoice(invoice) => invoice,
OffersMessage::InvoiceError(error) => panic!("Unexpected invoice_error: {:?}", error),
},
+ ParsedOnionMessageContents::AsyncPayments(message) => panic!("Unexpected async payments message: {:?}", message),
ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
},
Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
OffersMessage::Invoice(invoice) => panic!("Unexpected invoice: {:?}", invoice),
OffersMessage::InvoiceError(error) => error,
},
+ ParsedOnionMessageContents::AsyncPayments(message) => panic!("Unexpected async payments message: {:?}", message),
ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
},
Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
--- /dev/null
+// 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.
+
+//! Message handling for async payments.
+
+use crate::io;
+use crate::ln::msgs::DecodeError;
+use crate::onion_message::packet::OnionMessageContents;
+use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
+
+// TLV record types for the `onionmsg_tlv` TLV stream as defined in BOLT 4.
+const HELD_HTLC_AVAILABLE_TLV_TYPE: u64 = 72;
+const RELEASE_HELD_HTLC_TLV_TYPE: u64 = 74;
+
+/// Possible async payment messages sent and received via an [`OnionMessage`].
+///
+/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
+#[derive(Clone, Debug)]
+pub enum AsyncPaymentsMessage {
+ /// An HTLC is being held upstream for the often-offline recipient, to be released via
+ /// [`ReleaseHeldHtlc`].
+ HeldHtlcAvailable(HeldHtlcAvailable),
+
+ /// Releases the HTLC corresponding to an inbound [`HeldHtlcAvailable`] message.
+ ReleaseHeldHtlc(ReleaseHeldHtlc),
+}
+
+/// An HTLC destined for the recipient of this message is being held upstream. The reply path
+/// accompanying this onion message should be used to send a [`ReleaseHeldHtlc`] response, which
+/// will cause the upstream HTLC to be released.
+#[derive(Clone, Debug)]
+pub struct HeldHtlcAvailable {
+ /// The secret that will be used by the recipient of this message to release the held HTLC.
+ pub payment_release_secret: [u8; 32],
+}
+
+/// Releases the HTLC corresponding to an inbound [`HeldHtlcAvailable`] message.
+#[derive(Clone, Debug)]
+pub struct ReleaseHeldHtlc {
+ /// Used to release the HTLC held upstream if it matches the corresponding
+ /// [`HeldHtlcAvailable::payment_release_secret`].
+ pub payment_release_secret: [u8; 32],
+}
+
+impl OnionMessageContents for ReleaseHeldHtlc {
+ fn tlv_type(&self) -> u64 {
+ RELEASE_HELD_HTLC_TLV_TYPE
+ }
+ fn msg_type(&self) -> &'static str {
+ "Release Held HTLC"
+ }
+}
+
+impl_writeable_tlv_based!(HeldHtlcAvailable, {
+ (0, payment_release_secret, required),
+});
+
+impl_writeable_tlv_based!(ReleaseHeldHtlc, {
+ (0, payment_release_secret, required),
+});
+
+impl AsyncPaymentsMessage {
+ /// Returns whether `tlv_type` corresponds to a TLV record for async payment messages.
+ pub fn is_known_type(tlv_type: u64) -> bool {
+ match tlv_type {
+ HELD_HTLC_AVAILABLE_TLV_TYPE | RELEASE_HELD_HTLC_TLV_TYPE => true,
+ _ => false,
+ }
+ }
+}
+
+impl OnionMessageContents for AsyncPaymentsMessage {
+ fn tlv_type(&self) -> u64 {
+ match self {
+ Self::HeldHtlcAvailable(_) => HELD_HTLC_AVAILABLE_TLV_TYPE,
+ Self::ReleaseHeldHtlc(msg) => msg.tlv_type(),
+ }
+ }
+ fn msg_type(&self) -> &'static str {
+ match &self {
+ Self::HeldHtlcAvailable(_) => "Held HTLC Available",
+ Self::ReleaseHeldHtlc(msg) => msg.msg_type(),
+ }
+ }
+}
+
+impl Writeable for AsyncPaymentsMessage {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ match self {
+ Self::HeldHtlcAvailable(message) => message.write(w),
+ Self::ReleaseHeldHtlc(message) => message.write(w),
+ }
+ }
+}
+
+impl ReadableArgs<u64> for AsyncPaymentsMessage {
+ fn read<R: io::Read>(r: &mut R, tlv_type: u64) -> Result<Self, DecodeError> {
+ match tlv_type {
+ HELD_HTLC_AVAILABLE_TLV_TYPE => Ok(Self::HeldHtlcAvailable(Readable::read(r)?)),
+ RELEASE_HELD_HTLC_TLV_TYPE => Ok(Self::ReleaseHeldHtlc(Readable::read(r)?)),
+ _ => Err(DecodeError::InvalidValue),
+ }
+ }
+}
use crate::blinded_path::utils::Padding;
use crate::ln::msgs::DecodeError;
use crate::ln::onion_utils;
+use super::async_payments::AsyncPaymentsMessage;
use super::messenger::CustomOnionMessageHandler;
use super::offers::OffersMessage;
use crate::crypto::streams::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
pub enum ParsedOnionMessageContents<T: OnionMessageContents> {
/// A message related to BOLT 12 Offers.
Offers(OffersMessage),
+ /// A message related to async payments.
+ AsyncPayments(AsyncPaymentsMessage),
/// A custom onion message specified by the user.
Custom(T),
}
fn tlv_type(&self) -> u64 {
match self {
&ParsedOnionMessageContents::Offers(ref msg) => msg.tlv_type(),
+ &ParsedOnionMessageContents::AsyncPayments(ref msg) => msg.tlv_type(),
&ParsedOnionMessageContents::Custom(ref msg) => msg.tlv_type(),
}
}
fn msg_type(&self) -> &'static str {
match self {
ParsedOnionMessageContents::Offers(ref msg) => msg.msg_type(),
+ ParsedOnionMessageContents::AsyncPayments(ref msg) => msg.msg_type(),
ParsedOnionMessageContents::Custom(ref msg) => msg.msg_type(),
}
}
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
match self {
ParsedOnionMessageContents::Offers(msg) => Ok(msg.write(w)?),
+ ParsedOnionMessageContents::AsyncPayments(msg) => Ok(msg.write(w)?),
ParsedOnionMessageContents::Custom(msg) => Ok(msg.write(w)?),
}
}
message = Some(ParsedOnionMessageContents::Offers(msg));
Ok(true)
},
+ tlv_type if AsyncPaymentsMessage::is_known_type(tlv_type) => {
+ let msg = AsyncPaymentsMessage::read(msg_reader, tlv_type)?;
+ message = Some(ParsedOnionMessageContents::AsyncPayments(msg));
+ Ok(true)
+ },
_ => match handler.read_custom_message(msg_type, msg_reader)? {
Some(msg) => {
message = Some(ParsedOnionMessageContents::Custom(msg));