@@ -47,6 +47,8 @@ use crate::ln::chan_utils::{
4747 get_commitment_transaction_number_obscure_factor,
4848 ClosingTransaction, commit_tx_fee_sat,
4949};
50+ #[cfg(splicing)]
51+ use crate::ln::chan_utils::FUNDING_TRANSACTION_WITNESS_WEIGHT;
5052use crate::ln::chan_utils;
5153use crate::ln::onion_utils::HTLCFailReason;
5254use crate::chain::BestBlock;
@@ -4692,6 +4694,58 @@ fn estimate_v2_funding_transaction_fee(
46924694 fee_for_weight(funding_feerate_sat_per_1000_weight, weight)
46934695}
46944696
4697+ /// Verify that the provided inputs by a counterparty to the funding transaction are enough
4698+ /// to cover the intended contribution amount *plus* the proportional fees of the counterparty.
4699+ /// Fees are computed using `estimate_v2_funding_transaction_fee`, and contain
4700+ /// the fees of the inputs, fees of the inputs weight, and for the initiator,
4701+ /// the fees of the common fields as well as the output and extra input weights.
4702+ /// Returns estimated (partial) fees as additional information
4703+ #[cfg(splicing)]
4704+ pub(super) fn check_v2_funding_inputs_sufficient(
4705+ contribution_amount: i64, funding_inputs: &[(TxIn, Transaction, Weight)], is_initiator: bool,
4706+ is_splice: bool, funding_feerate_sat_per_1000_weight: u32,
4707+ ) -> Result<u64, ChannelError> {
4708+ let mut total_input_witness_weight = Weight::from_wu(funding_inputs.iter().map(|(_, _, w)| w.to_wu()).sum());
4709+ if is_initiator && is_splice {
4710+ // consider the weight of the witness needed for spending the old funding transaction
4711+ total_input_witness_weight += Weight::from_wu(FUNDING_TRANSACTION_WITNESS_WEIGHT);
4712+ }
4713+ let estimated_fee = estimate_v2_funding_transaction_fee(is_initiator, funding_inputs.len(), total_input_witness_weight, funding_feerate_sat_per_1000_weight);
4714+
4715+ let mut total_input_sats = 0u64;
4716+ for (idx, input) in funding_inputs.iter().enumerate() {
4717+ if let Some(output) = input.1.output.get(input.0.previous_output.vout as usize) {
4718+ total_input_sats = total_input_sats.saturating_add(output.value.to_sat());
4719+ } else {
4720+ return Err(ChannelError::Warn(format!(
4721+ "Transaction with txid {} does not have an output with vout of {} corresponding to TxIn at funding_inputs[{}]",
4722+ input.1.compute_txid(), input.0.previous_output.vout, idx
4723+ )));
4724+ }
4725+ }
4726+
4727+ // If the inputs are enough to cover intended contribution amount, with fees even when
4728+ // there is a change output, we are fine.
4729+ // If the inputs are less, but enough to cover intended contribution amount, with
4730+ // (lower) fees with no change, we are also fine (change will not be generated).
4731+ // So it's enough to check considering the lower, no-change fees.
4732+ //
4733+ // Note: dust limit is not relevant in this check.
4734+ //
4735+ // TODO(splicing): refine check including the fact wether a change will be added or not.
4736+ // Can be done once dual funding preparation is included.
4737+
4738+ let minimal_input_amount_needed = contribution_amount.saturating_add(estimated_fee as i64);
4739+ if (total_input_sats as i64) < minimal_input_amount_needed {
4740+ Err(ChannelError::Warn(format!(
4741+ "Total input amount {} is lower than needed for contribution {}, considering fees of {}. Need more inputs.",
4742+ total_input_sats, contribution_amount, estimated_fee,
4743+ )))
4744+ } else {
4745+ Ok(estimated_fee)
4746+ }
4747+ }
4748+
46954749/// Context for dual-funded channels.
46964750pub(super) struct DualFundingChannelContext {
46974751 /// The amount in satoshis we will be contributing to the channel.
@@ -8356,7 +8410,7 @@ impl<SP: Deref> FundedChannel<SP> where
83568410 /// Includes the witness weight for this input (e.g. P2WPKH_WITNESS_WEIGHT=109 for typical P2WPKH inputs).
83578411 #[cfg(splicing)]
83588412 pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
8359- _our_funding_inputs : &Vec<(TxIn, Transaction, Weight)>,
8413+ our_funding_inputs : &Vec<(TxIn, Transaction, Weight)>,
83608414 funding_feerate_per_kw: u32, locktime: u32,
83618415 ) -> Result<msgs::SpliceInit, APIError> {
83628416 // Check if a splice has been initiated already.
@@ -8390,6 +8444,13 @@ impl<SP: Deref> FundedChannel<SP> where
83908444 // Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
83918445 // (Cannot test for miminum required post-splice channel value)
83928446
8447+ // Check that inputs are sufficient to cover our contribution.
8448+ // Extra common weight is the weight for spending the old funding
8449+ let _fee = check_v2_funding_inputs_sufficient(our_funding_contribution_satoshis, &our_funding_inputs, true, true, funding_feerate_per_kw)
8450+ .map_err(|err| APIError::APIMisuseError { err: format!(
8451+ "Insufficient inputs for splicing; channel ID {}, err {}",
8452+ self.context.channel_id(), err,
8453+ )})?;
83938454
83948455 self.pending_splice_pre = Some(PendingSplice {
83958456 our_funding_contribution: our_funding_contribution_satoshis,
@@ -8441,7 +8502,8 @@ impl<SP: Deref> FundedChannel<SP> where
84418502
84428503 if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
84438504 return Err(ChannelError::Warn(format!(
8444- "Splice-out not supported, only splice in, relative {} + {}",
8505+ "Splice-out not supported, only splice in, contribution is {} ({} + {})",
8506+ their_funding_contribution_satoshis + our_funding_contribution_satoshis,
84458507 their_funding_contribution_satoshis, our_funding_contribution_satoshis,
84468508 )));
84478509 }
@@ -11051,8 +11113,12 @@ mod tests {
1105111113 use bitcoin::constants::ChainHash;
1105211114 use bitcoin::script::{ScriptBuf, Builder};
1105311115 use bitcoin::transaction::{Transaction, TxOut, Version};
11116+ #[cfg(splicing)]
11117+ use bitcoin::transaction::TxIn;
1105411118 use bitcoin::opcodes;
1105511119 use bitcoin::network::Network;
11120+ #[cfg(splicing)]
11121+ use bitcoin::Weight;
1105611122 use crate::ln::onion_utils::INVALID_ONION_BLINDING;
1105711123 use crate::types::payment::{PaymentHash, PaymentPreimage};
1105811124 use crate::ln::channel_keys::{RevocationKey, RevocationBasepoint};
@@ -12855,6 +12921,114 @@ mod tests {
1285512921 );
1285612922 }
1285712923
12924+ #[cfg(splicing)]
12925+ fn funding_input_sats(input_value_sats: u64) -> (TxIn, Transaction, Weight) {
12926+ use crate::sign::P2WPKH_WITNESS_WEIGHT;
12927+
12928+ let input_1_prev_out = TxOut { value: Amount::from_sat(input_value_sats), script_pubkey: ScriptBuf::default() };
12929+ let input_1_prev_tx = Transaction {
12930+ input: vec![], output: vec![input_1_prev_out],
12931+ version: Version::TWO, lock_time: bitcoin::absolute::LockTime::ZERO,
12932+ };
12933+ let input_1_txin = TxIn {
12934+ previous_output: bitcoin::OutPoint { txid: input_1_prev_tx.compute_txid(), vout: 0 },
12935+ ..Default::default()
12936+ };
12937+ (input_1_txin, input_1_prev_tx, Weight::from_wu(P2WPKH_WITNESS_WEIGHT))
12938+ }
12939+
12940+ #[cfg(splicing)]
12941+ #[test]
12942+ fn test_check_v2_funding_inputs_sufficient() {
12943+ use crate::ln::channel::check_v2_funding_inputs_sufficient;
12944+
12945+ // positive case, inputs well over intended contribution
12946+ assert_eq!(
12947+ check_v2_funding_inputs_sufficient(
12948+ 220_000,
12949+ &[
12950+ funding_input_sats(200_000),
12951+ funding_input_sats(100_000),
12952+ ],
12953+ true,
12954+ true,
12955+ 2000,
12956+ ).unwrap(),
12957+ 1948,
12958+ );
12959+
12960+ // negative case, inputs clearly insufficient
12961+ {
12962+ let res = check_v2_funding_inputs_sufficient(
12963+ 220_000,
12964+ &[
12965+ funding_input_sats(100_000),
12966+ ],
12967+ true,
12968+ true,
12969+ 2000,
12970+ );
12971+ assert_eq!(
12972+ format!("{:?}", res.err().unwrap()),
12973+ "Warn: Total input amount 100000 is lower than needed for contribution 220000, considering fees of 1410. Need more inputs.",
12974+ );
12975+ }
12976+
12977+ // barely covers
12978+ {
12979+ let expected_fee: u64 = 1948;
12980+ assert_eq!(
12981+ check_v2_funding_inputs_sufficient(
12982+ (300_000 - expected_fee - 20) as i64,
12983+ &[
12984+ funding_input_sats(200_000),
12985+ funding_input_sats(100_000),
12986+ ],
12987+ true,
12988+ true,
12989+ 2000,
12990+ ).unwrap(),
12991+ expected_fee,
12992+ );
12993+ }
12994+
12995+ // higher fee rate, does not cover
12996+ {
12997+ let res = check_v2_funding_inputs_sufficient(
12998+ 298032,
12999+ &[
13000+ funding_input_sats(200_000),
13001+ funding_input_sats(100_000),
13002+ ],
13003+ true,
13004+ true,
13005+ 2200,
13006+ );
13007+ assert_eq!(
13008+ format!("{:?}", res.err().unwrap()),
13009+ "Warn: Total input amount 300000 is lower than needed for contribution 298032, considering fees of 2143. Need more inputs.",
13010+ );
13011+ }
13012+
13013+ // barely covers, less fees (no extra weight, no init)
13014+ {
13015+ let expected_fee: u64 = 1076;
13016+ assert_eq!(
13017+ check_v2_funding_inputs_sufficient(
13018+ (300_000 - expected_fee - 20) as i64,
13019+ &[
13020+ funding_input_sats(200_000),
13021+ funding_input_sats(100_000),
13022+ ],
13023+ false,
13024+ false,
13025+ 2000,
13026+ ).unwrap(),
13027+ expected_fee,
13028+ );
13029+ }
13030+ }
13031+
1285813032 #[cfg(splicing)]
1285913033 fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
1286013034 use crate::ln::channel::PendingSplice;
0 commit comments