Skip to content

Commit 3c344ad

Browse files
Onion message payload for async payments
Async payments uses onion messages to indicate when HTLCs are held and released. Add these types along with the necessary parsing and encoding.
1 parent 66df329 commit 3c344ad

File tree

5 files changed

+116
-0
lines changed

5 files changed

+116
-0
lines changed

lightning/src/ln/offers_tests.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ fn extract_invoice_request<'a, 'b, 'c>(
194194
OffersMessage::Invoice(invoice) => panic!("Unexpected invoice: {:?}", invoice),
195195
OffersMessage::InvoiceError(error) => panic!("Unexpected invoice_error: {:?}", error),
196196
},
197+
ParsedOnionMessageContents::AsyncPayments(message) => panic!("Unexpected async payments message: {:?}", message),
197198
ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
198199
},
199200
Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
@@ -209,6 +210,7 @@ fn extract_invoice<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, message: &OnionMessage)
209210
OffersMessage::Invoice(invoice) => invoice,
210211
OffersMessage::InvoiceError(error) => panic!("Unexpected invoice_error: {:?}", error),
211212
},
213+
ParsedOnionMessageContents::AsyncPayments(message) => panic!("Unexpected async payments message: {:?}", message),
212214
ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
213215
},
214216
Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
@@ -226,6 +228,7 @@ fn extract_invoice_error<'a, 'b, 'c>(
226228
OffersMessage::Invoice(invoice) => panic!("Unexpected invoice: {:?}", invoice),
227229
OffersMessage::InvoiceError(error) => error,
228230
},
231+
ParsedOnionMessageContents::AsyncPayments(message) => panic!("Unexpected async payments message: {:?}", message),
229232
ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
230233
},
231234
Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
//! Message handling for async payments.
11+
12+
use crate::io;
13+
use crate::ln::msgs::DecodeError;
14+
use crate::onion_message::packet::OnionMessageContents;
15+
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
16+
17+
// TLV record types for the `onionmsg_tlv` TLV stream as defined in BOLT 4.
18+
const HELD_HTLC_AVAILABLE_TLV_TYPE: u64 = 72;
19+
const RELEASE_HELD_HTLC_TLV_TYPE: u64 = 74;
20+
21+
/// Possible async payment messages sent and received via an [`OnionMessage`].
22+
///
23+
/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
24+
#[derive(Clone, Debug)]
25+
pub enum AsyncPaymentsMessage {
26+
/// An HTLC is being held upstream for the often-offline recipient, to be released via
27+
/// [`ReleaseHeldHtlc`].
28+
HeldHtlcAvailable(HeldHtlcAvailable),
29+
30+
/// Releases the HTLC corresponding to an inbound [`HeldHtlcAvailable`] message.
31+
ReleaseHeldHtlc(ReleaseHeldHtlc),
32+
}
33+
34+
/// An HTLC destined for the recipient of this message is being held upstream. The reply path
35+
/// accompanying this onion message should be used to send a [`ReleaseHeldHtlc`] response, which
36+
/// will cause the upstream HTLC to be released.
37+
#[derive(Clone, Debug)]
38+
pub struct HeldHtlcAvailable {
39+
/// The secret that will be used by the recipient of this message to release the held HTLC.
40+
payment_release_secret: [u8; 32],
41+
}
42+
43+
/// Releases the HTLC corresponding to an inbound [`HeldHtlcAvailable`] message.
44+
#[derive(Clone, Debug)]
45+
pub struct ReleaseHeldHtlc {
46+
/// Used to release the HTLC held upstream.
47+
payment_release_secret: [u8; 32],
48+
}
49+
50+
impl_writeable_tlv_based!(HeldHtlcAvailable, {
51+
(0, payment_release_secret, required),
52+
});
53+
54+
impl_writeable_tlv_based!(ReleaseHeldHtlc, {
55+
(0, payment_release_secret, required),
56+
});
57+
58+
impl AsyncPaymentsMessage {
59+
/// Returns whether `tlv_type` corresponds to a TLV record for async payment messages.
60+
pub fn is_known_type(tlv_type: u64) -> bool {
61+
match tlv_type {
62+
HELD_HTLC_AVAILABLE_TLV_TYPE | RELEASE_HELD_HTLC_TLV_TYPE => true,
63+
_ => false,
64+
}
65+
}
66+
}
67+
68+
impl OnionMessageContents for AsyncPaymentsMessage {
69+
fn tlv_type(&self) -> u64 {
70+
match self {
71+
Self::HeldHtlcAvailable(_) => HELD_HTLC_AVAILABLE_TLV_TYPE,
72+
Self::ReleaseHeldHtlc(_) => RELEASE_HELD_HTLC_TLV_TYPE,
73+
}
74+
}
75+
fn msg_type(&self) -> &'static str {
76+
match &self {
77+
Self::HeldHtlcAvailable(_) => "Held HTLC Available",
78+
Self::ReleaseHeldHtlc(_) => "Release Held HTLC",
79+
}
80+
}
81+
}
82+
83+
impl Writeable for AsyncPaymentsMessage {
84+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
85+
match self {
86+
Self::HeldHtlcAvailable(message) => message.write(w),
87+
Self::ReleaseHeldHtlc(message) => message.write(w),
88+
}
89+
}
90+
}
91+
92+
impl ReadableArgs<u64> for AsyncPaymentsMessage {
93+
fn read<R: io::Read>(r: &mut R, tlv_type: u64) -> Result<Self, DecodeError> {
94+
match tlv_type {
95+
HELD_HTLC_AVAILABLE_TLV_TYPE => Ok(Self::HeldHtlcAvailable(Readable::read(r)?)),
96+
RELEASE_HELD_HTLC_TLV_TYPE => Ok(Self::ReleaseHeldHtlc(Readable::read(r)?)),
97+
_ => Err(DecodeError::InvalidValue),
98+
}
99+
}
100+
}

