|
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, |
@@ -765,10 +766,12 @@ impl SignerProvider for KeyProvider { |
765 | 766 | } |
766 | 767 | } |
767 | 768 |
|
768 | | -const SUPPORTED_SIGNER_OPS: [SignerOp; 3] = [ |
| 769 | +const SUPPORTED_SIGNER_OPS: [SignerOp; 5] = [ |
769 | 770 | SignerOp::SignCounterpartyCommitment, |
770 | 771 | SignerOp::GetPerCommitmentPoint, |
771 | 772 | SignerOp::ReleaseCommitmentSecret, |
| 773 | + SignerOp::SignHolderCommitment, |
| 774 | + SignerOp::SignHolderHtlcTransaction, |
772 | 775 | ]; |
773 | 776 |
|
774 | 777 | impl KeyProvider { |
@@ -1080,6 +1083,12 @@ impl<'a> HarnessNode<'a> { |
1080 | 1083 | self.node.timer_tick_occurred(); |
1081 | 1084 | } |
1082 | 1085 |
|
| 1086 | + fn enable_holder_signer_ops(&self) { |
| 1087 | + self.keys_manager.enable_op_for_all_signers(SignerOp::SignHolderCommitment); |
| 1088 | + self.keys_manager.enable_op_for_all_signers(SignerOp::SignHolderHtlcTransaction); |
| 1089 | + self.node.signer_unblocked(None); |
| 1090 | + } |
| 1091 | + |
1083 | 1092 | fn current_feerate_sat_per_kw(&self) -> FeeRate { |
1084 | 1093 | self.fee_estimator.feerate_sat_per_kw() |
1085 | 1094 | } |
@@ -1238,6 +1247,16 @@ impl<'a> HarnessNode<'a> { |
1238 | 1247 | } |
1239 | 1248 | } |
1240 | 1249 |
|
| 1250 | +#[inline] |
| 1251 | +fn inbound_dust_blocks_path(htlc: &InboundHTLCDetails) -> bool { |
| 1252 | + htlc.is_dust |
| 1253 | +} |
| 1254 | + |
| 1255 | +#[inline] |
| 1256 | +fn outbound_dust_blocks_path(htlc: &OutboundHTLCDetails) -> bool { |
| 1257 | + htlc.is_dust |
| 1258 | +} |
| 1259 | + |
1241 | 1260 | #[derive(Copy, Clone)] |
1242 | 1261 | enum MonitorReloadSelector { |
1243 | 1262 | Persisted, |
@@ -3663,6 +3682,65 @@ impl<'a, Out: Output + MaybeSend + MaybeSync> Harness<'a, Out> { |
3663 | 3682 | assert!(settled, "message-only settle exceeded budget: {}", self.pending_work_summary(),); |
3664 | 3683 | } |
3665 | 3684 |
|
| 3685 | + fn record_force_close_dust(&self, closer_idx: usize, channel_id: ChannelId) { |
| 3686 | + if let Some(channel) = self.nodes[closer_idx] |
| 3687 | + .node |
| 3688 | + .list_channels() |
| 3689 | + .into_iter() |
| 3690 | + .find(|chan| chan.channel_id == channel_id) |
| 3691 | + { |
| 3692 | + let mut dust_parts = channel |
| 3693 | + .pending_inbound_htlcs |
| 3694 | + .iter() |
| 3695 | + .filter(|htlc| inbound_dust_blocks_path(htlc)) |
| 3696 | + .map(|htlc| (htlc.payment_hash, htlc.amount_msat)) |
| 3697 | + .chain( |
| 3698 | + channel |
| 3699 | + .pending_outbound_htlcs |
| 3700 | + .iter() |
| 3701 | + .filter(|htlc| outbound_dust_blocks_path(htlc)) |
| 3702 | + .map(|htlc| (htlc.payment_hash, htlc.amount_msat)), |
| 3703 | + ) |
| 3704 | + .collect::<Vec<_>>(); |
| 3705 | + let payment_paths = self.payments.payment_paths_by_hash.borrow(); |
| 3706 | + let mut blocked_paths = self.payments.blocked_dust_paths_by_hash.borrow_mut(); |
| 3707 | + for (payment_hash, amount_msat) in dust_parts.drain(..) { |
| 3708 | + let Some(paths) = payment_paths.get(&payment_hash) else { |
| 3709 | + continue; |
| 3710 | + }; |
| 3711 | + let blocked_for_hash = |
| 3712 | + blocked_paths.entry(payment_hash).or_insert_with(HashSet::new); |
| 3713 | + if let Some((path_idx, _)) = paths.iter().enumerate().find(|(path_idx, path)| { |
| 3714 | + !blocked_for_hash.contains(path_idx) |
| 3715 | + && path.iter().any(|(chan_id, part_amt)| { |
| 3716 | + *chan_id == channel_id && *part_amt == amount_msat |
| 3717 | + }) |
| 3718 | + }) { |
| 3719 | + blocked_for_hash.insert(path_idx); |
| 3720 | + } |
| 3721 | + } |
| 3722 | + } |
| 3723 | + } |
| 3724 | + |
| 3725 | + fn force_close( |
| 3726 | + &mut self, closer_idx: usize, channel_id: ChannelId, counterparty_idx: usize, reason: &str, |
| 3727 | + ) { |
| 3728 | + self.flush_progress(32); |
| 3729 | + self.record_force_close_dust(closer_idx, channel_id); |
| 3730 | + if self.nodes[closer_idx] |
| 3731 | + .node |
| 3732 | + .force_close_broadcasting_latest_txn( |
| 3733 | + &channel_id, |
| 3734 | + &self.nodes[counterparty_idx].get_our_node_id(), |
| 3735 | + reason.to_string(), |
| 3736 | + ) |
| 3737 | + .is_ok() |
| 3738 | + { |
| 3739 | + self.payments.closed_channels.borrow_mut().insert(channel_id); |
| 3740 | + self.flush_progress(32); |
| 3741 | + } |
| 3742 | + } |
| 3743 | + |
3666 | 3744 | fn probe_amount_for_direction( |
3667 | 3745 | &self, source_idx: usize, dest_chan_id: ChannelId, |
3668 | 3746 | ) -> Option<u64> { |
@@ -4101,6 +4179,19 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) { |
4101 | 4179 | harness.nodes[2].signer_unblocked(None); |
4102 | 4180 | }, |
4103 | 4181 |
|
| 4182 | + 0xd0 => harness.force_close(0, harness.chan_a_id(), 1, "]]]]]]]]]"), |
| 4183 | + 0xd1 => harness.force_close(1, harness.chan_b_id(), 2, "]]]]]]]]"), |
| 4184 | + 0xd2 => harness.force_close(1, harness.chan_a_id(), 0, "]]]]]]]"), |
| 4185 | + 0xd3 => harness.force_close(2, harness.chan_b_id(), 1, "]]]]]"), |
| 4186 | + 0xd4 => harness.nodes[0].enable_holder_signer_ops(), |
| 4187 | + 0xd5 => harness.nodes[1].enable_holder_signer_ops(), |
| 4188 | + 0xd6 => harness.nodes[2].enable_holder_signer_ops(), |
| 4189 | + 0xd7 => { |
| 4190 | + harness.nodes[0].enable_holder_signer_ops(); |
| 4191 | + harness.nodes[1].enable_holder_signer_ops(); |
| 4192 | + harness.nodes[2].enable_holder_signer_ops(); |
| 4193 | + }, |
| 4194 | + |
4104 | 4195 | 0xd8 => harness.confirm_broadcasts_for_node(0), |
4105 | 4196 | 0xd9 => harness.confirm_broadcasts_for_node(1), |
4106 | 4197 | 0xda => harness.confirm_broadcasts_for_node(2), |
|
0 commit comments