Skip to content

Commit fb0d8e1

Browse files
Static invoice parsing tests
1 parent dc2f56f commit fb0d8e1

File tree

1 file changed

+268
-3
lines changed

1 file changed

+268
-3
lines changed

lightning/src/offers/static_invoice.rs

Lines changed: 268 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -566,19 +566,20 @@ mod tests {
566566
use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
567567
use crate::ln::features::{Bolt12InvoiceFeatures, OfferFeatures};
568568
use crate::ln::inbound_payment::ExpandedKey;
569+
use crate::ln::msgs::DecodeError;
569570
use crate::offers::invoice::InvoiceTlvStreamRef;
570571
use crate::offers::merkle;
571572
use crate::offers::merkle::{SignatureTlvStreamRef, TaggedHash};
572573
use crate::offers::offer::{Offer, OfferBuilder, OfferTlvStreamRef, Quantity};
573-
use crate::offers::parse::Bolt12SemanticError;
574+
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError};
574575
use crate::offers::static_invoice::{
575576
StaticInvoice, StaticInvoiceBuilder, DEFAULT_RELATIVE_EXPIRY, SIGNATURE_TAG,
576577
};
577578
use crate::offers::test_utils::*;
578579
use crate::sign::KeyMaterial;
579-
use crate::util::ser::{Iterable, Writeable};
580+
use crate::util::ser::{BigSize, Iterable, Writeable};
580581
use bitcoin::blockdata::constants::ChainHash;
581-
use bitcoin::secp256k1::Secp256k1;
582+
use bitcoin::secp256k1::{self, Secp256k1};
582583
use bitcoin::Network;
583584
use core::time::Duration;
584585

@@ -596,6 +597,43 @@ mod tests {
596597
}
597598
}
598599

