Skip to content

Commit f312357

Browse files
dunxenjkczyz
authored andcommitted
Allow passing funding inputs when manually accepting a dual-funded channel
We introduce a `ChannelManager::accept_inbound_channel_with_contribution` method allowing contributing to the overall channel capacity of an inbound dual-funded channel by contributing inputs.
1 parent 9150bc8 commit f312357

3 files changed

Lines changed: 118 additions & 42 deletions

File tree

lightning/src/ln/channel.rs

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13946,7 +13946,7 @@ where
1394613946
pub funding: FundingScope,
1394713947
pub context: ChannelContext<SP>,
1394813948
pub unfunded_context: UnfundedChannelContext,
13949-
pub funding_negotiation_context: FundingNegotiationContext,
13949+
pub funding_negotiation_context: Option<FundingNegotiationContext>,
1395013950
/// The current interactive transaction construction session under negotiation.
1395113951
pub interactive_tx_constructor: Option<InteractiveTxConstructor>,
1395213952
}
@@ -14021,7 +14021,7 @@ where
1402114021
funding,
1402214022
context,
1402314023
unfunded_context,
14024-
funding_negotiation_context,
14024+
funding_negotiation_context: Some(funding_negotiation_context),
1402514025
interactive_tx_constructor: None,
1402614026
};
1402714027
Ok(chan)
@@ -14096,29 +14096,28 @@ where
1409614096
},
1409714097
funding_feerate_sat_per_1000_weight: self.context.feerate_per_kw,
1409814098
second_per_commitment_point,
14099-
locktime: self.funding_negotiation_context.funding_tx_locktime.to_consensus_u32(),
14099+
locktime: self.funding_tx_locktime().to_consensus_u32(),
1410014100
require_confirmed_inputs: None,
1410114101
}
1410214102
}
1410314103

