Skip to content

Commit 2122528

Browse files
authored
Merge pull request #497 from tnull/2025-03-payment-details-nits
Add `counterparty_skimmed_fee_msat` field to `PaymentKind::Bolt11Jit`
2 parents 9c02f23 + 13c6d0e commit 2122528

File tree

5 files changed

+78
-4
lines changed

5 files changed

+78
-4
lines changed

Diff for: bindings/ldk_node.udl

+1-1
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ interface ClosureReason {
387387
interface PaymentKind {
388388
Onchain(Txid txid, ConfirmationStatus status);
389389
Bolt11(PaymentHash hash, PaymentPreimage? preimage, PaymentSecret? secret);
390-
Bolt11Jit(PaymentHash hash, PaymentPreimage? preimage, PaymentSecret? secret, LSPFeeLimits lsp_fee_limits);
390+
Bolt11Jit(PaymentHash hash, PaymentPreimage? preimage, PaymentSecret? secret, u64? counterparty_skimmed_fee_msat, LSPFeeLimits lsp_fee_limits);
391391
Bolt12Offer(PaymentHash? hash, PaymentPreimage? preimage, PaymentSecret? secret, OfferId offer_id, UntrustedString? payer_note, u64? quantity);
392392
Bolt12Refund(PaymentHash? hash, PaymentPreimage? preimage, PaymentSecret? secret, UntrustedString? payer_note, u64? quantity);
393393
Spontaneous(PaymentHash hash, PaymentPreimage? preimage);

Diff for: src/event.rs

+20
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,26 @@ where
659659
};
660660
}
661661

662+
// If the LSP skimmed anything, update our stored payment.
663+
if counterparty_skimmed_fee_msat > 0 {
664+
match info.kind {
665+
PaymentKind::Bolt11Jit { .. } => {
666+
let update = PaymentDetailsUpdate {
667+
counterparty_skimmed_fee_msat: Some(Some(counterparty_skimmed_fee_msat)),
668+
..PaymentDetailsUpdate::new(payment_id)
669+
};
670+
match self.payment_store.update(&update) {
671+
Ok(_) => (),
672+
Err(e) => {
673+
log_error!(self.logger, "Failed to access payment store: {}", e);
674+
return Err(ReplayEvent());
675+
},
676+
};
677+
}
678+
_ => debug_assert!(false, "We only expect the counterparty to get away with withholding fees for JIT payments."),
679+
}
680+
}
681+
662682
// If this is known by the store but ChannelManager doesn't know the preimage,
663683
// the payment has been registered via `_for_hash` variants and needs to be manually claimed via
664684
// user interaction.

Diff for: src/payment/bolt11.rs

