Skip to content

Commit 882fdb1

Browse files
committed
Invoice request parsing tests
Tests for checking invoice_request message semantics when parsing bytes as defined by BOLT 12.
1 parent 3ee05b6 commit 882fdb1

File tree

2 files changed

+371
-3
lines changed

2 files changed

+371
-3
lines changed

lightning/src/offers/invoice_request.rs

Lines changed: 361 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,30 @@ impl<'a> InvoiceRequestBuilder<'a> {
180180
}
181181
}
182182

183+
#[cfg(test)]
184+
impl<'a> InvoiceRequestBuilder<'a> {
185+
fn chain_unchecked(mut self, network: Network) -> Self {
186+
let chain = ChainHash::using_genesis_block(network);
187+
self.invoice_request.chain = Some(chain);
188+
self
189+
}
190+
191+
fn amount_msats_unchecked(mut self, amount_msats: u64) -> Self {
192+
self.invoice_request.amount_msats = Some(amount_msats);
193+
self
194+
}
195+
196+
fn quantity_unchecked(mut self, quantity: u64) -> Self {
197+
self.invoice_request.quantity = Some(quantity);
198+
self
199+
}
200+
201+
fn build_unchecked(self) -> UnsignedInvoiceRequest<'a> {
202+
let InvoiceRequestBuilder { offer, invoice_request } = self;
203+
UnsignedInvoiceRequest { offer, invoice_request }
204+
}
205+
}
206+
183207
/// A semantically valid [`InvoiceRequest`] that hasn't been signed.
184208
pub struct UnsignedInvoiceRequest<'a> {
185209
offer: &'a Offer,
@@ -464,7 +488,7 @@ mod tests {
464488
use crate::ln::features::InvoiceRequestFeatures;
465489
use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
466490
use crate::offers::merkle::SignError;
467-
use crate::offers::offer::{OfferBuilder, Quantity};
491+
use crate::offers::offer::{Amount, OfferBuilder, Quantity};
468492
use crate::offers::parse::{ParseError, SemanticError};
469493
use crate::util::ser::{BigSize, Writeable};
470494
use crate::util::string::PrintableString;
@@ -894,6 +918,342 @@ mod tests {
894918
}
895919
}
896920

