Skip to content

Commit 00daca8

Browse files
committed
Update PaymentPath, and ClaimAlongRoute arguments
Upcoming commits will need the ability to specify whether a blinded path contains dummy hops. This change adds that support to the testing framework ahead of time, so later tests can express dummy-hop scenarios explicitly.
1 parent fa07199 commit 00daca8

File tree

3 files changed

+85
-23
lines changed

3 files changed

+85
-23
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7144,14 +7144,10 @@ where
71447144
// (e.g. via mem::swap) because other threads may have enqueued new HTLCs
71457145
// meanwhile; merging preserves everything safely.
71467146
if !dummy_update_add_htlcs.is_empty() {
7147-
let mut decode_update_add_htlc_source =
7148-
self.decode_update_add_htlcs.lock().unwrap();
7147+
let mut decode_update_add_htlc_source = self.decode_update_add_htlcs.lock().unwrap();
71497148

71507149
for (incoming_scid_alias, htlcs) in dummy_update_add_htlcs.into_iter() {
7151-
decode_update_add_htlc_source
7152-
.entry(incoming_scid_alias)
7153-
.or_default()
7154-
.extend(htlcs);
7150+
decode_update_add_htlc_source.entry(incoming_scid_alias).or_default().extend(htlcs);
71557151
}
71567152
}
71577153

