Remove NegotiatedTxInput::weight
What changed, and why it matters
This commit removes a stored 'weight' field from a transaction-input data structure and instead calculates the weight on the fly when building a transaction. It is a code-cleanup and data-format simplification. There is no direct evidence in the commit that this fixes a security vulnerability, but any change to fee/weight calculations in Bitcoin-related code deserves a careful look because mistakes there can affect transaction validity or fees.
Review the new weight computation for arithmetic correctness and overflow handling. Verify that `satisfaction_weight()` returns the same witness-weight estimate previously captured in `NegotiatedTxInput::weight`, and that dropping the persisted field does not break backward compatibility or downgrade paths. Treat as a normal refactor unless additional context shows a bug was fixed.
Security signals we found
Change to Bitcoin transaction weight/fee calculation path
Serialization format change for a persisted Lightning transaction input structure
Removal of a cached weight value in favor of on-the-fly computation
No explicit security framing, CVE, or advisory referenced in commit
Evidence from the diff
The patch drops the weight field from NegotiatedTxInput and its TLV serialization. The input weight is now recomputed at ConstructedTransaction creation time from BASE_INPUT_WEIGHT plus the sum of satisfaction_weight() values. The weight() method on ConstructedTransaction becomes a private helper that takes the precomputed satisfaction weight as an argument. Serialization field numbers are renumbered (prev_output moves from field 7 to field 5). Test code is updated to stop providing the removed field.
Changed components
lightning/src/ln/interactivetxs.rsNegotiatedTxInput struct and serializationConstructedTransaction::weight calculationInteractiveTxInput::into_negotiated_inputInspect captured patch +14 / −12
diff --git a/lightning/src/ln/interactivetxs.rs b/lightning/src/ln/interactivetxs.rs
index 7336c0c..b2a7ad8 100644
--- a/lightning/src/ln/interactivetxs.rs
+++ b/lightning/src/ln/interactivetxs.rs
@@ -215,8 +215,6 @@ pub(crate) struct ConstructedTransaction {
pub(crate) struct NegotiatedTxInput {
serial_id: SerialId,
txin: TxIn,
- // The weight of the input including an estimate of its witness weight.
- weight: Weight,
prev_output: TxOut,
}
@@ -233,8 +231,7 @@ impl NegotiatedTxInput {
impl_writeable_tlv_based!(NegotiatedTxInput, {
(1, serial_id, required),
(3, txin, required),
- (5, weight, required),
- (7, prev_output, required),
+ (5, prev_output, required),
});
impl_writeable_tlv_based!(ConstructedTransaction, {
@@ -278,6 +275,12 @@ impl ConstructedTransaction {
let remote_inputs_value_satoshis = context.remote_inputs_value();
let remote_outputs_value_satoshis = context.remote_outputs_value();
+
+ let satisfaction_weight =
+ Weight::from_wu(context.inputs.iter().fold(0u64, |value, (_, input)| {
+ value.saturating_add(input.satisfaction_weight().to_wu())
+ }));
+
let mut inputs: Vec<NegotiatedTxInput> =
context.inputs.into_values().map(|tx_input| tx_input.into_negotiated_input()).collect();
let mut outputs: Vec<InteractiveTxOutput> = context.outputs.into_values().collect();
@@ -310,17 +313,18 @@ impl ConstructedTransaction {
shared_input_index,
};
- if constructed_tx.weight().to_wu() > MAX_STANDARD_TX_WEIGHT as u64 {
+ let tx_weight = constructed_tx.weight(satisfaction_weight);
+ if tx_weight > Weight::from_wu(MAX_STANDARD_TX_WEIGHT as u64) {
return Err(AbortReason::TransactionTooLarge);
}
Ok(constructed_tx)
}
- pub fn weight(&self) -> Weight {
- let inputs_weight = self.inputs.iter().fold(Weight::from_wu(0), |weight, input| {
- weight.checked_add(input.weight).unwrap_or(Weight::MAX)
- });
+ fn weight(&self, satisfaction_weight: Weight) -> Weight {
+ let inputs_weight = Weight::from_wu(self.inputs.len() as u64 * BASE_INPUT_WEIGHT)
+ .checked_add(satisfaction_weight)
+ .unwrap_or(Weight::MAX);
let outputs_weight = self.outputs.iter().fold(Weight::from_wu(0), |weight, output| {
weight.checked_add(get_output_weight(output.script_pubkey())).unwrap_or(Weight::MAX)
});
@@ -1852,9 +1856,8 @@ impl InteractiveTxInput {
}
fn into_negotiated_input(self) -> NegotiatedTxInput {
- let weight = Weight::from_wu(BASE_INPUT_WEIGHT) + self.input.satisfaction_weight();
let (txin, prev_output) = self.input.into_tx_in_with_prev_output();
- NegotiatedTxInput { serial_id: self.serial_id, txin, weight, prev_output }
+ NegotiatedTxInput { serial_id: self.serial_id, txin, prev_output }
}
}
@@ -3328,7 +3331,6 @@ mod tests {
NegotiatedTxInput {
serial_id: idx as u64, // even values will be holder (initiator in this test)
txin,
- weight: Weight::from_wu(0), // N/A for test
prev_output,
}
})
Why this scored 23/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.