|
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 { |
@@ -1054,6 +1057,12 @@ impl<'a> HarnessNode<'a> { |
1054 | 1057 | self.node.timer_tick_occurred(); |
1055 | 1058 | } |
1056 | 1059 |
|
| 1060 | + fn enable_holder_signer_ops(&self) { |
| 1061 | + self.keys_manager.enable_op_for_all_signers(SignerOp::SignHolderCommitment); |
| 1062 | + self.keys_manager.enable_op_for_all_signers(SignerOp::SignHolderHtlcTransaction); |
| 1063 | + self.node.signer_unblocked(None); |
| 1064 | + } |
| 1065 | + |
1057 | 1066 | fn current_feerate_sat_per_kw(&self) -> FeeRate { |
1058 | 1067 | self.fee_estimator.feerate_sat_per_kw() |
1059 | 1068 | } |
@@ -1210,6 +1219,16 @@ impl<'a> HarnessNode<'a> { |
1210 | 1219 | } |
1211 | 1220 | } |
1212 | 1221 |
|
| 1222 | +#[inline] |
| 1223 | +fn inbound_dust_blocks_path(htlc: &InboundHTLCDetails) -> bool { |
| 1224 | + htlc.is_dust |
| 1225 | +} |
| 1226 | + |
| 1227 | +#[inline] |
| 1228 | +fn outbound_dust_blocks_path(htlc: &OutboundHTLCDetails) -> bool { |
| 1229 | + htlc.is_dust |
| 1230 | +} |
| 1231 | + |
1213 | 1232 | #[derive(Copy, Clone)] |
1214 | 1233 | enum MonitorReloadSelector { |
1215 | 1234 | Persisted, |
@@ -3627,6 +3646,65 @@ impl<'a, Out: Output + MaybeSend + MaybeSync> Harness<'a, Out> { |
3627 | 3646 | assert!(settled, "message-only settle exceeded budget: {}", self.pending_work_summary(),); |
3628 | 3647 | } |
3629 | 3648 |
|
| 3649 | + fn record_force_close_dust(&self, closer_idx: usize, channel_id: ChannelId) { |
| 3650 | + if let Some(channel) = self.nodes[closer_idx] |
| 3651 | + .node |
| 3652 | + .list_channels() |
| 3653 | + .into_iter() |
| 3654 | + .find(|chan| chan.channel_id == channel_id) |
| 3655 | + { |
| 3656 | + let mut dust_parts = channel |
| 3657 | + .pending_inbound_htlcs |
| 3658 | + .iter() |
| 3659 | + .filter(|htlc| inbound_dust_blocks_path(htlc)) |
| 3660 | + .map(|htlc| (htlc.payment_hash, htlc.amount_msat)) |
| 3661 | + .chain( |
| 3662 | + channel |
| 3663 | + .pending_outbound_htlcs |
| 3664 | + .iter() |
| 3665 | + .filter(|htlc| outbound_dust_blocks_path(htlc)) |
| 3666 | + .map(|htlc| (htlc.payment_hash, htlc.amount_msat)), |
| 3667 | + ) |
| 3668 | + .collect::<Vec<_>>(); |
| 3669 | + let payment_paths = self.payments.payment_paths_by_hash.borrow(); |
| 3670 | + let mut blocked_paths = self.payments.blocked_dust_paths_by_hash.borrow_mut(); |
| 3671 | + for (payment_hash, amount_msat) in dust_parts.drain(..) { |
| 3672 | + let Some(paths) = payment_paths.get(&payment_hash) else { |
| 3673 | + continue; |
| 3674 | + }; |
| 3675 | + let blocked_for_hash = |
| 3676 | + blocked_paths.entry(payment_hash).or_insert_with(HashSet::new); |
| 3677 | + if let Some((path_idx, _)) = paths.iter().enumerate().find(|(path_idx, path)| { |
| 3678 | + !blocked_for_hash.contains(path_idx) |
| 3679 | + && path.iter().any(|(chan_id, part_amt)| { |
| 3680 | + *chan_id == channel_id && *part_amt == amount_msat |
| 3681 | + }) |
| 3682 | + }) { |
| 3683 | + blocked_for_hash.insert(path_idx); |
| 3684 | + } |
| 3685 | + } |
| 3686 | + } |
| 3687 | + } |
| 3688 | + |
| 3689 | + fn force_close( |
| 3690 | + &mut self, closer_idx: usize, channel_id: ChannelId, counterparty_idx: usize, reason: &str, |
| 3691 | + ) { |
| 3692 | + self.flush_progress(32); |
| 3693 | + self.record_force_close_dust(closer_idx, channel_id); |
| 3694 | + if self.nodes[closer_idx] |
| 3695 | + .node |
| 3696 | + .force_close_broadcasting_latest_txn( |
| 3697 | + &channel_id, |
| 3698 | + &self.nodes[counterparty_idx].get_our_node_id(), |
| 3699 | + reason.to_string(), |
| 3700 | + ) |
| 3701 | + .is_ok() |
| 3702 | + { |
| 3703 | + self.payments.closed_channels.borrow_mut().insert(channel_id); |
| 3704 | + self.flush_progress(32); |
| 3705 | + } |
| 3706 | + } |
| 3707 | + |
3630 | 3708 | fn probe_amount_for_direction( |
3631 | 3709 | &self, source_idx: usize, dest_chan_id: ChannelId, |
3632 | 3710 | ) -> Option<u64> { |
@@ -4036,27 +4114,20 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) { |
4036 | 4114 | .enable_op_for_all_signers(SignerOp::ReleaseCommitmentSecret); |
4037 | 4115 | harness.nodes[2].signer_unblocked(None); |
4038 | 4116 | }, |
4039 | | - 0xcc => { |
4040 | | - harness.nodes[1] |
4041 | | - .keys_manager |
4042 | | - .enable_op_for_all_signers(SignerOp::ReleaseCommitmentSecret); |
4043 | | - let filter = Some((harness.nodes[0].get_our_node_id(), harness.chan_a_id())); |
4044 | | - harness.nodes[1].signer_unblocked(filter); |
4045 | | - }, |
4046 | | - 0xcd => { |
4047 | | - harness.nodes[1] |
4048 | | - .keys_manager |
4049 | | - .enable_op_for_all_signers(SignerOp::ReleaseCommitmentSecret); |
4050 | | - let filter = Some((harness.nodes[2].get_our_node_id(), harness.chan_b_id())); |
4051 | | - harness.nodes[1].signer_unblocked(filter); |
4052 | | - }, |
4053 | | - 0xce => { |
4054 | | - harness.nodes[2] |
4055 | | - .keys_manager |
4056 | | - .enable_op_for_all_signers(SignerOp::ReleaseCommitmentSecret); |
4057 | | - harness.nodes[2].signer_unblocked(None); |
| 4117 | + 0xcc => harness.nodes[0].enable_holder_signer_ops(), |
| 4118 | + 0xcd => harness.nodes[1].enable_holder_signer_ops(), |
| 4119 | + 0xce => harness.nodes[2].enable_holder_signer_ops(), |
| 4120 | + 0xcf => { |
| 4121 | + harness.nodes[0].enable_holder_signer_ops(); |
| 4122 | + harness.nodes[1].enable_holder_signer_ops(); |
| 4123 | + harness.nodes[2].enable_holder_signer_ops(); |
4058 | 4124 | }, |
4059 | 4125 |
|
| 4126 | + 0xd0 => harness.force_close(0, harness.chan_a_id(), 1, "]]]]]]]]]"), |
| 4127 | + 0xd1 => harness.force_close(1, harness.chan_b_id(), 2, "]]]]]]]]"), |
| 4128 | + 0xd2 => harness.force_close(1, harness.chan_a_id(), 0, "]]]]]]]"), |
| 4129 | + 0xd3 => harness.force_close(2, harness.chan_b_id(), 1, "]]]]]"), |
| 4130 | + |
4060 | 4131 | 0xd8 => harness.confirm_broadcasts_for_node(0), |
4061 | 4132 | 0xd9 => harness.confirm_broadcasts_for_node(1), |
4062 | 4133 | 0xda => harness.confirm_broadcasts_for_node(2), |
|
0 commit comments