From: Jeffrey Czyz Date: Sat, 24 Feb 2024 02:30:50 +0000 (-0600) Subject: Add c_bindings version of RefundBuilder X-Git-Tag: v0.0.123-beta~46^2~9 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=b1ad95158e8cea0119df31fb5318a31fcc70e927;p=rust-lightning Add c_bindings version of RefundBuilder Use the macros introduced in the previous commit to define a builder called RefundMaybeWithDerivedMetadataBuilder. The difference between this and RefundBuilder is that this has 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. Because of this, the builder's contents must be cloned when building a Refund. Keeps RefundBuilder defined so that it can be used internally in ChannelManager::create_refund_builder even when compiled for c_bindings. --- diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index d41909beb..c9fc44994 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -85,6 +85,7 @@ use { #[cfg(c_bindings)] use { crate::offers::offer::OfferWithDerivedMetadataBuilder, + crate::offers::refund::RefundMaybeWithDerivedMetadataBuilder, }; use alloc::collections::{btree_map, BTreeMap}; @@ -7574,23 +7575,7 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => { } } } -impl ChannelManager -where - M::Target: chain::Watch<::EcdsaSigner>, - T::Target: BroadcasterInterface, - ES::Target: EntropySource, - NS::Target: NodeSigner, - SP::Target: SignerProvider, - F::Target: FeeEstimator, - R::Target: Router, - L::Target: Logger, -{ - #[cfg(not(c_bindings))] - create_offer_builder!(self, OfferBuilder); - - #[cfg(c_bindings)] - create_offer_builder!(self, OfferWithDerivedMetadataBuilder); - +macro_rules! create_refund_builder { ($self: ident, $builder: ty) => { /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the /// [`ChannelManager`] when handling [`Bolt12Invoice`] messages for the refund. /// @@ -7640,31 +7625,53 @@ where /// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths /// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments pub fn create_refund_builder( - &self, description: String, amount_msats: u64, absolute_expiry: Duration, + &$self, description: String, amount_msats: u64, absolute_expiry: Duration, payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option - ) -> Result, Bolt12SemanticError> { - let node_id = self.get_our_node_id(); - let expanded_key = &self.inbound_payment_key; - let entropy = &*self.entropy_source; - let secp_ctx = &self.secp_ctx; + ) -> Result<$builder, Bolt12SemanticError> { + let node_id = $self.get_our_node_id(); + let expanded_key = &$self.inbound_payment_key; + let entropy = &*$self.entropy_source; + let secp_ctx = &$self.secp_ctx; - let path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?; + let path = $self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?; let builder = RefundBuilder::deriving_payer_id( description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id )? - .chain_hash(self.chain_hash) + .chain_hash($self.chain_hash) .absolute_expiry(absolute_expiry) .path(path); let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry); - self.pending_outbound_payments + $self.pending_outbound_payments .add_new_awaiting_invoice( payment_id, expiration, retry_strategy, max_total_routing_fee_msat, ) .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?; - Ok(builder) + Ok(builder.into()) } +} } + +impl ChannelManager +where + M::Target: chain::Watch<::EcdsaSigner>, + T::Target: BroadcasterInterface, + ES::Target: EntropySource, + NS::Target: NodeSigner, + SP::Target: SignerProvider, + F::Target: FeeEstimator, + R::Target: Router, + L::Target: Logger, +{ + #[cfg(not(c_bindings))] + create_offer_builder!(self, OfferBuilder); + #[cfg(not(c_bindings))] + create_refund_builder!(self, RefundBuilder); + + #[cfg(c_bindings)] + create_offer_builder!(self, OfferWithDerivedMetadataBuilder); + #[cfg(c_bindings)] + create_refund_builder!(self, RefundMaybeWithDerivedMetadataBuilder); /// Pays for an [`Offer`] using the given parameters by creating an [`InvoiceRequest`] and /// enqueuing it to be sent via an onion message. [`ChannelManager`] will pay the actual diff --git a/lightning/src/offers/invoice.rs b/lightning/src/offers/invoice.rs index ebb23fb2e..da88be154 100644 --- a/lightning/src/offers/invoice.rs +++ b/lightning/src/offers/invoice.rs @@ -1310,14 +1310,15 @@ mod tests { #[cfg(not(c_bindings))] use { crate::offers::offer::OfferBuilder, + crate::offers::refund::RefundBuilder, }; #[cfg(c_bindings)] use { crate::offers::offer::OfferWithExplicitMetadataBuilder as OfferBuilder, + crate::offers::refund::RefundMaybeWithDerivedMetadataBuilder as RefundBuilder, }; use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError}; use crate::offers::payer::PayerTlvStreamRef; - use crate::offers::refund::RefundBuilder; use crate::offers::test_utils::*; use crate::util::ser::{BigSize, Iterable, Writeable}; use crate::util::string::PrintableString; diff --git a/lightning/src/offers/refund.rs b/lightning/src/offers/refund.rs index 6d3a34f61..f4e9c4a8c 100644 --- a/lightning/src/offers/refund.rs +++ b/lightning/src/offers/refund.rs @@ -124,7 +124,18 @@ pub struct RefundBuilder<'a, T: secp256k1::Signing> { secp_ctx: Option<&'a Secp256k1>, } -macro_rules! refund_without_secp256k1_builder_methods { () => { +/// Builds a [`Refund`] for the "offer for money" flow. +/// +/// See [module-level documentation] for usage. +/// +/// [module-level documentation]: self +#[cfg(c_bindings)] +pub struct RefundMaybeWithDerivedMetadataBuilder<'a> { + refund: RefundContents, + secp_ctx: Option<&'a Secp256k1>, +} + +macro_rules! refund_explicit_metadata_builder_methods { () => { /// Creates a new builder for a refund using the [`Refund::payer_id`] for the public node id to /// send to if no [`Refund::paths`] are set. Otherwise, it may be a transient pubkey. /// @@ -158,7 +169,7 @@ macro_rules! refund_without_secp256k1_builder_methods { () => { } } macro_rules! refund_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)? ) => { /// Similar to [`RefundBuilder::new`] except, if [`RefundBuilder::path`] is called, the payer id /// is derived from the given [`ExpandedKey`] and nonce. This provides sender privacy by using a @@ -175,7 +186,7 @@ macro_rules! refund_builder_methods { ( /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey pub fn deriving_payer_id( description: String, node_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES, - secp_ctx: &'a Secp256k1, amount_msats: u64, payment_id: PaymentId + secp_ctx: &'a Secp256k1<$secp_context>, amount_msats: u64, payment_id: PaymentId ) -> Result where ES::Target: EntropySource { if amount_msats > MAX_VALUE_MSAT { return Err(Bolt12SemanticError::InvalidAmount); @@ -199,7 +210,7 @@ macro_rules! refund_builder_methods { ( /// already passed is valid and can be checked for using [`Refund::is_expired`]. /// /// Successive calls to this method will override the previous setting. - pub fn absolute_expiry(mut $self: $self_type, absolute_expiry: Duration) -> $return_type { + pub fn absolute_expiry($($self_mut)* $self: $self_type, absolute_expiry: Duration) -> $return_type { $self.refund.absolute_expiry = Some(absolute_expiry); $return_value } @@ -207,7 +218,7 @@ macro_rules! refund_builder_methods { ( /// Sets the [`Refund::issuer`]. /// /// Successive calls to this method will override the previous setting. - pub fn issuer(mut $self: $self_type, issuer: String) -> $return_type { + pub fn issuer($($self_mut)* $self: $self_type, issuer: String) -> $return_type { $self.refund.issuer = Some(issuer); $return_value } @@ -217,7 +228,7 @@ macro_rules! refund_builder_methods { ( /// /// Successive calls to this method will add another blinded path. Caller is responsible for not /// adding duplicate paths. - pub fn path(mut $self: $self_type, path: BlindedPath) -> $return_type { + pub fn path($($self_mut)* $self: $self_type, path: BlindedPath) -> $return_type { $self.refund.paths.get_or_insert_with(Vec::new).push(path); $return_value } @@ -234,7 +245,7 @@ macro_rules! refund_builder_methods { ( /// [`Network::Bitcoin`] is assumed. /// /// Successive calls to this method will override the previous setting. - pub(crate) fn chain_hash(mut $self: $self_type, chain: ChainHash) -> $return_type { + pub(crate) fn chain_hash($($self_mut)* $self: $self_type, chain: ChainHash) -> $return_type { $self.refund.chain = Some(chain); $return_value } @@ -248,7 +259,7 @@ macro_rules! refund_builder_methods { ( /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice /// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity /// [`Offer`]: crate::offers::offer::Offer - pub fn quantity(mut $self: $self_type, quantity: u64) -> $return_type { + pub fn quantity($($self_mut)* $self: $self_type, quantity: u64) -> $return_type { $self.refund.quantity = Some(quantity); $return_value } @@ -256,13 +267,13 @@ macro_rules! refund_builder_methods { ( /// Sets the [`Refund::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.refund.payer_note = Some(payer_note); $return_value } /// Builds a [`Refund`] after checking for valid semantics. - pub fn build(mut $self: $self_type) -> Result { + pub fn build($($self_mut)* $self: $self_type) -> Result { if $self.refund.chain() == $self.refund.implied_chain() { $self.refund.chain = None; } @@ -293,34 +304,65 @@ macro_rules! refund_builder_methods { ( let mut bytes = Vec::new(); $self.refund.write(&mut bytes).unwrap(); - Ok(Refund { bytes, contents: $self.refund }) + Ok(Refund { + bytes, + #[cfg(not(c_bindings))] + contents: $self.refund, + #[cfg(c_bindings)] + contents: $self.refund.clone(), + }) } } } #[cfg(test)] macro_rules! refund_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)? ) => { - pub(crate) fn clear_paths(mut $self: $self_type) -> $return_type { + #[cfg_attr(c_bindings, allow(dead_code))] + pub(crate) fn clear_paths($($self_mut)* $self: $self_type) -> $return_type { $self.refund.paths = None; $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.refund.features = features; $return_value } } } impl<'a> RefundBuilder<'a, secp256k1::SignOnly> { - refund_without_secp256k1_builder_methods!(); + refund_explicit_metadata_builder_methods!(); } impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> { - refund_builder_methods!(self, Self, Self, self); + refund_builder_methods!(self, Self, Self, self, T, mut); #[cfg(test)] - refund_builder_test_methods!(self, Self, Self, self); + refund_builder_test_methods!(self, Self, Self, self, mut); +} + +#[cfg(all(c_bindings, not(test)))] +impl<'a> RefundMaybeWithDerivedMetadataBuilder<'a> { + refund_explicit_metadata_builder_methods!(); + refund_builder_methods!(self, &mut Self, (), (), secp256k1::All); +} + +#[cfg(all(c_bindings, test))] +impl<'a> RefundMaybeWithDerivedMetadataBuilder<'a> { + refund_explicit_metadata_builder_methods!(); + refund_builder_methods!(self, &mut Self, &mut Self, self, secp256k1::All); + refund_builder_test_methods!(self, &mut Self, &mut Self, self); +} + +#[cfg(c_bindings)] +impl<'a> From> +for RefundMaybeWithDerivedMetadataBuilder<'a> { + fn from(builder: RefundBuilder<'a, secp256k1::All>) -> Self { + let RefundBuilder { refund, secp_ctx } = builder; + + Self { refund, secp_ctx } + } } /// A `Refund` is a request to send an [`Bolt12Invoice`] without a preceding [`Offer`]. @@ -798,7 +840,15 @@ impl core::fmt::Display for Refund { #[cfg(test)] mod tests { - use super::{Refund, RefundBuilder, RefundTlvStreamRef}; + use super::{Refund, RefundTlvStreamRef}; + #[cfg(not(c_bindings))] + use { + super::RefundBuilder, + }; + #[cfg(c_bindings)] + use { + super::RefundMaybeWithDerivedMetadataBuilder as RefundBuilder, + }; use bitcoin::blockdata::constants::ChainHash; use bitcoin::network::constants::Network;