@@ -135,7 +135,7 @@ use crate::offers::merkle::{
135
135
};
136
136
use crate::offers::nonce::Nonce;
137
137
use crate::offers::offer::{
138
- Amount, ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, OfferTlvStream,
138
+ Amount, ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, OfferId, OfferTlvStream,
139
139
OfferTlvStreamRef, Quantity, EXPERIMENTAL_OFFER_TYPES, OFFER_TYPES,
140
140
};
141
141
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
@@ -686,6 +686,13 @@ macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $s
686
686
// Append the experimental bytes after the signature.
687
687
$self.bytes.extend_from_slice(&$self.experimental_bytes);
688
688
689
+ let offer_id = match &self.contents {
690
+ InvoiceContents::ForOffer { .. } => {
691
+ Some(OfferId::from_valid_bolt12_tlv_stream(&self.bytes))
692
+ },
693
+ InvoiceContents::ForRefund { .. } => None,
694
+ };
695
+
689
696
Ok(Bolt12Invoice {
690
697
#[cfg(not(c_bindings))]
691
698
bytes: $self.bytes,
@@ -700,6 +707,7 @@ macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $s
700
707
tagged_hash: $self.tagged_hash,
701
708
#[cfg(c_bindings)]
702
709
tagged_hash: $self.tagged_hash.clone(),
710
+ offer_id,
703
711
})
704
712
}
705
713
} }
@@ -734,6 +742,7 @@ pub struct Bolt12Invoice {
734
742
contents: InvoiceContents,
735
743
signature: Signature,
736
744
tagged_hash: TaggedHash,
745
+ offer_id: Option<OfferId>,
737
746
}
738
747
739
748
/// The contents of an [`Bolt12Invoice`] for responding to either an [`Offer`] or a [`Refund`].
@@ -967,6 +976,13 @@ impl Bolt12Invoice {
967
976
self.tagged_hash.as_digest().as_ref().clone()
968
977
}
969
978
979
+ /// Returns the [`OfferId`] if this invoice corresponds to an [`Offer`].
980
+ ///
981
+ /// [`Offer`]: crate::offers::offer::Offer
982
+ pub fn offer_id(&self) -> Option<OfferId> {
983
+ self.offer_id
984
+ }
985
+
970
986
/// Verifies that the invoice was for a request or refund created using the given key by
971
987
/// checking the payer metadata from the invoice request.
972
988
///
@@ -1622,7 +1638,11 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
1622
1638
let pubkey = contents.fields().signing_pubkey;
1623
1639
merkle::verify_signature(&signature, &tagged_hash, pubkey)?;
1624
1640
1625
- Ok(Bolt12Invoice { bytes, contents, signature, tagged_hash })
1641
+ let offer_id = match &contents {
1642
+ InvoiceContents::ForOffer { .. } => Some(OfferId::from_valid_bolt12_tlv_stream(&bytes)),
1643
+ InvoiceContents::ForRefund { .. } => None,
1644
+ };
1645
+ Ok(Bolt12Invoice { bytes, contents, signature, tagged_hash, offer_id })
1626
1646
}
1627
1647
}
1628
1648
@@ -3556,4 +3576,49 @@ mod tests {
3556
3576
),
3557
3577
}
3558
3578
}
3579
+
3580
+ #[test]
3581
+ fn invoice_offer_id_matches_offer_id() {
3582
+ let expanded_key = ExpandedKey::new([42; 32]);
3583
+ let entropy = FixedEntropy {};
3584
+ let nonce = Nonce::from_entropy_source(&entropy);
3585
+ let secp_ctx = Secp256k1::new();
3586
+ let payment_id = PaymentId([1; 32]);
3587
+
3588
+ let offer = OfferBuilder::new(recipient_pubkey()).amount_msats(1000).build().unwrap();
3589
+
3590
+ let offer_id = offer.id();
3591
+
3592
+ let invoice_request = offer
3593
+ .request_invoice(&expanded_key, nonce, &secp_ctx, payment_id)
3594
+ .unwrap()
3595
+ .build_and_sign()
3596
+ .unwrap();
3597
+
3598
+ let invoice = invoice_request
3599
+ .respond_with_no_std(payment_paths(), payment_hash(), now())
3600
+ .unwrap()
3601
+ .build()
3602
+ .unwrap()
3603
+ .sign(recipient_sign)
3604
+ .unwrap();
3605
+
3606
+ assert_eq!(invoice.offer_id(), Some(offer_id));
3607
+ }
3608
+
3609
+ #[test]
3610
+ fn refund_invoice_has_no_offer_id() {
3611
+ let refund =
3612
+ RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap().build().unwrap();
3613
+
3614
+ let invoice = refund
3615
+ .respond_with_no_std(payment_paths(), payment_hash(), recipient_pubkey(), now())
3616
+ .unwrap()
3617
+ .build()
3618
+ .unwrap()
3619
+ .sign(recipient_sign)
3620
+ .unwrap();
3621
+
3622
+ assert_eq!(invoice.offer_id(), None);
3623
+ }
3559
3624
}
0 commit comments