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