Enable monitor to rebuild initial counterparty commitment tx
authorAlec Chen <alecchendev@gmail.com>
Wed, 12 Jul 2023 18:14:10 +0000 (13:14 -0500)
committerAlec Chen <alecchendev@gmail.com>
Wed, 23 Aug 2023 17:33:07 +0000 (12:33 -0500)
Upon creating a channel monitor, it is provided with the initial
counterparty commitment transaction info directly before the very first
time it is persisted. Because of this, the very first counterparty
commitment is not seen as an update in the persistence pipeline, and so
our previous changes to the monitor and updates cannot be used to
reconstruct this commitment.

To be able to expose the counterparty's transaction for the very first
commitment, we add a thin wrapper around
`provide_latest_counterparty_commitment_tx`, that stores the necessary
data needed to reconstruct the initial commitment transaction in the
monitor.

lightning/src/chain/channelmonitor.rs
lightning/src/ln/channel.rs

index cb267524fb0ff18d1c2f312a8a5741efaa3300a9..0c207fa8b17429b32e31f1eda65ea0270dcdad82 100644 (file)
@@ -33,6 +33,7 @@ use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
 use bitcoin::secp256k1::{SecretKey, PublicKey};
 use bitcoin::secp256k1;
 
+use crate::ln::channel::INITIAL_COMMITMENT_NUMBER;
 use crate::ln::{PaymentHash, PaymentPreimage};
 use crate::ln::msgs::DecodeError;
 use crate::ln::chan_utils;
@@ -888,6 +889,14 @@ pub(crate) struct ChannelMonitorImpl<Signer: WriteableEcdsaChannelSigner> {
 
        /// The node_id of our counterparty
        counterparty_node_id: Option<PublicKey>,
+
+       /// Initial counterparty commmitment data needed to recreate the commitment tx
+       /// in the persistence pipeline for third-party watchtowers. This will only be present on
+       /// monitors created after 0.0.117.
+       ///
+       /// Ordering of tuple data: (their_per_commitment_point, feerate_per_kw, to_broadcaster_sats,
+       /// to_countersignatory_sats)
+       initial_counterparty_commitment_info: Option<(PublicKey, u32, u64, u64)>,
 }
 
 /// Transaction outputs to watch for on-chain spends.
@@ -1078,6 +1087,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signe
                        (11, self.confirmed_commitment_tx_counterparty_output, option),
                        (13, self.spendable_txids_confirmed, required_vec),
                        (15, self.counterparty_fulfilled_htlcs, required),
+                       (17, self.initial_counterparty_commitment_info, option),
                });
 
                Ok(())
@@ -1228,6 +1238,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
 
                        best_block,
                        counterparty_node_id: Some(counterparty_node_id),
+                       initial_counterparty_commitment_info: None,
                })
        }
 
@@ -1236,11 +1247,31 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                self.inner.lock().unwrap().provide_secret(idx, secret)
        }
 
+       /// A variant of `Self::provide_latest_counterparty_commitment_tx` used to provide
+       /// additional information to the monitor to store in order to recreate the initial
+       /// counterparty commitment transaction during persistence (mainly for use in third-party
+       /// watchtowers).
+       ///
+       /// This is used to provide the counterparty commitment information directly to the monitor
+       /// before the initial persistence of a new channel.
+       pub(crate) fn provide_initial_counterparty_commitment_tx<L: Deref>(
+               &self, txid: Txid, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>,
+               commitment_number: u64, their_cur_per_commitment_point: PublicKey, feerate_per_kw: u32,
+               to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, logger: &L,
+       )
+       where L::Target: Logger
+       {
+               self.inner.lock().unwrap().provide_initial_counterparty_commitment_tx(txid,
+                       htlc_outputs, commitment_number, their_cur_per_commitment_point, feerate_per_kw,
+                       to_broadcaster_value_sat, to_countersignatory_value_sat, logger);
+       }
+
        /// Informs this monitor of the latest counterparty (ie non-broadcastable) commitment transaction.
        /// The monitor watches for it to be broadcasted and then uses the HTLC information (and
        /// possibly future revocation/preimage information) to claim outputs where possible.
        /// We cache also the mapping hash:commitment number to lighten pruning of old preimages by watchtowers.
