Skip to content

Commit 9591d62

Browse files
committed
Support atomic partial updates to ChannelConfig
1 parent fb140b5 commit 9591d62

File tree

2 files changed

+146
-7
lines changed

2 files changed

+146
-7
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 91 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,7 @@ 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+
if !channel.update_config(&config_update.apply(&channel.config())) {
32783278
continue;
32793279
}
32803280
if let Ok(msg) = self.get_channel_update_for_broadcast(channel) {
@@ -3289,6 +3289,34 @@ where
32893289
Ok(())
32903290
}
32913291

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

85848612
#[test]
@@ -9489,6 +9517,62 @@ mod tests {
94899517

94909518
check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed);
94919519
}
9520+
9521+
#[test]
9522+
fn test_update_channel_config() {
9523+
let chanmon_cfg = create_chanmon_cfgs(2);
9524+
let node_cfg = create_node_cfgs(2, &chanmon_cfg);
9525+
let mut user_config = test_default_channel_config();
9526+
let node_chanmgr = create_node_chanmgrs(2, &node_cfg, &[Some(user_config), Some(user_config)]);
9527+
let nodes = create_network(2, &node_cfg, &node_chanmgr);
9528+
let _ = create_announced_chan_between_nodes(&nodes, 0, 1);
9529+
let channel = &nodes[0].node.list_channels()[0];
9530+
9531+
nodes[0].node.update_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &user_config.channel_config).unwrap();
9532+
let events = nodes[0].node.get_and_clear_pending_msg_events();
9533+
assert_eq!(events.len(), 0);
9534+
9535+
user_config.channel_config.forwarding_fee_base_msat += 10;
9536+
nodes[0].node.update_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &user_config.channel_config).unwrap();
9537+
assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().forwarding_fee_base_msat, user_config.channel_config.forwarding_fee_base_msat);
9538+
let events = nodes[0].node.get_and_clear_pending_msg_events();
9539+
assert_eq!(events.len(), 1);
9540+
match &events[0] {
9541+
MessageSendEvent::BroadcastChannelUpdate { .. } => {},
9542+
_ => panic!("expected BroadcastChannelUpdate event"),
9543+
}
9544+
9545+
nodes[0].node.update_partial_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &ChannelConfigUpdate::default()).unwrap();
9546+
let events = nodes[0].node.get_and_clear_pending_msg_events();
9547+
assert_eq!(events.len(), 0);
9548+
9549+
let new_cltv_expiry_delta = user_config.channel_config.cltv_expiry_delta + 6;
9550+
nodes[0].node.update_partial_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &ChannelConfigUpdate {
9551+
cltv_expiry_delta: Some(new_cltv_expiry_delta),
9552+
..Default::default()
9553+
}).unwrap();
9554+
assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().cltv_expiry_delta, new_cltv_expiry_delta);
9555+
let events = nodes[0].node.get_and_clear_pending_msg_events();
9556+
assert_eq!(events.len(), 1);
9557+
match &events[0] {
9558+
MessageSendEvent::BroadcastChannelUpdate { .. } => {},
9559+
_ => panic!("expected BroadcastChannelUpdate event"),
9560+
}
9561+
9562+
let new_fee = user_config.channel_config.forwarding_fee_proportional_millionths + 100;
9563+
nodes[0].node.update_partial_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &ChannelConfigUpdate {
9564+
forwarding_fee_proportional_millionths: Some(new_fee),
9565+
..Default::default()
9566+
}).unwrap();
9567+
assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().cltv_expiry_delta, new_cltv_expiry_delta);
9568+
assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().forwarding_fee_proportional_millionths, new_fee);
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+
}
94929576
}
94939577

94949578
#[cfg(ldk_bench)]

lightning/src/util/config.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,61 @@ impl_writeable_tlv_based!(ChannelConfig, {
423423
(10, force_close_avoidance_max_fee_satoshis, required),
424424
});
425425

426+
/// A parallel struct to [`ChannelConfig`] to define partial updates.
427+
pub struct ChannelConfigUpdate {
428+
pub forwarding_fee_proportional_millionths: Option<u32>,
429+
pub forwarding_fee_base_msat: Option<u32>,
430+
pub cltv_expiry_delta: Option<u16>,
431+
pub max_dust_htlc_exposure_msat: Option<u64>,
432+
pub force_close_avoidance_max_fee_satoshis: Option<u64>,
433+
}
434+
435+
impl ChannelConfigUpdate {
436+
pub fn apply(&self, config: &ChannelConfig) -> ChannelConfig {
437+
let mut updated_config = config.clone();
438+
if let Some(forwarding_fee_proportional_millionths) = self.forwarding_fee_proportional_millionths {
439+
updated_config.forwarding_fee_proportional_millionths = forwarding_fee_proportional_millionths;
440+
}
441+
if let Some(forwarding_fee_base_msat) = self.forwarding_fee_base_msat {
442+
updated_config.forwarding_fee_base_msat = forwarding_fee_base_msat;
443+
}
444+
if let Some(cltv_expiry_delta) = self.cltv_expiry_delta {
445+
updated_config.cltv_expiry_delta = cltv_expiry_delta;
446+
}
447+
if let Some(max_dust_htlc_exposure_msat) = self.max_dust_htlc_exposure_msat {
448+
updated_config.max_dust_htlc_exposure_msat = max_dust_htlc_exposure_msat;
449+
}
450+
if let Some(force_close_avoidance_max_fee_satoshis) = self.force_close_avoidance_max_fee_satoshis {
451+
updated_config.force_close_avoidance_max_fee_satoshis = force_close_avoidance_max_fee_satoshis;
452+
}
453+
return updated_config;
454+
}
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)