From 5a54218e659f02f9aba8e46adfb10e860010f101 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Thu, 23 May 2024 10:53:53 -0400 Subject: [PATCH] WIP: Static invoice building tests --- lightning/src/offers/offer.rs | 7 +++ lightning/src/offers/static_invoice.rs | 80 ++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/lightning/src/offers/offer.rs b/lightning/src/offers/offer.rs index eb9385a14d4..70149232a68 100644 --- a/lightning/src/offers/offer.rs +++ b/lightning/src/offers/offer.rs @@ -664,6 +664,13 @@ impl Offer { pub fn expects_quantity(&self) -> bool { self.contents.expects_quantity() } + + #[cfg(test)] + pub(crate) fn verify( + &self, key: &ExpandedKey, secp_ctx: &Secp256k1 + ) -> Result<(OfferId, Option), ()> { + self.contents.verify(&self.bytes, key, secp_ctx) + } } macro_rules! request_invoice_derived_payer_id { ($self: ident, $builder: ty) => { diff --git a/lightning/src/offers/static_invoice.rs b/lightning/src/offers/static_invoice.rs index 9d0d3d83a8d..e02fbfb6425 100644 --- a/lightning/src/offers/static_invoice.rs +++ b/lightning/src/offers/static_invoice.rs @@ -509,3 +509,83 @@ impl TryFrom<(OfferTlvStream, InvoiceTlvStream)> for InvoiceContents { }) } } + +#[cfg(test)] +mod tests { + use super::{StaticInvoiceBuilder, DEFAULT_RELATIVE_EXPIRY}; + + use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode}; + use crate::ln::features::{Bolt12InvoiceFeatures, OfferFeatures}; + use crate::ln::inbound_payment::ExpandedKey; + use crate::offers::invoice::SIGNATURE_TAG; + use crate::offers::merkle; + use crate::offers::merkle::TaggedHash; + use crate::offers::offer::{OfferBuilder, Quantity}; + use crate::offers::test_utils::*; + use crate::sign::KeyMaterial; + use crate::util::ser::Writeable; + use bitcoin::blockdata::constants::ChainHash; + use bitcoin::network::constants::Network; + use bitcoin::secp256k1::Secp256k1; + + #[test] + fn builds_invoice_for_offer_with_defaults() { + let node_id = recipient_pubkey(); + let payment_paths = payment_paths(); + let now = now(); + let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32])); + let entropy = FixedEntropy {}; + let secp_ctx = Secp256k1::new(); + + let blinded_path = BlindedPath { + introduction_node: IntroductionNode::NodeId(pubkey(40)), + blinding_point: pubkey(41), + blinded_hops: vec![ + BlindedHop { blinded_node_id: pubkey(42), encrypted_payload: vec![0; 43] }, + BlindedHop { blinded_node_id: node_id, encrypted_payload: vec![0; 44] }, + ], + }; + + let offer = + OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx) + .path(blinded_path.clone()) + .build() + .unwrap(); + + let (_offer_id, keys_opt) = offer.verify(&expanded_key, &secp_ctx).unwrap(); + let invoice = StaticInvoiceBuilder::for_offer_using_keys( + &offer, + payment_paths.clone(), + now, + keys_opt.unwrap(), + ) + .unwrap() + .build_and_sign(&secp_ctx) + .unwrap(); + + let mut buffer = Vec::new(); + invoice.write(&mut buffer).unwrap(); + + assert_eq!(invoice.bytes, buffer.as_slice()); + assert!(invoice.metadata().is_some()); + assert_eq!(invoice.amount(), None); + assert_eq!(invoice.description(), None); + assert_eq!(invoice.offer_features(), &OfferFeatures::empty()); + assert_eq!(invoice.absolute_expiry(), None); + assert_eq!(invoice.message_paths(), &[blinded_path]); + assert_eq!(invoice.issuer(), None); + assert_eq!(invoice.supported_quantity(), Quantity::One); + assert_ne!(invoice.signing_pubkey(), recipient_pubkey()); + assert_eq!(invoice.chain(), ChainHash::using_genesis_block(Network::Bitcoin)); + assert_eq!(invoice.payment_paths(), payment_paths.as_slice()); + assert_eq!(invoice.created_at(), now); + assert_eq!(invoice.relative_expiry(), DEFAULT_RELATIVE_EXPIRY); + #[cfg(feature = "std")] + assert!(!invoice.is_expired()); + assert_eq!(invoice.fallbacks(), vec![]); + assert_eq!(invoice.invoice_features(), &Bolt12InvoiceFeatures::empty()); + + let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &invoice.bytes); + assert!(merkle::verify_signature(&invoice.signature, &message, keys_opt.unwrap().public_key()).is_ok()); + } +}