@@ -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,53 @@ 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
+ is_initiator,
5891
+ 1, // spends the previous funding output
5892
+ Weight::from_wu(FUNDING_TRANSACTION_WITNESS_WEIGHT),
5893
+ contribution.outputs(),
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
- is_initiator: bool, input_count: usize, witness_weight: Weight,
5923
+ is_initiator: bool, input_count: usize, witness_weight: Weight, outputs: &[TxOut],
5889
5924
funding_feerate_sat_per_1000_weight: u32,
5890
5925
) -> u64 {
5891
- // Inputs
5892
5926
let mut weight = (input_count as u64) * BASE_INPUT_WEIGHT;
5893
-
5894
- // Witnesses
5895
5927
weight = weight.saturating_add(witness_weight.to_wu());
5928
+ weight = weight.saturating_add(outputs.iter().map(|txout| txout.weight().to_wu()).sum());
5896
5929
5897
5930
// If we are the initiator, we must pay for weight of all common fields in the funding transaction.
5898
5931
if is_initiator {
@@ -5930,7 +5963,7 @@ fn check_v2_funding_inputs_sufficient(
5930
5963
funding_inputs_len += 1;
5931
5964
total_input_satisfaction_weight += Weight::from_wu(FUNDING_TRANSACTION_WITNESS_WEIGHT);
5932
5965
}
5933
- let estimated_fee = estimate_v2_funding_transaction_fee(is_initiator, funding_inputs_len, total_input_satisfaction_weight, funding_feerate_sat_per_1000_weight);
5966
+ let estimated_fee = estimate_v2_funding_transaction_fee(is_initiator, funding_inputs_len, total_input_satisfaction_weight, &[], funding_feerate_sat_per_1000_weight);
5934
5967
5935
5968
let mut total_input_sats = 0u64;
5936
5969
for FundingTxInput { utxo, .. } in funding_inputs.iter() {
@@ -5977,6 +6010,9 @@ pub(super) struct FundingNegotiationContext {
5977
6010
/// The funding inputs we will be contributing to the channel.
5978
6011
#[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
5979
6012
pub our_funding_inputs: Vec<FundingTxInput>,
6013
+ /// The funding outputs we will be contributing to the channel.
6014
+ #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
6015
+ pub our_funding_outputs: Vec<TxOut>,
5980
6016
/// The change output script. This will be used if needed or -- if not set -- generated using
5981
6017
/// `SignerProvider::get_destination_script`.
5982
6018
#[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
@@ -6006,45 +6042,46 @@ impl FundingNegotiationContext {
6006
6042
debug_assert!(matches!(context.channel_state, ChannelState::NegotiatingFunding(_)));
6007
6043
}
6008
6044
6009
- // Add output for funding tx
6010
6045
// Note: For the error case when the inputs are insufficient, it will be handled after
6011
6046
// the `calculate_change_output_value` call below
6012
- let mut funding_outputs = Vec::new();
6013
6047
6014
6048
let shared_funding_output = TxOut {
6015
6049
value: Amount::from_sat(funding.get_value_satoshis()),
6016
6050
script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
6017
6051
};
6018
6052
6019
6053
// Optionally add change output
6020
- if self.our_funding_contribution > SignedAmount::ZERO {
6021
- let change_value_opt = calculate_change_output_value(
6054
+ let change_value_opt = if self.our_funding_contribution > SignedAmount::ZERO {
6055
+ calculate_change_output_value(
6022
6056
&self,
6023
6057
self.shared_funding_input.is_some(),
6024
6058
&shared_funding_output.script_pubkey,
6025
- &funding_outputs,
6026
6059
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
- }
6060
+ )?
6061
+ } else {
6062
+ None
6063
+ };
6064
+
6065
+ let mut funding_outputs = self.our_funding_outputs;
6066
+
6067
+ if let Some(change_value) = change_value_opt {
6068
+ let change_script = if let Some(script) = self.change_script {
6069
+ script
6070
+ } else {
6071
+ signer_provider
6072
+ .get_destination_script(context.channel_keys_id)
6073
+ .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6074
+ };
6075
+ let mut change_output =
6076
+ TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6077
+ let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6078
+ let change_output_fee =
6079
+ fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6080
+ let change_value_decreased_with_fee = change_value.saturating_sub(change_output_fee);
6081
+ // Check dust limit again
6082
+ if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6083
+ change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6084
+ funding_outputs.push(change_output);
6048
6085
}
6049
6086
}
6050
6087
@@ -10635,44 +10672,66 @@ where
10635
10672
if our_funding_contribution > SignedAmount::MAX_MONEY {
10636
10673
return Err(APIError::APIMisuseError {
10637
10674
err: format!(
10638
- "Channel {} cannot be spliced; contribution exceeds total bitcoin supply: {}",
10675
+ "Channel {} cannot be spliced in ; contribution exceeds total bitcoin supply: {}",
10639
10676
self.context.channel_id(),
10640
10677
our_funding_contribution,
10641
10678
),
10642
10679
});
10643
10680
}
10644
10681
10645
- if our_funding_contribution < SignedAmount::ZERO {
10682
+ if our_funding_contribution < - SignedAmount::MAX_MONEY {
10646
10683
return Err(APIError::APIMisuseError {
10647
10684
err: format!(
10648
- "TODO(splicing): Splice-out not supported, only splice in; channel ID {}, contribution {}",
10649
- self.context.channel_id(), our_funding_contribution,
10650
- ),
10685
+ "Channel {} cannot be spliced out; contribution exhausts total bitcoin supply: {}",
10686
+ self.context.channel_id(),
10687
+ our_funding_contribution,
10688
+ ),
10651
10689
});
10652
10690
}
10653
10691
10654
- // TODO(splicing): Once splice-out is supported, check that channel balance does not go below 0
10655
- // (or below channel reserve)
10656
-
10657
10692
// Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
10658
10693
// (Cannot test for miminum required post-splice channel value)
10659
10694
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,
10695
+ let channel_balance = Amount::from_sat(self.funding.get_value_to_self_msat() / 1000);
10696
+ let fees = check_splice_contribution_sufficient(
10697
+ channel_balance,
10698
+ &contribution,
10699
+ true, // is_initiator
10700
+ FeeRate::from_sat_per_kwu(funding_feerate_per_kw as u64),
10667
10701
)
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
- ),
10702
+ .map_err(|e| {
10703
+ let splice_type = if our_funding_contribution < SignedAmount::ZERO {
10704
+ "spliced out"
10705
+ } else {
10706
+ "spliced in"
10707
+ };
10708
+ APIError::APIMisuseError {
10709
+ err: format!(
10710
+ "Channel {} cannot be {}; {}",
10711
+ self.context.channel_id(),
10712
+ splice_type,
10713
+ e,
10714
+ ),
10715
+ }
10674
10716
})?;
10675
10717
10718
+ // Fees for splice-out are paid from the channel balance whereas fees for splice-in are paid
10719
+ // by the funding inputs.
10720
+ let adjusted_funding_contribution = if our_funding_contribution < SignedAmount::ZERO {
10721
+ let adjusted_funding_contribution = our_funding_contribution
10722
+ - fees.to_signed().expect("fees should never exceed splice-out value");
10723
+
10724
+ // TODO(splicing): Check that channel balance does not go below the channel reserve
10725
+ let _post_channel_balance = AddSigned::checked_add_signed(
10726
+ channel_balance.to_sat(),
10727
+ adjusted_funding_contribution.to_sat(),
10728
+ );
10729
+
10730
+ adjusted_funding_contribution
10731
+ } else {
10732
+ our_funding_contribution
10733
+ };
10734
+
10676
10735
for FundingTxInput { utxo, prevtx, .. } in contribution.inputs().iter() {
10677
10736
const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
10678
10737
channel_id: ChannelId([0; 32]),
@@ -10694,14 +10753,15 @@ where
10694
10753
}
10695
10754
10696
10755
let prev_funding_input = self.funding.to_splice_funding_input();
10697
- let (our_funding_inputs, change_script) = contribution.into_tx_parts();
10756
+ let (our_funding_inputs, our_funding_outputs, change_script) = contribution.into_tx_parts();
10698
10757
let funding_negotiation_context = FundingNegotiationContext {
10699
10758
is_initiator: true,
10700
- our_funding_contribution,
10759
+ our_funding_contribution: adjusted_funding_contribution ,
10701
10760
funding_tx_locktime: LockTime::from_consensus(locktime),
10702
10761
funding_feerate_sat_per_1000_weight: funding_feerate_per_kw,
10703
10762
shared_funding_input: Some(prev_funding_input),
10704
10763
our_funding_inputs,
10764
+ our_funding_outputs,
10705
10765
change_script,
10706
10766
};
10707
10767
@@ -10717,7 +10777,7 @@ where
10717
10777
10718
10778
Ok(msgs::SpliceInit {
10719
10779
channel_id: self.context.channel_id,
10720
- funding_contribution_satoshis: our_funding_contribution .to_sat(),
10780
+ funding_contribution_satoshis: adjusted_funding_contribution .to_sat(),
10721
10781
funding_feerate_per_kw,
10722
10782
locktime,
10723
10783
funding_pubkey,
@@ -10826,6 +10886,7 @@ where
10826
10886
funding_feerate_sat_per_1000_weight: msg.funding_feerate_per_kw,
10827
10887
shared_funding_input: Some(prev_funding_input),
10828
10888
our_funding_inputs: Vec::new(),
10889
+ our_funding_outputs: Vec::new(),
10829
10890
change_script: None,
10830
10891
};
10831
10892
@@ -12524,6 +12585,7 @@ where
12524
12585
funding_feerate_sat_per_1000_weight,
12525
12586
shared_funding_input: None,
12526
12587
our_funding_inputs: funding_inputs,
12588
+ our_funding_outputs: Vec::new(),
12527
12589
change_script: None,
12528
12590
};
12529
12591
let chan = Self {
@@ -12678,6 +12740,7 @@ where
12678
12740
funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
12679
12741
shared_funding_input: None,
12680
12742
our_funding_inputs: our_funding_inputs.clone(),
12743
+ our_funding_outputs: Vec::new(),
12681
12744
change_script: None,
12682
12745
};
12683
12746
let shared_funding_output = TxOut {
@@ -12703,7 +12766,7 @@ where
12703
12766
inputs_to_contribute,
12704
12767
shared_funding_input: None,
12705
12768
shared_funding_output: SharedOwnedOutput::new(shared_funding_output, our_funding_contribution_sats),
12706
- outputs_to_contribute: Vec::new (),
12769
+ outputs_to_contribute: funding_negotiation_context.our_funding_outputs.clone (),
12707
12770
}
12708
12771
).map_err(|err| {
12709
12772
let reason = ClosureReason::ProcessingError { err: err.to_string() };
@@ -15872,31 +15935,31 @@ mod tests {
15872
15935
15873
15936
// 2 inputs with weight 300, initiator, 2000 sat/kw feerate
15874
15937
assert_eq!(
15875
- estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), 2000),
15938
+ estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), &[], 2000),
15876
15939
1668
15877
15940
);
15878
15941
15879
15942
// higher feerate
15880
15943
assert_eq!(
15881
- estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), 3000),
15944
+ estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), &[], 3000),
15882
15945
2502
15883
15946
);
15884
15947
15885
15948
// only 1 input
15886
15949
assert_eq!(
15887
- estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(300), 2000),
15950
+ estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(300), &[], 2000),
15888
15951
1348
15889
15952
);
15890
15953
15891
15954
// 0 input weight
15892
15955
assert_eq!(
15893
- estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(0), 2000),
15956
+ estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(0), &[], 2000),
15894
15957
748
15895
15958
);
15896
15959
15897
15960
// not initiator
15898
15961
assert_eq!(
15899
- estimate_v2_funding_transaction_fee(false, 1, Weight::from_wu(0), 2000),
15962
+ estimate_v2_funding_transaction_fee(false, 1, Weight::from_wu(0), &[], 2000),
15900
15963
320
15901
15964
);
15902
15965
}
0 commit comments