Skip to content

Commit a799fc9

Browse files
committed
Onion message payload for BOLT 12 Offers
BOLT 12 Offers makes use of onion messages to request and respond with invoices. Add these types and an error type to OnionMessageContents along with the necessary parsing and encoding.
1 parent 7533a3c commit a799fc9

File tree

4 files changed

+131
-13
lines changed

4 files changed

+131
-13
lines changed

lightning/src/onion_message/messenger.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,8 @@ impl<ES: Deref, NS: Deref, L: Deref, CMH: Deref> OnionMessenger<ES, NS, L, CMH>
216216
return Err(SendError::TooFewBlindedHops);
217217
}
218218
}
219-
let OnionMessageContents::Custom(ref msg) = message;
220-
if msg.tlv_type() < 64 { return Err(SendError::InvalidMessage) }
219+
220+
if message.tlv_type() < 64 { return Err(SendError::InvalidMessage) }
221221

222222
// If we are sending straight to a blinded path and we are the introduction node, we need to
223223
// advance the blinded path by 1 hop so the second hop is the new introduction node.
@@ -342,6 +342,7 @@ impl<ES: Deref, NS: Deref, L: Deref, CMH: Deref> OnionMessageHandler for OnionMe
342342
"Received an onion message with path_id {:02x?} and {} reply_path",
343343
path_id, if reply_path.is_some() { "a" } else { "no" });
344344
match message {
345+
OnionMessageContents::Offers(_msg) => todo!(),
345346
OnionMessageContents::Custom(msg) => self.custom_handler.handle_custom_message(msg),
346347
}
347348
},

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
2323
mod messenger;
24+
mod offers;
2425
mod packet;
2526
#[cfg(test)]
2627
mod functional_tests;

lightning/src/onion_message/offers.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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 BOLT 12 Offers.
11+
12+
use core::convert::TryFrom;
13+
use crate::io::{self, Read};
14+
use crate::ln::msgs::DecodeError;
15+
use crate::offers::invoice_error::InvoiceError;
16+
use crate::offers::invoice_request::InvoiceRequest;
17+
use crate::offers::invoice::Invoice;
18+
use crate::offers::parse::ParseError;
19+
use crate::util::logger::Logger;
20+
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
21+
22+
use crate::prelude::*;
23+
24+
// TLV record types for the `onionmsg_tlv` TLV stream as defined in BOLT 4.
25+
const INVOICE_REQUEST_TLV_TYPE: u64 = 64;
26+
const INVOICE_TLV_TYPE: u64 = 66;
27+
const INVOICE_ERROR_TLV_TYPE: u64 = 68;
28+
29+
/// Possible BOLT 12 Offers messages sent and received via an [`OnionMessage`].
30+
///
31+
/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
32+
#[derive(Debug)]
33+
pub enum OffersMessage {
34+
/// A request for an [`Invoice`] for a particular [`Offer`].
35+
///
36+
/// [`Offer`]: crate::offers::offer::Offer
37+
InvoiceRequest(InvoiceRequest),
38+
39+
/// An [`Invoice`] sent in response to an [`InvoiceRequest`] or a [`Refund`].
40+
///
41+
/// [`Refund`]: crate::offers::refund::Refund
42+
Invoice(Invoice),
43+
44+
/// An error from handling an [`OffersMessage`].
45+
InvoiceError(InvoiceError),
46+
}
47+
48+
impl OffersMessage {
49+
/// Returns whether `tlv_type` corresponds to a TLV record for Offers.
50+
pub fn is_known_type(tlv_type: u64) -> bool {
51+
match tlv_type {
52+
INVOICE_REQUEST_TLV_TYPE | INVOICE_TLV_TYPE | INVOICE_ERROR_TLV_TYPE => true,
53+
_ => false,
54+
}
55+
}
56+
57+
/// The TLV record type for the message as used in an `onionmsg_tlv` TLV stream.
58+
pub fn tlv_type(&self) -> u64 {
59+
match self {
60+
OffersMessage::InvoiceRequest(_) => INVOICE_REQUEST_TLV_TYPE,
61+
OffersMessage::Invoice(_) => INVOICE_TLV_TYPE,
62+
OffersMessage::InvoiceError(_) => INVOICE_ERROR_TLV_TYPE,
63+
}
64+
}
65+
66+
fn parse(tlv_type: u64, bytes: Vec<u8>) -> Result<Self, ParseError> {
67+
match tlv_type {
68+
INVOICE_REQUEST_TLV_TYPE => Ok(Self::InvoiceRequest(InvoiceRequest::try_from(bytes)?)),
69+
INVOICE_TLV_TYPE => Ok(Self::Invoice(Invoice::try_from(bytes)?)),
70+
_ => Err(ParseError::Decode(DecodeError::InvalidValue)),
71+
}
72+
}
73+
}
74+
75+
impl Writeable for OffersMessage {
76+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
77+
match self {
78+
OffersMessage::InvoiceRequest(message) => message.write(w),
79+
OffersMessage::Invoice(message) => message.write(w),
80+
OffersMessage::InvoiceError(message) => message.write(w),
81+
}
82+
}
83+
}
84+
85+
impl<L: Logger> ReadableArgs<(u64, &L)> for OffersMessage {
86+
fn read<R: Read>(r: &mut R, read_args: (u64, &L)) -> Result<Self, DecodeError> {
87+
let (tlv_type, logger) = read_args;
88+
if tlv_type == INVOICE_ERROR_TLV_TYPE {
89+
return Ok(Self::InvoiceError(InvoiceError::read(r)?));
90+
}
91+
92+
let mut bytes = Vec::new();
93+
r.read_to_end(&mut bytes).unwrap();
94+
95+
match Self::parse(tlv_type, bytes) {
96+
Ok(message) => Ok(message),
97+
Err(ParseError::Decode(e)) => Err(e),
98+
Err(ParseError::InvalidSemantics(e)) => {
99+
log_trace!(logger, "Invalid semantics for TLV type {}: {:?}", tlv_type, e);
100+
Err(DecodeError::InvalidValue)
101+
},
102+
Err(ParseError::InvalidSignature(e)) => {
103+
log_trace!(logger, "Invalid signature for TLV type {}: {:?}", tlv_type, e);
104+
Err(DecodeError::InvalidValue)
105+
},
106+
Err(_) => Err(DecodeError::InvalidValue),
107+
}
108+
}
109+
}

