1use core::borrow::Borrow;
81use bitcoin::constants::ChainHash;
82use bitcoin::network::Network;
83use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, self};
84use core::hash::{Hash, Hasher};
85use core::num::NonZeroU64;
86use core::str::FromStr;
87use core::time::Duration;
88use crate::io;
89use crate::blinded_path::message::BlindedMessagePath;
90use crate::ln::channelmanager::PaymentId;
91use crate::types::features::OfferFeatures;
92use crate::ln::inbound_payment::{ExpandedKey, IV_LEN};
93use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
94use crate::offers::merkle::{TaggedHash, TlvRecord, TlvStream};
95use crate::offers::nonce::Nonce;
96use crate::offers::parse::{Bech32Encode, Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
97use crate::offers::signer::{Metadata, MetadataMaterial, self};
98use crate::util::ser::{CursorReadable, HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
99use crate::util::string::PrintableString;
100
101#[cfg(not(c_bindings))]
102use {
103 crate::offers::invoice_request::InvoiceRequestBuilder,
104};
105#[cfg(c_bindings)]
106use {
107 crate::offers::invoice_request::InvoiceRequestWithDerivedPayerSigningPubkeyBuilder,
108};
109
110#[allow(unused_imports)]
111use crate::prelude::*;
112
113#[cfg(feature = "std")]
114use std::time::SystemTime;
115use bitcoin::hex::impl_fmt_traits;
116
117pub(super) const IV_BYTES_WITH_METADATA: &[u8; IV_LEN] = b"LDK Offer ~~~~~~";
118pub(super) const IV_BYTES_WITHOUT_METADATA: &[u8; IV_LEN] = b"LDK Offer v2~~~~";
119
120#[derive(Clone, Copy, Eq, PartialEq)]
122pub struct OfferId(pub [u8; 32]);
123
124impl OfferId {
125 const ID_TAG: &'static str = "LDK Offer ID";
126
127 fn from_valid_offer_tlv_stream(bytes: &[u8]) -> Self {
128 let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(Self::ID_TAG, bytes);
129 Self(tagged_hash.to_bytes())
130 }
131
132 fn from_valid_invreq_tlv_stream(bytes: &[u8]) -> Self {
133 let tlv_stream = Offer::tlv_stream_iter(bytes);
134 let tagged_hash = TaggedHash::from_tlv_stream(Self::ID_TAG, tlv_stream);
135 Self(tagged_hash.to_bytes())
136 }
137}
138
139impl Borrow<[u8]> for OfferId {
140 fn borrow(&self) -> &[u8] {
141 &self.0[..]
142 }
143}
144
145impl_fmt_traits! {
146 impl fmt_traits for OfferId {
147 const LENGTH: usize = 32;
148 }
149}
150
151impl Writeable for OfferId {
152 fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
153 self.0.write(w)
154 }
155}
156
157impl Readable for OfferId {
158 fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
159 Ok(OfferId(Readable::read(r)?))
160 }
161}
162
163pub 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#[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#[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
202pub trait MetadataStrategy {}
206
207pub struct ExplicitMetadata {}
211
212pub struct DerivedMetadata {}
216
217impl MetadataStrategy for ExplicitMetadata {}
218
219impl MetadataStrategy for DerivedMetadata {}
220
221macro_rules! offer_explicit_metadata_builder_methods { (
222 $self: ident, $self_type: ty, $return_type: ty, $return_value: expr
223) => {
224 pub fn new(signing_pubkey: PublicKey) -> Self {
237 Self {
238 offer: OfferContents {
239 chains: None, metadata: None, amount: None, description: None,
240 features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
241 supported_quantity: Quantity::One, issuer_signing_pubkey: Some(signing_pubkey),
242 #[cfg(test)]
243 experimental_foo: None,
244 },
245 metadata_strategy: core::marker::PhantomData,
246 secp_ctx: None,
247 }
248 }
249
250 pub fn metadata(mut $self: $self_type, metadata: Vec<u8>) -> Result<$return_type, Bolt12SemanticError> {
254 $self.offer.metadata = Some(Metadata::Bytes(metadata));
255 Ok($return_value)
256 }
257} }
258
259macro_rules! offer_derived_metadata_builder_methods { ($secp_context: ty) => {
260 pub fn deriving_signing_pubkey(
275 node_id: PublicKey, expanded_key: &ExpandedKey, nonce: Nonce,
276 secp_ctx: &'a Secp256k1<$secp_context>
277 ) -> Self {
278 let derivation_material = MetadataMaterial::new(nonce, expanded_key, None);
279 let metadata = Metadata::DerivedSigningPubkey(derivation_material);
280 Self {
281 offer: OfferContents {
282 chains: None, metadata: Some(metadata), amount: None, description: None,
283 features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
284 supported_quantity: Quantity::One, issuer_signing_pubkey: Some(node_id),
285 #[cfg(test)]
286 experimental_foo: None,
287 },
288 metadata_strategy: core::marker::PhantomData,
289 secp_ctx: Some(secp_ctx),
290 }
291 }
292} }
293
294macro_rules! offer_builder_methods { (
295 $self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)?
296) => {
297 pub fn chain($self: $self_type, network: Network) -> $return_type {
304 $self.chain_hash(ChainHash::using_genesis_block(network))
305 }
306
307 pub(crate) fn chain_hash($($self_mut)* $self: $self_type, chain: ChainHash) -> $return_type {
314 let chains = $self.offer.chains.get_or_insert_with(Vec::new);
315 if !chains.contains(&chain) {
316 chains.push(chain);
317 }
318
319 $return_value
320 }
321
322 pub fn amount_msats($self: $self_type, amount_msats: u64) -> $return_type {
326 $self.amount(Amount::Bitcoin { amount_msats })
327 }
328
329 pub(super) fn amount($($self_mut)* $self: $self_type, amount: Amount) -> $return_type {
333 $self.offer.amount = Some(amount);
334 $return_value
335 }
336
337 #[cfg_attr(feature = "std", doc = "Any expiry that has already passed is valid and can be checked for using [`Offer::is_expired`].")]
339 pub fn absolute_expiry($($self_mut)* $self: $self_type, absolute_expiry: Duration) -> $return_type {
342 $self.offer.absolute_expiry = Some(absolute_expiry);
343 $return_value
344 }
345
346 pub fn description($($self_mut)* $self: $self_type, description: String) -> $return_type {
350 $self.offer.description = Some(description);
351 $return_value
352 }
353
354 pub fn issuer($($self_mut)* $self: $self_type, issuer: String) -> $return_type {
358 $self.offer.issuer = Some(issuer);
359 $return_value
360 }
361
362 pub fn path($($self_mut)* $self: $self_type, path: BlindedMessagePath) -> $return_type {
368 $self.offer.paths.get_or_insert_with(Vec::new).push(path);
369 $return_value
370 }
371
372 pub fn supported_quantity($($self_mut)* $self: $self_type, quantity: Quantity) -> $return_type {
377 $self.offer.supported_quantity = quantity;
378 $return_value
379 }
380
381 pub fn build($($self_mut)* $self: $self_type) -> Result<Offer, Bolt12SemanticError> {
383 match $self.offer.amount {
384 Some(Amount::Bitcoin { amount_msats }) => {
385 if amount_msats > MAX_VALUE_MSAT {
386 return Err(Bolt12SemanticError::InvalidAmount);
387 }
388 },
389 Some(Amount::Currency { .. }) => return Err(Bolt12SemanticError::UnsupportedCurrency),
390 None => {},
391 }
392
393 if $self.offer.amount.is_some() && $self.offer.description.is_none() {
394 $self.offer.description = Some(String::new());
395 }
396
397 if let Some(chains) = &$self.offer.chains {
398 if chains.len() == 1 && chains[0] == $self.offer.implied_chain() {
399 $self.offer.chains = None;
400 }
401 }
402
403 Ok($self.build_without_checks())
404 }
405
406 fn build_without_checks($($self_mut)* $self: $self_type) -> Offer {
407 if let Some(mut metadata) = $self.offer.metadata.take() {
408 if metadata.has_derivation_material() {
410
411 let iv_bytes = if $self.offer.paths.is_none() {
414 metadata = metadata.without_keys();
415 IV_BYTES_WITH_METADATA
416 } else {
417 IV_BYTES_WITHOUT_METADATA
418 };
419
420 let mut tlv_stream = $self.offer.as_tlv_stream();
421 debug_assert_eq!(tlv_stream.0.metadata, None);
422 tlv_stream.0.metadata = None;
423 if metadata.derives_recipient_keys() {
424 tlv_stream.0.issuer_id = None;
425 }
426
427 let (derived_metadata, keys) =
431 metadata.derive_from(iv_bytes, tlv_stream, $self.secp_ctx);
432 match keys {
433 Some(keys) => $self.offer.issuer_signing_pubkey = Some(keys.public_key()),
434 None => $self.offer.metadata = Some(derived_metadata),
435 }
436 } else {
437 $self.offer.metadata = Some(metadata);
438 }
439 }
440
441 const OFFER_ALLOCATION_SIZE: usize = 512;
442 let mut bytes = Vec::with_capacity(OFFER_ALLOCATION_SIZE);
443 $self.offer.write(&mut bytes).unwrap();
444
445 let id = OfferId::from_valid_offer_tlv_stream(&bytes);
446
447 Offer {
448 bytes,
449 #[cfg(not(c_bindings))]
450 contents: $self.offer,
451 #[cfg(c_bindings)]
452 contents: $self.offer.clone(),
453 id,
454 }
455 }
456} }
457
458#[cfg(test)]
459macro_rules! offer_builder_test_methods { (
460 $self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)?
461) => {
462 #[cfg_attr(c_bindings, allow(dead_code))]
463 fn features_unchecked($($self_mut)* $self: $self_type, features: OfferFeatures) -> $return_type {
464 $self.offer.features = features;
465 $return_value
466 }
467
468 #[cfg_attr(c_bindings, allow(dead_code))]
469 pub(crate) fn clear_chains($($self_mut)* $self: $self_type) -> $return_type {
470 $self.offer.chains = None;
471 $return_value
472 }
473
474 #[cfg_attr(c_bindings, allow(dead_code))]
475 pub(crate) fn clear_paths($($self_mut)* $self: $self_type) -> $return_type {
476 $self.offer.paths = None;
477 $return_value
478 }
479
480 #[cfg_attr(c_bindings, allow(dead_code))]
481 pub(crate) fn clear_issuer_signing_pubkey($($self_mut)* $self: $self_type) -> $return_type {
482 $self.offer.issuer_signing_pubkey = None;
483 $return_value
484 }
485
486 #[cfg_attr(c_bindings, allow(dead_code))]
487 pub(super) fn experimental_foo($($self_mut)* $self: $self_type, experimental_foo: u64) -> $return_type {
488 $self.offer.experimental_foo = Some(experimental_foo);
489 $return_value
490 }
491
492 #[cfg_attr(c_bindings, allow(dead_code))]
493 pub(super) fn build_unchecked($self: $self_type) -> Offer {
494 $self.build_without_checks()
495 }
496} }
497
498impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
499 offer_builder_methods!(self, Self, Self, self, mut);
500
501 #[cfg(test)]
502 offer_builder_test_methods!(self, Self, Self, self, mut);
503}
504
505impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> {
506 offer_explicit_metadata_builder_methods!(self, Self, Self, self);
507}
508
509impl<'a, T: secp256k1::Signing> OfferBuilder<'a, DerivedMetadata, T> {
510 offer_derived_metadata_builder_methods!(T);
511}
512
513#[cfg(all(c_bindings, not(test)))]
514impl<'a> OfferWithExplicitMetadataBuilder<'a> {
515 offer_explicit_metadata_builder_methods!(self, &mut Self, (), ());
516 offer_builder_methods!(self, &mut Self, (), ());
517}
518
519#[cfg(all(c_bindings, test))]
520impl<'a> OfferWithExplicitMetadataBuilder<'a> {
521 offer_explicit_metadata_builder_methods!(self, &mut Self, &mut Self, self);
522 offer_builder_methods!(self, &mut Self, &mut Self, self);
523 offer_builder_test_methods!(self, &mut Self, &mut Self, self);
524}
525
526#[cfg(all(c_bindings, not(test)))]
527impl<'a> OfferWithDerivedMetadataBuilder<'a> {
528 offer_derived_metadata_builder_methods!(secp256k1::All);
529 offer_builder_methods!(self, &mut Self, (), ());
530}
531
532#[cfg(all(c_bindings, test))]
533impl<'a> OfferWithDerivedMetadataBuilder<'a> {
534 offer_derived_metadata_builder_methods!(secp256k1::All);
535 offer_builder_methods!(self, &mut Self, &mut Self, self);
536 offer_builder_test_methods!(self, &mut Self, &mut Self, self);
537}
538
539#[cfg(c_bindings)]
540impl<'a> From<OfferBuilder<'a, DerivedMetadata, secp256k1::All>>
541for OfferWithDerivedMetadataBuilder<'a> {
542 fn from(builder: OfferBuilder<'a, DerivedMetadata, secp256k1::All>) -> Self {
543 let OfferBuilder { offer, metadata_strategy, secp_ctx } = builder;
544
545 Self { offer, metadata_strategy, secp_ctx }
546 }
547}
548
549#[cfg(c_bindings)]
550impl<'a> From<OfferWithDerivedMetadataBuilder<'a>>
551for OfferBuilder<'a, DerivedMetadata, secp256k1::All> {
552 fn from(builder: OfferWithDerivedMetadataBuilder<'a>) -> Self {
553 let OfferWithDerivedMetadataBuilder { offer, metadata_strategy, secp_ctx } = builder;
554
555 Self { offer, metadata_strategy, secp_ctx }
556 }
557}
558
559#[derive(Clone, Debug)]
573pub struct Offer {
574 pub(super) bytes: Vec<u8>,
577 pub(super) contents: OfferContents,
578 id: OfferId,
579}
580
581#[derive(Clone, Debug)]
587#[cfg_attr(test, derive(PartialEq))]
588pub(super) struct OfferContents {
589 chains: Option<Vec<ChainHash>>,
590 metadata: Option<Metadata>,
591 amount: Option<Amount>,
592 description: Option<String>,
593 features: OfferFeatures,
594 absolute_expiry: Option<Duration>,
595 issuer: Option<String>,
596 paths: Option<Vec<BlindedMessagePath>>,
597 supported_quantity: Quantity,
598 issuer_signing_pubkey: Option<PublicKey>,
599 #[cfg(test)]
600 experimental_foo: Option<u64>,
601}
602
603macro_rules! offer_accessors { ($self: ident, $contents: expr) => {
604 pub fn chains(&$self) -> Vec<bitcoin::constants::ChainHash> {
611 $contents.chains()
612 }
613
614 pub fn metadata(&$self) -> Option<&Vec<u8>> {
618 $contents.metadata()
619 }
620
621 pub fn amount(&$self) -> Option<$crate::offers::offer::Amount> {
623 $contents.amount()
624 }
625
626 pub fn description(&$self) -> Option<$crate::util::string::PrintableString> {
629 $contents.description()
630 }
631
632 pub fn offer_features(&$self) -> &$crate::types::features::OfferFeatures {
634 &$contents.features()
635 }
636
637 pub fn absolute_expiry(&$self) -> Option<core::time::Duration> {
641 $contents.absolute_expiry()
642 }
643
644 pub fn issuer(&$self) -> Option<$crate::util::string::PrintableString> {
647 $contents.issuer()
648 }
649
650 pub fn paths(&$self) -> &[$crate::blinded_path::message::BlindedMessagePath] {
653 $contents.paths()
654 }
655
656 pub fn supported_quantity(&$self) -> $crate::offers::offer::Quantity {
658 $contents.supported_quantity()
659 }
660
661 pub fn issuer_signing_pubkey(&$self) -> Option<bitcoin::secp256k1::PublicKey> {
673 $contents.issuer_signing_pubkey()
674 }
675} }
676
677impl Offer {
678 offer_accessors!(self, self.contents);
679
680 pub fn id(&self) -> OfferId {
682 self.id
683 }
684
685 pub(super) fn implied_chain(&self) -> ChainHash {
686 self.contents.implied_chain()
687 }
688
689 pub fn supports_chain(&self, chain: ChainHash) -> bool {
691 self.contents.supports_chain(chain)
692 }
693
694 #[cfg(feature = "std")]
696 pub fn is_expired(&self) -> bool {
697 self.contents.is_expired()
698 }
699
700 pub fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
702 self.contents.is_expired_no_std(duration_since_epoch)
703 }
704
705 pub fn is_valid_quantity(&self, quantity: u64) -> bool {
707 self.contents.is_valid_quantity(quantity)
708 }
709
710 pub fn expects_quantity(&self) -> bool {
714 self.contents.expects_quantity()
715 }
716
717 pub(super) fn tlv_stream_iter<'a>(
718 bytes: &'a [u8]
719 ) -> impl core::iter::Iterator<Item = TlvRecord<'a>> {
720 TlvStream::new(bytes).range(OFFER_TYPES)
721 .chain(TlvStream::new(bytes).range(EXPERIMENTAL_OFFER_TYPES))
722 }
723
724 #[cfg(async_payments)]
725 pub(super) fn verify<T: secp256k1::Signing>(
726 &self, nonce: Nonce, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
727 ) -> Result<(OfferId, Option<Keypair>), ()> {
728 self.contents.verify_using_recipient_data(&self.bytes, nonce, key, secp_ctx)
729 }
730}
731
732macro_rules! request_invoice_derived_signing_pubkey { ($self: ident, $builder: ty) => {
733 pub fn request_invoice<
750 'a, 'b,
751 #[cfg(not(c_bindings))]
752 T: secp256k1::Signing
753 >(
754 &'a $self, expanded_key: &ExpandedKey, nonce: Nonce,
755 #[cfg(not(c_bindings))]
756 secp_ctx: &'b Secp256k1<T>,
757 #[cfg(c_bindings)]
758 secp_ctx: &'b Secp256k1<secp256k1::All>,
759 payment_id: PaymentId
760 ) -> Result<$builder, Bolt12SemanticError> {
761 if $self.offer_features().requires_unknown_bits() {
762 return Err(Bolt12SemanticError::UnknownRequiredFeatures);
763 }
764
765 Ok(<$builder>::deriving_signing_pubkey($self, expanded_key, nonce, secp_ctx, payment_id))
766 }
767} }
768
769#[cfg(not(c_bindings))]
770impl Offer {
771 request_invoice_derived_signing_pubkey!(self, InvoiceRequestBuilder<'a, 'b, T>);
772}
773
774#[cfg(c_bindings)]
775impl Offer {
776 request_invoice_derived_signing_pubkey!(self, InvoiceRequestWithDerivedPayerSigningPubkeyBuilder<'a, 'b>);
777}
778
779#[cfg(test)]
780impl Offer {
781 pub(super) fn as_tlv_stream(&self) -> FullOfferTlvStreamRef {
782 self.contents.as_tlv_stream()
783 }
784}
785
786impl AsRef<[u8]> for Offer {
787 fn as_ref(&self) -> &[u8] {
788 &self.bytes
789 }
790}
791
792impl PartialEq for Offer {
793 fn eq(&self, other: &Self) -> bool {
794 self.bytes.eq(&other.bytes)
795 }
796}
797
798impl Eq for Offer {}
799
800impl Hash for Offer {
801 fn hash<H: Hasher>(&self, state: &mut H) {
802 self.bytes.hash(state);
803 }
804}
805
806impl OfferContents {
807 pub fn chains(&self) -> Vec<ChainHash> {
808 self.chains.as_ref().cloned().unwrap_or_else(|| vec![self.implied_chain()])
809 }
810
811 pub fn implied_chain(&self) -> ChainHash {
812 ChainHash::using_genesis_block(Network::Bitcoin)
813 }
814
815 pub fn supports_chain(&self, chain: ChainHash) -> bool {
816 self.chains().contains(&chain)
817 }
818
819 pub fn metadata(&self) -> Option<&Vec<u8>> {
820 self.metadata.as_ref().and_then(|metadata| metadata.as_bytes())
821 }
822
823 pub fn amount(&self) -> Option<Amount> {
824 self.amount
825 }
826
827 pub fn description(&self) -> Option<PrintableString> {
828 self.description.as_ref().map(|description| PrintableString(description))
829 }
830
831 pub fn features(&self) -> &OfferFeatures {
832 &self.features
833 }
834
835 pub fn absolute_expiry(&self) -> Option<Duration> {
836 self.absolute_expiry
837 }
838
839 #[cfg(feature = "std")]
840 pub(super) fn is_expired(&self) -> bool {
841 SystemTime::UNIX_EPOCH
842 .elapsed()
843 .map(|duration_since_epoch| self.is_expired_no_std(duration_since_epoch))
844 .unwrap_or(false)
845 }
846
847 pub(super) fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
848 self.absolute_expiry
849 .map(|absolute_expiry| duration_since_epoch > absolute_expiry)
850 .unwrap_or(false)
851 }
852
853 pub fn issuer(&self) -> Option<PrintableString> {
854 self.issuer.as_ref().map(|issuer| PrintableString(issuer.as_str()))
855 }
856
857 pub fn paths(&self) -> &[BlindedMessagePath] {
858 self.paths.as_ref().map(|paths| paths.as_slice()).unwrap_or(&[])
859 }
860
861 pub(super) fn check_amount_msats_for_quantity(
862 &self, amount_msats: Option<u64>, quantity: Option<u64>
863 ) -> Result<(), Bolt12SemanticError> {
864 let offer_amount_msats = match self.amount {
865 None => 0,
866 Some(Amount::Bitcoin { amount_msats }) => amount_msats,
867 Some(Amount::Currency { .. }) => return Err(Bolt12SemanticError::UnsupportedCurrency),
868 };
869
870 if !self.expects_quantity() || quantity.is_some() {
871 let expected_amount_msats = offer_amount_msats.checked_mul(quantity.unwrap_or(1))
872 .ok_or(Bolt12SemanticError::InvalidAmount)?;
873 let amount_msats = amount_msats.unwrap_or(expected_amount_msats);
874
875 if amount_msats < expected_amount_msats {
876 return Err(Bolt12SemanticError::InsufficientAmount);
877 }
878
879 if amount_msats > MAX_VALUE_MSAT {
880 return Err(Bolt12SemanticError::InvalidAmount);
881 }
882 }
883
884 Ok(())
885 }
886
887 pub fn supported_quantity(&self) -> Quantity {
888 self.supported_quantity
889 }
890
891 pub(super) fn check_quantity(&self, quantity: Option<u64>) -> Result<(), Bolt12SemanticError> {
892 let expects_quantity = self.expects_quantity();
893 match quantity {
894 None if expects_quantity => Err(Bolt12SemanticError::MissingQuantity),
895 Some(_) if !expects_quantity => Err(Bolt12SemanticError::UnexpectedQuantity),
896 Some(quantity) if !self.is_valid_quantity(quantity) => {
897 Err(Bolt12SemanticError::InvalidQuantity)
898 },
899 _ => Ok(()),
900 }
901 }
902
903 fn is_valid_quantity(&self, quantity: u64) -> bool {
904 match self.supported_quantity {
905 Quantity::Bounded(n) => quantity <= n.get(),
906 Quantity::Unbounded => quantity > 0,
907 Quantity::One => quantity == 1,
908 }
909 }
910
911 fn expects_quantity(&self) -> bool {
912 match self.supported_quantity {
913 Quantity::Bounded(_) => true,
914 Quantity::Unbounded => true,
915 Quantity::One => false,
916 }
917 }
918
919 pub(super) fn issuer_signing_pubkey(&self) -> Option<PublicKey> {
920 self.issuer_signing_pubkey
921 }
922
923 pub(super) fn verify_using_metadata<T: secp256k1::Signing>(
924 &self, bytes: &[u8], key: &ExpandedKey, secp_ctx: &Secp256k1<T>
925 ) -> Result<(OfferId, Option<Keypair>), ()> {
926 self.verify(bytes, self.metadata.as_ref(), key, IV_BYTES_WITH_METADATA, secp_ctx)
927 }
928
929 pub(super) fn verify_using_recipient_data<T: secp256k1::Signing>(
930 &self, bytes: &[u8], nonce: Nonce, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
931 ) -> Result<(OfferId, Option<Keypair>), ()> {
932 let metadata = Metadata::RecipientData(nonce);
933 self.verify(bytes, Some(&metadata), key, IV_BYTES_WITHOUT_METADATA, secp_ctx)
934 }
935
936 fn verify<T: secp256k1::Signing>(
938 &self, bytes: &[u8], metadata: Option<&Metadata>, key: &ExpandedKey,
939 iv_bytes: &[u8; IV_LEN], secp_ctx: &Secp256k1<T>
940 ) -> Result<(OfferId, Option<Keypair>), ()> {
941 match metadata {
942 Some(metadata) => {
943 let tlv_stream = TlvStream::new(bytes).range(OFFER_TYPES).filter(|record| {
944 match record.r#type {
945 OFFER_METADATA_TYPE => false,
946 OFFER_ISSUER_ID_TYPE => !metadata.derives_recipient_keys(),
947 _ => true,
948 }
949 })
950 .chain(TlvStream::new(bytes).range(EXPERIMENTAL_OFFER_TYPES));
951
952 let signing_pubkey = match self.issuer_signing_pubkey() {
953 Some(signing_pubkey) => signing_pubkey,
954 None => return Err(()),
955 };
956 let keys = signer::verify_recipient_metadata(
957 metadata.as_ref(), key, iv_bytes, signing_pubkey, tlv_stream, secp_ctx
958 )?;
959
960 let offer_id = OfferId::from_valid_invreq_tlv_stream(bytes);
961
962 Ok((offer_id, keys))
963 },
964 None => Err(()),
965 }
966 }
967
968 pub(super) fn as_tlv_stream(&self) -> FullOfferTlvStreamRef {
969 let (currency, amount) = match &self.amount {
970 None => (None, None),
971 Some(Amount::Bitcoin { amount_msats }) => (None, Some(*amount_msats)),
972 Some(Amount::Currency { iso4217_code, amount }) => (
973 Some(iso4217_code), Some(*amount)
974 ),
975 };
976
977 let features = {
978 if self.features == OfferFeatures::empty() { None } else { Some(&self.features) }
979 };
980
981 let offer = OfferTlvStreamRef {
982 chains: self.chains.as_ref(),
983 metadata: self.metadata(),
984 currency,
985 amount,
986 description: self.description.as_ref(),
987 features,
988 absolute_expiry: self.absolute_expiry.map(|duration| duration.as_secs()),
989 paths: self.paths.as_ref(),
990 issuer: self.issuer.as_ref(),
991 quantity_max: self.supported_quantity.to_tlv_record(),
992 issuer_id: self.issuer_signing_pubkey.as_ref(),
993 };
994
995 let experimental_offer = ExperimentalOfferTlvStreamRef {
996 #[cfg(test)]
997 experimental_foo: self.experimental_foo,
998 };
999
1000 (offer, experimental_offer)
1001 }
1002}
1003
1004impl Readable for Offer {
1005 fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
1006 let bytes: WithoutLength<Vec<u8>> = Readable::read(reader)?;
1007 Self::try_from(bytes.0).map_err(|_| DecodeError::InvalidValue)
1008 }
1009}
1010
1011impl Writeable for Offer {
1012 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
1013 WithoutLength(&self.bytes).write(writer)
1014 }
1015}
1016
1017impl Writeable for OfferContents {
1018 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
1019 self.as_tlv_stream().write(writer)
1020 }
1021}
1022
1023#[derive(Clone, Copy, Debug, PartialEq)]
1026pub enum Amount {
1027 Bitcoin {
1029 amount_msats: u64,
1031 },
1032 Currency {
1034 iso4217_code: CurrencyCode,
1036 amount: u64,
1038 },
1039}
1040
1041pub type CurrencyCode = [u8; 3];
1043
1044#[derive(Clone, Copy, Debug, PartialEq)]
1046pub enum Quantity {
1047 Bounded(NonZeroU64),
1053 Unbounded,
1055 One,
1057}
1058
1059impl Quantity {
1060 fn to_tlv_record(self) -> Option<u64> {
1061 match self {
1062 Quantity::Bounded(n) => Some(n.get()),
1063 Quantity::Unbounded => Some(0),
1064 Quantity::One => None,
1065 }
1066 }
1067}
1068
1069pub(super) const OFFER_TYPES: core::ops::Range<u64> = 1..80;
1071
1072const OFFER_METADATA_TYPE: u64 = 4;
1074
1075const OFFER_ISSUER_ID_TYPE: u64 = 22;
1077
1078tlv_stream!(OfferTlvStream, OfferTlvStreamRef<'a>, OFFER_TYPES, {
1079 (2, chains: (Vec<ChainHash>, WithoutLength)),
1080 (OFFER_METADATA_TYPE, metadata: (Vec<u8>, WithoutLength)),
1081 (6, currency: CurrencyCode),
1082 (8, amount: (u64, HighZeroBytesDroppedBigSize)),
1083 (10, description: (String, WithoutLength)),
1084 (12, features: (OfferFeatures, WithoutLength)),
1085 (14, absolute_expiry: (u64, HighZeroBytesDroppedBigSize)),
1086 (16, paths: (Vec<BlindedMessagePath>, WithoutLength)),
1087 (18, issuer: (String, WithoutLength)),
1088 (20, quantity_max: (u64, HighZeroBytesDroppedBigSize)),
1089 (OFFER_ISSUER_ID_TYPE, issuer_id: PublicKey),
1090});
1091
1092pub(super) const EXPERIMENTAL_OFFER_TYPES: core::ops::Range<u64> = 1_000_000_000..2_000_000_000;
1094
1095#[cfg(not(test))]
1096tlv_stream!(ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, EXPERIMENTAL_OFFER_TYPES, {
1097});
1098
1099#[cfg(test)]
1100tlv_stream!(ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, EXPERIMENTAL_OFFER_TYPES, {
1101 (1_999_999_999, experimental_foo: (u64, HighZeroBytesDroppedBigSize)),
1102});
1103
1104type FullOfferTlvStream = (OfferTlvStream, ExperimentalOfferTlvStream);
1105
1106type FullOfferTlvStreamRef<'a> = (OfferTlvStreamRef<'a>, ExperimentalOfferTlvStreamRef);
1107
1108impl CursorReadable for FullOfferTlvStream {
1109 fn read<R: AsRef<[u8]>>(r: &mut io::Cursor<R>) -> Result<Self, DecodeError> {
1110 let offer = CursorReadable::read(r)?;
1111 let experimental_offer = CursorReadable::read(r)?;
1112
1113 Ok((offer, experimental_offer))
1114 }
1115}
1116
1117impl Bech32Encode for Offer {
1118 const BECH32_HRP: &'static str = "lno";
1119}
1120
1121impl FromStr for Offer {
1122 type Err = Bolt12ParseError;
1123
1124 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
1125 Self::from_bech32_str(s)
1126 }
1127}
1128
1129impl TryFrom<Vec<u8>> for Offer {
1130 type Error = Bolt12ParseError;
1131
1132 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
1133 let offer = ParsedMessage::<FullOfferTlvStream>::try_from(bytes)?;
1134 let ParsedMessage { bytes, tlv_stream } = offer;
1135 let contents = OfferContents::try_from(tlv_stream)?;
1136 let id = OfferId::from_valid_offer_tlv_stream(&bytes);
1137
1138 Ok(Offer { bytes, contents, id })
1139 }
1140}
1141
1142impl TryFrom<FullOfferTlvStream> for OfferContents {
1143 type Error = Bolt12SemanticError;
1144
1145 fn try_from(tlv_stream: FullOfferTlvStream) -> Result<Self, Self::Error> {
1146 let (
1147 OfferTlvStream {
1148 chains, metadata, currency, amount, description, features, absolute_expiry, paths,
1149 issuer, quantity_max, issuer_id,
1150 },
1151 ExperimentalOfferTlvStream {
1152 #[cfg(test)]
1153 experimental_foo,
1154 },
1155 ) = tlv_stream;
1156
1157 let metadata = metadata.map(|metadata| Metadata::Bytes(metadata));
1158
1159 let amount = match (currency, amount) {
1160 (None, None) => None,
1161 (None, Some(amount_msats)) if amount_msats > MAX_VALUE_MSAT => {
1162 return Err(Bolt12SemanticError::InvalidAmount);
1163 },
1164 (None, Some(amount_msats)) => Some(Amount::Bitcoin { amount_msats }),
1165 (Some(_), None) => return Err(Bolt12SemanticError::MissingAmount),
1166 (Some(iso4217_code), Some(amount)) => Some(Amount::Currency { iso4217_code, amount }),
1167 };
1168
1169 if amount.is_some() && description.is_none() {
1170 return Err(Bolt12SemanticError::MissingDescription);
1171 }
1172
1173 let features = features.unwrap_or_else(OfferFeatures::empty);
1174
1175 let absolute_expiry = absolute_expiry
1176 .map(|seconds_from_epoch| Duration::from_secs(seconds_from_epoch));
1177
1178 let supported_quantity = match quantity_max {
1179 None => Quantity::One,
1180 Some(0) => Quantity::Unbounded,
1181 Some(n) => Quantity::Bounded(NonZeroU64::new(n).unwrap()),
1182 };
1183
1184 let (issuer_signing_pubkey, paths) = match (issuer_id, paths) {
1185 (None, None) => return Err(Bolt12SemanticError::MissingIssuerSigningPubkey),
1186 (_, Some(paths)) if paths.is_empty() => return Err(Bolt12SemanticError::MissingPaths),
1187 (issuer_id, paths) => (issuer_id, paths),
1188 };
1189
1190 Ok(OfferContents {
1191 chains, metadata, amount, description, features, absolute_expiry, issuer, paths,
1192 supported_quantity, issuer_signing_pubkey,
1193 #[cfg(test)]
1194 experimental_foo,
1195 })
1196 }
1197}
1198
1199impl core::fmt::Display for Offer {
1200 fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
1201 self.fmt_bech32_str(f)
1202 }
1203}
1204
1205#[cfg(test)]
1206mod tests {
1207 use super::{Amount, EXPERIMENTAL_OFFER_TYPES, ExperimentalOfferTlvStreamRef, OFFER_TYPES, Offer, OfferTlvStreamRef, Quantity};
1208 #[cfg(not(c_bindings))]
1209 use {
1210 super::OfferBuilder,
1211 };
1212 #[cfg(c_bindings)]
1213 use {
1214 super::OfferWithExplicitMetadataBuilder as OfferBuilder,
1215 };
1216
1217 use bitcoin::constants::ChainHash;
1218 use bitcoin::network::Network;
1219 use bitcoin::secp256k1::Secp256k1;
1220 use core::num::NonZeroU64;
1221 use core::time::Duration;
1222 use crate::blinded_path::BlindedHop;
1223 use crate::blinded_path::message::BlindedMessagePath;
1224 use crate::types::features::OfferFeatures;
1225 use crate::ln::channelmanager::PaymentId;
1226 use crate::ln::inbound_payment::ExpandedKey;
1227 use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
1228 use crate::offers::nonce::Nonce;
1229 use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError};
1230 use crate::offers::test_utils::*;
1231 use crate::util::ser::{BigSize, Writeable};
1232 use crate::util::string::PrintableString;
1233
1234 #[test]
1235 fn builds_offer_with_defaults() {
1236 let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1237
1238 let mut buffer = Vec::new();
1239 offer.write(&mut buffer).unwrap();
1240
1241 assert_eq!(offer.bytes, buffer.as_slice());
1242 assert_eq!(offer.chains(), vec![ChainHash::using_genesis_block(Network::Bitcoin)]);
1243 assert!(offer.supports_chain(ChainHash::using_genesis_block(Network::Bitcoin)));
1244 assert_eq!(offer.metadata(), None);
1245 assert_eq!(offer.amount(), None);
1246 assert_eq!(offer.description(), None);
1247 assert_eq!(offer.offer_features(), &OfferFeatures::empty());
1248 assert_eq!(offer.absolute_expiry(), None);
1249 #[cfg(feature = "std")]
1250 assert!(!offer.is_expired());
1251 assert_eq!(offer.paths(), &[]);
1252 assert_eq!(offer.issuer(), None);
1253 assert_eq!(offer.supported_quantity(), Quantity::One);
1254 assert!(!offer.expects_quantity());
1255 assert_eq!(offer.issuer_signing_pubkey(), Some(pubkey(42)));
1256
1257 assert_eq!(
1258 offer.as_tlv_stream(),
1259 (
1260 OfferTlvStreamRef {
1261 chains: None,
1262 metadata: None,
1263 currency: None,
1264 amount: None,
1265 description: None,
1266 features: None,
1267 absolute_expiry: None,
1268 paths: None,
1269 issuer: None,
1270 quantity_max: None,
1271 issuer_id: Some(&pubkey(42)),
1272 },
1273 ExperimentalOfferTlvStreamRef {
1274 experimental_foo: None,
1275 },
1276 ),
1277 );
1278
1279 if let Err(e) = Offer::try_from(buffer) {
1280 panic!("error parsing offer: {:?}", e);
1281 }
1282 }
1283
1284 #[test]
1285 fn builds_offer_with_chains() {
1286 let mainnet = ChainHash::using_genesis_block(Network::Bitcoin);
1287 let testnet = ChainHash::using_genesis_block(Network::Testnet);
1288
1289 let offer = OfferBuilder::new(pubkey(42))
1290 .chain(Network::Bitcoin)
1291 .build()
1292 .unwrap();
1293 assert!(offer.supports_chain(mainnet));
1294 assert_eq!(offer.chains(), vec![mainnet]);
1295 assert_eq!(offer.as_tlv_stream().0.chains, None);
1296
1297 let offer = OfferBuilder::new(pubkey(42))
1298 .chain(Network::Testnet)
1299 .build()
1300 .unwrap();
1301 assert!(offer.supports_chain(testnet));
1302 assert_eq!(offer.chains(), vec![testnet]);
1303 assert_eq!(offer.as_tlv_stream().0.chains, Some(&vec![testnet]));
1304
1305 let offer = OfferBuilder::new(pubkey(42))
1306 .chain(Network::Testnet)
1307 .chain(Network::Testnet)
1308 .build()
1309 .unwrap();
1310 assert!(offer.supports_chain(testnet));
1311 assert_eq!(offer.chains(), vec![testnet]);
1312 assert_eq!(offer.as_tlv_stream().0.chains, Some(&vec![testnet]));
1313
1314 let offer = OfferBuilder::new(pubkey(42))
1315 .chain(Network::Bitcoin)
1316 .chain(Network::Testnet)
1317 .build()
1318 .unwrap();
1319 assert!(offer.supports_chain(mainnet));
1320 assert!(offer.supports_chain(testnet));
1321 assert_eq!(offer.chains(), vec![mainnet, testnet]);
1322 assert_eq!(offer.as_tlv_stream().0.chains, Some(&vec![mainnet, testnet]));
1323 }
1324
1325 #[test]
1326 fn builds_offer_with_metadata() {
1327 let offer = OfferBuilder::new(pubkey(42))
1328 .metadata(vec![42; 32]).unwrap()
1329 .build()
1330 .unwrap();
1331 assert_eq!(offer.metadata(), Some(&vec![42; 32]));
1332 assert_eq!(offer.as_tlv_stream().0.metadata, Some(&vec![42; 32]));
1333
1334 let offer = OfferBuilder::new(pubkey(42))
1335 .metadata(vec![42; 32]).unwrap()
1336 .metadata(vec![43; 32]).unwrap()
1337 .build()
1338 .unwrap();
1339 assert_eq!(offer.metadata(), Some(&vec![43; 32]));
1340 assert_eq!(offer.as_tlv_stream().0.metadata, Some(&vec![43; 32]));
1341 }
1342
1343 #[test]
1344 fn builds_offer_with_metadata_derived() {
1345 let node_id = recipient_pubkey();
1346 let expanded_key = ExpandedKey::new([42; 32]);
1347 let entropy = FixedEntropy {};
1348 let nonce = Nonce::from_entropy_source(&entropy);
1349 let secp_ctx = Secp256k1::new();
1350 let payment_id = PaymentId([1; 32]);
1351
1352 #[cfg(c_bindings)]
1353 use super::OfferWithDerivedMetadataBuilder as OfferBuilder;
1354 let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx)
1355 .amount_msats(1000)
1356 .experimental_foo(42)
1357 .build().unwrap();
1358 assert!(offer.metadata().is_some());
1359 assert_eq!(offer.issuer_signing_pubkey(), Some(node_id));
1360
1361 let invoice_request = offer
1362 .request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1363 .build_and_sign().unwrap();
1364 match invoice_request.verify_using_metadata(&expanded_key, &secp_ctx) {
1365 Ok(invoice_request) => assert_eq!(invoice_request.offer_id, offer.id()),
1366 Err(_) => panic!("unexpected error"),
1367 }
1368
1369 let invoice_request = offer
1371 .request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1372 .build_and_sign().unwrap();
1373 assert!(
1374 invoice_request.verify_using_recipient_data(nonce, &expanded_key, &secp_ctx).is_err()
1375 );
1376
1377 let mut tlv_stream = offer.as_tlv_stream();
1379 tlv_stream.0.amount = Some(100);
1380
1381 let mut encoded_offer = Vec::new();
1382 tlv_stream.write(&mut encoded_offer).unwrap();
1383
1384 let invoice_request = Offer::try_from(encoded_offer).unwrap()
1385 .request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1386 .build_and_sign().unwrap();
1387 assert!(invoice_request.verify_using_metadata(&expanded_key, &secp_ctx).is_err());
1388
1389 let mut tlv_stream = offer.as_tlv_stream();
1391 let metadata = tlv_stream.0.metadata.unwrap().iter().copied().rev().collect();
1392 tlv_stream.0.metadata = Some(&metadata);
1393
1394 let mut encoded_offer = Vec::new();
1395 tlv_stream.write(&mut encoded_offer).unwrap();
1396
1397 let invoice_request = Offer::try_from(encoded_offer).unwrap()
1398 .request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1399 .build_and_sign().unwrap();
1400 assert!(invoice_request.verify_using_metadata(&expanded_key, &secp_ctx).is_err());
1401 }
1402
1403 #[test]
1404 fn builds_offer_with_derived_signing_pubkey() {
1405 let node_id = recipient_pubkey();
1406 let expanded_key = ExpandedKey::new([42; 32]);
1407 let entropy = FixedEntropy {};
1408 let nonce = Nonce::from_entropy_source(&entropy);
1409 let secp_ctx = Secp256k1::new();
1410 let payment_id = PaymentId([1; 32]);
1411
1412 let blinded_path = BlindedMessagePath::from_raw(
1413 pubkey(40), pubkey(41),
1414 vec![
1415 BlindedHop { blinded_node_id: pubkey(42), encrypted_payload: vec![0; 43] },
1416 BlindedHop { blinded_node_id: node_id, encrypted_payload: vec![0; 44] },
1417 ]
1418 );
1419
1420 #[cfg(c_bindings)]
1421 use super::OfferWithDerivedMetadataBuilder as OfferBuilder;
1422 let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx)
1423 .amount_msats(1000)
1424 .path(blinded_path)
1425 .experimental_foo(42)
1426 .build().unwrap();
1427 assert!(offer.metadata().is_none());
1428 assert_ne!(offer.issuer_signing_pubkey(), Some(node_id));
1429
1430 let invoice_request = offer
1431 .request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1432 .build_and_sign().unwrap();
1433 match invoice_request.verify_using_recipient_data(nonce, &expanded_key, &secp_ctx) {
1434 Ok(invoice_request) => assert_eq!(invoice_request.offer_id, offer.id()),
1435 Err(_) => panic!("unexpected error"),
1436 }
1437
1438 let invoice_request = offer
1440 .request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1441 .build_and_sign().unwrap();
1442 assert!(invoice_request.verify_using_metadata(&expanded_key, &secp_ctx).is_err());
1443
1444 let mut tlv_stream = offer.as_tlv_stream();
1446 tlv_stream.0.amount = Some(100);
1447
1448 let mut encoded_offer = Vec::new();
1449 tlv_stream.write(&mut encoded_offer).unwrap();
1450
1451 let invoice_request = Offer::try_from(encoded_offer).unwrap()
1452 .request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1453 .build_and_sign().unwrap();
1454 assert!(
1455 invoice_request.verify_using_recipient_data(nonce, &expanded_key, &secp_ctx).is_err()
1456 );
1457
1458 let mut tlv_stream = offer.as_tlv_stream();
1460 let issuer_id = pubkey(1);
1461 tlv_stream.0.issuer_id = Some(&issuer_id);
1462
1463 let mut encoded_offer = Vec::new();
1464 tlv_stream.write(&mut encoded_offer).unwrap();
1465
1466 let invoice_request = Offer::try_from(encoded_offer).unwrap()
1467 .request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
1468 .build_and_sign().unwrap();
1469 assert!(
1470 invoice_request.verify_using_recipient_data(nonce, &expanded_key, &secp_ctx).is_err()
1471 );
1472 }
1473
1474 #[test]
1475 fn builds_offer_with_amount() {
1476 let bitcoin_amount = Amount::Bitcoin { amount_msats: 1000 };
1477 let currency_amount = Amount::Currency { iso4217_code: *b"USD", amount: 10 };
1478
1479 let offer = OfferBuilder::new(pubkey(42))
1480 .amount_msats(1000)
1481 .build()
1482 .unwrap();
1483 let tlv_stream = offer.as_tlv_stream();
1484 assert_eq!(offer.amount(), Some(bitcoin_amount));
1485 assert_eq!(tlv_stream.0.amount, Some(1000));
1486 assert_eq!(tlv_stream.0.currency, None);
1487
1488 #[cfg(not(c_bindings))]
1489 let builder = OfferBuilder::new(pubkey(42))
1490 .amount(currency_amount.clone());
1491 #[cfg(c_bindings)]
1492 let mut builder = OfferBuilder::new(pubkey(42));
1493 #[cfg(c_bindings)]
1494 builder.amount(currency_amount.clone());
1495 let tlv_stream = builder.offer.as_tlv_stream();
1496 assert_eq!(builder.offer.amount, Some(currency_amount.clone()));
1497 assert_eq!(tlv_stream.0.amount, Some(10));
1498 assert_eq!(tlv_stream.0.currency, Some(b"USD"));
1499 match builder.build() {
1500 Ok(_) => panic!("expected error"),
1501 Err(e) => assert_eq!(e, Bolt12SemanticError::UnsupportedCurrency),
1502 }
1503
1504 let offer = OfferBuilder::new(pubkey(42))
1505 .amount(currency_amount.clone())
1506 .amount(bitcoin_amount.clone())
1507 .build()
1508 .unwrap();
1509 let tlv_stream = offer.as_tlv_stream();
1510 assert_eq!(tlv_stream.0.amount, Some(1000));
1511 assert_eq!(tlv_stream.0.currency, None);
1512
1513 let invalid_amount = Amount::Bitcoin { amount_msats: MAX_VALUE_MSAT + 1 };
1514 match OfferBuilder::new(pubkey(42)).amount(invalid_amount).build() {
1515 Ok(_) => panic!("expected error"),
1516 Err(e) => assert_eq!(e, Bolt12SemanticError::InvalidAmount),
1517 }
1518 }
1519
1520 #[test]
1521 fn builds_offer_with_description() {
1522 let offer = OfferBuilder::new(pubkey(42))
1523 .description("foo".into())
1524 .build()
1525 .unwrap();
1526 assert_eq!(offer.description(), Some(PrintableString("foo")));
1527 assert_eq!(offer.as_tlv_stream().0.description, Some(&String::from("foo")));
1528
1529 let offer = OfferBuilder::new(pubkey(42))
1530 .description("foo".into())
1531 .description("bar".into())
1532 .build()
1533 .unwrap();
1534 assert_eq!(offer.description(), Some(PrintableString("bar")));
1535 assert_eq!(offer.as_tlv_stream().0.description, Some(&String::from("bar")));
1536
1537 let offer = OfferBuilder::new(pubkey(42))
1538 .amount_msats(1000)
1539 .build()
1540 .unwrap();
1541 assert_eq!(offer.description(), Some(PrintableString("")));
1542 assert_eq!(offer.as_tlv_stream().0.description, Some(&String::from("")));
1543 }
1544
1545 #[test]
1546 fn builds_offer_with_features() {
1547 let offer = OfferBuilder::new(pubkey(42))
1548 .features_unchecked(OfferFeatures::unknown())
1549 .build()
1550 .unwrap();
1551 assert_eq!(offer.offer_features(), &OfferFeatures::unknown());
1552 assert_eq!(offer.as_tlv_stream().0.features, Some(&OfferFeatures::unknown()));
1553
1554 let offer = OfferBuilder::new(pubkey(42))
1555 .features_unchecked(OfferFeatures::unknown())
1556 .features_unchecked(OfferFeatures::empty())
1557 .build()
1558 .unwrap();
1559 assert_eq!(offer.offer_features(), &OfferFeatures::empty());
1560 assert_eq!(offer.as_tlv_stream().0.features, None);
1561 }
1562
1563 #[test]
1564 fn builds_offer_with_absolute_expiry() {
1565 let future_expiry = Duration::from_secs(u64::max_value());
1566 let past_expiry = Duration::from_secs(0);
1567 let now = future_expiry - Duration::from_secs(1_000);
1568
1569 let offer = OfferBuilder::new(pubkey(42))
1570 .absolute_expiry(future_expiry)
1571 .build()
1572 .unwrap();
1573 #[cfg(feature = "std")]
1574 assert!(!offer.is_expired());
1575 assert!(!offer.is_expired_no_std(now));
1576 assert_eq!(offer.absolute_expiry(), Some(future_expiry));
1577 assert_eq!(offer.as_tlv_stream().0.absolute_expiry, Some(future_expiry.as_secs()));
1578
1579 let offer = OfferBuilder::new(pubkey(42))
1580 .absolute_expiry(future_expiry)
1581 .absolute_expiry(past_expiry)
1582 .build()
1583 .unwrap();
1584 #[cfg(feature = "std")]
1585 assert!(offer.is_expired());
1586 assert!(offer.is_expired_no_std(now));
1587 assert_eq!(offer.absolute_expiry(), Some(past_expiry));
1588 assert_eq!(offer.as_tlv_stream().0.absolute_expiry, Some(past_expiry.as_secs()));
1589 }
1590
1591 #[test]
1592 fn builds_offer_with_paths() {
1593 let paths = vec![
1594 BlindedMessagePath::from_raw(
1595 pubkey(40), pubkey(41),
1596 vec![
1597 BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
1598 BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
1599 ]
1600 ),
1601 BlindedMessagePath::from_raw(
1602 pubkey(40), pubkey(41),
1603 vec![
1604 BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
1605 BlindedHop { blinded_node_id: pubkey(46), encrypted_payload: vec![0; 46] },
1606 ]
1607 ),
1608 ];
1609
1610 let offer = OfferBuilder::new(pubkey(42))
1611 .path(paths[0].clone())
1612 .path(paths[1].clone())
1613 .build()
1614 .unwrap();
1615 let tlv_stream = offer.as_tlv_stream();
1616 assert_eq!(offer.paths(), paths.as_slice());
1617 assert_eq!(offer.issuer_signing_pubkey(), Some(pubkey(42)));
1618 assert_ne!(pubkey(42), pubkey(44));
1619 assert_eq!(tlv_stream.0.paths, Some(&paths));
1620 assert_eq!(tlv_stream.0.issuer_id, Some(&pubkey(42)));
1621 }
1622
1623 #[test]
1624 fn builds_offer_with_issuer() {
1625 let offer = OfferBuilder::new(pubkey(42))
1626 .issuer("foo".into())
1627 .build()
1628 .unwrap();
1629 assert_eq!(offer.issuer(), Some(PrintableString("foo")));
1630 assert_eq!(offer.as_tlv_stream().0.issuer, Some(&String::from("foo")));
1631
1632 let offer = OfferBuilder::new(pubkey(42))
1633 .issuer("foo".into())
1634 .issuer("bar".into())
1635 .build()
1636 .unwrap();
1637 assert_eq!(offer.issuer(), Some(PrintableString("bar")));
1638 assert_eq!(offer.as_tlv_stream().0.issuer, Some(&String::from("bar")));
1639 }
1640
1641 #[test]
1642 fn builds_offer_with_supported_quantity() {
1643 let one = NonZeroU64::new(1).unwrap();
1644 let ten = NonZeroU64::new(10).unwrap();
1645
1646 let offer = OfferBuilder::new(pubkey(42))
1647 .supported_quantity(Quantity::One)
1648 .build()
1649 .unwrap();
1650 let tlv_stream = offer.as_tlv_stream();
1651 assert!(!offer.expects_quantity());
1652 assert_eq!(offer.supported_quantity(), Quantity::One);
1653 assert_eq!(tlv_stream.0.quantity_max, None);
1654
1655 let offer = OfferBuilder::new(pubkey(42))
1656 .supported_quantity(Quantity::Unbounded)
1657 .build()
1658 .unwrap();
1659 let tlv_stream = offer.as_tlv_stream();
1660 assert!(offer.expects_quantity());
1661 assert_eq!(offer.supported_quantity(), Quantity::Unbounded);
1662 assert_eq!(tlv_stream.0.quantity_max, Some(0));
1663
1664 let offer = OfferBuilder::new(pubkey(42))
1665 .supported_quantity(Quantity::Bounded(ten))
1666 .build()
1667 .unwrap();
1668 let tlv_stream = offer.as_tlv_stream();
1669 assert!(offer.expects_quantity());
1670 assert_eq!(offer.supported_quantity(), Quantity::Bounded(ten));
1671 assert_eq!(tlv_stream.0.quantity_max, Some(10));
1672
1673 let offer = OfferBuilder::new(pubkey(42))
1674 .supported_quantity(Quantity::Bounded(one))
1675 .build()
1676 .unwrap();
1677 let tlv_stream = offer.as_tlv_stream();
1678 assert!(offer.expects_quantity());
1679 assert_eq!(offer.supported_quantity(), Quantity::Bounded(one));
1680 assert_eq!(tlv_stream.0.quantity_max, Some(1));
1681
1682 let offer = OfferBuilder::new(pubkey(42))
1683 .supported_quantity(Quantity::Bounded(ten))
1684 .supported_quantity(Quantity::One)
1685 .build()
1686 .unwrap();
1687 let tlv_stream = offer.as_tlv_stream();
1688 assert!(!offer.expects_quantity());
1689 assert_eq!(offer.supported_quantity(), Quantity::One);
1690 assert_eq!(tlv_stream.0.quantity_max, None);
1691 }
1692
1693 #[test]
1694 fn fails_requesting_invoice_with_unknown_required_features() {
1695 let expanded_key = ExpandedKey::new([42; 32]);
1696 let entropy = FixedEntropy {};
1697 let nonce = Nonce::from_entropy_source(&entropy);
1698 let secp_ctx = Secp256k1::new();
1699 let payment_id = PaymentId([1; 32]);
1700
1701 match OfferBuilder::new(pubkey(42))
1702 .features_unchecked(OfferFeatures::unknown())
1703 .build().unwrap()
1704 .request_invoice(&expanded_key, nonce, &secp_ctx, payment_id)
1705 {
1706 Ok(_) => panic!("expected error"),
1707 Err(e) => assert_eq!(e, Bolt12SemanticError::UnknownRequiredFeatures),
1708 }
1709 }
1710
1711 #[test]
1712 fn parses_offer_with_chains() {
1713 let offer = OfferBuilder::new(pubkey(42))
1714 .chain(Network::Bitcoin)
1715 .chain(Network::Testnet)
1716 .build()
1717 .unwrap();
1718 if let Err(e) = offer.to_string().parse::<Offer>() {
1719 panic!("error parsing offer: {:?}", e);
1720 }
1721 }
1722
1723 #[test]
1724 fn parses_offer_with_amount() {
1725 let offer = OfferBuilder::new(pubkey(42))
1726 .amount(Amount::Bitcoin { amount_msats: 1000 })
1727 .build()
1728 .unwrap();
1729 if let Err(e) = offer.to_string().parse::<Offer>() {
1730 panic!("error parsing offer: {:?}", e);
1731 }
1732
1733 let mut tlv_stream = offer.as_tlv_stream();
1734 tlv_stream.0.amount = Some(1000);
1735 tlv_stream.0.currency = Some(b"USD");
1736
1737 let mut encoded_offer = Vec::new();
1738 tlv_stream.write(&mut encoded_offer).unwrap();
1739
1740 if let Err(e) = Offer::try_from(encoded_offer) {
1741 panic!("error parsing offer: {:?}", e);
1742 }
1743
1744 let mut tlv_stream = offer.as_tlv_stream();
1745 tlv_stream.0.amount = None;
1746 tlv_stream.0.currency = Some(b"USD");
1747
1748 let mut encoded_offer = Vec::new();
1749 tlv_stream.write(&mut encoded_offer).unwrap();
1750
1751 match Offer::try_from(encoded_offer) {
1752 Ok(_) => panic!("expected error"),
1753 Err(e) => assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingAmount)),
1754 }
1755
1756 let mut tlv_stream = offer.as_tlv_stream();
1757 tlv_stream.0.amount = Some(MAX_VALUE_MSAT + 1);
1758 tlv_stream.0.currency = None;
1759
1760 let mut encoded_offer = Vec::new();
1761 tlv_stream.write(&mut encoded_offer).unwrap();
1762
1763 match Offer::try_from(encoded_offer) {
1764 Ok(_) => panic!("expected error"),
1765 Err(e) => assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidAmount)),
1766 }
1767 }
1768
1769 #[test]
1770 fn parses_offer_with_description() {
1771 let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1772 if let Err(e) = offer.to_string().parse::<Offer>() {
1773 panic!("error parsing offer: {:?}", e);
1774 }
1775
1776 let offer = OfferBuilder::new(pubkey(42))
1777 .description("foo".to_string())
1778 .amount_msats(1000)
1779 .build().unwrap();
1780 if let Err(e) = offer.to_string().parse::<Offer>() {
1781 panic!("error parsing offer: {:?}", e);
1782 }
1783
1784 let mut tlv_stream = offer.as_tlv_stream();
1785 tlv_stream.0.description = None;
1786
1787 let mut encoded_offer = Vec::new();
1788 tlv_stream.write(&mut encoded_offer).unwrap();
1789
1790 match Offer::try_from(encoded_offer) {
1791 Ok(_) => panic!("expected error"),
1792 Err(e) => {
1793 assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingDescription));
1794 },
1795 }
1796 }
1797
1798 #[test]
1799 fn parses_offer_with_paths() {
1800 let offer = OfferBuilder::new(pubkey(42))
1801 .path(BlindedMessagePath::from_raw(
1802 pubkey(40), pubkey(41),
1803 vec![
1804 BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
1805 BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
1806 ]
1807 ))
1808 .path(BlindedMessagePath::from_raw(
1809 pubkey(40), pubkey(41),
1810 vec![
1811 BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
1812 BlindedHop { blinded_node_id: pubkey(46), encrypted_payload: vec![0; 46] },
1813 ]
1814 ))
1815 .build()
1816 .unwrap();
1817 if let Err(e) = offer.to_string().parse::<Offer>() {
1818 panic!("error parsing offer: {:?}", e);
1819 }
1820
1821 let offer = OfferBuilder::new(pubkey(42))
1822 .path(BlindedMessagePath::from_raw(
1823 pubkey(40), pubkey(41),
1824 vec![
1825 BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
1826 BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
1827 ]
1828 ))
1829 .clear_issuer_signing_pubkey()
1830 .build()
1831 .unwrap();
1832 if let Err(e) = offer.to_string().parse::<Offer>() {
1833 panic!("error parsing offer: {:?}", e);
1834 }
1835
1836 let mut builder = OfferBuilder::new(pubkey(42));
1837 builder.offer.paths = Some(vec![]);
1838
1839 let offer = builder.build().unwrap();
1840 match offer.to_string().parse::<Offer>() {
1841 Ok(_) => panic!("expected error"),
1842 Err(e) => {
1843 assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingPaths));
1844 },
1845 }
1846 }
1847
1848 #[test]
1849 fn parses_offer_with_quantity() {
1850 let offer = OfferBuilder::new(pubkey(42))
1851 .supported_quantity(Quantity::One)
1852 .build()
1853 .unwrap();
1854 if let Err(e) = offer.to_string().parse::<Offer>() {
1855 panic!("error parsing offer: {:?}", e);
1856 }
1857
1858 let offer = OfferBuilder::new(pubkey(42))
1859 .supported_quantity(Quantity::Unbounded)
1860 .build()
1861 .unwrap();
1862 if let Err(e) = offer.to_string().parse::<Offer>() {
1863 panic!("error parsing offer: {:?}", e);
1864 }
1865
1866 let offer = OfferBuilder::new(pubkey(42))
1867 .supported_quantity(Quantity::Bounded(NonZeroU64::new(10).unwrap()))
1868 .build()
1869 .unwrap();
1870 if let Err(e) = offer.to_string().parse::<Offer>() {
1871 panic!("error parsing offer: {:?}", e);
1872 }
1873
1874 let offer = OfferBuilder::new(pubkey(42))
1875 .supported_quantity(Quantity::Bounded(NonZeroU64::new(1).unwrap()))
1876 .build()
1877 .unwrap();
1878 if let Err(e) = offer.to_string().parse::<Offer>() {
1879 panic!("error parsing offer: {:?}", e);
1880 }
1881 }
1882
1883 #[test]
1884 fn parses_offer_with_issuer_id() {
1885 let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1886 if let Err(e) = offer.to_string().parse::<Offer>() {
1887 panic!("error parsing offer: {:?}", e);
1888 }
1889
1890 let mut tlv_stream = offer.as_tlv_stream();
1891 tlv_stream.0.issuer_id = None;
1892
1893 let mut encoded_offer = Vec::new();
1894 tlv_stream.write(&mut encoded_offer).unwrap();
1895
1896 match Offer::try_from(encoded_offer) {
1897 Ok(_) => panic!("expected error"),
1898 Err(e) => {
1899 assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingIssuerSigningPubkey));
1900 },
1901 }
1902 }
1903
1904 #[test]
1905 fn parses_offer_with_unknown_tlv_records() {
1906 const UNKNOWN_ODD_TYPE: u64 = OFFER_TYPES.end - 1;
1907 assert!(UNKNOWN_ODD_TYPE % 2 == 1);
1908
1909 let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1910
1911 let mut encoded_offer = Vec::new();
1912 offer.write(&mut encoded_offer).unwrap();
1913 BigSize(UNKNOWN_ODD_TYPE).write(&mut encoded_offer).unwrap();
1914 BigSize(32).write(&mut encoded_offer).unwrap();
1915 [42u8; 32].write(&mut encoded_offer).unwrap();
1916
1917 match Offer::try_from(encoded_offer.clone()) {
1918 Ok(offer) => assert_eq!(offer.bytes, encoded_offer),
1919 Err(e) => panic!("error parsing offer: {:?}", e),
1920 }
1921
1922 const UNKNOWN_EVEN_TYPE: u64 = OFFER_TYPES.end - 2;
1923 assert!(UNKNOWN_EVEN_TYPE % 2 == 0);
1924
1925 let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1926
1927 let mut encoded_offer = Vec::new();
1928 offer.write(&mut encoded_offer).unwrap();
1929 BigSize(UNKNOWN_EVEN_TYPE).write(&mut encoded_offer).unwrap();
1930 BigSize(32).write(&mut encoded_offer).unwrap();
1931 [42u8; 32].write(&mut encoded_offer).unwrap();
1932
1933 match Offer::try_from(encoded_offer) {
1934 Ok(_) => panic!("expected error"),
1935 Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::UnknownRequiredFeature)),
1936 }
1937 }
1938
1939 #[test]
1940 fn parses_offer_with_experimental_tlv_records() {
1941 let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1942
1943 let mut encoded_offer = Vec::new();
1944 offer.write(&mut encoded_offer).unwrap();
1945 BigSize(EXPERIMENTAL_OFFER_TYPES.start + 1).write(&mut encoded_offer).unwrap();
1946 BigSize(32).write(&mut encoded_offer).unwrap();
1947 [42u8; 32].write(&mut encoded_offer).unwrap();
1948
1949 match Offer::try_from(encoded_offer.clone()) {
1950 Ok(offer) => assert_eq!(offer.bytes, encoded_offer),
1951 Err(e) => panic!("error parsing offer: {:?}", e),
1952 }
1953
1954 let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1955
1956 let mut encoded_offer = Vec::new();
1957 offer.write(&mut encoded_offer).unwrap();
1958 BigSize(EXPERIMENTAL_OFFER_TYPES.start).write(&mut encoded_offer).unwrap();
1959 BigSize(32).write(&mut encoded_offer).unwrap();
1960 [42u8; 32].write(&mut encoded_offer).unwrap();
1961
1962 match Offer::try_from(encoded_offer) {
1963 Ok(_) => panic!("expected error"),
1964 Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::UnknownRequiredFeature)),
1965 }
1966 }
1967
1968 #[test]
1969 fn fails_parsing_offer_with_out_of_range_tlv_records() {
1970 let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1971
1972 let mut encoded_offer = Vec::new();
1973 offer.write(&mut encoded_offer).unwrap();
1974 BigSize(OFFER_TYPES.end).write(&mut encoded_offer).unwrap();
1975 BigSize(32).write(&mut encoded_offer).unwrap();
1976 [42u8; 32].write(&mut encoded_offer).unwrap();
1977
1978 match Offer::try_from(encoded_offer) {
1979 Ok(_) => panic!("expected error"),
1980 Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1981 }
1982
1983 let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
1984
1985 let mut encoded_offer = Vec::new();
1986 offer.write(&mut encoded_offer).unwrap();
1987 BigSize(EXPERIMENTAL_OFFER_TYPES.end).write(&mut encoded_offer).unwrap();
1988 BigSize(32).write(&mut encoded_offer).unwrap();
1989 [42u8; 32].write(&mut encoded_offer).unwrap();
1990
1991 match Offer::try_from(encoded_offer) {
1992 Ok(_) => panic!("expected error"),
1993 Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1994 }
1995 }
1996}
1997
1998#[cfg(test)]
1999mod bolt12_tests {
2000 use super::{Bolt12ParseError, Bolt12SemanticError, Offer};
2001 use crate::ln::msgs::DecodeError;
2002
2003 #[test]
2004 fn parses_bech32_encoded_offers() {
2005 let offers = [
2006 "lno1zcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese",
2008 "lno1pgx9getnwss8vetrw3hhyuckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg",
2010
2011 "lno1qgsyxjtl6luzd9t3pr62xr7eemp6awnejusgf6gw45q75vcfqqqqqqq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
2013
2014 "lno1qgsxlc5vp2m0rvmjcxn2y34wv0m5lyc7sdj7zksgn35dvxgqqqqqqqq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
2016
2017 "lno1qfqpge38tqmzyrdjj3x2qkdr5y80dlfw56ztq6yd9sme995g3gsxqqm0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq9qc4r9wd6zqan9vd6x7unnzcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese",
2019
2020 "lno1qsgqqqqqqqqqqqqqqqqqqqqqqqqqqzsv23jhxapqwejkxar0wfe3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
2022
2023 "lno1pqpzwyq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
2025
2026 "lno1qcp4256ypqpzwyq2p32x2um5ypmx2cm5dae8x93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
2028
2029 "lno1pgx9getnwss8vetrw3hhyucwq3ay997czcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese",
2031
2032 "lno1pgx9getnwss8vetrw3hhyucjy358garswvaz7tmzdak8gvfj9ehhyeeqgf85c4p3xgsxjmnyw4ehgunfv4e3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
2034
2035 "lno1pgx9getnwss8vetrw3hhyuc5qyz3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
2037
2038 "lno1pgx9getnwss8vetrw3hhyuc5qqtzzqhwcuj966ma9n9nqwqtl032xeyv6755yeflt235pmww58egx6rxry",
2040
2041 "lno1pgx9getnwss8vetrw3hhyuc5qyq3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
2043
2044 "lno1pgx9getnwss8vetrw3hhyucvp5yqqqqqqqqqqqqqqqqqqqqkyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg",
2046
2047 "lno1pgx9getnwss8vetrw3hhyucs5ypjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k8qzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqpqqqqqqqqqqqqqqqqqqqqqqqqqqqzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqzq3zyg3zyg3zyg3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
2049
2050 "lno1pgx9getnwss8vetrw3hhyucs3yqqqqqqqqqqqqp2qgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqyqqqqqqqqqqqqqqqqqqqqqqqqqqqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqqgzyg3zyg3zyg3z93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
2052
2053 "lno1pgx9getnwss8vetrw3hhyucs5ypjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k8qzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqpqqqqqqqqqqqqqqqqqqqqqqqqqqqzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqzq3zyg3zyg3zygs",
2055
2056 "lno1pgx9getnwss8vetrw3hhyucsl5qj5qeyv5l2cs6y3qqzesrth7mlzrlp3xg7xhulusczm04x6g6nms9trspqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqqsqqqqqqqqqqqqqqqqqqqqqqqqqqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqpqg3zyg3zyg3zygpqqqqzqqqqgqqxqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqqgqqqqqqqqqqqqqqqqqqqqqqqqqqqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqqsg3zyg3zyg3zygtzzqhwcuj966ma9n9nqwqtl032xeyv6755yeflt235pmww58egx6rxry",
2058
2059 "lno1pgx9getnwss8vetrw3hhyuckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxfppf5x2mrvdamk7unvvs",
2061
2062 "lno1pgx9getnwss8vetrw3hhyuckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvx078wdv5gg2dpjkcmr0wahhymry",
2064 ];
2065 for encoded_offer in &offers {
2066 if let Err(e) = encoded_offer.parse::<Offer>() {
2067 panic!("Invalid offer ({:?}): {}", e, encoded_offer);
2068 }
2069 }
2070 }
2071
2072 #[test]
2073 fn fails_parsing_bech32_encoded_offers() {
2074 assert_eq!(
2076 "lno1zcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszpgz5znzfgdzs".parse::<Offer>(),
2077 Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2078 );
2079
2080 assert_eq!(
2082 "lno1pgz5znzfgdz3vggzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpysgr0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq".parse::<Offer>(),
2083 Err(Bolt12ParseError::Decode(DecodeError::UnknownRequiredFeature)),
2084 );
2085
2086 assert_eq!(
2088 "lno1".parse::<Offer>(),
2089 Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingIssuerSigningPubkey)),
2090 );
2091
2092 assert_eq!(
2094 "lno1pg".parse::<Offer>(),
2095 Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2096 );
2097
2098 assert_eq!(
2100 "lno1pt7s".parse::<Offer>(),
2101 Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2102 );
2103
2104 assert_eq!(
2106 "lno1pgpq".parse::<Offer>(),
2107 Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2108 );
2109
2110 assert_eq!(
2112 "lno1pgpyz".parse::<Offer>(),
2113 Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2114 );
2115
2116 assert_eq!(
2118 "lno1qgqszzs9g9xyjs69zcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2119 Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2120 );
2121
2122 assert_eq!(
2124 "lno1qcqcqzs9g9xyjs69zcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2125 Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2126 );
2127
2128 assert_eq!(
2130 "lno1qcpgqsg2q4q5cj2rg5tzzqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqg".parse::<Offer>(),
2131 Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2132 );
2133
2134 assert_eq!(
2136 "lno1pgqcq93pqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqy".parse::<Offer>(),
2137 Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2138 );
2139
2140 assert_eq!(
2142 "lno1pgpgqsgkyypqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs".parse::<Offer>(),
2143 Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2144 );
2145
2146 assert_eq!(
2148 "lno1pgz5znzfgdz3qqgpzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2149 Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2150 );
2151
2152 assert_eq!(
2154 "lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2155 Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2156 );
2157
2158 assert_eq!(
2160 "lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqspqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqgkyypqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs".parse::<Offer>(),
2161 Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2162 );
2163
2164 assert_eq!(
2166 "lno1pgz5znzfgdz3qqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqspqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqgqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2167 Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2168 );
2169
2170 assert_eq!(
2172 "lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcpqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqgqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2173 Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2174 );
2175
2176 assert_eq!(
2178 "lno1pgz5znzfgdz3qqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqspqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqgqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2179 Err(Bolt12ParseError::Decode(DecodeError::ShortRead)),
2180 );
2181
2182 assert_eq!(
2184 "lno1pgz5znzfgdz3yqvqzcssyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsz".parse::<Offer>(),
2185 Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2186 );
2187
2188 assert_eq!(
2190 "lno1pgz5znzfgdz3yq5qgytzzqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqg".parse::<Offer>(),
2191 Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2192 );
2193
2194 assert_eq!(
2196 "lno1pgz5znzfgdz3vggzqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvpsxqcrqvps".parse::<Offer>(),
2197 Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2198 );
2199
2200 assert_eq!(
2202 "lno1pgz5znzfgdz3vggzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgp9qgr0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq".parse::<Offer>(),
2203 Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2204 );
2205
2206 assert_eq!(
2208 "lno1pgz5znzfgdz3vggzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgp06ae4jsq9qgr0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq".parse::<Offer>(),
2209 Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2210 );
2211
2212 assert_eq!(
2214 "lno1pgz5znzfgdz3vggzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgp06wu6egp9qgr0u2xq4dh3kdevrf4zg6hx8a60jv0gxe0ptgyfc6xkryqqqqqqqq".parse::<Offer>(),
2215 Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2216 );
2217
2218 assert!(
2221 "lno1pgx9getnwss8vetrw3hhyucvqdqqqqqkyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg".parse::<Offer>().is_ok()
2222 );
2223
2224 assert_eq!(
2226 "lno1pqpq86qkyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg".parse::<Offer>(),
2228 Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingDescription)),
2229 );
2230
2231 assert_eq!(
2233 "lno1pgx9getnwss8vetrw3hhyuc".parse::<Offer>(),
2234 Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingIssuerSigningPubkey)),
2235 );
2236
2237 assert_eq!(
2239 "lno1pgx9getnwss8vetrw3hhyucsespjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k8qzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqpqqqqqqqqqqqqqqqqqqqqqqqqqqqzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqzq3zyg3zyg3zygszqqqqyqqqqsqqvpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsq".parse::<Offer>(),
2240 Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)),
2241 );
2242 }
2243}