Skip to content

Commit 03700bc

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 9935a6f commit 03700bc

File tree

8 files changed

+88
-59
lines changed

8 files changed

+88
-59
lines changed

fuzz/src/invoice_request_deser.rs

+6-6
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

+3-3
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

+3-3
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

+4-2
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

+16-15
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, SignFunction, 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
}
@@ -534,11 +535,11 @@ macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $s
534535
/// Signs the [`TaggedHash`] of the invoice using the given function.
535536
///
536537
/// 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>>
538+
pub fn sign<F>(
539+
$($self_mut)* $self: $self_type, sign: F
540+
) -> Result<Bolt12Invoice, SignError<F::Error>>
540541
where
541-
F: FnOnce(&Self) -> Result<Signature, E>
542+
F: SignFunction<Self>,
542543
{
543544
let pubkey = $self.contents.fields().signing_pubkey;
544545
let signature = merkle::sign_message(sign, &$self, pubkey)?;
@@ -2013,7 +2014,7 @@ mod tests {
20132014
.sign(payer_sign).unwrap()
20142015
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
20152016
.build().unwrap()
2016-
.sign(|_| Err(()))
2017+
.sign(fail_sign)
20172018
{
20182019
Ok(_) => panic!("expected error"),
20192020
Err(e) => assert_eq!(e, SignError::Signing(())),

lightning/src/offers/invoice_request.rs

+16-15
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, SignFunction, 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
}
@@ -522,11 +523,11 @@ macro_rules! unsigned_invoice_request_sign_method { (
522523
/// Signs the [`TaggedHash`] of the invoice request using the given function.
523524
///
524525
/// 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>>
526+
pub fn sign<F>(
527+
$($self_mut)* $self: $self_type, sign: F
528+
) -> Result<InvoiceRequest, SignError<F::Error>>
528529
where
529-
F: FnOnce(&Self) -> Result<Signature, E>
530+
F: SignFunction<Self>,
530531
{
531532
let pubkey = $self.contents.payer_id;
532533
let signature = merkle::sign_message(sign, &$self, pubkey)?;
@@ -1712,7 +1713,7 @@ mod tests {
17121713
.build().unwrap()
17131714
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
17141715
.build().unwrap()
1715-
.sign(|_| Err(()))
1716+
.sign(fail_sign)
17161717
{
17171718
Ok(_) => panic!("expected error"),
17181719
Err(e) => assert_eq!(e, SignError::Signing(())),
@@ -2126,9 +2127,9 @@ mod tests {
21262127
.build().unwrap()
21272128
.request_invoice(vec![1; 32], keys.public_key()).unwrap()
21282129
.build().unwrap()
2129-
.sign::<_, Infallible>(
2130-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
2131-
)
2130+
.sign(|message: &UnsignedInvoiceRequest| -> Result<_, Infallible> {
2131+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
2132+
})
21322133
.unwrap();
21332134

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

lightning/src/offers/merkle.rs

+36-15
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,34 @@ impl AsRef<TaggedHash> for TaggedHash {
7676

7777
/// Error when signing messages.
7878
#[derive(Debug, PartialEq)]
79-
pub enum SignError<E> {
79+
pub enum SignError<E = ()> {
8080
/// User-defined error when signing the message.
8181
Signing(E),
8282
/// Error when verifying the produced signature using the given pubkey.
8383
Verification(secp256k1::Error),
8484
}
8585

86+
/// A function for signing a [`TaggedHash`].
87+
pub trait SignFunction<T: AsRef<TaggedHash>> {
88+
/// Error type returned by the function.
89+
type Error;
90+
91+
/// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream.
92+
fn sign(&self, message: &T) -> Result<Signature, Self::Error>;
93+
}
94+
95+
impl<F, T, E> SignFunction<T> for F
96+
where
97+
F: Fn(&T) -> Result<Signature, E>,
98+
T: AsRef<TaggedHash>,
99+
{
100+
type Error = E;
101+
102+
fn sign(&self, message: &T) -> Result<Signature, E> {
103+
self(message)
104+
}
105+
}
106+
86107
/// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream, checking if it
87108
/// can be verified with the supplied `pubkey`.
88109
///
@@ -92,14 +113,14 @@ pub enum SignError<E> {
92113
///
93114
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
94115
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
95-
pub(super) fn sign_message<F, E, T>(
96-
sign: F, message: &T, pubkey: PublicKey,
116+
pub(super) fn sign_message<F, T, E>(
117+
f: F, message: &T, pubkey: PublicKey,
97118
) -> Result<Signature, SignError<E>>
98119
where
99-
F: FnOnce(&T) -> Result<Signature, E>,
120+
F: SignFunction<T, Error = E>,
100121
T: AsRef<TaggedHash>,
101122
{
102-
let signature = sign(message).map_err(|e| SignError::Signing(e))?;
123+
let signature = f.sign(message).map_err(|e| SignError::Signing(e))?;
103124

104125
let digest = message.as_ref().as_digest();
105126
let pubkey = pubkey.into();
@@ -278,7 +299,7 @@ mod tests {
278299
use bitcoin::secp256k1::schnorr::Signature;
279300
use core::convert::Infallible;
280301
use crate::offers::offer::{Amount, OfferBuilder};
281-
use crate::offers::invoice_request::InvoiceRequest;
302+
use crate::offers::invoice_request::{InvoiceRequest, UnsignedInvoiceRequest};
282303
use crate::offers::parse::Bech32Encode;
283304
use crate::offers::test_utils::{payer_pubkey, recipient_pubkey};
284305
use crate::util::ser::Writeable;
@@ -321,9 +342,9 @@ mod tests {
321342
.build_unchecked()
322343
.request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
323344
.build_unchecked()
324-
.sign::<_, Infallible>(
325-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
326-
)
345+
.sign(|message: &UnsignedInvoiceRequest| -> Result<_, Infallible> {
346+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
347+
})
327348
.unwrap();
328349
assert_eq!(
329350
invoice_request.to_string(),
@@ -375,9 +396,9 @@ mod tests {
375396
.build_unchecked()
376397
.request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
377398
.build_unchecked()
378-
.sign::<_, Infallible>(
379-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
380-
)
399+
.sign(|message: &UnsignedInvoiceRequest| -> Result<_, Infallible> {
400+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
401+
})
381402
.unwrap();
382403

383404
let mut bytes_without_signature = Vec::new();
@@ -407,9 +428,9 @@ mod tests {
407428
.build_unchecked()
408429
.request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
409430
.build_unchecked()
410-
.sign::<_, Infallible>(
411-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
412-
)
431+
.sign(|message: &UnsignedInvoiceRequest| -> Result<_, Infallible> {
432+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
433+
})
413434
.unwrap();
414435

415436
let tlv_stream = TlvStream::new(&invoice_request.bytes).range(0..1)

lightning/src/offers/test_utils.rs

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ use crate::ln::features::BlindedHopFeatures;
2020
use crate::offers::invoice::BlindedPayInfo;
2121
use crate::offers::merkle::TaggedHash;
2222

23+
pub(crate) fn fail_sign<T: AsRef<TaggedHash>>(_message: &T) -> Result<Signature, ()> {
24+
Err(())
25+
}
26+
2327
pub(crate) fn payer_keys() -> KeyPair {
2428
let secp_ctx = Secp256k1::new();
2529
KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap())

0 commit comments

Comments
 (0)