Skip to content

Commit ac6ca72

Browse files
committed
Support signing BOLT 12 invoices in NodeSigner
BOLT 12 messages need to be signed in the following scenarios: - constructing an InvoiceRequest after scanning an Offer, - constructing an Invoice after scanning a Refund, and - constructing an Invoice when handling an InvoiceRequest. Extend the NodeSigner trait to support signing BOLT 12 invoices such that it can be used in the latter contexts. The method could be used in an OffersMessageHandler.
1 parent c7c8ef8 commit ac6ca72

File tree

6 files changed

+75
-1
lines changed

6 files changed

+75
-1
lines changed

fuzz/src/chanmon_consistency.rs

+8
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use lightning::ln::channel::FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
4444
use lightning::ln::msgs::{self, CommitmentUpdate, ChannelMessageHandler, DecodeError, UpdateAddHTLC, Init};
4545
use lightning::ln::script::ShutdownScript;
4646
use lightning::ln::functional_test_utils::*;
47+
use lightning::offers::invoice::UnsignedBolt12Invoice;
4748
use lightning::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
4849
use lightning::util::errors::APIError;
4950
use lightning::util::logger::Logger;
@@ -57,6 +58,7 @@ use crate::utils::test_persister::TestPersister;
5758
use bitcoin::secp256k1::{Message, PublicKey, SecretKey, Scalar, Secp256k1};
5859
use bitcoin::secp256k1::ecdh::SharedSecret;
5960
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
61+
use bitcoin::secp256k1::schnorr;
6062

6163
use std::mem;
6264
use std::cmp::{self, Ordering};
@@ -211,6 +213,12 @@ impl NodeSigner for KeyProvider {
211213
unreachable!()
212214
}
213215

216+
fn sign_bolt12_invoice(
217+
&self, _invoice: &UnsignedBolt12Invoice,
218+
) -> Result<schnorr::Signature, ()> {
219+
unreachable!()
220+
}
221+
214222
fn sign_gossip_message(&self, msg: lightning::ln::msgs::UnsignedGossipMessage) -> Result<Signature, ()> {
215223
let msg_hash = Message::from_slice(&Sha256dHash::hash(&msg.encode()[..])[..]).map_err(|_| ())?;
216224
let secp_ctx = Secp256k1::signing_only();

fuzz/src/full_stack.rs

+8
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor,Ig
4040
use lightning::ln::msgs::{self, DecodeError};
4141
use lightning::ln::script::ShutdownScript;
4242
use lightning::ln::functional_test_utils::*;
43+
use lightning::offers::invoice::UnsignedBolt12Invoice;
4344
use lightning::routing::gossip::{P2PGossipSync, NetworkGraph};
4445
use lightning::routing::utxo::UtxoLookup;
4546
use lightning::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteParameters, Router};
@@ -55,6 +56,7 @@ use crate::utils::test_persister::TestPersister;
5556
use bitcoin::secp256k1::{Message, PublicKey, SecretKey, Scalar, Secp256k1};
5657
use bitcoin::secp256k1::ecdh::SharedSecret;
5758
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
59+
use bitcoin::secp256k1::schnorr;
5860

5961
use std::cell::RefCell;
6062
use hashbrown::{HashMap, hash_map};
@@ -316,6 +318,12 @@ impl NodeSigner for KeyProvider {
316318
unreachable!()
317319
}
318320

321+
fn sign_bolt12_invoice(
322+
&self, _invoice: &UnsignedBolt12Invoice,
323+
) -> Result<schnorr::Signature, ()> {
324+
unreachable!()
325+
}
326+
319327
fn sign_gossip_message(&self, msg: lightning::ln::msgs::UnsignedGossipMessage) -> Result<Signature, ()> {
320328
let msg_hash = Message::from_slice(&Sha256dHash::hash(&msg.encode()[..])[..]).map_err(|_| ())?;
321329
let secp_ctx = Secp256k1::signing_only();

fuzz/src/onion_message.rs

+8
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ use bitcoin::blockdata::script::Script;
44
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};
55
use bitcoin::secp256k1::ecdh::SharedSecret;
66
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
7+
use bitcoin::secp256k1::schnorr;
78

89
use lightning::sign::{Recipient, KeyMaterial, EntropySource, NodeSigner, SignerProvider};
910
use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler};
1011
use lightning::ln::script::ShutdownScript;
12+
use lightning::offers::invoice::UnsignedBolt12Invoice;
1113
use lightning::util::enforcing_trait_impls::EnforcingSigner;
1214
use lightning::util::logger::Logger;
1315
use lightning::util::ser::{Readable, Writeable, Writer};
@@ -153,6 +155,12 @@ impl NodeSigner for KeyProvider {
153155
unreachable!()
154156
}
155157

158+
fn sign_bolt12_invoice(
159+
&self, _invoice: &UnsignedBolt12Invoice,
160+
) -> Result<schnorr::Signature, ()> {
161+
unreachable!()
162+
}
163+
156164
fn sign_gossip_message(&self, _msg: lightning::ln::msgs::UnsignedGossipMessage) -> Result<bitcoin::secp256k1::ecdsa::Signature, ()> {
157165
unreachable!()
158166
}

lightning/src/offers/invoice.rs

+5
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,11 @@ impl UnsignedBolt12Invoice {
401401
self.invoice.fields().signing_pubkey
402402
}
403403

404+
/// Returns the [`TaggedHash`] of the invoice to sign.
405+
pub fn tagged_hash(&self) -> &TaggedHash {
406+
&self.tagged_hash
407+
}
408+
404409
/// Signs the invoice using the given function.
405410
///
406411
/// This is not exported to bindings users as functions aren't currently mapped.

