Skip to content

Commit

Permalink
Add InboundV2Channel struct
Browse files Browse the repository at this point in the history
  • Loading branch information
dunxen committed Dec 5, 2023
1 parent 36aff73 commit 2b13ddb
Showing 1 changed file with 185 additions and 4 deletions.
189 changes: 185 additions & 4 deletions lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,11 @@ enum HTLCUpdateAwaitingACK {
/// `ChannelReady` can then get all remaining flags set on it, until we finish shutdown, then we
/// move on to `ShutdownComplete`, at which point most calls into this channel are disallowed.
enum ChannelState {
/// Implies we have (or are prepared to) send our open_channel/accept_channel message
/// Implies we have (or are prepared to) send our open_channel/accept_channel message or in the
/// case of V2 establishment, our open_channel2/accept_channel2 message
OurInitSent = 1 << 0,
/// Implies we have received their `open_channel`/`accept_channel` message
/// Implies we have received their `open_channel`/`accept_channel` message or in the case of
/// V2 establishment, their `open_channel2`/`accept_channel2` message
TheirInitSent = 1 << 1,
/// We have sent `funding_created` and are awaiting a `funding_signed` to advance to `FundingSent`.
/// Note that this is nonsense for an inbound channel as we immediately generate `funding_signed`
Expand Down Expand Up @@ -2651,6 +2653,20 @@ pub(crate) fn get_legacy_default_holder_selected_channel_reserve_satoshis(channe
cmp::min(channel_value_satoshis, cmp::max(q, 1000))
}

/// Returns a minimum channel reserve value each party needs to maintain, fixed in the spec to a
/// default of 1% of the total channel value.
///
/// Guaranteed to return a value no larger than channel_value_satoshis
///
/// This is used both for outbound and inbound channels and has lower bound
/// of `dust_limit_satoshis`.
fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satoshis: u64) -> u64 {
let channel_reserve_proportional_millionths = 10_000; // Fixed at 1% in spec.
let calculated_reserve =
channel_value_satoshis.saturating_mul(channel_reserve_proportional_millionths) / 1_000_000;
cmp::min(channel_value_satoshis, cmp::max(calculated_reserve, dust_limit_satoshis))
}

// Get the fee cost in SATS of a commitment tx with a given number of HTLC outputs.
// Note that num_htlcs should not include dust HTLCs.
#[inline]
Expand Down Expand Up @@ -2684,6 +2700,8 @@ pub(super) struct DualFundingChannelContext {
// Counterparty designates channel data owned by the another channel participant entity.
pub(super) struct Channel<SP: Deref> where SP::Target: SignerProvider {
pub context: ChannelContext<SP>,
#[cfg(dual_funding)]
pub dual_funding_channel_context: Option<DualFundingChannelContext>,
}

#[cfg(any(test, fuzzing))]
Expand Down Expand Up @@ -6617,6 +6635,8 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {

let channel = Channel {
context: self.context,
#[cfg(dual_funding)]
dual_funding_channel_context: None,
};

Ok((channel, funding_created))
Expand Down Expand Up @@ -7089,6 +7109,8 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
// `ChannelMonitor`.
let mut channel = Channel {
context: self.context,
#[cfg(dual_funding)]
dual_funding_channel_context: None,
};
let need_channel_ready = channel.check_get_channel_ready(0).is_some();
channel.monitor_updating_paused(false, false, need_channel_ready, Vec::new(), Vec::new(), Vec::new());
Expand All @@ -7097,6 +7119,159 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
}
}

// A not-yet-funded inbound (from counterparty) channel using V2 channel establishment.
#[cfg(dual_funding)]
pub(super) struct InboundV2Channel<SP: Deref> where SP::Target: SignerProvider {
pub context: ChannelContext<SP>,
pub unfunded_context: UnfundedChannelContext,
pub dual_funding_context: DualFundingChannelContext,
}

#[cfg(dual_funding)]
impl<SP: Deref> InboundV2Channel<SP> where SP::Target: SignerProvider {
/// Creates a new dual-funded channel from a remote side's request for one.
/// Assumes chain_hash has already been checked and corresponds with what we expect!
pub fn new<ES: Deref, F: Deref, L: Deref>(
fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP,
counterparty_node_id: PublicKey, our_supported_features: &ChannelTypeFeatures,
their_features: &InitFeatures, msg: &msgs::OpenChannelV2, funding_satoshis: u64, user_id: u128,
config: &UserConfig, current_chain_height: u32, logger: &L,
) -> Result<InboundV2Channel<SP>, ChannelError>
where ES::Target: EntropySource,
F::Target: FeeEstimator,
L::Target: Logger,
{
// TODO(dual_funding): Fix this
let channel_value_satoshis = funding_satoshis * 1000 + msg.funding_satoshis;
let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
channel_value_satoshis, msg.dust_limit_satoshis);
let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
channel_value_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS);

let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: msg.funding_pubkey,
revocation_basepoint: RevocationBasepoint(msg.revocation_basepoint),
payment_point: msg.payment_basepoint,
delayed_payment_basepoint: DelayedPaymentBasepoint(msg.delayed_payment_basepoint),
htlc_basepoint: HtlcBasepoint(msg.htlc_basepoint)
};

