Skip to content

Commit 15ce831

Browse files
committed
Time out incomplete MPP payments in chanmon_consistency
This requires calling `timer_tick_occurred`. As a result, when `timer_tick_occurred` is called, disabled/enabled updates and `WarnAndDisconnect` events may be triggered.
1 parent b77162c commit 15ce831

File tree

3 files changed

+66
-59
lines changed

3 files changed

+66
-59
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ use lightning::ln::functional_test_utils::*;
5757
use lightning::ln::funding::{FundingTxInput, SpliceContribution};
5858
use lightning::ln::inbound_payment::ExpandedKey;
5959
use lightning::ln::msgs::{
60-
BaseMessageHandler, ChannelMessageHandler, CommitmentUpdate, Init, MessageSendEvent,
60+
self, BaseMessageHandler, ChannelMessageHandler, CommitmentUpdate, Init, MessageSendEvent,
6161
UpdateAddHTLC,
6262
};
6363
use lightning::ln::outbound_payment::RecipientOnionFields;
@@ -897,6 +897,18 @@ fn send_mpp_hop_payment(
897897
}
898898
}
899899

900+
#[inline]
901+
fn assert_action_timeout_awaiting_response(action: &msgs::ErrorAction) {
902+
// Since sending/receiving messages may be delayed,
903+
// `timer_tick_occurred` may cause a node to disconnect their
904+
// counterparty if they're expecting a timely response.
905+
assert!(matches!(
906+
action,
907+
msgs::ErrorAction::DisconnectPeerWithWarning { msg }
908+
if msg.data.contains("Disconnecting due to timeout awaiting response")
909+
));
910+
}
911+
900912
#[inline]
901913
pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
902914
let out = SearchingOutput::new(underlying_out);
@@ -1477,8 +1489,12 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
14771489
},
14781490
MessageSendEvent::SendChannelReady { .. } => continue,
14791491
MessageSendEvent::SendAnnouncementSignatures { .. } => continue,
1480-
MessageSendEvent::SendChannelUpdate { ref node_id, ref msg } => {
1481-
assert_eq!(msg.contents.channel_flags & 2, 0); // The disable bit must never be set!
1492+
MessageSendEvent::SendChannelUpdate { ref node_id, .. } => {
1493+
if Some(*node_id) == expect_drop_id { panic!("peer_disconnected should drop msgs bound for the disconnected peer"); }
1494+
*node_id == a_id
1495+
},
1496+
MessageSendEvent::HandleError { ref action, ref node_id } => {
1497+
assert_action_timeout_awaiting_response(action);
14821498
if Some(*node_id) == expect_drop_id { panic!("peer_disconnected should drop msgs bound for the disconnected peer"); }
14831499
*node_id == a_id
14841500
},
@@ -1691,20 +1707,21 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
16911707
}
16921708
}
16931709
},
1710+
MessageSendEvent::HandleError { ref action, .. } => {
1711+
assert_action_timeout_awaiting_response(action);
1712+
},
16941713
MessageSendEvent::SendChannelReady { .. } => {
16951714
// Can be generated as a reestablish response
16961715
},
16971716
MessageSendEvent::SendAnnouncementSignatures { .. } => {
16981717
// Can be generated as a reestablish response
16991718
},
1700-
MessageSendEvent::SendChannelUpdate { ref msg, .. } => {
1701-
// When we reconnect we will resend a channel_update to make sure our
1702-
// counterparty has the latest parameters for receiving payments
1703-
// through us. We do, however, check that the message does not include
1704-
// the "disabled" bit, as we should never ever have a channel which is
1705-
// disabled when we send such an update (or it may indicate channel
1706-
// force-close which we should detect as an error).
1707-
assert_eq!(msg.contents.channel_flags & 2, 0);
1719+
MessageSendEvent::SendChannelUpdate { .. } => {
1720+
// Can be generated as a reestablish response
1721+
},
1722+
MessageSendEvent::BroadcastChannelUpdate { .. } => {
1723+
// Can be generated as a result of calling `timer_tick_occurred` enough
1724+
// times while peers are disconnected
17081725
},
17091726
_ => if out.may_fail.load(atomic::Ordering::Acquire) {
17101727
return;
@@ -1746,8 +1763,9 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
17461763
MessageSendEvent::SendStfu { .. } => {},
17471764
MessageSendEvent::SendChannelReady { .. } => {},
17481765
MessageSendEvent::SendAnnouncementSignatures { .. } => {},
1749-
MessageSendEvent::SendChannelUpdate { ref msg, .. } => {
1750-
assert_eq!(msg.contents.channel_flags & 2, 0); // The disable bit must never be set!
1766+
MessageSendEvent::SendChannelUpdate { .. } => {},
1767+
MessageSendEvent::HandleError { ref action, .. } => {
1768+
assert_action_timeout_awaiting_response(action);
17511769
},
17521770
_ => {
17531771
if out.may_fail.load(atomic::Ordering::Acquire) {
@@ -1773,8 +1791,9 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
17731791
MessageSendEvent::SendStfu { .. } => {},
17741792
MessageSendEvent::SendChannelReady { .. } => {},
17751793
MessageSendEvent::SendAnnouncementSignatures { .. } => {},
1776-
MessageSendEvent::SendChannelUpdate { ref msg, .. } => {
1777-
assert_eq!(msg.contents.channel_flags & 2, 0); // The disable bit must never be set!
1794+
MessageSendEvent::SendChannelUpdate { .. } => {},
1795+
MessageSendEvent::HandleError { ref action, .. } => {
1796+
assert_action_timeout_awaiting_response(action);
17781797
},
17791798
_ => {
17801799
if out.may_fail.load(atomic::Ordering::Acquire) {
@@ -2248,11 +2267,11 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
22482267
if fee_est_a.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250 > max_feerate {
22492268
fee_est_a.ret_val.store(max_feerate, atomic::Ordering::Release);
22502269
}
2251-
nodes[0].maybe_update_chan_fees();
2270+
nodes[0].timer_tick_occurred();
22522271
},
22532272
0x81 => {
22542273
fee_est_a.ret_val.store(253, atomic::Ordering::Release);
2255-
nodes[0].maybe_update_chan_fees();
2274+
nodes[0].timer_tick_occurred();
22562275
},
22572276

22582277
0x84 => {
@@ -2263,11 +2282,11 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
22632282
if fee_est_b.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250 > max_feerate {
22642283
fee_est_b.ret_val.store(max_feerate, atomic::Ordering::Release);
22652284
}
2266-
nodes[1].maybe_update_chan_fees();
2285+
nodes[1].timer_tick_occurred();
22672286
},
22682287
0x85 => {
22692288
fee_est_b.ret_val.store(253, atomic::Ordering::Release);
2270-
nodes[1].maybe_update_chan_fees();
2289+
nodes[1].timer_tick_occurred();
22712290
},
22722291

22732292
0x88 => {
@@ -2278,11 +2297,11 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
22782297
if fee_est_c.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250 > max_feerate {
22792298
fee_est_c.ret_val.store(max_feerate, atomic::Ordering::Release);
22802299
}
2281-
nodes[2].maybe_update_chan_fees();
2300+
nodes[2].timer_tick_occurred();
22822301
},
22832302
0x89 => {
22842303
fee_est_c.ret_val.store(253, atomic::Ordering::Release);
2285-
nodes[2].maybe_update_chan_fees();
2304+
nodes[2].timer_tick_occurred();
22862305
},
22872306

22882307
0xa0 => {
@@ -2779,6 +2798,14 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
27792798

27802799
process_all_events!();
27812800

2801+
// Since MPP payments are supported, we wait until we fully settle the state of all
2802+
// channels to see if we have any committed HTLC parts of an MPP payment that need
2803+
// to be failed back.
2804+
for node in &nodes {
2805+
node.timer_tick_occurred();
2806+
}
2807+
process_all_events!();
2808+
27822809
// Verify no payments are stuck - all should have resolved
27832810
for (idx, pending) in pending_payments.borrow().iter().enumerate() {
27842811
assert!(

lightning/src/ln/channelmanager.rs

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3051,7 +3051,10 @@ const _CHECK_CLTV_EXPIRY_OFFCHAIN: () = assert!(
30513051
);
30523052

30533053
/// The number of ticks of [`ChannelManager::timer_tick_occurred`] until expiry of incomplete MPPs
3054+
#[cfg(not(any(fuzzing, test, feature = "_test_utils")))]
30543055
pub(crate) const MPP_TIMEOUT_TICKS: u8 = 3;
3056+
#[cfg(any(fuzzing, test, feature = "_test_utils"))]
3057+
pub(crate) const MPP_TIMEOUT_TICKS: u8 = 1;
30553058

30563059
/// The number of ticks of [`ChannelManager::timer_tick_occurred`] where a peer is disconnected
30573060
/// until we mark the channel disabled and gossip the update.
@@ -8223,39 +8226,6 @@ impl<
82238226
NotifyOption::DoPersist
82248227
}
82258228

8226-
#[cfg(any(test, fuzzing, feature = "_externalize_tests"))]
8227-
/// In chanmon_consistency we want to sometimes do the channel fee updates done in
8228-
/// timer_tick_occurred, but we can't generate the disabled channel updates as it considers
8229-
/// these a fuzz failure (as they usually indicate a channel force-close, which is exactly what
8230-
/// it wants to detect). Thus, we have a variant exposed here for its benefit.
8231-
#[rustfmt::skip]
8232-
pub fn maybe_update_chan_fees(&self) {
8233-
PersistenceNotifierGuard::optionally_notify(self, || {
8234-
let mut should_persist = NotifyOption::SkipPersistNoEvents;
8235-
let mut feerate_cache = new_hash_map();
8236-
8237-
let per_peer_state = self.per_peer_state.read().unwrap();
8238-
for (_cp_id, peer_state_mutex) in per_peer_state.iter() {
8239-
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
8240-
let peer_state = &mut *peer_state_lock;
8241-
for (chan_id, chan) in peer_state.channel_by_id.iter_mut()
8242-
.filter_map(|(chan_id, chan)| chan.as_funded_mut().map(|chan| (chan_id, chan)))
8243-
{
8244-
let channel_type = chan.funding.get_channel_type();
8245-
let new_feerate = feerate_cache.get(channel_type).copied().or_else(|| {
8246-
let feerate = selected_commitment_sat_per_1000_weight(&self.fee_estimator, &channel_type);
8247-
feerate_cache.insert(channel_type.clone(), feerate);
8248-
Some(feerate)
8249-
}).unwrap();
8250-
let chan_needs_persist = self.update_channel_fee(chan_id, chan, new_feerate);
8251-
if chan_needs_persist == NotifyOption::DoPersist { should_persist = NotifyOption::DoPersist; }
8252-
}
8253-
}
8254-
8255-
should_persist
8256-
});
8257-
}
8258-
82598229
/// Performs actions which should happen on startup and roughly once per minute thereafter.
82608230
///
82618231
/// This currently includes:
@@ -13280,7 +13250,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1328013250
}
1328113251
}
1328213252

13283-
#[cfg(any(test, fuzzing))]
13253+
#[cfg(any(test, fuzzing, feature = "_test_utils"))]
1328413254
#[rustfmt::skip]
1328513255
pub fn maybe_propose_quiescence(&self, counterparty_node_id: &PublicKey, channel_id: &ChannelId) -> Result<(), APIError> {
1328613256
let mut result = Ok(());

lightning/src/ln/update_fee_tests.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,9 +1089,13 @@ pub fn do_cannot_afford_on_holding_cell_release(
10891089
*feerate_lock = target_feerate;
10901090
}
10911091

1092-
// Put the update fee into the holding cell of node 0
1093-
1094-
nodes[0].node.maybe_update_chan_fees();
1092+
// Put the update fee into the holding cell of node 0. We use quiescence as an easy way to force
1093+
// the update into the holding cell.
1094+
nodes[0].node.maybe_propose_quiescence(&node_b_id, &chan_id).unwrap();
1095+
let stfu = get_event_msg!(nodes[0], MessageSendEvent::SendStfu, node_b_id);
1096+
nodes[0].node.timer_tick_occurred();
1097+
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
1098+
check_added_monitors(&nodes[0], 0);
10951099

10961100
// While the update_fee is in the holding cell, add an inbound HTLC
10971101

@@ -1132,11 +1136,17 @@ pub fn do_cannot_afford_on_holding_cell_release(
11321136
panic!();
11331137
}
11341138

1135-
// Release the update_fee from its holding cell
1139+
// Release the update_fee from its holding cell by completing the quiescence handshake.
1140+
nodes[1].node.handle_stfu(node_a_id, &stfu);
1141+
let stfu = get_event_msg!(nodes[1], MessageSendEvent::SendStfu, node_a_id);
1142+
nodes[0].node.handle_stfu(node_b_id, &stfu);
1143+
let _ = nodes[0].node.exit_quiescence(&node_b_id, &chan_id);
1144+
let _ = nodes[1].node.exit_quiescence(&node_a_id, &chan_id);
11361145
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
11371146
if can_afford {
11381147
// We could afford the update_fee, sanity check everything
11391148
assert_eq!(events.len(), 1);
1149+
check_added_monitors(&nodes[0], 1);
11401150
if let MessageSendEvent::UpdateHTLCs { node_id, channel_id, updates } =
11411151
events.pop().unwrap()
11421152
{

0 commit comments

Comments
 (0)