From af318b6efb1398099fc9c42f630b74abcc0b4fba Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Fri, 23 Feb 2024 13:34:24 -0600 Subject: [PATCH] Add c_bindings version of InvoiceRequestBuilder Use the macros introduced in the previous commit to define two builders for each type parameterization of InvoiceRequestBuilder - InvoiceRequestWithExplicitPayerIdBuilder - InvoiceRequestWithDerivedPayerIdBuilder The difference between these and InvoiceRequestBuilder is that these have methods that take `self` by mutable reference instead of by value and don't return anything instead returning the modified builder. This is required because bindings don't support move semantics nor impl blocks specific to a certain type parameterization. Because of this, the builder's contents must be cloned when building an InvoiceRequest. Keeps InvoiceRequestBuilder defined so that it can be used internally in ChannelManager::pay_for_offer even when compiled for c_bindings. --- lightning/src/ln/channelmanager.rs | 7 +- lightning/src/offers/invoice_request.rs | 161 ++++++++++++++++++++---- lightning/src/offers/offer.rs | 35 ++++-- 3 files changed, 170 insertions(+), 33 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index c9fc44994..87428ca36 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -59,6 +59,7 @@ use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, PaymentA use crate::ln::wire::Encode; use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, InvoiceBuilder}; use crate::offers::invoice_error::InvoiceError; +use crate::offers::invoice_request::{DerivedPayerId, InvoiceRequestBuilder}; use crate::offers::merkle::SignError; use crate::offers::offer::{Offer, OfferBuilder}; use crate::offers::parse::Bolt12SemanticError; @@ -7735,9 +7736,11 @@ where let entropy = &*self.entropy_source; let secp_ctx = &self.secp_ctx; - let builder = offer + let builder: InvoiceRequestBuilder = offer .request_invoice_deriving_payer_id(expanded_key, entropy, secp_ctx, payment_id)? - .chain_hash(self.chain_hash)?; + .into(); + let builder = builder.chain_hash(self.chain_hash)?; + let builder = match quantity { None => builder, Some(quantity) => builder.quantity(quantity)?, diff --git a/lightning/src/offers/invoice_request.rs b/lightning/src/offers/invoice_request.rs index 02fcd578f..792214d52 100644 --- a/lightning/src/offers/invoice_request.rs +++ b/lightning/src/offers/invoice_request.rs @@ -36,9 +36,12 @@ //! let pubkey = PublicKey::from(keys); //! let mut buffer = Vec::new(); //! +//! # use lightning::offers::invoice_request::{ExplicitPayerId, InvoiceRequestBuilder}; +//! # >::from( //! "lno1qcp4256ypq" //! .parse::()? //! .request_invoice(vec![42; 64], pubkey)? +//! # ) //! .chain(Network::Testnet)? //! .amount_msats(1000)? //! .quantity(5)? @@ -99,6 +102,34 @@ pub struct InvoiceRequestBuilder<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signi secp_ctx: Option<&'b Secp256k1>, } +/// Builds an [`InvoiceRequest`] from an [`Offer`] for the "offer to be paid" flow. +/// +/// See [module-level documentation] for usage. +/// +/// [module-level documentation]: self +#[cfg(c_bindings)] +pub struct InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b> { + offer: &'a Offer, + invoice_request: InvoiceRequestContentsWithoutPayerId, + payer_id: Option, + payer_id_strategy: core::marker::PhantomData, + secp_ctx: Option<&'b Secp256k1>, +} + +/// Builds an [`InvoiceRequest`] from an [`Offer`] for the "offer to be paid" flow. +/// +/// See [module-level documentation] for usage. +/// +/// [module-level documentation]: self +#[cfg(c_bindings)] +pub struct InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b> { + offer: &'a Offer, + invoice_request: InvoiceRequestContentsWithoutPayerId, + payer_id: Option, + payer_id_strategy: core::marker::PhantomData, + secp_ctx: Option<&'b Secp256k1>, +} + /// Indicates how [`InvoiceRequest::payer_id`] will be set. /// /// This is not exported to bindings users as builder patterns don't map outside of move semantics. @@ -118,6 +149,7 @@ impl PayerIdStrategy for ExplicitPayerId {} impl PayerIdStrategy for DerivedPayerId {} macro_rules! invoice_request_explicit_payer_id_builder_methods { ($self: ident, $self_type: ty) => { + #[cfg_attr(c_bindings, allow(dead_code))] pub(super) fn new(offer: &'a Offer, metadata: Vec, payer_id: PublicKey) -> Self { Self { offer, @@ -128,6 +160,7 @@ macro_rules! invoice_request_explicit_payer_id_builder_methods { ($self: ident, } } + #[cfg_attr(c_bindings, allow(dead_code))] pub(super) fn deriving_metadata( offer: &'a Offer, payer_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES, payment_id: PaymentId, @@ -154,10 +187,13 @@ macro_rules! invoice_request_explicit_payer_id_builder_methods { ($self: ident, } } } -macro_rules! invoice_request_derived_payer_id_builder_methods { ($self: ident, $self_type: ty) => { +macro_rules! invoice_request_derived_payer_id_builder_methods { ( + $self: ident, $self_type: ty, $secp_context: ty +) => { + #[cfg_attr(c_bindings, allow(dead_code))] pub(super) fn deriving_payer_id( offer: &'a Offer, expanded_key: &ExpandedKey, entropy_source: ES, - secp_ctx: &'b Secp256k1, payment_id: PaymentId + secp_ctx: &'b Secp256k1<$secp_context>, payment_id: PaymentId ) -> Self where ES::Target: EntropySource { let nonce = Nonce::from_entropy_source(entropy_source); let payment_id = Some(payment_id); @@ -175,6 +211,8 @@ macro_rules! invoice_request_derived_payer_id_builder_methods { ($self: ident, $ /// Builds a signed [`InvoiceRequest`] after checking for valid semantics. pub fn build_and_sign($self: $self_type) -> Result { let (unsigned_invoice_request, keys, secp_ctx) = $self.build_with_checks()?; + #[cfg(c_bindings)] + let mut unsigned_invoice_request = unsigned_invoice_request; debug_assert!(keys.is_some()); let secp_ctx = secp_ctx.unwrap(); @@ -189,8 +227,9 @@ macro_rules! invoice_request_derived_payer_id_builder_methods { ($self: ident, $ } } macro_rules! invoice_request_builder_methods { ( - $self: ident, $self_type: ty, $return_type: ty, $return_value: expr + $self: ident, $self_type: ty, $return_type: ty, $return_value: expr, $secp_context: ty $(, $self_mut: tt)? ) => { + #[cfg_attr(c_bindings, allow(dead_code))] fn create_contents(offer: &Offer, metadata: Metadata) -> InvoiceRequestContentsWithoutPayerId { let offer = offer.contents.clone(); InvoiceRequestContentsWithoutPayerId { @@ -213,7 +252,7 @@ macro_rules! invoice_request_builder_methods { ( /// offer. /// /// Successive calls to this method will override the previous setting. - pub(crate) fn chain_hash(mut $self: $self_type, chain: ChainHash) -> Result<$return_type, Bolt12SemanticError> { + pub(crate) fn chain_hash($($self_mut)* $self: $self_type, chain: ChainHash) -> Result<$return_type, Bolt12SemanticError> { if !$self.offer.supports_chain(chain) { return Err(Bolt12SemanticError::UnsupportedChain); } @@ -228,7 +267,7 @@ macro_rules! invoice_request_builder_methods { ( /// Successive calls to this method will override the previous setting. /// /// [`quantity`]: Self::quantity - pub fn amount_msats(mut $self: $self_type, amount_msats: u64) -> Result<$return_type, Bolt12SemanticError> { + pub fn amount_msats($($self_mut)* $self: $self_type, amount_msats: u64) -> Result<$return_type, Bolt12SemanticError> { $self.invoice_request.offer.check_amount_msats_for_quantity( Some(amount_msats), $self.invoice_request.quantity )?; @@ -240,7 +279,7 @@ macro_rules! invoice_request_builder_methods { ( /// does not conform to [`Offer::is_valid_quantity`]. /// /// Successive calls to this method will override the previous setting. - pub fn quantity(mut $self: $self_type, quantity: u64) -> Result<$return_type, Bolt12SemanticError> { + pub fn quantity($($self_mut)* $self: $self_type, quantity: u64) -> Result<$return_type, Bolt12SemanticError> { $self.invoice_request.offer.check_quantity(Some(quantity))?; $self.invoice_request.quantity = Some(quantity); Ok($return_value) @@ -249,13 +288,13 @@ macro_rules! invoice_request_builder_methods { ( /// Sets the [`InvoiceRequest::payer_note`]. /// /// Successive calls to this method will override the previous setting. - pub fn payer_note(mut $self: $self_type, payer_note: String) -> $return_type { + pub fn payer_note($($self_mut)* $self: $self_type, payer_note: String) -> $return_type { $self.invoice_request.payer_note = Some(payer_note); $return_value } - fn build_with_checks(mut $self: $self_type) -> Result< - (UnsignedInvoiceRequest, Option, Option<&'b Secp256k1>), + fn build_with_checks($($self_mut)* $self: $self_type) -> Result< + (UnsignedInvoiceRequest, Option, Option<&'b Secp256k1<$secp_context>>), Bolt12SemanticError > { #[cfg(feature = "std")] { @@ -285,8 +324,8 @@ macro_rules! invoice_request_builder_methods { ( Ok($self.build_without_checks()) } - fn build_without_checks(mut $self: $self_type) -> - (UnsignedInvoiceRequest, Option, Option<&'b Secp256k1>) + fn build_without_checks($($self_mut)* $self: $self_type) -> + (UnsignedInvoiceRequest, Option, Option<&'b Secp256k1<$secp_context>>) { // Create the metadata for stateless verification of a Bolt12Invoice. let mut keys = None; @@ -317,7 +356,10 @@ macro_rules! invoice_request_builder_methods { ( let payer_id = $self.payer_id.unwrap(); let invoice_request = InvoiceRequestContents { + #[cfg(not(c_bindings))] inner: $self.invoice_request, + #[cfg(c_bindings)] + inner: $self.invoice_request.clone(), payer_id, }; let unsigned_invoice_request = UnsignedInvoiceRequest::new($self.offer, invoice_request); @@ -328,29 +370,34 @@ macro_rules! invoice_request_builder_methods { ( #[cfg(test)] macro_rules! invoice_request_builder_test_methods { ( - $self: ident, $self_type: ty, $return_type: ty, $return_value: expr + $self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)? ) => { - fn chain_unchecked(mut $self: $self_type, network: Network) -> $return_type { + #[cfg_attr(c_bindings, allow(dead_code))] + fn chain_unchecked($($self_mut)* $self: $self_type, network: Network) -> $return_type { let chain = ChainHash::using_genesis_block(network); $self.invoice_request.chain = Some(chain); $return_value } - fn amount_msats_unchecked(mut $self: $self_type, amount_msats: u64) -> $return_type { + #[cfg_attr(c_bindings, allow(dead_code))] + fn amount_msats_unchecked($($self_mut)* $self: $self_type, amount_msats: u64) -> $return_type { $self.invoice_request.amount_msats = Some(amount_msats); $return_value } - fn features_unchecked(mut $self: $self_type, features: InvoiceRequestFeatures) -> $return_type { + #[cfg_attr(c_bindings, allow(dead_code))] + fn features_unchecked($($self_mut)* $self: $self_type, features: InvoiceRequestFeatures) -> $return_type { $self.invoice_request.features = features; $return_value } - fn quantity_unchecked(mut $self: $self_type, quantity: u64) -> $return_type { + #[cfg_attr(c_bindings, allow(dead_code))] + fn quantity_unchecked($($self_mut)* $self: $self_type, quantity: u64) -> $return_type { $self.invoice_request.quantity = Some(quantity); $return_value } + #[cfg_attr(c_bindings, allow(dead_code))] pub(super) fn build_unchecked($self: $self_type) -> UnsignedInvoiceRequest { $self.build_without_checks().0 } @@ -361,14 +408,68 @@ impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, ExplicitPayerI } impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T> { - invoice_request_derived_payer_id_builder_methods!(self, Self); + invoice_request_derived_payer_id_builder_methods!(self, Self, T); } impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, P, T> { - invoice_request_builder_methods!(self, Self, Self, self); + invoice_request_builder_methods!(self, Self, Self, self, T, mut); #[cfg(test)] - invoice_request_builder_test_methods!(self, Self, Self, self); + invoice_request_builder_test_methods!(self, Self, Self, self, mut); +} + +#[cfg(all(c_bindings, not(test)))] +impl<'a, 'b> InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b> { + invoice_request_explicit_payer_id_builder_methods!(self, &mut Self); + invoice_request_builder_methods!(self, &mut Self, (), (), secp256k1::All); +} + +#[cfg(all(c_bindings, test))] +impl<'a, 'b> InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b> { + invoice_request_explicit_payer_id_builder_methods!(self, &mut Self); + invoice_request_builder_methods!(self, &mut Self, &mut Self, self, secp256k1::All); + invoice_request_builder_test_methods!(self, &mut Self, &mut Self, self); +} + +#[cfg(all(c_bindings, not(test)))] +impl<'a, 'b> InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b> { + invoice_request_derived_payer_id_builder_methods!(self, &mut Self, secp256k1::All); + invoice_request_builder_methods!(self, &mut Self, (), (), secp256k1::All); +} + +#[cfg(all(c_bindings, test))] +impl<'a, 'b> InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b> { + invoice_request_derived_payer_id_builder_methods!(self, &mut Self, secp256k1::All); + invoice_request_builder_methods!(self, &mut Self, &mut Self, self, secp256k1::All); + invoice_request_builder_test_methods!(self, &mut Self, &mut Self, self); +} + +#[cfg(c_bindings)] +impl<'a, 'b> From> +for InvoiceRequestBuilder<'a, 'b, ExplicitPayerId, secp256k1::All> { + fn from(builder: InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b>) -> Self { + let InvoiceRequestWithExplicitPayerIdBuilder { + offer, invoice_request, payer_id, payer_id_strategy, secp_ctx, + } = builder; + + Self { + offer, invoice_request, payer_id, payer_id_strategy, secp_ctx, + } + } +} + +#[cfg(c_bindings)] +impl<'a, 'b> From> +for InvoiceRequestBuilder<'a, 'b, DerivedPayerId, secp256k1::All> { + fn from(builder: InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b>) -> Self { + let InvoiceRequestWithDerivedPayerIdBuilder { + offer, invoice_request, payer_id, payer_id_strategy, secp_ctx, + } = builder; + + Self { + offer, invoice_request, payer_id, payer_id_strategy, secp_ctx, + } + } } /// A semantically valid [`InvoiceRequest`] that hasn't been signed. @@ -406,13 +507,15 @@ impl UnsignedInvoiceRequest { } } -macro_rules! unsigned_invoice_request_sign_method { ($self: ident, $self_type: ty) => { +macro_rules! unsigned_invoice_request_sign_method { ( + $self: ident, $self_type: ty $(, $self_mut: tt)? +) => { /// 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(mut $self: $self_type, sign: F) -> Result> + pub fn sign($($self_mut)* $self: $self_type, sign: F) -> Result> where F: FnOnce(&Self) -> Result { @@ -426,15 +529,27 @@ macro_rules! unsigned_invoice_request_sign_method { ($self: ident, $self_type: t signature_tlv_stream.write(&mut $self.bytes).unwrap(); Ok(InvoiceRequest { + #[cfg(not(c_bindings))] bytes: $self.bytes, + #[cfg(c_bindings)] + bytes: $self.bytes.clone(), + #[cfg(not(c_bindings))] contents: $self.contents, + #[cfg(c_bindings)] + contents: $self.contents.clone(), signature, }) } } } +#[cfg(not(c_bindings))] +impl UnsignedInvoiceRequest { + unsigned_invoice_request_sign_method!(self, Self, mut); +} + +#[cfg(c_bindings)] impl UnsignedInvoiceRequest { - unsigned_invoice_request_sign_method!(self, Self); + unsigned_invoice_request_sign_method!(self, &mut Self); } impl AsRef for UnsignedInvoiceRequest { @@ -984,6 +1099,8 @@ mod tests { .build().unwrap() .request_invoice(vec![1; 32], payer_pubkey()).unwrap() .build().unwrap(); + #[cfg(c_bindings)] + let mut unsigned_invoice_request = unsigned_invoice_request; let mut buffer = Vec::new(); unsigned_invoice_request.write(&mut buffer).unwrap(); diff --git a/lightning/src/offers/offer.rs b/lightning/src/offers/offer.rs index be1527276..8f6cac75d 100644 --- a/lightning/src/offers/offer.rs +++ b/lightning/src/offers/offer.rs @@ -91,13 +91,21 @@ use crate::ln::channelmanager::PaymentId; use crate::ln::features::OfferFeatures; use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce}; use crate::ln::msgs::MAX_VALUE_MSAT; -use crate::offers::invoice_request::{DerivedPayerId, ExplicitPayerId, InvoiceRequestBuilder}; use crate::offers::merkle::TlvStream; use crate::offers::parse::{Bech32Encode, Bolt12ParseError, Bolt12SemanticError, ParsedMessage}; use crate::offers::signer::{Metadata, MetadataMaterial, self}; use crate::util::ser::{HighZeroBytesDroppedBigSize, WithoutLength, Writeable, Writer}; use crate::util::string::PrintableString; +#[cfg(not(c_bindings))] +use { + crate::offers::invoice_request::{DerivedPayerId, ExplicitPayerId, InvoiceRequestBuilder}, +}; +#[cfg(c_bindings)] +use { + crate::offers::invoice_request::{InvoiceRequestWithDerivedPayerIdBuilder, InvoiceRequestWithExplicitPayerIdBuilder}, +}; + use crate::prelude::*; #[cfg(feature = "std")] @@ -598,14 +606,20 @@ macro_rules! request_invoice_derived_payer_id { ($self: ident, $builder: ty) => /// /// Useful to protect the sender's privacy. /// - /// This is not exported to bindings users as builder patterns don't map outside of move semantics. - /// /// [`InvoiceRequest::payer_id`]: crate::offers::invoice_request::InvoiceRequest::payer_id /// [`InvoiceRequest::payer_metadata`]: crate::offers::invoice_request::InvoiceRequest::payer_metadata /// [`Bolt12Invoice::verify`]: crate::offers::invoice::Bolt12Invoice::verify /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey - pub fn request_invoice_deriving_payer_id<'a, 'b, ES: Deref, T: secp256k1::Signing>( - &'a $self, expanded_key: &ExpandedKey, entropy_source: ES, secp_ctx: &'b Secp256k1, + pub fn request_invoice_deriving_payer_id< + 'a, 'b, ES: Deref, + #[cfg(not(c_bindings))] + T: secp256k1::Signing + >( + &'a $self, expanded_key: &ExpandedKey, entropy_source: ES, + #[cfg(not(c_bindings))] + secp_ctx: &'b Secp256k1, + #[cfg(c_bindings)] + secp_ctx: &'b Secp256k1, payment_id: PaymentId ) -> Result<$builder, Bolt12SemanticError> where @@ -625,8 +639,6 @@ macro_rules! request_invoice_explicit_payer_id { ($self: ident, $builder: ty) => /// /// Useful for recurring payments using the same `payer_id` with different invoices. /// - /// This is not exported to bindings users as builder patterns don't map outside of move semantics. - /// /// [`InvoiceRequest::payer_id`]: crate::offers::invoice_request::InvoiceRequest::payer_id pub fn request_invoice_deriving_metadata( &$self, payer_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES, @@ -654,8 +666,6 @@ macro_rules! request_invoice_explicit_payer_id { ($self: ident, $builder: ty) => /// /// Errors if the offer contains unknown required features. /// - /// This is not exported to bindings users as builder patterns don't map outside of move semantics. - /// /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest pub fn request_invoice( &$self, metadata: Vec, payer_id: PublicKey @@ -668,11 +678,18 @@ macro_rules! request_invoice_explicit_payer_id { ($self: ident, $builder: ty) => } } } +#[cfg(not(c_bindings))] impl Offer { request_invoice_derived_payer_id!(self, InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T>); request_invoice_explicit_payer_id!(self, InvoiceRequestBuilder); } +#[cfg(c_bindings)] +impl Offer { + request_invoice_derived_payer_id!(self, InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b>); + request_invoice_explicit_payer_id!(self, InvoiceRequestWithExplicitPayerIdBuilder); +} + #[cfg(test)] impl Offer { pub(super) fn as_tlv_stream(&self) -> OfferTlvStreamRef { -- 2.39.5