Skip to content
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
70 changes: 63 additions & 7 deletions lightning/src/ln/channel_open_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ use crate::chain::transaction::OutPoint;
use crate::chain::{self, ChannelMonitorUpdateStatus};
use crate::events::{ClosureReason, Event, FundingInfo};
use crate::ln::channel::{
get_holder_selected_channel_reserve_satoshis, ChannelError, InboundV1Channel,
OutboundV1Channel, COINBASE_MATURITY, MIN_THEIR_CHAN_RESERVE_SATOSHIS,
UNFUNDED_CHANNEL_AGE_LIMIT_TICKS,
get_holder_selected_channel_reserve_satoshis, get_v2_channel_reserve_satoshis, ChannelError,
InboundV1Channel, OutboundV1Channel, COINBASE_MATURITY, MIN_CHAN_DUST_LIMIT_SATOSHIS,
MIN_THEIR_CHAN_RESERVE_SATOSHIS, UNFUNDED_CHANNEL_AGE_LIMIT_TICKS,
};
use crate::ln::channelmanager::{
self, TrustedChannelFeatures, BREAKDOWN_TIMEOUT, MAX_UNFUNDED_CHANNEL_PEERS,
Expand Down Expand Up @@ -233,15 +233,71 @@ fn do_test_manual_inbound_accept_with_override(
nodes[2].node.handle_open_channel(node_a, &open_channel_msg);
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, &node_a, 23, config_overrides)
.unwrap(),
Event::OpenChannelRequest { temporary_channel_id, ref params, .. } => {
assert_eq!(params.channel_reserve_satoshis, open_channel_msg.channel_reserve_satoshis);
nodes[2]
.node
.accept_inbound_channel(&temporary_channel_id, &node_a, 23, config_overrides)
.unwrap()
},
_ => panic!("Unexpected event"),
}
get_event_msg!(nodes[2], MessageSendEvent::SendAcceptChannel, node_a)
}

#[test]
fn test_open_channel_request_exposes_v2_channel_reserve() {
let mut dual_fund_cfg = test_default_channel_config();
dual_fund_cfg.enable_dual_funded_channels = true;

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, &[Some(dual_fund_cfg.clone()), Some(dual_fund_cfg)]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);

let node_a = nodes[0].node.get_our_node_id();
let node_b = nodes[1].node.get_our_node_id();
nodes[0].node.create_channel(node_b, 100_000, 0, 42, None, None).unwrap();
let open_channel = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, node_b);
let funding_feerate_sat_per_1000_weight =
open_channel.common_fields.commitment_feerate_sat_per_1000_weight;
let second_per_commitment_point = open_channel.common_fields.first_per_commitment_point;
let mut open_channel_v2 = msgs::OpenChannelV2 {
common_fields: open_channel.common_fields,
funding_feerate_sat_per_1000_weight,
locktime: 0,
second_per_commitment_point,
require_confirmed_inputs: None,
disable_channel_reserve: None,
};

nodes[1].node.handle_open_channel_v2(node_a, &open_channel_v2);
let events = nodes[1].node.get_and_clear_pending_events();
match events[0] {
Event::OpenChannelRequest { ref params, .. } => {
assert_eq!(
params.channel_reserve_satoshis,
get_v2_channel_reserve_satoshis(100_000, MIN_CHAN_DUST_LIMIT_SATOSHIS, false)
.unwrap()
);
},
_ => panic!("Unexpected event"),
}

open_channel_v2.common_fields.temporary_channel_id =
ChannelId::temporary_from_entropy_source(&nodes[0].keys_manager);
open_channel_v2.disable_channel_reserve = Some(());
nodes[1].node.handle_open_channel_v2(node_a, &open_channel_v2);
let events = nodes[1].node.get_and_clear_pending_events();
match events[0] {
Event::OpenChannelRequest { ref params, .. } => {
assert_eq!(params.channel_reserve_satoshis, 0);
},
_ => panic!("Unexpected event"),
}
}

