Document channel reserve behavior a bit better
[rust-lightning] / src / ln / channel.rs
index 92f5ad0d50ef18ecd2c0be0f26a1bf1ae0fab75b..2dc7750e472be7d02a1a85fc2d109cb3a7031a67 100644 (file)
@@ -330,6 +330,7 @@ pub(super) struct Channel {
        our_dust_limit_satoshis: u64,
        their_max_htlc_value_in_flight_msat: u64,
        //get_our_max_htlc_value_in_flight_msat(): u64,
+       /// minimum channel reserve for **self** to maintain - set by them.
        their_channel_reserve_satoshis: u64,
        //get_our_channel_reserve_satoshis(): u64,
        their_htlc_minimum_msat: u64,
@@ -402,6 +403,8 @@ impl Channel {
                channel_value_satoshis * 1000 / 10 //TODO
        }
 
+       /// Returns a minimum channel reserve value **they** need to maintain
+       ///
        /// Guaranteed to return a value no larger than channel_value_satoshis
        fn get_our_channel_reserve_satoshis(channel_value_satoshis: u64) -> u64 {
                let (q, _) = channel_value_satoshis.overflowing_div(100);
@@ -1387,9 +1390,9 @@ impl Channel {
                Ok(self.channel_monitor.clone())
        }
 
-       pub fn funding_locked(&mut self, msg: &msgs::FundingLocked) -> Result<(), HandleError> {
+       pub fn funding_locked(&mut self, msg: &msgs::FundingLocked) -> Result<(), ChannelError> {
                if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
-                       return Err(HandleError{err: "Peer sent funding_locked when we needed a channel_reestablish", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer sent funding_locked when we needed a channel_reestablish".to_string(), channel_id: msg.channel_id}})});
+                       return Err(ChannelError::Close("Peer sent funding_locked when we needed a channel_reestablish"));
                }
                let non_shutdown_state = self.channel_state & (!BOTH_SIDES_SHUTDOWN_MASK);
                if non_shutdown_state == ChannelState::FundingSent as u32 {
@@ -1402,12 +1405,12 @@ impl Channel {
                                self.cur_local_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER - 1 &&
                                self.cur_remote_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER - 1 {
                        if self.their_cur_commitment_point != Some(msg.next_per_commitment_point) {
-                               return Err(HandleError{err: "Peer sent a reconnect funding_locked with a different point", action: None});
+                               return Err(ChannelError::Close("Peer sent a reconnect funding_locked with a different point"));
                        }
                        // They probably disconnected/reconnected and re-sent the funding_locked, which is required
                        return Ok(());
                } else {
-                       return Err(HandleError{err: "Peer sent a funding_locked at a strange time", action: None});
+                       return Err(ChannelError::Close("Peer sent a funding_locked at a strange time"));
                }
 
                self.their_prev_commitment_point = self.their_cur_commitment_point;
@@ -1467,7 +1470,7 @@ impl Channel {
                        return Err(HandleError{err: "Remote side tried to send less than our minimum HTLC value", action: None});
                }
 
-               let (inbound_htlc_count, _, htlc_outbound_value_msat, htlc_inbound_value_msat) = self.get_pending_htlc_stats(true);
+               let (inbound_htlc_count, _, _, htlc_inbound_value_msat) = self.get_pending_htlc_stats(true);
                if inbound_htlc_count + 1 > OUR_MAX_HTLCS as u32 {
                        return Err(HandleError{err: "Remote tried to push more than our max accepted HTLCs", action: None});
                }
@@ -1479,7 +1482,7 @@ impl Channel {
                // Check our_channel_reserve_satoshis (we're getting paid, so they have to at least meet
                // the reserve_satoshis we told them to always have as direct payment so that they lose
                // something if we punish them for broadcasting an old state).
-               if htlc_inbound_value_msat + htlc_outbound_value_msat + msg.amount_msat + self.value_to_self_msat > (self.channel_value_satoshis - Channel::get_our_channel_reserve_satoshis(self.channel_value_satoshis)) * 1000 {
+               if htlc_inbound_value_msat + msg.amount_msat + self.value_to_self_msat > (self.channel_value_satoshis - Channel::get_our_channel_reserve_satoshis(self.channel_value_satoshis)) * 1000 {
                        return Err(HandleError{err: "Remote HTLC add would put them over their reserve value", action: None});
                }
                if self.next_remote_htlc_id != msg.htlc_id {
@@ -1508,38 +1511,38 @@ impl Channel {
 
        /// Removes an outbound HTLC which has been commitment_signed by the remote end
        #[inline]
-       fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option<HTLCFailReason>) -> Result<&HTLCSource, HandleError> {
+       fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option<HTLCFailReason>) -> Result<&HTLCSource, ChannelError> {
                for htlc in self.pending_outbound_htlcs.iter_mut() {
                        if htlc.htlc_id == htlc_id {
                                match check_preimage {
                                        None => {},
                                        Some(payment_hash) =>
                                                if payment_hash != htlc.payment_hash {
-                                                       return Err(HandleError{err: "Remote tried to fulfill HTLC with an incorrect preimage", action: None});
+                                                       return Err(ChannelError::Close("Remote tried to fulfill HTLC with an incorrect preimage"));
                                                }
                                };
                                match htlc.state {
                                        OutboundHTLCState::LocalAnnounced =>
-                                               return Err(HandleError{err: "Remote tried to fulfill HTLC before it had been committed", action: None}),
+                                               return Err(ChannelError::Close("Remote tried to fulfill HTLC before it had been committed")),
                                        OutboundHTLCState::Committed => {
                                                htlc.state = OutboundHTLCState::RemoteRemoved;
                                                htlc.fail_reason = fail_reason;
                                        },
                                        OutboundHTLCState::AwaitingRemoteRevokeToRemove | OutboundHTLCState::AwaitingRemovedRemoteRevoke | OutboundHTLCState::RemoteRemoved =>
-                                               return Err(HandleError{err: "Remote tried to fulfill HTLC that they'd already fulfilled", action: None}),
+                                               return Err(ChannelError::Close("Remote tried to fulfill HTLC that they'd already fulfilled")),
                                }
                                return Ok(&htlc.source);
                        }
                }
-               Err(HandleError{err: "Remote tried to fulfill/fail an HTLC we couldn't find", action: None})
+               Err(ChannelError::Close("Remote tried to fulfill/fail an HTLC we couldn't find"))
        }
 
-       pub fn update_fulfill_htlc(&mut self, msg: &msgs::UpdateFulfillHTLC) -> Result<&HTLCSource, HandleError> {
+       pub fn update_fulfill_htlc(&mut self, msg: &msgs::UpdateFulfillHTLC) -> Result<&HTLCSource, ChannelError> {
                if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) {
-                       return Err(HandleError{err: "Got add HTLC message when channel was not in an operational state", action: None});
+                       return Err(ChannelError::Close("Got fulfill HTLC message when channel was not in an operational state"));
                }
                if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
-                       return Err(HandleError{err: "Peer sent update_fulfill_htlc when we needed a channel_reestablish", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer sent update_fulfill_htlc when we needed a channel_reestablish".to_string(), channel_id: msg.channel_id}})});
+                       return Err(ChannelError::Close("Peer sent update_fulfill_htlc when we needed a channel_reestablish"));
                }
 
                let mut sha = Sha256::new();
@@ -1550,23 +1553,23 @@ impl Channel {
                self.mark_outbound_htlc_removed(msg.htlc_id, Some(payment_hash), None)
        }
 
-       pub fn update_fail_htlc(&mut self, msg: &msgs::UpdateFailHTLC, fail_reason: HTLCFailReason) -> Result<&HTLCSource, HandleError> {
+       pub fn update_fail_htlc(&mut self, msg: &msgs::UpdateFailHTLC, fail_reason: HTLCFailReason) -> Result<&HTLCSource, ChannelError> {
                if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) {
-                       return Err(HandleError{err: "Got add HTLC message when channel was not in an operational state", action: None});
+                       return Err(ChannelError::Close("Got fail HTLC message when channel was not in an operational state"));
                }
                if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
-                       return Err(HandleError{err: "Peer sent update_fail_htlc when we needed a channel_reestablish", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer sent update_fail_htlc when we needed a channel_reestablish".to_string(), channel_id: msg.channel_id}})});
+                       return Err(ChannelError::Close("Peer sent update_fail_htlc when we needed a channel_reestablish"));
                }
 
                self.mark_outbound_htlc_removed(msg.htlc_id, None, Some(fail_reason))
        }
 
-       pub fn update_fail_malformed_htlc<'a>(&mut self, msg: &msgs::UpdateFailMalformedHTLC, fail_reason: HTLCFailReason) -> Result<&HTLCSource, HandleError> {
+       pub fn update_fail_malformed_htlc<'a>(&mut self, msg: &msgs::UpdateFailMalformedHTLC, fail_reason: HTLCFailReason) -> Result<&HTLCSource, ChannelError> {
                if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) {
-                       return Err(HandleError{err: "Got add HTLC message when channel was not in an operational state", action: None});
+                       return Err(ChannelError::Close("Got fail malformed HTLC message when channel was not in an operational state"));
                }
                if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
-                       return Err(HandleError{err: "Peer sent update_fail_malformed_htlc when we needed a channel_reestablish", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer sent update_fail_malformed_htlc when we needed a channel_reestablish".to_string(), channel_id: msg.channel_id}})});
+                       return Err(ChannelError::Close("Peer sent update_fail_malformed_htlc when we needed a channel_reestablish"));
                }
 
                self.mark_outbound_htlc_removed(msg.htlc_id, None, Some(fail_reason))
@@ -2014,14 +2017,17 @@ impl Channel {
 
        /// 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<msgs::FundingLocked>, Option<msgs::RevokeAndACK>, Option<msgs::CommitmentUpdate>, Option<ChannelMonitor>), HandleError> {
+       pub fn channel_reestablish(&mut self, msg: &msgs::ChannelReestablish) -> Result<(Option<msgs::FundingLocked>, Option<msgs::RevokeAndACK>, Option<msgs::CommitmentUpdate>, Option<ChannelMonitor>), ChannelError> {
                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}})});
+                       // While BOLT 2 doesn't indicate explicitly we should error this channel here, it
+                       // almost certainly indicates we are going to end up out-of-sync in some way, so we
+                       // just close here instead of trying to recover.
+                       return Err(ChannelError::Close("Peer sent a loose channel_reestablish not after reconnect"));
                }
 
                if msg.next_local_commitment_number == 0 || msg.next_local_commitment_number >= INITIAL_COMMITMENT_NUMBER ||
                                msg.next_remote_commitment_number == 0 || msg.next_remote_commitment_number >= INITIAL_COMMITMENT_NUMBER {
-                       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}})});
+                       return Err(ChannelError::Close("Peer send garbage channel_reestablish"));
                }
 
                // Go ahead and unmark PeerDisconnected as various calls we may make check for it (and all
@@ -2041,7 +2047,7 @@ impl Channel {
                                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}})});
