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