Skip to content

Commit ad8c56b

Browse files
Static invoice building tests
1 parent 3810066 commit ad8c56b

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

0 commit comments

Comments
 (0)