@@ -2715,6 +2715,7 @@ impl_writeable_tlv_based!(PendingFunding, {
27152715enum FundingNegotiation {
27162716 AwaitingAck {
27172717 context: FundingNegotiationContext,
2718+ change_strategy: ChangeStrategy,
27182719 new_holder_funding_key: PublicKey,
27192720 },
27202721 ConstructingTransaction {
@@ -6539,18 +6540,25 @@ pub(super) struct FundingNegotiationContext {
65396540 /// The funding outputs we will be contributing to the channel.
65406541 #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
65416542 pub our_funding_outputs: Vec<TxOut>,
6543+ }
6544+
6545+ /// How the funding transaction's change is determined.
6546+ #[derive(Debug)]
6547+ pub(super) enum ChangeStrategy {
6548+ /// The change output, if any, is included in the FundingContribution's outputs.
6549+ FromCoinSelection,
6550+
65426551 /// The change output script. This will be used if needed or -- if not set -- generated using
65436552 /// `SignerProvider::get_destination_script`.
6544- #[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
6545- pub change_script: Option<ScriptBuf>,
6553+ LegacyUserProvided(Option<ScriptBuf>),
65466554}
65476555
65486556impl FundingNegotiationContext {
65496557 /// Prepare and start interactive transaction negotiation.
65506558 /// If error occurs, it is caused by our side, not the counterparty.
65516559 fn into_interactive_tx_constructor<SP: Deref, ES: Deref>(
65526560 mut self, context: &ChannelContext<SP>, funding: &FundingScope, signer_provider: &SP,
6553- entropy_source: &ES, holder_node_id: PublicKey,
6561+ entropy_source: &ES, holder_node_id: PublicKey, change_strategy: ChangeStrategy,
65546562 ) -> Result<InteractiveTxConstructor, NegotiationError>
65556563 where
65566564 SP::Target: SignerProvider,
@@ -6575,46 +6583,15 @@ impl FundingNegotiationContext {
65756583 script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
65766584 };
65776585
6578- // Optionally add change output
6579- let change_value_opt = if !self.our_funding_inputs.is_empty() {
6580- match calculate_change_output_value(
6581- &self,
6582- self.shared_funding_input.is_some(),
6583- &shared_funding_output.script_pubkey,
6584- context.holder_dust_limit_satoshis,
6585- ) {
6586- Ok(change_value_opt) => change_value_opt,
6587- Err(reason) => {
6588- return Err(self.into_negotiation_error(reason));
6589- },
6590- }
6591- } else {
6592- None
6593- };
6594-
6595- if let Some(change_value) = change_value_opt {
6596- let change_script = if let Some(script) = self.change_script {
6597- script
6598- } else {
6599- match signer_provider.get_destination_script(context.channel_keys_id) {
6600- Ok(script) => script,
6601- Err(_) => {
6602- let reason = AbortReason::InternalError("Error getting change script");
6603- return Err(self.into_negotiation_error(reason));
6604- },
6605- }
6606- };
6607- let mut change_output = TxOut { value: change_value, script_pubkey: change_script };
6608- let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6609- let change_output_fee =
6610- fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6611- let change_value_decreased_with_fee =
6612- change_value.to_sat().saturating_sub(change_output_fee);
6613- // Check dust limit again
6614- if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6615- change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6616- self.our_funding_outputs.push(change_output);
6617- }
6586+ match self.calculate_change_output(
6587+ context,
6588+ signer_provider,
6589+ &shared_funding_output,
6590+ change_strategy,
6591+ ) {
6592+ Ok(Some(change_output)) => self.our_funding_outputs.push(change_output),
6593+ Ok(None) => {},
6594+ Err(reason) => return Err(self.into_negotiation_error(reason)),
66186595 }
66196596
66206597 let constructor_args = InteractiveTxConstructorArgs {
@@ -6636,6 +6613,55 @@ impl FundingNegotiationContext {
66366613 InteractiveTxConstructor::new(constructor_args)
66376614 }
66386615
6616+ fn calculate_change_output<SP: Deref>(
6617+ &self, context: &ChannelContext<SP>, signer_provider: &SP, shared_funding_output: &TxOut,
6618+ change_strategy: ChangeStrategy,
6619+ ) -> Result<Option<TxOut>, AbortReason>
6620+ where
6621+ SP::Target: SignerProvider,
6622+ {
6623+ if self.our_funding_inputs.is_empty() {
6624+ return Ok(None);
6625+ }
6626+
6627+ let change_script = match change_strategy {
6628+ ChangeStrategy::FromCoinSelection => return Ok(None),
6629+ ChangeStrategy::LegacyUserProvided(change_script) => change_script,
6630+ };
6631+
6632+ let change_value = calculate_change_output_value(
6633+ &self,
6634+ self.shared_funding_input.is_some(),
6635+ &shared_funding_output.script_pubkey,
6636+ context.holder_dust_limit_satoshis,
6637+ )?;
6638+
6639+ if let Some(change_value) = change_value {
6640+ let change_script = match change_script {
6641+ Some(script) => script,
6642+ None => match signer_provider.get_destination_script(context.channel_keys_id) {
6643+ Ok(script) => script,
6644+ Err(_) => {
6645+ return Err(AbortReason::InternalError("Error getting change script"))
6646+ },
6647+ },
6648+ };
6649+ let mut change_output = TxOut { value: change_value, script_pubkey: change_script };
6650+ let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6651+ let change_output_fee =
6652+ fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6653+ let change_value_decreased_with_fee =
6654+ change_value.to_sat().saturating_sub(change_output_fee);
6655+ // Check dust limit again
6656+ if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6657+ change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6658+ return Ok(Some(change_output));
6659+ }
6660+ }
6661+
6662+ Ok(None)
6663+ }
6664+
66396665 fn into_negotiation_error(self, reason: AbortReason) -> NegotiationError {
66406666 let (contributed_inputs, contributed_outputs) = self.into_contributed_inputs_and_outputs();
66416667 NegotiationError { reason, contributed_inputs, contributed_outputs }
@@ -12043,7 +12069,7 @@ where
1204312069 let prev_funding_input = self.funding.to_splice_funding_input();
1204412070 let is_initiator = contribution.is_initiator();
1204512071 let funding_feerate_per_kw = contribution.feerate().to_sat_per_kwu() as u32;
12046- let (our_funding_inputs, our_funding_outputs, change_script ) = contribution.into_tx_parts();
12072+ let (our_funding_inputs, our_funding_outputs) = contribution.into_tx_parts();
1204712073
1204812074 let context = FundingNegotiationContext {
1204912075 is_initiator,
@@ -12053,10 +12079,9 @@ where
1205312079 shared_funding_input: Some(prev_funding_input),
1205412080 our_funding_inputs,
1205512081 our_funding_outputs,
12056- change_script,
1205712082 };
1205812083
12059- Ok(self.send_splice_init_internal(context))
12084+ Ok(self.send_splice_init_internal(context, ChangeStrategy::FromCoinSelection ))
1206012085 }
1206112086
1206212087 fn send_splice_init(&mut self, instructions: SpliceInstructions) -> msgs::SpliceInit {
@@ -12078,14 +12103,13 @@ where
1207812103 shared_funding_input: Some(prev_funding_input),
1207912104 our_funding_inputs,
1208012105 our_funding_outputs,
12081- change_script,
1208212106 };
1208312107
12084- self.send_splice_init_internal(context)
12108+ self.send_splice_init_internal(context, ChangeStrategy::LegacyUserProvided(change_script) )
1208512109 }
1208612110
1208712111 fn send_splice_init_internal(
12088- &mut self, context: FundingNegotiationContext,
12112+ &mut self, context: FundingNegotiationContext, change_strategy: ChangeStrategy,
1208912113 ) -> msgs::SpliceInit {
1209012114 debug_assert!(self.pending_splice.is_none());
1209112115 // Rotate the funding pubkey using the prev_funding_txid as a tweak
@@ -12106,8 +12130,11 @@ where
1210612130 let funding_contribution_satoshis = context.our_funding_contribution.to_sat();
1210712131 let locktime = context.funding_tx_locktime.to_consensus_u32();
1210812132
12109- let funding_negotiation =
12110- FundingNegotiation::AwaitingAck { context, new_holder_funding_key: funding_pubkey };
12133+ let funding_negotiation = FundingNegotiation::AwaitingAck {
12134+ context,
12135+ change_strategy,
12136+ new_holder_funding_key: funding_pubkey,
12137+ };
1211112138 self.pending_splice = Some(PendingFunding {
1211212139 funding_negotiation: Some(funding_negotiation),
1211312140 negotiated_candidates: vec![],
@@ -12337,7 +12364,6 @@ where
1233712364 shared_funding_input: Some(prev_funding_input),
1233812365 our_funding_inputs: Vec::new(),
1233912366 our_funding_outputs: Vec::new(),
12340- change_script: None,
1234112367 };
1234212368
1234312369 let mut interactive_tx_constructor = funding_negotiation_context
@@ -12347,6 +12373,8 @@ where
1234712373 signer_provider,
1234812374 entropy_source,
1234912375 holder_node_id.clone(),
12376+ // ChangeStrategy doesn't matter when no inputs are contributed
12377+ ChangeStrategy::FromCoinSelection,
1235012378 )
1235112379 .map_err(|err| {
1235212380 ChannelError::WarnAndDisconnect(format!(
@@ -12401,11 +12429,11 @@ where
1240112429 let pending_splice =
1240212430 self.pending_splice.as_mut().expect("We should have returned an error earlier!");
1240312431 // TODO: Good candidate for a let else statement once MSRV >= 1.65
12404- let funding_negotiation_context =
12405- if let Some(FundingNegotiation::AwaitingAck { context, .. }) =
12432+ let ( funding_negotiation_context, change_strategy) =
12433+ if let Some(FundingNegotiation::AwaitingAck { context, change_strategy, .. }) =
1240612434 pending_splice.funding_negotiation.take()
1240712435 {
12408- context
12436+ ( context, change_strategy)
1240912437 } else {
1241012438 panic!("We should have returned an error earlier!");
1241112439 };
@@ -12417,6 +12445,7 @@ where
1241712445 signer_provider,
1241812446 entropy_source,
1241912447 holder_node_id.clone(),
12448+ change_strategy,
1242012449 )
1242112450 .map_err(|err| {
1242212451 ChannelError::WarnAndDisconnect(format!(
@@ -12447,7 +12476,7 @@ where
1244712476 let (funding_negotiation_context, new_holder_funding_key) = match &pending_splice
1244812477 .funding_negotiation
1244912478 {
12450- Some(FundingNegotiation::AwaitingAck { context, new_holder_funding_key }) => {
12479+ Some(FundingNegotiation::AwaitingAck { context, new_holder_funding_key, .. }) => {
1245112480 (context, new_holder_funding_key)
1245212481 },
1245312482 Some(FundingNegotiation::ConstructingTransaction { .. })
@@ -14239,7 +14268,6 @@ where
1423914268 shared_funding_input: None,
1424014269 our_funding_inputs: funding_inputs,
1424114270 our_funding_outputs: Vec::new(),
14242- change_script: None,
1424314271 };
1424414272 let chan = Self {
1424514273 funding,
@@ -14393,7 +14421,6 @@ where
1439314421 shared_funding_input: None,
1439414422 our_funding_inputs: our_funding_inputs.clone(),
1439514423 our_funding_outputs: Vec::new(),
14396- change_script: None,
1439714424 };
1439814425 let shared_funding_output = TxOut {
1439914426 value: Amount::from_sat(funding.get_value_satoshis()),
0 commit comments