Skip to content

Commit 33987e8

Browse files
committed
Count zero-fee-commitments channels in anchor reserve check
`can_support_additional_anchor_channel` decides whether the wallet has enough on-chain reserve to back another anchor channel by counting the node's existing anchor channels. The classification only checked the `anchors_zero_fee_htlc_tx` feature, so channels negotiated with the `anchor_zero_fee_commitments` (TRUC / 0FC, option 41) variant — which require the same on-chain reserve to fund commitment / HTLC fee bumps on force-close — were silently dropped from the count. A node enabling `negotiate_anchor_zero_fee_commitments` would therefore be green-lit to open more anchor channels than its wallet can actually back, risking unfunded fee bumps and HTLC loss on simultaneous force-closes. Treat both feature flags as marking a channel as an anchor channel for reserve-accounting purposes (factored into a small `is_anchor_channel_type` helper, used in both the chain-monitor and channel-manager loops), and add a regression test that opens a single 0FC channel with reserves sized for exactly one channel and asserts the function refuses to authorize a second. Co-Authored-By: HAL 9000
1 parent 1a26867 commit 33987e8

1 file changed

Lines changed: 54 additions & 2 deletions

File tree

lightning/src/util/anchor_channel_reserves.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,13 @@ pub fn get_supportable_anchor_channels(
260260
num_whole_utxos + total_fractional_amount.to_sat() / reserve_per_channel.to_sat() / 2
261261
}
262262

263+
/// Returns whether a channel of the given type requires an on-chain anchor reserve, i.e. uses
264+
/// either the `anchors_zero_fee_htlc_tx` or `anchor_zero_fee_commitments` (TRUC / 0FC) variant.
265+
fn is_anchor_channel_type(channel_type: &ChannelTypeFeatures) -> bool {
266+
channel_type.supports_anchors_zero_fee_htlc_tx()
267+
|| channel_type.supports_anchor_zero_fee_commitments()
268+
}
269+
263270
/// Verifies whether the anchor channel reserve provided by `utxos` is sufficient to support
264271
/// an additional anchor channel.
265272
///
@@ -296,7 +303,7 @@ where
296303
} else {
297304
continue;
298305
};
299-
if channel_monitor.channel_type_features().supports_anchors_zero_fee_htlc_tx()
306+
if is_anchor_channel_type(&channel_monitor.channel_type_features())
300307
&& !channel_monitor.get_claimable_balances().is_empty()
301308
{
302309
anchor_channels.insert(channel_id);
@@ -305,7 +312,7 @@ where
305312
// Also include channels that are in the middle of negotiation or anchor channels that don't have
306313
// a ChannelMonitor yet.
307314
for channel in a_channel_manager.get_cm().list_channels() {
308-
if channel.channel_type.map_or(true, |ct| ct.supports_anchors_zero_fee_htlc_tx()) {
315+
if channel.channel_type.map_or(true, |ct| is_anchor_channel_type(&ct)) {
309316
anchor_channels.insert(channel.channel_id);
310317
}
311318
}
@@ -315,6 +322,7 @@ where
315322
#[cfg(test)]
316323
mod test {
317324
use super::*;
325+
use crate::ln::functional_test_utils::*;
318326
use bitcoin::{OutPoint, ScriptBuf, Sequence, TxOut, Txid};
319327
use std::str::FromStr;
320328

@@ -425,4 +433,48 @@ mod test {
425433
1068
426434
);
427435
}
436+
437+
#[test]
438+
fn test_can_support_additional_anchor_channel_zero_fee_commitments() {
439+
// Regression test: a channel that uses the `anchor_zero_fee_commitments`
440+
// (option 41) variant is just as much an anchor channel — and requires
441+
// the same on-chain reserve — as one using `anchors_zero_fee_htlc_tx`.
442+
// The reserve check must therefore count it as an existing anchor
443+
// channel when deciding whether the wallet can safely support an
444+
// additional one. Currently `can_support_additional_anchor_channel`
445+
// only counts channels whose features set `anchors_zero_fee_htlc_tx`,
446+
// so a node whose reserves are exhausted by zero-fee-commitment
447+
// channels is incorrectly told it can open another anchor channel.
448+
let mut cfg = test_default_channel_config();
449+
cfg.channel_handshake_config.negotiate_anchor_zero_fee_commitments = true;
450+
451+
let chanmon_cfgs = create_chanmon_cfgs(2);
452+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
453+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(cfg.clone()), Some(cfg)]);
454+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
455+
456+
create_chan_between_nodes(&nodes[0], &nodes[1]);
457+
458+
let channels = nodes[0].node.list_channels();
459+
assert_eq!(channels.len(), 1);
460+
let channel_type = channels[0].channel_type.as_ref().unwrap();
461+
assert!(channel_type.supports_anchor_zero_fee_commitments());
462+
// Sanity check: a zero-fee-commitments channel does not also set the
463+
// older anchors_zero_fee_htlc_tx feature.
464+
assert!(!channel_type.supports_anchors_zero_fee_htlc_tx());
465+
466+
let context = AnchorChannelReserveContext::default();
467+
let reserve = get_reserve_per_channel(&context);
468+
// Provide a single UTXO with enough value to cover one channel reserve.
469+
let utxos = vec![make_p2wpkh_utxo(reserve * 2)];
470+
471+
// We already have one TRUC anchor channel and only enough reserve for
472+
// a single channel; we must not authorize an additional one.
473+
assert!(!can_support_additional_anchor_channel(
474+
&context,
475+
&utxos,
476+
nodes[0].node,
477+
&nodes[0].chain_monitor.chain_monitor,
478+
));
479+
}
428480
}

0 commit comments

Comments
 (0)