Skip to content

Commit f165fca

Browse files
Don't include below-dust inbound HTLCs in commit tx fee calculation
Also remove part of the holding cell channel reserve test that's newly failing but a bit of a redundant test anyway.
1 parent 773c2d1 commit f165fca

File tree

2 files changed

+208
-67
lines changed

2 files changed

+208
-67
lines changed

lightning/src/ln/channel.rs

Lines changed: 156 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,63 +1724,108 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
17241724
(COMMITMENT_TX_BASE_WEIGHT + num_htlcs as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC) * self.feerate_per_kw as u64 / 1000 * 1000
17251725
}
17261726

1727-
// Get the commitment tx fee for the local (i.e our) next commitment transaction
1728-
// based on the number of pending HTLCs that are on track to be in our next
1729-
// commitment tx. `addl_htcs` is an optional parameter allowing the caller
1730-
// to add a number of additional HTLCs to the calculation. Note that dust
1731-
// HTLCs are excluded.
1732-
fn next_local_commit_tx_fee_msat(&self, addl_htlcs: usize) -> u64 {
1727+
// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
1728+
// number of pending HTLCs that are on track to be in our next commitment tx, plus an additional
1729+
// HTLC if `fee_spike_buffer_htlc` is Some, plus a new HTLC given by `new_htlc_amount`. Dust HTLCs
1730+
// are excluded.
1731+
fn next_local_commit_tx_fee_msat(&self, new_htlc_amount: u64, locally_offered: bool, fee_spike_buffer_htlc: Option<()>) -> u64 {
17331732
assert!(self.channel_outbound);
17341733

1735-
let mut their_acked_htlcs = self.pending_inbound_htlcs.len();
1734+
let real_dust_limit_success_sat = (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
1735+
let real_dust_limit_timeout_sat = (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis;
1736+
1737+
let mut addl_htlcs = 0;
1738+
if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; }
1739+
if locally_offered && new_htlc_amount / 1000 >= real_dust_limit_timeout_sat {
1740+
addl_htlcs += 1;
1741+
} else if !locally_offered && new_htlc_amount / 1000 >= real_dust_limit_success_sat {
1742+
addl_htlcs += 1;
1743+
}
1744+
1745+
let mut included_htlcs = 0;
1746+
for ref htlc in self.pending_inbound_htlcs.iter() {
1747+
if htlc.amount_msat / 1000 < real_dust_limit_success_sat {
1748+
continue
1749+
}
1750+
// We include LocalRemoved HTLCs here because we may still need to broadcast a commitment
1751+
// transaction including this HTLC if it times out before they RAA.
1752+
included_htlcs += 1;
1753+
}
1754+
17361755
for ref htlc in self.pending_outbound_htlcs.iter() {
1737-
if htlc.amount_msat / 1000 <= self.holder_dust_limit_satoshis {
1756+
if htlc.amount_msat / 1000 < real_dust_limit_timeout_sat {
17381757
continue
17391758
}
17401759
match htlc.state {
1741-
OutboundHTLCState::Committed => their_acked_htlcs += 1,
1742-
OutboundHTLCState::RemoteRemoved {..} => their_acked_htlcs += 1,
1743-
OutboundHTLCState::LocalAnnounced {..} => their_acked_htlcs += 1,
1760+
OutboundHTLCState::LocalAnnounced {..} => included_htlcs += 1,
1761+
OutboundHTLCState::Committed => included_htlcs += 1,
1762+
OutboundHTLCState::RemoteRemoved {..} => included_htlcs += 1,
1763+
// We don't include AwaitingRemoteRevokeToRemove HTLCs because our next commitment
1764+
// transaction won't be generated until they send us their next RAA, which will mean
1765+
// dropping any HTLCs in this state.
17441766
_ => {},
17451767
}
17461768
}
17471769

17481770
for htlc in self.holding_cell_htlc_updates.iter() {
17491771
match htlc {
1750-
&HTLCUpdateAwaitingACK::AddHTLC { .. } => their_acked_htlcs += 1,
1751-
_ => {},
1772+
&HTLCUpdateAwaitingACK::AddHTLC { amount_msat, .. } => {
1773+
if amount_msat / 1000 < real_dust_limit_timeout_sat {
1774+
continue
1775+
}
1776+
included_htlcs += 1
1777+
},
1778+
_ => {}, // Don't include claims/fails that are awaiting ack, because once we get the
1779+
// ack we're guaranteed to never include them in commitment txs anymore.
17521780
}
17531781
}
17541782

1755-
self.commit_tx_fee_msat(their_acked_htlcs + addl_htlcs)
1783+
self.commit_tx_fee_msat(included_htlcs + addl_htlcs)
17561784
}
17571785

1758-
// Get the commitment tx fee for the remote's next commitment transaction
1759-
// based on the number of pending HTLCs that are on track to be in their
1760-
// next commitment tx. `addl_htcs` is an optional parameter allowing the caller
1761-
// to add a number of additional HTLCs to the calculation. Note that dust HTLCs
1762-
// are excluded.
1763-
fn next_remote_commit_tx_fee_msat(&self, addl_htlcs: usize) -> u64 {
1786+
// Get the commitment tx fee for the remote's next commitment transaction based on the number of
1787+
// pending HTLCs that are on track to be in their next commitment tx, plus an additional HTLC if
1788+
// `fee_spike_buffer_htlc` is Some, plus a new HTLC given by `new_htlc_amount`. Dust HTLCs are
1789+
// excluded.
1790+
fn next_remote_commit_tx_fee_msat(&self, new_htlc_amount: u64, locally_offered: bool, fee_spike_buffer_htlc: Option<()>) -> u64 {
17641791
assert!(!self.channel_outbound);
17651792

1766-
// When calculating the set of HTLCs which will be included in their next
1767-
// commitment_signed, all inbound HTLCs are included (as all states imply it will be
1768-
// included) and only committed outbound HTLCs, see below.
1769-
let mut their_acked_htlcs = self.pending_inbound_htlcs.len();
1793+
let real_dust_limit_success_sat = (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
1794+
let real_dust_limit_timeout_sat = (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
1795+
1796+
let mut addl_htlcs = 0;
1797+
if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; }
1798+
if locally_offered && new_htlc_amount / 1000 >= real_dust_limit_success_sat {
1799+
addl_htlcs += 1;
1800+
} else if !locally_offered && new_htlc_amount / 1000 >= real_dust_limit_timeout_sat {
1801+
addl_htlcs += 1;
1802+
}
1803+
1804+
// When calculating the set of HTLCs which will be included in their next commitment_signed, all
1805+
// non-dust inbound HTLCs are included (as all states imply it will be included) and only
1806+
// committed outbound HTLCs, see below.
1807+
let mut included_htlcs = 0;
1808+
for ref htlc in self.pending_inbound_htlcs.iter() {
1809+
if htlc.amount_msat / 1000 <= real_dust_limit_timeout_sat {
1810+
continue
1811+
}
1812+
included_htlcs += 1;
1813+
}
1814+
17701815
for ref htlc in self.pending_outbound_htlcs.iter() {
1771-
if htlc.amount_msat / 1000 <= self.counterparty_dust_limit_satoshis {
1816+
if htlc.amount_msat / 1000 <= real_dust_limit_success_sat {
17721817
continue
17731818
}
1774-
// We only include outbound HTLCs if it will not be included in their next
1775-
// commitment_signed, i.e. if they've responded to us with an RAA after announcement.
1819+
// We only include outbound HTLCs if it will not be included in their next commitment_signed,
1820+
// i.e. if they've responded to us with an RAA after announcement.
17761821
match htlc.state {
1777-
OutboundHTLCState::Committed => their_acked_htlcs += 1,
1778-
OutboundHTLCState::RemoteRemoved {..} => their_acked_htlcs += 1,
1822+
OutboundHTLCState::Committed => included_htlcs += 1,
1823+
OutboundHTLCState::RemoteRemoved {..} => included_htlcs += 1,
17791824
_ => {},
17801825
}
17811826
}
17821827

1783-
self.commit_tx_fee_msat(their_acked_htlcs + addl_htlcs)
1828+
self.commit_tx_fee_msat(included_htlcs + addl_htlcs)
17841829
}
17851830

17861831
pub fn update_add_htlc<F, L: Deref>(&mut self, msg: &msgs::UpdateAddHTLC, mut pending_forward_status: PendingHTLCStatus, create_pending_htlc_status: F, logger: &L) -> Result<(), ChannelError>
@@ -1848,8 +1893,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
18481893
// Check that the remote can afford to pay for this HTLC on-chain at the current
18491894
// feerate_per_kw, while maintaining their channel reserve (as required by the spec).
18501895
let remote_commit_tx_fee_msat = if self.channel_outbound { 0 } else {
1851-
// +1 for this HTLC.
1852-
self.next_remote_commit_tx_fee_msat(1)
1896+
self.next_remote_commit_tx_fee_msat(msg.amount_msat, false, None) // Don't include the extra fee spike buffer HTLC in calculations
18531897
};
18541898
if pending_remote_value_msat - msg.amount_msat < remote_commit_tx_fee_msat {
18551899
return Err(ChannelError::Close("Remote HTLC add would not leave enough to pay for fees".to_owned()));
@@ -1862,14 +1906,15 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
18621906
}
18631907

18641908
if !self.channel_outbound {
1865-
// `+1` for this HTLC, `2 *` and `+1` fee spike buffer we keep for the remote. This deviates from the
1866-
// spec because in the spec, the fee spike buffer requirement doesn't exist on the receiver's side,
1867-
// only on the sender's.
1868-
// Note that when we eventually remove support for fee updates and switch to anchor output fees,
1869-
// we will drop the `2 *`, since we no longer be as sensitive to fee spikes. But, keep the extra +1
1870-
// as we should still be able to afford adding this HTLC plus one more future HTLC, regardless of
1871-
// being sensitive to fee spikes.
1872-
let remote_fee_cost_incl_stuck_buffer_msat = 2 * self.next_remote_commit_tx_fee_msat(1 + 1);
1909+
// `2 *` and `Some(())` is for the fee spike buffer we keep for the remote. This deviates from
1910+
// the spec because in the spec, the fee spike buffer requirement doesn't exist on the
1911+
// receiver's side, only on the sender's.
1912+
// Note that when we eventually remove support for fee updates and switch to anchor output
1913+
// fees, we will drop the `2 *`, since we no longer be as sensitive to fee spikes. But, keep
1914+
// the extra htlc when calculating the next remote commitment transaction fee as we should
1915+
// still be able to afford adding this HTLC plus one more future HTLC, regardless of being
1916+
// sensitive to fee spikes.
1917+
let remote_fee_cost_incl_stuck_buffer_msat = 2 * self.next_remote_commit_tx_fee_msat(msg.amount_msat, false, Some(()));
18731918
if pending_remote_value_msat - msg.amount_msat - chan_reserve_msat < remote_fee_cost_incl_stuck_buffer_msat {
18741919
// Note that if the pending_forward_status is not updated here, then it's because we're already failing
18751920
// the HTLC, i.e. its status is already set to failing.
@@ -1878,9 +1923,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
18781923
}
18791924
} else {
18801925
// Check that they won't violate our local required channel reserve by adding this HTLC.
1881-
1882-
// +1 for this HTLC.
1883-
let local_commit_tx_fee_msat = self.next_local_commit_tx_fee_msat(1);
1926+
let local_commit_tx_fee_msat = self.next_local_commit_tx_fee_msat(msg.amount_msat, false, None);
18841927
if self.value_to_self_msat < self.counterparty_selected_channel_reserve_satoshis * 1000 + local_commit_tx_fee_msat {
18851928
return Err(ChannelError::Close("Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value".to_owned()));
18861929
}
@@ -3738,11 +3781,9 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
37383781

37393782
if !self.channel_outbound {
37403783
// Check that we won't violate the remote channel reserve by adding this HTLC.
3741-
37423784
let counterparty_balance_msat = self.channel_value_satoshis * 1000 - self.value_to_self_msat;
37433785
let holder_selected_chan_reserve_msat = Channel::<ChanSigner>::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis);
3744-
// 1 additional HTLC corresponding to this HTLC.
3745-
let counterparty_commit_tx_fee_msat = self.next_remote_commit_tx_fee_msat(1);
3786+
let counterparty_commit_tx_fee_msat = self.next_remote_commit_tx_fee_msat(amount_msat, true, None);
37463787
if counterparty_balance_msat < holder_selected_chan_reserve_msat + counterparty_commit_tx_fee_msat {
37473788
return Err(ChannelError::Ignore("Cannot send value that would put counterparty balance under holder-announced channel reserve value".to_owned()));
37483789
}
@@ -3753,10 +3794,9 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
37533794
return Err(ChannelError::Ignore(format!("Cannot send value that would overdraw remaining funds. Amount: {}, pending value to self {}", amount_msat, pending_value_to_self_msat)));
37543795
}
37553796

3756-
// The `+1` is for the HTLC currently being added to the commitment tx and
3757-
// the `2 *` and `+1` are for the fee spike buffer.
3797+
// `2 *` and extra HTLC are for the fee spike buffer.
37583798
let commit_tx_fee_msat = if self.channel_outbound {
3759-
2 * self.next_local_commit_tx_fee_msat(1 + 1)
3799+
2 * self.next_local_commit_tx_fee_msat(amount_msat, true, Some(()))
37603800
} else { 0 };
37613801
if pending_value_to_self_msat - amount_msat < commit_tx_fee_msat {
37623802
return Err(ChannelError::Ignore(format!("Cannot send value that would not leave enough to pay for fees. Pending value to self: {}. local_commit_tx_fee {}", pending_value_to_self_msat, commit_tx_fee_msat)));
@@ -4587,6 +4627,73 @@ mod tests {
45874627
assert_eq!(open_channel_msg.feerate_per_kw, original_fee);
45884628
}
45894629

4630+
#[test]
4631+
fn test_commit_tx_fees_with_dust() {
4632+
// Test that when calculating the local and remote commitment transaction fees, the correct
4633+
// dust limits are used.
4634+
let feeest = TestFeeEstimator{fee_est: 15000};
4635+
let secp_ctx = Secp256k1::new();
4636+
let seed = [42; 32];
4637+
let network = Network::Testnet;
4638+
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
4639+
4640+
// Go through the flow of opening a channel between two nodes, making sure
4641+
// they have different dust limits.
4642+
4643+
// Create Node A's channel pointing to Node B's pubkey
4644+
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
4645+
let config = UserConfig::default();
4646+
let mut node_a_chan = Channel::<EnforcingChannelKeys>::new_outbound(&&feeest, &&keys_provider, node_b_node_id, 10000000, 100000, 42, &config).unwrap();
4647+
4648+
// Create Node B's channel by receiving Node A's open_channel message
4649+
// Make sure A's dust limit is as we expect.
4650+
let open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash());
4651+
assert_eq!(open_channel_msg.dust_limit_satoshis, 1560);
4652+
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
4653+
let node_b_chan = Channel::<EnforcingChannelKeys>::new_from_req(&&feeest, &&keys_provider, node_b_node_id, InitFeatures::known(), &open_channel_msg, 7, &config).unwrap();
4654+
4655+
// Node B --> Node A: accept channel, explicitly setting B's dust limit.
4656+
let mut accept_channel_msg = node_b_chan.get_accept_channel();
4657+
accept_channel_msg.dust_limit_satoshis = 546;
4658+
node_a_chan.accept_channel(&accept_channel_msg, &config, InitFeatures::known()).unwrap();
4659+
4660+
// Put some inbound and outbound HTLCs in A's channel.
4661+
let htlc_amount_msat = 11_092_000; // put an amount below A's effective dust amount but above B's.
4662+
node_a_chan.pending_inbound_htlcs.push(InboundHTLCOutput {
4663+
htlc_id: 0,
4664+
amount_msat: htlc_amount_msat,
4665+
payment_hash: PaymentHash(Sha256::hash(&[42; 32]).into_inner()),
4666+
cltv_expiry: 300000000,
4667+
state: InboundHTLCState::Committed,
4668+
});
4669+
4670+
node_a_chan.pending_outbound_htlcs.push(OutboundHTLCOutput {
4671+
htlc_id: 1,
4672+
amount_msat: htlc_amount_msat, // put an amount below A's dust amount but above B's.
4673+
payment_hash: PaymentHash(Sha256::hash(&[43; 32]).into_inner()),
4674+
cltv_expiry: 200000000,
4675+
state: OutboundHTLCState::Committed,
4676+
source: HTLCSource::OutboundRoute {
4677+
path: Vec::new(),
4678+
session_priv: SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
4679+
first_hop_htlc_msat: 548,
4680+
}
4681+
});
4682+
4683+
// Make sure when Node A calculates their local commitment transaction, none of the HTLCs pass
4684+
// the dust limit check.
4685+
let local_commit_tx_fee = node_a_chan.next_local_commit_tx_fee_msat(htlc_amount_msat, true, None);
4686+
let local_commit_fee_0_htlcs = node_a_chan.commit_tx_fee_msat(0);
4687+
assert_eq!(local_commit_tx_fee, local_commit_fee_0_htlcs);
4688+
4689+
// Finally, make sure that when Node A calculates the remote's commitment transaction fees, all
4690+
// of the HTLCs are seen to be above the dust limit.
4691+
node_a_chan.channel_outbound = false;
4692+
let remote_commit_fee_3_htlcs = node_a_chan.commit_tx_fee_msat(3);
4693+
let remote_commit_tx_fee = node_a_chan.next_remote_commit_tx_fee_msat(htlc_amount_msat, true, None);
4694+
assert_eq!(remote_commit_tx_fee, remote_commit_fee_3_htlcs);
4695+
}
4696+
45904697
#[test]
45914698
fn channel_reestablish_no_updates() {
45924699
let feeest = TestFeeEstimator{fee_est: 15000};

0 commit comments

Comments
 (0)