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