Skip to content

Commit 583a5b2

Browse files
authored
Merge pull request #2330 from wvanlint/partial_config_updates
Support atomic partial updates to ChannelConfig
2 parents 9cafede + 8fd8966 commit 583a5b2

File tree

2 files changed

+148
-7
lines changed

2 files changed

+148
-7
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 93 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ use crate::ln::outbound_payment;
5656
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment};
5757
use crate::ln::wire::Encode;
5858
use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, ChannelSigner, WriteableEcdsaChannelSigner};
59-
use crate::util::config::{UserConfig, ChannelConfig};
59+
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
6060
use crate::util::wakers::{Future, Notifier};
6161
use crate::util::scid_utils::fake_scid;
6262
use crate::util::string::UntrustedString;
@@ -3228,7 +3228,7 @@ where
32283228
})
32293229
}
32303230

3231-
/// Atomically updates the [`ChannelConfig`] for the given channels.
3231+
/// Atomically applies partial updates to the [`ChannelConfig`] of the given channels.
32323232
///
32333233
/// Once the updates are applied, each eligible channel (advertised with a known short channel
32343234
/// ID and a change in [`forwarding_fee_proportional_millionths`], [`forwarding_fee_base_msat`],
@@ -3250,10 +3250,10 @@ where
32503250
/// [`ChannelUpdate`]: msgs::ChannelUpdate
32513251
/// [`ChannelUnavailable`]: APIError::ChannelUnavailable
32523252
/// [`APIMisuseError`]: APIError::APIMisuseError
3253-
pub fn update_channel_config(
3254-
&self, counterparty_node_id: &PublicKey, channel_ids: &[[u8; 32]], config: &ChannelConfig,
3253+
pub fn update_partial_channel_config(
3254+
&self, counterparty_node_id: &PublicKey, channel_ids: &[[u8; 32]], config_update: &ChannelConfigUpdate,
32553255
) -> Result<(), APIError> {
3256-
if config.cltv_expiry_delta < MIN_CLTV_EXPIRY_DELTA {
3256+
if config_update.cltv_expiry_delta.map(|delta| delta < MIN_CLTV_EXPIRY_DELTA).unwrap_or(false) {
32573257
return Err(APIError::APIMisuseError {
32583258
err: format!("The chosen CLTV expiry delta is below the minimum of {}", MIN_CLTV_EXPIRY_DELTA),
32593259
});
@@ -3274,7 +3274,9 @@ where
32743274
}
32753275
for channel_id in channel_ids {
32763276
let channel = peer_state.channel_by_id.get_mut(channel_id).unwrap();
3277-
if !channel.update_config(config) {
3277+
let mut config = channel.config();
3278+
config.apply(config_update);
3279+
if !channel.update_config(&config) {
32783280
continue;
32793281
}
32803282
if let Ok(msg) = self.get_channel_update_for_broadcast(channel) {
@@ -3289,6 +3291,34 @@ where
32893291
Ok(())
32903292
}
32913293

3294+
/// Atomically updates the [`ChannelConfig`] for the given channels.
3295+
///
3296+
/// Once the updates are applied, each eligible channel (advertised with a known short channel
3297+
/// ID and a change in [`forwarding_fee_proportional_millionths`], [`forwarding_fee_base_msat`],
3298+
/// or [`cltv_expiry_delta`]) has a [`BroadcastChannelUpdate`] event message generated
3299+
/// containing the new [`ChannelUpdate`] message which should be broadcast to the network.
3300+
///
3301+
/// Returns [`ChannelUnavailable`] when a channel is not found or an incorrect
3302+
/// `counterparty_node_id` is provided.
3303+
///
3304+
/// Returns [`APIMisuseError`] when a [`cltv_expiry_delta`] update is to be applied with a value
3305+
/// below [`MIN_CLTV_EXPIRY_DELTA`].
3306+
///
3307+
/// If an error is returned, none of the updates should be considered applied.
3308+
///
3309+
/// [`forwarding_fee_proportional_millionths`]: ChannelConfig::forwarding_fee_proportional_millionths
3310+
/// [`forwarding_fee_base_msat`]: ChannelConfig::forwarding_fee_base_msat
3311+
/// [`cltv_expiry_delta`]: ChannelConfig::cltv_expiry_delta
3312+
/// [`BroadcastChannelUpdate`]: events::MessageSendEvent::BroadcastChannelUpdate
3313+
/// [`ChannelUpdate`]: msgs::ChannelUpdate
3314+
/// [`ChannelUnavailable`]: APIError::ChannelUnavailable
3315+
/// [`APIMisuseError`]: APIError::APIMisuseError
3316+
pub fn update_channel_config(
3317+
&self, counterparty_node_id: &PublicKey, channel_ids: &[[u8; 32]], config: &ChannelConfig,
3318+
) -> Result<(), APIError> {
3319+
return self.update_partial_channel_config(counterparty_node_id, channel_ids, &(*config).into());
3320+
}
3321+
32923322
/// Attempts to forward an intercepted HTLC over the provided channel id and with the provided
32933323
/// amount to forward. Should only be called in response to an [`HTLCIntercepted`] event.
32943324
///
@@ -8582,7 +8612,7 @@ mod tests {
85828612
use crate::routing::router::{PaymentParameters, RouteParameters, find_route};
85838613
use crate::util::errors::APIError;
85848614
use crate::util::test_utils;
8585-
use crate::util::config::ChannelConfig;
8615+
use crate::util::config::{ChannelConfig, ChannelConfigUpdate};
85868616
use crate::sign::EntropySource;
85878617

85888618
#[test]
@@ -9501,6 +9531,62 @@ mod tests {
95019531

95029532
check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed);
95039533
}
9534+
9535+
#[test]
9536+
fn test_update_channel_config() {
9537+
let chanmon_cfg = create_chanmon_cfgs(2);
9538+
let node_cfg = create_node_cfgs(2, &chanmon_cfg);
9539+
let mut user_config = test_default_channel_config();
9540+
let node_chanmgr = create_node_chanmgrs(2, &node_cfg, &[Some(user_config), Some(user_config)]);
9541+
let nodes = create_network(2, &node_cfg, &node_chanmgr);
9542+
let _ = create_announced_chan_between_nodes(&nodes, 0, 1);
9543+
let channel = &nodes[0].node.list_channels()[0];
9544+
9545+
nodes[0].node.update_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &user_config.channel_config).unwrap();
9546+
let events = nodes[0].node.get_and_clear_pending_msg_events();
9547+
assert_eq!(events.len(), 0);
9548+
9549+
user_config.channel_config.forwarding_fee_base_msat += 10;
9550+
nodes[0].node.update_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &user_config.channel_config).unwrap();
9551+
assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().forwarding_fee_base_msat, user_config.channel_config.forwarding_fee_base_msat);
9552+
let events = nodes[0].node.get_and_clear_pending_msg_events();
9553+
assert_eq!(events.len(), 1);
9554+
match &events[0] {
9555+
MessageSendEvent::BroadcastChannelUpdate { .. } => {},
9556+
_ => panic!("expected BroadcastChannelUpdate event"),
9557+
}
9558+
9559+
nodes[0].node.update_partial_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &ChannelConfigUpdate::default()).unwrap();
9560+
let events = nodes[0].node.get_and_clear_pending_msg_events();
9561+
assert_eq!(events.len(), 0);
9562+
9563+
let new_cltv_expiry_delta = user_config.channel_config.cltv_expiry_delta + 6;
9564+
nodes[0].node.update_partial_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &ChannelConfigUpdate {
9565+
cltv_expiry_delta: Some(new_cltv_expiry_delta),
9566+
..Default::default()
9567+
}).unwrap();
9568+
assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().cltv_expiry_delta, new_cltv_expiry_delta);
9569+
let events = nodes[0].node.get_and_clear_pending_msg_events();
9570+
assert_eq!(events.len(), 1);
9571+
match &events[0] {
9572+
MessageSendEvent::BroadcastChannelUpdate { .. } => {},
9573+
_ => panic!("expected BroadcastChannelUpdate event"),
9574+
}
9575+
9576+
let new_fee = user_config.channel_config.forwarding_fee_proportional_millionths + 100;
9577+
nodes[0].node.update_partial_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &ChannelConfigUpdate {
9578+
forwarding_fee_proportional_millionths: Some(new_fee),
9579+
..Default::default()
9580+
}).unwrap();
9581+
assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().cltv_expiry_delta, new_cltv_expiry_delta);
9582+
assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().forwarding_fee_proportional_millionths, new_fee);
9583+
let events = nodes[0].node.get_and_clear_pending_msg_events();
9584+
assert_eq!(events.len(), 1);
9585+
match &events[0] {
9586+
MessageSendEvent::BroadcastChannelUpdate { .. } => {},
9587+
_ => panic!("expected BroadcastChannelUpdate event"),
9588+
}
9589+
}
95049590
}
95059591