#[test]
fn test_anchors_zero_fee_htlc_tx_downgrade() {
// Tests that if both nodes support anchors, but the remote node does not want to accept
Expand Down
31 changes: 30 additions & 1 deletion lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1910,6 +1910,34 @@ pub(super) enum OpenChannelMessageRef<'a> {
V2(&'a msgs::OpenChannelV2),
}

impl<'a> OpenChannelMessageRef<'a> {
fn channel_parameters(&self) -> Result<msgs::ChannelParameters, MsgHandleErrInternal> {
match self {
OpenChannelMessageRef::V1(msg) => {
Ok(msg.common_fields.channel_parameters(msg.channel_reserve_satoshis))
},
OpenChannelMessageRef::V2(msg) => {
let channel_value_satoshis = msg.common_fields.funding_satoshis;
let channel_reserve_satoshis = channel::get_v2_channel_reserve_satoshis(
channel_value_satoshis,
channel::MIN_CHAN_DUST_LIMIT_SATOSHIS,
msg.disable_channel_reserve.is_some(),
Comment on lines +1920 to +1924

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For V2 dual-funded channels the reserve is computed from only the peer's funding_satoshis, but the reserve actually enforced on the holder is derived at accept time from the total channel value (our_funding_contribution + msg.funding_satoshis, see InboundV2Channel::new). If the acceptor contributes funds (as is the whole point of dual funding), the value exposed here will be lower than the reserve that ultimately applies. At event time the acceptor's contribution isn't known yet, so this can't be fully accurate — but worth documenting on the field/changelog that for V2 this reflects only the initiator's contribution, otherwise consumers may rely on a value that doesn't match the final reserve.

)
.map_err(|()| {
MsgHandleErrInternal::send_err_msg_no_close(
format!(
"The channel value {channel_value_satoshis} is smaller than our dust limit {}",
channel::MIN_CHAN_DUST_LIMIT_SATOSHIS
),
msg.common_fields.temporary_channel_id,
)
})?;
Ok(msg.common_fields.channel_parameters(channel_reserve_satoshis))
},
}
}
}

/// A not-yet-accepted inbound (from counterparty) channel. Once
/// accepted, the parameters will be used to construct a channel.
pub(super) struct InboundChannelRequest {
Expand Down Expand Up @@ -11676,6 +11704,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/

let mut pending_events = self.pending_events.lock().unwrap();
let is_announced = (common_fields.channel_flags & 1) == 1;
let params = msg.channel_parameters()?;
pending_events.push_back((
events::Event::OpenChannelRequest {
temporary_channel_id: common_fields.temporary_channel_id,
Expand All @@ -11687,7 +11716,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
},
channel_type,
is_announced,
params: common_fields.channel_parameters(),
params,
},
None,
));
Expand Down
12 changes: 11 additions & 1 deletion lightning/src/ln/msgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,10 @@ pub struct CommonOpenChannelFields {

impl CommonOpenChannelFields {
/// The [`ChannelParameters`] for this channel.
pub fn channel_parameters(&self) -> ChannelParameters {
pub(crate) fn channel_parameters(&self, channel_reserve_satoshis: u64) -> ChannelParameters {
ChannelParameters {
dust_limit_satoshis: self.dust_limit_satoshis,
channel_reserve_satoshis,
max_htlc_value_in_flight_msat: self.max_htlc_value_in_flight_msat,
htlc_minimum_msat: self.htlc_minimum_msat,
commitment_feerate_sat_per_1000_weight: self.commitment_feerate_sat_per_1000_weight,
Expand All @@ -257,6 +258,15 @@ pub struct ChannelParameters {
/// The threshold below which outputs on transactions broadcast by the channel initiator will be
/// omitted.
pub dust_limit_satoshis: u64,
/// The minimum value unencumbered by HTLCs for the non-channel-initiator to keep in the
/// channel.
///
/// For V2 channels, this is computed from the initiator's funding contribution known when
/// [`Event::OpenChannelRequest`] is generated. The finally enforced reserve may be higher if
/// the acceptor later contributes additional funds.
///
/// [`Event::OpenChannelRequest`]: crate::events::Event::OpenChannelRequest
pub channel_reserve_satoshis: u64,
/// The maximum inbound HTLC value in flight towards channel initiator, in milli-satoshi
pub max_htlc_value_in_flight_msat: u64,
/// The minimum HTLC size for HTLCs towards the channel initiator, in milli-satoshi
Expand Down
11 changes: 11 additions & 0 deletions pending_changelog/3909-open-channel-request-reserve.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# API Updates

* `Event::OpenChannelRequest::params` now exposes the channel reserve in
`msgs::ChannelParameters::channel_reserve_satoshis`. For V1 channels this is
the reserve provided in `open_channel`; for V2 channels this is derived from
the initiator's funding contribution, the V2 reserve rules, and
`disable_channel_reserve`. For V2 channels the finally enforced reserve may
be higher if the acceptor later contributes additional funds.
`msgs::CommonOpenChannelFields::channel_parameters` is now crate-internal
because common open-channel fields alone do not contain enough information to
construct the full `ChannelParameters`.