Skip to content

Commit 67bcdec

Browse files
committed
fuzz: add force close actions to chanmon_consistency
1 parent cf85191 commit 67bcdec

1 file changed

Lines changed: 100 additions & 11 deletions

File tree

fuzz/src/chanmon_consistency.rs

Lines changed: 100 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
//! actions such as sending payments, handling events, or changing monitor update return values on
1616
//! a per-node basis. This should allow it to find any cases where the ordering of actions results
1717
//! in us getting out of sync with ourselves, and, assuming at least one of our recieve- or
18-
//! send-side handling is correct, other peers.
18+
//! send-side handling is correct, other peers. The fuzzer also exercises user-initiated
19+
//! force-closes with on-chain commitment transaction confirmation.
1920
2021
use bitcoin::amount::Amount;
2122
use bitcoin::constants::genesis_block;
@@ -49,7 +50,7 @@ use lightning::events::{self, EventsProvider};
4950
use lightning::ln::channel::{
5051
FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS,
5152
};
52-
use lightning::ln::channel_state::ChannelDetails;
53+
use lightning::ln::channel_state::{ChannelDetails, InboundHTLCDetails, OutboundHTLCDetails};
5354
use lightning::ln::channelmanager::{
5455
ChainParameters, ChannelManager, ChannelManagerReadArgs, PaymentId, RecentPaymentDetails,
5556
TrustedChannelFeatures,
@@ -562,10 +563,12 @@ impl SignerProvider for KeyProvider {
562563
}
563564
}
564565

