Skip to content

Commit f17770a

Browse files
valentinewallaceTheBlueMatt
authored andcommitted
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 lightningdevkit#740. Co-authored-by: Matt Corallo <[email protected]> Co-authored-by: Valentine Wallace <[email protected]>
1 parent 0af046c commit f17770a

File tree

2 files changed

+431
-90
lines changed

2 files changed

+431
-90
lines changed

lightning/src/ln/channel.rs

Lines changed: 155 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 {
@@ -1687,6 +1688,73 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
16871688
cmp::min(self.value_to_self_msat as i64 - self.get_outbound_pending_htlc_stats().1 as i64, 0) as u64)
16881689
}
16891690

1691+
// Get the fee cost of a commitment tx with a given number of HTLC outputs.
1692+
// Note that num_htlcs should not include dust HTLCs.
1693+
fn commit_tx_fee_msat(&self, num_htlcs: usize) -> u64 {
1694+
// Note that we need to divide before multiplying to round properly,
1695+
// since the lowest denomination of bitcoin on-chain is the satoshi.
1696+
(COMMITMENT_TX_BASE_WEIGHT + num_htlcs as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC) * self.feerate_per_kw / 1000 * 1000
1697+
}
1698+
1699+
// Get the commitment tx fee for the local (i.e our) next commitment transaction
1700+
// based on the number of pending HTLCs that are on track to be in our next
1701+
// commitment tx. `addl_htcs` is an optional parameter allowing the caller
1702+
// to add a number of additional HTLCs to the calculation. Note that dust
1703+
// HTLCs are excluded.
1704+
fn next_local_commit_tx_fee_msat(&self, addl_htlcs: usize) -> u64 {
1705+
assert!(self.channel_outbound);
1706+
1707+
let mut their_acked_htlcs = self.pending_inbound_htlcs.len();
1708+
for ref htlc in self.pending_outbound_htlcs.iter() {
1709+
if htlc.amount_msat / 1000 <= self.our_dust_limit_satoshis {
1710+
continue
1711+
}
1712+
match htlc.state {
1713+
OutboundHTLCState::Committed => their_acked_htlcs += 1,
1714+
OutboundHTLCState::RemoteRemoved {..} => their_acked_htlcs += 1,
1715+
OutboundHTLCState::LocalAnnounced {..} => their_acked_htlcs += 1,
1716+
_ => {},
1717+
}
1718+
}
1719+
1720+
for htlc in self.holding_cell_htlc_updates.iter() {
1721+
match htlc {
1722+
&HTLCUpdateAwaitingACK::AddHTLC { .. } => their_acked_htlcs += 1,
1723+
_ => {},
1724+
}
1725+
}
1726+
1727+
self.commit_tx_fee_msat(their_acked_htlcs + addl_htlcs)
1728+
}
1729+
1730+
// Get the commitment tx fee for the remote's next commitment transaction
1731+
// based on the number of pending HTLCs that are on track to be in their
1732+
// next commitment tx. `addl_htcs` is an optional parameter allowing the caller
1733+
// to add a number of additional HTLCs to the calculation. Note that dust HTLCs
1734+
// are excluded.
1735+
fn next_remote_commit_tx_fee_msat(&self, addl_htlcs: usize) -> u64 {
1736+
assert!(!self.channel_outbound);
1737+
1738+
// When calculating the set of HTLCs which will be included in their next
1739+
// commitment_signed, all inbound HTLCs are included (as all states imply it will be
1740+
// included) and only committed outbound HTLCs, see below.
1741+
let mut their_acked_htlcs = self.pending_inbound_htlcs.len();
1742+
for ref htlc in self.pending_outbound_htlcs.iter() {
1743+
if htlc.amount_msat / 1000 <= self.their_dust_limit_satoshis {
1744+
continue
1745+
}
1746+
// We only include outbound HTLCs if it will not be included in their next
1747+
// commitment_signed, i.e. if they've responded to us with an RAA after announcement.
1748+
match htlc.state {
1749+
OutboundHTLCState::Committed => their_acked_htlcs += 1,
1750+
OutboundHTLCState::RemoteRemoved {..} => their_acked_htlcs += 1,
1751+
_ => {},
1752+
}
1753+
}
1754+
1755+
self.commit_tx_fee_msat(their_acked_htlcs + addl_htlcs)
1756+
}
1757+
16901758
pub fn update_add_htlc<F>(&mut self, msg: &msgs::UpdateAddHTLC, mut pending_forward_status: PendingHTLCStatus, create_pending_htlc_status: F) -> Result<(), ChannelError>
16911759
where F: for<'a> Fn(&'a Self, PendingHTLCStatus, u16) -> PendingHTLCStatus {
16921760
// We can't accept HTLCs sent after we've sent a shutdown.
@@ -1740,9 +1808,55 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
17401808
removed_outbound_total_msat += htlc.amount_msat;
17411809
}
17421810
}
1743-
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 {
1744-
return Err(ChannelError::Close("Remote HTLC add would put them under their reserve value"));
1811+
1812+
let pending_value_to_self_msat =
1813+
self.value_to_self_msat + htlc_inbound_value_msat - removed_outbound_total_msat;
1814+
let pending_remote_value_msat =
1815+
self.channel_value_satoshis * 1000 - pending_value_to_self_msat;
1816+
if pending_remote_value_msat < msg.amount_msat {
1817+
return Err(ChannelError::Close("Remote HTLC add would overdraw remaining funds"));
1818+
}
1819+
1820+
// Check that the remote can afford to pay for this HTLC on-chain at the current
1821+
// feerate_per_kw, while maintaining their channel reserve (as required by the spec).
1822+
let remote_commit_tx_fee_msat = if self.channel_outbound { 0 } else {
1823+
// +1 for this HTLC.
1824+
self.next_remote_commit_tx_fee_msat(1)
1825+
};
1826+
if pending_remote_value_msat - msg.amount_msat < remote_commit_tx_fee_msat {
1827+
return Err(ChannelError::Close("Remote HTLC add would not leave enough to pay for fees"));
1828+
};
1829+
1830+
let chan_reserve_msat =
1831+
Channel::<ChanSigner>::get_remote_channel_reserve_satoshis(self.channel_value_satoshis) * 1000;
1832+
if pending_remote_value_msat - msg.amount_msat - remote_commit_tx_fee_msat < chan_reserve_msat {
1833+
return Err(ChannelError::Close("Remote HTLC add would put them under remote reserve value"));
1834+
}
1835+
1836+
if !self.channel_outbound {
1837+
// `+1` for this HTLC, `2 *` and `+1` fee spike buffer we keep for the remote. This deviates from the
1838+
// spec because in the spec, the fee spike buffer requirement doesn't exist on the receiver's side,
1839+
// only on the sender's.
1840+
// Note that when we eventually remove support for fee updates and switch to anchor output fees,
1841+
// we will drop the `2 *`, since we no longer be as sensitive to fee spikes. But, keep the extra +1
1842+
// as we should still be able to afford adding this HTLC plus one more future HTLC, regardless of
1843+
// being sensitive to fee spikes.
1844+
let remote_fee_cost_incl_stuck_buffer_msat = 2 * self.next_remote_commit_tx_fee_msat(1 + 1);
1845+
if pending_remote_value_msat - msg.amount_msat - chan_reserve_msat < remote_fee_cost_incl_stuck_buffer_msat {
1846+
// Note that if the pending_forward_status is not updated here, then it's because we're already failing
1847+
// the HTLC, i.e. its status is already set to failing.
1848+
pending_forward_status = create_pending_htlc_status(self, pending_forward_status, 0x1000|7);
1849+
}
1850+
} else {
1851+
// Check that they won't violate our local required channel reserve by adding this HTLC.
1852+
1853+
// +1 for this HTLC.
1854+
let local_commit_tx_fee_msat = self.next_local_commit_tx_fee_msat(1);
1855+
if self.value_to_self_msat < self.local_channel_reserve_satoshis * 1000 + local_commit_tx_fee_msat {
1856+
return Err(ChannelError::Close("Cannot receive value that would put us under local channel reserve value"));
1857+
}
17451858
}
1859+
17461860
if self.next_remote_htlc_id != msg.htlc_id {
17471861
return Err(ChannelError::Close("Remote skipped HTLC ID"));
17481862
}
@@ -3068,6 +3182,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
30683182
res
30693183
},
30703184
their_max_htlc_value_in_flight_msat: self.their_max_htlc_value_in_flight_msat,
3185+
their_dust_limit_msat: self.their_dust_limit_satoshis * 1000,
30713186
}
30723187
}
30733188

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

