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 PaymentKind {
449 pub(crate) fn is_lightning_payment(&self) -> bool {
450 match self {
451 Self::Onchain { .. } => false,
452 Self::Bolt11 { .. }
453 | Self::Bolt11Jit { .. }
454 | Self::Bolt12Offer { .. }
455 | Self::Bolt12Refund { .. }
456 | Self::Spontaneous { .. } => true,
457 }
458 }
459}
460
461impl_writeable_tlv_based_enum!(PaymentKind,
462 (0, Onchain) => {
463 (0, txid, required),
464 (2, status, required),
465 },
466 (2, Bolt11) => {
467 (0, hash, required),
468 (2, preimage, option),
469 (4, secret, option),
470 },
471 (4, Bolt11Jit) => {
472 (0, hash, required),
473 (1, counterparty_skimmed_fee_msat, option),
474 (2, preimage, option),
475 (4, secret, option),
476 (6, lsp_fee_limits, required),
477 },
478 (6, Bolt12Offer) => {
479 (0, hash, option),
480 (1, payer_note, option),
481 (2, preimage, option),
482 (3, quantity, option),
483 (4, secret, option),
484 (6, offer_id, required),
485 },
486 (8, Spontaneous) => {
487 (0, hash, required),
488 (2, preimage, option),
489 },
490 (10, Bolt12Refund) => {
491 (0, hash, option),
492 (1, payer_note, option),
493 (2, preimage, option),
494 (3, quantity, option),
495 (4, secret, option),
496 }
497);
498
499#[derive(Copy, Clone, Debug, PartialEq, Eq)]
501pub enum ConfirmationStatus {
502 Confirmed {
504 block_hash: BlockHash,
506 height: u32,
508 timestamp: u64,
510 },
511 Unconfirmed,
513}
514
515impl_writeable_tlv_based_enum!(ConfirmationStatus,
516 (0, Confirmed) => {
517 (0, block_hash, required),
518 (2, height, required),
519 (4, timestamp, required),
520 },
521 (2, Unconfirmed) => {},
522);
523
524#[derive(Copy, Clone, Debug, PartialEq, Eq)]
530pub struct LSPFeeLimits {
531 pub max_total_opening_fee_msat: Option<u64>,
534 pub max_proportional_opening_fee_ppm_msat: Option<u64>,
537}
538
539impl_writeable_tlv_based!(LSPFeeLimits, {
540 (0, max_total_opening_fee_msat, option),
541 (2, max_proportional_opening_fee_ppm_msat, option),
542});
543
544#[derive(Clone, Debug, PartialEq, Eq)]
545pub(crate) struct PaymentDetailsUpdate {
546 pub id: PaymentId,
547 pub hash: Option<Option<PaymentHash>>,
548 pub preimage: Option<Option<PaymentPreimage>>,
549 pub secret: Option<Option<PaymentSecret>>,
550 pub amount_msat: Option<Option<u64>>,
551 pub fee_paid_msat: Option<Option<u64>>,
552 pub counterparty_skimmed_fee_msat: Option<Option<u64>>,
553 pub direction: Option<PaymentDirection>,
554 pub status: Option<PaymentStatus>,
555 pub confirmation_status: Option<ConfirmationStatus>,
556}
557
558impl PaymentDetailsUpdate {
559 pub fn new(id: PaymentId) -> Self {
560 Self {
561 id,
562 hash: None,
563 preimage: None,
564 secret: None,
565 amount_msat: None,
566 fee_paid_msat: None,
567 counterparty_skimmed_fee_msat: None,
568 direction: None,
569 status: None,
570 confirmation_status: None,
571 }
572 }
573}
574
575impl From<&PaymentDetails> for PaymentDetailsUpdate {
576 fn from(value: &PaymentDetails) -> Self {
577 let (hash, preimage, secret) = match value.kind {
578 PaymentKind::Bolt11 { hash, preimage, secret, .. } => (Some(hash), preimage, secret),
579 PaymentKind::Bolt11Jit { hash, preimage, secret, .. } => (Some(hash), preimage, secret),
580 PaymentKind::Bolt12Offer { hash, preimage, secret, .. } => (hash, preimage, secret),
581 PaymentKind::Bolt12Refund { hash, preimage, secret, .. } => (hash, preimage, secret),
582 PaymentKind::Spontaneous { hash, preimage, .. } => (Some(hash), preimage, None),
583 _ => (None, None, None),
584 };
585
586 let confirmation_status = match value.kind {
587 PaymentKind::Onchain { status, .. } => Some(status),
588 _ => None,
589 };
590
591 let counterparty_skimmed_fee_msat = match value.kind {
592 PaymentKind::Bolt11Jit { counterparty_skimmed_fee_msat, .. } => {
593 Some(counterparty_skimmed_fee_msat)
594 },
595 _ => None,
596 };
597
598 Self {
599 id: value.id,
600 hash: Some(hash),
601 preimage: Some(preimage),
602 secret: Some(secret),
603 amount_msat: Some(value.amount_msat),
604 fee_paid_msat: Some(value.fee_paid_msat),
605 counterparty_skimmed_fee_msat,
606 direction: Some(value.direction),
607 status: Some(value.status),
608 confirmation_status,
609 }
610 }
611}
612
613impl StorableObjectUpdate<PaymentDetails> for PaymentDetailsUpdate {
614 fn id(&self) -> <PaymentDetails as StorableObject>::Id {
615 self.id
616 }
617}
618
619#[cfg(test)]
620mod tests {
621 use bitcoin::io::Cursor;
622 use lightning::util::ser::Readable;
623
624 use super::*;
625
626 #[derive(Clone, Debug, PartialEq, Eq)]
629 struct OldPaymentDetails {
630 pub hash: PaymentHash,
631 pub preimage: Option<PaymentPreimage>,
632 pub secret: Option<PaymentSecret>,
633 pub amount_msat: Option<u64>,
634 pub direction: PaymentDirection,
635 pub status: PaymentStatus,
636 pub lsp_fee_limits: Option<LSPFeeLimits>,
637 }
638
639 impl_writeable_tlv_based!(OldPaymentDetails, {
640 (0, hash, required),
641 (1, lsp_fee_limits, option),
642 (2, preimage, required),
643 (4, secret, required),
644 (6, amount_msat, required),
645 (8, direction, required),
646 (10, status, required)
647 });
648
649 #[test]
650 fn old_payment_details_deser_compat() {
651 let hash = PaymentHash([42u8; 32]);
654 let preimage = Some(PaymentPreimage([43u8; 32]));
655 let secret = Some(PaymentSecret([44u8; 32]));
656 let amount_msat = Some(45_000_000);
657
658 {
660 let old_bolt11_payment = OldPaymentDetails {
661 hash,
662 preimage,
663 secret,
664 amount_msat,
665 direction: PaymentDirection::Inbound,
666 status: PaymentStatus::Pending,
667 lsp_fee_limits: None,
668 };
669
670 let old_bolt11_encoded = old_bolt11_payment.encode();
671 assert_eq!(
672 old_bolt11_payment,
673 OldPaymentDetails::read(&mut Cursor::new(old_bolt11_encoded.clone())).unwrap()
674 );
675
676 let bolt11_decoded =
677 PaymentDetails::read(&mut Cursor::new(old_bolt11_encoded)).unwrap();
678 let bolt11_reencoded = bolt11_decoded.encode();
679 assert_eq!(
680 bolt11_decoded,
681 PaymentDetails::read(&mut Cursor::new(bolt11_reencoded)).unwrap()
682 );
683
684 match bolt11_decoded.kind {
685 PaymentKind::Bolt11 { hash: h, preimage: p, secret: s } => {
686 assert_eq!(hash, h);
687 assert_eq!(preimage, p);
688 assert_eq!(secret, s);
689 },
690 _ => {
691 panic!("Unexpected kind!");
692 },
693 }
694 }
695
696 {
698 let lsp_fee_limits = Some(LSPFeeLimits {
699 max_total_opening_fee_msat: Some(46_000),
700 max_proportional_opening_fee_ppm_msat: Some(47_000),
701 });
702
703 let old_bolt11_jit_payment = OldPaymentDetails {
704 hash,
705 preimage,
706 secret,
707 amount_msat,
708 direction: PaymentDirection::Inbound,
709 status: PaymentStatus::Pending,
710 lsp_fee_limits,
711 };
712
713 let old_bolt11_jit_encoded = old_bolt11_jit_payment.encode();
714 assert_eq!(
715 old_bolt11_jit_payment,
716 OldPaymentDetails::read(&mut Cursor::new(old_bolt11_jit_encoded.clone())).unwrap()
717 );
718
719 let bolt11_jit_decoded =
720 PaymentDetails::read(&mut Cursor::new(old_bolt11_jit_encoded)).unwrap();
721 let bolt11_jit_reencoded = bolt11_jit_decoded.encode();
722 assert_eq!(
723 bolt11_jit_decoded,
724 PaymentDetails::read(&mut Cursor::new(bolt11_jit_reencoded)).unwrap()
725 );
726
727 match bolt11_jit_decoded.kind {
728 PaymentKind::Bolt11Jit {
729 hash: h,
730 preimage: p,
731 secret: s,
732 counterparty_skimmed_fee_msat: c,
733 lsp_fee_limits: l,
734 } => {
735 assert_eq!(hash, h);
736 assert_eq!(preimage, p);
737 assert_eq!(secret, s);
738 assert_eq!(None, c);
739 assert_eq!(lsp_fee_limits, Some(l));
740 },
741 _ => {
742 panic!("Unexpected kind!");
743 },
744 }
745 }
746
747 {
749 let old_spontaneous_payment = OldPaymentDetails {
750 hash,
751 preimage,
752 secret: None,
753 amount_msat,
754 direction: PaymentDirection::Inbound,
755 status: PaymentStatus::Pending,
756 lsp_fee_limits: None,
757 };
758
759 let old_spontaneous_encoded = old_spontaneous_payment.encode();
760 assert_eq!(
761 old_spontaneous_payment,
762 OldPaymentDetails::read(&mut Cursor::new(old_spontaneous_encoded.clone())).unwrap()
763 );
764
765 let spontaneous_decoded =
766 PaymentDetails::read(&mut Cursor::new(old_spontaneous_encoded)).unwrap();
767 let spontaneous_reencoded = spontaneous_decoded.encode();
768 assert_eq!(
769 spontaneous_decoded,
770 PaymentDetails::read(&mut Cursor::new(spontaneous_reencoded)).unwrap()
771 );
772
773 match spontaneous_decoded.kind {
774 PaymentKind::Spontaneous { hash: h, preimage: p } => {
775 assert_eq!(hash, h);
776 assert_eq!(preimage, p);
777 },
778 _ => {
779 panic!("Unexpected kind!");
780 },
781 }
782 }
783 }
784}