Document channel reserve behavior a bit better
[rust-lightning] / src / ln / channel.rs
index fdddd2368a1e3c601a07e13d5dd35c719923c6d1..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);
@@ -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 {
@@ -2648,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()[..];
@@ -2722,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});
                }
@@ -2731,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});
                }
 
@@ -2879,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();