@@ -5981,6 +5981,9 @@ pub(super) struct FundingNegotiationContext {
59815981 /// The funding inputs we will be contributing to the channel.
59825982 #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
59835983 pub our_funding_inputs: Vec<FundingTxInput>,
5984+ /// The funding outputs we will be contributing to the channel.
5985+ #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
5986+ pub our_funding_outputs: Vec<TxOut>,
59845987 /// The change output script. This will be used if needed or -- if not set -- generated using
59855988 /// `SignerProvider::get_destination_script`.
59865989 #[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
@@ -6010,45 +6013,47 @@ impl FundingNegotiationContext {
60106013 debug_assert!(matches!(context.channel_state, ChannelState::NegotiatingFunding(_)));
60116014 }
60126015
6013- // Add output for funding tx
60146016 // Note: For the error case when the inputs are insufficient, it will be handled after
60156017 // the `calculate_change_output_value` call below
6016- let mut funding_outputs = Vec::new();
60176018
60186019 let shared_funding_output = TxOut {
60196020 value: Amount::from_sat(funding.get_value_satoshis()),
60206021 script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
60216022 };
60226023
60236024 // Optionally add change output
6024- if self.our_funding_contribution > SignedAmount::ZERO {
6025- let change_value_opt = calculate_change_output_value(
6025+ let change_value_opt = if self.our_funding_contribution > SignedAmount::ZERO {
6026+ calculate_change_output_value(
60266027 &self,
60276028 self.shared_funding_input.is_some(),
60286029 &shared_funding_output.script_pubkey,
6029- &funding_outputs,
60306030 context.holder_dust_limit_satoshis,
6031- )?;
6032- if let Some(change_value) = change_value_opt {
6033- let change_script = if let Some(script) = self.change_script {
6034- script
6035- } else {
6036- signer_provider
6037- .get_destination_script(context.channel_keys_id)
6038- .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6039- };
6040- let mut change_output =
6041- TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6042- let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6043- let change_output_fee =
6044- fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6045- let change_value_decreased_with_fee =
6046- change_value.saturating_sub(change_output_fee);
6047- // Check dust limit again
6048- if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6049- change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6050- funding_outputs.push(change_output);
6051- }
6031+ )?
6032+ } else {
6033+ None
6034+ };
6035+
6036+ let mut funding_outputs = self.our_funding_outputs;
6037+
6038+ if let Some(change_value) = change_value_opt {
6039+ let change_script = if let Some(script) = self.change_script {
6040+ script
6041+ } else {
6042+ signer_provider
6043+ .get_destination_script(context.channel_keys_id)
6044+ .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6045+ };
6046+ let mut change_output =
6047+ TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6048+ let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6049+ let change_output_fee =
6050+ fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6051+ let change_value_decreased_with_fee =
6052+ change_value.saturating_sub(change_output_fee);
6053+ // Check dust limit again
6054+ if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6055+ change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6056+ funding_outputs.push(change_output);
60526057 }
60536058 }
60546059
@@ -10644,43 +10649,84 @@ where
1064410649 if our_funding_contribution > SignedAmount::MAX_MONEY {
1064510650 return Err(APIError::APIMisuseError {
1064610651 err: format!(
10647- "Channel {} cannot be spliced; contribution exceeds total bitcoin supply: {}",
10652+ "Channel {} cannot be spliced in ; contribution exceeds total bitcoin supply: {}",
1064810653 self.context.channel_id(),
1064910654 our_funding_contribution,
1065010655 ),
1065110656 });
1065210657 }
1065310658
10654- if our_funding_contribution < SignedAmount::ZERO {
10659+ if our_funding_contribution < - SignedAmount::MAX_MONEY {
1065510660 return Err(APIError::APIMisuseError {
1065610661 err: format!(
10657- "TODO(splicing): Splice-out not supported, only splice in; channel ID {}, contribution {}",
10658- self.context.channel_id(), our_funding_contribution,
10659- ),
10662+ "Channel {} cannot be spliced out; contribution exceeds total bitcoin supply: {}",
10663+ self.context.channel_id(),
10664+ our_funding_contribution,
10665+ ),
1066010666 });
1066110667 }
1066210668
10663- // TODO(splicing): Once splice-out is supported, check that channel balance does not go below 0
10664- // (or below channel reserve)
10669+ let funding_inputs = contribution.inputs();
10670+ let funding_outputs = contribution.outputs();
10671+ if !funding_inputs.is_empty() && !funding_outputs.is_empty() {
10672+ return Err(APIError::APIMisuseError {
10673+ err: format!(
10674+ "Channel {} cannot be both spliced in and out; operation not supported",
10675+ self.context.channel_id(),
10676+ ),
10677+ });
10678+ }
10679+
10680+ if our_funding_contribution < SignedAmount::ZERO {
10681+ // TODO(splicing): Check that channel balance does not go below the channel reserve
10682+ let post_channel_value = AddSigned::checked_add_signed(
10683+ self.funding.get_value_satoshis(),
10684+ our_funding_contribution.to_sat(),
10685+ );
10686+ // FIXME: Should we check value_to_self instead? Do HTLCs need to be accounted for?
10687+ // FIXME: Check that we can pay for the outputs from the channel value?
10688+ if post_channel_value.is_none() {
10689+ return Err(APIError::APIMisuseError {
10690+ err: format!(
10691+ "Channel {} cannot be spliced out; contribution exceeds the channel value: {}",
10692+ self.context.channel_id(),
10693+ our_funding_contribution,
10694+ ),
10695+ });
10696+ }
1066510697
10666- // Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
10667- // (Cannot test for miminum required post-splice channel value)
10698+ let value_removed: Amount =
10699+ contribution.outputs().iter().map(|txout| txout.value).sum();
10700+ let negated_value_removed = -value_removed.to_signed().unwrap_or(SignedAmount::MAX);
10701+ if negated_value_removed != our_funding_contribution {
10702+ return Err(APIError::APIMisuseError {
10703+ err: format!(
10704+ "Channel {} cannot be spliced out; unexpected txout amounts: {}",
10705+ self.context.channel_id(),
10706+ value_removed,
10707+ ),
10708+ });
10709+ }
10710+ } else {
10711+ // Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
10712+ // (Cannot test for miminum required post-splice channel value)
1066810713
10669- // Check that inputs are sufficient to cover our contribution.
10670- let _fee = check_v2_funding_inputs_sufficient(
10671- our_funding_contribution.to_sat(),
10672- contribution.inputs(),
10673- true,
10674- true,
10675- funding_feerate_per_kw,
10676- )
10677- .map_err(|err| APIError::APIMisuseError {
10678- err: format!(
10679- "Insufficient inputs for splicing; channel ID {}, err {}",
10680- self.context.channel_id(),
10681- err,
10682- ),
10683- })?;
10714+ // Check that inputs are sufficient to cover our contribution.
10715+ let _fee = check_v2_funding_inputs_sufficient(
10716+ our_funding_contribution.to_sat(),
10717+ contribution.inputs(),
10718+ true,
10719+ true,
10720+ funding_feerate_per_kw,
10721+ )
10722+ .map_err(|err| APIError::APIMisuseError {
10723+ err: format!(
10724+ "Insufficient inputs for splicing; channel ID {}, err {}",
10725+ self.context.channel_id(),
10726+ err,
10727+ ),
10728+ })?;
10729+ }
1068410730
1068510731 for FundingTxInput { txin, prevtx, .. } in contribution.inputs().iter() {
1068610732 const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
@@ -10703,14 +10749,15 @@ where
1070310749 }
1070410750
1070510751 let prev_funding_input = self.funding.to_splice_funding_input();
10706- let (our_funding_inputs, change_script) = contribution.into_tx_parts();
10752+ let (our_funding_inputs, our_funding_outputs, change_script) = contribution.into_tx_parts();
1070710753 let funding_negotiation_context = FundingNegotiationContext {
1070810754 is_initiator: true,
1070910755 our_funding_contribution,
1071010756 funding_tx_locktime: LockTime::from_consensus(locktime),
1071110757 funding_feerate_sat_per_1000_weight: funding_feerate_per_kw,
1071210758 shared_funding_input: Some(prev_funding_input),
1071310759 our_funding_inputs,
10760+ our_funding_outputs,
1071410761 change_script,
1071510762 };
1071610763
@@ -10833,6 +10880,7 @@ where
1083310880 funding_feerate_sat_per_1000_weight: msg.funding_feerate_per_kw,
1083410881 shared_funding_input: Some(prev_funding_input),
1083510882 our_funding_inputs: Vec::new(),
10883+ our_funding_outputs: Vec::new(),
1083610884 change_script: None,
1083710885 };
1083810886
@@ -12531,6 +12579,7 @@ where
1253112579 funding_feerate_sat_per_1000_weight,
1253212580 shared_funding_input: None,
1253312581 our_funding_inputs: funding_inputs,
12582+ our_funding_outputs: Vec::new(),
1253412583 change_script: None,
1253512584 };
1253612585 let chan = Self {
@@ -12685,6 +12734,7 @@ where
1268512734 funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
1268612735 shared_funding_input: None,
1268712736 our_funding_inputs: our_funding_inputs.clone(),
12737+ our_funding_outputs: Vec::new(),
1268812738 change_script: None,
1268912739 };
1269012740 let shared_funding_output = TxOut {
@@ -12708,7 +12758,7 @@ where
1270812758 inputs_to_contribute,
1270912759 shared_funding_input: None,
1271012760 shared_funding_output: SharedOwnedOutput::new(shared_funding_output, our_funding_contribution_sats),
12711- outputs_to_contribute: Vec::new (),
12761+ outputs_to_contribute: funding_negotiation_context.our_funding_outputs.clone (),
1271212762 }
1271312763 ).map_err(|err| {
1271412764 let reason = ClosureReason::ProcessingError { err: err.to_string() };
0 commit comments