use crate::io;
use crate::blinded_path::BlindedPath;
use crate::ln::PaymentHash;
+use crate::ln::channelmanager::PaymentId;
use crate::ln::features::InvoiceRequestFeatures;
use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
use crate::ln::msgs::DecodeError;
}
pub(super) fn deriving_metadata<ES: Deref>(
- offer: &'a Offer, payer_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES
+ offer: &'a Offer, payer_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
+ payment_id: PaymentId,
) -> Self where ES::Target: EntropySource {
let nonce = Nonce::from_entropy_source(entropy_source);
- let derivation_material = MetadataMaterial::new(nonce, expanded_key, IV_BYTES);
+ let payment_id = Some(payment_id);
+ let derivation_material = MetadataMaterial::new(nonce, expanded_key, IV_BYTES, payment_id);
let metadata = Metadata::Derived(derivation_material);
Self {
offer,
impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T> {
pub(super) fn deriving_payer_id<ES: Deref>(
- offer: &'a Offer, expanded_key: &ExpandedKey, entropy_source: ES, secp_ctx: &'b Secp256k1<T>
+ offer: &'a Offer, expanded_key: &ExpandedKey, entropy_source: ES,
+ secp_ctx: &'b Secp256k1<T>, payment_id: PaymentId
) -> Self where ES::Target: EntropySource {
let nonce = Nonce::from_entropy_source(entropy_source);
- let derivation_material = MetadataMaterial::new(nonce, expanded_key, IV_BYTES);
+ let payment_id = Some(payment_id);
+ let derivation_material = MetadataMaterial::new(nonce, expanded_key, IV_BYTES, payment_id);
let metadata = Metadata::DerivedSigningPubkey(derivation_material);
Self {
offer,
let mut tlv_stream = self.invoice_request.as_tlv_stream();
debug_assert!(tlv_stream.2.payer_id.is_none());
tlv_stream.0.metadata = None;
- if !metadata.derives_keys() {
+ if !metadata.derives_payer_keys() {
tlv_stream.2.payer_id = self.payer_id.as_ref();
}
}
/// A semantically valid [`InvoiceRequest`] that hasn't been signed.
+///
+/// # Serialization
+///
+/// This is serialized as a TLV stream, which includes TLV records from the originating message. As
+/// such, it may include unknown, odd TLV records.
pub struct UnsignedInvoiceRequest {
bytes: Vec<u8>,
contents: InvoiceRequestContents,
Self { bytes, contents, tagged_hash }
}
- /// Signs the invoice request using the given function.
+ /// Returns the [`TaggedHash`] of the invoice to sign.
+ pub fn tagged_hash(&self) -> &TaggedHash {
+ &self.tagged_hash
+ }
+
+ /// Signs the [`TaggedHash`] of the invoice request using the given function.
+ ///
+ /// Note: The hash computation may have included unknown, odd TLV records.
///
/// This is not exported to bindings users as functions are not yet mapped.
pub fn sign<F, E>(mut self, sign: F) -> Result<InvoiceRequest, SignError<E>>
signature: Signature,
}
+/// An [`InvoiceRequest`] that has been verified by [`InvoiceRequest::verify`] and exposes different
+/// ways to respond depending on whether the signing keys were derived.
+#[derive(Clone, Debug)]
+pub struct VerifiedInvoiceRequest {
+ /// The verified request.
+ inner: InvoiceRequest,
+
+ /// Keys used for signing a [`Bolt12Invoice`] if they can be derived.
+ ///
+ /// If `Some`, must call [`respond_using_derived_keys`] when responding. Otherwise, call
+ /// [`respond_with`].
+ ///
+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
+ /// [`respond_using_derived_keys`]: Self::respond_using_derived_keys
+ /// [`respond_with`]: Self::respond_with
+ pub keys: Option<KeyPair>,
+}
+
/// The contents of an [`InvoiceRequest`], which may be shared with an [`Bolt12Invoice`].
///
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
payer_note: Option<String>,
}
-impl InvoiceRequest {
+macro_rules! invoice_request_accessors { ($self: ident, $contents: expr) => {
/// An unpredictable series of bytes, typically containing information about the derivation of
/// [`payer_id`].
///
/// [`payer_id`]: Self::payer_id
- pub fn metadata(&self) -> &[u8] {
- self.contents.metadata()
+ pub fn payer_metadata(&$self) -> &[u8] {
+ $contents.metadata()
}
/// A chain from [`Offer::chains`] that the offer is valid for.
- pub fn chain(&self) -> ChainHash {
- self.contents.chain()
+ pub fn chain(&$self) -> ChainHash {
+ $contents.chain()
}
/// The amount to pay in msats (i.e., the minimum lightning-payable unit for [`chain`]), which
/// must be greater than or equal to [`Offer::amount`], converted if necessary.
///
/// [`chain`]: Self::chain
- pub fn amount_msats(&self) -> Option<u64> {
- self.contents.inner.amount_msats
+ pub fn amount_msats(&$self) -> Option<u64> {
+ $contents.amount_msats()
}
/// Features pertaining to requesting an invoice.
- pub fn features(&self) -> &InvoiceRequestFeatures {
- &self.contents.inner.features
+ pub fn invoice_request_features(&$self) -> &InvoiceRequestFeatures {
+ &$contents.features()
}
/// The quantity of the offer's item conforming to [`Offer::is_valid_quantity`].
- pub fn quantity(&self) -> Option<u64> {
- self.contents.inner.quantity
+ pub fn quantity(&$self) -> Option<u64> {
+ $contents.quantity()
}
/// A possibly transient pubkey used to sign the invoice request.
- pub fn payer_id(&self) -> PublicKey {
- self.contents.payer_id
+ pub fn payer_id(&$self) -> PublicKey {
+ $contents.payer_id()
}
/// A payer-provided note which will be seen by the recipient and reflected back in the invoice
/// response.
- pub fn payer_note(&self) -> Option<PrintableString> {
- self.contents.inner.payer_note.as_ref()
- .map(|payer_note| PrintableString(payer_note.as_str()))
+ pub fn payer_note(&$self) -> Option<PrintableString> {
+ $contents.payer_note()
}
+} }
+
+impl UnsignedInvoiceRequest {
+ offer_accessors!(self, self.contents.inner.offer);
+ invoice_request_accessors!(self, self.contents);
+}
+
+impl InvoiceRequest {
+ offer_accessors!(self, self.contents.inner.offer);
+ invoice_request_accessors!(self, self.contents);
/// Signature of the invoice request using [`payer_id`].
///
///
/// Errors if the request contains unknown required features.
///
+ /// # Note
+ ///
+ /// If the originating [`Offer`] was created using [`OfferBuilder::deriving_signing_pubkey`],
+ /// then use [`InvoiceRequest::verify`] and [`VerifiedInvoiceRequest`] methods instead.
+ ///
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
///
/// [`Bolt12Invoice::created_at`]: crate::offers::invoice::Bolt12Invoice::created_at
+ /// [`OfferBuilder::deriving_signing_pubkey`]: crate::offers::offer::OfferBuilder::deriving_signing_pubkey
pub fn respond_with_no_std(
&self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
created_at: core::time::Duration
) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
- if self.features().requires_unknown_bits() {
+ if self.invoice_request_features().requires_unknown_bits() {
return Err(Bolt12SemanticError::UnknownRequiredFeatures);
}
InvoiceBuilder::for_offer(self, payment_paths, created_at, payment_hash)
}
+ /// Verifies that the request was for an offer created using the given key. Returns the verified
+ /// request which contains the derived keys needed to sign a [`Bolt12Invoice`] for the request
+ /// if they could be extracted from the metadata.
+ ///
+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
+ pub fn verify<T: secp256k1::Signing>(
+ self, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
+ ) -> Result<VerifiedInvoiceRequest, ()> {
+ let keys = self.contents.inner.offer.verify(&self.bytes, key, secp_ctx)?;
+ Ok(VerifiedInvoiceRequest {
+ inner: self,
+ keys,
+ })
+ }
+
+ #[cfg(test)]
+ fn as_tlv_stream(&self) -> FullInvoiceRequestTlvStreamRef {
+ let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream) =
+ self.contents.as_tlv_stream();
+ let signature_tlv_stream = SignatureTlvStreamRef {
+ signature: Some(&self.signature),
+ };
+ (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, signature_tlv_stream)
+ }
+}
+
+impl VerifiedInvoiceRequest {
+ offer_accessors!(self, self.inner.contents.inner.offer);
+ invoice_request_accessors!(self, self.inner.contents);
+
+ /// Creates an [`InvoiceBuilder`] for the request with the given required fields and using the
+ /// [`Duration`] since [`std::time::SystemTime::UNIX_EPOCH`] as the creation time.
+ ///
+ /// See [`InvoiceRequest::respond_with_no_std`] for further details.
+ ///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ ///
+ /// [`Duration`]: core::time::Duration
+ #[cfg(feature = "std")]
+ pub fn respond_with(
+ &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash
+ ) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
+ self.inner.respond_with(payment_paths, payment_hash)
+ }
+
+ /// Creates an [`InvoiceBuilder`] for the request with the given required fields.
+ ///
+ /// See [`InvoiceRequest::respond_with_no_std`] for further details.
+ ///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ pub fn respond_with_no_std(
+ &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
+ created_at: core::time::Duration
+ ) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
+ self.inner.respond_with_no_std(payment_paths, payment_hash, created_at)
+ }
+
/// Creates an [`InvoiceBuilder`] for the request using the given required fields and that uses
/// derived signing keys from the originating [`Offer`] to sign the [`Bolt12Invoice`]. Must use
/// the same [`ExpandedKey`] as the one used to create the offer.
///
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
#[cfg(feature = "std")]
- pub fn verify_and_respond_using_derived_keys<T: secp256k1::Signing>(
- &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
- expanded_key: &ExpandedKey, secp_ctx: &Secp256k1<T>
+ pub fn respond_using_derived_keys(
+ &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash
) -> Result<InvoiceBuilder<DerivedSigningPubkey>, Bolt12SemanticError> {
let created_at = std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
- self.verify_and_respond_using_derived_keys_no_std(
- payment_paths, payment_hash, created_at, expanded_key, secp_ctx
- )
+ self.respond_using_derived_keys_no_std(payment_paths, payment_hash, created_at)
}
/// Creates an [`InvoiceBuilder`] for the request using the given required fields and that uses
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
///
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
- pub fn verify_and_respond_using_derived_keys_no_std<T: secp256k1::Signing>(
+ pub fn respond_using_derived_keys_no_std(
&self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
- created_at: core::time::Duration, expanded_key: &ExpandedKey, secp_ctx: &Secp256k1<T>
+ created_at: core::time::Duration
) -> Result<InvoiceBuilder<DerivedSigningPubkey>, Bolt12SemanticError> {
- if self.features().requires_unknown_bits() {
+ if self.inner.invoice_request_features().requires_unknown_bits() {
return Err(Bolt12SemanticError::UnknownRequiredFeatures);
}
- let keys = match self.verify(expanded_key, secp_ctx) {
- Err(()) => return Err(Bolt12SemanticError::InvalidMetadata),
- Ok(None) => return Err(Bolt12SemanticError::InvalidMetadata),
- Ok(Some(keys)) => keys,
+ let keys = match self.keys {
+ None => return Err(Bolt12SemanticError::InvalidMetadata),
+ Some(keys) => keys,
};
- InvoiceBuilder::for_offer_using_keys(self, payment_paths, created_at, payment_hash, keys)
- }
-
- /// Verifies that the request was for an offer created using the given key. Returns the derived
- /// keys need to sign an [`Bolt12Invoice`] for the request if they could be extracted from the
- /// metadata.
- ///
- /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
- pub fn verify<T: secp256k1::Signing>(
- &self, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
- ) -> Result<Option<KeyPair>, ()> {
- self.contents.inner.offer.verify(&self.bytes, key, secp_ctx)
- }
-
- #[cfg(test)]
- fn as_tlv_stream(&self) -> FullInvoiceRequestTlvStreamRef {
- let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream) =
- self.contents.as_tlv_stream();
- let signature_tlv_stream = SignatureTlvStreamRef {
- signature: Some(&self.signature),
- };
- (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, signature_tlv_stream)
+ InvoiceBuilder::for_offer_using_keys(
+ &self.inner, payment_paths, created_at, payment_hash, keys
+ )
}
}
}
pub(super) fn derives_keys(&self) -> bool {
- self.inner.payer.0.derives_keys()
+ self.inner.payer.0.derives_payer_keys()
}
pub(super) fn chain(&self) -> ChainHash {
self.inner.chain()
}
+ pub(super) fn amount_msats(&self) -> Option<u64> {
+ self.inner.amount_msats
+ }
+
+ pub(super) fn features(&self) -> &InvoiceRequestFeatures {
+ &self.inner.features
+ }
+
+ pub(super) fn quantity(&self) -> Option<u64> {
+ self.inner.quantity
+ }
+
pub(super) fn payer_id(&self) -> PublicKey {
self.payer_id
}
+ pub(super) fn payer_note(&self) -> Option<PrintableString> {
+ self.inner.payer_note.as_ref()
+ .map(|payer_note| PrintableString(payer_note.as_str()))
+ }
+
pub(super) fn as_tlv_stream(&self) -> PartialInvoiceRequestTlvStreamRef {
let (payer, offer, mut invoice_request) = self.inner.as_tlv_stream();
invoice_request.payer_id = Some(&self.payer_id);
}
}
+impl Writeable for UnsignedInvoiceRequest {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ WithoutLength(&self.bytes).write(writer)
+ }
+}
+
impl Writeable for InvoiceRequest {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
WithoutLength(&self.bytes).write(writer)
InvoiceRequestTlvStreamRef<'a>,
);
+impl TryFrom<Vec<u8>> for UnsignedInvoiceRequest {
+ type Error = Bolt12ParseError;
+
+ fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
+ let invoice_request = ParsedMessage::<PartialInvoiceRequestTlvStream>::try_from(bytes)?;
+ let ParsedMessage { bytes, tlv_stream } = invoice_request;
+ let (
+ payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
+ ) = tlv_stream;
+ let contents = InvoiceRequestContents::try_from(
+ (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
+ )?;
+
+ let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
+
+ Ok(UnsignedInvoiceRequest { bytes, contents, tagged_hash })
+ }
+}
+
impl TryFrom<Vec<u8>> for InvoiceRequest {
type Error = Bolt12ParseError;
None => return Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSignature)),
Some(signature) => signature,
};
- merkle::verify_signature(&signature, SIGNATURE_TAG, &bytes, contents.payer_id)?;
+ let message = TaggedHash::new(SIGNATURE_TAG, &bytes);
+ merkle::verify_signature(&signature, message, contents.payer_id)?;
Ok(InvoiceRequest { bytes, contents, signature })
}
#[cfg(test)]
mod tests {
- use super::{InvoiceRequest, InvoiceRequestTlvStreamRef, SIGNATURE_TAG};
+ use super::{InvoiceRequest, InvoiceRequestTlvStreamRef, SIGNATURE_TAG, UnsignedInvoiceRequest};
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::network::constants::Network;
#[cfg(feature = "std")]
use core::time::Duration;
use crate::sign::KeyMaterial;
- use crate::ln::features::InvoiceRequestFeatures;
+ use crate::ln::channelmanager::PaymentId;
+ use crate::ln::features::{InvoiceRequestFeatures, OfferFeatures};
use crate::ln::inbound_payment::ExpandedKey;
use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
use crate::offers::invoice::{Bolt12Invoice, SIGNATURE_TAG as INVOICE_SIGNATURE_TAG};
#[test]
fn builds_invoice_request_with_defaults() {
- let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ let unsigned_invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
.build().unwrap()
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
- .build().unwrap()
- .sign(payer_sign).unwrap();
+ .build().unwrap();
+
+ let mut buffer = Vec::new();
+ unsigned_invoice_request.write(&mut buffer).unwrap();
+
+ assert_eq!(unsigned_invoice_request.bytes, buffer.as_slice());
+ assert_eq!(unsigned_invoice_request.payer_metadata(), &[1; 32]);
+ assert_eq!(unsigned_invoice_request.chains(), vec![ChainHash::using_genesis_block(Network::Bitcoin)]);
+ assert_eq!(unsigned_invoice_request.metadata(), None);
+ assert_eq!(unsigned_invoice_request.amount(), Some(&Amount::Bitcoin { amount_msats: 1000 }));
+ assert_eq!(unsigned_invoice_request.description(), PrintableString("foo"));
+ assert_eq!(unsigned_invoice_request.offer_features(), &OfferFeatures::empty());
+ assert_eq!(unsigned_invoice_request.absolute_expiry(), None);
+ assert_eq!(unsigned_invoice_request.paths(), &[]);
+ assert_eq!(unsigned_invoice_request.issuer(), None);
+ assert_eq!(unsigned_invoice_request.supported_quantity(), Quantity::One);
+ assert_eq!(unsigned_invoice_request.signing_pubkey(), recipient_pubkey());
+ assert_eq!(unsigned_invoice_request.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
+ assert_eq!(unsigned_invoice_request.amount_msats(), None);
+ assert_eq!(unsigned_invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty());
+ assert_eq!(unsigned_invoice_request.quantity(), None);
+ assert_eq!(unsigned_invoice_request.payer_id(), payer_pubkey());
+ assert_eq!(unsigned_invoice_request.payer_note(), None);
+
+ match UnsignedInvoiceRequest::try_from(buffer) {
+ Err(e) => panic!("error parsing unsigned invoice request: {:?}", e),
+ Ok(parsed) => {
+ assert_eq!(parsed.bytes, unsigned_invoice_request.bytes);
+ assert_eq!(parsed.tagged_hash, unsigned_invoice_request.tagged_hash);
+ },
+ }
+
+ let invoice_request = unsigned_invoice_request.sign(payer_sign).unwrap();
let mut buffer = Vec::new();
invoice_request.write(&mut buffer).unwrap();
assert_eq!(invoice_request.bytes, buffer.as_slice());
- assert_eq!(invoice_request.metadata(), &[1; 32]);
+ assert_eq!(invoice_request.payer_metadata(), &[1; 32]);
+ assert_eq!(invoice_request.chains(), vec![ChainHash::using_genesis_block(Network::Bitcoin)]);
+ assert_eq!(invoice_request.metadata(), None);
+ assert_eq!(invoice_request.amount(), Some(&Amount::Bitcoin { amount_msats: 1000 }));
+ assert_eq!(invoice_request.description(), PrintableString("foo"));
+ assert_eq!(invoice_request.offer_features(), &OfferFeatures::empty());
+ assert_eq!(invoice_request.absolute_expiry(), None);
+ assert_eq!(invoice_request.paths(), &[]);
+ assert_eq!(invoice_request.issuer(), None);
+ assert_eq!(invoice_request.supported_quantity(), Quantity::One);
+ assert_eq!(invoice_request.signing_pubkey(), recipient_pubkey());
assert_eq!(invoice_request.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
assert_eq!(invoice_request.amount_msats(), None);
- assert_eq!(invoice_request.features(), &InvoiceRequestFeatures::empty());
+ assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty());
assert_eq!(invoice_request.quantity(), None);
assert_eq!(invoice_request.payer_id(), payer_pubkey());
assert_eq!(invoice_request.payer_note(), None);
- assert!(
- merkle::verify_signature(
- &invoice_request.signature, SIGNATURE_TAG, &invoice_request.bytes, payer_pubkey()
- ).is_ok()
- );
+
+ let message = TaggedHash::new(SIGNATURE_TAG, &invoice_request.bytes);
+ assert!(merkle::verify_signature(&invoice_request.signature, message, payer_pubkey()).is_ok());
assert_eq!(
invoice_request.as_tlv_stream(),
let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
let entropy = FixedEntropy {};
let secp_ctx = Secp256k1::new();
+ let payment_id = PaymentId([1; 32]);
let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
.build().unwrap();
let invoice_request = offer
- .request_invoice_deriving_metadata(payer_id, &expanded_key, &entropy)
+ .request_invoice_deriving_metadata(payer_id, &expanded_key, &entropy, payment_id)
.unwrap()
.build().unwrap()
.sign(payer_sign).unwrap();
.unwrap()
.build().unwrap()
.sign(recipient_sign).unwrap();
- assert!(invoice.verify(&expanded_key, &secp_ctx));
+ match invoice.verify(&expanded_key, &secp_ctx) {
+ Ok(payment_id) => assert_eq!(payment_id, PaymentId([1; 32])),
+ Err(()) => panic!("verification failed"),
+ }
// Fails verification with altered fields
let (
signature_tlv_stream.write(&mut encoded_invoice).unwrap();
let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap();
- assert!(!invoice.verify(&expanded_key, &secp_ctx));
+ assert!(invoice.verify(&expanded_key, &secp_ctx).is_err());
// Fails verification with altered metadata
let (
signature_tlv_stream.write(&mut encoded_invoice).unwrap();
let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap();
- assert!(!invoice.verify(&expanded_key, &secp_ctx));
+ assert!(invoice.verify(&expanded_key, &secp_ctx).is_err());
}
#[test]
let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
let entropy = FixedEntropy {};
let secp_ctx = Secp256k1::new();
+ let payment_id = PaymentId([1; 32]);
let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
.build().unwrap();
let invoice_request = offer
- .request_invoice_deriving_payer_id(&expanded_key, &entropy, &secp_ctx)
+ .request_invoice_deriving_payer_id(&expanded_key, &entropy, &secp_ctx, payment_id)
.unwrap()
.build_and_sign()
.unwrap();
.unwrap()
.build().unwrap()
.sign(recipient_sign).unwrap();
- assert!(invoice.verify(&expanded_key, &secp_ctx));
+ match invoice.verify(&expanded_key, &secp_ctx) {
+ Ok(payment_id) => assert_eq!(payment_id, PaymentId([1; 32])),
+ Err(()) => panic!("verification failed"),
+ }
// Fails verification with altered fields
let (
signature_tlv_stream.write(&mut encoded_invoice).unwrap();
let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap();
- assert!(!invoice.verify(&expanded_key, &secp_ctx));
+ assert!(invoice.verify(&expanded_key, &secp_ctx).is_err());
// Fails verification with altered payer id
let (
signature_tlv_stream.write(&mut encoded_invoice).unwrap();
let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap();
- assert!(!invoice.verify(&expanded_key, &secp_ctx));
+ assert!(invoice.verify(&expanded_key, &secp_ctx).is_err());
}
#[test]
.build().unwrap()
.sign(payer_sign).unwrap();
let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
- assert_eq!(invoice_request.features(), &InvoiceRequestFeatures::unknown());
+ assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::unknown());
assert_eq!(tlv_stream.features, Some(&InvoiceRequestFeatures::unknown()));
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
.build().unwrap()
.sign(payer_sign).unwrap();
let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream();
- assert_eq!(invoice_request.features(), &InvoiceRequestFeatures::empty());
+ assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty());
assert_eq!(tlv_stream.features, None);
}