Skip to content

Commit 650b5df

Browse files
Static invoice building tests
1 parent adff96a commit 650b5df

File tree

1 file changed

+301
-0
lines changed

1 file changed

+301
-0
lines changed

lightning/src/offers/static_invoice.rs

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,3 +547,304 @@ impl TryFrom<(OfferTlvStream, InvoiceTlvStream)> for InvoiceContents {
547547
})
548548
}
549549
}
550+
551+
#[cfg(test)]
552+
mod tests {
553+
use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
554+
use crate::ln::features::{Bolt12InvoiceFeatures, OfferFeatures};
555+
use crate::ln::inbound_payment::ExpandedKey;
556+
use crate::offers::invoice::SIGNATURE_TAG;
557+
use crate::offers::merkle;
558+
use crate::offers::merkle::TaggedHash;
559+
use crate::offers::offer::{Offer, OfferBuilder, Quantity};
560+
use crate::offers::parse::Bolt12SemanticError;
561+
use crate::offers::static_invoice::{
562+
StaticInvoice, StaticInvoiceBuilder, DEFAULT_RELATIVE_EXPIRY,
563+
};
564+
use crate::offers::test_utils::*;
565+
use crate::sign::KeyMaterial;
566+
use crate::util::ser::Writeable;
567+
use bitcoin::blockdata::constants::ChainHash;
568+
use bitcoin::secp256k1::Secp256k1;
569+
use bitcoin::Network;
570+
use core::time::Duration;
571+
572+
fn blinded_path() -> BlindedPath {
573+
BlindedPath {
574+
introduction_node: IntroductionNode::NodeId(pubkey(40)),
575+
blinding_point: pubkey(41),
576+
blinded_hops: vec![
577+
BlindedHop { blinded_node_id: pubkey(42), encrypted_payload: vec![0; 43] },
578+
BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 44] },
579+
],
580+
}
581+
}
582+
583+
#[test]
584+
fn builds_invoice_for_offer_with_defaults() {
585+
let node_id = recipient_pubkey();
586+
let payment_paths = payment_paths();
587+
let now = now();
588+
let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
589+
let entropy = FixedEntropy {};
590+
let secp_ctx = Secp256k1::new();
591+
592+
let offer =
593+
OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
594+
.path(blinded_path())
595+
.build()
596+
.unwrap();
597+
598+
let (_offer_id, keys_opt) = offer.verify(&expanded_key, &secp_ctx).unwrap();
599+
let invoice = StaticInvoiceBuilder::for_offer_using_keys(
600+
&offer,
601+
payment_paths.clone(),
602+
vec![blinded_path()],
603+
now,
604+
keys_opt.unwrap(),
605+
)
606+
.unwrap()
607+
.build_and_sign(&secp_ctx)
608+
.unwrap();
609+
610+
let mut buffer = Vec::new();
611+
invoice.write(&mut buffer).unwrap();
612+
613+
assert_eq!(invoice.bytes, buffer.as_slice());
614+
assert!(invoice.metadata().is_some());
615+
assert_eq!(invoice.amount(), None);
616+
assert_eq!(invoice.description(), None);
617+
assert_eq!(invoice.offer_features(), &OfferFeatures::empty());
618+
assert_eq!(invoice.absolute_expiry(), None);
619+
assert_eq!(invoice.offer_message_paths(), &[blinded_path()]);
620+
assert_eq!(invoice.message_paths(), &[blinded_path()]);
621+
assert_eq!(invoice.issuer(), None);
622+
assert_eq!(invoice.supported_quantity(), Quantity::One);
623+
assert_ne!(invoice.signing_pubkey(), recipient_pubkey());
624+
assert_eq!(invoice.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
625+
assert_eq!(invoice.payment_paths(), payment_paths.as_slice());
626+
assert_eq!(invoice.created_at(), now);
627+
assert_eq!(invoice.relative_expiry(), DEFAULT_RELATIVE_EXPIRY);
628+
#[cfg(feature = "std")]
629+
assert!(!invoice.is_expired());
630+
assert!(invoice.fallbacks().is_empty());
631+
assert_eq!(invoice.invoice_features(), &Bolt12InvoiceFeatures::empty());
632+
633+
let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &invoice.bytes);
634+
assert!(merkle::verify_signature(
635+
&invoice.signature,
636+
&message,
637+
keys_opt.unwrap().public_key()
638+
)
639+
.is_ok());
640+
641+
if let Err(e) = StaticInvoice::try_from(buffer) {
642+
panic!("error parsing invoice: {:?}", e);
643+
}
644+
}
645+
646+
#[cfg(feature = "std")]
647+
#[test]
648+
fn builds_invoice_from_offer_with_expiration() {
649+
let node_id = recipient_pubkey();
650+
let now = now();
651+
let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
652+
let entropy = FixedEntropy {};
653+
let secp_ctx = Secp256k1::new();
654+
655+
let future_expiry = Duration::from_secs(u64::max_value());
656+
let past_expiry = Duration::from_secs(0);
657+
658+
let valid_offer =
659+
OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
660+
.path(blinded_path())
661+
.absolute_expiry(future_expiry)
662+
.build()
663+
.unwrap();
664+
665+
let (_offer_id, keys_opt) = valid_offer.verify(&expanded_key, &secp_ctx).unwrap();
666+
let invoice = StaticInvoiceBuilder::for_offer_using_keys(
667+
&valid_offer,
668+
payment_paths(),
669+
vec![blinded_path()],
670+
now,
671+
keys_opt.unwrap(),
672+
)
673+
.unwrap()
674+
.build_and_sign(&secp_ctx)
675+
.unwrap();
676+
assert!(!invoice.is_expired());
677+
assert_eq!(invoice.absolute_expiry(), Some(future_expiry));
678+
679+
let expired_offer =
680+
OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
681+
.path(blinded_path())
682+
.absolute_expiry(past_expiry)
683+
.build()
684+
.unwrap();
685+
let (_offer_id, keys_opt) = expired_offer.verify(&expanded_key, &secp_ctx).unwrap();
686+
if let Err(e) = StaticInvoiceBuilder::for_offer_using_keys(
687+
&expired_offer,
688+
payment_paths(),
689+
vec![blinded_path()],
690+
now,
691+
keys_opt.unwrap(),
692+
)
693+
.unwrap()
694+
.build_and_sign(&secp_ctx)
695+
{
696+
assert_eq!(e, Bolt12SemanticError::AlreadyExpired);
697+
} else {
698+
panic!("expected error")
699+
}
700+
}
701+
702+
#[test]
703+
fn fails_build_with_missing_paths() {
704+
let node_id = recipient_pubkey();
705+
let now = now();
706+
let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
707+
let entropy = FixedEntropy {};
708+
let secp_ctx = Secp256k1::new();
709+
710+
let valid_offer =
711+
OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
712+
.path(blinded_path())
713+
.build()
714+
.unwrap();
715+
let (_offer_id, keys_opt) = valid_offer.verify(&expanded_key, &secp_ctx).unwrap();
716+
717+
// Error if payment paths are missing.
718+
if let Err(e) = StaticInvoiceBuilder::for_offer_using_keys(
719+
&valid_offer,
720+
Vec::new(),
721+
vec![blinded_path()],
722+
now,
723+
keys_opt.unwrap(),
724+
) {
725+
assert_eq!(e, Bolt12SemanticError::MissingPaths);
726+
} else {
727+
panic!("expected error")
728+
}
729+
730+
// Error if message paths are missing.
731+
if let Err(e) = StaticInvoiceBuilder::for_offer_using_keys(
732+
&valid_offer,
733+
payment_paths(),
734+
Vec::new(),
735+
now,
736+
keys_opt.unwrap(),
737+
) {
738+
assert_eq!(e, Bolt12SemanticError::MissingPaths);
739+
} else {
740+
panic!("expected error")
741+
}
742+
743+
// Error if offer paths are missing.
744+
let mut offer_without_paths = valid_offer.clone();
745+
let mut offer_tlv_stream = offer_without_paths.as_tlv_stream();
746+
offer_tlv_stream.paths.take();
747+
let mut buffer = Vec::new();
748+
offer_tlv_stream.write(&mut buffer).unwrap();
749+
offer_without_paths = Offer::try_from(buffer).unwrap();
750+
if let Err(e) = StaticInvoiceBuilder::for_offer_using_keys(
751+
&offer_without_paths,
752+
payment_paths(),
753+
vec![blinded_path()],
754+
now,
755+
keys_opt.unwrap(),
756+
) {
757+
assert_eq!(e, Bolt12SemanticError::MissingPaths);
758+
} else {
759+
panic!("expected error")
760+
}
761+
}
762+
763+
#[test]
764+
fn fails_build_offer_signing_pubkey() {
765+
let node_id = recipient_pubkey();
766+
let now = now();
767+
let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
768+
let entropy = FixedEntropy {};
769+
let secp_ctx = Secp256k1::new();
770+
771+
let valid_offer =
772+
OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
773+
.path(blinded_path())
774+
.build()
775+
.unwrap();
776+
let (_offer_id, keys_opt) = valid_offer.verify(&expanded_key, &secp_ctx).unwrap();
777+
778+
// Error if offer signing pubkey is missing.
779+
let mut offer_missing_signing_pubkey = valid_offer.clone();
780+
let mut offer_tlv_stream = offer_missing_signing_pubkey.as_tlv_stream();
781+
offer_tlv_stream.node_id.take();
782+
let mut buffer = Vec::new();
783+
offer_tlv_stream.write(&mut buffer).unwrap();
784+
offer_missing_signing_pubkey = Offer::try_from(buffer).unwrap();
785+
786+
if let Err(e) = StaticInvoiceBuilder::for_offer_using_keys(
787+
&offer_missing_signing_pubkey,
788+
payment_paths(),
789+
vec![blinded_path()],
790+
now,
791+
keys_opt.unwrap(),
792+
) {
793+
assert_eq!(e, Bolt12SemanticError::MissingSigningPubkey);
794+
} else {
795+
panic!("expected error")
796+
}
797+
798+
// Error if the offer's signing pubkey doesn't match the invoice's.
799+
let mut offer_invalid_signing_pubkey = valid_offer.clone();
800+
let mut offer_tlv_stream = offer_invalid_signing_pubkey.as_tlv_stream();
801+
let invalid_node_id = payer_pubkey();
802+
offer_tlv_stream.node_id = Some(&invalid_node_id);
803+
let mut buffer = Vec::new();
804+
offer_tlv_stream.write(&mut buffer).unwrap();
805+
offer_invalid_signing_pubkey = Offer::try_from(buffer).unwrap();
806+
807+
if let Err(e) = StaticInvoiceBuilder::for_offer_using_keys(
808+
&offer_invalid_signing_pubkey,
809+
payment_paths(),
810+
vec![blinded_path()],
811+
now,
812+
keys_opt.unwrap(),
813+
) {
814+
assert_eq!(e, Bolt12SemanticError::InvalidSigningPubkey);
815+
} else {
816+
panic!("expected error")
817+
}
818+
}
819+
820+
#[test]
821+
fn fails_build_with_extra_offer_chains() {
822+
let node_id = recipient_pubkey();
823+
let now = now();
824+
let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
825+
let entropy = FixedEntropy {};
826+
let secp_ctx = Secp256k1::new();
827+
828+
let offer_with_extra_chain =
829+
OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
830+
.path(blinded_path())
831+
.chain(Network::Bitcoin)
832+
.chain(Network::Testnet)
833+
.build()
834+
.unwrap();
835+
let (_offer_id, keys_opt) =
836+
offer_with_extra_chain.verify(&expanded_key, &secp_ctx).unwrap();
837+
838+
if let Err(e) = StaticInvoiceBuilder::for_offer_using_keys(
839+
&offer_with_extra_chain,
840+
payment_paths(),
841+
vec![blinded_path()],
842+
now,
843+
keys_opt.unwrap(),
844+
) {
845+
assert_eq!(e, Bolt12SemanticError::UnexpectedChain);
846+
} else {
847+
panic!("expected error")
848+
}
849+
}
850+
}

0 commit comments

Comments
 (0)