Skip to content

Commit 984c906

Browse files
committed
Invoice request parsing tests
Tests for checking invoice_request message semantics when parsing bytes as defined by BOLT 12.
1 parent d666eb6 commit 984c906

File tree

2 files changed

+367
-3
lines changed

2 files changed

+367
-3
lines changed

lightning/src/offers/invoice_request.rs

Lines changed: 358 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,31 @@ impl<'a> InvoiceRequestBuilder<'a> {
173173

174174
#[cfg(test)]
175175
impl<'a> InvoiceRequestBuilder<'a> {
176+
fn chain_unchecked(mut self, network: Network) -> Self {
177+
let chain = ChainHash::using_genesis_block(network);
178+
self.invoice_request.chain = Some(chain);
179+
self
180+
}
181+
182+
fn amount_msats_unchecked(mut self, amount_msats: u64) -> Self {
183+
self.invoice_request.amount_msats = Some(amount_msats);
184+
self
185+
}
186+
176187
fn features_unchecked(mut self, features: InvoiceRequestFeatures) -> Self {
177188
self.invoice_request.features = features;
178189
self
179190
}
191+
192+
fn quantity_unchecked(mut self, quantity: u64) -> Self {
193+
self.invoice_request.quantity = Some(quantity);
194+
self
195+
}
196+
197+
fn build_unchecked(self) -> UnsignedInvoiceRequest<'a> {
198+
let InvoiceRequestBuilder { offer, invoice_request } = self;
199+
UnsignedInvoiceRequest { offer, invoice_request }
200+
}
180201
}
181202

182203
/// A semantically valid [`InvoiceRequest`] that hasn't been signed.
@@ -463,7 +484,7 @@ mod tests {
463484
use crate::ln::features::InvoiceRequestFeatures;
464485
use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
465486
use crate::offers::merkle::SignError;
466-
use crate::offers::offer::{OfferBuilder, Quantity};
487+
use crate::offers::offer::{Amount, OfferBuilder, Quantity};
467488
use crate::offers::parse::{ParseError, SemanticError};
468489
use crate::util::ser::{BigSize, Writeable};
469490
use crate::util::string::PrintableString;
@@ -893,6 +914,342 @@ mod tests {
893914
}
894915
}
895916

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

0 commit comments

Comments
 (0)