Skip to content

Commit f32a93a

Browse files
committed
Allow blinded path diversification by expanding create_blinded_paths
- The current usage of `blinded_paths` is limited because `create_blinded_path` only returns a single `BlindedPath`. - This commit expands the functionality of `create_blinded_path` by allowing it to return multiple `BlindedPaths`, as determined by the new `count` parameter. - Additionally, this commit integrates this new capability throughout the codebase by: - Allowing multiple paths in offers and refund builders. - Sending Offers Response messages, such as `InvoiceRequest` (in `pay_for_offer`) and `Invoice` (in `request_refund_payment`), using multiple reply paths. - As a proof-of-concept, this commit increases the maximum count of `create_blinded_paths` to 10, enabling the generation of more reply paths. It also increases the number of `blinded_paths` used in offer and refund builders and responders to 5, demonstrating the usage of multiple reply paths.
1 parent c57b94a commit f32a93a

File tree

11 files changed

+93
-82
lines changed

11 files changed

+93
-82
lines changed

fuzz/src/chanmon_consistency.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl MessageRouter for FuzzRouter {
120120
}
121121

122122
fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
123-
&self, _recipient: PublicKey, _peers: Vec<ForwardNode>, _secp_ctx: &Secp256k1<T>,
123+
&self, _recipient: PublicKey, _peers: Vec<ForwardNode>, _secp_ctx: &Secp256k1<T>, count: usize,
124124
) -> Result<Vec<BlindedPath>, ()> {
125125
unreachable!()
126126
}

fuzz/src/full_stack.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ impl MessageRouter for FuzzRouter {
158158
}
159159

160160
fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
161-
&self, _recipient: PublicKey, _peers: Vec<ForwardNode>, _secp_ctx: &Secp256k1<T>,
161+
&self, _recipient: PublicKey, _peers: Vec<ForwardNode>, _secp_ctx: &Secp256k1<T>, count: usize,
162162
) -> Result<Vec<BlindedPath>, ()> {
163163
unreachable!()
164164
}

fuzz/src/onion_message.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ impl MessageRouter for TestMessageRouter {
8989
}
9090

9191
fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
92-
&self, _recipient: PublicKey, _peers: Vec<ForwardNode>, _secp_ctx: &Secp256k1<T>,
92+
&self, _recipient: PublicKey, _peers: Vec<ForwardNode>, _secp_ctx: &Secp256k1<T>, count: usize
9393
) -> Result<Vec<BlindedPath>, ()> {
9494
unreachable!()
9595
}

lightning/src/ln/channelmanager.rs

+49-32
Original file line numberDiff line numberDiff line change
@@ -8584,12 +8584,15 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
85848584
let entropy = &*$self.entropy_source;
85858585
let secp_ctx = &$self.secp_ctx;
85868586

8587-
let path = $self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
8587+
// TODO: Introduce a parameter to allow adding more paths to the created Offer.
8588+
let paths = $self.create_blinded_path(1)
8589+
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
8590+
85888591
let builder = OfferBuilder::deriving_signing_pubkey(
85898592
node_id, expanded_key, entropy, secp_ctx
85908593
)
85918594
.chain_hash($self.chain_hash)
8592-
.path(path);
8595+
.path(paths);
85938596

85948597
Ok(builder.into())
85958598
}
@@ -8651,13 +8654,16 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
86518654
let entropy = &*$self.entropy_source;
86528655
let secp_ctx = &$self.secp_ctx;
86538656

8654-
let path = $self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
8657+
// TODO: Introduce a parameter to allow adding more paths to the created Refund.
8658+
let paths = $self.create_blinded_path(1)
8659+
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
8660+
86558661
let builder = RefundBuilder::deriving_payer_id(
86568662
node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
86578663
)?
86588664
.chain_hash($self.chain_hash)
86598665
.absolute_expiry(absolute_expiry)
8660-
.path(path);
8666+
.path(paths);
86618667

86628668
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop($self);
86638669