3696+
if !self.channel_outbound {
3697+
// Check that we won't violate the remote channel reserve by adding this HTLC.
3698+
3699+
let remote_balance_msat = self.channel_value_satoshis * 1000 - self.value_to_self_msat;
3700+
let remote_chan_reserve_msat = Channel::<ChanSigner>::get_remote_channel_reserve_satoshis(self.channel_value_satoshis);
3701+
// 1 additional HTLC corresponding to this HTLC.
3702+
let remote_commit_tx_fee_msat = self.next_remote_commit_tx_fee_msat(1);
3703+
if remote_balance_msat < remote_chan_reserve_msat + remote_commit_tx_fee_msat {
3704+
return Err(ChannelError::Ignore("Cannot send value that would put them under remote channel reserve value"));
3705+
}
3706+
}
3707+
3708+
let pending_value_to_self_msat = self.value_to_self_msat - htlc_outbound_value_msat;
3709+
if pending_value_to_self_msat < amount_msat {
3710+
return Err(ChannelError::Ignore("Cannot send value that would overdraw remaining funds"));
3711+
}
3712+
3713+
// The `+1` is for the HTLC currently being added to the commitment tx and
3714+
// the `2 *` and `+1` are for the fee spike buffer.
3715+
let local_commit_tx_fee_msat = if self.channel_outbound {
3716+
2 * self.next_local_commit_tx_fee_msat(1 + 1)
3717+
} else { 0 };
3718+
if pending_value_to_self_msat - amount_msat < local_commit_tx_fee_msat {
3719+
return Err(ChannelError::Ignore("Cannot send value that would not leave enough to pay for fees"));
3720+
}
3721+
35813722
// Check self.local_channel_reserve_satoshis (the amount we must keep as
35823723
// reserve for the remote to have something to claim if we misbehave)
3583-
if self.value_to_self_msat < self.local_channel_reserve_satoshis * 1000 + amount_msat + htlc_outbound_value_msat {
3724+
let chan_reserve_msat = self.local_channel_reserve_satoshis * 1000;
3725+
if pending_value_to_self_msat - amount_msat - local_commit_tx_fee_msat < chan_reserve_msat {
35843726
return Err(ChannelError::Ignore("Cannot send value that would put us under local channel reserve value"));
35853727
}
35863728

35873729
// Now update local state:
35883730
if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32)) == (ChannelState::AwaitingRemoteRevoke as u32) {
35893731
self.holding_cell_htlc_updates.push(HTLCUpdateAwaitingACK::AddHTLC {
3590-
amount_msat: amount_msat,
3591-
payment_hash: payment_hash,
3592-
cltv_expiry: cltv_expiry,
3732+
amount_msat,
3733+
payment_hash,
3734+
cltv_expiry,
35933735
source,
3594-
onion_routing_packet: onion_routing_packet,
3736+
onion_routing_packet,
35953737
});
35963738
return Ok(None);
35973739
}
35983740

35993741
self.pending_outbound_htlcs.push(OutboundHTLCOutput {
36003742
htlc_id: self.next_local_htlc_id,
3601-
amount_msat: amount_msat,
3743+
amount_msat,
36023744
payment_hash: payment_hash.clone(),
3603-
cltv_expiry: cltv_expiry,
3745+
cltv_expiry,
36043746
state: OutboundHTLCState::LocalAnnounced(Box::new(onion_routing_packet.clone())),
36053747
source,
36063748
});
36073749

36083750
let res = msgs::UpdateAddHTLC {
36093751
channel_id: self.channel_id,
36103752
htlc_id: self.next_local_htlc_id,
3611-
amount_msat: amount_msat,
3612-
payment_hash: payment_hash,
3613-
cltv_expiry: cltv_expiry,
3614-
onion_routing_packet: onion_routing_packet,
3753+
amount_msat,
3754+
payment_hash,
3755+
cltv_expiry,
3756+
onion_routing_packet,
36153757
};
36163758
self.next_local_htlc_id += 1;
36173759

0 commit comments

Comments
 (0)