-       pub(crate) fn provide_latest_counterparty_commitment_tx<L: Deref>(
+       #[cfg(test)]
+       fn provide_latest_counterparty_commitment_tx<L: Deref>(
                &self,
                txid: Txid,
                htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>,
@@ -1376,6 +1407,22 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                ret
        }
 
+       /// Gets the counterparty's initial commitment transaction. The returned commitment
+       /// transaction is unsigned. This is intended to be called during the initial persistence of
+       /// the monitor (inside an implementation of [`Persist::persist_new_channel`]), to allow for
+       /// watchtowers in the persistence pipeline to have enough data to form justice transactions.
+       ///
+       /// This is similar to [`Self::counterparty_commitment_txs_from_update`], except
+       /// that for the initial commitment transaction, we don't have a corresponding update.
+       ///
+       /// This will only return `Some` for channel monitors that have been created after upgrading
+       /// to LDK 0.0.117+.
+       ///
+       /// [`Persist::persist_new_channel`]: crate::chain::chainmonitor::Persist::persist_new_channel
+       pub fn initial_counterparty_commitment_tx(&self) -> Option<CommitmentTransaction> {
+               self.inner.lock().unwrap().initial_counterparty_commitment_tx()
+       }
+
        /// Gets all of the counterparty commitment transactions provided by the given update. This
        /// may be empty if the update doesn't include any new counterparty commitments. Returned
        /// commitment transactions are unsigned.
@@ -2255,6 +2302,25 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                Ok(())
        }
 
+       pub(crate) fn provide_initial_counterparty_commitment_tx<L: Deref>(
+               &mut self, txid: Txid, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>,
+               commitment_number: u64, their_per_commitment_point: PublicKey, feerate_per_kw: u32,
+               to_broadcaster_value: u64, to_countersignatory_value: u64, logger: &L
+       )
+       where L::Target: Logger
+       {
+               self.initial_counterparty_commitment_info = Some((their_per_commitment_point.clone(),
+                       feerate_per_kw, to_broadcaster_value, to_countersignatory_value));
+
+               #[cfg(debug_assertions)] {
+                       let rebuilt_commitment_tx = self.initial_counterparty_commitment_tx().unwrap();
+                       debug_assert_eq!(rebuilt_commitment_tx.trust().txid(), txid);
+               }
+
+               self.provide_latest_counterparty_commitment_tx(txid, htlc_outputs, commitment_number,
+                               their_per_commitment_point, logger);
+       }
+
        pub(crate) fn provide_latest_counterparty_commitment_tx<L: Deref>(&mut self, txid: Txid, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>, commitment_number: u64, their_per_commitment_point: PublicKey, logger: &L) where L::Target: Logger {
                // TODO: Encrypt the htlc_outputs data with the single-hash of the commitment transaction
                // so that a remote monitor doesn't learn anything unless there is a malicious close.
@@ -2684,6 +2750,17 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                ret
        }
 
+       pub(crate) fn initial_counterparty_commitment_tx(&mut self) -> Option<CommitmentTransaction> {
+               let (their_per_commitment_point, feerate_per_kw, to_broadcaster_value,
+                       to_countersignatory_value) = self.initial_counterparty_commitment_info?;
+               let htlc_outputs = vec![];
+
+               let commitment_tx = self.build_counterparty_commitment_tx(INITIAL_COMMITMENT_NUMBER,
+                       &their_per_commitment_point, to_broadcaster_value, to_countersignatory_value,
+                       feerate_per_kw, htlc_outputs);
+               Some(commitment_tx)
+       }
+
        fn build_counterparty_commitment_tx(
                &self, commitment_number: u64, their_per_commitment_point: &PublicKey,
                to_broadcaster_value: u64, to_countersignatory_value: u64, feerate_per_kw: u32,
@@ -4195,6 +4272,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
                let mut confirmed_commitment_tx_counterparty_output = None;
                let mut spendable_txids_confirmed = Some(Vec::new());
                let mut counterparty_fulfilled_htlcs = Some(HashMap::new());
+               let mut initial_counterparty_commitment_info = None;
                read_tlv_fields!(reader, {
                        (1, funding_spend_confirmed, option),
                        (3, htlcs_resolved_on_chain, optional_vec),
@@ -4204,6 +4282,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
                        (11, confirmed_commitment_tx_counterparty_output, option),
                        (13, spendable_txids_confirmed, optional_vec),
                        (15, counterparty_fulfilled_htlcs, option),
+                       (17, initial_counterparty_commitment_info, option),
                });
 
                Ok((best_block.block_hash(), ChannelMonitor::from_impl(ChannelMonitorImpl {
@@ -4259,6 +4338,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
 
                        best_block,
                        counterparty_node_id,
+                       initial_counterparty_commitment_info,
                })))
        }
 }