921+
#[test]
922+
fn parses_invoice_request_with_metadata() {
923+
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
924+
.amount_msats(1000)
925+
.build().unwrap()
926+
.request_invoice(vec![42; 32], payer_pubkey()).unwrap()
927+
.build().unwrap()
928+
.sign(payer_sign).unwrap();
929+
930+
let mut buffer = Vec::new();
931+
invoice_request.write(&mut buffer).unwrap();
932+
933+
if let Err(e) = InvoiceRequest::try_from(buffer) {
934+
panic!("error parsing invoice_request: {:?}", e);
935+
}
936+
}
937+
938+
#[test]
939+
fn parses_invoice_request_with_chain() {
940+
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
941+
.amount_msats(1000)
942+
.build().unwrap()
943+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
944+
.chain(Network::Bitcoin).unwrap()
945+
.build().unwrap()
946+
.sign(payer_sign).unwrap();
947+
948+
let mut buffer = Vec::new();
949+
invoice_request.write(&mut buffer).unwrap();
950+
951+
if let Err(e) = InvoiceRequest::try_from(buffer) {
952+
panic!("error parsing invoice_request: {:?}", e);
953+
}
954+
955+
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
956+
.amount_msats(1000)
957+
.build().unwrap()
958+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
959+
.chain_unchecked(Network::Testnet)
960+
.build_unchecked()
961+
.sign(payer_sign).unwrap();
962+
963+
let mut buffer = Vec::new();
964+
invoice_request.write(&mut buffer).unwrap();
965+
966+
match InvoiceRequest::try_from(buffer) {
967+
Ok(_) => panic!("expected error"),
968+
Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::UnsupportedChain)),
969+
}
970+
}
971+
972+
#[test]
973+
fn parses_invoice_request_with_amount() {
974+
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
975+
.amount_msats(1000)
976+
.build().unwrap()
977+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
978+
.build().unwrap()
979+
.sign(payer_sign).unwrap();
980+
981+
let mut buffer = Vec::new();
982+
invoice_request.write(&mut buffer).unwrap();
983+
984+
if let Err(e) = InvoiceRequest::try_from(buffer) {
985+
panic!("error parsing invoice_request: {:?}", e);
986+
}
987+
988+
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
989+
.build().unwrap()
990+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
991+
.amount_msats(1000).unwrap()
992+
.build().unwrap()
993+
.sign(payer_sign).unwrap();
994+
995+
let mut buffer = Vec::new();
996+
invoice_request.write(&mut buffer).unwrap();
997+
998+
if let Err(e) = InvoiceRequest::try_from(buffer) {
999+
panic!("error parsing invoice_request: {:?}", e);
1000+
}
1001+
1002+
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
1003+
.build().unwrap()
1004+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1005+
.build_unchecked()
1006+
.sign(payer_sign).unwrap();
1007+
1008+
let mut buffer = Vec::new();
1009+
invoice_request.write(&mut buffer).unwrap();
1010+
1011+
match InvoiceRequest::try_from(buffer) {
1012+
Ok(_) => panic!("expected error"),
1013+
Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingAmount)),
1014+
}
1015+
1016+
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
1017+
.amount_msats(1000)
1018+
.build().unwrap()
1019+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1020+
.amount_msats_unchecked(999)
1021+
.build_unchecked()
1022+
.sign(payer_sign).unwrap();
1023+
1024+
let mut buffer = Vec::new();
1025+
invoice_request.write(&mut buffer).unwrap();
1026+
1027+
match InvoiceRequest::try_from(buffer) {
1028+
Ok(_) => panic!("expected error"),
1029+
Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::InsufficientAmount)),
1030+
}
1031+
1032+
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
1033+
.amount(Amount::Currency { iso4217_code: *b"USD", amount: 1000 })
1034+
.build_unchecked()
1035+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1036+
.build_unchecked()
1037+
.sign(payer_sign).unwrap();
1038+
1039+
let mut buffer = Vec::new();
1040+
invoice_request.write(&mut buffer).unwrap();
1041+
1042+
match InvoiceRequest::try_from(buffer) {
1043+
Ok(_) => panic!("expected error"),
1044+
Err(e) => {
1045+
assert_eq!(e, ParseError::InvalidSemantics(SemanticError::UnsupportedCurrency));
1046+
},
1047+
}
1048+
}
1049+
1050+
#[test]
1051+
fn parses_invoice_request_with_quantity() {
1052+
let ten = NonZeroU64::new(10).unwrap();
1053+
1054+
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
1055+
.amount_msats(1000)
1056+
.supported_quantity(Quantity::one())
1057+
.build().unwrap()
1058+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1059+
.build().unwrap()
1060+
.sign(payer_sign).unwrap();
1061+
1062+
let mut buffer = Vec::new();
1063+
invoice_request.write(&mut buffer).unwrap();
1064+
1065+
if let Err(e) = InvoiceRequest::try_from(buffer) {
1066+
panic!("error parsing invoice_request: {:?}", e);
1067+
}
1068+
1069+
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
1070+
.amount_msats(1000)
1071+
.supported_quantity(Quantity::one())
1072+
.build().unwrap()
1073+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1074+
.amount_msats(2_000).unwrap()
1075+
.quantity_unchecked(2)
1076+
.build_unchecked()
1077+
.sign(payer_sign).unwrap();
1078+
1079+
let mut buffer = Vec::new();
1080+
invoice_request.write(&mut buffer).unwrap();
1081+
1082+
match InvoiceRequest::try_from(buffer) {
1083+
Ok(_) => panic!("expected error"),
1084+
Err(e) => {
1085+
assert_eq!(e, ParseError::InvalidSemantics(SemanticError::UnexpectedQuantity));
1086+
},
1087+
}
1088+
1089+
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
1090+
.amount_msats(1000)
1091+
.supported_quantity(Quantity::Bounded(ten))
1092+
.build().unwrap()
1093+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1094+
.amount_msats(10_000).unwrap()
1095+
.quantity(10).unwrap()
1096+
.build().unwrap()
1097+
.sign(payer_sign).unwrap();
1098+
1099+
let mut buffer = Vec::new();
1100+
invoice_request.write(&mut buffer).unwrap();
1101+
1102+
if let Err(e) = InvoiceRequest::try_from(buffer) {
1103+
panic!("error parsing invoice_request: {:?}", e);
1104+
}
1105+
1106+
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
1107+
.amount_msats(1000)
1108+
.supported_quantity(Quantity::Bounded(ten))
1109+
.build().unwrap()
1110+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1111+
.amount_msats(11_000).unwrap()
1112+
.quantity_unchecked(11)
1113+
.build_unchecked()
1114+
.sign(payer_sign).unwrap();
1115+
1116+
let mut buffer = Vec::new();
1117+
invoice_request.write(&mut buffer).unwrap();
1118+
1119+
match InvoiceRequest::try_from(buffer) {
1120+
Ok(_) => panic!("expected error"),
1121+
Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::InvalidQuantity)),
1122+
}
1123+
1124+
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
1125+
.amount_msats(1000)
1126+
.supported_quantity(Quantity::Unbounded)
1127+
.build().unwrap()
1128+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1129+
.amount_msats(2_000).unwrap()
1130+
.quantity(2).unwrap()
1131+
.build().unwrap()
1132+
.sign(payer_sign).unwrap();
1133+
1134+
let mut buffer = Vec::new();
1135+
invoice_request.write(&mut buffer).unwrap();
1136+
1137+
if let Err(e) = InvoiceRequest::try_from(buffer) {
1138+
panic!("error parsing invoice_request: {:?}", e);
1139+
}
1140+
1141+
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
1142+
.amount_msats(1000)
1143+
.supported_quantity(Quantity::Unbounded)
1144+
.build().unwrap()
1145+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1146+
.build_unchecked()
1147+
.sign(payer_sign).unwrap();
1148+
1149+
let mut buffer = Vec::new();
1150+
invoice_request.write(&mut buffer).unwrap();
1151+
1152+
match InvoiceRequest::try_from(buffer) {
1153+
Ok(_) => panic!("expected error"),
1154+
Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingQuantity)),
1155+
}
1156+
}
1157+
1158+
#[test]
1159+
fn fails_parsing_invoice_request_without_metadata() {
1160+
let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
1161+
.amount_msats(1000)
1162+
.build().unwrap();
1163+
let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1164+
.build().unwrap();
1165+
let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream();
1166+
tlv_stream.0.metadata = None;
1167+
1168+
let mut buffer = Vec::new();
1169+
tlv_stream.write(&mut buffer).unwrap();
1170+
1171+
match InvoiceRequest::try_from(buffer) {
1172+
Ok(_) => panic!("expected error"),
1173+
Err(e) => {
1174+
assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingPayerMetadata));
1175+
},
1176+
}
1177+
}
1178+
1179+
#[test]
1180+
fn fails_parsing_invoice_request_without_payer_id() {
1181+
let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
1182+
.amount_msats(1000)
1183+
.build().unwrap();
1184+
let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1185+
.build().unwrap();
1186+
let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream();
1187+
tlv_stream.2.payer_id = None;
1188+
1189+
let mut buffer = Vec::new();
1190+
tlv_stream.write(&mut buffer).unwrap();
1191+
1192+
match InvoiceRequest::try_from(buffer) {
1193+
Ok(_) => panic!("expected error"),
1194+
Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingPayerId)),
1195+
}
1196+
}
1197+
1198+
#[test]
1199+
fn fails_parsing_invoice_request_without_node_id() {
1200+
let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
1201+
.amount_msats(1000)
1202+
.build().unwrap();
1203+
let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1204+
.build().unwrap();
1205+
let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream();
1206+
tlv_stream.1.node_id = None;
1207+
1208+
let mut buffer = Vec::new();
1209+
tlv_stream.write(&mut buffer).unwrap();
1210+
1211+
match InvoiceRequest::try_from(buffer) {
1212+
Ok(_) => panic!("expected error"),
1213+
Err(e) => {
1214+
assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingSigningPubkey));
1215+
},
1216+
}
1217+
}
1218+
1219+
#[test]
1220+
fn parses_invoice_request_without_signature() {
1221+
let mut buffer = Vec::new();
1222+
OfferBuilder::new("foo".into(), recipient_pubkey())
1223+
.amount_msats(1000)
1224+
.build().unwrap()
1225+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1226+
.build().unwrap()
1227+
.invoice_request
1228+
.write(&mut buffer).unwrap();
1229+
1230+
if let Err(e) = InvoiceRequest::try_from(buffer) {
1231+
panic!("error parsing invoice_request: {:?}", e);
1232+
}
1233+
}
1234+
1235+
#[test]
1236+
fn fails_parsing_invoice_request_with_invalid_signature() {
1237+
let mut invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
1238+
.amount_msats(1000)
1239+
.build().unwrap()
1240+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
1241+
.build().unwrap()
1242+
.sign(payer_sign).unwrap();
1243+
let last_signature_byte = invoice_request.bytes.last_mut().unwrap();
1244+
*last_signature_byte = last_signature_byte.wrapping_add(1);
1245+
1246+
let mut buffer = Vec::new();
1247+
invoice_request.write(&mut buffer).unwrap();
1248+
1249+
match InvoiceRequest::try_from(buffer) {
1250+
Ok(_) => panic!("expected error"),
1251+
Err(e) => {
1252+
assert_eq!(e, ParseError::InvalidSignature(secp256k1::Error::InvalidSignature));
1253+
},
1254+
}
1255+
}
1256+
8971257
#[test]
8981258
fn fails_parsing_invoice_request_with_extra_tlv_records() {
8991259
let secp_ctx = Secp256k1::new();

0 commit comments

Comments
 (0)