mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-03-14 07:06:42 +01:00
Merge pull request #3588 from wpaulino/quiescence
Implement quiescence protocol
This commit is contained in:
commit
609f89de43
7 changed files with 1252 additions and 128 deletions
|
@ -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;
|
||||
|
@ -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 } => {
|
||||
|
@ -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 {
|
||||
|
@ -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() ||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1154,7 +1160,7 @@ 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);
|
||||
}
|
||||
}
|
||||
|
@ -1162,11 +1168,19 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
|
|||
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
|
||||
},
|
||||
|
@ -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, .. } => {
|
||||
|
@ -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, .. } => {
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
@ -152,7 +154,7 @@ mod sealed {
|
|||
// Byte 3
|
||||
RouteBlinding | ShutdownAnySegwit | DualFund | Taproot,
|
||||
// Byte 4
|
||||
OnionMessages,
|
||||
Quiescence | OnionMessages,
|
||||
// Byte 5
|
||||
ProvideStorage | ChannelType | SCIDPrivacy,
|
||||
// Byte 6
|
||||
|
@ -173,7 +175,7 @@ mod sealed {
|
|||
// Byte 3
|
||||
RouteBlinding | ShutdownAnySegwit | DualFund | Taproot,
|
||||
// Byte 4
|
||||
OnionMessages,
|
||||
Quiescence | OnionMessages,
|
||||
// Byte 5
|
||||
ProvideStorage | ChannelType | SCIDPrivacy,
|
||||
// Byte 6
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -474,6 +474,10 @@ mod state_flags {
|
|||
pub const LOCAL_SHUTDOWN_SENT: u32 = 1 << 11;
|
||||
pub const SHUTDOWN_COMPLETE: u32 = 1 << 12;
|
||||
pub const WAITING_FOR_BATCH: u32 = 1 << 13;
|
||||
pub const AWAITING_QUIESCENCE: u32 = 1 << 14;
|
||||
pub const LOCAL_STFU_SENT: u32 = 1 << 15;
|
||||
pub const REMOTE_STFU_SENT: u32 = 1 << 16;
|
||||
pub const QUIESCENT: u32 = 1 << 17;
|
||||
}
|
||||
|
||||
define_state_flags!(
|
||||
|
@ -532,7 +536,26 @@ define_state_flags!(
|
|||
messages as we'd be unable to determine which HTLCs they included in their `revoke_and_ack` \
|
||||
implicit ACK, so instead we have to hold them away temporarily to be sent later.",
|
||||
AWAITING_REMOTE_REVOKE, state_flags::AWAITING_REMOTE_REVOKE,
|
||||
is_awaiting_remote_revoke, set_awaiting_remote_revoke, clear_awaiting_remote_revoke)
|
||||
is_awaiting_remote_revoke, set_awaiting_remote_revoke, clear_awaiting_remote_revoke),
|
||||
("Indicates a local request has been made for the channel to become quiescent. Both nodes \
|
||||
must send `stfu` for the channel to become quiescent. This flag will be cleared and we \
|
||||
will no longer attempt quiescence if either node requests a shutdown.",
|
||||
AWAITING_QUIESCENCE, state_flags::AWAITING_QUIESCENCE,
|
||||
is_awaiting_quiescence, set_awaiting_quiescence, clear_awaiting_quiescence),
|
||||
("Indicates we have sent a `stfu` message to the counterparty. This message can only be sent \
|
||||
if either `AWAITING_QUIESCENCE` or `REMOTE_STFU_SENT` is set. Shutdown requests are \
|
||||
rejected if this flag is set.",
|
||||
LOCAL_STFU_SENT, state_flags::LOCAL_STFU_SENT,
|
||||
is_local_stfu_sent, set_local_stfu_sent, clear_local_stfu_sent),
|
||||
("Indicates we have received a `stfu` message from the counterparty. Shutdown requests are \
|
||||
rejected if this flag is set.",
|
||||
REMOTE_STFU_SENT, state_flags::REMOTE_STFU_SENT,
|
||||
is_remote_stfu_sent, set_remote_stfu_sent, clear_remote_stfu_sent),
|
||||
("Indicates the quiescence handshake has completed and the channel is now quiescent. \
|
||||
Updates are not allowed while this flag is set, and any outbound updates will go \
|
||||
directly into the holding cell.",
|
||||
QUIESCENT, state_flags::QUIESCENT,
|
||||
is_quiescent, set_quiescent, clear_quiescent)
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -646,6 +669,8 @@ impl ChannelState {
|
|||
match self {
|
||||
ChannelState::ChannelReady(flags) =>
|
||||
!flags.is_set(ChannelReadyFlags::AWAITING_REMOTE_REVOKE) &&
|
||||
!flags.is_set(ChannelReadyFlags::LOCAL_STFU_SENT) &&
|
||||
!flags.is_set(ChannelReadyFlags::QUIESCENT) &&
|
||||
!flags.is_set(FundedStateFlags::MONITOR_UPDATE_IN_PROGRESS.into()) &&
|
||||
!flags.is_set(FundedStateFlags::PEER_DISCONNECTED.into()),
|
||||
_ => {
|
||||
|
@ -663,6 +688,10 @@ impl ChannelState {
|
|||
impl_state_flag!(is_their_channel_ready, set_their_channel_ready, clear_their_channel_ready, AwaitingChannelReady);
|
||||
impl_state_flag!(is_waiting_for_batch, set_waiting_for_batch, clear_waiting_for_batch, AwaitingChannelReady);
|
||||
impl_state_flag!(is_awaiting_remote_revoke, set_awaiting_remote_revoke, clear_awaiting_remote_revoke, ChannelReady);
|
||||
impl_state_flag!(is_awaiting_quiescence, set_awaiting_quiescence, clear_awaiting_quiescence, ChannelReady);
|
||||
impl_state_flag!(is_local_stfu_sent, set_local_stfu_sent, clear_local_stfu_sent, ChannelReady);
|
||||
impl_state_flag!(is_remote_stfu_sent, set_remote_stfu_sent, clear_remote_stfu_sent, ChannelReady);
|
||||
impl_state_flag!(is_quiescent, set_quiescent, clear_quiescent, ChannelReady);
|
||||
}
|
||||
|
||||
pub const INITIAL_COMMITMENT_NUMBER: u64 = (1 << 48) - 1;
|
||||
|
@ -713,6 +742,7 @@ pub const MIN_THEIR_CHAN_RESERVE_SATOSHIS: u64 = 1000;
|
|||
pub(super) enum ChannelError {
|
||||
Ignore(String),
|
||||
Warn(String),
|
||||
WarnAndDisconnect(String),
|
||||
Close((String, ClosureReason)),
|
||||
SendError(String),
|
||||
}
|
||||
|
@ -720,10 +750,11 @@ pub(super) enum ChannelError {
|
|||
impl fmt::Debug for ChannelError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&ChannelError::Ignore(ref e) => write!(f, "Ignore : {}", e),
|
||||
&ChannelError::Warn(ref e) => write!(f, "Warn : {}", e),
|
||||
&ChannelError::Close((ref e, _)) => write!(f, "Close : {}", e),
|
||||
&ChannelError::SendError(ref e) => write!(f, "Not Found : {}", e),
|
||||
&ChannelError::Ignore(ref e) => write!(f, "Ignore: {}", e),
|
||||
&ChannelError::Warn(ref e) => write!(f, "Warn: {}", e),
|
||||
&ChannelError::WarnAndDisconnect(ref e) => write!(f, "Disconnecting with warning: {}", e),
|
||||
&ChannelError::Close((ref e, _)) => write!(f, "Close: {}", e),
|
||||
&ChannelError::SendError(ref e) => write!(f, "Not Found: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -733,6 +764,7 @@ impl fmt::Display for ChannelError {
|
|||
match self {
|
||||
&ChannelError::Ignore(ref e) => write!(f, "{}", e),
|
||||
&ChannelError::Warn(ref e) => write!(f, "{}", e),
|
||||
&ChannelError::WarnAndDisconnect(ref e) => write!(f, "{}", e),
|
||||
&ChannelError::Close((ref e, _)) => write!(f, "{}", e),
|
||||
&ChannelError::SendError(ref e) => write!(f, "{}", e),
|
||||
}
|
||||
|
@ -1112,9 +1144,8 @@ pub(crate) const MIN_AFFORDABLE_HTLC_COUNT: usize = 4;
|
|||
/// * `EXPIRE_PREV_CONFIG_TICKS` = convergence_delay / tick_interval
|
||||
pub(crate) const EXPIRE_PREV_CONFIG_TICKS: usize = 5;
|
||||
|
||||
/// The number of ticks that may elapse while we're waiting for a response to a
|
||||
/// [`msgs::RevokeAndACK`] or [`msgs::ChannelReestablish`] message before we attempt to disconnect
|
||||
/// them.
|
||||
/// The number of ticks that may elapse while we're waiting for a response before we attempt to
|
||||
/// disconnect them.
|
||||
///
|
||||
/// See [`ChannelContext::sent_message_awaiting_response`] for more information.
|
||||
pub(crate) const DISCONNECT_PEER_AWAITING_RESPONSE_TICKS: usize = 2;
|
||||
|
@ -1842,16 +1873,14 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
|
|||
pub workaround_lnd_bug_4006: Option<msgs::ChannelReady>,
|
||||
|
||||
/// An option set when we wish to track how many ticks have elapsed while waiting for a response
|
||||
/// from our counterparty after sending a message. If the peer has yet to respond after reaching
|
||||
/// `DISCONNECT_PEER_AWAITING_RESPONSE_TICKS`, a reconnection should be attempted to try to
|
||||
/// unblock the state machine.
|
||||
/// from our counterparty after entering specific states. If the peer has yet to respond after
|
||||
/// reaching `DISCONNECT_PEER_AWAITING_RESPONSE_TICKS`, a reconnection should be attempted to
|
||||
/// try to unblock the state machine.
|
||||
///
|
||||
/// This behavior is mostly motivated by a lnd bug in which we don't receive a message we expect
|
||||
/// to in a timely manner, which may lead to channels becoming unusable and/or force-closed. An
|
||||
/// example of such can be found at <https://github.com/lightningnetwork/lnd/issues/7682>.
|
||||
///
|
||||
/// This is currently only used when waiting for a [`msgs::ChannelReestablish`] or
|
||||
/// [`msgs::RevokeAndACK`] message from the counterparty.
|
||||
/// This behavior was initially motivated by a lnd bug in which we don't receive a message we
|
||||
/// expect to in a timely manner, which may lead to channels becoming unusable and/or
|
||||
/// force-closed. An example of such can be found at
|
||||
/// <https://github.com/lightningnetwork/lnd/issues/7682>.
|
||||
sent_message_awaiting_response: Option<usize>,
|
||||
|
||||
/// This channel's type, as negotiated during channel open
|
||||
|
@ -1897,6 +1926,7 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
|
|||
/// If we can't release a [`ChannelMonitorUpdate`] until some external action completes, we
|
||||
/// store it here and only release it to the `ChannelManager` once it asks for it.
|
||||
blocked_monitor_updates: Vec<PendingChannelMonitorUpdate>,
|
||||
|
||||
// The `next_funding_txid` field allows peers to finalize the signing steps of an interactive
|
||||
// transaction construction, or safely abort that transaction if it was not signed by one of the
|
||||
// peers, who has thus already removed it from its state.
|
||||
|
@ -1912,6 +1942,10 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
|
|||
// TODO(dual_funding): Persist this when we actually contribute funding inputs. For now we always
|
||||
// send an empty witnesses array in `tx_signatures` as a V2 channel acceptor
|
||||
next_funding_txid: Option<Txid>,
|
||||
|
||||
/// Only set when a counterparty `stfu` has been processed to track which node is allowed to
|
||||
/// propose "something fundamental" upon becoming quiescent.
|
||||
is_holder_quiescence_initiator: Option<bool>,
|
||||
}
|
||||
|
||||
/// A channel struct implementing this trait can receive an initial counterparty commitment
|
||||
|
@ -2603,6 +2637,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
|
|||
is_manual_broadcast: false,
|
||||
|
||||
next_funding_txid: None,
|
||||
|
||||
is_holder_quiescence_initiator: None,
|
||||
};
|
||||
|
||||
Ok((funding, channel_context))
|
||||
|
@ -2832,6 +2868,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
|
|||
local_initiated_shutdown: None,
|
||||
is_manual_broadcast: false,
|
||||
next_funding_txid: None,
|
||||
|
||||
is_holder_quiescence_initiator: None,
|
||||
};
|
||||
|
||||
Ok((funding, channel_context))
|
||||
|
@ -2921,6 +2959,57 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks whether the channel has any HTLC additions, HTLC removals, or fee updates that have
|
||||
/// been sent by either side but not yet irrevocably committed on both commitments. Holding cell
|
||||
/// updates are not considered because they haven't been sent to the peer yet.
|
||||
///
|
||||
/// This can be used to satisfy quiescence's requirement when sending `stfu`:
|
||||
/// - MUST NOT send `stfu` if any of the sender's htlc additions, htlc removals
|
||||
/// or fee updates are pending for either peer.
|
||||
fn has_pending_channel_update(&self) -> bool {
|
||||
// An update from the local/remote node may be pending on the remote/local commitment since
|
||||
// they are not tracked within our state, so we rely on whether any `commitment_signed` or
|
||||
// `revoke_and_ack` messages are owed.
|
||||
//
|
||||
// We check these flags first as they are more likely to be set.
|
||||
if self.channel_state.is_awaiting_remote_revoke() || self.expecting_peer_commitment_signed
|
||||
|| self.monitor_pending_revoke_and_ack || self.signer_pending_revoke_and_ack
|
||||
|| self.monitor_pending_commitment_signed || self.signer_pending_commitment_update
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// A fee update is pending on either commitment.
|
||||
if self.pending_update_fee.is_some() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.pending_inbound_htlcs.iter()
|
||||
.any(|htlc| match htlc.state {
|
||||
InboundHTLCState::Committed => false,
|
||||
// An HTLC removal from the local node is pending on the remote commitment.
|
||||
InboundHTLCState::LocalRemoved(_) => true,
|
||||
// An HTLC add from the remote node is pending on the local commitment.
|
||||
InboundHTLCState::RemoteAnnounced(_)
|
||||
| InboundHTLCState::AwaitingRemoteRevokeToAnnounce(_)
|
||||
| InboundHTLCState::AwaitingAnnouncedRemoteRevoke(_) => true,
|
||||
})
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
self.pending_outbound_htlcs.iter()
|
||||
.any(|htlc| match htlc.state {
|
||||
OutboundHTLCState::Committed => false,
|
||||
// An HTLC add from the local node is pending on the remote commitment.
|
||||
OutboundHTLCState::LocalAnnounced(_) => true,
|
||||
// An HTLC removal from the remote node is pending on the local commitment.
|
||||
OutboundHTLCState::RemoteRemoved(_)
|
||||
| OutboundHTLCState::AwaitingRemoteRevokeToRemove(_)
|
||||
| OutboundHTLCState::AwaitingRemovedRemoteRevoke(_) => true,
|
||||
})
|
||||
}
|
||||
|
||||
// Public utilities:
|
||||
|
||||
pub fn channel_id(&self) -> ChannelId {
|
||||
|
@ -5140,6 +5229,9 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
pub fn update_add_htlc<F: Deref>(
|
||||
&mut self, msg: &msgs::UpdateAddHTLC, fee_estimator: &LowerBoundedFeeEstimator<F>,
|
||||
) -> Result<(), ChannelError> where F::Target: FeeEstimator {
|
||||
if self.context.channel_state.is_remote_stfu_sent() || self.context.channel_state.is_quiescent() {
|
||||
return Err(ChannelError::WarnAndDisconnect("Got add HTLC message while quiescent".to_owned()));
|
||||
}
|
||||
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
|
||||
return Err(ChannelError::close("Got add HTLC message when channel was not in an operational state".to_owned()));
|
||||
}
|
||||
|
@ -5284,6 +5376,9 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
}
|
||||
|
||||
pub fn update_fulfill_htlc(&mut self, msg: &msgs::UpdateFulfillHTLC) -> Result<(HTLCSource, u64, Option<u64>), ChannelError> {
|
||||
if self.context.channel_state.is_remote_stfu_sent() || self.context.channel_state.is_quiescent() {
|
||||
return Err(ChannelError::WarnAndDisconnect("Got fulfill HTLC message while quiescent".to_owned()));
|
||||
}
|
||||
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
|
||||
return Err(ChannelError::close("Got fulfill HTLC message when channel was not in an operational state".to_owned()));
|
||||
}
|
||||
|
@ -5295,6 +5390,9 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
}
|
||||
|
||||
pub fn update_fail_htlc(&mut self, msg: &msgs::UpdateFailHTLC, fail_reason: HTLCFailReason) -> Result<(), ChannelError> {
|
||||
if self.context.channel_state.is_remote_stfu_sent() || self.context.channel_state.is_quiescent() {
|
||||
return Err(ChannelError::WarnAndDisconnect("Got fail HTLC message while quiescent".to_owned()));
|
||||
}
|
||||
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
|
||||
return Err(ChannelError::close("Got fail HTLC message when channel was not in an operational state".to_owned()));
|
||||
}
|
||||
|
@ -5307,6 +5405,9 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
}
|
||||
|
||||
pub fn update_fail_malformed_htlc(&mut self, msg: &msgs::UpdateFailMalformedHTLC, fail_reason: HTLCFailReason) -> Result<(), ChannelError> {
|
||||
if self.context.channel_state.is_remote_stfu_sent() || self.context.channel_state.is_quiescent() {
|
||||
return Err(ChannelError::WarnAndDisconnect("Got fail malformed HTLC message while quiescent".to_owned()));
|
||||
}
|
||||
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
|
||||
return Err(ChannelError::close("Got fail malformed HTLC message when channel was not in an operational state".to_owned()));
|
||||
}
|
||||
|
@ -5358,6 +5459,9 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
pub fn commitment_signed<L: Deref>(&mut self, msg: &msgs::CommitmentSigned, logger: &L) -> Result<Option<ChannelMonitorUpdate>, ChannelError>
|
||||
where L::Target: Logger
|
||||
{
|
||||
if self.context.channel_state.is_quiescent() {
|
||||
return Err(ChannelError::WarnAndDisconnect("Got commitment_signed message while quiescent".to_owned()));
|
||||
}
|
||||
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
|
||||
return Err(ChannelError::close("Got commitment signed message when channel was not in an operational state".to_owned()));
|
||||
}
|
||||
|
@ -5607,7 +5711,9 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
) -> (Option<ChannelMonitorUpdate>, Vec<(HTLCSource, PaymentHash)>)
|
||||
where F::Target: FeeEstimator, L::Target: Logger
|
||||
{
|
||||
assert!(matches!(self.context.channel_state, ChannelState::ChannelReady(_)));
|
||||
assert!(!self.context.channel_state.is_monitor_update_in_progress());
|
||||
assert!(!self.context.channel_state.is_quiescent());
|
||||
if self.context.holding_cell_htlc_updates.len() != 0 || self.context.holding_cell_update_fee.is_some() {
|
||||
log_trace!(logger, "Freeing holding cell with {} HTLC updates{} in channel {}", self.context.holding_cell_htlc_updates.len(),
|
||||
if self.context.holding_cell_update_fee.is_some() { " and a fee update" } else { "" }, &self.context.channel_id());
|
||||
|
@ -5640,7 +5746,16 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
amount_msat, *payment_hash, cltv_expiry, source.clone(), onion_routing_packet.clone(),
|
||||
false, skimmed_fee_msat, blinding_point, fee_estimator, logger
|
||||
) {
|
||||
Ok(_) => update_add_count += 1,
|
||||
Ok(update_add_msg_opt) => {
|
||||
// `send_htlc` only returns `Ok(None)`, when an update goes into
|
||||
// the holding cell, but since we're currently freeing it, we should
|
||||
// always expect to see the `update_add` go out.
|
||||
debug_assert!(
|
||||
update_add_msg_opt.is_some(),
|
||||
"Must generate new update if we're freeing the holding cell"
|
||||
);
|
||||
update_add_count += 1;
|
||||
},
|
||||
Err(e) => {
|
||||
match e {
|
||||
ChannelError::Ignore(ref msg) => {
|
||||
|
@ -5743,6 +5858,9 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
) -> Result<(Vec<(HTLCSource, PaymentHash)>, Option<ChannelMonitorUpdate>), ChannelError>
|
||||
where F::Target: FeeEstimator, L::Target: Logger,
|
||||
{
|
||||
if self.context.channel_state.is_quiescent() {
|
||||
return Err(ChannelError::WarnAndDisconnect("Got revoke_and_ack message while quiescent".to_owned()));
|
||||
}
|
||||
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
|
||||
return Err(ChannelError::close("Got revoke/ACK message when channel was not in an operational state".to_owned()));
|
||||
}
|
||||
|
@ -5808,7 +5926,7 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
// OK, we step the channel here and *then* if the new generation fails we can fail the
|
||||
// channel based on that, but stepping stuff here should be safe either way.
|
||||
self.context.channel_state.clear_awaiting_remote_revoke();
|
||||
self.context.sent_message_awaiting_response = None;
|
||||
self.mark_response_received();
|
||||
self.context.counterparty_prev_commitment_point = self.context.counterparty_cur_commitment_point;
|
||||
self.context.counterparty_cur_commitment_point = Some(msg.next_per_commitment_point);
|
||||
self.context.cur_counterparty_commitment_transaction_number -= 1;
|
||||
|
@ -5960,29 +6078,7 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
|
||||
self.context.monitor_pending_update_adds.append(&mut pending_update_adds);
|
||||
|
||||
if self.context.channel_state.is_monitor_update_in_progress() {
|
||||
// We can't actually generate a new commitment transaction (incl by freeing holding
|
||||
// cells) while we can't update the monitor, so we just return what we have.
|
||||
if require_commitment {
|
||||
self.context.monitor_pending_commitment_signed = true;
|
||||
// When the monitor updating is restored we'll call
|
||||
// get_last_commitment_update_for_send(), which does not update state, but we're
|
||||
// definitely now awaiting a remote revoke before we can step forward any more, so
|
||||
// set it here.
|
||||
let mut additional_update = self.build_commitment_no_status_check(logger);
|
||||
// build_commitment_no_status_check may bump latest_monitor_id but we want them to be
|
||||
// strictly increasing by one, so decrement it here.
|
||||
self.context.latest_monitor_update_id = monitor_update.update_id;
|
||||
monitor_update.updates.append(&mut additional_update.updates);
|
||||
}
|
||||
self.context.monitor_pending_forwards.append(&mut to_forward_infos);
|
||||
self.context.monitor_pending_failures.append(&mut revoked_htlcs);
|
||||
self.context.monitor_pending_finalized_fulfills.append(&mut finalized_claimed_htlcs);
|
||||
log_debug!(logger, "Received a valid revoke_and_ack for channel {} but awaiting a monitor update resolution to reply.", &self.context.channel_id());
|
||||
return_with_htlcs_to_fail!(Vec::new());
|
||||
}
|
||||
|
||||
match self.free_holding_cell_htlcs(fee_estimator, logger) {
|
||||
match self.maybe_free_holding_cell_htlcs(fee_estimator, logger) {
|
||||
(Some(mut additional_update), htlcs_to_fail) => {
|
||||
// free_holding_cell_htlcs may bump latest_monitor_id multiple times but we want them to be
|
||||
// strictly increasing by one, so decrement it here.
|
||||
|
@ -5997,6 +6093,11 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
},
|
||||
(None, htlcs_to_fail) => {
|
||||
if require_commitment {
|
||||
// We can't generate a new commitment transaction yet so we just return what we
|
||||
// have. When the monitor updating is restored we'll call
|
||||
// get_last_commitment_update_for_send(), which does not update state, but we're
|
||||
// definitely now awaiting a remote revoke before we can step forward any more,
|
||||
// so set it here.
|
||||
let mut additional_update = self.build_commitment_no_status_check(logger);
|
||||
|
||||
// build_commitment_no_status_check may bump latest_monitor_id but we want them to be
|
||||
|
@ -6004,10 +6105,24 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
self.context.latest_monitor_update_id = monitor_update.update_id;
|
||||
monitor_update.updates.append(&mut additional_update.updates);
|
||||
|
||||
log_debug!(logger, "Received a valid revoke_and_ack for channel {}. Responding with a commitment update with {} HTLCs failed. {} monitor update.",
|
||||
&self.context.channel_id(),
|
||||
update_fail_htlcs.len() + update_fail_malformed_htlcs.len(),
|
||||
release_state_str);
|
||||
log_debug!(logger, "Received a valid revoke_and_ack for channel {}. {} monitor update.",
|
||||
&self.context.channel_id(), release_state_str);
|
||||
if self.context.channel_state.can_generate_new_commitment() {
|
||||
log_debug!(logger, "Responding with a commitment update with {} HTLCs failed for channel {}",
|
||||
update_fail_htlcs.len() + update_fail_malformed_htlcs.len(),
|
||||
&self.context.channel_id);
|
||||
} else {
|
||||
debug_assert!(htlcs_to_fail.is_empty());
|
||||
let reason = if self.context.channel_state.is_local_stfu_sent() {
|
||||
"exits quiescence"
|
||||
} else if self.context.channel_state.is_monitor_update_in_progress() {
|
||||
"completes pending monitor update"
|
||||
} else {
|
||||
"can continue progress"
|
||||
};
|
||||
log_debug!(logger, "Holding back commitment update until channel {} {}",
|
||||
&self.context.channel_id, reason);
|
||||
}
|
||||
|
||||
self.monitor_updating_paused(false, true, false, to_forward_infos, revoked_htlcs, finalized_claimed_htlcs);
|
||||
return_with_htlcs_to_fail!(htlcs_to_fail);
|
||||
|
@ -6144,7 +6259,10 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
return None;
|
||||
}
|
||||
|
||||
if self.context.channel_state.is_awaiting_remote_revoke() || self.context.channel_state.is_monitor_update_in_progress() {
|
||||
// Some of the checks of `can_generate_new_commitment` have already been done above, but
|
||||
// it's much more brittle to not use it in favor of checking the remaining flags left, as it
|
||||
// gives us one less code path to update if the method changes.
|
||||
if !self.context.channel_state.can_generate_new_commitment() {
|
||||
force_holding_cell = true;
|
||||
}
|
||||
|
||||
|
@ -6174,6 +6292,10 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
return Err(())
|
||||
}
|
||||
|
||||
// We only clear `peer_disconnected` if we were able to reestablish the channel. We always
|
||||
// reset our awaiting response in case we failed reestablishment and are disconnecting.
|
||||
self.context.sent_message_awaiting_response = None;
|
||||
|
||||
if self.context.channel_state.is_peer_disconnected() {
|
||||
// While the below code should be idempotent, it's simpler to just return early, as
|
||||
// redundant disconnect events can fire, though they should be rare.
|
||||
|
@ -6234,7 +6356,14 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
}
|
||||
}
|
||||
|
||||
self.context.sent_message_awaiting_response = None;
|
||||
// Reset any quiescence-related state as it is implicitly terminated once disconnected.
|
||||
if matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
|
||||
self.context.channel_state.clear_awaiting_quiescence();
|
||||
self.context.channel_state.clear_local_stfu_sent();
|
||||
self.context.channel_state.clear_remote_stfu_sent();
|
||||
self.context.channel_state.clear_quiescent();
|
||||
self.context.is_holder_quiescence_initiator.take();
|
||||
}
|
||||
|
||||
self.context.channel_state.set_peer_disconnected();
|
||||
log_trace!(logger, "Peer disconnection resulted in {} remote-announced HTLC drops on channel {}", inbound_drop_count, &self.context.channel_id());
|
||||
|
@ -6351,10 +6480,6 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
commitment_update = None;
|
||||
}
|
||||
|
||||
if commitment_update.is_some() {
|
||||
self.mark_awaiting_response();
|
||||
}
|
||||
|
||||
self.context.monitor_pending_revoke_and_ack = false;
|
||||
self.context.monitor_pending_commitment_signed = false;
|
||||
let order = self.context.resend_order.clone();
|
||||
|
@ -6397,6 +6522,9 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
if self.context.channel_state.is_peer_disconnected() {
|
||||
return Err(ChannelError::close("Peer sent update_fee when we needed a channel_reestablish".to_owned()));
|
||||
}
|
||||
if self.context.channel_state.is_remote_stfu_sent() || self.context.channel_state.is_quiescent() {
|
||||
return Err(ChannelError::WarnAndDisconnect("Got fee update message while quiescent".to_owned()));
|
||||
}
|
||||
FundedChannel::<SP>::check_remote_fee(&self.context.channel_type, fee_estimator, msg.feerate_per_kw, Some(self.context.feerate_per_kw), logger)?;
|
||||
|
||||
self.context.pending_update_fee = Some((msg.feerate_per_kw, FeeUpdateState::RemoteAnnounced));
|
||||
|
@ -6708,7 +6836,7 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
// Go ahead and unmark PeerDisconnected as various calls we may make check for it (and all
|
||||
// remaining cases either succeed or ErrorMessage-fail).
|
||||
self.context.channel_state.clear_peer_disconnected();
|
||||
self.context.sent_message_awaiting_response = None;
|
||||
self.mark_response_received();
|
||||
|
||||
let shutdown_msg = self.get_outbound_shutdown();
|
||||
|
||||
|
@ -6764,9 +6892,6 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
// AwaitingRemoteRevoke set, which indicates we sent a commitment_signed but haven't gotten
|
||||
// the corresponding revoke_and_ack back yet.
|
||||
let is_awaiting_remote_revoke = self.context.channel_state.is_awaiting_remote_revoke();
|
||||
if is_awaiting_remote_revoke && !self.is_awaiting_monitor_update() {
|
||||
self.mark_awaiting_response();
|
||||
}
|
||||
let next_counterparty_commitment_number = INITIAL_COMMITMENT_NUMBER - self.context.cur_counterparty_commitment_transaction_number + if is_awaiting_remote_revoke { 1 } else { 0 };
|
||||
|
||||
let channel_ready = if msg.next_local_commitment_number == 1 && INITIAL_COMMITMENT_NUMBER - self.holder_commitment_point.transaction_number() == 1 {
|
||||
|
@ -6951,26 +7076,38 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
Ok((closing_signed, None, None))
|
||||
}
|
||||
|
||||
// Marks a channel as waiting for a response from the counterparty. If it's not received
|
||||
// [`DISCONNECT_PEER_AWAITING_RESPONSE_TICKS`] after sending our own to them, then we'll attempt
|
||||
// a reconnection.
|
||||
fn mark_awaiting_response(&mut self) {
|
||||
self.context.sent_message_awaiting_response = Some(0);
|
||||
fn mark_response_received(&mut self) {
|
||||
self.context.sent_message_awaiting_response = None;
|
||||
}
|
||||
|
||||
/// Determines whether we should disconnect the counterparty due to not receiving a response
|
||||
/// within our expected timeframe.
|
||||
///
|
||||
/// This should be called on every [`super::channelmanager::ChannelManager::timer_tick_occurred`].
|
||||
/// This should be called for peers with an active socket on every
|
||||
/// [`super::channelmanager::ChannelManager::timer_tick_occurred`].
|
||||
#[allow(clippy::assertions_on_constants)]
|
||||
pub fn should_disconnect_peer_awaiting_response(&mut self) -> bool {
|
||||
let ticks_elapsed = if let Some(ticks_elapsed) = self.context.sent_message_awaiting_response.as_mut() {
|
||||
ticks_elapsed
|
||||
if let Some(ticks_elapsed) = self.context.sent_message_awaiting_response.as_mut() {
|
||||
*ticks_elapsed += 1;
|
||||
*ticks_elapsed >= DISCONNECT_PEER_AWAITING_RESPONSE_TICKS
|
||||
} else if
|
||||
// Cleared upon receiving `channel_reestablish`.
|
||||
self.context.channel_state.is_peer_disconnected()
|
||||
// Cleared upon receiving `stfu`.
|
||||
|| self.context.channel_state.is_local_stfu_sent()
|
||||
// Cleared upon receiving a message that triggers the end of quiescence.
|
||||
|| self.context.channel_state.is_quiescent()
|
||||
// Cleared upon receiving `revoke_and_ack`.
|
||||
|| self.context.has_pending_channel_update()
|
||||
{
|
||||
// This is the first tick we've seen after expecting to make forward progress.
|
||||
self.context.sent_message_awaiting_response = Some(1);
|
||||
debug_assert!(DISCONNECT_PEER_AWAITING_RESPONSE_TICKS > 1);
|
||||
false
|
||||
} else {
|
||||
// Don't disconnect when we're not waiting on a response.
|
||||
return false;
|
||||
};
|
||||
*ticks_elapsed += 1;
|
||||
*ticks_elapsed >= DISCONNECT_PEER_AWAITING_RESPONSE_TICKS
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shutdown(
|
||||
|
@ -6993,6 +7130,14 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
}
|
||||
assert!(!matches!(self.context.channel_state, ChannelState::ShutdownComplete));
|
||||
|
||||
// TODO: The spec is pretty vague regarding the handling of shutdown within quiescence.
|
||||
if self.context.channel_state.is_local_stfu_sent()
|
||||
|| self.context.channel_state.is_remote_stfu_sent()
|
||||
|| self.context.channel_state.is_quiescent()
|
||||
{
|
||||
return Err(ChannelError::WarnAndDisconnect("Got shutdown request while quiescent".to_owned()));
|
||||
}
|
||||
|
||||
if !script::is_bolt2_compliant(&msg.scriptpubkey, their_features) {
|
||||
return Err(ChannelError::Warn(format!("Got a nonstandard scriptpubkey ({}) from remote peer", msg.scriptpubkey.to_hex_string())));
|
||||
}
|
||||
|
@ -7029,6 +7174,11 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
// From here on out, we may not fail!
|
||||
|
||||
self.context.channel_state.set_remote_shutdown_sent();
|
||||
if self.context.channel_state.is_awaiting_quiescence() {
|
||||
// We haven't been able to send `stfu` yet, and there's no point in attempting
|
||||
// quiescence anymore since the counterparty wishes to close the channel.
|
||||
self.context.channel_state.clear_awaiting_quiescence();
|
||||
}
|
||||
self.context.update_time_counter += 1;
|
||||
|
||||
let monitor_update = if update_shutdown_script {
|
||||
|
@ -8120,7 +8270,6 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
log_info!(logger, "Sending a data_loss_protect with no previous remote per_commitment_secret for channel {}", &self.context.channel_id());
|
||||
[0;32]
|
||||
};
|
||||
self.mark_awaiting_response();
|
||||
msgs::ChannelReestablish {
|
||||
channel_id: self.context.channel_id(),
|
||||
// The protocol has two different commitment number concepts - the "commitment
|
||||
|
@ -8481,6 +8630,12 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
target_feerate_sats_per_kw: Option<u32>, override_shutdown_script: Option<ShutdownScript>)
|
||||
-> Result<(msgs::Shutdown, Option<ChannelMonitorUpdate>, Vec<(HTLCSource, PaymentHash)>), APIError>
|
||||
{
|
||||
if self.context.channel_state.is_local_stfu_sent()
|
||||
|| self.context.channel_state.is_remote_stfu_sent()
|
||||
|| self.context.channel_state.is_quiescent()
|
||||
{
|
||||
return Err(APIError::APIMisuseError { err: "Cannot begin shutdown while quiescent".to_owned() });
|
||||
}
|
||||
for htlc in self.context.pending_outbound_htlcs.iter() {
|
||||
if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
|
||||
return Err(APIError::APIMisuseError{err: "Cannot begin shutdown with pending HTLCs. Process pending events first".to_owned()});
|
||||
|
@ -8525,6 +8680,9 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
// From here on out, we may not fail!
|
||||
self.context.target_closing_feerate_sats_per_kw = target_feerate_sats_per_kw;
|
||||
self.context.channel_state.set_local_shutdown_sent();
|
||||
if self.context.channel_state.is_awaiting_quiescence() {
|
||||
self.context.channel_state.clear_awaiting_quiescence();
|
||||
}
|
||||
self.context.local_initiated_shutdown = Some(());
|
||||
self.context.update_time_counter += 1;
|
||||
|
||||
|
@ -8590,6 +8748,200 @@ impl<SP: Deref> FundedChannel<SP> where
|
|||
self.context.counterparty_max_htlc_value_in_flight_msat
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(test, fuzzing))]
|
||||
pub fn propose_quiescence<L: Deref>(
|
||||
&mut self, logger: &L,
|
||||
) -> Result<Option<msgs::Stfu>, ChannelError>
|
||||
where
|
||||
L::Target: Logger,
|
||||
{
|
||||
log_debug!(logger, "Attempting to initiate quiescence");
|
||||
|
||||
if !self.context.is_live() {
|
||||
return Err(ChannelError::Ignore(
|
||||
"Channel is not in a live state to propose quiescence".to_owned()
|
||||
));
|
||||
}
|
||||
if self.context.channel_state.is_quiescent() {
|
||||
return Err(ChannelError::Ignore("Channel is already quiescent".to_owned()));
|
||||
}
|
||||
|
||||
if self.context.channel_state.is_awaiting_quiescence()
|
||||
|| self.context.channel_state.is_local_stfu_sent()
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
self.context.channel_state.set_awaiting_quiescence();
|
||||
Ok(Some(self.send_stfu(logger)?))
|
||||
}
|
||||
|
||||
// Assumes we are either awaiting quiescence or our counterparty has requested quiescence.
|
||||
pub fn send_stfu<L: Deref>(&mut self, logger: &L) -> Result<msgs::Stfu, ChannelError>
|
||||
where
|
||||
L::Target: Logger,
|
||||
{
|
||||
debug_assert!(!self.context.channel_state.is_local_stfu_sent());
|
||||
// Either state being set implies the channel is live.
|
||||
debug_assert!(
|
||||
self.context.channel_state.is_awaiting_quiescence()
|
||||
|| self.context.channel_state.is_remote_stfu_sent()
|
||||
);
|
||||
debug_assert!(self.context.is_live());
|
||||
|
||||
if self.context.has_pending_channel_update() {
|
||||
return Err(ChannelError::Ignore(
|
||||
"We cannot send `stfu` while state machine is pending".to_owned()
|
||||
));
|
||||
}
|
||||
|
||||
let initiator = if self.context.channel_state.is_remote_stfu_sent() {
|
||||
// We may have also attempted to initiate quiescence.
|
||||
self.context.channel_state.clear_awaiting_quiescence();
|
||||
self.context.channel_state.clear_remote_stfu_sent();
|
||||
self.context.channel_state.set_quiescent();
|
||||
if let Some(initiator) = self.context.is_holder_quiescence_initiator.as_ref() {
|
||||
log_debug!(
|
||||
logger,
|
||||
"Responding to counterparty stfu with our own, channel is now quiescent and we are{} the initiator",
|
||||
if !initiator { " not" } else { "" }
|
||||
);
|
||||
|
||||
*initiator
|
||||
} else {
|
||||
debug_assert!(false, "Quiescence initiator must have been set when we received stfu");
|
||||
false
|
||||
}
|
||||
} else {
|
||||
log_debug!(logger, "Sending stfu as quiescence initiator");
|
||||
debug_assert!(self.context.channel_state.is_awaiting_quiescence());
|
||||
self.context.channel_state.clear_awaiting_quiescence();
|
||||
self.context.channel_state.set_local_stfu_sent();
|
||||
true
|
||||
};
|
||||
|
||||
Ok(msgs::Stfu { channel_id: self.context.channel_id, initiator })
|
||||
}
|
||||
|
||||
pub fn stfu<L: Deref>(
|
||||
&mut self, msg: &msgs::Stfu, logger: &L
|
||||
) -> Result<Option<msgs::Stfu>, ChannelError> where L::Target: Logger {
|
||||
if self.context.channel_state.is_quiescent() {
|
||||
return Err(ChannelError::Warn("Channel is already quiescent".to_owned()));
|
||||
}
|
||||
if self.context.channel_state.is_remote_stfu_sent() {
|
||||
return Err(ChannelError::Warn(
|
||||
"Peer sent `stfu` when they already sent it and we've yet to become quiescent".to_owned()
|
||||
));
|
||||
}
|
||||
|
||||
if !self.context.is_live() {
|
||||
return Err(ChannelError::Warn(
|
||||
"Peer sent `stfu` when we were not in a live state".to_owned()
|
||||
));
|
||||
}
|
||||
|
||||
if self.context.channel_state.is_awaiting_quiescence()
|
||||
|| !self.context.channel_state.is_local_stfu_sent()
|
||||
{
|
||||
if !msg.initiator {
|
||||
return Err(ChannelError::WarnAndDisconnect(
|
||||
"Peer sent unexpected `stfu` without signaling as initiator".to_owned()
|
||||
));
|
||||
}
|
||||
|
||||
// We don't check `has_pending_channel_update` prior to setting the flag because it
|
||||
// considers pending updates from either node. This means we may accept a counterparty
|
||||
// `stfu` while they had pending updates, but that's fine as we won't send ours until
|
||||
// _all_ pending updates complete, allowing the channel to become quiescent then.
|
||||
self.context.channel_state.set_remote_stfu_sent();
|
||||
|
||||
let is_holder_initiator = if self.context.channel_state.is_awaiting_quiescence() {
|
||||
// We were also planning to propose quiescence, let the tie-breaker decide the
|
||||
// initiator.
|
||||
self.context.is_outbound()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
self.context.is_holder_quiescence_initiator = Some(is_holder_initiator);
|
||||
|
||||
log_debug!(logger, "Received counterparty stfu proposing quiescence");
|
||||
return self.send_stfu(logger).map(|stfu| Some(stfu));
|
||||
}
|
||||
|
||||
// We already sent `stfu` and are now processing theirs. It may be in response to ours, or
|
||||
// we happened to both send `stfu` at the same time and a tie-break is needed.
|
||||
let is_holder_quiescence_initiator = !msg.initiator || self.context.is_outbound();
|
||||
self.context.is_holder_quiescence_initiator = Some(is_holder_quiescence_initiator);
|
||||
|
||||
// We were expecting to receive `stfu` because we already sent ours.
|
||||
self.mark_response_received();
|
||||
|
||||
if self.context.has_pending_channel_update() {
|
||||
// Since we've already sent `stfu`, it should not be possible for one of our updates to
|
||||
// be pending, so anything pending currently must be from a counterparty update.
|
||||
return Err(ChannelError::WarnAndDisconnect(
|
||||
"Received counterparty stfu while having pending counterparty updates".to_owned()
|
||||
));
|
||||
}
|
||||
|
||||
self.context.channel_state.clear_local_stfu_sent();
|
||||
self.context.channel_state.set_quiescent();
|
||||
|
||||
log_debug!(
|
||||
logger,
|
||||
"Received counterparty stfu, channel is now quiescent and we are{} the initiator",
|
||||
if !is_holder_quiescence_initiator { " not" } else { "" }
|
||||
);
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn try_send_stfu<L: Deref>(
|
||||
&mut self, logger: &L,
|
||||
) -> Result<Option<msgs::Stfu>, ChannelError>
|
||||
where
|
||||
L::Target: Logger,
|
||||
{
|
||||
// We must never see both stfu flags set, we always set the quiescent flag instead.
|
||||
debug_assert!(
|
||||
!(self.context.channel_state.is_local_stfu_sent()
|
||||
&& self.context.channel_state.is_remote_stfu_sent())
|
||||
);
|
||||
|
||||
// We need to send our `stfu`, either because we're trying to initiate quiescence, or the
|
||||
// counterparty is and we've yet to send ours.
|
||||
if self.context.channel_state.is_awaiting_quiescence()
|
||||
|| (self.context.channel_state.is_remote_stfu_sent()
|
||||
&& !self.context.channel_state.is_local_stfu_sent())
|
||||
{
|
||||
return self.send_stfu(logger).map(|stfu| Some(stfu));
|
||||
}
|
||||
|
||||
// We're either:
|
||||
// - already quiescent
|
||||
// - in a state where quiescence is not possible
|
||||
// - not currently trying to become quiescent
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[cfg(any(test, fuzzing))]
|
||||
pub fn exit_quiescence(&mut self) -> bool {
|
||||
// Make sure we either finished the quiescence handshake and are quiescent, or we never
|
||||
// attempted to initiate quiescence at all.
|
||||
debug_assert!(!self.context.channel_state.is_awaiting_quiescence());
|
||||
debug_assert!(!self.context.channel_state.is_local_stfu_sent());
|
||||
debug_assert!(!self.context.channel_state.is_remote_stfu_sent());
|
||||
|
||||
if self.context.channel_state.is_quiescent() {
|
||||
self.mark_response_received();
|
||||
self.context.channel_state.clear_quiescent();
|
||||
self.context.is_holder_quiescence_initiator.take().expect("Must always be set while quiescent")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A not-yet-funded outbound (from holder) channel using V1 channel establishment.
|
||||
|
@ -9581,11 +9933,17 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
|
|||
self.context.channel_id.write(writer)?;
|
||||
{
|
||||
let mut channel_state = self.context.channel_state;
|
||||
if matches!(channel_state, ChannelState::AwaitingChannelReady(_)|ChannelState::ChannelReady(_)) {
|
||||
channel_state.set_peer_disconnected();
|
||||
} else {
|
||||
debug_assert!(false, "Pre-funded/shutdown channels should not be written");
|
||||
match channel_state {
|
||||
ChannelState::AwaitingChannelReady(_) => {},
|
||||
ChannelState::ChannelReady(_) => {
|
||||
channel_state.clear_awaiting_quiescence();
|
||||
channel_state.clear_local_stfu_sent();
|
||||
channel_state.clear_remote_stfu_sent();
|
||||
channel_state.clear_quiescent();
|
||||
},
|
||||
_ => debug_assert!(false, "Pre-funded/shutdown channels should not be written"),
|
||||
}
|
||||
channel_state.set_peer_disconnected();
|
||||
channel_state.to_u32().write(writer)?;
|
||||
}
|
||||
self.funding.channel_value_satoshis.write(writer)?;
|
||||
|
@ -10497,6 +10855,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
|
|||
|
||||
blocked_monitor_updates: blocked_monitor_updates.unwrap(),
|
||||
is_manual_broadcast: is_manual_broadcast.unwrap_or(false),
|
||||
|
||||
// TODO(dual_funding): Instead of getting this from persisted value, figure it out based on the
|
||||
// funding transaction and other channel state.
|
||||
//
|
||||
|
@ -10504,6 +10863,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
|
|||
// during a signing session, but have not received `tx_signatures` we MUST set `next_funding_txid`
|
||||
// to the txid of that interactive transaction, else we MUST NOT set it.
|
||||
next_funding_txid: None,
|
||||
|
||||
is_holder_quiescence_initiator: None,
|
||||
},
|
||||
interactive_tx_signing_session: None,
|
||||
holder_commitment_point,
|
||||
|
|
|
@ -840,6 +840,15 @@ impl MsgHandleErrInternal {
|
|||
log_level: Level::Warn,
|
||||
},
|
||||
},
|
||||
ChannelError::WarnAndDisconnect(msg) => LightningError {
|
||||
err: msg.clone(),
|
||||
action: msgs::ErrorAction::DisconnectPeerWithWarning {
|
||||
msg: msgs::WarningMessage {
|
||||
channel_id,
|
||||
data: msg
|
||||
},
|
||||
},
|
||||
},
|
||||
ChannelError::Ignore(msg) => LightningError {
|
||||
err: msg,
|
||||
action: msgs::ErrorAction::IgnoreError,
|
||||
|
@ -3069,6 +3078,9 @@ macro_rules! convert_channel_err {
|
|||
ChannelError::Warn(msg) => {
|
||||
(false, MsgHandleErrInternal::from_chan_no_close(ChannelError::Warn(msg), *$channel_id))
|
||||
},
|
||||
ChannelError::WarnAndDisconnect(msg) => {
|
||||
(false, MsgHandleErrInternal::from_chan_no_close(ChannelError::WarnAndDisconnect(msg), *$channel_id))
|
||||
},
|
||||
ChannelError::Ignore(msg) => {
|
||||
(false, MsgHandleErrInternal::from_chan_no_close(ChannelError::Ignore(msg), *$channel_id))
|
||||
},
|
||||
|
@ -6620,19 +6632,21 @@ where
|
|||
|
||||
funded_chan.context.maybe_expire_prev_config();
|
||||
|
||||
if funded_chan.should_disconnect_peer_awaiting_response() {
|
||||
let logger = WithChannelContext::from(&self.logger, &funded_chan.context, None);
|
||||
log_debug!(logger, "Disconnecting peer {} due to not making any progress on channel {}",
|
||||
counterparty_node_id, chan_id);
|
||||
pending_msg_events.push(MessageSendEvent::HandleError {
|
||||
node_id: counterparty_node_id,
|
||||
action: msgs::ErrorAction::DisconnectPeerWithWarning {
|
||||
msg: msgs::WarningMessage {
|
||||
channel_id: *chan_id,
|
||||
data: "Disconnecting due to timeout awaiting response".to_owned(),
|
||||
if peer_state.is_connected {
|
||||
if funded_chan.should_disconnect_peer_awaiting_response() {
|
||||
let logger = WithChannelContext::from(&self.logger, &funded_chan.context, None);
|
||||
log_debug!(logger, "Disconnecting peer {} due to not making any progress on channel {}",
|
||||
counterparty_node_id, chan_id);
|
||||
pending_msg_events.push(MessageSendEvent::HandleError {
|
||||
node_id: counterparty_node_id,
|
||||
action: msgs::ErrorAction::DisconnectPeerWithWarning {
|
||||
msg: msgs::WarningMessage {
|
||||
channel_id: *chan_id,
|
||||
data: "Disconnecting due to timeout awaiting response".to_owned(),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
|
@ -9214,6 +9228,58 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn internal_stfu(&self, counterparty_node_id: &PublicKey, msg: &msgs::Stfu) -> Result<bool, MsgHandleErrInternal> {
|
||||
let per_peer_state = self.per_peer_state.read().unwrap();
|
||||
let peer_state_mutex = per_peer_state.get(counterparty_node_id).ok_or_else(|| {
|
||||
debug_assert!(false);
|
||||
MsgHandleErrInternal::send_err_msg_no_close(
|
||||
format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id),
|
||||
msg.channel_id
|
||||
)
|
||||
})?;
|
||||
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
|
||||
let peer_state = &mut *peer_state_lock;
|
||||
|
||||
if !self.init_features().supports_quiescence() {
|
||||
return Err(MsgHandleErrInternal::from_chan_no_close(
|
||||
ChannelError::Warn("Quiescense not supported".to_string()), msg.channel_id
|
||||
));
|
||||
}
|
||||
|
||||
let mut sent_stfu = false;
|
||||
match peer_state.channel_by_id.entry(msg.channel_id) {
|
||||
hash_map::Entry::Occupied(mut chan_entry) => {
|
||||
if let Some(chan) = chan_entry.get_mut().as_funded_mut() {
|
||||
let logger = WithContext::from(
|
||||
&self.logger, Some(*counterparty_node_id), Some(msg.channel_id), None
|
||||
);
|
||||
|
||||
if let Some(stfu) = try_channel_entry!(
|
||||
self, peer_state, chan.stfu(&msg, &&logger), chan_entry
|
||||
) {
|
||||
sent_stfu = true;
|
||||
peer_state.pending_msg_events.push(MessageSendEvent::SendStfu {
|
||||
node_id: *counterparty_node_id,
|
||||
msg: stfu,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
let msg = "Peer sent `stfu` for an unfunded channel";
|
||||
let err = Err(ChannelError::Close(
|
||||
(msg.into(), ClosureReason::ProcessingError { err: msg.into() })
|
||||
));
|
||||
return try_channel_entry!(self, peer_state, err, chan_entry);
|
||||
}
|
||||
},
|
||||
hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close(
|
||||
format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id),
|
||||
msg.channel_id
|
||||
))
|
||||
}
|
||||
|
||||
Ok(sent_stfu)
|
||||
}
|
||||
|
||||
fn internal_announcement_signatures(&self, counterparty_node_id: &PublicKey, msg: &msgs::AnnouncementSignatures) -> Result<(), MsgHandleErrInternal> {
|
||||
let per_peer_state = self.per_peer_state.read().unwrap();
|
||||
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
|
||||
|
@ -9739,6 +9805,118 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
|
|||
has_update
|
||||
}
|
||||
|
||||
fn maybe_send_stfu(&self) {
|
||||
let per_peer_state = self.per_peer_state.read().unwrap();
|
||||
for (counterparty_node_id, peer_state_mutex) in per_peer_state.iter() {
|
||||
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
|
||||
let peer_state = &mut *peer_state_lock;
|
||||
let pending_msg_events = &mut peer_state.pending_msg_events;
|
||||
for (channel_id, chan) in &mut peer_state.channel_by_id {
|
||||
if let Some(funded_chan) = chan.as_funded_mut() {
|
||||
let logger = WithContext::from(
|
||||
&self.logger, Some(*counterparty_node_id), Some(*channel_id), None
|
||||
);
|
||||
match funded_chan.try_send_stfu(&&logger) {
|
||||
Ok(None) => {},
|
||||
Ok(Some(stfu)) => {
|
||||
pending_msg_events.push(events::MessageSendEvent::SendStfu {
|
||||
node_id: chan.context().get_counterparty_node_id(),
|
||||
msg: stfu,
|
||||
});
|
||||
},
|
||||
Err(e) => {
|
||||
log_debug!(logger, "Could not advance quiescence handshake: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, fuzzing))]
|
||||
pub fn maybe_propose_quiescence(&self, counterparty_node_id: &PublicKey, channel_id: &ChannelId) -> Result<(), APIError> {
|
||||
let mut result = Ok(());
|
||||
PersistenceNotifierGuard::optionally_notify(self, || {
|
||||
let mut notify = NotifyOption::SkipPersistNoEvents;
|
||||
|
||||
let per_peer_state = self.per_peer_state.read().unwrap();
|
||||
let peer_state_mutex_opt = per_peer_state.get(counterparty_node_id);
|
||||
if peer_state_mutex_opt.is_none() {
|
||||
result = Err(APIError::ChannelUnavailable {
|
||||
err: format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id)
|
||||
});
|
||||
return notify;
|
||||
}
|
||||
|
||||
let mut peer_state = peer_state_mutex_opt.unwrap().lock().unwrap();
|
||||
if !peer_state.latest_features.supports_quiescence() {
|
||||
result = Err(APIError::ChannelUnavailable { err: "Peer does not support quiescence".to_owned() });
|
||||
return notify;
|
||||
}
|
||||
|
||||
match peer_state.channel_by_id.entry(channel_id.clone()) {
|
||||
hash_map::Entry::Occupied(mut chan_entry) => {
|
||||
if let Some(chan) = chan_entry.get_mut().as_funded_mut() {
|
||||
let logger = WithContext::from(
|
||||
&self.logger, Some(*counterparty_node_id), Some(*channel_id), None
|
||||
);
|
||||
|
||||
match chan.propose_quiescence(&&logger) {
|
||||
Ok(None) => {},
|
||||
Ok(Some(stfu)) => {
|
||||
peer_state.pending_msg_events.push(MessageSendEvent::SendStfu {
|
||||
node_id: *counterparty_node_id, msg: stfu
|
||||
});
|
||||
notify = NotifyOption::SkipPersistHandleEvents;
|
||||
},
|
||||
Err(msg) => log_trace!(logger, "{}", msg),
|
||||
}
|
||||
} else {
|
||||
result = Err(APIError::APIMisuseError {
|
||||
err: format!("Unfunded channel {} cannot be quiescent", channel_id),
|
||||
});
|
||||
}
|
||||
},
|
||||
hash_map::Entry::Vacant(_) => {
|
||||
result = Err(APIError::ChannelUnavailable {
|
||||
err: format!("Channel with id {} not found for the passed counterparty node_id {}",
|
||||
channel_id, counterparty_node_id),
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
notify
|
||||
});
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(any(test, fuzzing))]
|
||||
pub fn exit_quiescence(&self, counterparty_node_id: &PublicKey, channel_id: &ChannelId) -> Result<bool, APIError> {
|
||||
let per_peer_state = self.per_peer_state.read().unwrap();
|
||||
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
|
||||
.ok_or_else(|| APIError::ChannelUnavailable {
|
||||
err: format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id)
|
||||
})?;
|
||||
let mut peer_state = peer_state_mutex.lock().unwrap();
|
||||
let initiator = match peer_state.channel_by_id.entry(*channel_id) {
|
||||
hash_map::Entry::Occupied(mut chan_entry) => {
|
||||
if let Some(chan) = chan_entry.get_mut().as_funded_mut() {
|
||||
chan.exit_quiescence()
|
||||
} else {
|
||||
return Err(APIError::APIMisuseError {
|
||||
err: format!("Unfunded channel {} cannot be quiescent", channel_id),
|
||||
})
|
||||
}
|
||||
},
|
||||
hash_map::Entry::Vacant(_) => return Err(APIError::ChannelUnavailable {
|
||||
err: format!("Channel with id {} not found for the passed counterparty node_id {}",
|
||||
channel_id, counterparty_node_id),
|
||||
}),
|
||||
};
|
||||
Ok(initiator)
|
||||
}
|
||||
|
||||
/// Utility for creating a BOLT11 invoice that can be verified by [`ChannelManager`] without
|
||||
/// storing any additional state. It achieves this by including a [`PaymentSecret`] in the
|
||||
/// invoice which it uses to verify that the invoice has not expired and the payment amount is
|
||||
|
@ -10927,6 +11105,9 @@ where
|
|||
result = NotifyOption::DoPersist;
|
||||
}
|
||||
|
||||
// Quiescence is an in-memory protocol, so we don't have to persist because of it.
|
||||
self.maybe_send_stfu();
|
||||
|
||||
let mut is_any_peer_connected = false;
|
||||
let mut pending_events = Vec::new();
|
||||
let per_peer_state = self.per_peer_state.read().unwrap();
|
||||
|
@ -11547,9 +11728,20 @@ where
|
|||
}
|
||||
|
||||
fn handle_stfu(&self, counterparty_node_id: PublicKey, msg: &msgs::Stfu) {
|
||||
let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
|
||||
"Quiescence not supported".to_owned(),
|
||||
msg.channel_id.clone())), counterparty_node_id);
|
||||
let _persistence_guard = PersistenceNotifierGuard::optionally_notify(self, || {
|
||||
let res = self.internal_stfu(&counterparty_node_id, msg);
|
||||
let persist = match &res {
|
||||
Err(e) if e.closes_channel() => NotifyOption::DoPersist,
|
||||
Err(_) => NotifyOption::SkipPersistHandleEvents,
|
||||
Ok(sent_stfu) => if *sent_stfu {
|
||||
NotifyOption::SkipPersistHandleEvents
|
||||
} else {
|
||||
NotifyOption::SkipPersistNoEvents
|
||||
},
|
||||
};
|
||||
let _ = handle_error!(self, res, counterparty_node_id);
|
||||
persist
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(splicing)]
|
||||
|
@ -12558,6 +12750,10 @@ pub fn provided_init_features(config: &UserConfig) -> InitFeatures {
|
|||
}
|
||||
#[cfg(dual_funding)]
|
||||
features.set_dual_fund_optional();
|
||||
// Only signal quiescence support in tests for now, as we don't yet support any
|
||||
// quiescent-dependent protocols (e.g., splicing).
|
||||
#[cfg(any(test, fuzzing))]
|
||||
features.set_quiescence_optional();
|
||||
features
|
||||
}
|
||||
|
||||
|
|
|
@ -89,6 +89,8 @@ mod monitor_tests;
|
|||
#[allow(unused_mut)]
|
||||
mod shutdown_tests;
|
||||
#[cfg(test)]
|
||||
mod quiescence_tests;
|
||||
#[cfg(test)]
|
||||
#[allow(unused_mut)]
|
||||
mod async_signer_tests;
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -455,8 +455,8 @@ pub type SerialId = u64;
|
|||
pub struct Stfu {
|
||||
/// The channel ID where quiescence is intended
|
||||
pub channel_id: ChannelId,
|
||||
/// Initiator flag, 1 if initiating, 0 if replying to an stfu.
|
||||
pub initiator: u8,
|
||||
/// Initiator flag, true if initiating, false if replying to an stfu.
|
||||
pub initiator: bool,
|
||||
}
|
||||
|
||||
/// A `splice_init` message to be sent by or received from the stfu initiator (splice initiator).
|
||||
|
@ -4112,10 +4112,17 @@ mod tests {
|
|||
fn encoding_stfu() {
|
||||
let stfu = msgs::Stfu {
|
||||
channel_id: ChannelId::from_bytes([2; 32]),
|
||||
initiator: 1,
|
||||
initiator: true,
|
||||
};
|
||||
let encoded_value = stfu.encode();
|
||||
assert_eq!(encoded_value.as_hex().to_string(), "020202020202020202020202020202020202020202020202020202020202020201");
|
||||
|
||||
let stfu = msgs::Stfu {
|
||||
channel_id: ChannelId::from_bytes([3; 32]),
|
||||
initiator: false,
|
||||
};
|
||||
let encoded_value = stfu.encode();
|
||||
assert_eq!(encoded_value.as_hex().to_string(), "030303030303030303030303030303030303030303030303030303030303030300");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
501
lightning/src/ln/quiescence_tests.rs
Normal file
501
lightning/src/ln/quiescence_tests.rs
Normal file
|
@ -0,0 +1,501 @@
|
|||
use crate::chain::ChannelMonitorUpdateStatus;
|
||||
use crate::events::Event;
|
||||
use crate::events::HTLCDestination;
|
||||
use crate::events::MessageSendEvent;
|
||||
use crate::events::MessageSendEventsProvider;
|
||||
use crate::ln::channel::DISCONNECT_PEER_AWAITING_RESPONSE_TICKS;
|
||||
use crate::ln::channelmanager::PaymentId;
|
||||
use crate::ln::channelmanager::RecipientOnionFields;
|
||||
use crate::ln::functional_test_utils::*;
|
||||
use crate::ln::msgs;
|
||||
use crate::ln::msgs::{ChannelMessageHandler, ErrorAction};
|
||||
use crate::util::errors::APIError;
|
||||
use crate::util::test_channel_signer::SignerOp;
|
||||
|
||||
#[test]
|
||||
fn test_quiescence_tie() {
|
||||
// Test that both nodes proposing quiescence at the same time results in the channel funder
|
||||
// becoming the quiescence initiator.
|
||||
let chanmon_cfgs = create_chanmon_cfgs(2);
|
||||
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
|
||||
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
|
||||
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
|
||||
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
|
||||
|
||||
nodes[0].node.maybe_propose_quiescence(&nodes[1].node.get_our_node_id(), &chan_id).unwrap();
|
||||
nodes[1].node.maybe_propose_quiescence(&nodes[0].node.get_our_node_id(), &chan_id).unwrap();
|
||||
|
||||
let stfu_node_0 =
|
||||
get_event_msg!(nodes[0], MessageSendEvent::SendStfu, nodes[1].node.get_our_node_id());
|
||||
nodes[1].node.handle_stfu(nodes[0].node.get_our_node_id(), &stfu_node_0);
|
||||
|
||||
let stfu_node_1 =
|
||||
get_event_msg!(nodes[1], MessageSendEvent::SendStfu, nodes[0].node.get_our_node_id());
|
||||
nodes[0].node.handle_stfu(nodes[1].node.get_our_node_id(), &stfu_node_1);
|
||||
|
||||
assert!(stfu_node_0.initiator && stfu_node_1.initiator);
|
||||
|
||||
assert!(nodes[0].node.exit_quiescence(&nodes[1].node.get_our_node_id(), &chan_id).unwrap());
|
||||
assert!(!nodes[1].node.exit_quiescence(&nodes[0].node.get_our_node_id(), &chan_id).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quiescence_shutdown_ignored() {
|
||||
// Test that a shutdown sent/received during quiescence is ignored.
|
||||
let chanmon_cfgs = create_chanmon_cfgs(2);
|
||||
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
|
||||
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
|
||||
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
|
||||
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
|
||||
|
||||
nodes[0].node.maybe_propose_quiescence(&nodes[1].node.get_our_node_id(), &chan_id).unwrap();
|
||||
let _ = get_event_msg!(nodes[0], MessageSendEvent::SendStfu, nodes[1].node.get_our_node_id());
|
||||
|
||||
if let Err(e) = nodes[0].node.close_channel(&chan_id, &nodes[1].node.get_our_node_id()) {
|
||||
assert_eq!(
|
||||
e,
|
||||
APIError::APIMisuseError { err: "Cannot begin shutdown while quiescent".to_owned() }
|
||||
);
|
||||
} else {
|
||||
panic!("Expected shutdown to be ignored while quiescent");
|
||||
}
|
||||
|
||||
nodes[1].node.close_channel(&chan_id, &nodes[0].node.get_our_node_id()).unwrap();
|
||||
let shutdown =
|
||||
get_event_msg!(nodes[1], MessageSendEvent::SendShutdown, nodes[0].node.get_our_node_id());
|
||||
|
||||
nodes[0].node.handle_shutdown(nodes[1].node.get_our_node_id(), &shutdown);
|
||||
let msg_events = nodes[0].node.get_and_clear_pending_msg_events();
|
||||
match msg_events[0] {
|
||||
MessageSendEvent::HandleError {
|
||||
action: ErrorAction::DisconnectPeerWithWarning { ref msg, .. },
|
||||
..
|
||||
} => {
|
||||
assert_eq!(msg.data, "Got shutdown request while quiescent".to_owned());
|
||||
},
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_allow_shutdown_while_awaiting_quiescence() {
|
||||
allow_shutdown_while_awaiting_quiescence(false);
|
||||
allow_shutdown_while_awaiting_quiescence(true);
|
||||
}
|
||||
|
||||
fn allow_shutdown_while_awaiting_quiescence(local_shutdown: bool) {
|
||||
// Test that a shutdown sent/received while we're still awaiting quiescence (stfu has not been
|
||||
// sent yet) is honored and the channel is closed cooperatively.
|
||||
let chanmon_cfgs = create_chanmon_cfgs(2);
|
||||
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
|
||||
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
|
||||
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
|
||||
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
|
||||
|
||||
let local_node = &nodes[0];
|
||||
let remote_node = &nodes[1];
|
||||
let local_node_id = local_node.node.get_our_node_id();
|
||||
let remote_node_id = remote_node.node.get_our_node_id();
|
||||
|
||||
let payment_amount = 1_000_000;
|
||||
let (route, payment_hash, _, payment_secret) =
|
||||
get_route_and_payment_hash!(local_node, remote_node, payment_amount);
|
||||
let onion = RecipientOnionFields::secret_only(payment_secret);
|
||||
let payment_id = PaymentId(payment_hash.0);
|
||||
local_node.node.send_payment_with_route(route, payment_hash, onion, payment_id).unwrap();
|
||||
check_added_monitors!(local_node, 1);
|
||||
|
||||
// Attempt to send an HTLC, but don't fully commit it yet.
|
||||
let update_add = get_htlc_update_msgs!(local_node, remote_node_id);
|
||||
remote_node.node.handle_update_add_htlc(local_node_id, &update_add.update_add_htlcs[0]);
|
||||
remote_node.node.handle_commitment_signed(local_node_id, &update_add.commitment_signed);
|
||||
let (revoke_and_ack, commit_sig) = get_revoke_commit_msgs!(remote_node, local_node_id);
|
||||
local_node.node.handle_revoke_and_ack(remote_node_id, &revoke_and_ack);
|
||||
check_added_monitors(local_node, 1);
|
||||
|
||||
// Request the local node to propose quiescence, and immediately try to close the channel. Since
|
||||
// we haven't sent `stfu` yet as the state machine is pending, we should forget about our
|
||||
// quiescence attempt.
|
||||
local_node.node.maybe_propose_quiescence(&remote_node_id, &chan_id).unwrap();
|
||||
assert!(local_node.node.get_and_clear_pending_msg_events().is_empty());
|
||||
|
||||
let (closer_node, closee_node) =
|
||||
if local_shutdown { (local_node, remote_node) } else { (remote_node, local_node) };
|
||||
let closer_node_id = closer_node.node.get_our_node_id();
|
||||
let closee_node_id = closee_node.node.get_our_node_id();
|
||||
|
||||
closer_node.node.close_channel(&chan_id, &closee_node_id).unwrap();
|
||||
check_added_monitors(&remote_node, 1);
|
||||
let shutdown_initiator =
|
||||
get_event_msg!(closer_node, MessageSendEvent::SendShutdown, closee_node_id);
|
||||
closee_node.node.handle_shutdown(closer_node_id, &shutdown_initiator);
|
||||
let shutdown_responder =
|
||||
get_event_msg!(closee_node, MessageSendEvent::SendShutdown, closer_node_id);
|
||||
closer_node.node.handle_shutdown(closee_node_id, &shutdown_responder);
|
||||
|
||||
// Continue exchanging messages until the HTLC is irrevocably committed and eventually failed
|
||||
// back as we are shutting down.
|
||||
local_node.node.handle_commitment_signed(remote_node_id, &commit_sig);
|
||||
check_added_monitors(local_node, 1);
|
||||
|
||||
let last_revoke_and_ack =
|
||||
get_event_msg!(local_node, MessageSendEvent::SendRevokeAndACK, remote_node_id);
|
||||
remote_node.node.handle_revoke_and_ack(local_node_id, &last_revoke_and_ack);
|
||||
check_added_monitors(remote_node, 1);
|
||||
expect_pending_htlcs_forwardable!(remote_node);
|
||||
expect_htlc_handling_failed_destinations!(
|
||||
remote_node.node.get_and_clear_pending_events(),
|
||||
&[HTLCDestination::FailedPayment { payment_hash }]
|
||||
);
|
||||
check_added_monitors(remote_node, 1);
|
||||
|
||||
let update_fail = get_htlc_update_msgs!(remote_node, local_node_id);
|
||||
local_node.node.handle_update_fail_htlc(remote_node_id, &update_fail.update_fail_htlcs[0]);
|
||||
local_node.node.handle_commitment_signed(remote_node_id, &update_fail.commitment_signed);
|
||||
|
||||
let (revoke_and_ack, commit_sig) = get_revoke_commit_msgs!(local_node, remote_node_id);
|
||||
remote_node.node.handle_revoke_and_ack(local_node_id, &revoke_and_ack);
|
||||
check_added_monitors(remote_node, 1);
|
||||
remote_node.node.handle_commitment_signed(local_node_id, &commit_sig);
|
||||
check_added_monitors(remote_node, 1);
|
||||
|
||||
let last_revoke_and_ack =
|
||||
get_event_msg!(remote_node, MessageSendEvent::SendRevokeAndACK, local_node_id);
|
||||
local_node.node.handle_revoke_and_ack(remote_node_id, &last_revoke_and_ack);
|
||||
|
||||
expect_payment_failed_conditions(
|
||||
local_node,
|
||||
payment_hash,
|
||||
true,
|
||||
PaymentFailedConditions::new(),
|
||||
);
|
||||
|
||||
// Now that the state machine is no longer pending, and `closing_signed` is ready to be sent,
|
||||
// make sure we're still not waiting for the quiescence handshake to complete.
|
||||
local_node.node.exit_quiescence(&remote_node_id, &chan_id).unwrap();
|
||||
|
||||
let _ = get_event_msg!(local_node, MessageSendEvent::SendClosingSigned, remote_node_id);
|
||||
check_added_monitors(local_node, 2); // One for the last revoke_and_ack, another for closing_signed
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quiescence_tracks_monitor_update_in_progress_and_waits_for_async_signer() {
|
||||
// Test that quiescence:
|
||||
// a) considers an async signer when determining whether a pending channel update exists
|
||||
// b) tracks in-progress monitor updates until no longer quiescent
|
||||
let chanmon_cfgs = create_chanmon_cfgs(2);
|
||||
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
|
||||
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
|
||||
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
|
||||
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
|
||||
|
||||
let node_id_0 = nodes[0].node.get_our_node_id();
|
||||
let node_id_1 = nodes[1].node.get_our_node_id();
|
||||
|
||||
let payment_amount = 1_000_000;
|
||||
let (preimage, payment_hash, ..) = route_payment(&nodes[0], &[&nodes[1]], payment_amount);
|
||||
nodes[1].node.claim_funds(preimage);
|
||||
check_added_monitors(&nodes[1], 1);
|
||||
|
||||
let update = get_htlc_update_msgs!(&nodes[1], node_id_0);
|
||||
nodes[0].node.handle_update_fulfill_htlc(node_id_1, &update.update_fulfill_htlcs[0]);
|
||||
nodes[0].node.handle_commitment_signed(node_id_1, &update.commitment_signed);
|
||||
check_added_monitors(&nodes[0], 1);
|
||||
|
||||
// While settling back the payment, propose quiescence from nodes[1]. We won't see its `stfu` go
|
||||
// out yet as the `update_fulfill` is still pending on both sides.
|
||||
nodes[1].node.maybe_propose_quiescence(&node_id_0, &chan_id).unwrap();
|
||||
|
||||
// Disable releasing commitment secrets on nodes[1], to hold back their `stfu` until the
|
||||
// `revoke_and_ack` goes out, and drive the state machine forward.
|
||||
nodes[1].disable_channel_signer_op(&node_id_0, &chan_id, SignerOp::ReleaseCommitmentSecret);
|
||||
|
||||
let (revoke_and_ack, commit_sig) = get_revoke_commit_msgs!(&nodes[0], node_id_1);
|
||||
nodes[1].node.handle_revoke_and_ack(node_id_0, &revoke_and_ack);
|
||||
check_added_monitors(&nodes[1], 1);
|
||||
nodes[1].node.handle_commitment_signed(node_id_0, &commit_sig);
|
||||
check_added_monitors(&nodes[1], 1);
|
||||
|
||||
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
|
||||
|
||||
// Resume the signer. We should now expect to see both messages.
|
||||
nodes[1].enable_channel_signer_op(&node_id_0, &chan_id, SignerOp::ReleaseCommitmentSecret);
|
||||
nodes[1].node.signer_unblocked(Some((node_id_0, chan_id)));
|
||||
|
||||
expect_payment_claimed!(&nodes[1], payment_hash, payment_amount);
|
||||
|
||||
macro_rules! find_msg {
|
||||
($events: expr, $msg: ident) => {{
|
||||
$events
|
||||
.iter()
|
||||
.find_map(|event| {
|
||||
if let MessageSendEvent::$msg { ref msg, .. } = event {
|
||||
Some(msg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
}};
|
||||
}
|
||||
let msg_events = nodes[1].node.get_and_clear_pending_msg_events();
|
||||
assert_eq!(msg_events.len(), 2);
|
||||
let revoke_and_ack = find_msg!(msg_events, SendRevokeAndACK);
|
||||
let stfu = find_msg!(msg_events, SendStfu);
|
||||
|
||||
// While handling the last `revoke_and_ack` on nodes[0], we'll hold the monitor update and
|
||||
// become quiescent.
|
||||
chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress);
|
||||
nodes[0].node.handle_revoke_and_ack(node_id_1, &revoke_and_ack);
|
||||
|
||||
nodes[0].node.handle_stfu(node_id_1, &stfu);
|
||||
let stfu = get_event_msg!(&nodes[0], MessageSendEvent::SendStfu, node_id_1);
|
||||
nodes[1].node.handle_stfu(node_id_0, &stfu);
|
||||
|
||||
nodes[0].node.exit_quiescence(&node_id_1, &chan_id).unwrap();
|
||||
nodes[1].node.exit_quiescence(&node_id_0, &chan_id).unwrap();
|
||||
|
||||
// After exiting quiescence, we should be able to resume payments from nodes[0], but the monitor
|
||||
// update has yet to complete. Attempting to send a payment now will be delayed until the
|
||||
// monitor update completes.
|
||||
{
|
||||
let (route, payment_hash, _, payment_secret) =
|
||||
get_route_and_payment_hash!(&nodes[0], &nodes[1], payment_amount);
|
||||
let onion = RecipientOnionFields::secret_only(payment_secret);
|
||||
let payment_id = PaymentId(payment_hash.0);
|
||||
nodes[0].node.send_payment_with_route(route, payment_hash, onion, payment_id).unwrap();
|
||||
}
|
||||
check_added_monitors(&nodes[0], 0);
|
||||
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
|
||||
|
||||
// We have two updates pending:
|
||||
{
|
||||
let chain_monitor = &nodes[0].chain_monitor;
|
||||
let (_, latest_update) =
|
||||
chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_id).unwrap().clone();
|
||||
let chain_monitor = &nodes[0].chain_monitor.chain_monitor;
|
||||
// One for the latest commitment transaction update from the last `revoke_and_ack`
|
||||
chain_monitor.channel_monitor_updated(chan_id, latest_update - 1).unwrap();
|
||||
expect_payment_sent(&nodes[0], preimage, None, true, true);
|
||||
// One for the commitment secret update from the last `revoke_and_ack`
|
||||
chain_monitor.channel_monitor_updated(chan_id, latest_update).unwrap();
|
||||
}
|
||||
|
||||
// With the pending monitor updates complete, we'll see a new monitor update go out when freeing
|
||||
// the holding cells to send out the new HTLC.
|
||||
nodes[0].chain_monitor.complete_sole_pending_chan_update(&chan_id);
|
||||
let _ = get_htlc_update_msgs!(&nodes[0], node_id_1);
|
||||
check_added_monitors(&nodes[0], 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quiescence_updates_go_to_holding_cell() {
|
||||
quiescence_updates_go_to_holding_cell(false);
|
||||
quiescence_updates_go_to_holding_cell(true);
|
||||
}
|
||||
|
||||
fn quiescence_updates_go_to_holding_cell(fail_htlc: bool) {
|
||||
// Test that any updates made to a channel while quiescent go to the holding cell.
|
||||
let chanmon_cfgs = create_chanmon_cfgs(2);
|
||||
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
|
||||
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
|
||||
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
|
||||
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
|
||||
|
||||
let node_id_0 = nodes[0].node.get_our_node_id();
|
||||
let node_id_1 = nodes[1].node.get_our_node_id();
|
||||
|
||||
// Send enough to be able to pay from both directions.
|
||||
let payment_amount = 1_000_000;
|
||||
send_payment(&nodes[0], &[&nodes[1]], payment_amount * 4);
|
||||
|
||||
// Propose quiescence from nodes[1], and immediately try to send a payment. Since its `stfu` has
|
||||
// already gone out first, the outbound HTLC will go into the holding cell.
|
||||
nodes[1].node.maybe_propose_quiescence(&node_id_0, &chan_id).unwrap();
|
||||
let stfu = get_event_msg!(&nodes[1], MessageSendEvent::SendStfu, node_id_0);
|
||||
|
||||
let (route1, payment_hash1, payment_preimage1, payment_secret1) =
|
||||
get_route_and_payment_hash!(&nodes[1], &nodes[0], payment_amount);
|
||||
let onion1 = RecipientOnionFields::secret_only(payment_secret1);
|
||||
let payment_id1 = PaymentId(payment_hash1.0);
|
||||
nodes[1].node.send_payment_with_route(route1, payment_hash1, onion1, payment_id1).unwrap();
|
||||
check_added_monitors!(&nodes[1], 0);
|
||||
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
|
||||
|
||||
// Send a payment in the opposite direction. Since nodes[0] hasn't sent its own `stfu` yet, it's
|
||||
// allowed to make updates.
|
||||
let (route2, payment_hash2, payment_preimage2, payment_secret2) =
|
||||
get_route_and_payment_hash!(&nodes[0], &nodes[1], payment_amount);
|
||||
let onion2 = RecipientOnionFields::secret_only(payment_secret2);
|
||||
let payment_id2 = PaymentId(payment_hash2.0);
|
||||
nodes[0].node.send_payment_with_route(route2, payment_hash2, onion2, payment_id2).unwrap();
|
||||
check_added_monitors!(&nodes[0], 1);
|
||||
|
||||
let update_add = get_htlc_update_msgs!(&nodes[0], node_id_1);
|
||||
nodes[1].node.handle_update_add_htlc(node_id_0, &update_add.update_add_htlcs[0]);
|
||||
commitment_signed_dance!(&nodes[1], &nodes[0], update_add.commitment_signed, false);
|
||||
expect_pending_htlcs_forwardable!(&nodes[1]);
|
||||
expect_payment_claimable!(nodes[1], payment_hash2, payment_secret2, payment_amount);
|
||||
|
||||
// Have nodes[1] attempt to fail/claim nodes[0]'s payment. Since nodes[1] already sent out
|
||||
// `stfu`, the `update_fail/fulfill` will go into the holding cell.
|
||||
if fail_htlc {
|
||||
nodes[1].node.fail_htlc_backwards(&payment_hash2);
|
||||
let failed_payment = HTLCDestination::FailedPayment { payment_hash: payment_hash2 };
|
||||
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(&nodes[1], vec![failed_payment]);
|
||||
} else {
|
||||
nodes[1].node.claim_funds(payment_preimage2);
|
||||
check_added_monitors(&nodes[1], 1);
|
||||
}
|
||||
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
|
||||
|
||||
// Finish the quiescence handshake.
|
||||
nodes[0].node.handle_stfu(node_id_1, &stfu);
|
||||
let stfu = get_event_msg!(&nodes[0], MessageSendEvent::SendStfu, node_id_1);
|
||||
nodes[1].node.handle_stfu(node_id_0, &stfu);
|
||||
|
||||
nodes[0].node.exit_quiescence(&node_id_1, &chan_id).unwrap();
|
||||
nodes[1].node.exit_quiescence(&node_id_0, &chan_id).unwrap();
|
||||
|
||||
// Now that quiescence is over, nodes are allowed to make updates again. nodes[1] will have its
|
||||
// outbound HTLC finally go out, along with the fail/claim of nodes[0]'s payment.
|
||||
let update = get_htlc_update_msgs!(&nodes[1], node_id_0);
|
||||
check_added_monitors(&nodes[1], 1);
|
||||
nodes[0].node.handle_update_add_htlc(node_id_1, &update.update_add_htlcs[0]);
|
||||
if fail_htlc {
|
||||
nodes[0].node.handle_update_fail_htlc(node_id_1, &update.update_fail_htlcs[0]);
|
||||
} else {
|
||||
nodes[0].node.handle_update_fulfill_htlc(node_id_1, &update.update_fulfill_htlcs[0]);
|
||||
}
|
||||
commitment_signed_dance!(&nodes[0], &nodes[1], update.commitment_signed, false);
|
||||
|
||||
if !fail_htlc {
|
||||
expect_payment_claimed!(nodes[1], payment_hash2, payment_amount);
|
||||
}
|
||||
|
||||
// The payment from nodes[0] should now be seen as failed/successful.
|
||||
let events = nodes[0].node.get_and_clear_pending_events();
|
||||
assert_eq!(events.len(), 3);
|
||||
assert!(events.iter().find(|e| matches!(e, Event::PendingHTLCsForwardable { .. })).is_some());
|
||||
if fail_htlc {
|
||||
assert!(events.iter().find(|e| matches!(e, Event::PaymentFailed { .. })).is_some());
|
||||
assert!(events.iter().find(|e| matches!(e, Event::PaymentPathFailed { .. })).is_some());
|
||||
} else {
|
||||
assert!(events.iter().find(|e| matches!(e, Event::PaymentSent { .. })).is_some());
|
||||
assert!(events.iter().find(|e| matches!(e, Event::PaymentPathSuccessful { .. })).is_some());
|
||||
check_added_monitors(&nodes[0], 1);
|
||||
}
|
||||
nodes[0].node.process_pending_htlc_forwards();
|
||||
expect_payment_claimable!(nodes[0], payment_hash1, payment_secret1, payment_amount);
|
||||
|
||||
// Have nodes[0] fail/claim nodes[1]'s payment.
|
||||
if fail_htlc {
|
||||
nodes[0].node.fail_htlc_backwards(&payment_hash1);
|
||||
let failed_payment = HTLCDestination::FailedPayment { payment_hash: payment_hash1 };
|
||||
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(&nodes[0], vec![failed_payment]);
|
||||
} else {
|
||||
nodes[0].node.claim_funds(payment_preimage1);
|
||||
}
|
||||
check_added_monitors(&nodes[0], 1);
|
||||
|
||||
let update = get_htlc_update_msgs!(&nodes[0], node_id_1);
|
||||
if fail_htlc {
|
||||
nodes[1].node.handle_update_fail_htlc(node_id_0, &update.update_fail_htlcs[0]);
|
||||
} else {
|
||||
nodes[1].node.handle_update_fulfill_htlc(node_id_0, &update.update_fulfill_htlcs[0]);
|
||||
}
|
||||
commitment_signed_dance!(&nodes[1], &nodes[0], update.commitment_signed, false);
|
||||
|
||||
// The payment from nodes[1] should now be seen as failed/successful.
|
||||
if fail_htlc {
|
||||
let conditions = PaymentFailedConditions::new();
|
||||
expect_payment_failed_conditions(&nodes[1], payment_hash1, true, conditions);
|
||||
} else {
|
||||
expect_payment_claimed!(nodes[0], payment_hash1, payment_amount);
|
||||
expect_payment_sent(&nodes[1], payment_preimage1, None, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quiescence_timeout() {
|
||||
// Test that we'll disconnect if we remain quiescent for `DISCONNECT_PEER_AWAITING_RESPONSE_TICKS`.
|
||||
let chanmon_cfgs = create_chanmon_cfgs(2);
|
||||
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
|
||||
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
|
||||
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
|
||||
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
|
||||
|
||||
let node_id_0 = nodes[0].node.get_our_node_id();
|
||||
let node_id_1 = nodes[1].node.get_our_node_id();
|
||||
|
||||
nodes[0].node.maybe_propose_quiescence(&nodes[1].node.get_our_node_id(), &chan_id).unwrap();
|
||||
|
||||
let stfu_initiator = get_event_msg!(nodes[0], MessageSendEvent::SendStfu, node_id_1);
|
||||
nodes[1].node.handle_stfu(node_id_0, &stfu_initiator);
|
||||
|
||||
let stfu_responder = get_event_msg!(nodes[1], MessageSendEvent::SendStfu, node_id_0);
|
||||
nodes[0].node.handle_stfu(node_id_1, &stfu_responder);
|
||||
|
||||
assert!(stfu_initiator.initiator && !stfu_responder.initiator);
|
||||
|
||||
for _ in 0..DISCONNECT_PEER_AWAITING_RESPONSE_TICKS {
|
||||
nodes[0].node.timer_tick_occurred();
|
||||
nodes[1].node.timer_tick_occurred();
|
||||
}
|
||||
|
||||
let f = |event| {
|
||||
if let MessageSendEvent::HandleError { action, .. } = event {
|
||||
if let msgs::ErrorAction::DisconnectPeerWithWarning { .. } = action {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
assert!(nodes[0].node.get_and_clear_pending_msg_events().into_iter().find_map(f).is_some());
|
||||
assert!(nodes[1].node.get_and_clear_pending_msg_events().into_iter().find_map(f).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quiescence_timeout_while_waiting_for_counterparty_stfu() {
|
||||
// Test that we'll disconnect if the counterparty does not send their stfu within a reasonable
|
||||
// time if we've already sent ours.
|
||||
let chanmon_cfgs = create_chanmon_cfgs(2);
|
||||
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
|
||||
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
|
||||
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
|
||||
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
|
||||
|
||||
let node_id_0 = nodes[0].node.get_our_node_id();
|
||||
|
||||
nodes[1].node.maybe_propose_quiescence(&node_id_0, &chan_id).unwrap();
|
||||
let _ = get_event_msg!(nodes[1], MessageSendEvent::SendStfu, node_id_0);
|
||||
|
||||
// Route a payment in between to ensure expecting to receive `revoke_and_ack` doesn't override
|
||||
// the expectation of receiving `stfu` as well.
|
||||
let _ = route_payment(&nodes[0], &[&nodes[1]], 1_000_000);
|
||||
|
||||
for _ in 0..DISCONNECT_PEER_AWAITING_RESPONSE_TICKS {
|
||||
nodes[0].node.timer_tick_occurred();
|
||||
nodes[1].node.timer_tick_occurred();
|
||||
}
|
||||
|
||||
// nodes[0] hasn't received stfu from nodes[1], so it's not enforcing any timeouts.
|
||||
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
|
||||
|
||||
// nodes[1] didn't receive nodes[0]'s stfu within the timeout so it'll disconnect.
|
||||
let f = |&ref event| {
|
||||
if let MessageSendEvent::HandleError { action, .. } = event {
|
||||
if let msgs::ErrorAction::DisconnectPeerWithWarning { .. } = action {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
assert!(nodes[1].node.get_and_clear_pending_msg_events().iter().find_map(f).is_some());
|
||||
}
|
Loading…
Add table
Reference in a new issue