lightning/blinded_path/message.rs
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//! Data structures and methods for constructing [`BlindedMessagePath`]s to send a message over.
11
12use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
13
14#[allow(unused_imports)]
15use crate::prelude::*;
16
17use crate::blinded_path::utils::{self, BlindedPathWithPadding};
18use crate::blinded_path::{BlindedHop, BlindedPath, Direction, IntroductionNode, NodeIdLookUp};
19use crate::crypto::streams::ChaChaPolyReadAdapter;
20use crate::io;
21use crate::io::Cursor;
22use crate::ln::channelmanager::{InterceptId, PaymentId};
23use crate::ln::msgs::DecodeError;
24use crate::ln::onion_utils;
25use crate::offers::nonce::Nonce;
26use crate::offers::offer::OfferId;
27use crate::onion_message::packet::ControlTlvs;
28use crate::routing::gossip::{NodeId, ReadOnlyNetworkGraph};
29use crate::sign::{EntropySource, NodeSigner, ReceiveAuthKey, Recipient};
30use crate::types::payment::PaymentHash;
31use crate::util::scid_utils;
32use crate::util::ser::{FixedLengthReader, LengthReadableArgs, Readable, Writeable, Writer};
33
34use core::ops::Deref;
35use core::time::Duration;
36use core::{cmp, mem};
37
38/// A blinded path to be used for sending or receiving a message, hiding the identity of the
39/// recipient.
40#[derive(Clone, Debug, Hash, PartialEq, Eq)]
41pub struct BlindedMessagePath(pub(super) BlindedPath);
42
43impl Writeable for BlindedMessagePath {
44 fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
45 self.0.write(w)
46 }
47}
48
49impl Readable for BlindedMessagePath {
50 fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
51 Ok(Self(BlindedPath::read(r)?))
52 }
53}
54
55impl BlindedMessagePath {
56 /// Create a one-hop blinded path for a message.
57 pub fn one_hop<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
58 recipient_node_id: PublicKey, local_node_receive_key: ReceiveAuthKey,
59 context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1<T>,
60 ) -> Self
61 where
62 ES::Target: EntropySource,
63 {
64 Self::new(&[], recipient_node_id, local_node_receive_key, context, entropy_source, secp_ctx)
65 }
66
67 /// Create a path for an onion message, to be forwarded along `node_pks`.
68 pub fn new<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
69 intermediate_nodes: &[MessageForwardNode], recipient_node_id: PublicKey,
70 local_node_receive_key: ReceiveAuthKey, context: MessageContext, entropy_source: ES,
71 secp_ctx: &Secp256k1<T>,
72 ) -> Self
73 where
74 ES::Target: EntropySource,
75 {
76 BlindedMessagePath::new_with_dummy_hops(
77 intermediate_nodes,
78 recipient_node_id,
79 0,
80 local_node_receive_key,
81 context,
82 entropy_source,
83 secp_ctx,
84 )
85 }
86
87 /// Same as [`BlindedMessagePath::new`], but allows specifying a number of dummy hops.
88 ///
89 /// Note:
90 /// At most [`MAX_DUMMY_HOPS_COUNT`] dummy hops can be added to the blinded path.
91 pub fn new_with_dummy_hops<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
92 intermediate_nodes: &[MessageForwardNode], recipient_node_id: PublicKey,
93 dummy_hop_count: usize, local_node_receive_key: ReceiveAuthKey, context: MessageContext,
94 entropy_source: ES, secp_ctx: &Secp256k1<T>,
95 ) -> Self
96 where
97 ES::Target: EntropySource,
98 {
99 let introduction_node = IntroductionNode::NodeId(
100 intermediate_nodes.first().map_or(recipient_node_id, |n| n.node_id),
101 );
102 let blinding_secret_bytes = entropy_source.get_secure_random_bytes();
103 let blinding_secret =
104 SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
105
106 Self(BlindedPath {
107 introduction_node,
108 blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
109 blinded_hops: blinded_hops(
110 secp_ctx,
111 intermediate_nodes,
112 recipient_node_id,
113 dummy_hop_count,
114 context,
115 &blinding_secret,
116 local_node_receive_key,
117 ),
118 })
119 }
120
121 /// Attempts to a use a compact representation for the [`IntroductionNode`] by using a directed
122 /// short channel id from a channel in `network_graph` leading to the introduction node.
123 ///
124 /// While this may result in a smaller encoding, there is a trade off in that the path may
125 /// become invalid if the channel is closed or hasn't been propagated via gossip. Therefore,
126 /// calling this may not be suitable for long-lived blinded paths.
127 pub fn use_compact_introduction_node(&mut self, network_graph: &ReadOnlyNetworkGraph) {
128 if let IntroductionNode::NodeId(pubkey) = &self.0.introduction_node {
129 let node_id = NodeId::from_pubkey(pubkey);
130 if let Some(node_info) = network_graph.node(&node_id) {
131 if let Some((scid, channel_info)) = node_info
132 .channels
133 .iter()
134 .filter_map(|scid| network_graph.channel(*scid).map(|info| (*scid, info)))
135 .min_by_key(|(scid, _)| scid_utils::block_from_scid(*scid))
136 {
137 let direction = if node_id == channel_info.node_one {
138 Direction::NodeOne
139 } else {
140 debug_assert_eq!(node_id, channel_info.node_two);
141 Direction::NodeTwo
142 };
143 self.0.introduction_node =
144 IntroductionNode::DirectedShortChannelId(direction, scid);
145 }
146 }
147 }
148 }
149
150 /// Returns the introduction [`NodeId`] of the blinded path, if it is publicly reachable (i.e.,
151 /// it is found in the network graph).
152 pub fn public_introduction_node_id<'a>(
153 &self, network_graph: &'a ReadOnlyNetworkGraph,
154 ) -> Option<&'a NodeId> {
155 self.0.public_introduction_node_id(network_graph)
156 }
157
158 /// The [`IntroductionNode`] of the blinded path.
159 pub fn introduction_node(&self) -> &IntroductionNode {
160 &self.0.introduction_node
161 }
162
163 /// Used by the [`IntroductionNode`] to decrypt its [`encrypted_payload`] to forward the message.
164 ///
165 /// [`encrypted_payload`]: BlindedHop::encrypted_payload
166 pub fn blinding_point(&self) -> PublicKey {
167 self.0.blinding_point
168 }
169
170 /// The [`BlindedHop`]s within the blinded path.
171 pub fn blinded_hops(&self) -> &[BlindedHop] {
172 &self.0.blinded_hops
173 }
174
175 /// Advance the blinded onion message path by one hop, making the second hop into the new
176 /// introduction node.
177 ///
178 /// Will only modify `self` when returning `Ok`.
179 pub fn advance_path_by_one<NS: Deref, NL: Deref, T>(
180 &mut self, node_signer: &NS, node_id_lookup: &NL, secp_ctx: &Secp256k1<T>,
181 ) -> Result<(), ()>
182 where
183 NS::Target: NodeSigner,
184 NL::Target: NodeIdLookUp,
185 T: secp256k1::Signing + secp256k1::Verification,
186 {
187 let control_tlvs_ss = node_signer.ecdh(Recipient::Node, &self.0.blinding_point, None)?;
188 let rho = onion_utils::gen_rho_from_shared_secret(&control_tlvs_ss.secret_bytes());
189 let encrypted_control_tlvs = &self.0.blinded_hops.get(0).ok_or(())?.encrypted_payload;
190 let mut s = Cursor::new(encrypted_control_tlvs);
191 let mut reader = FixedLengthReader::new(&mut s, encrypted_control_tlvs.len() as u64);
192 match ChaChaPolyReadAdapter::read(&mut reader, rho) {
193 Ok(ChaChaPolyReadAdapter {
194 readable: ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override }),
195 }) => {
196 let next_node_id = match next_hop {
197 NextMessageHop::NodeId(pubkey) => pubkey,
198 NextMessageHop::ShortChannelId(scid) => match node_id_lookup.next_node_id(scid)
199 {
200 Some(pubkey) => pubkey,
201 None => return Err(()),
202 },
203 };
204 let mut new_blinding_point = match next_blinding_override {
205 Some(blinding_point) => blinding_point,
206 None => onion_utils::next_hop_pubkey(
207 secp_ctx,
208 self.0.blinding_point,
209 control_tlvs_ss.as_ref(),
210 )
211 .map_err(|_| ())?,
212 };
213 mem::swap(&mut self.0.blinding_point, &mut new_blinding_point);
214 self.0.introduction_node = IntroductionNode::NodeId(next_node_id);
215 self.0.blinded_hops.remove(0);
216 Ok(())
217 },
218 _ => Err(()),
219 }
220 }
221
222 pub(crate) fn introduction_node_mut(&mut self) -> &mut IntroductionNode {
223 &mut self.0.introduction_node
224 }
225
226 /// Creates a new [`BlindedMessagePath`] from its constituent parts.
227 ///
228 /// Useful when you need to reconstruct a blinded path from previously serialized components.
229 ///
230 /// Parameters:
231 /// * `introduction_node_id`: The public key of the introduction node in the path
232 /// * `blinding_point`: The public key used for blinding the path
233 /// * `blinded_hops`: The encrypted routing information for each hop in the path
234 pub fn from_blinded_path(
235 introduction_node_id: PublicKey, blinding_point: PublicKey, blinded_hops: Vec<BlindedHop>,
236 ) -> Self {
237 Self(BlindedPath {
238 introduction_node: IntroductionNode::NodeId(introduction_node_id),
239 blinding_point,
240 blinded_hops,
241 })
242 }
243
244 #[cfg(test)]
245 pub fn clear_blinded_hops(&mut self) {
246 self.0.blinded_hops.clear()
247 }
248}
249
250/// The next hop to forward an onion message along its path.
251///
252/// Note that payment blinded paths always specify their next hop using an explicit node id.
253#[derive(Clone, Debug, Hash, PartialEq, Eq)]
254pub enum NextMessageHop {
255 /// The node id of the next hop.
256 NodeId(PublicKey),
257 /// The short channel id leading to the next hop.
258 ShortChannelId(u64),
259}
260
261/// An intermediate node, and possibly a short channel id leading to the next node.
262///
263/// Note:
264/// [`MessageForwardNode`] must represent a node that supports [`supports_onion_messages`]
265/// in order to be included in valid blinded paths for onion messaging.
266///
267/// [`supports_onion_messages`]: crate::types::features::Features::supports_onion_messages
268#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
269pub struct MessageForwardNode {
270 /// This node's pubkey.
271 pub node_id: PublicKey,
272 /// The channel between `node_id` and the next hop. If set, the constructed [`BlindedHop`]'s
273 /// `encrypted_payload` will use this instead of the next [`MessageForwardNode::node_id`] for a
274 /// more compact representation.
275 pub short_channel_id: Option<u64>,
276}
277
278/// TLVs to encode in an intermediate onion message packet's hop data. When provided in a blinded
279/// route, they are encoded into [`BlindedHop::encrypted_payload`].
280pub(crate) struct ForwardTlvs {
281 /// The next hop in the onion message's path.
282 pub(crate) next_hop: NextMessageHop,
283 /// Senders to a blinded path use this value to concatenate the route they find to the
284 /// introduction node with the blinded path.
285 pub(crate) next_blinding_override: Option<PublicKey>,
286}
287
288/// Represents the dummy TLV encoded immediately before the actual [`ReceiveTlvs`] in a blinded path.
289/// These TLVs are intended for the final node and are recursively authenticated until the real
290/// [`ReceiveTlvs`] is reached.
291///
292/// Their purpose is to arbitrarily extend the path length, obscuring the receiver's position in the
293/// route and thereby enhancing privacy.
294pub(crate) struct DummyTlv;
295
296impl Writeable for DummyTlv {
297 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
298 encode_tlv_stream!(writer, {
299 (65539, (), required),
300 });
301 Ok(())
302 }
303}
304
305/// Similar to [`ForwardTlvs`], but these TLVs are for the final node.
306pub(crate) struct ReceiveTlvs {
307 /// If `context` is `Some`, it is used to identify the blinded path that this onion message is
308 /// sending to. This is useful for receivers to check that said blinded path is being used in
309 /// the right context.
310 pub context: Option<MessageContext>,
311}
312
313impl Writeable for ForwardTlvs {
314 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
315 let (next_node_id, short_channel_id) = match self.next_hop {
316 NextMessageHop::NodeId(pubkey) => (Some(pubkey), None),
317 NextMessageHop::ShortChannelId(scid) => (None, Some(scid)),
318 };
319 encode_tlv_stream!(writer, {
320 (2, short_channel_id, option),
321 (4, next_node_id, option),
322 (8, self.next_blinding_override, option)
323 });
324 Ok(())
325 }
326}
327
328impl Writeable for ReceiveTlvs {
329 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
330 encode_tlv_stream!(writer, {
331 (65537, self.context, option),
332 });
333 Ok(())
334 }
335}
336
337/// Additional data included by the recipient in a [`BlindedMessagePath`].
338///
339/// This data is encrypted by the recipient and will be given to the corresponding message handler
340/// when handling a message sent over the [`BlindedMessagePath`]. The recipient can use this data to
341/// authenticate the message or for further processing if needed.
342#[derive(Clone, Debug)]
343pub enum MessageContext {
344 /// Context specific to an [`OffersMessage`].
345 ///
346 /// [`OffersMessage`]: crate::onion_message::offers::OffersMessage
347 Offers(OffersContext),
348 /// Context specific to an [`AsyncPaymentsMessage`].
349 ///
350 /// [`AsyncPaymentsMessage`]: crate::onion_message::async_payments::AsyncPaymentsMessage
351 AsyncPayments(AsyncPaymentsContext),
352 /// Represents a context for a blinded path used in a reply path when requesting a DNSSEC proof
353 /// in a [`DNSResolverMessage`].
354 ///
355 /// [`DNSResolverMessage`]: crate::onion_message::dns_resolution::DNSResolverMessage
356 DNSResolver(DNSResolverContext),
357 /// Context specific to a [`CustomOnionMessageHandler::CustomMessage`].
358 ///
359 /// [`CustomOnionMessageHandler::CustomMessage`]: crate::onion_message::messenger::CustomOnionMessageHandler::CustomMessage
360 Custom(Vec<u8>),
361}
362
363/// Contains data specific to an [`OffersMessage`].
364///
365/// [`OffersMessage`]: crate::onion_message::offers::OffersMessage
366#[derive(Clone, Debug, Eq, PartialEq)]
367pub enum OffersContext {
368 /// Context used by a [`BlindedMessagePath`] within an [`Offer`].
369 ///
370 /// This variant is intended to be received when handling an [`InvoiceRequest`].
371 ///
372 /// [`Offer`]: crate::offers::offer::Offer
373 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
374 InvoiceRequest {
375 /// A nonce used for authenticating that an [`InvoiceRequest`] is for a valid [`Offer`] and
376 /// for deriving the offer's signing keys.
377 ///
378 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
379 /// [`Offer`]: crate::offers::offer::Offer
380 nonce: Nonce,
381 },
382 /// Context used by a [`BlindedMessagePath`] within the [`Offer`] of an async recipient.
383 ///
384 /// This variant is received by the static invoice server when handling an [`InvoiceRequest`] on
385 /// behalf of said async recipient.
386 ///
387 /// [`Offer`]: crate::offers::offer::Offer
388 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
389 StaticInvoiceRequested {
390 /// An identifier for the async recipient for whom we as a static invoice server are serving
391 /// [`StaticInvoice`]s. Used paired with the
392 /// [`OffersContext::StaticInvoiceRequested::invoice_slot`] when looking up a corresponding
393 /// [`StaticInvoice`] to return to the payer if the recipient is offline. This id was previously
394 /// provided via [`AsyncPaymentsContext::ServeStaticInvoice::recipient_id`].
395 ///
396 /// Also useful for rate limiting the number of [`InvoiceRequest`]s we will respond to on
397 /// recipient's behalf.
398 ///
399 /// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
400 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
401 recipient_id: Vec<u8>,
402
403 /// The slot number for a specific [`StaticInvoice`] that the recipient previously
404 /// requested be served on their behalf. Useful when paired with the
405 /// [`OffersContext::StaticInvoiceRequested::recipient_id`] to pull that specific invoice from
406 /// the database when payers send an [`InvoiceRequest`]. This id was previously
407 /// provided via [`AsyncPaymentsContext::ServeStaticInvoice::invoice_slot`].
408 ///
409 /// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
410 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
411 invoice_slot: u16,
412
413 /// The time as duration since the Unix epoch at which this path expires and messages sent over
414 /// it should be ignored.
415 ///
416 /// Useful to timeout async recipients that are no longer supported as clients.
417 path_absolute_expiry: Duration,
418 },
419 /// Context used by a [`BlindedMessagePath`] within a [`Refund`] or as a reply path for an
420 /// [`InvoiceRequest`].
421 ///
422 /// This variant is intended to be received when handling a [`Bolt12Invoice`] or an
423 /// [`InvoiceError`].
424 ///
425 /// [`Refund`]: crate::offers::refund::Refund
426 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
427 /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
428 /// [`InvoiceError`]: crate::offers::invoice_error::InvoiceError
429 OutboundPayment {
430 /// Payment ID used when creating a [`Refund`] or [`InvoiceRequest`].
431 ///
432 /// [`Refund`]: crate::offers::refund::Refund
433 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
434 payment_id: PaymentId,
435
436 /// A nonce used for authenticating that a [`Bolt12Invoice`] is for a valid [`Refund`] or
437 /// [`InvoiceRequest`] and for deriving their signing keys.
438 ///
439 /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
440 /// [`Refund`]: crate::offers::refund::Refund
441 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
442 nonce: Nonce,
443 },
444 /// Context used by a [`BlindedMessagePath`] as a reply path for a [`Bolt12Invoice`].
445 ///
446 /// This variant is intended to be received when handling an [`InvoiceError`].
447 ///
448 /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
449 /// [`InvoiceError`]: crate::offers::invoice_error::InvoiceError
450 InboundPayment {
451 /// The same payment hash as [`Bolt12Invoice::payment_hash`].
452 ///
453 /// [`Bolt12Invoice::payment_hash`]: crate::offers::invoice::Bolt12Invoice::payment_hash
454 payment_hash: PaymentHash,
455 },
456}
457
458/// Contains data specific to an [`AsyncPaymentsMessage`].
459///
460/// [`AsyncPaymentsMessage`]: crate::onion_message::async_payments::AsyncPaymentsMessage
461#[derive(Clone, Debug)]
462pub enum AsyncPaymentsContext {
463 /// Context used by a [`BlindedMessagePath`] provided out-of-band to an async recipient, where the
464 /// context is provided back to the static invoice server in corresponding [`OfferPathsRequest`]s.
465 ///
466 /// [`OfferPathsRequest`]: crate::onion_message::async_payments::OfferPathsRequest
467 OfferPathsRequest {
468 /// An identifier for the async recipient that is requesting blinded paths to include in their
469 /// [`Offer::paths`]. This ID will be surfaced when the async recipient eventually sends a
470 /// corresponding [`ServeStaticInvoice`] message, and can be used to rate limit the recipient.
471 ///
472 /// [`Offer::paths`]: crate::offers::offer::Offer::paths
473 /// [`ServeStaticInvoice`]: crate::onion_message::async_payments::ServeStaticInvoice
474 recipient_id: Vec<u8>,
475 /// An optional field indicating the time as duration since the Unix epoch at which this path
476 /// expires and messages sent over it should be ignored.
477 ///
478 /// Useful to timeout async recipients that are no longer supported as clients.
479 path_absolute_expiry: Option<core::time::Duration>,
480 },
481 /// Context used by a reply path to an [`OfferPathsRequest`], provided back to us as an async
482 /// recipient in corresponding [`OfferPaths`] messages from the static invoice server.
483 ///
484 /// [`OfferPathsRequest`]: crate::onion_message::async_payments::OfferPathsRequest
485 /// [`OfferPaths`]: crate::onion_message::async_payments::OfferPaths
486 OfferPaths {
487 /// The "slot" in the static invoice server's database that the invoice corresponding to these
488 /// offer paths should go into, originally set by us in [`OfferPathsRequest::invoice_slot`]. This
489 /// value allows us as the recipient to replace a specific invoice that is stored by the server,
490 /// which is useful for limiting the number of invoices stored by the server while also keeping
491 /// all the invoices persisted with the server fresh.
492 ///
493 /// [`OfferPathsRequest::invoice_slot`]: crate::onion_message::async_payments::OfferPathsRequest::invoice_slot
494 invoice_slot: u16,
495 /// The time as duration since the Unix epoch at which this path expires and messages sent over
496 /// it should be ignored.
497 ///
498 /// This avoids the situation where the [`OfferPaths`] message is very delayed and thus
499 /// outdated.
500 ///
501 /// [`OfferPaths`]: crate::onion_message::async_payments::OfferPaths
502 path_absolute_expiry: core::time::Duration,
503 },
504 /// Context used by a reply path to an [`OfferPaths`] message, provided back to us as the static
505 /// invoice server in corresponding [`ServeStaticInvoice`] messages.
506 ///
507 /// [`OfferPaths`]: crate::onion_message::async_payments::OfferPaths
508 /// [`ServeStaticInvoice`]: crate::onion_message::async_payments::ServeStaticInvoice
509 ServeStaticInvoice {
510 /// An identifier for the async recipient that is requesting that a [`StaticInvoice`] be served
511 /// on their behalf.
512 ///
513 /// Useful when surfaced alongside the below `invoice_slot` when payers send an
514 /// [`InvoiceRequest`], to pull the specific static invoice from the database.
515 ///
516 /// Also useful to rate limit the invoices being persisted on behalf of a particular recipient.
517 ///
518 /// This id will be provided back to us as the static invoice server via
519 /// [`OffersContext::StaticInvoiceRequested::recipient_id`].
520 ///
521 /// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
522 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
523 recipient_id: Vec<u8>,
524 /// The slot number for the specific [`StaticInvoice`] that the recipient is requesting be
525 /// served on their behalf. Useful when surfaced alongside the above `recipient_id` when payers
526 /// send an [`InvoiceRequest`], to pull the specific static invoice from the database. This id
527 /// will be provided back to us as the static invoice server via
528 /// [`OffersContext::StaticInvoiceRequested::invoice_slot`].
529 ///
530 /// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
531 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
532 invoice_slot: u16,
533 /// The time as duration since the Unix epoch at which this path expires and messages sent over
534 /// it should be ignored.
535 ///
536 /// Useful to timeout async recipients that are no longer supported as clients.
537 path_absolute_expiry: core::time::Duration,
538 },
539 /// Context used by a reply path to a [`ServeStaticInvoice`] message, provided back to us in
540 /// corresponding [`StaticInvoicePersisted`] messages.
541 ///
542 /// [`ServeStaticInvoice`]: crate::onion_message::async_payments::ServeStaticInvoice
543 /// [`StaticInvoicePersisted`]: crate::onion_message::async_payments::StaticInvoicePersisted
544 StaticInvoicePersisted {
545 /// The id of the offer in the cache corresponding to the [`StaticInvoice`] that has been
546 /// persisted. This invoice is now ready to be provided by the static invoice server in response
547 /// to [`InvoiceRequest`]s, so the corresponding offer can be marked as ready to receive
548 /// payments.
549 ///
550 /// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
551 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
552 offer_id: OfferId,
553 /// The time as duration since the Unix epoch at which the invoice corresponding to this path
554 /// was created. Useful to know when an invoice needs replacement.
555 invoice_created_at: core::time::Duration,
556 },
557 /// Context contained within the reply [`BlindedMessagePath`] we put in outbound
558 /// [`HeldHtlcAvailable`] messages, provided back to us in corresponding [`ReleaseHeldHtlc`]
559 /// messages if we are an always-online sender paying an async recipient.
560 ///
561 /// [`HeldHtlcAvailable`]: crate::onion_message::async_payments::HeldHtlcAvailable
562 /// [`ReleaseHeldHtlc`]: crate::onion_message::async_payments::ReleaseHeldHtlc
563 OutboundPayment {
564 /// ID used when payment to the originating [`Offer`] was initiated. Useful for us to identify
565 /// which of our pending outbound payments should be released to its often-offline payee.
566 ///
567 /// [`Offer`]: crate::offers::offer::Offer
568 payment_id: PaymentId,
569 },
570 /// Context contained within the [`BlindedMessagePath`]s we put in static invoices, provided back
571 /// to us in corresponding [`HeldHtlcAvailable`] messages.
572 ///
573 /// [`HeldHtlcAvailable`]: crate::onion_message::async_payments::HeldHtlcAvailable
574 InboundPayment {
575 /// The time as duration since the Unix epoch at which this path expires and messages sent over
576 /// it should be ignored. Without this, anyone with the path corresponding to this context is
577 /// able to trivially ask if we're online forever.
578 path_absolute_expiry: core::time::Duration,
579 },
580 /// Context contained within the reply [`BlindedMessagePath`] put in outbound
581 /// [`HeldHtlcAvailable`] messages, provided back to the async sender's always-online counterparty
582 /// in corresponding [`ReleaseHeldHtlc`] messages.
583 ///
584 /// [`HeldHtlcAvailable`]: crate::onion_message::async_payments::HeldHtlcAvailable
585 /// [`ReleaseHeldHtlc`]: crate::onion_message::async_payments::ReleaseHeldHtlc
586 ReleaseHeldHtlc {
587 /// An identifier for the HTLC that should be released by us as the sender's always-online
588 /// channel counterparty to the often-offline recipient.
589 intercept_id: InterceptId,
590 /// The short channel id alias corresponding to the to-be-released inbound HTLC, to help locate
591 /// the HTLC internally if the [`ReleaseHeldHtlc`] races our node decoding the held HTLC's
592 /// onion.
593 ///
594 /// We use the outbound scid alias because it is stable even if the channel splices, unlike
595 /// regular short channel ids.
596 ///
597 /// [`ReleaseHeldHtlc`]: crate::onion_message::async_payments::ReleaseHeldHtlc
598 prev_outbound_scid_alias: u64,
599 /// The id of the to-be-released HTLC, to help locate the HTLC internally if the
600 /// [`ReleaseHeldHtlc`] races our node decoding the held HTLC's onion.
601 ///
602 /// [`ReleaseHeldHtlc`]: crate::onion_message::async_payments::ReleaseHeldHtlc
603 htlc_id: u64,
604 },
605}
606
607impl_writeable_tlv_based_enum!(MessageContext,
608 {0, Offers} => (),
609 {1, Custom} => (),
610 {2, AsyncPayments} => (),
611 {3, DNSResolver} => (),
612);
613
614// Note: Several TLV fields (`nonce`, `hmac`, etc.) were removed in LDK v0.2 following the
615// introduction of `ReceiveAuthKey`-based authentication for inbound `BlindedMessagePath`s. Because
616// we do not support receiving to those contexts anymore (they will fail the `ReceiveAuthKey`-based
617// authentication checks), we can reuse those fields here.
618impl_writeable_tlv_based_enum!(OffersContext,
619 (0, InvoiceRequest) => {
620 (0, nonce, required),
621 },
622 (1, OutboundPayment) => {
623 (0, payment_id, required),
624 (1, nonce, required),
625 },
626 (2, InboundPayment) => {
627 (0, payment_hash, required),
628 },
629 (3, StaticInvoiceRequested) => {
630 (0, recipient_id, required),
631 (2, invoice_slot, required),
632 (4, path_absolute_expiry, required),
633 },
634);
635
636impl_writeable_tlv_based_enum!(AsyncPaymentsContext,
637 (0, OutboundPayment) => {
638 (0, payment_id, required),
639 },
640 (1, InboundPayment) => {
641 (4, path_absolute_expiry, required),
642 },
643 (2, OfferPaths) => {
644 (0, path_absolute_expiry, required),
645 (2, invoice_slot, required),
646 },
647 (3, StaticInvoicePersisted) => {
648 (0, offer_id, required),
649 (2, invoice_created_at, required),
650 },
651 (4, OfferPathsRequest) => {
652 (0, recipient_id, required),
653 (2, path_absolute_expiry, option),
654 },
655 (5, ServeStaticInvoice) => {
656 (0, recipient_id, required),
657 (2, invoice_slot, required),
658 (4, path_absolute_expiry, required),
659 },
660 (6, ReleaseHeldHtlc) => {
661 (0, intercept_id, required),
662 (2, prev_outbound_scid_alias, required),
663 (4, htlc_id, required),
664 },
665);
666
667/// Contains a simple nonce for use in a blinded path's context.
668///
669/// Such a context is required when receiving a [`DNSSECProof`] message.
670///
671/// [`DNSSECProof`]: crate::onion_message::dns_resolution::DNSSECProof
672#[derive(Clone, Debug, Hash, PartialEq, Eq)]
673pub struct DNSResolverContext {
674 /// A nonce which uniquely describes a DNS resolution, useful for looking up metadata about the
675 /// request.
676 pub nonce: [u8; 16],
677}
678
679impl_writeable_tlv_based!(DNSResolverContext, {
680 (0, nonce, required),
681});
682
683/// Represents the padding round off size (in bytes) that is used
684/// to pad message blinded path's [`BlindedHop`]
685pub(crate) const MESSAGE_PADDING_ROUND_OFF: usize = 100;
686
687/// The maximum number of dummy hops that can be added to a blinded path.
688/// This is to prevent paths from becoming too long and potentially causing
689/// issues with message processing or routing.
690pub const MAX_DUMMY_HOPS_COUNT: usize = 10;
691
692/// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`.
693pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
694 secp_ctx: &Secp256k1<T>, intermediate_nodes: &[MessageForwardNode],
695 recipient_node_id: PublicKey, dummy_hop_count: usize, context: MessageContext,
696 session_priv: &SecretKey, local_node_receive_key: ReceiveAuthKey,
697) -> Vec<BlindedHop> {
698 let dummy_count = cmp::min(dummy_hop_count, MAX_DUMMY_HOPS_COUNT);
699 let pks = intermediate_nodes
700 .iter()
701 .map(|node| (node.node_id, None))
702 .chain(
703 core::iter::repeat((recipient_node_id, Some(local_node_receive_key))).take(dummy_count),
704 )
705 .chain(core::iter::once((recipient_node_id, Some(local_node_receive_key))));
706 let is_compact = intermediate_nodes.iter().any(|node| node.short_channel_id.is_some());
707
708 let tlvs = pks
709 .clone()
710 .skip(1) // The first node's TLVs contains the next node's pubkey
711 .zip(intermediate_nodes.iter().map(|node| node.short_channel_id))
712 .map(|((pubkey, _), scid)| match scid {
713 Some(scid) => NextMessageHop::ShortChannelId(scid),
714 None => NextMessageHop::NodeId(pubkey),
715 })
716 .map(|next_hop| {
717 ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override: None })
718 })
719 .chain((0..dummy_count).map(|_| ControlTlvs::Dummy))
720 .chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { context: Some(context) })));
721
722 if is_compact {
723 let path = pks.zip(tlvs);
724 utils::construct_blinded_hops(secp_ctx, path, session_priv)
725 } else {
726 let path =
727 pks.zip(tlvs.map(|tlv| BlindedPathWithPadding {
728 tlvs: tlv,
729 round_off: MESSAGE_PADDING_ROUND_OFF,
730 }));
731 utils::construct_blinded_hops(secp_ctx, path, session_priv)
732 }
733}