565-
const SUPPORTED_SIGNER_OPS: [SignerOp; 3] = [
566+
const SUPPORTED_SIGNER_OPS: [SignerOp; 5] = [
566567
SignerOp::SignCounterpartyCommitment,
567568
SignerOp::GetPerCommitmentPoint,
568569
SignerOp::ReleaseCommitmentSecret,
570+
SignerOp::SignHolderCommitment,
571+
SignerOp::SignHolderHtlcTransaction,
569572
];
570573

571574
impl KeyProvider {
@@ -1323,6 +1326,16 @@ impl<'a> HarnessNode<'a> {
13231326
}
13241327
}
13251328

1329+
#[inline]
1330+
fn inbound_dust_blocks_path(htlc: &InboundHTLCDetails) -> bool {
1331+
htlc.is_dust
1332+
}
1333+
1334+
#[inline]
1335+
fn outbound_dust_blocks_path(htlc: &OutboundHTLCDetails) -> bool {
1336+
htlc.is_dust
1337+
}
1338+
13261339
#[derive(Copy, Clone)]
13271340
enum MonitorUpdateSelector {
13281341
First,
@@ -3012,6 +3025,65 @@ impl<'a, 'd, Out: Output + MaybeSend + MaybeSync> Harness<'a, 'd, Out> {
30123025
assert!(settled, "message-only settle exceeded budget: {}", self.pending_work_summary(),);
30133026
}
30143027

3028+
fn record_force_close_dust(&self, closer_idx: usize, channel_id: ChannelId) {
3029+
if let Some(channel) = self.nodes[closer_idx]
3030+
.node
3031+
.list_channels()
3032+
.into_iter()
3033+
.find(|chan| chan.channel_id == channel_id)
3034+
{
3035+
let mut dust_parts = channel
3036+
.pending_inbound_htlcs
3037+
.iter()
3038+
.filter(|htlc| inbound_dust_blocks_path(htlc))
3039+
.map(|htlc| (htlc.payment_hash, htlc.amount_msat))
3040+
.chain(
3041+
channel
3042+
.pending_outbound_htlcs
3043+
.iter()
3044+
.filter(|htlc| outbound_dust_blocks_path(htlc))
3045+
.map(|htlc| (htlc.payment_hash, htlc.amount_msat)),
3046+
)
3047+
.collect::<Vec<_>>();
3048+
let payment_paths = self.payments.payment_paths_by_hash.borrow();
3049+
let mut blocked_paths = self.payments.blocked_dust_paths_by_hash.borrow_mut();
3050+
for (payment_hash, amount_msat) in dust_parts.drain(..) {
3051+
let Some(paths) = payment_paths.get(&payment_hash) else {
3052+
continue;
3053+
};
3054+
let blocked_for_hash =
3055+
blocked_paths.entry(payment_hash).or_insert_with(HashSet::new);
3056+
if let Some((path_idx, _)) = paths.iter().enumerate().find(|(path_idx, path)| {
3057+
!blocked_for_hash.contains(path_idx)
3058+
&& path.iter().any(|(chan_id, part_amt)| {
3059+
*chan_id == channel_id && *part_amt == amount_msat
3060+
})
3061+
}) {
3062+
blocked_for_hash.insert(path_idx);
3063+
}
3064+
}
3065+
}
3066+
}
3067+
3068+
fn force_close(
3069+
&mut self, closer_idx: usize, channel_id: ChannelId, counterparty_idx: usize, reason: &str,
3070+
) {
3071+
self.flush_progress(32);
3072+
self.record_force_close_dust(closer_idx, channel_id);
3073+
if self.nodes[closer_idx]
3074+
.node
3075+
.force_close_broadcasting_latest_txn(
3076+
&channel_id,
3077+
&self.nodes[counterparty_idx].our_node_id(),
3078+
reason.to_string(),
3079+
)
3080+
.is_ok()
3081+
{
3082+
self.payments.closed_channels.borrow_mut().insert(channel_id);
3083+
self.flush_progress(32);
3084+
}
3085+
}
3086+
30153087
fn probe_amount_for_direction(
30163088
&self, source_idx: usize, dest_chan_id: ChannelId,
30173089
) -> Option<u64> {
@@ -3931,25 +4003,42 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
39314003
harness.nodes[2].node.signer_unblocked(None);
39324004
},
39334005
0xcc => {
3934-
harness.nodes[1]
4006+
harness.nodes[0]
39354007
.keys_manager
3936-
.enable_op_for_all_signers(SignerOp::ReleaseCommitmentSecret);
3937-
let filter = Some((harness.nodes[0].our_node_id(), harness.chan_a_id()));
3938-
harness.nodes[1].node.signer_unblocked(filter);
4008+
.enable_op_for_all_signers(SignerOp::SignHolderCommitment);
4009+
harness.nodes[0].node.signer_unblocked(None);
39394010
},
39404011
0xcd => {
39414012
harness.nodes[1]
39424013
.keys_manager
3943-
.enable_op_for_all_signers(SignerOp::ReleaseCommitmentSecret);
3944-
let filter = Some((harness.nodes[2].our_node_id(), harness.chan_b_id()));
3945-
harness.nodes[1].node.signer_unblocked(filter);
4014+
.enable_op_for_all_signers(SignerOp::SignHolderCommitment);
4015+
harness.nodes[1].node.signer_unblocked(None);
39464016
},
39474017
0xce => {
39484018
harness.nodes[2]
39494019
.keys_manager
3950-
.enable_op_for_all_signers(SignerOp::ReleaseCommitmentSecret);
4020+
.enable_op_for_all_signers(SignerOp::SignHolderCommitment);
39514021
harness.nodes[2].node.signer_unblocked(None);
39524022
},
4023+
0xcf => {
4024+
harness.nodes[0]
4025+
.keys_manager
4026+
.enable_op_for_all_signers(SignerOp::SignHolderHtlcTransaction);
4027+
harness.nodes[1]
4028+
.keys_manager
4029+
.enable_op_for_all_signers(SignerOp::SignHolderHtlcTransaction);
4030+
harness.nodes[2]
4031+
.keys_manager
4032+
.enable_op_for_all_signers(SignerOp::SignHolderHtlcTransaction);
4033+
harness.nodes[0].node.signer_unblocked(None);
4034+
harness.nodes[1].node.signer_unblocked(None);
4035+
harness.nodes[2].node.signer_unblocked(None);
4036+
},
4037+
4038+
0xd0 => harness.force_close(0, harness.chan_a_id(), 1, "]]]]]]]]]"),
4039+
0xd1 => harness.force_close(1, harness.chan_b_id(), 2, "]]]]]]]]"),
4040+
0xd2 => harness.force_close(1, harness.chan_a_id(), 0, "]]]]]]]"),
4041+
0xd3 => harness.force_close(2, harness.chan_b_id(), 1, "]]]]]"),
39534042

39544043
0xd8 => harness.confirm_broadcasts_for_node(0),
39554044
0xd9 => harness.confirm_broadcasts_for_node(1),

0 commit comments

Comments
 (0)