Skip to content

Commit 116fb63

Browse files
authored
Merge pull request #468 from tnull/2025-02-add-fee-field-to-onchain
Fix amount calculation and track paid fees in `PaymentKind::Onchain`
2 parents 4780f94 + 0b7af80 commit 116fb63

File tree

9 files changed

+122
-21
lines changed

9 files changed

+122
-21
lines changed

bindings/ldk_node.udl

+1
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@ dictionary PaymentDetails {
412412
PaymentId id;
413413
PaymentKind kind;
414414
u64? amount_msat;
415+
u64? fee_paid_msat;
415416
PaymentDirection direction;
416417
PaymentStatus status;
417418
u64 latest_update_timestamp;

src/event.rs

+3
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,7 @@ where
726726
payment_id,
727727
kind,
728728
Some(amount_msat),
729+
None,
729730
PaymentDirection::Inbound,
730731
PaymentStatus::Pending,
731732
);
@@ -766,6 +767,7 @@ where
766767
payment_id,
767768
kind,
768769
Some(amount_msat),
770+
None,
769771
PaymentDirection::Inbound,
770772
PaymentStatus::Pending,
771773
);
@@ -932,6 +934,7 @@ where
932934
let update = PaymentDetailsUpdate {
933935
hash: Some(Some(payment_hash)),
934936
preimage: Some(Some(payment_preimage)),
937+
fee_paid_msat: Some(fee_paid_msat),
935938
status: Some(PaymentStatus::Succeeded),
936939
..PaymentDetailsUpdate::new(payment_id)
937940
};

src/payment/bolt11.rs

+6
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ impl Bolt11Payment {
160160
payment_id,
161161
kind,
162162
invoice.amount_milli_satoshis(),
163+
None,
163164
PaymentDirection::Outbound,
164165
PaymentStatus::Pending,
165166
);
@@ -182,6 +183,7 @@ impl Bolt11Payment {
182183
payment_id,
183184
kind,
184185
invoice.amount_milli_satoshis(),
186+
None,
185187
PaymentDirection::Outbound,
186188
PaymentStatus::Failed,
187189
);
@@ -293,6 +295,7 @@ impl Bolt11Payment {
293295
payment_id,
294296
kind,
295297
Some(amount_msat),
298+
None,
296299
PaymentDirection::Outbound,
297300
PaymentStatus::Pending,
298301
);
@@ -315,6 +318,7 @@ impl Bolt11Payment {
315318
payment_id,
316319
kind,
317320
Some(amount_msat),
321+
None,
318322
PaymentDirection::Outbound,
319323
PaymentStatus::Failed,
320324
);
@@ -531,6 +535,7 @@ impl Bolt11Payment {
531535
id,
532536
kind,
533537
amount_msat,
538+
None,
534539
PaymentDirection::Inbound,
535540
PaymentStatus::Pending,
536541
);
@@ -666,6 +671,7 @@ impl Bolt11Payment {
666671
id,
667672
kind,
668673
amount_msat,
674+
None,
669675
PaymentDirection::Inbound,
670676
PaymentStatus::Pending,
671677
);

src/payment/bolt12.rs

+6
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ impl Bolt12Payment {
113113
payment_id,
114114
kind,
115115
Some(offer_amount_msat),
116+
None,
116117
PaymentDirection::Outbound,
117118
PaymentStatus::Pending,
118119
);
@@ -137,6 +138,7 @@ impl Bolt12Payment {
137138
payment_id,
138139
kind,
139140
Some(offer_amount_msat),
141+
None,
140142
PaymentDirection::Outbound,
141143
PaymentStatus::Failed,
142144
);
@@ -217,6 +219,7 @@ impl Bolt12Payment {
217219
payment_id,
218220
kind,
219221
Some(amount_msat),
222+
None,
220223
PaymentDirection::Outbound,
221224
PaymentStatus::Pending,
222225
);
@@ -241,6 +244,7 @@ impl Bolt12Payment {
241244
payment_id,
242245
kind,
243246
Some(amount_msat),
247+
None,
244248
PaymentDirection::Outbound,
245249
PaymentStatus::Failed,
246250
);
@@ -338,6 +342,7 @@ impl Bolt12Payment {
338342
payment_id,
339343
kind,
340344
Some(refund.amount_msats()),
345+
None,
341346
PaymentDirection::Inbound,
342347
PaymentStatus::Pending,
343348
);
@@ -402,6 +407,7 @@ impl Bolt12Payment {
402407
payment_id,
403408
kind,
404409
Some(amount_msat),
410+
None,
405411
PaymentDirection::Outbound,
406412
PaymentStatus::Pending,
407413
);

src/payment/spontaneous.rs