1410414104
/// Creates a new dual-funded channel from a remote side's request for one.
1410514105
/// Assumes chain_hash has already been checked and corresponds with what we expect!
14106-
/// TODO(dual_funding): Allow contributions, pass intended amount and inputs
1410714106
#[allow(dead_code)] // TODO(dual_funding): Remove once V2 channels is enabled.
1410814107
#[rustfmt::skip]
1410914108
pub fn new_inbound<ES: Deref, F: Deref, L: Deref>(
1411014109
fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP,
1411114110
holder_node_id: PublicKey, counterparty_node_id: PublicKey, our_supported_features: &ChannelTypeFeatures,
14112-
their_features: &InitFeatures, msg: &msgs::OpenChannelV2,
14113-
user_id: u128, config: &UserConfig, current_chain_height: u32, logger: &L,
14111+
their_features: &InitFeatures, msg: &msgs::OpenChannelV2, user_id: u128, config: &UserConfig,
14112+
current_chain_height: u32, logger: &L, our_funding_contribution: Amount,
14113+
our_funding_inputs: Vec<FundingTxInput>,
1411414114
) -> Result<Self, ChannelError>
1411514115
where ES::Target: EntropySource,
1411614116
F::Target: FeeEstimator,
1411714117
L::Target: Logger,
1411814118
{
14119-
// TODO(dual_funding): Take these as input once supported
14120-
let (our_funding_contribution, our_funding_contribution_sats) = (SignedAmount::ZERO, 0u64);
14121-
let our_funding_inputs = Vec::new();
14119+
debug_assert!(our_funding_contribution <= Amount::MAX_MONEY);
14120+
let our_funding_contribution_sats = our_funding_contribution.to_sat();
1412214121

1412314122
let channel_value_satoshis =
1412414123
our_funding_contribution_sats.saturating_add(msg.common_fields.funding_satoshis);
@@ -14163,37 +14162,30 @@ where
1416314162

1416414163
let funding_negotiation_context = FundingNegotiationContext {
1416514164
is_initiator: false,
14166-
our_funding_contribution,
14165+
our_funding_contribution: our_funding_contribution
14166+
.to_signed()
14167+
.expect("our_funding_contribution should not be greater than Amount::MAX_MONEY"),
1416714168
funding_tx_locktime: LockTime::from_consensus(msg.locktime),
1416814169
funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
1416914170
shared_funding_input: None,
1417014171
our_funding_inputs: our_funding_inputs.clone(),
1417114172
our_funding_outputs: Vec::new(),
1417214173
change_script: None,
1417314174
};
14174-
let shared_funding_output = TxOut {
14175-
value: Amount::from_sat(funding.get_value_satoshis()),
14176-
script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
14177-
};
1417814175

14179-
let interactive_tx_constructor = Some(InteractiveTxConstructor::new(
14180-
InteractiveTxConstructorArgs {
14176+
let mut interactive_tx_constructor = funding_negotiation_context
14177+
.into_interactive_tx_constructor(
14178+
&context,
14179+
&funding,
14180+
signer_provider,
1418114181
entropy_source,
1418214182
holder_node_id,
14183-
counterparty_node_id,
14184-
channel_id: context.channel_id,
14185-
feerate_sat_per_kw: funding_negotiation_context.funding_feerate_sat_per_1000_weight,
14186-
funding_tx_locktime: funding_negotiation_context.funding_tx_locktime,
14187-
is_initiator: false,
14188-
inputs_to_contribute: our_funding_inputs,
14189-
shared_funding_input: None,
14190-
shared_funding_output: SharedOwnedOutput::new(shared_funding_output, our_funding_contribution_sats),
14191-
outputs_to_contribute: funding_negotiation_context.our_funding_outputs.clone(),
14192-
}
14193-
).map_err(|err| {
14194-
let reason = ClosureReason::ProcessingError { err: err.reason.to_string() };
14195-
ChannelError::Close((err.reason.to_string(), reason))
14196-
})?);
14183+
)
14184+
.map_err(|err| {
14185+
let reason = ClosureReason::ProcessingError { err: err.reason.to_string() };
14186+
ChannelError::Close((err.reason.to_string(), reason))
14187+
})?;
14188+
debug_assert!(interactive_tx_constructor.take_initiator_first_message().is_none());
1419714189

1419814190
let unfunded_context = UnfundedChannelContext {
1419914191
unfunded_channel_age_ticks: 0,
@@ -14202,8 +14194,8 @@ where
1420214194
Ok(Self {
1420314195
funding,
1420414196
context,
14205-
funding_negotiation_context,
14206-
interactive_tx_constructor,
14197+
funding_negotiation_context: None,
14198+
interactive_tx_constructor: Some(interactive_tx_constructor),
1420714199
unfunded_context,
1420814200
})
1420914201
}
@@ -14267,8 +14259,7 @@ where
1426714259
}),
1426814260
channel_type: Some(self.funding.get_channel_type().clone()),
1426914261
},
14270-
funding_satoshis: self.funding_negotiation_context.our_funding_contribution.to_sat()
14271-
as u64,
14262+
funding_satoshis: self.our_funding_contribution().to_sat(),
1427214263
second_per_commitment_point,
1427314264
require_confirmed_inputs: None,
1427414265
}
@@ -14283,6 +14274,24 @@ where
1428314274
pub fn get_accept_channel_v2_message(&self) -> msgs::AcceptChannelV2 {
1428414275
self.generate_accept_channel_v2_message()
1428514276
}
14277+
14278+
pub fn our_funding_contribution(&self) -> Amount {
14279+
Amount::from_sat(self.funding.value_to_self_msat / 1000)
14280+
}
14281+
14282+
pub fn funding_tx_locktime(&self) -> LockTime {
14283+
self.funding_negotiation_context
14284+
.as_ref()
14285+
.map(|context| context.funding_tx_locktime)
14286+
.or_else(|| {
14287+
self.interactive_tx_constructor
14288+
.as_ref()
14289+
.map(|constructor| constructor.funding_tx_locktime())
14290+
})
14291+
.expect(
14292+
"either funding_negotiation_context or interactive_tx_constructor should be set",
14293+
)
14294+
}
1428614295
}
1428714296

1428814297
// Unfunded channel utilities

lightning/src/ln/channelmanager.rs

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use bitcoin::hashes::{Hash, HashEngine, HmacEngine};
3030

3131
use bitcoin::secp256k1::Secp256k1;
3232
use bitcoin::secp256k1::{PublicKey, SecretKey};
33-
use bitcoin::{secp256k1, Sequence, SignedAmount};
33+
use bitcoin::{secp256k1, Amount, Sequence};
3434

