Skip to content

Commit 1480262

Browse files
author
Antoine Riard
committed
Implement concurrent broadcast tolerance for distributed watchtowers
With a distrbuted watchtowers deployment, where each monitor is plugged to its own chain view, there is no guarantee that block are going to be seen in same order. Watchtower may diverge in their acceptance of a submitted `commitment_signed` update due to a block timing-out a HTLC and provoking a subset but yet not seen by the other watchtower subset. Any update reject by one of the watchtower must block offchain coordinator to move channel state forward and release revocation secret for previous state. In this case, we want any watchtower from the rejection subset to still be able to claim outputs if the concurrent state, has accepted by the other subset, is confirming. This improve overall watchtower system fault-tolerance. This change stores local commitment transaction unconditionally and fail the update if there is knowledge of an already signed commitment transaction (ChannelMonitor.local_tx_signed=true).
1 parent 3defcc8 commit 1480262

File tree

2 files changed

+15
-27
lines changed

2 files changed

+15
-27
lines changed

lightning/src/ln/channelmonitor.rs

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,10 @@ pub struct ChannelMonitor<ChanSigner: ChannelKeys> {
823823
// Set once we've signed a local commitment transaction and handed it over to our
824824
// OnchainTxHandler. After this is set, no future updates to our local commitment transactions
825825
// may occur, and we fail any such monitor updates.
826+
//
827+
// In case of update rejection due to a locally already signed commitment transaction, we
828+
// nevertheless store update content to track in case of concurrent broadcast by another
829+
// remote monitor out-of-order with regards to the block view.
826830
local_tx_signed: bool,
827831

828832
// We simply modify last_block_hash in Channel's block_connected so that serialization is
@@ -887,6 +891,11 @@ pub trait ManyChannelMonitor: Send + Sync {
887891
///
888892
/// Any spends of outputs which should have been registered which aren't passed to
889893
/// ChannelMonitors via block_connected may result in FUNDS LOSS.
894+
///
895+
/// In case of distributed watchtowers deployment, even if an Err is return, the new version
896+
/// must be written to disk, as state may have been stored but rejected due to a block forcing
897+
/// a commitment broadcast. This storage is used to claim outputs of rejected state confirmed
898+
/// onchain by another watchtower, lagging behind on block processing.
890899
fn update_monitor(&self, funding_txo: OutPoint, monitor: ChannelMonitorUpdate) -> Result<(), ChannelMonitorUpdateErr>;
891900

892901
/// Used by ChannelManager to get list of HTLC resolved onchain and which needed to be updated
@@ -1166,12 +1175,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
11661175
feerate_per_kw: initial_local_commitment_tx.feerate_per_kw,
11671176
htlc_outputs: Vec::new(), // There are never any HTLCs in the initial commitment transactions
11681177
};
1169-
// Returning a monitor error before updating tracking points means in case of using
1170-
// a concurrent watchtower implementation for same channel, if this one doesn't
1171-
// reject update as we do, you MAY have the latest local valid commitment tx onchain
1172-
// for which you want to spend outputs. We're NOT robust again this scenario right
1173-
// now but we should consider it later.
1174-
onchain_tx_handler.provide_latest_local_tx(initial_local_commitment_tx).unwrap();
1178+
onchain_tx_handler.provide_latest_local_tx(initial_local_commitment_tx);
11751179

11761180
ChannelMonitor {
11771181
latest_update_id: 0,
@@ -1326,9 +1330,6 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
13261330
/// up-to-date as our local commitment transaction is updated.
13271331
/// Panics if set_on_local_tx_csv has never been called.
13281332
pub(super) fn provide_latest_local_commitment_tx_info(&mut self, commitment_tx: LocalCommitmentTransaction, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>) -> Result<(), MonitorUpdateError> {
1329-
if self.local_tx_signed {
1330-
return Err(MonitorUpdateError("A local commitment tx has already been signed, no new local commitment txn can be sent to our counterparty"));
1331-
}
13321333
let txid = commitment_tx.txid();
13331334
let sequence = commitment_tx.unsigned_tx.input[0].sequence as u64;
13341335
let locktime = commitment_tx.unsigned_tx.lock_time as u64;
@@ -1342,17 +1343,13 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
13421343
feerate_per_kw: commitment_tx.feerate_per_kw,
13431344
htlc_outputs: htlc_outputs,
13441345
};
1345-
// Returning a monitor error before updating tracking points means in case of using
1346-
// a concurrent watchtower implementation for same channel, if this one doesn't
1347-
// reject update as we do, you MAY have the latest local valid commitment tx onchain
1348-
// for which you want to spend outputs. We're NOT robust again this scenario right
1349-
// now but we should consider it later.
1350-
if let Err(_) = self.onchain_tx_handler.provide_latest_local_tx(commitment_tx) {
1351-
return Err(MonitorUpdateError("Local commitment signed has already been signed, no further update of LOCAL commitment transaction is allowed"));
1352-
}
1346+
self.onchain_tx_handler.provide_latest_local_tx(commitment_tx);
13531347
self.current_local_commitment_number = 0xffff_ffff_ffff - ((((sequence & 0xffffff) << 3*8) | (locktime as u64 & 0xffffff)) ^ self.commitment_transaction_number_obscure_factor);
13541348
mem::swap(&mut new_local_commitment_tx, &mut self.current_local_commitment_tx);
13551349
self.prev_local_signed_commitment_tx = Some(new_local_commitment_tx);
1350+
if self.local_tx_signed {
1351+
return Err(MonitorUpdateError("Latest local commitment signed has already been signed, update is rejected"));
1352+
}
13561353
Ok(())
13571354
}
13581355

lightning/src/ln/onchaintx.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -877,18 +877,9 @@ impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
877877
}
878878
}
879879

880-
pub(super) fn provide_latest_local_tx(&mut self, tx: LocalCommitmentTransaction) -> Result<(), ()> {
881-
// To prevent any unsafe state discrepancy between offchain and onchain, once local
882-
// commitment transaction has been signed due to an event (either block height for
883-
// HTLC-timeout or channel force-closure), don't allow any further update of local
884-
// commitment transaction view to avoid delivery of revocation secret to counterparty
885-
// for the aformentionned signed transaction.
886-
if self.local_htlc_sigs.is_some() || self.prev_local_htlc_sigs.is_some() {
887-
return Err(());
888-
}
880+
pub(super) fn provide_latest_local_tx(&mut self, tx: LocalCommitmentTransaction) {
889881
self.prev_local_commitment = self.local_commitment.take();
890882
self.local_commitment = Some(tx);
891-
Ok(())
892883
}
893884

894885
fn sign_latest_local_htlcs(&mut self) {

0 commit comments

Comments
 (0)