Merge pull request #3596 from joostjager/inbound-channel-config-override

Allow to override config defaults for inbound channels on a per-channel basis
This commit is contained in:
Matt Corallo 2025-02-19 23:05:59 +00:00 committed by GitHub
commit cdc8e2130d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 253 additions and 55 deletions

View file

@ -809,6 +809,7 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
temporary_channel_id,
counterparty_node_id,
user_channel_id,
None,
)
.unwrap();
} else {

View file

@ -74,7 +74,7 @@ fn do_test_open_channel(zero_conf: bool) {
match &events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(
temporary_channel_id, &nodes[0].node.get_our_node_id(), 0)
temporary_channel_id, &nodes[0].node.get_our_node_id(), 0, None)
.expect("Unable to accept inbound zero-conf channel");
},
ev => panic!("Expected OpenChannelRequest, not {:?}", ev)
@ -319,7 +319,7 @@ fn do_test_funding_signed_0conf(signer_ops: Vec<SignerOp>) {
match &events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(
temporary_channel_id, &nodes[0].node.get_our_node_id(), 0)
temporary_channel_id, &nodes[0].node.get_our_node_id(), 0, None)
.expect("Unable to accept inbound zero-conf channel");
},
ev => panic!("Expected OpenChannelRequest, not {:?}", ev)

View file

@ -2775,9 +2775,9 @@ fn do_test_outbound_reload_without_init_mon(use_0conf: bool) {
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
if use_0conf {
nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0).unwrap();
nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0, None).unwrap();
} else {
nodes[1].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0).unwrap();
nodes[1].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0, None).unwrap();
}
},
_ => panic!("Unexpected event"),
@ -2866,9 +2866,9 @@ fn do_test_inbound_reload_without_init_mon(use_0conf: bool, lock_commitment: boo
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
if use_0conf {
nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0).unwrap();
nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0, None).unwrap();
} else {
nodes[1].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0).unwrap();
nodes[1].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0, None).unwrap();
}
},
_ => panic!("Unexpected event"),

View file

