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