@@ -5983,6 +5983,9 @@ pub(super) struct FundingNegotiationContext {
5983
5983
/// The funding inputs we will be contributing to the channel.
5984
5984
#[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
5985
5985
pub our_funding_inputs: Vec<FundingTxInput>,
5986
+ /// The funding outputs we will be contributing to the channel.
5987
+ #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
5988
+ pub our_funding_outputs: Vec<TxOut>,
5986
5989
/// The change output script. This will be used if needed or -- if not set -- generated using
5987
5990
/// `SignerProvider::get_destination_script`.
5988
5991
#[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
@@ -6012,45 +6015,47 @@ impl FundingNegotiationContext {
6012
6015
debug_assert!(matches!(context.channel_state, ChannelState::NegotiatingFunding(_)));
6013
6016
}
6014
6017
6015
- // Add output for funding tx
6016
6018
// Note: For the error case when the inputs are insufficient, it will be handled after
6017
6019
// the `calculate_change_output_value` call below
6018
- let mut funding_outputs = Vec::new();
6019
6020
6020
6021
let shared_funding_output = TxOut {
6021
6022
value: Amount::from_sat(funding.get_value_satoshis()),
6022
6023
script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
6023
6024
};
6024
6025
6025
6026
// Optionally add change output
6026
- if self.our_funding_contribution > SignedAmount::ZERO {
6027
- let change_value_opt = calculate_change_output_value(
6027
+ let change_value_opt = if self.our_funding_contribution > SignedAmount::ZERO {
6028
+ calculate_change_output_value(
6028
6029
&self,
6029
6030
self.shared_funding_input.is_some(),
6030
6031
&shared_funding_output.script_pubkey,
6031
- &funding_outputs,
6032
6032
context.holder_dust_limit_satoshis,
6033
- )?;
6034
- if let Some(change_value) = change_value_opt {
6035
- let change_script = if let Some(script) = self.change_script {
6036
- script
6037
- } else {
6038
- signer_provider
6039
- .get_destination_script(context.channel_keys_id)
6040
- .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6041
- };
6042
- let mut change_output =
6043
- TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6044
- let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6045
- let change_output_fee =
6046
- fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6047
- let change_value_decreased_with_fee =
6048
- change_value.saturating_sub(change_output_fee);
6049
- // Check dust limit again
6050
- if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6051
- change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6052
- funding_outputs.push(change_output);
6053
- }
6033
+ )?
6034
+ } else {
6035
+ None
6036
+ };
6037
+
6038
+ let mut funding_outputs = self.our_funding_outputs;
6039
+
6040
+ if let Some(change_value) = change_value_opt {
6041
+ let change_script = if let Some(script) = self.change_script {
6042
+ script
6043
+ } else {
6044
+ signer_provider
6045
+ .get_destination_script(context.channel_keys_id)
6046
+ .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6047
+ };
6048
+ let mut change_output =
6049
+ TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6050
+ let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6051
+ let change_output_fee =
6052
+ fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6053
+ let change_value_decreased_with_fee =
6054
+ change_value.saturating_sub(change_output_fee);
6055
+ // Check dust limit again
6056
+ if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6057
+ change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6058
+ funding_outputs.push(change_output);
6054
6059
}
6055
6060
}
6056
6061
@@ -10646,43 +10651,84 @@ where
10646
10651
if our_funding_contribution > SignedAmount::MAX_MONEY {
10647
10652
return Err(APIError::APIMisuseError {
10648
10653
err: format!(
10649
- "Channel {} cannot be spliced; contribution exceeds total bitcoin supply: {}",
10654
+ "Channel {} cannot be spliced in ; contribution exceeds total bitcoin supply: {}",
10650
10655
self.context.channel_id(),
10651
10656
our_funding_contribution,
10652
10657
),
10653
10658
});
10654
10659
}
10655
10660
10656
- if our_funding_contribution < SignedAmount::ZERO {
10661
+ if our_funding_contribution < - SignedAmount::MAX_MONEY {
10657
10662
return Err(APIError::APIMisuseError {
10658
10663
err: format!(
10659
- "TODO(splicing): Splice-out not supported, only splice in; channel ID {}, contribution {}",
10660
- self.context.channel_id(), our_funding_contribution,
10661
- ),
10664
+ "Channel {} cannot be spliced out; contribution exceeds total bitcoin supply: {}",
10665
+ self.context.channel_id(),
10666
+ our_funding_contribution,
10667
+ ),
10662
10668
});
10663
10669
}
10664
10670
10665
- // TODO(splicing): Once splice-out is supported, check that channel balance does not go below 0
10666
- // (or below channel reserve)
10671
+ let funding_inputs = contribution.inputs();
10672
+ let funding_outputs = contribution.outputs();
10673
+ if !funding_inputs.is_empty() && !funding_outputs.is_empty() {
10674
+ return Err(APIError::APIMisuseError {
10675
+ err: format!(
10676
+ "Channel {} cannot be both spliced in and out; operation not supported",
10677
+ self.context.channel_id(),
10678
+ ),
10679
+ });
10680
+ }
10681
+
10682
+ if our_funding_contribution < SignedAmount::ZERO {
10683
+ // TODO(splicing): Check that channel balance does not go below the channel reserve
10684
+ let post_channel_value = AddSigned::checked_add_signed(
10685
+ self.funding.get_value_satoshis(),
10686
+ our_funding_contribution.to_sat(),
10687
+ );
10688
+ // FIXME: Should we check value_to_self instead? Do HTLCs need to be accounted for?
10689
+ // FIXME: Check that we can pay for the outputs from the channel value?
10690
+ if post_channel_value.is_none() {
10691
+ return Err(APIError::APIMisuseError {
10692
+ err: format!(
10693
+ "Channel {} cannot be spliced out; contribution exceeds the channel value: {}",
10694
+ self.context.channel_id(),
10695
+ our_funding_contribution,
10696
+ ),
10697
+ });
10698
+ }
10667
10699
10668
- // Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
10669
- // (Cannot test for miminum required post-splice channel value)
10700
+ let value_removed: Amount =
10701
+ contribution.outputs().iter().map(|txout| txout.value).sum();
10702
+ let negated_value_removed = -value_removed.to_signed().unwrap_or(SignedAmount::MAX);
10703
+ if negated_value_removed != our_funding_contribution {
10704
+ return Err(APIError::APIMisuseError {
10705
+ err: format!(
10706
+ "Channel {} cannot be spliced out; unexpected txout amounts: {}",
10707
+ self.context.channel_id(),
10708
+ value_removed,
10709
+ ),
10710
+ });
10711
+ }
10712
+ } else {
10713
+ // Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
10714
+ // (Cannot test for miminum required post-splice channel value)
10670
10715
10671
- // Check that inputs are sufficient to cover our contribution.
10672
- let _fee = check_v2_funding_inputs_sufficient(
10673
- our_funding_contribution.to_sat(),
10674
- contribution.inputs(),
10675
- true,
10676
- true,
10677
- funding_feerate_per_kw,
10678
- )
10679
- .map_err(|err| APIError::APIMisuseError {
10680
- err: format!(
10681
- "Insufficient inputs for splicing; channel ID {}, err {}",
10682
- self.context.channel_id(),
10683
- err,
10684
- ),
10685
- })?;
10716
+ // Check that inputs are sufficient to cover our contribution.
10717
+ let _fee = check_v2_funding_inputs_sufficient(
10718
+ our_funding_contribution.to_sat(),
10719
+ contribution.inputs(),
10720
+ true,
10721
+ true,
10722
+ funding_feerate_per_kw,
10723
+ )
10724
+ .map_err(|err| APIError::APIMisuseError {
10725
+ err: format!(
10726
+ "Insufficient inputs for splicing; channel ID {}, err {}",
10727
+ self.context.channel_id(),
10728
+ err,
10729
+ ),
10730
+ })?;
10731
+ }
10686
10732
10687
10733
for FundingTxInput { txin, prevtx, .. } in contribution.inputs().iter() {
10688
10734
const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
@@ -10705,14 +10751,15 @@ where
10705
10751
}
10706
10752
10707
10753
let prev_funding_input = self.funding.to_splice_funding_input();
10708
- let (our_funding_inputs, change_script) = contribution.into_tx_parts();
10754
+ let (our_funding_inputs, our_funding_outputs, change_script) = contribution.into_tx_parts();
10709
10755
let funding_negotiation_context = FundingNegotiationContext {
10710
10756
is_initiator: true,
10711
10757
our_funding_contribution,
10712
10758
funding_tx_locktime: LockTime::from_consensus(locktime),
10713
10759
funding_feerate_sat_per_1000_weight: funding_feerate_per_kw,
10714
10760
shared_funding_input: Some(prev_funding_input),
10715
10761
our_funding_inputs,
10762
+ our_funding_outputs,
10716
10763
change_script,
10717
10764
};
10718
10765
@@ -10835,6 +10882,7 @@ where
10835
10882
funding_feerate_sat_per_1000_weight: msg.funding_feerate_per_kw,
10836
10883
shared_funding_input: Some(prev_funding_input),
10837
10884
our_funding_inputs: Vec::new(),
10885
+ our_funding_outputs: Vec::new(),
10838
10886
change_script: None,
10839
10887
};
10840
10888
@@ -12533,6 +12581,7 @@ where
12533
12581
funding_feerate_sat_per_1000_weight,
12534
12582
shared_funding_input: None,
12535
12583
our_funding_inputs: funding_inputs,
12584
+ our_funding_outputs: Vec::new(),
12536
12585
change_script: None,
12537
12586
};
12538
12587
let chan = Self {
@@ -12687,6 +12736,7 @@ where
12687
12736
funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
12688
12737
shared_funding_input: None,
12689
12738
our_funding_inputs: our_funding_inputs.clone(),
12739
+ our_funding_outputs: Vec::new(),
12690
12740
change_script: None,
12691
12741
};
12692
12742
let shared_funding_output = TxOut {
@@ -12710,7 +12760,7 @@ where
12710
12760
inputs_to_contribute,
12711
12761
shared_funding_input: None,
12712
12762
shared_funding_output: SharedOwnedOutput::new(shared_funding_output, our_funding_contribution_sats),
12713
- outputs_to_contribute: Vec::new (),
12763
+ outputs_to_contribute: funding_negotiation_context.our_funding_outputs.clone (),
12714
12764
}
12715
12765
).map_err(|err| {
12716
12766
let reason = ClosureReason::ProcessingError { err: err.to_string() };
0 commit comments