lightning/offers/
offer.rs

1// This file is Copyright its original authors, visible in version control
2// history.
3//
4// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7// You may not use this file except in accordance with one or both of these
8// licenses.
9
10//! Data structures and encoding for `offer` messages.
11//!
12//! An [`Offer`] represents an "offer to be paid." It is typically constructed by a merchant and
13//! published as a QR code to be scanned by a customer. The customer uses the offer to request an
14//! invoice from the merchant to be paid.
15//!
16//! # Example
17//!
18//! ```
19//! extern crate bitcoin;
20//! extern crate core;
21//! extern crate lightning;
22//!
23//! use core::convert::TryFrom;
24//! use core::num::NonZeroU64;
25//! use core::time::Duration;
26//!
27//! use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, SecretKey};
28//! use lightning::offers::offer::{Offer, OfferBuilder, Quantity};
29//! use lightning::offers::parse::Bolt12ParseError;
30//! use lightning::util::ser::{Readable, Writeable};
31//!
32//! # use lightning::blinded_path::message::BlindedMessagePath;
33//! # #[cfg(feature = "std")]
34//! # use std::time::SystemTime;
35//! #
36//! # fn create_blinded_path() -> BlindedMessagePath { unimplemented!() }
37//! # fn create_another_blinded_path() -> BlindedMessagePath { unimplemented!() }
38//! #
39//! # #[cfg(feature = "std")]
40//! # fn build() -> Result<(), Bolt12ParseError> {
41//! let secp_ctx = Secp256k1::new();
42//! let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
43//! let pubkey = PublicKey::from(keys);
44//!
45//! let expiration = SystemTime::now() + Duration::from_secs(24 * 60 * 60);
46//! let offer = OfferBuilder::new(pubkey)
47//!     .description("coffee, large".to_string())
48//!     .amount_msats(20_000)
49//!     .supported_quantity(Quantity::Unbounded)
50//!     .absolute_expiry(expiration.duration_since(SystemTime::UNIX_EPOCH).unwrap())
51//!     .issuer("Foo Bar".to_string())
52//!     .path(create_blinded_path())
53//!     .path(create_another_blinded_path())
54//!     .build()?;
55//!
56//! // Encode as a bech32 string for use in a QR code.
57//! let encoded_offer = offer.to_string();
58//!
59//! // Parse from a bech32 string after scanning from a QR code.
60//! let offer = encoded_offer.parse::<Offer>()?;
61//!
62//! // Encode offer as raw bytes.
63//! let mut bytes = Vec::new();
64//! offer.write(&mut bytes).unwrap();
65//!
66//! // Decode raw bytes into an offer.
67//! let offer = Offer::try_from(bytes)?;
68//! # Ok(())
69//! # }
70//! ```
71//!
72//! # Note
73//!
74//! If constructing an [`Offer`] for use with a [`ChannelManager`], use
75//! [`ChannelManager::create_offer_builder`] instead of [`OfferBuilder::new`].
76//!
77//! [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
78//! [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder
79
80use core::borrow::Borrow;
81use bitcoin::constants::ChainHash;
82use bitcoin::network::Network;
83use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, self};
84use core::hash::{Hash, Hasher};
85use core::num::NonZeroU64;
86use core::str::FromStr;
87use core::time::Duration;
88use crate::io;
89use crate::blinded_path::message::BlindedMessagePath;
90use crate::ln::channelmanager::PaymentId;
91use crate::types::features::OfferFeatures;
92use crate::ln::inbound_payment::{ExpandedKey, IV_LEN};
93use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
94use crate::offers::merkle::{TaggedHash, TlvRecord, TlvStream};
95use crate::offers::nonce::Nonce;
96use crate::offers::parse::{Bech32Encode, Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
97use crate::offers::signer::{Metadata, MetadataMaterial, self};
98use crate::util::ser::{CursorReadable, HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
99use crate::util::string::PrintableString;
100
101#[cfg(not(c_bindings))]
102use {
103	crate::offers::invoice_request::InvoiceRequestBuilder,
104};
105#[cfg(c_bindings)]
106use {
107	crate::offers::invoice_request::InvoiceRequestWithDerivedPayerSigningPubkeyBuilder,
108};
109
110#[allow(unused_imports)]
111use crate::prelude::*;
112
113#[cfg(feature = "std")]
114use std::time::SystemTime;
115use bitcoin::hex::impl_fmt_traits;
116
117pub(super) const IV_BYTES_WITH_METADATA: &[u8; IV_LEN] = b"LDK Offer ~~~~~~";
118pub(super) const IV_BYTES_WITHOUT_METADATA: &[u8; IV_LEN] = b"LDK Offer v2~~~~";
119
120/// An identifier for an [`Offer`] built using [`DerivedMetadata`].
121#[derive(Clone, Copy, Eq, PartialEq)]
122pub struct OfferId(pub [u8; 32]);
123
124impl OfferId {
125	const ID_TAG: &'static str = "LDK Offer ID";
126
127	fn from_valid_offer_tlv_stream(bytes: &[u8]) -> Self {
128		let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(Self::ID_TAG, bytes);
129		Self(tagged_hash.to_bytes())
130	}
131
132	fn from_valid_invreq_tlv_stream(bytes: &[u8]) -> Self {
133		let tlv_stream = Offer::tlv_stream_iter(bytes);
134		let tagged_hash = TaggedHash::from_tlv_stream(Self::ID_TAG, tlv_stream);
135		Self(tagged_hash.to_bytes())
136	}
137}
138
139impl Borrow<[u8]> for OfferId {
140	fn borrow(&self) -> &[u8] {
141		&self.0[..]
142	}
143}
144
145impl_fmt_traits! {
146	impl fmt_traits for OfferId {
147		const LENGTH: usize = 32;
148	}
149}
150
151impl Writeable for OfferId {
152	fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
153		self.0.write(w)
154	}
155}
156
157impl Readable for OfferId {
158	fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
159		Ok(OfferId(Readable::read(r)?))
160	}
161}
162
163/// Builds an [`Offer`] for the "offer to be paid" flow.
164///
165/// See [module-level documentation] for usage.
166///
167/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
168///
169/// [module-level documentation]: self
170pub struct OfferBuilder<'a, M: MetadataStrategy, T: secp256k1::Signing> {
171	offer: OfferContents,
172	metadata_strategy: core::marker::PhantomData<M>,
173	secp_ctx: Option<&'a Secp256k1<T>>,
174}
175
176/// Builds an [`Offer`] for the "offer to be paid" flow.
177///
178/// See [module-level documentation] for usage.
179///
180/// [module-level documentation]: self
181#[cfg(c_bindings)]
182#[derive(Clone)]
183pub struct OfferWithExplicitMetadataBuilder<'a> {
184	offer: OfferContents,
185	metadata_strategy: core::marker::PhantomData<ExplicitMetadata>,
186	secp_ctx: Option<&'a Secp256k1<secp256k1::All>>,
187}
188
189/// Builds an [`Offer`] for the "offer to be paid" flow.
190///
191/// See [module-level documentation] for usage.
192///
193/// [module-level documentation]: self
194#[cfg(c_bindings)]
195#[derive(Clone)]
196pub struct OfferWithDerivedMetadataBuilder<'a> {
197	offer: OfferContents,
198	metadata_strategy: core::marker::PhantomData<DerivedMetadata>,
199	secp_ctx: Option<&'a Secp256k1<secp256k1::All>>,
200}
201
202/// Indicates how [`Offer::metadata`] may be set.
203///
204/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
205pub trait MetadataStrategy {}
206
207/// [`Offer::metadata`] may be explicitly set or left empty.
208///
209/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
210pub struct ExplicitMetadata {}
211
212/// [`Offer::metadata`] will be derived.
213///
214/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
215pub struct DerivedMetadata {}
216
217impl MetadataStrategy for ExplicitMetadata {}
218
219impl MetadataStrategy for DerivedMetadata {}
220
221macro_rules! offer_explicit_metadata_builder_methods { (
222	$self: ident, $self_type: ty, $return_type: ty, $return_value: expr
223) => {
224	/// Creates a new builder for an offer using the `signing_pubkey` for signing invoices. The
225	/// associated secret key must be remembered while the offer is valid.
226	///
227	/// Use a different pubkey per offer to avoid correlating offers.
228	///
229	/// # Note
230	///
231	/// If constructing an [`Offer`] for use with a [`ChannelManager`], use
232	/// [`ChannelManager::create_offer_builder`] instead of [`OfferBuilder::new`].
233	///
234	/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
235	/// [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder
236	pub fn new(signing_pubkey: PublicKey) -> Self {
237		Self {
238			offer: OfferContents {
239				chains: None, metadata: None, amount: None, description: None,
240				features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
241				supported_quantity: Quantity::One, issuer_signing_pubkey: Some(signing_pubkey),
242				#[cfg(test)]
243				experimental_foo: None,
244			},
245			metadata_strategy: core::marker::PhantomData,
246			secp_ctx: None,
247		}
248	}
249
250	/// Sets the [`Offer::metadata`] to the given bytes.
251	///
252	/// Successive calls to this method will override the previous setting.
253	pub fn metadata(mut $self: $self_type, metadata: Vec<u8>) -> Result<$return_type, Bolt12SemanticError> {
254		$self.offer.metadata = Some(Metadata::Bytes(metadata));
255		Ok($return_value)
256	}
257} }
258
259macro_rules! offer_derived_metadata_builder_methods { ($secp_context: ty) => {
260	/// Similar to [`OfferBuilder::new`] except, if [`OfferBuilder::path`] is called, the signing
261	/// pubkey is derived from the given [`ExpandedKey`] and [`Nonce`]. This provides recipient
262	/// privacy by using a different signing pubkey for each offer. Otherwise, the provided
263	/// `node_id` is used for [`Offer::issuer_signing_pubkey`].
264	///
265	/// Also, sets the metadata when [`OfferBuilder::build`] is called such that it can be used by
266	/// [`InvoiceRequest::verify_using_metadata`] to determine if the request was produced for the
267	/// offer given an [`ExpandedKey`]. However, if [`OfferBuilder::path`] is called, then the
268	/// metadata will not be set and must be included in each [`BlindedMessagePath`] instead. In this case,
269	/// use [`InvoiceRequest::verify_using_recipient_data`].
270	///
271	/// [`InvoiceRequest::verify_using_metadata`]: crate::offers::invoice_request::InvoiceRequest::verify_using_metadata
272	/// [`InvoiceRequest::verify_using_recipient_data`]: crate::offers::invoice_request::InvoiceRequest::verify_using_recipient_data
273	/// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
274	pub fn deriving_signing_pubkey(
275		node_id: PublicKey, expanded_key: &ExpandedKey, nonce: Nonce,
276		secp_ctx: &'a Secp256k1<$secp_context>
277	) -> Self {
278		let derivation_material = MetadataMaterial::new(nonce, expanded_key, None);
279		let metadata = Metadata::DerivedSigningPubkey(derivation_material);
280		Self {
281			offer: OfferContents {
282				chains: None, metadata: Some(metadata), amount: None, description: None,
283				features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
284				supported_quantity: Quantity::One, issuer_signing_pubkey: Some(node_id),
285				#[cfg(test)]
286				experimental_foo: None,
287			},
288			metadata_strategy: core::marker::PhantomData,
289			secp_ctx: Some(secp_ctx),
290		}
291	}
292} }
293
294macro_rules! offer_builder_methods { (
295	$self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)?
296) => {
297	/// Adds the chain hash of the given [`Network`] to [`Offer::chains`]. If not called,
298	/// the chain hash of [`Network::Bitcoin`] is assumed to be the only one supported.
299	///
300	/// See [`Offer::chains`] on how this relates to the payment currency.
301	///
302	/// Successive calls to this method will add another chain hash.
303	pub fn chain($self: $self_type, network: Network) -> $return_type {
304		$self.chain_hash(ChainHash::using_genesis_block(network))
305	}
306
307	/// Adds the [`ChainHash`] to [`Offer::chains`]. If not called, the chain hash of
308	/// [`Network::Bitcoin`] is assumed to be the only one supported.
309	///
310	/// See [`Offer::chains`] on how this relates to the payment currency.
311	///
312	/// Successive calls to this method will add another chain hash.
313	pub(crate) fn chain_hash($($self_mut)* $self: $self_type, chain: ChainHash) -> $return_type {
314		let chains = $self.offer.chains.get_or_insert_with(Vec::new);
315		if !chains.contains(&chain) {
316			chains.push(chain);
317		}
318
319		$return_value
320	}
321
322	/// Sets the [`Offer::amount`] as an [`Amount::Bitcoin`].
323	///
324	/// Successive calls to this method will override the previous setting.
325	pub fn amount_msats($self: $self_type, amount_msats: u64) -> $return_type {
326		$self.amount(Amount::Bitcoin { amount_msats })
327	}
328
329	/// Sets the [`Offer::amount`].
330	///
331	/// Successive calls to this method will override the previous setting.
332	pub(super) fn amount($($self_mut)* $self: $self_type, amount: Amount) -> $return_type {
333		$self.offer.amount = Some(amount);
334		$return_value
335	}
336
337	/// Sets the [`Offer::absolute_expiry`] as seconds since the Unix epoch.
338	#[cfg_attr(feature = "std", doc = "Any expiry that has already passed is valid and can be checked for using [`Offer::is_expired`].")]
339	///
340	/// Successive calls to this method will override the previous setting.
341	pub fn absolute_expiry($($self_mut)* $self: $self_type, absolute_expiry: Duration) -> $return_type {
342		$self.offer.absolute_expiry = Some(absolute_expiry);
343		$return_value
344	}
345
346	/// Sets the [`Offer::description`].
347	///
348	/// Successive calls to this method will override the previous setting.
349	pub fn description($($self_mut)* $self: $self_type, description: String) -> $return_type {
350		$self.offer.description = Some(description);
351		$return_value
352	}
353
354	/// Sets the [`Offer::issuer`].
355	///
356	/// Successive calls to this method will override the previous setting.
357	pub fn issuer($($self_mut)* $self: $self_type, issuer: String) -> $return_type {
358		$self.offer.issuer = Some(issuer);
359		$return_value
360	}
361
362	/// Adds a blinded path to [`Offer::paths`]. Must include at least one path if only connected by
363	/// private channels or if [`Offer::issuer_signing_pubkey`] is not a public node id.
364	///
365	/// Successive calls to this method will add another blinded path. Caller is responsible for not
366	/// adding duplicate paths.
367	pub fn path($($self_mut)* $self: $self_type, path: BlindedMessagePath) -> $return_type {
368		$self.offer.paths.get_or_insert_with(Vec::new).push(path);
369		$return_value
370	}
371
372	/// Sets the quantity of items for [`Offer::supported_quantity`]. If not called, defaults to
373	/// [`Quantity::One`].
374	///
375	/// Successive calls to this method will override the previous setting.
376	pub fn supported_quantity($($self_mut)* $self: $self_type, quantity: Quantity) -> $return_type {
377		$self.offer.supported_quantity = quantity;
378		$return_value
379	}
380
381	/// Builds an [`Offer`] from the builder's settings.
382	pub fn build($($self_mut)* $self: $self_type) -> Result<Offer, Bolt12SemanticError> {
383		match $self.offer.amount {
384			Some(Amount::Bitcoin { amount_msats }) => {
385				if amount_msats > MAX_VALUE_MSAT {
386					return Err(Bolt12SemanticError::InvalidAmount);
387				}
388			},
389			Some(Amount::Currency { .. }) => return Err(Bolt12SemanticError::UnsupportedCurrency),
390			None => {},
391		}
392
393		if $self.offer.amount.is_some() && $self.offer.description.is_none() {
394			$self.offer.description = Some(String::new());
395		}
396
397		if let Some(chains) = &$self.offer.chains {
398			if chains.len() == 1 && chains[0] == $self.offer.implied_chain() {
399				$self.offer.chains = None;
400			}
401		}
402
403		Ok($self.build_without_checks())
404	}
405
406	fn build_without_checks($($self_mut)* $self: $self_type) -> Offer {
407		if let Some(mut metadata) = $self.offer.metadata.take() {
408			// Create the metadata for stateless verification of an InvoiceRequest.
409			if metadata.has_derivation_material() {
410
411				// Don't derive keys if no blinded paths were given since this means the signing
412				// pubkey must be the node id of an announced node.
413				let iv_bytes = if $self.offer.paths.is_none() {
414					metadata = metadata.without_keys();
415					IV_BYTES_WITH_METADATA
416				} else {
417					IV_BYTES_WITHOUT_METADATA
418				};
419
420				let mut tlv_stream = $self.offer.as_tlv_stream();
421				debug_assert_eq!(tlv_stream.0.metadata, None);
422				tlv_stream.0.metadata = None;
423				if metadata.derives_recipient_keys() {
424					tlv_stream.0.issuer_id = None;
425				}
426
427				// Either replace the signing pubkey with the derived pubkey or include the metadata
428				// for verification. In the former case, the blinded paths must include
429				// `OffersContext::InvoiceRequest` instead.
430				let (derived_metadata, keys) =
431					metadata.derive_from(iv_bytes, tlv_stream, $self.secp_ctx);
432				match keys {
433					Some(keys) => $self.offer.issuer_signing_pubkey = Some(keys.public_key()),
434					None => $self.offer.metadata = Some(derived_metadata),
435				}
436			} else {
437				$self.offer.metadata = Some(metadata);
438			}
439		}
440
441		const OFFER_ALLOCATION_SIZE: usize = 512;
442		let mut bytes = Vec::with_capacity(OFFER_ALLOCATION_SIZE);
443		$self.offer.write(&mut bytes).unwrap();
444
445		let id = OfferId::from_valid_offer_tlv_stream(&bytes);
446
447		Offer {
448			bytes,
449			#[cfg(not(c_bindings))]
450			contents: $self.offer,
451			#[cfg(c_bindings)]
452			contents: $self.offer.clone(),
453			id,
454		}
455	}
456} }
457
458#[cfg(test)]
459macro_rules! offer_builder_test_methods { (
460	$self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)?
461) => {
462	#[cfg_attr(c_bindings, allow(dead_code))]
463	fn features_unchecked($($self_mut)* $self: $self_type, features: OfferFeatures) -> $return_type {
464		$self.offer.features = features;
465		$return_value
466	}
467
468	#[cfg_attr(c_bindings, allow(dead_code))]
469	pub(crate) fn clear_chains($($self_mut)* $self: $self_type) -> $return_type {
470		$self.offer.chains = None;
471		$return_value
472	}
473
474	#[cfg_attr(c_bindings, allow(dead_code))]
475	pub(crate) fn clear_paths($($self_mut)* $self: $self_type) -> $return_type {
476		$self.offer.paths = None;
477		$return_value
478	}
479
480	#[cfg_attr(c_bindings, allow(dead_code))]
481	pub(crate) fn clear_issuer_signing_pubkey($($self_mut)* $self: $self_type) -> $return_type {
482		$self.offer.issuer_signing_pubkey = None;
483		$return_value
484	}
485
486	#[cfg_attr(c_bindings, allow(dead_code))]
487	pub(super) fn experimental_foo($($self_mut)* $self: $self_type, experimental_foo: u64) -> $return_type {
488		$self.offer.experimental_foo = Some(experimental_foo);
489		$return_value
490	}
491
492	#[cfg_attr(c_bindings, allow(dead_code))]
493	pub(super) fn build_unchecked($self: $self_type) -> Offer {
494		$self.build_without_checks()
495	}
496} }
497
498impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
499	offer_builder_methods!(self, Self, Self, self, mut);
500
501	#[cfg(test)]
502	offer_builder_test_methods!(self, Self, Self, self, mut);
503}
504
505impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> {
506	offer_explicit_metadata_builder_methods!(self, Self, Self, self);
507}
508
509impl<'a, T: secp256k1::Signing> OfferBuilder<'a, DerivedMetadata, T> {
510	offer_derived_metadata_builder_methods!(T);
511}
512
513#[cfg(all(c_bindings, not(test)))]
514impl<'a> OfferWithExplicitMetadataBuilder<'a> {
515	offer_explicit_metadata_builder_methods!(self, &mut Self, (), ());
516	offer_builder_methods!(self, &mut Self, (), ());
517}
518
519#[cfg(all(c_bindings, test))]
520impl<'a> OfferWithExplicitMetadataBuilder<'a> {
521	offer_explicit_metadata_builder_methods!(self, &mut Self, &mut Self, self);
522	offer_builder_methods!(self, &mut Self, &mut Self, self);
523	offer_builder_test_methods!(self, &mut Self, &mut Self, self);
524}
525
526#[cfg(all(c_bindings, not(test)))]
527impl<'a> OfferWithDerivedMetadataBuilder<'a> {
528	offer_derived_metadata_builder_methods!(secp256k1::All);
529	offer_builder_methods!(self, &mut Self, (), ());
530}
531
532#[cfg(all(c_bindings, test))]
533impl<'a> OfferWithDerivedMetadataBuilder<'a> {
534	offer_derived_metadata_builder_methods!(secp256k1::All);
535	offer_builder_methods!(self, &mut Self, &mut Self, self);
536	offer_builder_test_methods!(self, &mut Self, &mut Self, self);
537}
538
539#[cfg(c_bindings)]
540impl<'a> From<OfferBuilder<'a, DerivedMetadata, secp256k1::All>>
541for OfferWithDerivedMetadataBuilder<'a> {
542	fn from(builder: OfferBuilder<'a, DerivedMetadata, secp256k1::All>) -> Self {
543		let OfferBuilder { offer, metadata_strategy, secp_ctx } = builder;
544
545		Self { offer, metadata_strategy, secp_ctx }
546	}
547}
548
549#[cfg(c_bindings)]
550impl<'a> From<OfferWithDerivedMetadataBuilder<'a>>
551for OfferBuilder<'a, DerivedMetadata, secp256k1::All> {
552	fn from(builder: OfferWithDerivedMetadataBuilder<'a>) -> Self {
553		let OfferWithDerivedMetadataBuilder { offer, metadata_strategy, secp_ctx } = builder;
554
555		Self { offer, metadata_strategy, secp_ctx }
556	}
557}
558
559/// An `Offer` is a potentially long-lived proposal for payment of a good or service.
560///
561/// An offer is a precursor to an [`InvoiceRequest`]. A merchant publishes an offer from which a
562/// customer may request an [`Bolt12Invoice`] for a specific quantity and using an amount sufficient
563/// to cover that quantity (i.e., at least `quantity * amount`). See [`Offer::amount`].
564///
565/// Offers may be denominated in currency other than bitcoin but are ultimately paid using the
566/// latter.
567///
568/// Through the use of [`BlindedMessagePath`]s, offers provide recipient privacy.
569///
570/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
571/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
572#[derive(Clone, Debug)]
573pub struct Offer {
574	// The serialized offer. Needed when creating an `InvoiceRequest` if the offer contains unknown
575	// fields.
576	pub(super) bytes: Vec<u8>,
577	pub(super) contents: OfferContents,
578	id: OfferId,
579}
580
581/// The contents of an [`Offer`], which may be shared with an [`InvoiceRequest`] or a
582/// [`Bolt12Invoice`].
583///
584/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
585/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
586#[derive(Clone, Debug)]
587#[cfg_attr(test, derive(PartialEq))]
588pub(super) struct OfferContents {
589	chains: Option<Vec<ChainHash>>,
590	metadata: Option<Metadata>,
591	amount: Option<Amount>,
592	description: Option<String>,
593	features: OfferFeatures,
594	absolute_expiry: Option<Duration>,
595	issuer: Option<String>,
596	paths: Option<Vec<BlindedMessagePath>>,
597	supported_quantity: Quantity,
598	issuer_signing_pubkey: Option<PublicKey>,
599	#[cfg(test)]
600	experimental_foo: Option<u64>,
601}
602
603macro_rules! offer_accessors { ($self: ident, $contents: expr) => {
604	// TODO: Return a slice once ChainHash has constants.
605	// - https://github.com/rust-bitcoin/rust-bitcoin/pull/1283
606	// - https://github.com/rust-bitcoin/rust-bitcoin/pull/1286
607	/// The chains that may be used when paying a requested invoice (e.g., bitcoin mainnet).
608	/// Payments must be denominated in units of the minimal lightning-payable unit (e.g., msats)
609	/// for the selected chain.
610	pub fn chains(&$self) -> Vec<bitcoin::constants::ChainHash> {
611		$contents.chains()
612	}
613
614	// TODO: Link to corresponding method in `InvoiceRequest`.
615	/// Opaque bytes set by the originator. Useful for authentication and validating fields since it
616	/// is reflected in `invoice_request` messages along with all the other fields from the `offer`.
617	pub fn metadata(&$self) -> Option<&Vec<u8>> {
618		$contents.metadata()
619	}
620
621	/// The minimum amount required for a successful payment of a single item.
622	pub fn amount(&$self) -> Option<$crate::offers::offer::Amount> {
623		$contents.amount()
624	}
625
626	/// A complete description of the purpose of the payment. Intended to be displayed to the user
627	/// but with the caveat that it has not been verified in any way.
628	pub fn description(&$self) -> Option<$crate::util::string::PrintableString> {
629		$contents.description()
630	}
631
632	/// Features pertaining to the offer.
633	pub fn offer_features(&$self) -> &$crate::types::features::OfferFeatures {
634		&$contents.features()
635	}
636
637	/// Duration since the Unix epoch when an invoice should no longer be requested.
638	///
639	/// If `None`, the offer does not expire.
640	pub fn absolute_expiry(&$self) -> Option<core::time::Duration> {
641		$contents.absolute_expiry()
642	}
643
644	/// The issuer of the offer, possibly beginning with `user@domain` or `domain`. Intended to be
645	/// displayed to the user but with the caveat that it has not been verified in any way.
646	pub fn issuer(&$self) -> Option<$crate::util::string::PrintableString> {
647		$contents.issuer()
648	}
649
650	/// Paths to the recipient originating from publicly reachable nodes. Blinded paths provide
651	/// recipient privacy by obfuscating its node id.
652	pub fn paths(&$self) -> &[$crate::blinded_path::message::BlindedMessagePath] {
653		$contents.paths()
654	}
655
656	/// The quantity of items supported.
657	pub fn supported_quantity(&$self) -> $crate::offers::offer::Quantity {
658		$contents.supported_quantity()
659	}
660
661	/// The public key corresponding to the key used by the recipient to sign invoices.
662	/// - If [`Offer::paths`] is empty, MUST be `Some` and contain the recipient's node id for
663	///   sending an [`InvoiceRequest`].
664	/// - If [`Offer::paths`] is not empty, MAY be `Some` and contain a transient id.
665	/// - If `None`, the signing pubkey will be the final blinded node id from the
666	///   [`BlindedMessagePath`] in [`Offer::paths`] used to send the [`InvoiceRequest`].
667	///
668	/// See also [`Bolt12Invoice::signing_pubkey`].
669	///
670	/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
671	/// [`Bolt12Invoice::signing_pubkey`]: crate::offers::invoice::Bolt12Invoice::signing_pubkey
672	pub fn issuer_signing_pubkey(&$self) -> Option<bitcoin::secp256k1::PublicKey> {
673		$contents.issuer_signing_pubkey()
674	}
675} }
676
677impl Offer {
678	offer_accessors!(self, self.contents);
679
680	/// Returns the id of the offer.
681	pub fn id(&self) -> OfferId {
682		self.id
683	}
684
685	pub(super) fn implied_chain(&self) -> ChainHash {
686		self.contents.implied_chain()
687	}
688
689	/// Returns whether the given chain is supported by the offer.
690	pub fn supports_chain(&self, chain: ChainHash) -> bool {
691		self.contents.supports_chain(chain)
692	}
693
694	/// Whether the offer has expired.
695	#[cfg(feature = "std")]
696	pub fn is_expired(&self) -> bool {
697		self.contents.is_expired()
698	}
699
700	/// Whether the offer has expired given the duration since the Unix epoch.
701	pub fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
702		self.contents.is_expired_no_std(duration_since_epoch)
703	}
704
705	/// Returns whether the given quantity is valid for the offer.
706	pub fn is_valid_quantity(&self, quantity: u64) -> bool {
707		self.contents.is_valid_quantity(quantity)
708	}
709
710	/// Returns whether a quantity is expected in an [`InvoiceRequest`] for the offer.
711	///
712	/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
713	pub fn expects_quantity(&self) -> bool {
714		self.contents.expects_quantity()
715	}
716
717	pub(super) fn tlv_stream_iter<'a>(
718		bytes: &'a [u8]
719	) -> impl core::iter::Iterator<Item = TlvRecord<'a>> {
720		TlvStream::new(bytes).range(OFFER_TYPES)
721			.chain(TlvStream::new(bytes).range(EXPERIMENTAL_OFFER_TYPES))
722	}
723
724	#[cfg(async_payments)]
725	pub(super) fn verify<T: secp256k1::Signing>(
726		&self, nonce: Nonce, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
727	) -> Result<(OfferId, Option<Keypair>), ()> {
728		self.contents.verify_using_recipient_data(&self.bytes, nonce, key, secp_ctx)
729	}
730}
731
732macro_rules! request_invoice_derived_signing_pubkey { ($self: ident, $builder: ty) => {
733	/// Creates an [`InvoiceRequestBuilder`] for the offer, which
734	/// - derives the [`InvoiceRequest::payer_signing_pubkey`] such that a different key can be used
735	///   for each request in order to protect the sender's privacy,
736	/// - sets [`InvoiceRequest::payer_metadata`] when [`InvoiceRequestBuilder::build_and_sign`] is
737	///   called such that it can be used by [`Bolt12Invoice::verify_using_metadata`] to determine
738	///   if the invoice was requested using a base [`ExpandedKey`] from which the payer id was
739	///   derived, and
740	/// - includes the [`PaymentId`] encrypted in [`InvoiceRequest::payer_metadata`] so that it can
741	///   be used when sending the payment for the requested invoice.
742	///
743	/// Errors if the offer contains unknown required features.
744	///
745	/// [`InvoiceRequest::payer_signing_pubkey`]: crate::offers::invoice_request::InvoiceRequest::payer_signing_pubkey
746	/// [`InvoiceRequest::payer_metadata`]: crate::offers::invoice_request::InvoiceRequest::payer_metadata
747	/// [`Bolt12Invoice::verify_using_metadata`]: crate::offers::invoice::Bolt12Invoice::verify_using_metadata
748	/// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
749	pub fn request_invoice<
750		'a, 'b,
751		#[cfg(not(c_bindings))]
752		T: secp256k1::Signing
753	>(
754		&'a $self, expanded_key: &ExpandedKey, nonce: Nonce,
755		#[cfg(not(c_bindings))]
756		secp_ctx: &'b Secp256k1<T>,
757		#[cfg(c_bindings)]
758		secp_ctx: &'b Secp256k1<secp256k1::All>,
759		payment_id: PaymentId
760	) -> Result<$builder, Bolt12SemanticError> {
761		if $self.offer_features().requires_unknown_bits() {
762			return Err(Bolt12SemanticError::UnknownRequiredFeatures);
763		}
764
765		Ok(<$builder>::deriving_signing_pubkey($self, expanded_key, nonce, secp_ctx, payment_id))
766	}
767} }
768
769#[cfg(not(c_bindings))]
770impl Offer {
771	request_invoice_derived_signing_pubkey!(self, InvoiceRequestBuilder<'a, 'b, T>);
772}
773
774#[cfg(c_bindings)]
775impl Offer {
776	request_invoice_derived_signing_pubkey!(self, InvoiceRequestWithDerivedPayerSigningPubkeyBuilder<'a, 'b>);
777}
778
779#[cfg(test)]
780impl Offer {
781	pub(super) fn as_tlv_stream(&self) -> FullOfferTlvStreamRef {
782		self.contents.as_tlv_stream()
783	}
784}
785
786impl AsRef<[u8]> for Offer {
787	fn as_ref(&self) -> &[u8] {
788		&self.bytes
789	}
790}
791
792impl PartialEq for Offer {
793	fn eq(&self, other: &Self) -> bool {
794		self.bytes.eq(&other.bytes)
795	}
796}
797
798impl Eq for Offer {}
799
800impl Hash for Offer {
801	fn hash<H: Hasher>(&self, state: &mut H) {
802		self.bytes.hash(state);
803	}
804}
805
806impl OfferContents {
807	pub fn chains(&self) -> Vec<ChainHash> {
808		self.chains.as_ref().cloned().unwrap_or_else(|| vec![self.implied_chain()])
809	}
810
811	pub fn implied_chain(&self) -> ChainHash {
812		ChainHash::using_genesis_block(Network::Bitcoin)
813	}
814
815	pub fn supports_chain(&self, chain: ChainHash) -> bool {
816		self.chains().contains(&chain)
817	}
818
819	pub fn metadata(&self) -> Option<&Vec<u8>> {
820		self.metadata.as_ref().and_then(|metadata| metadata.as_bytes())
821	}
822
823	pub fn amount(&self) -> Option<Amount> {
824		self.amount
825	}
826
827	pub fn description(&self) -> Option<PrintableString> {
828		self.description.as_ref().map(|description| PrintableString(description))
829	}
830
831	pub fn features(&self) -> &OfferFeatures {
832		&self.features
833	}
834
835	pub fn absolute_expiry(&self) -> Option<Duration> {
836		self.absolute_expiry
837	}
838
839	#[cfg(feature = "std")]
840	pub(super) fn is_expired(&self) -> bool {
841		SystemTime::UNIX_EPOCH
842			.elapsed()
843			.map(|duration_since_epoch| self.is_expired_no_std(duration_since_epoch))
844			.unwrap_or(false)
845	}
846
847	pub(super) fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
848		self.absolute_expiry
849			.map(|absolute_expiry| duration_since_epoch > absolute_expiry)
850			.unwrap_or(false)
851	}
852
853	pub fn issuer(&self) -> Option<PrintableString> {
854		self.issuer.as_ref().map(|issuer| PrintableString(issuer.as_str()))
855	}
856
857	pub fn paths(&self) -> &[BlindedMessagePath] {
858		self.paths.as_ref().map(|paths| paths.as_slice()).unwrap_or(&[])
859	}
860
861	pub(super) fn check_amount_msats_for_quantity(
862		&self, amount_msats: Option<u64>, quantity: Option<u64>
863	) -> Result<(), Bolt12SemanticError> {
864		let offer_amount_msats = match self.amount {
865			None => 0,
866			Some(Amount::Bitcoin { amount_msats }) => amount_msats,
867			Some(Amount::Currency { .. }) => return Err(Bolt12SemanticError::UnsupportedCurrency),
868		};
869
870		if !self.expects_quantity() || quantity.is_some() {
871			let expected_amount_msats = offer_amount_msats.checked_mul(quantity.unwrap_or(1))
872				.ok_or(Bolt12SemanticError::InvalidAmount)?;
873			let amount_msats = amount_msats.unwrap_or(expected_amount_msats);
874
875			if amount_msats < expected_amount_msats {
876				return Err(Bolt12SemanticError::InsufficientAmount);
877			}
878
879			if amount_msats > MAX_VALUE_MSAT {
880				return Err(Bolt12SemanticError::InvalidAmount);
881			}
882		}
883
884		Ok(())
885	}
886
887	pub fn supported_quantity(&self) -> Quantity {
888		self.supported_quantity
889	}
890
891	pub(super) fn check_quantity(&self, quantity: Option<u64>) -> Result<(), Bolt12SemanticError> {
892		let expects_quantity = self.expects_quantity();
893		match quantity {
894			None if expects_quantity => Err(Bolt12SemanticError::MissingQuantity),
895			Some(_) if !expects_quantity => Err(Bolt12SemanticError::UnexpectedQuantity),
896			Some(quantity) if !self.is_valid_quantity(quantity) => {
897				Err(Bolt12SemanticError::InvalidQuantity)
898			},
899			_ => Ok(()),
900		}
901	}
902
903	fn is_valid_quantity(&self, quantity: u64) -> bool {
904		match self.supported_quantity {
905			Quantity::Bounded(n) => quantity <= n.get(),
906			Quantity::Unbounded => quantity > 0,
907			Quantity::One => quantity == 1,
908		}
909	}
910
911	fn expects_quantity(&self) -> bool {
912		match self.supported_quantity {
913			Quantity::Bounded(_) => true,
914			Quantity::Unbounded => true,
915			Quantity::One => false,
916		}
917	}
918
919	pub(super) fn issuer_signing_pubkey(&self) -> Option<PublicKey> {
920		self.issuer_signing_pubkey
921	}
922
923	pub(super) fn verify_using_metadata<T: secp256k1::Signing>(
924		&self, bytes: &[u8], key: &ExpandedKey, secp_ctx: &Secp256k1<T>
925	) -> Result<(OfferId, Option<Keypair>), ()> {
926		self.verify(bytes, self.metadata.as_ref(), key, IV_BYTES_WITH_METADATA, secp_ctx)
927	}
928
929	pub(super) fn verify_using_recipient_data<T: secp256k1::Signing>(
930		&self, bytes: &[u8], nonce: Nonce, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
931	) -> Result<(OfferId, Option<Keypair>), ()> {
932		let metadata = Metadata::RecipientData(nonce);
933		self.verify(bytes, Some(&metadata), key, IV_BYTES_WITHOUT_METADATA, secp_ctx)
934	}
935
936	/// Verifies that the offer metadata was produced from the offer in the TLV stream.
937	fn verify<T: secp256k1::Signing>(
938		&self, bytes: &[u8], metadata: Option<&Metadata>, key: &ExpandedKey,
939		iv_bytes: &[u8; IV_LEN], secp_ctx: &Secp256k1<T>
940	) -> Result<(OfferId, Option<Keypair>), ()> {
941		match metadata {
942			Some(metadata) => {
943				let tlv_stream = TlvStream::new(bytes).range(OFFER_TYPES).filter(|record| {
944					match record.r#type {
945						OFFER_METADATA_TYPE => false,
946						OFFER_ISSUER_ID_TYPE => !metadata.derives_recipient_keys(),
947						_ => true,
948					}
949				})
950				.chain(TlvStream::new(bytes).range(EXPERIMENTAL_OFFER_TYPES));
951
952				let signing_pubkey = match self.issuer_signing_pubkey() {
953					Some(signing_pubkey) => signing_pubkey,
954					None => return Err(()),
955				};
956				let keys = signer::verify_recipient_metadata(
957					metadata.as_ref(), key, iv_bytes, signing_pubkey, tlv_stream, secp_ctx
958				)?;
959
960				let offer_id = OfferId::from_valid_invreq_tlv_stream(bytes);
961
962				Ok((offer_id, keys))
963			},
964			None => Err(()),
965		}
966	}
967
968	pub(super) fn as_tlv_stream(&self) -> FullOfferTlvStreamRef {
969		let (currency, amount) = match &self.amount {
970			None => (None, None),
971			Some(Amount::Bitcoin { amount_msats }) => (None, Some(*amount_msats)),
972			Some(Amount::Currency { iso4217_code, amount }) => (
973				Some(iso4217_code), Some(*amount)
974			),
975		};
976
977		let features = {
978			if self.features == OfferFeatures::empty() { None } else { Some(&self.features) }
979		};
980
981		let offer = OfferTlvStreamRef {
982			chains: self.chains.as_ref(),
983			metadata: self.metadata(),
984			currency,
985			amount,
986			description: self.description.as_ref(),
987			features,
988			absolute_expiry: self.absolute_expiry.map(|duration| duration.as_secs()),
989			paths: self.paths.as_ref(),
990			issuer: self.issuer.as_ref(),
991			quantity_max: self.supported_quantity.to_tlv_record(),
992			issuer_id: self.issuer_signing_pubkey.as_ref(),
993		};
994
995		let experimental_offer = ExperimentalOfferTlvStreamRef {
996			#[cfg(test)]
997			experimental_foo: self.experimental_foo,
998		};
999
1000		(offer, experimental_offer)
1001	}
1002}
1003
1004impl Readable for Offer {
1005	fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
1006		let bytes: WithoutLength<Vec<u8>> = Readable::read(reader)?;
1007		Self::try_from(bytes.0).map_err(|_| DecodeError::InvalidValue)
1008	}
1009}
1010
1011impl Writeable for Offer {
1012	fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
1013		WithoutLength(&self.bytes).write(writer)
1014	}
1015}
1016
1017impl Writeable for OfferContents {
1018	fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
1019		self.as_tlv_stream().write(writer)
1020	}
1021}
1022
1023/// The minimum amount required for an item in an [`Offer`], denominated in either bitcoin or
1024/// another currency.
1025#[derive(Clone, Copy, Debug, PartialEq)]
1026pub enum Amount {
1027	/// An amount of bitcoin.
1028	Bitcoin {
1029		/// The amount in millisatoshi.
1030		amount_msats: u64,
1031	},
1032	/// An amount of currency specified using ISO 4217.
1033	Currency {
1034		/// The currency that the amount is denominated in.
1035		iso4217_code: CurrencyCode,
1036		/// The amount in the currency unit adjusted by the ISO 4217 exponent (e.g., USD cents).
1037		amount: u64,
1038	},
1039}
1040
1041/// An ISO 4217 three-letter currency code (e.g., USD).
1042pub type CurrencyCode = [u8; 3];
1043
1044/// Quantity of items supported by an [`Offer`].
1045#[derive(Clone, Copy, Debug, PartialEq)]
1046pub enum Quantity {
1047	/// Up to a specific number of items (inclusive). Use when more than one item can be requested
1048	/// but is limited (e.g., because of per customer or inventory limits).
1049	///
1050	/// May be used with `NonZeroU64::new(1)` but prefer to use [`Quantity::One`] if only one item
1051	/// is supported.
1052	Bounded(NonZeroU64),
1053	/// One or more items. Use when more than one item can be requested without any limit.
1054	Unbounded,
1055	/// Only one item. Use when only a single item can be requested.
1056	One,
1057}
1058
1059impl Quantity {
1060	fn to_tlv_record(self) -> Option<u64> {
1061		match self {
1062			Quantity::Bounded(n) => Some(n.get()),
1063			Quantity::Unbounded => Some(0),
1064			Quantity::One => None,
1065		}
1066	}
1067}
1068
1069/// Valid type range for offer TLV records.
1070pub(super) const OFFER_TYPES: core::ops::Range<u64> = 1..80;
1071
1072/// TLV record type for [`Offer::metadata`].
1073const OFFER_METADATA_TYPE: u64 = 4;
1074
1075/// TLV record type for [`Offer::issuer_signing_pubkey`].
1076const OFFER_ISSUER_ID_TYPE: u64 = 22;
1077
1078tlv_stream!(OfferTlvStream, OfferTlvStreamRef<'a>, OFFER_TYPES, {
1079	(2, chains: (Vec<ChainHash>, WithoutLength)),
1080	(OFFER_METADATA_TYPE, metadata: (Vec<u8>, WithoutLength)),
1081	(6, currency: CurrencyCode),
1082	(8, amount: (u64, HighZeroBytesDroppedBigSize)),
1083	(10, description: (String, WithoutLength)),
1084	(12, features: (OfferFeatures, WithoutLength)),
1085	(14, absolute_expiry: (u64, HighZeroBytesDroppedBigSize)),
1086	(16, paths: (Vec<BlindedMessagePath>, WithoutLength)),
1087	(18, issuer: (String, WithoutLength)),
1088	(20, quantity_max: (u64, HighZeroBytesDroppedBigSize)),
1089	(OFFER_ISSUER_ID_TYPE, issuer_id: PublicKey),
1090});
1091
1092/// Valid type range for experimental offer TLV records.
1093pub(super) const EXPERIMENTAL_OFFER_TYPES: core::ops::Range<u64> = 1_000_000_000..2_000_000_000;
1094
1095#[cfg(not(test))]
1096tlv_stream!(ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, EXPERIMENTAL_OFFER_TYPES, {
1097});
1098
1099#[cfg(test)]
1100tlv_stream!(ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, EXPERIMENTAL_OFFER_TYPES, {
1101	(1_999_999_999, experimental_foo: (u64, HighZeroBytesDroppedBigSize)),
1102});
1103
1104type FullOfferTlvStream = (OfferTlvStream, ExperimentalOfferTlvStream);
1105
1106type FullOfferTlvStreamRef<'a> = (OfferTlvStreamRef<'a>, ExperimentalOfferTlvStreamRef);
1107
1108impl CursorReadable for FullOfferTlvStream {
1109	fn read<R: AsRef<[u8]>>(r: &mut io::Cursor<R>) -> Result<Self, DecodeError> {
1110		let offer = CursorReadable::read(r)?;
1111		let experimental_offer = CursorReadable::read(r)?;
1112
1113		Ok((offer, experimental_offer))
1114	}
1115}
1116
1117impl Bech32Encode for Offer {
1118	const BECH32_HRP: &'static str = "lno";
1119}
1120
1121impl FromStr for Offer {
1122	type Err = Bolt12ParseError;
1123
1124	fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
1125		Self::from_bech32_str(s)
1126	}
1127}
1128
1129impl TryFrom<Vec<u8>> for Offer {
1130	type Error = Bolt12ParseError;
1131
1132	fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
1133		let offer = ParsedMessage::<FullOfferTlvStream>::try_from(bytes)?;
1134		let ParsedMessage { bytes, tlv_stream } = offer;
1135		let contents = OfferContents::try_from(tlv_stream)?;
1136		let id = OfferId::from_valid_offer_tlv_stream(&bytes);
1137
1138		Ok(Offer { bytes, contents, id })
1139	}
1140}
1141
1142impl TryFrom<FullOfferTlvStream> for OfferContents {
1143	type Error = Bolt12SemanticError;
1144
1145	fn try_from(tlv_stream: FullOfferTlvStream) -> Result<Self, Self::Error> {
1146		let (
1147			OfferTlvStream {
1148				chains, metadata, currency, amount, description, features, absolute_expiry, paths,
1149				issuer, quantity_max, issuer_id,
1150			},
1151			ExperimentalOfferTlvStream {
1152				#[cfg(test)]
1153				experimental_foo,
1154			},
1155		) = tlv_stream;
1156
1157		let metadata = metadata.map(|metadata| Metadata::Bytes(metadata));
1158
1159		let amount = match (currency, amount) {
1160			(None, None) => None,
1161			(None, Some(amount_msats)) if amount_msats > MAX_VALUE_MSAT => {
1162				return Err(Bolt12SemanticError::InvalidAmount);
1163			},
1164			(None, Some(amount_msats)) => Some(Amount::Bitcoin { amount_msats }),
1165			(Some(_), None) => return Err(Bolt12SemanticError::MissingAmount),
1166			(Some(iso4217_code), Some(amount)) => Some(Amount::Currency { iso4217_code, amount }),
1167		};
1168
1169		if amount.is_some() && description.is_none() {
1170			return Err(Bolt12SemanticError::MissingDescription);
1171		}
1172
1173		let features = features.unwrap_or_else(OfferFeatures::empty);
1174
1175		let absolute_expiry = absolute_expiry
1176			.map(|seconds_from_epoch| Duration::from_secs(seconds_from_epoch));
1177
1178		let supported_quantity = match quantity_max {
1179			None => Quantity::One,
1180			Some(0) => Quantity::Unbounded,
1181			Some(n) => Quantity::Bounded(NonZeroU64::new(n).unwrap()),
1182		};
1183
1184		let (issuer_signing_pubkey, paths) = match (issuer_id, paths) {
1185			(None, None) => return Err(Bolt12SemanticError::MissingIssuerSigningPubkey),
1186			(_, Some(paths)) if paths.is_empty() => return Err(Bolt12SemanticError::MissingPaths),
1187			(issuer_id, paths) => (issuer_id, paths),
1188		};
1189
1190		Ok(OfferContents {
1191			chains, metadata, amount, description, features, absolute_expiry, issuer, paths,
1192			supported_quantity, issuer_signing_pubkey,
1193			#[cfg(test)]
1194			experimental_foo,
1195		})
1196	}
1197}
1198
1199impl core::fmt::Display for Offer {
1200	fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
1201		self.fmt_bech32_str(f)
1202	}
1203}
1204
1205#[cfg(test)]
1206mod tests {
1207	use super::{Amount, EXPERIMENTAL_OFFER_TYPES, ExperimentalOfferTlvStreamRef, OFFER_TYPES, Offer, OfferTlvStreamRef, Quantity};
1208	#[cfg(not(c_bindings))]
1209	use {
1210		super::OfferBuilder,
1211	};
1212	#[cfg(c_bindings)]
1213	use {
1214		super::OfferWithExplicitMetadataBuilder as OfferBuilder,
1215	};
1216
1217	use bitcoin::constants::ChainHash;
1218	use bitcoin::network::Network;
1219	use bitcoin::secp256k1::Secp256k1;
1220	use core::num::NonZeroU64;
1221	use core::time::Duration;
1222	use crate::blinded_path::BlindedHop;
1223	use crate::blinded_path::message::BlindedMessagePath;
1224	use crate::types::features::OfferFeatures;
1225	use crate::ln::channelmanager::PaymentId;
1226	use crate::ln::inbound_payment::ExpandedKey;
1227	use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
1228	use crate::offers::nonce::Nonce;
1229	use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError};
1230	use crate::offers::test_utils::*;
1231	use crate::util::ser::{BigSize, Writeable};
1232	use crate::util::string::PrintableString;
1233
1234	#[test]
1235	fn builds_offer_with_defaults() {
1236		let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1237
1238		let mut buffer = Vec::new();
1239		offer.write(&mut buffer).unwrap();
1240
1241		assert_eq!(offer.bytes, buffer.as_slice());
1242		assert_eq!(offer.chains(), vec![ChainHash::using_genesis_block(Network::Bitcoin)]);
1243		assert!(offer.supports_chain(ChainHash::using_genesis_block(Network::Bitcoin)));
1244		assert_eq!(offer.metadata(), None);
1245		assert_eq!(offer.amount(), None);
1246		assert_eq!(offer.description(), None);
1247		assert_eq!(offer.offer_features(), &OfferFeatures::empty());
1248		assert_eq!(offer.absolute_expiry(), None);
1249		#[cfg(feature = "std")]
1250		assert!(!offer.is_expired());
1251		assert_eq!(offer.paths(), &[]);
1252		assert_eq!(offer.issuer(), None);
1253		assert_eq!(offer.supported_quantity(), Quantity::One);
1254		assert!(!offer.expects_quantity());
1255		assert_eq!(offer.issuer_signing_pubkey(), Some(pubkey(42)));
1256
1257		assert_eq!(
1258			offer.as_tlv_stream(),
1259			(
1260				OfferTlvStreamRef {
1261					chains: None,
1262					metadata: None,
1263					currency: None,
1264					amount: None,
1265					description: None,
1266					features: None,
1267					absolute_expiry: None,
1268					paths: None,
1269					issuer: None,
1270					quantity_max: None,
1271					issuer_id: Some(&pubkey(42)),
1272				},
1273				ExperimentalOfferTlvStreamRef {
1274					experimental_foo: None,
1275				},
1276			),
1277		);
1278
1279		if let Err(e) = Offer::try_from(buffer) {
1280			panic!("error parsing offer: {:?}", e);
1281		}
1282	}
1283
1284	#[test]
1285	fn builds_offer_with_chains() {
1286		let mainnet = ChainHash::using_genesis_block(Network::Bitcoin);
1287		let testnet = ChainHash::using_genesis_block(Network::Testnet);
1288
1289		let offer = OfferBuilder::new(pubkey(42))
1290			.chain(Network::Bitcoin)
1291			.build()
1292			.unwrap();
1293		assert!(offer.supports_chain(mainnet));
1294		assert_eq!(offer.chains(), vec![mainnet]);
1295		assert_eq!(offer.as_tlv_stream().0.chains, None);
1296
1297		let offer = OfferBuilder::new(pubkey(42))
1298			.chain(Network::Testnet)
1299			.build()
1300			.unwrap();
1301		assert!(offer.supports_chain(testnet));
1302		assert_eq!(offer.chains(), vec![testnet]);
1303		assert_eq!(offer.as_tlv_stream().0.chains, Some(&vec![testnet]));
1304
1305		let offer = OfferBuilder::new(pubkey(42))
1306			.chain(Network::Testnet)
1307			.chain(Network::Testnet)
1308			.build()
1309			.unwrap();
1310		assert!(offer.supports_chain(testnet));
1311		assert_eq!(offer.chains(), vec![testnet]);
1312		assert_eq!(offer.as_tlv_stream().0.chains, Some(&vec![testnet]));
1313
1314		let offer = OfferBuilder::new(pubkey(42))
1315			.chain(Network::Bitcoin)
1316			.chain(Network::Testnet)
1317			.build()
1318			.unwrap();
1319		assert!(offer.supports_chain(mainnet));
1320		assert!(offer.supports_chain(testnet));
1321		assert_eq!(offer.chains(), vec![mainnet, testnet]);
1322		assert_eq!(offer.as_tlv_stream().0.chains, Some(&vec![mainnet, testnet]));
1323	}
1324
1325	#[test]
1326	fn builds_offer_with_metadata() {
1327		let offer = OfferBuilder::new(pubkey(42))
1328			.metadata(vec![42; 32]).unwrap()
1329			.build()
1330			.unwrap();
1331		assert_eq!(offer.metadata(), Some(&vec![42; 32]));
1332		assert_eq!(offer.as_tlv_stream().0.metadata, Some(&vec![42; 32]));
1333
1334		let offer = OfferBuilder::new(pubkey(42))
1335			.metadata(vec![42; 32]).unwrap()
1336			.metadata(vec![43; 32]).unwrap()
1337			.build()
1338			.unwrap();
1339		assert_eq!(offer.metadata(), Some(&vec![43; 32]));
1340		assert_eq!(offer.as_tlv_stream().0.metadata, Some(&vec![43; 32]));
1341	}
1342
1343	#[test]
1344	fn builds_offer_with_metadata_derived() {
1345		let node_id = recipient_pubkey();
1346		let expanded_key = ExpandedKey::new([42; 32]);
1347		let entropy = FixedEntropy {};
1348		let nonce = Nonce::from_entropy_source(&entropy);
1349		let secp_ctx = Secp256k1::new();
1350		let payment_id = PaymentId([1; 32]);
1351
1352		#[cfg(c_bindings)]
1353		use super::OfferWithDerivedMetadataBuilder as OfferBuilder;
1354		let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx)
1355			.amount_msats(1000)
1356			.experimental_foo(42)
1357			.build().unwrap();
1358		assert!(offer.metadata().is_some());
1359		assert_eq!(offer.issuer_signing_pubkey(), Some(node_id));
1360
1361		let invoice_request = offer
1362			.request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1363			.build_and_sign().unwrap();
1364		match invoice_request.verify_using_metadata(&expanded_key, &secp_ctx) {
1365			Ok(invoice_request) => assert_eq!(invoice_request.offer_id, offer.id()),
1366			Err(_) => panic!("unexpected error"),
1367		}
1368
1369		// Fails verification when using the wrong method
1370		let invoice_request = offer
1371			.request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1372			.build_and_sign().unwrap();
1373		assert!(
1374			invoice_request.verify_using_recipient_data(nonce, &expanded_key, &secp_ctx).is_err()
1375		);
1376
1377		// Fails verification with altered offer field
1378		let mut tlv_stream = offer.as_tlv_stream();
1379		tlv_stream.0.amount = Some(100);
1380
1381		let mut encoded_offer = Vec::new();
1382		tlv_stream.write(&mut encoded_offer).unwrap();
1383
1384		let invoice_request = Offer::try_from(encoded_offer).unwrap()
1385			.request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1386			.build_and_sign().unwrap();
1387		assert!(invoice_request.verify_using_metadata(&expanded_key, &secp_ctx).is_err());
1388
1389		// Fails verification with altered metadata
1390		let mut tlv_stream = offer.as_tlv_stream();
1391		let metadata = tlv_stream.0.metadata.unwrap().iter().copied().rev().collect();
1392		tlv_stream.0.metadata = Some(&metadata);
1393
1394		let mut encoded_offer = Vec::new();
1395		tlv_stream.write(&mut encoded_offer).unwrap();
1396
1397		let invoice_request = Offer::try_from(encoded_offer).unwrap()
1398			.request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1399			.build_and_sign().unwrap();
1400		assert!(invoice_request.verify_using_metadata(&expanded_key, &secp_ctx).is_err());
1401	}
1402
1403	#[test]
1404	fn builds_offer_with_derived_signing_pubkey() {
1405		let node_id = recipient_pubkey();
1406		let expanded_key = ExpandedKey::new([42; 32]);
1407		let entropy = FixedEntropy {};
1408		let nonce = Nonce::from_entropy_source(&entropy);
1409		let secp_ctx = Secp256k1::new();
1410		let payment_id = PaymentId([1; 32]);
1411
1412		let blinded_path = BlindedMessagePath::from_raw(
1413			pubkey(40), pubkey(41),
1414			vec![
1415				BlindedHop { blinded_node_id: pubkey(42), encrypted_payload: vec![0; 43] },
1416				BlindedHop { blinded_node_id: node_id, encrypted_payload: vec![0; 44] },
1417			]
1418		);
1419
1420		#[cfg(c_bindings)]
1421		use super::OfferWithDerivedMetadataBuilder as OfferBuilder;
1422		let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx)
1423			.amount_msats(1000)
1424			.path(blinded_path)
1425			.experimental_foo(42)
1426			.build().unwrap();
1427		assert!(offer.metadata().is_none());
1428		assert_ne!(offer.issuer_signing_pubkey(), Some(node_id));
1429
1430		let invoice_request = offer
1431			.request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1432			.build_and_sign().unwrap();
1433		match invoice_request.verify_using_recipient_data(nonce, &expanded_key, &secp_ctx) {
1434			Ok(invoice_request) => assert_eq!(invoice_request.offer_id, offer.id()),
1435			Err(_) => panic!("unexpected error"),
1436		}
1437
1438		// Fails verification when using the wrong method
1439		let invoice_request = offer
1440			.request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1441			.build_and_sign().unwrap();
1442		assert!(invoice_request.verify_using_metadata(&expanded_key, &secp_ctx).is_err());
1443
1444		// Fails verification with altered offer field
1445		let mut tlv_stream = offer.as_tlv_stream();
1446		tlv_stream.0.amount = Some(100);
1447
1448		let mut encoded_offer = Vec::new();
1449		tlv_stream.write(&mut encoded_offer).unwrap();
1450
1451		let invoice_request = Offer::try_from(encoded_offer).unwrap()
1452			.request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1453			.build_and_sign().unwrap();
1454		assert!(
1455			invoice_request.verify_using_recipient_data(nonce, &expanded_key, &secp_ctx).is_err()
1456		);
1457
1458		// Fails verification with altered signing pubkey
1459		let mut tlv_stream = offer.as_tlv_stream();
1460		let issuer_id = pubkey(1);
1461		tlv_stream.0.issuer_id = Some(&issuer_id);
1462
1463		let mut encoded_offer = Vec::new();
1464		tlv_stream.write(&mut encoded_offer).unwrap();
1465
1466		let invoice_request = Offer::try_from(encoded_offer).unwrap()
1467			.request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1468			.build_and_sign().unwrap();
1469		assert!(
1470			invoice_request.verify_using_recipient_data(nonce, &expanded_key, &secp_ctx).is_err()
1471		);
1472	}
1473
1474	#[test]
1475	fn builds_offer_with_amount() {
1476		let bitcoin_amount = Amount::Bitcoin { amount_msats: 1000 };
1477		let currency_amount = Amount::Currency { iso4217_code: *b"USD", amount: 10 };
1478
1479		let offer = OfferBuilder::new(pubkey(42))
1480			.amount_msats(1000)
1481			.build()
1482			.unwrap();
1483		let tlv_stream = offer.as_tlv_stream();
1484		assert_eq!(offer.amount(), Some(bitcoin_amount));
1485		assert_eq!(tlv_stream.0.amount, Some(1000));
1486		assert_eq!(tlv_stream.0.currency, None);
1487
1488		#[cfg(not(c_bindings))]
1489		let builder = OfferBuilder::new(pubkey(42))
1490			.amount(currency_amount.clone());
1491		#[cfg(c_bindings)]
1492		let mut builder = OfferBuilder::new(pubkey(42));
1493		#[cfg(c_bindings)]
1494		builder.amount(currency_amount.clone());
1495		let tlv_stream = builder.offer.as_tlv_stream();
1496		assert_eq!(builder.offer.amount, Some(currency_amount.clone()));
1497		assert_eq!(tlv_stream.0.amount, Some(10));
1498		assert_eq!(tlv_stream.0.currency, Some(b"USD"));
1499		match builder.build() {
1500			Ok(_) => panic!("expected error"),
1501			Err(e) => assert_eq!(e, Bolt12SemanticError::UnsupportedCurrency),
1502		}
1503
1504		let offer = OfferBuilder::new(pubkey(42))
1505			.amount(currency_amount.clone())
1506			.amount(bitcoin_amount.clone())
1507			.build()
1508			.unwrap();
1509		let tlv_stream = offer.as_tlv_stream();
1510		assert_eq!(tlv_stream.0.amount, Some(1000));
1511		assert_eq!(tlv_stream.0.currency, None);
1512
1513		let invalid_amount = Amount::Bitcoin { amount_msats: MAX_VALUE_MSAT + 1 };
1514		match OfferBuilder::new(pubkey(42)).amount(invalid_amount).build() {
1515			Ok(_) => panic!("expected error"),
1516			Err(e) => assert_eq!(e, Bolt12SemanticError::InvalidAmount),
1517		}
1518	}
1519
1520	#[test]
1521	fn builds_offer_with_description() {
1522		let offer = OfferBuilder::new(pubkey(42))
1523			.description("foo".into())
1524			.build()
1525			.unwrap();
1526		assert_eq!(offer.description(), Some(PrintableString("foo")));
1527		assert_eq!(offer.as_tlv_stream().0.description, Some(&String::from("foo")));
1528
1529		let offer = OfferBuilder::new(pubkey(42))
1530			.description("foo".into())
1531			.description("bar".into())
1532			.build()
1533			.unwrap();
1534		assert_eq!(offer.description(), Some(PrintableString("bar")));
1535		assert_eq!(offer.as_tlv_stream().0.description, Some(&String::from("bar")));
1536
1537		let offer = OfferBuilder::new(pubkey(42))
1538			.amount_msats(1000)
1539			.build()
1540			.unwrap();
1541		assert_eq!(offer.description(), Some(PrintableString("")));
1542		assert_eq!(offer.as_tlv_stream().0.description, Some(&String::from("")));
1543	}
1544
1545	#[test]
1546	fn builds_offer_with_features() {
1547		let offer = OfferBuilder::new(pubkey(42))
1548			.features_unchecked(OfferFeatures::unknown())
1549			.build()
1550			.unwrap();
1551		assert_eq!(offer.offer_features(), &OfferFeatures::unknown());
1552		assert_eq!(offer.as_tlv_stream().0.features, Some(&OfferFeatures::unknown()));
1553
1554		let offer = OfferBuilder::new(pubkey(42))
1555			.features_unchecked(OfferFeatures::unknown())
1556			.features_unchecked(OfferFeatures::empty())
1557			.build()
1558			.unwrap();
1559		assert_eq!(offer.offer_features(), &OfferFeatures::empty());
1560		assert_eq!(offer.as_tlv_stream().0.features, None);
1561	}
1562
1563	#[test]
1564	fn builds_offer_with_absolute_expiry() {
1565		let future_expiry = Duration::from_secs(u64::max_value());
1566		let past_expiry = Duration::from_secs(0);
1567		let now = future_expiry - Duration::from_secs(1_000);
1568
1569		let offer = OfferBuilder::new(pubkey(42))
1570			.absolute_expiry(future_expiry)
1571			.build()
1572			.unwrap();
1573		#[cfg(feature = "std")]
1574		assert!(!offer.is_expired());
1575		assert!(!offer.is_expired_no_std(now));
1576		assert_eq!(offer.absolute_expiry(), Some(future_expiry));
1577		assert_eq!(offer.as_tlv_stream().0.absolute_expiry, Some(future_expiry.as_secs()));
1578
1579		let offer = OfferBuilder::new(pubkey(42))
1580			.absolute_expiry(future_expiry)
1581			.absolute_expiry(past_expiry)
1582			.build()
1583			.unwrap();
1584		#[cfg(feature = "std")]
1585		assert!(offer.is_expired());
1586		assert!(offer.is_expired_no_std(now));
1587		assert_eq!(offer.absolute_expiry(), Some(past_expiry));
1588		assert_eq!(offer.as_tlv_stream().0.absolute_expiry, Some(past_expiry.as_secs()));
1589	}
1590
1591	#[test]
1592	fn builds_offer_with_paths() {
1593		let paths = vec![
1594			BlindedMessagePath::from_raw(
1595				pubkey(40), pubkey(41),
1596				vec![
1597					BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
1598					BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
1599				]
1600			),
1601			BlindedMessagePath::from_raw(
1602				pubkey(40), pubkey(41),
1603				vec![
1604					BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
1605					BlindedHop { blinded_node_id: pubkey(46), encrypted_payload: vec![0; 46] },
1606				]
1607			),
1608		];
1609
1610		let offer = OfferBuilder::new(pubkey(42))
1611			.path(paths[0].clone())
1612			.path(paths[1].clone())
1613			.build()
1614			.unwrap();
1615		let tlv_stream = offer.as_tlv_stream();
1616		assert_eq!(offer.paths(), paths.as_slice());
1617		assert_eq!(offer.issuer_signing_pubkey(), Some(pubkey(42)));
1618		assert_ne!(pubkey(42), pubkey(44));
1619		assert_eq!(tlv_stream.0.paths, Some(&paths));
1620		assert_eq!(tlv_stream.0.issuer_id, Some(&pubkey(42)));
1621	}
1622
1623	#[test]
1624	fn builds_offer_with_issuer() {
1625		let offer = OfferBuilder::new(pubkey(42))
1626			.issuer("foo".into())
1627			.build()
1628			.unwrap();
1629		assert_eq!(offer.issuer(), Some(PrintableString("foo")));
1630		assert_eq!(offer.as_tlv_stream().0.issuer, Some(&String::from("foo")));
1631
1632		let offer = OfferBuilder::new(pubkey(42))
1633			.issuer("foo".into())
1634			.issuer("bar".into())
1635			.build()
1636			.unwrap();
1637		assert_eq!(offer.issuer(), Some(PrintableString("bar")));
1638		assert_eq!(offer.as_tlv_stream().0.issuer, Some(&String::from("bar")));
1639	}
1640
1641	#[test]
1642	fn builds_offer_with_supported_quantity() {
1643		let one = NonZeroU64::new(1).unwrap();
1644		let ten = NonZeroU64::new(10).unwrap();
1645
1646		let offer = OfferBuilder::new(pubkey(42))
1647			.supported_quantity(Quantity::One)
1648			.build()
1649			.unwrap();
1650		let tlv_stream = offer.as_tlv_stream();
1651		assert!(!offer.expects_quantity());
1652		assert_eq!(offer.supported_quantity(), Quantity::One);
1653		assert_eq!(tlv_stream.0.quantity_max, None);
1654
1655		let offer = OfferBuilder::new(pubkey(42))
1656			.supported_quantity(Quantity::Unbounded)
1657			.build()
1658			.unwrap();
1659		let tlv_stream = offer.as_tlv_stream();
1660		assert!(offer.expects_quantity());
1661		assert_eq!(offer.supported_quantity(), Quantity::Unbounded);
1662		assert_eq!(tlv_stream.0.quantity_max, Some(0));
1663
1664		let offer = OfferBuilder::new(pubkey(42))
1665			.supported_quantity(Quantity::Bounded(ten))
1666			.build()
1667			.unwrap();
1668		let tlv_stream = offer.as_tlv_stream();
1669		assert!(offer.expects_quantity());
1670		assert_eq!(offer.supported_quantity(), Quantity::Bounded(ten));
1671		assert_eq!(tlv_stream.0.quantity_max, Some(10));
1672
1673		let offer = OfferBuilder::new(pubkey(42))
1674			.supported_quantity(Quantity::Bounded(one))
1675			.build()
1676			.unwrap();
1677		let tlv_stream = offer.as_tlv_stream();
1678		assert!(offer.expects_quantity());
1679		assert_eq!(offer.supported_quantity(), Quantity::Bounded(one));
1680		assert_eq!(tlv_stream.0.quantity_max, Some(1));
1681
1682		let offer = OfferBuilder::new(pubkey(42))
1683			.supported_quantity(Quantity::Bounded(ten))
1684			.supported_quantity(Quantity::One)
1685			.build()
1686			.unwrap();
1687		let tlv_stream = offer.as_tlv_stream();
1688		assert!(!offer.expects_quantity());
1689		assert_eq!(offer.supported_quantity(), Quantity::One);
1690		assert_eq!(tlv_stream.0.quantity_max, None);
1691	}
1692
1693	#[test]
1694	fn fails_requesting_invoice_with_unknown_required_features() {
1695		let expanded_key = ExpandedKey::new([42; 32]);
1696		let entropy = FixedEntropy {};
1697		let nonce = Nonce::from_entropy_source(&entropy);
1698		let secp_ctx = Secp256k1::new();
1699		let payment_id = PaymentId([1; 32]);
1700
1701		match OfferBuilder::new(pubkey(42))
1702			.features_unchecked(OfferFeatures::unknown())
1703			.build().unwrap()
1704			.request_invoice(&expanded_key, nonce, &secp_ctx, payment_id)
1705		{
1706			Ok(_) => panic!("expected error"),
1707			Err(e) => assert_eq!(e, Bolt12SemanticError::UnknownRequiredFeatures),
1708		}
1709	}
1710
1711	#[test]
1712	fn parses_offer_with_chains() {
1713		let offer = OfferBuilder::new(pubkey(42))
1714			.chain(Network::Bitcoin)
1715			.chain(Network::Testnet)
1716			.build()
1717			.unwrap();
1718		if let Err(e) = offer.to_string().parse::<Offer>() {
1719			panic!("error parsing offer: {:?}", e);
1720		}
1721	}
1722
1723	#[test]
1724	fn parses_offer_with_amount() {
1725		let offer = OfferBuilder::new(pubkey(42))
1726			.amount(Amount::Bitcoin { amount_msats: 1000 })
1727			.build()
1728			.unwrap();
1729		if let Err(e) = offer.to_string().parse::<Offer>() {
1730			panic!("error parsing offer: {:?}", e);
1731		}
1732
1733		let mut tlv_stream = offer.as_tlv_stream();
1734		tlv_stream.0.amount = Some(1000);
1735		tlv_stream.0.currency = Some(b"USD");
1736
1737		let mut encoded_offer = Vec::new();
1738		tlv_stream.write(&mut encoded_offer).unwrap();
1739
1740		if let Err(e) = Offer::try_from(encoded_offer) {
1741			panic!("error parsing offer: {:?}", e);
1742		}
1743
1744		let mut tlv_stream = offer.as_tlv_stream();
1745		tlv_stream.0.amount = None;
1746		tlv_stream.0.currency = Some(b"USD");
1747
1748		let mut encoded_offer = Vec::new();
1749		tlv_stream.write(&mut encoded_offer).unwrap();
1750
1751		match Offer::try_from(encoded_offer) {
1752			Ok(_) => panic!("expected error"),
1753			Err(e) => assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingAmount)),
1754		}
1755
1756		let mut tlv_stream = offer.as_tlv_stream();
1757		tlv_stream.0.amount = Some(MAX_VALUE_MSAT + 1);
1758		tlv_stream.0.currency = None;
1759
1760		let mut encoded_offer = Vec::new();
1761		tlv_stream.write(&mut encoded_offer).unwrap();
1762
1763		match Offer::try_from(encoded_offer) {
1764			Ok(_) => panic!("expected error"),
1765			Err(e) => assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidAmount)),
1766		}
1767	}
1768
1769	#[test]
1770	fn parses_offer_with_description() {
1771		let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1772		if let Err(e) = offer.to_string().parse::<Offer>() {
1773			panic!("error parsing offer: {:?}", e);
1774		}
1775
1776		let offer = OfferBuilder::new(pubkey(42))
1777			.description("foo".to_string())
1778			.amount_msats(1000)
1779			.build().unwrap();
1780		if let Err(e) = offer.to_string().parse::<Offer>() {
1781			panic!("error parsing offer: {:?}", e);
1782		}
1783
1784		let mut tlv_stream = offer.as_tlv_stream();
1785		tlv_stream.0.description = None;
1786
1787		let mut encoded_offer = Vec::new();
1788		tlv_stream.write(&mut encoded_offer).unwrap();
1789
1790		match Offer::try_from(encoded_offer) {
1791			Ok(_) => panic!("expected error"),
1792			Err(e) => {
1793				assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingDescription));
1794			},
1795		}
1796	}
1797
1798	#[test]
1799	fn parses_offer_with_paths() {
1800		let offer = OfferBuilder::new(pubkey(42))
1801			.path(BlindedMessagePath::from_raw(
1802				pubkey(40), pubkey(41),
1803				vec![
1804					BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
1805					BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
1806				]
1807			))
1808			.path(BlindedMessagePath::from_raw(
1809					pubkey(40), pubkey(41),
1810					vec![
1811						BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
1812						BlindedHop { blinded_node_id: pubkey(46), encrypted_payload: vec![0; 46] },
1813					]
1814			))
1815			.build()
1816			.unwrap();
1817		if let Err(e) = offer.to_string().parse::<Offer>() {
1818			panic!("error parsing offer: {:?}", e);
1819		}
1820
1821		let offer = OfferBuilder::new(pubkey(42))
1822			.path(BlindedMessagePath::from_raw(
1823					pubkey(40), pubkey(41),
1824					vec![
1825						BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
1826						BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
1827					]
1828			))
1829			.clear_issuer_signing_pubkey()
1830			.build()
1831			.unwrap();
1832		if let Err(e) = offer.to_string().parse::<Offer>() {
1833			panic!("error parsing offer: {:?}", e);
1834		}
1835
1836		let mut builder = OfferBuilder::new(pubkey(42));
1837		builder.offer.paths = Some(vec![]);
1838
1839		let offer = builder.build().unwrap();
1840		match offer.to_string().parse::<Offer>() {
1841			Ok(_) => panic!("expected error"),
1842			Err(e) => {
1843				assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingPaths));
1844			},
1845		}
1846	}
1847
1848	#[test]
1849	fn parses_offer_with_quantity() {
1850		let offer = OfferBuilder::new(pubkey(42))
1851			.supported_quantity(Quantity::One)
1852			.build()
1853			.unwrap();
1854		if let Err(e) = offer.to_string().parse::<Offer>() {
1855			panic!("error parsing offer: {:?}", e);
1856		}
1857
1858		let offer = OfferBuilder::new(pubkey(42))
1859			.supported_quantity(Quantity::Unbounded)
1860			.build()
1861			.unwrap();
1862		if let Err(e) = offer.to_string().parse::<Offer>() {
1863			panic!("error parsing offer: {:?}", e);
1864		}
1865
1866		let offer = OfferBuilder::new(pubkey(42))
1867			.supported_quantity(Quantity::Bounded(NonZeroU64::new(10).unwrap()))
1868			.build()
1869			.unwrap();
1870		if let Err(e) = offer.to_string().parse::<Offer>() {
1871			panic!("error parsing offer: {:?}", e);
1872		}
1873
1874		let offer = OfferBuilder::new(pubkey(42))
1875			.supported_quantity(Quantity::Bounded(NonZeroU64::new(1).unwrap()))
1876			.build()
1877			.unwrap();
1878		if let Err(e) = offer.to_string().parse::<Offer>() {
1879			panic!("error parsing offer: {:?}", e);
1880		}
1881	}
1882
1883	#[test]
1884	fn parses_offer_with_issuer_id() {
1885		let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1886		if let Err(e) = offer.to_string().parse::<Offer>() {
1887			panic!("error parsing offer: {:?}", e);
1888		}
1889
1890		let mut tlv_stream = offer.as_tlv_stream();
1891		tlv_stream.0.issuer_id = None;
1892
1893		let mut encoded_offer = Vec::new();
1894		tlv_stream.write(&mut encoded_offer).unwrap();
1895
1896		match Offer::try_from(encoded_offer) {
1897			Ok(_) => panic!("expected error"),
1898			Err(e) => {
1899				assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingIssuerSigningPubkey));
1900			},
1901		}
1902	}
1903
1904	#[test]
1905	fn parses_offer_with_unknown_tlv_records() {
1906		const UNKNOWN_ODD_TYPE: u64 = OFFER_TYPES.end - 1;
1907		assert!(UNKNOWN_ODD_TYPE % 2 == 1);
1908
1909		let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1910
1911		let mut encoded_offer = Vec::new();
1912		offer.write(&mut encoded_offer).unwrap();
1913		BigSize(UNKNOWN_ODD_TYPE).write(&mut encoded_offer).unwrap();
1914		BigSize(32).write(&mut encoded_offer).unwrap();
1915		[42u8; 32].write(&mut encoded_offer).unwrap();
1916
1917		match Offer::try_from(encoded_offer.clone()) {
1918			Ok(offer) => assert_eq!(offer.bytes, encoded_offer),
1919			Err(e) => panic!("error parsing offer: {:?}", e),
1920		}
1921
1922		const UNKNOWN_EVEN_TYPE: u64 = OFFER_TYPES.end - 2;
1923		assert!(UNKNOWN_EVEN_TYPE % 2 == 0);
1924
1925		let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1926
1927		let mut encoded_offer = Vec::new();
1928		offer.write(&mut encoded_offer).unwrap();
1929		BigSize(UNKNOWN_EVEN_TYPE).write(&mut encoded_offer).unwrap();
1930		BigSize(32).write(&mut encoded_offer).unwrap();
1931		[42u8; 32].write(&mut encoded_offer).unwrap();
1932
1933		match Offer::try_from(encoded_offer) {
1934			Ok(_) => panic!("expected error"),
1935			Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::UnknownRequiredFeature)),
1936		}
1937	}
1938
1939	#[test]
1940	fn parses_offer_with_experimental_tlv_records() {
1941		let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1942
1943		let mut encoded_offer = Vec::new();
1944		offer.write(&mut encoded_offer).unwrap();
1945		BigSize(EXPERIMENTAL_OFFER_TYPES.start + 1).write(&mut encoded_offer).unwrap();
1946		BigSize(32).write(&mut encoded_offer).unwrap();
1947		[42u8; 32].write(&mut encoded_offer).unwrap();
1948
1949		match Offer::try_from(encoded_offer.clone()) {
1950			Ok(offer) => assert_eq!(offer.bytes, encoded_offer),
1951			Err(e) => panic!("error parsing offer: {:?}", e),
1952		}
1953
1954		let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1955
1956		let mut encoded_offer = Vec::new();
1957		offer.write(&mut encoded_offer).unwrap();
1958		BigSize(EXPERIMENTAL_OFFER_TYPES.start).write(&mut encoded_offer).unwrap();
1959		BigSize(32).write(&mut encoded_offer).unwrap();
1960		[42u8; 32].write(&mut encoded_offer).unwrap();
1961
1962		match Offer::try_from(encoded_offer) {
1963			Ok(_) => panic!("expected error"),
1964			Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::UnknownRequiredFeature)),
1965		}
1966	}
1967
1968	#[test]
1969	fn fails_parsing_offer_with_out_of_range_tlv_records() {
1970		let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1971
1972		let mut encoded_offer = Vec::new();
1973		offer.write(&mut encoded_offer).unwrap();
1974		BigSize(OFFER_TYPES.end).write(&mut encoded_offer).unwrap();
1975		BigSize(32).write(&mut encoded_offer).unwrap();
1976		[42u8; 32].write(&mut encoded_offer).unwrap();
1977
1978		match Offer::try_from(encoded_offer) {
1979			Ok(_) => panic!("expected error"),
1980			Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1981		}
1982
1983		let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1984
1985		let mut encoded_offer = Vec::new();
1986		offer.write(&mut encoded_offer).unwrap();
1987		BigSize(EXPERIMENTAL_OFFER_TYPES.end).write(&mut encoded_offer).unwrap();
1988		BigSize(32).write(&mut encoded_offer).unwrap();
1989		[42u8; 32].write(&mut encoded_offer).unwrap();
1990
1991		match Offer::try_from(encoded_offer) {
1992			Ok(_) => panic!("expected error"),
1993			Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1994		}
1995	}
1996}
1997
1998#[cfg(test)]
1999mod bolt12_tests {
2000	use super::{Bolt12ParseError, Bolt12SemanticError, Offer};
2001	use crate::ln::msgs::DecodeError;
2002
2003	#[test]
2004	fn parses_bech32_encoded_offers() {
2005		let offers = [
2006			// Minimal bolt12 offer
2007			"lno1zcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese",
2008			// with description (but no amount)
2009			"lno1pgx9getnwss8vetrw3hhyuckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg",
2010
2011			// for testnet
2012			"lno1qgsyxjtl6luzd9t3pr62xr7eemp6awnejusgf6gw45q75vcfqqqqqqq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
2013
2014			// for bitcoin (redundant)
2015			"lno1qgsxlc5vp2m0rvmjcxn2y34wv0m5lyc7sdj7zksgn35dvxgqqqqqqqq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
2016
2017			// for bitcoin or liquidv1
2018			"lno1qfqpge38tqmzyrdjj3x2qkdr5y80dlfw56ztq6yd9sme995g3gsxqqm0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq9qc4r9wd6zqan9vd6x7unnzcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese",
2019
2020			// with metadata
2021			"lno1qsgqqqqqqqqqqqqqqqqqqqqqqqqqqzsv23jhxapqwejkxar0wfe3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
2022
2023			// with amount
2024			"lno1pqpzwyq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
2025
2026			// with currency
2027			"lno1qcp4256ypqpzwyq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
2028
2029			// with expiry
2030			"lno1pgx9getnwss8vetrw3hhyucwq3ay997czcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese",
2031
2032			// with issuer
2033			"lno1pgx9getnwss8vetrw3hhyucjy358garswvaz7tmzdak8gvfj9ehhyeeqgf85c4p3xgsxjmnyw4ehgunfv4e3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
2034
2035			// with quantity
2036			"lno1pgx9getnwss8vetrw3hhyuc5qyz3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
2037
2038			// with unlimited (or unknown) quantity
2039			"lno1pgx9getnwss8vetrw3hhyuc5qqtzzqhwcuj966ma9n9nqwqtl032xeyv6755yeflt235pmww58egx6rxry",
2040
2041			// with single quantity (weird but valid)
2042			"lno1pgx9getnwss8vetrw3hhyuc5qyq3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
2043
2044			// with feature
2045			"lno1pgx9getnwss8vetrw3hhyucvp5yqqqqqqqqqqqqqqqqqqqqkyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg",
2046
2047			// with blinded path via Bob (0x424242...), blinding 020202...
2048			"lno1pgx9getnwss8vetrw3hhyucs5ypjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k8qzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqpqqqqqqqqqqqqqqqqqqqqqqqqqqqzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqzq3zyg3zyg3zyg3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
2049
2050			// ... and with sciddir introduction node
2051			"lno1pgx9getnwss8vetrw3hhyucs3yqqqqqqqqqqqqp2qgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqyqqqqqqqqqqqqqqqqqqqqqqqqqqqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqqgzyg3zyg3zyg3z93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
2052
2053			// with no issuer_id and blinded path via Bob (0x424242...), blinding 020202...
2054			"lno1pgx9getnwss8vetrw3hhyucs5ypjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k8qzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqpqqqqqqqqqqqqqqqqqqqqqqqqqqqzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqzq3zyg3zyg3zygs",
2055
2056			//... and with second blinded path via 1x2x3 (direction 1), blinding 020202...
2057			"lno1pgx9getnwss8vetrw3hhyucsl5qj5qeyv5l2cs6y3qqzesrth7mlzrlp3xg7xhulusczm04x6g6nms9trspqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqqsqqqqqqqqqqqqqqqqqqqqqqqqqqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqpqg3zyg3zyg3zygpqqqqzqqqqgqqxqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqqgqqqqqqqqqqqqqqqqqqqqqqqqqqqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqqsg3zyg3zyg3zygtzzqhwcuj966ma9n9nqwqtl032xeyv6755yeflt235pmww58egx6rxry",
2058
2059			// unknown odd field
2060			"lno1pgx9getnwss8vetrw3hhyuckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxfppf5x2mrvdamk7unvvs",
2061
2062			// unknown odd experimental field
2063			"lno1pgx9getnwss8vetrw3hhyuckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvx078wdv5gg2dpjkcmr0wahhymry",
2064		];
2065		for encoded_offer in &offers {
2066			if let Err(e) = encoded_offer.parse::<Offer>() {
2067				panic!("Invalid offer ({:?}): {}", e, encoded_offer);
2068			}
2069		}
2070	}
2071
2072	#[test]
2073	fn fails_parsing_bech32_encoded_offers() {
2074		// Malformed: fields out of order
2075		assert_eq!(
2076			"lno1zcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszpgz5znzfgdzs".parse::<Offer>(),
2077			Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2078		);
2079
2080		// Malformed: unknown even TLV type 78
2081		assert_eq!(
2082			"lno1pgz5znzfgdz3vggzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpysgr0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq".parse::<Offer>(),
2083			Err(Bolt12ParseError::Decode(DecodeError::UnknownRequiredFeature)),
2084		);
2085
2086		// Malformed: empty
2087		assert_eq!(
2088			"lno1".parse::<Offer>(),
2089			Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingIssuerSigningPubkey)),
2090		);
2091
2092		// Malformed: truncated at type
2093		assert_eq!(
2094			"lno1pg".parse::<Offer>(),
2095			Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2096		);
2097
2098		// Malformed: truncated in length
2099		assert_eq!(
2100			"lno1pt7s".parse::<Offer>(),
2101			Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2102		);
2103
2104		// Malformed: truncated after length
2105		assert_eq!(
2106			"lno1pgpq".parse::<Offer>(),
2107			Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2108		);
2109
2110		// Malformed: truncated in description
2111		assert_eq!(
2112			"lno1pgpyz".parse::<Offer>(),
2113			Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2114		);
2115
2116		// Malformed: invalid offer_chains length
2117		assert_eq!(
2118			"lno1qgqszzs9g9xyjs69zcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2119			Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2120		);
2121
2122		// Malformed: truncated currency UTF-8
2123		assert_eq!(
2124			"lno1qcqcqzs9g9xyjs69zcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2125			Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2126		);
2127
2128		// Malformed: invalid currency UTF-8
2129		assert_eq!(
2130			"lno1qcpgqsg2q4q5cj2rg5tzzqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqg".parse::<Offer>(),
2131			Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2132		);
2133
2134		// Malformed: truncated description UTF-8
2135		assert_eq!(
2136			"lno1pgqcq93pqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqy".parse::<Offer>(),
2137			Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2138		);
2139
2140		// Malformed: invalid description UTF-8
2141		assert_eq!(
2142			"lno1pgpgqsgkyypqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs".parse::<Offer>(),
2143			Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2144		);
2145
2146		// Malformed: truncated offer_paths
2147		assert_eq!(
2148			"lno1pgz5znzfgdz3qqgpzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2149			Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2150		);
2151
2152		// Malformed: zero num_hops in blinded_path
2153		assert_eq!(
2154			"lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2155			Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2156		);
2157
2158		// Malformed: truncated onionmsg_hop in blinded_path
2159		assert_eq!(
2160			"lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqspqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqgkyypqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs".parse::<Offer>(),
2161			Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2162		);
2163
2164		// Malformed: bad first_node_id in blinded_path
2165		assert_eq!(
2166			"lno1pgz5znzfgdz3qqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqspqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqgqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2167			Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2168		);
2169
2170		// Malformed: bad blinding in blinded_path
2171		assert_eq!(
2172			"lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcpqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqgqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2173			Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2174		);
2175
2176		// Malformed: bad blinded_node_id in onionmsg_hop
2177		assert_eq!(
2178			"lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqspqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqgqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2179			Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2180		);
2181
2182		// Malformed: truncated issuer UTF-8
2183		assert_eq!(
2184			"lno1pgz5znzfgdz3yqvqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2185			Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2186		);
2187
2188		// Malformed: invalid issuer UTF-8
2189		assert_eq!(
2190			"lno1pgz5znzfgdz3yq5qgytzzqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqg".parse::<Offer>(),
2191			Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2192		);
2193
2194		// Malformed: invalid offer_issuer_id
2195		assert_eq!(
2196			"lno1pgz5znzfgdz3vggzqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvps".parse::<Offer>(),
2197			Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2198		);
2199
2200		// Contains type >= 80
2201		assert_eq!(
2202			"lno1pgz5znzfgdz3vggzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgp9qgr0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq".parse::<Offer>(),
2203			Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2204		);
2205
2206		// Contains type > 1999999999
2207		assert_eq!(
2208			"lno1pgz5znzfgdz3vggzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgp06ae4jsq9qgr0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq".parse::<Offer>(),
2209			Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2210		);
2211
2212		// Contains unknown even type (1000000002)
2213		assert_eq!(
2214			"lno1pgz5znzfgdz3vggzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgp06wu6egp9qgr0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq".parse::<Offer>(),
2215			Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2216		);
2217
2218		// TODO: Resolved in spec https://github.com/lightning/bolts/pull/798/files#r1334851959
2219		// Contains unknown feature 22
2220		assert!(
2221			"lno1pgx9getnwss8vetrw3hhyucvqdqqqqqkyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg".parse::<Offer>().is_ok()
2222		);
2223
2224		// Missing offer_description
2225		assert_eq!(
2226			// TODO: Match the spec once it is updated.
2227			"lno1pqpq86qkyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg".parse::<Offer>(),
2228			Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingDescription)),
2229		);
2230
2231		// Missing offer_issuer_id
2232		assert_eq!(
2233			"lno1pgx9getnwss8vetrw3hhyuc".parse::<Offer>(),
2234			Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingIssuerSigningPubkey)),
2235		);
2236
2237		// Second offer_path is empty
2238		assert_eq!(
2239			"lno1pgx9getnwss8vetrw3hhyucsespjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k8qzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqpqqqqqqqqqqqqqqqqqqqqqqqqqqqzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqzq3zyg3zyg3zygszqqqqyqqqqsqqvpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsq".parse::<Offer>(),
2240			Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2241		);
2242	}
2243}