From 30c45469e52105cbe979b5160a467658c8e964be Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Tue, 17 Jan 2023 20:33:54 -0800 Subject: [PATCH] Separate channel-type-agnostic methods into `ChannelSigner` trait. --- lightning/src/chain/keysinterface.rs | 98 ++++++++++++--------- lightning/src/chain/onchaintx.rs | 2 +- lightning/src/ln/chan_utils.rs | 2 +- lightning/src/ln/channel.rs | 4 +- lightning/src/ln/functional_tests.rs | 2 +- lightning/src/util/enforcing_trait_impls.rs | 15 ++-- 6 files changed, 69 insertions(+), 54 deletions(-) diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index c915562a..bba7d759 100644 --- a/lightning/src/chain/keysinterface.rs +++ b/lightning/src/chain/keysinterface.rs @@ -75,7 +75,7 @@ pub struct DelayedPaymentOutputDescriptor { /// 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 [`EcdsaChannelSigner::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. @@ -107,7 +107,7 @@ pub struct StaticPaymentOutputDescriptor { pub outpoint: OutPoint, /// The output which is referenced by the given outpoint. pub output: TxOut, - /// Arbitrary identification information returned by a call to [`EcdsaChannelSigner::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. @@ -172,15 +172,15 @@ pub enum SpendableOutputDescriptor { /// /// 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 [`EcdsaChannelSigner::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 [`EcdsaChannelSigner::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 - /// [`EcdsaChannelSigner::provide_channel_parameters`]) and the provided + /// [`ChannelSigner::provide_channel_parameters`]) and the provided /// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to /// [`chan_utils::derive_public_revocation_key`]. /// @@ -191,7 +191,7 @@ pub enum SpendableOutputDescriptor { /// [`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 [`EcdsaChannelSigner::pubkeys`]). The witness + /// which corresponds to the `payment_point` in [`ChannelSigner::pubkeys`]). The witness /// in the spending input is, thus, simply: /// ```bitcoin /// @@ -212,18 +212,14 @@ impl_writeable_tlv_based_enum!(SpendableOutputDescriptor, (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 EcdsaChannelSigner { +/// 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) -> 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 @@ -234,6 +230,7 @@ pub trait EcdsaChannelSigner { /// 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 @@ -249,12 +246,35 @@ pub trait EcdsaChannelSigner { /// irrelevant or duplicate preimages. fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction, preimages: Vec) -> 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 /// [`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. @@ -395,16 +415,6 @@ pub trait EcdsaChannelSigner { fn sign_channel_announcement_with_funding_key( &self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1 ) -> Result; - /// 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 [`EcdsaChannelSigner`] - /// 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 writeable signer. @@ -511,7 +521,7 @@ pub trait SignerProvider { /// To derive a new `Signer`, a fresh `channel_keys_id` should be obtained through /// [`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 - /// [`EcdsaChannelSigner::channel_keys_id`]. + /// [`ChannelSigner::channel_keys_id`]. fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer; /// Reads a [`Signer`] for this [`SignerProvider`] from the given input stream. @@ -620,38 +630,38 @@ impl InMemorySigner { /// Returns the counterparty's pubkeys. /// - /// Will panic if [`EcdsaChannelSigner::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 [`EcdsaChannelSigner::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 [`EcdsaChannelSigner::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 [`EcdsaChannelSigner::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 [`EcdsaChannelSigner::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 [`EcdsaChannelSigner::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 [`EcdsaChannelSigner::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() } @@ -725,7 +735,7 @@ impl InMemorySigner { } } -impl EcdsaChannelSigner for InMemorySigner { +impl ChannelSigner for InMemorySigner { fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1) -> 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) @@ -743,6 +753,18 @@ impl EcdsaChannelSigner for InMemorySigner { 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, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { let trusted_tx = commitment_tx.trust(); let keys = trusted_tx.keys(); @@ -871,16 +893,6 @@ impl EcdsaChannelSigner for InMemorySigner { let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]); Ok(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()); - } } const SERIALIZATION_VERSION: u8 = 1; diff --git a/lightning/src/chain/onchaintx.rs b/lightning/src/chain/onchaintx.rs index 00d07d86..c14d4fa8 100644 --- a/lightning/src/chain/onchaintx.rs +++ b/lightning/src/chain/onchaintx.rs @@ -21,7 +21,7 @@ use bitcoin::hash_types::{Txid, BlockHash}; use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature}; use bitcoin::secp256k1; -use crate::chain::keysinterface::{EcdsaChannelSigner, EntropySource, SignerProvider}; +use crate::chain::keysinterface::{ChannelSigner, EntropySource, SignerProvider}; use crate::ln::msgs::DecodeError; use crate::ln::PaymentPreimage; #[cfg(anchors)] diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index 059aa05a..31881d05 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -1635,7 +1635,7 @@ mod tests { use crate::ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment}; use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1}; use crate::util::test_utils; - use crate::chain::keysinterface::{EcdsaChannelSigner, SignerProvider}; + use crate::chain::keysinterface::{ChannelSigner, SignerProvider}; use bitcoin::{Network, Txid}; use bitcoin::hashes::Hash; use crate::ln::PaymentHash; diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index b7501611..fb60a882 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -35,7 +35,7 @@ use crate::chain::BestBlock; use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, LowerBoundedFeeEstimator}; use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, LATENCY_GRACE_PERIOD_BLOCKS}; use crate::chain::transaction::{OutPoint, TransactionData}; -use crate::chain::keysinterface::{Sign, EntropySource, EcdsaChannelSigner, SignerProvider, NodeSigner, Recipient}; +use crate::chain::keysinterface::{Sign, EntropySource, ChannelSigner, SignerProvider, NodeSigner, Recipient}; use crate::util::events::ClosureReason; use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, VecWriter}; use crate::util::logger::Logger; @@ -6901,7 +6901,7 @@ mod tests { use crate::ln::chan_utils::{htlc_success_tx_weight, htlc_timeout_tx_weight}; use crate::chain::BestBlock; use crate::chain::chaininterface::{FeeEstimator, LowerBoundedFeeEstimator, ConfirmationTarget}; - use crate::chain::keysinterface::{EcdsaChannelSigner, InMemorySigner, EntropySource, SignerProvider}; + use crate::chain::keysinterface::{ChannelSigner, InMemorySigner, EntropySource, SignerProvider}; use crate::chain::transaction::OutPoint; use crate::util::config::UserConfig; use crate::util::enforcing_trait_impls::EnforcingSigner; diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 6ff4aeb1..f645b9df 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -17,7 +17,7 @@ use crate::chain::chaininterface::LowerBoundedFeeEstimator; use crate::chain::channelmonitor; use crate::chain::channelmonitor::{CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY}; use crate::chain::transaction::OutPoint; -use crate::chain::keysinterface::{EcdsaChannelSigner, EntropySource}; +use crate::chain::keysinterface::{ChannelSigner, EcdsaChannelSigner, EntropySource}; use crate::ln::{PaymentPreimage, PaymentSecret, PaymentHash}; use crate::ln::channel::{commitment_tx_base_weight, COMMITMENT_TX_WEIGHT_PER_HTLC, CONCURRENT_INBOUND_HTLC_FEE_BUFFER, FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_AFFORDABLE_HTLC_COUNT}; use crate::ln::channelmanager::{self, PaymentId, RAACommitmentOrder, PaymentSendFailure, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA}; diff --git a/lightning/src/util/enforcing_trait_impls.rs b/lightning/src/util/enforcing_trait_impls.rs index f3e94b55..30a7c39c 100644 --- a/lightning/src/util/enforcing_trait_impls.rs +++ b/lightning/src/util/enforcing_trait_impls.rs @@ -10,7 +10,7 @@ use crate::ln::channel::{ANCHOR_OUTPUT_VALUE_SATOSHI, MIN_CHAN_DUST_LIMIT_SATOSHIS}; use crate::ln::chan_utils::{HTLCOutputInCommitment, ChannelPublicKeys, HolderCommitmentTransaction, CommitmentTransaction, ChannelTransactionParameters, TrustedCommitmentTransaction, ClosingTransaction}; use crate::ln::{chan_utils, msgs, PaymentPreimage}; -use crate::chain::keysinterface::{Sign, InMemorySigner, EcdsaChannelSigner}; +use crate::chain::keysinterface::{Sign, InMemorySigner, ChannelSigner, EcdsaChannelSigner}; use crate::prelude::*; use core::cmp; @@ -90,7 +90,7 @@ impl EnforcingSigner { } } -impl EcdsaChannelSigner for EnforcingSigner { +impl ChannelSigner for EnforcingSigner { fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1) -> PublicKey { self.inner.get_per_commitment_point(idx, secp_ctx) } @@ -114,8 +114,15 @@ impl EcdsaChannelSigner for EnforcingSigner { } fn pubkeys(&self) -> &ChannelPublicKeys { self.inner.pubkeys() } + fn channel_keys_id(&self) -> [u8; 32] { self.inner.channel_keys_id() } + fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters) { + self.inner.provide_channel_parameters(channel_parameters) + } +} + +impl EcdsaChannelSigner for EnforcingSigner { fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, preimages: Vec, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { self.verify_counterparty_commitment_tx(commitment_tx, secp_ctx); @@ -228,10 +235,6 @@ impl EcdsaChannelSigner for EnforcingSigner { ) -> Result { self.inner.sign_channel_announcement_with_funding_key(msg, secp_ctx) } - - fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters) { - self.inner.provide_channel_parameters(channel_parameters) - } } impl Sign for EnforcingSigner {} -- 2.30.2