Skip to content

Commit 279ae3d

Browse files
Add fee spike buffer + incl commit tx fee in chan reserve calculation
When we receive an inbound HTLC from a peer on an inbound channel, make sure the funder can still cover the additional on-chain cost of the HTLC while maintaining their channel reserve. When we're sending an outbound HTLC, make sure the funder can still cover the additional on-chain cost of the HTLC while maintaining their channel reserve. + implement fee spike buffer for channel initiators sending payments. Also add an additional spec-deviating fee spike buffer on the receiving side (but don't close the channel if this reserve is violated, just fail the HTLC). From lightning-rfc PR #740. Co-authored-by: Matt Corallo <[email protected]> Co-authored-by: Valentine Wallace <[email protected]>
1 parent 283c947 commit 279ae3d

File tree

2 files changed

+428
-90
lines changed

2 files changed

+428
-90
lines changed

lightning/src/ln/channel.rs

Lines changed: 152 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub struct ChannelValueStat {
4444
pub pending_inbound_htlcs_amount_msat: u64,
4545
pub holding_cell_outbound_amount_msat: u64,
4646
pub their_max_htlc_value_in_flight_msat: u64, // outgoing
47+
pub their_dust_limit_msat: u64,
4748
}
4849

4950
enum InboundHTLCRemovalReason {
@@ -1663,6 +1664,73 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
16631664
cmp::min(self.value_to_self_msat as i64 - self.get_outbound_pending_htlc_stats().1 as i64, 0) as u64)
16641665
}
16651666

