From a654d35a060079f103f01bc499bc225d1a6c9d16 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Sun, 25 Feb 2024 20:42:57 -0600 Subject: [PATCH] Add c_bindings version of InvoiceBuilder Use the macros introduced in the previous commit to define two builders for each type parameterization of InvoiceBuilder - InvoiceWithExplicitSigningPubkeyBuilder - InvoiceWithDerivedSigningPubkeyBuilder The difference between these and InvoiceBuilder 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 a Bolt12Invoice. Keeps InvoiceBuilder defined so that it can be used internally in ChannelManager's OffersMessageHandler even when compiled for c_bindings. --- lightning/src/ln/channelmanager.rs | 7 +- lightning/src/offers/invoice.rs | 120 ++++++++++++++++++++++-- lightning/src/offers/invoice_request.rs | 50 +++++++--- lightning/src/offers/refund.rs | 18 +++- 4 files changed, 173 insertions(+), 22 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 87428ca36aa..8b23f311a2c 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -57,7 +57,7 @@ use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError}; use crate::ln::outbound_payment; use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration}; use crate::ln::wire::Encode; -use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, InvoiceBuilder}; +use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder}; use crate::offers::invoice_error::InvoiceError; use crate::offers::invoice_request::{DerivedPayerId, InvoiceRequestBuilder}; use crate::offers::merkle::SignError; @@ -7834,6 +7834,7 @@ where let builder = refund.respond_using_derived_keys_no_std( payment_paths, payment_hash, created_at, expanded_key, entropy )?; + let builder: InvoiceBuilder = builder.into(); let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?; let reply_path = self.create_blinded_path() .map_err(|_| Bolt12SemanticError::MissingPaths)?; @@ -9281,6 +9282,8 @@ where let builder = invoice_request.respond_using_derived_keys_no_std( payment_paths, payment_hash, created_at ); + let builder: Result, _> = + builder.map(|b| b.into()); match builder.and_then(|b| b.allow_mpp().build_and_sign(secp_ctx)) { Ok(invoice) => Some(OffersMessage::Invoice(invoice)), Err(error) => Some(OffersMessage::InvoiceError(error.into())), @@ -9292,6 +9295,8 @@ where let builder = invoice_request.respond_with_no_std( payment_paths, payment_hash, created_at ); + let builder: Result, _> = + builder.map(|b| b.into()); let response = builder.and_then(|builder| builder.allow_mpp().build()) .map_err(|e| OffersMessage::InvoiceError(e.into())) .and_then(|invoice| diff --git a/lightning/src/offers/invoice.rs b/lightning/src/offers/invoice.rs index d298e77d238..c1f2b502bcd 100644 --- a/lightning/src/offers/invoice.rs +++ b/lightning/src/offers/invoice.rs @@ -22,6 +22,7 @@ //! //! use bitcoin::hashes::Hash; //! use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey}; +//! # use lightning::offers::invoice::{ExplicitSigningPubkey, InvoiceBuilder}; //! use core::convert::{Infallible, TryFrom}; //! use lightning::offers::invoice_request::InvoiceRequest; //! use lightning::offers::refund::Refund; @@ -44,6 +45,7 @@ //! let mut buffer = Vec::new(); //! //! // Invoice for the "offer to be paid" flow. +//! # >::from( //! InvoiceRequest::try_from(bytes)? #![cfg_attr(feature = "std", doc = " .respond_with(payment_paths, payment_hash)? @@ -51,6 +53,7 @@ #![cfg_attr(not(feature = "std"), doc = " .respond_with_no_std(payment_paths, payment_hash, core::time::Duration::from_secs(0))? ")] +//! # ) //! .relative_expiry(3600) //! .allow_mpp() //! .fallback_v0_p2wpkh(&wpubkey_hash) @@ -74,6 +77,7 @@ //! # let mut buffer = Vec::new(); //! //! // Invoice for the "offer for money" flow. +//! # >::from( //! "lnr1qcp4256ypq" //! .parse::()? #![cfg_attr(feature = "std", doc = " @@ -82,6 +86,7 @@ #![cfg_attr(not(feature = "std"), doc = " .respond_with_no_std(payment_paths, payment_hash, pubkey, core::time::Duration::from_secs(0))? ")] +//! # ) //! .relative_expiry(3600) //! .allow_mpp() //! .fallback_v0_p2wpkh(&wpubkey_hash) @@ -151,6 +156,38 @@ pub struct InvoiceBuilder<'a, S: SigningPubkeyStrategy> { signing_pubkey_strategy: S, } +/// Builds a [`Bolt12Invoice`] from either: +/// - an [`InvoiceRequest`] for the "offer to be paid" flow or +/// - a [`Refund`] for the "offer for money" flow. +/// +/// See [module-level documentation] for usage. +/// +/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest +/// [`Refund`]: crate::offers::refund::Refund +/// [module-level documentation]: self +#[cfg(c_bindings)] +pub struct InvoiceWithExplicitSigningPubkeyBuilder<'a> { + invreq_bytes: &'a Vec, + invoice: InvoiceContents, + signing_pubkey_strategy: ExplicitSigningPubkey, +} + +/// Builds a [`Bolt12Invoice`] from either: +/// - an [`InvoiceRequest`] for the "offer to be paid" flow or +/// - a [`Refund`] for the "offer for money" flow. +/// +/// See [module-level documentation] for usage. +/// +/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest +/// [`Refund`]: crate::offers::refund::Refund +/// [module-level documentation]: self +#[cfg(c_bindings)] +pub struct InvoiceWithDerivedSigningPubkeyBuilder<'a> { + invreq_bytes: &'a Vec, + invoice: InvoiceContents, + signing_pubkey_strategy: DerivedSigningPubkey, +} + /// Indicates how [`Bolt12Invoice::signing_pubkey`] was set. /// /// This is not exported to bindings users as builder patterns don't map outside of move semantics. @@ -216,8 +253,13 @@ macro_rules! invoice_explicit_signing_pubkey_builder_methods { ($self: ident, $s } } - let InvoiceBuilder { invreq_bytes, invoice, .. } = $self; - Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice)) + 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())) + } } } } @@ -272,10 +314,13 @@ macro_rules! invoice_derived_signing_pubkey_builder_methods { ( } } - let InvoiceBuilder { + 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 unsigned_invoice = UnsignedBolt12Invoice::new(invreq_bytes, invoice.clone()); let invoice = unsigned_invoice .sign::<_, Infallible>( @@ -287,7 +332,7 @@ macro_rules! invoice_derived_signing_pubkey_builder_methods { ( } } macro_rules! invoice_builder_methods { ( - $self: ident, $self_type: ty, $return_type: ty, $return_value: expr + $self: ident, $self_type: ty, $return_type: ty, $return_value: expr, $type_param: ty ) => { pub(crate) fn amount_msats( invoice_request: &InvoiceRequest @@ -316,7 +361,7 @@ macro_rules! invoice_builder_methods { ( } fn new( - invreq_bytes: &'a Vec, contents: InvoiceContents, signing_pubkey_strategy: S + invreq_bytes: &'a Vec, contents: InvoiceContents, signing_pubkey_strategy: $type_param ) -> Result { if contents.fields().payment_paths.is_empty() { return Err(Bolt12SemanticError::MissingPaths); @@ -392,7 +437,59 @@ impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> { } impl<'a, S: SigningPubkeyStrategy> InvoiceBuilder<'a, S> { - invoice_builder_methods!(self, Self, Self, self); + invoice_builder_methods!(self, Self, Self, self, S); +} + +#[cfg(all(c_bindings, not(test)))] +impl<'a> InvoiceWithExplicitSigningPubkeyBuilder<'a> { + invoice_explicit_signing_pubkey_builder_methods!(self, &mut Self); + invoice_builder_methods!(self, &mut Self, (), (), ExplicitSigningPubkey); +} + +#[cfg(all(c_bindings, test))] +impl<'a> InvoiceWithExplicitSigningPubkeyBuilder<'a> { + invoice_explicit_signing_pubkey_builder_methods!(self, &mut Self); + invoice_builder_methods!(self, &mut Self, &mut Self, self, ExplicitSigningPubkey); +} + +#[cfg(all(c_bindings, not(test)))] +impl<'a> InvoiceWithDerivedSigningPubkeyBuilder<'a> { + invoice_derived_signing_pubkey_builder_methods!(self, &mut Self, secp256k1::All); + invoice_builder_methods!(self, &mut Self, (), (), DerivedSigningPubkey); +} + +#[cfg(all(c_bindings, test))] +impl<'a> InvoiceWithDerivedSigningPubkeyBuilder<'a> { + invoice_derived_signing_pubkey_builder_methods!(self, &mut Self, secp256k1::All); + invoice_builder_methods!(self, &mut Self, &mut Self, self, DerivedSigningPubkey); +} + +#[cfg(c_bindings)] +impl<'a> From> +for InvoiceBuilder<'a, ExplicitSigningPubkey> { + fn from(builder: InvoiceWithExplicitSigningPubkeyBuilder<'a>) -> Self { + let InvoiceWithExplicitSigningPubkeyBuilder { + invreq_bytes, invoice, signing_pubkey_strategy, + } = builder; + + Self { + invreq_bytes, invoice, signing_pubkey_strategy, + } + } +} + +#[cfg(c_bindings)] +impl<'a> From> +for InvoiceBuilder<'a, DerivedSigningPubkey> { + fn from(builder: InvoiceWithDerivedSigningPubkeyBuilder<'a>) -> Self { + let InvoiceWithDerivedSigningPubkeyBuilder { + invreq_bytes, invoice, signing_pubkey_strategy, + } = builder; + + Self { + invreq_bytes, invoice, signing_pubkey_strategy, + } + } } /// A semantically valid [`Bolt12Invoice`] that hasn't been signed. @@ -1685,7 +1782,7 @@ mod tests { if let Err(e) = invoice_request.clone() .verify(&expanded_key, &secp_ctx).unwrap() .respond_using_derived_keys_no_std(payment_paths(), payment_hash(), now()).unwrap() - .build_and_sign(&secp_ctx) + .build_and_sign::(&secp_ctx) { panic!("error building invoice: {:?}", e); } @@ -1726,7 +1823,7 @@ mod tests { payment_paths(), payment_hash(), now(), &expanded_key, &entropy ) .unwrap() - .build_and_sign(&secp_ctx) + .build_and_sign::(&secp_ctx) { panic!("error building invoice: {:?}", e); } @@ -2122,8 +2219,13 @@ mod tests { .request_invoice(vec![1; 32], payer_pubkey()).unwrap() .build().unwrap() .sign(payer_sign).unwrap(); + #[cfg(not(c_bindings))] + let invoice_builder = invoice_request + .respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap(); + #[cfg(c_bindings)] let mut invoice_builder = invoice_request - .respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap() + .respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap(); + let mut invoice_builder = invoice_builder .fallback_v0_p2wsh(&script.wscript_hash()) .fallback_v0_p2wpkh(&pubkey.wpubkey_hash().unwrap()) .fallback_v1_p2tr_tweaked(&tweaked_pubkey); diff --git a/lightning/src/offers/invoice_request.rs b/lightning/src/offers/invoice_request.rs index 7226b27da29..0c292a08e70 100644 --- a/lightning/src/offers/invoice_request.rs +++ b/lightning/src/offers/invoice_request.rs @@ -71,7 +71,7 @@ use crate::ln::channelmanager::PaymentId; use crate::ln::features::InvoiceRequestFeatures; use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce}; use crate::ln::msgs::DecodeError; -use crate::offers::invoice::{BlindedPayInfo, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder}; +use crate::offers::invoice::BlindedPayInfo; use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, self}; use crate::offers::offer::{Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef}; use crate::offers::parse::{Bolt12ParseError, ParsedMessage, Bolt12SemanticError}; @@ -80,6 +80,15 @@ use crate::offers::signer::{Metadata, MetadataMaterial}; use crate::util::ser::{HighZeroBytesDroppedBigSize, SeekReadable, WithoutLength, Writeable, Writer}; use crate::util::string::PrintableString; +#[cfg(not(c_bindings))] +use { + crate::offers::invoice::{DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder}, +}; +#[cfg(c_bindings)] +use { + crate::offers::invoice::{InvoiceWithDerivedSigningPubkeyBuilder, InvoiceWithExplicitSigningPubkeyBuilder}, +}; + use crate::prelude::*; /// Tag for the hash function used when signing an [`InvoiceRequest`]'s merkle root. @@ -660,8 +669,6 @@ macro_rules! invoice_request_respond_with_explicit_signing_pubkey_methods { ( /// See [`InvoiceRequest::respond_with_no_std`] for further details where the aforementioned /// creation time is used for the `created_at` parameter. /// - /// 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( @@ -695,8 +702,6 @@ macro_rules! invoice_request_respond_with_explicit_signing_pubkey_methods { ( /// 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( @@ -717,24 +722,45 @@ macro_rules! invoice_request_verify_method { ($self: ident, $self_type: ty) => { /// if they could be extracted from the metadata. /// /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice - pub fn verify( - $self: $self_type, key: &ExpandedKey, secp_ctx: &Secp256k1 + pub fn verify< + #[cfg(not(c_bindings))] + T: secp256k1::Signing + >( + $self: $self_type, key: &ExpandedKey, + #[cfg(not(c_bindings))] + secp_ctx: &Secp256k1, + #[cfg(c_bindings)] + secp_ctx: &Secp256k1, ) -> Result { let keys = $self.contents.inner.offer.verify(&$self.bytes, key, secp_ctx)?; Ok(VerifiedInvoiceRequest { + #[cfg(not(c_bindings))] inner: $self, + #[cfg(c_bindings)] + inner: $self.clone(), keys, }) } } } +#[cfg(not(c_bindings))] impl InvoiceRequest { offer_accessors!(self, self.contents.inner.offer); invoice_request_accessors!(self, self.contents); invoice_request_respond_with_explicit_signing_pubkey_methods!(self, self, InvoiceBuilder); invoice_request_verify_method!(self, Self); +} +#[cfg(c_bindings)] +impl InvoiceRequest { + offer_accessors!(self, self.contents.inner.offer); + invoice_request_accessors!(self, self.contents); + invoice_request_respond_with_explicit_signing_pubkey_methods!(self, self, InvoiceWithExplicitSigningPubkeyBuilder); + invoice_request_verify_method!(self, &Self); +} + +impl InvoiceRequest { /// Signature of the invoice request using [`payer_id`]. /// /// [`payer_id`]: Self::payer_id @@ -761,8 +787,6 @@ macro_rules! invoice_request_respond_with_derived_signing_pubkey_methods { ( /// /// See [`InvoiceRequest::respond_with`] for further details. /// - /// This is not exported to bindings users as builder patterns don't map outside of move semantics. - /// /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice #[cfg(feature = "std")] pub fn respond_using_derived_keys( @@ -781,8 +805,6 @@ macro_rules! invoice_request_respond_with_derived_signing_pubkey_methods { ( /// /// 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. - /// /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice pub fn respond_using_derived_keys_no_std( &$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash, @@ -806,8 +828,14 @@ macro_rules! invoice_request_respond_with_derived_signing_pubkey_methods { ( impl VerifiedInvoiceRequest { offer_accessors!(self, self.inner.contents.inner.offer); invoice_request_accessors!(self, self.inner.contents); + #[cfg(not(c_bindings))] invoice_request_respond_with_explicit_signing_pubkey_methods!(self, self.inner, InvoiceBuilder); + #[cfg(c_bindings)] + invoice_request_respond_with_explicit_signing_pubkey_methods!(self, self.inner, InvoiceWithExplicitSigningPubkeyBuilder); + #[cfg(not(c_bindings))] invoice_request_respond_with_derived_signing_pubkey_methods!(self, self.inner, InvoiceBuilder); + #[cfg(c_bindings)] + invoice_request_respond_with_derived_signing_pubkey_methods!(self, self.inner, InvoiceWithDerivedSigningPubkeyBuilder); } impl InvoiceRequestContents { diff --git a/lightning/src/offers/refund.rs b/lightning/src/offers/refund.rs index a2805b5b0b6..78c1b7f7eb2 100644 --- a/lightning/src/offers/refund.rs +++ b/lightning/src/offers/refund.rs @@ -96,7 +96,7 @@ use crate::ln::channelmanager::PaymentId; use crate::ln::features::InvoiceRequestFeatures; use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce}; use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT}; -use crate::offers::invoice::{BlindedPayInfo, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder}; +use crate::offers::invoice::BlindedPayInfo; use crate::offers::invoice_request::{InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef}; use crate::offers::offer::{OfferTlvStream, OfferTlvStreamRef}; use crate::offers::parse::{Bech32Encode, Bolt12ParseError, Bolt12SemanticError, ParsedMessage}; @@ -105,6 +105,15 @@ use crate::offers::signer::{Metadata, MetadataMaterial, self}; use crate::util::ser::{SeekReadable, WithoutLength, Writeable, Writer}; use crate::util::string::PrintableString; +#[cfg(not(c_bindings))] +use { + crate::offers::invoice::{DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder}, +}; +#[cfg(c_bindings)] +use { + crate::offers::invoice::{InvoiceWithDerivedSigningPubkeyBuilder, InvoiceWithExplicitSigningPubkeyBuilder}, +}; + use crate::prelude::*; #[cfg(feature = "std")] @@ -587,11 +596,18 @@ macro_rules! respond_with_derived_signing_pubkey_methods { ($self: ident, $build } } } +#[cfg(not(c_bindings))] impl Refund { respond_with_explicit_signing_pubkey_methods!(self, InvoiceBuilder); respond_with_derived_signing_pubkey_methods!(self, InvoiceBuilder); } +#[cfg(c_bindings)] +impl Refund { + respond_with_explicit_signing_pubkey_methods!(self, InvoiceWithExplicitSigningPubkeyBuilder); + respond_with_derived_signing_pubkey_methods!(self, InvoiceWithDerivedSigningPubkeyBuilder); +} + #[cfg(test)] impl Refund { fn as_tlv_stream(&self) -> RefundTlvStreamRef {