mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-03-15 15:39:09 +01:00
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.
This commit is contained in:
parent
63d0d5583d
commit
39012e3595
7 changed files with 146 additions and 1 deletions
|
@ -44,6 +44,8 @@ use lightning::ln::channel::FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
|
|||
use lightning::ln::msgs::{self, CommitmentUpdate, ChannelMessageHandler, DecodeError, UpdateAddHTLC, Init};
|
||||
use lightning::ln::script::ShutdownScript;
|
||||
use lightning::ln::functional_test_utils::*;
|
||||
use lightning::offers::invoice::UnsignedBolt12Invoice;
|
||||
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
|
||||
use lightning::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
|
||||
use lightning::util::errors::APIError;
|
||||
use lightning::util::logger::Logger;
|
||||
|
@ -57,6 +59,7 @@ use crate::utils::test_persister::TestPersister;
|
|||
use bitcoin::secp256k1::{Message, PublicKey, SecretKey, Scalar, Secp256k1};
|
||||
use bitcoin::secp256k1::ecdh::SharedSecret;
|
||||
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
|
||||
use bitcoin::secp256k1::schnorr;
|
||||
|
||||
use std::mem;
|
||||
use std::cmp::{self, Ordering};
|
||||
|
@ -211,6 +214,18 @@ impl NodeSigner for KeyProvider {
|
|||
unreachable!()
|
||||
}
|
||||
|
||||
fn sign_bolt12_invoice_request(
|
||||
&self, _invoice_request: &UnsignedInvoiceRequest
|
||||
) -> Result<schnorr::Signature, ()> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn sign_bolt12_invoice(
|
||||
&self, _invoice: &UnsignedBolt12Invoice,
|
||||
) -> Result<schnorr::Signature, ()> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn sign_gossip_message(&self, msg: lightning::ln::msgs::UnsignedGossipMessage) -> Result<Signature, ()> {
|
||||
let msg_hash = Message::from_slice(&Sha256dHash::hash(&msg.encode()[..])[..]).map_err(|_| ())?;
|
||||
let secp_ctx = Secp256k1::signing_only();
|
||||
|
|
|
@ -40,6 +40,8 @@ use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor,Ig
|
|||
use lightning::ln::msgs::{self, DecodeError};
|
||||
use lightning::ln::script::ShutdownScript;
|
||||
use lightning::ln::functional_test_utils::*;
|
||||
use lightning::offers::invoice::UnsignedBolt12Invoice;
|
||||
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
|
||||
use lightning::routing::gossip::{P2PGossipSync, NetworkGraph};
|
||||
use lightning::routing::utxo::UtxoLookup;
|
||||
use lightning::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteParameters, Router};
|
||||
|
@ -55,6 +57,7 @@ use crate::utils::test_persister::TestPersister;
|
|||
use bitcoin::secp256k1::{Message, PublicKey, SecretKey, Scalar, Secp256k1};
|
||||
use bitcoin::secp256k1::ecdh::SharedSecret;
|
||||
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
|
||||
use bitcoin::secp256k1::schnorr;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use hashbrown::{HashMap, hash_map};
|
||||
|
@ -316,6 +319,18 @@ impl NodeSigner for KeyProvider {
|
|||
unreachable!()
|
||||
}
|
||||
|
||||
fn sign_bolt12_invoice_request(
|
||||
&self, _invoice_request: &UnsignedInvoiceRequest
|
||||
) -> Result<schnorr::Signature, ()> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn sign_bolt12_invoice(
|
||||
&self, _invoice: &UnsignedBolt12Invoice,
|
||||
) -> Result<schnorr::Signature, ()> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn sign_gossip_message(&self, msg: lightning::ln::msgs::UnsignedGossipMessage) -> Result<Signature, ()> {
|
||||
let msg_hash = Message::from_slice(&Sha256dHash::hash(&msg.encode()[..])[..]).map_err(|_| ())?;
|
||||
let secp_ctx = Secp256k1::signing_only();
|
||||
|
|
|
@ -4,10 +4,13 @@ use bitcoin::blockdata::script::Script;
|
|||
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};
|
||||
use bitcoin::secp256k1::ecdh::SharedSecret;
|
||||
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
|
||||
use bitcoin::secp256k1::schnorr;
|
||||
|
||||
use lightning::sign::{Recipient, KeyMaterial, EntropySource, NodeSigner, SignerProvider};
|
||||
use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler};
|
||||
use lightning::ln::script::ShutdownScript;
|
||||
use lightning::offers::invoice::UnsignedBolt12Invoice;
|
||||
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
|
||||
use lightning::util::enforcing_trait_impls::EnforcingSigner;
|
||||
use lightning::util::logger::Logger;
|
||||
use lightning::util::ser::{Readable, Writeable, Writer};
|
||||
|
@ -153,6 +156,18 @@ impl NodeSigner for KeyProvider {
|
|||
unreachable!()
|
||||
}
|
||||
|
||||
fn sign_bolt12_invoice_request(
|
||||
&self, _invoice_request: &UnsignedInvoiceRequest
|
||||
) -> Result<schnorr::Signature, ()> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn sign_bolt12_invoice(
|
||||
&self, _invoice: &UnsignedBolt12Invoice,
|
||||
) -> Result<schnorr::Signature, ()> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn sign_gossip_message(&self, _msg: lightning::ln::msgs::UnsignedGossipMessage) -> Result<bitcoin::secp256k1::ecdsa::Signature, ()> {
|
||||
unreachable!()
|
||||
}
|
||||
|
|
|
@ -397,6 +397,11 @@ impl UnsignedBolt12Invoice {
|
|||
Self { bytes, contents, tagged_hash }
|
||||
}
|
||||
|
||||
/// Returns the [`TaggedHash`] of the invoice to sign.
|
||||
pub fn tagged_hash(&self) -> &TaggedHash {
|
||||
&self.tagged_hash
|
||||
}
|
||||
|
||||
/// Signs the [`TaggedHash`] of the invoice using the given function.
|
||||
///
|
||||
/// Note: The hash computation may have included unknown, odd TLV records.
|
||||
|
|
|
@ -372,6 +372,11 @@ impl UnsignedInvoiceRequest {
|
|||
Self { bytes, contents, tagged_hash }
|
||||
}
|
||||
|
||||
/// Returns the [`TaggedHash`] of the invoice to sign.
|
||||
pub fn tagged_hash(&self) -> &TaggedHash {
|
||||
&self.tagged_hash
|
||||
}
|
||||
|
||||
/// Signs the [`TaggedHash`] of the invoice request using the given function.
|
||||
///
|
||||
/// Note: The hash computation may have included unknown, odd TLV records.
|
||||
|
|
|
@ -26,9 +26,10 @@ use bitcoin::hashes::sha256::Hash as Sha256;
|
|||
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
|
||||
use bitcoin::hash_types::WPubkeyHash;
|
||||
|
||||
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, Signing};
|
||||
use bitcoin::secp256k1::{KeyPair, PublicKey, Scalar, Secp256k1, SecretKey, Signing};
|
||||
use bitcoin::secp256k1::ecdh::SharedSecret;
|
||||
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
|
||||
use bitcoin::secp256k1::schnorr;
|
||||
use bitcoin::{PackedLockTime, secp256k1, Sequence, Witness};
|
||||
|
||||
use crate::util::transaction_utils;
|
||||
|
@ -41,6 +42,8 @@ use crate::ln::{chan_utils, PaymentPreimage};
|
|||
use crate::ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction};
|
||||
use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage};
|
||||
use crate::ln::script::ShutdownScript;
|
||||
use crate::offers::invoice::UnsignedBolt12Invoice;
|
||||
use crate::offers::invoice_request::UnsignedInvoiceRequest;
|
||||
|
||||
use crate::prelude::*;
|
||||
use core::convert::TryInto;
|
||||
|
@ -619,6 +622,36 @@ pub trait NodeSigner {
|
|||
/// Errors if the [`Recipient`] variant is not supported by the implementation.
|
||||
fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()>;
|
||||
|
||||
/// Signs the [`TaggedHash`] of a BOLT 12 invoice request.
|
||||
///
|
||||
/// May be called by a function passed to [`UnsignedInvoiceRequest::sign`] where
|
||||
/// `invoice_request` is the callee.
|
||||
///
|
||||
/// Implementors may check that the `invoice_request` is expected rather than blindly signing
|
||||
/// the tagged hash. An `Ok` result should sign `invoice_request.tagged_hash().as_digest()` with
|
||||
/// the node's signing key or an ephemeral key to preserve privacy, whichever is associated with
|
||||
/// [`UnsignedInvoiceRequest::payer_id`].
|
||||
///
|
||||
/// [`TaggedHash`]: crate::offers::merkle::TaggedHash
|
||||
fn sign_bolt12_invoice_request(
|
||||
&self, invoice_request: &UnsignedInvoiceRequest
|
||||
) -> Result<schnorr::Signature, ()>;
|
||||
|
||||
/// Signs the [`TaggedHash`] of a BOLT 12 invoice.
|
||||
///
|
||||
/// May be called by a function passed to [`UnsignedBolt12Invoice::sign`] where `invoice` is the
|
||||
/// callee.
|
||||
///
|
||||
/// Implementors may check that the `invoice` is expected rather than blindly signing the tagged
|
||||
/// hash. An `Ok` result should sign `invoice.tagged_hash().as_digest()` with the node's signing
|
||||
/// key or an ephemeral key to preserve privacy, whichever is associated with
|
||||
/// [`UnsignedBolt12Invoice::signing_pubkey`].
|
||||
///
|
||||
/// [`TaggedHash`]: crate::offers::merkle::TaggedHash
|
||||
fn sign_bolt12_invoice(
|
||||
&self, invoice: &UnsignedBolt12Invoice
|
||||
) -> Result<schnorr::Signature, ()>;
|
||||
|
||||
/// Sign a gossip message.
|
||||
///
|
||||
/// Note that if this fails, LDK may panic and the message will not be broadcast to the network
|
||||
|
@ -1449,6 +1482,24 @@ impl NodeSigner for KeysManager {
|
|||
Ok(self.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), secret))
|
||||
}
|
||||
|
||||
fn sign_bolt12_invoice_request(
|
||||
&self, invoice_request: &UnsignedInvoiceRequest
|
||||
) -> Result<schnorr::Signature, ()> {
|
||||
let message = invoice_request.tagged_hash().as_digest();
|
||||
let keys = KeyPair::from_secret_key(&self.secp_ctx, &self.node_secret);
|
||||
let aux_rand = self.get_secure_random_bytes();
|
||||
Ok(self.secp_ctx.sign_schnorr_with_aux_rand(message, &keys, &aux_rand))
|
||||
}
|
||||
|
||||
fn sign_bolt12_invoice(
|
||||
&self, invoice: &UnsignedBolt12Invoice
|
||||
) -> Result<schnorr::Signature, ()> {
|
||||
let message = invoice.tagged_hash().as_digest();
|
||||
let keys = KeyPair::from_secret_key(&self.secp_ctx, &self.node_secret);
|
||||
let aux_rand = self.get_secure_random_bytes();
|
||||
Ok(self.secp_ctx.sign_schnorr_with_aux_rand(message, &keys, &aux_rand))
|
||||
}
|
||||
|
||||
fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
|
||||
let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
|
||||
Ok(self.secp_ctx.sign_ecdsa(&msg_hash, &self.node_secret))
|
||||
|
@ -1557,6 +1608,18 @@ impl NodeSigner for PhantomKeysManager {
|
|||
Ok(self.inner.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), secret))
|
||||
}
|
||||
|
||||
fn sign_bolt12_invoice_request(
|
||||
&self, invoice_request: &UnsignedInvoiceRequest
|
||||
) -> Result<schnorr::Signature, ()> {
|
||||
self.inner.sign_bolt12_invoice_request(invoice_request)
|
||||
}
|
||||
|
||||
fn sign_bolt12_invoice(
|
||||
&self, invoice: &UnsignedBolt12Invoice
|
||||
) -> Result<schnorr::Signature, ()> {
|
||||
self.inner.sign_bolt12_invoice(invoice)
|
||||
}
|
||||
|
||||
fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
|
||||
self.inner.sign_gossip_message(msg)
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ use crate::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
|
|||
use crate::ln::{msgs, wire};
|
||||
use crate::ln::msgs::LightningError;
|
||||
use crate::ln::script::ShutdownScript;
|
||||
use crate::offers::invoice::UnsignedBolt12Invoice;
|
||||
use crate::offers::invoice_request::UnsignedInvoiceRequest;
|
||||
use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId};
|
||||
use crate::routing::utxo::{UtxoLookup, UtxoLookupError, UtxoResult};
|
||||
use crate::routing::router::{find_route, InFlightHtlcs, Path, Route, RouteParameters, Router, ScorerAccountingForInFlightHtlcs};
|
||||
|
@ -47,6 +49,7 @@ use bitcoin::util::sighash::SighashCache;
|
|||
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};
|
||||
use bitcoin::secp256k1::ecdh::SharedSecret;
|
||||
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
|
||||
use bitcoin::secp256k1::schnorr;
|
||||
|
||||
#[cfg(any(test, feature = "_test_utils"))]
|
||||
use regex;
|
||||
|
@ -800,6 +803,18 @@ impl NodeSigner for TestNodeSigner {
|
|||
unreachable!()
|
||||
}
|
||||
|
||||
fn sign_bolt12_invoice_request(
|
||||
&self, _invoice_request: &UnsignedInvoiceRequest
|
||||
) -> Result<schnorr::Signature, ()> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn sign_bolt12_invoice(
|
||||
&self, _invoice: &UnsignedBolt12Invoice,
|
||||
) -> Result<schnorr::Signature, ()> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn sign_gossip_message(&self, _msg: msgs::UnsignedGossipMessage) -> Result<Signature, ()> {
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -840,6 +855,18 @@ impl NodeSigner for TestKeysInterface {
|
|||
self.backing.sign_invoice(hrp_bytes, invoice_data, recipient)
|
||||
}
|
||||
|
||||
fn sign_bolt12_invoice_request(
|
||||
&self, invoice_request: &UnsignedInvoiceRequest
|
||||
) -> Result<schnorr::Signature, ()> {
|
||||
self.backing.sign_bolt12_invoice_request(invoice_request)
|
||||
}
|
||||
|
||||
fn sign_bolt12_invoice(
|
||||
&self, invoice: &UnsignedBolt12Invoice,
|
||||
) -> Result<schnorr::Signature, ()> {
|
||||
self.backing.sign_bolt12_invoice(invoice)
|
||||
}
|
||||
|
||||
fn sign_gossip_message(&self, msg: msgs::UnsignedGossipMessage) -> Result<Signature, ()> {
|
||||
self.backing.sign_gossip_message(msg)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue