mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-25 07:17:40 +01:00
Merge pull request #2721 from TheBlueMatt/2023-11-log-forward-peer
Handle missing case in reestablish local commitment number checks
This commit is contained in:
commit
c2bbfffb1e
2 changed files with 169 additions and 78 deletions
|
@ -4176,6 +4176,7 @@ impl<SP: Deref> Channel<SP> where
|
|||
return Err(ChannelError::Close("Peer sent an invalid channel_reestablish to force close in a non-standard way".to_owned()));
|
||||
}
|
||||
|
||||
let our_commitment_transaction = INITIAL_COMMITMENT_NUMBER - self.context.cur_holder_commitment_transaction_number - 1;
|
||||
if msg.next_remote_commitment_number > 0 {
|
||||
let expected_point = self.context.holder_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - msg.next_remote_commitment_number + 1, &self.context.secp_ctx);
|
||||
let given_secret = SecretKey::from_slice(&msg.your_last_per_commitment_secret)
|
||||
|
@ -4183,7 +4184,7 @@ impl<SP: Deref> Channel<SP> where
|
|||
if expected_point != PublicKey::from_secret_key(&self.context.secp_ctx, &given_secret) {
|
||||
return Err(ChannelError::Close("Peer sent a garbage channel_reestablish with secret key not matching the commitment height provided".to_owned()));
|
||||
}
|
||||
if msg.next_remote_commitment_number > INITIAL_COMMITMENT_NUMBER - self.context.cur_holder_commitment_transaction_number {
|
||||
if msg.next_remote_commitment_number > our_commitment_transaction {
|
||||
macro_rules! log_and_panic {
|
||||
($err_msg: expr) => {
|
||||
log_error!(logger, $err_msg, &self.context.channel_id, log_pubkey!(self.context.counterparty_node_id));
|
||||
|
@ -4203,11 +4204,12 @@ impl<SP: Deref> Channel<SP> where
|
|||
|
||||
// Before we change the state of the channel, we check if the peer is sending a very old
|
||||
// commitment transaction number, if yes we send a warning message.
|
||||
let our_commitment_transaction = INITIAL_COMMITMENT_NUMBER - self.context.cur_holder_commitment_transaction_number - 1;
|
||||
if msg.next_remote_commitment_number + 1 < our_commitment_transaction {
|
||||
return Err(
|
||||
ChannelError::Warn(format!("Peer attempted to reestablish channel with a very old local commitment transaction: {} (received) vs {} (expected)", msg.next_remote_commitment_number, our_commitment_transaction))
|
||||
);
|
||||
if msg.next_remote_commitment_number + 1 < our_commitment_transaction {
|
||||
return Err(ChannelError::Warn(format!(
|
||||
"Peer attempted to reestablish channel with a very old local commitment transaction: {} (received) vs {} (expected)",
|
||||
msg.next_remote_commitment_number,
|
||||
our_commitment_transaction
|
||||
)));
|
||||
}
|
||||
|
||||
// Go ahead and unmark PeerDisconnected as various calls we may make check for it (and all
|
||||
|
@ -4249,11 +4251,11 @@ impl<SP: Deref> Channel<SP> where
|
|||
});
|
||||
}
|
||||
|
||||
let required_revoke = if msg.next_remote_commitment_number + 1 == INITIAL_COMMITMENT_NUMBER - self.context.cur_holder_commitment_transaction_number {
|
||||
let required_revoke = if msg.next_remote_commitment_number == our_commitment_transaction {
|
||||
// Remote isn't waiting on any RevokeAndACK from us!
|
||||
// Note that if we need to repeat our ChannelReady we'll do that in the next if block.
|
||||
None
|
||||
} else if msg.next_remote_commitment_number + 1 == (INITIAL_COMMITMENT_NUMBER - 1) - self.context.cur_holder_commitment_transaction_number {
|
||||
} else if msg.next_remote_commitment_number + 1 == our_commitment_transaction {
|
||||
if self.context.channel_state & (ChannelState::MonitorUpdateInProgress as u32) != 0 {
|
||||
self.context.monitor_pending_revoke_and_ack = true;
|
||||
None
|
||||
|
@ -4261,7 +4263,12 @@ impl<SP: Deref> Channel<SP> where
|
|||
Some(self.get_last_revoke_and_ack())
|
||||
}
|
||||
} else {
|
||||
return Err(ChannelError::Close("Peer attempted to reestablish channel with a very old local commitment transaction".to_owned()));
|
||||
debug_assert!(false, "All values should have been handled in the four cases above");
|
||||
return Err(ChannelError::Close(format!(
|
||||
"Peer attempted to reestablish channel expecting a future local commitment transaction: {} (received) vs {} (expected)",
|
||||
msg.next_remote_commitment_number,
|
||||
our_commitment_transaction
|
||||
)));
|
||||
};
|
||||
|
||||
// We increment cur_counterparty_commitment_transaction_number only upon receipt of
|
||||
|
@ -4319,8 +4326,18 @@ impl<SP: Deref> Channel<SP> where
|
|||
order: self.context.resend_order.clone(),
|
||||
})
|
||||
}
|
||||
} else if msg.next_local_commitment_number < next_counterparty_commitment_number {
|
||||
Err(ChannelError::Close(format!(
|
||||
"Peer attempted to reestablish channel with a very old remote commitment transaction: {} (received) vs {} (expected)",
|
||||
msg.next_local_commitment_number,
|
||||
next_counterparty_commitment_number,
|
||||
)))
|
||||
} else {
|
||||
Err(ChannelError::Close("Peer attempted to reestablish channel with a very old remote commitment transaction".to_owned()))
|
||||
Err(ChannelError::Close(format!(
|
||||
"Peer attempted to reestablish channel with a future remote commitment transaction: {} (received) vs {} (expected)",
|
||||
msg.next_local_commitment_number,
|
||||
next_counterparty_commitment_number,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,9 +15,10 @@ use crate::chain::channelmonitor::{CLOSED_CHANNEL_UPDATE_ID, ChannelMonitor};
|
|||
use crate::sign::EntropySource;
|
||||
use crate::chain::transaction::OutPoint;
|
||||
use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
|
||||
use crate::ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, PaymentId, RecipientOnionFields};
|
||||
use crate::ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, PaymentId, Retry, RecipientOnionFields};
|
||||
use crate::ln::msgs;
|
||||
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
|
||||
use crate::routing::router::{RouteParameters, PaymentParameters};
|
||||
use crate::util::test_channel_signer::TestChannelSigner;
|
||||
use crate::util::test_utils;
|
||||
use crate::util::errors::APIError;
|
||||
|
@ -493,7 +494,8 @@ fn test_manager_serialize_deserialize_inconsistent_monitor() {
|
|||
assert!(found_err);
|
||||
}
|
||||
|
||||
fn do_test_data_loss_protect(reconnect_panicing: bool) {
|
||||
#[cfg(feature = "std")]
|
||||
fn do_test_data_loss_protect(reconnect_panicing: bool, substantially_old: bool, not_stale: bool) {
|
||||
// When we get a data_loss_protect proving we're behind, we immediately panic as the
|
||||
// chain::Watch API requirements have been violated (e.g. the user restored from a backup). The
|
||||
// panic message informs the user they should force-close without broadcasting, which is tested
|
||||
|
@ -517,8 +519,38 @@ fn do_test_data_loss_protect(reconnect_panicing: bool) {
|
|||
let previous_node_state = nodes[0].node.encode();
|
||||
let previous_chain_monitor_state = get_monitor!(nodes[0], chan.2).encode();
|
||||
|
||||
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
|
||||
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
|
||||
assert!(!substantially_old || !not_stale, "substantially_old and not_stale doesn't make sense");
|
||||
if not_stale || !substantially_old {
|
||||
// Previously, we'd only hit the data_loss_protect assertion if we had a state which
|
||||
// revoked at least two revocations ago, not the latest revocation. Here, we use
|
||||
// `not_stale` to test the boundary condition.
|
||||
let pay_params = PaymentParameters::for_keysend(nodes[1].node.get_our_node_id(), 100, false);
|
||||
let route_params = RouteParameters::from_payment_params_and_value(pay_params, 40000);
|
||||
nodes[0].node.send_spontaneous_payment_with_retry(None, RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]), route_params, Retry::Attempts(0));
|
||||
check_added_monitors(&nodes[0], 1);
|
||||
let update_add_commit = SendEvent::from_node(&nodes[0]);
|
||||
|
||||
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &update_add_commit.msgs[0]);
|
||||
nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &update_add_commit.commitment_msg);
|
||||
check_added_monitors(&nodes[1], 1);
|
||||
let (raa, cs) = get_revoke_commit_msgs(&nodes[1], &nodes[0].node.get_our_node_id());
|
||||
|
||||
nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &raa);
|
||||
check_added_monitors(&nodes[0], 1);
|
||||
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
|
||||
if !not_stale {
|
||||
nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &cs);
|
||||
check_added_monitors(&nodes[0], 1);
|
||||
// A now revokes their original state, at which point reconnect should panic
|
||||
let raa = get_event_msg!(nodes[0], MessageSendEvent::SendRevokeAndACK, nodes[1].node.get_our_node_id());
|
||||
nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &raa);
|
||||
check_added_monitors(&nodes[1], 1);
|
||||
expect_pending_htlcs_forwardable_ignore!(nodes[1]);
|
||||
}
|
||||
} else {
|
||||
send_payment(&nodes[0], &[&nodes[1]], 8000000);
|
||||
send_payment(&nodes[0], &[&nodes[1]], 8000000);
|
||||
}
|
||||
|
||||
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
|
||||
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
|
||||
|
@ -535,89 +567,131 @@ fn do_test_data_loss_protect(reconnect_panicing: bool) {
|
|||
|
||||
let reestablish_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
|
||||
|
||||
// Check we close channel detecting A is fallen-behind
|
||||
// Check that we sent the warning message when we detected that A has fallen behind,
|
||||
// and give the possibility for A to recover from the warning.
|
||||
// If A has fallen behind substantially, B should send it a message letting it know
|
||||
// that.
|
||||
nodes[1].node.handle_channel_reestablish(&nodes[0].node.get_our_node_id(), &reestablish_1[0]);
|
||||
let warn_msg = "Peer attempted to reestablish channel with a very old local commitment transaction".to_owned();
|
||||
assert!(check_warn_msg!(nodes[1], nodes[0].node.get_our_node_id(), chan.2).contains(&warn_msg));
|
||||
let reestablish_msg;
|
||||
if substantially_old {
|
||||
let warn_msg = "Peer attempted to reestablish channel with a very old local commitment transaction: 0 (received) vs 4 (expected)".to_owned();
|
||||
|
||||
let warn_reestablish = nodes[1].node.get_and_clear_pending_msg_events();
|
||||
assert_eq!(warn_reestablish.len(), 2);
|
||||
match warn_reestablish[1] {
|
||||
MessageSendEvent::HandleError { action: ErrorAction::SendWarningMessage { ref msg, .. }, .. } => {
|
||||
assert_eq!(msg.data, warn_msg);
|
||||
},
|
||||
_ => panic!("Unexpected events: {:?}", warn_reestablish),
|
||||
}
|
||||
reestablish_msg = match &warn_reestablish[0] {
|
||||
MessageSendEvent::SendChannelReestablish { msg, .. } => msg.clone(),
|
||||
_ => panic!("Unexpected events: {:?}", warn_reestablish),
|
||||
};
|
||||
} else {
|
||||
let msgs = nodes[1].node.get_and_clear_pending_msg_events();
|
||||
assert!(msgs.len() >= 4);
|
||||
match msgs.last() {
|
||||
Some(MessageSendEvent::SendChannelUpdate { .. }) => {},
|
||||
_ => panic!("Unexpected events: {:?}", msgs),
|
||||
}
|
||||
assert!(msgs.iter().any(|msg| matches!(msg, MessageSendEvent::SendRevokeAndACK { .. })));
|
||||
assert!(msgs.iter().any(|msg| matches!(msg, MessageSendEvent::UpdateHTLCs { .. })));
|
||||
reestablish_msg = match &msgs[0] {
|
||||
MessageSendEvent::SendChannelReestablish { msg, .. } => msg.clone(),
|
||||
_ => panic!("Unexpected events: {:?}", msgs),
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
let mut node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clone();
|
||||
// The node B should not broadcast the transaction to force close the channel!
|
||||
let mut node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
|
||||
// The node B should never force-close the channel.
|
||||
assert!(node_txn.is_empty());
|
||||
}
|
||||
|
||||
let reestablish_0 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
|
||||
// Check A panics upon seeing proof it has fallen behind.
|
||||
nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &reestablish_0[0]);
|
||||
return; // By this point we should have panic'ed!
|
||||
}
|
||||
let reconnect_res = std::panic::catch_unwind(|| {
|
||||
nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &reestablish_msg);
|
||||
});
|
||||
if not_stale {
|
||||
assert!(reconnect_res.is_ok());
|
||||
// At this point A gets confused because B expects a commitment state newer than A
|
||||
// has sent, but not a newer revocation secret, so A just (correctly) closes.
|
||||
check_closed_broadcast(&nodes[0], 1, true);
|
||||
check_added_monitors(&nodes[0], 1);
|
||||
check_closed_event!(nodes[0], 1, ClosureReason::ProcessingError {
|
||||
err: "Peer attempted to reestablish channel with a future remote commitment transaction: 2 (received) vs 1 (expected)".to_owned()
|
||||
}, [nodes[1].node.get_our_node_id()], 1000000);
|
||||
} else {
|
||||
assert!(reconnect_res.is_err());
|
||||
// Skip the `Drop` handler for `Node`s as some may be in an invalid (panicked) state.
|
||||
std::mem::forget(nodes);
|
||||
}
|
||||
} else {
|
||||
assert!(!not_stale, "We only care about the stale case when not testing panicking");
|
||||
|
||||
nodes[0].node.force_close_without_broadcasting_txn(&chan.2, &nodes[1].node.get_our_node_id()).unwrap();
|
||||
check_added_monitors!(nodes[0], 1);
|
||||
check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed, [nodes[1].node.get_our_node_id()], 1000000);
|
||||
{
|
||||
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
|
||||
assert_eq!(node_txn.len(), 0);
|
||||
}
|
||||
nodes[0].node.force_close_without_broadcasting_txn(&chan.2, &nodes[1].node.get_our_node_id()).unwrap();
|
||||
check_added_monitors!(nodes[0], 1);
|
||||
check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed, [nodes[1].node.get_our_node_id()], 1000000);
|
||||
{
|
||||
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
|
||||
assert_eq!(node_txn.len(), 0);
|
||||
}
|
||||
|
||||
for msg in nodes[0].node.get_and_clear_pending_msg_events() {
|
||||
if let MessageSendEvent::BroadcastChannelUpdate { .. } = msg {
|
||||
} else if let MessageSendEvent::HandleError { ref action, .. } = msg {
|
||||
for msg in nodes[0].node.get_and_clear_pending_msg_events() {
|
||||
if let MessageSendEvent::BroadcastChannelUpdate { .. } = msg {
|
||||
} else if let MessageSendEvent::HandleError { ref action, .. } = msg {
|
||||
match action {
|
||||
&ErrorAction::DisconnectPeer { ref msg } => {
|
||||
assert_eq!(msg.as_ref().unwrap().data, "Channel force-closed");
|
||||
},
|
||||
_ => panic!("Unexpected event!"),
|
||||
}
|
||||
} else {
|
||||
panic!("Unexpected event {:?}", msg)
|
||||
}
|
||||
}
|
||||
|
||||
// after the warning message sent by B, we should not able to
|
||||
// use the channel, or reconnect with success to the channel.
|
||||
assert!(nodes[0].node.list_usable_channels().is_empty());
|
||||
nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init {
|
||||
features: nodes[1].node.init_features(), networks: None, remote_network_address: None
|
||||
}, true).unwrap();
|
||||
nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init {
|
||||
features: nodes[0].node.init_features(), networks: None, remote_network_address: None
|
||||
}, false).unwrap();
|
||||
let retry_reestablish = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
|
||||
|
||||
nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &retry_reestablish[0]);
|
||||
let mut err_msgs_0 = Vec::with_capacity(1);
|
||||
if let MessageSendEvent::HandleError { ref action, .. } = nodes[0].node.get_and_clear_pending_msg_events()[1] {
|
||||
match action {
|
||||
&ErrorAction::DisconnectPeer { ref msg } => {
|
||||
assert_eq!(msg.as_ref().unwrap().data, "Channel force-closed");
|
||||
&ErrorAction::SendErrorMessage { ref msg } => {
|
||||
assert_eq!(msg.data, format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", &nodes[1].node.get_our_node_id()));
|
||||
err_msgs_0.push(msg.clone());
|
||||
},
|
||||
_ => panic!("Unexpected event!"),
|
||||
}
|
||||
} else {
|
||||
panic!("Unexpected event {:?}", msg)
|
||||
panic!("Unexpected event!");
|
||||
}
|
||||
assert_eq!(err_msgs_0.len(), 1);
|
||||
nodes[1].node.handle_error(&nodes[0].node.get_our_node_id(), &err_msgs_0[0]);
|
||||
assert!(nodes[1].node.list_usable_channels().is_empty());
|
||||
check_added_monitors!(nodes[1], 1);
|
||||
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", &nodes[1].node.get_our_node_id())) }
|
||||
, [nodes[0].node.get_our_node_id()], 1000000);
|
||||
check_closed_broadcast!(nodes[1], false);
|
||||
}
|
||||
|
||||
// after the warning message sent by B, we should not able to
|
||||
// use the channel, or reconnect with success to the channel.
|
||||
assert!(nodes[0].node.list_usable_channels().is_empty());
|
||||
nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init {
|
||||
features: nodes[1].node.init_features(), networks: None, remote_network_address: None
|
||||
}, true).unwrap();
|
||||
nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init {
|
||||
features: nodes[0].node.init_features(), networks: None, remote_network_address: None
|
||||
}, false).unwrap();
|
||||
let retry_reestablish = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
|
||||
|
||||
nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &retry_reestablish[0]);
|
||||
let mut err_msgs_0 = Vec::with_capacity(1);
|
||||
if let MessageSendEvent::HandleError { ref action, .. } = nodes[0].node.get_and_clear_pending_msg_events()[1] {
|
||||
match action {
|
||||
&ErrorAction::SendErrorMessage { ref msg } => {
|
||||
assert_eq!(msg.data, format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", &nodes[1].node.get_our_node_id()));
|
||||
err_msgs_0.push(msg.clone());
|
||||
},
|
||||
_ => panic!("Unexpected event!"),
|
||||
}
|
||||
} else {
|
||||
panic!("Unexpected event!");
|
||||
}
|
||||
assert_eq!(err_msgs_0.len(), 1);
|
||||
nodes[1].node.handle_error(&nodes[0].node.get_our_node_id(), &err_msgs_0[0]);
|
||||
assert!(nodes[1].node.list_usable_channels().is_empty());
|
||||
check_added_monitors!(nodes[1], 1);
|
||||
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", &nodes[1].node.get_our_node_id())) }
|
||||
, [nodes[0].node.get_our_node_id()], 1000000);
|
||||
check_closed_broadcast!(nodes[1], false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_data_loss_protect_showing_stale_state_panics() {
|
||||
do_test_data_loss_protect(true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_force_close_without_broadcast() {
|
||||
do_test_data_loss_protect(false);
|
||||
#[cfg(feature = "std")]
|
||||
fn test_data_loss_protect() {
|
||||
do_test_data_loss_protect(true, false, true);
|
||||
do_test_data_loss_protect(true, true, false);
|
||||
do_test_data_loss_protect(true, false, false);
|
||||
do_test_data_loss_protect(false, true, false);
|
||||
do_test_data_loss_protect(false, false, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Add table
Reference in a new issue