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