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