+1
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,7 @@ impl Bolt11Payment {
664664
hash: payment_hash,
665665
preimage,
666666
secret: Some(payment_secret.clone()),
667+
counterparty_skimmed_fee_msat: None,
667668
lsp_fee_limits,
668669
};
669670
let payment = PaymentDetails::new(

Diff for: src/payment/store.rs

+47-2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub struct PaymentDetails {
4141
/// The kind of the payment.
4242
pub kind: PaymentKind,
4343
/// The amount transferred.
44+
///
45+
/// Will be `None` for variable-amount payments until we receive them.
4446
pub amount_msat: Option<u64>,
4547
/// The fees that were paid for this payment.
4648
///
@@ -165,6 +167,18 @@ impl PaymentDetails {
165167
update_if_necessary!(self.fee_paid_msat, fee_paid_msat_opt);
166168
}
167169

170+
if let Some(skimmed_fee_msat) = update.counterparty_skimmed_fee_msat {
171+
match self.kind {
172+
PaymentKind::Bolt11Jit { ref mut counterparty_skimmed_fee_msat, .. } => {
173+
update_if_necessary!(*counterparty_skimmed_fee_msat, skimmed_fee_msat);
174+
},
175+
_ => debug_assert!(
176+
false,
177+
"We should only ever override counterparty_skimmed_fee_msat for JIT payments"
178+
),
179+
}
180+
}
181+
168182
if let Some(status) = update.status {
169183
update_if_necessary!(self.status, status);
170184
}
@@ -257,7 +271,14 @@ impl Readable for PaymentDetails {
257271

258272
if secret.is_some() {
259273
if let Some(lsp_fee_limits) = lsp_fee_limits {
260-
PaymentKind::Bolt11Jit { hash, preimage, secret, lsp_fee_limits }
274+
let counterparty_skimmed_fee_msat = None;
275+
PaymentKind::Bolt11Jit {
276+
hash,
277+
preimage,
278+
secret,
279+
counterparty_skimmed_fee_msat,
280+
lsp_fee_limits,
281+
}
261282
} else {
262283
PaymentKind::Bolt11 { hash, preimage, secret }
263284
}
@@ -346,6 +367,12 @@ pub enum PaymentKind {
346367
preimage: Option<PaymentPreimage>,
347368
/// The secret used by the payment.
348369
secret: Option<PaymentSecret>,
370+
/// The value, in thousands of a satoshi, that was deducted from this payment as an extra
371+
/// fee taken by our channel counterparty.
372+
///
373+
/// Will only be `Some` once we received the payment. Will always be `None` for LDK Node
374+
/// v0.4 and prior.
375+
counterparty_skimmed_fee_msat: Option<u64>,
349376
/// Limits applying to how much fee we allow an LSP to deduct from the payment amount.
350377
///
351378
/// Allowing them to deduct this fee from the first inbound payment will pay for the LSP's
@@ -423,6 +450,7 @@ impl_writeable_tlv_based_enum!(PaymentKind,
423450
},
424451
(4, Bolt11Jit) => {
425452
(0, hash, required),
453+
(1, counterparty_skimmed_fee_msat, option),
426454
(2, preimage, option),
427455
(4, secret, option),
428456
(6, lsp_fee_limits, required),
@@ -501,6 +529,7 @@ pub(crate) struct PaymentDetailsUpdate {
501529
pub secret: Option<Option<PaymentSecret>>,
502530
pub amount_msat: Option<Option<u64>>,
503531
pub fee_paid_msat: Option<Option<u64>>,
532+
pub counterparty_skimmed_fee_msat: Option<Option<u64>>,
504533
pub direction: Option<PaymentDirection>,
505534
pub status: Option<PaymentStatus>,
506535
pub confirmation_status: Option<ConfirmationStatus>,
@@ -515,6 +544,7 @@ impl PaymentDetailsUpdate {
515544
secret: None,
516545
amount_msat: None,
517546
fee_paid_msat: None,
547+
counterparty_skimmed_fee_msat: None,
518548
direction: None,
519549
status: None,
520550
confirmation_status: None,
@@ -538,13 +568,21 @@ impl From<&PaymentDetails> for PaymentDetailsUpdate {
538568
_ => None,
539569
};
540570

571+
let counterparty_skimmed_fee_msat = match value.kind {
572+
PaymentKind::Bolt11Jit { counterparty_skimmed_fee_msat, .. } => {
573+
Some(counterparty_skimmed_fee_msat)
574+
},
575+
_ => None,
576+
};
577+
541578
Self {
542579
id: value.id,
543580
hash: Some(hash),
544581
preimage: Some(preimage),
545582
secret: Some(secret),
546583
amount_msat: Some(value.amount_msat),
547584
fee_paid_msat: Some(value.fee_paid_msat),
585+
counterparty_skimmed_fee_msat,
548586
direction: Some(value.direction),
549587
status: Some(value.status),
550588
confirmation_status,
@@ -841,10 +879,17 @@ mod tests {
841879
);
842880

843881
match bolt11_jit_decoded.kind {
844-
PaymentKind::Bolt11Jit { hash: h, preimage: p, secret: s, lsp_fee_limits: l } => {
882+
PaymentKind::Bolt11Jit {
883+
hash: h,
884+
preimage: p,
885+
secret: s,
886+
counterparty_skimmed_fee_msat: c,
887+
lsp_fee_limits: l,
888+
} => {
845889
assert_eq!(hash, h);
846890
assert_eq!(preimage, p);
847891
assert_eq!(secret, s);
892+
assert_eq!(None, c);
848893
assert_eq!(lsp_fee_limits, Some(l));
849894
},
850895
_ => {

Diff for: tests/integration_tests_rust.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1137,7 +1137,15 @@ fn lsps2_client_service_integration() {
11371137
let service_fee_msat = (jit_amount_msat * channel_opening_fee_ppm as u64) / 1_000_000;
11381138
let expected_received_amount_msat = jit_amount_msat - service_fee_msat;
11391139
expect_payment_successful_event!(payer_node, Some(payment_id), None);
1140-
expect_payment_received_event!(client_node, expected_received_amount_msat);
1140+
let client_payment_id =
1141+
expect_payment_received_event!(client_node, expected_received_amount_msat).unwrap();
1142+
let client_payment = client_node.payment(&client_payment_id).unwrap();
1143+
match client_payment.kind {
1144+
PaymentKind::Bolt11Jit { counterparty_skimmed_fee_msat, .. } => {
1145+
assert_eq!(counterparty_skimmed_fee_msat, Some(service_fee_msat));
1146+
},
1147+
_ => panic!("Unexpected payment kind"),
1148+
}
11411149

11421150
let expected_channel_overprovisioning_msat =
11431151
(expected_received_amount_msat * channel_over_provisioning_ppm as u64) / 1_000_000;

0 commit comments

Comments
 (0)