Skip to content

Commit

Permalink
Allow create_blinded_paths functions to accept MessageContext as inpu…
Browse files Browse the repository at this point in the history
…t field

- Enabled `create_blinded_paths` to accept `MessageContext` TLVs as
  an input field.
- `MessageContext` is intended to be sent along with the `reply_path`
   to the counterparty.
- Added `MessageContext` in the `create_blinded_paths` flow, optionally
  appending it within the `reply_path`.
- Updated tests to verify the new feature.
  • Loading branch information
shaavan committed Jul 9, 2024
1 parent fbe9dfa commit 42c096c
Show file tree
Hide file tree
Showing 14 changed files with 163 additions and 86 deletions.
4 changes: 3 additions & 1 deletion fuzz/src/chanmon_consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hashes::Hash as TraitImport;
use bitcoin::WPubkeyHash;

use lightning::blinded_path::message::MessageContext;
use lightning::blinded_path::payment::ReceiveTlvs;
use lightning::blinded_path::BlindedPath;
use lightning::chain;
Expand Down Expand Up @@ -138,7 +139,8 @@ impl MessageRouter for FuzzRouter {
}

fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, _recipient: PublicKey, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
&self, _recipient: PublicKey, _context: MessageContext, _peers: Vec<PublicKey>,
_secp_ctx: &Secp256k1<T>,
) -> Result<Vec<BlindedPath>, ()> {
unreachable!()
}
Expand Down
4 changes: 3 additions & 1 deletion fuzz/src/full_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hashes::Hash as _;
use bitcoin::WPubkeyHash;

use lightning::blinded_path::message::MessageContext;
use lightning::blinded_path::payment::ReceiveTlvs;
use lightning::blinded_path::BlindedPath;
use lightning::chain;
Expand Down Expand Up @@ -175,7 +176,8 @@ impl MessageRouter for FuzzRouter {
}

fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, _recipient: PublicKey, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
&self, _recipient: PublicKey, _context: MessageContext, _peers: Vec<PublicKey>,
_secp_ctx: &Secp256k1<T>,
) -> Result<Vec<BlindedPath>, ()> {
unreachable!()
}
Expand Down
22 changes: 17 additions & 5 deletions fuzz/src/invoice_request_deser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use crate::utils::test_logger;
use bitcoin::secp256k1::{self, Keypair, Parity, PublicKey, Secp256k1, SecretKey};
use core::convert::TryFrom;
use lightning::blinded_path::message::ForwardNode;
use lightning::blinded_path::message::{ForwardNode, MessageContext, OffersContext};
use lightning::blinded_path::BlindedPath;
use lightning::ln::features::BlindedHopFeatures;
use lightning::ln::PaymentHash;
Expand Down Expand Up @@ -87,10 +87,22 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
],
];
let paths = vec![
BlindedPath::new_for_message(&intermediate_nodes[0], pubkey(42), &entropy_source, secp_ctx)
.unwrap(),
BlindedPath::new_for_message(&intermediate_nodes[1], pubkey(42), &entropy_source, secp_ctx)
.unwrap(),
BlindedPath::new_for_message(
&intermediate_nodes[0],
pubkey(42),
MessageContext::Offers(OffersContext::Unknown {}),
&entropy_source,
secp_ctx,
)
.unwrap(),
BlindedPath::new_for_message(
&intermediate_nodes[1],
pubkey(42),
MessageContext::Offers(OffersContext::Unknown {}),
&entropy_source,
secp_ctx,
)
.unwrap(),
];

let payinfo = vec![
Expand Down
3 changes: 2 additions & 1 deletion fuzz/src/onion_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ impl MessageRouter for TestMessageRouter {
}

fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, _recipient: PublicKey, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
&self, _recipient: PublicKey, _context: MessageContext, _peers: Vec<PublicKey>,
_secp_ctx: &Secp256k1<T>,
) -> Result<Vec<BlindedPath>, ()> {
unreachable!()
}
Expand Down
22 changes: 17 additions & 5 deletions fuzz/src/refund_deser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use crate::utils::test_logger;
use bitcoin::secp256k1::{self, Keypair, PublicKey, Secp256k1, SecretKey};
use core::convert::TryFrom;
use lightning::blinded_path::message::ForwardNode;
use lightning::blinded_path::message::{ForwardNode, MessageContext, OffersContext};
use lightning::blinded_path::BlindedPath;
use lightning::ln::features::BlindedHopFeatures;
use lightning::ln::PaymentHash;
Expand Down Expand Up @@ -76,10 +76,22 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
],
];
let paths = vec![
BlindedPath::new_for_message(&intermediate_nodes[0], pubkey(42), &entropy_source, secp_ctx)
.unwrap(),
BlindedPath::new_for_message(&intermediate_nodes[1], pubkey(42), &entropy_source, secp_ctx)
.unwrap(),
BlindedPath::new_for_message(
&intermediate_nodes[0],
pubkey(42),
MessageContext::Offers(OffersContext::Unknown {}),
&entropy_source,
secp_ctx,
)
.unwrap(),
BlindedPath::new_for_message(
&intermediate_nodes[1],
pubkey(42),
MessageContext::Offers(OffersContext::Unknown {}),
&entropy_source,
secp_ctx,
)
.unwrap(),
];

