Skip to content

Commit 3daa79e

Browse files
Static invoice parsing tests
1 parent 5d5158c commit 3daa79e

File tree

1 file changed

+284
-5
lines changed

1 file changed

+284
-5
lines changed

lightning/src/offers/static_invoice.rs

+284-5
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)