@@ -2893,6 +2893,7 @@ impl_writeable_tlv_based!(PendingFunding, {
28932893enum FundingNegotiation {
28942894 AwaitingAck {
28952895 context: FundingNegotiationContext,
2896+ change_strategy: ChangeStrategy,
28962897 new_holder_funding_key: PublicKey,
28972898 },
28982899 ConstructingTransaction {
@@ -6741,18 +6742,25 @@ pub(super) struct FundingNegotiationContext {
67416742 /// The funding outputs we will be contributing to the channel.
67426743 #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
67436744 pub our_funding_outputs: Vec<TxOut>,
6745+ }
6746+
6747+ /// How the funding transaction's change is determined.
6748+ #[derive(Debug)]
6749+ pub(super) enum ChangeStrategy {
6750+ /// The change output, if any, is included in the FundingContribution's outputs.
6751+ FromCoinSelection,
6752+
67446753 /// The change output script. This will be used if needed or -- if not set -- generated using
67456754 /// `SignerProvider::get_destination_script`.
6746- #[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
6747- pub change_script: Option<ScriptBuf>,
6755+ LegacyUserProvided(Option<ScriptBuf>),
67486756}
67496757
67506758impl FundingNegotiationContext {
67516759 /// Prepare and start interactive transaction negotiation.
67526760 /// If error occurs, it is caused by our side, not the counterparty.
67536761 fn into_interactive_tx_constructor<SP: Deref, ES: Deref>(
67546762 mut self, context: &ChannelContext<SP>, funding: &FundingScope, signer_provider: &SP,
6755- entropy_source: &ES, holder_node_id: PublicKey,
6763+ entropy_source: &ES, holder_node_id: PublicKey, change_strategy: ChangeStrategy,
67566764 ) -> Result<InteractiveTxConstructor, NegotiationError>
67576765 where
67586766 SP::Target: SignerProvider,
@@ -6777,46 +6785,15 @@ impl FundingNegotiationContext {
67776785 script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
67786786 };
67796787
6780- // Optionally add change output
6781- let change_value_opt = if !self.our_funding_inputs.is_empty() {
6782- match calculate_change_output_value(
6783- &self,
6784- self.shared_funding_input.is_some(),
6785- &shared_funding_output.script_pubkey,
6786- context.holder_dust_limit_satoshis,
6787- ) {
6788- Ok(change_value_opt) => change_value_opt,
6789- Err(reason) => {
6790- return Err(self.into_negotiation_error(reason));
6791- },
6792- }
6793- } else {
6794- None
6795- };
6796-
6797- if let Some(change_value) = change_value_opt {
6798- let change_script = if let Some(script) = self.change_script {
6799- script
6800- } else {
6801- match signer_provider.get_destination_script(context.channel_keys_id) {
6802- Ok(script) => script,
6803- Err(_) => {
6804- let reason = AbortReason::InternalError("Error getting change script");
6805- return Err(self.into_negotiation_error(reason));
6806- },
6807- }
6808- };
6809- let mut change_output = TxOut { value: change_value, script_pubkey: change_script };
6810- let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6811- let change_output_fee =
6812- fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6813- let change_value_decreased_with_fee =
6814- change_value.to_sat().saturating_sub(change_output_fee);
6815- // Check dust limit again
6816- if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6817- change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6818- self.our_funding_outputs.push(change_output);
6819- }
6788+ match self.calculate_change_output(
6789+ context,
6790+ signer_provider,
6791+ &shared_funding_output,
6792+ change_strategy,
6793+ ) {
6794+ Ok(Some(change_output)) => self.our_funding_outputs.push(change_output),
6795+ Ok(None) => {},
6796+ Err(reason) => return Err(self.into_negotiation_error(reason)),
68206797 }
68216798
68226799 let constructor_args = InteractiveTxConstructorArgs {
@@ -6838,6 +6815,55 @@ impl FundingNegotiationContext {
68386815 InteractiveTxConstructor::new(constructor_args)
68396816 }
68406817
6818+ fn calculate_change_output<SP: Deref>(
6819+ &self, context: &ChannelContext<SP>, signer_provider: &SP, shared_funding_output: &TxOut,
6820+ change_strategy: ChangeStrategy,
6821+ ) -> Result<Option<TxOut>, AbortReason>
6822+ where
6823+ SP::Target: SignerProvider,
6824+ {
6825+ if self.our_funding_inputs.is_empty() {
6826+ return Ok(None);
6827+ }
6828+
6829+ let change_script = match change_strategy {
6830+ ChangeStrategy::FromCoinSelection => return Ok(None),
6831+ ChangeStrategy::LegacyUserProvided(change_script) => change_script,
6832+ };
6833+
6834+ let change_value = calculate_change_output_value(
6835+ &self,
6836+ self.shared_funding_input.is_some(),
6837+ &shared_funding_output.script_pubkey,
6838+ context.holder_dust_limit_satoshis,
6839+ )?;
6840+
6841+ if let Some(change_value) = change_value {
6842+ let change_script = match change_script {
6843+ Some(script) => script,
6844+ None => match signer_provider.get_destination_script(context.channel_keys_id) {
6845+ Ok(script) => script,
6846+ Err(_) => {
6847+ return Err(AbortReason::InternalError("Error getting change script"))
6848+ },
6849+ },
6850+ };
6851+ let mut change_output = TxOut { value: change_value, script_pubkey: change_script };
6852+ let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6853+ let change_output_fee =
6854+ fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6855+ let change_value_decreased_with_fee =
6856+ change_value.to_sat().saturating_sub(change_output_fee);
6857+ // Check dust limit again
6858+ if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6859+ change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6860+ return Ok(Some(change_output));
6861+ }
6862+ }
6863+
6864+ Ok(None)
6865+ }
6866+
68416867 fn into_negotiation_error(self, reason: AbortReason) -> NegotiationError {
68426868 let (contributed_inputs, contributed_outputs) = self.into_contributed_inputs_and_outputs();
68436869 NegotiationError { reason, contributed_inputs, contributed_outputs }
@@ -12262,14 +12288,13 @@ where
1226212288 shared_funding_input: Some(prev_funding_input),
1226312289 our_funding_inputs,
1226412290 our_funding_outputs,
12265- change_script,
1226612291 };
1226712292
12268- self.send_splice_init_internal(context)
12293+ self.send_splice_init_internal(context, ChangeStrategy::LegacyUserProvided(change_script) )
1226912294 }
1227012295
1227112296 fn send_splice_init_internal(
12272- &mut self, context: FundingNegotiationContext,
12297+ &mut self, context: FundingNegotiationContext, change_strategy: ChangeStrategy,
1227312298 ) -> msgs::SpliceInit {
1227412299 debug_assert!(self.pending_splice.is_none());
1227512300 // Rotate the funding pubkey using the prev_funding_txid as a tweak
@@ -12290,8 +12315,11 @@ where
1229012315 let funding_contribution_satoshis = context.our_funding_contribution.to_sat();
1229112316 let locktime = context.funding_tx_locktime.to_consensus_u32();
1229212317
12293- let funding_negotiation =
12294- FundingNegotiation::AwaitingAck { context, new_holder_funding_key: funding_pubkey };
12318+ let funding_negotiation = FundingNegotiation::AwaitingAck {
12319+ context,
12320+ change_strategy,
12321+ new_holder_funding_key: funding_pubkey,
12322+ };
1229512323 self.pending_splice = Some(PendingFunding {
1229612324 funding_negotiation: Some(funding_negotiation),
1229712325 negotiated_candidates: vec![],
@@ -12521,7 +12549,6 @@ where
1252112549 shared_funding_input: Some(prev_funding_input),
1252212550 our_funding_inputs: Vec::new(),
1252312551 our_funding_outputs: Vec::new(),
12524- change_script: None,
1252512552 };
1252612553
1252712554 let mut interactive_tx_constructor = funding_negotiation_context
@@ -12531,6 +12558,8 @@ where
1253112558 signer_provider,
1253212559 entropy_source,
1253312560 holder_node_id.clone(),
12561+ // ChangeStrategy doesn't matter when no inputs are contributed
12562+ ChangeStrategy::FromCoinSelection,
1253412563 )
1253512564 .map_err(|err| {
1253612565 ChannelError::WarnAndDisconnect(format!(
@@ -12585,11 +12614,11 @@ where
1258512614 let pending_splice =
1258612615 self.pending_splice.as_mut().expect("We should have returned an error earlier!");
1258712616 // TODO: Good candidate for a let else statement once MSRV >= 1.65
12588- let funding_negotiation_context =
12589- if let Some(FundingNegotiation::AwaitingAck { context, .. }) =
12617+ let ( funding_negotiation_context, change_strategy) =
12618+ if let Some(FundingNegotiation::AwaitingAck { context, change_strategy, .. }) =
1259012619 pending_splice.funding_negotiation.take()
1259112620 {
12592- context
12621+ ( context, change_strategy)
1259312622 } else {
1259412623 panic!("We should have returned an error earlier!");
1259512624 };
@@ -12601,6 +12630,7 @@ where
1260112630 signer_provider,
1260212631 entropy_source,
1260312632 holder_node_id.clone(),
12633+ change_strategy,
1260412634 )
1260512635 .map_err(|err| {
1260612636 ChannelError::WarnAndDisconnect(format!(
@@ -12631,7 +12661,7 @@ where
1263112661 let (funding_negotiation_context, new_holder_funding_key) = match &pending_splice
1263212662 .funding_negotiation
1263312663 {
12634- Some(FundingNegotiation::AwaitingAck { context, new_holder_funding_key }) => {
12664+ Some(FundingNegotiation::AwaitingAck { context, new_holder_funding_key, .. }) => {
1263512665 (context, new_holder_funding_key)
1263612666 },
1263712667 Some(FundingNegotiation::ConstructingTransaction { .. })
@@ -13594,7 +13624,7 @@ where
1359413624 },
1359513625 };
1359613626 let funding_feerate_per_kw = contribution.feerate().to_sat_per_kwu() as u32;
13597- let (our_funding_inputs, our_funding_outputs, change_script ) = contribution.into_tx_parts();
13627+ let (our_funding_inputs, our_funding_outputs) = contribution.into_tx_parts();
1359813628
1359913629 let context = FundingNegotiationContext {
1360013630 is_initiator,
@@ -13604,10 +13634,9 @@ where
1360413634 shared_funding_input: Some(prev_funding_input),
1360513635 our_funding_inputs,
1360613636 our_funding_outputs,
13607- change_script,
1360813637 };
1360913638
13610- let splice_init = self.send_splice_init_internal(context);
13639+ let splice_init = self.send_splice_init_internal(context, ChangeStrategy::FromCoinSelection );
1361113640 return Ok(Some(StfuResponse::SpliceInit(splice_init)));
1361213641 },
1361313642 #[cfg(any(test, fuzzing))]
@@ -14444,7 +14473,6 @@ where
1444414473 shared_funding_input: None,
1444514474 our_funding_inputs: funding_inputs,
1444614475 our_funding_outputs: Vec::new(),
14447- change_script: None,
1444814476 };
1444914477 let chan = Self {
1445014478 funding,
@@ -14598,7 +14626,6 @@ where
1459814626 shared_funding_input: None,
1459914627 our_funding_inputs: our_funding_inputs.clone(),
1460014628 our_funding_outputs: Vec::new(),
14601- change_script: None,
1460214629 };
1460314630 let shared_funding_output = TxOut {
1460414631 value: Amount::from_sat(funding.get_value_satoshis()),
0 commit comments