@@ -567,19 +567,20 @@ mod tests {
567567 use crate :: blinded_path:: { BlindedHop , BlindedPath , IntroductionNode } ;
568568 use crate :: ln:: features:: { Bolt12InvoiceFeatures , OfferFeatures } ;
569569 use crate :: ln:: inbound_payment:: ExpandedKey ;
570+ use crate :: ln:: msgs:: DecodeError ;
570571 use crate :: offers:: invoice:: InvoiceTlvStreamRef ;
571572 use crate :: offers:: merkle;
572573 use crate :: offers:: merkle:: { SignatureTlvStreamRef , TaggedHash } ;
573574 use crate :: offers:: offer:: { Offer , OfferBuilder , OfferTlvStreamRef , Quantity } ;
574- use crate :: offers:: parse:: Bolt12SemanticError ;
575+ use crate :: offers:: parse:: { Bolt12ParseError , Bolt12SemanticError } ;
575576 use crate :: offers:: static_invoice:: {
576577 StaticInvoice , StaticInvoiceBuilder , DEFAULT_RELATIVE_EXPIRY , SIGNATURE_TAG ,
577578 } ;
578579 use crate :: offers:: test_utils:: * ;
579580 use crate :: sign:: KeyMaterial ;
580- use crate :: util:: ser:: { Iterable , Writeable } ;
581+ use crate :: util:: ser:: { BigSize , Iterable , Writeable } ;
581582 use bitcoin:: blockdata:: constants:: ChainHash ;
582- use bitcoin:: secp256k1:: Secp256k1 ;
583+ use bitcoin:: secp256k1:: { self , Secp256k1 } ;
583584 use bitcoin:: Network ;
584585 use core:: time:: Duration ;
585586
@@ -597,6 +598,43 @@ mod tests {
597598 }
598599 }
599600
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+
600638 fn blinded_path ( ) -> BlindedPath {
601639 BlindedPath {
602640 introduction_node : IntroductionNode :: NodeId ( pubkey ( 40 ) ) ,
@@ -907,4 +945,231 @@ mod tests {
907945 panic ! ( "expected error" )
908946 }
909947 }
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+ }
9101175}
0 commit comments