Skip to content

Commit 9b536ec

Browse files
author
Antoine Riard
committed
Implement block_disconnect for pruning of waiting-conf HTLC updates
Modify ChainListener API by adding height, txn_matched, indexes_of_txn_matched fields to block_disconnect Modify ChainWatchInterfaceUtil to move to listener previously txn registered as watched at block_disconnected
1 parent 98fc5dc commit 9b536ec

File tree

4 files changed

+44
-6
lines changed

4 files changed

+44
-6
lines changed

src/chain/chaininterface.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ pub trait ChainListener: Sync + Send {
7777
fn block_connected(&self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[u32]);
7878
/// Notifies a listener that a block was disconnected.
7979
/// Unlike block_connected, this *must* never be called twice for the same disconnect event.
80-
fn block_disconnected(&self, header: &BlockHeader);
80+
///
81+
/// Provide listeners with filtered txn previously registered as watched because channels are
82+
/// driven by onchain events (tx broadcast, height), a cancel of one of them may conduct to
83+
/// rollback state (ChannelMonitor or Channel).
84+
fn block_disconnected(&self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[u32]);
8185
}
8286

8387
/// An enum that represents the speed at which we want a transaction to confirm used for feerate
@@ -278,11 +282,20 @@ impl ChainWatchInterfaceUtil {
278282
}
279283

280284
/// Notify listeners that a block was disconnected.
281-
pub fn block_disconnected(&self, header: &BlockHeader) {
285+
pub fn block_disconnected(&self, block: &Block, height: u32) {
282286
let listeners = self.listeners.lock().unwrap().clone();
287+
let mut matched = Vec::new();
288+
let mut matched_index = Vec::new();
289+
let watched = self.watched.lock().unwrap();
290+
for (index, transaction) in block.txdata.iter().enumerate() {
291+
if self.does_match_tx_unguarded(transaction, &watched) {
292+
matched.push(transaction);
293+
matched_index.push(index as u32);
294+
}
295+
}
283296
for listener in listeners.iter() {
284297
match listener.upgrade() {
285-
Some(arc) => arc.block_disconnected(header),
298+
Some(arc) => arc.block_disconnected(&block.header, height, matched.as_slice(), matched_index.as_slice()),
286299
None => ()
287300
}
288301
}

src/ln/channelmanager.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2473,7 +2473,7 @@ impl ChainListener for ChannelManager {
24732473
}
24742474

24752475
/// We force-close the channel without letting our counterparty participate in the shutdown
2476-
fn block_disconnected(&self, header: &BlockHeader) {
2476+
fn block_disconnected(&self, header: &BlockHeader, _: u32, _: &[&Transaction], _: &[u32]) {
24772477
let _ = self.total_consistency_lock.read().unwrap();
24782478
let mut failed_channels = Vec::new();
24792479
{

src/ln/channelmonitor.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,13 @@ impl<Key : Send + cmp::Eq + hash::Hash> ChainListener for SimpleManyChannelMonit
199199
pending_events.append(&mut new_events);
200200
}
201201

202-
fn block_disconnected(&self, _: &BlockHeader) { }
202+
fn block_disconnected(&self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], _: &[u32]) {
203+
let block_hash = header.bitcoin_hash();
204+
let mut monitors = self.monitors.lock().unwrap();
205+
for monitor in monitors.values_mut() {
206+
monitor.block_disconnected(txn_matched, height, &block_hash);
207+
}
208+
}
203209
}
204210

205211
impl<Key : Send + cmp::Eq + hash::Hash + 'static> SimpleManyChannelMonitor<Key> {
@@ -1855,6 +1861,23 @@ impl ChannelMonitor {
18551861
(watch_outputs, spendable_outputs, htlc_updated)
18561862
}
18571863

1864+
fn block_disconnected(&mut self, txn_matched: &[&Transaction], height: u32, block_hash: &Sha256dHash) {
1865+
for tx in txn_matched {
1866+
if tx.input.len() == 1 {
1867+
let txid = tx.txid();
1868+
for (idx, _) in tx.output.iter().enumerate() {
1869+
if let Some(_) = self.htlc_updated_waiting_outpoint_solving.remove(&BitcoinOutPoint { txid, vout: idx as u32 }) {
1870+
//We discard htlc update there as waiting-solving-trigger tx (non-revoked commitment tx) has been disconnected
1871+
}
1872+
}
1873+
}
1874+
}
1875+
if let Some(_) = self.htlc_updated_waiting_threshold_conf.remove(&(height + HTLC_FAIL_ANTI_REORG_DELAY - 1)) {
1876+
//We discard htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected
1877+
}
1878+
self.last_block_hash = block_hash.clone();
1879+
}
1880+
18581881
pub(super) fn would_broadcast_at_height(&self, height: u32) -> bool {
18591882
// We need to consider all HTLCs which are:
18601883
// * in any unrevoked remote commitment transaction, as they could broadcast said

src/ln/functional_tests.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2526,8 +2526,10 @@ fn test_unconf_chan() {
25262526
header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
25272527
headers.push(header.clone());
25282528
}
2529+
let mut height = 99;
25292530
while !headers.is_empty() {
2530-
nodes[0].node.block_disconnected(&headers.pop().unwrap());
2531+
nodes[0].node.block_disconnected(&headers.pop().unwrap(), height, &Vec::new(), &Vec::new());
2532+
height -= 1;
25312533
}
25322534
check_closed_broadcast!(nodes[0]);
25332535
let channel_state = nodes[0].node.channel_state.lock().unwrap();

0 commit comments

Comments
 (0)