95069592
#[cfg(ldk_bench)]

lightning/src/util/config.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,27 @@ pub struct ChannelConfig {
399399
pub force_close_avoidance_max_fee_satoshis: u64,
400400
}
401401

402+
impl ChannelConfig {
403+
/// Applies the given [`ChannelConfigUpdate`] as a partial update to the [`ChannelConfig`].
404+
pub fn apply(&mut self, update: &ChannelConfigUpdate) {
405+
if let Some(forwarding_fee_proportional_millionths) = update.forwarding_fee_proportional_millionths {
406+
self.forwarding_fee_proportional_millionths = forwarding_fee_proportional_millionths;
407+
}
408+
if let Some(forwarding_fee_base_msat) = update.forwarding_fee_base_msat {
409+
self.forwarding_fee_base_msat = forwarding_fee_base_msat;
410+
}
411+
if let Some(cltv_expiry_delta) = update.cltv_expiry_delta {
412+
self.cltv_expiry_delta = cltv_expiry_delta;
413+
}
414+
if let Some(max_dust_htlc_exposure_msat) = update.max_dust_htlc_exposure_msat {
415+
self.max_dust_htlc_exposure_msat = max_dust_htlc_exposure_msat;
416+
}
417+
if let Some(force_close_avoidance_max_fee_satoshis) = update.force_close_avoidance_max_fee_satoshis {
418+
self.force_close_avoidance_max_fee_satoshis = force_close_avoidance_max_fee_satoshis;
419+
}
420+
}
421+
}
422+
402423
impl Default for ChannelConfig {
403424
/// Provides sane defaults for most configurations (but with zero relay fees!).
404425
fn default() -> Self {
@@ -423,6 +444,40 @@ impl_writeable_tlv_based!(ChannelConfig, {
423444
(10, force_close_avoidance_max_fee_satoshis, required),
424445
});
425446

447+
/// A parallel struct to [`ChannelConfig`] to define partial updates.
448+
#[allow(missing_docs)]
449+
pub struct ChannelConfigUpdate {
450+
pub forwarding_fee_proportional_millionths: Option<u32>,
451+
pub forwarding_fee_base_msat: Option<u32>,
452+
pub cltv_expiry_delta: Option<u16>,
453+
pub max_dust_htlc_exposure_msat: Option<u64>,
454+
pub force_close_avoidance_max_fee_satoshis: Option<u64>,
455+
}
456+
457+
impl Default for ChannelConfigUpdate {
458+
fn default() -> ChannelConfigUpdate {
459+
ChannelConfigUpdate {
460+
forwarding_fee_proportional_millionths: None,
461+
forwarding_fee_base_msat: None,
462+
cltv_expiry_delta: None,
463+
max_dust_htlc_exposure_msat: None,
464+
force_close_avoidance_max_fee_satoshis: None,
465+
}
466+
}
467+
}
468+
469+
impl From<ChannelConfig> for ChannelConfigUpdate {
470+
fn from(config: ChannelConfig) -> ChannelConfigUpdate {
471+
ChannelConfigUpdate {
472+
forwarding_fee_proportional_millionths: Some(config.forwarding_fee_proportional_millionths),
473+
forwarding_fee_base_msat: Some(config.forwarding_fee_base_msat),
474+
cltv_expiry_delta: Some(config.cltv_expiry_delta),
475+
max_dust_htlc_exposure_msat: Some(config.max_dust_htlc_exposure_msat),
476+
force_close_avoidance_max_fee_satoshis: Some(config.force_close_avoidance_max_fee_satoshis),
477+
}
478+
}
479+
}
480+
426481
/// Legacy version of [`ChannelConfig`] that stored the static
427482
/// [`ChannelHandshakeConfig::announced_channel`] and
428483
/// [`ChannelHandshakeConfig::commit_upfront_shutdown_pubkey`] fields.

0 commit comments

Comments
 (0)