Skip to content

Commit f9fee1a

Browse files
Impl fee spike reserve + incl commit tx fee in channel reserve calculation
When we receive an inbound HTLC from a peer on an inbound channel, the peer needs to be able to pay for the additional commitment tx fees as well. When we're sending an outbound HTLC on an outbound channel, we need to be able to pay for the additional commitment tx fees as well. + implement fee spike reserve for channel initiators sending payments. From lightning-rfc PR #740. Co-authored-by: Matt Corallo <[email protected]> Co-authored-by: Valentine Wallace <[email protected]>
1 parent 59b1bf6 commit f9fee1a

File tree

2 files changed

+212
-79
lines changed

2 files changed

+212
-79
lines changed

lightning/src/ln/channel.rs

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub struct ChannelValueStat {
4646
pub pending_inbound_htlcs_amount_msat: u64,
4747
pub holding_cell_outbound_amount_msat: u64,
4848
pub their_max_htlc_value_in_flight_msat: u64, // outgoing
49+
pub commit_tx_fee_outbound: u64,
4950
}
5051

5152
enum InboundHTLCRemovalReason {
@@ -1656,6 +1657,50 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
16561657
cmp::min(self.value_to_self_msat as i64 - self.get_outbound_pending_htlc_stats().1 as i64, 0) as u64)
16571658
}
16581659

1660+
fn htlc_count_next_local_commit_tx(&self) -> u64 {
1661+
assert!(self.channel_outbound);
1662+
1663+
let mut their_acked_htlcs = self.pending_inbound_htlcs.len() as u64;
1664+
for ref htlc in self.pending_outbound_htlcs.iter() {
1665+
match htlc.state {
1666+
OutboundHTLCState::Committed => their_acked_htlcs +=1,
1667+
OutboundHTLCState::RemoteRemoved {..} => their_acked_htlcs +=1,
1668+
OutboundHTLCState::LocalAnnounced {..} => their_acked_htlcs += 1,
1669+
_ => {},
1670+
}
1671+
}
1672+
1673+
for ref htlc in self.holding_cell_htlc_updates.iter() {
1674+
match htlc {
1675+
&&HTLCUpdateAwaitingACK::AddHTLC { .. } => their_acked_htlcs += 1,
1676+
_ => {},
1677+
}
1678+
}
1679+
1680+
their_acked_htlcs
1681+
}
1682+
1683+
fn htlc_count_next_remote_commit_tx(&self) -> u64 {
1684+
assert!(!self.channel_outbound);
1685+
1686+
let mut their_acked_htlcs = self.pending_inbound_htlcs.len() as u64;
1687+
// When calculating the set of HTLCs which will be included in their next
1688+
// commitment_signed, all inbound HTLCs are included (as all states imply it will be
1689+
// included) and only committed outbound HTLCs, see below.
1690+
for ref htlc in self.pending_outbound_htlcs.iter() {
1691+
// We only include outbound HTLCs if it will not be included in their next
1692+
// commitment_signed, ie if they've responded to us with an RAA after announcement
1693+
// and if the removal process hasn't been finalized.
1694+
match htlc.state {
1695+
OutboundHTLCState::Committed => their_acked_htlcs +=1,
1696+
OutboundHTLCState::RemoteRemoved {..} => their_acked_htlcs +=1,
1697+
_ => {},
1698+
}
1699+
}
1700+
1701+
their_acked_htlcs
1702+
}
1703+
16591704
pub fn update_add_htlc(&mut self, msg: &msgs::UpdateAddHTLC, pending_forward_state: PendingHTLCStatus) -> Result<(), ChannelError> {
16601705
if (self.channel_state & (ChannelState::ChannelFunded as u32 | ChannelState::RemoteShutdownSent as u32)) != (ChannelState::ChannelFunded as u32) {
16611706
return Err(ChannelError::Close("Got add HTLC message when channel was not in an operational state"));
@@ -1701,8 +1746,10 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
17011746
removed_outbound_total_msat += htlc.amount_msat;
17021747
}
17031748
}
1704-
if htlc_inbound_value_msat + msg.amount_msat + self.value_to_self_msat > (self.channel_value_satoshis - Channel::<ChanSigner>::get_our_channel_reserve_satoshis(self.channel_value_satoshis)) * 1000 + removed_outbound_total_msat {
1705-
return Err(ChannelError::Close("Remote HTLC add would put them over their reserve value"));
1749+
// The + 1 is for the HTLC that is currently being added to the commitment tx.
1750+
let remote_fee_cost_msat = if self.channel_outbound { 0 } else { (self.feerate_per_kw * (COMMITMENT_TX_BASE_WEIGHT + (self.htlc_count_next_remote_commit_tx() + 1) * COMMITMENT_TX_WEIGHT_PER_HTLC)) };
1751+
if htlc_inbound_value_msat + msg.amount_msat + self.value_to_self_msat + remote_fee_cost_msat > (self.channel_value_satoshis - Channel::<ChanSigner>::get_our_channel_reserve_satoshis(self.channel_value_satoshis)) * 1000 + removed_outbound_total_msat {
1752+
return Err(ChannelError::Ignore("Remote HTLC add would put them over their reserve value"));
17061753
}
17071754
if self.next_remote_htlc_id != msg.htlc_id {
17081755
return Err(ChannelError::Close("Remote skipped HTLC ID"));
@@ -3031,6 +3078,9 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
30313078
res
30323079
},
30333080
their_max_htlc_value_in_flight_msat: self.their_max_htlc_value_in_flight_msat,
3081+
commit_tx_fee_outbound: if self.channel_outbound {
3082+
(self.feerate_per_kw * (COMMITMENT_TX_BASE_WEIGHT + (self.htlc_count_next_local_commit_tx() + 1) * COMMITMENT_TX_WEIGHT_PER_HTLC))
3083+
} else { 0 },
30343084
}
30353085
}
30363086

@@ -3534,9 +3584,15 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
35343584
return Err(ChannelError::Ignore("Cannot send value that would put us over the max HTLC value in flight our peer will accept"));
35353585
}
35363586

3587+
// Add additional reserve that avoids stuck channels in the case of fee spikes.
3588+
let fee_spike_reserve = if self.channel_outbound { 2 * COMMITMENT_TX_WEIGHT_PER_HTLC * self.feerate_per_kw } else { 0 };
3589+
3590+
// The + 1 is for the HTLC currently being added to the commitment tx.
3591+
let local_fee_cost_msat = if self.channel_outbound { (self.feerate_per_kw * ((COMMITMENT_TX_BASE_WEIGHT + (self.htlc_count_next_local_commit_tx() + 1) * COMMITMENT_TX_WEIGHT_PER_HTLC))) } else { 0 };
3592+
35373593
// Check self.their_channel_reserve_satoshis (the amount we must keep as
35383594
// reserve for them to have something to claim if we misbehave)
3539-
if self.value_to_self_msat < self.their_channel_reserve_satoshis * 1000 + amount_msat + htlc_outbound_value_msat {
3595+
if self.value_to_self_msat < self.their_channel_reserve_satoshis * 1000 + amount_msat + htlc_outbound_value_msat + local_fee_cost_msat + fee_spike_reserve {
35403596
return Err(ChannelError::Ignore("Cannot send value that would put us over their reserve value"));
35413597
}
35423598

0 commit comments

Comments
 (0)