Skip to content

Commit 53f2e2e

Browse files
authored
Merge pull request #2812 from valentinewallace/2023-12-blinded-forwarding
Complete route blinding support
2 parents ea5de93 + aae39b4 commit 53f2e2e

File tree

6 files changed

+59
-19
lines changed

6 files changed

+59
-19
lines changed

lightning/src/ln/blinded_payment_tests.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,29 @@ fn two_hop_blinded_path_success() {
490490
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
491491
}
492492

493+
#[test]
494+
fn three_hop_blinded_path_success() {
495+
let chanmon_cfgs = create_chanmon_cfgs(5);
496+
let node_cfgs = create_node_cfgs(5, &chanmon_cfgs);
497+
let node_chanmgrs = create_node_chanmgrs(5, &node_cfgs, &[None, None, None, None, None]);
498+
let mut nodes = create_network(5, &node_cfgs, &node_chanmgrs);
499+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
500+
create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
501+
let chan_upd_2_3 = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0).0.contents;
502+
let chan_upd_3_4 = create_announced_chan_between_nodes_with_value(&nodes, 3, 4, 1_000_000, 0).0.contents;
503+
504+
let amt_msat = 5000;
505+
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[4], Some(amt_msat), None);
506+
let route_params = get_blinded_route_parameters(amt_msat, payment_secret,
507+
nodes.iter().skip(2).map(|n| n.node.get_our_node_id()).collect(),
508+
&[&chan_upd_2_3, &chan_upd_3_4], &chanmon_cfgs[4].keys_manager);
509+
510+
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
511+
check_added_monitors(&nodes[0], 1);
512+
pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3], &nodes[4]]], amt_msat, payment_hash, payment_secret);
513+
claim_payment(&nodes[0], &[&nodes[1], &nodes[2], &nodes[3], &nodes[4]], payment_preimage);
514+
}
515+
493516
#[derive(PartialEq)]
494517
enum ReceiveCheckFail {
495518
// The recipient fails the payment upon `PaymentClaimable`.
@@ -537,19 +560,20 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
537560
};
538561

539562
let amt_msat = 5000;
540-
let final_cltv_delta = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck {
563+
let excess_final_cltv_delta_opt = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck {
541564
// Set the final CLTV expiry too low to trigger the failure in process_pending_htlc_forwards.
542565
Some(TEST_FINAL_CLTV as u16 - 2)
543566
} else { None };
544-
let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), final_cltv_delta);
567+
let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), excess_final_cltv_delta_opt);
545568
let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret,
546569
nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2],
547570
&chanmon_cfgs[2].keys_manager);
548571

549572
let route = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck {
550573
let mut route = get_route(&nodes[0], &route_params).unwrap();
551574
// Set the final CLTV expiry too low to trigger the failure in process_pending_htlc_forwards.
552-
route.paths[0].blinded_tail.as_mut().map(|bt| bt.excess_final_cltv_expiry_delta = TEST_FINAL_CLTV - 2);
575+
route.paths[0].hops.last_mut().map(|h| h.cltv_expiry_delta += excess_final_cltv_delta_opt.unwrap() as u32);
576+
route.paths[0].blinded_tail.as_mut().map(|bt| bt.excess_final_cltv_expiry_delta = excess_final_cltv_delta_opt.unwrap() as u32);
553577
route
554578
} else if check == ReceiveCheckFail::PaymentConstraints {
555579
// Create a blinded path where the receiver's encrypted payload has an htlc_minimum_msat that is
@@ -657,6 +681,7 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
657681
commitment_signed_dance!(nodes[2], nodes[1], (), false, true, false, false);
658682
},
659683
ReceiveCheckFail::ProcessPendingHTLCsCheck => {
684+
assert_eq!(payment_event_1_2.msgs[0].cltv_expiry, nodes[0].best_block_info().1 + 1 + excess_final_cltv_delta_opt.unwrap() as u32);
660685
nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &payment_event_1_2.msgs[0]);
661686
check_added_monitors!(nodes[2], 0);
662687
do_commitment_signed_dance(&nodes[2], &nodes[1], &payment_event_1_2.commitment_msg, true, true);