let payinfo = vec![
Expand Down
5 changes: 3 additions & 2 deletions lightning/src/blinded_path/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
//! [`BlindedPath`]: crate::blinded_path::BlindedPath
use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};

#[allow(unused_imports)]
use crate::prelude::*;

Expand Down Expand Up @@ -133,7 +134,7 @@ impl_writeable_tlv_based_enum!(OffersContext,
/// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`.
pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
secp_ctx: &Secp256k1<T>, intermediate_nodes: &[ForwardNode], recipient_node_id: PublicKey,
session_priv: &SecretKey
context: MessageContext, session_priv: &SecretKey
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
let pks = intermediate_nodes.iter().map(|node| &node.node_id)
.chain(core::iter::once(&recipient_node_id));
Expand All @@ -145,7 +146,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
None => NextMessageHop::NodeId(*pubkey),
})
.map(|next_hop| ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override: None }))
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { context: None })));
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs{ context: Some(context) })));

utils::construct_blinded_hops(secp_ctx, pks, tlvs, session_priv)
}
Expand Down
10 changes: 6 additions & 4 deletions lightning/src/blinded_path/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod message;
pub(crate) mod utils;

use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
use message::MessageContext;
use core::ops::Deref;

use crate::ln::msgs::DecodeError;
Expand Down Expand Up @@ -123,9 +124,9 @@ pub struct BlindedHop {
impl BlindedPath {
/// Create a one-hop blinded path for a message.
pub fn one_hop_for_message<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
recipient_node_id: PublicKey, entropy_source: ES, secp_ctx: &Secp256k1<T>
recipient_node_id: PublicKey, context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1<T>
) -> Result<Self, ()> where ES::Target: EntropySource {
Self::new_for_message(&[], recipient_node_id, entropy_source, secp_ctx)
Self::new_for_message(&[], recipient_node_id, context, entropy_source, secp_ctx)
}

/// Create a blinded path for an onion message, to be forwarded along `node_pks`. The last node
Expand All @@ -135,7 +136,7 @@ impl BlindedPath {
// TODO: make all payloads the same size with padding + add dummy hops
pub fn new_for_message<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
intermediate_nodes: &[message::ForwardNode], recipient_node_id: PublicKey,
entropy_source: ES, secp_ctx: &Secp256k1<T>
context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1<T>
) -> Result<Self, ()> where ES::Target: EntropySource {
let introduction_node = IntroductionNode::NodeId(
intermediate_nodes.first().map_or(recipient_node_id, |n| n.node_id)
Expand All @@ -147,7 +148,8 @@ impl BlindedPath {
introduction_node,
blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
blinded_hops: message::blinded_hops(
secp_ctx, intermediate_nodes, recipient_node_id, &blinding_secret,
secp_ctx, intermediate_nodes, recipient_node_id,
context, &blinding_secret,
).map_err(|_| ())?,
})
}
Expand Down
27 changes: 15 additions & 12 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use bitcoin::secp256k1::{SecretKey,PublicKey};
use bitcoin::secp256k1::Secp256k1;
use bitcoin::{secp256k1, Sequence};

use crate::blinded_path::message::OffersContext;
use crate::blinded_path::message::{MessageContext, OffersContext};
use crate::blinded_path::{BlindedPath, NodeIdLookUp};
use crate::blinded_path::message::ForwardNode;
use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentConstraints, PaymentContext, ReceiveTlvs};
Expand Down Expand Up @@ -8377,7 +8377,7 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
let entropy = &*$self.entropy_source;
let secp_ctx = &$self.secp_ctx;

let path = $self.create_blinded_path_using_absolute_expiry(absolute_expiry)
let path = $self.create_blinded_path_using_absolute_expiry(OffersContext::Unknown {}, absolute_expiry)
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
let builder = OfferBuilder::deriving_signing_pubkey(
node_id, expanded_key, entropy, secp_ctx
Expand Down Expand Up @@ -8449,7 +8449,8 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
let entropy = &*$self.entropy_source;
let secp_ctx = &$self.secp_ctx;

let path = $self.create_blinded_path_using_absolute_expiry(Some(absolute_expiry))
let context = OffersContext::OutboundPayment { payment_id };
let path = $self.create_blinded_path_using_absolute_expiry(context, Some(absolute_expiry))
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
let builder = RefundBuilder::deriving_payer_id(
node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
Expand Down Expand Up @@ -8572,7 +8573,9 @@ where
Some(payer_note) => builder.payer_note(payer_note),
};
let invoice_request = builder.build_and_sign()?;
let reply_path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;

let context = OffersContext::OutboundPayment { payment_id };
let reply_path = self.create_blinded_path(context).map_err(|_| Bolt12SemanticError::MissingPaths)?;

let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);

