@@ -24,9 +24,9 @@ use bitcoin::hashes::Hash;
2424use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE;
2525use bitcoin::secp256k1::{ecdsa::Signature, Secp256k1};
2626use bitcoin::secp256k1::{PublicKey, SecretKey};
27- #[cfg(splicing)]
28- use bitcoin::Sequence;
2927use bitcoin::{secp256k1, sighash, TxIn};
28+ #[cfg(splicing)]
29+ use bitcoin::{FeeRate, Sequence};
3030
3131use crate::chain::chaininterface::{
3232 fee_for_weight, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator,
@@ -5879,20 +5879,62 @@ fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satos
58795879 cmp::min(channel_value_satoshis, cmp::max(q, dust_limit_satoshis))
58805880}
58815881
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+
58825917/// Estimate our part of the fee of the new funding transaction.
58835918/// input_count: Number of contributed inputs.
58845919/// witness_weight: The witness weight for contributed inputs.
58855920#[allow(dead_code)] // TODO(dual_funding): TODO(splicing): Remove allow once used.
58865921#[rustfmt::skip]
58875922fn 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,
58895924 funding_feerate_sat_per_1000_weight: u32,
58905925) -> u64 {
5891- let mut weight : u64 = funding_inputs
5926+ let input_weight : u64 = funding_inputs
58925927 .iter()
58935928 .map(|input| BASE_INPUT_WEIGHT.saturating_add(input.utxo.satisfaction_weight))
58945929 .fold(0, |total_weight, input_weight| total_weight.saturating_add(input_weight));
58955930
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+
58965938 // The initiator pays for all common fields and the shared output in the funding transaction.
58975939 if is_initiator {
58985940 weight = weight
@@ -5929,7 +5971,7 @@ fn check_v2_funding_inputs_sufficient(
59295971 is_splice: bool, funding_feerate_sat_per_1000_weight: u32,
59305972) -> Result<u64, ChannelError> {
59315973 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,
59335975 );
59345976
59355977 let mut total_input_sats = 0u64;
@@ -5977,6 +6019,9 @@ pub(super) struct FundingNegotiationContext {
59776019 /// The funding inputs we will be contributing to the channel.
59786020 #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
59796021 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>,
59806025 /// The change output script. This will be used if needed or -- if not set -- generated using
59816026 /// `SignerProvider::get_destination_script`.
59826027 #[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
@@ -6006,45 +6051,46 @@ impl FundingNegotiationContext {
60066051 debug_assert!(matches!(context.channel_state, ChannelState::NegotiatingFunding(_)));
60076052 }
60086053
6009- // Add output for funding tx
60106054 // Note: For the error case when the inputs are insufficient, it will be handled after
60116055 // the `calculate_change_output_value` call below
6012- let mut funding_outputs = Vec::new();
60136056
60146057 let shared_funding_output = TxOut {
60156058 value: Amount::from_sat(funding.get_value_satoshis()),
60166059 script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
60176060 };
60186061
60196062 // 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(
60226065 &self,
60236066 self.shared_funding_input.is_some(),
60246067 &shared_funding_output.script_pubkey,
6025- &funding_outputs,
60266068 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);
60486094 }
60496095 }
60506096
@@ -10635,44 +10681,66 @@ where
1063510681 if our_funding_contribution > SignedAmount::MAX_MONEY {
1063610682 return Err(APIError::APIMisuseError {
1063710683 err: format!(
10638- "Channel {} cannot be spliced; contribution exceeds total bitcoin supply: {}",
10684+ "Channel {} cannot be spliced in ; contribution exceeds total bitcoin supply: {}",
1063910685 self.context.channel_id(),
1064010686 our_funding_contribution,
1064110687 ),
1064210688 });
1064310689 }
1064410690
10645- if our_funding_contribution < SignedAmount::ZERO {
10691+ if our_funding_contribution < - SignedAmount::MAX_MONEY {
1064610692 return Err(APIError::APIMisuseError {
1064710693 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+ ),
1065110698 });
1065210699 }
1065310700
10654- // TODO(splicing): Once splice-out is supported, check that channel balance does not go below 0
10655- // (or below channel reserve)
10656-
1065710701 // Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
1065810702 // (Cannot test for miminum required post-splice channel value)
1065910703
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),
1066710710 )
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+ }
1067410725 })?;
1067510726
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+
1067610744 for FundingTxInput { utxo, prevtx, .. } in contribution.inputs().iter() {
1067710745 const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
1067810746 channel_id: ChannelId([0; 32]),
@@ -10695,14 +10763,15 @@ where
1069510763 }
1069610764
1069710765 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();
1069910767 let funding_negotiation_context = FundingNegotiationContext {
1070010768 is_initiator: true,
10701- our_funding_contribution,
10769+ our_funding_contribution: adjusted_funding_contribution ,
1070210770 funding_tx_locktime: LockTime::from_consensus(locktime),
1070310771 funding_feerate_sat_per_1000_weight: funding_feerate_per_kw,
1070410772 shared_funding_input: Some(prev_funding_input),
1070510773 our_funding_inputs,
10774+ our_funding_outputs,
1070610775 change_script,
1070710776 };
1070810777
@@ -10718,7 +10787,7 @@ where
1071810787
1071910788 Ok(msgs::SpliceInit {
1072010789 channel_id: self.context.channel_id,
10721- funding_contribution_satoshis: our_funding_contribution .to_sat(),
10790+ funding_contribution_satoshis: adjusted_funding_contribution .to_sat(),
1072210791 funding_feerate_per_kw,
1072310792 locktime,
1072410793 funding_pubkey,
@@ -10827,6 +10896,7 @@ where
1082710896 funding_feerate_sat_per_1000_weight: msg.funding_feerate_per_kw,
1082810897 shared_funding_input: Some(prev_funding_input),
1082910898 our_funding_inputs: Vec::new(),
10899+ our_funding_outputs: Vec::new(),
1083010900 change_script: None,
1083110901 };
1083210902
@@ -12525,6 +12595,7 @@ where
1252512595 funding_feerate_sat_per_1000_weight,
1252612596 shared_funding_input: None,
1252712597 our_funding_inputs: funding_inputs,
12598+ our_funding_outputs: Vec::new(),
1252812599 change_script: None,
1252912600 };
1253012601 let chan = Self {
@@ -12679,6 +12750,7 @@ where
1267912750 funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
1268012751 shared_funding_input: None,
1268112752 our_funding_inputs: our_funding_inputs.clone(),
12753+ our_funding_outputs: Vec::new(),
1268212754 change_script: None,
1268312755 };
1268412756 let shared_funding_output = TxOut {
@@ -12704,7 +12776,7 @@ where
1270412776 inputs_to_contribute,
1270512777 shared_funding_input: None,
1270612778 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 (),
1270812780 }
1270912781 ).map_err(|err| {
1271012782 let reason = ClosureReason::ProcessingError { err: err.to_string() };
@@ -15875,43 +15947,43 @@ mod tests {
1587515947
1587615948 // 2 inputs, initiator, 2000 sat/kw feerate
1587715949 assert_eq!(
15878- estimate_v2_funding_transaction_fee(&two_inputs, true, false, 2000),
15950+ estimate_v2_funding_transaction_fee(&two_inputs, &[], true, false, 2000),
1587915951 1520,
1588015952 );
1588115953
1588215954 // higher feerate
1588315955 assert_eq!(
15884- estimate_v2_funding_transaction_fee(&two_inputs, true, false, 3000),
15956+ estimate_v2_funding_transaction_fee(&two_inputs, &[], true, false, 3000),
1588515957 2280,
1588615958 );
1588715959
1588815960 // only 1 input
1588915961 assert_eq!(
15890- estimate_v2_funding_transaction_fee(&one_input, true, false, 2000),
15962+ estimate_v2_funding_transaction_fee(&one_input, &[], true, false, 2000),
1589115963 974,
1589215964 );
1589315965
1589415966 // 0 inputs
1589515967 assert_eq!(
15896- estimate_v2_funding_transaction_fee(&[], true, false, 2000),
15968+ estimate_v2_funding_transaction_fee(&[], &[], true, false, 2000),
1589715969 428,
1589815970 );
1589915971
1590015972 // not initiator
1590115973 assert_eq!(
15902- estimate_v2_funding_transaction_fee(&[], false, false, 2000),
15974+ estimate_v2_funding_transaction_fee(&[], &[], false, false, 2000),
1590315975 0,
1590415976 );
1590515977
1590615978 // splice initiator
1590715979 assert_eq!(
15908- estimate_v2_funding_transaction_fee(&one_input, true, true, 2000),
15980+ estimate_v2_funding_transaction_fee(&one_input, &[], true, true, 2000),
1590915981 1746,
1591015982 );
1591115983
1591215984 // splice acceptor
1591315985 assert_eq!(
15914- estimate_v2_funding_transaction_fee(&one_input, false, true, 2000),
15986+ estimate_v2_funding_transaction_fee(&one_input, &[], false, true, 2000),
1591515987 546,
1591615988 );
1591715989 }
0 commit comments