@@ -563,19 +563,20 @@ mod tests {
563563 use crate :: blinded_path:: { BlindedHop , BlindedPath , IntroductionNode } ;
564564 use crate :: ln:: features:: { Bolt12InvoiceFeatures , OfferFeatures } ;
565565 use crate :: ln:: inbound_payment:: ExpandedKey ;
566+ use crate :: ln:: msgs:: DecodeError ;
566567 use crate :: offers:: invoice:: InvoiceTlvStreamRef ;
567568 use crate :: offers:: merkle;
568569 use crate :: offers:: merkle:: { SignatureTlvStreamRef , TaggedHash } ;
569570 use crate :: offers:: offer:: { Offer , OfferBuilder , OfferTlvStreamRef , Quantity } ;
570- use crate :: offers:: parse:: Bolt12SemanticError ;
571+ use crate :: offers:: parse:: { Bolt12ParseError , Bolt12SemanticError } ;
571572 use crate :: offers:: static_invoice:: {
572573 StaticInvoice , StaticInvoiceBuilder , DEFAULT_RELATIVE_EXPIRY , SIGNATURE_TAG ,
573574 } ;
574575 use crate :: offers:: test_utils:: * ;
575576 use crate :: sign:: KeyMaterial ;
576- use crate :: util:: ser:: { Iterable , Writeable } ;
577+ use crate :: util:: ser:: { BigSize , Iterable , Writeable } ;
577578 use bitcoin:: blockdata:: constants:: ChainHash ;
578- use bitcoin:: secp256k1:: Secp256k1 ;
579+ use bitcoin:: secp256k1:: { self , Secp256k1 } ;
579580 use bitcoin:: Network ;
580581 use core:: time:: Duration ;
581582
@@ -593,6 +594,43 @@ mod tests {
593594 }
594595 }
595596
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+
596634 fn blinded_path ( ) -> BlindedPath {
597635 BlindedPath {
598636 introduction_node : IntroductionNode :: NodeId ( pubkey ( 40 ) ) ,
@@ -903,4 +941,231 @@ mod tests {
903941 panic ! ( "expected error" )
904942 }
905943 }
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+ }
9061171}
0 commit comments