@@ -552,22 +552,72 @@ mod tests {
552
552
use crate :: blinded_path:: { BlindedHop , BlindedPath , IntroductionNode } ;
553
553
use crate :: ln:: features:: { Bolt12InvoiceFeatures , OfferFeatures } ;
554
554
use crate :: ln:: inbound_payment:: ExpandedKey ;
555
+ use crate :: ln:: msgs:: DecodeError ;
555
556
use crate :: offers:: invoice:: SIGNATURE_TAG ;
556
557
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 } ;
560
561
use crate :: offers:: static_invoice:: {
561
562
StaticInvoice , StaticInvoiceBuilder , DEFAULT_RELATIVE_EXPIRY ,
562
563
} ;
563
564
use crate :: offers:: test_utils:: * ;
564
565
use crate :: sign:: KeyMaterial ;
565
- use crate :: util:: ser:: Writeable ;
566
+ use crate :: util:: ser:: { BigSize , Writeable } ;
566
567
use bitcoin:: blockdata:: constants:: ChainHash ;
567
568
use bitcoin:: network:: constants:: Network ;
568
- use bitcoin:: secp256k1:: Secp256k1 ;
569
+ use bitcoin:: secp256k1:: { self , Secp256k1 } ;
569
570
use core:: time:: Duration ;
570
571
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
+
571
621
fn blinded_path ( ) -> BlindedPath {
572
622
BlindedPath {
573
623
introduction_node : IntroductionNode :: NodeId ( pubkey ( 40 ) ) ,
@@ -846,4 +896,233 @@ mod tests {
846
896
panic ! ( "expected error" )
847
897
}
848
898
}
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
+ }
849
1128
}
0 commit comments