@@ -553,22 +553,70 @@ mod tests {
553553 use crate :: blinded_path:: { BlindedHop , BlindedPath , IntroductionNode } ;
554554 use crate :: ln:: features:: { Bolt12InvoiceFeatures , OfferFeatures } ;
555555 use crate :: ln:: inbound_payment:: ExpandedKey ;
556- use crate :: offers:: invoice:: SIGNATURE_TAG ;
556+ use crate :: ln:: msgs:: DecodeError ;
557+ use crate :: offers:: invoice:: { InvoiceTlvStreamRef , SIGNATURE_TAG } ;
557558 use crate :: offers:: merkle;
558- use crate :: offers:: merkle:: TaggedHash ;
559- use crate :: offers:: offer:: { Offer , OfferBuilder , Quantity } ;
560- use crate :: offers:: parse:: Bolt12SemanticError ;
559+ use crate :: offers:: merkle:: { SignatureTlvStreamRef , TaggedHash } ;
560+ use crate :: offers:: offer:: { Offer , OfferBuilder , OfferTlvStreamRef , Quantity } ;
561+ use crate :: offers:: parse:: { Bolt12ParseError , Bolt12SemanticError } ;
561562 use crate :: offers:: static_invoice:: {
562563 StaticInvoice , StaticInvoiceBuilder , DEFAULT_RELATIVE_EXPIRY ,
563564 } ;
564565 use crate :: offers:: test_utils:: * ;
565566 use crate :: sign:: KeyMaterial ;
566- use crate :: util:: ser:: Writeable ;
567+ use crate :: util:: ser:: { BigSize , Writeable } ;
567568 use bitcoin:: blockdata:: constants:: ChainHash ;
568- use bitcoin:: secp256k1:: Secp256k1 ;
569+ use bitcoin:: secp256k1:: { self , Secp256k1 } ;
569570 use bitcoin:: Network ;
570571 use core:: time:: Duration ;
571572
573+ impl StaticInvoice {
574+ fn as_tlv_stream ( & self ) -> ( OfferTlvStreamRef , InvoiceTlvStreamRef , SignatureTlvStreamRef ) {
575+ (
576+ self . contents . offer . as_tlv_stream ( ) ,
577+ self . contents . as_invoice_fields_tlv_stream ( ) ,
578+ SignatureTlvStreamRef { signature : Some ( & self . signature ) } ,
579+ )
580+ }
581+ }
582+
583+ fn tlv_stream_to_bytes (
584+ tlv_stream : & ( OfferTlvStreamRef , InvoiceTlvStreamRef , SignatureTlvStreamRef ) ,
585+ ) -> Vec < u8 > {
586+ let mut buffer = Vec :: new ( ) ;
587+ tlv_stream. 0 . write ( & mut buffer) . unwrap ( ) ;
588+ tlv_stream. 1 . write ( & mut buffer) . unwrap ( ) ;
589+ tlv_stream. 2 . write ( & mut buffer) . unwrap ( ) ;
590+ buffer
591+ }
592+
593+ fn invoice ( ) -> StaticInvoice {
594+ let node_id = recipient_pubkey ( ) ;
595+ let payment_paths = payment_paths ( ) ;
596+ let now = now ( ) ;
597+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
598+ let entropy = FixedEntropy { } ;
599+ let secp_ctx = Secp256k1 :: new ( ) ;
600+
601+ let offer =
602+ OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, & entropy, & secp_ctx)
603+ . path ( blinded_path ( ) )
604+ . build ( )
605+ . unwrap ( ) ;
606+
607+ let ( _offer_id, keys_opt) = offer. verify ( & expanded_key, & secp_ctx) . unwrap ( ) ;
608+ StaticInvoiceBuilder :: for_offer_using_keys (
609+ & offer,
610+ payment_paths. clone ( ) ,
611+ vec ! [ blinded_path( ) ] ,
612+ now,
613+ keys_opt. unwrap ( ) ,
614+ )
615+ . unwrap ( )
616+ . build_and_sign ( & secp_ctx)
617+ . unwrap ( )
618+ }
619+
572620 fn blinded_path ( ) -> BlindedPath {
573621 BlindedPath {
574622 introduction_node : IntroductionNode :: NodeId ( pubkey ( 40 ) ) ,
@@ -851,4 +899,233 @@ mod tests {
851899 panic ! ( "expected error" )
852900 }
853901 }
902+
903+ #[ test]
904+ fn parses_invoice_with_relative_expiry ( ) {
905+ let node_id = recipient_pubkey ( ) ;
906+ let payment_paths = payment_paths ( ) ;
907+ let now = now ( ) ;
908+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
909+ let entropy = FixedEntropy { } ;
910+ let secp_ctx = Secp256k1 :: new ( ) ;
911+
912+ let offer =
913+ OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, & entropy, & secp_ctx)
914+ . path ( blinded_path ( ) )
915+ . build ( )
916+ . unwrap ( ) ;
917+
918+ const TEST_RELATIVE_EXPIRY : u32 = 3600 ;
919+ let ( _offer_id, keys_opt) = offer. verify ( & expanded_key, & secp_ctx) . unwrap ( ) ;
920+ let invoice = StaticInvoiceBuilder :: for_offer_using_keys (
921+ & offer,
922+ payment_paths. clone ( ) ,
923+ vec ! [ blinded_path( ) ] ,
924+ now,
925+ keys_opt. unwrap ( ) ,
926+ )
927+ . unwrap ( )
928+ . relative_expiry ( TEST_RELATIVE_EXPIRY )
929+ . build_and_sign ( & secp_ctx)
930+ . unwrap ( ) ;
931+
932+ let mut buffer = Vec :: new ( ) ;
933+ invoice. write ( & mut buffer) . unwrap ( ) ;
934+
935+ match StaticInvoice :: try_from ( buffer) {
936+ Ok ( invoice) => assert_eq ! (
937+ invoice. relative_expiry( ) ,
938+ Duration :: from_secs( TEST_RELATIVE_EXPIRY as u64 )
939+ ) ,
940+ Err ( e) => panic ! ( "error parsing invoice: {:?}" , e) ,
941+ }
942+ }
943+
944+ #[ test]
945+ fn parses_invoice_with_allow_mpp ( ) {
946+ let node_id = recipient_pubkey ( ) ;
947+ let payment_paths = payment_paths ( ) ;
948+ let now = now ( ) ;
949+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
950+ let entropy = FixedEntropy { } ;
951+ let secp_ctx = Secp256k1 :: new ( ) ;
952+
953+ let offer =
954+ OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, & entropy, & secp_ctx)
955+ . path ( blinded_path ( ) )
956+ . build ( )
957+ . unwrap ( ) ;
958+
959+ let ( _offer_id, keys_opt) = offer. verify ( & expanded_key, & secp_ctx) . unwrap ( ) ;
960+ let invoice = StaticInvoiceBuilder :: for_offer_using_keys (
961+ & offer,
962+ payment_paths. clone ( ) ,
963+ vec ! [ blinded_path( ) ] ,
964+ now,
965+ keys_opt. unwrap ( ) ,
966+ )
967+ . unwrap ( )
968+ . allow_mpp ( )
969+ . build_and_sign ( & secp_ctx)
970+ . unwrap ( ) ;
971+
972+ let mut buffer = Vec :: new ( ) ;
973+ invoice. write ( & mut buffer) . unwrap ( ) ;
974+
975+ match StaticInvoice :: try_from ( buffer) {
976+ Ok ( invoice) => {
977+ let mut features = Bolt12InvoiceFeatures :: empty ( ) ;
978+ features. set_basic_mpp_optional ( ) ;
979+ assert_eq ! ( invoice. invoice_features( ) , & features) ;
980+ } ,
981+ Err ( e) => panic ! ( "error parsing invoice: {:?}" , e) ,
982+ }
983+ }
984+
985+ #[ test]
986+ fn fails_parse_missing_invoice_fields ( ) {
987+ // Error if `created_at` is missing.
988+ let missing_created_at_invoice = invoice ( ) ;
989+ let mut tlv_stream = missing_created_at_invoice. as_tlv_stream ( ) ;
990+ tlv_stream. 1 . created_at = None ;
991+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
992+ Ok ( _) => panic ! ( "expected error" ) ,
993+ Err ( e) => {
994+ assert_eq ! (
995+ e,
996+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingCreationTime )
997+ ) ;
998+ } ,
999+ }
1000+
1001+ // Error if `node_id` is missing.
1002+ let missing_node_id_invoice = invoice ( ) ;
1003+ let mut tlv_stream = missing_node_id_invoice. as_tlv_stream ( ) ;
1004+ tlv_stream. 1 . node_id = None ;
1005+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1006+ Ok ( _) => panic ! ( "expected error" ) ,
1007+ Err ( e) => {
1008+ assert_eq ! (
1009+ e,
1010+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingSigningPubkey )
1011+ ) ;
1012+ } ,
1013+ }
1014+
1015+ // Error if message paths are missing.
1016+ let missing_message_paths_invoice = invoice ( ) ;
1017+ let mut tlv_stream = missing_message_paths_invoice. as_tlv_stream ( ) ;
1018+ tlv_stream. 1 . message_paths = None ;
1019+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1020+ Ok ( _) => panic ! ( "expected error" ) ,
1021+ Err ( e) => {
1022+ assert_eq ! (
1023+ e,
1024+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingPaths )
1025+ ) ;
1026+ } ,
1027+ }
1028+
1029+ // Error if signature is missing.
1030+ let invoice = invoice ( ) ;
1031+ let mut buffer = Vec :: new ( ) ;
1032+ ( invoice. contents . offer . as_tlv_stream ( ) , invoice. contents . as_invoice_fields_tlv_stream ( ) )
1033+ . write ( & mut buffer)
1034+ . unwrap ( ) ;
1035+ match StaticInvoice :: try_from ( buffer) {
1036+ Ok ( _) => panic ! ( "expected error" ) ,
1037+ Err ( e) => assert_eq ! (
1038+ e,
1039+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingSignature )
1040+ ) ,
1041+ }
1042+ }
1043+
1044+ #[ test]
1045+ fn fails_parse_invalid_signing_pubkey ( ) {
1046+ let invoice = invoice ( ) ;
1047+ let invalid_pubkey = payer_pubkey ( ) ;
1048+ let mut tlv_stream = invoice. as_tlv_stream ( ) ;
1049+ tlv_stream. 1 . node_id = Some ( & invalid_pubkey) ;
1050+
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 :: InvalidSigningPubkey )
1057+ ) ;
1058+ } ,
1059+ }
1060+ }
1061+
1062+ #[ test]
1063+ fn fails_parsing_invoice_with_invalid_signature ( ) {
1064+ let mut invoice = invoice ( ) ;
1065+ let last_signature_byte = invoice. bytes . last_mut ( ) . unwrap ( ) ;
1066+ * last_signature_byte = last_signature_byte. wrapping_add ( 1 ) ;
1067+
1068+ let mut buffer = Vec :: new ( ) ;
1069+ invoice. write ( & mut buffer) . unwrap ( ) ;
1070+
1071+ match StaticInvoice :: try_from ( buffer) {
1072+ Ok ( _) => panic ! ( "expected error" ) ,
1073+ Err ( e) => {
1074+ assert_eq ! (
1075+ e,
1076+ Bolt12ParseError :: InvalidSignature ( secp256k1:: Error :: InvalidSignature )
1077+ ) ;
1078+ } ,
1079+ }
1080+ }
1081+
1082+ #[ test]
1083+ fn fails_parsing_invoice_with_extra_tlv_records ( ) {
1084+ let invoice = invoice ( ) ;
1085+ let mut encoded_invoice = Vec :: new ( ) ;
1086+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
1087+ BigSize ( 1002 ) . write ( & mut encoded_invoice) . unwrap ( ) ;
1088+ BigSize ( 32 ) . write ( & mut encoded_invoice) . unwrap ( ) ;
1089+ [ 42u8 ; 32 ] . write ( & mut encoded_invoice) . unwrap ( ) ;
1090+
1091+ match StaticInvoice :: try_from ( encoded_invoice) {
1092+ Ok ( _) => panic ! ( "expected error" ) ,
1093+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: Decode ( DecodeError :: InvalidValue ) ) ,
1094+ }
1095+ }
1096+
1097+ #[ test]
1098+ fn fails_parsing_invoice_with_invalid_offer_fields ( ) {
1099+ // Error if the offer is missing paths.
1100+ let missing_offer_paths_invoice = invoice ( ) ;
1101+ let mut tlv_stream = missing_offer_paths_invoice. as_tlv_stream ( ) ;
1102+ tlv_stream. 0 . paths = None ;
1103+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1104+ Ok ( _) => panic ! ( "expected error" ) ,
1105+ Err ( e) => {
1106+ assert_eq ! (
1107+ e,
1108+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingPaths )
1109+ ) ;
1110+ } ,
1111+ }
1112+
1113+ // Error if the offer has more than one chain.
1114+ let invalid_offer_chains_invoice = invoice ( ) ;
1115+ let mut tlv_stream = invalid_offer_chains_invoice. as_tlv_stream ( ) ;
1116+ let invalid_chains = vec ! [
1117+ ChainHash :: using_genesis_block( Network :: Bitcoin ) ,
1118+ ChainHash :: using_genesis_block( Network :: Testnet ) ,
1119+ ] ;
1120+ tlv_stream. 0 . chains = Some ( & invalid_chains) ;
1121+ match StaticInvoice :: try_from ( tlv_stream_to_bytes ( & tlv_stream) ) {
1122+ Ok ( _) => panic ! ( "expected error" ) ,
1123+ Err ( e) => {
1124+ assert_eq ! (
1125+ e,
1126+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: UnexpectedChain )
1127+ ) ;
1128+ } ,
1129+ }
1130+ }
8541131}
0 commit comments