Skip to content

Commit c8e7993

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 2701bc5 commit c8e7993

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
@@ -124,7 +124,7 @@ impl MessageRouter for FuzzRouter {
124124
}
125125

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

fuzz/src/full_stack.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ impl MessageRouter for FuzzRouter {
162162
}
163163

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

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
@@ -8270,12 +8270,15 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
82708270
let entropy = &*$self.entropy_source;
82718271
let secp_ctx = &$self.secp_ctx;
82728272

8273-
let path = $self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
8273+
// TODO: Introduce a parameter to allow adding more paths to the created Offer.
8274+
let paths = $self.create_blinded_path(1)
8275+
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
8276+
82748277
let builder = OfferBuilder::deriving_signing_pubkey(
82758278
node_id, expanded_key, entropy, secp_ctx
82768279
)
82778280
.chain_hash($self.chain_hash)
8278-
.path(path);
8281+
.path(paths);
82798282

82808283
Ok(builder.into())
82818284
}
@@ -8337,13 +8340,16 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
83378340
let entropy = &*$self.entropy_source;
83388341
let secp_ctx = &$self.secp_ctx;
83398342

8340-
let path = $self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
8343+
// TODO: Introduce a parameter to allow adding more paths to the created Refund.
8344+
let paths = $self.create_blinded_path(1)
8345+
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
8346+
83418347
let builder = RefundBuilder::deriving_payer_id(
83428348
node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
83438349
)?
83448350
.chain_hash($self.chain_hash)
83458351
.absolute_expiry(absolute_expiry)
8346-
.path(path);
8352+
.path(paths);
83478353

83488354
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop($self);
83498355

@@ -8460,7 +8466,8 @@ where
84608466
Some(payer_note) => builder.payer_note(payer_note),
84618467
};
84628468
let invoice_request = builder.build_and_sign()?;
8463-
let reply_path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
8469+
let reply_paths = self.create_blinded_path(5)
8470+
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
84648471

84658472
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
84668473

@@ -8473,25 +8480,32 @@ where
84738480

