use bitcoin::bech32::u5;
use bitcoin::hashes::{Hash, HashEngine};
-use bitcoin::hashes::sha256::HashEngine as Sha256State;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hash_types::WPubkeyHash;
use crate::util::transaction_utils;
use crate::util::crypto::{hkdf_extract_expand_twice, sign};
-use crate::util::ser::{Writeable, Writer, Readable, ReadableArgs};
-#[cfg(anchors)]
-use crate::util::events::HTLCDescriptor;
+use crate::util::ser::{Writeable, Writer, Readable};
use crate::chain::transaction::OutPoint;
+#[cfg(anchors)]
+use crate::events::bump_transaction::HTLCDescriptor;
use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
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;
+use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage};
use crate::ln::script::ShutdownScript;
use crate::prelude::*;
use core::sync::atomic::{AtomicUsize, Ordering};
use crate::io::{self, Error};
use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
+use crate::util::atomic_counter::AtomicCounter;
+use crate::util::chacha20::ChaCha20;
use crate::util::invoice::construct_invoice_preimage;
/// Used as initial key material, to be expanded into multiple secret keys (but not to be used
/// directly). This is used within LDK to encrypt/decrypt inbound payment data.
///
-/// (C-not exported) as we just use `[u8; 32]` directly
+/// This is not exported to bindings users as we just use `[u8; 32]` directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
pub struct KeyMaterial(pub [u8; 32]);
/// The revocation point specific to the commitment transaction which was broadcast. Used to
/// derive the witnessScript for this output.
pub revocation_pubkey: PublicKey,
- /// Arbitrary identification information returned by a call to [`BaseSign::channel_keys_id`].
+ /// Arbitrary identification information returned by a call to [`ChannelSigner::channel_keys_id`].
/// This may be useful in re-deriving keys used in the channel to spend the output.
pub channel_keys_id: [u8; 32],
/// The value of the channel which this output originated from, possibly indirectly.
pub outpoint: OutPoint,
/// The output which is referenced by the given outpoint.
pub output: TxOut,
- /// Arbitrary identification information returned by a call to [`BaseSign::channel_keys_id`].
+ /// Arbitrary identification information returned by a call to [`ChannelSigner::channel_keys_id`].
/// This may be useful in re-deriving keys used in the channel to spend the output.
pub channel_keys_id: [u8; 32],
/// The value of the channel which this transactions spends.
/// outpoint describing which `txid` and output `index` is available, the full output which exists
/// at that `txid`/`index`, and any keys or other information required to sign.
///
-/// [`SpendableOutputs`]: crate::util::events::Event::SpendableOutputs
+/// [`SpendableOutputs`]: crate::events::Event::SpendableOutputs
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SpendableOutputDescriptor {
- /// An output to a script which was provided via [`KeysInterface`] directly, either from
+ /// An output to a script which was provided via [`SignerProvider`] directly, either from
/// [`get_destination_script`] or [`get_shutdown_scriptpubkey`], thus you should already
/// know how to spend it. No secret keys are provided as LDK was never given any key.
/// These may include outputs from a transaction punishing our counterparty or claiming an HTLC
/// on-chain using the payment preimage or after it has timed out.
///
- /// [`get_shutdown_scriptpubkey`]: KeysInterface::get_shutdown_scriptpubkey
- /// [`get_destination_script`]: KeysInterface::get_shutdown_scriptpubkey
+ /// [`get_shutdown_scriptpubkey`]: SignerProvider::get_shutdown_scriptpubkey
+ /// [`get_destination_script`]: SignerProvider::get_shutdown_scriptpubkey
StaticOutput {
/// The outpoint which is spendable.
outpoint: OutPoint,
///
/// To derive the delayed payment key which is used to sign this input, you must pass the
/// holder [`InMemorySigner::delayed_payment_base_key`] (i.e., the private key which corresponds to the
- /// [`ChannelPublicKeys::delayed_payment_basepoint`] in [`BaseSign::pubkeys`]) and the provided
+ /// [`ChannelPublicKeys::delayed_payment_basepoint`] in [`ChannelSigner::pubkeys`]) and the provided
/// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to [`chan_utils::derive_private_key`]. The public key can be
/// generated without the secret key using [`chan_utils::derive_public_key`] and only the
- /// [`ChannelPublicKeys::delayed_payment_basepoint`] which appears in [`BaseSign::pubkeys`].
+ /// [`ChannelPublicKeys::delayed_payment_basepoint`] which appears in [`ChannelSigner::pubkeys`].
///
/// To derive the [`DelayedPaymentOutputDescriptor::revocation_pubkey`] provided here (which is
/// used in the witness script generation), you must pass the counterparty
/// [`ChannelPublicKeys::revocation_basepoint`] (which appears in the call to
- /// [`BaseSign::provide_channel_parameters`]) and the provided
+ /// [`ChannelSigner::provide_channel_parameters`]) and the provided
/// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to
/// [`chan_utils::derive_public_revocation_key`].
///
/// [`chan_utils::get_revokeable_redeemscript`].
DelayedPaymentOutput(DelayedPaymentOutputDescriptor),
/// An output to a P2WPKH, spendable exclusively by our payment key (i.e., the private key
- /// which corresponds to the `payment_point` in [`BaseSign::pubkeys`]). The witness
+ /// which corresponds to the `payment_point` in [`ChannelSigner::pubkeys`]). The witness
/// in the spending input is, thus, simply:
/// ```bitcoin
/// <BIP 143 signature> <payment key>
(2, StaticPaymentOutput),
);
-/// A trait to sign Lightning channel transactions as described in
-/// [BOLT 3](https://github.com/lightning/bolts/blob/master/03-transactions.md).
-///
-/// Signing services could be implemented on a hardware wallet and should implement signing
-/// policies in order to be secure. Please refer to the [VLS Policy
-/// Controls](https://gitlab.com/lightning-signer/validating-lightning-signer/-/blob/main/docs/policy-controls.md)
-/// for an example of such policies.
-pub trait BaseSign {
+/// A trait to handle Lightning channel key material without concretizing the channel type or
+/// the signature mechanism.
+pub trait ChannelSigner {
/// Gets the per-commitment point for a specific commitment number
///
/// Note that the commitment number starts at `(1 << 48) - 1` and counts backwards.
fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1<secp256k1::All>) -> PublicKey;
+
/// Gets the commitment secret for a specific commitment number as part of the revocation process
///
/// An external signer implementation should error here if the commitment was already signed
/// Note that the commitment number starts at `(1 << 48) - 1` and counts backwards.
// TODO: return a Result so we can signal a validation error
fn release_commitment_secret(&self, idx: u64) -> [u8; 32];
+
/// Validate the counterparty's signatures on the holder commitment transaction and HTLCs.
///
/// This is required in order for the signer to make sure that releasing a commitment
/// irrelevant or duplicate preimages.
fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction,
preimages: Vec<PaymentPreimage>) -> Result<(), ()>;
+
/// Returns the holder's channel public keys and basepoints.
fn pubkeys(&self) -> &ChannelPublicKeys;
+
/// Returns an arbitrary identifier describing the set of keys which are provided back to you in
/// some [`SpendableOutputDescriptor`] types. This should be sufficient to identify this
- /// [`BaseSign`] object uniquely and lookup or re-derive its keys.
+ /// [`EcdsaChannelSigner`] object uniquely and lookup or re-derive its keys.
fn channel_keys_id(&self) -> [u8; 32];
+
+ /// Set the counterparty static channel data, including basepoints,
+ /// `counterparty_selected`/`holder_selected_contest_delay` and funding outpoint.
+ ///
+ /// This data is static, and will never change for a channel once set. For a given [`ChannelSigner`]
+ /// instance, LDK will call this method exactly once - either immediately after construction
+ /// (not including if done via [`SignerProvider::read_chan_signer`]) or when the funding
+ /// information has been generated.
+ ///
+ /// channel_parameters.is_populated() MUST be true.
+ fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters);
+}
+
+/// A trait to sign Lightning channel transactions as described in
+/// [BOLT 3](https://github.com/lightning/bolts/blob/master/03-transactions.md).
+///
+/// Signing services could be implemented on a hardware wallet and should implement signing
+/// policies in order to be secure. Please refer to the [VLS Policy
+/// Controls](https://gitlab.com/lightning-signer/validating-lightning-signer/-/blob/main/docs/policy-controls.md)
+/// for an example of such policies.
+pub trait EcdsaChannelSigner: ChannelSigner {
/// Create a signature for a counterparty's commitment transaction and associated HTLC transactions.
///
/// Note that if signing fails or is rejected, the channel will be force-closed.
fn sign_holder_anchor_input(
&self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1<secp256k1::All>,
) -> Result<Signature, ()>;
- /// Signs a channel announcement message with our funding key and our node secret key (aka
- /// node_id or network_key), proving it comes from one of the channel participants.
+ /// Signs a channel announcement message with our funding key proving it comes from one of the
+ /// channel participants.
///
- /// The first returned signature should be from our node secret key, the second from our
- /// funding key.
+ /// Channel announcements also require a signature from each node's network key. Our node
+ /// signature is computed through [`NodeSigner::sign_gossip_message`].
///
/// Note that if this fails or is rejected, the channel will not be publicly announced and
/// our counterparty may (though likely will not) close the channel on us for violating the
/// protocol.
- fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>)
- -> Result<(Signature, Signature), ()>;
- /// Set the counterparty static channel data, including basepoints,
- /// `counterparty_selected`/`holder_selected_contest_delay` and funding outpoint. Since these
- /// are static channel data, they MUST NOT be allowed to change to different values once set,
- /// as LDK may call this method more than once.
- ///
- /// channel_parameters.is_populated() MUST be true.
- fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters);
+ fn sign_channel_announcement_with_funding_key(
+ &self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>
+ ) -> Result<Signature, ()>;
}
/// A writeable signer.
///
/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
/// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
-pub trait Sign: BaseSign + Writeable {}
+pub trait WriteableEcdsaChannelSigner: EcdsaChannelSigner + Writeable {}
/// Specifies the recipient of an invoice.
///
-/// This indicates to [`KeysInterface::sign_invoice`] what node secret key should be used to sign
+/// This indicates to [`NodeSigner::sign_invoice`] what node secret key should be used to sign
/// the invoice.
pub enum Recipient {
/// The invoice should be signed with the local node secret key.
PhantomNode,
}
-/// A trait to describe an object which can get user secrets and key material.
-pub trait KeysInterface {
- /// A type which implements [`Sign`] which will be returned by [`Self::derive_channel_signer`].
- type Signer : Sign;
- /// Get node secret key based on the provided [`Recipient`].
+/// A trait that describes a source of entropy.
+pub trait EntropySource {
+ /// Gets a unique, cryptographically-secure, random 32-byte value. This method must return a
+ /// different value each time it is called.
+ fn get_secure_random_bytes(&self) -> [u8; 32];
+}
+
+/// A trait that can handle cryptographic operations at the scope level of a node.
+pub trait NodeSigner {
+ /// Get secret key material as bytes for use in encrypting and decrypting inbound payment data.
///
- /// The `node_id`/`network_key` is the public key that corresponds to this secret key.
+ /// If the implementor of this trait supports [phantom node payments], then every node that is
+ /// intended to be included in the phantom invoice route hints must return the same value from
+ /// this method.
+ // This is because LDK avoids storing inbound payment data by encrypting payment data in the
+ // payment hash and/or payment secret, therefore for a payment to be receivable by multiple
+ // nodes, they must share the key that encrypts this payment data.
///
- /// This method must return the same value each time it is called with a given [`Recipient`]
- /// parameter.
+ /// This method must return the same value each time it is called.
///
- /// Errors if the [`Recipient`] variant is not supported by the implementation.
- fn get_node_secret(&self, recipient: Recipient) -> Result<SecretKey, ()>;
- /// Get node id based on the provided [`Recipient`]. This public key corresponds to the secret in
- /// [`get_node_secret`].
+ /// [phantom node payments]: PhantomKeysManager
+ fn get_inbound_payment_key_material(&self) -> KeyMaterial;
+
+ /// Get node id based on the provided [`Recipient`].
///
/// This method must return the same value each time it is called with a given [`Recipient`]
/// parameter.
///
/// Errors if the [`Recipient`] variant is not supported by the implementation.
- ///
- /// [`get_node_secret`]: Self::get_node_secret
- fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()> {
- let secp_ctx = Secp256k1::signing_only();
- Ok(PublicKey::from_secret_key(&secp_ctx, &self.get_node_secret(recipient)?))
- }
- /// Gets the ECDH shared secret of our [`node secret`] and `other_key`, multiplying by `tweak` if
+ fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()>;
+
+ /// Gets the ECDH shared secret of our node secret and `other_key`, multiplying by `tweak` if
/// one is provided. Note that this tweak can be applied to `other_key` instead of our node
/// secret, though this is less efficient.
///
- /// Errors if the [`Recipient`] variant is not supported by the implementation.
+ /// Note that if this fails while attempting to forward an HTLC, LDK will panic. The error
+ /// should be resolved to allow LDK to resume forwarding HTLCs.
///
- /// [`node secret`]: Self::get_node_secret
+ /// Errors if the [`Recipient`] variant is not supported by the implementation.
fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result<SharedSecret, ()>;
- /// Get a script pubkey which we send funds to when claiming on-chain contestable outputs.
+
+ /// Sign an invoice.
///
- /// This method should return a different value each time it is called, to avoid linking
- /// on-chain funds across channels as controlled to the same user.
- fn get_destination_script(&self) -> Script;
- /// Get a script pubkey which we will send funds to when closing a channel.
+ /// By parameterizing by the raw invoice bytes instead of the hash, we allow implementors of
+ /// this trait to parse the invoice and make sure they're signing what they expect, rather than
+ /// blindly signing the hash.
///
- /// This method should return a different value each time it is called, to avoid linking
- /// on-chain funds across channels as controlled to the same user.
- fn get_shutdown_scriptpubkey(&self) -> ShutdownScript;
- /// Get a new set of [`Sign`] for per-channel secrets. These MUST be unique even if you
- /// restarted with some stale data!
+ /// The `hrp_bytes` are ASCII bytes, while the `invoice_data` is base32.
+ ///
+ /// The secret key used to sign the invoice is dependent on the [`Recipient`].
+ ///
+ /// 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, ()>;
+
+ /// Sign a gossip message.
+ ///
+ /// Note that if this fails, LDK may panic and the message will not be broadcast to the network
+ /// or a possible channel counterparty. If LDK panics, the error should be resolved to allow the
+ /// message to be broadcast, as otherwise it may prevent one from receiving funds over the
+ /// corresponding channel.
+ fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()>;
+}
+
+/// A trait that can return signer instances for individual channels.
+pub trait SignerProvider {
+ /// A type which implements [`WriteableEcdsaChannelSigner`] which will be returned by [`Self::derive_channel_signer`].
+ type Signer : WriteableEcdsaChannelSigner;
+
+ /// Generates a unique `channel_keys_id` that can be used to obtain a [`Self::Signer`] through
+ /// [`SignerProvider::derive_channel_signer`]. The `user_channel_id` is provided to allow
+ /// implementations of [`SignerProvider`] to maintain a mapping between itself and the generated
+ /// `channel_keys_id`.
///
/// This method must return a different value each time it is called.
fn generate_channel_keys_id(&self, inbound: bool, channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32];
+
/// Derives the private key material backing a `Signer`.
///
/// To derive a new `Signer`, a fresh `channel_keys_id` should be obtained through
- /// [`KeysInterface::generate_channel_keys_id`]. Otherwise, an existing `Signer` can be
+ /// [`SignerProvider::generate_channel_keys_id`]. Otherwise, an existing `Signer` can be
/// re-derived from its `channel_keys_id`, which can be obtained through its trait method
- /// [`BaseSign::channel_keys_id`].
+ /// [`ChannelSigner::channel_keys_id`].
fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer;
- /// Gets a unique, cryptographically-secure, random 32 byte value. This is used for encrypting
- /// onion packets and for temporary channel IDs. There is no requirement that these be
- /// persisted anywhere, though they must be unique across restarts.
- ///
- /// This method must return a different value each time it is called.
- fn get_secure_random_bytes(&self) -> [u8; 32];
- /// Reads a [`Signer`] for this [`KeysInterface`] from the given input stream.
+
+ /// Reads a [`Signer`] for this [`SignerProvider`] from the given input stream.
/// This is only called during deserialization of other objects which contain
- /// [`Sign`]-implementing objects (i.e., [`ChannelMonitor`]s and [`ChannelManager`]s).
+ /// [`WriteableEcdsaChannelSigner`]-implementing objects (i.e., [`ChannelMonitor`]s and [`ChannelManager`]s).
/// The bytes are exactly those which `<Self::Signer as Writeable>::write()` writes, and
/// contain no versioning scheme. You may wish to include your own version prefix and ensure
/// you've read all of the provided bytes to ensure no corruption occurred.
/// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError>;
- /// Sign an invoice.
- /// By parameterizing by the raw invoice bytes instead of the hash, we allow implementors of
- /// this trait to parse the invoice and make sure they're signing what they expect, rather than
- /// blindly signing the hash.
- /// The `hrp` is ASCII bytes, while the invoice data is base32-encoded.
- ///
- /// The secret key used to sign the invoice is dependent on the [`Recipient`].
- ///
- /// Errors if the [`Recipient`] variant is not supported by the implementation.
- fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], receipient: Recipient) -> Result<RecoverableSignature, ()>;
- /// Get secret key material as bytes for use in encrypting and decrypting inbound payment data.
- ///
- /// If the implementor of this trait supports [phantom node payments], then every node that is
- /// intended to be included in the phantom invoice route hints must return the same value from
- /// this method.
- // This is because LDK avoids storing inbound payment data by encrypting payment data in the
- // payment hash and/or payment secret, therefore for a payment to be receivable by multiple
- // nodes, they must share the key that encrypts this payment data.
+
+ /// Get a script pubkey which we send funds to when claiming on-chain contestable outputs.
///
- /// This method must return the same value each time it is called.
+ /// This method should return a different value each time it is called, to avoid linking
+ /// on-chain funds across channels as controlled to the same user.
+ fn get_destination_script(&self) -> Script;
+
+ /// Get a script pubkey which we will send funds to when closing a channel.
///
- /// [phantom node payments]: PhantomKeysManager
- fn get_inbound_payment_key_material(&self) -> KeyMaterial;
+ /// This method should return a different value each time it is called, to avoid linking
+ /// on-chain funds across channels as controlled to the same user.
+ fn get_shutdown_scriptpubkey(&self) -> ShutdownScript;
}
#[derive(Clone)]
-/// A simple implementation of [`Sign`] that just keeps the private keys in memory.
+/// A simple implementation of [`WriteableEcdsaChannelSigner`] that just keeps the private keys in memory.
///
/// This implementation performs no policy checks and is insufficient by itself as
/// a secure external signer.
pub commitment_seed: [u8; 32],
/// Holder public keys and basepoints.
pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
- /// Private key of our node secret, used for signing channel announcements.
- node_secret: SecretKey,
/// Counterparty public keys and counterparty/holder `selected_contest_delay`, populated on channel acceptance.
channel_parameters: Option<ChannelTransactionParameters>,
/// The total value of this channel.
/// Creates a new [`InMemorySigner`].
pub fn new<C: Signing>(
secp_ctx: &Secp256k1<C>,
- node_secret: SecretKey,
funding_key: SecretKey,
revocation_base_key: SecretKey,
payment_key: SecretKey,
delayed_payment_base_key,
htlc_base_key,
commitment_seed,
- node_secret,
channel_value_satoshis,
holder_channel_pubkeys,
channel_parameters: None,
/// Returns the counterparty's pubkeys.
///
- /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before.
+ /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.get_channel_parameters().counterparty_parameters.as_ref().unwrap().pubkeys }
/// Returns the `contest_delay` value specified by our counterparty and applied on holder-broadcastable
/// transactions, i.e., the amount of time that we have to wait to recover our funds if we
/// broadcast a transaction.
///
- /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before.
+ /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
pub fn counterparty_selected_contest_delay(&self) -> u16 { self.get_channel_parameters().counterparty_parameters.as_ref().unwrap().selected_contest_delay }
/// Returns the `contest_delay` value specified by us and applied on transactions broadcastable
/// by our counterparty, i.e., the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
///
- /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before.
+ /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
pub fn holder_selected_contest_delay(&self) -> u16 { self.get_channel_parameters().holder_selected_contest_delay }
/// Returns whether the holder is the initiator.
///
- /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before.
+ /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
pub fn is_outbound(&self) -> bool { self.get_channel_parameters().is_outbound_from_holder }
/// Funding outpoint
///
- /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before.
+ /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
pub fn funding_outpoint(&self) -> &OutPoint { self.get_channel_parameters().funding_outpoint.as_ref().unwrap() }
/// Returns a [`ChannelTransactionParameters`] for this channel, to be used when verifying or
/// building transactions.
///
- /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before.
+ /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
pub fn get_channel_parameters(&self) -> &ChannelTransactionParameters {
self.channel_parameters.as_ref().unwrap()
}
/// Returns whether anchors should be used.
///
- /// Will panic if [`BaseSign::provide_channel_parameters`] has not been called before.
+ /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
pub fn opt_anchors(&self) -> bool {
self.get_channel_parameters().opt_anchors.is_some()
}
}
}
-impl BaseSign for InMemorySigner {
+impl ChannelSigner for InMemorySigner {
fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1<secp256k1::All>) -> PublicKey {
let commitment_secret = SecretKey::from_slice(&chan_utils::build_commitment_secret(&self.commitment_seed, idx)).unwrap();
PublicKey::from_secret_key(secp_ctx, &commitment_secret)
fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id }
+ fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters) {
+ assert!(self.channel_parameters.is_none() || self.channel_parameters.as_ref().unwrap() == channel_parameters);
+ if self.channel_parameters.is_some() {
+ // The channel parameters were already set and they match, return early.
+ return;
+ }
+ assert!(channel_parameters.is_populated(), "Channel parameters must be fully populated");
+ self.channel_parameters = Some(channel_parameters.clone());
+ }
+}
+
+impl EcdsaChannelSigner for InMemorySigner {
fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, _preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
let trusted_tx = commitment_tx.trust();
let keys = trusted_tx.keys();
Ok(sign(secp_ctx, &hash_to_message!(&sighash[..]), &self.funding_key))
}
- fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>)
- -> Result<(Signature, Signature), ()> {
+ fn sign_channel_announcement_with_funding_key(
+ &self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>
+ ) -> Result<Signature, ()> {
let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
- Ok((sign(secp_ctx, &msghash, &self.node_secret), sign(secp_ctx, &msghash, &self.funding_key)))
- }
-
- fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters) {
- assert!(self.channel_parameters.is_none() || self.channel_parameters.as_ref().unwrap() == channel_parameters);
- if self.channel_parameters.is_some() {
- // The channel parameters were already set and they match, return early.
- return;
- }
- assert!(channel_parameters.is_populated(), "Channel parameters must be fully populated");
- self.channel_parameters = Some(channel_parameters.clone());
+ Ok(sign(secp_ctx, &msghash, &self.funding_key))
}
}
const MIN_SERIALIZATION_VERSION: u8 = 1;
-impl Sign for InMemorySigner {}
+impl WriteableEcdsaChannelSigner for InMemorySigner {}
impl Writeable for InMemorySigner {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
}
}
-impl ReadableArgs<SecretKey> for InMemorySigner {
- fn read<R: io::Read>(reader: &mut R, node_secret: SecretKey) -> Result<Self, DecodeError> {
+impl Readable for InMemorySigner {
+ fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);
let funding_key = Readable::read(reader)?;
payment_key,
delayed_payment_base_key,
htlc_base_key,
- node_secret,
commitment_seed,
channel_value_satoshis,
holder_channel_pubkeys,
}
}
-/// Simple [`KeysInterface`] implementation that takes a 32-byte seed for use as a BIP 32 extended
-/// key and derives keys from that.
+/// Simple implementation of [`EntropySource`], [`NodeSigner`], and [`SignerProvider`] that takes a
+/// 32-byte seed for use as a BIP 32 extended key and derives keys from that.
///
/// Your `node_id` is seed/0'.
/// Unilateral closes may use seed/1'.
channel_master_key: ExtendedPrivKey,
channel_child_index: AtomicUsize,
- rand_bytes_master_key: ExtendedPrivKey,
- rand_bytes_child_index: AtomicUsize,
- rand_bytes_unique_start: Sha256State,
+ rand_bytes_unique_start: [u8; 32],
+ rand_bytes_index: AtomicCounter,
seed: [u8; 32],
starting_time_secs: u64,
Err(_) => panic!("Your RNG is busted"),
};
let channel_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(3).unwrap()).expect("Your RNG is busted");
- let rand_bytes_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(4).unwrap()).expect("Your RNG is busted");
let inbound_payment_key: SecretKey = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(5).unwrap()).expect("Your RNG is busted").private_key;
let mut inbound_pmt_key_bytes = [0; 32];
inbound_pmt_key_bytes.copy_from_slice(&inbound_payment_key[..]);
- let mut rand_bytes_unique_start = Sha256::engine();
- rand_bytes_unique_start.input(&starting_time_secs.to_be_bytes());
- rand_bytes_unique_start.input(&starting_time_nanos.to_be_bytes());
- rand_bytes_unique_start.input(seed);
+ let mut rand_bytes_engine = Sha256::engine();
+ rand_bytes_engine.input(&starting_time_secs.to_be_bytes());
+ rand_bytes_engine.input(&starting_time_nanos.to_be_bytes());
+ rand_bytes_engine.input(seed);
+ rand_bytes_engine.input(b"LDK PRNG Seed");
+ let rand_bytes_unique_start = Sha256::from_engine(rand_bytes_engine).into_inner();
let mut res = KeysManager {
secp_ctx,
channel_master_key,
channel_child_index: AtomicUsize::new(0),
- rand_bytes_master_key,
- rand_bytes_child_index: AtomicUsize::new(0),
rand_bytes_unique_start,
+ rand_bytes_index: AtomicCounter::new(),
seed: *seed,
starting_time_secs,
Err(_) => panic!("Your rng is busted"),
}
}
- /// Derive an old [`Sign`] containing per-channel secrets based on a key derivation parameters.
+
+ /// Gets the "node_id" secret key used to sign gossip announcements, decode onion data, etc.
+ pub fn get_node_secret_key(&self) -> SecretKey {
+ self.node_secret
+ }
+
+ /// Derive an old [`WriteableEcdsaChannelSigner`] containing per-channel secrets based on a key derivation parameters.
pub fn derive_channel_keys(&self, channel_value_satoshis: u64, params: &[u8; 32]) -> InMemorySigner {
let chan_id = u64::from_be_bytes(params[0..8].try_into().unwrap());
let mut unique_start = Sha256::engine();
InMemorySigner::new(
&self.secp_ctx,
- self.node_secret,
funding_key,
revocation_base_key,
payment_key,
}
}
-impl KeysInterface for KeysManager {
- type Signer = InMemorySigner;
-
- fn get_node_secret(&self, recipient: Recipient) -> Result<SecretKey, ()> {
- match recipient {
- Recipient::Node => Ok(self.node_secret.clone()),
- Recipient::PhantomNode => Err(())
- }
+impl EntropySource for KeysManager {
+ fn get_secure_random_bytes(&self) -> [u8; 32] {
+ let index = self.rand_bytes_index.get_increment();
+ let mut nonce = [0u8; 16];
+ nonce[..8].copy_from_slice(&index.to_be_bytes());
+ ChaCha20::get_single_block(&self.rand_bytes_unique_start, &nonce)
}
+}
+impl NodeSigner for KeysManager {
fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()> {
match recipient {
Recipient::Node => Ok(self.node_id.clone()),
}
fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result<SharedSecret, ()> {
- let mut node_secret = self.get_node_secret(recipient)?;
+ let mut node_secret = match recipient {
+ Recipient::Node => Ok(self.node_secret.clone()),
+ Recipient::PhantomNode => Err(())
+ }?;
if let Some(tweak) = tweak {
node_secret = node_secret.mul_tweak(tweak).map_err(|_| ())?;
}
self.inbound_payment_key.clone()
}
- fn get_destination_script(&self) -> Script {
- self.destination_script.clone()
+ fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> {
+ let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data);
+ let secret = match recipient {
+ Recipient::Node => Ok(&self.node_secret),
+ Recipient::PhantomNode => Err(())
+ }?;
+ Ok(self.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), secret))
}
- fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
- ShutdownScript::new_p2wpkh_from_pubkey(self.shutdown_pubkey.clone())
+ fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
+ let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
+ Ok(sign(&self.secp_ctx, &msg_hash, &self.node_secret))
}
+}
+
+impl SignerProvider for KeysManager {
+ type Signer = InMemorySigner;
fn generate_channel_keys_id(&self, _inbound: bool, _channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32] {
let child_idx = self.channel_child_index.fetch_add(1, Ordering::AcqRel);
self.derive_channel_keys(channel_value_satoshis, &channel_keys_id)
}
- fn get_secure_random_bytes(&self) -> [u8; 32] {
- let mut sha = self.rand_bytes_unique_start.clone();
-
- let child_ix = self.rand_bytes_child_index.fetch_add(1, Ordering::AcqRel);
- let child_privkey = self.rand_bytes_master_key.ckd_priv(&self.secp_ctx, ChildNumber::from_hardened_idx(child_ix as u32).expect("key space exhausted")).expect("Your RNG is busted");
- sha.input(&child_privkey.private_key[..]);
-
- sha.input(b"Unique Secure Random Bytes Salt");
- Sha256::from_engine(sha).into_inner()
+ fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
+ InMemorySigner::read(&mut io::Cursor::new(reader))
}
- fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
- InMemorySigner::read(&mut io::Cursor::new(reader), self.node_secret.clone())
+ fn get_destination_script(&self) -> Script {
+ self.destination_script.clone()
}
- fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> {
- let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data);
- let secret = match recipient {
- Recipient::Node => self.get_node_secret(Recipient::Node)?,
- Recipient::PhantomNode => return Err(()),
- };
- Ok(self.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), &secret))
+ fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
+ ShutdownScript::new_p2wpkh_from_pubkey(self.shutdown_pubkey.clone())
}
}
phantom_node_id: PublicKey,
}
-impl KeysInterface for PhantomKeysManager {
- type Signer = InMemorySigner;
-
- fn get_node_secret(&self, recipient: Recipient) -> Result<SecretKey, ()> {
- match recipient {
- Recipient::Node => self.inner.get_node_secret(Recipient::Node),
- Recipient::PhantomNode => Ok(self.phantom_secret.clone()),
- }
+impl EntropySource for PhantomKeysManager {
+ fn get_secure_random_bytes(&self) -> [u8; 32] {
+ self.inner.get_secure_random_bytes()
}
+}
+impl NodeSigner for PhantomKeysManager {
fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()> {
match recipient {
Recipient::Node => self.inner.get_node_id(Recipient::Node),
}
fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result<SharedSecret, ()> {
- let mut node_secret = self.get_node_secret(recipient)?;
+ let mut node_secret = match recipient {
+ Recipient::Node => self.inner.node_secret.clone(),
+ Recipient::PhantomNode => self.phantom_secret.clone(),
+ };
if let Some(tweak) = tweak {
node_secret = node_secret.mul_tweak(tweak).map_err(|_| ())?;
}
self.inbound_payment_key.clone()
}
- fn get_destination_script(&self) -> Script {
- self.inner.get_destination_script()
+ fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> {
+ let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data);
+ let secret = match recipient {
+ Recipient::Node => &self.inner.node_secret,
+ Recipient::PhantomNode => &self.phantom_secret,
+ };
+ Ok(self.inner.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), secret))
}
- fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
- self.inner.get_shutdown_scriptpubkey()
+ fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
+ self.inner.sign_gossip_message(msg)
}
+}
+
+impl SignerProvider for PhantomKeysManager {
+ type Signer = InMemorySigner;
fn generate_channel_keys_id(&self, inbound: bool, channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32] {
self.inner.generate_channel_keys_id(inbound, channel_value_satoshis, user_channel_id)
self.inner.derive_channel_signer(channel_value_satoshis, channel_keys_id)
}
- fn get_secure_random_bytes(&self) -> [u8; 32] {
- self.inner.get_secure_random_bytes()
- }
-
fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
self.inner.read_chan_signer(reader)
}
- fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> {
- let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data);
- let secret = self.get_node_secret(recipient)?;
- Ok(self.inner.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), &secret))
+ fn get_destination_script(&self) -> Script {
+ self.inner.get_destination_script()
+ }
+
+ fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
+ self.inner.get_shutdown_scriptpubkey()
}
}
pub fn derive_channel_keys(&self, channel_value_satoshis: u64, params: &[u8; 32]) -> InMemorySigner {
self.inner.derive_channel_keys(channel_value_satoshis, params)
}
+
+ /// Gets the "node_id" secret key used to sign gossip announcements, decode onion data, etc.
+ pub fn get_node_secret_key(&self) -> SecretKey {
+ self.inner.get_node_secret_key()
+ }
+
+ /// Gets the "node_id" secret key of the phantom node used to sign invoices, decode the
+ /// last-hop onion data, etc.
+ pub fn get_phantom_node_secret_key(&self) -> SecretKey {
+ self.phantom_secret
+ }
}
-// Ensure that BaseSign can have a vtable
+// Ensure that EcdsaChannelSigner can have a vtable
#[test]
pub fn dyn_sign() {
- let _signer: Box<dyn BaseSign>;
+ let _signer: Box<dyn EcdsaChannelSigner>;
+}
+
+#[cfg(all(test, feature = "_bench_unstable", not(feature = "no-std")))]
+mod benches {
+ use std::sync::{Arc, mpsc};
+ use std::sync::mpsc::TryRecvError;
+ use std::thread;
+ use std::time::Duration;
+ use bitcoin::blockdata::constants::genesis_block;
+ use bitcoin::Network;
+ use crate::chain::keysinterface::{EntropySource, KeysManager};
+
+ use test::Bencher;
+
+ #[bench]
+ fn bench_get_secure_random_bytes(bench: &mut Bencher) {
+ let seed = [0u8; 32];
+ let now = Duration::from_secs(genesis_block(Network::Testnet).header.time as u64);
+ let keys_manager = Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_micros()));
+
+ let mut handles = Vec::new();
+ let mut stops = Vec::new();
+ for _ in 1..5 {
+ let keys_manager_clone = Arc::clone(&keys_manager);
+ let (stop_sender, stop_receiver) = mpsc::channel();
+ let handle = thread::spawn(move || {
+ loop {
+ keys_manager_clone.get_secure_random_bytes();
+ match stop_receiver.try_recv() {
+ Ok(_) | Err(TryRecvError::Disconnected) => {
+ println!("Terminating.");
+ break;
+ }
+ Err(TryRecvError::Empty) => {}
+ }
+ }
+ });
+ handles.push(handle);
+ stops.push(stop_sender);
+ }
+
+ bench.iter(|| {
+ for _ in 1..100 {
+ keys_manager.get_secure_random_bytes();
+ }
+ });
+
+ for stop in stops {
+ let _ = stop.send(());
+ }
+ for handle in handles {
+ handle.join().unwrap();
+ }
+ }
+
}