diff --git a/protos/src/lib.rs b/protos/src/lib.rs index bd29df4..87af55e 100644 --- a/protos/src/lib.rs +++ b/protos/src/lib.rs @@ -229,6 +229,24 @@ pub struct OpenChannelResponse { #[prost(bytes = "bytes", tag = "1")] pub user_channel_id: ::prost::bytes::Bytes, } +/// Update the config for a previously opened channel. +/// See more: +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UpdateChannelConfigRequest { + /// The hex-encoded local `user_channel_id` of this channel. + #[prost(string, tag = "1")] + pub user_channel_id: ::prost::alloc::string::String, + /// The hex-encoded public key of the counterparty node to update channel config with. + #[prost(string, tag = "2")] + pub counterparty_node_id: ::prost::alloc::string::String, + /// The updated channel configuration settings for a channel. + #[prost(message, optional, tag = "3")] + pub channel_config: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UpdateChannelConfigResponse {} /// ChannelConfig represents the configuration settings for a channel in a Lightning Network node. /// See more: #[allow(clippy::derive_partial_eq_without_eq)] diff --git a/protos/src/proto/ldk_node_server.proto b/protos/src/proto/ldk_node_server.proto index 79d935a..342f704 100644 --- a/protos/src/proto/ldk_node_server.proto +++ b/protos/src/proto/ldk_node_server.proto @@ -220,6 +220,23 @@ message OpenChannelResponse { bytes user_channel_id = 1; } +// Update the config for a previously opened channel. +// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.update_channel_config +message UpdateChannelConfigRequest { + + // The hex-encoded local `user_channel_id` of this channel. + string user_channel_id = 1; + + // The hex-encoded public key of the counterparty node to update channel config with. + string counterparty_node_id = 2; + + // The updated channel configuration settings for a channel. + ChannelConfig channel_config = 3; +} + +message UpdateChannelConfigResponse { +} + // ChannelConfig represents the configuration settings for a channel in a Lightning Network node. // See more: https://docs.rs/lightning/latest/lightning/util/config/struct.ChannelConfig.html message ChannelConfig { diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs index c1079f2..e7561fd 100644 --- a/server/src/api/mod.rs +++ b/server/src/api/mod.rs @@ -10,3 +10,4 @@ pub(crate) mod list_payments; pub(crate) mod onchain_receive; pub(crate) mod onchain_send; pub(crate) mod open_channel; +pub(crate) mod update_channel_config; diff --git a/server/src/api/update_channel_config.rs b/server/src/api/update_channel_config.rs new file mode 100644 index 0000000..1f8cec2 --- /dev/null +++ b/server/src/api/update_channel_config.rs @@ -0,0 +1,75 @@ +use ldk_node::bitcoin::secp256k1::PublicKey; +use ldk_node::config::{ChannelConfig, MaxDustHTLCExposure}; +use ldk_node::{Node, UserChannelId}; +use protos::channel_config::MaxDustHtlcExposure; +use protos::{UpdateChannelConfigRequest, UpdateChannelConfigResponse}; +use std::str::FromStr; +use std::sync::Arc; + +pub(crate) const UPDATE_CHANNEL_CONFIG_PATH: &str = "UpdateChannelConfig"; + +pub(crate) fn handle_update_channel_config_request( + node: Arc, request: UpdateChannelConfigRequest, +) -> Result { + let user_channel_id: u128 = + request.user_channel_id.parse().map_err(|_| ldk_node::NodeError::InvalidChannelId)?; + + //FIXME: Use ldk/ldk-node's partial config update api. + let current_config = node + .list_channels() + .into_iter() + .find(|c| c.user_channel_id.0 == user_channel_id) + .ok_or_else(|| ldk_node::NodeError::InvalidChannelId)? + .config; + + let updated_channel_config = + build_updated_channel_config(current_config, request.channel_config.unwrap()); + + let counterparty_node_id = PublicKey::from_str(&request.counterparty_node_id) + .map_err(|_| ldk_node::NodeError::InvalidPublicKey)?; + node.update_channel_config( + &UserChannelId(user_channel_id), + counterparty_node_id, + updated_channel_config, + ) + .map_err(ldk_node::NodeError::from)?; + + Ok(UpdateChannelConfigResponse {}) +} + +fn build_updated_channel_config( + current_config: ChannelConfig, proto_channel_config: protos::ChannelConfig, +) -> ChannelConfig { + let max_dust_htlc_exposure = proto_channel_config + .max_dust_htlc_exposure + .map(|max_dust_htlc_exposure| match max_dust_htlc_exposure { + MaxDustHtlcExposure::FixedLimitMsat(limit_msat) => { + MaxDustHTLCExposure::FixedLimit { limit_msat } + }, + MaxDustHtlcExposure::FeeRateMultiplier(multiplier) => { + MaxDustHTLCExposure::FeeRateMultiplier { multiplier } + }, + }) + .unwrap_or(current_config.max_dust_htlc_exposure); + + let cltv_expiry_delta = proto_channel_config + .cltv_expiry_delta.map(|c| u16::try_from(c).unwrap()) + .unwrap_or(current_config.cltv_expiry_delta); + + ChannelConfig { + forwarding_fee_proportional_millionths: proto_channel_config + .forwarding_fee_proportional_millionths + .unwrap_or(current_config.forwarding_fee_proportional_millionths), + forwarding_fee_base_msat: proto_channel_config + .forwarding_fee_base_msat + .unwrap_or(current_config.forwarding_fee_base_msat), + cltv_expiry_delta, + max_dust_htlc_exposure, + force_close_avoidance_max_fee_satoshis: proto_channel_config + .force_close_avoidance_max_fee_satoshis + .unwrap_or(current_config.force_close_avoidance_max_fee_satoshis), + accept_underpaying_htlcs: proto_channel_config + .accept_underpaying_htlcs + .unwrap_or(current_config.accept_underpaying_htlcs), + } +} diff --git a/server/src/service.rs b/server/src/service.rs index 510131d..b8fb525 100644 --- a/server/src/service.rs +++ b/server/src/service.rs @@ -25,6 +25,9 @@ use crate::api::list_payments::{handle_list_payments_request, LIST_PAYMENTS_PATH use crate::api::onchain_receive::{handle_onchain_receive_request, ONCHAIN_RECEIVE_PATH}; use crate::api::onchain_send::{handle_onchain_send_request, ONCHAIN_SEND_PATH}; use crate::api::open_channel::{handle_open_channel, OPEN_CHANNEL_PATH}; +use crate::api::update_channel_config::{ + handle_update_channel_config_request, UPDATE_CHANNEL_CONFIG_PATH, +}; #[derive(Clone)] pub struct NodeService { @@ -62,6 +65,9 @@ impl Service> for NodeService { OPEN_CHANNEL_PATH => Box::pin(handle_request(node, req, handle_open_channel)), CLOSE_CHANNEL_PATH => Box::pin(handle_request(node, req, handle_close_channel_request)), LIST_CHANNELS_PATH => Box::pin(handle_request(node, req, handle_list_channels_request)), + UPDATE_CHANNEL_CONFIG_PATH => { + Box::pin(handle_request(node, req, handle_update_channel_config_request)) + }, GET_PAYMENT_DETAILS_PATH => { Box::pin(handle_request(node, req, handle_get_payment_details_request)) },