Add fees value for recent payments
What changed, and why it matters
This commit adds optional fee fields to LDK's recent-payment tracking structures so users can see routing fees for pending and completed payments. It is a straightforward feature/data exposure change with no security-relevant behavior visible in the diff.
No security action required; review as normal feature code.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch extends RecentPaymentDetails::Pending with pending_fee_msat: Option<u64> and RecentPaymentDetails::Fulfilled with fee_paid_msat: Option<u64>. It threads these values through PendingOutboundPayment::Retryable, Abandoned, and Fulfilled, copies the pending fee into the fulfilled state when a payment succeeds, and updates serialization (TLV) and tests. No logic changes affect authorization, cryptographic safety, channel state, or payment handling.
Changed components
lightning/src/ln/channelmanager.rslightning/src/ln/outbound_payment.rslightning/src/ln/payment_tests.rsInspect captured patch +32 / −6
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 6398613..2c9f298 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -3291,9 +3291,13 @@ pub enum RecentPaymentDetails {
/// Hash of the payment that is currently being sent but has yet to be fulfilled or
/// abandoned.
payment_hash: PaymentHash,
- /// Total amount (in msat, excluding fees) across all paths for this payment,
+ /// Total amount (excluding fees) across all paths for this payment,
/// not just the amount currently inflight.
total_msat: u64,
+ /// Total routing fees of the HTLCs currently in-flight for this payment.
+ ///
+ /// `None` for payments serialized by LDK versions prior to 0.0.103.
+ pending_fee_msat: Option<u64>,
/// Whether this payment is a liquidity probe.
is_probe: bool,
},
@@ -3310,6 +3314,13 @@ pub enum RecentPaymentDetails {
/// Hash of the payment that was claimed. `None` for serializations of [`ChannelManager`]
/// made before LDK version 0.0.104.
payment_hash: Option<PaymentHash>,
+ /// Total routing fees paid for this payment, as also reported via the `fee_paid_msat`
+ /// field of [`Event::PaymentSent`].
+ ///
+ /// `None` for payments serialized by LDK versions prior to 0.3.0.
+ ///
+ /// [`Event::PaymentSent`]: events::Event::PaymentSent
+ fee_paid_msat: Option<u64>,
},
/// After a payment's retries are exhausted per the provided [`Retry`], or it is explicitly
/// abandoned via [`ChannelManager::abandon_payment`], it is marked as abandoned until all
@@ -4118,12 +4129,13 @@ impl<
PendingOutboundPayment::StaticInvoiceReceived { .. } => {
Some(RecentPaymentDetails::AwaitingInvoice { payment_id: *payment_id })
},
- PendingOutboundPayment::Retryable { payment_hash, total_msat, .. } => {
+ PendingOutboundPayment::Retryable { payment_hash, total_msat, pending_fee_msat, .. } => {
let is_probe = outbound_payment::payment_is_probe(payment_hash, payment_id, self.probing_cookie_secret);
Some(RecentPaymentDetails::Pending {
payment_id: *payment_id,
payment_hash: *payment_hash,
total_msat: *total_msat,
+ pending_fee_msat: *pending_fee_msat,
is_probe,
})
},
@@ -4135,8 +4147,12 @@ impl<
is_probe,
})
},
- PendingOutboundPayment::Fulfilled { payment_hash, .. } => {
- Some(RecentPaymentDetails::Fulfilled { payment_id: *payment_id, payment_hash: *payment_hash })
+ PendingOutboundPayment::Fulfilled { payment_hash, fee_paid_msat, .. } => {
+ Some(RecentPaymentDetails::Fulfilled {
+ payment_id: *payment_id,
+ payment_hash: *payment_hash,
+ fee_paid_msat: *fee_paid_msat,
+ })
},
PendingOutboundPayment::Legacy { .. } => None
})
diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs
index 04e8003..6805d9c 100644
--- a/lightning/src/ln/outbound_payment.rs
+++ b/lightning/src/ln/outbound_payment.rs
@@ -152,6 +152,8 @@ pub(crate) enum PendingOutboundPayment {
timer_ticks_without_htlcs: u8,
/// The total payment amount across all paths, used to be able to issue `PaymentSent`.
total_msat: Option<u64>,
+ /// Total routing fees paid, as reported in `PaymentSent::fee_paid_msat`.
+ fee_paid_msat: Option<u64>,
},
/// When we've decided to give up retrying a payment, we mark it as abandoned so we can eventually
/// generate a `PaymentFailed` event when all HTLCs have irrevocably failed.
@@ -256,6 +258,7 @@ impl PendingOutboundPayment {
match self {
PendingOutboundPayment::Retryable { pending_fee_msat, .. } => pending_fee_msat.clone(),
PendingOutboundPayment::Abandoned { pending_fee_msat, .. } => pending_fee_msat.clone(),
+ PendingOutboundPayment::Fulfilled { fee_paid_msat, .. } => fee_paid_msat.clone(),
_ => None,
}
}
@@ -298,7 +301,8 @@ impl PendingOutboundPayment {
});
let payment_hash = self.payment_hash();
let total_msat = self.total_msat();
- *self = PendingOutboundPayment::Fulfilled { session_privs, payment_hash, timer_ticks_without_htlcs: 0, total_msat };
+ let fee_paid_msat = self.get_pending_fee_msat();
+ *self = PendingOutboundPayment::Fulfilled { session_privs, payment_hash, timer_ticks_without_htlcs: 0, total_msat, fee_paid_msat };
}
#[rustfmt::skip]
@@ -2743,6 +2747,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
(1, payment_hash, option),
(3, timer_ticks_without_htlcs, (default_value, 0)),
(5, total_msat, option),
+ (7, fee_paid_msat, option),
},
(2, Retryable) => {
(0, session_privs, required),
diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs
index 079a212..7768491 100644
--- a/lightning/src/ln/payment_tests.rs
+++ b/lightning/src/ln/payment_tests.rs
@@ -2343,7 +2343,11 @@ fn test_trivial_inflight_htlc_tracking() {
}
let pending_payments = nodes[0].node.list_recent_payments();
assert_eq!(pending_payments.len(), 1);
- let details = RecentPaymentDetails::Fulfilled { payment_hash: Some(payment_hash), payment_id };
+ let details = RecentPaymentDetails::Fulfilled {
+ payment_hash: Some(payment_hash),
+ payment_id,
+ fee_paid_msat: Some(1000),
+ };
assert_eq!(pending_payments[0], details);
// Remove fulfilled payment
@@ -2389,6 +2393,7 @@ fn test_trivial_inflight_htlc_tracking() {
payment_id,
payment_hash,
total_msat: 500000,
+ pending_fee_msat: Some(1000),
is_probe: false,
};
assert_eq!(pending_payments[0], details);
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.