@@ -5981,6 +5981,9 @@ pub(super) struct FundingNegotiationContext {
5981
5981
/// The funding inputs we will be contributing to the channel.
5982
5982
#[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
5983
5983
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>,
5984
5987
/// The change output script. This will be used if needed or -- if not set -- generated using
5985
5988
/// `SignerProvider::get_destination_script`.
5986
5989
#[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
@@ -6010,45 +6013,47 @@ impl FundingNegotiationContext {
6010
6013
debug_assert!(matches!(context.channel_state, ChannelState::NegotiatingFunding(_)));
6011
6014
}
6012
6015
6013
- // Add output for funding tx
6014
6016
// Note: For the error case when the inputs are insufficient, it will be handled after
6015
6017
// the `calculate_change_output_value` call below
6016
- let mut funding_outputs = Vec::new();
6017
6018
6018
6019
let shared_funding_output = TxOut {
6019
6020
value: Amount::from_sat(funding.get_value_satoshis()),
6020
6021
script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
6021
6022
};
6022
6023
6023
6024
// 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(
6026
6027
&self,
6027
6028
self.shared_funding_input.is_some(),
6028
6029
&shared_funding_output.script_pubkey,
6029
- &funding_outputs,
6030
6030
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);
6052
6057
}
6053
6058
}
6054
6059
@@ -10644,43 +10649,84 @@ where
10644
10649
if our_funding_contribution > SignedAmount::MAX_MONEY {
10645
10650
return Err(APIError::APIMisuseError {
10646
10651
err: format!(
10647
- "Channel {} cannot be spliced; contribution exceeds total bitcoin supply: {}",
10652
+ "Channel {} cannot be spliced in ; contribution exceeds total bitcoin supply: {}",
10648
10653
self.context.channel_id(),
10649
10654
our_funding_contribution,
10650
10655
),
10651
10656
});
10652
10657
}
10653
10658
10654
- if our_funding_contribution < SignedAmount::ZERO {
10659
+ if our_funding_contribution < - SignedAmount::MAX_MONEY {
10655
10660
return Err(APIError::APIMisuseError {
10656
10661
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
+ ),
10660
10666
});
10661
10667
}
10662
10668
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
+ }
10665
10697
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)
10668
10713
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
+ }
10684
10730
10685
10731
for FundingTxInput { txin, prevtx, .. } in contribution.inputs().iter() {
10686
10732
const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
@@ -10703,14 +10749,15 @@ where
10703
10749
}
10704
10750
10705
10751
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();
10707
10753
let funding_negotiation_context = FundingNegotiationContext {
10708
10754
is_initiator: true,
10709
10755
our_funding_contribution,
10710
10756
funding_tx_locktime: LockTime::from_consensus(locktime),
10711
10757
funding_feerate_sat_per_1000_weight: funding_feerate_per_kw,
10712
10758
shared_funding_input: Some(prev_funding_input),
10713
10759
our_funding_inputs,
10760
+ our_funding_outputs,
10714
10761
change_script,
10715
10762
};
10716
10763
@@ -10833,6 +10880,7 @@ where
10833
10880
funding_feerate_sat_per_1000_weight: msg.funding_feerate_per_kw,
10834
10881
shared_funding_input: Some(prev_funding_input),
10835
10882
our_funding_inputs: Vec::new(),
10883
+ our_funding_outputs: Vec::new(),
10836
10884
change_script: None,
10837
10885
};
10838
10886
@@ -12531,6 +12579,7 @@ where
12531
12579
funding_feerate_sat_per_1000_weight,
12532
12580
shared_funding_input: None,
12533
12581
our_funding_inputs: funding_inputs,
12582
+ our_funding_outputs: Vec::new(),
12534
12583
change_script: None,
12535
12584
};
12536
12585
let chan = Self {
@@ -12685,6 +12734,7 @@ where
12685
12734
funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
12686
12735
shared_funding_input: None,
12687
12736
our_funding_inputs: our_funding_inputs.clone(),
12737
+ our_funding_outputs: Vec::new(),
12688
12738
change_script: None,
12689
12739
};
12690
12740
let shared_funding_output = TxOut {
@@ -12708,7 +12758,7 @@ where
12708
12758
inputs_to_contribute,
12709
12759
shared_funding_input: None,
12710
12760
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 (),
12712
12762
}
12713
12763
).map_err(|err| {
12714
12764
let reason = ClosureReason::ProcessingError { err: err.to_string() };
0 commit comments