lightning/src/onion_message/messenger.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,6 +1435,7 @@ where
14351435
let response_instructions = self.custom_handler.handle_custom_message(msg, responder);
14361436
let _ = self.handle_onion_message_response(response_instructions);
14371437
},
1438+
ParsedOnionMessageContents::AsyncPayments(_msg) => todo!(),
14381439
}
14391440
},
14401441
Ok(PeeledOnion::Forward(next_hop, onion_message)) => {

lightning/src/onion_message/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
//! [blinded paths]: crate::blinded_path::BlindedPath
2222
//! [`OnionMessenger`]: self::messenger::OnionMessenger
2323
24+
pub mod async_payments;
2425
pub mod messenger;
2526
pub mod offers;
2627
pub mod packet;

lightning/src/onion_message/packet.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::blinded_path::message::{ForwardTlvs, ReceiveTlvs};
1717
use crate::blinded_path::utils::Padding;
1818
use crate::ln::msgs::DecodeError;
1919
use crate::ln::onion_utils;
20+
use super::async_payments::AsyncPaymentsMessage;
2021
use super::messenger::CustomOnionMessageHandler;
2122
use super::offers::OffersMessage;
2223
use crate::crypto::streams::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
@@ -128,6 +129,8 @@ pub(super) enum Payload<T: OnionMessageContents> {
128129
pub enum ParsedOnionMessageContents<T: OnionMessageContents> {
129130
/// A message related to BOLT 12 Offers.
130131
Offers(OffersMessage),
132+
/// A message related to async payments.
133+
AsyncPayments(AsyncPaymentsMessage),
131134
/// A custom onion message specified by the user.
132135
Custom(T),
133136
}
@@ -139,12 +142,14 @@ impl<T: OnionMessageContents> OnionMessageContents for ParsedOnionMessageContent
139142
fn tlv_type(&self) -> u64 {
140143
match self {
141144
&ParsedOnionMessageContents::Offers(ref msg) => msg.tlv_type(),
145+
&ParsedOnionMessageContents::AsyncPayments(ref msg) => msg.tlv_type(),
142146
&ParsedOnionMessageContents::Custom(ref msg) => msg.tlv_type(),
143147
}
144148
}
145149
fn msg_type(&self) -> &'static str {
146150
match self {
147151
ParsedOnionMessageContents::Offers(ref msg) => msg.msg_type(),
152+
ParsedOnionMessageContents::AsyncPayments(ref msg) => msg.msg_type(),
148153
ParsedOnionMessageContents::Custom(ref msg) => msg.msg_type(),
149154
}
150155
}
@@ -154,6 +159,7 @@ impl<T: OnionMessageContents> Writeable for ParsedOnionMessageContents<T> {
154159
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
155160
match self {
156161
ParsedOnionMessageContents::Offers(msg) => Ok(msg.write(w)?),
162+
ParsedOnionMessageContents::AsyncPayments(msg) => Ok(msg.write(w)?),
157163
ParsedOnionMessageContents::Custom(msg) => Ok(msg.write(w)?),
158164
}
159165
}
@@ -255,6 +261,11 @@ for Payload<ParsedOnionMessageContents<<H as CustomOnionMessageHandler>::CustomM
255261
message = Some(ParsedOnionMessageContents::Offers(msg));
256262
Ok(true)
257263
},
264+
tlv_type if AsyncPaymentsMessage::is_known_type(tlv_type) => {
265+
let msg = AsyncPaymentsMessage::read(msg_reader, tlv_type)?;
266+
message = Some(ParsedOnionMessageContents::AsyncPayments(msg));
267+
Ok(true)
268+
},
258269
_ => match handler.read_custom_message(msg_type, msg_reader)? {
259270
Some(msg) => {
260271
message = Some(ParsedOnionMessageContents::Custom(msg));

0 commit comments

Comments
 (0)