Skip to content

Commit a517925

Browse files
committed
Macro-ize OfferBuilder
OfferBuilder is not exported to bindings because it has methods that take `self` by value and are only implemented for certain type parameterizations. Define these methods using macros such that different builders and related methods can be defined for c_bindings.
1 parent 51d9ee3 commit a517925

File tree

1 file changed

+79
-57
lines changed

1 file changed

+79
-57
lines changed

lightning/src/offers/offer.rs

Lines changed: 79 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,12 @@ pub struct ExplicitMetadata {}
134134
pub struct DerivedMetadata {}
135135

136136
impl MetadataStrategy for ExplicitMetadata {}
137+
137138
impl MetadataStrategy for DerivedMetadata {}
138139

139-
impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> {
140+
macro_rules! offer_explicit_metadata_builder_methods { (
141+
$self: ident, $self_type: ty, $return_type: ty, $return_value: expr
142+
) => {
140143
/// Creates a new builder for an offer setting the [`Offer::description`] and using the
141144
/// [`Offer::signing_pubkey`] for signing invoices. The associated secret key must be remembered
142145
/// while the offer is valid.
@@ -151,7 +154,7 @@ impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> {
151154
/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
152155
/// [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder
153156
pub fn new(description: String, signing_pubkey: PublicKey) -> Self {
154-
OfferBuilder {
157+
Self {
155158
offer: OfferContents {
156159
chains: None, metadata: None, amount: None, description,
157160
features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
@@ -165,13 +168,13 @@ impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> {
165168
/// Sets the [`Offer::metadata`] to the given bytes.
166169
///
167170
/// Successive calls to this method will override the previous setting.
168-
pub fn metadata(mut self, metadata: Vec<u8>) -> Result<Self, Bolt12SemanticError> {
169-
self.offer.metadata = Some(Metadata::Bytes(metadata));
170-
Ok(self)
171+
pub fn metadata(mut $self: $self_type, metadata: Vec<u8>) -> Result<$return_type, Bolt12SemanticError> {
172+
$self.offer.metadata = Some(Metadata::Bytes(metadata));
173+
Ok($return_value)
171174
}
172-
}
175+
} }
173176

174-
impl<'a, T: secp256k1::Signing> OfferBuilder<'a, DerivedMetadata, T> {
177+
macro_rules! offer_derived_metadata_builder_methods { () => {
175178
/// Similar to [`OfferBuilder::new`] except, if [`OfferBuilder::path`] is called, the signing
176179
/// pubkey is derived from the given [`ExpandedKey`] and [`EntropySource`]. This provides
177180
/// recipient privacy by using a different signing pubkey for each offer. Otherwise, the
@@ -190,7 +193,7 @@ impl<'a, T: secp256k1::Signing> OfferBuilder<'a, DerivedMetadata, T> {
190193
let nonce = Nonce::from_entropy_source(entropy_source);
191194
let derivation_material = MetadataMaterial::new(nonce, expanded_key, IV_BYTES, None);
192195
let metadata = Metadata::DerivedSigningPubkey(derivation_material);
193-
OfferBuilder {
196+
Self {
194197
offer: OfferContents {
195198
chains: None, metadata: Some(metadata), amount: None, description,
196199
features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
@@ -200,17 +203,19 @@ impl<'a, T: secp256k1::Signing> OfferBuilder<'a, DerivedMetadata, T> {
200203
secp_ctx: Some(secp_ctx),
201204
}
202205
}
203-
}
206+
} }
204207

205-
impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
208+
macro_rules! offer_builder_methods { (
209+
$self: ident, $self_type: ty, $return_type: ty, $return_value: expr
210+
) => {
206211
/// Adds the chain hash of the given [`Network`] to [`Offer::chains`]. If not called,
207212
/// the chain hash of [`Network::Bitcoin`] is assumed to be the only one supported.
208213
///
209214
/// See [`Offer::chains`] on how this relates to the payment currency.
210215
///
211216
/// Successive calls to this method will add another chain hash.
212-
pub fn chain(self, network: Network) -> Self {
213-
self.chain_hash(ChainHash::using_genesis_block(network))
217+
pub fn chain($self: $self_type, network: Network) -> $return_type {
218+
$self.chain_hash(ChainHash::using_genesis_block(network))
214219
}
215220

216221
/// Adds the [`ChainHash`] to [`Offer::chains`]. If not called, the chain hash of
@@ -219,69 +224,69 @@ impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
219224
/// See [`Offer::chains`] on how this relates to the payment currency.
220225
///
221226
/// Successive calls to this method will add another chain hash.
222-
pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Self {
223-
let chains = self.offer.chains.get_or_insert_with(Vec::new);
227+
pub(crate) fn chain_hash(mut $self: $self_type, chain: ChainHash) -> $return_type {
228+
let chains = $self.offer.chains.get_or_insert_with(Vec::new);
224229
if !chains.contains(&chain) {
225230
chains.push(chain);
226231
}
227232

228-
self
233+
$return_value
229234
}
230235

231236
/// Sets the [`Offer::amount`] as an [`Amount::Bitcoin`].
232237
///
233238
/// Successive calls to this method will override the previous setting.
234-
pub fn amount_msats(self, amount_msats: u64) -> Self {
235-
self.amount(Amount::Bitcoin { amount_msats })
239+
pub fn amount_msats($self: $self_type, amount_msats: u64) -> $return_type {
240+
$self.amount(Amount::Bitcoin { amount_msats })
236241
}
237242

238243
/// Sets the [`Offer::amount`].
239244
///
240245
/// Successive calls to this method will override the previous setting.
241-
pub(super) fn amount(mut self, amount: Amount) -> Self {
242-
self.offer.amount = Some(amount);
243-
self
246+
pub(super) fn amount(mut $self: $self_type, amount: Amount) -> $return_type {
247+
$self.offer.amount = Some(amount);
248+
$return_value
244249
}
245250

246251
/// Sets the [`Offer::absolute_expiry`] as seconds since the Unix epoch. Any expiry that has
247252
/// already passed is valid and can be checked for using [`Offer::is_expired`].
248253
///
249254
/// Successive calls to this method will override the previous setting.
250-
pub fn absolute_expiry(mut self, absolute_expiry: Duration) -> Self {
251-
self.offer.absolute_expiry = Some(absolute_expiry);
252-
self
255+
pub fn absolute_expiry(mut $self: $self_type, absolute_expiry: Duration) -> $return_type {
256+
$self.offer.absolute_expiry = Some(absolute_expiry);
257+
$return_value
253258
}
254259

255260
/// Sets the [`Offer::issuer`].
256261
///
257262
/// Successive calls to this method will override the previous setting.
258-
pub fn issuer(mut self, issuer: String) -> Self {
259-
self.offer.issuer = Some(issuer);
260-
self
263+
pub fn issuer(mut $self: $self_type, issuer: String) -> $return_type {
264+
$self.offer.issuer = Some(issuer);
265+
$return_value
261266
}
262267

263268
/// Adds a blinded path to [`Offer::paths`]. Must include at least one path if only connected by
264269
/// private channels or if [`Offer::signing_pubkey`] is not a public node id.
265270
///
266271
/// Successive calls to this method will add another blinded path. Caller is responsible for not
267272
/// adding duplicate paths.
268-
pub fn path(mut self, path: BlindedPath) -> Self {
269-
self.offer.paths.get_or_insert_with(Vec::new).push(path);
270-
self
273+
pub fn path(mut $self: $self_type, path: BlindedPath) -> $return_type {
274+
$self.offer.paths.get_or_insert_with(Vec::new).push(path);
275+
$return_value
271276
}
272277

273278
/// Sets the quantity of items for [`Offer::supported_quantity`]. If not called, defaults to
274279
/// [`Quantity::One`].
275280
///
276281
/// Successive calls to this method will override the previous setting.
277-
pub fn supported_quantity(mut self, quantity: Quantity) -> Self {
278-
self.offer.supported_quantity = quantity;
279-
self
282+
pub fn supported_quantity(mut $self: $self_type, quantity: Quantity) -> $return_type {
283+
$self.offer.supported_quantity = quantity;
284+
$return_value
280285
}
281286

282287
/// Builds an [`Offer`] from the builder's settings.
283-
pub fn build(mut self) -> Result<Offer, Bolt12SemanticError> {
284-
match self.offer.amount {
288+
pub fn build(mut $self: $self_type) -> Result<Offer, Bolt12SemanticError> {
289+
match $self.offer.amount {
285290
Some(Amount::Bitcoin { amount_msats }) => {
286291
if amount_msats > MAX_VALUE_MSAT {
287292
return Err(Bolt12SemanticError::InvalidAmount);
@@ -291,62 +296,79 @@ impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
291296
None => {},
292297
}
293298

294-
if let Some(chains) = &self.offer.chains {
295-
if chains.len() == 1 && chains[0] == self.offer.implied_chain() {
296-
self.offer.chains = None;
299+
if let Some(chains) = &$self.offer.chains {
300+
if chains.len() == 1 && chains[0] == $self.offer.implied_chain() {
301+
$self.offer.chains = None;
297302
}
298303
}
299304

300-
Ok(self.build_without_checks())
305+
Ok($self.build_without_checks())
301306
}
302307

303-
fn build_without_checks(mut self) -> Offer {
308+
fn build_without_checks(mut $self: $self_type) -> Offer {
304309
// Create the metadata for stateless verification of an InvoiceRequest.
305-
if let Some(mut metadata) = self.offer.metadata.take() {
310+
if let Some(mut metadata) = $self.offer.metadata.take() {
306311
if metadata.has_derivation_material() {
307-
if self.offer.paths.is_none() {
312+
if $self.offer.paths.is_none() {
308313
metadata = metadata.without_keys();
309314
}
310315

311-
let mut tlv_stream = self.offer.as_tlv_stream();
316+
let mut tlv_stream = $self.offer.as_tlv_stream();
312317
debug_assert_eq!(tlv_stream.metadata, None);
313318
tlv_stream.metadata = None;
314319
if metadata.derives_recipient_keys() {
315320
tlv_stream.node_id = None;
316321
}
317322

318-
let (derived_metadata, keys) = metadata.derive_from(tlv_stream, self.secp_ctx);
323+
let (derived_metadata, keys) = metadata.derive_from(tlv_stream, $self.secp_ctx);
319324
metadata = derived_metadata;
320325
if let Some(keys) = keys {
321-
self.offer.signing_pubkey = keys.public_key();
326+
$self.offer.signing_pubkey = keys.public_key();
322327
}
323328
}
324329

325-
self.offer.metadata = Some(metadata);
330+
$self.offer.metadata = Some(metadata);
326331
}
327332

328333
let mut bytes = Vec::new();
329-
self.offer.write(&mut bytes).unwrap();
334+
$self.offer.write(&mut bytes).unwrap();
330335

331-
Offer { bytes, contents: self.offer }
336+
Offer { bytes, contents: $self.offer }
332337
}
333-
}
338+
} }
334339

335340
#[cfg(test)]
336-
impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
337-
fn features_unchecked(mut self, features: OfferFeatures) -> Self {
338-
self.offer.features = features;
339-
self
341+
macro_rules! offer_builder_test_methods { (
342+
$self: ident, $self_type: ty, $return_type: ty, $return_value: expr
343+
) => {
344+
fn features_unchecked(mut $self: $self_type, features: OfferFeatures) -> $return_type {
345+
$self.offer.features = features;
346+
$return_value
340347
}
341348

342-
pub(crate) fn clear_paths(mut self) -> Self {
343-
self.offer.paths = None;
344-
self
349+
pub(crate) fn clear_paths(mut $self: $self_type) -> $return_type {
350+
$self.offer.paths = None;
351+
$return_value
345352
}
346353

347-
pub(super) fn build_unchecked(self) -> Offer {
348-
self.build_without_checks()
354+
pub(super) fn build_unchecked($self: $self_type) -> Offer {
355+
$self.build_without_checks()
349356
}
357+
} }
358+
359+
impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
360+
offer_builder_methods!(self, Self, Self, self);
361+
362+
#[cfg(test)]
363+
offer_builder_test_methods!(self, Self, Self, self);
364+
}
365+
366+
impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> {
367+
offer_explicit_metadata_builder_methods!(self, Self, Self, self);
368+
}
369+
370+
impl<'a, T: secp256k1::Signing> OfferBuilder<'a, DerivedMetadata, T> {
371+
offer_derived_metadata_builder_methods!();
350372
}
351373

352374
/// An `Offer` is a potentially long-lived proposal for payment of a good or service.

0 commit comments

Comments
 (0)