lightning/src/ln/channelmanager.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -202,15 +202,16 @@ pub struct BlindedForward {
202202
/// onion payload if we're the introduction node. Useful for calculating the next hop's
203203
/// [`msgs::UpdateAddHTLC::blinding_point`].
204204
pub inbound_blinding_point: PublicKey,
205-
// Another field will be added here when we support forwarding as a non-intro node.
205+
/// If needed, this determines how this HTLC should be failed backwards, based on whether we are
206+
/// the introduction node.
207+
pub failure: BlindedFailure,
206208
}
207209

208210
impl PendingHTLCRouting {
209211
// Used to override the onion failure code and data if the HTLC is blinded.
210212
fn blinded_failure(&self) -> Option<BlindedFailure> {
211-
// TODO: needs update when we support forwarding blinded HTLCs as non-intro node
212213
match self {
213-
Self::Forward { blinded: Some(_), .. } => Some(BlindedFailure::FromIntroductionNode),
214+
Self::Forward { blinded: Some(BlindedForward { failure, .. }), .. } => Some(*failure),
214215
Self::Receive { requires_blinded_error: true, .. } => Some(BlindedFailure::FromBlindedNode),
215216
_ => None,
216217
}
@@ -305,10 +306,15 @@ pub(super) enum HTLCForwardInfo {
305306
},
306307
}
307308

308-
// Used for failing blinded HTLCs backwards correctly.
309+
/// Whether this blinded HTLC is being failed backwards by the introduction node or a blinded node,
310+
/// which determines the failure message that should be used.
309311
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
310-
enum BlindedFailure {
312+
pub enum BlindedFailure {
313+
/// This HTLC is being failed backwards by the introduction node, and thus should be failed with
314+
/// [`msgs::UpdateFailHTLC`] and error code `0x8000|0x4000|24`.
311315
FromIntroductionNode,
316+
/// This HTLC is being failed backwards by a blinded node within the path, and thus should be
317+
/// failed with [`msgs::UpdateFailMalformedHTLC`] and error code `0x8000|0x4000|24`.
312318
FromBlindedNode,
313319
}
314320

@@ -3025,8 +3031,9 @@ where
30253031

30263032
let is_intro_node_forward = match next_hop {
30273033
onion_utils::Hop::Forward {
3028-
// TODO: update this when we support blinded forwarding as non-intro node
3029-
next_hop_data: msgs::InboundOnionPayload::BlindedForward { .. }, ..
3034+
next_hop_data: msgs::InboundOnionPayload::BlindedForward {
3035+
intro_node_blinding_point: Some(_), ..
3036+
}, ..
30303037
} => true,
30313038
_ => false,
30323039
};
@@ -4371,7 +4378,7 @@ where
43714378
incoming_packet_shared_secret: incoming_shared_secret,
43724379
// Phantom payments are only PendingHTLCRouting::Receive.
43734380
phantom_shared_secret: None,
4374-
blinded_failure: blinded.map(|_| BlindedFailure::FromIntroductionNode),
4381+
blinded_failure: blinded.map(|b| b.failure),
43754382
});
43764383
let next_blinding_point = blinded.and_then(|b| {
43774384
let encrypted_tlvs_ss = self.node_signer.ecdh(
@@ -9351,6 +9358,7 @@ pub fn provided_init_features(config: &UserConfig) -> InitFeatures {
93519358
features.set_channel_type_optional();
93529359
features.set_scid_privacy_optional();
93539360
features.set_zero_conf_optional();
9361+
features.set_route_blinding_optional();
93549362
if config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx {
93559363
features.set_anchors_zero_fee_htlc_tx_optional();
93569364
}
@@ -9496,6 +9504,7 @@ impl_writeable_tlv_based!(PhantomRouteHints, {
94969504

94979505
impl_writeable_tlv_based!(BlindedForward, {
94989506
(0, inbound_blinding_point, required),
9507+
(1, failure, (default_value, BlindedFailure::FromIntroductionNode)),
94999508
});
95009509

95019510
impl_writeable_tlv_based_enum!(PendingHTLCRouting,

lightning/src/ln/msgs.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,7 +1714,7 @@ mod fuzzy_internal_msgs {
17141714
payment_relay: PaymentRelay,
17151715
payment_constraints: PaymentConstraints,
17161716
features: BlindedHopFeatures,
1717-
intro_node_blinding_point: PublicKey,
1717+
intro_node_blinding_point: Option<PublicKey>,
17181718
},
17191719
BlindedReceive {
17201720
sender_intended_htlc_amt_msat: u64,
@@ -2394,7 +2394,7 @@ impl<NS: Deref> ReadableArgs<(Option<PublicKey>, &NS)> for InboundOnionPayload w
23942394
payment_relay,
23952395
payment_constraints,
23962396
features,
2397-
intro_node_blinding_point: intro_node_blinding_point.ok_or(DecodeError::InvalidValue)?,
2397+
intro_node_blinding_point,
23982398
})
23992399
},
24002400
ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Receive(ReceiveTlvs {

lightning/src/ln/onion_payment.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::blinded_path;
1212
use crate::blinded_path::payment::{PaymentConstraints, PaymentRelay};
1313
use crate::chain::channelmonitor::{HTLC_FAIL_BACK_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS};
1414
use crate::ln::PaymentHash;
15-
use crate::ln::channelmanager::{BlindedForward, CLTV_FAR_FAR_AWAY, HTLCFailureMsg, MIN_CLTV_EXPIRY_DELTA, PendingHTLCInfo, PendingHTLCRouting};
15+
use crate::ln::channelmanager::{BlindedFailure, BlindedForward, CLTV_FAR_FAR_AWAY, HTLCFailureMsg, MIN_CLTV_EXPIRY_DELTA, PendingHTLCInfo, PendingHTLCRouting};
1616
use crate::ln::features::BlindedHopFeatures;
1717
use crate::ln::msgs;
1818
use crate::ln::onion_utils;
@@ -73,7 +73,7 @@ pub(super) fn create_fwd_pending_htlc_info(
7373
};
7474

7575
let (
76-
short_channel_id, amt_to_forward, outgoing_cltv_value, inbound_blinding_point
76+
short_channel_id, amt_to_forward, outgoing_cltv_value, intro_node_blinding_point
7777
) = match hop_data {
7878
msgs::InboundOnionPayload::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } =>
7979
(short_channel_id, amt_to_forward, outgoing_cltv_value, None),
@@ -91,7 +91,7 @@ pub(super) fn create_fwd_pending_htlc_info(
9191
err_data: vec![0; 32],
9292
}
9393
})?;
94-
(short_channel_id, amt_to_forward, outgoing_cltv_value, Some(intro_node_blinding_point))
94+
(short_channel_id, amt_to_forward, outgoing_cltv_value, intro_node_blinding_point)
9595
},
9696
msgs::InboundOnionPayload::Receive { .. } | msgs::InboundOnionPayload::BlindedReceive { .. } =>
9797
return Err(InboundHTLCErr {
@@ -105,7 +105,13 @@ pub(super) fn create_fwd_pending_htlc_info(
105105
routing: PendingHTLCRouting::Forward {
106106
onion_packet: outgoing_packet,
107107
short_channel_id,
108-
blinded: inbound_blinding_point.map(|bp| BlindedForward { inbound_blinding_point: bp }),
108+
blinded: intro_node_blinding_point.or(msg.blinding_point)
109+
.map(|bp| BlindedForward {
110+
inbound_blinding_point: bp,
111+
failure: intro_node_blinding_point
112+
.map(|_| BlindedFailure::FromIntroductionNode)
113+
.unwrap_or(BlindedFailure::FromBlindedNode),
114+
}),
109115
},
110116
payment_hash: msg.payment_hash,
111117
incoming_shared_secret: shared_secret,

lightning/src/ln/onion_utils.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,10 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o
188188
for (i, blinded_hop) in hops.iter().enumerate() {
189189
if i == hops.len() - 1 {
190190
cur_value_msat += final_value_msat;
191-
cur_cltv += excess_final_cltv_expiry_delta;
192191
res.push(msgs::OutboundOnionPayload::BlindedReceive {
193192
sender_intended_htlc_amt_msat: *final_value_msat,
194193
total_msat,
195-
cltv_expiry_height: cltv,
194+
cltv_expiry_height: cur_cltv + excess_final_cltv_expiry_delta,
196195
encrypted_tlvs: blinded_hop.encrypted_payload.clone(),
197196
intro_node_blinding_point: blinding_point.take(),
198197
});

lightning/src/ln/peer_handler.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ impl ChannelMessageHandler for ErroringMessageHandler {
305305
features.set_channel_type_optional();
306306
features.set_scid_privacy_optional();
307307
features.set_zero_conf_optional();
308+
features.set_route_blinding_optional();
308309
features
309310
}
310311

0 commit comments

Comments
 (0)