use crate::ln::onion_utils::INVALID_ONION_BLINDING;
use crate::ln::outbound_payment::Retry;
use crate::prelude::*;
-use crate::routing::router::{PaymentParameters, RouteParameters};
+use crate::routing::router::{Payee, PaymentParameters, RouteParameters};
use crate::util::config::UserConfig;
use crate::util::test_utils;
// The HTLC is successfully added to the inbound channel but fails receive checks in
// process_pending_htlc_forwards.
ProcessPendingHTLCsCheck,
+ // The HTLC violates the `PaymentConstraints` contained within the receiver's encrypted payload.
+ PaymentConstraints,
}
#[test]
do_multi_hop_receiver_fail(ReceiveCheckFail::ReceiveRequirements);
do_multi_hop_receiver_fail(ReceiveCheckFail::ChannelCheck);
do_multi_hop_receiver_fail(ReceiveCheckFail::ProcessPendingHTLCsCheck);
+ do_multi_hop_receiver_fail(ReceiveCheckFail::PaymentConstraints);
}
fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
Some(TEST_FINAL_CLTV as u16 - 2)
} else { None };
let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), final_cltv_delta);
- let route_params = get_blinded_route_parameters(amt_msat, payment_secret,
+ let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret,
nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2],
&chanmon_cfgs[2].keys_manager);
// Set the final CLTV expiry too low to trigger the failure in process_pending_htlc_forwards.
route.paths[0].blinded_tail.as_mut().map(|bt| bt.excess_final_cltv_expiry_delta = TEST_FINAL_CLTV - 2);
route
- } else {
+ } else if check == ReceiveCheckFail::PaymentConstraints {
+ // Create a blinded path where the receiver's encrypted payload has an htlc_minimum_msat that is
+ // violated by `amt_msat`, and stick it in the route_params without changing the corresponding
+ // BlindedPayInfo (to ensure pathfinding still succeeds).
+ let high_htlc_min_bp = {
+ let mut high_htlc_minimum_upd = chan_upd_1_2.clone();
+ high_htlc_minimum_upd.htlc_minimum_msat = amt_msat + 1000;
+ let high_htlc_min_params = get_blinded_route_parameters(amt_msat, payment_secret,
+ nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&high_htlc_minimum_upd],
+ &chanmon_cfgs[2].keys_manager);
+ if let Payee::Blinded { route_hints, .. } = high_htlc_min_params.payment_params.payee {
+ route_hints[0].1.clone()
+ } else { panic!() }
+ };
+ if let Payee::Blinded { ref mut route_hints, .. } = route_params.payment_params.payee {
+ route_hints[0].1 = high_htlc_min_bp;
+ } else { panic!() }
+ find_route(&nodes[0], &route_params).unwrap()
+ } else {
find_route(&nodes[0], &route_params).unwrap()
};
node_cfgs[0].router.expect_find_route(route_params.clone(), Ok(route.clone()));
expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!(nodes[2],
vec![HTLCDestination::FailedPayment { payment_hash }]);
check_added_monitors!(nodes[2], 1);
+ },
+ ReceiveCheckFail::PaymentConstraints => {
+ nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &payment_event_1_2.msgs[0]);
+ check_added_monitors!(nodes[2], 0);
+ do_commitment_signed_dance(&nodes[2], &nodes[1], &payment_event_1_2.commitment_msg, true, true);
}
}
pub msg: &'static str,
}
+fn check_blinded_payment_constraints(
+ amt_msat: u64, cltv_expiry: u32, constraints: &PaymentConstraints
+) -> Result<(), ()> {
+ if amt_msat < constraints.htlc_minimum_msat ||
+ cltv_expiry > constraints.max_cltv_expiry
+ { return Err(()) }
+ Ok(())
+}
+
fn check_blinded_forward(
inbound_amt_msat: u64, inbound_cltv_expiry: u32, payment_relay: &PaymentRelay,
payment_constraints: &PaymentConstraints, features: &BlindedHopFeatures
let outgoing_cltv_value = inbound_cltv_expiry.checked_sub(
payment_relay.cltv_expiry_delta as u32
).ok_or(())?;
- if inbound_amt_msat < payment_constraints.htlc_minimum_msat ||
- outgoing_cltv_value > payment_constraints.max_cltv_expiry
- { return Err(()) }
+ check_blinded_payment_constraints(inbound_amt_msat, outgoing_cltv_value, payment_constraints)?;
+
if features.requires_unknown_bits_from(&BlindedHopFeatures::empty()) { return Err(()) }
Ok((amt_to_forward, outgoing_cltv_value))
}
(payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata,
false),
msgs::InboundOnionPayload::BlindedReceive {
- amt_msat, total_msat, outgoing_cltv_value, payment_secret, intro_node_blinding_point, ..
+ amt_msat, total_msat, outgoing_cltv_value, payment_secret, intro_node_blinding_point,
+ payment_constraints, ..
} => {
+ check_blinded_payment_constraints(amt_msat, cltv_expiry, &payment_constraints)
+ .map_err(|()| {
+ InboundOnionErr {
+ err_code: INVALID_ONION_BLINDING,
+ err_data: vec![0; 32],
+ msg: "Amount or cltv_expiry violated blinded payment constraints",
+ }
+ })?;
let payment_data = msgs::FinalOnionHopData { payment_secret, total_msat };
(Some(payment_data), None, Vec::new(), amt_msat, outgoing_cltv_value, None,
intro_node_blinding_point.is_none())