1667+
// Get the fee cost of a commitment tx with a given number of HTLC outputs.
1668+
// Note that num_htlcs should not include dust HTLCs.
1669+
fn commit_tx_fee_msat(&self, num_htlcs: usize) -> u64 {
1670+
// Note that we need to divide before multiplying to round properly,
1671+
// since the lowest denomination of bitcoin on-chain is the satoshi.
1672+
(COMMITMENT_TX_BASE_WEIGHT + num_htlcs as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC) * self.feerate_per_kw / 1000 * 1000
1673+
}
1674+
1675+
// Get the commitment tx fee for the local (i.e our) next commitment transaction
1676+
// based on the number of pending HTLCs that are on track to be in our next
1677+
// commitment tx. `addl_htcs` is an optional parameter allowing the caller
1678+
// to add a number of additional HTLCs to the calculation. Note that dust
1679+
// HTLCs are excluded.
1680+
fn next_local_commit_tx_fee_msat(&self, addl_htlcs: usize) -> u64 {
1681+
assert!(self.channel_outbound);
1682+
1683+
let mut their_acked_htlcs = self.pending_inbound_htlcs.len();
1684+
for ref htlc in self.pending_outbound_htlcs.iter() {
1685+
if htlc.amount_msat / 1000 <= self.our_dust_limit_satoshis {
1686+
continue
1687+
}
1688+
match htlc.state {
1689+
OutboundHTLCState::Committed => their_acked_htlcs += 1,
1690+
OutboundHTLCState::RemoteRemoved {..} => their_acked_htlcs += 1,
1691+
OutboundHTLCState::LocalAnnounced {..} => their_acked_htlcs += 1,
1692+
_ => {},
1693+
}
1694+
}
1695+
1696+
for htlc in self.holding_cell_htlc_updates.iter() {
1697+
match htlc {
1698+
&HTLCUpdateAwaitingACK::AddHTLC { .. } => their_acked_htlcs += 1,
1699+
_ => {},
1700+
}
1701+
}
1702+
1703+
self.commit_tx_fee_msat(their_acked_htlcs + addl_htlcs)
1704+
}
1705+
1706+
// Get the commitment tx fee for the remote's next commitment transaction
1707+
// based on the number of pending HTLCs that are on track to be in their
1708+
// next commitment tx. `addl_htcs` is an optional parameter allowing the caller
1709+
// to add a number of additional HTLCs to the calculation. Note that dust HTLCs
1710+
// are excluded.
1711+
fn next_remote_commit_tx_fee_msat(&self, addl_htlcs: usize) -> u64 {
1712+
assert!(!self.channel_outbound);
1713+
1714+
// When calculating the set of HTLCs which will be included in their next
1715+
// commitment_signed, all inbound HTLCs are included (as all states imply it will be
1716+
// included) and only committed outbound HTLCs, see below.
1717+
let mut their_acked_htlcs = self.pending_inbound_htlcs.len();
1718+
for ref htlc in self.pending_outbound_htlcs.iter() {
1719+
if htlc.amount_msat / 1000 <= self.their_dust_limit_satoshis {
1720+
continue
1721+
}
1722+
// We only include outbound HTLCs if it will not be included in their next
1723+
// commitment_signed, i.e. if they've responded to us with an RAA after announcement.
1724+
match htlc.state {
1725+
OutboundHTLCState::Committed => their_acked_htlcs += 1,
1726+
OutboundHTLCState::RemoteRemoved {..} => their_acked_htlcs += 1,
1727+
_ => {},
1728+
}
1729+
}
1730+
1731+
self.commit_tx_fee_msat(their_acked_htlcs + addl_htlcs)
1732+
}
1733+
16661734
pub fn update_add_htlc<F>(&mut self, msg: &msgs::UpdateAddHTLC, mut pending_forward_status: PendingHTLCStatus, create_pending_htlc_status: F) -> Result<(), ChannelError>
16671735
where F: for<'a> Fn(&'a Self, PendingHTLCStatus, u16) -> PendingHTLCStatus {
16681736
// We can't accept HTLCs sent after we've sent a shutdown.
@@ -1716,9 +1784,52 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
17161784
removed_outbound_total_msat += htlc.amount_msat;
17171785
}
17181786
}
1719-
if htlc_inbound_value_msat + msg.amount_msat + self.value_to_self_msat > (self.channel_value_satoshis - Channel::<ChanSigner>::get_remote_channel_reserve_satoshis(self.channel_value_satoshis)) * 1000 + removed_outbound_total_msat {
1720-
return Err(ChannelError::Close("Remote HTLC add would put them under their reserve value"));
1787+
1788+
let pending_value_to_self_msat =
1789+
self.value_to_self_msat + htlc_inbound_value_msat - removed_outbound_total_msat;
1790+
let pending_remote_value_msat =
1791+
self.channel_value_satoshis * 1000 - pending_value_to_self_msat;
1792+
if pending_remote_value_msat < msg.amount_msat {
1793+
return Err(ChannelError::Close("Remote HTLC add would overdraw remaining funds"));
1794+
}
1795+
1796+
let remote_commit_tx_fee_msat = if self.channel_outbound { 0 } else {
1797+
// +1 for this HTLC.
1798+
self.next_remote_commit_tx_fee_msat(1)
1799+
};
1800+
if pending_remote_value_msat - msg.amount_msat < remote_commit_tx_fee_msat {
1801+
return Err(ChannelError::Close("Remote HTLC add would not leave enough to pay for fees"));
1802+
};
1803+
1804+
let chan_reserve_msat =
1805+
Channel::<ChanSigner>::get_remote_channel_reserve_satoshis(self.channel_value_satoshis) * 1000;
1806+
if pending_remote_value_msat - msg.amount_msat - remote_commit_tx_fee_msat < chan_reserve_msat {
1807+
return Err(ChannelError::Close("Remote HTLC add would put them under remote reserve value"));
1808+
}
1809+
1810+
if !self.channel_outbound {
1811+
// `+1` for this HTLC, `2 *` and `+1` fee spike buffer we keep for the remote (this deviates from the spec
1812+
// but should help protect us from stuck channels).
1813+
// Note that when we eventually remove support for fee updates and switch to anchor output fees,
1814+
// we will drop the `2 *`, since we no longer be as sensitive to fee spikes. But, keep the extra +1
1815+
// as we should still be able to afford adding this HTLC plus one more future HTLC, regardless of
1816+
// being sensitive to fee spikes.
1817+
let remote_fee_cost_incl_stuck_buffer_msat = 2 * self.next_remote_commit_tx_fee_msat(1 + 1);
1818+
if pending_remote_value_msat - msg.amount_msat - chan_reserve_msat < remote_fee_cost_incl_stuck_buffer_msat {
1819+
// Note that if the pending_forward_status is not updated here, then it's because we're already failing
1820+
// the HTLC, i.e. its status is already set to failing.
1821+
pending_forward_status = create_pending_htlc_status(self, pending_forward_status, 0x1000|7);
1822+
}
1823+
} else {
1824+
// Check that they won't violate our local required channel reserve by adding this HTLC.
1825+
1826+
// +1 for this HTLC.
1827+
let local_commit_tx_fee_msat = self.next_local_commit_tx_fee_msat(1);
1828+
if self.value_to_self_msat < self.local_channel_reserve_satoshis * 1000 + local_commit_tx_fee_msat {
1829+
return Err(ChannelError::Close("Cannot receive value that would put us under local channel reserve value"));
1830+
}
17211831
}
1832+
17221833
if self.next_remote_htlc_id != msg.htlc_id {
17231834
return Err(ChannelError::Close("Remote skipped HTLC ID"));
17241835
}
@@ -3044,6 +3155,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
30443155
res
30453156
},
30463157
their_max_htlc_value_in_flight_msat: self.their_max_htlc_value_in_flight_msat,
3158+
their_dust_limit_msat: self.their_dust_limit_satoshis * 1000,
30473159
}
30483160
}
30493161

