Skip to content

Adds InboundChannelsConfig configuration #594

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@ dictionary Config {
u64 probing_liquidity_limit_multiplier;
AnchorChannelsConfig? anchor_channels_config;
SendingParameters? sending_parameters;
InboundChannelsConfig inbound_channels_config;
};

dictionary AnchorChannelsConfig {
sequence<PublicKey> trusted_peers_no_reserve;
u64 per_channel_reserve_sats;
};

dictionary InboundChannelsConfig {
boolean reject_announced_channel_requests;
u64? minimum_channel_size;
};

dictionary BackgroundSyncConfig {
u64 onchain_wallet_sync_interval_secs;
u64 lightning_wallet_sync_interval_secs;
Expand Down
46 changes: 43 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,12 @@ pub const WALLET_KEYS_SEED_LEN: usize = 64;
/// | `trusted_peers_0conf` | [] |
/// | `probing_liquidity_limit_multiplier` | 3 |
/// | `log_level` | Debug |
/// | `anchor_channels_config` | Some(..) |
/// | `anchor_channels_config` | Some(..) |
/// | `sending_parameters` | None |
/// | `inbound_channels_config` . | ::Default |
///
/// See [`AnchorChannelsConfig`] and [`SendingParameters`] for more information regarding their
/// respective default values.
/// See [`AnchorChannelsConfig`], [`InboundChannelsConfig`] and [`SendingParameters`]
/// for more information regarding their respective default values.
///
/// [`Node`]: crate::Node
pub struct Config {
Expand Down Expand Up @@ -167,6 +168,11 @@ pub struct Config {
/// **Note:** If unset, default parameters will be used, and you will be able to override the
/// parameters on a per-payment basis in the corresponding method calls.
pub sending_parameters: Option<SendingParameters>,
/// Configuration options for inbound channels requests.
///
/// These options can be used to customize the behavior whenever a channel is requested by a
/// remote peer.
pub inbound_channels_config: InboundChannelsConfig,
}

impl Default for Config {
Expand All @@ -181,10 +187,44 @@ impl Default for Config {
anchor_channels_config: Some(AnchorChannelsConfig::default()),
sending_parameters: None,
node_alias: None,
inbound_channels_config: InboundChannelsConfig::default(),
}
}
}

/// Configuration options for inbound channels requests.
///
/// These options can be used to customize the behavior whenever a channel is requested by a
/// remote peer.
///
/// ### Defaults
///
/// | Parameter | Value |
/// |-------------------------------------|--------|
/// | `reject_announced_channel_requests` | false |
/// | `minimum_channel_size` | None |
///

#[derive(Debug, Clone)]
pub struct InboundChannelsConfig {
/// Boolean flag indicating whether to reject announced channel requests.
///
/// If `true`, the node will reject any inbound channel requests with announced channel
/// requests.
pub reject_announced_channel_requests: bool,
/// Optional minimum channel size in satoshis.
///
/// If set, the node will reject any inbound channel requests with a channel size smaller than
/// the specified value.
pub minimum_channel_size: Option<u64>,
}

impl Default for InboundChannelsConfig {
fn default() -> Self {
Self { reject_announced_channel_requests: false, minimum_channel_size: None }
}
}

/// Configuration options pertaining to 'Anchor' channels, i.e., channels for which the
/// `option_anchors_zero_fee_htlc_tx` channel type is negotiated.
///
Expand Down
44 changes: 44 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,27 @@ where
is_announced,
params: _,
} => {
if is_announced
&& self.config.inbound_channels_config.reject_announced_channel_requests
{
log_error!(
self.logger,
"Rejecting inbound announced channel from peer {} due to reject_announced_channel_requests configuration",
counterparty_node_id
);

self.channel_manager
.force_close_without_broadcasting_txn(
&temporary_channel_id,
&counterparty_node_id,
"Channel request rejected".to_string(),
)
.unwrap_or_else(|e| {
log_error!(self.logger, "Failed to reject channel: {:?}", e)
});
return Ok(());
}

if is_announced {
if let Err(err) = may_announce_channel(&*self.config) {
log_error!(self.logger, "Rejecting inbound announced channel from peer {} due to missing configuration: {}", counterparty_node_id, err);
Expand All @@ -1096,6 +1117,29 @@ where
}
}

if funding_satoshis
< self.config.inbound_channels_config.minimum_channel_size.unwrap_or(0)
{
log_error!(
self.logger,
"Rejecting inbound announced channel from peer {} due to minimum channel size of {}sats",
counterparty_node_id,
self.config.inbound_channels_config.minimum_channel_size.unwrap_or(0),
);

self.channel_manager
.force_close_without_broadcasting_txn(
&temporary_channel_id,
&counterparty_node_id,
"Channel request rejected".to_string(),
)
.unwrap_or_else(|e| {
log_error!(self.logger, "Failed to reject channel: {:?}", e)
});

return Ok(());
}

let anchor_channel = channel_type.requires_anchors_zero_fee_htlc_tx();
if anchor_channel {
if let Some(anchor_channels_config) =
Expand Down
2 changes: 1 addition & 1 deletion src/ffi/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

pub use crate::config::{
default_config, AnchorChannelsConfig, BackgroundSyncConfig, ElectrumSyncConfig,
EsploraSyncConfig, MaxDustHTLCExposure,
EsploraSyncConfig, InboundChannelsConfig, MaxDustHTLCExposure,
};
pub use crate::graph::{ChannelInfo, ChannelUpdateInfo, NodeAnnouncementInfo, NodeInfo};
pub use crate::liquidity::{LSPS1OrderStatus, LSPS2ServiceConfig, OnchainPaymentInfo, PaymentInfo};
Expand Down
86 changes: 86 additions & 0 deletions tests/integration_tests_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,92 @@ fn channel_open_fails_when_funds_insufficient() {
);
}

#[test]
fn channel_open_fails_when_inbound_channels_config_rejects_announced_channels() {
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let chain_source = TestChainSource::Esplora(&electrsd);
let config_a = random_config(true);
let node_a = setup_node(&chain_source, config_a, None);
let mut config_b = random_config(true);
config_b.node_config.inbound_channels_config.reject_announced_channel_requests = true;

let node_b = setup_node(&chain_source, config_b, None);

let addr_a = node_a.onchain_payment().new_address().unwrap();
let addr_b = node_b.onchain_payment().new_address().unwrap();

let premine_amount_sat = 100_000;

premine_and_distribute_funds(
&bitcoind.client,
&electrsd.client,
vec![addr_a, addr_b],
Amount::from_sat(premine_amount_sat),
);
node_a.sync_wallets().unwrap();
node_b.sync_wallets().unwrap();
assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat);
assert_eq!(node_b.list_balances().spendable_onchain_balance_sats, premine_amount_sat);

println!("\nA -- open_channel -> B");
node_a
.open_announced_channel(
node_b.node_id(),
node_b.listening_addresses().unwrap().first().unwrap().clone(),
20_000,
None,
None,
)
.unwrap();

assert_eq!(node_a.list_peers().first().unwrap().node_id, node_b.node_id());
assert!(node_a.list_peers().first().unwrap().is_persisted);
expect_event!(node_a, ChannelClosed);
}

