1use crate::blinded_path::message::BlindedMessagePath;
86use crate::blinded_path::payment::BlindedPaymentPath;
87use crate::io;
88use crate::ln::channelmanager::PaymentId;
89use crate::ln::inbound_payment::{ExpandedKey, IV_LEN};
90use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
91use crate::offers::invoice_request::{
92 ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceRequestTlvStreamRef,
93 InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef,
94};
95use crate::offers::nonce::Nonce;
96use crate::offers::offer::{
97 ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, OfferTlvStream, OfferTlvStreamRef,
98};
99use crate::offers::parse::{Bech32Encode, Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
100use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef};
101use crate::offers::signer::{self, Metadata, MetadataMaterial};
102use crate::sign::EntropySource;
103use crate::types::features::InvoiceRequestFeatures;
104use crate::types::payment::PaymentHash;
105use crate::types::string::PrintableString;
106use crate::util::ser::{
107 CursorReadable, LengthLimitedRead, LengthReadable, WithoutLength, Writeable, Writer,
108};
109use bitcoin::constants::ChainHash;
110use bitcoin::network::Network;
111use bitcoin::secp256k1::{self, PublicKey, Secp256k1};
112use core::hash::{Hash, Hasher};
113use core::ops::Deref;
114use core::str::FromStr;
115use core::time::Duration;
116
117#[cfg(not(c_bindings))]
118use crate::offers::invoice::{DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder};
119#[cfg(c_bindings)]
120use crate::offers::invoice::{
121 InvoiceWithDerivedSigningPubkeyBuilder, InvoiceWithExplicitSigningPubkeyBuilder,
122};
123
124#[allow(unused_imports)]
125use crate::prelude::*;
126
127#[cfg(feature = "std")]
128use std::time::SystemTime;
129
130pub(super) const IV_BYTES_WITH_METADATA: &[u8; IV_LEN] = b"LDK Refund ~~~~~";
131pub(super) const IV_BYTES_WITHOUT_METADATA: &[u8; IV_LEN] = b"LDK Refund v2~~~";
132
133pub struct RefundBuilder<'a, T: secp256k1::Signing> {
141 refund: RefundContents,
142 secp_ctx: Option<&'a Secp256k1<T>>,
143}
144
145#[cfg(c_bindings)]
151#[derive(Clone)]
152pub struct RefundMaybeWithDerivedMetadataBuilder<'a> {
153 refund: RefundContents,
154 secp_ctx: Option<&'a Secp256k1<secp256k1::All>>,
155}
156
157macro_rules! refund_explicit_metadata_builder_methods {
158 () => {
159 pub fn new(
173 metadata: Vec<u8>, signing_pubkey: PublicKey, amount_msats: u64,
174 ) -> Result<Self, Bolt12SemanticError> {
175 if amount_msats > MAX_VALUE_MSAT {
176 return Err(Bolt12SemanticError::InvalidAmount);
177 }
178
179 let metadata = Metadata::Bytes(metadata);
180 Ok(Self {
181 refund: RefundContents {
182 payer: PayerContents(metadata),
183 description: String::new(),
184 absolute_expiry: None,
185 issuer: None,
186 chain: None,
187 amount_msats,
188 features: InvoiceRequestFeatures::empty(),
189 quantity: None,
190 payer_signing_pubkey: signing_pubkey,
191 payer_note: None,
192 paths: None,
193 #[cfg(test)]
194 experimental_foo: None,
195 #[cfg(test)]
196 experimental_bar: None,
197 },
198 secp_ctx: None,
199 })
200 }
201 };
202}
203
204macro_rules! refund_builder_methods { (
205 $self: ident, $self_type: ty, $return_type: ty, $return_value: expr, $secp_context: ty $(, $self_mut: tt)?
206) => {
207 pub fn deriving_signing_pubkey(
225 node_id: PublicKey, expanded_key: &ExpandedKey, nonce: Nonce,
226 secp_ctx: &'a Secp256k1<$secp_context>, amount_msats: u64, payment_id: PaymentId
227 ) -> Result<Self, Bolt12SemanticError> {
228 if amount_msats > MAX_VALUE_MSAT {
229 return Err(Bolt12SemanticError::InvalidAmount);
230 }
231
232 let payment_id = Some(payment_id);
233 let derivation_material = MetadataMaterial::new(nonce, expanded_key, payment_id);
234 let metadata = Metadata::DerivedSigningPubkey(derivation_material);
235 Ok(Self {
236 refund: RefundContents {
237 payer: PayerContents(metadata), description: String::new(), absolute_expiry: None,
238 issuer: None, chain: None, amount_msats, features: InvoiceRequestFeatures::empty(),
239 quantity: None, payer_signing_pubkey: node_id, payer_note: None, paths: None,
240 #[cfg(test)]
241 experimental_foo: None,
242 #[cfg(test)]
243 experimental_bar: None,
244 },
245 secp_ctx: Some(secp_ctx),
246 })
247 }
248
249 pub fn description($($self_mut)* $self: $self_type, description: String) -> $return_type {
253 $self.refund.description = description;
254 $return_value
255 }
256
257 #[cfg_attr(feature = "std", doc = "Any expiry that has already passed is valid and can be checked for using [`Refund::is_expired`].")]
259 pub fn absolute_expiry($($self_mut)* $self: $self_type, absolute_expiry: Duration) -> $return_type {
262 $self.refund.absolute_expiry = Some(absolute_expiry);
263 $return_value
264 }
265
266 pub fn issuer($($self_mut)* $self: $self_type, issuer: String) -> $return_type {
270 $self.refund.issuer = Some(issuer);
271 $return_value
272 }
273
274 pub fn path($($self_mut)* $self: $self_type, path: BlindedMessagePath) -> $return_type {
280 $self.refund.paths.get_or_insert_with(Vec::new).push(path);
281 $return_value
282 }
283
284 pub fn chain($self: $self_type, network: Network) -> $return_type {
289 $self.chain_hash(ChainHash::using_genesis_block(network))
290 }
291
292 pub(crate) fn chain_hash($($self_mut)* $self: $self_type, chain: ChainHash) -> $return_type {
297 $self.refund.chain = Some(chain);
298 $return_value
299 }
300
301 pub fn quantity($($self_mut)* $self: $self_type, quantity: u64) -> $return_type {
311 $self.refund.quantity = Some(quantity);
312 $return_value
313 }
314
315 pub fn payer_note($($self_mut)* $self: $self_type, payer_note: String) -> $return_type {
319 $self.refund.payer_note = Some(payer_note);
320 $return_value
321 }
322
323 pub fn build($($self_mut)* $self: $self_type) -> Result<Refund, Bolt12SemanticError> {
325 if $self.refund.chain() == $self.refund.implied_chain() {
326 $self.refund.chain = None;
327 }
328
329 if $self.refund.payer.0.has_derivation_material() {
331 let mut metadata = core::mem::take(&mut $self.refund.payer.0);
332
333 let iv_bytes = if $self.refund.paths.is_none() {
334 metadata = metadata.without_keys();
335 IV_BYTES_WITH_METADATA
336 } else {
337 IV_BYTES_WITHOUT_METADATA
338 };
339
340 let mut tlv_stream = $self.refund.as_tlv_stream();
341 tlv_stream.0.metadata = None;
342 if metadata.derives_payer_keys() {
343 tlv_stream.2.payer_id = None;
344 }
345
346 let (derived_metadata, keys) =
347 metadata.derive_from(iv_bytes, tlv_stream, $self.secp_ctx);
348 metadata = derived_metadata;
349 if let Some(keys) = keys {
350 $self.refund.payer_signing_pubkey = keys.public_key();
351 }
352
353 $self.refund.payer.0 = metadata;
354 }
355
356 const REFUND_ALLOCATION_SIZE: usize = 512;
357 let mut bytes = Vec::with_capacity(REFUND_ALLOCATION_SIZE);
358 $self.refund.write(&mut bytes).unwrap();
359
360 Ok(Refund {
361 bytes,
362 #[cfg(not(c_bindings))]
363 contents: $self.refund,
364 #[cfg(c_bindings)]
365 contents: $self.refund.clone(),
366 })
367 }
368} }
369
370#[cfg(test)]
371macro_rules! refund_builder_test_methods { (
372 $self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)?
373) => {
374 #[cfg_attr(c_bindings, allow(dead_code))]
375 pub(crate) fn clear_paths($($self_mut)* $self: $self_type) -> $return_type {
376 $self.refund.paths = None;
377 $return_value
378 }
379
380 #[cfg_attr(c_bindings, allow(dead_code))]
381 fn features_unchecked($($self_mut)* $self: $self_type, features: InvoiceRequestFeatures) -> $return_type {
382 $self.refund.features = features;
383 $return_value
384 }
385
386 #[cfg_attr(c_bindings, allow(dead_code))]
387 pub(super) fn experimental_foo($($self_mut)* $self: $self_type, experimental_foo: u64) -> $return_type {
388 $self.refund.experimental_foo = Some(experimental_foo);
389 $return_value
390 }
391
392 #[cfg_attr(c_bindings, allow(dead_code))]
393 pub(super) fn experimental_bar($($self_mut)* $self: $self_type, experimental_bar: u64) -> $return_type {
394 $self.refund.experimental_bar = Some(experimental_bar);
395 $return_value
396 }
397} }
398
399impl<'a> RefundBuilder<'a, secp256k1::SignOnly> {
400 refund_explicit_metadata_builder_methods!();
401}
402
403impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> {
404 refund_builder_methods!(self, Self, Self, self, T, mut);
405
406 #[cfg(test)]
407 refund_builder_test_methods!(self, Self, Self, self, mut);
408}
409
410#[cfg(all(c_bindings, not(test)))]
411impl<'a> RefundMaybeWithDerivedMetadataBuilder<'a> {
412 refund_explicit_metadata_builder_methods!();
413 refund_builder_methods!(self, &mut Self, (), (), secp256k1::All);
414}
415
416#[cfg(all(c_bindings, test))]
417impl<'a> RefundMaybeWithDerivedMetadataBuilder<'a> {
418 refund_explicit_metadata_builder_methods!();
419 refund_builder_methods!(self, &mut Self, &mut Self, self, secp256k1::All);
420 refund_builder_test_methods!(self, &mut Self, &mut Self, self);
421}
422
423#[cfg(c_bindings)]
424impl<'a> From<RefundBuilder<'a, secp256k1::All>> for RefundMaybeWithDerivedMetadataBuilder<'a> {
425 fn from(builder: RefundBuilder<'a, secp256k1::All>) -> Self {
426 let RefundBuilder { refund, secp_ctx } = builder;
427
428 Self { refund, secp_ctx }
429 }
430}
431
432#[cfg(c_bindings)]
433impl<'a> From<RefundMaybeWithDerivedMetadataBuilder<'a>> for RefundBuilder<'a, secp256k1::All> {
434 fn from(builder: RefundMaybeWithDerivedMetadataBuilder<'a>) -> Self {
435 let RefundMaybeWithDerivedMetadataBuilder { refund, secp_ctx } = builder;
436
437 Self { refund, secp_ctx }
438 }
439}
440
441#[derive(Clone, Debug)]
450pub struct Refund {
451 pub(super) bytes: Vec<u8>,
452 pub(super) contents: RefundContents,
453}
454
455#[derive(Clone, Debug)]
459#[cfg_attr(test, derive(PartialEq))]
460pub(super) struct RefundContents {
461 pub(super) payer: PayerContents,
462 description: String,
464 absolute_expiry: Option<Duration>,
465 issuer: Option<String>,
466 chain: Option<ChainHash>,
468 amount_msats: u64,
469 features: InvoiceRequestFeatures,
470 quantity: Option<u64>,
471 payer_signing_pubkey: PublicKey,
472 payer_note: Option<String>,
473 paths: Option<Vec<BlindedMessagePath>>,
474 #[cfg(test)]
475 experimental_foo: Option<u64>,
476 #[cfg(test)]
477 experimental_bar: Option<u64>,
478}
479
480impl Refund {
481 pub fn description(&self) -> PrintableString<'_> {
484 self.contents.description()
485 }
486
487 pub fn absolute_expiry(&self) -> Option<Duration> {
491 self.contents.absolute_expiry()
492 }
493
494 #[cfg(feature = "std")]
496 pub fn is_expired(&self) -> bool {
497 self.contents.is_expired()
498 }
499
500 pub fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
502 self.contents.is_expired_no_std(duration_since_epoch)
503 }
504
505 pub fn issuer(&self) -> Option<PrintableString<'_>> {
508 self.contents.issuer()
509 }
510
511 pub fn paths(&self) -> &[BlindedMessagePath] {
514 self.contents.paths()
515 }
516
517 pub fn payer_metadata(&self) -> &[u8] {
522 self.contents.metadata()
523 }
524
525 pub fn chain(&self) -> ChainHash {
527 self.contents.chain()
528 }
529
530 pub fn amount_msats(&self) -> u64 {
534 self.contents.amount_msats()
535 }
536
537 pub fn features(&self) -> &InvoiceRequestFeatures {
539 &self.contents.features()
540 }
541
542 pub fn quantity(&self) -> Option<u64> {
544 self.contents.quantity()
545 }
546
547 pub fn payer_signing_pubkey(&self) -> PublicKey {
552 self.contents.payer_signing_pubkey()
553 }
554
555 pub fn payer_note(&self) -> Option<PrintableString<'_>> {
557 self.contents.payer_note()
558 }
559}
560
561macro_rules! respond_with_explicit_signing_pubkey_methods { ($self: ident, $builder: ty) => {
562 #[cfg(feature = "std")]
572 pub fn respond_with(
573 &$self, payment_paths: Vec<BlindedPaymentPath>, payment_hash: PaymentHash,
574 signing_pubkey: PublicKey,
575 ) -> Result<$builder, Bolt12SemanticError> {
576 let created_at = std::time::SystemTime::now()
577 .duration_since(std::time::SystemTime::UNIX_EPOCH)
578 .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
579
580 $self.respond_with_no_std(payment_paths, payment_hash, signing_pubkey, created_at)
581 }
582
583 #[cfg_attr(feature = "std", doc = "Useful for non-`std` builds where [`std::time::SystemTime`] is not available.")]
588 pub fn respond_with_no_std(
606 &$self, payment_paths: Vec<BlindedPaymentPath>, payment_hash: PaymentHash,
607 signing_pubkey: PublicKey, created_at: Duration
608 ) -> Result<$builder, Bolt12SemanticError> {
609 if $self.features().requires_unknown_bits() {
610 return Err(Bolt12SemanticError::UnknownRequiredFeatures);
611 }
612
613 <$builder>::for_refund($self, payment_paths, created_at, payment_hash, signing_pubkey)
614 }
615} }
616
617macro_rules! respond_with_derived_signing_pubkey_methods { ($self: ident, $builder: ty) => {
618 #[cfg(feature = "std")]
627 pub fn respond_using_derived_keys<ES: Deref>(
628 &$self, payment_paths: Vec<BlindedPaymentPath>, payment_hash: PaymentHash,
629 expanded_key: &ExpandedKey, entropy_source: ES
630 ) -> Result<$builder, Bolt12SemanticError>
631 where
632 ES::Target: EntropySource,
633 {
634 let created_at = std::time::SystemTime::now()
635 .duration_since(std::time::SystemTime::UNIX_EPOCH)
636 .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
637
638 $self.respond_using_derived_keys_no_std(
639 payment_paths, payment_hash, created_at, expanded_key, entropy_source
640 )
641 }
642
643 pub fn respond_using_derived_keys_no_std<ES: Deref>(
652 &$self, payment_paths: Vec<BlindedPaymentPath>, payment_hash: PaymentHash,
653 created_at: core::time::Duration, expanded_key: &ExpandedKey, entropy_source: ES
654 ) -> Result<$builder, Bolt12SemanticError>
655 where
656 ES::Target: EntropySource,
657 {
658 if $self.features().requires_unknown_bits() {
659 return Err(Bolt12SemanticError::UnknownRequiredFeatures);
660 }
661
662 let nonce = Nonce::from_entropy_source(entropy_source);
663 let keys = signer::derive_keys(nonce, expanded_key);
664 <$builder>::for_refund_using_keys($self, payment_paths, created_at, payment_hash, keys)
665 }
666} }
667
668#[cfg(not(c_bindings))]
669impl Refund {
670 respond_with_explicit_signing_pubkey_methods!(self, InvoiceBuilder<'_, ExplicitSigningPubkey>);
671 respond_with_derived_signing_pubkey_methods!(self, InvoiceBuilder<'_, DerivedSigningPubkey>);
672}
673
674#[cfg(c_bindings)]
675impl Refund {
676 respond_with_explicit_signing_pubkey_methods!(self, InvoiceWithExplicitSigningPubkeyBuilder);
677 respond_with_derived_signing_pubkey_methods!(self, InvoiceWithDerivedSigningPubkeyBuilder);
678}
679
680#[cfg(test)]
681impl Refund {
682 fn as_tlv_stream(&self) -> RefundTlvStreamRef<'_> {
683 self.contents.as_tlv_stream()
684 }
685}
686
687impl AsRef<[u8]> for Refund {
688 fn as_ref(&self) -> &[u8] {
689 &self.bytes
690 }
691}
692
693impl PartialEq for Refund {
694 fn eq(&self, other: &Self) -> bool {
695 self.bytes.eq(&other.bytes)
696 }
697}
698
699impl Eq for Refund {}
700
701impl Hash for Refund {
702 fn hash<H: Hasher>(&self, state: &mut H) {
703 self.bytes.hash(state);
704 }
705}
706
707impl RefundContents {
708 pub fn description(&self) -> PrintableString<'_> {
709 PrintableString(&self.description)
710 }
711
712 pub fn absolute_expiry(&self) -> Option<Duration> {
713 self.absolute_expiry
714 }
715
716 #[cfg(feature = "std")]
717 pub(super) fn is_expired(&self) -> bool {
718 SystemTime::UNIX_EPOCH
719 .elapsed()
720 .map(|duration_since_epoch| self.is_expired_no_std(duration_since_epoch))
721 .unwrap_or(false)
722 }
723
724 pub(super) fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
725 self.absolute_expiry
726 .map(|absolute_expiry| duration_since_epoch > absolute_expiry)
727 .unwrap_or(false)
728 }
729
730 pub fn issuer(&self) -> Option<PrintableString<'_>> {
731 self.issuer.as_ref().map(|issuer| PrintableString(issuer.as_str()))
732 }
733
734 pub fn paths(&self) -> &[BlindedMessagePath] {
735 self.paths.as_ref().map(|paths| paths.as_slice()).unwrap_or(&[])
736 }
737
738 pub(super) fn metadata(&self) -> &[u8] {
739 self.payer.0.as_bytes().map(|bytes| bytes.as_slice()).unwrap_or(&[])
740 }
741
742 pub(super) fn chain(&self) -> ChainHash {
743 self.chain.unwrap_or_else(|| self.implied_chain())
744 }
745
746 pub fn implied_chain(&self) -> ChainHash {
747 ChainHash::using_genesis_block(Network::Bitcoin)
748 }
749
750 pub fn amount_msats(&self) -> u64 {
751 self.amount_msats
752 }
753
754 pub fn features(&self) -> &InvoiceRequestFeatures {
756 &self.features
757 }
758
759 pub fn quantity(&self) -> Option<u64> {
761 self.quantity
762 }
763
764 pub fn payer_signing_pubkey(&self) -> PublicKey {
769 self.payer_signing_pubkey
770 }
771
772 pub fn payer_note(&self) -> Option<PrintableString<'_>> {
774 self.payer_note.as_ref().map(|payer_note| PrintableString(payer_note.as_str()))
775 }
776
777 pub(super) fn as_tlv_stream(&self) -> RefundTlvStreamRef<'_> {
778 let payer = PayerTlvStreamRef { metadata: self.payer.0.as_bytes() };
779
780 let offer = OfferTlvStreamRef {
781 chains: None,
782 metadata: None,
783 currency: None,
784 amount: None,
785 description: Some(&self.description),
786 features: None,
787 absolute_expiry: self.absolute_expiry.map(|duration| duration.as_secs()),
788 paths: None,
789 issuer: self.issuer.as_ref(),
790 quantity_max: None,
791 issuer_id: None,
792 };
793
794 let features = {
795 if self.features == InvoiceRequestFeatures::empty() {
796 None
797 } else {
798 Some(&self.features)
799 }
800 };
801
802 let invoice_request = InvoiceRequestTlvStreamRef {
803 chain: self.chain.as_ref(),
804 amount: Some(self.amount_msats),
805 features,
806 quantity: self.quantity,
807 payer_id: Some(&self.payer_signing_pubkey),
808 payer_note: self.payer_note.as_ref(),
809 paths: self.paths.as_ref(),
810 offer_from_hrn: None,
811 };
812
813 let experimental_offer = ExperimentalOfferTlvStreamRef {
814 #[cfg(test)]
815 experimental_foo: self.experimental_foo,
816 };
817
818 let experimental_invoice_request = ExperimentalInvoiceRequestTlvStreamRef {
819 #[cfg(test)]
820 experimental_bar: self.experimental_bar,
821 };
822
823 (payer, offer, invoice_request, experimental_offer, experimental_invoice_request)
824 }
825}
826
827impl LengthReadable for Refund {
828 fn read_from_fixed_length_buffer<R: LengthLimitedRead>(
829 reader: &mut R,
830 ) -> Result<Self, DecodeError> {
831 let bytes: WithoutLength<Vec<u8>> = LengthReadable::read_from_fixed_length_buffer(reader)?;
832 Self::try_from(bytes.0).map_err(|e| match e {
833 Bolt12ParseError::Decode(e) => e,
834 _ => DecodeError::InvalidValue,
835 })
836 }
837}
838
839impl Writeable for Refund {
840 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
841 WithoutLength(&self.bytes).write(writer)
842 }
843}
844
845impl Writeable for RefundContents {
846 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
847 self.as_tlv_stream().write(writer)
848 }
849}
850
851type RefundTlvStream = (
852 PayerTlvStream,
853 OfferTlvStream,
854 InvoiceRequestTlvStream,
855 ExperimentalOfferTlvStream,
856 ExperimentalInvoiceRequestTlvStream,
857);
858
859type RefundTlvStreamRef<'a> = (
860 PayerTlvStreamRef<'a>,
861 OfferTlvStreamRef<'a>,
862 InvoiceRequestTlvStreamRef<'a>,
863 ExperimentalOfferTlvStreamRef,
864 ExperimentalInvoiceRequestTlvStreamRef,
865);
866
867impl CursorReadable for RefundTlvStream {
868 fn read<R: AsRef<[u8]>>(r: &mut io::Cursor<R>) -> Result<Self, DecodeError> {
869 let payer = CursorReadable::read(r)?;
870 let offer = CursorReadable::read(r)?;
871 let invoice_request = CursorReadable::read(r)?;
872 let experimental_offer = CursorReadable::read(r)?;
873 let experimental_invoice_request = CursorReadable::read(r)?;
874
875 Ok((payer, offer, invoice_request, experimental_offer, experimental_invoice_request))
876 }
877}
878
879impl Bech32Encode for Refund {
880 const BECH32_HRP: &'static str = "lnr";
881}
882
883impl FromStr for Refund {
884 type Err = Bolt12ParseError;
885
886 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
887 Refund::from_bech32_str(s)
888 }
889}
890
891impl TryFrom<Vec<u8>> for Refund {
892 type Error = Bolt12ParseError;
893
894 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
895 let refund = ParsedMessage::<RefundTlvStream>::try_from(bytes)?;
896 let ParsedMessage { bytes, tlv_stream } = refund;
897 let contents = RefundContents::try_from(tlv_stream)?;
898
899 Ok(Refund { bytes, contents })
900 }
901}
902
903impl TryFrom<RefundTlvStream> for RefundContents {
904 type Error = Bolt12SemanticError;
905
906 fn try_from(tlv_stream: RefundTlvStream) -> Result<Self, Self::Error> {
907 let (
908 PayerTlvStream { metadata: payer_metadata },
909 OfferTlvStream {
910 chains,
911 metadata,
912 currency,
913 amount: offer_amount,
914 description,
915 features: offer_features,
916 absolute_expiry,
917 paths: offer_paths,
918 issuer,
919 quantity_max,
920 issuer_id,
921 },
922 InvoiceRequestTlvStream {
923 chain,
924 amount,
925 features,
926 quantity,
927 payer_id,
928 payer_note,
929 paths,
930 offer_from_hrn,
931 },
932 ExperimentalOfferTlvStream {
933 #[cfg(test)]
934 experimental_foo,
935 },
936 ExperimentalInvoiceRequestTlvStream {
937 #[cfg(test)]
938 experimental_bar,
939 },
940 ) = tlv_stream;
941
942 let payer = match payer_metadata {
943 None => return Err(Bolt12SemanticError::MissingPayerMetadata),
944 Some(metadata) => PayerContents(Metadata::Bytes(metadata)),
945 };
946
947 if metadata.is_some() {
948 return Err(Bolt12SemanticError::UnexpectedMetadata);
949 }
950
951 if chains.is_some() {
952 return Err(Bolt12SemanticError::UnexpectedChain);
953 }
954
955 if currency.is_some() || offer_amount.is_some() {
956 return Err(Bolt12SemanticError::UnexpectedAmount);
957 }
958
959 let description = match description {
960 None => return Err(Bolt12SemanticError::MissingDescription),
961 Some(description) => description,
962 };
963
964 if offer_features.is_some() {
965 return Err(Bolt12SemanticError::UnexpectedFeatures);
966 }
967
968 let absolute_expiry = absolute_expiry.map(Duration::from_secs);
969
970 if offer_paths.is_some() {
971 return Err(Bolt12SemanticError::UnexpectedPaths);
972 }
973
974 if quantity_max.is_some() {
975 return Err(Bolt12SemanticError::UnexpectedQuantity);
976 }
977
978 if issuer_id.is_some() {
979 return Err(Bolt12SemanticError::UnexpectedIssuerSigningPubkey);
980 }
981
982 if offer_from_hrn.is_some() {
983 return Err(Bolt12SemanticError::UnexpectedHumanReadableName);
985 }
986
987 let amount_msats = match amount {
988 None => return Err(Bolt12SemanticError::MissingAmount),
989 Some(amount_msats) if amount_msats > MAX_VALUE_MSAT => {
990 return Err(Bolt12SemanticError::InvalidAmount);
991 },
992 Some(amount_msats) => amount_msats,
993 };
994
995 let features = features.unwrap_or_else(InvoiceRequestFeatures::empty);
996
997 let payer_signing_pubkey = match payer_id {
998 None => return Err(Bolt12SemanticError::MissingPayerSigningPubkey),
999 Some(payer_id) => payer_id,
1000 };
1001
1002 Ok(RefundContents {
1003 payer,
1004 description,
1005 absolute_expiry,
1006 issuer,
1007 chain,
1008 amount_msats,
1009 features,
1010 quantity,
1011 payer_signing_pubkey,
1012 payer_note,
1013 paths,
1014 #[cfg(test)]
1015 experimental_foo,
1016 #[cfg(test)]
1017 experimental_bar,
1018 })
1019 }
1020}
1021
1022impl core::fmt::Display for Refund {
1023 fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
1024 self.fmt_bech32_str(f)
1025 }
1026}
1027
1028#[cfg(test)]
1029mod tests {
1030 #[cfg(not(c_bindings))]
1031 use super::RefundBuilder;
1032 #[cfg(c_bindings)]
1033 use super::RefundMaybeWithDerivedMetadataBuilder as RefundBuilder;
1034 use super::{Refund, RefundTlvStreamRef};
1035
1036 use bitcoin::constants::ChainHash;
1037 use bitcoin::network::Network;
1038 use bitcoin::secp256k1::{Keypair, Secp256k1, SecretKey};
1039
1040 use core::time::Duration;
1041
1042 use crate::blinded_path::message::BlindedMessagePath;
1043 use crate::blinded_path::BlindedHop;
1044 use crate::ln::channelmanager::PaymentId;
1045 use crate::ln::inbound_payment::ExpandedKey;
1046 use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
1047 use crate::offers::invoice_request::{
1048 ExperimentalInvoiceRequestTlvStreamRef, InvoiceRequestTlvStreamRef,
1049 EXPERIMENTAL_INVOICE_REQUEST_TYPES, INVOICE_REQUEST_TYPES,
1050 };
1051 use crate::offers::nonce::Nonce;
1052 use crate::offers::offer::{ExperimentalOfferTlvStreamRef, OfferTlvStreamRef};
1053 use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError};
1054 use crate::offers::payer::PayerTlvStreamRef;
1055 use crate::offers::test_utils::*;
1056 use crate::prelude::*;
1057 use crate::types::features::{InvoiceRequestFeatures, OfferFeatures};
1058 use crate::types::string::PrintableString;
1059 use crate::util::ser::{BigSize, Writeable};
1060
1061 trait ToBytes {
1062 fn to_bytes(&self) -> Vec<u8>;
1063 }
1064
1065 impl<'a> ToBytes for RefundTlvStreamRef<'a> {
1066 fn to_bytes(&self) -> Vec<u8> {
1067 let mut buffer = Vec::new();
1068 self.write(&mut buffer).unwrap();
1069 buffer
1070 }
1071 }
1072
1073 #[test]
1074 fn builds_refund_with_defaults() {
1075 let refund =
1076 RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap().build().unwrap();
1077
1078 let mut buffer = Vec::new();
1079 refund.write(&mut buffer).unwrap();
1080
1081 assert_eq!(refund.bytes, buffer.as_slice());
1082 assert_eq!(refund.payer_metadata(), &[1; 32]);
1083 assert_eq!(refund.description(), PrintableString(""));
1084 assert_eq!(refund.absolute_expiry(), None);
1085 #[cfg(feature = "std")]
1086 assert!(!refund.is_expired());
1087 assert_eq!(refund.paths(), &[]);
1088 assert_eq!(refund.issuer(), None);
1089 assert_eq!(refund.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
1090 assert_eq!(refund.amount_msats(), 1000);
1091 assert_eq!(refund.features(), &InvoiceRequestFeatures::empty());
1092 assert_eq!(refund.payer_signing_pubkey(), payer_pubkey());
1093 assert_eq!(refund.payer_note(), None);
1094
1095 assert_eq!(
1096 refund.as_tlv_stream(),
1097 (
1098 PayerTlvStreamRef { metadata: Some(&vec![1; 32]) },
1099 OfferTlvStreamRef {
1100 chains: None,
1101 metadata: None,
1102 currency: None,
1103 amount: None,
1104 description: Some(&String::from("")),
1105 features: None,
1106 absolute_expiry: None,
1107 paths: None,
1108 issuer: None,
1109 quantity_max: None,
1110 issuer_id: None,
1111 },
1112 InvoiceRequestTlvStreamRef {
1113 chain: None,
1114 amount: Some(1000),
1115 features: None,
1116 quantity: None,
1117 payer_id: Some(&payer_pubkey()),
1118 payer_note: None,
1119 paths: None,
1120 offer_from_hrn: None,
1121 },
1122 ExperimentalOfferTlvStreamRef { experimental_foo: None },
1123 ExperimentalInvoiceRequestTlvStreamRef { experimental_bar: None },
1124 ),
1125 );
1126
1127 if let Err(e) = Refund::try_from(buffer) {
1128 panic!("error parsing refund: {:?}", e);
1129 }
1130 }
1131
1132 #[test]
1133 fn fails_building_refund_with_invalid_amount() {
1134 match RefundBuilder::new(vec![1; 32], payer_pubkey(), MAX_VALUE_MSAT + 1) {
1135 Ok(_) => panic!("expected error"),
1136 Err(e) => assert_eq!(e, Bolt12SemanticError::InvalidAmount),
1137 }
1138 }
1139
1140 #[test]
1141 fn builds_refund_with_metadata_derived() {
1142 let node_id = payer_pubkey();
1143 let expanded_key = ExpandedKey::new([42; 32]);
1144 let entropy = FixedEntropy {};
1145 let nonce = Nonce::from_entropy_source(&entropy);
1146 let secp_ctx = Secp256k1::new();
1147 let payment_id = PaymentId([1; 32]);
1148
1149 let refund = RefundBuilder::deriving_signing_pubkey(
1150 node_id,
1151 &expanded_key,
1152 nonce,
1153 &secp_ctx,
1154 1000,
1155 payment_id,
1156 )
1157 .unwrap()
1158 .experimental_foo(42)
1159 .experimental_bar(42)
1160 .build()
1161 .unwrap();
1162 assert_eq!(refund.payer_signing_pubkey(), node_id);
1163
1164 let invoice = refund
1166 .respond_with_no_std(payment_paths(), payment_hash(), recipient_pubkey(), now())
1167 .unwrap()
1168 .experimental_baz(42)
1169 .build()
1170 .unwrap()
1171 .sign(recipient_sign)
1172 .unwrap();
1173 match invoice.verify_using_metadata(&expanded_key, &secp_ctx) {
1174 Ok(payment_id) => assert_eq!(payment_id, PaymentId([1; 32])),
1175 Err(()) => panic!("verification failed"),
1176 }
1177 assert!(invoice
1178 .verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx)
1179 .is_err());
1180
1181 let mut tlv_stream = refund.as_tlv_stream();
1182 tlv_stream.2.amount = Some(2000);
1183
1184 let mut encoded_refund = Vec::new();
1185 tlv_stream.write(&mut encoded_refund).unwrap();
1186
1187 let invoice = Refund::try_from(encoded_refund)
1188 .unwrap()
1189 .respond_with_no_std(payment_paths(), payment_hash(), recipient_pubkey(), now())
1190 .unwrap()
1191 .build()
1192 .unwrap()
1193 .sign(recipient_sign)
1194 .unwrap();
1195 assert!(invoice.verify_using_metadata(&expanded_key, &secp_ctx).is_err());
1196
1197 let mut tlv_stream = refund.as_tlv_stream();
1199 let metadata = tlv_stream.0.metadata.unwrap().iter().copied().rev().collect();
1200 tlv_stream.0.metadata = Some(&metadata);
1201
1202 let mut encoded_refund = Vec::new();
1203 tlv_stream.write(&mut encoded_refund).unwrap();
1204
1205 let invoice = Refund::try_from(encoded_refund)
1206 .unwrap()
1207 .respond_with_no_std(payment_paths(), payment_hash(), recipient_pubkey(), now())
1208 .unwrap()
1209 .build()
1210 .unwrap()
1211 .sign(recipient_sign)
1212 .unwrap();
1213 assert!(invoice.verify_using_metadata(&expanded_key, &secp_ctx).is_err());
1214 }
1215
1216 #[test]
1217 fn builds_refund_with_derived_signing_pubkey() {
1218 let node_id = payer_pubkey();
1219 let expanded_key = ExpandedKey::new([42; 32]);
1220 let entropy = FixedEntropy {};
1221 let nonce = Nonce::from_entropy_source(&entropy);
1222 let secp_ctx = Secp256k1::new();
1223 let payment_id = PaymentId([1; 32]);
1224
1225 let blinded_path = BlindedMessagePath::from_blinded_path(
1226 pubkey(40),
1227 pubkey(41),
1228 vec![
1229 BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
1230 BlindedHop { blinded_node_id: node_id, encrypted_payload: vec![0; 44] },
1231 ],
1232 );
1233
1234 let refund = RefundBuilder::deriving_signing_pubkey(
1235 node_id,
1236 &expanded_key,
1237 nonce,
1238 &secp_ctx,
1239 1000,
1240 payment_id,
1241 )
1242 .unwrap()
1243 .path(blinded_path)
1244 .experimental_foo(42)
1245 .experimental_bar(42)
1246 .build()
1247 .unwrap();
1248 assert_ne!(refund.payer_signing_pubkey(), node_id);
1249
1250 let invoice = refund
1251 .respond_with_no_std(payment_paths(), payment_hash(), recipient_pubkey(), now())
1252 .unwrap()
1253 .experimental_baz(42)
1254 .build()
1255 .unwrap()
1256 .sign(recipient_sign)
1257 .unwrap();
1258 assert!(invoice.verify_using_metadata(&expanded_key, &secp_ctx).is_err());
1259 assert!(invoice
1260 .verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx)
1261 .is_ok());
1262
1263 let mut tlv_stream = refund.as_tlv_stream();
1265 tlv_stream.2.amount = Some(2000);
1266
1267 let mut encoded_refund = Vec::new();
1268 tlv_stream.write(&mut encoded_refund).unwrap();
1269
1270 let invoice = Refund::try_from(encoded_refund)
1271 .unwrap()
1272 .respond_with_no_std(payment_paths(), payment_hash(), recipient_pubkey(), now())
1273 .unwrap()
1274 .build()
1275 .unwrap()
1276 .sign(recipient_sign)
1277 .unwrap();
1278 assert!(invoice
1279 .verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx)
1280 .is_err());
1281
1282 let mut tlv_stream = refund.as_tlv_stream();
1284 let payer_id = pubkey(1);
1285 tlv_stream.2.payer_id = Some(&payer_id);
1286
1287 let mut encoded_refund = Vec::new();
1288 tlv_stream.write(&mut encoded_refund).unwrap();
1289
1290 let invoice = Refund::try_from(encoded_refund)
1291 .unwrap()
1292 .respond_with_no_std(payment_paths(), payment_hash(), recipient_pubkey(), now())
1293 .unwrap()
1294 .build()
1295 .unwrap()
1296 .sign(recipient_sign)
1297 .unwrap();
1298 assert!(invoice
1299 .verify_using_payer_data(payment_id, nonce, &expanded_key, &secp_ctx)
1300 .is_err());
1301 }
1302
1303 #[test]
1304 fn builds_refund_with_absolute_expiry() {
1305 let future_expiry = Duration::from_secs(u64::max_value());
1306 let past_expiry = Duration::from_secs(0);
1307 let now = future_expiry - Duration::from_secs(1_000);
1308
1309 let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000)
1310 .unwrap()
1311 .absolute_expiry(future_expiry)
1312 .build()
1313 .unwrap();
1314 let (_, tlv_stream, _, _, _) = refund.as_tlv_stream();
1315 #[cfg(feature = "std")]
1316 assert!(!refund.is_expired());
1317 assert!(!refund.is_expired_no_std(now));
1318 assert_eq!(refund.absolute_expiry(), Some(future_expiry));
1319 assert_eq!(tlv_stream.absolute_expiry, Some(future_expiry.as_secs()));
1320
1321 let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000)
1322 .unwrap()
1323 .absolute_expiry(future_expiry)
1324 .absolute_expiry(past_expiry)
1325 .build()
1326 .unwrap();
1327 let (_, tlv_stream, _, _, _) = refund.as_tlv_stream();
1328 #[cfg(feature = "std")]
1329 assert!(refund.is_expired());
1330 assert!(refund.is_expired_no_std(now));
1331 assert_eq!(refund.absolute_expiry(), Some(past_expiry));
1332 assert_eq!(tlv_stream.absolute_expiry, Some(past_expiry.as_secs()));
1333 }
1334
1335 #[test]
1336 fn builds_refund_with_paths() {
1337 let paths = vec![
1338 BlindedMessagePath::from_blinded_path(
1339 pubkey(40),
1340 pubkey(41),
1341 vec![
1342 BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
1343 BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
1344 ],
1345 ),
1346 BlindedMessagePath::from_blinded_path(
1347 pubkey(40),
1348 pubkey(41),
1349 vec![
1350 BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
1351 BlindedHop { blinded_node_id: pubkey(46), encrypted_payload: vec![0; 46] },
1352 ],
1353 ),
1354 ];
1355
1356 let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000)
1357 .unwrap()
1358 .path(paths[0].clone())
1359 .path(paths[1].clone())
1360 .build()
1361 .unwrap();
1362 let (_, _, invoice_request_tlv_stream, _, _) = refund.as_tlv_stream();
1363 assert_eq!(refund.payer_signing_pubkey(), pubkey(42));
1364 assert_eq!(refund.paths(), paths.as_slice());
1365 assert_ne!(pubkey(42), pubkey(44));
1366 assert_eq!(invoice_request_tlv_stream.payer_id, Some(&pubkey(42)));
1367 assert_eq!(invoice_request_tlv_stream.paths, Some(&paths));
1368 }
1369
1370 #[test]
1371 fn builds_refund_with_issuer() {
1372 let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000)
1373 .unwrap()
1374 .issuer("bar".into())
1375 .build()
1376 .unwrap();
1377 let (_, tlv_stream, _, _, _) = refund.as_tlv_stream();
1378 assert_eq!(refund.issuer(), Some(PrintableString("bar")));
1379 assert_eq!(tlv_stream.issuer, Some(&String::from("bar")));
1380
1381 let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000)
1382 .unwrap()
1383 .issuer("bar".into())
1384 .issuer("baz".into())
1385 .build()
1386 .unwrap();
1387 let (_, tlv_stream, _, _, _) = refund.as_tlv_stream();
1388 assert_eq!(refund.issuer(), Some(PrintableString("baz")));
1389 assert_eq!(tlv_stream.issuer, Some(&String::from("baz")));
1390 }
1391
1392 #[test]
1393 fn builds_refund_with_chain() {
1394 let mainnet = ChainHash::using_genesis_block(Network::Bitcoin);
1395 let testnet = ChainHash::using_genesis_block(Network::Testnet);
1396
1397 let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000)
1398 .unwrap()
1399 .chain(Network::Bitcoin)
1400 .build()
1401 .unwrap();
1402 let (_, _, tlv_stream, _, _) = refund.as_tlv_stream();
1403 assert_eq!(refund.chain(), mainnet);
1404 assert_eq!(tlv_stream.chain, None);
1405
1406 let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000)
1407 .unwrap()
1408 .chain(Network::Testnet)
1409 .build()
1410 .unwrap();
1411 let (_, _, tlv_stream, _, _) = refund.as_tlv_stream();
1412 assert_eq!(refund.chain(), testnet);
1413 assert_eq!(tlv_stream.chain, Some(&testnet));
1414
1415 let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000)
1416 .unwrap()
1417 .chain(Network::Regtest)
1418 .chain(Network::Testnet)
1419 .build()
1420 .unwrap();
1421 let (_, _, tlv_stream, _, _) = refund.as_tlv_stream();
1422 assert_eq!(refund.chain(), testnet);
1423 assert_eq!(tlv_stream.chain, Some(&testnet));
1424 }
1425
1426 #[test]
1427 fn builds_refund_with_quantity() {
1428 let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000)
1429 .unwrap()
1430 .quantity(10)
1431 .build()
1432 .unwrap();
1433 let (_, _, tlv_stream, _, _) = refund.as_tlv_stream();
1434 assert_eq!(refund.quantity(), Some(10));
1435 assert_eq!(tlv_stream.quantity, Some(10));
1436
1437 let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000)
1438 .unwrap()
1439 .quantity(10)
1440 .quantity(1)
1441 .build()
1442 .unwrap();
1443 let (_, _, tlv_stream, _, _) = refund.as_tlv_stream();
1444 assert_eq!(refund.quantity(), Some(1));
1445 assert_eq!(tlv_stream.quantity, Some(1));
1446 }
1447
1448 #[test]
1449 fn builds_refund_with_payer_note() {
1450 let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000)
1451 .unwrap()
1452 .payer_note("bar".into())
1453 .build()
1454 .unwrap();
1455 let (_, _, tlv_stream, _, _) = refund.as_tlv_stream();
1456 assert_eq!(refund.payer_note(), Some(PrintableString("bar")));
1457 assert_eq!(tlv_stream.payer_note, Some(&String::from("bar")));
1458
1459 let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000)
1460 .unwrap()
1461 .payer_note("bar".into())
1462 .payer_note("baz".into())
1463 .build()
1464 .unwrap();
1465 let (_, _, tlv_stream, _, _) = refund.as_tlv_stream();
1466 assert_eq!(refund.payer_note(), Some(PrintableString("baz")));
1467 assert_eq!(tlv_stream.payer_note, Some(&String::from("baz")));
1468 }
1469
1470 #[test]
1471 fn fails_responding_with_unknown_required_features() {
1472 match RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000)
1473 .unwrap()
1474 .features_unchecked(InvoiceRequestFeatures::unknown())
1475 .build()
1476 .unwrap()
1477 .respond_with_no_std(payment_paths(), payment_hash(), recipient_pubkey(), now())
1478 {
1479 Ok(_) => panic!("expected error"),
1480 Err(e) => assert_eq!(e, Bolt12SemanticError::UnknownRequiredFeatures),
1481 }
1482 }
1483
1484 #[test]
1485 fn parses_refund_with_metadata() {
1486 let refund =
1487 RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap().build().unwrap();
1488 if let Err(e) = refund.to_string().parse::<Refund>() {
1489 panic!("error parsing refund: {:?}", e);
1490 }
1491
1492 let mut tlv_stream = refund.as_tlv_stream();
1493 tlv_stream.0.metadata = None;
1494
1495 match Refund::try_from(tlv_stream.to_bytes()) {
1496 Ok(_) => panic!("expected error"),
1497 Err(e) => {
1498 assert_eq!(
1499 e,
1500 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingPayerMetadata)
1501 );
1502 },
1503 }
1504 }
1505
1506 #[test]
1507 fn parses_refund_with_description() {
1508 let refund =
1509 RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap().build().unwrap();
1510 if let Err(e) = refund.to_string().parse::<Refund>() {
1511 panic!("error parsing refund: {:?}", e);
1512 }
1513
1514 let mut tlv_stream = refund.as_tlv_stream();
1515 tlv_stream.1.description = None;
1516
1517 match Refund::try_from(tlv_stream.to_bytes()) {
1518 Ok(_) => panic!("expected error"),
1519 Err(e) => {
1520 assert_eq!(
1521 e,
1522 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingDescription)
1523 );
1524 },
1525 }
1526 }
1527
1528 #[test]
1529 fn parses_refund_with_amount() {
1530 let refund =
1531 RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap().build().unwrap();
1532 if let Err(e) = refund.to_string().parse::<Refund>() {
1533 panic!("error parsing refund: {:?}", e);
1534 }
1535
1536 let mut tlv_stream = refund.as_tlv_stream();
1537 tlv_stream.2.amount = None;
1538
1539 match Refund::try_from(tlv_stream.to_bytes()) {
1540 Ok(_) => panic!("expected error"),
1541 Err(e) => {
1542 assert_eq!(
1543 e,
1544 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingAmount)
1545 );
1546 },
1547 }
1548
1549 let mut tlv_stream = refund.as_tlv_stream();
1550 tlv_stream.2.amount = Some(MAX_VALUE_MSAT + 1);
1551
1552 match Refund::try_from(tlv_stream.to_bytes()) {
1553 Ok(_) => panic!("expected error"),
1554 Err(e) => {
1555 assert_eq!(
1556 e,
1557 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidAmount)
1558 );
1559 },
1560 }
1561 }
1562
1563 #[test]
1564 fn parses_refund_with_payer_id() {
1565 let refund =
1566 RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap().build().unwrap();
1567 if let Err(e) = refund.to_string().parse::<Refund>() {
1568 panic!("error parsing refund: {:?}", e);
1569 }
1570
1571 let mut tlv_stream = refund.as_tlv_stream();
1572 tlv_stream.2.payer_id = None;
1573
1574 match Refund::try_from(tlv_stream.to_bytes()) {
1575 Ok(_) => panic!("expected error"),
1576 Err(e) => {
1577 assert_eq!(
1578 e,
1579 Bolt12ParseError::InvalidSemantics(
1580 Bolt12SemanticError::MissingPayerSigningPubkey
1581 )
1582 );
1583 },
1584 }
1585 }
1586
1587 #[test]
1588 fn parses_refund_with_optional_fields() {
1589 let past_expiry = Duration::from_secs(0);
1590 let paths = [
1591 BlindedMessagePath::from_blinded_path(
1592 pubkey(40),
1593 pubkey(41),
1594 vec![
1595 BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
1596 BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
1597 ],
1598 ),
1599 BlindedMessagePath::from_blinded_path(
1600 pubkey(40),
1601 pubkey(41),
1602 vec![
1603 BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
1604 BlindedHop { blinded_node_id: pubkey(46), encrypted_payload: vec![0; 46] },
1605 ],
1606 ),
1607 ];
1608
1609 let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000)
1610 .unwrap()
1611 .absolute_expiry(past_expiry)
1612 .issuer("bar".into())
1613 .path(paths[0].clone())
1614 .path(paths[1].clone())
1615 .chain(Network::Testnet)
1616 .features_unchecked(InvoiceRequestFeatures::unknown())
1617 .quantity(10)
1618 .payer_note("baz".into())
1619 .build()
1620 .unwrap();
1621 match refund.to_string().parse::<Refund>() {
1622 Ok(refund) => {
1623 assert_eq!(refund.absolute_expiry(), Some(past_expiry));
1624 #[cfg(feature = "std")]
1625 assert!(refund.is_expired());
1626 assert_eq!(refund.paths(), &paths[..]);
1627 assert_eq!(refund.issuer(), Some(PrintableString("bar")));
1628 assert_eq!(refund.chain(), ChainHash::using_genesis_block(Network::Testnet));
1629 assert_eq!(refund.features(), &InvoiceRequestFeatures::unknown());
1630 assert_eq!(refund.quantity(), Some(10));
1631 assert_eq!(refund.payer_note(), Some(PrintableString("baz")));
1632 },
1633 Err(e) => panic!("error parsing refund: {:?}", e),
1634 }
1635 }
1636
1637 #[test]
1638 fn fails_parsing_refund_with_unexpected_fields() {
1639 let refund =
1640 RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap().build().unwrap();
1641 if let Err(e) = refund.to_string().parse::<Refund>() {
1642 panic!("error parsing refund: {:?}", e);
1643 }
1644
1645 let metadata = vec![42; 32];
1646 let mut tlv_stream = refund.as_tlv_stream();
1647 tlv_stream.1.metadata = Some(&metadata);
1648
1649 match Refund::try_from(tlv_stream.to_bytes()) {
1650 Ok(_) => panic!("expected error"),
1651 Err(e) => {
1652 assert_eq!(
1653 e,
1654 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::UnexpectedMetadata)
1655 );
1656 },
1657 }
1658
1659 let chains = vec![ChainHash::using_genesis_block(Network::Testnet)];
1660 let mut tlv_stream = refund.as_tlv_stream();
1661 tlv_stream.1.chains = Some(&chains);
1662
1663 match Refund::try_from(tlv_stream.to_bytes()) {
1664 Ok(_) => panic!("expected error"),
1665 Err(e) => {
1666 assert_eq!(
1667 e,
1668 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::UnexpectedChain)
1669 );
1670 },
1671 }
1672
1673 let mut tlv_stream = refund.as_tlv_stream();
1674 tlv_stream.1.currency = Some(&b"USD");
1675 tlv_stream.1.amount = Some(1000);
1676
1677 match Refund::try_from(tlv_stream.to_bytes()) {
1678 Ok(_) => panic!("expected error"),
1679 Err(e) => {
1680 assert_eq!(
1681 e,
1682 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::UnexpectedAmount)
1683 );
1684 },
1685 }
1686
1687 let features = OfferFeatures::unknown();
1688 let mut tlv_stream = refund.as_tlv_stream();
1689 tlv_stream.1.features = Some(&features);
1690
1691 match Refund::try_from(tlv_stream.to_bytes()) {
1692 Ok(_) => panic!("expected error"),
1693 Err(e) => {
1694 assert_eq!(
1695 e,
1696 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::UnexpectedFeatures)
1697 );
1698 },
1699 }
1700
1701 let mut tlv_stream = refund.as_tlv_stream();
1702 tlv_stream.1.quantity_max = Some(10);
1703
1704 match Refund::try_from(tlv_stream.to_bytes()) {
1705 Ok(_) => panic!("expected error"),
1706 Err(e) => {
1707 assert_eq!(
1708 e,
1709 Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::UnexpectedQuantity)
1710 );
1711 },
1712 }
1713
1714 let issuer_id = payer_pubkey();
1715 let mut tlv_stream = refund.as_tlv_stream();
1716 tlv_stream.1.issuer_id = Some(&issuer_id);
1717
1718 match Refund::try_from(tlv_stream.to_bytes()) {
1719 Ok(_) => panic!("expected error"),
1720 Err(e) => {
1721 assert_eq!(
1722 e,
1723 Bolt12ParseError::InvalidSemantics(
1724 Bolt12SemanticError::UnexpectedIssuerSigningPubkey
1725 )
1726 );
1727 },
1728 }
1729 }
1730
1731 #[test]
1732 fn parses_refund_with_unknown_tlv_records() {
1733 const UNKNOWN_ODD_TYPE: u64 = INVOICE_REQUEST_TYPES.end - 1;
1734 assert!(UNKNOWN_ODD_TYPE % 2 == 1);
1735
1736 let refund =
1737 RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap().build().unwrap();
1738
1739 let mut encoded_refund = Vec::new();
1740 refund.write(&mut encoded_refund).unwrap();
1741 BigSize(UNKNOWN_ODD_TYPE).write(&mut encoded_refund).unwrap();
1742 BigSize(32).write(&mut encoded_refund).unwrap();
1743 [42u8; 32].write(&mut encoded_refund).unwrap();
1744
1745 match Refund::try_from(encoded_refund.clone()) {
1746 Ok(refund) => assert_eq!(refund.bytes, encoded_refund),
1747 Err(e) => panic!("error parsing refund: {:?}", e),
1748 }
1749
1750 const UNKNOWN_EVEN_TYPE: u64 = INVOICE_REQUEST_TYPES.end - 2;
1751 assert!(UNKNOWN_EVEN_TYPE % 2 == 0);
1752
1753 let refund =
1754 RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap().build().unwrap();
1755
1756 let mut encoded_refund = Vec::new();
1757 refund.write(&mut encoded_refund).unwrap();
1758 BigSize(UNKNOWN_EVEN_TYPE).write(&mut encoded_refund).unwrap();
1759 BigSize(32).write(&mut encoded_refund).unwrap();
1760 [42u8; 32].write(&mut encoded_refund).unwrap();
1761
1762 match Refund::try_from(encoded_refund) {
1763 Ok(_) => panic!("expected error"),
1764 Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::UnknownRequiredFeature)),
1765 }
1766 }
1767
1768 #[test]
1769 fn parses_refund_with_experimental_tlv_records() {
1770 const UNKNOWN_ODD_TYPE: u64 = EXPERIMENTAL_INVOICE_REQUEST_TYPES.start + 1;
1771 assert!(UNKNOWN_ODD_TYPE % 2 == 1);
1772
1773 let refund =
1774 RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap().build().unwrap();
1775
1776 let mut encoded_refund = Vec::new();
1777 refund.write(&mut encoded_refund).unwrap();
1778 BigSize(UNKNOWN_ODD_TYPE).write(&mut encoded_refund).unwrap();
1779 BigSize(32).write(&mut encoded_refund).unwrap();
1780 [42u8; 32].write(&mut encoded_refund).unwrap();
1781
1782 match Refund::try_from(encoded_refund.clone()) {
1783 Ok(refund) => assert_eq!(refund.bytes, encoded_refund),
1784 Err(e) => panic!("error parsing refund: {:?}", e),
1785 }
1786
1787 const UNKNOWN_EVEN_TYPE: u64 = EXPERIMENTAL_INVOICE_REQUEST_TYPES.start;
1788 assert!(UNKNOWN_EVEN_TYPE % 2 == 0);
1789
1790 let refund =
1791 RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap().build().unwrap();
1792
1793 let mut encoded_refund = Vec::new();
1794 refund.write(&mut encoded_refund).unwrap();
1795 BigSize(UNKNOWN_EVEN_TYPE).write(&mut encoded_refund).unwrap();
1796 BigSize(32).write(&mut encoded_refund).unwrap();
1797 [42u8; 32].write(&mut encoded_refund).unwrap();
1798
1799 match Refund::try_from(encoded_refund) {
1800 Ok(_) => panic!("expected error"),
1801 Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::UnknownRequiredFeature)),
1802 }
1803 }
1804
1805 #[test]
1806 fn fails_parsing_refund_with_out_of_range_tlv_records() {
1807 let secp_ctx = Secp256k1::new();
1808 let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
1809 let refund =
1810 RefundBuilder::new(vec![1; 32], keys.public_key(), 1000).unwrap().build().unwrap();
1811
1812 let mut encoded_refund = Vec::new();
1813 refund.write(&mut encoded_refund).unwrap();
1814 BigSize(1002).write(&mut encoded_refund).unwrap();
1815 BigSize(32).write(&mut encoded_refund).unwrap();
1816 [42u8; 32].write(&mut encoded_refund).unwrap();
1817
1818 match Refund::try_from(encoded_refund) {
1819 Ok(_) => panic!("expected error"),
1820 Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1821 }
1822
1823 let mut encoded_refund = Vec::new();
1824 refund.write(&mut encoded_refund).unwrap();
1825 BigSize(EXPERIMENTAL_INVOICE_REQUEST_TYPES.end).write(&mut encoded_refund).unwrap();
1826 BigSize(32).write(&mut encoded_refund).unwrap();
1827 [42u8; 32].write(&mut encoded_refund).unwrap();
1828
1829 match Refund::try_from(encoded_refund) {
1830 Ok(_) => panic!("expected error"),
1831 Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1832 }
1833 }
1834}