3535
use crate::blinded_path::message::{
3636
AsyncPaymentsContext, BlindedMessagePath, MessageForwardNode, OffersContext,
@@ -65,7 +65,7 @@ use crate::ln::channel::{
6565
WithChannelContext,
6666
};
6767
use crate::ln::channel_state::ChannelDetails;
68-
use crate::ln::funding::SpliceContribution;
68+
use crate::ln::funding::{FundingTxInput, SpliceContribution};
6969
use crate::ln::inbound_payment;
7070
use crate::ln::interactivetxs::InteractiveTxMessageSend;
7171
use crate::ln::msgs;
@@ -9814,6 +9814,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
98149814
false,
98159815
user_channel_id,
98169816
config_overrides,
9817+
Amount::ZERO,
9818+
vec![],
98179819
)
98189820
}
98199821

@@ -9845,15 +9847,69 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
98459847
true,
98469848
user_channel_id,
98479849
config_overrides,
9850+
Amount::ZERO,
9851+
vec![],
9852+
)
9853+
}
9854+
9855+
/// Accepts a request to open a dual-funded channel with a contribution provided after an
9856+
/// [`Event::OpenChannelRequest`].
9857+
///
9858+
/// The [`Event::OpenChannelRequest::channel_negotiation_type`] field will indicate the open channel
9859+
/// request is for a dual-funded channel when the variant is [`InboundChannelFunds::DualFunded`].
9860+
///
9861+
/// The `temporary_channel_id` parameter indicates which inbound channel should be accepted,
9862+
/// and the `counterparty_node_id` parameter is the id of the peer that has requested to open
9863+
/// the channel.
9864+
///
9865+
/// The `user_channel_id` parameter will be provided back in
9866+
/// [`Event::ChannelClosed::user_channel_id`] to allow tracking of which events correspond
9867+
/// with which `accept_inbound_channel_*` call.
9868+
///
9869+
/// The `funding_inputs` parameter provides which UTXOs to use for `our_funding_contribution`
9870+
/// along with the corresponding satisfaction weight. They must be able to cover any fees needed
9871+
/// to pay for the contributed weight to the funding transaction. This includes the witnesses
9872+
/// provided through calling [`ChannelManager::funding_transaction_signed`] after receiving
9873+
/// [`Event::FundingTransactionReadyForSigning`].
9874+
///
9875+
/// Note that this method will return an error and reject the channel if it requires support for
9876+
/// zero confirmations.
9877+
// TODO(dual_funding): Discussion on complications with 0conf dual-funded channels where "locking"
9878+
// of UTXOs used for funding would be required and other issues.
9879+
// See https://diyhpl.us/~bryan/irc/bitcoin/bitcoin-dev/linuxfoundation-pipermail/lightning-dev/2023-May/003922.txt
9880+
///
9881+
/// [`Event::OpenChannelRequest`]: events::Event::OpenChannelRequest
9882+
/// [`Event::OpenChannelRequest::channel_negotiation_type`]: events::Event::OpenChannelRequest::channel_negotiation_type
9883+
/// [`Event::ChannelClosed::user_channel_id`]: events::Event::ChannelClosed::user_channel_id
9884+
/// [`Event::FundingTransactionReadyForSigning`]: events::Event::FundingTransactionReadyForSigning
9885+
/// [`ChannelManager::funding_transaction_signed`]: ChannelManager::funding_transaction_signed
9886+
pub fn accept_inbound_channel_with_contribution(
9887+
&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey,
9888+
user_channel_id: u128, config_overrides: Option<ChannelConfigOverrides>,
9889+
our_funding_contribution: Amount, funding_inputs: Vec<FundingTxInput>,
9890+
) -> Result<(), APIError> {
9891+
self.do_accept_inbound_channel(
9892+
temporary_channel_id,
9893+
counterparty_node_id,
9894+
false,
9895+
user_channel_id,
9896+
config_overrides,
9897+
our_funding_contribution,
9898+
funding_inputs,
98489899
)
98499900
}
98509901

