@@ -57,9 +57,8 @@ use crate::ln::channelmanager::{
5757};
5858use crate::ln::interactivetxs::{
5959 calculate_change_output_value, get_output_weight, AbortReason, HandleTxCompleteResult,
60- InteractiveTxConstructor, InteractiveTxConstructorArgs, InteractiveTxMessageSend,
61- InteractiveTxMessageSendResult, InteractiveTxSigningSession, SharedOwnedOutput,
62- TX_COMMON_FIELDS_WEIGHT,
60+ InteractiveTxConstructor, InteractiveTxConstructorArgs, InteractiveTxMessageSendResult,
61+ InteractiveTxSigningSession, SharedOwnedInput, SharedOwnedOutput, TX_COMMON_FIELDS_WEIGHT,
6362};
6463use crate::ln::msgs;
6564use crate::ln::msgs::{ClosingSigned, ClosingSignedFeeRange, DecodeError, OnionErrorPacket};
@@ -2174,7 +2173,6 @@ impl FundingScope {
21742173/// Info about a pending splice, used in the pre-splice channel
21752174#[cfg(splicing)]
21762175struct PendingSplice {
2177- pub our_funding_contribution: i64,
21782176 funding_negotiation: Option<FundingNegotiation>,
21792177
21802178 /// The funding txid used in the `splice_locked` sent to the counterparty.
@@ -2778,86 +2776,6 @@ impl<SP: Deref> PendingV2Channel<SP>
27782776where
27792777 SP::Target: SignerProvider,
27802778{
2781- /// Prepare and start interactive transaction negotiation.
2782- /// `change_destination_opt` - Optional destination for optional change; if None,
2783- /// default destination address is used.
2784- /// If error occurs, it is caused by our side, not the counterparty.
2785- #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled
2786- #[rustfmt::skip]
2787- fn begin_interactive_funding_tx_construction<ES: Deref>(
2788- &mut self, signer_provider: &SP, entropy_source: &ES, holder_node_id: PublicKey,
2789- change_destination_opt: Option<ScriptBuf>,
2790- ) -> Result<Option<InteractiveTxMessageSend>, AbortReason>
2791- where ES::Target: EntropySource
2792- {
2793- debug_assert!(matches!(self.context.channel_state, ChannelState::NegotiatingFunding(_)));
2794- debug_assert!(self.interactive_tx_constructor.is_none());
2795-
2796- let mut funding_inputs = Vec::new();
2797- mem::swap(&mut self.funding_negotiation_context.our_funding_inputs, &mut funding_inputs);
2798-
2799- // TODO(splicing): Add prev funding tx as input, must be provided as a parameter
2800-
2801- // Add output for funding tx
2802- // Note: For the error case when the inputs are insufficient, it will be handled after
2803- // the `calculate_change_output_value` call below
2804- let mut funding_outputs = Vec::new();
2805-
2806- let shared_funding_output = TxOut {
2807- value: Amount::from_sat(self.funding.get_value_satoshis()),
2808- script_pubkey: self.funding.get_funding_redeemscript().to_p2wsh(),
2809- };
2810-
2811- // Optionally add change output
2812- let change_script = if let Some(script) = change_destination_opt {
2813- script
2814- } else {
2815- signer_provider.get_destination_script(self.context.channel_keys_id)
2816- .map_err(|_err| AbortReason::InternalError("Error getting destination script"))?
2817- };
2818- let change_value_opt = calculate_change_output_value(
2819- self.funding.is_outbound(), self.funding_negotiation_context.our_funding_satoshis,
2820- &funding_inputs, None,
2821- &shared_funding_output.script_pubkey, &funding_outputs,
2822- self.funding_negotiation_context.funding_feerate_sat_per_1000_weight,
2823- change_script.minimal_non_dust().to_sat(),
2824- )?;
2825- if let Some(change_value) = change_value_opt {
2826- let mut change_output = TxOut {
2827- value: Amount::from_sat(change_value),
2828- script_pubkey: change_script,
2829- };
2830- let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
2831- let change_output_fee = fee_for_weight(self.funding_negotiation_context.funding_feerate_sat_per_1000_weight, change_output_weight);
2832- let change_value_decreased_with_fee = change_value.saturating_sub(change_output_fee);
2833- // Check dust limit again
2834- if change_value_decreased_with_fee > self.context.holder_dust_limit_satoshis {
2835- change_output.value = Amount::from_sat(change_value_decreased_with_fee);
2836- funding_outputs.push(change_output);
2837- }
2838- }
2839-
2840- let constructor_args = InteractiveTxConstructorArgs {
2841- entropy_source,
2842- holder_node_id,
2843- counterparty_node_id: self.context.counterparty_node_id,
2844- channel_id: self.context.channel_id(),
2845- feerate_sat_per_kw: self.funding_negotiation_context.funding_feerate_sat_per_1000_weight,
2846- is_initiator: self.funding.is_outbound(),
2847- funding_tx_locktime: self.funding_negotiation_context.funding_tx_locktime,
2848- inputs_to_contribute: funding_inputs,
2849- shared_funding_input: None,
2850- shared_funding_output: SharedOwnedOutput::new(shared_funding_output, self.funding_negotiation_context.our_funding_satoshis),
2851- outputs_to_contribute: funding_outputs,
2852- };
2853- let mut tx_constructor = InteractiveTxConstructor::new(constructor_args)?;
2854- let msg = tx_constructor.take_initiator_first_message();
2855-
2856- self.interactive_tx_constructor = Some(tx_constructor);
2857-
2858- Ok(msg)
2859- }
2860-
28612779 pub fn tx_add_input(&mut self, msg: &msgs::TxAddInput) -> InteractiveTxMessageSendResult {
28622780 InteractiveTxMessageSendResult(match &mut self.interactive_tx_constructor {
28632781 Some(ref mut tx_constructor) => tx_constructor
@@ -2937,7 +2855,6 @@ where
29372855 where
29382856 L::Target: Logger
29392857 {
2940- let our_funding_satoshis = self.funding_negotiation_context.our_funding_satoshis;
29412858 let transaction_number = self.unfunded_context.transaction_number();
29422859
29432860 let mut output_index = None;
@@ -2972,7 +2889,7 @@ where
29722889 };
29732890
29742891 let funding_ready_for_sig_event = if signing_session.local_inputs_count() == 0 {
2975- debug_assert_eq!(our_funding_satoshis , 0);
2892+ debug_assert_eq!(self.funding_negotiation_context.our_funding_contribution_satoshis , 0);
29762893 if signing_session.provide_holder_witnesses(self.context.channel_id, Vec::new()).is_err() {
29772894 debug_assert!(
29782895 false,
@@ -5877,28 +5794,107 @@ fn check_v2_funding_inputs_sufficient(
58775794
58785795/// Context for negotiating channels (dual-funded V2 open, splicing)
58795796pub(super) struct FundingNegotiationContext {
5797+ /// Whether we initiated the funding negotiation.
5798+ pub is_initiator: bool,
58805799 /// The amount in satoshis we will be contributing to the channel.
5881- pub our_funding_satoshis: u64 ,
5800+ pub our_funding_contribution_satoshis: i64 ,
58825801 /// The amount in satoshis our counterparty will be contributing to the channel.
58835802 #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
5884- pub their_funding_satoshis : Option<u64 >,
5803+ pub their_funding_contribution_satoshis : Option<i64 >,
58855804 /// The funding transaction locktime suggested by the initiator. If set by us, it is always set
58865805 /// to the current block height to align incentives against fee-sniping.
58875806 pub funding_tx_locktime: LockTime,
58885807 /// The feerate set by the initiator to be used for the funding transaction.
58895808 #[allow(dead_code)] // TODO(dual_funding): Remove once V2 channels is enabled.
58905809 pub funding_feerate_sat_per_1000_weight: u32,
58915810 /// The funding inputs we will be contributing to the channel.
5892- ///
5893- /// Note that the `our_funding_satoshis` field is equal to the total value of `our_funding_inputs`
5894- /// minus any fees paid for our contributed weight. This means that change will never be generated
5895- /// and the maximum value possible will go towards funding the channel.
5896- ///
5897- /// Note that this field may be emptied once the interactive negotiation has been started.
58985811 #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
58995812 pub our_funding_inputs: Vec<(TxIn, TransactionU16LenLimited)>,
59005813}
59015814
5815+ impl FundingNegotiationContext {
5816+ /// Prepare and start interactive transaction negotiation.
5817+ /// `change_destination_opt` - Optional destination for optional change; if None,
5818+ /// default destination address is used.
5819+ /// If error occurs, it is caused by our side, not the counterparty.
5820+ #[cfg(splicing)]
5821+ fn into_interactive_tx_constructor<SP: Deref, ES: Deref>(
5822+ self, context: &ChannelContext<SP>, funding: &FundingScope, signer_provider: &SP,
5823+ entropy_source: &ES, holder_node_id: PublicKey, change_destination_opt: Option<ScriptBuf>,
5824+ shared_funding_input: Option<SharedOwnedInput>,
5825+ ) -> Result<InteractiveTxConstructor, AbortReason>
5826+ where
5827+ SP::Target: SignerProvider,
5828+ ES::Target: EntropySource,
5829+ {
5830+ if shared_funding_input.is_some() {
5831+ debug_assert!(matches!(context.channel_state, ChannelState::ChannelReady(_)));
5832+ } else {
5833+ debug_assert!(matches!(context.channel_state, ChannelState::NegotiatingFunding(_)));
5834+ }
5835+
5836+ // Add output for funding tx
5837+ // Note: For the error case when the inputs are insufficient, it will be handled after
5838+ // the `calculate_change_output_value` call below
5839+ let mut funding_outputs = Vec::new();
5840+
5841+ let shared_funding_output = TxOut {
5842+ value: Amount::from_sat(funding.get_value_satoshis()),
5843+ script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
5844+ };
5845+
5846+ // Optionally add change output
5847+ if self.our_funding_contribution_satoshis > 0 {
5848+ let change_value_opt = calculate_change_output_value(
5849+ &self,
5850+ funding.channel_transaction_parameters.splice_parent_funding_txid.is_some(),
5851+ &shared_funding_output.script_pubkey,
5852+ &funding_outputs,
5853+ context.holder_dust_limit_satoshis,
5854+ )?;
5855+ if let Some(change_value) = change_value_opt {
5856+ let change_script = if let Some(script) = change_destination_opt {
5857+ script
5858+ } else {
5859+ signer_provider.get_destination_script(context.channel_keys_id).map_err(
5860+ |_err| AbortReason::InternalError("Error getting destination script"),
5861+ )?
5862+ };
5863+ let mut change_output =
5864+ TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
5865+ let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
5866+ let change_output_fee =
5867+ fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
5868+ let change_value_decreased_with_fee =
5869+ change_value.saturating_sub(change_output_fee);
5870+ // Check dust limit again
5871+ if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
5872+ change_output.value = Amount::from_sat(change_value_decreased_with_fee);
5873+ funding_outputs.push(change_output);
5874+ }
5875+ }
5876+ }
5877+
5878+ let constructor_args = InteractiveTxConstructorArgs {
5879+ entropy_source,
5880+ holder_node_id,
5881+ counterparty_node_id: context.counterparty_node_id,
5882+ channel_id: context.channel_id(),
5883+ feerate_sat_per_kw: self.funding_feerate_sat_per_1000_weight,
5884+ is_initiator: self.is_initiator,
5885+ funding_tx_locktime: self.funding_tx_locktime,
5886+ inputs_to_contribute: self.our_funding_inputs,
5887+ shared_funding_input,
5888+ shared_funding_output: SharedOwnedOutput::new(
5889+ shared_funding_output,
5890+ funding.value_to_self_msat / 1000,
5891+ ),
5892+ outputs_to_contribute: funding_outputs,
5893+ };
5894+ InteractiveTxConstructor::new(constructor_args)
5895+ }
5896+ }
5897+
59025898// Holder designates channel data owned for the benefit of the user client.
59035899// Counterparty designates channel data owned by the another channel participant entity.
59045900pub(super) struct FundedChannel<SP: Deref>
@@ -10424,11 +10420,13 @@ where
1042410420 ) -> Result<msgs::SpliceInit, APIError> {
1042510421 // Check if a splice has been initiated already.
1042610422 // Note: only a single outstanding splice is supported (per spec)
10427- if let Some(splice_info) = &self.pending_splice {
10428- return Err(APIError::APIMisuseError { err: format!(
10429- "Channel {} cannot be spliced, as it has already a splice pending (contribution {})",
10430- self.context.channel_id(), splice_info.our_funding_contribution
10431- )});
10423+ if self.pending_splice.is_some() {
10424+ return Err(APIError::APIMisuseError {
10425+ err: format!(
10426+ "Channel {} cannot be spliced, as it has already a splice pending",
10427+ self.context.channel_id(),
10428+ ),
10429+ });
1043210430 }
1043310431
1043410432 if !self.context.is_live() {
@@ -10461,7 +10459,6 @@ where
1046110459 )})?;
1046210460
1046310461 self.pending_splice = Some(PendingSplice {
10464- our_funding_contribution: our_funding_contribution_satoshis,
1046510462 funding_negotiation: None,
1046610463 sent_funding_txid: None,
1046710464 received_funding_txid: None,
@@ -10498,9 +10495,10 @@ where
1049810495 let our_funding_contribution_satoshis = 0i64;
1049910496
1050010497 // Check if a splice has been initiated already.
10501- if let Some(splice_info) = & self.pending_splice {
10498+ if self.pending_splice.is_some() {
1050210499 return Err(ChannelError::Warn(format!(
10503- "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
10500+ "Channel {} already has a splice pending",
10501+ self.context.channel_id(),
1050410502 )));
1050510503 }
1050610504
@@ -12111,9 +12109,10 @@ where
1211112109 holder_commitment_point: HolderCommitmentPoint::new(&context.holder_signer, &context.secp_ctx),
1211212110 };
1211312111 let funding_negotiation_context = FundingNegotiationContext {
12114- our_funding_satoshis: funding_satoshis,
12112+ is_initiator: true,
12113+ our_funding_contribution_satoshis: funding_satoshis as i64,
1211512114 // TODO(dual_funding) TODO(splicing) Include counterparty contribution, once that's enabled
12116- their_funding_satoshis : None,
12115+ their_funding_contribution_satoshis : None,
1211712116 funding_tx_locktime,
1211812117 funding_feerate_sat_per_1000_weight,
1211912118 our_funding_inputs: funding_inputs,
@@ -12265,8 +12264,9 @@ where
1226512264 context.channel_id = channel_id;
1226612265
1226712266 let funding_negotiation_context = FundingNegotiationContext {
12268- our_funding_satoshis: our_funding_satoshis,
12269- their_funding_satoshis: Some(msg.common_fields.funding_satoshis),
12267+ is_initiator: false,
12268+ our_funding_contribution_satoshis: our_funding_satoshis as i64,
12269+ their_funding_contribution_satoshis: Some(msg.common_fields.funding_satoshis as i64),
1227012270 funding_tx_locktime: LockTime::from_consensus(msg.locktime),
1227112271 funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
1227212272 our_funding_inputs: our_funding_inputs.clone(),
@@ -12368,7 +12368,8 @@ where
1236812368 }),
1236912369 channel_type: Some(self.funding.get_channel_type().clone()),
1237012370 },
12371- funding_satoshis: self.funding_negotiation_context.our_funding_satoshis,
12371+ funding_satoshis: self.funding_negotiation_context.our_funding_contribution_satoshis
12372+ as u64,
1237212373 second_per_commitment_point,
1237312374 require_confirmed_inputs: None,
1237412375 }
0 commit comments