84748481
let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
84758482
if !offer.paths().is_empty() {
8476-
// Send as many invoice requests as there are paths in the offer (with an upper bound).
8477-
// Using only one path could result in a failure if the path no longer exists. But only
8478-
// one invoice for a given payment id will be paid, even if more than one is received.
8483+
// Send as many invoice requests as there are paths in the offer using as many different
8484+
// reply_paths possible (with an upper bound).
8485+
// Using only one path could result in a failure if the path no longer exists.
8486+
// But only one invoice for a given payment ID will be paid, even if more than one is received.
84798487
const REQUEST_LIMIT: usize = 10;
8480-
for path in offer.paths().into_iter().take(REQUEST_LIMIT) {
8488+
reply_paths
8489+
.iter()
8490+
.flat_map(|reply_path| offer.paths().iter().map(|path| (path.clone(), reply_path.clone())))
8491+
.take(REQUEST_LIMIT)
8492+
.for_each(|(path, reply_path)| {
8493+
let message = new_pending_onion_message(
8494+
OffersMessage::InvoiceRequest(invoice_request.clone()),
8495+
Destination::BlindedPath(path.clone()),
8496+
Some(reply_path.clone()),
8497+
);
8498+
pending_offers_messages.push(message);
8499+
});
8500+
} else if let Some(signing_pubkey) = offer.signing_pubkey() {
8501+
for reply_path in reply_paths {
84818502
let message = new_pending_onion_message(
84828503
OffersMessage::InvoiceRequest(invoice_request.clone()),
8483-
Destination::BlindedPath(path.clone()),
8484-
Some(reply_path.clone()),
8504+
Destination::Node(signing_pubkey),
8505+
Some(reply_path),
84858506
);
84868507
pending_offers_messages.push(message);
84878508
}
8488-
} else if let Some(signing_pubkey) = offer.signing_pubkey() {
8489-
let message = new_pending_onion_message(
8490-
OffersMessage::InvoiceRequest(invoice_request),
8491-
Destination::Node(signing_pubkey),
8492-
Some(reply_path),
8493-
);
8494-
pending_offers_messages.push(message);
84958509
} else {
84968510
debug_assert!(false);
84978511
return Err(Bolt12SemanticError::MissingSigningPubkey);
@@ -8560,26 +8574,30 @@ where
85608574
)?;
85618575
let builder: InvoiceBuilder<DerivedSigningPubkey> = builder.into();
85628576
let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;
8563-
let reply_path = self.create_blinded_path()
8577+
let reply_paths = self.create_blinded_path(5)
85648578
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
85658579

85668580
let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
85678581
if refund.paths().is_empty() {
8568-
let message = new_pending_onion_message(
8569-
OffersMessage::Invoice(invoice.clone()),
8570-
Destination::Node(refund.payer_id()),
8571-
Some(reply_path),
8572-
);
8573-
pending_offers_messages.push(message);
8574-
} else {
8575-
for path in refund.paths() {
8582+
for reply_path in reply_paths {
85768583
let message = new_pending_onion_message(
85778584
OffersMessage::Invoice(invoice.clone()),
8578-
Destination::BlindedPath(path.clone()),
8579-
Some(reply_path.clone()),
8585+
Destination::Node(refund.payer_id()),
8586+
Some(reply_path),
85808587
);
85818588
pending_offers_messages.push(message);
85828589
}
8590+
} else {
8591+
for path in refund.paths() {
8592+
for reply_path in reply_paths.clone() {
8593+
let message = new_pending_onion_message(
8594+
OffersMessage::Invoice(invoice.clone()),
8595+
Destination::BlindedPath(path.clone()),
8596+
Some(reply_path.clone()),
8597+
);
8598+
pending_offers_messages.push(message);
8599+
}
8600+
}
85838601
}
85848602

85858603
Ok(invoice)
@@ -8689,7 +8707,7 @@ where
86898707
/// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`].
86908708
///
86918709
/// Errors if the `MessageRouter` errors or returns an empty `Vec`.
8692-
fn create_blinded_path(&self) -> Result<BlindedPath, ()> {
8710+
fn create_blinded_path(&self, count: usize) -> Result<Vec<BlindedPath>, ()> {
86938711
let recipient = self.get_our_node_id();
86948712
let secp_ctx = &self.secp_ctx;
86958713

@@ -8708,8 +8726,7 @@ where
87088726
.collect::<Vec<_>>();
87098727

87108728
self.router
8711-
.create_blinded_paths(recipient, peers, secp_ctx)
8712-
.and_then(|paths| paths.into_iter().next().ok_or(()))
8729+
.create_blinded_paths(recipient, peers, secp_ctx, count)
87138730
}
87148731

87158732
/// 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

@@ -1323,14 +1324,14 @@ mod tests {
13231324
let entropy = FixedEntropy {};
13241325
let secp_ctx = Secp256k1::new();
13251326

1326-
let blinded_path = BlindedPath {
1327+
let blinded_path = vec![BlindedPath {
13271328
introduction_node: IntroductionNode::NodeId(pubkey(40)),
13281329
blinding_point: pubkey(41),
13291330
blinded_hops: vec![
13301331
BlindedHop { blinded_node_id: pubkey(42), encrypted_payload: vec![0; 43] },
13311332
BlindedHop { blinded_node_id: node_id, encrypted_payload: vec![0; 44] },
13321333
],
1333-
};
1334+
}];
13341335

13351336
#[cfg(c_bindings)]
13361337
use super::OfferWithDerivedMetadataBuilder as OfferBuilder;
@@ -1516,8 +1517,7 @@ mod tests {
15161517
];
15171518

15181519
let offer = OfferBuilder::new(pubkey(42))
1519-
.path(paths[0].clone())
1520-
.path(paths[1].clone())
1520+
.path(paths.clone())
15211521
.build()
15221522
.unwrap();
15231523
let tlv_stream = offer.as_tlv_stream();
@@ -1700,37 +1700,36 @@ mod tests {
17001700
#[test]
17011701
fn parses_offer_with_paths() {
17021702
let offer = OfferBuilder::new(pubkey(42))
1703-
.path(BlindedPath {
1703+
.path(vec![BlindedPath {
17041704
introduction_node: IntroductionNode::NodeId(pubkey(40)),
17051705
blinding_point: pubkey(41),
17061706
blinded_hops: vec![
17071707
BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
17081708
BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
17091709
],
1710-
})
1711-
.path(BlindedPath {
1710+
}, BlindedPath {
17121711
introduction_node: IntroductionNode::NodeId(pubkey(40)),
17131712
blinding_point: pubkey(41),
17141713
blinded_hops: vec![
17151714
BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
17161715
BlindedHop { blinded_node_id: pubkey(46), encrypted_payload: vec![0; 46] },
17171716
],
1718-
})
1717+
}])
17191718
.build()
17201719
.unwrap();
17211720
if let Err(e) = offer.to_string().parse::<Offer>() {
17221721
panic!("error parsing offer: {:?}", e);
17231722
}
17241723

17251724
let offer = OfferBuilder::new(pubkey(42))
1726-
.path(BlindedPath {
1725+
.path(vec![BlindedPath {
17271726
introduction_node: IntroductionNode::NodeId(pubkey(40)),
17281727
blinding_point: pubkey(41),
17291728
blinded_hops: vec![
17301729
BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
17311730
BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
17321731
],
1733-
})
1732+
}])
17341733
.clear_signing_pubkey()
17351734
.build()
17361735
.unwrap();

0 commit comments

Comments
 (0)