|
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, |
@@ -648,10 +649,12 @@ impl SignerProvider for KeyProvider { |
648 | 649 | } |
649 | 650 | } |
650 | 651 |
|
651 | | -const SUPPORTED_SIGNER_OPS: [SignerOp; 3] = [ |
| 652 | +const SUPPORTED_SIGNER_OPS: [SignerOp; 5] = [ |
652 | 653 | SignerOp::SignCounterpartyCommitment, |
653 | 654 | SignerOp::GetPerCommitmentPoint, |
654 | 655 | SignerOp::ReleaseCommitmentSecret, |
| 656 | + SignerOp::SignHolderCommitment, |
| 657 | + SignerOp::SignHolderHtlcTransaction, |
655 | 658 | ]; |
656 | 659 |
|
657 | 660 | impl KeyProvider { |
@@ -1432,6 +1435,16 @@ impl<'a> HarnessNode<'a> { |
1432 | 1435 | } |
1433 | 1436 | } |
1434 | 1437 |
|
| 1438 | +#[inline] |
| 1439 | +fn inbound_dust_blocks_path(htlc: &InboundHTLCDetails) -> bool { |
| 1440 | + htlc.is_dust |
| 1441 | +} |
| 1442 | + |
| 1443 | +#[inline] |
| 1444 | +fn outbound_dust_blocks_path(htlc: &OutboundHTLCDetails) -> bool { |
| 1445 | + htlc.is_dust |
| 1446 | +} |
| 1447 | + |
1435 | 1448 | struct EventQueues { |
1436 | 1449 | ab: Vec<MessageSendEvent>, |
1437 | 1450 | ba: Vec<MessageSendEvent>, |
@@ -3150,6 +3163,65 @@ impl<'a, 'd, Out: Output + MaybeSend + MaybeSync> Harness<'a, 'd, Out> { |
3150 | 3163 | assert!(settled, "message-only settle exceeded budget: {}", self.pending_work_summary(),); |
3151 | 3164 | } |
3152 | 3165 |
|
| 3166 | + fn record_force_close_dust(&self, closer_idx: usize, channel_id: ChannelId) { |
| 3167 | + if let Some(channel) = self.nodes[closer_idx] |
| 3168 | + .node |
| 3169 | + .list_channels() |
| 3170 | + .into_iter() |
| 3171 | + .find(|chan| chan.channel_id == channel_id) |
| 3172 | + { |
| 3173 | + let mut dust_parts = channel |
| 3174 | + .pending_inbound_htlcs |
| 3175 | + .iter() |
| 3176 | + .filter(|htlc| inbound_dust_blocks_path(htlc)) |
| 3177 | + .map(|htlc| (htlc.payment_hash, htlc.amount_msat)) |
| 3178 | + .chain( |
| 3179 | + channel |
| 3180 | + .pending_outbound_htlcs |
| 3181 | + .iter() |
| 3182 | + .filter(|htlc| outbound_dust_blocks_path(htlc)) |
| 3183 | + .map(|htlc| (htlc.payment_hash, htlc.amount_msat)), |
| 3184 | + ) |
| 3185 | + .collect::<Vec<_>>(); |
| 3186 | + let payment_paths = self.payments.payment_paths_by_hash.borrow(); |
| 3187 | + let mut blocked_paths = self.payments.blocked_dust_paths_by_hash.borrow_mut(); |
| 3188 | + for (payment_hash, amount_msat) in dust_parts.drain(..) { |
| 3189 | + let Some(paths) = payment_paths.get(&payment_hash) else { |
| 3190 | + continue; |
| 3191 | + }; |
| 3192 | + let blocked_for_hash = |
| 3193 | + blocked_paths.entry(payment_hash).or_insert_with(HashSet::new); |
| 3194 | + if let Some((path_idx, _)) = paths.iter().enumerate().find(|(path_idx, path)| { |
| 3195 | + !blocked_for_hash.contains(path_idx) |
| 3196 | + && path.iter().any(|(chan_id, part_amt)| { |
| 3197 | + *chan_id == channel_id && *part_amt == amount_msat |
| 3198 | + }) |
| 3199 | + }) { |
| 3200 | + blocked_for_hash.insert(path_idx); |
| 3201 | + } |
| 3202 | + } |
| 3203 | + } |
| 3204 | + } |
| 3205 | + |
| 3206 | + fn force_close( |
| 3207 | + &mut self, closer_idx: usize, channel_id: ChannelId, counterparty_idx: usize, reason: &str, |
| 3208 | + ) { |
| 3209 | + self.flush_progress(32); |
| 3210 | + self.record_force_close_dust(closer_idx, channel_id); |
| 3211 | + if self.nodes[closer_idx] |
| 3212 | + .node |
| 3213 | + .force_close_broadcasting_latest_txn( |
| 3214 | + &channel_id, |
| 3215 | + &self.nodes[counterparty_idx].our_node_id(), |
| 3216 | + reason.to_string(), |
| 3217 | + ) |
| 3218 | + .is_ok() |
| 3219 | + { |
| 3220 | + self.payments.closed_channels.borrow_mut().insert(channel_id); |
| 3221 | + self.flush_progress(32); |
| 3222 | + } |
| 3223 | + } |
| 3224 | + |
3153 | 3225 | fn probe_amount_for_direction( |
3154 | 3226 | &self, source_idx: usize, dest_chan_id: ChannelId, |
3155 | 3227 | ) -> Option<u64> { |
@@ -4092,25 +4164,42 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) { |
4092 | 4164 | harness.nodes[2].node.signer_unblocked(None); |
4093 | 4165 | }, |
4094 | 4166 | 0xcc => { |
4095 | | - harness.nodes[1] |
| 4167 | + harness.nodes[0] |
4096 | 4168 | .keys_manager |
4097 | | - .enable_op_for_all_signers(SignerOp::ReleaseCommitmentSecret); |
4098 | | - let filter = Some((harness.nodes[0].our_node_id(), harness.chan_a_id())); |
4099 | | - harness.nodes[1].node.signer_unblocked(filter); |
| 4169 | + .enable_op_for_all_signers(SignerOp::SignHolderCommitment); |
| 4170 | + harness.nodes[0].node.signer_unblocked(None); |
4100 | 4171 | }, |
4101 | 4172 | 0xcd => { |
4102 | 4173 | harness.nodes[1] |
4103 | 4174 | .keys_manager |
4104 | | - .enable_op_for_all_signers(SignerOp::ReleaseCommitmentSecret); |
4105 | | - let filter = Some((harness.nodes[2].our_node_id(), harness.chan_b_id())); |
4106 | | - harness.nodes[1].node.signer_unblocked(filter); |
| 4175 | + .enable_op_for_all_signers(SignerOp::SignHolderCommitment); |
| 4176 | + harness.nodes[1].node.signer_unblocked(None); |
4107 | 4177 | }, |
4108 | 4178 | 0xce => { |
4109 | 4179 | harness.nodes[2] |
4110 | 4180 | .keys_manager |
4111 | | - .enable_op_for_all_signers(SignerOp::ReleaseCommitmentSecret); |
| 4181 | + .enable_op_for_all_signers(SignerOp::SignHolderCommitment); |
4112 | 4182 | harness.nodes[2].node.signer_unblocked(None); |
4113 | 4183 | }, |
| 4184 | + 0xcf => { |
| 4185 | + harness.nodes[0] |
| 4186 | + .keys_manager |
| 4187 | + .enable_op_for_all_signers(SignerOp::SignHolderHtlcTransaction); |
| 4188 | + harness.nodes[1] |
| 4189 | + .keys_manager |
| 4190 | + .enable_op_for_all_signers(SignerOp::SignHolderHtlcTransaction); |
| 4191 | + harness.nodes[2] |
| 4192 | + .keys_manager |
| 4193 | + .enable_op_for_all_signers(SignerOp::SignHolderHtlcTransaction); |
| 4194 | + harness.nodes[0].node.signer_unblocked(None); |
| 4195 | + harness.nodes[1].node.signer_unblocked(None); |
| 4196 | + harness.nodes[2].node.signer_unblocked(None); |
| 4197 | + }, |
| 4198 | + |
| 4199 | + 0xd0 => harness.force_close(0, harness.chan_a_id(), 1, "]]]]]]]]]"), |
| 4200 | + 0xd1 => harness.force_close(1, harness.chan_b_id(), 2, "]]]]]]]]"), |
| 4201 | + 0xd2 => harness.force_close(1, harness.chan_a_id(), 0, "]]]]]]]"), |
| 4202 | + 0xd3 => harness.force_close(2, harness.chan_b_id(), 1, "]]]]]"), |
4114 | 4203 |
|
4115 | 4204 | 0xd8 => harness.confirm_broadcasts_for_node(0), |
4116 | 4205 | 0xd9 => harness.confirm_broadcasts_for_node(1), |
|
0 commit comments