+ /// Builds an unsigned [`Bolt12Invoice`] after checking for valid semantics. It can be signed by
+ /// [`UnsignedBolt12Invoice::sign`].
+ pub fn build($self: $self_type) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
+ #[cfg(feature = "std")] {
+ if $self.invoice.is_offer_or_refund_expired() {
+ return Err(Bolt12SemanticError::AlreadyExpired);
+ }
+ }
+
+ #[cfg(not(feature = "std"))] {
+ if $self.invoice.is_offer_or_refund_expired_no_std($self.invoice.created_at()) {
+ return Err(Bolt12SemanticError::AlreadyExpired);
+ }
+ }
+
+ let Self { invreq_bytes, invoice, .. } = $self;
+ #[cfg(not(c_bindings))] {
+ Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice))
+ }
+ #[cfg(c_bindings)] {
+ Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice.clone()))
+ }
+ }
+} }
+
+macro_rules! invoice_derived_signing_pubkey_builder_methods { ($self: ident, $self_type: ty) => {
+ #[cfg_attr(c_bindings, allow(dead_code))]
+ pub(super) fn for_offer_using_keys(
+ invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
+ created_at: Duration, payment_hash: PaymentHash, keys: KeyPair
+ ) -> Result<Self, Bolt12SemanticError> {
+ let amount_msats = Self::amount_msats(invoice_request)?;
+ let signing_pubkey = invoice_request.contents.inner.offer.signing_pubkey();
+ let contents = InvoiceContents::ForOffer {
+ invoice_request: invoice_request.contents.clone(),
+ fields: Self::fields(
+ payment_paths, created_at, payment_hash, amount_msats, signing_pubkey
+ ),
+ };
+
+ Self::new(&invoice_request.bytes, contents, DerivedSigningPubkey(keys))
+ }
+
+ #[cfg_attr(c_bindings, allow(dead_code))]
+ pub(super) fn for_refund_using_keys(
+ refund: &'a Refund, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, created_at: Duration,
+ payment_hash: PaymentHash, keys: KeyPair,
+ ) -> Result<Self, Bolt12SemanticError> {
+ let amount_msats = refund.amount_msats();
+ let signing_pubkey = keys.public_key();
+ let contents = InvoiceContents::ForRefund {
+ refund: refund.contents.clone(),
+ fields: Self::fields(
+ payment_paths, created_at, payment_hash, amount_msats, signing_pubkey
+ ),
+ };
+
+ Self::new(&refund.bytes, contents, DerivedSigningPubkey(keys))
+ }
+
+ /// Builds a signed [`Bolt12Invoice`] after checking for valid semantics.
+ pub fn build_and_sign<T: secp256k1::Signing>(
+ $self: $self_type, secp_ctx: &Secp256k1<T>
+ ) -> Result<Bolt12Invoice, Bolt12SemanticError> {
+ #[cfg(feature = "std")] {
+ if $self.invoice.is_offer_or_refund_expired() {
+ return Err(Bolt12SemanticError::AlreadyExpired);
+ }
+ }
+
+ #[cfg(not(feature = "std"))] {
+ if $self.invoice.is_offer_or_refund_expired_no_std($self.invoice.created_at()) {
+ return Err(Bolt12SemanticError::AlreadyExpired);
+ }
+ }
+
+ let Self {
+ invreq_bytes, invoice, signing_pubkey_strategy: DerivedSigningPubkey(keys)
+ } = $self;
+ #[cfg(not(c_bindings))]
+ let unsigned_invoice = UnsignedBolt12Invoice::new(invreq_bytes, invoice);
+ #[cfg(c_bindings)]
+ let mut unsigned_invoice = UnsignedBolt12Invoice::new(invreq_bytes, invoice.clone());
+
+ let invoice = unsigned_invoice
+ .sign(|message: &UnsignedBolt12Invoice|
+ Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+ )
+ .unwrap();
+ Ok(invoice)
+ }
+} }
+
+macro_rules! invoice_builder_methods { (
+ $self: ident, $self_type: ty, $return_type: ty, $return_value: expr, $type_param: ty $(, $self_mut: tt)?
+) => {
+ pub(crate) fn amount_msats(
+ invoice_request: &InvoiceRequest
+ ) -> Result<u64, Bolt12SemanticError> {
+ match invoice_request.amount_msats() {
+ Some(amount_msats) => Ok(amount_msats),
+ None => match invoice_request.contents.inner.offer.amount() {
+ Some(Amount::Bitcoin { amount_msats }) => {
+ amount_msats.checked_mul(invoice_request.quantity().unwrap_or(1))
+ .ok_or(Bolt12SemanticError::InvalidAmount)
+ },
+ Some(Amount::Currency { .. }) => Err(Bolt12SemanticError::UnsupportedCurrency),
+ None => Err(Bolt12SemanticError::MissingAmount),
+ },
+ }
+ }
+
+ #[cfg_attr(c_bindings, allow(dead_code))]
+ fn fields(
+ payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, created_at: Duration,
+ payment_hash: PaymentHash, amount_msats: u64, signing_pubkey: PublicKey
+ ) -> InvoiceFields {
+ InvoiceFields {
+ payment_paths, created_at, relative_expiry: None, payment_hash, amount_msats,
+ fallbacks: None, features: Bolt12InvoiceFeatures::empty(), signing_pubkey,
+ }
+ }
+
+ #[cfg_attr(c_bindings, allow(dead_code))]
+ fn new(
+ invreq_bytes: &'a Vec<u8>, contents: InvoiceContents, signing_pubkey_strategy: $type_param
+ ) -> Result<Self, Bolt12SemanticError> {