From e46e183084ed858f41aa304acd78503aea1a96ed Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Fri, 27 Mar 2020 17:53:52 -0400 Subject: [PATCH] Prevent any update of local commitment transaction once signed To prevent any unsafe state discrepancy between offchain and onchain, once local commitment transaction has been signed due to an event (either block height for HTLC-timeout or channel force-closure), don't allow any further update of local commitment transaction view to avoid delivery of revocation secret to counterparty for the aformentionned signed transaction. --- lightning/src/ln/chan_utils.rs | 11 ++++++++++- lightning/src/ln/channelmonitor.rs | 11 +++++++++-- lightning/src/ln/onchaintx.rs | 11 ++++++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index 0ef269c8..a5e0c146 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -522,9 +522,18 @@ pub struct LocalCommitmentTransaction { impl LocalCommitmentTransaction { #[cfg(test)] pub fn dummy() -> Self { + let dummy_input = TxIn { + previous_output: OutPoint { + txid: Default::default(), + vout: 0, + }, + script_sig: Default::default(), + sequence: 0, + witness: vec![vec![], vec![], vec![]] + }; Self { tx: Transaction { version: 2, - input: Vec::new(), + input: vec![dummy_input], output: Vec::new(), lock_time: 0, } } diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index 70c92143..2fa416ed 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -1241,10 +1241,18 @@ impl ChannelMonitor { if self.their_to_self_delay.is_none() { return Err(MonitorUpdateError("Got a local commitment tx info update before we'd set basic information about the channel")); } + // Returning a monitor error before updating tracking points means in case of using + // a concurrent watchtower implementation for same channel, if this one doesn't + // reject update as we do, you MAY have the latest local valid commitment tx onchain + // for which you want to spend outputs. We're NOT robust again this scenario right + // now but we should consider it later. + if let Err(_) = self.onchain_tx_handler.provide_latest_local_tx(commitment_tx.clone()) { + return Err(MonitorUpdateError("Local commitment signed has already been signed, no further update of LOCAL commitment transaction is allowed")); + } self.prev_local_signed_commitment_tx = self.current_local_signed_commitment_tx.take(); self.current_local_signed_commitment_tx = Some(LocalSignedTx { txid: commitment_tx.txid(), - tx: commitment_tx.clone(), + tx: commitment_tx, revocation_key: local_keys.revocation_key, a_htlc_key: local_keys.a_htlc_key, b_htlc_key: local_keys.b_htlc_key, @@ -1253,7 +1261,6 @@ impl ChannelMonitor { feerate_per_kw, htlc_outputs, }); - self.onchain_tx_handler.provide_latest_local_tx(commitment_tx); Ok(()) } diff --git a/lightning/src/ln/onchaintx.rs b/lightning/src/ln/onchaintx.rs index 4c935ea6..d0d1f29e 100644 --- a/lightning/src/ln/onchaintx.rs +++ b/lightning/src/ln/onchaintx.rs @@ -725,8 +725,17 @@ impl OnchainTxHandler { } } - pub(super) fn provide_latest_local_tx(&mut self, tx: LocalCommitmentTransaction) { + pub(super) fn provide_latest_local_tx(&mut self, tx: LocalCommitmentTransaction) -> Result<(), ()> { + // To prevent any unsafe state discrepancy between offchain and onchain, once local + // commitment transaction has been signed due to an event (either block height for + // HTLC-timeout or channel force-closure), don't allow any further update of local + // commitment transaction view to avoid delivery of revocation secret to counterparty + // for the aformentionned signed transaction. + if let Some(ref local_commitment) = self.local_commitment { + if local_commitment.has_local_sig() { return Err(()) } + } self.prev_local_commitment = self.local_commitment.take(); self.local_commitment = Some(tx); + Ok(()) } } -- 2.30.2