@@ -3554,40 +3666,67 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
35543666
return Err(ChannelError::Ignore("Cannot send value that would put us over the max HTLC value in flight our peer will accept"));
35553667
}
35563668

3669+
if !self.channel_outbound {
3670+
// Check that we won't violate the remote channel reserve by adding this HTLC.
3671+
3672+
let remote_balance_msat = self.channel_value_satoshis * 1000 - self.value_to_self_msat;
3673+
let remote_chan_reserve_msat = Channel::<ChanSigner>::get_remote_channel_reserve_satoshis(self.channel_value_satoshis);
3674+
// 1 additional HTLC corresponding to this HTLC.
3675+
let remote_commit_tx_fee_msat = self.next_remote_commit_tx_fee_msat(1);
3676+
if remote_balance_msat < remote_chan_reserve_msat + remote_commit_tx_fee_msat {
3677+
return Err(ChannelError::Ignore("Cannot send value that would put them under remote channel reserve value"));
3678+
}
3679+
}
3680+
3681+
let pending_value_to_self_msat = self.value_to_self_msat - htlc_outbound_value_msat;
3682+
if pending_value_to_self_msat < amount_msat {
3683+
return Err(ChannelError::Ignore("Cannot send value that would overdraw remaining funds"));
3684+
}
3685+
3686+
// The `+1` is for the HTLC currently being added to the commitment tx and
3687+
// the `2 *` and `+1` are for the fee spike buffer.
3688+
let local_commit_tx_fee_msat = if self.channel_outbound {
3689+
2 * self.next_local_commit_tx_fee_msat(1 + 1)
3690+
} else { 0 };
3691+
if pending_value_to_self_msat - amount_msat < local_commit_tx_fee_msat {
3692+
return Err(ChannelError::Ignore("Cannot send value that would not leave enough to pay for fees"));
3693+
}
3694+
35573695
// Check self.local_channel_reserve_satoshis (the amount we must keep as
35583696
// reserve for the remote to have something to claim if we misbehave)
3559-
if self.value_to_self_msat < self.local_channel_reserve_satoshis * 1000 + amount_msat + htlc_outbound_value_msat {
3697+
let chan_reserve_msat = self.local_channel_reserve_satoshis * 1000;
3698+
if pending_value_to_self_msat - amount_msat - local_commit_tx_fee_msat < chan_reserve_msat {
35603699
return Err(ChannelError::Ignore("Cannot send value that would put us under local channel reserve value"));
35613700
}
35623701

35633702
// Now update local state:
35643703
if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32)) == (ChannelState::AwaitingRemoteRevoke as u32) {
35653704
self.holding_cell_htlc_updates.push(HTLCUpdateAwaitingACK::AddHTLC {
3566-
amount_msat: amount_msat,
3567-
payment_hash: payment_hash,
3568-
cltv_expiry: cltv_expiry,
3705+
amount_msat,
3706+
payment_hash,
3707+
cltv_expiry,
35693708
source,
3570-
onion_routing_packet: onion_routing_packet,
3709+
onion_routing_packet,
35713710
});
35723711
return Ok(None);
35733712
}
35743713

35753714
self.pending_outbound_htlcs.push(OutboundHTLCOutput {
35763715
htlc_id: self.next_local_htlc_id,
3577-
amount_msat: amount_msat,
3716+
amount_msat,
35783717
payment_hash: payment_hash.clone(),
3579-
cltv_expiry: cltv_expiry,
3718+
cltv_expiry,
35803719
state: OutboundHTLCState::LocalAnnounced(Box::new(onion_routing_packet.clone())),
35813720
source,
35823721
});
35833722

35843723
let res = msgs::UpdateAddHTLC {
35853724
channel_id: self.channel_id,
35863725
htlc_id: self.next_local_htlc_id,
3587-
amount_msat: amount_msat,
3588-
payment_hash: payment_hash,
3589-
cltv_expiry: cltv_expiry,
3590-
onion_routing_packet: onion_routing_packet,
3726+
amount_msat,
3727+
payment_hash,
3728+
cltv_expiry,
3729+
onion_routing_packet,
35913730
};
35923731
self.next_local_htlc_id += 1;
35933732

0 commit comments

Comments
 (0)