600+
fn tlv_stream_to_bytes(
601+
tlv_stream: &(OfferTlvStreamRef, InvoiceTlvStreamRef, SignatureTlvStreamRef),
602+
) -> Vec<u8> {
603+
let mut buffer = Vec::new();
604+
tlv_stream.0.write(&mut buffer).unwrap();
605+
tlv_stream.1.write(&mut buffer).unwrap();
606+
tlv_stream.2.write(&mut buffer).unwrap();
607+
buffer
608+
}
609+
610+
fn invoice() -> StaticInvoice {
611+
let node_id = recipient_pubkey();
612+
let payment_paths = payment_paths();
613+
let now = now();
614+
let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
615+
let entropy = FixedEntropy {};
616+
let secp_ctx = Secp256k1::new();
617+
618+
let offer =
619+
OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
620+
.path(blinded_path())
621+
.build()
622+
.unwrap();
623+
624+
StaticInvoiceBuilder::for_offer_using_derived_keys(
625+
&offer,
626+
payment_paths.clone(),
627+
vec![blinded_path()],
628+
now,
629+
&expanded_key,
630+
&secp_ctx,
631+
)
632+
.unwrap()
633+
.build_and_sign(&secp_ctx)
634+
.unwrap()
635+
}
636+
599637
fn blinded_path() -> BlindedPath {
600638
BlindedPath {
601639
introduction_node: IntroductionNode::NodeId(pubkey(40)),
@@ -906,4 +944,231 @@ mod tests {
906944
panic!("expected error")
907945
}
908946
}
947+
948+
#[test]
949+
fn parses_invoice_with_relative_expiry() {
950+
let node_id = recipient_pubkey();
951+
let payment_paths = payment_paths();
952+
let now = now();
953+
let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
954+
let entropy = FixedEntropy {};
955+
let secp_ctx = Secp256k1::new();
956+
957+
let offer =
958+
OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
959+
.path(blinded_path())
960+
.build()
961+
.unwrap();
962+
963+
const TEST_RELATIVE_EXPIRY: u32 = 3600;
964+
let invoice = StaticInvoiceBuilder::for_offer_using_derived_keys(
965+
&offer,
966+
payment_paths.clone(),
967+
vec![blinded_path()],
968+
now,
969+
&expanded_key,
970+
&secp_ctx,
971+
)
972+
.unwrap()
973+
.relative_expiry(TEST_RELATIVE_EXPIRY)
974+
.build_and_sign(&secp_ctx)
975+
.unwrap();
976+
977+
let mut buffer = Vec::new();
978+
invoice.write(&mut buffer).unwrap();
979+
980+
match StaticInvoice::try_from(buffer) {
981+
Ok(invoice) => assert_eq!(
982+
invoice.relative_expiry(),
983+
Duration::from_secs(TEST_RELATIVE_EXPIRY as u64)
984+
),
985+
Err(e) => panic!("error parsing invoice: {:?}", e),
986+
}
987+
}
988+
989+
#[test]
990+
fn parses_invoice_with_allow_mpp() {
991+
let node_id = recipient_pubkey();
992+
let payment_paths = payment_paths();
993+
let now = now();
994+
let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
995+
let entropy = FixedEntropy {};
996+
let secp_ctx = Secp256k1::new();
997+
998+
let offer =
999+
OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
1000+
.path(blinded_path())
1001+
.build()
1002+
.unwrap();
1003+
1004+
let invoice = StaticInvoiceBuilder::for_offer_using_derived_keys(
1005+
&offer,
1006+
payment_paths.clone(),
1007+
vec![blinded_path()],
1008+
now,
1009+
&expanded_key,
1010+
&secp_ctx,
1011+
)
1012+
.unwrap()
1013+
.allow_mpp()
1014+
.build_and_sign(&secp_ctx)
1015+
.unwrap();
1016+
1017+
let mut buffer = Vec::new();
1018+
invoice.write(&mut buffer).unwrap();
1019+
1020+
match StaticInvoice::try_from(buffer) {
1021+
Ok(invoice) => {
1022+
let mut features = Bolt12InvoiceFeatures::empty();
1023+
features.set_basic_mpp_optional();
1024+
assert_eq!(invoice.invoice_features(), &features);
1025+
},
1026+
Err(e) => panic!("error parsing invoice: {:?}", e),
1027+
}
1028+
}
1029+
1030+
#[test]
1031+
fn fails_parsing_missing_invoice_fields() {
1032+
// Error if `created_at` is missing.
1033+
let missing_created_at_invoice = invoice();
1034+
let mut tlv_stream = missing_created_at_invoice.as_tlv_stream();
1035+
tlv_stream.1.created_at = None;
1036+
match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1037+
Ok(_) => panic!("expected error"),
1038+
Err(e) => {
1039+
assert_eq!(
1040+
e,
1041+
Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingCreationTime)
1042+
);
1043+
},
1044+
}
1045+
1046+
// Error if `node_id` is missing.
1047+
let missing_node_id_invoice = invoice();
1048+
let mut tlv_stream = missing_node_id_invoice.as_tlv_stream();
1049+
tlv_stream.1.node_id = None;
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::MissingSigningPubkey)
1056+
);
1057+
},
1058+
}
1059+
1060+
// Error if message paths are missing.
1061+
let missing_message_paths_invoice = invoice();
1062+
let mut tlv_stream = missing_message_paths_invoice.as_tlv_stream();
1063+
tlv_stream.1.message_paths = None;
1064+
match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1065+
Ok(_) => panic!("expected error"),
1066+
Err(e) => {
1067+
assert_eq!(
1068+
e,
1069+
Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingPaths)
1070+
);
1071+
},
1072+
}
1073+
1074+
// Error if signature is missing.
1075+
let invoice = invoice();
1076+
let mut buffer = Vec::new();
1077+
invoice.contents.as_tlv_stream().write(&mut buffer).unwrap();
1078+
match StaticInvoice::try_from(buffer) {
1079+
Ok(_) => panic!("expected error"),
1080+
Err(e) => assert_eq!(
1081+
e,
1082+
Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSignature)
1083+
),
1084+
}
1085+
}
1086+
1087+
#[test]
1088+
fn fails_parsing_invalid_signing_pubkey() {
1089+
let invoice = invoice();
1090+
let invalid_pubkey = payer_pubkey();
1091+
let mut tlv_stream = invoice.as_tlv_stream();
1092+
tlv_stream.1.node_id = Some(&invalid_pubkey);
1093+
1094+
match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1095+
Ok(_) => panic!("expected error"),
1096+
Err(e) => {
1097+
assert_eq!(
1098+
e,
1099+
Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidSigningPubkey)
1100+
);
1101+
},
1102+
}
1103+
}
1104+
1105+
#[test]
1106+
fn fails_parsing_invoice_with_invalid_signature() {
1107+
let mut invoice = invoice();
1108+
let last_signature_byte = invoice.bytes.last_mut().unwrap();
1109+
*last_signature_byte = last_signature_byte.wrapping_add(1);
1110+
1111+
let mut buffer = Vec::new();
1112+
invoice.write(&mut buffer).unwrap();
1113+
1114+
match StaticInvoice::try_from(buffer) {
1115+
Ok(_) => panic!("expected error"),
1116+
Err(e) => {
1117+
assert_eq!(
1118+
e,
1119+
Bolt12ParseError::InvalidSignature(secp256k1::Error::InvalidSignature)
1120+
);
1121+
},
1122+
}
1123+
}
1124+
1125+
#[test]
1126+
fn fails_parsing_invoice_with_extra_tlv_records() {
1127+
let invoice = invoice();
1128+
let mut encoded_invoice = Vec::new();
1129+
invoice.write(&mut encoded_invoice).unwrap();
1130+
BigSize(1002).write(&mut encoded_invoice).unwrap();
1131+
BigSize(32).write(&mut encoded_invoice).unwrap();
1132+
[42u8; 32].write(&mut encoded_invoice).unwrap();
1133+
1134+
match StaticInvoice::try_from(encoded_invoice) {
1135+
Ok(_) => panic!("expected error"),
1136+
Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::InvalidValue)),
1137+
}
1138+
}
1139+
1140+
#[test]
1141+
fn fails_parsing_invoice_with_invalid_offer_fields() {
1142+
// Error if the offer is missing paths.
1143+
let missing_offer_paths_invoice = invoice();
1144+
let mut tlv_stream = missing_offer_paths_invoice.as_tlv_stream();
1145+
tlv_stream.0.paths = None;
1146+
match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1147+
Ok(_) => panic!("expected error"),
1148+
Err(e) => {
1149+
assert_eq!(
1150+
e,
1151+
Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingPaths)
1152+
);
1153+
},
1154+
}
1155+
1156+
// Error if the offer has more than one chain.
1157+
let invalid_offer_chains_invoice = invoice();
1158+
let mut tlv_stream = invalid_offer_chains_invoice.as_tlv_stream();
1159+
let invalid_chains = vec![
1160+
ChainHash::using_genesis_block(Network::Bitcoin),
1161+
ChainHash::using_genesis_block(Network::Testnet),
1162+
];
1163+
tlv_stream.0.chains = Some(&invalid_chains);
1164+
match StaticInvoice::try_from(tlv_stream_to_bytes(&tlv_stream)) {
1165+
Ok(_) => panic!("expected error"),
1166+
Err(e) => {
1167+
assert_eq!(
1168+
e,
1169+
Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::UnexpectedChain)
1170+
);
1171+
},
1172+
}
1173+
}
9091174
}

0 commit comments

Comments
 (0)