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