Skip to content

Commit 15a92be

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 15a92be

File tree

2 files changed

+167
-44
lines changed

2 files changed

+167
-44
lines changed

lightning/src/ln/channel.rs

Lines changed: 115 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,10 +1729,23 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
17291729
// commitment tx. `addl_htcs` is an optional parameter allowing the caller
17301730
// to add a number of additional HTLCs to the calculation. Note that dust
17311731
// HTLCs are excluded.
1732-
fn next_local_commit_tx_fee_msat(&self, addl_htlcs: usize) -> u64 {
1732+
fn next_local_commit_tx_fee_msat(&self, new_htlc_amount: u64, fee_spike_buffer_htlc: Option<()>) -> u64 {
17331733
assert!(self.channel_outbound);
17341734

1735-
let mut their_acked_htlcs = self.pending_inbound_htlcs.len();
1735+
let mut addl_htlcs = 1;
1736+
if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; }
1737+
if new_htlc_amount / 1000 <= self.holder_dust_limit_satoshis {
1738+
addl_htlcs -= 1;
1739+
}
1740+
1741+
let mut their_acked_htlcs = 0;
1742+
for ref htlc in self.pending_inbound_htlcs.iter() {
1743+
if htlc.amount_msat / 1000 <= self.holder_dust_limit_satoshis {
1744+
continue
1745+
}
1746+
their_acked_htlcs += 1;
1747+
}
1748+
17361749
for ref htlc in self.pending_outbound_htlcs.iter() {
17371750
if htlc.amount_msat / 1000 <= self.holder_dust_limit_satoshis {
17381751
continue
@@ -1760,13 +1773,27 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
17601773
// next commitment tx. `addl_htcs` is an optional parameter allowing the caller
17611774
// to add a number of additional HTLCs to the calculation. Note that dust HTLCs
17621775
// are excluded.
1763-
fn next_remote_commit_tx_fee_msat(&self, addl_htlcs: usize) -> u64 {
1776+
fn next_remote_commit_tx_fee_msat(&self, new_htlc_amount: u64, fee_spike_buffer_htlc: Option<()>) -> u64 {
17641777
assert!(!self.channel_outbound);
17651778

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();
1779+
let mut addl_htlcs = 1;
1780+
if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; }
1781+
if new_htlc_amount / 1000 <= self.counterparty_dust_limit_satoshis {
1782+
addl_htlcs -= 1;
1783+
}
1784+
1785+
// When calculating the set of HTLCs which will be included in their next commitment_signed, all
1786+
// non-dust inbound HTLCs are included (as all states imply it will be included) and only
1787+
// committed outbound HTLCs, see below.
1788+
// let mut their_acked_htlcs = self.pending_inbound_htlcs.len();
1789+
let mut their_acked_htlcs = 0;
1790+
for ref htlc in self.pending_inbound_htlcs.iter() {
1791+
if htlc.amount_msat / 1000 <= self.counterparty_dust_limit_satoshis {
1792+
continue
1793+
}
1794+
their_acked_htlcs += 1;
1795+
}
1796+
17701797
for ref htlc in self.pending_outbound_htlcs.iter() {
17711798
if htlc.amount_msat / 1000 <= self.counterparty_dust_limit_satoshis {
17721799
continue
@@ -1848,8 +1875,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
18481875
// Check that the remote can afford to pay for this HTLC on-chain at the current
18491876
// feerate_per_kw, while maintaining their channel reserve (as required by the spec).
18501877
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)
1878+
self.next_remote_commit_tx_fee_msat(msg.amount_msat, None) // Don't include the extra fee spike buffer HTLC in calculations
18531879
};
18541880
if pending_remote_value_msat - msg.amount_msat < remote_commit_tx_fee_msat {
18551881
return Err(ChannelError::Close("Remote HTLC add would not leave enough to pay for fees".to_owned()));
@@ -1862,14 +1888,15 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
18621888
}
18631889

18641890
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);
1891+
// `2 *` and `Some(())` is for the fee spike buffer we keep for the remote. This deviates from
1892+
// the spec because in the spec, the fee spike buffer requirement doesn't exist on the
1893+
// receiver's side, only on the sender's.
1894+
// Note that when we eventually remove support for fee updates and switch to anchor output
1895+
// fees, we will drop the `2 *`, since we no longer be as sensitive to fee spikes. But, keep
1896+
// the extra htlc when calculating the next remote commitment transaction fee as we should
1897+
// still be able to afford adding this HTLC plus one more future HTLC, regardless of being
1898+
// sensitive to fee spikes.
1899+
let remote_fee_cost_incl_stuck_buffer_msat = 2 * self.next_remote_commit_tx_fee_msat(msg.amount_msat, Some(()));
18731900
if pending_remote_value_msat - msg.amount_msat - chan_reserve_msat < remote_fee_cost_incl_stuck_buffer_msat {
18741901
// Note that if the pending_forward_status is not updated here, then it's because we're already failing
18751902
// the HTLC, i.e. its status is already set to failing.
@@ -1878,9 +1905,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
18781905
}
18791906
} else {
18801907
// 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);
1908+
let local_commit_tx_fee_msat = self.next_local_commit_tx_fee_msat(msg.amount_msat, None);
18841909
if self.value_to_self_msat < self.counterparty_selected_channel_reserve_satoshis * 1000 + local_commit_tx_fee_msat {
18851910
return Err(ChannelError::Close("Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value".to_owned()));
18861911
}
@@ -3738,11 +3763,9 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
37383763

37393764
if !self.channel_outbound {
37403765
// Check that we won't violate the remote channel reserve by adding this HTLC.
3741-
37423766
let counterparty_balance_msat = self.channel_value_satoshis * 1000 - self.value_to_self_msat;
37433767
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);
3768+
let counterparty_commit_tx_fee_msat = self.next_remote_commit_tx_fee_msat(amount_msat, None);
37463769
if counterparty_balance_msat < holder_selected_chan_reserve_msat + counterparty_commit_tx_fee_msat {
37473770
return Err(ChannelError::Ignore("Cannot send value that would put counterparty balance under holder-announced channel reserve value".to_owned()));
37483771
}
@@ -3753,10 +3776,9 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
37533776
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)));
37543777
}
37553778

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.
3779+
// `2 *` and extra HTLC are for the fee spike buffer.
37583780
let commit_tx_fee_msat = if self.channel_outbound {
3759-
2 * self.next_local_commit_tx_fee_msat(1 + 1)
3781+
2 * self.next_local_commit_tx_fee_msat(amount_msat, Some(()))
37603782
} else { 0 };
37613783
if pending_value_to_self_msat - amount_msat < commit_tx_fee_msat {
37623784
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 +4609,73 @@ mod tests {
45874609
assert_eq!(open_channel_msg.feerate_per_kw, original_fee);
45884610
}
45894611

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

lightning/src/ln/functional_tests.rs

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,7 +1764,7 @@ fn test_chan_reserve_violation_outbound_htlc_inbound_chan() {
17641764
}}
17651765
};
17661766

