Skip to content

Commit d21a356

Browse files
joostjagerclaude
andcommitted
chanmon_consistency: resolve timelocked HTLCs during settlement
When channels have been force-closed, the 0xff settlement needs to advance chain height past HTLC cltv_expiry timelocks so that the OnchainTxHandler releases timelocked HTLC-timeout claim packages for broadcast. Without this, HTLC-timeout transactions would never be generated and in-flight HTLCs would remain unresolved. The settlement advances in two phases of 250 blocks each: 1. Past cltv_expiry: triggers HTLC-timeout tx broadcasts, which the existing drain-and-confirm loop picks up and confirms on-chain. 2. Past the CSV delay (BREAKDOWN_TIMEOUT=144): allows SpendableOutputs events to fire for both the to_local output and resolved HTLC outputs. Each phase syncs all nodes to the new chain tip and runs process_all_events\!() to drain all resulting messages, events, broadcasts, and monitor updates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 15e2359 commit d21a356

1 file changed

Lines changed: 55 additions & 0 deletions

File tree

fuzz/src/chanmon_consistency.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2890,6 +2890,61 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(
28902890
}
28912891
process_all_events!();
28922892

2893+
// If any channels were force-closed, advance chain height past HTLC
2894+
// timelocks so HTLC-timeout transactions can be broadcast, confirmed,
2895+
// and fully resolved. We advance in two phases:
2896+
// 1) Past cltv_expiry so HTLC-timeout txs are released
2897+
// 2) Past the CSV delay so SpendableOutputs events fire
2898+
if !closed_channels.borrow().is_empty() {
2899+
chain_state.advance_height(250);
2900+
sync_with_chain_state(
2901+
&chain_state,
2902+
&nodes[0],
2903+
&monitor_a,
2904+
&mut node_height_a,
2905+
None,
2906+
);
2907+
sync_with_chain_state(
2908+
&chain_state,
2909+
&nodes[1],
2910+
&monitor_b,
2911+
&mut node_height_b,
2912+
None,
2913+
);
2914+
sync_with_chain_state(
2915+
&chain_state,
2916+
&nodes[2],
2917+
&monitor_c,
2918+
&mut node_height_c,
2919+
None,
2920+
);
2921+
process_all_events!();
2922+
2923+
chain_state.advance_height(250);
2924+
sync_with_chain_state(
2925+
&chain_state,
2926+
&nodes[0],
2927+
&monitor_a,
2928+
&mut node_height_a,
2929+
None,
2930+
);
2931+
sync_with_chain_state(
2932+
&chain_state,
2933+
&nodes[1],
2934+
&monitor_b,
2935+
&mut node_height_b,
2936+
None,
2937+
);
2938+
sync_with_chain_state(
2939+
&chain_state,
2940+
&nodes[2],
2941+
&monitor_c,
2942+
&mut node_height_c,
2943+
None,
2944+
);
2945+
process_all_events!();
2946+
}
2947+
28932948
// Verify no payments are stuck - all should have resolved
28942949
for (idx, pending) in pending_payments.borrow().iter().enumerate() {
28952950
assert!(

0 commit comments

Comments
 (0)