lightning/src/sign/mod.rs

+32-1
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ use bitcoin::hashes::sha256::Hash as Sha256;
2626
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
2727
use bitcoin::hash_types::WPubkeyHash;
2828

29-
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, Signing};
29+
use bitcoin::secp256k1::{KeyPair, PublicKey, Scalar, Secp256k1, SecretKey, Signing};
3030
use bitcoin::secp256k1::ecdh::SharedSecret;
3131
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
32+
use bitcoin::secp256k1::schnorr;
3233
use bitcoin::{PackedLockTime, secp256k1, Sequence, Witness};
3334

3435
use crate::util::transaction_utils;
@@ -41,6 +42,7 @@ use crate::ln::{chan_utils, PaymentPreimage};
4142
use crate::ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction};
4243
use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage};
4344
use crate::ln::script::ShutdownScript;
45+
use crate::offers::invoice::UnsignedBolt12Invoice;
4446

4547
use crate::prelude::*;
4648
use core::convert::TryInto;
@@ -619,6 +621,20 @@ pub trait NodeSigner {
619621
/// Errors if the [`Recipient`] variant is not supported by the implementation.
620622
fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()>;
621623

624+
/// Signs the [`TaggedHash`] of a BOLT 12 invoice.
625+
///
626+
/// May be called by a function passed to [`UnsignedBolt12Invoice::sign`] where `invoice` is the
627+
/// callee.
628+
///
629+
/// Implementors may check that the `invoice` is expected rather than blindly signing the tagged
630+
/// hash. An `Ok` result should sign `invoice.tagged_hash().as_digest()` with the node's signing
631+
/// key.
632+
///
633+
/// [`TaggedHash`]: crate::offers::merkle::TaggedHash
634+
fn sign_bolt12_invoice(
635+
&self, invoice: &UnsignedBolt12Invoice
636+
) -> Result<schnorr::Signature, ()>;
637+
622638
/// Sign a gossip message.
623639
///
624640
/// Note that if this fails, LDK may panic and the message will not be broadcast to the network
@@ -1449,6 +1465,15 @@ impl NodeSigner for KeysManager {
14491465
Ok(self.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), secret))
14501466
}
14511467

1468+
fn sign_bolt12_invoice(
1469+
&self, invoice: &UnsignedBolt12Invoice
1470+
) -> Result<schnorr::Signature, ()> {
1471+
let message = invoice.tagged_hash().as_digest();
1472+
let keys = KeyPair::from_secret_key(&self.secp_ctx, &self.node_secret);
1473+
let aux_rand = self.get_secure_random_bytes();
1474+
Ok(self.secp_ctx.sign_schnorr_with_aux_rand(message, &keys, &aux_rand))
1475+
}
1476+
14521477
fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
14531478
let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
14541479
Ok(self.secp_ctx.sign_ecdsa(&msg_hash, &self.node_secret))
@@ -1557,6 +1582,12 @@ impl NodeSigner for PhantomKeysManager {
15571582
Ok(self.inner.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), secret))
15581583
}
15591584

1585+
fn sign_bolt12_invoice(
1586+
&self, invoice: &UnsignedBolt12Invoice
1587+
) -> Result<schnorr::Signature, ()> {
1588+
self.inner.sign_bolt12_invoice(invoice)
1589+
}
1590+
15601591
fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
15611592
self.inner.sign_gossip_message(msg)
15621593
}

lightning/src/util/test_utils.rs

+14
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use crate::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
2424
use crate::ln::{msgs, wire};
2525
use crate::ln::msgs::LightningError;
2626
use crate::ln::script::ShutdownScript;
27+
use crate::offers::invoice::UnsignedBolt12Invoice;
2728
use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId};
2829
use crate::routing::utxo::{UtxoLookup, UtxoLookupError, UtxoResult};
2930
use crate::routing::router::{find_route, InFlightHtlcs, Path, Route, RouteParameters, Router, ScorerAccountingForInFlightHtlcs};
@@ -47,6 +48,7 @@ use bitcoin::util::sighash::SighashCache;
4748
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};
4849
use bitcoin::secp256k1::ecdh::SharedSecret;
4950
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
51+
use bitcoin::secp256k1::schnorr;
5052

5153
#[cfg(any(test, feature = "_test_utils"))]
5254
use regex;
@@ -800,6 +802,12 @@ impl NodeSigner for TestNodeSigner {
800802
unreachable!()
801803
}
802804

805+
fn sign_bolt12_invoice(
806+
&self, _invoice: &UnsignedBolt12Invoice,
807+
) -> Result<schnorr::Signature, ()> {
808+
unreachable!()
809+
}
810+
803811
fn sign_gossip_message(&self, _msg: msgs::UnsignedGossipMessage) -> Result<Signature, ()> {
804812
unreachable!()
805813
}
@@ -840,6 +848,12 @@ impl NodeSigner for TestKeysInterface {
840848
self.backing.sign_invoice(hrp_bytes, invoice_data, recipient)
841849
}
842850

851+
fn sign_bolt12_invoice(
852+
&self, invoice: &UnsignedBolt12Invoice,
853+
) -> Result<schnorr::Signature, ()> {
854+
self.backing.sign_bolt12_invoice(invoice)
855+
}
856+
843857
fn sign_gossip_message(&self, msg: msgs::UnsignedGossipMessage) -> Result<Signature, ()> {
844858
self.backing.sign_gossip_message(msg)
845859
}

0 commit comments

Comments
 (0)