@@ -25,7 +25,7 @@ use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE;
2525use bitcoin::secp256k1::{ecdsa::Signature, Secp256k1};
2626use bitcoin::secp256k1::{PublicKey, SecretKey};
2727#[cfg(splicing)]
28- use bitcoin::Sequence;
28+ use bitcoin::{FeeRate, Sequence} ;
2929use bitcoin::{secp256k1, sighash, TxIn};
3030
3131use crate::chain::chaininterface::{
@@ -5878,20 +5878,53 @@ fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satos
58785878 cmp::min(channel_value_satoshis, cmp::max(q, dust_limit_satoshis))
58795879}
58805880
5881+ #[cfg(splicing)]
5882+ fn check_splice_contribution_sufficient(
5883+ channel_balance: Amount, contribution: &SpliceContribution, is_initiator: bool,
5884+ funding_feerate: FeeRate,
5885+ ) -> Result<Amount, ChannelError> {
5886+ let contribution_amount = contribution.value();
5887+ if contribution_amount < SignedAmount::ZERO {
5888+ let estimated_fee = Amount::from_sat(estimate_v2_funding_transaction_fee(
5889+ is_initiator,
5890+ 1, // spends the previous funding output
5891+ Weight::from_wu(FUNDING_TRANSACTION_WITNESS_WEIGHT),
5892+ contribution.outputs(),
5893+ funding_feerate.to_sat_per_kwu() as u32,
5894+ ));
5895+
5896+ if channel_balance > contribution_amount.unsigned_abs() + estimated_fee {
5897+ Ok(estimated_fee)
5898+ } else {
5899+ Err(ChannelError::Warn(format!(
5900+ "Available channel balance {} is lower than needed for splicing out {}, considering fees of {}",
5901+ channel_balance, contribution_amount.unsigned_abs(), estimated_fee,
5902+ )))
5903+ }
5904+ } else {
5905+ check_v2_funding_inputs_sufficient(
5906+ contribution_amount.to_sat(),
5907+ contribution.inputs(),
5908+ is_initiator,
5909+ true,
5910+ funding_feerate.to_sat_per_kwu() as u32,
5911+ )
5912+ .map(Amount::from_sat)
5913+ }
5914+ }
5915+
58815916/// Estimate our part of the fee of the new funding transaction.
58825917/// input_count: Number of contributed inputs.
58835918/// witness_weight: The witness weight for contributed inputs.
58845919#[allow(dead_code)] // TODO(dual_funding): TODO(splicing): Remove allow once used.
58855920#[rustfmt::skip]
58865921fn estimate_v2_funding_transaction_fee(
5887- is_initiator: bool, input_count: usize, witness_weight: Weight,
5922+ is_initiator: bool, input_count: usize, witness_weight: Weight, outputs: &[TxOut],
58885923 funding_feerate_sat_per_1000_weight: u32,
58895924) -> u64 {
5890- // Inputs
58915925 let mut weight = (input_count as u64) * BASE_INPUT_WEIGHT;
5892-
5893- // Witnesses
58945926 weight = weight.saturating_add(witness_weight.to_wu());
5927+ weight = weight.saturating_add(outputs.iter().map(|txout| txout.weight().to_wu()).sum());
58955928
58965929 // If we are the initiator, we must pay for weight of all common fields in the funding transaction.
58975930 if is_initiator {
@@ -5929,7 +5962,7 @@ fn check_v2_funding_inputs_sufficient(
59295962 funding_inputs_len += 1;
59305963 total_input_witness_weight += Weight::from_wu(FUNDING_TRANSACTION_WITNESS_WEIGHT);
59315964 }
5932- let estimated_fee = estimate_v2_funding_transaction_fee(is_initiator, funding_inputs_len, total_input_witness_weight, funding_feerate_sat_per_1000_weight);
5965+ let estimated_fee = estimate_v2_funding_transaction_fee(is_initiator, funding_inputs_len, total_input_witness_weight, &[], funding_feerate_sat_per_1000_weight);
59335966
59345967 let mut total_input_sats = 0u64;
59355968 for FundingTxInput { utxo, .. } in funding_inputs.iter() {
@@ -5976,6 +6009,9 @@ pub(super) struct FundingNegotiationContext {
59766009 /// The funding inputs we will be contributing to the channel.
59776010 #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
59786011 pub our_funding_inputs: Vec<FundingTxInput>,
6012+ /// The funding outputs we will be contributing to the channel.
6013+ #[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
6014+ pub our_funding_outputs: Vec<TxOut>,
59796015 /// The change output script. This will be used if needed or -- if not set -- generated using
59806016 /// `SignerProvider::get_destination_script`.
59816017 #[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
@@ -6005,45 +6041,47 @@ impl FundingNegotiationContext {
60056041 debug_assert!(matches!(context.channel_state, ChannelState::NegotiatingFunding(_)));
60066042 }
60076043
6008- // Add output for funding tx
60096044 // Note: For the error case when the inputs are insufficient, it will be handled after
60106045 // the `calculate_change_output_value` call below
6011- let mut funding_outputs = Vec::new();
60126046
60136047 let shared_funding_output = TxOut {
60146048 value: Amount::from_sat(funding.get_value_satoshis()),
60156049 script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
60166050 };
60176051
60186052 // Optionally add change output
6019- if self.our_funding_contribution > SignedAmount::ZERO {
6020- let change_value_opt = calculate_change_output_value(
6053+ let change_value_opt = if self.our_funding_contribution > SignedAmount::ZERO {
6054+ calculate_change_output_value(
60216055 &self,
60226056 self.shared_funding_input.is_some(),
60236057 &shared_funding_output.script_pubkey,
6024- &funding_outputs,
60256058 context.holder_dust_limit_satoshis,
6026- )?;
6027- if let Some(change_value) = change_value_opt {
6028- let change_script = if let Some(script) = self.change_script {
6029- script
6030- } else {
6031- signer_provider
6032- .get_destination_script(context.channel_keys_id)
6033- .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6034- };
6035- let mut change_output =
6036- TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6037- let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6038- let change_output_fee =
6039- fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6040- let change_value_decreased_with_fee =
6041- change_value.saturating_sub(change_output_fee);
6042- // Check dust limit again
6043- if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6044- change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6045- funding_outputs.push(change_output);
6046- }
6059+ )?
6060+ } else {
6061+ None
6062+ };
6063+
6064+ let mut funding_outputs = self.our_funding_outputs;
6065+
6066+ if let Some(change_value) = change_value_opt {
6067+ let change_script = if let Some(script) = self.change_script {
6068+ script
6069+ } else {
6070+ signer_provider
6071+ .get_destination_script(context.channel_keys_id)
6072+ .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6073+ };
6074+ let mut change_output =
6075+ TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
6076+ let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
6077+ let change_output_fee =
6078+ fee_for_weight(self.funding_feerate_sat_per_1000_weight, change_output_weight);
6079+ let change_value_decreased_with_fee =
6080+ 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);
60476085 }
60486086 }
60496087
@@ -10634,44 +10672,77 @@ where
1063410672 if our_funding_contribution > SignedAmount::MAX_MONEY {
1063510673 return Err(APIError::APIMisuseError {
1063610674 err: format!(
10637- "Channel {} cannot be spliced; contribution exceeds total bitcoin supply: {}",
10675+ "Channel {} cannot be spliced in ; contribution exceeds total bitcoin supply: {}",
1063810676 self.context.channel_id(),
1063910677 our_funding_contribution,
1064010678 ),
1064110679 });
1064210680 }
1064310681
10644- if our_funding_contribution < SignedAmount::ZERO {
10682+ if our_funding_contribution < - SignedAmount::MAX_MONEY {
1064510683 return Err(APIError::APIMisuseError {
1064610684 err: format!(
10647- "TODO(splicing): Splice-out not supported, only splice in; channel ID {}, contribution {}",
10648- self.context.channel_id(), our_funding_contribution,
10649- ),
10685+ "Channel {} cannot be spliced out; contribution exceeds total bitcoin supply: {}",
10686+ self.context.channel_id(),
10687+ our_funding_contribution,
10688+ ),
1065010689 });
1065110690 }
1065210691
10653- // TODO(splicing): Once splice-out is supported, check that channel balance does not go below 0
10654- // (or below channel reserve)
10692+ let funding_inputs = contribution.inputs();
10693+ let funding_outputs = contribution.outputs();
10694+ if !funding_inputs.is_empty() && !funding_outputs.is_empty() {
10695+ return Err(APIError::APIMisuseError {
10696+ err: format!(
10697+ "Channel {} cannot be both spliced in and out; operation not supported",
10698+ self.context.channel_id(),
10699+ ),
10700+ });
10701+ }
1065510702
1065610703 // Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
1065710704 // (Cannot test for miminum required post-splice channel value)
1065810705
10659- // Check that inputs are sufficient to cover our contribution.
10660- let _fee = check_v2_funding_inputs_sufficient(
10661- our_funding_contribution.to_sat(),
10662- contribution.inputs(),
10663- true,
10664- true,
10665- funding_feerate_per_kw,
10706+ let channel_balance = Amount::from_sat(self.funding.get_value_to_self_msat() / 1000);
10707+ let fees = check_splice_contribution_sufficient(
10708+ channel_balance,
10709+ &contribution,
10710+ true, // is_initiator
10711+ FeeRate::from_sat_per_kwu(funding_feerate_per_kw as u64),
1066610712 )
10667- .map_err(|err| APIError::APIMisuseError {
10668- err: format!(
10669- "Insufficient inputs for splicing; channel ID {}, err {}",
10670- self.context.channel_id(),
10671- err,
10672- ),
10713+ .map_err(|e| {
10714+ let splice_type = if our_funding_contribution < SignedAmount::ZERO {
10715+ "spliced out"
10716+ } else {
10717+ "spliced in"
10718+ };
10719+ APIError::APIMisuseError {
10720+ err: format!(
10721+ "Channel {} cannot be {}; {}",
10722+ self.context.channel_id(),
10723+ splice_type,
10724+ e,
10725+ ),
10726+ }
1067310727 })?;
1067410728
10729+ // Fees for splice-out are paid from the channel balance whereas fees for splice-in are paid
10730+ // by the funding inputs.
10731+ let adjusted_funding_contribution = if our_funding_contribution < SignedAmount::ZERO {
10732+ let adjusted_funding_contribution = our_funding_contribution
10733+ - fees.to_signed().expect("fees should never exceed splice-out value");
10734+
10735+ // TODO(splicing): Check that channel balance does not go below the channel reserve
10736+ let _post_channel_balance = AddSigned::checked_add_signed(
10737+ channel_balance.to_sat(),
10738+ adjusted_funding_contribution.to_sat(),
10739+ );
10740+
10741+ adjusted_funding_contribution
10742+ } else {
10743+ our_funding_contribution
10744+ };
10745+
1067510746 for FundingTxInput { utxo, prevtx, .. } in contribution.inputs().iter() {
1067610747 const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
1067710748 channel_id: ChannelId([0; 32]),
@@ -10693,14 +10764,15 @@ where
1069310764 }
1069410765
1069510766 let prev_funding_input = self.funding.to_splice_funding_input();
10696- let (our_funding_inputs, change_script) = contribution.into_tx_parts();
10767+ let (our_funding_inputs, our_funding_outputs, change_script) = contribution.into_tx_parts();
1069710768 let funding_negotiation_context = FundingNegotiationContext {
1069810769 is_initiator: true,
10699- our_funding_contribution,
10770+ our_funding_contribution: adjusted_funding_contribution ,
1070010771 funding_tx_locktime: LockTime::from_consensus(locktime),
1070110772 funding_feerate_sat_per_1000_weight: funding_feerate_per_kw,
1070210773 shared_funding_input: Some(prev_funding_input),
1070310774 our_funding_inputs,
10775+ our_funding_outputs,
1070410776 change_script,
1070510777 };
1070610778
@@ -10716,7 +10788,7 @@ where
1071610788
1071710789 Ok(msgs::SpliceInit {
1071810790 channel_id: self.context.channel_id,
10719- funding_contribution_satoshis: our_funding_contribution .to_sat(),
10791+ funding_contribution_satoshis: adjusted_funding_contribution .to_sat(),
1072010792 funding_feerate_per_kw,
1072110793 locktime,
1072210794 funding_pubkey,
@@ -10825,6 +10897,7 @@ where
1082510897 funding_feerate_sat_per_1000_weight: msg.funding_feerate_per_kw,
1082610898 shared_funding_input: Some(prev_funding_input),
1082710899 our_funding_inputs: Vec::new(),
10900+ our_funding_outputs: Vec::new(),
1082810901 change_script: None,
1082910902 };
1083010903
@@ -12523,6 +12596,7 @@ where
1252312596 funding_feerate_sat_per_1000_weight,
1252412597 shared_funding_input: None,
1252512598 our_funding_inputs: funding_inputs,
12599+ our_funding_outputs: Vec::new(),
1252612600 change_script: None,
1252712601 };
1252812602 let chan = Self {
@@ -12677,6 +12751,7 @@ where
1267712751 funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
1267812752 shared_funding_input: None,
1267912753 our_funding_inputs: our_funding_inputs.clone(),
12754+ our_funding_outputs: Vec::new(),
1268012755 change_script: None,
1268112756 };
1268212757 let shared_funding_output = TxOut {
@@ -12702,7 +12777,7 @@ where
1270212777 inputs_to_contribute,
1270312778 shared_funding_input: None,
1270412779 shared_funding_output: SharedOwnedOutput::new(shared_funding_output, our_funding_contribution_sats),
12705- outputs_to_contribute: Vec::new (),
12780+ outputs_to_contribute: funding_negotiation_context.our_funding_outputs.clone (),
1270612781 }
1270712782 ).map_err(|err| {
1270812783 let reason = ClosureReason::ProcessingError { err: err.to_string() };
@@ -15871,31 +15946,31 @@ mod tests {
1587115946
1587215947 // 2 inputs with weight 300, initiator, 2000 sat/kw feerate
1587315948 assert_eq!(
15874- estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), 2000),
15949+ estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), &[], 2000),
1587515950 1668
1587615951 );
1587715952
1587815953 // higher feerate
1587915954 assert_eq!(
15880- estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), 3000),
15955+ estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), &[], 3000),
1588115956 2502
1588215957 );
1588315958
1588415959 // only 1 input
1588515960 assert_eq!(
15886- estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(300), 2000),
15961+ estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(300), &[], 2000),
1588715962 1348
1588815963 );
1588915964
1589015965 // 0 input weight
1589115966 assert_eq!(
15892- estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(0), 2000),
15967+ estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(0), &[], 2000),
1589315968 748
1589415969 );
1589515970
1589615971 // not initiator
1589715972 assert_eq!(
15898- estimate_v2_funding_transaction_fee(false, 1, Weight::from_wu(0), 2000),
15973+ estimate_v2_funding_transaction_fee(false, 1, Weight::from_wu(0), &[], 2000),
1589915974 320
1590015975 );
1590115976 }
0 commit comments