@@ -5982,6 +5982,9 @@ pub(super) struct FundingNegotiationContext {
5982
5982
/// The funding inputs we will be contributing to the channel.
5983
5983
#[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
5984
5984
pub our_funding_inputs: Vec<FundingTxInput>,
5985
+ /// The funding outputs we will be contributing to the channel.
5986
+ #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
5987
+ pub our_funding_outputs: Vec<TxOut>,
5985
5988
/// The change output script. This will be used if needed or -- if not set -- generated using
5986
5989
/// `SignerProvider::get_destination_script`.
5987
5990
#[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
@@ -6011,45 +6014,47 @@ impl FundingNegotiationContext {
6011
6014
debug_assert!(matches!(context.channel_state, ChannelState::NegotiatingFunding(_)));
6012
6015
}
6013
6016
6014
- // Add output for funding tx
6015
6017
// Note: For the error case when the inputs are insufficient, it will be handled after
6016
6018
// the `calculate_change_output_value` call below
6017
- let mut funding_outputs = Vec::new();
6018
6019
6019
6020
let shared_funding_output = TxOut {
6020
6021
value: Amount::from_sat(funding.get_value_satoshis()),
6021
6022
script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
6022
6023
};
6023
6024
6024
6025
// Optionally add change output
6025
- if self.our_funding_contribution > SignedAmount::ZERO {
6026
- let change_value_opt = calculate_change_output_value(
6026
+ let change_value_opt = if self.our_funding_contribution > SignedAmount::ZERO {
6027
+ calculate_change_output_value(
6027
6028
&self,
6028
6029
self.shared_funding_input.is_some(),
6029
6030
&shared_funding_output.script_pubkey,
6030
- &funding_outputs,
6031
6031
context.holder_dust_limit_satoshis,
6032
- )?;
6033
- if let Some(change_value) = change_value_opt {
6034
- let change_script = if let Some(script) = self.change_script {
6035
- script
6036
- } else {
6037
- signer_provider
6038
- .get_destination_script(context.channel_keys_id)
6039
- .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6040
- };
6041
- let mut change_output =
6042
- TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6043
- let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6044
- let change_output_fee =
6045
- fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6046
- let change_value_decreased_with_fee =
6047
- change_value.saturating_sub(change_output_fee);
6048
- // Check dust limit again
6049
- if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6050
- change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6051
- funding_outputs.push(change_output);
6052
- }
6032
+ )?
6033
+ } else {
6034
+ None
6035
+ };
6036
+
6037
+ let mut funding_outputs = self.our_funding_outputs;
6038
+
6039
+ if let Some(change_value) = change_value_opt {
6040
+ let change_script = if let Some(script) = self.change_script {
6041
+ script
6042
+ } else {
6043
+ signer_provider
6044
+ .get_destination_script(context.channel_keys_id)
6045
+ .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6046
+ };
6047
+ let mut change_output =
6048
+ TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6049
+ let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6050
+ let change_output_fee =
6051
+ fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6052
+ let change_value_decreased_with_fee =
6053
+ change_value.saturating_sub(change_output_fee);
6054
+ // Check dust limit again
6055
+ if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6056
+ change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6057
+ funding_outputs.push(change_output);
6053
6058
}
6054
6059
}
6055
6060
@@ -10638,43 +10643,84 @@ where
10638
10643
if our_funding_contribution > SignedAmount::MAX_MONEY {
10639
10644
return Err(APIError::APIMisuseError {
10640
10645
err: format!(
10641
- "Channel {} cannot be spliced; contribution exceeds total bitcoin supply: {}",
10646
+ "Channel {} cannot be spliced in ; contribution exceeds total bitcoin supply: {}",
10642
10647
self.context.channel_id(),
10643
10648
our_funding_contribution,
10644
10649
),
10645
10650
});
10646
10651
}
10647
10652
10648
- if our_funding_contribution < SignedAmount::ZERO {
10653
+ if our_funding_contribution < - SignedAmount::MAX_MONEY {
10649
10654
return Err(APIError::APIMisuseError {
10650
10655
err: format!(
10651
- "TODO(splicing): Splice-out not supported, only splice in; channel ID {}, contribution {}",
10652
- self.context.channel_id(), our_funding_contribution,
10653
- ),
10656
+ "Channel {} cannot be spliced out; contribution exceeds total bitcoin supply: {}",
10657
+ self.context.channel_id(),
10658
+ our_funding_contribution,
10659
+ ),
10654
10660
});
10655
10661
}
10656
10662
10657
- // TODO(splicing): Once splice-out is supported, check that channel balance does not go below 0
10658
- // (or below channel reserve)
10663
+ let funding_inputs = contribution.inputs();
10664
+ let funding_outputs = contribution.outputs();
10665
+ if !funding_inputs.is_empty() && !funding_outputs.is_empty() {
10666
+ return Err(APIError::APIMisuseError {
10667
+ err: format!(
10668
+ "Channel {} cannot be both spliced in and out; operation not supported",
10669
+ self.context.channel_id(),
10670
+ ),
10671
+ });
10672
+ }
10673
+
10674
+ if our_funding_contribution < SignedAmount::ZERO {
10675
+ // TODO(splicing): Check that channel balance does not go below the channel reserve
10676
+ let post_channel_value = AddSigned::checked_add_signed(
10677
+ self.funding.get_value_satoshis(),
10678
+ our_funding_contribution.to_sat(),
10679
+ );
10680
+ // FIXME: Should we check value_to_self instead? Do HTLCs need to be accounted for?
10681
+ // FIXME: Check that we can pay for the outputs from the channel value?
10682
+ if post_channel_value.is_none() {
10683
+ return Err(APIError::APIMisuseError {
10684
+ err: format!(
10685
+ "Channel {} cannot be spliced out; contribution exceeds the channel value: {}",
10686
+ self.context.channel_id(),
10687
+ our_funding_contribution,
10688
+ ),
10689
+ });
10690
+ }
10659
10691
10660
- // Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
10661
- // (Cannot test for miminum required post-splice channel value)
10692
+ let value_removed: Amount =
10693
+ contribution.outputs().iter().map(|txout| txout.value).sum();
10694
+ let negated_value_removed = -value_removed.to_signed().unwrap_or(SignedAmount::MAX);
10695
+ if negated_value_removed != our_funding_contribution {
10696
+ return Err(APIError::APIMisuseError {
10697
+ err: format!(
10698
+ "Channel {} cannot be spliced out; unexpected txout amounts: {}",
10699
+ self.context.channel_id(),
10700
+ value_removed,
10701
+ ),
10702
+ });
10703
+ }
10704
+ } else {
10705
+ // Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
10706
+ // (Cannot test for miminum required post-splice channel value)
10662
10707
10663
- // Check that inputs are sufficient to cover our contribution.
10664
- let _fee = check_v2_funding_inputs_sufficient(
10665
- our_funding_contribution.to_sat(),
10666
- contribution.inputs(),
10667
- true,
10668
- true,
10669
- funding_feerate_per_kw,
10670
- )
10671
- .map_err(|err| APIError::APIMisuseError {
10672
- err: format!(
10673
- "Insufficient inputs for splicing; channel ID {}, err {}",
10674
- self.context.channel_id(),
10675
- err,
10676
- ),
10677
- })?;
10708
+ // Check that inputs are sufficient to cover our contribution.
10709
+ let _fee = check_v2_funding_inputs_sufficient(
10710
+ our_funding_contribution.to_sat(),
10711
+ contribution.inputs(),
10712
+ true,
10713
+ true,
10714
+ funding_feerate_per_kw,
10715
+ )
10716
+ .map_err(|err| APIError::APIMisuseError {
10717
+ err: format!(
10718
+ "Insufficient inputs for splicing; channel ID {}, err {}",
10719
+ self.context.channel_id(),
10720
+ err,
10721
+ ),
10722
+ })?;
10723
+ }
10678
10724
10679
10725
for FundingTxInput { txin, prevtx, .. } in contribution.inputs().iter() {
10680
10726
const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
@@ -10697,14 +10743,15 @@ where
10697
10743
}
10698
10744
10699
10745
let prev_funding_input = self.funding.to_splice_funding_input();
10700
- let (our_funding_inputs, change_script) = contribution.into_tx_parts();
10746
+ let (our_funding_inputs, our_funding_outputs, change_script) = contribution.into_tx_parts();
10701
10747
let funding_negotiation_context = FundingNegotiationContext {
10702
10748
is_initiator: true,
10703
10749
our_funding_contribution,
10704
10750
funding_tx_locktime: LockTime::from_consensus(locktime),
10705
10751
funding_feerate_sat_per_1000_weight: funding_feerate_per_kw,
10706
10752
shared_funding_input: Some(prev_funding_input),
10707
10753
our_funding_inputs,
10754
+ our_funding_outputs,
10708
10755
change_script,
10709
10756
};
10710
10757
@@ -10827,6 +10874,7 @@ where
10827
10874
funding_feerate_sat_per_1000_weight: msg.funding_feerate_per_kw,
10828
10875
shared_funding_input: Some(prev_funding_input),
10829
10876
our_funding_inputs: Vec::new(),
10877
+ our_funding_outputs: Vec::new(),
10830
10878
change_script: None,
10831
10879
};
10832
10880
@@ -12525,6 +12573,7 @@ where
12525
12573
funding_feerate_sat_per_1000_weight,
12526
12574
shared_funding_input: None,
12527
12575
our_funding_inputs: funding_inputs,
12576
+ our_funding_outputs: Vec::new(),
12528
12577
change_script: None,
12529
12578
};
12530
12579
let chan = Self {
@@ -12679,6 +12728,7 @@ where
12679
12728
funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
12680
12729
shared_funding_input: None,
12681
12730
our_funding_inputs: our_funding_inputs.clone(),
12731
+ our_funding_outputs: Vec::new(),
12682
12732
change_script: None,
12683
12733
};
12684
12734
let shared_funding_output = TxOut {
@@ -12702,7 +12752,7 @@ where
12702
12752
inputs_to_contribute,
12703
12753
shared_funding_input: None,
12704
12754
shared_funding_output: SharedOwnedOutput::new(shared_funding_output, our_funding_contribution_sats),
12705
- outputs_to_contribute: Vec::new (),
12755
+ outputs_to_contribute: funding_negotiation_context.our_funding_outputs.clone (),
12706
12756
}
12707
12757
).map_err(|err| {
12708
12758
let reason = ClosureReason::ProcessingError { err: err.to_string() };
0 commit comments