lightning/src/onion_message/packet.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::blinded_path::{BlindedPath, ForwardTlvs, ReceiveTlvs};
1616
use crate::ln::msgs::DecodeError;
1717
use crate::ln::onion_utils;
1818
use super::messenger::CustomOnionMessageHandler;
19+
use super::offers::OffersMessage;
1920
use crate::util::chacha20poly1305rfc::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
2021
use crate::util::logger::Logger;
2122
use crate::util::ser::{BigSize, FixedLengthReader, LengthRead, LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer};
@@ -109,10 +110,8 @@ pub(super) enum Payload<T: CustomOnionMessageContents> {
109110
/// The contents of an onion message. In the context of offers, this would be the invoice, invoice
110111
/// request, or invoice error.
111112
pub enum OnionMessageContents<T: CustomOnionMessageContents> {
112-
// Coming soon:
113-
// Invoice,
114-
// InvoiceRequest,
115-
// InvoiceError,
113+
/// A message related to BOLT 12 Offers.
114+
Offers(OffersMessage),
116115
/// A custom onion message specified by the user.
117116
Custom(T),
118117
}
@@ -123,6 +122,7 @@ impl<T: CustomOnionMessageContents> OnionMessageContents<T> {
123122
/// This is not exported to bindings users as methods on non-cloneable enums are not currently exportable
124123
pub fn tlv_type(&self) -> u64 {
125124
match self {
125+
&OnionMessageContents::Offers(ref msg) => msg.tlv_type(),
126126
&OnionMessageContents::Custom(ref msg) => msg.tlv_type(),
127127
}
128128
}
@@ -132,6 +132,7 @@ impl<T: CustomOnionMessageContents> OnionMessageContents<T> {
132132
impl<T: CustomOnionMessageContents> Writeable for OnionMessageContents<T> {
133133
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
134134
match self {
135+
OnionMessageContents::Offers(msg) => Ok(msg.write(w)?),
135136
OnionMessageContents::Custom(msg) => Ok(msg.write(w)?),
136137
}
137138
}
@@ -205,7 +206,7 @@ impl<T: CustomOnionMessageContents> Writeable for (Payload<T>, [u8; 32]) {
205206
impl<H: CustomOnionMessageHandler, L: Logger>
206207
ReadableArgs<(SharedSecret, &H, &L)> for Payload<<H as CustomOnionMessageHandler>::CustomMessage> {
207208
fn read<R: Read>(r: &mut R, args: (SharedSecret, &H, &L)) -> Result<Self, DecodeError> {
208-
let (encrypted_tlvs_ss, handler, _logger) = args;
209+
let (encrypted_tlvs_ss, handler, logger) = args;
209210

210211
let v: BigSize = Readable::read(r)?;
211212
let mut rd = FixedLengthReader::new(r, v.0);
@@ -223,13 +224,19 @@ ReadableArgs<(SharedSecret, &H, &L)> for Payload<<H as CustomOnionMessageHandler
223224
if message_type.is_some() { return Err(DecodeError::InvalidValue) }
224225

225226
message_type = Some(msg_type);
226-
match handler.read_custom_message(msg_type, msg_reader) {
227-
Ok(Some(msg)) => {
228-
message = Some(msg);
227+
match msg_type {
228+
tlv_type if OffersMessage::is_known_type(tlv_type) => {
229+
let msg = OffersMessage::read(msg_reader, (tlv_type, logger))?;
230+
message = Some(OnionMessageContents::Offers(msg));
229231
Ok(true)
230232
},
231-
Ok(None) => Ok(false),
232-
Err(e) => Err(e),
233+
_ => match handler.read_custom_message(msg_type, msg_reader)? {
234+
Some(msg) => {
235+
message = Some(OnionMessageContents::Custom(msg));
236+
Ok(true)
237+
},
238+
None => Ok(false),
239+
},
233240
}
234241
});
235242
rd.eat_remaining().map_err(|_| DecodeError::ShortRead)?;
@@ -247,7 +254,7 @@ ReadableArgs<(SharedSecret, &H, &L)> for Payload<<H as CustomOnionMessageHandler
247254
Ok(Payload::Receive {
248255
control_tlvs: ReceiveControlTlvs::Unblinded(tlvs),
249256
reply_path,
250-
message: OnionMessageContents::Custom(message.unwrap()),
257+
message: message.unwrap(),
251258
})
252259
}
253260
}

0 commit comments

Comments
 (0)