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