+2
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ impl SpontaneousPayment {
140140
payment_id,
141141
kind,
142142
Some(amount_msat),
143+
None,
143144
PaymentDirection::Outbound,
144145
PaymentStatus::Pending,
145146
);
@@ -161,6 +162,7 @@ impl SpontaneousPayment {
161162
payment_id,
162163
kind,
163164
Some(amount_msat),
165+
None,
164166
PaymentDirection::Outbound,
165167
PaymentStatus::Failed,
166168
);

src/payment/store.rs

+36-6
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ pub struct PaymentDetails {
4242
pub kind: PaymentKind,
4343
/// The amount transferred.
4444
pub amount_msat: Option<u64>,
45+
/// The fees that were paid for this payment.
46+
///
47+
/// For Lightning payments, this will only be updated for outbound payments once they
48+
/// succeeded.
49+
///
50+
/// Will be `None` for Lightning payments made with LDK Node v0.4.x and earlier.
51+
pub fee_paid_msat: Option<u64>,
4552
/// The direction of the payment.
4653
pub direction: PaymentDirection,
4754
/// The status of the payment.
@@ -52,14 +59,14 @@ pub struct PaymentDetails {
5259

5360
impl PaymentDetails {
5461
pub(crate) fn new(
55-
id: PaymentId, kind: PaymentKind, amount_msat: Option<u64>, direction: PaymentDirection,
56-
status: PaymentStatus,
62+
id: PaymentId, kind: PaymentKind, amount_msat: Option<u64>, fee_paid_msat: Option<u64>,
63+
direction: PaymentDirection, status: PaymentStatus,
5764
) -> Self {
5865
let latest_update_timestamp = SystemTime::now()
5966
.duration_since(UNIX_EPOCH)
6067
.unwrap_or(Duration::from_secs(0))
6168
.as_secs();
62-
Self { id, kind, amount_msat, direction, status, latest_update_timestamp }
69+
Self { id, kind, amount_msat, fee_paid_msat, direction, status, latest_update_timestamp }
6370
}
6471

6572
pub(crate) fn update(&mut self, update: &PaymentDetailsUpdate) -> bool {
@@ -154,6 +161,10 @@ impl PaymentDetails {
154161
update_if_necessary!(self.amount_msat, amount_opt);
155162
}
156163

164+
if let Some(fee_paid_msat_opt) = update.fee_paid_msat {
165+
update_if_necessary!(self.fee_paid_msat, fee_paid_msat_opt);
166+
}
167+
157168
if let Some(status) = update.status {
158169
update_if_necessary!(self.status, status);
159170
}
@@ -192,6 +203,7 @@ impl Writeable for PaymentDetails {
192203
(4, None::<Option<PaymentSecret>>, required),
193204
(5, self.latest_update_timestamp, required),
194205
(6, self.amount_msat, required),
206+
(7, self.fee_paid_msat, option),
195207
(8, self.direction, required),
196208
(10, self.status, required)
197209
});
@@ -213,6 +225,7 @@ impl Readable for PaymentDetails {
213225
(4, secret, required),
214226
(5, latest_update_timestamp, (default_value, unix_time_secs)),
215227
(6, amount_msat, required),
228+
(7, fee_paid_msat, option),
216229
(8, direction, required),
217230
(10, status, required)
218231
});
@@ -253,7 +266,15 @@ impl Readable for PaymentDetails {
253266
}
254267
};
255268

256-
Ok(PaymentDetails { id, kind, amount_msat, direction, status, latest_update_timestamp })
269+
Ok(PaymentDetails {
270+
id,
271+
kind,
272+
amount_msat,
273+
fee_paid_msat,
274+
direction,
275+
status,
276+
latest_update_timestamp,
277+
})
257278
}
258279
}
259280

