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