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