1767-
let (route, our_payment_hash, _) = get_route_and_payment_hash!(1000);
1767+
let (route, our_payment_hash, _) = get_route_and_payment_hash!(1001000);
17681768
unwrap_send_err!(nodes[1].node.send_payment(&route, our_payment_hash, &None), true, APIError::ChannelUnavailable { ref err },
17691769
assert_eq!(err, "Cannot send value that would put counterparty balance under holder-announced channel reserve value"));
17701770
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
@@ -1822,6 +1822,57 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
18221822
check_added_monitors!(nodes[0], 1);
18231823
}
18241824

1825+
#[test]
1826+
fn test_chan_reserve_dust_inbound_htlcs_outbound_chan() {
1827+
// Test that if we receive many dust HTLCs over an outbound channel, they don't count when
1828+
// calculating our commitment transaction fee (this was previously broken).
1829+
let chanmon_cfgs = create_chanmon_cfgs(2);
1830+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1831+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None, None]);
1832+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1833+
1834+
// Set nodes[0]'s balance such that they will consider any above-dust received HTLC to be a
1835+
// channel reserve violation (so their balance is channel reserve (1000 sats) + commitment
1836+
// transaction fee with 0 HTLCs (183 sats)).
1837+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 98817000, InitFeatures::known(), InitFeatures::known());
1838+
1839+
let dust_amt = 546000; // Dust amount
1840+
// In the previous code, routing this dust payment would cause nodes[0] to perceive a channel
1841+
// reserve violation even though it's a dust HTLC and therefore shouldn't count towards the
1842+
// commitment transaction fee.
1843+
let (_, _) = route_payment(&nodes[1], &[&nodes[0]], dust_amt);
1844+
}
1845+
1846+
#[test]
1847+
fn test_chan_reserve_dust_inbound_htlcs_inbound_chan() {
1848+
// Test that if we receive many dust HTLCs over an inbound channel, they don't count when
1849+
// calculating our counterparty's commitment transaction fee (this was previously broken).
1850+
let chanmon_cfgs = create_chanmon_cfgs(2);
1851+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1852+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None, None]);
1853+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1854+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 98000000, InitFeatures::known(), InitFeatures::known());
1855+
1856+
let payment_amt = 46000; // Dust amount
1857+
// In the previous code, these first four payments would succeed.
1858+
let (_, _) = route_payment(&nodes[0], &[&nodes[1]], payment_amt);
1859+
let (_, _) = route_payment(&nodes[0], &[&nodes[1]], payment_amt);
1860+
let (_, _) = route_payment(&nodes[0], &[&nodes[1]], payment_amt);
1861+
let (_, _) = route_payment(&nodes[0], &[&nodes[1]], payment_amt);
1862+
1863+
// Then these next 5 would be interpreted by nodes[1] as violating the fee spike buffer.
1864+
let (_, _) = route_payment(&nodes[0], &[&nodes[1]], payment_amt);
1865+
let (_, _) = route_payment(&nodes[0], &[&nodes[1]], payment_amt);
1866+
let (_, _) = route_payment(&nodes[0], &[&nodes[1]], payment_amt);
1867+
let (_, _) = route_payment(&nodes[0], &[&nodes[1]], payment_amt);
1868+
let (_, _) = route_payment(&nodes[0], &[&nodes[1]], payment_amt);
1869+
1870+
// And this last payment previously resulted in nodes[1] closing on its inbound-channel
1871+
// counterparty, because it counted all the previous dust HTLCs against nodes[0]'s commitment
1872+
// transaction fee and therefore perceived this next payment as a channel reserve violation.
1873+
let (_, _) = route_payment(&nodes[0], &[&nodes[1]], payment_amt);
1874+
}
1875+
18251876
#[test]
18261877
fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
18271878
let chanmon_cfgs = create_chanmon_cfgs(3);
@@ -2133,23 +2184,6 @@ fn test_channel_reserve_holding_cell_htlcs() {
21332184

21342185
let commit_tx_fee_0_htlcs = 2*commit_tx_fee_msat(feerate, 1);
21352186
let recv_value_3 = commit_tx_fee_2_htlcs - commit_tx_fee_0_htlcs - total_fee_msat;
2136-
{
2137-
let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value_3 + 1);
2138-
let err = nodes[0].node.send_payment(&route, our_payment_hash, &None).err().unwrap();
2139-
match err {
2140-
PaymentSendFailure::AllFailedRetrySafe(ref fails) => {
2141-
match &fails[0] {
2142-
&APIError::ChannelUnavailable{ref err} =>
2143-
assert!(regex::Regex::new(r"Cannot send value that would put our balance under counterparty-announced channel reserve value \(\d+\)").unwrap().is_match(err)),
2144-
_ => panic!("Unexpected error variant"),
2145-
}
2146-
},
2147-
_ => panic!("Unexpected error variant"),
2148-
}
2149-
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
2150-
nodes[0].logger.assert_log_contains("lightning::ln::channelmanager".to_string(), "Cannot send value that would put our balance under counterparty-announced channel reserve value".to_string(), 3);
2151-
}
2152-
21532187
send_payment(&nodes[0], &vec![&nodes[1], &nodes[2]][..], recv_value_3, recv_value_3);
21542188

21552189
let commit_tx_fee_1_htlc = 2*commit_tx_fee_msat(feerate, 1 + 1);

0 commit comments

Comments
 (0)