Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement quiescence protocol #3588

Merged
merged 7 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 78 additions & 34 deletions fuzz/src/chanmon_consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -978,7 +978,9 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
lock_fundings!(nodes);

let chan_a = nodes[0].list_usable_channels()[0].short_channel_id.unwrap();
let chan_a_id = nodes[0].list_usable_channels()[0].channel_id;
let chan_b = nodes[2].list_usable_channels()[0].short_channel_id.unwrap();
let chan_b_id = nodes[2].list_usable_channels()[0].channel_id;

let mut p_id: u8 = 0;
let mut p_idx: u64 = 0;
Expand Down Expand Up @@ -1039,6 +1041,10 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
if Some(*node_id) == expect_drop_id { panic!("peer_disconnected should drop msgs bound for the disconnected peer"); }
*node_id == a_id
},
events::MessageSendEvent::SendStfu { ref node_id, .. } => {
if Some(*node_id) == expect_drop_id { panic!("peer_disconnected should drop msgs bound for the disconnected peer"); }
*node_id == a_id
},
events::MessageSendEvent::SendChannelReady { .. } => continue,
events::MessageSendEvent::SendAnnouncementSignatures { .. } => continue,
events::MessageSendEvent::SendChannelUpdate { ref node_id, ref msg } => {
Expand Down Expand Up @@ -1101,7 +1107,7 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
for (idx, dest) in nodes.iter().enumerate() {
if dest.get_our_node_id() == node_id {
for update_add in update_add_htlcs.iter() {
out.locked_write(format!("Delivering update_add_htlc to node {}.\n", idx).as_bytes());
out.locked_write(format!("Delivering update_add_htlc from node {} to node {}.\n", $node, idx).as_bytes());
if !$corrupt_forward {
dest.handle_update_add_htlc(nodes[$node].get_our_node_id(), update_add);
} else {
Expand All @@ -1116,19 +1122,19 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
}
}
for update_fulfill in update_fulfill_htlcs.iter() {
out.locked_write(format!("Delivering update_fulfill_htlc to node {}.\n", idx).as_bytes());
out.locked_write(format!("Delivering update_fulfill_htlc from node {} to node {}.\n", $node, idx).as_bytes());
dest.handle_update_fulfill_htlc(nodes[$node].get_our_node_id(), update_fulfill);
}
for update_fail in update_fail_htlcs.iter() {
out.locked_write(format!("Delivering update_fail_htlc to node {}.\n", idx).as_bytes());
out.locked_write(format!("Delivering update_fail_htlc from node {} to node {}.\n", $node, idx).as_bytes());
dest.handle_update_fail_htlc(nodes[$node].get_our_node_id(), update_fail);
}
for update_fail_malformed in update_fail_malformed_htlcs.iter() {
out.locked_write(format!("Delivering update_fail_malformed_htlc to node {}.\n", idx).as_bytes());
out.locked_write(format!("Delivering update_fail_malformed_htlc from node {} to node {}.\n", $node, idx).as_bytes());
dest.handle_update_fail_malformed_htlc(nodes[$node].get_our_node_id(), update_fail_malformed);
}
if let Some(msg) = update_fee {
out.locked_write(format!("Delivering update_fee to node {}.\n", idx).as_bytes());
out.locked_write(format!("Delivering update_fee from node {} to node {}.\n", $node, idx).as_bytes());
dest.handle_update_fee(nodes[$node].get_our_node_id(), &msg);
}
let processed_change = !update_add_htlcs.is_empty() || !update_fulfill_htlcs.is_empty() ||
Expand All @@ -1145,7 +1151,7 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
} });
break;
}
out.locked_write(format!("Delivering commitment_signed to node {}.\n", idx).as_bytes());
out.locked_write(format!("Delivering commitment_signed from node {} to node {}.\n", $node, idx).as_bytes());
dest.handle_commitment_signed(nodes[$node].get_our_node_id(), &commitment_signed);
break;
}
Expand All @@ -1154,19 +1160,27 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
events::MessageSendEvent::SendRevokeAndACK { ref node_id, ref msg } => {
for (idx, dest) in nodes.iter().enumerate() {
if dest.get_our_node_id() == *node_id {
out.locked_write(format!("Delivering revoke_and_ack to node {}.\n", idx).as_bytes());
out.locked_write(format!("Delivering revoke_and_ack from node {} to node {}.\n", $node, idx).as_bytes());
dest.handle_revoke_and_ack(nodes[$node].get_our_node_id(), msg);
}
}
},
events::MessageSendEvent::SendChannelReestablish { ref node_id, ref msg } => {
for (idx, dest) in nodes.iter().enumerate() {
if dest.get_our_node_id() == *node_id {
out.locked_write(format!("Delivering channel_reestablish to node {}.\n", idx).as_bytes());
out.locked_write(format!("Delivering channel_reestablish from node {} to node {}.\n", $node, idx).as_bytes());
dest.handle_channel_reestablish(nodes[$node].get_our_node_id(), msg);
}
}
},
events::MessageSendEvent::SendStfu { ref node_id, ref msg } => {
for (idx, dest) in nodes.iter().enumerate() {
if dest.get_our_node_id() == *node_id {
out.locked_write(format!("Delivering stfu from node {} to node {}.\n", $node, idx).as_bytes());
dest.handle_stfu(nodes[$node].get_our_node_id(), msg);
}
}
}
events::MessageSendEvent::SendChannelReady { .. } => {
// Can be generated as a reestablish response
},
Expand Down Expand Up @@ -1219,6 +1233,7 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
events::MessageSendEvent::UpdateHTLCs { .. } => {},
events::MessageSendEvent::SendRevokeAndACK { .. } => {},
events::MessageSendEvent::SendChannelReestablish { .. } => {},
events::MessageSendEvent::SendStfu { .. } => {},
events::MessageSendEvent::SendChannelReady { .. } => {},
events::MessageSendEvent::SendAnnouncementSignatures { .. } => {},
events::MessageSendEvent::SendChannelUpdate { ref msg, .. } => {
Expand All @@ -1245,6 +1260,7 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
events::MessageSendEvent::UpdateHTLCs { .. } => {},
events::MessageSendEvent::SendRevokeAndACK { .. } => {},
events::MessageSendEvent::SendChannelReestablish { .. } => {},
events::MessageSendEvent::SendStfu { .. } => {},
events::MessageSendEvent::SendChannelReady { .. } => {},
events::MessageSendEvent::SendAnnouncementSignatures { .. } => {},
events::MessageSendEvent::SendChannelUpdate { ref msg, .. } => {
Expand Down Expand Up @@ -1688,6 +1704,19 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
nodes[2].maybe_update_chan_fees();
},

0xa0 => {
nodes[0].maybe_propose_quiescence(&nodes[1].get_our_node_id(), &chan_a_id).unwrap()
},
0xa1 => {
nodes[1].maybe_propose_quiescence(&nodes[0].get_our_node_id(), &chan_a_id).unwrap()
},
0xa2 => {
nodes[1].maybe_propose_quiescence(&nodes[2].get_our_node_id(), &chan_b_id).unwrap()
},
0xa3 => {
nodes[2].maybe_propose_quiescence(&nodes[1].get_our_node_id(), &chan_b_id).unwrap()
},

0xf0 => complete_monitor_update(&monitor_a, &chan_1_id, &complete_first),
0xf1 => complete_monitor_update(&monitor_a, &chan_1_id, &complete_second),
0xf2 => complete_monitor_update(&monitor_a, &chan_1_id, &Vec::pop),
Expand Down Expand Up @@ -1753,34 +1782,49 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
chan_b_disconnected = false;
}

