Fix MAX_STANDARD_TX_WEIGHT check
What changed, and why it matters
This commit fixes how rust-lightning estimates the final size of a Bitcoin transaction during the interactive-tx protocol used to build funding transactions for Lightning channels. The old estimate double-counted a fixed empty-signature weight and forgot to add the SegWit marker/flag bytes, so it could wrongly allow a transaction that is actually too large to be accepted by the Bitcoin network. Such a transaction would not propagate through standard Bitcoin nodes, potentially causing a channel-funding attempt to fail or get stuck.
Review whether any released versions shipped with the incorrect weight estimate and assess whether an oversized funding tx could be induced by a peer. If so, consider a security advisory and patch release. Add regression tests covering transactions near MAX_STANDARD_TX_WEIGHT with multiple SegWit inputs.
Security signals we found
Denial-of-service / protocol-stall risk from non-standard oversized transaction
Incorrect transaction-weight estimation in consensus-adjacent code
Fixes mempool-standard policy check (MAX_STANDARD_TX_WEIGHT)
Interactive-tx construction protocol correctness
Evidence from the diff
In lightning/src/ln/interactivetxs.rs the weight estimation for a constructed transaction is corrected. Previously the code added each input’s full satisfaction_weight() to the unsigned transaction weight. satisfaction_weight() includes EMPTY_SCRIPT_SIG_WEIGHT, so adding it per-input on top of an already script-sig-aware base count double-counted that constant. The patch subtracts EMPTY_SCRIPT_SIG_WEIGHT per input and separately adds SEGWIT_MARKER_FLAG_WEIGHT. The corrected total is then compared against MAX_STANDARD_TX_WEIGHT. This is a protocol-correctness fix: an oversized funding transaction would be non-standard and likely rejected by Bitcoin mempool policy.
Changed components
lightning/src/ln/interactivetxs.rsConstructedTransaction::build_transaction or equivalent interactive-tx construction pathLightning channel funding transaction constructionInspect captured patch +13 / −6
diff --git a/lightning/src/ln/interactivetxs.rs b/lightning/src/ln/interactivetxs.rs
index e729f45..4dceff9 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;
@@ -266,10 +267,11 @@ impl ConstructedTransaction {
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;
@@ -342,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);
}
Why this scored 59/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.