@@ -546,3 +546,304 @@ impl TryFrom<(OfferTlvStream, InvoiceTlvStream)> for InvoiceContents {
546546 } )
547547 }
548548}
549+
550+ #[ cfg( test) ]
551+ mod tests {
552+ use crate :: blinded_path:: { BlindedHop , BlindedPath , IntroductionNode } ;
553+ use crate :: ln:: features:: { Bolt12InvoiceFeatures , OfferFeatures } ;
554+ use crate :: ln:: inbound_payment:: ExpandedKey ;
555+ use crate :: offers:: invoice:: SIGNATURE_TAG ;
556+ use crate :: offers:: merkle;
557+ use crate :: offers:: merkle:: TaggedHash ;
558+ use crate :: offers:: offer:: { Offer , OfferBuilder , Quantity } ;
559+ use crate :: offers:: parse:: Bolt12SemanticError ;
560+ use crate :: offers:: static_invoice:: {
561+ StaticInvoice , StaticInvoiceBuilder , DEFAULT_RELATIVE_EXPIRY ,
562+ } ;
563+ use crate :: offers:: test_utils:: * ;
564+ use crate :: sign:: KeyMaterial ;
565+ use crate :: util:: ser:: Writeable ;
566+ use bitcoin:: blockdata:: constants:: ChainHash ;
567+ use bitcoin:: network:: constants:: Network ;
568+ use bitcoin:: secp256k1:: Secp256k1 ;
569+ use core:: time:: Duration ;
570+
571+ fn blinded_path ( ) -> BlindedPath {
572+ BlindedPath {
573+ introduction_node : IntroductionNode :: NodeId ( pubkey ( 40 ) ) ,
574+ blinding_point : pubkey ( 41 ) ,
575+ blinded_hops : vec ! [
576+ BlindedHop { blinded_node_id: pubkey( 42 ) , encrypted_payload: vec![ 0 ; 43 ] } ,
577+ BlindedHop { blinded_node_id: pubkey( 43 ) , encrypted_payload: vec![ 0 ; 44 ] } ,
578+ ] ,
579+ }
580+ }
581+
582+ #[ test]
583+ fn builds_invoice_for_offer_with_defaults ( ) {
584+ let node_id = recipient_pubkey ( ) ;
585+ let payment_paths = payment_paths ( ) ;
586+ let now = now ( ) ;
587+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
588+ let entropy = FixedEntropy { } ;
589+ let secp_ctx = Secp256k1 :: new ( ) ;
590+
591+ let offer =
592+ OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, & entropy, & secp_ctx)
593+ . path ( blinded_path ( ) )
594+ . build ( )
595+ . unwrap ( ) ;
596+
597+ let ( _offer_id, keys_opt) = offer. verify ( & expanded_key, & secp_ctx) . unwrap ( ) ;
598+ let invoice = StaticInvoiceBuilder :: for_offer_using_keys (
599+ & offer,
600+ payment_paths. clone ( ) ,
601+ vec ! [ blinded_path( ) ] ,
602+ now,
603+ keys_opt. unwrap ( ) ,
604+ )
605+ . unwrap ( )
606+ . build_and_sign ( & secp_ctx)
607+ . unwrap ( ) ;
608+
609+ let mut buffer = Vec :: new ( ) ;
610+ invoice. write ( & mut buffer) . unwrap ( ) ;
611+
612+ assert_eq ! ( invoice. bytes, buffer. as_slice( ) ) ;
613+ assert ! ( invoice. metadata( ) . is_some( ) ) ;
614+ assert_eq ! ( invoice. amount( ) , None ) ;
615+ assert_eq ! ( invoice. description( ) , None ) ;
616+ assert_eq ! ( invoice. offer_features( ) , & OfferFeatures :: empty( ) ) ;
617+ assert_eq ! ( invoice. absolute_expiry( ) , None ) ;
618+ assert_eq ! ( invoice. offer_message_paths( ) , & [ blinded_path( ) ] ) ;
619+ assert_eq ! ( invoice. message_paths( ) , & [ blinded_path( ) ] ) ;
620+ assert_eq ! ( invoice. issuer( ) , None ) ;
621+ assert_eq ! ( invoice. supported_quantity( ) , Quantity :: One ) ;
622+ assert_ne ! ( invoice. signing_pubkey( ) , recipient_pubkey( ) ) ;
623+ assert_eq ! ( invoice. chain( ) , ChainHash :: using_genesis_block( Network :: Bitcoin ) ) ;
624+ assert_eq ! ( invoice. payment_paths( ) , payment_paths. as_slice( ) ) ;
625+ assert_eq ! ( invoice. created_at( ) , now) ;
626+ assert_eq ! ( invoice. relative_expiry( ) , DEFAULT_RELATIVE_EXPIRY ) ;
627+ #[ cfg( feature = "std" ) ]
628+ assert ! ( !invoice. is_expired( ) ) ;
629+ assert_eq ! ( invoice. fallbacks( ) , vec![ ] ) ;
630+ assert_eq ! ( invoice. invoice_features( ) , & Bolt12InvoiceFeatures :: empty( ) ) ;
631+
632+ let message = TaggedHash :: from_valid_tlv_stream_bytes ( SIGNATURE_TAG , & invoice. bytes ) ;
633+ assert ! ( merkle:: verify_signature(
634+ & invoice. signature,
635+ & message,
636+ keys_opt. unwrap( ) . public_key( )
637+ )
638+ . is_ok( ) ) ;
639+
640+ if let Err ( e) = StaticInvoice :: try_from ( buffer) {
641+ panic ! ( "error parsing invoice: {:?}" , e) ;
642+ }
643+ }
644+
645+ #[ cfg( feature = "std" ) ]
646+ #[ test]
647+ fn builds_invoice_from_offer_with_expiration ( ) {
648+ let node_id = recipient_pubkey ( ) ;
649+ let now = now ( ) ;
650+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
651+ let entropy = FixedEntropy { } ;
652+ let secp_ctx = Secp256k1 :: new ( ) ;
653+
654+ let future_expiry = Duration :: from_secs ( u64:: max_value ( ) ) ;
655+ let past_expiry = Duration :: from_secs ( 0 ) ;
656+
657+ let valid_offer =
658+ OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, & entropy, & secp_ctx)
659+ . path ( blinded_path ( ) )
660+ . absolute_expiry ( future_expiry)
661+ . build ( )
662+ . unwrap ( ) ;
663+
664+ let ( _offer_id, keys_opt) = valid_offer. verify ( & expanded_key, & secp_ctx) . unwrap ( ) ;
665+ let invoice = StaticInvoiceBuilder :: for_offer_using_keys (
666+ & valid_offer,
667+ payment_paths ( ) ,
668+ vec ! [ blinded_path( ) ] ,
669+ now,
670+ keys_opt. unwrap ( ) ,
671+ )
672+ . unwrap ( )
673+ . build_and_sign ( & secp_ctx)
674+ . unwrap ( ) ;
675+ assert ! ( !invoice. is_expired( ) ) ;
676+ assert_eq ! ( invoice. absolute_expiry( ) , Some ( future_expiry) ) ;
677+
678+ let expired_offer =
679+ OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, & entropy, & secp_ctx)
680+ . path ( blinded_path ( ) )
681+ . absolute_expiry ( past_expiry)
682+ . build ( )
683+ . unwrap ( ) ;
684+ let ( _offer_id, keys_opt) = expired_offer. verify ( & expanded_key, & secp_ctx) . unwrap ( ) ;
685+ if let Err ( e) = StaticInvoiceBuilder :: for_offer_using_keys (
686+ & expired_offer,
687+ payment_paths ( ) ,
688+ vec ! [ blinded_path( ) ] ,
689+ now,
690+ keys_opt. unwrap ( ) ,
691+ )
692+ . unwrap ( )
693+ . build_and_sign ( & secp_ctx)
694+ {
695+ assert_eq ! ( e, Bolt12SemanticError :: AlreadyExpired ) ;
696+ } else {
697+ panic ! ( "expected error" )
698+ }
699+ }
700+
701+ #[ test]
702+ fn fails_build_with_missing_paths ( ) {
703+ let node_id = recipient_pubkey ( ) ;
704+ let now = now ( ) ;
705+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
706+ let entropy = FixedEntropy { } ;
707+ let secp_ctx = Secp256k1 :: new ( ) ;
708+
709+ let valid_offer =
710+ OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, & entropy, & secp_ctx)
711+ . path ( blinded_path ( ) )
712+ . build ( )
713+ . unwrap ( ) ;
714+ let ( _offer_id, keys_opt) = valid_offer. verify ( & expanded_key, & secp_ctx) . unwrap ( ) ;
715+
716+ // Error if payment paths are missing.
717+ if let Err ( e) = StaticInvoiceBuilder :: for_offer_using_keys (
718+ & valid_offer,
719+ Vec :: new ( ) ,
720+ vec ! [ blinded_path( ) ] ,
721+ now,
722+ keys_opt. unwrap ( ) ,
723+ ) {
724+ assert_eq ! ( e, Bolt12SemanticError :: MissingPaths ) ;
725+ } else {
726+ panic ! ( "expected error" )
727+ }
728+
729+ // Error if message paths are missing.
730+ if let Err ( e) = StaticInvoiceBuilder :: for_offer_using_keys (
731+ & valid_offer,
732+ payment_paths ( ) ,
733+ Vec :: new ( ) ,
734+ now,
735+ keys_opt. unwrap ( ) ,
736+ ) {
737+ assert_eq ! ( e, Bolt12SemanticError :: MissingPaths ) ;
738+ } else {
739+ panic ! ( "expected error" )
740+ }
741+
742+ // Error if offer paths are missing.
743+ let mut offer_without_paths = valid_offer. clone ( ) ;
744+ let mut offer_tlv_stream = offer_without_paths. as_tlv_stream ( ) ;
745+ offer_tlv_stream. paths . take ( ) ;
746+ let mut buffer = Vec :: new ( ) ;
747+ offer_tlv_stream. write ( & mut buffer) . unwrap ( ) ;
748+ offer_without_paths = Offer :: try_from ( buffer) . unwrap ( ) ;
749+ if let Err ( e) = StaticInvoiceBuilder :: for_offer_using_keys (
750+ & offer_without_paths,
751+ payment_paths ( ) ,
752+ vec ! [ blinded_path( ) ] ,
753+ now,
754+ keys_opt. unwrap ( ) ,
755+ ) {
756+ assert_eq ! ( e, Bolt12SemanticError :: MissingPaths ) ;
757+ } else {
758+ panic ! ( "expected error" )
759+ }
760+ }
761+
762+ #[ test]
763+ fn fails_build_offer_signing_pubkey ( ) {
764+ let node_id = recipient_pubkey ( ) ;
765+ let now = now ( ) ;
766+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
767+ let entropy = FixedEntropy { } ;
768+ let secp_ctx = Secp256k1 :: new ( ) ;
769+
770+ let valid_offer =
771+ OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, & entropy, & secp_ctx)
772+ . path ( blinded_path ( ) )
773+ . build ( )
774+ . unwrap ( ) ;
775+ let ( _offer_id, keys_opt) = valid_offer. verify ( & expanded_key, & secp_ctx) . unwrap ( ) ;
776+
777+ // Error if offer signing pubkey is missing.
778+ let mut offer_missing_signing_pubkey = valid_offer. clone ( ) ;
779+ let mut offer_tlv_stream = offer_missing_signing_pubkey. as_tlv_stream ( ) ;
780+ offer_tlv_stream. node_id . take ( ) ;
781+ let mut buffer = Vec :: new ( ) ;
782+ offer_tlv_stream. write ( & mut buffer) . unwrap ( ) ;
783+ offer_missing_signing_pubkey = Offer :: try_from ( buffer) . unwrap ( ) ;
784+
785+ if let Err ( e) = StaticInvoiceBuilder :: for_offer_using_keys (
786+ & offer_missing_signing_pubkey,
787+ payment_paths ( ) ,
788+ vec ! [ blinded_path( ) ] ,
789+ now,
790+ keys_opt. unwrap ( ) ,
791+ ) {
792+ assert_eq ! ( e, Bolt12SemanticError :: MissingSigningPubkey ) ;
793+ } else {
794+ panic ! ( "expected error" )
795+ }
796+
797+ // Error if the offer's signing pubkey doesn't match the invoice's.
798+ let mut offer_invalid_signing_pubkey = valid_offer. clone ( ) ;
799+ let mut offer_tlv_stream = offer_invalid_signing_pubkey. as_tlv_stream ( ) ;
800+ let invalid_node_id = payer_pubkey ( ) ;
801+ offer_tlv_stream. node_id = Some ( & invalid_node_id) ;
802+ let mut buffer = Vec :: new ( ) ;
803+ offer_tlv_stream. write ( & mut buffer) . unwrap ( ) ;
804+ offer_invalid_signing_pubkey = Offer :: try_from ( buffer) . unwrap ( ) ;
805+
806+ if let Err ( e) = StaticInvoiceBuilder :: for_offer_using_keys (
807+ & offer_invalid_signing_pubkey,
808+ payment_paths ( ) ,
809+ vec ! [ blinded_path( ) ] ,
810+ now,
811+ keys_opt. unwrap ( ) ,
812+ ) {
813+ assert_eq ! ( e, Bolt12SemanticError :: InvalidSigningPubkey ) ;
814+ } else {
815+ panic ! ( "expected error" )
816+ }
817+ }
818+
819+ #[ test]
820+ fn fails_build_with_extra_offer_chains ( ) {
821+ let node_id = recipient_pubkey ( ) ;
822+ let now = now ( ) ;
823+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
824+ let entropy = FixedEntropy { } ;
825+ let secp_ctx = Secp256k1 :: new ( ) ;
826+
827+ let offer_with_extra_chain =
828+ OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, & entropy, & secp_ctx)
829+ . path ( blinded_path ( ) )
830+ . chain ( Network :: Bitcoin )
831+ . chain ( Network :: Testnet )
832+ . build ( )
833+ . unwrap ( ) ;
834+ let ( _offer_id, keys_opt) =
835+ offer_with_extra_chain. verify ( & expanded_key, & secp_ctx) . unwrap ( ) ;
836+
837+ if let Err ( e) = StaticInvoiceBuilder :: for_offer_using_keys (
838+ & offer_with_extra_chain,
839+ payment_paths ( ) ,
840+ vec ! [ blinded_path( ) ] ,
841+ now,
842+ keys_opt. unwrap ( ) ,
843+ ) {
844+ assert_eq ! ( e, Bolt12SemanticError :: UnexpectedChain ) ;
845+ } else {
846+ panic ! ( "expected error" )
847+ }
848+ }
849+ }
0 commit comments