@ -80,7 +80,7 @@ use crate::onion_message::messenger::{Destination, MessageRouter, Responder, Res
use crate::onion_message::offers::{OffersMessage, OffersMessageHandler};
use crate::sign::{EntropySource, NodeSigner, Recipient, SignerProvider};
use crate::sign::ecdsa::EcdsaChannelSigner;
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
use crate::util::config::{ChannelConfig, ChannelConfigUpdate, ChannelConfigOverrides, UserConfig};
use crate::util::wakers::{Future, Notifier};
use crate::util::scid_utils::fake_scid;
use crate::util::string::UntrustedString;
@ -1902,7 +1902,7 @@ where
///
/// let user_channel_id = 43;
/// match channel_manager.accept_inbound_channel(
/// &temporary_channel_id, &counterparty_node_id, user_channel_id
/// &temporary_channel_id, &counterparty_node_id, user_channel_id, None
/// ) {
/// Ok(()) => println!("Accepting channel {}", temporary_channel_id),
/// Err(e) => println!("Error accepting channel {}: {:?}", temporary_channel_id, e),
@ -7755,8 +7755,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
///
/// [`Event::OpenChannelRequest`]: events::Event::OpenChannelRequest
/// [`Event::ChannelClosed::user_channel_id`]: events::Event::ChannelClosed::user_channel_id
pub fn accept_inbound_channel(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, user_channel_id: u128) -> Result<(), APIError> {
self.do_accept_inbound_channel(temporary_channel_id, counterparty_node_id, false, user_channel_id)
pub fn accept_inbound_channel(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, user_channel_id: u128, config_overrides: Option<ChannelConfigOverrides>) -> Result<(), APIError> {
self.do_accept_inbound_channel(temporary_channel_id, counterparty_node_id, false, user_channel_id, config_overrides)
}
/// Accepts a request to open a channel after a [`events::Event::OpenChannelRequest`], treating
@ -7777,15 +7777,23 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
///
/// [`Event::OpenChannelRequest`]: events::Event::OpenChannelRequest
/// [`Event::ChannelClosed::user_channel_id`]: events::Event::ChannelClosed::user_channel_id
pub fn accept_inbound_channel_from_trusted_peer_0conf(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, user_channel_id: u128) -> Result<(), APIError> {
self.do_accept_inbound_channel(temporary_channel_id, counterparty_node_id, true, user_channel_id)
pub fn accept_inbound_channel_from_trusted_peer_0conf(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, user_channel_id: u128, config_overrides: Option<ChannelConfigOverrides>) -> Result<(), APIError> {
self.do_accept_inbound_channel(temporary_channel_id, counterparty_node_id, true, user_channel_id, config_overrides)
}
/// TODO(dual_funding): Allow contributions, pass intended amount and inputs
fn do_accept_inbound_channel(
&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, accept_0conf: bool,
user_channel_id: u128,
user_channel_id: u128, config_overrides: Option<ChannelConfigOverrides>
) -> Result<(), APIError> {
let mut config = self.default_configuration.clone();
// Apply configuration overrides.
if let Some(overrides) = config_overrides {
config.apply(&overrides);
};
let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), Some(*temporary_channel_id), None);
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
@ -7815,7 +7823,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
InboundV1Channel::new(
&self.fee_estimator, &self.entropy_source, &self.signer_provider, *counterparty_node_id,
&self.channel_type_features(), &peer_state.latest_features, &open_channel_msg,
user_channel_id, &self.default_configuration, best_block_height, &self.logger, accept_0conf
user_channel_id, &config, best_block_height, &self.logger, accept_0conf
).map_err(|err| MsgHandleErrInternal::from_chan_no_close(err, *temporary_channel_id)
).map(|mut channel| {
let logger = WithChannelContext::from(&self.logger, &channel.context, None);
@ -7835,7 +7843,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
self.get_our_node_id(), *counterparty_node_id,
&self.channel_type_features(), &peer_state.latest_features,
&open_channel_msg,
user_channel_id, &self.default_configuration, best_block_height,
user_channel_id, &config, best_block_height,
&self.logger,
).map_err(|_| MsgHandleErrInternal::from_chan_no_close(
ChannelError::Close(
@ -14657,9 +14665,9 @@ mod tests {
use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
use crate::ln::types::ChannelId;
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
use crate::ln::channelmanager::{create_recv_pending_htlc_info, HTLCForwardInfo, inbound_payment, PaymentId, RecipientOnionFields, InterceptId};
use crate::ln::channelmanager::{create_recv_pending_htlc_info, inbound_payment, ChannelConfigOverrides, HTLCForwardInfo, InterceptId, PaymentId, RecipientOnionFields};
use crate::ln::functional_test_utils::*;
use crate::ln::msgs::{self, ErrorAction};
use crate::ln::msgs::{self, AcceptChannel, ErrorAction};
use crate::ln::msgs::ChannelMessageHandler;
use crate::ln::outbound_payment::Retry;
use crate::prelude::*;
@ -14667,7 +14675,7 @@ mod tests {
use crate::util::errors::APIError;
use crate::util::ser::Writeable;
use crate::util::test_utils;
use crate::util::config::{ChannelConfig, ChannelConfigUpdate};
use crate::util::config::{ChannelConfig, ChannelConfigUpdate, ChannelHandshakeConfigUpdate};
use crate::sign::EntropySource;
#[test]
@ -15480,7 +15488,7 @@ mod tests {
// Test the API functions.
check_not_connected_to_peer_error(nodes[0].node.create_channel(unkown_public_key, 1_000_000, 500_000_000, 42, None, None), unkown_public_key);
check_unkown_peer_error(nodes[0].node.accept_inbound_channel(&channel_id, &unkown_public_key, 42), unkown_public_key);
check_unkown_peer_error(nodes[0].node.accept_inbound_channel(&channel_id, &unkown_public_key, 42, None), unkown_public_key);
check_unkown_peer_error(nodes[0].node.close_channel(&channel_id, &unkown_public_key), unkown_public_key);
@ -15511,7 +15519,7 @@ mod tests {
let error_message = "Channel force-closed";
// Test the API functions.
check_api_misuse_error(nodes[0].node.accept_inbound_channel(&channel_id, &counterparty_node_id, 42));
check_api_misuse_error(nodes[0].node.accept_inbound_channel(&channel_id, &counterparty_node_id, 42, None));
check_channel_unavailable_error(nodes[0].node.close_channel(&channel_id, &counterparty_node_id), channel_id, counterparty_node_id);
@ -15702,7 +15710,7 @@ mod tests {
let events = nodes[1].node.get_and_clear_pending_events();
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
nodes[1].node.accept_inbound_channel(&temporary_channel_id, &random_pk, 23).unwrap();
nodes[1].node.accept_inbound_channel(&temporary_channel_id, &random_pk, 23, None).unwrap();
}
_ => panic!("Unexpected event"),
}
@ -15720,7 +15728,7 @@ mod tests {
let events = nodes[1].node.get_and_clear_pending_events();
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
match nodes[1].node.accept_inbound_channel(&temporary_channel_id, &last_random_pk, 23) {
match nodes[1].node.accept_inbound_channel(&temporary_channel_id, &last_random_pk, 23, None) {
Err(APIError::APIMisuseError { err }) =>
assert_eq!(err, "Too many peers with unfunded channels, refusing to accept new ones"),
_ => panic!(),
@ -15736,7 +15744,7 @@ mod tests {
let events = nodes[1].node.get_and_clear_pending_events();
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &last_random_pk, 23).unwrap();
nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &last_random_pk, 23, None).unwrap();
}
_ => panic!("Unexpected event"),
}
@ -15819,6 +15827,33 @@ mod tests {
#[test]
fn test_inbound_anchors_manual_acceptance() {
test_inbound_anchors_manual_acceptance_with_override(None);
}
#[test]
fn test_inbound_anchors_manual_acceptance_overridden() {
let overrides = ChannelConfigOverrides {
handshake_overrides: Some(ChannelHandshakeConfigUpdate {
max_inbound_htlc_value_in_flight_percent_of_channel: Some(5),
htlc_minimum_msat: Some(1000),
minimum_depth: Some(2),
to_self_delay: Some(200),
max_accepted_htlcs: Some(5),
channel_reserve_proportional_millionths: Some(20000),
}),
update_overrides: None,
};
let accept_message = test_inbound_anchors_manual_acceptance_with_override(Some(overrides));
assert_eq!(accept_message.common_fields.max_htlc_value_in_flight_msat, 5_000_000);
assert_eq!(accept_message.common_fields.htlc_minimum_msat, 1_000);
assert_eq!(accept_message.common_fields.minimum_depth, 2);
assert_eq!(accept_message.common_fields.to_self_delay, 200);
assert_eq!(accept_message.common_fields.max_accepted_htlcs, 5);
assert_eq!(accept_message.channel_reserve_satoshis, 2_000);
}
fn test_inbound_anchors_manual_acceptance_with_override(config_overrides: Option<ChannelConfigOverrides>) -> AcceptChannel {
// Tests that we properly limit inbound channels when we have the manual-channel-acceptance
// flag set and (sometimes) accept channels as 0conf.
let mut anchors_cfg = test_default_channel_config();
@ -15855,10 +15890,10 @@ mod tests {
let events = nodes[2].node.get_and_clear_pending_events();
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } =>
nodes[2].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 23).unwrap(),
nodes[2].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 23, config_overrides).unwrap(),
_ => panic!("Unexpected event"),
}
get_event_msg!(nodes[2], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
get_event_msg!(nodes[2], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id())
}
#[test]
@ -15943,10 +15978,12 @@ mod tests {
let new_fee = user_config.channel_config.forwarding_fee_proportional_millionths + 100;
nodes[0].node.update_partial_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &ChannelConfigUpdate {
forwarding_fee_proportional_millionths: Some(new_fee),
accept_underpaying_htlcs: Some(true),
..Default::default()
}).unwrap();
assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().cltv_expiry_delta, new_cltv_expiry_delta);
assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().forwarding_fee_proportional_millionths, new_fee);
assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().accept_underpaying_htlcs, true);
let events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
match &events[0] {

View file

@ -27,7 +27,7 @@ use crate::onion_message::messenger::OnionMessenger;
use crate::routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate};
use crate::routing::router::{self, PaymentParameters, Route, RouteParameters};
use crate::sign::{EntropySource, RandomBytes};
use crate::util::config::{UserConfig, MaxDustHTLCExposure};
use crate::util::config::{MaxDustHTLCExposure, UserConfig};
#[cfg(test)]
use crate::util::logger::Logger;
use crate::util::scid_utils;
@ -1327,7 +1327,7 @@ pub fn open_zero_conf_channel<'a, 'b, 'c, 'd>(initiator: &'a Node<'b, 'c, 'd>, r
assert_eq!(events.len(), 1);
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
receiver.node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &initiator.node.get_our_node_id(), 0).unwrap();
receiver.node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &initiator.node.get_our_node_id(), 0, None).unwrap();
},
_ => panic!("Unexpected event"),
};
@ -1395,7 +1395,7 @@ pub fn exchange_open_accept_chan<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b:
assert_eq!(events.len(), 1);
match &events[0] {
Event::OpenChannelRequest { temporary_channel_id, counterparty_node_id, .. } =>
node_b.node.accept_inbound_channel(temporary_channel_id, counterparty_node_id, 42).unwrap(),
node_b.node.accept_inbound_channel(temporary_channel_id, counterparty_node_id, 42, None).unwrap(),
_ => panic!("Unexpected event"),
};
}

View file

@ -22,7 +22,7 @@ use crate::events::bump_transaction::WalletSource;
use crate::events::{Event, FundingInfo, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination, PaymentFailureReason};
use crate::ln::types::ChannelId;
use crate::types::payment::{PaymentPreimage, PaymentSecret, PaymentHash};
use crate::ln::channel::{CONCURRENT_INBOUND_HTLC_FEE_BUFFER, FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_AFFORDABLE_HTLC_COUNT, get_holder_selected_channel_reserve_satoshis, OutboundV1Channel, InboundV1Channel, COINBASE_MATURITY, Channel};
use crate::ln::channel::{get_holder_selected_channel_reserve_satoshis, Channel, InboundV1Channel, OutboundV1Channel, COINBASE_MATURITY, CONCURRENT_INBOUND_HTLC_FEE_BUFFER, FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_AFFORDABLE_HTLC_COUNT};
use crate::ln::channelmanager::{self, PaymentId, RAACommitmentOrder, RecipientOnionFields, BREAKDOWN_TIMEOUT, ENABLE_GOSSIP_TICKS, DISABLE_GOSSIP_TICKS, MIN_CLTV_EXPIRY_DELTA};
use crate::ln::channel::{DISCONNECT_PEER_AWAITING_RESPONSE_TICKS, ChannelError};
use crate::ln::{chan_utils, onion_utils};
@ -30,14 +30,14 @@ use crate::ln::chan_utils::{commitment_tx_base_weight, COMMITMENT_TX_WEIGHT_PER_
use crate::routing::gossip::{NetworkGraph, NetworkUpdate};
use crate::routing::router::{Path, PaymentParameters, Route, RouteHop, get_route, RouteParameters};
use crate::types::features::{ChannelFeatures, ChannelTypeFeatures, NodeFeatures};
use crate::ln::msgs;
use crate::ln::msgs::{self, AcceptChannel};
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
use crate::util::test_channel_signer::TestChannelSigner;
use crate::util::test_utils::{self, TestLogger, WatchtowerPersister};
use crate::util::errors::APIError;
use crate::util::ser::{Writeable, ReadableArgs};
use crate::util::string::UntrustedString;
use crate::util::config::{UserConfig, MaxDustHTLCExposure};
use crate::util::config::{ChannelConfigOverrides, ChannelHandshakeConfigUpdate, ChannelConfigUpdate, MaxDustHTLCExposure, UserConfig};
use bitcoin::hash_types::BlockHash;
use bitcoin::locktime::absolute::LockTime;
@ -8408,12 +8408,14 @@ fn test_channel_update_has_correct_htlc_maximum_msat() {
fn test_manually_accept_inbound_channel_request() {
let mut manually_accept_conf = UserConfig::default();
manually_accept_conf.manually_accept_inbound_channels = true;
manually_accept_conf.channel_handshake_config.minimum_depth = 1;
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(manually_accept_conf.clone())]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
let temp_channel_id = nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None, Some(manually_accept_conf)).unwrap();
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None, Some(manually_accept_conf)).unwrap();
let res = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
nodes[1].node.handle_open_channel(nodes[0].node.get_our_node_id(), &res);
@ -8422,10 +8424,28 @@ fn test_manually_accept_inbound_channel_request() {
// accepting the inbound channel request.
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
let config_overrides = ChannelConfigOverrides {
handshake_overrides: Some(ChannelHandshakeConfigUpdate {
max_inbound_htlc_value_in_flight_percent_of_channel: None,
htlc_minimum_msat: None,
minimum_depth: None,
to_self_delay: None,
max_accepted_htlcs: Some(3),
channel_reserve_proportional_millionths: None,
}),
update_overrides: Some(ChannelConfigUpdate {
forwarding_fee_proportional_millionths: None,
forwarding_fee_base_msat: Some(555),
cltv_expiry_delta: None,
max_dust_htlc_exposure_msat: None,
force_close_avoidance_max_fee_satoshis: None,
accept_underpaying_htlcs: None,
}),
};
let events = nodes[1].node.get_and_clear_pending_events();
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
nodes[1].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 23).unwrap();
nodes[1].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 23, Some(config_overrides)).unwrap();
}
_ => panic!("Unexpected event"),
}
@ -8433,25 +8453,65 @@ fn test_manually_accept_inbound_channel_request() {
let accept_msg_ev = nodes[1].node.get_and_clear_pending_msg_events();
assert_eq!(accept_msg_ev.len(), 1);
let ref accept_channel: AcceptChannel;
match accept_msg_ev[0] {
MessageSendEvent::SendAcceptChannel { ref node_id, .. } => {
MessageSendEvent::SendAcceptChannel { ref node_id, ref msg } => {
assert_eq!(*node_id, nodes[0].node.get_our_node_id());
// Assert overriden handshake parameter.
assert_eq!(msg.common_fields.max_accepted_htlcs, 3);
accept_channel = msg;
}
_ => panic!("Unexpected event"),
}
let error_message = "Channel force-closed";
nodes[1].node.force_close_broadcasting_latest_txn(&temp_channel_id, &nodes[0].node.get_our_node_id(), error_message.to_string()).unwrap();
let close_msg_ev = nodes[1].node.get_and_clear_pending_msg_events();
assert_eq!(close_msg_ev.len(), 1);
// Continue channel opening process until channel update messages are sent.
nodes[0].node.handle_accept_channel(nodes[1].node.get_our_node_id(), &accept_channel);
let (temporary_channel_id, tx, funding_outpoint) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 100_000, 42);
nodes[0].node.unsafe_manual_funding_transaction_generated(temporary_channel_id, nodes[1].node.get_our_node_id(), funding_outpoint).unwrap();
check_added_monitors!(nodes[0], 0);
let events = nodes[1].node.get_and_clear_pending_events();
match events[0] {
Event::ChannelClosed { user_channel_id, .. } => {
assert_eq!(user_channel_id, 23);
}
let funding_created = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
nodes[1].node.handle_funding_created(nodes[0].node.get_our_node_id(), &funding_created);
check_added_monitors!(nodes[1], 1);
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
let funding_signed = get_event_msg!(nodes[1], MessageSendEvent::SendFundingSigned, nodes[0].node.get_our_node_id());
nodes[0].node.handle_funding_signed(nodes[1].node.get_our_node_id(), &funding_signed);
check_added_monitors!(nodes[0], 1);
let events = &nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 2);
match &events[0] {
crate::events::Event::FundingTxBroadcastSafe { funding_txo, .. } => {
assert_eq!(funding_txo.txid, funding_outpoint.txid);
assert_eq!(funding_txo.vout, funding_outpoint.index.into());
},
_ => panic!("Unexpected event"),
}
};
match &events[1] {
crate::events::Event::ChannelPending { counterparty_node_id, .. } => {
assert_eq!(*&nodes[1].node.get_our_node_id(), *counterparty_node_id);
},
_ => panic!("Unexpected event"),
};
mine_transaction(&nodes[0], &tx);
mine_transaction(&nodes[1], &tx);
let as_channel_ready = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReady, nodes[0].node.get_our_node_id());
nodes[1].node.handle_channel_ready(nodes[0].node.get_our_node_id(), &as_channel_ready);
let as_channel_ready = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReady, nodes[1].node.get_our_node_id());
nodes[0].node.handle_channel_ready(nodes[1].node.get_our_node_id(), &as_channel_ready);
expect_channel_ready_event(&nodes[0], &nodes[1].node.get_our_node_id());
expect_channel_ready_event(&nodes[1], &nodes[0].node.get_our_node_id());
// Assert that the overriden base fee surfaces in the channel update.
let channel_update = get_event_msg!(nodes[1], MessageSendEvent::SendChannelUpdate, nodes[0].node.get_our_node_id());
assert_eq!(channel_update.contents.fee_base_msat, 555);
get_event_msg!(nodes[0], MessageSendEvent::SendChannelUpdate, nodes[1].node.get_our_node_id());
}
#[test]
@ -8515,8 +8575,8 @@ fn test_can_not_accept_inbound_channel_twice() {
let events = nodes[1].node.get_and_clear_pending_events();
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
nodes[1].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0).unwrap();
let api_res = nodes[1].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0);
nodes[1].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0, None).unwrap();
let api_res = nodes[1].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0, None);
match api_res {
Err(APIError::APIMisuseError { err }) => {
assert_eq!(err, "No such channel awaiting to be accepted.");
@ -8548,7 +8608,7 @@ fn test_can_not_accept_unknown_inbound_channel() {
let nodes = create_network(2, &node_cfg, &node_chanmgr);
let unknown_channel_id = ChannelId::new_zero();
let api_res = nodes[0].node.accept_inbound_channel(&unknown_channel_id, &nodes[1].node.get_our_node_id(), 0);
let api_res = nodes[0].node.accept_inbound_channel(&unknown_channel_id, &nodes[1].node.get_our_node_id(), 0, None);
match api_res {
Err(APIError::APIMisuseError { err }) => {
assert_eq!(err, "No such channel awaiting to be accepted.");
@ -11557,7 +11617,7 @@ fn test_accept_inbound_channel_errors_queued() {
let events = nodes[1].node.get_and_clear_pending_events();
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
match nodes[1].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 23) {
match nodes[1].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 23, None) {
Err(APIError::ChannelUnavailable { err: _ }) => (),
_ => panic!(),
}
@ -11667,4 +11727,3 @@ fn test_funding_signed_event() {
nodes[0].node.get_and_clear_pending_msg_events();
nodes[1].node.get_and_clear_pending_msg_events();
}

View file

@ -20,7 +20,7 @@ use crate::types::features::ChannelTypeFeatures;
use crate::ln::msgs;
use crate::ln::types::ChannelId;
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
use crate::util::config::{UserConfig, MaxDustHTLCExposure};
use crate::util::config::{MaxDustHTLCExposure, UserConfig};
use crate::util::ser::Writeable;
use crate::prelude::*;
@ -591,7 +591,7 @@ fn test_0conf_channel_with_async_monitor() {
assert_eq!(events.len(), 1);
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0).unwrap();
nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0, None).unwrap();
},
_ => panic!("Unexpected event"),
};
@ -919,7 +919,7 @@ fn test_zero_conf_accept_reject() {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
// Assert we fail to accept via the non-0conf method
assert!(nodes[1].node.accept_inbound_channel(&temporary_channel_id,
&nodes[0].node.get_our_node_id(), 0).is_err());
&nodes[0].node.get_our_node_id(), 0, None).is_err());
},
_ => panic!(),
}
@ -948,7 +948,7 @@ fn test_zero_conf_accept_reject() {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
// Assert we can accept via the 0conf method
assert!(nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(
&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0).is_ok());
&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0, None).is_ok());
},
_ => panic!(),
}
@ -983,7 +983,7 @@ fn test_connect_before_funding() {
assert_eq!(events.len(), 1);
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0).unwrap();
nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0, None).unwrap();
},
_ => panic!("Unexpected event"),
};

