Skip to content

Commit fac8a1f

Browse files
committed
Remove the ability to disable anchor channels
We previously allowed users to disable anchor channels and drain their anchor reserve while still having anchor channels open or pending resolution. This was acceptable for keyed anchor channels, as the commitment transaction therein still contained some fees, and had some chance of getting mined into a block without any anchor bumps. In upcoming commits, we will add support for 0FC channels, and their commitment transactions have zero fees and depend entirely on the anchor reserve to reach miners and get confirmed in a block. It is thus dangerous to disable anchor channels and drain the reserve after 0FC channels have been opened. Therefore, we make `AnchorChannelsConfig` required, and prevent this case from ever happening.
1 parent 2c2c287 commit fac8a1f

16 files changed

Lines changed: 163 additions & 291 deletions

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
- `EsploraSyncConfig` and `ElectrumSyncConfig` now support `force_wallet_full_scan`. When set,
1919
the on-chain wallet keeps using BDK `full_scan` instead of incremental sync until a full scan
2020
succeeds, allowing restored wallets to rediscover funds sent to previously-unknown addresses.
21+
- `Config::anchor_channels_config` is no longer optional, hence anchor channels can no longer be
22+
disabled. We still negotiate legacy channels if the peer does not support anchor channels.
2123

2224
## Bug Fixes and Improvements
2325
- Building a fresh node against a Bitcoin Core RPC or REST chain source that fails to return the

