Skip to content

Commit 306e9a5

Browse files
committed
Fix channel reserve calculation on the sending side
As the variable name implies holder_selected_chan_reserve_msat is intended to be in millisatoshis, but is instead calculated in satoshis. We fix that error here and update the relevant tests to more accurately calculate the expected reserve value and test both success and failure cases. Bug discovered by chanmon_consistency fuzz target.
1 parent afae12e commit 306e9a5

File tree

3 files changed

+33
-8
lines changed

3 files changed

+33
-8
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ fn check_api_err(api_err: APIError) {
241241
_ if err.starts_with("Cannot push more than their max accepted HTLCs ") => {},
242242
_ if err.starts_with("Cannot send value that would put us over the max HTLC value in flight our peer will accept ") => {},
243243
_ if err.starts_with("Cannot send value that would put our balance under counterparty-announced channel reserve value") => {},
244+
_ if err.starts_with("Cannot send value that would put counterparty balance under holder-announced channel reserve value") => {},
244245
_ if err.starts_with("Cannot send value that would overdraw remaining funds.") => {},
245246
_ if err.starts_with("Cannot send value that would not leave enough to pay for fees.") => {},
246247
_ => panic!("{}", err),

lightning/src/ln/channel.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4087,7 +4087,7 @@ impl<Signer: Sign> Channel<Signer> {
40874087
if !self.is_outbound() {
40884088
// Check that we won't violate the remote channel reserve by adding this HTLC.
40894089
let counterparty_balance_msat = self.channel_value_satoshis * 1000 - self.value_to_self_msat;
4090-
let holder_selected_chan_reserve_msat = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis);
4090+
let holder_selected_chan_reserve_msat = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis) * 1000;
40914091
let htlc_candidate = HTLCCandidate::new(amount_msat, HTLCInitiator::LocalOffered);
40924092
let counterparty_commit_tx_fee_msat = self.next_remote_commit_tx_fee_msat(htlc_candidate, None);
40934093
if counterparty_balance_msat < holder_selected_chan_reserve_msat + counterparty_commit_tx_fee_msat {

lightning/src/ln/functional_tests.rs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use ln::channel::{COMMITMENT_TX_BASE_WEIGHT, COMMITMENT_TX_WEIGHT_PER_HTLC};
2222
use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA};
2323
use ln::channel::{Channel, ChannelError};
2424
use ln::{chan_utils, onion_utils};
25+
use ln::chan_utils::HTLC_SUCCESS_TX_WEIGHT;
2526
use routing::router::{Route, RouteHop, RouteHint, RouteHintHop, get_route};
2627
use routing::network_graph::RoutingFees;
2728
use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
@@ -1700,14 +1701,24 @@ fn test_chan_reserve_violation_outbound_htlc_inbound_chan() {
17001701
// sending any above-dust amount would result in a channel reserve violation.
17011702
// In this test we check that we would be prevented from sending an HTLC in
17021703
// this situation.
1703-
chanmon_cfgs[0].fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(6000) };
1704-
chanmon_cfgs[1].fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(6000) };
1704+
let feerate_per_kw = 253;
1705+
chanmon_cfgs[0].fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(feerate_per_kw) };
1706+
chanmon_cfgs[1].fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(feerate_per_kw) };
17051707
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
17061708
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
17071709
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1708-
let _ = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 95000000, InitFeatures::known(), InitFeatures::known());
17091710

1710-
let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 4843000);
1711+
let mut push_amt = 100_000_000;
1712+
push_amt -= feerate_per_kw as u64 * (COMMITMENT_TX_BASE_WEIGHT + COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000 * 1000;
1713+
push_amt -= Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100_000) * 1000;
1714+
1715+
let _ = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, push_amt, InitFeatures::known(), InitFeatures::known());
1716+
1717+
// Sending exactly enough to hit the reserve amount should be accepted
1718+
let (_, _, _) = route_payment(&nodes[1], &[&nodes[0]], 1_000_000);
1719+
1720+
// However one more HTLC should be significantly over the reserve amount and fail.
1721+
let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 1_000_000);
17111722
unwrap_send_err!(nodes[1].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)), true, APIError::ChannelUnavailable { ref err },
17121723
assert_eq!(err, "Cannot send value that would put counterparty balance under holder-announced channel reserve value"));
17131724
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
@@ -1759,21 +1770,34 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
17591770
fn test_chan_reserve_dust_inbound_htlcs_outbound_chan() {
17601771
// Test that if we receive many dust HTLCs over an outbound channel, they don't count when
17611772
// calculating our commitment transaction fee (this was previously broken).
1762-
let chanmon_cfgs = create_chanmon_cfgs(2);
1773+
let mut chanmon_cfgs = create_chanmon_cfgs(2);
1774+
let feerate_per_kw = 253;
1775+
chanmon_cfgs[0].fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(feerate_per_kw) };
1776+
chanmon_cfgs[1].fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(feerate_per_kw) };
1777+
17631778
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
17641779
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None, None]);
17651780
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
17661781

17671782
// Set nodes[0]'s balance such that they will consider any above-dust received HTLC to be a
17681783
// channel reserve violation (so their balance is channel reserve (1000 sats) + commitment
17691784
// transaction fee with 0 HTLCs (183 sats)).
1770-
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 98817000, InitFeatures::known(), InitFeatures::known());
1785+
let mut push_amt = 100_000_000;
1786+
push_amt -= feerate_per_kw as u64 * (COMMITMENT_TX_BASE_WEIGHT) / 1000 * 1000;
1787+
push_amt -= Channel::<EnforcingSigner>::get_holder_selected_channel_reserve_satoshis(100_000) * 1000;
1788+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, push_amt, InitFeatures::known(), InitFeatures::known());
17711789

1772-
let dust_amt = 329000; // Dust amount
1790+
let dust_amt = crate::ln::channel::MIN_DUST_LIMIT_SATOSHIS * 1000
1791+
+ feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 * 1000 - 1;
17731792
// In the previous code, routing this dust payment would cause nodes[0] to perceive a channel
17741793
// reserve violation even though it's a dust HTLC and therefore shouldn't count towards the
17751794
// commitment transaction fee.
17761795
let (_, _, _) = route_payment(&nodes[1], &[&nodes[0]], dust_amt);
1796+
1797+
// One more than the dust amt should fail, however.
1798+
let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], dust_amt + 1);
1799+
unwrap_send_err!(nodes[1].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)), true, APIError::ChannelUnavailable { ref err },
1800+
assert_eq!(err, "Cannot send value that would put counterparty balance under holder-announced channel reserve value"));
17771801
}
17781802

17791803
#[test]

0 commit comments

Comments
 (0)