Skip to content

Commit 0773209

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 b4d11b7 commit 0773209

File tree

8 files changed

+63
-43
lines changed

8 files changed

+63
-43
lines changed

fuzz/src/invoice_request_deser.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,16 @@ 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))
40+
.sign::<_, Infallible>(|message: &UnsignedBolt12Invoice|
41+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
4242
)
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))
48+
.sign::<_, Infallible>(|message: &UnsignedBolt12Invoice|
49+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
5050
)
5151
.unwrap_err();
5252
}

fuzz/src/offer_deser.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ 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))
32+
.sign::<_, Infallible>(|message: &UnsignedInvoiceRequest|
33+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
3434
)
3535
.unwrap()
3636
.write(&mut buffer)

fuzz/src/refund_deser.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ 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))
36+
.sign::<_, Infallible>(|message: &UnsignedBolt12Invoice|
37+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
3838
)
3939
.unwrap()
4040
.write(&mut buffer)

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

+10-11
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,8 +58,8 @@
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))
61+
//! .sign::<_, Infallible>(|message: &UnsignedBolt12Invoice|
62+
//! Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
6263
//! )
6364
//! .expect("failed verifying signature")
6465
//! .write(&mut buffer)
@@ -90,8 +91,8 @@
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))
94+
//! .sign::<_, Infallible>(|message: &UnsignedBolt12Invoice|
95+
//! Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
9596
//! )
9697
//! .expect("failed verifying signature")
9798
//! .write(&mut buffer)
@@ -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};
@@ -320,8 +321,8 @@ macro_rules! invoice_derived_signing_pubkey_builder_methods { ($self: ident, $se
320321
let mut unsigned_invoice = UnsignedBolt12Invoice::new(invreq_bytes, invoice.clone());
321322

322323
let invoice = unsigned_invoice
323-
.sign::<_, Infallible>(
324-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
324+
.sign::<_, Infallible>(|message: &UnsignedBolt12Invoice|
325+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
325326
)
326327
.unwrap();
327328
Ok(invoice)
@@ -528,11 +529,9 @@ macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $s
528529
/// Signs the [`TaggedHash`] of the invoice using the given function.
529530
///
530531
/// Note: The hash computation may have included unknown, odd TLV records.
531-
///
532-
/// This is not exported to bindings users as functions aren't currently mapped.
533532
pub fn sign<F, E>($($self_mut)* $self: $self_type, sign: F) -> Result<Bolt12Invoice, SignError<E>>
534533
where
535-
F: FnOnce(&Self) -> Result<Signature, E>
534+
F: SignFunction<Self, E>,
536535
{
537536
let pubkey = $self.contents.fields().signing_pubkey;
538537
let signature = merkle::sign_message(sign, &$self, pubkey)?;
@@ -2007,7 +2006,7 @@ mod tests {
20072006
.sign(payer_sign).unwrap()
20082007
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
20092008
.build().unwrap()
2010-
.sign(|_| Err(()))
2009+
.sign(fail_sign)
20112010
{
20122011
Ok(_) => panic!("expected error"),
20132012
Err(e) => assert_eq!(e, SignError::Signing(())),

lightning/src/offers/invoice_request.rs

+10-11
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,8 +48,8 @@
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))
51+
//! .sign::<_, Infallible>(|message: &UnsignedInvoiceRequest|
52+
//! Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
5253
//! )
5354
//! .expect("failed verifying signature")
5455
//! .write(&mut buffer)
@@ -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};
@@ -224,8 +225,8 @@ macro_rules! invoice_request_derived_payer_id_builder_methods { (
224225
let secp_ctx = secp_ctx.unwrap();
225226
let keys = keys.unwrap();
226227
let invoice_request = unsigned_invoice_request
227-
.sign::<_, Infallible>(
228-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
228+
.sign::<_, Infallible>(|message: &UnsignedInvoiceRequest|
229+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
229230
)
230231
.unwrap();
231232
Ok(invoice_request)
@@ -513,11 +514,9 @@ macro_rules! unsigned_invoice_request_sign_method { (
513514
/// Signs the [`TaggedHash`] of the invoice request using the given function.
514515
///
515516
/// Note: The hash computation may have included unknown, odd TLV records.
516-
///
517-
/// This is not exported to bindings users as functions are not yet mapped.
518517
pub fn sign<F, E>($($self_mut)* $self: $self_type, sign: F) -> Result<InvoiceRequest, SignError<E>>
519518
where
520-
F: FnOnce(&Self) -> Result<Signature, E>
519+
F: SignFunction<Self, E>,
521520
{
522521
let pubkey = $self.contents.payer_id;
523522
let signature = merkle::sign_message(sign, &$self, pubkey)?;
@@ -1703,7 +1702,7 @@ mod tests {
17031702
.build().unwrap()
17041703
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
17051704
.build().unwrap()
1706-
.sign(|_| Err(()))
1705+
.sign(fail_sign)
17071706
{
17081707
Ok(_) => panic!("expected error"),
17091708
Err(e) => assert_eq!(e, SignError::Signing(())),
@@ -2117,8 +2116,8 @@ mod tests {
21172116
.build().unwrap()
21182117
.request_invoice(vec![1; 32], keys.public_key()).unwrap()
21192118
.build().unwrap()
2120-
.sign::<_, Infallible>(
2121-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
2119+
.sign::<_, Infallible>(|message: &UnsignedInvoiceRequest|
2120+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
21222121
)
21232122
.unwrap();
21242123

lightning/src/offers/merkle.rs

+27-11
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,22 @@ pub enum SignError<E> {
8383
Verification(secp256k1::Error),
8484
}
8585

86+
/// A function for signing a [`TaggedHash`].
87+
pub trait SignFunction<T: AsRef<TaggedHash>, E> {
88+
/// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream.
89+
fn sign(&self, message: &T) -> Result<Signature, E>;
90+
}
91+
92+
impl<F, T, E> SignFunction<T, E> for F
93+
where
94+
F: Fn(&T) -> Result<Signature, E>,
95+
T: AsRef<TaggedHash>,
96+
{
97+
fn sign(&self, message: &T) -> Result<Signature, E> {
98+
self(message)
99+
}
100+
}
101+
86102
/// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream, checking if it
87103
/// can be verified with the supplied `pubkey`.
88104
///
@@ -92,14 +108,14 @@ pub enum SignError<E> {
92108
///
93109
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
94110
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
95-
pub(super) fn sign_message<F, E, T>(
96-
sign: F, message: &T, pubkey: PublicKey,
111+
pub(super) fn sign_message<F, T, E>(
112+
f: F, message: &T, pubkey: PublicKey,
97113
) -> Result<Signature, SignError<E>>
98114
where
99-
F: FnOnce(&T) -> Result<Signature, E>,
115+
F: SignFunction<T, E>,
100116
T: AsRef<TaggedHash>,
101117
{
102-
let signature = sign(message).map_err(|e| SignError::Signing(e))?;
118+
let signature = f.sign(message).map_err(|e| SignError::Signing(e))?;
103119

104120
let digest = message.as_ref().as_digest();
105121
let pubkey = pubkey.into();
@@ -278,7 +294,7 @@ mod tests {
278294
use bitcoin::secp256k1::schnorr::Signature;
279295
use core::convert::Infallible;
280296
use crate::offers::offer::{Amount, OfferBuilder};
281-
use crate::offers::invoice_request::InvoiceRequest;
297+
use crate::offers::invoice_request::{InvoiceRequest, UnsignedInvoiceRequest};
282298
use crate::offers::parse::Bech32Encode;
283299
use crate::offers::test_utils::{payer_pubkey, recipient_pubkey};
284300
use crate::util::ser::Writeable;
@@ -321,8 +337,8 @@ mod tests {
321337
.build_unchecked()
322338
.request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
323339
.build_unchecked()
324-
.sign::<_, Infallible>(
325-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
340+
.sign::<_, Infallible>(|message: &UnsignedInvoiceRequest|
341+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
326342
)
327343
.unwrap();
328344
assert_eq!(
@@ -375,8 +391,8 @@ mod tests {
375391
.build_unchecked()
376392
.request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
377393
.build_unchecked()
378-
.sign::<_, Infallible>(
379-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
394+
.sign::<_, Infallible>(|message: &UnsignedInvoiceRequest|
395+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
380396
)
381397
.unwrap();
382398

@@ -407,8 +423,8 @@ mod tests {
407423
.build_unchecked()
408424
.request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
409425
.build_unchecked()
410-
.sign::<_, Infallible>(
411-
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
426+
.sign::<_, Infallible>(|message: &UnsignedInvoiceRequest|
427+
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
412428
)
413429
.unwrap();
414430

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)