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