Skip to content

Commit

Permalink
Add c_bindings version of InvoiceBuilder
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jkczyz committed Feb 26, 2024
1 parent 5d7cfa4 commit a654d35
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 22 deletions.
7 changes: 6 additions & 1 deletion lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<DerivedSigningPubkey> = builder.into();
let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;
let reply_path = self.create_blinded_path()
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
Expand Down Expand Up @@ -9281,6 +9282,8 @@ where
let builder = invoice_request.respond_using_derived_keys_no_std(
payment_paths, payment_hash, created_at
);
let builder: Result<InvoiceBuilder<DerivedSigningPubkey>, _> =
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())),
Expand All @@ -9292,6 +9295,8 @@ where
let builder = invoice_request.respond_with_no_std(
payment_paths, payment_hash, created_at
);
let builder: Result<InvoiceBuilder<ExplicitSigningPubkey>, _> =
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|
Expand Down
120 changes: 111 additions & 9 deletions lightning/src/offers/invoice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -44,13 +45,15 @@
//! let mut buffer = Vec::new();
//!
//! // Invoice for the "offer to be paid" flow.
//! # <InvoiceBuilder<ExplicitSigningPubkey>>::from(
//! InvoiceRequest::try_from(bytes)?
#![cfg_attr(feature = "std", doc = "
.respond_with(payment_paths, payment_hash)?
")]
#![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)
Expand All @@ -74,6 +77,7 @@
//! # let mut buffer = Vec::new();
//!
//! // Invoice for the "offer for money" flow.
//! # <InvoiceBuilder<ExplicitSigningPubkey>>::from(
//! "lnr1qcp4256ypq"
//! .parse::<Refund>()?
#![cfg_attr(feature = "std", doc = "
Expand All @@ -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)
Expand Down Expand Up @@ -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<u8>,
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<u8>,
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.
Expand Down Expand Up @@ -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()))
}
}
} }

Expand Down Expand Up @@ -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>(
Expand All @@ -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
Expand Down Expand Up @@ -316,7 +361,7 @@ macro_rules! invoice_builder_methods { (
}

fn new(
invreq_bytes: &'a Vec<u8>, contents: InvoiceContents, signing_pubkey_strategy: S
invreq_bytes: &'a Vec<u8>, contents: InvoiceContents, signing_pubkey_strategy: $type_param
) -> Result<Self, Bolt12SemanticError> {
if contents.fields().payment_paths.is_empty() {
return Err(Bolt12SemanticError::MissingPaths);
Expand Down Expand Up @@ -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<InvoiceWithExplicitSigningPubkeyBuilder<'a>>
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<InvoiceWithDerivedSigningPubkeyBuilder<'a>>
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.
Expand Down Expand Up @@ -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::<secp256k1::All>(&secp_ctx)
{
panic!("error building invoice: {:?}", e);
}
Expand Down Expand Up @@ -1726,7 +1823,7 @@ mod tests {
payment_paths(), payment_hash(), now(), &expanded_key, &entropy
)
.unwrap()
.build_and_sign(&secp_ctx)
.build_and_sign::<secp256k1::All>(&secp_ctx)
{
panic!("error building invoice: {:?}", e);
}
Expand Down Expand Up @@ -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);
Expand Down
50 changes: 39 additions & 11 deletions lightning/src/offers/invoice_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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.
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand All @@ -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<T: secp256k1::Signing>(
$self: $self_type, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
pub fn verify<
#[cfg(not(c_bindings))]
T: secp256k1::Signing
>(
$self: $self_type, key: &ExpandedKey,
#[cfg(not(c_bindings))]
secp_ctx: &Secp256k1<T>,
#[cfg(c_bindings)]
secp_ctx: &Secp256k1<secp256k1::All>,
) -> Result<VerifiedInvoiceRequest, ()> {
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<ExplicitSigningPubkey>);
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
Expand All @@ -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(
Expand All @@ -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,
Expand All @@ -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<ExplicitSigningPubkey>);
#[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<DerivedSigningPubkey>);
#[cfg(c_bindings)]
invoice_request_respond_with_derived_signing_pubkey_methods!(self, self.inner, InvoiceWithDerivedSigningPubkeyBuilder);
}

impl InvoiceRequestContents {
Expand Down
Loading

0 comments on commit a654d35

Please sign in to comment.