diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index ff4005024e2..e70d935778c 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -116,19 +116,27 @@ pub const HTLC_SUCCESS_INPUT_P2A_ANCHOR_WITNESS_WEIGHT: u64 = 324; /// The size of the 2-of-2 multisig script const MULTISIG_SCRIPT_SIZE: u64 = 1 + // OP_2 1 + // data len - 33 + // pubkey1 + crate::sign::COMPRESSED_PUBLIC_KEY_SIZE as u64 + // pubkey1 1 + // data len - 33 + // pubkey2 + crate::sign::COMPRESSED_PUBLIC_KEY_SIZE as u64 + // pubkey2 1 + // OP_2 1; // OP_CHECKMULTISIG -/// The weight of a funding transaction input (2-of-2 P2WSH) -/// See https://github.com/lightning/bolts/blob/master/03-transactions.md#expected-weight-of-the-commitment-transaction + +/// The weight of a funding transaction input (2-of-2 P2WSH). +/// +/// Unlike in the [spec], 72 WU is used for the max signature size since 73 WU signatures are +/// non-standard. +/// +/// Note: If you have the `grind_signatures` feature enabled, this will be at least 1 byte +/// shorter. +/// +/// [spec]: https://github.com/lightning/bolts/blob/master/03-transactions.md#expected-weight-of-the-commitment-transaction pub const FUNDING_TRANSACTION_WITNESS_WEIGHT: u64 = 1 + // number_of_witness_elements 1 + // nil_len 1 + // sig len - 73 + // sig1 + crate::sign::MAX_STANDARD_SIGNATURE_SIZE as u64 + // sig1 1 + // sig len - 73 + // sig2 + crate::sign::MAX_STANDARD_SIGNATURE_SIZE as u64 + // sig2 1 + // witness_script_length MULTISIG_SCRIPT_SIZE; diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 75905dba1cd..eab70559417 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -6547,6 +6547,11 @@ fn estimate_v2_funding_transaction_fee( .saturating_add(BASE_INPUT_WEIGHT) .saturating_add(EMPTY_SCRIPT_SIG_WEIGHT) .saturating_add(FUNDING_TRANSACTION_WITNESS_WEIGHT); + #[cfg(feature = "grind_signatures")] + { + // Guarantees a low R signature + weight -= 1; + } } } @@ -17397,19 +17402,19 @@ mod tests { // 2 inputs, initiator, 2000 sat/kw feerate assert_eq!( estimate_v2_funding_transaction_fee(&two_inputs, &[], true, false, 2000), - 1520, + if cfg!(feature = "grind_signatures") { 1512 } else { 1516 }, ); // higher feerate assert_eq!( estimate_v2_funding_transaction_fee(&two_inputs, &[], true, false, 3000), - 2280, + if cfg!(feature = "grind_signatures") { 2268 } else { 2274 }, ); // only 1 input assert_eq!( estimate_v2_funding_transaction_fee(&one_input, &[], true, false, 2000), - 974, + if cfg!(feature = "grind_signatures") { 970 } else { 972 }, ); // 0 inputs @@ -17427,13 +17432,13 @@ mod tests { // splice initiator assert_eq!( estimate_v2_funding_transaction_fee(&one_input, &[], true, true, 2000), - 1746, + if cfg!(feature = "grind_signatures") { 1736 } else { 1740 }, ); // splice acceptor assert_eq!( estimate_v2_funding_transaction_fee(&one_input, &[], false, true, 2000), - 546, + if cfg!(feature = "grind_signatures") { 542 } else { 544 }, ); } @@ -17457,40 +17462,46 @@ mod tests { use crate::ln::channel::check_v2_funding_inputs_sufficient; // positive case, inputs well over intended contribution - assert_eq!( - check_v2_funding_inputs_sufficient( - 220_000, - &[ - funding_input_sats(200_000), - funding_input_sats(100_000), - ], - true, - true, - 2000, - ).unwrap(), - 2292, - ); + { + let expected_fee = if cfg!(feature = "grind_signatures") { 2278 } else { 2284 }; + assert_eq!( + check_v2_funding_inputs_sufficient( + 220_000, + &[ + funding_input_sats(200_000), + funding_input_sats(100_000), + ], + true, + true, + 2000, + ).unwrap(), + expected_fee, + ); + } // negative case, inputs clearly insufficient { - let res = check_v2_funding_inputs_sufficient( - 220_000, - &[ - funding_input_sats(100_000), - ], - true, - true, - 2000, - ); + let expected_fee = if cfg!(feature = "grind_signatures") { 1736 } else { 1740 }; assert_eq!( - res.err().unwrap(), - "Total input amount 100000 is lower than needed for contribution 220000, considering fees of 1746. Need more inputs.", + check_v2_funding_inputs_sufficient( + 220_000, + &[ + funding_input_sats(100_000), + ], + true, + true, + 2000, + ), + Err(format!( + "Total input amount 100000 is lower than needed for contribution 220000, considering fees of {}. Need more inputs.", + expected_fee, + )), ); } // barely covers { - let expected_fee: u64 = 2292; + let expected_fee = if cfg!(feature = "grind_signatures") { 2278 } else { 2284 }; assert_eq!( check_v2_funding_inputs_sufficient( (300_000 - expected_fee - 20) as i64, @@ -17508,25 +17519,28 @@ mod tests { // higher fee rate, does not cover { - let res = check_v2_funding_inputs_sufficient( - 298032, - &[ - funding_input_sats(200_000), - funding_input_sats(100_000), - ], - true, - true, - 2200, - ); + let expected_fee = if cfg!(feature = "grind_signatures") { 2506 } else { 2513 }; assert_eq!( - res.err().unwrap(), - "Total input amount 300000 is lower than needed for contribution 298032, considering fees of 2522. Need more inputs.", + check_v2_funding_inputs_sufficient( + 298032, + &[ + funding_input_sats(200_000), + funding_input_sats(100_000), + ], + true, + true, + 2200, + ), + Err(format!( + "Total input amount 300000 is lower than needed for contribution 298032, considering fees of {}. Need more inputs.", + expected_fee + )), ); } - // barely covers, less fees (no extra weight, no init) + // barely covers, less fees (no extra weight, not initiator) { - let expected_fee: u64 = 1092; + let expected_fee = if cfg!(feature = "grind_signatures") { 1084 } else { 1088 }; assert_eq!( check_v2_funding_inputs_sufficient( (300_000 - expected_fee - 20) as i64, diff --git a/lightning/src/ln/funding.rs b/lightning/src/ln/funding.rs index 3cabb8201a9..f80b2b6daea 100644 --- a/lightning/src/ln/funding.rs +++ b/lightning/src/ln/funding.rs @@ -143,7 +143,13 @@ impl FundingTxInput { /// [`TxIn::sequence`]: bitcoin::TxIn::sequence /// [`set_sequence`]: Self::set_sequence pub fn new_p2wpkh(prevtx: Transaction, vout: u32) -> Result { - let witness_weight = Weight::from_wu(P2WPKH_WITNESS_WEIGHT); + let witness_weight = Weight::from_wu(P2WPKH_WITNESS_WEIGHT) + - if cfg!(feature = "grind_signatures") { + // Guarantees a low R signature + Weight::from_wu(1) + } else { + Weight::ZERO + }; FundingTxInput::new(prevtx, vout, witness_weight, Script::is_p2wpkh) } @@ -224,4 +230,9 @@ impl FundingTxInput { pub fn set_sequence(&mut self, sequence: Sequence) { self.sequence = sequence; } + + /// Converts the [`FundingTxInput`] into a [`Utxo`] for coin selection. + pub fn into_utxo(self) -> Utxo { + self.utxo + } } diff --git a/lightning/src/ln/interactivetxs.rs b/lightning/src/ln/interactivetxs.rs index 97047eb3a0d..4dceff93ad9 100644 --- a/lightning/src/ln/interactivetxs.rs +++ b/lightning/src/ln/interactivetxs.rs @@ -29,6 +29,7 @@ use bitcoin::{ use crate::chain::chaininterface::fee_for_weight; use crate::ln::chan_utils::{ BASE_INPUT_WEIGHT, EMPTY_SCRIPT_SIG_WEIGHT, FUNDING_TRANSACTION_WITNESS_WEIGHT, + SEGWIT_MARKER_FLAG_WEIGHT, }; use crate::ln::channel::{FundingNegotiationContext, TOTAL_BITCOIN_SUPPLY_SATOSHIS}; use crate::ln::funding::FundingTxInput; @@ -257,16 +258,20 @@ impl_writeable_tlv_based!(ConstructedTransaction, { (11, shared_output_index, required), }); +/// The percent tolerance given to the remote when estimating if they paid enough fees. +const REMOTE_FEE_TOLERANCE_PERCENT: u64 = 95; + impl ConstructedTransaction { fn new(context: NegotiationContext) -> Result { let remote_inputs_value = context.remote_inputs_value(); let remote_outputs_value = context.remote_outputs_value(); let remote_weight_contributed = context.remote_weight_contributed(); - let satisfaction_weight = - Weight::from_wu(context.inputs.iter().fold(0u64, |value, (_, input)| { - value.saturating_add(input.satisfaction_weight().to_wu()) - })); + let expected_witness_weight = context.inputs.iter().fold(0u64, |value, (_, input)| { + value + .saturating_add(input.satisfaction_weight().to_wu()) + .saturating_sub(EMPTY_SCRIPT_SIG_WEIGHT) + }); let lock_time = context.tx_locktime; @@ -315,8 +320,10 @@ impl ConstructedTransaction { // - the peer's paid feerate does not meet or exceed the agreed feerate (based on the minimum fee). let remote_fees_contributed = remote_inputs_value.saturating_sub(remote_outputs_value); - let required_remote_contribution_fee = - fee_for_weight(context.feerate_sat_per_kw, remote_weight_contributed); + let required_remote_contribution_fee = fee_for_weight( + (context.feerate_sat_per_kw as u64 * REMOTE_FEE_TOLERANCE_PERCENT / 100) as u32, + remote_weight_contributed, + ); if remote_fees_contributed < required_remote_contribution_fee { return Err(AbortReason::InsufficientFees); } @@ -337,8 +344,13 @@ impl ConstructedTransaction { return Err(AbortReason::MissingFundingOutput); } - let tx_weight = tx.tx.weight().checked_add(satisfaction_weight).unwrap_or(Weight::MAX); - if tx_weight > Weight::from_wu(MAX_STANDARD_TX_WEIGHT as u64) { + let tx_weight = tx + .tx + .weight() + .to_wu() + .saturating_add(SEGWIT_MARKER_FLAG_WEIGHT) + .saturating_add(expected_witness_weight); + if tx_weight > MAX_STANDARD_TX_WEIGHT as u64 { return Err(AbortReason::TransactionTooLarge); } @@ -1706,7 +1718,15 @@ impl InputOwned { InputOwned::Single(single) => single.satisfaction_weight, // TODO(taproot): Needs to consider different weights based on channel type InputOwned::Shared(_) => { - Weight::from_wu(EMPTY_SCRIPT_SIG_WEIGHT + FUNDING_TRANSACTION_WITNESS_WEIGHT) + let mut weight = 0; + weight += EMPTY_SCRIPT_SIG_WEIGHT + FUNDING_TRANSACTION_WITNESS_WEIGHT; + #[cfg(feature = "grind_signatures")] + { + // Guarantees a low R signature + weight -= 1; + } + + Weight::from_wu(weight) }, } } @@ -2314,6 +2334,11 @@ pub(super) fn calculate_change_output_value( weight = weight.saturating_add(BASE_INPUT_WEIGHT); weight = weight.saturating_add(EMPTY_SCRIPT_SIG_WEIGHT); weight = weight.saturating_add(FUNDING_TRANSACTION_WITNESS_WEIGHT); + #[cfg(feature = "grind_signatures")] + { + // Guarantees a low R signature + weight -= 1; + } } } @@ -2366,7 +2391,7 @@ mod tests { use super::{ get_output_weight, ConstructedTransaction, InteractiveTxSigningSession, TxInMetadata, P2TR_INPUT_WEIGHT_LOWER_BOUND, P2WPKH_INPUT_WEIGHT_LOWER_BOUND, - P2WSH_INPUT_WEIGHT_LOWER_BOUND, TX_COMMON_FIELDS_WEIGHT, + P2WSH_INPUT_WEIGHT_LOWER_BOUND, REMOTE_FEE_TOLERANCE_PERCENT, TX_COMMON_FIELDS_WEIGHT, }; const TEST_FEERATE_SATS_PER_KW: u32 = FEERATE_FLOOR_SATS_PER_KW * 10; @@ -2831,7 +2856,7 @@ mod tests { let outputs_weight = get_output_weight(&generate_p2wsh_script_pubkey()).to_wu(); let amount_adjusted_with_p2wpkh_fee = 1_000_000 - fee_for_weight( - TEST_FEERATE_SATS_PER_KW, + (TEST_FEERATE_SATS_PER_KW as u64 * REMOTE_FEE_TOLERANCE_PERCENT / 100) as u32, P2WPKH_INPUT_WEIGHT_LOWER_BOUND + TX_COMMON_FIELDS_WEIGHT + outputs_weight, ); do_test_interactive_tx_constructor(TestSession { @@ -2867,7 +2892,7 @@ mod tests { }); let amount_adjusted_with_p2wsh_fee = 1_000_000 - fee_for_weight( - TEST_FEERATE_SATS_PER_KW, + (TEST_FEERATE_SATS_PER_KW as u64 * REMOTE_FEE_TOLERANCE_PERCENT / 100) as u32, P2WSH_INPUT_WEIGHT_LOWER_BOUND + TX_COMMON_FIELDS_WEIGHT + outputs_weight, ); do_test_interactive_tx_constructor(TestSession { @@ -2903,7 +2928,7 @@ mod tests { }); let amount_adjusted_with_p2tr_fee = 1_000_000 - fee_for_weight( - TEST_FEERATE_SATS_PER_KW, + (TEST_FEERATE_SATS_PER_KW as u64 * REMOTE_FEE_TOLERANCE_PERCENT / 100) as u32, P2TR_INPUT_WEIGHT_LOWER_BOUND + TX_COMMON_FIELDS_WEIGHT + outputs_weight, ); do_test_interactive_tx_constructor(TestSession { @@ -3353,21 +3378,23 @@ mod tests { FundingTxInput::new_p2wpkh(prevtx, 0).unwrap() }) .collect(); - let our_contributed = 110_000; let txout = TxOut { value: Amount::from_sat(10_000), script_pubkey: ScriptBuf::new() }; let outputs = vec![txout]; let funding_feerate_sat_per_1000_weight = 3000; - let total_inputs: u64 = input_prevouts.iter().map(|o| o.value.to_sat()).sum(); - let total_outputs: u64 = outputs.iter().map(|o| o.value.to_sat()).sum(); - let gross_change = total_inputs - total_outputs - our_contributed; - let fees = 1746; - let common_fees = 234; + let total_inputs: Amount = input_prevouts.iter().map(|o| o.value).sum(); + let total_outputs: Amount = outputs.iter().map(|o| o.value).sum(); + let fees = if cfg!(feature = "grind_signatures") { + Amount::from_sat(1734) + } else { + Amount::from_sat(1740) + }; + let common_fees = Amount::from_sat(234); // There is leftover for change let context = FundingNegotiationContext { is_initiator: true, - our_funding_contribution: SignedAmount::from_sat(our_contributed as i64), + our_funding_contribution: SignedAmount::from_sat(110_000), funding_tx_locktime: AbsoluteLockTime::ZERO, funding_feerate_sat_per_1000_weight, shared_funding_input: None, @@ -3375,16 +3402,18 @@ mod tests { our_funding_outputs: outputs, change_script: None, }; + let gross_change = + total_inputs - total_outputs - context.our_funding_contribution.to_unsigned().unwrap(); assert_eq!( calculate_change_output_value(&context, false, &ScriptBuf::new(), 300), - Ok(Some(gross_change - fees - common_fees)), + Ok(Some((gross_change - fees - common_fees).to_sat())), ); // There is leftover for change, without common fees let context = FundingNegotiationContext { is_initiator: false, ..context }; assert_eq!( calculate_change_output_value(&context, false, &ScriptBuf::new(), 300), - Ok(Some(gross_change - fees)), + Ok(Some((gross_change - fees).to_sat())), ); // Insufficient inputs, no leftover @@ -3415,21 +3444,25 @@ mod tests { our_funding_contribution: SignedAmount::from_sat(117_992), ..context }; + let gross_change = + total_inputs - total_outputs - context.our_funding_contribution.to_unsigned().unwrap(); assert_eq!( calculate_change_output_value(&context, false, &ScriptBuf::new(), 100), - Ok(Some(262)), + Ok(Some((gross_change - fees).to_sat())), ); // Larger fee, smaller change let context = FundingNegotiationContext { is_initiator: true, - our_funding_contribution: SignedAmount::from_sat(our_contributed as i64), + our_funding_contribution: SignedAmount::from_sat(110_000), funding_feerate_sat_per_1000_weight: funding_feerate_sat_per_1000_weight * 3, ..context }; + let gross_change = + total_inputs - total_outputs - context.our_funding_contribution.to_unsigned().unwrap(); assert_eq!( calculate_change_output_value(&context, false, &ScriptBuf::new(), 300), - Ok(Some(4060)), + Ok(Some((gross_change - fees * 3 - common_fees * 3).to_sat())), ); } diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index 1d771d22783..77076a408e4 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -81,6 +81,11 @@ pub mod ecdsa; pub mod taproot; pub mod tx_builder; +pub(crate) const COMPRESSED_PUBLIC_KEY_SIZE: usize = bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE; + +pub(crate) const MAX_STANDARD_SIGNATURE_SIZE: usize = + bitcoin::secp256k1::constants::MAX_SIGNATURE_SIZE; + /// Information about a spendable output to a P2WSH script. /// /// See [`SpendableOutputDescriptor::DelayedPaymentOutput`] for more details on how to spend this. @@ -112,12 +117,15 @@ pub struct DelayedPaymentOutputDescriptor { impl DelayedPaymentOutputDescriptor { /// The maximum length a well-formed witness spending one of these should have. - /// Note: If you have the grind_signatures feature enabled, this will be at least 1 byte + /// + /// Note: If you have the `grind_signatures` feature enabled, this will be at least 1 byte /// shorter. - // Calculated as 1 byte length + 73 byte signature, 1 byte empty vec push, 1 byte length plus - // redeemscript push length. - pub const MAX_WITNESS_LENGTH: u64 = - 1 + 73 + 1 + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH as u64 + 1; + pub const MAX_WITNESS_LENGTH: u64 = (1 /* witness items */ + + 1 /* sig push */ + + MAX_STANDARD_SIGNATURE_SIZE + + 1 /* empty vec push */ + + 1 /* redeemscript push */ + + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH) as u64; } impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, { @@ -131,15 +139,18 @@ impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, { (13, channel_transaction_parameters, (option: ReadableArgs, Some(channel_value_satoshis.0.unwrap()))), }); -pub(crate) const P2WPKH_WITNESS_WEIGHT: u64 = 1 /* num stack items */ + - 1 /* sig length */ + - 73 /* sig including sighash flag */ + - 1 /* pubkey length */ + - 33 /* pubkey */; +/// Witness weight for satisfying a P2WPKH spend. +pub(crate) const P2WPKH_WITNESS_WEIGHT: u64 = (1 /* witness items */ + + 1 /* sig push */ + + MAX_STANDARD_SIGNATURE_SIZE + + 1 /* pubkey push */ + + COMPRESSED_PUBLIC_KEY_SIZE) as u64; -/// Witness weight for satisying a P2TR key-path spend. -pub(crate) const P2TR_KEY_PATH_WITNESS_WEIGHT: u64 = 1 /* witness items */ - + 1 /* schnorr sig len */ + 64 /* schnorr sig */; +/// Witness weight for satisfying a P2TR key-path spend. +pub(crate) const P2TR_KEY_PATH_WITNESS_WEIGHT: u64 = (1 /* witness items */ + + 1 /* sig push */ + + bitcoin::secp256k1::constants::SCHNORR_SIGNATURE_SIZE) + as u64; /// If a [`KeysManager`] is built with [`KeysManager::new`] with `v2_remote_key_derivation` set /// (and for all channels after they've been spliced), the script which we receive funds to on-chain @@ -188,14 +199,21 @@ impl StaticPaymentOutputDescriptor { } /// The maximum length a well-formed witness spending one of these should have. - /// Note: If you have the grind_signatures feature enabled, this will be at least 1 byte + /// + /// Note: If you have the `grind_signatures` feature enabled, this will be at least 1 byte /// shorter. pub fn max_witness_length(&self) -> u64 { if self.needs_csv_1_for_spend() { - let witness_script_weight = 1 /* pubkey push */ + 33 /* pubkey */ + - 1 /* OP_CHECKSIGVERIFY */ + 1 /* OP_1 */ + 1 /* OP_CHECKSEQUENCEVERIFY */; - 1 /* num witness items */ + 1 /* sig push */ + 73 /* sig including sighash flag */ + - 1 /* witness script push */ + witness_script_weight + let witness_script_weight = 1 /* pubkey push */ + + COMPRESSED_PUBLIC_KEY_SIZE + + 1 /* OP_CHECKSIGVERIFY */ + + 1 /* OP_1 */ + + 1 /* OP_CHECKSEQUENCEVERIFY */; + (1 /* num witness items */ + + 1 /* sig push */ + + MAX_STANDARD_SIGNATURE_SIZE + + 1 /* witness script push */ + + witness_script_weight) as u64 } else { P2WPKH_WITNESS_WEIGHT } @@ -511,7 +529,7 @@ impl SpendableOutputDescriptor { sequence: Sequence::ZERO, witness: Witness::new(), }); - witness_weight += 1 + 73 + 34; + witness_weight += P2WPKH_WITNESS_WEIGHT; #[cfg(feature = "grind_signatures")] { // Guarantees a low R signature diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index ad8ea224205..e195b481362 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -2226,10 +2226,16 @@ impl TestWalletSource { utxo.output.value, EcdsaSighashType::All, )?; + #[cfg(not(feature = "grind_signatures"))] let signature = self.secp.sign_ecdsa( &secp256k1::Message::from_digest(sighash.to_byte_array()), &self.secret_key, ); + #[cfg(feature = "grind_signatures")] + let signature = self.secp.sign_ecdsa_low_r( + &secp256k1::Message::from_digest(sighash.to_byte_array()), + &self.secret_key, + ); let bitcoin_sig = bitcoin::ecdsa::Signature { signature, sighash_type: EcdsaSighashType::All }; tx.input[i].witness =