for i in 0..std::usize::MAX {
if i == 100 {
panic!("It may take may iterations to settle the state, but it should not take forever");
}
// Then, make sure any current forwards make their way to their destination
if process_msg_events!(0, false, ProcessMessages::AllMessages) {
continue;
}
if process_msg_events!(1, false, ProcessMessages::AllMessages) {
continue;
}
if process_msg_events!(2, false, ProcessMessages::AllMessages) {
continue;
}
// ...making sure any pending PendingHTLCsForwardable events are handled and
// payments claimed.
if process_events!(0, false) {
continue;
}
if process_events!(1, false) {
continue;
}
if process_events!(2, false) {
continue;
}
break;
macro_rules! process_all_events {
() => {
for i in 0..std::usize::MAX {
if i == 100 {
panic!("It may take may iterations to settle the state, but it should not take forever");
}
// Then, make sure any current forwards make their way to their destination
if process_msg_events!(0, false, ProcessMessages::AllMessages) {
continue;
}
if process_msg_events!(1, false, ProcessMessages::AllMessages) {
continue;
}
if process_msg_events!(2, false, ProcessMessages::AllMessages) {
continue;
}
// ...making sure any pending PendingHTLCsForwardable events are handled and
// payments claimed.
if process_events!(0, false) {
continue;
}
if process_events!(1, false) {
continue;
}
if process_events!(2, false) {
continue;
}
break;
}
};
}

// At this point, we may be pending quiescence, so we'll process all messages to
// ensure we can complete its handshake. We'll then exit quiescence and process all
// messages again, to resolve any pending HTLCs (only irrevocably committed ones)
// before attempting to send more payments.
process_all_events!();
nodes[0].exit_quiescence(&nodes[1].get_our_node_id(), &chan_a_id).unwrap();
nodes[1].exit_quiescence(&nodes[0].get_our_node_id(), &chan_a_id).unwrap();
nodes[1].exit_quiescence(&nodes[2].get_our_node_id(), &chan_b_id).unwrap();
nodes[2].exit_quiescence(&nodes[1].get_our_node_id(), &chan_b_id).unwrap();
process_all_events!();

// Finally, make sure that at least one end of each channel can make a substantial payment
assert!(
send_payment(&nodes[0], &nodes[1], chan_a, 10_000_000, &mut p_id, &mut p_idx)
Expand Down
19 changes: 16 additions & 3 deletions lightning-types/src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
//! (see [bLIP 32](https://github.com/lightning/blips/blob/master/blip-0032.md) for more information).
//! - `ProvideStorage` - Indicates that we offer the capability to store data of our peers
//! (see https://github.com/lightning/bolts/pull/1110 for more info).
//! - `Quiescence` - protocol to quiesce a channel by indicating that "SomeThing Fundamental is Underway"
//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#channel-quiescence) for more information).
//!
//! LDK knows about the following features, but does not support them:
//! - `AnchorsNonzeroFeeHtlcTx` - the initial version of anchor outputs, which was later found to be
Expand Down Expand Up @@ -152,7 +154,7 @@ mod sealed {
// Byte 3
RouteBlinding | ShutdownAnySegwit | DualFund | Taproot,
// Byte 4
OnionMessages,
Quiescence | OnionMessages,
// Byte 5
ProvideStorage | ChannelType | SCIDPrivacy,
// Byte 6
Expand All @@ -173,7 +175,7 @@ mod sealed {
// Byte 3
RouteBlinding | ShutdownAnySegwit | DualFund | Taproot,
// Byte 4
OnionMessages,
Quiescence | OnionMessages,
// Byte 5
ProvideStorage | ChannelType | SCIDPrivacy,
// Byte 6
Expand Down Expand Up @@ -536,6 +538,16 @@ mod sealed {
supports_taproot,
requires_taproot
);
define_feature!(
35,
Quiescence,
[InitContext, NodeContext],
"Feature flags for `option_quiesce`.",
set_quiescence_optional,
set_quiescence_required,
supports_quiescence,
requires_quiescence
);
define_feature!(
39,
OnionMessages,
Expand Down Expand Up @@ -1195,6 +1207,7 @@ mod tests {
init_features.set_channel_type_optional();
init_features.set_scid_privacy_optional();
init_features.set_zero_conf_optional();
init_features.set_quiescence_optional();

assert!(init_features.initial_routing_sync());
assert!(!init_features.supports_upfront_shutdown_script());
Expand All @@ -1215,7 +1228,7 @@ mod tests {
assert_eq!(node_features.flags[1], 0b01010001);
assert_eq!(node_features.flags[2], 0b10001010);
assert_eq!(node_features.flags[3], 0b00001010);
assert_eq!(node_features.flags[4], 0b10000000);
assert_eq!(node_features.flags[4], 0b10001000);
assert_eq!(node_features.flags[5], 0b10100000);
assert_eq!(node_features.flags[6], 0b00001000);
}
Expand Down
Loading
Loading