lightning/src/ln/functional_test_utils.rs

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3435,6 +3435,7 @@ fn fail_payment_along_path<'a, 'b, 'c>(expected_path: &[&Node<'a, 'b, 'c>]) {
34353435
pub struct PassAlongPathArgs<'a, 'b, 'c, 'd> {
34363436
pub origin_node: &'a Node<'b, 'c, 'd>,
34373437
pub expected_path: &'a [&'a Node<'b, 'c, 'd>],
3438+
pub dummy_hop_override: Option<usize>,
34383439
pub recv_value: u64,
34393440
pub payment_hash: PaymentHash,
34403441
pub payment_secret: Option<PaymentSecret>,
@@ -3456,6 +3457,7 @@ impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> {
34563457
Self {
34573458
origin_node,
34583459
expected_path,
3460+
dummy_hop_override: None,
34593461
recv_value,
34603462
payment_hash,
34613463
payment_secret: None,
@@ -3503,12 +3505,17 @@ impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> {
35033505
self.expected_failure = Some(failure);
35043506
self
35053507
}
3508+
pub fn with_dummy_override(mut self, dummy_override: usize) -> Self {
3509+
self.dummy_hop_override = Some(dummy_override);
3510+
self
3511+
}
35063512
}
35073513

35083514
pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option<Event> {
35093515
let PassAlongPathArgs {
35103516
origin_node,
35113517
expected_path,
3518+
dummy_hop_override,
35123519
recv_value,
35133520
payment_hash: our_payment_hash,
35143521
payment_secret: our_payment_secret,
@@ -3755,6 +3762,29 @@ pub struct ClaimAlongRouteArgs<'a, 'b, 'c, 'd> {
37553762
pub origin_node: &'a Node<'b, 'c, 'd>,
37563763
pub expected_paths: &'a [&'a [&'a Node<'b, 'c, 'd>]],
37573764
pub expected_extra_fees: Vec<u32>,
3765+
/// A one-off adjustment used only in tests to account for an existing
3766+
/// fee-handling trade-off in LDK.
3767+
///
3768+
/// When the payer is the introduction node of a blinded path, LDK does not
3769+
/// subtract the forward fee for the `payer -> next_hop` channel
3770+
/// (see [`BlindedPaymentPath::advance_path_by_one`]). This keeps the fee
3771+
/// logic simpler at the cost of a small, intentional overpayment.
3772+
///
3773+
/// In the simple two-hop case (payer as introduction node → payee),
3774+
/// this overpayment has historically been avoided by simply not charging
3775+
/// the payer the forward fee, since the payer knows there is only
3776+
/// a single hop after them.
3777+
///
3778+
/// However, with the introduction of dummy hops in LDK v0.3, even a
3779+
/// two-node real path (payer as introduction node → payee) may appear as a
3780+
/// multi-hop blinded path. This makes the existing overpayment surface in
3781+
/// tests.
3782+
///
3783+
/// Until the fee-handling trade-off is revisited, this field allows tests
3784+
/// to compensate for that expected difference.
3785+
///
3786+
/// [`BlindedPaymentPath::advance_path_by_one`]: crate::blinded_path::payment::BlindedPaymentPath::advance_path_by_one
3787+
pub expected_extra_total_fees_msat: u64,
37583788
pub expected_min_htlc_overpay: Vec<u32>,
37593789
pub skip_last: bool,
37603790
pub payment_preimage: PaymentPreimage,
@@ -3778,6 +3808,7 @@ impl<'a, 'b, 'c, 'd> ClaimAlongRouteArgs<'a, 'b, 'c, 'd> {
37783808
origin_node,
37793809
expected_paths,
37803810
expected_extra_fees: vec![0; expected_paths.len()],
3811+
expected_extra_total_fees_msat: 0,
37813812
expected_min_htlc_overpay: vec![0; expected_paths.len()],
37823813
skip_last: false,
37833814
payment_preimage,
@@ -3793,6 +3824,10 @@ impl<'a, 'b, 'c, 'd> ClaimAlongRouteArgs<'a, 'b, 'c, 'd> {
37933824
self.expected_extra_fees = extra_fees;
37943825
self
37953826
}
3827+
pub fn with_expected_extra_total_fees_msat(mut self, extra_total_fees: u64) -> Self {
3828+
self.expected_extra_total_fees_msat = extra_total_fees;
3829+
self
3830+
}
37963831
pub fn with_expected_min_htlc_overpay(mut self, extra_fees: Vec<u32>) -> Self {
37973832
self.expected_min_htlc_overpay = extra_fees;
37983833
self
@@ -3817,6 +3852,7 @@ pub fn pass_claimed_payment_along_route(args: ClaimAlongRouteArgs) -> u64 {
38173852
payment_preimage: our_payment_preimage,
38183853
allow_1_msat_fee_overpay,
38193854
custom_tlvs,
3855+
..
38203856
} = args;
38213857
let claim_event = expected_paths[0].last().unwrap().node.get_and_clear_pending_events();
38223858
assert_eq!(claim_event.len(), 1);
@@ -4049,13 +4085,21 @@ pub fn pass_claimed_payment_along_route(args: ClaimAlongRouteArgs) -> u64 {
40494085

40504086
expected_total_fee_msat
40514087
}
4088+
40524089
pub fn claim_payment_along_route(
40534090
args: ClaimAlongRouteArgs,
40544091
) -> (Option<PaidBolt12Invoice>, Vec<Event>) {
4055-
let origin_node = args.origin_node;
4056-
let payment_preimage = args.payment_preimage;
4057-
let skip_last = args.skip_last;
4058-
let expected_total_fee_msat = do_claim_payment_along_route(args);
4092+
let ClaimAlongRouteArgs {
4093+
origin_node,
4094+
payment_preimage,
4095+
skip_last,
4096+
expected_extra_total_fees_msat,
4097+
..
4098+
} = args;
4099+
4100+
let expected_total_fee_msat =
4101+
do_claim_payment_along_route(args) + expected_extra_total_fees_msat;
4102+
40594103
if !skip_last {
40604104
expect_payment_sent!(origin_node, payment_preimage, Some(expected_total_fee_msat))
40614105
} else {

lightning/src/ln/offers_tests.rs

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,20 @@ fn route_bolt12_payment<'a, 'b, 'c>(
185185
fn claim_bolt12_payment<'a, 'b, 'c>(
186186
node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>], expected_payment_context: PaymentContext, invoice: &Bolt12Invoice
187187
) {
188-
let recipient = &path[path.len() - 1];
188+
claim_bolt12_payment_with_extra_fees(
189+
node,
190+
path,
191+
expected_payment_context,
192+
invoice,
193+
None,
194+
)
195+
}
196+
197+
fn claim_bolt12_payment_with_extra_fees<'a, 'b, 'c>(
198+
node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>], expected_payment_context: PaymentContext, invoice: &Bolt12Invoice,
199+
expected_extra_fees_msat: Option<u64>,
200+
) {
201+
let recipient = path.last().expect("Empty path?");
189202
let payment_purpose = match get_event!(recipient, Event::PaymentClaimable) {
190203
Event::PaymentClaimable { purpose, .. } => purpose,
191204
_ => panic!("No Event::PaymentClaimable"),
@@ -194,20 +207,29 @@ fn claim_bolt12_payment<'a, 'b, 'c>(
194207
Some(preimage) => preimage,
195208
None => panic!("No preimage in Event::PaymentClaimable"),
196209
};
197-
match payment_purpose {
198-
PaymentPurpose::Bolt12OfferPayment { payment_context, .. } => {
199-
assert_eq!(PaymentContext::Bolt12Offer(payment_context), expected_payment_context);
200-
},
201-
PaymentPurpose::Bolt12RefundPayment { payment_context, .. } => {
202-
assert_eq!(PaymentContext::Bolt12Refund(payment_context), expected_payment_context);
203-
},
210+
let context = match payment_purpose {
211+
PaymentPurpose::Bolt12OfferPayment { payment_context, .. } =>
212+
PaymentContext::Bolt12Offer(payment_context),
213+
PaymentPurpose::Bolt12RefundPayment { payment_context, .. } =>
214+
PaymentContext::Bolt12Refund(payment_context),
204215
_ => panic!("Unexpected payment purpose: {:?}", payment_purpose),
205-
}
206-
if let Some(inv) = claim_payment(node, path, payment_preimage) {
207-
assert_eq!(inv, PaidBolt12Invoice::Bolt12Invoice(invoice.to_owned()));
208-
} else {
209-
panic!("Expected PaidInvoice::Bolt12Invoice");
210216
};
217+
218+
assert_eq!(context, expected_payment_context);
219+
220+
let expected_paths = [path];
221+
let mut args = ClaimAlongRouteArgs::new(
222+
node,
223+
&expected_paths,
224+
payment_preimage,
225+
);
226+
227+
if let Some(extra) = expected_extra_fees_msat {
228+
args = args.with_expected_extra_total_fees_msat(extra);
229+
}
230+
231+
let (inv, _) = claim_payment_along_route(args);
232+
assert_eq!(inv, Some(PaidBolt12Invoice::Bolt12Invoice(invoice.clone())));
211233
}
212234

213235
fn extract_offer_nonce<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, message: &OnionMessage) -> Nonce {

0 commit comments

Comments
 (0)