@@ -8774,7 +8780,8 @@ where
87748780
Some(payer_note) => builder.payer_note(payer_note),
87758781
};
87768782
let invoice_request = builder.build_and_sign()?;
8777-
let reply_path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
8783+
let reply_paths = self.create_blinded_path(5)
8784+
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
87788785

87798786
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
87808787

@@ -8787,25 +8794,32 @@ where
87878794

87888795
let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
87898796
if !offer.paths().is_empty() {
8790-
// Send as many invoice requests as there are paths in the offer (with an upper bound).
8791-
// Using only one path could result in a failure if the path no longer exists. But only
8792-
// one invoice for a given payment id will be paid, even if more than one is received.
8797+
// Send as many invoice requests as there are paths in the offer using as many different
8798+
// reply_paths possible (with an upper bound).
8799+
// Using only one path could result in a failure if the path no longer exists.
8800+
// But only one invoice for a given payment ID will be paid, even if more than one is received.
87938801
const REQUEST_LIMIT: usize = 10;
8794-
for path in offer.paths().into_iter().take(REQUEST_LIMIT) {
8802+
reply_paths
8803+
.iter()
8804+
.flat_map(|reply_path| offer.paths().iter().map(|path| (path.clone(), reply_path.clone())))
8805+
.take(REQUEST_LIMIT)
8806+
.for_each(|(path, reply_path)| {
8807+
let message = new_pending_onion_message(
8808+
OffersMessage::InvoiceRequest(invoice_request.clone()),
8809+
Destination::BlindedPath(path.clone()),
8810+
Some(reply_path.clone()),
8811+
);
8812+
pending_offers_messages.push(message);
8813+
});
8814+
} else if let Some(signing_pubkey) = offer.signing_pubkey() {
8815+
for reply_path in reply_paths {
87958816
let message = new_pending_onion_message(
87968817
OffersMessage::InvoiceRequest(invoice_request.clone()),
8797-
Destination::BlindedPath(path.clone()),
8798-
Some(reply_path.clone()),
8818+
Destination::Node(signing_pubkey),
8819+
Some(reply_path),
87998820
);
88008821
pending_offers_messages.push(message);
88018822
}
8802-
} else if let Some(signing_pubkey) = offer.signing_pubkey() {
8803-
let message = new_pending_onion_message(
8804-
OffersMessage::InvoiceRequest(invoice_request),
8805-
Destination::Node(signing_pubkey),
8806-
Some(reply_path),
8807-
);
8808-
pending_offers_messages.push(message);
88098823
} else {
88108824
debug_assert!(false);
88118825
return Err(Bolt12SemanticError::MissingSigningPubkey);
@@ -8874,26 +8888,30 @@ where
88748888
)?;
88758889
let builder: InvoiceBuilder<DerivedSigningPubkey> = builder.into();
88768890
let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;
8877-
let reply_path = self.create_blinded_path()
8891+
let reply_paths = self.create_blinded_path(5)
88788892
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
88798893

88808894
let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
88818895
if refund.paths().is_empty() {
8882-
let message = new_pending_onion_message(
8883-
OffersMessage::Invoice(invoice.clone()),
8884-
Destination::Node(refund.payer_id()),
8885-
Some(reply_path),
8886-
);
8887-
pending_offers_messages.push(message);
8888-
} else {
8889-
for path in refund.paths() {
8896+
for reply_path in reply_paths {
88908897
let message = new_pending_onion_message(
88918898
OffersMessage::Invoice(invoice.clone()),
8892-
Destination::BlindedPath(path.clone()),
8893-
Some(reply_path.clone()),
8899+
Destination::Node(refund.payer_id()),
8900+
Some(reply_path),
88948901
);
88958902
pending_offers_messages.push(message);
88968903
}
8904+
} else {
8905+
for path in refund.paths() {
8906+
for reply_path in reply_paths.clone() {
8907+
let message = new_pending_onion_message(
8908+
OffersMessage::Invoice(invoice.clone()),
8909+
Destination::BlindedPath(path.clone()),
8910+
Some(reply_path.clone()),
8911+
);
8912+
pending_offers_messages.push(message);
8913+
}
8914+
}
88978915
}
88988916

88998917
Ok(invoice)
@@ -9003,7 +9021,7 @@ where
90039021
/// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`].
90049022
///
90059023
/// Errors if the `MessageRouter` errors or returns an empty `Vec`.
9006-
fn create_blinded_path(&self) -> Result<BlindedPath, ()> {
9024+
fn create_blinded_path(&self, count: usize) -> Result<Vec<BlindedPath>, ()> {
90079025
let recipient = self.get_our_node_id();
90089026
let secp_ctx = &self.secp_ctx;
90099027

@@ -9022,8 +9040,7 @@ where
90229040
.collect::<Vec<_>>();
90239041

90249042
self.router
9025-
.create_blinded_paths(recipient, peers, secp_ctx)
9026-
.and_then(|paths| paths.into_iter().next().ok_or(()))
9043+
.create_blinded_paths(recipient, peers, secp_ctx, count)
90279044
}
90289045

90299046
/// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to

lightning/src/ln/offers_tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ fn prefers_more_connected_nodes_in_blinded_paths() {
345345
.amount_msats(10_000_000)
346346
.build().unwrap();
347347
assert_ne!(offer.signing_pubkey(), Some(bob_id));
348+
348349
assert!(!offer.paths().is_empty());
349350
for path in offer.paths() {
350351
let introduction_node_id = resolve_introduction_node(david, &path);

lightning/src/offers/invoice.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -1833,14 +1833,14 @@ mod tests {
18331833
let entropy = FixedEntropy {};
18341834
let secp_ctx = Secp256k1::new();
18351835

1836-
let blinded_path = BlindedPath {
1836+
let blinded_path = vec![BlindedPath {
18371837
introduction_node: IntroductionNode::NodeId(pubkey(40)),
18381838
blinding_point: pubkey(41),
18391839
blinded_hops: vec![
18401840
BlindedHop { blinded_node_id: pubkey(42), encrypted_payload: vec![0; 43] },
18411841
BlindedHop { blinded_node_id: node_id, encrypted_payload: vec![0; 44] },
18421842
],
1843-
};
1843+
}];
18441844

18451845
#[cfg(c_bindings)]
18461846
use crate::offers::offer::OfferWithDerivedMetadataBuilder as OfferBuilder;
@@ -2409,8 +2409,7 @@ mod tests {
24092409
let invoice = OfferBuilder::new(recipient_pubkey())
24102410
.clear_signing_pubkey()
24112411
.amount_msats(1000)
2412-
.path(paths[0].clone())
2413-
.path(paths[1].clone())
2412+
.path(paths.clone())
24142413
.build().unwrap()
24152414
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
24162415
.build().unwrap()
@@ -2431,8 +2430,7 @@ mod tests {
24312430
let invoice = OfferBuilder::new(recipient_pubkey())
24322431
.clear_signing_pubkey()
24332432
.amount_msats(1000)
2434-
.path(paths[0].clone())
2435-
.path(paths[1].clone())
2433+
.path(paths.clone())
24362434
.build().unwrap()
24372435
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
24382436
.build().unwrap()

lightning/src/offers/offer.rs

+15-16
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
//! # #[cfg(feature = "std")]
3434
//! # use std::time::SystemTime;
3535
//! #
36-
//! # fn create_blinded_path() -> BlindedPath { unimplemented!() }
37-
//! # fn create_another_blinded_path() -> BlindedPath { unimplemented!() }
36+
//! # fn create_blinded_path(count: usize) -> Vec<BlindedPath> { unimplemented!() }
37+
//! # fn create_another_blinded_path(count: usize) -> Vec<BlindedPath> { unimplemented!() }
3838
//! #
3939
//! # #[cfg(feature = "std")]
4040
//! # fn build() -> Result<(), Bolt12ParseError> {
@@ -49,8 +49,8 @@
4949
//! .supported_quantity(Quantity::Unbounded)
5050
//! .absolute_expiry(expiration.duration_since(SystemTime::UNIX_EPOCH).unwrap())
5151
//! .issuer("Foo Bar".to_string())
52-
//! .path(create_blinded_path())
53-
//! .path(create_another_blinded_path())
52+
//! .path(create_blinded_path(1))
53+
//! .path(create_another_blinded_path(1))
5454
//! .build()?;
5555
//!
5656
//! // Encode as a bech32 string for use in a QR code.
@@ -344,8 +344,9 @@ macro_rules! offer_builder_methods { (
344344
///
345345
/// Successive calls to this method will add another blinded path. Caller is responsible for not
346346
/// adding duplicate paths.
347-
pub fn path($($self_mut)* $self: $self_type, path: BlindedPath) -> $return_type {
348-
$self.offer.paths.get_or_insert_with(Vec::new).push(path);
347+
pub fn path($($self_mut)* $self: $self_type, paths: Vec<BlindedPath>) -> $return_type {
348+
let entry = $self.offer.paths.get_or_insert_with(Vec::new);
349+
entry.extend(paths);
349350
$return_value
350351
}
351352

@@ -1316,14 +1317,14 @@ mod tests {
13161317
let entropy = FixedEntropy {};
13171318
let secp_ctx = Secp256k1::new();
13181319

1319-
let blinded_path = BlindedPath {
1320+
let blinded_path = vec![BlindedPath {
13201321
introduction_node: IntroductionNode::NodeId(pubkey(40)),
13211322
blinding_point: pubkey(41),
13221323
blinded_hops: vec![
13231324
BlindedHop { blinded_node_id: pubkey(42), encrypted_payload: vec![0; 43] },
13241325
BlindedHop { blinded_node_id: node_id, encrypted_payload: vec![0; 44] },
13251326
],
1326-
};
1327+
}];
13271328

13281329
#[cfg(c_bindings)]
13291330
use super::OfferWithDerivedMetadataBuilder as OfferBuilder;
@@ -1509,8 +1510,7 @@ mod tests {
15091510
];
15101511

15111512
let offer = OfferBuilder::new(pubkey(42))
1512-
.path(paths[0].clone())
1513-
.path(paths[1].clone())
1513+
.path(paths.clone())
15141514
.build()
15151515
.unwrap();
15161516
let tlv_stream = offer.as_tlv_stream();
@@ -1693,37 +1693,36 @@ mod tests {
16931693
#[test]
16941694
fn parses_offer_with_paths() {
16951695
let offer = OfferBuilder::new(pubkey(42))
1696-
.path(BlindedPath {
1696+
.path(vec![BlindedPath {
16971697
introduction_node: IntroductionNode::NodeId(pubkey(40)),
16981698
blinding_point: pubkey(41),
16991699
blinded_hops: vec![
17001700
BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
17011701
BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
17021702
],
1703-
})
1704-
.path(BlindedPath {
1703+
}, BlindedPath {
17051704
introduction_node: IntroductionNode::NodeId(pubkey(40)),
17061705
blinding_point: pubkey(41),
17071706
blinded_hops: vec![
17081707
BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
17091708
BlindedHop { blinded_node_id: pubkey(46), encrypted_payload: vec![0; 46] },
17101709
],
1711-
})
1710+
}])
17121711
.build()
17131712
.unwrap();
17141713
if let Err(e) = offer.to_string().parse::<Offer>() {
17151714
panic!("error parsing offer: {:?}", e);
17161715
}
17171716

17181717
let offer = OfferBuilder::new(pubkey(42))
1719-
.path(BlindedPath {
1718+
.path(vec![BlindedPath {
17201719
introduction_node: IntroductionNode::NodeId(pubkey(40)),
17211720
blinding_point: pubkey(41),
17221721
blinded_hops: vec![
17231722
BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
17241723
BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
17251724
],
1726-
})
1725+
}])
17271726
.clear_signing_pubkey()
17281727
.build()
17291728
.unwrap();

0 commit comments

Comments
 (0)