@@ -479,6 +500,7 @@ pub(crate) struct PaymentDetailsUpdate {
479500
pub preimage: Option<Option<PaymentPreimage>>,
480501
pub secret: Option<Option<PaymentSecret>>,
481502
pub amount_msat: Option<Option<u64>>,
503+
pub fee_paid_msat: Option<Option<u64>>,
482504
pub direction: Option<PaymentDirection>,
483505
pub status: Option<PaymentStatus>,
484506
pub confirmation_status: Option<ConfirmationStatus>,
@@ -492,6 +514,7 @@ impl PaymentDetailsUpdate {
492514
preimage: None,
493515
secret: None,
494516
amount_msat: None,
517+
fee_paid_msat: None,
495518
direction: None,
496519
status: None,
497520
confirmation_status: None,
@@ -521,6 +544,7 @@ impl From<&PaymentDetails> for PaymentDetailsUpdate {
521544
preimage: Some(preimage),
522545
secret: Some(secret),
523546
amount_msat: Some(value.amount_msat),
547+
fee_paid_msat: Some(value.fee_paid_msat),
524548
direction: Some(value.direction),
525549
status: Some(value.status),
526550
confirmation_status,
@@ -708,8 +732,14 @@ mod tests {
708732
.is_err());
709733

710734
let kind = PaymentKind::Bolt11 { hash, preimage: None, secret: None };
711-
let payment =
712-
PaymentDetails::new(id, kind, None, PaymentDirection::Inbound, PaymentStatus::Pending);
735+
let payment = PaymentDetails::new(
736+
id,
737+
kind,
738+
None,
739+
None,
740+
PaymentDirection::Inbound,
741+
PaymentStatus::Pending,
742+
);
713743

714744
assert_eq!(Ok(false), payment_store.insert(payment.clone()));
715745
assert!(payment_store.get(&id).is_some());

src/wallet/mod.rs

+15-12
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ use bitcoin::{
4949

5050
use std::ops::Deref;
5151
use std::sync::{Arc, Mutex};
52-
use std::time::{Duration, SystemTime, UNIX_EPOCH};
5352

5453
pub(crate) enum OnchainSendAmount {
5554
ExactRetainingReserve { amount_sats: u64, cur_anchor_reserve_sats: u64 },
@@ -146,11 +145,6 @@ where
146145
fn update_payment_store<'a>(
147146
&self, locked_wallet: &'a mut PersistedWallet<KVStoreWalletPersister>,
148147
) -> Result<(), Error> {
149-
let latest_update_timestamp = SystemTime::now()
150-
.duration_since(UNIX_EPOCH)
151-
.unwrap_or(Duration::from_secs(0))
152-
.as_secs();
153-
154148
for wtx in locked_wallet.transactions() {
155149
let id = PaymentId(wtx.tx_node.txid.to_byte_array());
156150
let txid = wtx.tx_node.txid;
@@ -187,25 +181,34 @@ where
187181
// here to determine the `PaymentKind`, but that's not really satisfactory, so
188182
// we're punting on it until we can come up with a better solution.
189183
let kind = crate::payment::PaymentKind::Onchain { txid, status: confirmation_status };
184+
let fee = locked_wallet.calculate_fee(&wtx.tx_node.tx).unwrap_or(Amount::ZERO);
190185
let (sent, received) = locked_wallet.sent_and_received(&wtx.tx_node.tx);
191186
let (direction, amount_msat) = if sent > received {
192187
let direction = PaymentDirection::Outbound;
193-
let amount_msat = Some(sent.to_sat().saturating_sub(received.to_sat()) * 1000);
188+
let amount_msat = Some(
189+
sent.to_sat().saturating_sub(fee.to_sat()).saturating_sub(received.to_sat())
190+
* 1000,
191+
);
194192
(direction, amount_msat)
195193
} else {
196194
let direction = PaymentDirection::Inbound;
197-
let amount_msat = Some(received.to_sat().saturating_sub(sent.to_sat()) * 1000);
195+
let amount_msat = Some(
196+
received.to_sat().saturating_sub(sent.to_sat().saturating_sub(fee.to_sat()))
197+
* 1000,
198+
);
198199
(direction, amount_msat)
199200
};
200201

201-
let payment = PaymentDetails {
202+
let fee_paid_msat = Some(fee.to_sat() * 1000);
203+
204+
let payment = PaymentDetails::new(
202205
id,
203206
kind,
204207
amount_msat,
208+
fee_paid_msat,
205209
direction,
206-
status: payment_status,
207-
latest_update_timestamp,
208-
};
210+
payment_status,
211+
);
209212

210213
self.payment_store.insert_or_update(&payment)?;
211214
}

tests/common/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ macro_rules! expect_payment_received_event {
103103
ref e @ Event::PaymentReceived { payment_id, amount_msat, .. } => {
104104
println!("{} got event {:?}", $node.node_id(), e);
105105
assert_eq!(amount_msat, $amount_msat);
106+
let payment = $node.payment(&payment_id.unwrap()).unwrap();
107+
if !matches!(payment.kind, PaymentKind::Onchain { .. }) {
108+
assert_eq!(payment.fee_paid_msat, None);
109+
}
106110
$node.event_handled();
107111
payment_id
108112
},
@@ -148,6 +152,8 @@ macro_rules! expect_payment_successful_event {
148152
if let Some(fee_msat) = $fee_paid_msat {
149153
assert_eq!(fee_paid_msat, fee_msat);
150154
}
155+
let payment = $node.payment(&$payment_id.unwrap()).unwrap();
156+
assert_eq!(payment.fee_paid_msat, fee_paid_msat);
151157
assert_eq!(payment_id, $payment_id);
152158
$node.event_handled();
153159
},

0 commit comments

Comments
 (0)