+                       return Err(ChannelError::Close("Peer attempted to reestablish channel with a very old local commitment transaction"));
                }
 
                if msg.next_local_commitment_number == INITIAL_COMMITMENT_NUMBER - self.cur_remote_commitment_transaction_number {
@@ -2065,11 +2071,12 @@ impl Channel {
                                match self.free_holding_cell_htlcs() {
                                        Err(e) => {
                                                if let &Some(msgs::ErrorAction::DisconnectPeer{msg: Some(_)}) = &e.action {
+                                                       return Err(ChannelError::Close(e.err));
                                                } else if let &Some(msgs::ErrorAction::SendErrorMessage{msg: _}) = &e.action {
+                                                       return Err(ChannelError::Close(e.err));
                                                } 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)),
@@ -2088,7 +2095,7 @@ impl Channel {
                                                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}})});
+                       return Err(ChannelError::Close("Peer attempted to reestablish channel with a very old remote commitment transaction"));
                }
        }
 
@@ -2644,15 +2651,15 @@ impl Channel {
        /// closing).
        /// Note that the "channel must be funded" requirement is stricter than BOLT 7 requires - see
        /// https://github.com/lightningnetwork/lightning-rfc/issues/468
-       pub fn get_channel_announcement(&self, our_node_id: PublicKey, chain_hash: Sha256dHash) -> Result<(msgs::UnsignedChannelAnnouncement, Signature), HandleError> {
+       pub fn get_channel_announcement(&self, our_node_id: PublicKey, chain_hash: Sha256dHash) -> Result<(msgs::UnsignedChannelAnnouncement, Signature), ChannelError> {
                if !self.announce_publicly {
-                       return Err(HandleError{err: "Channel is not available for public announcements", action: Some(msgs::ErrorAction::IgnoreError)});
+                       return Err(ChannelError::Ignore("Channel is not available for public announcements"));
                }
                if self.channel_state & (ChannelState::ChannelFunded as u32) == 0 {
-                       return Err(HandleError{err: "Cannot get a ChannelAnnouncement until the channel funding has been locked", action: Some(msgs::ErrorAction::IgnoreError)});
+                       return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement until the channel funding has been locked"));
                }
                if (self.channel_state & (ChannelState::LocalShutdownSent as u32 | ChannelState::ShutdownComplete as u32)) != 0 {
-                       return Err(HandleError{err: "Cannot get a ChannelAnnouncement once the channel is closing", action: Some(msgs::ErrorAction::IgnoreError)});
+                       return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement once the channel is closing"));
                }
 
                let were_node_one = our_node_id.serialize()[..] < self.their_node_id.serialize()[..];
@@ -2718,7 +2725,7 @@ impl Channel {
                        return Err(HandleError{err: "Cannot send an HTLC while disconnected", action: Some(ErrorAction::IgnoreError)});
                }
 
-               let (_, outbound_htlc_count, htlc_outbound_value_msat, htlc_inbound_value_msat) = self.get_pending_htlc_stats(false);
+               let (_, outbound_htlc_count, htlc_outbound_value_msat, _) = self.get_pending_htlc_stats(false);
                if outbound_htlc_count + 1 > self.their_max_accepted_htlcs as u32 {
                        return Err(HandleError{err: "Cannot push more than their max accepted HTLCs", action: None});
                }
@@ -2727,8 +2734,20 @@ impl Channel {
                if htlc_outbound_value_msat + amount_msat > self.their_max_htlc_value_in_flight_msat {
                        return Err(HandleError{err: "Cannot send value that would put us over our max HTLC value in flight", action: None});
                }
-               // Check their_channel_reserve_satoshis:
-               if htlc_inbound_value_msat + htlc_outbound_value_msat + amount_msat + (self.channel_value_satoshis * 1000 - self.value_to_self_msat) > (self.channel_value_satoshis - self.their_channel_reserve_satoshis) * 1000 {
+
+               let mut holding_cell_outbound_amount_msat = 0;
+               for holding_htlc in self.holding_cell_htlc_updates.iter() {
+                       match holding_htlc {
+                               &HTLCUpdateAwaitingACK::AddHTLC { ref amount_msat, .. } => {
+                                       holding_cell_outbound_amount_msat += *amount_msat;
+                               }
+                               _ => {}
+                       }
+               }
+
+               // Check self.their_channel_reserve_satoshis (the amount we must keep as
+               // reserve for them to have something to claim if we misbehave)
+               if self.value_to_self_msat < self.their_channel_reserve_satoshis * 1000 + amount_msat + holding_cell_outbound_amount_msat + htlc_outbound_value_msat {
                        return Err(HandleError{err: "Cannot send value that would put us over our reserve value", action: None});
                }
 
@@ -2875,18 +2894,23 @@ impl Channel {
 
        /// Begins the shutdown process, getting a message for the remote peer and returning all
        /// holding cell HTLCs for payment failure.
-       pub fn get_shutdown(&mut self) -> Result<(msgs::Shutdown, Vec<(HTLCSource, [u8; 32])>), HandleError> {
+       pub fn get_shutdown(&mut self) -> Result<(msgs::Shutdown, Vec<(HTLCSource, [u8; 32])>), APIError> {
                for htlc in self.pending_outbound_htlcs.iter() {
                        if htlc.state == OutboundHTLCState::LocalAnnounced {
-                               return Err(HandleError{err: "Cannot begin shutdown with pending HTLCs, call send_commitment first", action: None});
+                               return Err(APIError::APIMisuseError{err: "Cannot begin shutdown with pending HTLCs. Process pending events first"});
                        }
                }
                if self.channel_state & BOTH_SIDES_SHUTDOWN_MASK != 0 {
-                       return Err(HandleError{err: "Shutdown already in progress", action: None});
+                       if (self.channel_state & ChannelState::LocalShutdownSent as u32) == ChannelState::LocalShutdownSent as u32 {
+                               return Err(APIError::APIMisuseError{err: "Shutdown already in progress"});
+                       }
+                       else if (self.channel_state & ChannelState::RemoteShutdownSent as u32) == ChannelState::RemoteShutdownSent as u32 {
+                               return Err(APIError::ChannelUnavailable{err: "Shutdown already in progress by remote"});
+                       }
                }
                assert_eq!(self.channel_state & ChannelState::ShutdownComplete as u32, 0);
                if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
-                       return Err(HandleError{err: "Cannot begin shutdown while peer is disconnected, maybe force-close instead?", action: None});
+                       return Err(APIError::ChannelUnavailable{err: "Cannot begin shutdown while peer is disconnected, maybe force-close instead?"});
                }
 
                let our_closing_script = self.get_closing_scriptpubkey();