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