@@ -2942,87 +2942,6 @@ where
29422942/// - [`FundedChannel`], when splicing.
29432943pub(super) struct NegotiatingChannelView<'a>(&'a mut InteractiveTxConstructor);
29442944
2945- /// Prepare and start interactive transaction negotiation.
2946- /// `change_destination_opt` - Optional destination for optional change; if None,
2947- /// default destination address is used.
2948- /// If error occurs, it is caused by our side, not the counterparty.
2949- #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled
2950- fn begin_interactive_funding_tx_construction<SP: Deref, ES: Deref>(
2951- funding: &FundingScope, context: &ChannelContext<SP>,
2952- funding_negotiation_context: FundingNegotiationContext, is_splice: bool,
2953- signer_provider: &SP, entropy_source: &ES, holder_node_id: PublicKey,
2954- change_destination_opt: Option<ScriptBuf>, shared_funding_input: Option<SharedOwnedInput>,
2955- ) -> Result<InteractiveTxConstructor, AbortReason>
2956- where
2957- SP::Target: SignerProvider,
2958- ES::Target: EntropySource,
2959- {
2960- if is_splice {
2961- debug_assert!(matches!(context.channel_state, ChannelState::ChannelReady(_)));
2962- } else {
2963- debug_assert!(matches!(context.channel_state, ChannelState::NegotiatingFunding(_)));
2964- }
2965-
2966- // Add output for funding tx
2967- // Note: For the error case when the inputs are insufficient, it will be handled after
2968- // the `calculate_change_output_value` call below
2969- let mut funding_outputs = Vec::new();
2970-
2971- let shared_funding_output = TxOut {
2972- value: Amount::from_sat(funding.get_value_satoshis()),
2973- script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
2974- };
2975-
2976- // Optionally add change output
2977- let change_script = if let Some(script) = change_destination_opt {
2978- script
2979- } else {
2980- signer_provider
2981- .get_destination_script(context.channel_keys_id)
2982- .map_err(|_err| AbortReason::InternalError("Error getting destination script"))?
2983- };
2984- let change_value_opt = calculate_change_output_value(
2985- &funding_negotiation_context,
2986- shared_funding_input.as_ref().map(|input| input.local_owned()),
2987- &shared_funding_output.script_pubkey,
2988- &funding_outputs,
2989- change_script.minimal_non_dust().to_sat(),
2990- )?;
2991- if let Some(change_value) = change_value_opt {
2992- let mut change_output =
2993- TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
2994- let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
2995- let change_output_fee = fee_for_weight(
2996- funding_negotiation_context.funding_feerate_sat_per_1000_weight,
2997- change_output_weight,
2998- );
2999- let change_value_decreased_with_fee = change_value.saturating_sub(change_output_fee);
3000- // Check dust limit again
3001- if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
3002- change_output.value = Amount::from_sat(change_value_decreased_with_fee);
3003- funding_outputs.push(change_output);
3004- }
3005- }
3006-
3007- let constructor_args = InteractiveTxConstructorArgs {
3008- entropy_source,
3009- holder_node_id,
3010- counterparty_node_id: context.counterparty_node_id,
3011- channel_id: context.channel_id(),
3012- feerate_sat_per_kw: funding_negotiation_context.funding_feerate_sat_per_1000_weight,
3013- is_initiator: funding_negotiation_context.is_initiator,
3014- funding_tx_locktime: funding_negotiation_context.funding_tx_locktime,
3015- inputs_to_contribute: funding_negotiation_context.our_funding_inputs,
3016- shared_funding_input,
3017- shared_funding_output: SharedOwnedOutput::new(
3018- shared_funding_output,
3019- funding_negotiation_context.our_funding_satoshis,
3020- ),
3021- outputs_to_contribute: funding_outputs,
3022- };
3023- InteractiveTxConstructor::new(constructor_args)
3024- }
3025-
30262945#[rustfmt::skip]
30272946fn funding_tx_constructed<SP:Deref, L: Deref>(
30282947 funding: &mut FundingScope, context: &mut ChannelContext<SP>,
@@ -6031,6 +5950,85 @@ pub(super) struct FundingNegotiationContext {
60315950 pub our_funding_inputs: Vec<(TxIn, TransactionU16LenLimited)>,
60325951}
60335952
5953+ impl FundingNegotiationContext {
5954+ /// Prepare and start interactive transaction negotiation.
5955+ /// `change_destination_opt` - Optional destination for optional change; if None,
5956+ /// default destination address is used.
5957+ /// If error occurs, it is caused by our side, not the counterparty.
5958+ fn into_interactive_tx_constructor<SP: Deref, ES: Deref>(
5959+ self, context: &ChannelContext<SP>, funding: &FundingScope, is_splice: bool,
5960+ signer_provider: &SP, entropy_source: &ES, holder_node_id: PublicKey,
5961+ change_destination_opt: Option<ScriptBuf>, shared_funding_input: Option<SharedOwnedInput>,
5962+ ) -> Result<InteractiveTxConstructor, AbortReason>
5963+ where
5964+ SP::Target: SignerProvider,
5965+ ES::Target: EntropySource,
5966+ {
5967+ if is_splice {
5968+ debug_assert!(matches!(context.channel_state, ChannelState::ChannelReady(_)));
5969+ } else {
5970+ debug_assert!(matches!(context.channel_state, ChannelState::NegotiatingFunding(_)));
5971+ }
5972+
5973+ // Add output for funding tx
5974+ // Note: For the error case when the inputs are insufficient, it will be handled after
5975+ // the `calculate_change_output_value` call below
5976+ let mut funding_outputs = Vec::new();
5977+
5978+ let shared_funding_output = TxOut {
5979+ value: Amount::from_sat(funding.get_value_satoshis()),
5980+ script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
5981+ };
5982+
5983+ // Optionally add change output
5984+ let change_script = if let Some(script) = change_destination_opt {
5985+ script
5986+ } else {
5987+ signer_provider
5988+ .get_destination_script(context.channel_keys_id)
5989+ .map_err(|_err| AbortReason::InternalError("Error getting destination script"))?
5990+ };
5991+ let change_value_opt = calculate_change_output_value(
5992+ &self,
5993+ shared_funding_input.as_ref().map(|input| input.local_owned()),
5994+ &shared_funding_output.script_pubkey,
5995+ &funding_outputs,
5996+ change_script.minimal_non_dust().to_sat(),
5997+ )?;
5998+ if let Some(change_value) = change_value_opt {
5999+ let mut change_output =
6000+ TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6001+ let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6002+ let change_output_fee =
6003+ fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6004+ let change_value_decreased_with_fee = change_value.saturating_sub(change_output_fee);
6005+ // Check dust limit again
6006+ if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6007+ change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6008+ funding_outputs.push(change_output);
6009+ }
6010+ }
6011+
6012+ let constructor_args = InteractiveTxConstructorArgs {
6013+ entropy_source,
6014+ holder_node_id,
6015+ counterparty_node_id: context.counterparty_node_id,
6016+ channel_id: context.channel_id(),
6017+ feerate_sat_per_kw: self.funding_feerate_sat_per_1000_weight,
6018+ is_initiator: self.is_initiator,
6019+ funding_tx_locktime: self.funding_tx_locktime,
6020+ inputs_to_contribute: self.our_funding_inputs,
6021+ shared_funding_input,
6022+ shared_funding_output: SharedOwnedOutput::new(
6023+ shared_funding_output,
6024+ self.our_funding_satoshis,
6025+ ),
6026+ outputs_to_contribute: funding_outputs,
6027+ };
6028+ InteractiveTxConstructor::new(constructor_args)
6029+ }
6030+ }
6031+
60346032// Holder designates channel data owned for the benefit of the user client.
60356033// Counterparty designates channel data owned by the another channel participant entity.
60366034pub(super) struct FundedChannel<SP: Deref>
@@ -10530,7 +10528,7 @@ where
1053010528 their_funding_contribution,
1053110529 );
1053210530
10533- let funding_scope = FundingScope::for_splice(
10531+ let splice_funding = FundingScope::for_splice(
1053410532 &self.funding,
1053510533 &self.context,
1053610534 our_funding_contribution,
@@ -10556,43 +10554,39 @@ where
1055610554 our_funding_inputs: Vec::new(),
1055710555 };
1055810556
10559- self.pending_splice = Some(PendingSplice {
10560- our_funding_contribution,
10561- funding: Some(funding_scope),
10562- funding_negotiation_context: Some(funding_negotiation_context),
10563- interactive_tx_constructor: None,
10564- received_funding_txid: None,
10565- sent_funding_txid: None,
10566- });
10567-
1056810557 log_info!(logger, "Splicing process started after splice_init, new channel value {}, old {}, outgoing {}, channel_id {}",
1056910558 post_channel_value, pre_channel_value, false, self.context.channel_id);
1057010559
1057110560 let splice_ack_msg = self.get_splice_ack(our_funding_contribution);
1057210561
10573- let pending_splice = self.pending_splice.as_mut().unwrap();
10574- let mut interactive_tx_constructor = begin_interactive_funding_tx_construction(
10575- pending_splice.funding.as_ref().unwrap(),
10576- &self.context,
10577- pending_splice.funding_negotiation_context.take().unwrap(),
10578- true,
10579- signer_provider,
10580- entropy_source,
10581- holder_node_id.clone(),
10582- None,
10583- Some(prev_funding_input),
10584- )
10585- .map_err(|err| {
10586- ChannelError::Warn(format!(
10587- "Failed to start interactive transaction construction, {:?}",
10588- err
10589- ))
10590- })?;
10591-
10562+ let mut interactive_tx_constructor = funding_negotiation_context
10563+ .into_interactive_tx_constructor(
10564+ &self.context,
10565+ &splice_funding,
10566+ true,
10567+ signer_provider,
10568+ entropy_source,
10569+ holder_node_id.clone(),
10570+ None,
10571+ Some(prev_funding_input),
10572+ )
10573+ .map_err(|err| {
10574+ ChannelError::Warn(format!(
10575+ "Failed to start interactive transaction construction, {:?}",
10576+ err
10577+ ))
10578+ })?;
1059210579 // TODO: Propagate message
1059310580 let _msg = interactive_tx_constructor.take_initiator_first_message();
10594- self.pending_splice.as_mut().unwrap().interactive_tx_constructor =
10595- Some(interactive_tx_constructor);
10581+
10582+ self.pending_splice = Some(PendingSplice {
10583+ our_funding_contribution,
10584+ funding: Some(splice_funding),
10585+ funding_negotiation_context: None,
10586+ interactive_tx_constructor: Some(interactive_tx_constructor),
10587+ received_funding_txid: None,
10588+ sent_funding_txid: None,
10589+ });
1059610590
1059710591 Ok(splice_ack_msg)
1059810592 }
@@ -10654,7 +10648,7 @@ where
1065410648 // TODO(splicing): Pre-check for reserve requirement
1065510649 // (Note: It should also be checked later at tx_complete)
1065610650
10657- let funding_scope = FundingScope::for_splice(
10651+ let splice_funding = FundingScope::for_splice(
1065810652 &self.funding,
1065910653 &self.context,
1066010654 our_funding_contribution,
@@ -10671,37 +10665,39 @@ where
1067110665
1067210666 let prev_funding_input = self.funding.to_splice_funding_input();
1067310667
10674- debug_assert!(pending_splice.funding.is_none());
10675- pending_splice.funding = Some(funding_scope);
1067610668 // update funding values
1067710669 pending_splice.funding_negotiation_context.as_mut().unwrap().our_funding_satoshis =
1067810670 our_funding_satoshis;
1067910671 pending_splice.funding_negotiation_context.as_mut().unwrap().their_funding_satoshis =
1068010672 Some(their_funding_satoshis);
10681- debug_assert!(pending_splice.interactive_tx_constructor.is_none());
10682- debug_assert!(self.interactive_tx_signing_session.is_none());
1068310673
1068410674 log_info!(logger, "Splicing process started after splice_ack, new channel value {}, old {}, outgoing {}, channel_id {}",
1068510675 post_channel_value, pre_channel_value, true, self.context.channel_id);
1068610676
1068710677 // Start interactive funding negotiation, with the previous funding transaction as an extra shared input
10688- let mut interactive_tx_constructor = begin_interactive_funding_tx_construction(
10689- pending_splice.funding.as_ref().unwrap(),
10690- &self.context,
10691- pending_splice.funding_negotiation_context.take().unwrap(),
10692- true,
10693- signer_provider,
10694- entropy_source,
10695- holder_node_id.clone(),
10696- None,
10697- Some(prev_funding_input),
10698- )
10699- .map_err(|err| {
10700- ChannelError::Warn(format!("V2 channel rejected due to sender error, {:?}", err))
10701- })?;
10678+ let funding_negotiation_context = pending_splice.funding_negotiation_context.take().unwrap();
10679+ let mut interactive_tx_constructor = funding_negotiation_context
10680+ .into_interactive_tx_constructor(
10681+ &self.context,
10682+ &splice_funding,
10683+ true,
10684+ signer_provider,
10685+ entropy_source,
10686+ holder_node_id.clone(),
10687+ None,
10688+ Some(prev_funding_input),
10689+ )
10690+ .map_err(|err| {
10691+ ChannelError::Warn(format!("V2 channel rejected due to sender error, {:?}", err))
10692+ })?;
1070210693 let tx_msg_opt = interactive_tx_constructor.take_initiator_first_message();
10703- self.pending_splice.as_mut().unwrap().interactive_tx_constructor =
10704- Some(interactive_tx_constructor);
10694+
10695+ debug_assert!(pending_splice.funding.is_none());
10696+ debug_assert!(pending_splice.interactive_tx_constructor.is_none());
10697+ debug_assert!(self.interactive_tx_signing_session.is_none());
10698+ pending_splice.funding = Some(splice_funding);
10699+ pending_splice.interactive_tx_constructor = Some(interactive_tx_constructor);
10700+
1070510701 Ok(tx_msg_opt)
1070610702 }
1070710703
0 commit comments