@@ -566,19 +566,20 @@ mod tests {
566
566
use crate :: blinded_path:: { BlindedHop , BlindedPath , IntroductionNode } ;
567
567
use crate :: ln:: features:: { Bolt12InvoiceFeatures , OfferFeatures } ;
568
568
use crate :: ln:: inbound_payment:: ExpandedKey ;
569
+ use crate :: ln:: msgs:: DecodeError ;
569
570
use crate :: offers:: invoice:: InvoiceTlvStreamRef ;
570
571
use crate :: offers:: merkle;
571
572
use crate :: offers:: merkle:: { SignatureTlvStreamRef , TaggedHash } ;
572
573
use crate :: offers:: offer:: { Offer , OfferBuilder , OfferTlvStreamRef , Quantity } ;
573
- use crate :: offers:: parse:: Bolt12SemanticError ;
574
+ use crate :: offers:: parse:: { Bolt12ParseError , Bolt12SemanticError } ;
574
575
use crate :: offers:: static_invoice:: {
575
576
StaticInvoice , StaticInvoiceBuilder , DEFAULT_RELATIVE_EXPIRY , SIGNATURE_TAG ,
576
577
} ;
577
578
use crate :: offers:: test_utils:: * ;
578
579
use crate :: sign:: KeyMaterial ;
579
- use crate :: util:: ser:: { Iterable , Writeable } ;
580
+ use crate :: util:: ser:: { BigSize , Iterable , Writeable } ;
580
581
use bitcoin:: blockdata:: constants:: ChainHash ;
581
- use bitcoin:: secp256k1:: Secp256k1 ;
582
+ use bitcoin:: secp256k1:: { self , Secp256k1 } ;
582
583
use bitcoin:: Network ;
583
584
use core:: time:: Duration ;
584
585
@@ -596,6 +597,43 @@ mod tests {
596
597
}
597
598
}
598
599
600
+ fn tlv_stream_to_bytes (
601
+ tlv_stream : & ( OfferTlvStreamRef , InvoiceTlvStreamRef , SignatureTlvStreamRef ) ,
602
+ ) -> Vec < u8 > {
603
+ let mut buffer = Vec :: new ( ) ;
604
+ tlv_stream. 0 . write ( & mut buffer) . unwrap ( ) ;
605
+ tlv_stream. 1 . write ( & mut buffer) . unwrap ( ) ;
606
+ tlv_stream. 2 . write ( & mut buffer) . unwrap ( ) ;
607
+ buffer
608
+ }
609
+
610
+ fn invoice ( ) -> StaticInvoice {
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
+ 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
+
599
637
fn blinded_path ( ) -> BlindedPath {
600
638
BlindedPath {
601
639
introduction_node : IntroductionNode :: NodeId ( pubkey ( 40 ) ) ,
@@ -906,4 +944,231 @@ mod tests {
906
944
panic ! ( "expected error" )
907
945
}
908
946
}
947
+
948
+ #[ test]
949
+ fn parses_invoice_with_relative_expiry ( ) {
950
+ let node_id = recipient_pubkey ( ) ;
951
+ let payment_paths = payment_paths ( ) ;
952
+ let now = now ( ) ;
953
+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
954
+ let entropy = FixedEntropy { } ;
955
+ let secp_ctx = Secp256k1 :: new ( ) ;
956
+
957
+ let offer =
958
+ OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, & entropy, & secp_ctx)
959
+ . path ( blinded_path ( ) )
960
+ . build ( )
961
+ . unwrap ( ) ;
962
+
963
+ const TEST_RELATIVE_EXPIRY : u32 = 3600 ;
964
+ let invoice = StaticInvoiceBuilder :: for_offer_using_derived_keys (
965
+ & offer,
966
+ payment_paths. clone ( ) ,
967
+ vec ! [ blinded_path( ) ] ,
968
+ now,
969
+ & expanded_key,
970
+ & secp_ctx,
971
+ )
972
+ . unwrap ( )
973
+ . relative_expiry ( TEST_RELATIVE_EXPIRY )
974
+ . build_and_sign ( & secp_ctx)
975
+ . unwrap ( ) ;
976
+
977
+ let mut buffer = Vec :: new ( ) ;
978
+ invoice. write ( & mut buffer) . unwrap ( ) ;
979
+
980
+ match StaticInvoice :: try_from ( buffer) {
981
+ Ok ( invoice) => assert_eq ! (
982
+ invoice. relative_expiry( ) ,
983
+ Duration :: from_secs( TEST_RELATIVE_EXPIRY as u64 )
984
+ ) ,
985
+ Err ( e) => panic ! ( "error parsing invoice: {:?}" , e) ,
986
+ }
987
+ }
988
+
989
+ #[ test]
990
+ fn parses_invoice_with_allow_mpp ( ) {
991
+ let node_id = recipient_pubkey ( ) ;
992
+ let payment_paths = payment_paths ( ) ;
993
+ let now = now ( ) ;
994
+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
995
+ let entropy = FixedEntropy { } ;
996
+ let secp_ctx = Secp256k1 :: new ( ) ;
997
+
998
+ let offer =
999
+ OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, & entropy, & secp_ctx)
1000
+ . path ( blinded_path ( ) )
1001
+ . build ( )
1002
+ . unwrap ( ) ;
1003
+
1004
+ let invoice = StaticInvoiceBuilder :: for_offer_using_derived_keys (
1005
+ & offer,
1006
+ payment_paths. clone ( ) ,
1007
+ vec ! [ blinded_path( ) ] ,
1008
+ now,
1009
+ & expanded_key,
1010
+ & secp_ctx,
1011
+ )
1012
+ . unwrap ( )
1013
+ . allow_mpp ( )
1014
+ . build_and_sign ( & secp_ctx)
1015
+ . unwrap ( ) ;
1016
+
1017
+ let mut buffer = Vec :: new ( ) ;
1018
+ invoice. write ( & mut buffer) . unwrap ( ) ;
1019
+
1020
+ match StaticInvoice :: try_from ( buffer) {
1021
+ Ok ( invoice) => {
1022
+ let mut features = Bolt12InvoiceFeatures :: empty ( ) ;
1023
+ features. set_basic_mpp_optional ( ) ;
1024
+ assert_eq ! ( invoice. invoice_features( ) , & features) ;
1025
+ } ,
1026
+ Err ( e) => panic ! ( "error parsing invoice: {:?}" , e) ,
1027
+ }
1028
+ }
1029
+
1030
+ #[ test]
1031
+ fn fails_parsing_missing_invoice_fields ( ) {
1032
+ // Error if `created_at` is missing.
1033
+ let missing_created_at_invoice = invoice ( ) ;
1034
+ let mut tlv_stream = missing_created_at_invoice. as_tlv_stream ( ) ;
1035
+ tlv_stream. 1 . created_at = None ;
1036
+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1037
+ Ok ( _) => panic ! ( "expected error" ) ,
1038
+ Err ( e) => {
1039
+ assert_eq ! (
1040
+ e,
1041
+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingCreationTime )
1042
+ ) ;
1043
+ } ,
1044
+ }
1045
+
1046
+ // Error if `node_id` is missing.
1047
+ let missing_node_id_invoice = invoice ( ) ;
1048
+ let mut tlv_stream = missing_node_id_invoice. as_tlv_stream ( ) ;
1049
+ tlv_stream. 1 . node_id = None ;
1050
+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1051
+ Ok ( _) => panic ! ( "expected error" ) ,
1052
+ Err ( e) => {
1053
+ assert_eq ! (
1054
+ e,
1055
+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingSigningPubkey )
1056
+ ) ;
1057
+ } ,
1058
+ }
1059
+
1060
+ // Error if message paths are missing.
1061
+ let missing_message_paths_invoice = invoice ( ) ;
1062
+ let mut tlv_stream = missing_message_paths_invoice. as_tlv_stream ( ) ;
1063
+ tlv_stream. 1 . message_paths = None ;
1064
+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1065
+ Ok ( _) => panic ! ( "expected error" ) ,
1066
+ Err ( e) => {
1067
+ assert_eq ! (
1068
+ e,
1069
+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingPaths )
1070
+ ) ;
1071
+ } ,
1072
+ }
1073
+
1074
+ // Error if signature is missing.
1075
+ let invoice = invoice ( ) ;
1076
+ let mut buffer = Vec :: new ( ) ;
1077
+ invoice. contents . as_tlv_stream ( ) . write ( & mut buffer) . unwrap ( ) ;
1078
+ match StaticInvoice :: try_from ( buffer) {
1079
+ Ok ( _) => panic ! ( "expected error" ) ,
1080
+ Err ( e) => assert_eq ! (
1081
+ e,
1082
+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingSignature )
1083
+ ) ,
1084
+ }
1085
+ }
1086
+
1087
+ #[ test]
1088
+ fn fails_parsing_invalid_signing_pubkey ( ) {
1089
+ let invoice = invoice ( ) ;
1090
+ let invalid_pubkey = payer_pubkey ( ) ;
1091
+ let mut tlv_stream = invoice. as_tlv_stream ( ) ;
1092
+ tlv_stream. 1 . node_id = Some ( & invalid_pubkey) ;
1093
+
1094
+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1095
+ Ok ( _) => panic ! ( "expected error" ) ,
1096
+ Err ( e) => {
1097
+ assert_eq ! (
1098
+ e,
1099
+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: InvalidSigningPubkey )
1100
+ ) ;
1101
+ } ,
1102
+ }
1103
+ }
1104
+
1105
+ #[ test]
1106
+ fn fails_parsing_invoice_with_invalid_signature ( ) {
1107
+ let mut invoice = invoice ( ) ;
1108
+ let last_signature_byte = invoice. bytes . last_mut ( ) . unwrap ( ) ;
1109
+ * last_signature_byte = last_signature_byte. wrapping_add ( 1 ) ;
1110
+
1111
+ let mut buffer = Vec :: new ( ) ;
1112
+ invoice. write ( & mut buffer) . unwrap ( ) ;
1113
+
1114
+ match StaticInvoice :: try_from ( buffer) {
1115
+ Ok ( _) => panic ! ( "expected error" ) ,
1116
+ Err ( e) => {
1117
+ assert_eq ! (
1118
+ e,
1119
+ Bolt12ParseError :: InvalidSignature ( secp256k1:: Error :: InvalidSignature )
1120
+ ) ;
1121
+ } ,
1122
+ }
1123
+ }
1124
+
1125
+ #[ test]
1126
+ fn fails_parsing_invoice_with_extra_tlv_records ( ) {
1127
+ let invoice = invoice ( ) ;
1128
+ let mut encoded_invoice = Vec :: new ( ) ;
1129
+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
1130
+ BigSize ( 1002 ) . write ( & mut encoded_invoice) . unwrap ( ) ;
1131
+ BigSize ( 32 ) . write ( & mut encoded_invoice) . unwrap ( ) ;
1132
+ [ 42u8 ; 32 ] . write ( & mut encoded_invoice) . unwrap ( ) ;
1133
+
1134
+ match StaticInvoice :: try_from ( encoded_invoice) {
1135
+ Ok ( _) => panic ! ( "expected error" ) ,
1136
+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: Decode ( DecodeError :: InvalidValue ) ) ,
1137
+ }
1138
+ }
1139
+
1140
+ #[ test]
1141
+ fn fails_parsing_invoice_with_invalid_offer_fields ( ) {
1142
+ // Error if the offer is missing paths.
1143
+ let missing_offer_paths_invoice = invoice ( ) ;
1144
+ let mut tlv_stream = missing_offer_paths_invoice. as_tlv_stream ( ) ;
1145
+ tlv_stream. 0 . paths = None ;
1146
+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1147
+ Ok ( _) => panic ! ( "expected error" ) ,
1148
+ Err ( e) => {
1149
+ assert_eq ! (
1150
+ e,
1151
+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingPaths )
1152
+ ) ;
1153
+ } ,
1154
+ }
1155
+
1156
+ // Error if the offer has more than one chain.
1157
+ let invalid_offer_chains_invoice = invoice ( ) ;
1158
+ let mut tlv_stream = invalid_offer_chains_invoice. as_tlv_stream ( ) ;
1159
+ let invalid_chains = vec ! [
1160
+ ChainHash :: using_genesis_block( Network :: Bitcoin ) ,
1161
+ ChainHash :: using_genesis_block( Network :: Testnet ) ,
1162
+ ] ;
1163
+ tlv_stream. 0 . chains = Some ( & invalid_chains) ;
1164
+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1165
+ Ok ( _) => panic ! ( "expected error" ) ,
1166
+ Err ( e) => {
1167
+ assert_eq ! (
1168
+ e,
1169
+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: UnexpectedChain )
1170
+ ) ;
1171
+ } ,
1172
+ }
1173
+ }
909
1174
}
0 commit comments