From 8e3744813a8730a0b4fa6219d314635167947f27 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Thu, 1 Apr 2021 19:44:46 -0700 Subject: [PATCH] Add ChannelMonitor::update_best_block Expose a way for Electrum users to update the best block on a ChannelMonitor independently of confirming transactions. --- lightning/src/chain/channelmonitor.rs | 93 +++++++++++++++++++++++++-- lightning/src/ln/onchaintx.rs | 8 +-- 2 files changed, 90 insertions(+), 11 deletions(-) diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 1a0b3dcd..6481e5a1 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -1304,12 +1304,15 @@ impl ChannelMonitor { header, height, broadcaster, fee_estimator, logger) } - /// Processes transactions from a block with the given header and height, returning new outputs - /// to watch. See [`block_connected`] for details. + /// Processes transactions confirmed in a block with the given header and height, returning new + /// outputs to watch. See [`block_connected`] for details. /// - /// TODO: Expand docs. + /// Used instead of [`block_connected`] by clients that are notified of transactions rather than + /// blocks. May be called before or after [`update_best_block`] for transactions in the + /// corresponding block. See [`update_best_block`] for further calling expectations. /// /// [`block_connected`]: Self::block_connected + /// [`update_best_block`]: Self::update_best_block pub fn transactions_confirmed( &self, header: &BlockHeader, @@ -1327,6 +1330,35 @@ impl ChannelMonitor { self.inner.lock().unwrap().transactions_confirmed( header, txdata, height, broadcaster, fee_estimator, logger) } + + /// Updates the monitor with the current best chain tip, returning new outputs to watch. See + /// [`block_connected`] for details. + /// + /// Used instead of [`block_connected`] by clients that are notified of transactions rather than + /// blocks. May be called before or after [`transactions_confirmed`] for the corresponding + /// block. + /// + /// Must be called after new blocks become available for the most recent block. Intermediary + /// blocks, however, may be safely skipped. + /// + /// [`block_connected`]: Self::block_connected + /// [`transactions_confirmed`]: Self::transactions_confirmed + pub fn update_best_block( + &self, + header: &BlockHeader, + height: u32, + broadcaster: B, + fee_estimator: F, + logger: L, + ) -> Vec<(Txid, Vec<(u32, TxOut)>)> + where + B::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + { + self.inner.lock().unwrap().update_best_block( + header, height, broadcaster, fee_estimator, logger) + } } impl ChannelMonitorImpl { @@ -2028,10 +2060,40 @@ impl ChannelMonitorImpl { F::Target: FeeEstimator, L::Target: Logger, { - self.best_block = BestBlock::new(header.block_hash(), height); + let block_hash = header.block_hash(); + log_trace!(logger, "New best block {} at height {}", block_hash, height); + self.best_block = BestBlock::new(block_hash, height); + self.transactions_confirmed(header, txdata, height, broadcaster, fee_estimator, logger) } + fn update_best_block( + &mut self, + header: &BlockHeader, + height: u32, + broadcaster: B, + fee_estimator: F, + logger: L, + ) -> Vec<(Txid, Vec<(u32, TxOut)>)> + where + B::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + { + let block_hash = header.block_hash(); + log_trace!(logger, "New best block {} at height {}", block_hash, height); + + if height > self.best_block.height() { + self.best_block = BestBlock::new(block_hash, height); + self.block_confirmed(height, vec![], vec![], vec![], broadcaster, fee_estimator, logger) + } else { + self.best_block = BestBlock::new(block_hash, height); + self.onchain_events_waiting_threshold_conf.retain(|ref entry| entry.height <= height); + self.onchain_tx_handler.block_disconnected(height + 1, broadcaster, fee_estimator, logger); + Vec::new() + } + } + fn transactions_confirmed( &mut self, header: &BlockHeader, @@ -2100,11 +2162,28 @@ impl ChannelMonitorImpl { self.is_paying_spendable_output(&tx, height, &logger); } + + self.block_confirmed(height, txn_matched, watch_outputs, claimable_outpoints, broadcaster, fee_estimator, logger) + } + + fn block_confirmed( + &mut self, + height: u32, + txn_matched: Vec<&Transaction>, + mut watch_outputs: Vec<(Txid, Vec<(u32, TxOut)>)>, + mut claimable_outpoints: Vec, + broadcaster: B, + fee_estimator: F, + logger: L, + ) -> Vec<(Txid, Vec<(u32, TxOut)>)> + where + B::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + { let should_broadcast = self.would_broadcast_at_height(height, &logger); if should_broadcast { claimable_outpoints.push(ClaimRequest { absolute_timelock: height, aggregable: false, outpoint: BitcoinOutPoint { txid: self.funding_info.0.txid.clone(), vout: self.funding_info.0.index as u32 }, witness_data: InputMaterial::Funding { funding_redeemscript: self.funding_redeemscript.clone() }}); - } - if should_broadcast { self.pending_monitor_events.push(MonitorEvent::CommitmentTxBroadcasted(self.funding_info.0)); let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript); self.holder_tx_signed = true; @@ -2211,7 +2290,7 @@ impl ChannelMonitorImpl { //We may discard: //- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected //- maturing spendable output has transaction paying us has been disconnected - self.onchain_events_waiting_threshold_conf.retain(|ref entry| entry.height != height); + self.onchain_events_waiting_threshold_conf.retain(|ref entry| entry.height < height); self.onchain_tx_handler.block_disconnected(height, broadcaster, fee_estimator, logger); diff --git a/lightning/src/ln/onchaintx.rs b/lightning/src/ln/onchaintx.rs index 7613fa2b..67a82a48 100644 --- a/lightning/src/ln/onchaintx.rs +++ b/lightning/src/ln/onchaintx.rs @@ -848,8 +848,8 @@ impl OnchainTxHandler { // Check if any pending claim request must be rescheduled for (first_claim_txid, ref claim_data) in self.pending_claim_requests.iter() { - if let Some(h) = claim_data.height_timer { - if h == height { + if let Some(height_timer) = claim_data.height_timer { + if height >= height_timer { bump_candidates.insert(*first_claim_txid, (*claim_data).clone()); } } @@ -878,7 +878,7 @@ impl OnchainTxHandler { let onchain_events_waiting_threshold_conf = self.onchain_events_waiting_threshold_conf.drain(..).collect::>(); for entry in onchain_events_waiting_threshold_conf { - if entry.height == height { + if entry.height >= height { //- our claim tx on a commitment tx output //- resurect outpoint back in its claimable set and regenerate tx match entry.event { @@ -912,7 +912,7 @@ impl OnchainTxHandler { // right now if one of the outpoint get disconnected, just erase whole pending claim request. let mut remove_request = Vec::new(); self.claimable_outpoints.retain(|_, ref v| - if v.1 == height { + if v.1 >= height { remove_request.push(v.0.clone()); false } else { true }); -- 2.30.2