index 0dcb56ff65c67f5644c197bb68dd889fd3fbdc23..a8e8c8053b6b5d627ad02776665cde72cf2cb3ec 100644 (file)
@@ -2534,7 +2534,13 @@ impl<SP: Deref> Channel<SP> where
                                                          obscure_factor,
                                                          holder_commitment_tx, best_block, self.context.counterparty_node_id);
 
-               channel_monitor.provide_latest_counterparty_commitment_tx(counterparty_initial_bitcoin_tx.txid, Vec::new(), self.context.cur_counterparty_commitment_transaction_number, self.context.counterparty_cur_commitment_point.unwrap(), logger);
+               channel_monitor.provide_initial_counterparty_commitment_tx(
+                       counterparty_initial_bitcoin_tx.txid, Vec::new(),
+                       self.context.cur_counterparty_commitment_transaction_number,
+                       self.context.counterparty_cur_commitment_point.unwrap(),
+                       counterparty_initial_commitment_tx.feerate_per_kw(),
+                       counterparty_initial_commitment_tx.to_broadcaster_value_sat(),
+                       counterparty_initial_commitment_tx.to_countersignatory_value_sat(), logger);
 
                assert_eq!(self.context.channel_state & (ChannelState::MonitorUpdateInProgress as u32), 0); // We have no had any monitor(s) yet to fail update!
                self.context.channel_state = ChannelState::FundingSent as u32;
@@ -6464,7 +6470,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
                self.generate_accept_channel_message()
        }
 
-       fn funding_created_signature<L: Deref>(&mut self, sig: &Signature, logger: &L) -> Result<(Txid, CommitmentTransaction, Signature), ChannelError> where L::Target: Logger {
+       fn funding_created_signature<L: Deref>(&mut self, sig: &Signature, logger: &L) -> Result<(CommitmentTransaction, CommitmentTransaction, Signature), ChannelError> where L::Target: Logger {
                let funding_script = self.context.get_funding_redeemscript();
 
                let keys = self.context.build_holder_transaction_keys(self.context.cur_holder_commitment_transaction_number);
@@ -6496,7 +6502,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
                                        .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0;
 
                                // We sign "counterparty" commitment transaction, allowing them to broadcast the tx if they wish.
-                               Ok((counterparty_initial_bitcoin_tx.txid, initial_commitment_tx, counterparty_signature))
+                               Ok((counterparty_initial_commitment_tx, initial_commitment_tx, counterparty_signature))
                        }
                }
        }
@@ -6528,7 +6534,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
                // funding_created_signature may fail.
                self.context.holder_signer.as_mut().provide_channel_parameters(&self.context.channel_transaction_parameters);
 
-               let (counterparty_initial_commitment_txid, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
+               let (counterparty_initial_commitment_tx, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
                        Ok(res) => res,
                        Err(ChannelError::Close(e)) => {
                                self.context.channel_transaction_parameters.funding_outpoint = None;
@@ -6569,7 +6575,12 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
                                                          obscure_factor,
                                                          holder_commitment_tx, best_block, self.context.counterparty_node_id);
 
-               channel_monitor.provide_latest_counterparty_commitment_tx(counterparty_initial_commitment_txid, Vec::new(), self.context.cur_counterparty_commitment_transaction_number, self.context.counterparty_cur_commitment_point.unwrap(), logger);
+               channel_monitor.provide_initial_counterparty_commitment_tx(
+                       counterparty_initial_commitment_tx.trust().txid(), Vec::new(),
+                       self.context.cur_counterparty_commitment_transaction_number,
+                       self.context.counterparty_cur_commitment_point.unwrap(), self.context.feerate_per_kw,
+                       counterparty_initial_commitment_tx.to_broadcaster_value_sat(),
+                       counterparty_initial_commitment_tx.to_countersignatory_value_sat(), logger);
 
                self.context.channel_state = ChannelState::FundingSent as u32;
                self.context.channel_id = funding_txo.to_channel_id();