Expand Down Expand Up @@ -8672,7 +8675,7 @@ where
)?;
let builder: InvoiceBuilder<DerivedSigningPubkey> = builder.into();
let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;
let reply_path = self.create_blinded_path()
let reply_path = self.create_blinded_path(OffersContext::Unknown {})
.map_err(|_| Bolt12SemanticError::MissingPaths)?;

let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
Expand Down Expand Up @@ -8805,15 +8808,15 @@ where
/// respectively, based on the given `absolute_expiry` as seconds since the Unix epoch. See
/// [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`].
fn create_blinded_path_using_absolute_expiry(
&self, absolute_expiry: Option<Duration>
&self, context: OffersContext, absolute_expiry: Option<Duration>,
) -> Result<BlindedPath, ()> {
let now = self.duration_since_epoch();
let max_short_lived_absolute_expiry = now.saturating_add(MAX_SHORT_LIVED_RELATIVE_EXPIRY);

if absolute_expiry.unwrap_or(Duration::MAX) <= max_short_lived_absolute_expiry {
self.create_compact_blinded_path()
self.create_compact_blinded_path(context)
} else {
self.create_blinded_path()
self.create_blinded_path(context)
}
}

Expand All @@ -8833,7 +8836,7 @@ where
/// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`].
///
/// Errors if the `MessageRouter` errors or returns an empty `Vec`.
fn create_blinded_path(&self) -> Result<BlindedPath, ()> {
fn create_blinded_path(&self, context: OffersContext) -> Result<BlindedPath, ()> {
let recipient = self.get_our_node_id();
let secp_ctx = &self.secp_ctx;

Expand All @@ -8846,14 +8849,14 @@ where
.collect::<Vec<_>>();

self.router
.create_blinded_paths(recipient, peers, secp_ctx)
.create_blinded_paths(recipient, MessageContext::Offers(context), peers, secp_ctx)
.and_then(|paths| paths.into_iter().next().ok_or(()))
}

/// Creates a blinded path by delegating to [`MessageRouter::create_compact_blinded_paths`].
///
/// Errors if the `MessageRouter` errors or returns an empty `Vec`.
fn create_compact_blinded_path(&self) -> Result<BlindedPath, ()> {
fn create_compact_blinded_path(&self, context: OffersContext) -> Result<BlindedPath, ()> {
let recipient = self.get_our_node_id();
let secp_ctx = &self.secp_ctx;

Expand All @@ -8873,7 +8876,7 @@ where
.collect::<Vec<_>>();

self.router
.create_compact_blinded_paths(recipient, peers, secp_ctx)
.create_compact_blinded_paths(recipient, MessageContext::Offers(context), peers, secp_ctx)
.and_then(|paths| paths.into_iter().next().ok_or(()))
}

Expand Down
11 changes: 10 additions & 1 deletion lightning/src/ln/max_payment_path_len_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use bitcoin::secp256k1::{Secp256k1, PublicKey};
use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
use crate::blinded_path::payment::{PaymentConstraints, PaymentContext, ReceiveTlvs};
use crate::events::MessageSendEventsProvider;
use crate::events::{Event, MessageSendEventsProvider};
use crate::ln::PaymentSecret;
use crate::ln::blinded_payment_tests::get_blinded_route_parameters;
use crate::ln::channelmanager::PaymentId;
Expand Down Expand Up @@ -389,4 +389,13 @@ fn bolt12_invoice_too_large_blinded_paths() {
nodes[0].onion_messenger.handle_onion_message(&nodes[1].node.get_our_node_id(), &invoice_om);
// TODO: assert on the invoice error once we support replying to invoice OMs with failure info
nodes[0].logger.assert_log_contains("lightning::ln::channelmanager", "Failed paying invoice: OnionPacketSizeExceeded", 1);

let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
Event::PaymentFailed { payment_id: id, .. } => {
assert_eq!(id, payment_id)
},
_ => panic!("Unexpected event"),
}
}
8 changes: 8 additions & 0 deletions lightning/src/ln/offers_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1412,6 +1412,14 @@ fn fails_sending_invoice_without_blinded_payment_paths_for_offer() {

let invoice_error = extract_invoice_error(david, &onion_message);
assert_eq!(invoice_error, InvoiceError::from(Bolt12SemanticError::MissingPaths));

// Confirm that david drops this failed payment from his pending outbound payments.
match get_event!(david, Event::InvoiceRequestFailed) {
Event::InvoiceRequestFailed { payment_id: pay_id } => {
assert_eq!(pay_id, payment_id)
},
_ => panic!("No Event::InvoiceRequestFailed"),
}
}

#[test]
Expand Down
Loading

0 comments on commit 42c096c

Please sign in to comment.