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