@@ -553,22 +553,70 @@ mod tests {
553
553
use crate :: blinded_path:: { BlindedHop , BlindedPath , IntroductionNode } ;
554
554
use crate :: ln:: features:: { Bolt12InvoiceFeatures , OfferFeatures } ;
555
555
use crate :: ln:: inbound_payment:: ExpandedKey ;
556
- use crate :: offers:: invoice:: SIGNATURE_TAG ;
556
+ use crate :: ln:: msgs:: DecodeError ;
557
+ use crate :: offers:: invoice:: { InvoiceTlvStreamRef , SIGNATURE_TAG } ;
557
558
use crate :: offers:: merkle;
558
- use crate :: offers:: merkle:: TaggedHash ;
559
- use crate :: offers:: offer:: { Offer , OfferBuilder , Quantity } ;
560
- use crate :: offers:: parse:: Bolt12SemanticError ;
559
+ use crate :: offers:: merkle:: { SignatureTlvStreamRef , TaggedHash } ;
560
+ use crate :: offers:: offer:: { Offer , OfferBuilder , OfferTlvStreamRef , Quantity } ;
561
+ use crate :: offers:: parse:: { Bolt12ParseError , Bolt12SemanticError } ;
561
562
use crate :: offers:: static_invoice:: {
562
563
StaticInvoice , StaticInvoiceBuilder , DEFAULT_RELATIVE_EXPIRY ,
563
564
} ;
564
565
use crate :: offers:: test_utils:: * ;
565
566
use crate :: sign:: KeyMaterial ;
566
- use crate :: util:: ser:: Writeable ;
567
+ use crate :: util:: ser:: { BigSize , Writeable } ;
567
568
use bitcoin:: blockdata:: constants:: ChainHash ;
568
- use bitcoin:: secp256k1:: Secp256k1 ;
569
+ use bitcoin:: secp256k1:: { self , Secp256k1 } ;
569
570
use bitcoin:: Network ;
570
571
use core:: time:: Duration ;
571
572
573
+ impl StaticInvoice {
574
+ fn as_tlv_stream ( & self ) -> ( OfferTlvStreamRef , InvoiceTlvStreamRef , SignatureTlvStreamRef ) {
575
+ (
576
+ self . contents . offer . as_tlv_stream ( ) ,
577
+ self . contents . as_invoice_fields_tlv_stream ( ) ,
578
+ SignatureTlvStreamRef { signature : Some ( & self . signature ) } ,
579
+ )
580
+ }
581
+ }
582
+
583
+ fn tlv_stream_to_bytes (
584
+ tlv_stream : & ( OfferTlvStreamRef , InvoiceTlvStreamRef , SignatureTlvStreamRef ) ,
585
+ ) -> Vec < u8 > {
586
+ let mut buffer = Vec :: new ( ) ;
587
+ tlv_stream. 0 . write ( & mut buffer) . unwrap ( ) ;
588
+ tlv_stream. 1 . write ( & mut buffer) . unwrap ( ) ;
589
+ tlv_stream. 2 . write ( & mut buffer) . unwrap ( ) ;
590
+ buffer
591
+ }
592
+
593
+ fn invoice ( ) -> StaticInvoice {
594
+ let node_id = recipient_pubkey ( ) ;
595
+ let payment_paths = payment_paths ( ) ;
596
+ let now = now ( ) ;
597
+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
598
+ let entropy = FixedEntropy { } ;
599
+ let secp_ctx = Secp256k1 :: new ( ) ;
600
+
601
+ let offer =
602
+ OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, & entropy, & secp_ctx)
603
+ . path ( blinded_path ( ) )
604
+ . build ( )
605
+ . unwrap ( ) ;
606
+
607
+ let ( _offer_id, keys_opt) = offer. verify ( & expanded_key, & secp_ctx) . unwrap ( ) ;
608
+ StaticInvoiceBuilder :: for_offer_using_keys (
609
+ & offer,
610
+ payment_paths. clone ( ) ,
611
+ vec ! [ blinded_path( ) ] ,
612
+ now,
613
+ keys_opt. unwrap ( ) ,
614
+ )
615
+ . unwrap ( )
616
+ . build_and_sign ( & secp_ctx)
617
+ . unwrap ( )
618
+ }
619
+
572
620
fn blinded_path ( ) -> BlindedPath {
573
621
BlindedPath {
574
622
introduction_node : IntroductionNode :: NodeId ( pubkey ( 40 ) ) ,
@@ -851,4 +899,233 @@ mod tests {
851
899
panic ! ( "expected error" )
852
900
}
853
901
}
902
+
903
+ #[ test]
904
+ fn parses_invoice_with_relative_expiry ( ) {
905
+ let node_id = recipient_pubkey ( ) ;
906
+ let payment_paths = payment_paths ( ) ;
907
+ let now = now ( ) ;
908
+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
909
+ let entropy = FixedEntropy { } ;
910
+ let secp_ctx = Secp256k1 :: new ( ) ;
911
+
912
+ let offer =
913
+ OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, & entropy, & secp_ctx)
914
+ . path ( blinded_path ( ) )
915
+ . build ( )
916
+ . unwrap ( ) ;
917
+
918
+ const TEST_RELATIVE_EXPIRY : u32 = 3600 ;
919
+ let ( _offer_id, keys_opt) = offer. verify ( & expanded_key, & secp_ctx) . unwrap ( ) ;
920
+ let invoice = StaticInvoiceBuilder :: for_offer_using_keys (
921
+ & offer,
922
+ payment_paths. clone ( ) ,
923
+ vec ! [ blinded_path( ) ] ,
924
+ now,
925
+ keys_opt. unwrap ( ) ,
926
+ )
927
+ . unwrap ( )
928
+ . relative_expiry ( TEST_RELATIVE_EXPIRY )
929
+ . build_and_sign ( & secp_ctx)
930
+ . unwrap ( ) ;
931
+
932
+ let mut buffer = Vec :: new ( ) ;
933
+ invoice. write ( & mut buffer) . unwrap ( ) ;
934
+
935
+ match StaticInvoice :: try_from ( buffer) {
936
+ Ok ( invoice) => assert_eq ! (
937
+ invoice. relative_expiry( ) ,
938
+ Duration :: from_secs( TEST_RELATIVE_EXPIRY as u64 )
939
+ ) ,
940
+ Err ( e) => panic ! ( "error parsing invoice: {:?}" , e) ,
941
+ }
942
+ }
943
+
944
+ #[ test]
945
+ fn parses_invoice_with_allow_mpp ( ) {
946
+ let node_id = recipient_pubkey ( ) ;
947
+ let payment_paths = payment_paths ( ) ;
948
+ let now = now ( ) ;
949
+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
950
+ let entropy = FixedEntropy { } ;
951
+ let secp_ctx = Secp256k1 :: new ( ) ;
952
+
953
+ let offer =
954
+ OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, & entropy, & secp_ctx)
955
+ . path ( blinded_path ( ) )
956
+ . build ( )
957
+ . unwrap ( ) ;
958
+
959
+ let ( _offer_id, keys_opt) = offer. verify ( & expanded_key, & secp_ctx) . unwrap ( ) ;
960
+ let invoice = StaticInvoiceBuilder :: for_offer_using_keys (
961
+ & offer,
962
+ payment_paths. clone ( ) ,
963
+ vec ! [ blinded_path( ) ] ,
964
+ now,
965
+ keys_opt. unwrap ( ) ,
966
+ )
967
+ . unwrap ( )
968
+ . allow_mpp ( )
969
+ . build_and_sign ( & secp_ctx)
970
+ . unwrap ( ) ;
971
+
972
+ let mut buffer = Vec :: new ( ) ;
973
+ invoice. write ( & mut buffer) . unwrap ( ) ;
974
+
975
+ match StaticInvoice :: try_from ( buffer) {
976
+ Ok ( invoice) => {
977
+ let mut features = Bolt12InvoiceFeatures :: empty ( ) ;
978
+ features. set_basic_mpp_optional ( ) ;
979
+ assert_eq ! ( invoice. invoice_features( ) , & features) ;
980
+ } ,
981
+ Err ( e) => panic ! ( "error parsing invoice: {:?}" , e) ,
982
+ }
983
+ }
984
+
985
+ #[ test]
986
+ fn fails_parse_missing_invoice_fields ( ) {
987
+ // Error if `created_at` is missing.
988
+ let missing_created_at_invoice = invoice ( ) ;
989
+ let mut tlv_stream = missing_created_at_invoice. as_tlv_stream ( ) ;
990
+ tlv_stream. 1 . created_at = None ;
991
+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
992
+ Ok ( _) => panic ! ( "expected error" ) ,
993
+ Err ( e) => {
994
+ assert_eq ! (
995
+ e,
996
+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingCreationTime )
997
+ ) ;
998
+ } ,
999
+ }
1000
+
1001
+ // Error if `node_id` is missing.
1002
+ let missing_node_id_invoice = invoice ( ) ;
1003
+ let mut tlv_stream = missing_node_id_invoice. as_tlv_stream ( ) ;
1004
+ tlv_stream. 1 . node_id = None ;
1005
+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1006
+ Ok ( _) => panic ! ( "expected error" ) ,
1007
+ Err ( e) => {
1008
+ assert_eq ! (
1009
+ e,
1010
+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingSigningPubkey )
1011
+ ) ;
1012
+ } ,
1013
+ }
1014
+
1015
+ // Error if message paths are missing.
1016
+ let missing_message_paths_invoice = invoice ( ) ;
1017
+ let mut tlv_stream = missing_message_paths_invoice. as_tlv_stream ( ) ;
1018
+ tlv_stream. 1 . message_paths = None ;
1019
+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1020
+ Ok ( _) => panic ! ( "expected error" ) ,
1021
+ Err ( e) => {
1022
+ assert_eq ! (
1023
+ e,
1024
+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingPaths )
1025
+ ) ;
1026
+ } ,
1027
+ }
1028
+
1029
+ // Error if signature is missing.
1030
+ let invoice = invoice ( ) ;
1031
+ let mut buffer = Vec :: new ( ) ;
1032
+ ( invoice. contents . offer . as_tlv_stream ( ) , invoice. contents . as_invoice_fields_tlv_stream ( ) )
1033
+ . write ( & mut buffer)
1034
+ . unwrap ( ) ;
1035
+ match StaticInvoice :: try_from ( buffer) {
1036
+ Ok ( _) => panic ! ( "expected error" ) ,
1037
+ Err ( e) => assert_eq ! (
1038
+ e,
1039
+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingSignature )
1040
+ ) ,
1041
+ }
1042
+ }
1043
+
1044
+ #[ test]
1045
+ fn fails_parse_invalid_signing_pubkey ( ) {
1046
+ let invoice = invoice ( ) ;
1047
+ let invalid_pubkey = payer_pubkey ( ) ;
1048
+ let mut tlv_stream = invoice. as_tlv_stream ( ) ;
1049
+ tlv_stream. 1 . node_id = Some ( & invalid_pubkey) ;
1050
+
1051
+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1052
+ Ok ( _) => panic ! ( "expected error" ) ,
1053
+ Err ( e) => {
1054
+ assert_eq ! (
1055
+ e,
1056
+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: InvalidSigningPubkey )
1057
+ ) ;
1058
+ } ,
1059
+ }
1060
+ }
1061
+
1062
+ #[ test]
1063
+ fn fails_parsing_invoice_with_invalid_signature ( ) {
1064
+ let mut invoice = invoice ( ) ;
1065
+ let last_signature_byte = invoice. bytes . last_mut ( ) . unwrap ( ) ;
1066
+ * last_signature_byte = last_signature_byte. wrapping_add ( 1 ) ;
1067
+
1068
+ let mut buffer = Vec :: new ( ) ;
1069
+ invoice. write ( & mut buffer) . unwrap ( ) ;
1070
+
1071
+ match StaticInvoice :: try_from ( buffer) {
1072
+ Ok ( _) => panic ! ( "expected error" ) ,
1073
+ Err ( e) => {
1074
+ assert_eq ! (
1075
+ e,
1076
+ Bolt12ParseError :: InvalidSignature ( secp256k1:: Error :: InvalidSignature )
1077
+ ) ;
1078
+ } ,
1079
+ }
1080
+ }
1081
+
1082
+ #[ test]
1083
+ fn fails_parsing_invoice_with_extra_tlv_records ( ) {
1084
+ let invoice = invoice ( ) ;
1085
+ let mut encoded_invoice = Vec :: new ( ) ;
1086
+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
1087
+ BigSize ( 1002 ) . write ( & mut encoded_invoice) . unwrap ( ) ;
1088
+ BigSize ( 32 ) . write ( & mut encoded_invoice) . unwrap ( ) ;
1089
+ [ 42u8 ; 32 ] . write ( & mut encoded_invoice) . unwrap ( ) ;
1090
+
1091
+ match StaticInvoice :: try_from ( encoded_invoice) {
1092
+ Ok ( _) => panic ! ( "expected error" ) ,
1093
+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: Decode ( DecodeError :: InvalidValue ) ) ,
1094
+ }
1095
+ }
1096
+
1097
+ #[ test]
1098
+ fn fails_parsing_invoice_with_invalid_offer_fields ( ) {
1099
+ // Error if the offer is missing paths.
1100
+ let missing_offer_paths_invoice = invoice ( ) ;
1101
+ let mut tlv_stream = missing_offer_paths_invoice. as_tlv_stream ( ) ;
1102
+ tlv_stream. 0 . paths = None ;
1103
+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1104
+ Ok ( _) => panic ! ( "expected error" ) ,
1105
+ Err ( e) => {
1106
+ assert_eq ! (
1107
+ e,
1108
+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingPaths )
1109
+ ) ;
1110
+ } ,
1111
+ }
1112
+
1113
+ // Error if the offer has more than one chain.
1114
+ let invalid_offer_chains_invoice = invoice ( ) ;
1115
+ let mut tlv_stream = invalid_offer_chains_invoice. as_tlv_stream ( ) ;
1116
+ let invalid_chains = vec ! [
1117
+ ChainHash :: using_genesis_block( Network :: Bitcoin ) ,
1118
+ ChainHash :: using_genesis_block( Network :: Testnet ) ,
1119
+ ] ;
1120
+ tlv_stream. 0 . chains = Some ( & invalid_chains) ;
1121
+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1122
+ Ok ( _) => panic ! ( "expected error" ) ,
1123
+ Err ( e) => {
1124
+ assert_eq ! (
1125
+ e,
1126
+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: UnexpectedChain )
1127
+ ) ;
1128
+ } ,
1129
+ }
1130
+ }
854
1131
}
0 commit comments