View file

@ -601,6 +601,9 @@ impl ChannelConfig {
{
self.force_close_avoidance_max_fee_satoshis = force_close_avoidance_max_fee_satoshis;
}
if let Some(accept_underpaying_htlcs) = update.accept_underpaying_htlcs {
self.accept_underpaying_htlcs = accept_underpaying_htlcs;
}
}
}
@ -674,14 +677,30 @@ impl crate::util::ser::Readable for ChannelConfig {
}
/// A parallel struct to [`ChannelConfig`] to define partial updates.
#[allow(missing_docs)]
#[derive(Default)]
pub struct ChannelConfigUpdate {
/// Amount (in millionths of a satoshi) charged per satoshi for payments forwarded outbound over the channel. See
/// [`ChannelConfig::forwarding_fee_proportional_millionths`].
pub forwarding_fee_proportional_millionths: Option<u32>,
/// Amount (in milli-satoshi) charged for payments forwarded outbound over the channel. See
/// [`ChannelConfig::forwarding_fee_base_msat`].
pub forwarding_fee_base_msat: Option<u32>,
/// The difference in the CLTV value between incoming HTLCs and an outbound HTLC forwarded over the channel this
/// config applies to. See [`ChannelConfig::cltv_expiry_delta`].
pub cltv_expiry_delta: Option<u16>,
/// The total exposure we are willing to allow to dust HTLCs. See [`ChannelConfig::max_dust_htlc_exposure`].
pub max_dust_htlc_exposure_msat: Option<MaxDustHTLCExposure>,
/// The additional fee we're willing to pay to avoid waiting for the counterparty's `to_self_delay` to reclaim
/// funds. See [`ChannelConfig::force_close_avoidance_max_fee_satoshis`].
pub force_close_avoidance_max_fee_satoshis: Option<u64>,
/// If set, allows this channel's counterparty to skim an additional fee off this node's inbound HTLCs. See
/// [`ChannelConfig::accept_underpaying_htlcs`].
pub accept_underpaying_htlcs: Option<bool>,
}
impl From<ChannelConfig> for ChannelConfigUpdate {
@ -696,6 +715,7 @@ impl From<ChannelConfig> for ChannelConfigUpdate {
force_close_avoidance_max_fee_satoshis: Some(
config.force_close_avoidance_max_fee_satoshis,
),
accept_underpaying_htlcs: Some(config.accept_underpaying_htlcs),
}
}
}
@ -893,3 +913,84 @@ impl Readable for UserConfig {
})
}
}
/// Config structure for overriding channel parameters.
#[derive(Default)]
pub struct ChannelConfigOverrides {
/// Overrides for channel handshake parameters.
pub handshake_overrides: Option<ChannelHandshakeConfigUpdate>,
/// Overrides for channel update parameters.
pub update_overrides: Option<ChannelConfigUpdate>,
}
impl UserConfig {
/// Applies given channel config overrides to the user config.
pub fn apply(&mut self, config: &ChannelConfigOverrides) {
if let Some(handshake_overrides) = &config.handshake_overrides {
self.channel_handshake_config.apply(&handshake_overrides);
}
if let Some(update_overrides) = &config.update_overrides {
self.channel_config.apply(&update_overrides);
}
}
}
/// Config structure for overriding channel handshake parameters.
#[derive(Default)]
pub struct ChannelHandshakeConfigUpdate {
/// Overrides the percentage of the channel value we will cap the total value of outstanding inbound HTLCs to. See
/// [`ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel`].
pub max_inbound_htlc_value_in_flight_percent_of_channel: Option<u8>,
/// Overrides the smallest value HTLC we will accept to process. See [`ChannelHandshakeConfig::our_htlc_minimum_msat`].
pub htlc_minimum_msat: Option<u64>,
/// Overrides confirmations we will wait for before considering the channel locked in. See
/// [`ChannelHandshakeConfig::minimum_depth`].
pub minimum_depth: Option<u32>,
/// Overrides the number of blocks we require our counterparty to wait to claim their money. See
/// [`ChannelHandshakeConfig::our_to_self_delay`].
pub to_self_delay: Option<u16>,
/// The maximum number of HTLCs in-flight from our counterparty towards us at the same time. See
/// [`ChannelHandshakeConfig::our_max_accepted_htlcs`].
pub max_accepted_htlcs: Option<u16>,
/// The Proportion of the channel value to configure as counterparty's channel reserve. See
/// [`ChannelHandshakeConfig::their_channel_reserve_proportional_millionths`].
pub channel_reserve_proportional_millionths: Option<u32>,
}
impl ChannelHandshakeConfig {
/// Applies the provided handshake config update.
pub fn apply(&mut self, config: &ChannelHandshakeConfigUpdate) {
if let Some(max_in_flight_percent) =
config.max_inbound_htlc_value_in_flight_percent_of_channel
{
self.max_inbound_htlc_value_in_flight_percent_of_channel = max_in_flight_percent;
}
if let Some(htlc_minimum_msat) = config.htlc_minimum_msat {
self.our_htlc_minimum_msat = htlc_minimum_msat;
}
if let Some(minimum_depth) = config.minimum_depth {
self.minimum_depth = minimum_depth;
}
if let Some(to_self_delay) = config.to_self_delay {
self.our_to_self_delay = to_self_delay;
}
if let Some(max_accepted_htlcs) = config.max_accepted_htlcs {
self.our_max_accepted_htlcs = max_accepted_htlcs;
}
if let Some(channel_reserve) = config.channel_reserve_proportional_millionths {
self.their_channel_reserve_proportional_millionths = channel_reserve;
}
}
}