@@ -566,19 +566,20 @@ mod tests {
566566 use crate :: blinded_path:: { BlindedHop , BlindedPath , IntroductionNode } ;
567567 use crate :: ln:: features:: { Bolt12InvoiceFeatures , OfferFeatures } ;
568568 use crate :: ln:: inbound_payment:: ExpandedKey ;
569+ use crate :: ln:: msgs:: DecodeError ;
569570 use crate :: offers:: invoice:: InvoiceTlvStreamRef ;
570571 use crate :: offers:: merkle;
571572 use crate :: offers:: merkle:: { SignatureTlvStreamRef , TaggedHash } ;
572573 use crate :: offers:: offer:: { Offer , OfferBuilder , OfferTlvStreamRef , Quantity } ;
573- use crate :: offers:: parse:: Bolt12SemanticError ;
574+ use crate :: offers:: parse:: { Bolt12ParseError , Bolt12SemanticError } ;
574575 use crate :: offers:: static_invoice:: {
575576 StaticInvoice , StaticInvoiceBuilder , DEFAULT_RELATIVE_EXPIRY , SIGNATURE_TAG ,
576577 } ;
577578 use crate :: offers:: test_utils:: * ;
578579 use crate :: sign:: KeyMaterial ;
579- use crate :: util:: ser:: { Iterable , Writeable } ;
580+ use crate :: util:: ser:: { BigSize , Iterable , Writeable } ;
580581 use bitcoin:: blockdata:: constants:: ChainHash ;
581- use bitcoin:: secp256k1:: Secp256k1 ;
582+ use bitcoin:: secp256k1:: { self , Secp256k1 } ;
582583 use bitcoin:: Network ;
583584 use core:: time:: Duration ;
584585
@@ -596,6 +597,43 @@ mod tests {
596597 }
597598 }
598599
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+
599637 fn blinded_path ( ) -> BlindedPath {
600638 BlindedPath {
601639 introduction_node : IntroductionNode :: NodeId ( pubkey ( 40 ) ) ,
@@ -906,4 +944,231 @@ mod tests {
906944 panic ! ( "expected error" )
907945 }
908946 }
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+ }
9091174}
0 commit comments