Skip to main content

ldk_node/payment/
store.rs

1// This file is Copyright its original authors, visible in version control history.
2//
3// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
6// accordance with one or both of these licenses.
7
8use 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/// Represents a payment.
26#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct PaymentDetails {
28	/// The identifier of this payment.
29	pub id: PaymentId,
30	/// The kind of the payment.
31	pub kind: PaymentKind,
32	/// The amount transferred.
33	///
34	/// Will be `None` for variable-amount payments until we receive them.
35	pub amount_msat: Option<u64>,
36	/// The fees that were paid for this payment.
37	///
38	/// For Lightning payments, this will only be updated for outbound payments once they
39	/// succeeded.
40	///
41	/// Will be `None` for Lightning payments made with LDK Node v0.4.x and earlier.
42	pub fee_paid_msat: Option<u64>,
43	/// The direction of the payment.
44	pub direction: PaymentDirection,
45	/// The status of the payment.
46	pub status: PaymentStatus,
47	/// The timestamp, in seconds since start of the UNIX epoch, when this entry was last updated.
48	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), // Used to be `hash` for v0.2.1 and prior
70			// 1 briefly used to be lsp_fee_limits, could probably be reused at some point in the future.
71			// 2 used to be `preimage` before it was moved to `kind` in v0.3.0
72			(2, None::<Option<PaymentPreimage>>, required),
73			(3, self.kind, required),
74			// 4 used to be `secret` before it was moved to `kind` in v0.3.0
75			(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), // Used to be `hash`
94			(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			// If we serialized the payment kind, use it.
116			// This will always be the case for any version after v0.2.1.
117			kind
118		} else {
119			// Otherwise we persisted with v0.2.1 or before, and puzzle together the kind from the
120			// provided fields.
121
122			// We used to track everything by hash, but switched to track everything by id
123			// post-v0.2.1. As both are serialized identically, we just switched the `0`-type field above
124			// from `PaymentHash` to `PaymentId` and serialize a separate `PaymentHash` in
125			// `PaymentKind` when needed. Here, for backwards compat, we can just re-create the
126			// `PaymentHash` from the id, as 'back then' `payment_hash == payment_id` was always
127			// true.
128			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					// We can omit updating the hash for BOLT11 payments as the payment hash
218					// will always be known from the beginning.
219				},
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/// Represents the direction of a payment.
310#[derive(Copy, Clone, Debug, PartialEq, Eq)]
311pub enum PaymentDirection {
312	/// The payment is inbound.
313	Inbound,
314	/// The payment is outbound.
315	Outbound,
316}
317
318impl_writeable_tlv_based_enum!(PaymentDirection,
319	(0, Inbound) => {},
320	(1, Outbound) => {}
321);
322
323/// Represents the current status of a payment.
324#[derive(Copy, Clone, Debug, PartialEq, Eq)]
325pub enum PaymentStatus {
326	/// The payment is still pending.
327	Pending,
328	/// The payment succeeded.
329	Succeeded,
330	/// The payment failed.
331	Failed,
332}
333
334impl_writeable_tlv_based_enum!(PaymentStatus,
335	(0, Pending) => {},
336	(2, Succeeded) => {},
337	(4, Failed) => {}
338);
339
340/// Represents the kind of a payment.
341#[derive(Clone, Debug, PartialEq, Eq)]
342pub enum PaymentKind {
343	/// An on-chain payment.
344	///
345	/// Payments of this kind will be considered pending until the respective transaction has
346	/// reached [`ANTI_REORG_DELAY`] confirmations on-chain.
347	///
348	/// [`ANTI_REORG_DELAY`]: lightning::chain::channelmonitor::ANTI_REORG_DELAY
349	Onchain {
350		/// The transaction identifier of this payment.
351		txid: Txid,
352		/// The confirmation status of this payment.
353		status: ConfirmationStatus,
354	},
355	/// A [BOLT 11] payment.
356	///
357	/// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md
358	Bolt11 {
359		/// The payment hash, i.e., the hash of the `preimage`.
360		hash: PaymentHash,
361		/// The pre-image used by the payment.
362		preimage: Option<PaymentPreimage>,
363		/// The secret used by the payment.
364		secret: Option<PaymentSecret>,
365	},
366	/// A [BOLT 11] payment intended to open an [bLIP-52 / LSPS 2] just-in-time channel.
367	///
368	/// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md
369	/// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md
370	Bolt11Jit {
371		/// The payment hash, i.e., the hash of the `preimage`.
372		hash: PaymentHash,
373		/// The pre-image used by the payment.
374		preimage: Option<PaymentPreimage>,
375		/// The secret used by the payment.
376		secret: Option<PaymentSecret>,
377		/// The value, in thousands of a satoshi, that was deducted from this payment as an extra
378		/// fee taken by our channel counterparty.
379		///
380		/// Will only be `Some` once we received the payment. Will always be `None` for LDK Node
381		/// v0.4 and prior.
382		counterparty_skimmed_fee_msat: Option<u64>,
383		/// Limits applying to how much fee we allow an LSP to deduct from the payment amount.
384		///
385		/// Allowing them to deduct this fee from the first inbound payment will pay for the LSP's
386		/// channel opening fees.
387		///
388		/// See [`LdkChannelConfig::accept_underpaying_htlcs`] for more information.
389		///
390		/// [`LdkChannelConfig::accept_underpaying_htlcs`]: lightning::util::config::ChannelConfig::accept_underpaying_htlcs
391		lsp_fee_limits: LSPFeeLimits,
392	},
393	/// A [BOLT 12] 'offer' payment, i.e., a payment for an [`Offer`].
394	///
395	/// [BOLT 12]: https://github.com/lightning/bolts/blob/master/12-offer-encoding.md
396	/// [`Offer`]: crate::lightning::offers::offer::Offer
397	Bolt12Offer {
398		/// The payment hash, i.e., the hash of the `preimage`.
399		hash: Option<PaymentHash>,
400		/// The pre-image used by the payment.
401		preimage: Option<PaymentPreimage>,
402		/// The secret used by the payment.
403		secret: Option<PaymentSecret>,
404		/// The ID of the offer this payment is for.
405		offer_id: OfferId,
406		/// The payer note for the payment.
407		///
408		/// Truncated to [`PAYER_NOTE_LIMIT`] characters.
409		///
410		/// This will always be `None` for payments serialized with version `v0.3.0`.
411		///
412		/// [`PAYER_NOTE_LIMIT`]: lightning::offers::invoice_request::PAYER_NOTE_LIMIT
413		payer_note: Option<UntrustedString>,
414		/// The quantity of an item requested in the offer.
415		///
416		/// This will always be `None` for payments serialized with version `v0.3.0`.
417		quantity: Option<u64>,
418	},
419	/// A [BOLT 12] 'refund' payment, i.e., a payment for a [`Refund`].
420	///
421	/// [BOLT 12]: https://github.com/lightning/bolts/blob/master/12-offer-encoding.md
422	/// [`Refund`]: lightning::offers::refund::Refund
423	Bolt12Refund {
424		/// The payment hash, i.e., the hash of the `preimage`.
425		hash: Option<PaymentHash>,
426		/// The pre-image used by the payment.
427		preimage: Option<PaymentPreimage>,
428		/// The secret used by the payment.
429		secret: Option<PaymentSecret>,
430		/// The payer note for the refund payment.
431		///
432		/// This will always be `None` for payments serialized with version `v0.3.0`.
433		payer_note: Option<UntrustedString>,
434		/// The quantity of an item that the refund is for.
435		///
436		/// This will always be `None` for payments serialized with version `v0.3.0`.
437		quantity: Option<u64>,
438	},
439	/// A spontaneous ("keysend") payment.
440	Spontaneous {
441		/// The payment hash, i.e., the hash of the `preimage`.
442		hash: PaymentHash,
443		/// The pre-image used by the payment.
444		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/// Represents the confirmation status of a transaction.
500#[derive(Copy, Clone, Debug, PartialEq, Eq)]
501pub enum ConfirmationStatus {
502	/// The transaction is confirmed in the best chain.
503	Confirmed {
504		/// The hash of the block in which the transaction was confirmed.
505		block_hash: BlockHash,
506		/// The height under which the block was confirmed.
507		height: u32,
508		/// The timestamp, in seconds since start of the UNIX epoch, when this entry was last updated.
509		timestamp: u64,
510	},
511	/// The transaction is unconfirmed.
512	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/// Limits applying to how much fee we allow an LSP to deduct from the payment amount.
525///
526/// See [`LdkChannelConfig::accept_underpaying_htlcs`] for more information.
527///
528/// [`LdkChannelConfig::accept_underpaying_htlcs`]: lightning::util::config::ChannelConfig::accept_underpaying_htlcs
529#[derive(Copy, Clone, Debug, PartialEq, Eq)]
530pub struct LSPFeeLimits {
531	/// The maximal total amount we allow any configured LSP withhold from us when forwarding the
532	/// payment.
533	pub max_total_opening_fee_msat: Option<u64>,
534	/// The maximal proportional fee, in parts-per-million millisatoshi, we allow any configured
535	/// LSP withhold from us when forwarding the payment.
536	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	/// We refactored `PaymentDetails` to hold a payment id and moved some required fields into
627	/// `PaymentKind`. Here, we keep the old layout available in order test de/ser compatibility.
628	#[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		// We refactored `PaymentDetails` to hold a payment id and moved some required fields into
652		// `PaymentKind`. Here, we test compatibility with the old layout.
653		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		// Test `Bolt11` de/ser
659		{
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		// Test `Bolt11Jit` de/ser
697		{
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		// Test `Spontaneous` de/ser
748		{
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}