@@ -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,18 +5879,60 @@ 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#[allow(dead_code)] // TODO(dual_funding): TODO(splicing): Remove allow once used.
58845919#[rustfmt::skip]
58855920fn estimate_v2_funding_transaction_fee(
5886- funding_inputs: &[FundingTxInput], is_initiator: bool, is_splice: bool,
5921+ funding_inputs: &[FundingTxInput], outputs: &[TxOut], is_initiator: bool, is_splice: bool,
58875922 funding_feerate_sat_per_1000_weight: u32,
58885923) -> u64 {
5889- let mut weight : u64 = funding_inputs
5924+ let input_weight : u64 = funding_inputs
58905925 .iter()
58915926 .map(|input| BASE_INPUT_WEIGHT.saturating_add(input.utxo.satisfaction_weight))
58925927 .fold(0, |total_weight, input_weight| total_weight.saturating_add(input_weight));
58935928
5929+ let output_weight: u64 = outputs
5930+ .iter()
5931+ .map(|txout| txout.weight().to_wu())
5932+ .fold(0, |total_weight, output_weight| total_weight.saturating_add(output_weight));
5933+
5934+ let mut weight = input_weight.saturating_add(output_weight);
5935+
58945936 // The initiator pays for all common fields and the shared output in the funding transaction.
58955937 if is_initiator {
58965938 weight = weight
@@ -5927,7 +5969,7 @@ fn check_v2_funding_inputs_sufficient(
59275969 is_splice: bool, funding_feerate_sat_per_1000_weight: u32,
59285970) -> Result<u64, ChannelError> {
59295971 let estimated_fee = estimate_v2_funding_transaction_fee(
5930- funding_inputs, is_initiator, is_splice, funding_feerate_sat_per_1000_weight,
5972+ funding_inputs, &[], is_initiator, is_splice, funding_feerate_sat_per_1000_weight,
59315973 );
59325974
59335975 let mut total_input_sats = 0u64;
@@ -5975,6 +6017,9 @@ pub(super) struct FundingNegotiationContext {
59756017 /// The funding inputs we will be contributing to the channel.
59766018 #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
59776019 pub our_funding_inputs: Vec<FundingTxInput>,
6020+ /// The funding outputs we will be contributing to the channel.
6021+ #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
6022+ pub our_funding_outputs: Vec<TxOut>,
59786023 /// The change output script. This will be used if needed or -- if not set -- generated using
59796024 /// `SignerProvider::get_destination_script`.
59806025 #[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
@@ -6004,45 +6049,46 @@ impl FundingNegotiationContext {
60046049 debug_assert!(matches!(context.channel_state, ChannelState::NegotiatingFunding(_)));
60056050 }
60066051
6007- // Add output for funding tx
60086052 // Note: For the error case when the inputs are insufficient, it will be handled after
60096053 // the `calculate_change_output_value` call below
6010- let mut funding_outputs = Vec::new();
60116054
60126055 let shared_funding_output = TxOut {
60136056 value: Amount::from_sat(funding.get_value_satoshis()),
60146057 script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
60156058 };
60166059
60176060 // Optionally add change output
6018- if self.our_funding_contribution > SignedAmount::ZERO {
6019- let change_value_opt = calculate_change_output_value(
6061+ let change_value_opt = if self.our_funding_contribution > SignedAmount::ZERO {
6062+ calculate_change_output_value(
60206063 &self,
60216064 self.shared_funding_input.is_some(),
60226065 &shared_funding_output.script_pubkey,
6023- &funding_outputs,
60246066 context.holder_dust_limit_satoshis,
6025- )?;
6026- if let Some(change_value) = change_value_opt {
6027- let change_script = if let Some(script) = self.change_script {
6028- script
6029- } else {
6030- signer_provider
6031- .get_destination_script(context.channel_keys_id)
6032- .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6033- };
6034- let mut change_output =
6035- TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6036- let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6037- let change_output_fee =
6038- fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6039- let change_value_decreased_with_fee =
6040- change_value.saturating_sub(change_output_fee);
6041- // Check dust limit again
6042- if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6043- change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6044- funding_outputs.push(change_output);
6045- }
6067+ )?
6068+ } else {
6069+ None
6070+ };
6071+
6072+ let mut funding_outputs = self.our_funding_outputs;
6073+
6074+ if let Some(change_value) = change_value_opt {
6075+ let change_script = if let Some(script) = self.change_script {
6076+ script
6077+ } else {
6078+ signer_provider
6079+ .get_destination_script(context.channel_keys_id)
6080+ .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6081+ };
6082+ let mut change_output =
6083+ TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6084+ let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6085+ let change_output_fee =
6086+ fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6087+ let change_value_decreased_with_fee = change_value.saturating_sub(change_output_fee);
6088+ // Check dust limit again
6089+ if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6090+ change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6091+ funding_outputs.push(change_output);
60466092 }
60476093 }
60486094
@@ -10630,47 +10676,78 @@ where
1063010676 // TODO(splicing): check for quiescence
1063110677
1063210678 let our_funding_contribution = contribution.value();
10679+ if our_funding_contribution == SignedAmount::ZERO {
10680+ return Err(APIError::APIMisuseError {
10681+ err: format!(
10682+ "Channel {} cannot be spliced; contribution cannot be zero",
10683+ self.context.channel_id(),
10684+ ),
10685+ });
10686+ }
10687+
1063310688 if our_funding_contribution > SignedAmount::MAX_MONEY {
1063410689 return Err(APIError::APIMisuseError {
1063510690 err: format!(
10636- "Channel {} cannot be spliced; contribution exceeds total bitcoin supply: {}",
10691+ "Channel {} cannot be spliced in ; contribution exceeds total bitcoin supply: {}",
1063710692 self.context.channel_id(),
1063810693 our_funding_contribution,
1063910694 ),
1064010695 });
1064110696 }
1064210697
10643- if our_funding_contribution < SignedAmount::ZERO {
10698+ if our_funding_contribution < - SignedAmount::MAX_MONEY {
1064410699 return Err(APIError::APIMisuseError {
1064510700 err: format!(
10646- "TODO(splicing): Splice-out not supported, only splice in; channel ID {}, contribution {}",
10647- self.context.channel_id(), our_funding_contribution,
10648- ),
10701+ "Channel {} cannot be spliced out; contribution exhausts total bitcoin supply: {}",
10702+ self.context.channel_id(),
10703+ our_funding_contribution,
10704+ ),
1064910705 });
1065010706 }
1065110707
10652- // TODO(splicing): Once splice-out is supported, check that channel balance does not go below 0
10653- // (or below channel reserve)
10654-
1065510708 // Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
1065610709 // (Cannot test for miminum required post-splice channel value)
1065710710
10658- // Check that inputs are sufficient to cover our contribution.
10659- let _fee = check_v2_funding_inputs_sufficient(
10660- our_funding_contribution.to_sat(),
10661- contribution.inputs(),
10662- true,
10663- true,
10664- funding_feerate_per_kw,
10711+ let channel_balance = Amount::from_sat(self.funding.get_value_to_self_msat() / 1000);
10712+ let fees = check_splice_contribution_sufficient(
10713+ channel_balance,
10714+ &contribution,
10715+ true, // is_initiator
10716+ FeeRate::from_sat_per_kwu(funding_feerate_per_kw as u64),
1066510717 )
10666- .map_err(|err| APIError::APIMisuseError {
10667- err: format!(
10668- "Insufficient inputs for splicing; channel ID {}, err {}",
10669- self.context.channel_id(),
10670- err,
10671- ),
10718+ .map_err(|e| {
10719+ let splice_type = if our_funding_contribution < SignedAmount::ZERO {
10720+ "spliced out"
10721+ } else {
10722+ "spliced in"
10723+ };
10724+ APIError::APIMisuseError {
10725+ err: format!(
10726+ "Channel {} cannot be {}; {}",
10727+ self.context.channel_id(),
10728+ splice_type,
10729+ e,
10730+ ),
10731+ }
1067210732 })?;
1067310733
10734+ // Fees for splice-out are paid from the channel balance whereas fees for splice-in are paid
10735+ // by the funding inputs.
10736+ let adjusted_funding_contribution = if our_funding_contribution < SignedAmount::ZERO {
10737+ let adjusted_funding_contribution = our_funding_contribution
10738+ - fees.to_signed().expect("fees should never exceed Amount::MAX_MONEY");
10739+
10740+ // TODO(splicing): Check that channel balance does not go below the channel reserve
10741+ let _post_channel_balance = AddSigned::checked_add_signed(
10742+ channel_balance.to_sat(),
10743+ adjusted_funding_contribution.to_sat(),
10744+ );
10745+
10746+ adjusted_funding_contribution
10747+ } else {
10748+ our_funding_contribution
10749+ };
10750+
1067410751 for FundingTxInput { utxo, prevtx, .. } in contribution.inputs().iter() {
1067510752 const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
1067610753 channel_id: ChannelId([0; 32]),
@@ -10693,14 +10770,15 @@ where
1069310770 }
1069410771
1069510772 let prev_funding_input = self.funding.to_splice_funding_input();
10696- let (our_funding_inputs, change_script) = contribution.into_tx_parts();
10773+ let (our_funding_inputs, our_funding_outputs, change_script) = contribution.into_tx_parts();
1069710774 let funding_negotiation_context = FundingNegotiationContext {
1069810775 is_initiator: true,
10699- our_funding_contribution,
10776+ our_funding_contribution: adjusted_funding_contribution ,
1070010777 funding_tx_locktime: LockTime::from_consensus(locktime),
1070110778 funding_feerate_sat_per_1000_weight: funding_feerate_per_kw,
1070210779 shared_funding_input: Some(prev_funding_input),
1070310780 our_funding_inputs,
10781+ our_funding_outputs,
1070410782 change_script,
1070510783 };
1070610784
@@ -10716,7 +10794,7 @@ where
1071610794
1071710795 Ok(msgs::SpliceInit {
1071810796 channel_id: self.context.channel_id,
10719- funding_contribution_satoshis: our_funding_contribution .to_sat(),
10797+ funding_contribution_satoshis: adjusted_funding_contribution .to_sat(),
1072010798 funding_feerate_per_kw,
1072110799 locktime,
1072210800 funding_pubkey,
@@ -10825,6 +10903,7 @@ where
1082510903 funding_feerate_sat_per_1000_weight: msg.funding_feerate_per_kw,
1082610904 shared_funding_input: Some(prev_funding_input),
1082710905 our_funding_inputs: Vec::new(),
10906+ our_funding_outputs: Vec::new(),
1082810907 change_script: None,
1082910908 };
1083010909
@@ -12523,6 +12602,7 @@ where
1252312602 funding_feerate_sat_per_1000_weight,
1252412603 shared_funding_input: None,
1252512604 our_funding_inputs: funding_inputs,
12605+ our_funding_outputs: Vec::new(),
1252612606 change_script: None,
1252712607 };
1252812608 let chan = Self {
@@ -12677,6 +12757,7 @@ where
1267712757 funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
1267812758 shared_funding_input: None,
1267912759 our_funding_inputs: our_funding_inputs.clone(),
12760+ our_funding_outputs: Vec::new(),
1268012761 change_script: None,
1268112762 };
1268212763 let shared_funding_output = TxOut {
@@ -12702,7 +12783,7 @@ where
1270212783 inputs_to_contribute,
1270312784 shared_funding_input: None,
1270412785 shared_funding_output: SharedOwnedOutput::new(shared_funding_output, our_funding_contribution_sats),
12705- outputs_to_contribute: Vec::new (),
12786+ outputs_to_contribute: funding_negotiation_context.our_funding_outputs.clone (),
1270612787 }
1270712788 ).map_err(|err| {
1270812789 let reason = ClosureReason::ProcessingError { err: err.to_string() };
@@ -15870,43 +15951,43 @@ mod tests {
1587015951
1587115952 // 2 inputs, initiator, 2000 sat/kw feerate
1587215953 assert_eq!(
15873- estimate_v2_funding_transaction_fee(&two_inputs, true, false, 2000),
15954+ estimate_v2_funding_transaction_fee(&two_inputs, &[], true, false, 2000),
1587415955 1520,
1587515956 );
1587615957
1587715958 // higher feerate
1587815959 assert_eq!(
15879- estimate_v2_funding_transaction_fee(&two_inputs, true, false, 3000),
15960+ estimate_v2_funding_transaction_fee(&two_inputs, &[], true, false, 3000),
1588015961 2280,
1588115962 );
1588215963
1588315964 // only 1 input
1588415965 assert_eq!(
15885- estimate_v2_funding_transaction_fee(&one_input, true, false, 2000),
15966+ estimate_v2_funding_transaction_fee(&one_input, &[], true, false, 2000),
1588615967 974,
1588715968 );
1588815969
1588915970 // 0 inputs
1589015971 assert_eq!(
15891- estimate_v2_funding_transaction_fee(&[], true, false, 2000),
15972+ estimate_v2_funding_transaction_fee(&[], &[], true, false, 2000),
1589215973 428,
1589315974 );
1589415975
1589515976 // not initiator
1589615977 assert_eq!(
15897- estimate_v2_funding_transaction_fee(&[], false, false, 2000),
15978+ estimate_v2_funding_transaction_fee(&[], &[], false, false, 2000),
1589815979 0,
1589915980 );
1590015981
1590115982 // splice initiator
1590215983 assert_eq!(
15903- estimate_v2_funding_transaction_fee(&one_input, true, true, 2000),
15984+ estimate_v2_funding_transaction_fee(&one_input, &[], true, true, 2000),
1590415985 1746,
1590515986 );
1590615987
1590715988 // splice acceptor
1590815989 assert_eq!(
15909- estimate_v2_funding_transaction_fee(&one_input, false, true, 2000),
15990+ estimate_v2_funding_transaction_fee(&one_input, &[], false, true, 2000),
1591015991 546,
1591115992 );
1591215993 }
0 commit comments