let mut context = ChannelContext::new_for_inbound_channel(
fee_estimator,
entropy_source,
signer_provider,
counterparty_node_id,
our_supported_features,
their_features,
user_id,
config,
current_chain_height,
logger,
false,

funding_satoshis,

counterparty_pubkeys,
msg.channel_flags,
msg.channel_type.clone(),
msg.funding_satoshis,
msg.to_self_delay,
holder_selected_channel_reserve_satoshis,
counterparty_selected_channel_reserve_satoshis,
0 /* push_msat not used in dual-funding */,
msg.dust_limit_satoshis,
msg.htlc_minimum_msat,
msg.commitment_feerate_sat_per_1000_weight,
msg.max_accepted_htlcs,
msg.shutdown_scriptpubkey.clone(),
msg.max_htlc_value_in_flight_msat,
msg.temporary_channel_id,
msg.first_per_commitment_point,
)?;
let channel_id = ChannelId::v2_from_revocation_basepoints(
&context.get_holder_pubkeys().revocation_basepoint,
&context.get_counterparty_pubkeys().revocation_basepoint);
context.channel_id = channel_id;

let chan = Self {
context,
unfunded_context: UnfundedChannelContext { unfunded_channel_age_ticks: 0 },
dual_funding_context: DualFundingChannelContext {
our_funding_satoshis: funding_satoshis,
their_funding_satoshis: msg.funding_satoshis,
funding_tx_locktime: msg.locktime,
funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
}
};

Ok(chan)
}

/// Marks an inbound channel as accepted and generates a [`msgs::AcceptChannelV2`] message which
/// should be sent back to the counterparty node.
///
/// [`msgs::AcceptChannelV2`]: crate::ln::msgs::AcceptChannelV2
pub fn accept_inbound_dual_funded_channel(&mut self) -> msgs::AcceptChannelV2 {
if self.context.is_outbound() {
panic!("Tried to send accept_channel2 for an outbound channel?");
}
if self.context.channel_state != (ChannelState::OurInitSent as u32) | (ChannelState::TheirInitSent as u32) {
panic!("Tried to send accept_channel2 after channel had moved forward");
}
if self.context.cur_holder_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER {
panic!("Tried to send an accept_channel2 for a channel that has already advanced");
}

self.generate_accept_channel_v2_message()
}

/// This function is used to explicitly generate a [`msgs::AcceptChannel`] message for an
/// inbound channel. If the intention is to accept an inbound channel, use
/// [`InboundV1Channel::accept_inbound_channel`] instead.
///
/// [`msgs::AcceptChannelV2`]: crate::ln::msgs::AcceptChannelV2
fn generate_accept_channel_v2_message(&self) -> msgs::AcceptChannelV2 {
let first_per_commitment_point = self.context.holder_signer.as_ref().get_per_commitment_point(
self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx);
let second_per_commitment_point = self.context.holder_signer.as_ref().get_per_commitment_point(
self.context.cur_holder_commitment_transaction_number - 1, &self.context.secp_ctx);
let keys = self.context.get_holder_pubkeys();

msgs::AcceptChannelV2 {
temporary_channel_id: self.context.temporary_channel_id.unwrap(),
funding_satoshis: self.dual_funding_context.our_funding_satoshis,
dust_limit_satoshis: self.context.holder_dust_limit_satoshis,
max_htlc_value_in_flight_msat: self.context.holder_max_htlc_value_in_flight_msat,
htlc_minimum_msat: self.context.holder_htlc_minimum_msat,
minimum_depth: self.context.minimum_depth.unwrap(),
to_self_delay: self.context.get_holder_selected_contest_delay(),
max_accepted_htlcs: self.context.holder_max_accepted_htlcs,
funding_pubkey: keys.funding_pubkey,
revocation_basepoint: keys.revocation_basepoint.to_public_key(),
payment_basepoint: keys.payment_point,
delayed_payment_basepoint: keys.delayed_payment_basepoint.to_public_key(),
htlc_basepoint: keys.htlc_basepoint.to_public_key(),
first_per_commitment_point,
second_per_commitment_point,
shutdown_scriptpubkey: Some(match &self.context.shutdown_scriptpubkey {
Some(script) => script.clone().into_inner(),
None => Builder::new().into_script(),
}),
channel_type: Some(self.context.channel_type.clone()),
require_confirmed_inputs: None,
}
}

/// Enables the possibility for tests to extract a [`msgs::AcceptChannelV2`] message for an
/// inbound channel without accepting it.
///
/// [`msgs::AcceptChannelV2`]: crate::ln::msgs::AcceptChannelV2
#[cfg(test)]
pub fn get_accept_channel_v2_message(&self) -> msgs::AcceptChannelV2 {
self.generate_accept_channel_v2_message()
}
}

const SERIALIZATION_VERSION: u8 = 3;
const MIN_SERIALIZATION_VERSION: u8 = 3;

Expand Down Expand Up @@ -8006,7 +8181,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
channel_keys_id,

blocked_monitor_updates: blocked_monitor_updates.unwrap(),
}
},
#[cfg(dual_funding)]
dual_funding_channel_context: None,
})
}
}
Expand Down Expand Up @@ -8564,7 +8741,11 @@ mod tests {
let config = UserConfig::default();
let features = channelmanager::provided_init_features(&config);
let outbound_chan = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &features, 10000000, 100000, 42, &config, 0, 42, None).unwrap();
let mut chan = Channel { context: outbound_chan.context };
let mut chan = Channel {
context: outbound_chan.context,
#[cfg(dual_funding)]
dual_funding_channel_context: None,
};

let dummy_htlc_source = HTLCSource::OutboundRoute {
path: Path {
Expand Down

0 comments on commit 2b13ddb

Please sign in to comment.