#[test]
fn channel_open_fails_when_inbound_channels_config_has_min_channel_size() {
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let chain_source = TestChainSource::Esplora(&electrsd);
let config_a = random_config(true);
let node_a = setup_node(&chain_source, config_a, None);
let mut config_b = random_config(true);
config_b.node_config.inbound_channels_config.minimum_channel_size = Some(100_000);

let node_b = setup_node(&chain_source, config_b, None);

let addr_a = node_a.onchain_payment().new_address().unwrap();
let addr_b = node_b.onchain_payment().new_address().unwrap();

let premine_amount_sat = 100_000;

premine_and_distribute_funds(
&bitcoind.client,
&electrsd.client,
vec![addr_a, addr_b],
Amount::from_sat(premine_amount_sat),
);
node_a.sync_wallets().unwrap();
node_b.sync_wallets().unwrap();
assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat);
assert_eq!(node_b.list_balances().spendable_onchain_balance_sats, premine_amount_sat);

println!("\nA -- open_channel -> B");
node_a
.open_announced_channel(
node_b.node_id(),
node_b.listening_addresses().unwrap().first().unwrap().clone(),
20_000,
None,
None,
)
.unwrap();

assert_eq!(node_a.list_peers().first().unwrap().node_id, node_b.node_id());
assert!(node_a.list_peers().first().unwrap().is_persisted);
expect_event!(node_a, ChannelClosed);
}

#[test]
fn multi_hop_sending() {
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
Expand Down
Loading