Skip to content

Commit 2e78346

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 b5f80c2 commit 2e78346

File tree

2 files changed

+424
-90
lines changed

2 files changed

+424
-90
lines changed

lightning/src/ln/channel.rs

Lines changed: 148 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 {
@@ -1665,6 +1666,73 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
16651666
cmp::min(self.value_to_self_msat as i64 - self.get_outbound_pending_htlc_stats().1 as i64, 0) as u64)
16661667
}
16671668

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

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

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+
35613695
// Check self.local_channel_reserve_satoshis (the amount we must keep as
35623696
// reserve for the remote to have something to claim if we misbehave)
3563-
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 {
35643699
return Err(ChannelError::Ignore("Cannot send value that would put us under local channel reserve value"));
35653700
}
35663701

35673702
// Now update local state:
35683703
if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32)) == (ChannelState::AwaitingRemoteRevoke as u32) {
35693704
self.holding_cell_htlc_updates.push(HTLCUpdateAwaitingACK::AddHTLC {
3570-
amount_msat: amount_msat,
3571-
payment_hash: payment_hash,
3572-
cltv_expiry: cltv_expiry,
3705+
amount_msat,
3706+
payment_hash,
3707+
cltv_expiry,
35733708
source,
3574-
onion_routing_packet: onion_routing_packet,
3709+
onion_routing_packet,
35753710
});
35763711
return Ok(None);
35773712
}
35783713

35793714
self.pending_outbound_htlcs.push(OutboundHTLCOutput {
35803715
htlc_id: self.next_local_htlc_id,
3581-
amount_msat: amount_msat,
3716+
amount_msat,
35823717
payment_hash: payment_hash.clone(),
3583-
cltv_expiry: cltv_expiry,
3718+
cltv_expiry,
35843719
state: OutboundHTLCState::LocalAnnounced(Box::new(onion_routing_packet.clone())),
35853720
source,
35863721
});
35873722

35883723
let res = msgs::UpdateAddHTLC {
35893724
channel_id: self.channel_id,
35903725
htlc_id: self.next_local_htlc_id,
3591-
amount_msat: amount_msat,
3592-
payment_hash: payment_hash,
3593-
cltv_expiry: cltv_expiry,
3594-
onion_routing_packet: onion_routing_packet,
3726+
amount_msat,
3727+
payment_hash,
3728+
cltv_expiry,
3729+
onion_routing_packet,
35953730
};
35963731
self.next_local_htlc_id += 1;
35973732

0 commit comments

Comments
 (0)