//! Data structures and encoding for `invoice_request` messages.
//!
-//! An [`InvoiceRequest`] can be either built from a parsed [`Offer`] as an "offer to be paid" or
-//! built directly as an "offer for money" (e.g., refund, ATM withdrawal). In the former case, it is
+//! An [`InvoiceRequest`] can be built from a parsed [`Offer`] as an "offer to be paid". It is
//! typically constructed by a customer and sent to the merchant who had published the corresponding
-//! offer. In the latter case, an offer doesn't exist as a precursor to the request. Rather the
-//! merchant would typically construct the invoice request and present it to the customer.
+//! offer. The recipient of the request responds with an `Invoice`.
//!
-//! The recipient of the request responds with an `Invoice`.
+//! For an "offer for money" (e.g., refund, ATM withdrawal), where an offer doesn't exist as a
+//! precursor, see [`Refund`].
+//!
+//! [`Refund`]: crate::offers::refund::Refund
//!
//! ```ignore
//! extern crate bitcoin;
//! let pubkey = PublicKey::from(keys);
//! let mut buffer = Vec::new();
//!
-//! // "offer to be paid" flow
//! "lno1qcp4256ypq"
//! .parse::<Offer>()?
//! .request_invoice(vec![42; 64], pubkey)?
unsigned_tlv_stream.write(&mut bytes).unwrap();
let pubkey = self.invoice_request.payer_id;
- let signature = Some(merkle::sign_message(sign, SIGNATURE_TAG, &bytes, pubkey)?);
+ let signature = merkle::sign_message(sign, SIGNATURE_TAG, &bytes, pubkey)?;
// Append the signature TLV record to the bytes.
let signature_tlv_stream = SignatureTlvStreamRef {
- signature: signature.as_ref(),
+ signature: Some(&signature),
};
signature_tlv_stream.write(&mut bytes).unwrap();
pub struct InvoiceRequest {
pub(super) bytes: Vec<u8>,
contents: InvoiceRequestContents,
- signature: Option<Signature>,
+ signature: Signature,
}
/// The contents of an [`InvoiceRequest`], which may be shared with an `Invoice`.
self.contents.amount_msats
}
- /// Features for paying the invoice.
+ /// Features pertaining to requesting an invoice.
pub fn features(&self) -> &InvoiceRequestFeatures {
&self.contents.features
}
/// Signature of the invoice request using [`payer_id`].
///
/// [`payer_id`]: Self::payer_id
- pub fn signature(&self) -> Option<Signature> {
+ pub fn signature(&self) -> Signature {
self.signature
}
let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream) =
self.contents.as_tlv_stream();
let signature_tlv_stream = SignatureTlvStreamRef {
- signature: self.signature.as_ref(),
+ signature: Some(&self.signature),
};
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, signature_tlv_stream)
}
tlv_stream!(InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef, 80..160, {
(80, chain: ChainHash),
(82, amount: (u64, HighZeroBytesDroppedBigSize)),
- (84, features: InvoiceRequestFeatures),
+ (84, features: (InvoiceRequestFeatures, WithoutLength)),
(86, quantity: (u64, HighZeroBytesDroppedBigSize)),
(88, payer_id: PublicKey),
(89, payer_note: (String, WithoutLength)),
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
)?;
- if let Some(signature) = &signature {
- merkle::verify_signature(signature, SIGNATURE_TAG, &bytes, contents.payer_id)?;
- }
+ let signature = match signature {
+ None => return Err(ParseError::InvalidSemantics(SemanticError::MissingSignature)),
+ Some(signature) => signature,
+ };
+ merkle::verify_signature(&signature, SIGNATURE_TAG, &bytes, contents.payer_id)?;
Ok(InvoiceRequest { bytes, contents, signature })
}
#[cfg(test)]
mod tests {
- use super::InvoiceRequest;
+ use super::{InvoiceRequest, InvoiceRequestTlvStreamRef, SIGNATURE_TAG};
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::network::constants::Network;
use core::time::Duration;
use crate::ln::features::InvoiceRequestFeatures;
use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
- use crate::offers::merkle::SignError;
- use crate::offers::offer::{Amount, OfferBuilder, Quantity};
+ use crate::offers::merkle::{SignError, SignatureTlvStreamRef, self};
+ use crate::offers::offer::{Amount, OfferBuilder, OfferTlvStreamRef, Quantity};
use crate::offers::parse::{ParseError, SemanticError};
+ use crate::offers::payer::PayerTlvStreamRef;
use crate::util::ser::{BigSize, Writeable};
use crate::util::string::PrintableString;
#[test]
fn builds_invoice_request_with_defaults() {
- let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+ let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
- .build().unwrap();
- let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
- .build().unwrap().sign(payer_sign).unwrap();
+ .build().unwrap()
+ .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+ .build().unwrap()
+ .sign(payer_sign).unwrap();
- let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, signature_tlv_stream) =
- invoice_request.as_tlv_stream();
let mut buffer = Vec::new();
invoice_request.write(&mut buffer).unwrap();
assert_eq!(invoice_request.quantity(), None);
assert_eq!(invoice_request.payer_id(), payer_pubkey());
assert_eq!(invoice_request.payer_note(), None);
- assert!(invoice_request.signature().is_some());
-
- assert_eq!(payer_tlv_stream.metadata, Some(&vec![1; 32]));
- assert_eq!(offer_tlv_stream.chains, None);
- assert_eq!(offer_tlv_stream.metadata, None);
- assert_eq!(offer_tlv_stream.currency, None);
- assert_eq!(offer_tlv_stream.amount, Some(1000));
- assert_eq!(offer_tlv_stream.description, Some(&String::from("foo")));
- assert_eq!(offer_tlv_stream.features, None);
- assert_eq!(offer_tlv_stream.absolute_expiry, None);
- assert_eq!(offer_tlv_stream.paths, None);
- assert_eq!(offer_tlv_stream.issuer, None);
- assert_eq!(offer_tlv_stream.quantity_max, None);
- assert_eq!(offer_tlv_stream.node_id, Some(&recipient_pubkey()));
- assert_eq!(invoice_request_tlv_stream.chain, None);
- assert_eq!(invoice_request_tlv_stream.amount, None);
- assert_eq!(invoice_request_tlv_stream.features, None);
- assert_eq!(invoice_request_tlv_stream.quantity, None);
- assert_eq!(invoice_request_tlv_stream.payer_id, Some(&payer_pubkey()));
- assert_eq!(invoice_request_tlv_stream.payer_note, None);
- assert!(signature_tlv_stream.signature.is_some());
+ assert!(
+ merkle::verify_signature(
+ &invoice_request.signature, SIGNATURE_TAG, &invoice_request.bytes, payer_pubkey()
+ ).is_ok()
+ );
+
+ assert_eq!(
+ invoice_request.as_tlv_stream(),
+ (
+ PayerTlvStreamRef { metadata: Some(&vec![1; 32]) },
+ OfferTlvStreamRef {
+ chains: None,
+ metadata: None,
+ currency: None,
+ amount: Some(1000),
+ description: Some(&String::from("foo")),
+ features: None,
+ absolute_expiry: None,
+ paths: None,
+ issuer: None,
+ quantity_max: None,
+ node_id: Some(&recipient_pubkey()),
+ },
+ InvoiceRequestTlvStreamRef {
+ chain: None,
+ amount: None,
+ features: None,
+ quantity: None,
+ payer_id: Some(&payer_pubkey()),
+ payer_note: None,
+ },
+ SignatureTlvStreamRef { signature: Some(&invoice_request.signature()) },
+ ),
+ );
if let Err(e) = InvoiceRequest::try_from(buffer) {
panic!("error parsing invoice request: {:?}", e);
}
#[test]
- fn parses_invoice_request_without_signature() {
+ fn fails_parsing_invoice_request_without_signature() {
let mut buffer = Vec::new();
OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
.invoice_request
.write(&mut buffer).unwrap();
- if let Err(e) = InvoiceRequest::try_from(buffer) {
- panic!("error parsing invoice_request: {:?}", e);
+ match InvoiceRequest::try_from(buffer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingSignature)),
}
}