9851-
/// TODO(dual_funding): Allow contributions, pass intended amount and inputs
98529902
#[rustfmt::skip]
98539903
fn do_accept_inbound_channel(
9854-
&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, accept_0conf: bool,
9855-
user_channel_id: u128, config_overrides: Option<ChannelConfigOverrides>
9904+
&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey,
9905+
accept_0conf: bool, user_channel_id: u128, config_overrides: Option<ChannelConfigOverrides>,
9906+
our_funding_contribution: Amount, funding_inputs: Vec<FundingTxInput>
98569907
) -> Result<(), APIError> {
9908+
if our_funding_contribution > Amount::MAX_MONEY {
9909+
return Err(APIError::APIMisuseError {
9910+
err: format!("the funding contribution must be smaller than the total bitcoin supply, it was {}", our_funding_contribution)
9911+
});
9912+
}
98579913

98589914
let mut config = self.config.read().unwrap().clone();
98599915

@@ -9911,7 +9967,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
99119967
&self.channel_type_features(), &peer_state.latest_features,
99129968
&open_channel_msg,
99139969
user_channel_id, &config, best_block_height,
9914-
&self.logger,
9970+
&self.logger, our_funding_contribution, funding_inputs,
99159971
).map_err(|e| {
99169972
let channel_id = open_channel_msg.common_fields.temporary_channel_id;
99179973
MsgHandleErrInternal::from_chan_no_close(e, channel_id)
@@ -10055,7 +10111,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1005510111

1005610112
// Inbound V2 channels with contributed inputs are not considered unfunded.
1005710113
if let Some(unfunded_chan) = chan.as_unfunded_v2() {
10058-
if unfunded_chan.funding_negotiation_context.our_funding_contribution > SignedAmount::ZERO {
10114+
if unfunded_chan.our_funding_contribution() > Amount::ZERO {
1005910115
continue;
1006010116
}
1006110117
}
@@ -10197,7 +10253,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1019710253
&self.fee_estimator, &self.entropy_source, &self.signer_provider,
1019810254
self.get_our_node_id(), *counterparty_node_id, &self.channel_type_features(),
1019910255
&peer_state.latest_features, msg, user_channel_id,
10200-
&self.config.read().unwrap(), best_block_height, &self.logger,
10256+
&self.config.read().unwrap(), best_block_height, &self.logger, Amount::ZERO, vec![],
1020110257
).map_err(|e| MsgHandleErrInternal::from_chan_no_close(e, msg.common_fields.temporary_channel_id))?;
1020210258
let message_send_event = MessageSendEvent::SendAcceptChannelV2 {
1020310259
node_id: *counterparty_node_id,

lightning/src/ln/interactivetxs.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1947,6 +1947,7 @@ pub(super) struct InteractiveTxConstructor {
19471947
is_initiator: bool,
19481948
initiator_first_message: Option<InteractiveTxMessageSend>,
19491949
channel_id: ChannelId,
1950+
funding_tx_locktime: AbsoluteLockTime,
19501951
inputs_to_contribute: Vec<(SerialId, InputOwned)>,
19511952
outputs_to_contribute: Vec<(SerialId, OutputOwned)>,
19521953
next_input_index: Option<usize>,
@@ -2112,6 +2113,7 @@ impl InteractiveTxConstructor {
21122113
is_initiator,
21132114
initiator_first_message: None,
21142115
channel_id,
2116+
funding_tx_locktime,
21152117
inputs_to_contribute,
21162118
outputs_to_contribute,
21172119
next_input_index,
@@ -2131,6 +2133,10 @@ impl InteractiveTxConstructor {
21312133
Ok(constructor)
21322134
}
21332135

2136+
pub(super) fn funding_tx_locktime(&self) -> AbsoluteLockTime {
2137+
self.funding_tx_locktime
2138+
}
2139+
21342140
fn into_negotiation_error(self, reason: AbortReason) -> NegotiationError {
21352141
let (contributed_inputs, contributed_outputs) = self.into_contributed_inputs_and_outputs();
21362142
NegotiationError { reason, contributed_inputs, contributed_outputs }
@@ -2320,6 +2326,11 @@ impl InteractiveTxConstructor {
23202326
/// given inputs and outputs, and intended contribution. Takes into account the fees and the dust
23212327
/// limit.
23222328
///
2329+
/// Note that since the type of change output cannot be determined at this point, this calculation
2330+
/// does not account for the weight contributed by the change output itself. The fees for the
2331+
/// weight of this change output should be subtracted from the result of this function call to get
2332+
/// the final amount for the change output (if above dust).
2333+
///
23232334
/// Three outcomes are possible:
23242335
/// - Inputs are sufficient for intended contribution, fees, and a larger-than-dust change:
23252336
/// `Ok(Some(change_amount))`

0 commit comments

Comments
 (0)