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