pub counterparty_dust_limit_msat: u64,
}
+pub struct AvailableBalances {
+ /// The amount that would go to us if we close the channel, ignoring any on-chain fees.
+ pub balance_msat: u64,
+ /// Total amount available for our counterparty to send to us.
+ pub inbound_capacity_msat: u64,
+ /// Total amount available for us to send to our counterparty.
+ pub outbound_capacity_msat: u64,
+ /// The maximum value we can assign to the next outbound HTLC
+ pub next_outbound_htlc_limit_msat: u64,
+}
+
#[derive(Debug, Clone, Copy, PartialEq)]
enum FeeUpdateState {
// Inbound states mirroring InboundHTLCState
if msg.channel_reserve_satoshis > msg.funding_satoshis {
return Err(ChannelError::Close(format!("Bogus channel_reserve_satoshis ({}). Must be not greater than funding_satoshis: {}", msg.channel_reserve_satoshis, msg.funding_satoshis)));
}
- let funding_value = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000;
- if msg.push_msat > funding_value {
- return Err(ChannelError::Close(format!("push_msat {} was larger than funding value {}", msg.push_msat, funding_value)));
+ let full_channel_value_msat = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000;
+ if msg.push_msat > full_channel_value_msat {
+ return Err(ChannelError::Close(format!("push_msat {} was larger than channel amount minus reserve ({})", msg.push_msat, full_channel_value_msat)));
}
if msg.dust_limit_satoshis > msg.funding_satoshis {
return Err(ChannelError::Close(format!("dust_limit_satoshis {} was larger than funding_satoshis {}. Peer never wants payout outputs?", msg.dust_limit_satoshis, msg.funding_satoshis)));
}
- let full_channel_value_msat = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000;
if msg.htlc_minimum_msat >= full_channel_value_msat {
return Err(ChannelError::Close(format!("Minimum htlc value ({}) was larger than full channel value ({})", msg.htlc_minimum_msat, full_channel_value_msat)));
}
if holder_selected_channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). dust_limit_satoshis is ({}).", holder_selected_channel_reserve_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS)));
}
+ if holder_selected_channel_reserve_satoshis * 1000 >= full_channel_value_msat {
+ return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). Channel value is ({} - {}).", holder_selected_channel_reserve_satoshis, full_channel_value_msat, msg.push_msat)));
+ }
if msg.channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
log_debug!(logger, "channel_reserve_satoshis ({}) is smaller than our dust limit ({}). We can broadcast stale states without any risk, implying this channel is very insecure for our counterparty.",
msg.channel_reserve_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS);
stats
}
- /// Get the available (ie not including pending HTLCs) inbound and outbound balance in msat.
+ /// Get the available balances, see [`AvailableBalances`]'s fields for more info.
/// Doesn't bother handling the
/// if-we-removed-it-already-but-haven't-fully-resolved-they-can-still-send-an-inbound-HTLC
/// corner case properly.
- /// The channel reserve is subtracted from each balance.
- /// See also [`Channel::get_balance_msat`]
- pub fn get_inbound_outbound_available_balance_msat(&self) -> (u64, u64) {
+ pub fn get_available_balances(&self) -> AvailableBalances {
// Note that we have to handle overflow due to the above case.
- (
- cmp::max(self.channel_value_satoshis as i64 * 1000
- - self.value_to_self_msat as i64
- - self.get_inbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
- - self.holder_selected_channel_reserve_satoshis as i64 * 1000,
- 0) as u64,
- cmp::max(self.value_to_self_msat as i64
- - self.get_outbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
- - self.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) as i64 * 1000,
- 0) as u64
- )
- }
+ let outbound_stats = self.get_outbound_pending_htlc_stats(None);
- /// Get our total balance in msat.
- /// This is the amount that would go to us if we close the channel, ignoring any on-chain fees.
- /// See also [`Channel::get_inbound_outbound_available_balance_msat`]
- pub fn get_balance_msat(&self) -> u64 {
- // Include our local balance, plus any inbound HTLCs we know the preimage for, minus any
- // HTLCs sent or which will be sent after commitment signed's are exchanged.
let mut balance_msat = self.value_to_self_msat;
for ref htlc in self.pending_inbound_htlcs.iter() {
if let InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_)) = htlc.state {
balance_msat += htlc.amount_msat;
}
}
- balance_msat - self.get_outbound_pending_htlc_stats(None).pending_htlcs_value_msat
+ balance_msat -= outbound_stats.pending_htlcs_value_msat;
+
+ let outbound_capacity_msat = cmp::max(self.value_to_self_msat as i64
+ - outbound_stats.pending_htlcs_value_msat as i64
+ - self.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) as i64 * 1000,
+ 0) as u64;
+ AvailableBalances {
+ inbound_capacity_msat: cmp::max(self.channel_value_satoshis as i64 * 1000
+ - self.value_to_self_msat as i64
+ - self.get_inbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
+ - self.holder_selected_channel_reserve_satoshis as i64 * 1000,
+ 0) as u64,
+ outbound_capacity_msat,
+ next_outbound_htlc_limit_msat: cmp::max(cmp::min(outbound_capacity_msat as i64,
+ self.counterparty_max_htlc_value_in_flight_msat as i64
+ - outbound_stats.pending_htlcs_value_msat as i64),
+ 0) as u64,
+ balance_msat,
+ }
}
pub fn get_holder_counterparty_selected_channel_reserve_satoshis(&self) -> (u64, Option<u64>) {
}
/// Allowed in any state (including after shutdown)
- #[cfg(test)]
pub fn get_holder_htlc_minimum_msat(&self) -> u64 {
self.holder_htlc_minimum_msat
}
+ /// Allowed in any state (including after shutdown), but will return none before TheirInitSent
+ pub fn get_holder_htlc_maximum_msat(&self) -> Option<u64> {
+ self.get_htlc_maximum_msat(self.holder_max_htlc_value_in_flight_msat)
+ }
+
/// Allowed in any state (including after shutdown)
pub fn get_announced_htlc_max_msat(&self) -> u64 {
return cmp::min(
self.counterparty_htlc_minimum_msat
}
+ /// Allowed in any state (including after shutdown), but will return none before TheirInitSent
+ pub fn get_counterparty_htlc_maximum_msat(&self) -> Option<u64> {
+ self.get_htlc_maximum_msat(self.counterparty_max_htlc_value_in_flight_msat)
+ }
+
+ fn get_htlc_maximum_msat(&self, party_max_htlc_value_in_flight_msat: u64) -> Option<u64> {
+ self.counterparty_selected_channel_reserve_satoshis.map(|counterparty_reserve| {
+ let holder_reserve = self.holder_selected_channel_reserve_satoshis;
+ cmp::min(
+ (self.channel_value_satoshis - counterparty_reserve - holder_reserve) * 1000,
+ party_max_htlc_value_in_flight_msat
+ )
+ })
+ }
+
pub fn get_value_satoshis(&self) -> u64 {
self.channel_value_satoshis
}
}
// If we've sent funding_locked (or have both sent and received funding_locked), and
- // the funding transaction's confirmation count has dipped below minimum_depth / 2,
+ // the funding transaction has become unconfirmed,
// close the channel and hope we can get the latest state on chain (because presumably
// the funding transaction is at least still in the mempool of most nodes).
- if funding_tx_confirmations < self.minimum_depth.unwrap() as i64 / 2 {
+ //
+ // Note that ideally we wouldn't force-close if we see *any* reorg on a 1-conf channel,
+ // but not doing so may lead to the `ChannelManager::short_to_id` map being
+ // inconsistent, so we currently have to.
+ if funding_tx_confirmations == 0 && self.funding_tx_confirmed_in.is_some() {
let err_reason = format!("Funding transaction was un-confirmed. Locked at {} confs, now have {} confs.",
self.minimum_depth.unwrap(), funding_tx_confirmations);
return Err(ClosureReason::ProcessingError { err: err_reason });