@@ -26,7 +26,7 @@ use bitcoin::secp256k1::{ecdsa::Signature, Secp256k1};
26
26
use bitcoin::secp256k1::{PublicKey, SecretKey};
27
27
use bitcoin::{secp256k1, sighash};
28
28
#[cfg(splicing)]
29
- use bitcoin::{Sequence, TxIn};
29
+ use bitcoin::{FeeRate, Sequence, TxIn};
30
30
31
31
use crate::chain::chaininterface::{
32
32
fee_for_weight, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator,
@@ -5878,20 +5878,53 @@ fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satos
5878
5878
cmp::min(channel_value_satoshis, cmp::max(q, dust_limit_satoshis))
5879
5879
}
5880
5880
5881
+ #[cfg(splicing)]
5882
+ fn check_splice_contribution_sufficient(
5883
+ channel_balance: Amount, contribution: &SpliceContribution, is_initiator: bool,
5884
+ funding_feerate: FeeRate,
5885
+ ) -> Result<Amount, ChannelError> {
5886
+ let contribution_amount = contribution.value();
5887
+ if contribution_amount < SignedAmount::ZERO {
5888
+ let estimated_fee = Amount::from_sat(estimate_v2_funding_transaction_fee(
5889
+ is_initiator,
5890
+ 1, // spends the previous funding output
5891
+ Weight::from_wu(FUNDING_TRANSACTION_WITNESS_WEIGHT),
5892
+ contribution.outputs(),
5893
+ funding_feerate.to_sat_per_kwu() as u32,
5894
+ ));
5895
+
5896
+ if channel_balance > contribution_amount.unsigned_abs() + estimated_fee {
5897
+ Ok(estimated_fee)
5898
+ } else {
5899
+ Err(ChannelError::Warn(format!(
5900
+ "Available channel balance {} is lower than needed for splicing out {}, considering fees of {}",
5901
+ channel_balance, contribution_amount.unsigned_abs(), estimated_fee,
5902
+ )))
5903
+ }
5904
+ } else {
5905
+ check_v2_funding_inputs_sufficient(
5906
+ contribution_amount.to_sat(),
5907
+ contribution.inputs(),
5908
+ is_initiator,
5909
+ true,
5910
+ funding_feerate.to_sat_per_kwu() as u32,
5911
+ )
5912
+ .map(Amount::from_sat)
5913
+ }
5914
+ }
5915
+
5881
5916
/// Estimate our part of the fee of the new funding transaction.
5882
5917
/// input_count: Number of contributed inputs.
5883
5918
/// witness_weight: The witness weight for contributed inputs.
5884
5919
#[allow(dead_code)] // TODO(dual_funding): TODO(splicing): Remove allow once used.
5885
5920
#[rustfmt::skip]
5886
5921
fn estimate_v2_funding_transaction_fee(
5887
- is_initiator: bool, input_count: usize, witness_weight: Weight,
5922
+ is_initiator: bool, input_count: usize, witness_weight: Weight, outputs: &[TxOut],
5888
5923
funding_feerate_sat_per_1000_weight: u32,
5889
5924
) -> u64 {
5890
- // Inputs
5891
5925
let mut weight = (input_count as u64) * BASE_INPUT_WEIGHT;
5892
-
5893
- // Witnesses
5894
5926
weight = weight.saturating_add(witness_weight.to_wu());
5927
+ weight = weight.saturating_add(outputs.iter().map(|txout| txout.weight().to_wu()).sum());
5895
5928
5896
5929
// If we are the initiator, we must pay for weight of all common fields in the funding transaction.
5897
5930
if is_initiator {
@@ -5928,7 +5961,7 @@ fn check_v2_funding_inputs_sufficient(
5928
5961
funding_inputs_len += 1;
5929
5962
total_input_witness_weight += Weight::from_wu(FUNDING_TRANSACTION_WITNESS_WEIGHT);
5930
5963
}
5931
- let estimated_fee = estimate_v2_funding_transaction_fee(is_initiator, funding_inputs_len, total_input_witness_weight, funding_feerate_sat_per_1000_weight);
5964
+ let estimated_fee = estimate_v2_funding_transaction_fee(is_initiator, funding_inputs_len, total_input_witness_weight, &[], funding_feerate_sat_per_1000_weight);
5932
5965
5933
5966
let mut total_input_sats = 0u64;
5934
5967
for (idx, FundingTxInput { txin, prevtx, .. }) in funding_inputs.iter().enumerate() {
@@ -5982,6 +6015,9 @@ pub(super) struct FundingNegotiationContext {
5982
6015
/// The funding inputs we will be contributing to the channel.
5983
6016
#[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
5984
6017
pub our_funding_inputs: Vec<FundingTxInput>,
6018
+ /// The funding outputs we will be contributing to the channel.
6019
+ #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
6020
+ pub our_funding_outputs: Vec<TxOut>,
5985
6021
/// The change output script. This will be used if needed or -- if not set -- generated using
5986
6022
/// `SignerProvider::get_destination_script`.
5987
6023
#[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
@@ -6011,45 +6047,47 @@ impl FundingNegotiationContext {
6011
6047
debug_assert!(matches!(context.channel_state, ChannelState::NegotiatingFunding(_)));
6012
6048
}
6013
6049
6014
- // Add output for funding tx
6015
6050
// Note: For the error case when the inputs are insufficient, it will be handled after
6016
6051
// the `calculate_change_output_value` call below
6017
- let mut funding_outputs = Vec::new();
6018
6052
6019
6053
let shared_funding_output = TxOut {
6020
6054
value: Amount::from_sat(funding.get_value_satoshis()),
6021
6055
script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
6022
6056
};
6023
6057
6024
6058
// Optionally add change output
6025
- if self.our_funding_contribution > SignedAmount::ZERO {
6026
- let change_value_opt = calculate_change_output_value(
6059
+ let change_value_opt = if self.our_funding_contribution > SignedAmount::ZERO {
6060
+ calculate_change_output_value(
6027
6061
&self,
6028
6062
self.shared_funding_input.is_some(),
6029
6063
&shared_funding_output.script_pubkey,
6030
- &funding_outputs,
6031
6064
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
- }
6065
+ )?
6066
+ } else {
6067
+ None
6068
+ };
6069
+
6070
+ let mut funding_outputs = self.our_funding_outputs;
6071
+
6072
+ if let Some(change_value) = change_value_opt {
6073
+ let change_script = if let Some(script) = self.change_script {
6074
+ script
6075
+ } else {
6076
+ signer_provider
6077
+ .get_destination_script(context.channel_keys_id)
6078
+ .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6079
+ };
6080
+ let mut change_output =
6081
+ TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6082
+ let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6083
+ let change_output_fee =
6084
+ fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6085
+ let change_value_decreased_with_fee =
6086
+ change_value.saturating_sub(change_output_fee);
6087
+ // Check dust limit again
6088
+ if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6089
+ change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6090
+ funding_outputs.push(change_output);
6053
6091
}
6054
6092
}
6055
6093
@@ -10638,44 +10676,77 @@ where
10638
10676
if our_funding_contribution > SignedAmount::MAX_MONEY {
10639
10677
return Err(APIError::APIMisuseError {
10640
10678
err: format!(
10641
- "Channel {} cannot be spliced; contribution exceeds total bitcoin supply: {}",
10679
+ "Channel {} cannot be spliced in ; contribution exceeds total bitcoin supply: {}",
10642
10680
self.context.channel_id(),
10643
10681
our_funding_contribution,
10644
10682
),
10645
10683
});
10646
10684
}
10647
10685
10648
- if our_funding_contribution < SignedAmount::ZERO {
10686
+ if our_funding_contribution < - SignedAmount::MAX_MONEY {
10649
10687
return Err(APIError::APIMisuseError {
10650
10688
err: format!(
10651
- "TODO(splicing): Splice-out not supported, only splice in; channel ID {}, contribution {}",
10652
- self.context.channel_id(), our_funding_contribution,
10653
- ),
10689
+ "Channel {} cannot be spliced out; contribution exceeds total bitcoin supply: {}",
10690
+ self.context.channel_id(),
10691
+ our_funding_contribution,
10692
+ ),
10654
10693
});
10655
10694
}
10656
10695
10657
- // TODO(splicing): Once splice-out is supported, check that channel balance does not go below 0
10658
- // (or below channel reserve)
10696
+ let funding_inputs = contribution.inputs();
10697
+ let funding_outputs = contribution.outputs();
10698
+ if !funding_inputs.is_empty() && !funding_outputs.is_empty() {
10699
+ return Err(APIError::APIMisuseError {
10700
+ err: format!(
10701
+ "Channel {} cannot be both spliced in and out; operation not supported",
10702
+ self.context.channel_id(),
10703
+ ),
10704
+ });
10705
+ }
10659
10706
10660
10707
// Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
10661
10708
// (Cannot test for miminum required post-splice channel value)
10662
10709
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,
10710
+ let channel_balance = Amount::from_sat(self.funding.get_value_to_self_msat() / 1000);
10711
+ let fees = check_splice_contribution_sufficient(
10712
+ channel_balance,
10713
+ &contribution,
10714
+ true, // is_initiator
10715
+ FeeRate::from_sat_per_kwu(funding_feerate_per_kw as u64),
10670
10716
)
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
- ),
10717
+ .map_err(|e| {
10718
+ let splice_type = if our_funding_contribution < SignedAmount::ZERO {
10719
+ "spliced out"
10720
+ } else {
10721
+ "spliced in"
10722
+ };
10723
+ APIError::APIMisuseError {
10724
+ err: format!(
10725
+ "Channel {} cannot be {}; {}",
10726
+ self.context.channel_id(),
10727
+ splice_type,
10728
+ e,
10729
+ ),
10730
+ }
10677
10731
})?;
10678
10732
10733
+ // Fees for splice-out are paid from the channel balance whereas fees for splice-in are paid
10734
+ // by the funding inputs.
10735
+ let adjusted_funding_contribution = if our_funding_contribution < SignedAmount::ZERO {
10736
+ let adjusted_funding_contribution = our_funding_contribution
10737
+ - fees.to_signed().expect("fees should never exceed splice-out value");
10738
+
10739
+ // TODO(splicing): Check that channel balance does not go below the channel reserve
10740
+ let _post_channel_balance = AddSigned::checked_add_signed(
10741
+ channel_balance.to_sat(),
10742
+ adjusted_funding_contribution.to_sat(),
10743
+ );
10744
+
10745
+ adjusted_funding_contribution
10746
+ } else {
10747
+ our_funding_contribution
10748
+ };
10749
+
10679
10750
for FundingTxInput { txin, prevtx, .. } in contribution.inputs().iter() {
10680
10751
const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
10681
10752
channel_id: ChannelId([0; 32]),
@@ -10697,14 +10768,15 @@ where
10697
10768
}
10698
10769
10699
10770
let prev_funding_input = self.funding.to_splice_funding_input();
10700
- let (our_funding_inputs, change_script) = contribution.into_tx_parts();
10771
+ let (our_funding_inputs, our_funding_outputs, change_script) = contribution.into_tx_parts();
10701
10772
let funding_negotiation_context = FundingNegotiationContext {
10702
10773
is_initiator: true,
10703
- our_funding_contribution,
10774
+ our_funding_contribution: adjusted_funding_contribution ,
10704
10775
funding_tx_locktime: LockTime::from_consensus(locktime),
10705
10776
funding_feerate_sat_per_1000_weight: funding_feerate_per_kw,
10706
10777
shared_funding_input: Some(prev_funding_input),
10707
10778
our_funding_inputs,
10779
+ our_funding_outputs,
10708
10780
change_script,
10709
10781
};
10710
10782
@@ -10720,7 +10792,7 @@ where
10720
10792
10721
10793
Ok(msgs::SpliceInit {
10722
10794
channel_id: self.context.channel_id,
10723
- funding_contribution_satoshis: our_funding_contribution .to_sat(),
10795
+ funding_contribution_satoshis: adjusted_funding_contribution .to_sat(),
10724
10796
funding_feerate_per_kw,
10725
10797
locktime,
10726
10798
funding_pubkey,
@@ -10827,6 +10899,7 @@ where
10827
10899
funding_feerate_sat_per_1000_weight: msg.funding_feerate_per_kw,
10828
10900
shared_funding_input: Some(prev_funding_input),
10829
10901
our_funding_inputs: Vec::new(),
10902
+ our_funding_outputs: Vec::new(),
10830
10903
change_script: None,
10831
10904
};
10832
10905
@@ -12525,6 +12598,7 @@ where
12525
12598
funding_feerate_sat_per_1000_weight,
12526
12599
shared_funding_input: None,
12527
12600
our_funding_inputs: funding_inputs,
12601
+ our_funding_outputs: Vec::new(),
12528
12602
change_script: None,
12529
12603
};
12530
12604
let chan = Self {
@@ -12679,6 +12753,7 @@ where
12679
12753
funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
12680
12754
shared_funding_input: None,
12681
12755
our_funding_inputs: our_funding_inputs.clone(),
12756
+ our_funding_outputs: Vec::new(),
12682
12757
change_script: None,
12683
12758
};
12684
12759
let shared_funding_output = TxOut {
@@ -12702,7 +12777,7 @@ where
12702
12777
inputs_to_contribute,
12703
12778
shared_funding_input: None,
12704
12779
shared_funding_output: SharedOwnedOutput::new(shared_funding_output, our_funding_contribution_sats),
12705
- outputs_to_contribute: Vec::new (),
12780
+ outputs_to_contribute: funding_negotiation_context.our_funding_outputs.clone (),
12706
12781
}
12707
12782
).map_err(|err| {
12708
12783
let reason = ClosureReason::ProcessingError { err: err.to_string() };
@@ -15873,31 +15948,31 @@ mod tests {
15873
15948
15874
15949
// 2 inputs with weight 300, initiator, 2000 sat/kw feerate
15875
15950
assert_eq!(
15876
- estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), 2000),
15951
+ estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), &[], 2000),
15877
15952
1668
15878
15953
);
15879
15954
15880
15955
// higher feerate
15881
15956
assert_eq!(
15882
- estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), 3000),
15957
+ estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), &[], 3000),
15883
15958
2502
15884
15959
);
15885
15960
15886
15961
// only 1 input
15887
15962
assert_eq!(
15888
- estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(300), 2000),
15963
+ estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(300), &[], 2000),
15889
15964
1348
15890
15965
);
15891
15966
15892
15967
// 0 input weight
15893
15968
assert_eq!(
15894
- estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(0), 2000),
15969
+ estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(0), &[], 2000),
15895
15970
748
15896
15971
);
15897
15972
15898
15973
// not initiator
15899
15974
assert_eq!(
15900
- estimate_v2_funding_transaction_fee(false, 1, Weight::from_wu(0), 2000),
15975
+ estimate_v2_funding_transaction_fee(false, 1, Weight::from_wu(0), &[], 2000),
15901
15976
320
15902
15977
);
15903
15978
}
0 commit comments