Skip to content

Commit 5ddcb4d

Browse files
Fail blinded received HTLCs if they violate PaymentConstraints
.. contained within their encrypted payload.
1 parent 1550c11 commit 5ddcb4d

File tree

2 files changed

+50
-7
lines changed

2 files changed

+50
-7
lines changed

lightning/src/ln/blinded_payment_tests.rs

+29-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::ln::onion_utils;
2222
use crate::ln::onion_utils::INVALID_ONION_BLINDING;
2323
use crate::ln::outbound_payment::Retry;
2424
use crate::prelude::*;
25-
use crate::routing::router::{PaymentParameters, RouteParameters};
25+
use crate::routing::router::{Payee, PaymentParameters, RouteParameters};
2626
use crate::util::config::UserConfig;
2727
use crate::util::test_utils;
2828

@@ -505,6 +505,8 @@ enum ReceiveCheckFail {
505505
// The HTLC is successfully added to the inbound channel but fails receive checks in
506506
// process_pending_htlc_forwards.
507507
ProcessPendingHTLCsCheck,
508+
// The HTLC violates the `PaymentConstraints` contained within the receiver's encrypted payload.
509+
PaymentConstraints,
508510
}
509511

510512
#[test]
@@ -514,6 +516,7 @@ fn multi_hop_receiver_fail() {
514516
do_multi_hop_receiver_fail(ReceiveCheckFail::ReceiveRequirements);
515517
do_multi_hop_receiver_fail(ReceiveCheckFail::ChannelCheck);
516518
do_multi_hop_receiver_fail(ReceiveCheckFail::ProcessPendingHTLCsCheck);
519+
do_multi_hop_receiver_fail(ReceiveCheckFail::PaymentConstraints);
517520
}
518521

519522
fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
@@ -539,7 +542,7 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
539542
Some(TEST_FINAL_CLTV as u16 - 2)
540543
} else { None };
541544
let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), final_cltv_delta);
542-
let route_params = get_blinded_route_parameters(amt_msat, payment_secret,
545+
let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret,
543546
nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2],
544547
&chanmon_cfgs[2].keys_manager);
545548

@@ -548,7 +551,25 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
548551
// Set the final CLTV expiry too low to trigger the failure in process_pending_htlc_forwards.
549552
route.paths[0].blinded_tail.as_mut().map(|bt| bt.excess_final_cltv_expiry_delta = TEST_FINAL_CLTV - 2);
550553
route
551-
} else {
554+
} else if check == ReceiveCheckFail::PaymentConstraints {
555+
// Create a blinded path where the receiver's encrypted payload has an htlc_minimum_msat that is
556+
// violated by `amt_msat`, and stick it in the route_params without changing the corresponding
557+
// BlindedPayInfo (to ensure pathfinding still succeeds).
558+
let high_htlc_min_bp = {
559+
let mut high_htlc_minimum_upd = chan_upd_1_2.clone();
560+
high_htlc_minimum_upd.htlc_minimum_msat = amt_msat + 1000;
561+
let high_htlc_min_params = get_blinded_route_parameters(amt_msat, payment_secret,
562+
nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&high_htlc_minimum_upd],
563+
&chanmon_cfgs[2].keys_manager);
564+
if let Payee::Blinded { route_hints, .. } = high_htlc_min_params.payment_params.payee {
565+
route_hints[0].1.clone()
566+
} else { panic!() }
567+
};
568+
if let Payee::Blinded { ref mut route_hints, .. } = route_params.payment_params.payee {
569+
route_hints[0].1 = high_htlc_min_bp;
570+
} else { panic!() }
571+
find_route(&nodes[0], &route_params).unwrap()
572+
} else {
552573
find_route(&nodes[0], &route_params).unwrap()
553574
};
554575
node_cfgs[0].router.expect_find_route(route_params.clone(), Ok(route.clone()));
@@ -643,6 +664,11 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
643664
expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!(nodes[2],
644665
vec![HTLCDestination::FailedPayment { payment_hash }]);
645666
check_added_monitors!(nodes[2], 1);
667+
},
668+
ReceiveCheckFail::PaymentConstraints => {
669+
nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &payment_event_1_2.msgs[0]);
670+
check_added_monitors!(nodes[2], 0);
671+
do_commitment_signed_dance(&nodes[2], &nodes[1], &payment_event_1_2.commitment_msg, true, true);
646672
}
647673
}
648674

lightning/src/ln/onion_payment.rs

+21-4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ pub struct InboundOnionErr {
3232
pub msg: &'static str,
3333
}
3434

35+
fn check_blinded_payment_constraints(
36+
amt_msat: u64, cltv_expiry: u32, constraints: &PaymentConstraints
37+
) -> Result<(), ()> {
38+
if amt_msat < constraints.htlc_minimum_msat ||
39+
cltv_expiry > constraints.max_cltv_expiry
40+
{ return Err(()) }
41+
Ok(())
42+
}
43+
3544
fn check_blinded_forward(
3645
inbound_amt_msat: u64, inbound_cltv_expiry: u32, payment_relay: &PaymentRelay,
3746
payment_constraints: &PaymentConstraints, features: &BlindedHopFeatures
@@ -42,9 +51,8 @@ fn check_blinded_forward(
4251
let outgoing_cltv_value = inbound_cltv_expiry.checked_sub(
4352
payment_relay.cltv_expiry_delta as u32
4453
).ok_or(())?;
45-
if inbound_amt_msat < payment_constraints.htlc_minimum_msat ||
46-
outgoing_cltv_value > payment_constraints.max_cltv_expiry
47-
{ return Err(()) }
54+
check_blinded_payment_constraints(inbound_amt_msat, outgoing_cltv_value, payment_constraints)?;
55+
4856
if features.requires_unknown_bits_from(&BlindedHopFeatures::empty()) { return Err(()) }
4957
Ok((amt_to_forward, outgoing_cltv_value))
5058
}
@@ -121,8 +129,17 @@ pub(super) fn create_recv_pending_htlc_info(
121129
(payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata,
122130
false),
123131
msgs::InboundOnionPayload::BlindedReceive {
124-
amt_msat, total_msat, outgoing_cltv_value, payment_secret, intro_node_blinding_point, ..
132+
amt_msat, total_msat, outgoing_cltv_value, payment_secret, intro_node_blinding_point,
133+
payment_constraints, ..
125134
} => {
135+
check_blinded_payment_constraints(amt_msat, cltv_expiry, &payment_constraints)
136+
.map_err(|()| {
137+
InboundOnionErr {
138+
err_code: INVALID_ONION_BLINDING,
139+
err_data: vec![0; 32],
140+
msg: "Amount or cltv_expiry violated blinded payment constraints",
141+
}
142+
})?;
126143
let payment_data = msgs::FinalOnionHopData { payment_secret, total_msat };
127144
let is_blinded = if intro_node_blinding_point.is_some() { false } else { true };
128145
(Some(payment_data), None, Vec::new(), amt_msat, outgoing_cltv_value, None, is_blinded)

0 commit comments

Comments
 (0)