@@ -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,
@@ -5880,20 +5880,62 @@ fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satos
58805880 cmp::min(channel_value_satoshis, cmp::max(q, dust_limit_satoshis))
58815881}
58825882
5883+ #[cfg(splicing)]
5884+ fn check_splice_contribution_sufficient(
5885+ channel_balance: Amount, contribution: &SpliceContribution, is_initiator: bool,
5886+ funding_feerate: FeeRate,
5887+ ) -> Result<Amount, ChannelError> {
5888+ let contribution_amount = contribution.value();
5889+ if contribution_amount < SignedAmount::ZERO {
5890+ let estimated_fee = Amount::from_sat(estimate_v2_funding_transaction_fee(
5891+ contribution.inputs(),
5892+ contribution.outputs(),
5893+ is_initiator,
5894+ true, // is_splice
5895+ funding_feerate.to_sat_per_kwu() as u32,
5896+ ));
5897+
5898+ if channel_balance > contribution_amount.unsigned_abs() + estimated_fee {
5899+ Ok(estimated_fee)
5900+ } else {
5901+ Err(ChannelError::Warn(format!(
5902+ "Available channel balance {} is lower than needed for splicing out {}, considering fees of {}",
5903+ channel_balance, contribution_amount.unsigned_abs(), estimated_fee,
5904+ )))
5905+ }
5906+ } else {
5907+ check_v2_funding_inputs_sufficient(
5908+ contribution_amount.to_sat(),
5909+ contribution.inputs(),
5910+ is_initiator,
5911+ true,
5912+ funding_feerate.to_sat_per_kwu() as u32,
5913+ )
5914+ .map(Amount::from_sat)
5915+ }
5916+ }
5917+
58835918/// Estimate our part of the fee of the new funding transaction.
58845919/// input_count: Number of contributed inputs.
58855920/// witness_weight: The witness weight for contributed inputs.
58865921#[allow(dead_code)] // TODO(dual_funding): TODO(splicing): Remove allow once used.
58875922#[rustfmt::skip]
58885923fn estimate_v2_funding_transaction_fee(
5889- funding_inputs: &[FundingTxInput], is_initiator: bool, is_splice: bool,
5924+ funding_inputs: &[FundingTxInput], outputs: &[TxOut], is_initiator: bool, is_splice: bool,
58905925 funding_feerate_sat_per_1000_weight: u32,
58915926) -> u64 {
5892- let mut weight : u64 = funding_inputs
5927+ let input_weight : u64 = funding_inputs
58935928 .iter()
58945929 .map(|input| BASE_INPUT_WEIGHT.saturating_add(input.utxo.satisfaction_weight))
58955930 .fold(0, |total_weight, input_weight| total_weight.saturating_add(input_weight));
58965931
5932+ let output_weight: u64 = outputs
5933+ .iter()
5934+ .map(|txout| txout.weight().to_wu())
5935+ .fold(0, |total_weight, output_weight| total_weight.saturating_add(output_weight));
5936+
5937+ let mut weight = input_weight.saturating_add(output_weight);
5938+
58975939 // The initiator pays for all common fields and the shared output in the funding transaction.
58985940 if is_initiator {
58995941 weight = weight
@@ -5930,7 +5972,7 @@ fn check_v2_funding_inputs_sufficient(
59305972 is_splice: bool, funding_feerate_sat_per_1000_weight: u32,
59315973) -> Result<u64, ChannelError> {
59325974 let estimated_fee = estimate_v2_funding_transaction_fee(
5933- funding_inputs, is_initiator, is_splice, funding_feerate_sat_per_1000_weight,
5975+ funding_inputs, &[], is_initiator, is_splice, funding_feerate_sat_per_1000_weight,
59345976 );
59355977
59365978 let mut total_input_sats = 0u64;
@@ -5978,6 +6020,9 @@ pub(super) struct FundingNegotiationContext {
59786020 /// The funding inputs we will be contributing to the channel.
59796021 #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
59806022 pub our_funding_inputs: Vec<FundingTxInput>,
6023+ /// The funding outputs we will be contributing to the channel.
6024+ #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
6025+ pub our_funding_outputs: Vec<TxOut>,
59816026 /// The change output script. This will be used if needed or -- if not set -- generated using
59826027 /// `SignerProvider::get_destination_script`.
59836028 #[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
@@ -6007,45 +6052,46 @@ impl FundingNegotiationContext {
60076052 debug_assert!(matches!(context.channel_state, ChannelState::NegotiatingFunding(_)));
60086053 }
60096054
6010- // Add output for funding tx
60116055 // Note: For the error case when the inputs are insufficient, it will be handled after
60126056 // the `calculate_change_output_value` call below
6013- let mut funding_outputs = Vec::new();
60146057
60156058 let shared_funding_output = TxOut {
60166059 value: Amount::from_sat(funding.get_value_satoshis()),
60176060 script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
60186061 };
60196062
60206063 // Optionally add change output
6021- if self.our_funding_contribution > SignedAmount::ZERO {
6022- let change_value_opt = calculate_change_output_value(
6064+ let change_value_opt = if self.our_funding_contribution > SignedAmount::ZERO {
6065+ calculate_change_output_value(
60236066 &self,
60246067 self.shared_funding_input.is_some(),
60256068 &shared_funding_output.script_pubkey,
6026- &funding_outputs,
60276069 context.holder_dust_limit_satoshis,
6028- )?;
6029- if let Some(change_value) = change_value_opt {
6030- let change_script = if let Some(script) = self.change_script {
6031- script
6032- } else {
6033- signer_provider
6034- .get_destination_script(context.channel_keys_id)
6035- .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6036- };
6037- let mut change_output =
6038- TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6039- let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6040- let change_output_fee =
6041- fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6042- let change_value_decreased_with_fee =
6043- change_value.saturating_sub(change_output_fee);
6044- // Check dust limit again
6045- if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6046- change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6047- funding_outputs.push(change_output);
6048- }
6070+ )?
6071+ } else {
6072+ None
6073+ };
6074+
6075+ let mut funding_outputs = self.our_funding_outputs;
6076+
6077+ if let Some(change_value) = change_value_opt {
6078+ let change_script = if let Some(script) = self.change_script {
6079+ script
6080+ } else {
6081+ signer_provider
6082+ .get_destination_script(context.channel_keys_id)
6083+ .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6084+ };
6085+ let mut change_output =
6086+ TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6087+ let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6088+ let change_output_fee =
6089+ fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6090+ let change_value_decreased_with_fee = change_value.saturating_sub(change_output_fee);
6091+ // Check dust limit again
6092+ if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6093+ change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6094+ funding_outputs.push(change_output);
60496095 }
60506096 }
60516097
@@ -10636,44 +10682,66 @@ where
1063610682 if our_funding_contribution > SignedAmount::MAX_MONEY {
1063710683 return Err(APIError::APIMisuseError {
1063810684 err: format!(
10639- "Channel {} cannot be spliced; contribution exceeds total bitcoin supply: {}",
10685+ "Channel {} cannot be spliced in ; contribution exceeds total bitcoin supply: {}",
1064010686 self.context.channel_id(),
1064110687 our_funding_contribution,
1064210688 ),
1064310689 });
1064410690 }
1064510691
10646- if our_funding_contribution < SignedAmount::ZERO {
10692+ if our_funding_contribution < - SignedAmount::MAX_MONEY {
1064710693 return Err(APIError::APIMisuseError {
1064810694 err: format!(
10649- "TODO(splicing): Splice-out not supported, only splice in; channel ID {}, contribution {}",
10650- self.context.channel_id(), our_funding_contribution,
10651- ),
10695+ "Channel {} cannot be spliced out; contribution exhausts total bitcoin supply: {}",
10696+ self.context.channel_id(),
10697+ our_funding_contribution,
10698+ ),
1065210699 });
1065310700 }
1065410701
10655- // TODO(splicing): Once splice-out is supported, check that channel balance does not go below 0
10656- // (or below channel reserve)
10657-
1065810702 // Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
1065910703 // (Cannot test for miminum required post-splice channel value)
1066010704
10661- // Check that inputs are sufficient to cover our contribution.
10662- let _fee = check_v2_funding_inputs_sufficient(
10663- our_funding_contribution.to_sat(),
10664- contribution.inputs(),
10665- true,
10666- true,
10667- funding_feerate_per_kw,
10705+ let channel_balance = Amount::from_sat(self.funding.get_value_to_self_msat() / 1000);
10706+ let fees = check_splice_contribution_sufficient(
10707+ channel_balance,
10708+ &contribution,
10709+ true, // is_initiator
10710+ FeeRate::from_sat_per_kwu(funding_feerate_per_kw as u64),
1066810711 )
10669- .map_err(|err| APIError::APIMisuseError {
10670- err: format!(
10671- "Insufficient inputs for splicing; channel ID {}, err {}",
10672- self.context.channel_id(),
10673- err,
10674- ),
10712+ .map_err(|e| {
10713+ let splice_type = if our_funding_contribution < SignedAmount::ZERO {
10714+ "spliced out"
10715+ } else {
10716+ "spliced in"
10717+ };
10718+ APIError::APIMisuseError {
10719+ err: format!(
10720+ "Channel {} cannot be {}; {}",
10721+ self.context.channel_id(),
10722+ splice_type,
10723+ e,
10724+ ),
10725+ }
1067510726 })?;
1067610727
10728+ // Fees for splice-out are paid from the channel balance whereas fees for splice-in are paid
10729+ // by the funding inputs.
10730+ let adjusted_funding_contribution = if our_funding_contribution < SignedAmount::ZERO {
10731+ let adjusted_funding_contribution = our_funding_contribution
10732+ - fees.to_signed().expect("fees should never exceed splice-out value");
10733+
10734+ // TODO(splicing): Check that channel balance does not go below the channel reserve
10735+ let _post_channel_balance = AddSigned::checked_add_signed(
10736+ channel_balance.to_sat(),
10737+ adjusted_funding_contribution.to_sat(),
10738+ );
10739+
10740+ adjusted_funding_contribution
10741+ } else {
10742+ our_funding_contribution
10743+ };
10744+
1067710745 for FundingTxInput { utxo, prevtx, .. } in contribution.inputs().iter() {
1067810746 const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
1067910747 channel_id: ChannelId([0; 32]),
@@ -10696,14 +10764,15 @@ where
1069610764 }
1069710765
1069810766 let prev_funding_input = self.funding.to_splice_funding_input();
10699- let (our_funding_inputs, change_script) = contribution.into_tx_parts();
10767+ let (our_funding_inputs, our_funding_outputs, change_script) = contribution.into_tx_parts();
1070010768 let funding_negotiation_context = FundingNegotiationContext {
1070110769 is_initiator: true,
10702- our_funding_contribution,
10770+ our_funding_contribution: adjusted_funding_contribution ,
1070310771 funding_tx_locktime: LockTime::from_consensus(locktime),
1070410772 funding_feerate_sat_per_1000_weight: funding_feerate_per_kw,
1070510773 shared_funding_input: Some(prev_funding_input),
1070610774 our_funding_inputs,
10775+ our_funding_outputs,
1070710776 change_script,
1070810777 };
1070910778
@@ -10719,7 +10788,7 @@ where
1071910788
1072010789 Ok(msgs::SpliceInit {
1072110790 channel_id: self.context.channel_id,
10722- funding_contribution_satoshis: our_funding_contribution .to_sat(),
10791+ funding_contribution_satoshis: adjusted_funding_contribution .to_sat(),
1072310792 funding_feerate_per_kw,
1072410793 locktime,
1072510794 funding_pubkey,
@@ -10828,6 +10897,7 @@ where
1082810897 funding_feerate_sat_per_1000_weight: msg.funding_feerate_per_kw,
1082910898 shared_funding_input: Some(prev_funding_input),
1083010899 our_funding_inputs: Vec::new(),
10900+ our_funding_outputs: Vec::new(),
1083110901 change_script: None,
1083210902 };
1083310903
@@ -12526,6 +12596,7 @@ where
1252612596 funding_feerate_sat_per_1000_weight,
1252712597 shared_funding_input: None,
1252812598 our_funding_inputs: funding_inputs,
12599+ our_funding_outputs: Vec::new(),
1252912600 change_script: None,
1253012601 };
1253112602 let chan = Self {
@@ -12680,6 +12751,7 @@ where
1268012751 funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
1268112752 shared_funding_input: None,
1268212753 our_funding_inputs: our_funding_inputs.clone(),
12754+ our_funding_outputs: Vec::new(),
1268312755 change_script: None,
1268412756 };
1268512757 let shared_funding_output = TxOut {
@@ -12705,7 +12777,7 @@ where
1270512777 inputs_to_contribute,
1270612778 shared_funding_input: None,
1270712779 shared_funding_output: SharedOwnedOutput::new(shared_funding_output, our_funding_contribution_sats),
12708- outputs_to_contribute: Vec::new (),
12780+ outputs_to_contribute: funding_negotiation_context.our_funding_outputs.clone (),
1270912781 }
1271012782 ).map_err(|err| {
1271112783 let reason = ClosureReason::ProcessingError { err: err.to_string() };
@@ -15876,43 +15948,43 @@ mod tests {
1587615948
1587715949 // 2 inputs, initiator, 2000 sat/kw feerate
1587815950 assert_eq!(
15879- estimate_v2_funding_transaction_fee(&two_inputs, true, false, 2000),
15951+ estimate_v2_funding_transaction_fee(&two_inputs, &[], true, false, 2000),
1588015952 1520,
1588115953 );
1588215954
1588315955 // higher feerate
1588415956 assert_eq!(
15885- estimate_v2_funding_transaction_fee(&two_inputs, true, false, 3000),
15957+ estimate_v2_funding_transaction_fee(&two_inputs, &[], true, false, 3000),
1588615958 2280,
1588715959 );
1588815960
1588915961 // only 1 input
1589015962 assert_eq!(
15891- estimate_v2_funding_transaction_fee(&one_input, true, false, 2000),
15963+ estimate_v2_funding_transaction_fee(&one_input, &[], true, false, 2000),
1589215964 974,
1589315965 );
1589415966
1589515967 // 0 inputs
1589615968 assert_eq!(
15897- estimate_v2_funding_transaction_fee(&[], true, false, 2000),
15969+ estimate_v2_funding_transaction_fee(&[], &[], true, false, 2000),
1589815970 428,
1589915971 );
1590015972
1590115973 // not initiator
1590215974 assert_eq!(
15903- estimate_v2_funding_transaction_fee(&[], false, false, 2000),
15975+ estimate_v2_funding_transaction_fee(&[], &[], false, false, 2000),
1590415976 0,
1590515977 );
1590615978
1590715979 // splice initiator
1590815980 assert_eq!(
15909- estimate_v2_funding_transaction_fee(&one_input, true, true, 2000),
15981+ estimate_v2_funding_transaction_fee(&one_input, &[], true, true, 2000),
1591015982 1746,
1591115983 );
1591215984
1591315985 // splice acceptor
1591415986 assert_eq!(
15915- estimate_v2_funding_transaction_fee(&one_input, false, true, 2000),
15987+ estimate_v2_funding_transaction_fee(&one_input, &[], false, true, 2000),
1591615988 546,
1591715989 );
1591815990 }
0 commit comments