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