@@ -24,9 +24,9 @@ use bitcoin::hashes::Hash;
24
24
use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE;
25
25
use bitcoin::secp256k1::{ecdsa::Signature, Secp256k1};
26
26
use bitcoin::secp256k1::{PublicKey, SecretKey};
27
- #[cfg(splicing)]
28
- use bitcoin::Sequence;
29
27
use bitcoin::{secp256k1, sighash, TxIn};
28
+ #[cfg(splicing)]
29
+ use bitcoin::{FeeRate, Sequence};
30
30
31
31
use crate::chain::chaininterface::{
32
32
fee_for_weight, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator,
@@ -5879,20 +5879,62 @@ fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satos
5879
5879
cmp::min(channel_value_satoshis, cmp::max(q, dust_limit_satoshis))
5880
5880
}
5881
5881
5882
+ #[cfg(splicing)]
5883
+ fn check_splice_contribution_sufficient(
5884
+ channel_balance: Amount, contribution: &SpliceContribution, is_initiator: bool,
5885
+ funding_feerate: FeeRate,
5886
+ ) -> Result<Amount, ChannelError> {
5887
+ let contribution_amount = contribution.value();
5888
+ if contribution_amount < SignedAmount::ZERO {
5889
+ let estimated_fee = Amount::from_sat(estimate_v2_funding_transaction_fee(
5890
+ contribution.inputs(),
5891
+ contribution.outputs(),
5892
+ is_initiator,
5893
+ true, // is_splice
5894
+ funding_feerate.to_sat_per_kwu() as u32,
5895
+ ));
5896
+
5897
+ if channel_balance >= contribution_amount.unsigned_abs() + estimated_fee {
5898
+ Ok(estimated_fee)
5899
+ } else {
5900
+ Err(ChannelError::Warn(format!(
5901
+ "Available channel balance {} is lower than needed for splicing out {}, considering fees of {}",
5902
+ channel_balance, contribution_amount.unsigned_abs(), estimated_fee,
5903
+ )))
5904
+ }
5905
+ } else {
5906
+ check_v2_funding_inputs_sufficient(
5907
+ contribution_amount.to_sat(),
5908
+ contribution.inputs(),
5909
+ is_initiator,
5910
+ true,
5911
+ funding_feerate.to_sat_per_kwu() as u32,
5912
+ )
5913
+ .map(Amount::from_sat)
5914
+ }
5915
+ }
5916
+
5882
5917
/// Estimate our part of the fee of the new funding transaction.
5883
5918
/// input_count: Number of contributed inputs.
5884
5919
/// witness_weight: The witness weight for contributed inputs.
5885
5920
#[allow(dead_code)] // TODO(dual_funding): TODO(splicing): Remove allow once used.
5886
5921
#[rustfmt::skip]
5887
5922
fn estimate_v2_funding_transaction_fee(
5888
- funding_inputs: &[FundingTxInput], is_initiator: bool, is_splice: bool,
5923
+ funding_inputs: &[FundingTxInput], outputs: &[TxOut], is_initiator: bool, is_splice: bool,
5889
5924
funding_feerate_sat_per_1000_weight: u32,
5890
5925
) -> u64 {
5891
- let mut weight : u64 = funding_inputs
5926
+ let input_weight : u64 = funding_inputs
5892
5927
.iter()
5893
5928
.map(|input| BASE_INPUT_WEIGHT.saturating_add(input.utxo.satisfaction_weight))
5894
5929
.fold(0, |total_weight, input_weight| total_weight.saturating_add(input_weight));
5895
5930
5931
+ let output_weight: u64 = outputs
5932
+ .iter()
5933
+ .map(|txout| txout.weight().to_wu())
5934
+ .fold(0, |total_weight, output_weight| total_weight.saturating_add(output_weight));
5935
+
5936
+ let mut weight = input_weight.saturating_add(output_weight);
5937
+
5896
5938
// The initiator pays for all common fields and the shared output in the funding transaction.
5897
5939
if is_initiator {
5898
5940
weight = weight
@@ -5929,7 +5971,7 @@ fn check_v2_funding_inputs_sufficient(
5929
5971
is_splice: bool, funding_feerate_sat_per_1000_weight: u32,
5930
5972
) -> Result<u64, ChannelError> {
5931
5973
let estimated_fee = estimate_v2_funding_transaction_fee(
5932
- funding_inputs, is_initiator, is_splice, funding_feerate_sat_per_1000_weight,
5974
+ funding_inputs, &[], is_initiator, is_splice, funding_feerate_sat_per_1000_weight,
5933
5975
);
5934
5976
5935
5977
let mut total_input_sats = 0u64;
@@ -5977,6 +6019,9 @@ pub(super) struct FundingNegotiationContext {
5977
6019
/// The funding inputs we will be contributing to the channel.
5978
6020
#[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
5979
6021
pub our_funding_inputs: Vec<FundingTxInput>,
6022
+ /// The funding outputs we will be contributing to the channel.
6023
+ #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
6024
+ pub our_funding_outputs: Vec<TxOut>,
5980
6025
/// The change output script. This will be used if needed or -- if not set -- generated using
5981
6026
/// `SignerProvider::get_destination_script`.
5982
6027
#[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
@@ -6006,45 +6051,46 @@ impl FundingNegotiationContext {
6006
6051
debug_assert!(matches!(context.channel_state, ChannelState::NegotiatingFunding(_)));
6007
6052
}
6008
6053
6009
- // Add output for funding tx
6010
6054
// Note: For the error case when the inputs are insufficient, it will be handled after
6011
6055
// the `calculate_change_output_value` call below
6012
- let mut funding_outputs = Vec::new();
6013
6056
6014
6057
let shared_funding_output = TxOut {
6015
6058
value: Amount::from_sat(funding.get_value_satoshis()),
6016
6059
script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
6017
6060
};
6018
6061
6019
6062
// Optionally add change output
6020
- if self.our_funding_contribution > SignedAmount::ZERO {
6021
- let change_value_opt = calculate_change_output_value(
6063
+ let change_value_opt = if self.our_funding_contribution > SignedAmount::ZERO {
6064
+ calculate_change_output_value(
6022
6065
&self,
6023
6066
self.shared_funding_input.is_some(),
6024
6067
&shared_funding_output.script_pubkey,
6025
- &funding_outputs,
6026
6068
context.holder_dust_limit_satoshis,
6027
- )?;
6028
- if let Some(change_value) = change_value_opt {
6029
- let change_script = if let Some(script) = self.change_script {
6030
- script
6031
- } else {
6032
- signer_provider
6033
- .get_destination_script(context.channel_keys_id)
6034
- .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6035
- };
6036
- let mut change_output =
6037
- TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6038
- let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6039
- let change_output_fee =
6040
- fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6041
- let change_value_decreased_with_fee =
6042
- change_value.saturating_sub(change_output_fee);
6043
- // Check dust limit again
6044
- if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6045
- change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6046
- funding_outputs.push(change_output);
6047
- }
6069
+ )?
6070
+ } else {
6071
+ None
6072
+ };
6073
+
6074
+ let mut funding_outputs = self.our_funding_outputs;
6075
+
6076
+ if let Some(change_value) = change_value_opt {
6077
+ let change_script = if let Some(script) = self.change_script {
6078
+ script
6079
+ } else {
6080
+ signer_provider
6081
+ .get_destination_script(context.channel_keys_id)
6082
+ .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6083
+ };
6084
+ let mut change_output =
6085
+ TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6086
+ let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6087
+ let change_output_fee =
6088
+ fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6089
+ let change_value_decreased_with_fee = change_value.saturating_sub(change_output_fee);
6090
+ // Check dust limit again
6091
+ if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6092
+ change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6093
+ funding_outputs.push(change_output);
6048
6094
}
6049
6095
}
6050
6096
@@ -10635,44 +10681,66 @@ where
10635
10681
if our_funding_contribution > SignedAmount::MAX_MONEY {
10636
10682
return Err(APIError::APIMisuseError {
10637
10683
err: format!(
10638
- "Channel {} cannot be spliced; contribution exceeds total bitcoin supply: {}",
10684
+ "Channel {} cannot be spliced in ; contribution exceeds total bitcoin supply: {}",
10639
10685
self.context.channel_id(),
10640
10686
our_funding_contribution,
10641
10687
),
10642
10688
});
10643
10689
}
10644
10690
10645
- if our_funding_contribution < SignedAmount::ZERO {
10691
+ if our_funding_contribution < - SignedAmount::MAX_MONEY {
10646
10692
return Err(APIError::APIMisuseError {
10647
10693
err: format!(
10648
- "TODO(splicing): Splice-out not supported, only splice in; channel ID {}, contribution {}",
10649
- self.context.channel_id(), our_funding_contribution,
10650
- ),
10694
+ "Channel {} cannot be spliced out; contribution exhausts total bitcoin supply: {}",
10695
+ self.context.channel_id(),
10696
+ our_funding_contribution,
10697
+ ),
10651
10698
});
10652
10699
}
10653
10700
10654
- // TODO(splicing): Once splice-out is supported, check that channel balance does not go below 0
10655
- // (or below channel reserve)
10656
-
10657
10701
// Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
10658
10702
// (Cannot test for miminum required post-splice channel value)
10659
10703
10660
- // Check that inputs are sufficient to cover our contribution.
10661
- let _fee = check_v2_funding_inputs_sufficient(
10662
- our_funding_contribution.to_sat(),
10663
- contribution.inputs(),
10664
- true,
10665
- true,
10666
- funding_feerate_per_kw,
10704
+ let channel_balance = Amount::from_sat(self.funding.get_value_to_self_msat() / 1000);
10705
+ let fees = check_splice_contribution_sufficient(
10706
+ channel_balance,
10707
+ &contribution,
10708
+ true, // is_initiator
10709
+ FeeRate::from_sat_per_kwu(funding_feerate_per_kw as u64),
10667
10710
)
10668
- .map_err(|err| APIError::APIMisuseError {
10669
- err: format!(
10670
- "Insufficient inputs for splicing; channel ID {}, err {}",
10671
- self.context.channel_id(),
10672
- err,
10673
- ),
10711
+ .map_err(|e| {
10712
+ let splice_type = if our_funding_contribution < SignedAmount::ZERO {
10713
+ "spliced out"
10714
+ } else {
10715
+ "spliced in"
10716
+ };
10717
+ APIError::APIMisuseError {
10718
+ err: format!(
10719
+ "Channel {} cannot be {}; {}",
10720
+ self.context.channel_id(),
10721
+ splice_type,
10722
+ e,
10723
+ ),
10724
+ }
10674
10725
})?;
10675
10726
10727
+ // Fees for splice-out are paid from the channel balance whereas fees for splice-in are paid
10728
+ // by the funding inputs.
10729
+ let adjusted_funding_contribution = if our_funding_contribution < SignedAmount::ZERO {
10730
+ let adjusted_funding_contribution = our_funding_contribution
10731
+ - fees.to_signed().expect("fees should never exceed Amount::MAX_MONEY");
10732
+
10733
+ // TODO(splicing): Check that channel balance does not go below the channel reserve
10734
+ let _post_channel_balance = AddSigned::checked_add_signed(
10735
+ channel_balance.to_sat(),
10736
+ adjusted_funding_contribution.to_sat(),
10737
+ );
10738
+
10739
+ adjusted_funding_contribution
10740
+ } else {
10741
+ our_funding_contribution
10742
+ };
10743
+
10676
10744
for FundingTxInput { utxo, prevtx, .. } in contribution.inputs().iter() {
10677
10745
const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
10678
10746
channel_id: ChannelId([0; 32]),
@@ -10695,14 +10763,15 @@ where
10695
10763
}
10696
10764
10697
10765
let prev_funding_input = self.funding.to_splice_funding_input();
10698
- let (our_funding_inputs, change_script) = contribution.into_tx_parts();
10766
+ let (our_funding_inputs, our_funding_outputs, change_script) = contribution.into_tx_parts();
10699
10767
let funding_negotiation_context = FundingNegotiationContext {
10700
10768
is_initiator: true,
10701
- our_funding_contribution,
10769
+ our_funding_contribution: adjusted_funding_contribution ,
10702
10770
funding_tx_locktime: LockTime::from_consensus(locktime),
10703
10771
funding_feerate_sat_per_1000_weight: funding_feerate_per_kw,
10704
10772
shared_funding_input: Some(prev_funding_input),
10705
10773
our_funding_inputs,
10774
+ our_funding_outputs,
10706
10775
change_script,
10707
10776
};
10708
10777
@@ -10718,7 +10787,7 @@ where
10718
10787
10719
10788
Ok(msgs::SpliceInit {
10720
10789
channel_id: self.context.channel_id,
10721
- funding_contribution_satoshis: our_funding_contribution .to_sat(),
10790
+ funding_contribution_satoshis: adjusted_funding_contribution .to_sat(),
10722
10791
funding_feerate_per_kw,
10723
10792
locktime,
10724
10793
funding_pubkey,
@@ -10827,6 +10896,7 @@ where
10827
10896
funding_feerate_sat_per_1000_weight: msg.funding_feerate_per_kw,
10828
10897
shared_funding_input: Some(prev_funding_input),
10829
10898
our_funding_inputs: Vec::new(),
10899
+ our_funding_outputs: Vec::new(),
10830
10900
change_script: None,
10831
10901
};
10832
10902
@@ -12525,6 +12595,7 @@ where
12525
12595
funding_feerate_sat_per_1000_weight,
12526
12596
shared_funding_input: None,
12527
12597
our_funding_inputs: funding_inputs,
12598
+ our_funding_outputs: Vec::new(),
12528
12599
change_script: None,
12529
12600
};
12530
12601
let chan = Self {
@@ -12679,6 +12750,7 @@ where
12679
12750
funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
12680
12751
shared_funding_input: None,
12681
12752
our_funding_inputs: our_funding_inputs.clone(),
12753
+ our_funding_outputs: Vec::new(),
12682
12754
change_script: None,
12683
12755
};
12684
12756
let shared_funding_output = TxOut {
@@ -12704,7 +12776,7 @@ where
12704
12776
inputs_to_contribute,
12705
12777
shared_funding_input: None,
12706
12778
shared_funding_output: SharedOwnedOutput::new(shared_funding_output, our_funding_contribution_sats),
12707
- outputs_to_contribute: Vec::new (),
12779
+ outputs_to_contribute: funding_negotiation_context.our_funding_outputs.clone (),
12708
12780
}
12709
12781
).map_err(|err| {
12710
12782
let reason = ClosureReason::ProcessingError { err: err.to_string() };
@@ -15875,43 +15947,43 @@ mod tests {
15875
15947
15876
15948
// 2 inputs, initiator, 2000 sat/kw feerate
15877
15949
assert_eq!(
15878
- estimate_v2_funding_transaction_fee(&two_inputs, true, false, 2000),
15950
+ estimate_v2_funding_transaction_fee(&two_inputs, &[], true, false, 2000),
15879
15951
1520,
15880
15952
);
15881
15953
15882
15954
// higher feerate
15883
15955
assert_eq!(
15884
- estimate_v2_funding_transaction_fee(&two_inputs, true, false, 3000),
15956
+ estimate_v2_funding_transaction_fee(&two_inputs, &[], true, false, 3000),
15885
15957
2280,
15886
15958
);
15887
15959
15888
15960
// only 1 input
15889
15961
assert_eq!(
15890
- estimate_v2_funding_transaction_fee(&one_input, true, false, 2000),
15962
+ estimate_v2_funding_transaction_fee(&one_input, &[], true, false, 2000),
15891
15963
974,
15892
15964
);
15893
15965
15894
15966
// 0 inputs
15895
15967
assert_eq!(
15896
- estimate_v2_funding_transaction_fee(&[], true, false, 2000),
15968
+ estimate_v2_funding_transaction_fee(&[], &[], true, false, 2000),
15897
15969
428,
15898
15970
);
15899
15971
15900
15972
// not initiator
15901
15973
assert_eq!(
15902
- estimate_v2_funding_transaction_fee(&[], false, false, 2000),
15974
+ estimate_v2_funding_transaction_fee(&[], &[], false, false, 2000),
15903
15975
0,
15904
15976
);
15905
15977
15906
15978
// splice initiator
15907
15979
assert_eq!(
15908
- estimate_v2_funding_transaction_fee(&one_input, true, true, 2000),
15980
+ estimate_v2_funding_transaction_fee(&one_input, &[], true, true, 2000),
15909
15981
1746,
15910
15982
);
15911
15983
15912
15984
// splice acceptor
15913
15985
assert_eq!(
15914
- estimate_v2_funding_transaction_fee(&one_input, false, true, 2000),
15986
+ estimate_v2_funding_transaction_fee(&one_input, &[], false, true, 2000),
15915
15987
546,
15916
15988
);
15917
15989
}
0 commit comments