benches/payments.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,8 @@ fn payment_benchmark(c: &mut Criterion) {
121121
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
122122
let chain_source = random_chain_source(&bitcoind, &electrsd);
123123

124-
let (node_a, node_b) = setup_two_nodes_with_store(
125-
&chain_source,
126-
false,
127-
true,
128-
false,
129-
common::TestStoreType::Sqlite,
130-
);
124+
let (node_a, node_b) =
125+
setup_two_nodes_with_store(&chain_source, false, false, common::TestStoreType::Sqlite);
131126

132127
let runtime =
133128
tokio::runtime::Builder::new_multi_thread().worker_threads(4).enable_all().build().unwrap();

src/config.rs

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ pub(crate) const LNURL_AUTH_TIMEOUT_SECS: u64 = 15;
143143
/// | `node_alias` | None |
144144
/// | `trusted_peers_0conf` | [] |
145145
/// | `probing_liquidity_limit_multiplier` | 3 |
146-
/// | `anchor_channels_config` | Some(..) |
146+
/// | `anchor_channels_config` | AnchorChannelsConfig::default() |
147147
/// | `route_parameters` | None |
148148
/// | `tor_config` | None |
149149
/// | `hrn_config` | HumanReadableNamesConfig::default() |
@@ -190,19 +190,7 @@ pub struct Config {
190190
/// `option_anchors_zero_fee_htlc_tx` channel type is negotiated.
191191
///
192192
/// Please refer to [`AnchorChannelsConfig`] for further information on Anchor channels.
193-
///
194-
/// If set to `Some`, we'll try to open new channels with Anchors enabled, i.e., new channels
195-
/// will be negotiated with the `option_anchors_zero_fee_htlc_tx` channel type if supported by
196-
/// the counterparty. Note that this won't prevent us from opening non-Anchor channels if the
197-
/// counterparty doesn't support `option_anchors_zero_fee_htlc_tx`. If set to `None`, new
198-
/// channels will be negotiated with the legacy `option_static_remotekey` channel type only.
199-
///
200-
/// **Note:** If set to `None` *after* some Anchor channels have already been
201-
/// opened, no dedicated emergency on-chain reserve will be maintained for these channels,
202-
/// which can be dangerous if only insufficient funds are available at the time of channel
203-
/// closure. We *will* however still try to get the Anchor spending transactions confirmed
204-
/// on-chain with the funds available.
205-
pub anchor_channels_config: Option<AnchorChannelsConfig>,
193+
pub anchor_channels_config: AnchorChannelsConfig,
206194
/// Configuration options for payment routing and pathfinding.
207195
///
208196
/// Setting the [`RouteParametersConfig`] provides flexibility to customize how payments are routed,
@@ -233,7 +221,7 @@ impl Default for Config {
233221
announcement_addresses: None,
234222
trusted_peers_0conf: Vec::new(),
235223
probing_liquidity_limit_multiplier: DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER,
236-
anchor_channels_config: Some(AnchorChannelsConfig::default()),
224+
anchor_channels_config: AnchorChannelsConfig::default(),
237225
tor_config: None,
238226
route_parameters: None,
239227
node_alias: None,
@@ -418,8 +406,6 @@ pub(crate) fn default_user_config(config: &Config) -> UserConfig {
418406
// will mostly be relevant for inbound channels.
419407
let mut user_config = UserConfig::default();
420408
user_config.channel_handshake_limits.force_announced_channel_preference = false;
421-
user_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx =
422-
config.anchor_channels_config.is_some();
423409
user_config.reject_inbound_splices = false;
424410

425411
if may_announce_channel(config).is_err() {

src/event.rs

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,24 +1267,6 @@ where
12671267
}
12681268

12691269
let anchor_channel = channel_type.requires_anchors_zero_fee_htlc_tx();
1270-
if anchor_channel && self.config.anchor_channels_config.is_none() {
1271-
log_error!(
1272-
self.logger,
1273-
"Rejecting inbound channel from peer {} due to Anchor channels being disabled.",
1274-
counterparty_node_id,
1275-
);
1276-
self.channel_manager
1277-
.force_close_broadcasting_latest_txn(
1278-
&temporary_channel_id,
1279-
&counterparty_node_id,
1280-
"Channel request rejected".to_string(),
1281-
)
1282-
.unwrap_or_else(|e| {
1283-
log_error!(self.logger, "Failed to reject channel: {:?}", e)
1284-
});
1285-
return Ok(());
1286-
}
1287-
12881270
let required_reserve_sats = crate::new_channel_anchor_reserve_sats(
12891271
&self.config,
12901272
&counterparty_node_id,
@@ -1750,19 +1732,17 @@ where
17501732
..
17511733
} => {
17521734
// Skip bumping channel closes if our counterparty is trusted.
1753-
if let Some(anchor_channels_config) =
1754-
self.config.anchor_channels_config.as_ref()
1735+
if self
1736+
.config
1737+
.anchor_channels_config
1738+
.trusted_peers_no_reserve
1739+
.contains(counterparty_node_id)
17551740
{
1756-
if anchor_channels_config
1757-
.trusted_peers_no_reserve
1758-
.contains(counterparty_node_id)
1759-
{
1760-
log_debug!(self.logger,
1761-
"Ignoring BumpTransactionEvent::ChannelClose for channel {} due to trusted counterparty {}",
1762-
channel_id, counterparty_node_id
1763-
);
1764-
return Ok(());
1765-
}
1741+
log_debug!(self.logger,
1742+
"Ignoring BumpTransactionEvent::ChannelClose for channel {} due to trusted counterparty {}",
1743+
channel_id, counterparty_node_id
1744+
);
1745+
return Ok(());
17661746
}
17671747
},
17681748
BumpTransactionEvent::HTLCResolution { .. } => {},

src/lib.rs

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,7 +1169,7 @@ impl Node {
11691169
self.channel_manager
11701170
.list_channels()
11711171
.into_iter()
1172-
.map(|c| ChannelDetails::from_ldk(c, self.config.anchor_channels_config.as_ref()))
1172+
.map(|c| ChannelDetails::from_ldk(c, &self.config.anchor_channels_config))
11731173
.collect()
11741174
}
11751175

@@ -2406,21 +2406,20 @@ impl_writeable_tlv_based!(NodeMetrics, {
24062406
pub(crate) fn total_anchor_channels_reserve_sats(
24072407
channel_manager: &ChannelManager, config: &Config,
24082408
) -> u64 {
2409-
config.anchor_channels_config.as_ref().map_or(0, |anchor_channels_config| {
2410-
channel_manager
2411-
.list_channels()
2412-
.into_iter()
2413-
.filter(|c| {
2414-
!anchor_channels_config.trusted_peers_no_reserve.contains(&c.counterparty.node_id)
2415-
&& c.channel_shutdown_state
2416-
.map_or(true, |s| s != ChannelShutdownState::ShutdownComplete)
2417-
&& c.channel_type
2418-
.as_ref()
2419-
.map_or(false, |t| t.requires_anchors_zero_fee_htlc_tx())
2420-
})
2421-
.count() as u64
2422-
* anchor_channels_config.per_channel_reserve_sats
2423-
})
2409+
channel_manager
2410+
.list_channels()
2411+
.into_iter()
2412+
.filter(|c| {
2413+
!config
2414+
.anchor_channels_config
2415+
.trusted_peers_no_reserve
2416+
.contains(&c.counterparty.node_id)
2417+
&& c.channel_shutdown_state
2418+
.map_or(true, |s| s != ChannelShutdownState::ShutdownComplete)
2419+
&& c.channel_type.as_ref().map_or(false, |t| t.requires_anchors_zero_fee_htlc_tx())
2420+
})
2421+
.count() as u64
2422+
* config.anchor_channels_config.per_channel_reserve_sats
24242423
}
24252424

24262425
pub(crate) fn new_channel_anchor_reserve_sats(
@@ -2430,13 +2429,11 @@ pub(crate) fn new_channel_anchor_reserve_sats(
24302429
return 0;
24312430
}
24322431

2433-
config.anchor_channels_config.as_ref().map_or(0, |c| {
2434-
if c.trusted_peers_no_reserve.contains(peer_node_id) {
2435-
0
2436-
} else {
2437-
c.per_channel_reserve_sats
2438-
}
2439-
})
2432+
if config.anchor_channels_config.trusted_peers_no_reserve.contains(peer_node_id) {
2433+
0
2434+
} else {
2435+
config.anchor_channels_config.per_channel_reserve_sats
2436+
}
24402437
}
24412438

24422439
#[cfg(test)]

src/liquidity/service/lsps2.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -453,15 +453,17 @@ where
453453
let spendable_amount_sats =
454454
self.wallet.get_spendable_amount_sats(cur_anchor_reserve_sats).unwrap_or(0);
455455
let required_funds_sats = channel_amount_sats
456-
+ self.config.anchor_channels_config.as_ref().map_or(0, |c| {
457-
if init_features.supports_anchors_zero_fee_htlc_tx()
458-
&& !c.trusted_peers_no_reserve.contains(&their_network_key)
459-
{
460-
c.per_channel_reserve_sats
461-
} else {
462-
0
463-
}
464-
});
456+
+ if init_features.supports_anchors_zero_fee_htlc_tx()
457+
&& !self
458+
.config
459+
.anchor_channels_config
460+
.trusted_peers_no_reserve
461+
.contains(&their_network_key)
462+
{
463+
self.config.anchor_channels_config.per_channel_reserve_sats
464+
} else {
465+
0
466+
};
465467
if spendable_amount_sats < required_funds_sats {
466468
log_error!(self.logger,
467469
"Unable to create channel due to insufficient funds. Available: {}sats, Required: {}sats",

src/types.rs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -646,24 +646,16 @@ pub struct ChannelDetails {
646646

647647
impl ChannelDetails {
648648
pub(crate) fn from_ldk(
649-
value: LdkChannelDetails, anchor_channels_config: Option<&AnchorChannelsConfig>,
649+
value: LdkChannelDetails, anchor_channels_config: &AnchorChannelsConfig,
650650
) -> Self {
651651
let reserve_type = value.channel_type.as_ref().map(|channel_type| {
652652
if channel_type.supports_anchors_zero_fee_htlc_tx() {
653-
if let Some(config) = anchor_channels_config {
654-
if config.trusted_peers_no_reserve.contains(&value.counterparty.node_id) {
655-
ReserveType::TrustedPeersNoReserve
656-
} else {
657-
ReserveType::Adaptive
658-
}
653+
if anchor_channels_config
654+
.trusted_peers_no_reserve
655+
.contains(&value.counterparty.node_id)
656+
{
657+
ReserveType::TrustedPeersNoReserve
659658
} else {
660-
// Edge case: if `AnchorChannelsConfig` was previously set and later
661-
// removed, we can no longer distinguish whether this anchor channel's
662-
// reserve was `Adaptive` or `TrustedPeersNoReserve`. We default to
663-
// `Adaptive` here, which may incorrectly override a prior
664-
// `TrustedPeersNoReserve` designation. This is acceptable since
665-
// unsetting `AnchorChannelsConfig` on a node with existing anchor
666-
// channels is not an expected operation.
667659
ReserveType::Adaptive
668660
}
669661
} else {

tests/common/mod.rs

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -379,13 +379,9 @@ pub(crate) fn random_node_alias() -> Option<NodeAlias> {
379379
Some(NodeAlias(bytes))
380380
}
381381

382-
pub(crate) fn random_config(anchor_channels: bool) -> TestConfig {
382+
pub(crate) fn random_config() -> TestConfig {
383383
let mut node_config = Config::default();
384384

385-
if !anchor_channels {
386-
node_config.anchor_channels_config = None;
387-
}
388-
389385
node_config.network = Network::Regtest;
390386
println!("Setting network: {}", node_config.network);
391387

@@ -594,24 +590,22 @@ pub(crate) use setup_builder;
594590
pub(crate) mod scenarios;
595591

596592
pub(crate) fn setup_two_nodes(
597-
chain_source: &TestChainSource, allow_0conf: bool, anchor_channels: bool,
598-
anchors_trusted_no_reserve: bool,
593+
chain_source: &TestChainSource, allow_0conf: bool, anchors_trusted_no_reserve: bool,
599594
) -> (TestNode, TestNode) {
600595
setup_two_nodes_with_store(
601596
chain_source,
602597
allow_0conf,
603-
anchor_channels,
604598
anchors_trusted_no_reserve,
605599
TestStoreType::TestSyncStore,
606600
)
607601
}
608602

609603
pub(crate) fn setup_two_nodes_with_store(
610-
chain_source: &TestChainSource, allow_0conf: bool, anchor_channels: bool,
611-
anchors_trusted_no_reserve: bool, store_type: TestStoreType,
604+
chain_source: &TestChainSource, allow_0conf: bool, anchors_trusted_no_reserve: bool,
605+
store_type: TestStoreType,
612606
) -> (TestNode, TestNode) {
613607
println!("== Node A ==");
614-
let mut config_a = random_config(anchor_channels);
608+
let mut config_a = random_config();
615609
config_a.store_type = store_type;
616610

617611
if cfg!(hrn_tests) {
@@ -622,7 +616,7 @@ pub(crate) fn setup_two_nodes_with_store(
622616
let node_a = setup_node(chain_source, config_a);
623617

624618
println!("\n== Node B ==");
625-
let mut config_b = random_config(anchor_channels);
619+
let mut config_b = random_config();
626620
config_b.store_type = store_type;
627621

628622
if cfg!(hrn_tests) {
@@ -637,14 +631,8 @@ pub(crate) fn setup_two_nodes_with_store(
637631
if allow_0conf {
638632
config_b.node_config.trusted_peers_0conf.push(node_a.node_id());
639633
}
640-
if anchor_channels && anchors_trusted_no_reserve {
641-
config_b
642-
.node_config
643-
.anchor_channels_config
644-
.as_mut()
645-
.unwrap()
646-
.trusted_peers_no_reserve
647-
.push(node_a.node_id());
634+
if anchors_trusted_no_reserve {
635+
config_b.node_config.anchor_channels_config.trusted_peers_no_reserve.push(node_a.node_id());
648636
}
649637
let node_b = setup_node(chain_source, config_b);
650638
(node_a, node_b)
@@ -1199,7 +1187,8 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
11991187
let node_b_anchor_reserve_sat = if node_b
12001188
.config()
12011189
.anchor_channels_config
1202-
.map_or(true, |acc| acc.trusted_peers_no_reserve.contains(&node_a.node_id()))
1190+
.trusted_peers_no_reserve
1191+
.contains(&node_a.node_id())
12031192
{
12041193
0
12051194
} else {

tests/common/scenarios/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub(crate) async fn wait_for_htlcs_settled(
9090
/// Build a fresh LDK node configured for interop tests. Uses electrum at the
9191
/// docker-compose default port and bumps sync timeouts for combo stress.
9292
pub(crate) fn setup_ldk_node() -> Node {
93-
let config = crate::common::random_config(true);
93+
let config = crate::common::random_config();
9494
let mut builder = ldk_node::Builder::from_config(config.node_config);
9595
let mut sync_config = ldk_node::config::ElectrumSyncConfig::default();
9696
sync_config.timeouts_config.onchain_wallet_sync_timeout_secs = 180;

tests/integration_tests_hrn.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ async fn unified_send_to_hrn() {
2424
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
2525
let chain_source = random_chain_source(&bitcoind, &electrsd);
2626

27-
let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false);
27+
let (node_a, node_b) = setup_two_nodes(&chain_source, false, false);
2828

2929
let address_a = node_a.onchain_payment().new_address().unwrap();
3030
let premined_sats = 5_000_000;

0 commit comments

Comments
 (0)