Skip to content

Commit 4077624

Browse files
committed
Support BOLT 12 signing in c_bindings
Replace the Fn trait bound on signing methods with a dedicated trait since Fn is not supported in bindings. Implement the trait for Fn so that closures can still be used in Rust.
1 parent 3bd00b9 commit 4077624

File tree

8 files changed

+147
-63
lines changed

8 files changed

+147
-63
lines changed

fuzz/src/invoice_request_deser.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,17 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
3737
let even_pubkey = x_only_pubkey.public_key(Parity::Even);
3838
if signing_pubkey == odd_pubkey || signing_pubkey == even_pubkey {
3939
unsigned_invoice
40-
.sign::<_, Infallible>(
41-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
42-
)
40+
.sign(|message: &UnsignedBolt12Invoice| -> Result<_, Infallible> {
41+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
42+
})
4343
.unwrap()
4444
.write(&mut buffer)
4545
.unwrap();
4646
} else {
4747
unsigned_invoice
48-
.sign::<_, Infallible>(
49-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
50-
)
48+
.sign(|message: &UnsignedBolt12Invoice| -> Result<_, Infallible> {
49+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
50+
})
5151
.unwrap_err();
5252
}
5353
}

fuzz/src/offer_deser.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
2929

3030
if let Ok(invoice_request) = build_response(&offer, pubkey) {
3131
invoice_request
32-
.sign::<_, Infallible>(
33-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
34-
)
32+
.sign(|message: &UnsignedInvoiceRequest| -> Result<_, Infallible> {
33+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
34+
})
3535
.unwrap()
3636
.write(&mut buffer)
3737
.unwrap();

fuzz/src/refund_deser.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
3333

3434
if let Ok(invoice) = build_response(&refund, pubkey, &secp_ctx) {
3535
invoice
36-
.sign::<_, Infallible>(
37-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
38-
)
36+
.sign(|message: &UnsignedBolt12Invoice| -> Result<_, Infallible> {
37+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
38+
})
3939
.unwrap()
4040
.write(&mut buffer)
4141
.unwrap();

lightning/src/ln/channelmanager.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
5757
use crate::ln::outbound_payment;
5858
use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration};
5959
use crate::ln::wire::Encode;
60-
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder};
60+
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice};
6161
use crate::offers::invoice_error::InvoiceError;
6262
use crate::offers::invoice_request::{DerivedPayerId, InvoiceRequestBuilder};
6363
use crate::offers::merkle::SignError;
@@ -9302,7 +9302,9 @@ where
93029302
.and_then(|invoice| {
93039303
#[cfg(c_bindings)]
93049304
let mut invoice = invoice;
9305-
match invoice.sign(|invoice| self.node_signer.sign_bolt12_invoice(invoice)) {
9305+
match invoice.sign(|invoice: &UnsignedBolt12Invoice|
9306+
self.node_signer.sign_bolt12_invoice(invoice)
9307+
) {
93069308
Ok(invoice) => Ok(OffersMessage::Invoice(invoice)),
93079309
Err(SignError::Signing(())) => Err(OffersMessage::InvoiceError(
93089310
InvoiceError::from_string("Failed signing invoice".to_string())

lightning/src/offers/invoice.rs

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
//! use bitcoin::hashes::Hash;
2424
//! use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
2525
//! use core::convert::{Infallible, TryFrom};
26+
//! use lightning::offers::invoice::UnsignedBolt12Invoice;
2627
//! use lightning::offers::invoice_request::InvoiceRequest;
2728
//! use lightning::offers::refund::Refund;
2829
//! use lightning::util::ser::Writeable;
@@ -57,9 +58,9 @@
5758
//! .allow_mpp()
5859
//! .fallback_v0_p2wpkh(&wpubkey_hash)
5960
//! .build()?
60-
//! .sign::<_, Infallible>(
61-
//! |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
62-
//! )
61+
//! .sign(|message: &UnsignedBolt12Invoice| -> Result<_, Infallible> {
62+
//! Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
63+
//! })
6364
//! .expect("failed verifying signature")
6465
//! .write(&mut buffer)
6566
//! .unwrap();
@@ -90,9 +91,9 @@
9091
//! .allow_mpp()
9192
//! .fallback_v0_p2wpkh(&wpubkey_hash)
9293
//! .build()?
93-
//! .sign::<_, Infallible>(
94-
//! |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
95-
//! )
94+
//! .sign(|message: &UnsignedBolt12Invoice| -> Result<_, Infallible> {
95+
//! Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
96+
//! })
9697
//! .expect("failed verifying signature")
9798
//! .write(&mut buffer)
9899
//! .unwrap();
@@ -119,7 +120,7 @@ use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures, InvoiceRequ
119120
use crate::ln::inbound_payment::ExpandedKey;
120121
use crate::ln::msgs::DecodeError;
121122
use crate::offers::invoice_request::{INVOICE_REQUEST_PAYER_ID_TYPE, INVOICE_REQUEST_TYPES, IV_BYTES as INVOICE_REQUEST_IV_BYTES, InvoiceRequest, InvoiceRequestContents, InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef};
122-
use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, WithoutSignatures, self};
123+
use crate::offers::merkle::{SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, WithoutSignatures, self};
123124
use crate::offers::offer::{Amount, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity};
124125
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
125126
use crate::offers::payer::{PAYER_METADATA_TYPE, PayerTlvStream, PayerTlvStreamRef};
@@ -324,9 +325,9 @@ macro_rules! invoice_derived_signing_pubkey_builder_methods { ($self: ident, $se
324325
let mut unsigned_invoice = UnsignedBolt12Invoice::new(invreq_bytes, invoice.clone());
325326

326327
let invoice = unsigned_invoice
327-
.sign::<_, Infallible>(
328-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
329-
)
328+
.sign(|message: &UnsignedBolt12Invoice| -> Result<_, Infallible> {
329+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
330+
})
330331
.unwrap();
331332
Ok(invoice)
332333
}
@@ -507,6 +508,37 @@ pub struct UnsignedBolt12Invoice {
507508
tagged_hash: TaggedHash,
508509
}
509510

511+
/// A function for signing an [`UnsignedBolt12Invoice`].
512+
pub trait SignBolt12InvoiceFn {
513+
/// Error type returned by the function.
514+
type Error;
515+
516+
/// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream.
517+
fn sign_invoice(&self, message: &UnsignedBolt12Invoice) -> Result<Signature, Self::Error>;
518+
}
519+
520+
impl<F, E> SignBolt12InvoiceFn for F
521+
where
522+
F: Fn(&UnsignedBolt12Invoice) -> Result<Signature, E>,
523+
{
524+
type Error = E;
525+
526+
fn sign_invoice(&self, message: &UnsignedBolt12Invoice) -> Result<Signature, E> {
527+
self(message)
528+
}
529+
}
530+
531+
impl<F, E> SignFn<UnsignedBolt12Invoice> for F
532+
where
533+
F: SignBolt12InvoiceFn<Error = E>,
534+
{
535+
type Error = E;
536+
537+
fn sign(&self, message: &UnsignedBolt12Invoice) -> Result<Signature, Self::Error> {
538+
self.sign_invoice(message)
539+
}
540+
}
541+
510542
impl UnsignedBolt12Invoice {
511543
fn new(invreq_bytes: &[u8], contents: InvoiceContents) -> Self {
512544
// Use the invoice_request bytes instead of the invoice_request TLV stream as the latter may
@@ -534,12 +566,9 @@ macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $s
534566
/// Signs the [`TaggedHash`] of the invoice using the given function.
535567
///
536568
/// Note: The hash computation may have included unknown, odd TLV records.
537-
///
538-
/// This is not exported to bindings users as functions aren't currently mapped.
539-
pub fn sign<F, E>($($self_mut)* $self: $self_type, sign: F) -> Result<Bolt12Invoice, SignError<E>>
540-
where
541-
F: FnOnce(&Self) -> Result<Signature, E>
542-
{
569+
pub fn sign<F: SignBolt12InvoiceFn>(
570+
$($self_mut)* $self: $self_type, sign: F
571+
) -> Result<Bolt12Invoice, SignError<F::Error>> {
543572
let pubkey = $self.contents.fields().signing_pubkey;
544573
let signature = merkle::sign_message(sign, &$self, pubkey)?;
545574

@@ -2013,7 +2042,7 @@ mod tests {
20132042
.sign(payer_sign).unwrap()
20142043
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
20152044
.build().unwrap()
2016-
.sign(|_| Err(()))
2045+
.sign(fail_sign)
20172046
{
20182047
Ok(_) => panic!("expected error"),
20192048
Err(e) => assert_eq!(e, SignError::Signing(())),

lightning/src/offers/invoice_request.rs

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
//! use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
2828
//! use core::convert::Infallible;
2929
//! use lightning::ln::features::OfferFeatures;
30+
//! use lightning::offers::invoice_request::UnsignedInvoiceRequest;
3031
//! use lightning::offers::offer::Offer;
3132
//! use lightning::util::ser::Writeable;
3233
//!
@@ -47,9 +48,9 @@
4748
//! .quantity(5)?
4849
//! .payer_note("foo".to_string())
4950
//! .build()?
50-
//! .sign::<_, Infallible>(
51-
//! |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
52-
//! )
51+
//! .sign(|message: &UnsignedInvoiceRequest| -> Result<_, Infallible> {
52+
//! Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
53+
//! })
5354
//! .expect("failed verifying signature")
5455
//! .write(&mut buffer)
5556
//! .unwrap();
@@ -72,7 +73,7 @@ use crate::ln::features::InvoiceRequestFeatures;
7273
use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
7374
use crate::ln::msgs::DecodeError;
7475
use crate::offers::invoice::BlindedPayInfo;
75-
use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, self};
76+
use crate::offers::merkle::{SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, self};
7677
use crate::offers::offer::{Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef};
7778
use crate::offers::parse::{Bolt12ParseError, ParsedMessage, Bolt12SemanticError};
7879
use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef};
@@ -227,9 +228,9 @@ macro_rules! invoice_request_derived_payer_id_builder_methods { (
227228
let secp_ctx = secp_ctx.unwrap();
228229
let keys = keys.unwrap();
229230
let invoice_request = unsigned_invoice_request
230-
.sign::<_, Infallible>(
231-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
232-
)
231+
.sign(|message: &UnsignedInvoiceRequest| -> Result<_, Infallible> {
232+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
233+
})
233234
.unwrap();
234235
Ok(invoice_request)
235236
}
@@ -493,6 +494,37 @@ pub struct UnsignedInvoiceRequest {
493494
tagged_hash: TaggedHash,
494495
}
495496

497+
/// A function for signing an [`UnsignedInvoiceRequest`].
498+
pub trait SignInvoiceRequestFn {
499+
/// Error type returned by the function.
500+
type Error;
501+
502+
/// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream.
503+
fn sign_invoice_request(&self, message: &UnsignedInvoiceRequest) -> Result<Signature, Self::Error>;
504+
}
505+
506+
impl<F, E> SignInvoiceRequestFn for F
507+
where
508+
F: Fn(&UnsignedInvoiceRequest) -> Result<Signature, E>,
509+
{
510+
type Error = E;
511+
512+
fn sign_invoice_request(&self, message: &UnsignedInvoiceRequest) -> Result<Signature, E> {
513+
self(message)
514+
}
515+
}
516+
517+
impl<F, E> SignFn<UnsignedInvoiceRequest> for F
518+
where
519+
F: SignInvoiceRequestFn<Error = E>,
520+
{
521+
type Error = E;
522+
523+
fn sign(&self, message: &UnsignedInvoiceRequest) -> Result<Signature, Self::Error> {
524+
self.sign_invoice_request(message)
525+
}
526+
}
527+
496528
impl UnsignedInvoiceRequest {
497529
fn new(offer: &Offer, contents: InvoiceRequestContents) -> Self {
498530
// Use the offer bytes instead of the offer TLV stream as the offer may have contained
@@ -522,12 +554,9 @@ macro_rules! unsigned_invoice_request_sign_method { (
522554
/// Signs the [`TaggedHash`] of the invoice request using the given function.
523555
///
524556
/// Note: The hash computation may have included unknown, odd TLV records.
525-
///
526-
/// This is not exported to bindings users as functions are not yet mapped.
527-
pub fn sign<F, E>($($self_mut)* $self: $self_type, sign: F) -> Result<InvoiceRequest, SignError<E>>
528-
where
529-
F: FnOnce(&Self) -> Result<Signature, E>
530-
{
557+
pub fn sign<F: SignInvoiceRequestFn>(
558+
$($self_mut)* $self: $self_type, sign: F
559+
) -> Result<InvoiceRequest, SignError<F::Error>> {
531560
let pubkey = $self.contents.payer_id;
532561
let signature = merkle::sign_message(sign, &$self, pubkey)?;
533562

@@ -1712,7 +1741,7 @@ mod tests {
17121741
.build().unwrap()
17131742
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
17141743
.build().unwrap()
1715-
.sign(|_| Err(()))
1744+
.sign(fail_sign)
17161745
{
17171746
Ok(_) => panic!("expected error"),
17181747
Err(e) => assert_eq!(e, SignError::Signing(())),
@@ -2126,9 +2155,9 @@ mod tests {
21262155
.build().unwrap()
21272156
.request_invoice(vec![1; 32], keys.public_key()).unwrap()
21282157
.build().unwrap()
2129-
.sign::<_, Infallible>(
2130-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
2131-
)
2158+
.sign(|message: &UnsignedInvoiceRequest| -> Result<_, Infallible> {
2159+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
2160+
})
21322161
.unwrap();
21332162

21342163
let mut encoded_invoice_request = Vec::new();

0 commit comments

Comments
 (0)