Skip to content

Commit 584920c

Browse files
committed
Include inbound-claimed-HTLCs in reported channel balances
Given the balance is reported as "total balance if we went to chain ignoring fees", it seems reasonable to include claimed HTLCs - if we went to chain we'd get those funds, less on-chain fees. Further, if we do not include them, its possible to have pending outbound holding-cell HTLCs underflow the balance calculation, causing a panic in debug mode, and bogus values in release. This resolves a subtraction underflow bug found by the `chanmon_consistency` fuzz target.
1 parent 99c230c commit 584920c

File tree

2 files changed

+48
-5
lines changed

2 files changed

+48
-5
lines changed

lightning/src/ln/channel.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,8 +2155,15 @@ impl<Signer: Sign> Channel<Signer> {
21552155
/// This is the amount that would go to us if we close the channel, ignoring any on-chain fees.
21562156
/// See also [`Channel::get_inbound_outbound_available_balance_msat`]
21572157
pub fn get_balance_msat(&self) -> u64 {
2158-
self.value_to_self_msat
2159-
- self.get_outbound_pending_htlc_stats(None).pending_htlcs_value_msat
2158+
// Include our local balance, plus any inbound HTLCs we know the preimage for, minus any
2159+
// HTLCs sent or which will be sent after commitment signed's are exchanged.
2160+
let mut balance_msat = self.value_to_self_msat;
2161+
for ref htlc in self.pending_inbound_htlcs.iter() {
2162+
if let InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_)) = htlc.state {
2163+
balance_msat += htlc.amount_msat;
2164+
}
2165+
}
2166+
balance_msat - self.get_outbound_pending_htlc_stats(None).pending_htlcs_value_msat
21602167
}
21612168

21622169
pub fn get_holder_counterparty_selected_channel_reserve_satoshis(&self) -> (u64, Option<u64>) {

lightning/src/ln/functional_unit_tests.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,15 @@
1313
use chain::keysinterface::BaseSign;
1414
use ln::PaymentPreimage;
1515
use ln::channel::{commitment_tx_base_weight, COMMITMENT_TX_WEIGHT_PER_HTLC};
16-
use ln::channelmanager::{ChannelManager, RAACommitmentOrder, PaymentSendFailure, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA};
17-
use ln::channel::{Channel, ChannelError};
16+
use ln::channelmanager::{RAACommitmentOrder, PaymentSendFailure, BREAKDOWN_TIMEOUT};
17+
use ln::channel::Channel;
1818
use ln::onion_utils;
1919
use ln::features::InitFeatures;
2020
use ln::msgs;
2121
use ln::msgs::{ChannelMessageHandler, ErrorAction};
2222
use util::enforcing_trait_impls::EnforcingSigner;
2323
use util::events::{Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose, ClosureReason};
2424
use util::errors::APIError;
25-
use util::ser::Writeable;
2625
use util::config::UserConfig;
2726

2827
use bitcoin::blockdata::constants::genesis_block;
@@ -895,6 +894,43 @@ fn test_counterparty_raa_skip_no_crash() {
895894
check_closed_event!(nodes[1], 1, ClosureReason::ProcessingError { err: "Received an unexpected revoke_and_ack".to_string() });
896895
}
897896

897+
#[test]
898+
fn test_pending_claimed_htlc_no_balance_underflow() {
899+
// Tests that if we have a pending outbound HTLC as well as a claimed-but-not-fully-removed
900+
// HTLC we will not underflow when we call `Channel::get_balance_msat()`.
901+
let chanmon_cfgs = create_chanmon_cfgs(2);
902+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
903+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
904+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
905+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, 0, InitFeatures::known(), InitFeatures::known());
906+
907+
let payment_preimage = route_payment(&nodes[0], &[&nodes[1]], 1_010_000).0;
908+
nodes[1].node.claim_funds(payment_preimage);
909+
check_added_monitors!(nodes[1], 1);
910+
let fulfill_ev = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
911+
912+
nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &fulfill_ev.update_fulfill_htlcs[0]);
913+
expect_payment_sent_without_paths!(nodes[0], payment_preimage);
914+
nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &fulfill_ev.commitment_signed);
915+
check_added_monitors!(nodes[0], 1);
916+
let (_raa, _cs) = get_revoke_commit_msgs!(nodes[0], nodes[1].node.get_our_node_id());
917+
918+
// At this point nodes[1] has received 1,010k msat (10k msat more than their reserve) and can
919+
// send an HTLC back (though it will go in the holding cell). Send an HTLC back and check we
920+
// can get our balance.
921+
922+
// Get a route from nodes[1] to nodes[0] by getting a route going the other way and then flip
923+
// the public key of the only hop. This works around ChannelDetails not showing the
924+
// almost-claimed HTLC as available balance.
925+
let (mut route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[1], 10_000);
926+
route.payment_params = None; // This is all wrong, but unnecessary
927+
route.paths[0][0].pubkey = nodes[0].node.get_our_node_id();
928+
let (_, payment_hash_2, payment_secret_2) = get_payment_preimage_hash!(nodes[0]);
929+
nodes[1].node.send_payment(&route, payment_hash_2, &Some(payment_secret_2)).unwrap();
930+
931+
assert_eq!(nodes[1].node.list_channels()[0].balance_msat, 1_000_000);
932+
}
933+
898934
#[test]
899935
fn test_channel_conf_timeout() {
900936
// Tests that, for inbound channels, we give up on them if the funding transaction does not

0 commit comments

Comments
 (0)