@@ -4611,6 +4611,54 @@ pub(super) fn calculate_our_funding_satoshis(
46114611 }
46124612}
46134613
4614+ /// Verify that the provided inputs by a counterparty to the funding transaction are enough
4615+ /// to cover the intended contribution amount *plus* the proportional fees of the counterparty.
4616+ /// Fees are computed using `estimate_v2_funding_transaction_fee`, and contain
4617+ /// the fees of the inputs, fees of the inputs weight, and for the initiator,
4618+ /// the fees of the common fields as well as the output and extra input weights.
4619+ /// Returns estimated (partial) fees as additional information
4620+ pub(super) fn check_v2_funding_inputs_sufficient(
4621+ contribution_amount: i64, funding_inputs: &[(TxIn, Transaction, Weight)], is_initiator: bool,
4622+ extra_common_input_weight: Option<Weight>, funding_feerate_sat_per_1000_weight: u32,
4623+ ) -> Result<u64, ChannelError> {
4624+ let mut total_input_witness_weight = Weight::from_wu(funding_inputs.iter().map(|(_, _, w)| w.to_wu()).sum());
4625+ if is_initiator {
4626+ if let Some(extra) = extra_common_input_weight {
4627+ total_input_witness_weight += extra;
4628+ }
4629+ }
4630+ let estimated_fee = estimate_v2_funding_transaction_fee(is_initiator, funding_inputs.len(), total_input_witness_weight, funding_feerate_sat_per_1000_weight);
4631+
4632+ let mut total_input_sats = 0u64;
4633+ for (idx, input) in funding_inputs.iter().enumerate() {
4634+ if let Some(output) = input.1.output.get(input.0.previous_output.vout as usize) {
4635+ total_input_sats = total_input_sats.saturating_add(output.value.to_sat());
4636+ } else {
4637+ return Err(ChannelError::Warn(format!(
4638+ "Transaction with txid {} does not have an output with vout of {} corresponding to TxIn at funding_inputs[{}]",
4639+ input.1.compute_txid(), input.0.previous_output.vout, idx
4640+ )));
4641+ }
4642+ }
4643+
4644+ // If the inputs are enough to cover intended contribution amount, with fees even when
4645+ // there is a change output, we are fine.
4646+ // If the inputs are less, but enough to cover intended contribution amount, with
4647+ // (lower) fees with no change, we are also fine (change will not be generated).
4648+ // So it's enough to check considering the lower, no-change fees.
4649+ // Note: dust limit is not relevant in this check.
4650+
4651+ let minimal_input_amount_needed = contribution_amount.saturating_add(estimated_fee as i64);
4652+ if (total_input_sats as i64) < minimal_input_amount_needed {
4653+ Err(ChannelError::Warn(format!(
4654+ "Total input amount {} is lower than needed for contribution {}, considering fees of {}. Need more inputs.",
4655+ total_input_sats, contribution_amount, estimated_fee,
4656+ )))
4657+ } else {
4658+ Ok(estimated_fee)
4659+ }
4660+ }
4661+
46144662/// Context for dual-funded channels.
46154663pub(super) struct DualFundingChannelContext {
46164664 /// The amount in satoshis we will be contributing to the channel.
@@ -8259,28 +8307,10 @@ impl<SP: Deref> FundedChannel<SP> where
82598307 // Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
82608308 // (Cannot test for miminum required post-splice channel value)
82618309
8262- // Pre-check that inputs are sufficient to cover our contribution.
8263- // Note: fees are not taken into account here.
8264- let sum_input: u64 = our_funding_inputs.iter().map(
8265- |(txin, tx, _)| tx.output.get(txin.previous_output.vout as usize).map(|tx| tx.value.to_sat()).unwrap_or(0)
8266- ).sum();
8267-
8268- // The +1 is to include the input of the old funding
8269- let funding_input_count = our_funding_inputs.len() + 1;
8270- // Input witness weight, extended with weight for spending old funding
8271- let total_witness_weight = Weight::from_wu(
8272- our_funding_inputs.iter().map(|(_, _, w)| w.to_wu()).sum::<u64>()
8273- .saturating_add(FUNDING_TRANSACTION_WITNESS_WEIGHT)
8274- );
8275- let estimated_fee = estimate_v2_funding_transaction_fee(true, funding_input_count, total_witness_weight, funding_feerate_per_kw);
8276- let available_input = sum_input.saturating_sub(estimated_fee);
8277-
8278- if (available_input as i64) < our_funding_contribution_satoshis {
8279- return Err(ChannelError::Warn(format!(
8280- "Provided inputs are insufficient for our contribution, {} {}",
8281- sum_input, our_funding_contribution_satoshis,
8282- )));
8283- }
8310+ // Check that inputs are sufficient to cover our contribution.
8311+ // Extra common weight is the weight for spending the old funding
8312+ let extra_input_weight = Weight::from_wu(FUNDING_TRANSACTION_WITNESS_WEIGHT);
8313+ let _fee = check_v2_funding_inputs_sufficient(our_funding_contribution_satoshis, &our_funding_inputs, true, Some(extra_input_weight), funding_feerate_per_kw)?;
82848314
82858315 self.pending_splice_pre = Some(PendingSplice {
82868316 our_funding_contribution: our_funding_contribution_satoshis,
@@ -10727,8 +10757,12 @@ mod tests {
1072710757 use bitcoin::constants::ChainHash;
1072810758 use bitcoin::script::{ScriptBuf, Builder};
1072910759 use bitcoin::transaction::{Transaction, TxOut, Version};
10760+ #[cfg(splicing)]
10761+ use bitcoin::transaction::TxIn;
1073010762 use bitcoin::opcodes;
1073110763 use bitcoin::network::Network;
10764+ #[cfg(splicing)]
10765+ use bitcoin::Weight;
1073210766 use crate::ln::onion_utils::INVALID_ONION_BLINDING;
1073310767 use crate::types::payment::{PaymentHash, PaymentPreimage};
1073410768 use crate::ln::channel_keys::{RevocationKey, RevocationBasepoint};
@@ -12536,15 +12570,126 @@ mod tests {
1253612570 );
1253712571 }
1253812572
12539- #[cfg(all(test, splicing))]
12573+ #[cfg(splicing)]
12574+ fn funding_input_sats(input_value_sats: u64) -> (TxIn, Transaction, Weight) {
12575+ use crate::sign::P2WPKH_WITNESS_WEIGHT;
12576+
12577+ let input_1_prev_out = TxOut { value: Amount::from_sat(input_value_sats), script_pubkey: ScriptBuf::default() };
12578+ let input_1_prev_tx = Transaction {
12579+ input: vec![], output: vec![input_1_prev_out],
12580+ version: Version::TWO, lock_time: bitcoin::absolute::LockTime::ZERO,
12581+ };
12582+ let input_1_txin = TxIn {
12583+ previous_output: bitcoin::OutPoint { txid: input_1_prev_tx.compute_txid(), vout: 0 },
12584+ ..Default::default()
12585+ };
12586+ (input_1_txin, input_1_prev_tx, Weight::from_wu(P2WPKH_WITNESS_WEIGHT))
12587+ }
12588+
12589+ #[cfg(splicing)]
12590+ #[test]
12591+ fn test_check_v2_funding_inputs_sufficient() {
12592+ use crate::ln::chan_utils::FUNDING_TRANSACTION_WITNESS_WEIGHT;
12593+ use crate::ln::channel::check_v2_funding_inputs_sufficient;
12594+ use bitcoin::Weight;
12595+
12596+ // positive case, inputs well over intended contribution
12597+ assert_eq!(
12598+ check_v2_funding_inputs_sufficient(
12599+ 220_000,
12600+ &[
12601+ funding_input_sats(200_000),
12602+ funding_input_sats(100_000),
12603+ ],
12604+ true,
12605+ Some(Weight::from_wu(FUNDING_TRANSACTION_WITNESS_WEIGHT)),
12606+ 2000,
12607+ ).unwrap(),
12608+ 1948,
12609+ );
12610+
12611+ // negative case, inputs clearly insufficient
12612+ {
12613+ let res = check_v2_funding_inputs_sufficient(
12614+ 220_000,
12615+ &[
12616+ funding_input_sats(100_000),
12617+ ],
12618+ true,
12619+ Some(Weight::from_wu(FUNDING_TRANSACTION_WITNESS_WEIGHT)),
12620+ 2000,
12621+ );
12622+ assert_eq!(
12623+ format!("{:?}", res.err().unwrap()),
12624+ "Warn : Total input amount 100000 is lower than needed for contribution 220000, considering fees of 1410. Need more inputs.",
12625+ );
12626+ }
12627+
12628+ // barely covers
12629+ {
12630+ let expected_fee: u64 = 1948;
12631+ assert_eq!(
12632+ check_v2_funding_inputs_sufficient(
12633+ (300_000 - expected_fee - 20) as i64,
12634+ &[
12635+ funding_input_sats(200_000),
12636+ funding_input_sats(100_000),
12637+ ],
12638+ true,
12639+ Some(Weight::from_wu(FUNDING_TRANSACTION_WITNESS_WEIGHT)),
12640+ 2000,
12641+ ).unwrap(),
12642+ expected_fee,
12643+ );
12644+ }
12645+
12646+ // higher fee rate, does not cover
12647+ {
12648+ let expected_fee: u64 = 1948;
12649+ let res = check_v2_funding_inputs_sufficient(
12650+ 298032,
12651+ &[
12652+ funding_input_sats(200_000),
12653+ funding_input_sats(100_000),
12654+ ],
12655+ true,
12656+ Some(Weight::from_wu(FUNDING_TRANSACTION_WITNESS_WEIGHT)),
12657+ 2200,
12658+ );
12659+ assert_eq!(
12660+ format!("{:?}", res.err().unwrap()),
12661+ "Warn : Total input amount 300000 is lower than needed for contribution 298032, considering fees of 2143. Need more inputs.",
12662+ );
12663+ }
12664+
12665+ // barely covers, less fees (no extra weight, no init)
12666+ {
12667+ let expected_fee: u64 = 1076;
12668+ assert_eq!(
12669+ check_v2_funding_inputs_sufficient(
12670+ (300_000 - expected_fee - 20) as i64,
12671+ &[
12672+ funding_input_sats(200_000),
12673+ funding_input_sats(100_000),
12674+ ],
12675+ false,
12676+ None,
12677+ 2000,
12678+ ).unwrap(),
12679+ expected_fee,
12680+ );
12681+ }
12682+ }
12683+
12684+ #[cfg(splicing)]
1254012685 fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
1254112686 use crate::ln::channel::PendingSplice;
1254212687
1254312688 let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
1254412689 (pre_channel_value, post_channel_value)
1254512690 }
1254612691
12547- #[cfg(all(test, splicing) )]
12692+ #[cfg(splicing)]
1254812693 #[test]
1254912694 fn test_splice_compute_post_value() {
1255012695 {
0 commit comments