1use std::time::{Duration, SystemTime, UNIX_EPOCH};
9
10use bitcoin::{BlockHash, Txid};
11use lightning::ln::channelmanager::PaymentId;
12use lightning::ln::msgs::DecodeError;
13use lightning::offers::offer::OfferId;
14use lightning::util::ser::{Readable, Writeable};
15use lightning::{
16 _init_and_read_len_prefixed_tlv_fields, impl_writeable_tlv_based,
17 impl_writeable_tlv_based_enum, write_tlv_fields,
18};
19use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
20use lightning_types::string::UntrustedString;
21
22use crate::data_store::{StorableObject, StorableObjectId, StorableObjectUpdate};
23use crate::hex_utils;
24
25#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct PaymentDetails {
28 pub id: PaymentId,
30 pub kind: PaymentKind,
32 pub amount_msat: Option<u64>,
36 pub fee_paid_msat: Option<u64>,
43 pub direction: PaymentDirection,
45 pub status: PaymentStatus,
47 pub latest_update_timestamp: u64,
49}
50
51impl PaymentDetails {
52 pub(crate) fn new(
53 id: PaymentId, kind: PaymentKind, amount_msat: Option<u64>, fee_paid_msat: Option<u64>,
54 direction: PaymentDirection, status: PaymentStatus,
55 ) -> Self {
56 let latest_update_timestamp = SystemTime::now()
57 .duration_since(UNIX_EPOCH)
58 .unwrap_or(Duration::from_secs(0))
59 .as_secs();
60 Self { id, kind, amount_msat, fee_paid_msat, direction, status, latest_update_timestamp }
61 }
62}
63
64impl Writeable for PaymentDetails {
65 fn write<W: lightning::util::ser::Writer>(
66 &self, writer: &mut W,
67 ) -> Result<(), lightning::io::Error> {
68 write_tlv_fields!(writer, {
69 (0, self.id, required), (2, None::<Option<PaymentPreimage>>, required),
73 (3, self.kind, required),
74 (4, None::<Option<PaymentSecret>>, required),
76 (5, self.latest_update_timestamp, required),
77 (6, self.amount_msat, required),
78 (7, self.fee_paid_msat, option),
79 (8, self.direction, required),
80 (10, self.status, required)
81 });
82 Ok(())
83 }
84}
85
86impl Readable for PaymentDetails {
87 fn read<R: lightning::io::Read>(reader: &mut R) -> Result<PaymentDetails, DecodeError> {
88 let unix_time_secs = SystemTime::now()
89 .duration_since(UNIX_EPOCH)
90 .unwrap_or(Duration::from_secs(0))
91 .as_secs();
92 _init_and_read_len_prefixed_tlv_fields!(reader, {
93 (0, id, required), (1, lsp_fee_limits, option),
95 (2, preimage, required),
96 (3, kind_opt, option),
97 (4, secret, required),
98 (5, latest_update_timestamp, (default_value, unix_time_secs)),
99 (6, amount_msat, required),
100 (7, fee_paid_msat, option),
101 (8, direction, required),
102 (10, status, required)
103 });
104
105 let id: PaymentId = id.0.ok_or(DecodeError::InvalidValue)?;
106 let preimage: Option<PaymentPreimage> = preimage.0.ok_or(DecodeError::InvalidValue)?;
107 let secret: Option<PaymentSecret> = secret.0.ok_or(DecodeError::InvalidValue)?;
108 let latest_update_timestamp: u64 =
109 latest_update_timestamp.0.ok_or(DecodeError::InvalidValue)?;
110 let amount_msat: Option<u64> = amount_msat.0.ok_or(DecodeError::InvalidValue)?;
111 let direction: PaymentDirection = direction.0.ok_or(DecodeError::InvalidValue)?;
112 let status: PaymentStatus = status.0.ok_or(DecodeError::InvalidValue)?;
113
114 let kind = if let Some(kind) = kind_opt {
115 kind
118 } else {
119 let hash = PaymentHash(id.0);
129
130 if secret.is_some() {
131 if let Some(lsp_fee_limits) = lsp_fee_limits {
132 let counterparty_skimmed_fee_msat = None;
133 PaymentKind::Bolt11Jit {
134 hash,
135 preimage,
136 secret,
137 counterparty_skimmed_fee_msat,
138 lsp_fee_limits,
139 }
140 } else {
141 PaymentKind::Bolt11 { hash, preimage, secret }
142 }
143 } else {
144 PaymentKind::Spontaneous { hash, preimage }
145 }
146 };
147
148 Ok(PaymentDetails {
149 id,
150 kind,
151 amount_msat,
152 fee_paid_msat,
153 direction,
154 status,
155 latest_update_timestamp,
156 })
157 }
158}
159
160impl StorableObjectId for PaymentId {
161 fn encode_to_hex_str(&self) -> String {
162 hex_utils::to_string(&self.0)
163 }
164}
165impl StorableObject for PaymentDetails {
166 type Id = PaymentId;
167 type Update = PaymentDetailsUpdate;
168
169 fn id(&self) -> Self::Id {
170 self.id
171 }
172
173 fn update(&mut self, update: &Self::Update) -> bool {
174 debug_assert_eq!(
175 self.id, update.id,
176 "We should only ever override payment data for the same payment id"
177 );
178
179 let mut updated = false;
180
181 macro_rules! update_if_necessary {
182 ($val:expr, $update:expr) => {
183 if $val != $update {
184 $val = $update;
185 updated = true;
186 }
187 };
188 }
189
190 if let Some(hash_opt) = update.hash {
191 match self.kind {
192 PaymentKind::Bolt12Offer { ref mut hash, .. } => {
193 debug_assert_eq!(
194 self.direction,
195 PaymentDirection::Outbound,
196 "We should only ever override payment hash for outbound BOLT 12 payments"
197 );
198 debug_assert!(
199 hash.is_none() || *hash == hash_opt,
200 "We should never change a payment hash after being initially set"
201 );
202 update_if_necessary!(*hash, hash_opt);
203 },
204 PaymentKind::Bolt12Refund { ref mut hash, .. } => {
205 debug_assert_eq!(
206 self.direction,
207 PaymentDirection::Outbound,
208 "We should only ever override payment hash for outbound BOLT 12 payments"
209 );
210 debug_assert!(
211 hash.is_none() || *hash == hash_opt,
212 "We should never change a payment hash after being initially set"
213 );
214 update_if_necessary!(*hash, hash_opt);
215 },
216 _ => {
217 },
220 }
221 }
222 if let Some(preimage_opt) = update.preimage {
223 match self.kind {
224 PaymentKind::Bolt11 { ref mut preimage, .. } => {
225 update_if_necessary!(*preimage, preimage_opt)
226 },
227 PaymentKind::Bolt11Jit { ref mut preimage, .. } => {
228 update_if_necessary!(*preimage, preimage_opt)
229 },
230 PaymentKind::Bolt12Offer { ref mut preimage, .. } => {
231 update_if_necessary!(*preimage, preimage_opt)
232 },
233 PaymentKind::Bolt12Refund { ref mut preimage, .. } => {
234 update_if_necessary!(*preimage, preimage_opt)
235 },
236 PaymentKind::Spontaneous { ref mut preimage, .. } => {
237 update_if_necessary!(*preimage, preimage_opt)
238 },
239 _ => {},
240 }
241 }
242
243 if let Some(secret_opt) = update.secret {
244 match self.kind {
245 PaymentKind::Bolt11 { ref mut secret, .. } => {
246 update_if_necessary!(*secret, secret_opt)
247 },
248 PaymentKind::Bolt11Jit { ref mut secret, .. } => {
249 update_if_necessary!(*secret, secret_opt)
250 },
251 PaymentKind::Bolt12Offer { ref mut secret, .. } => {
252 update_if_necessary!(*secret, secret_opt)
253 },
254 PaymentKind::Bolt12Refund { ref mut secret, .. } => {
255 update_if_necessary!(*secret, secret_opt)
256 },
257 _ => {},
258 }
259 }
260
261 if let Some(amount_opt) = update.amount_msat {
262 update_if_necessary!(self.amount_msat, amount_opt);
263 }
264
265 if let Some(fee_paid_msat_opt) = update.fee_paid_msat {
266 update_if_necessary!(self.fee_paid_msat, fee_paid_msat_opt);
267 }
268
269 if let Some(skimmed_fee_msat) = update.counterparty_skimmed_fee_msat {
270 match self.kind {
271 PaymentKind::Bolt11Jit { ref mut counterparty_skimmed_fee_msat, .. } => {
272 update_if_necessary!(*counterparty_skimmed_fee_msat, skimmed_fee_msat);
273 },
274 _ => debug_assert!(
275 false,
276 "We should only ever override counterparty_skimmed_fee_msat for JIT payments"
277 ),
278 }
279 }
280
281 if let Some(status) = update.status {
282 update_if_necessary!(self.status, status);
283 }
284
285 if let Some(confirmation_status) = update.confirmation_status {
286 match self.kind {
287 PaymentKind::Onchain { ref mut status, .. } => {
288 update_if_necessary!(*status, confirmation_status);
289 },
290 _ => {},
291 }
292 }
293
294 if updated {
295 self.latest_update_timestamp = SystemTime::now()
296 .duration_since(UNIX_EPOCH)
297 .unwrap_or(Duration::from_secs(0))
298 .as_secs();
299 }
300
301 updated
302 }
303
304 fn to_update(&self) -> Self::Update {
305 self.into()
306 }
307}
308
309#[derive(Copy, Clone, Debug, PartialEq, Eq)]
311pub enum PaymentDirection {
312 Inbound,
314 Outbound,
316}
317
318impl_writeable_tlv_based_enum!(PaymentDirection,
319 (0, Inbound) => {},
320 (1, Outbound) => {}
321);
322
323#[derive(Copy, Clone, Debug, PartialEq, Eq)]
325pub enum PaymentStatus {
326 Pending,
328 Succeeded,
330 Failed,
332}
333
334impl_writeable_tlv_based_enum!(PaymentStatus,
335 (0, Pending) => {},
336 (2, Succeeded) => {},
337 (4, Failed) => {}
338);
339
340#[derive(Clone, Debug, PartialEq, Eq)]
342pub enum PaymentKind {
343 Onchain {
350 txid: Txid,
352 status: ConfirmationStatus,
354 },
355 Bolt11 {
359 hash: PaymentHash,
361 preimage: Option<PaymentPreimage>,
363 secret: Option<PaymentSecret>,
365 },
366 Bolt11Jit {
371 hash: PaymentHash,
373 preimage: Option<PaymentPreimage>,
375 secret: Option<PaymentSecret>,
377 counterparty_skimmed_fee_msat: Option<u64>,
383 lsp_fee_limits: LSPFeeLimits,
392 },
393 Bolt12Offer {
398 hash: Option<PaymentHash>,
400 preimage: Option<PaymentPreimage>,
402 secret: Option<PaymentSecret>,
404 offer_id: OfferId,
406 payer_note: Option<UntrustedString>,
414 quantity: Option<u64>,
418 },
419 Bolt12Refund {
424 hash: Option<PaymentHash>,
426 preimage: Option<PaymentPreimage>,
428 secret: Option<PaymentSecret>,
430 payer_note: Option<UntrustedString>,
434 quantity: Option<u64>,
438 },
439 Spontaneous {
441 hash: PaymentHash,
443 preimage: Option<PaymentPreimage>,
445 },
446}
447
448impl_writeable_tlv_based_enum!(PaymentKind,
449 (0, Onchain) => {
450 (0, txid, required),
451 (2, status, required),
452 },
453 (2, Bolt11) => {
454 (0, hash, required),
455 (2, preimage, option),
456 (4, secret, option),
457 },
458 (4, Bolt11Jit) => {
459 (0, hash, required),
460 (1, counterparty_skimmed_fee_msat, option),
461 (2, preimage, option),
462 (4, secret, option),
463 (6, lsp_fee_limits, required),
464 },
465 (6, Bolt12Offer) => {
466 (0, hash, option),
467 (1, payer_note, option),
468 (2, preimage, option),
469 (3, quantity, option),
470 (4, secret, option),
471 (6, offer_id, required),
472 },
473 (8, Spontaneous) => {
474 (0, hash, required),
475 (2, preimage, option),
476 },
477 (10, Bolt12Refund) => {
478 (0, hash, option),
479 (1, payer_note, option),
480 (2, preimage, option),
481 (3, quantity, option),
482 (4, secret, option),
483 }
484);
485
486#[derive(Copy, Clone, Debug, PartialEq, Eq)]
488pub enum ConfirmationStatus {
489 Confirmed {
491 block_hash: BlockHash,
493 height: u32,
495 timestamp: u64,
497 },
498 Unconfirmed,
500}
501
502impl_writeable_tlv_based_enum!(ConfirmationStatus,
503 (0, Confirmed) => {
504 (0, block_hash, required),
505 (2, height, required),
506 (4, timestamp, required),
507 },
508 (2, Unconfirmed) => {},
509);
510
511#[derive(Copy, Clone, Debug, PartialEq, Eq)]
517pub struct LSPFeeLimits {
518 pub max_total_opening_fee_msat: Option<u64>,
521 pub max_proportional_opening_fee_ppm_msat: Option<u64>,
524}
525
526impl_writeable_tlv_based!(LSPFeeLimits, {
527 (0, max_total_opening_fee_msat, option),
528 (2, max_proportional_opening_fee_ppm_msat, option),
529});
530
531#[derive(Clone, Debug, PartialEq, Eq)]
532pub(crate) struct PaymentDetailsUpdate {
533 pub id: PaymentId,
534 pub hash: Option<Option<PaymentHash>>,
535 pub preimage: Option<Option<PaymentPreimage>>,
536 pub secret: Option<Option<PaymentSecret>>,
537 pub amount_msat: Option<Option<u64>>,
538 pub fee_paid_msat: Option<Option<u64>>,
539 pub counterparty_skimmed_fee_msat: Option<Option<u64>>,
540 pub direction: Option<PaymentDirection>,
541 pub status: Option<PaymentStatus>,
542 pub confirmation_status: Option<ConfirmationStatus>,
543}
544
545impl PaymentDetailsUpdate {
546 pub fn new(id: PaymentId) -> Self {
547 Self {
548 id,
549 hash: None,
550 preimage: None,
551 secret: None,
552 amount_msat: None,
553 fee_paid_msat: None,
554 counterparty_skimmed_fee_msat: None,
555 direction: None,
556 status: None,
557 confirmation_status: None,
558 }
559 }
560}
561
562impl From<&PaymentDetails> for PaymentDetailsUpdate {
563 fn from(value: &PaymentDetails) -> Self {
564 let (hash, preimage, secret) = match value.kind {
565 PaymentKind::Bolt11 { hash, preimage, secret, .. } => (Some(hash), preimage, secret),
566 PaymentKind::Bolt11Jit { hash, preimage, secret, .. } => (Some(hash), preimage, secret),
567 PaymentKind::Bolt12Offer { hash, preimage, secret, .. } => (hash, preimage, secret),
568 PaymentKind::Bolt12Refund { hash, preimage, secret, .. } => (hash, preimage, secret),
569 PaymentKind::Spontaneous { hash, preimage, .. } => (Some(hash), preimage, None),
570 _ => (None, None, None),
571 };
572
573 let confirmation_status = match value.kind {
574 PaymentKind::Onchain { status, .. } => Some(status),
575 _ => None,
576 };
577
578 let counterparty_skimmed_fee_msat = match value.kind {
579 PaymentKind::Bolt11Jit { counterparty_skimmed_fee_msat, .. } => {
580 Some(counterparty_skimmed_fee_msat)
581 },
582 _ => None,
583 };
584
585 Self {
586 id: value.id,
587 hash: Some(hash),
588 preimage: Some(preimage),
589 secret: Some(secret),
590 amount_msat: Some(value.amount_msat),
591 fee_paid_msat: Some(value.fee_paid_msat),
592 counterparty_skimmed_fee_msat,
593 direction: Some(value.direction),
594 status: Some(value.status),
595 confirmation_status,
596 }
597 }
598}
599
600impl StorableObjectUpdate<PaymentDetails> for PaymentDetailsUpdate {
601 fn id(&self) -> <PaymentDetails as StorableObject>::Id {
602 self.id
603 }
604}
605
606#[cfg(test)]
607mod tests {
608 use bitcoin::io::Cursor;
609 use lightning::util::ser::Readable;
610
611 use super::*;
612
613 #[derive(Clone, Debug, PartialEq, Eq)]
616 struct OldPaymentDetails {
617 pub hash: PaymentHash,
618 pub preimage: Option<PaymentPreimage>,
619 pub secret: Option<PaymentSecret>,
620 pub amount_msat: Option<u64>,
621 pub direction: PaymentDirection,
622 pub status: PaymentStatus,
623 pub lsp_fee_limits: Option<LSPFeeLimits>,
624 }
625
626 impl_writeable_tlv_based!(OldPaymentDetails, {
627 (0, hash, required),
628 (1, lsp_fee_limits, option),
629 (2, preimage, required),
630 (4, secret, required),
631 (6, amount_msat, required),
632 (8, direction, required),
633 (10, status, required)
634 });
635
636 #[test]
637 fn old_payment_details_deser_compat() {
638 let hash = PaymentHash([42u8; 32]);
641 let preimage = Some(PaymentPreimage([43u8; 32]));
642 let secret = Some(PaymentSecret([44u8; 32]));
643 let amount_msat = Some(45_000_000);
644
645 {
647 let old_bolt11_payment = OldPaymentDetails {
648 hash,
649 preimage,
650 secret,
651 amount_msat,
652 direction: PaymentDirection::Inbound,
653 status: PaymentStatus::Pending,
654 lsp_fee_limits: None,
655 };
656
657 let old_bolt11_encoded = old_bolt11_payment.encode();
658 assert_eq!(
659 old_bolt11_payment,
660 OldPaymentDetails::read(&mut Cursor::new(old_bolt11_encoded.clone())).unwrap()
661 );
662
663 let bolt11_decoded =
664 PaymentDetails::read(&mut Cursor::new(old_bolt11_encoded)).unwrap();
665 let bolt11_reencoded = bolt11_decoded.encode();
666 assert_eq!(
667 bolt11_decoded,
668 PaymentDetails::read(&mut Cursor::new(bolt11_reencoded)).unwrap()
669 );
670
671 match bolt11_decoded.kind {
672 PaymentKind::Bolt11 { hash: h, preimage: p, secret: s } => {
673 assert_eq!(hash, h);
674 assert_eq!(preimage, p);
675 assert_eq!(secret, s);
676 },
677 _ => {
678 panic!("Unexpected kind!");
679 },
680 }
681 }
682
683 {
685 let lsp_fee_limits = Some(LSPFeeLimits {
686 max_total_opening_fee_msat: Some(46_000),
687 max_proportional_opening_fee_ppm_msat: Some(47_000),
688 });
689
690 let old_bolt11_jit_payment = OldPaymentDetails {
691 hash,
692 preimage,
693 secret,
694 amount_msat,
695 direction: PaymentDirection::Inbound,
696 status: PaymentStatus::Pending,
697 lsp_fee_limits,
698 };
699
700 let old_bolt11_jit_encoded = old_bolt11_jit_payment.encode();
701 assert_eq!(
702 old_bolt11_jit_payment,
703 OldPaymentDetails::read(&mut Cursor::new(old_bolt11_jit_encoded.clone())).unwrap()
704 );
705
706 let bolt11_jit_decoded =
707 PaymentDetails::read(&mut Cursor::new(old_bolt11_jit_encoded)).unwrap();
708 let bolt11_jit_reencoded = bolt11_jit_decoded.encode();
709 assert_eq!(
710 bolt11_jit_decoded,
711 PaymentDetails::read(&mut Cursor::new(bolt11_jit_reencoded)).unwrap()
712 );
713
714 match bolt11_jit_decoded.kind {
715 PaymentKind::Bolt11Jit {
716 hash: h,
717 preimage: p,
718 secret: s,
719 counterparty_skimmed_fee_msat: c,
720 lsp_fee_limits: l,
721 } => {
722 assert_eq!(hash, h);
723 assert_eq!(preimage, p);
724 assert_eq!(secret, s);
725 assert_eq!(None, c);
726 assert_eq!(lsp_fee_limits, Some(l));
727 },
728 _ => {
729 panic!("Unexpected kind!");
730 },
731 }
732 }
733
734 {
736 let old_spontaneous_payment = OldPaymentDetails {
737 hash,
738 preimage,
739 secret: None,
740 amount_msat,
741 direction: PaymentDirection::Inbound,
742 status: PaymentStatus::Pending,
743 lsp_fee_limits: None,
744 };
745
746 let old_spontaneous_encoded = old_spontaneous_payment.encode();
747 assert_eq!(
748 old_spontaneous_payment,
749 OldPaymentDetails::read(&mut Cursor::new(old_spontaneous_encoded.clone())).unwrap()
750 );
751
752 let spontaneous_decoded =
753 PaymentDetails::read(&mut Cursor::new(old_spontaneous_encoded)).unwrap();
754 let spontaneous_reencoded = spontaneous_decoded.encode();
755 assert_eq!(
756 spontaneous_decoded,
757 PaymentDetails::read(&mut Cursor::new(spontaneous_reencoded)).unwrap()
758 );
759
760 match spontaneous_decoded.kind {
761 PaymentKind::Spontaneous { hash: h, preimage: p } => {
762 assert_eq!(hash, h);
763 assert_eq!(preimage, p);
764 },
765 _ => {
766 panic!("Unexpected kind!");
767 },
768 }
769 }
770 }
771}