Skip to content

Commit 8c2700b

Browse files
Static invoice parsing tests
1 parent 132264a commit 8c2700b

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
@@ -554,22 +554,72 @@ mod tests {
554554
use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
555555
use crate::ln::features::{Bolt12InvoiceFeatures, OfferFeatures};
556556
use crate::ln::inbound_payment::ExpandedKey;
557+
use crate::ln::msgs::DecodeError;
557558
use crate::offers::invoice::SIGNATURE_TAG;
558559
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};
562563
use crate::offers::static_invoice::{
563564
StaticInvoice, StaticInvoiceBuilder, DEFAULT_RELATIVE_EXPIRY,
564565
};
565566
use crate::offers::test_utils::*;
566567
use crate::sign::KeyMaterial;
567-
use crate::util::ser::Writeable;
568+
use crate::util::ser::{BigSize, Writeable};
568569
use bitcoin::blockdata::constants::ChainHash;
569570
use bitcoin::network::constants::Network;
570-
use bitcoin::secp256k1::Secp256k1;
571+
use bitcoin::secp256k1::{self, Secp256k1};
571572
use core::time::Duration;
572573

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+
573623
fn blinded_path() -> BlindedPath {
574624
BlindedPath {
575625
introduction_node: IntroductionNode::NodeId(pubkey(40)),
@@ -848,4 +898,233 @@ mod tests {
848898
panic!("expected error")
849899
}
850900
}
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+
}
8511130
}

0 commit comments

Comments
 (0)