|
15 | 15 | //! actions such as sending payments, handling events, or changing monitor update return values on |
16 | 16 | //! a per-node basis. This should allow it to find any cases where the ordering of actions results |
17 | 17 | //! 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. |
19 | 20 |
|
20 | 21 | use bitcoin::amount::Amount; |
21 | 22 | use bitcoin::constants::genesis_block; |
@@ -49,7 +50,7 @@ use lightning::events::{self, EventsProvider}; |
49 | 50 | use lightning::ln::channel::{ |
50 | 51 | FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS, |
51 | 52 | }; |
52 | | -use lightning::ln::channel_state::ChannelDetails; |
| 53 | +use lightning::ln::channel_state::{ChannelDetails, InboundHTLCDetails, OutboundHTLCDetails}; |
53 | 54 | use lightning::ln::channelmanager::{ |
54 | 55 | ChainParameters, ChannelManager, ChannelManagerReadArgs, PaymentId, RecentPaymentDetails, |
55 | 56 | TrustedChannelFeatures, |
@@ -562,10 +563,12 @@ impl SignerProvider for KeyProvider { |
562 | 563 | } |
563 | 564 | } |
564 | 565 |
|
565 | | -const SUPPORTED_SIGNER_OPS: [SignerOp; 3] = [ |
| 566 | +const SUPPORTED_SIGNER_OPS: [SignerOp; 5] = [ |
566 | 567 | SignerOp::SignCounterpartyCommitment, |
567 | 568 | SignerOp::GetPerCommitmentPoint, |
568 | 569 | SignerOp::ReleaseCommitmentSecret, |
| 570 | + SignerOp::SignHolderCommitment, |
| 571 | + SignerOp::SignHolderHtlcTransaction, |
569 | 572 | ]; |
570 | 573 |
|
571 | 574 | impl KeyProvider { |
@@ -1323,6 +1326,16 @@ impl<'a> HarnessNode<'a> { |
1323 | 1326 | } |
1324 | 1327 | } |
1325 | 1328 |
|
| 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 | + |
1326 | 1339 | #[derive(Copy, Clone)] |
1327 | 1340 | enum MonitorUpdateSelector { |
1328 | 1341 | First, |
@@ -3012,6 +3025,65 @@ impl<'a, 'd, Out: Output + MaybeSend + MaybeSync> Harness<'a, 'd, Out> { |
3012 | 3025 | assert!(settled, "message-only settle exceeded budget: {}", self.pending_work_summary(),); |
3013 | 3026 | } |
3014 | 3027 |
|
| 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 | + |
3015 | 3087 | fn probe_amount_for_direction( |
3016 | 3088 | &self, source_idx: usize, dest_chan_id: ChannelId, |
3017 | 3089 | ) -> Option<u64> { |
@@ -3931,25 +4003,42 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) { |
3931 | 4003 | harness.nodes[2].node.signer_unblocked(None); |
3932 | 4004 | }, |
3933 | 4005 | 0xcc => { |
3934 | | - harness.nodes[1] |
| 4006 | + harness.nodes[0] |
3935 | 4007 | .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); |
3939 | 4010 | }, |
3940 | 4011 | 0xcd => { |
3941 | 4012 | harness.nodes[1] |
3942 | 4013 | .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); |
3946 | 4016 | }, |
3947 | 4017 | 0xce => { |
3948 | 4018 | harness.nodes[2] |
3949 | 4019 | .keys_manager |
3950 | | - .enable_op_for_all_signers(SignerOp::ReleaseCommitmentSecret); |
| 4020 | + .enable_op_for_all_signers(SignerOp::SignHolderCommitment); |
3951 | 4021 | harness.nodes[2].node.signer_unblocked(None); |
3952 | 4022 | }, |
| 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, "]]]]]"), |
3953 | 4042 |
|
3954 | 4043 | 0xd8 => harness.confirm_broadcasts_for_node(0), |
3955 | 4044 | 0xd9 => harness.confirm_broadcasts_for_node(1), |
|
0 commit comments