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