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