From: Matt Corallo Date: Sat, 8 Sep 2018 20:02:46 +0000 (-0400) Subject: Track peer-disconnection in Channel and handle channel_reestablish X-Git-Tag: v0.0.12~307^2~2 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;ds=inline;h=28c5f6f309ccf538ae9da0072fc66311f4e56684;hp=-c;p=rust-lightning Track peer-disconnection in Channel and handle channel_reestablish --- 28c5f6f309ccf538ae9da0072fc66311f4e56684 diff --git a/src/ln/channel.rs b/src/ln/channel.rs index 3c605859..d11447a8 100644 --- a/src/ln/channel.rs +++ b/src/ln/channel.rs @@ -1826,7 +1826,9 @@ impl Channel { /// HTLCs that we intended to add but haven't as we were waiting on a remote revoke. /// Returns the set of PendingHTLCStatuses from remote uncommitted HTLCs (which we're /// implicitly dropping) and the payment_hashes of HTLCs we tried to add but are dropping. - pub fn remove_uncommitted_htlcs(&mut self) -> Vec<(HTLCSource, [u8; 32])> { + /// No further message handling calls may be made until a channel_reestablish dance has + /// completed. + pub fn remove_uncommitted_htlcs_and_mark_paused(&mut self) -> Vec<(HTLCSource, [u8; 32])> { let mut outbound_drops = Vec::new(); assert_eq!(self.channel_state & ChannelState::ShutdownComplete as u32, 0); @@ -1835,12 +1837,14 @@ impl Channel { return outbound_drops; } + let mut inbound_drop_count = 0; self.pending_inbound_htlcs.retain(|htlc| { match htlc.state { InboundHTLCState::RemoteAnnounced => { // They sent us an update_add_htlc but we never got the commitment_signed. // We'll tell them what commitment_signed we're expecting next and they'll drop // this HTLC accordingly + inbound_drop_count += 1; false }, InboundHTLCState::AwaitingRemoteRevokeToAnnounce|InboundHTLCState::AwaitingAnnouncedRemoteRevoke => { @@ -1879,6 +1883,8 @@ impl Channel { &HTLCUpdateAwaitingACK::ClaimHTLC {..} | &HTLCUpdateAwaitingACK::FailHTLC {..} => true, } }); + self.channel_state |= ChannelState::PeerDisconnected as u32; + log_debug!(self, "Peer disconnection resulted in {} remote-announced HTLC drops and {} waiting-to-locally-announced HTLC drops on channel {}", outbound_drops.len(), inbound_drop_count, log_bytes!(self.channel_id())); outbound_drops } @@ -1895,6 +1901,83 @@ impl Channel { Ok(()) } + /// May panic if some calls other than message-handling calls (which will all Err immediately) + /// have been called between remove_uncommitted_htlcs_and_mark_paused and this call. + pub fn channel_reestablish(&mut self, msg: &msgs::ChannelReestablish) -> Result<(Option, Option, Option, Option), HandleError> { + if self.channel_state & (ChannelState::PeerDisconnected as u32) == 0 { + return Err(HandleError{err: "Peer sent a loose channel_reestablish not after reconnect", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer sent a loose channel_reestablish not after reconnect".to_string(), channel_id: msg.channel_id}})}); + } + + if msg.next_local_commitment_number == 0 || msg.next_local_commitment_number >= 0xffffffffffff || + msg.next_remote_commitment_number == 0 || msg.next_remote_commitment_number >= 0xffffffffffff { + return Err(HandleError{err: "Peer send garbage channel_reestablish", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer send garbage channel_reestablish".to_string(), channel_id: msg.channel_id}})}); + } + + // Go ahead and unmark PeerDisconnected as various calls we may make check for it (and all + // remaining cases either succeed or ErrorMessage-fail). + self.channel_state &= !(ChannelState::PeerDisconnected as u32); + + let mut required_revoke = None; + if msg.next_remote_commitment_number == 0xffffffffffff - self.cur_local_commitment_transaction_number { + } else if msg.next_remote_commitment_number == 0xfffffffffffe - self.cur_local_commitment_transaction_number { + let next_per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &self.build_local_commitment_secret(self.cur_local_commitment_transaction_number)); + let per_commitment_secret = chan_utils::build_commitment_secret(self.local_keys.commitment_seed, self.cur_local_commitment_transaction_number + 2); + required_revoke = Some(msgs::RevokeAndACK { + channel_id: self.channel_id, + per_commitment_secret, + next_per_commitment_point, + }); + } else { + return Err(HandleError{err: "Peer attempted to reestablish channel with a very old local commitment transaction", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer attempted to reestablish channel with a very old remote commitment transaction".to_string(), channel_id: msg.channel_id}})}); + } + + if msg.next_local_commitment_number == 0xffffffffffff - self.cur_remote_commitment_transaction_number { + if msg.next_remote_commitment_number == 0xffffffffffff - self.cur_local_commitment_transaction_number { + log_debug!(self, "Reconnected channel {} with no lost commitment txn", log_bytes!(self.channel_id())); + if msg.next_local_commitment_number == 1 && msg.next_remote_commitment_number == 1 { + let next_per_commitment_secret = self.build_local_commitment_secret(self.cur_local_commitment_transaction_number); + let next_per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &next_per_commitment_secret); + return Ok((Some(msgs::FundingLocked { + channel_id: self.channel_id(), + next_per_commitment_point: next_per_commitment_point, + }), None, None, None)); + } + } + + if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32)) == 0 { + // We're up-to-date and not waiting on a remote revoke (if we are our + // channel_reestablish should result in them sending a revoke_and_ack), but we may + // have received some updates while we were disconnected. Free the holding cell + // now! + match self.free_holding_cell_htlcs() { + Err(e) => { + if let &Some(msgs::ErrorAction::DisconnectPeer{msg: Some(_)}) = &e.action { + } else if let &Some(msgs::ErrorAction::SendErrorMessage{msg: _}) = &e.action { + } else { + panic!("Got non-channel-failing result from free_holding_cell_htlcs"); + } + return Err(e); + }, + Ok(Some((commitment_update, channel_monitor))) => return Ok((None, required_revoke, Some(commitment_update), Some(channel_monitor))), + Ok(None) => return Ok((None, required_revoke, None, None)), + } + } else { + return Ok((None, required_revoke, None, None)); + } + } else if msg.next_local_commitment_number == 0xfffffffffffe - self.cur_remote_commitment_transaction_number { + return Ok((None, required_revoke, + Some(msgs::CommitmentUpdate { + update_add_htlcs: Vec::new(), + update_fulfill_htlcs: Vec::new(), + update_fail_htlcs: Vec::new(), + update_fail_malformed_htlcs: Vec::new(), + commitment_signed: self.send_commitment_no_state_update().expect("It looks like we failed to re-generate a commitment_signed we had previously sent?").0, + }), None)); + } else { + return Err(HandleError{err: "Peer attempted to reestablish channel with a very old remote commitment transaction", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer attempted to reestablish channel with a very old remote commitment transaction".to_string(), channel_id: msg.channel_id}})}); + } + } + pub fn shutdown(&mut self, fee_estimator: &FeeEstimator, msg: &msgs::Shutdown) -> Result<(Option, Option, Vec<(HTLCSource, [u8; 32])>), HandleError> { if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 { return Err(HandleError{err: "Peer sent shutdown when we needed a channel_reestablish", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer sent shutdown when we needed a channel_reestablish".to_string(), channel_id: msg.channel_id}})}); @@ -2167,6 +2250,11 @@ impl Channel { res as u32 } + /// Returns true if we've ever received a message from the remote end for this Channel + pub fn have_received_message(&self) -> bool { + self.channel_state > (ChannelState::OurInitSent as u32) + } + /// Returns true if this channel is fully established and not known to be closing. /// Allowed in any state (including after shutdown) pub fn is_usable(&self) -> bool { @@ -2457,6 +2545,18 @@ impl Channel { Ok((msg, sig)) } + /// May panic if called on a channel that wasn't immediately-previously + /// self.remove_uncommitted_htlcs_and_mark_paused()'d + pub fn get_channel_reestablish(&self) -> msgs::ChannelReestablish { + assert_eq!(self.channel_state & ChannelState::PeerDisconnected as u32, ChannelState::PeerDisconnected as u32); + msgs::ChannelReestablish { + channel_id: self.channel_id(), + next_local_commitment_number: 0xffffffffffff - self.cur_local_commitment_transaction_number, + next_remote_commitment_number: 0xffffffffffff - self.cur_remote_commitment_transaction_number, + data_loss_protect: None, + } + } + // Send stuff to our remote peers: diff --git a/src/ln/channelmanager.rs b/src/ln/channelmanager.rs index c1f09c50..87dc059a 100644 --- a/src/ln/channelmanager.rs +++ b/src/ln/channelmanager.rs @@ -1902,7 +1902,27 @@ impl ChannelManager { Ok(()) } - + fn internal_channel_reestablish(&self, their_node_id: &PublicKey, msg: &msgs::ChannelReestablish) -> Result<(Option, Option, Option), MsgHandleErrInternal> { + let (res, chan_monitor) = { + let mut channel_state = self.channel_state.lock().unwrap(); + match channel_state.by_id.get_mut(&msg.channel_id) { + Some(chan) => { + if chan.get_their_node_id() != *their_node_id { + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); + } + let (funding_locked, revoke_and_ack, commitment_update, channel_monitor) = chan.channel_reestablish(msg).map_err(|e| MsgHandleErrInternal::from_maybe_close(e))?; + (Ok((funding_locked, revoke_and_ack, commitment_update)), channel_monitor) + }, + None => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) + } + }; + if let Some(monitor) = chan_monitor { + if let Err(_e) = self.monitor.add_update_monitor(monitor.get_funding_txo().unwrap(), monitor) { + unimplemented!(); + } + } + res + } } impl events::EventsProvider for ChannelManager { @@ -2124,7 +2144,7 @@ impl ChannelMessageHandler for ChannelManager { } fn handle_channel_reestablish(&self, their_node_id: &PublicKey, msg: &msgs::ChannelReestablish) -> Result<(Option, Option, Option), HandleError> { - Ok((None, None, None)) + handle_error!(self, self.internal_channel_reestablish(their_node_id, msg), their_node_id) } fn peer_disconnected(&self, their_node_id: &PublicKey, no_connection_possible: bool) { @@ -2156,7 +2176,7 @@ impl ChannelMessageHandler for ChannelManager { channel_state.by_id.retain(|_, chan| { if chan.get_their_node_id() == *their_node_id { //TODO: mark channel disabled (and maybe announce such after a timeout). - let failed_adds = chan.remove_uncommitted_htlcs(); + let failed_adds = chan.remove_uncommitted_htlcs_and_mark_paused(); if !failed_adds.is_empty() { let chan_update = self.get_channel_update(&chan).map(|u| u.encode_with_len()).unwrap(); // Cannot add/recv HTLCs before we have a short_id so unwrap is safe failed_payments.push((chan_update, failed_adds)); @@ -2188,8 +2208,25 @@ impl ChannelMessageHandler for ChannelManager { } } - fn peer_connected(&self, _their_node_id: &PublicKey) -> Vec { - Vec::new() + fn peer_connected(&self, their_node_id: &PublicKey) -> Vec { + let mut res = Vec::new(); + let mut channel_state = self.channel_state.lock().unwrap(); + channel_state.by_id.retain(|_, chan| { + if chan.get_their_node_id() == *their_node_id { + if !chan.have_received_message() { + // If we created this (outbound) channel while we were disconnected from the + // peer we probably failed to send the open_channel message, which is now + // lost. We can't have had anything pending related to this channel, so we just + // drop it. + false + } else { + res.push(chan.get_channel_reestablish()); + true + } + } else { true } + }); + //TODO: Also re-broadcast announcement_signatures + res } fn handle_error(&self, their_node_id: &PublicKey, msg: &msgs::ErrorMessage) {