Account for fuzz signature weight
What changed, and why it matters
This commit fixes an internal accounting issue that only appears when running the code under a special fuzz-testing build of the secp256k1 cryptography library. In that test-only mode, dummy signatures can be one byte larger than normal, so the code now adds a small buffer to transaction weight estimates. This prevents debug-only assertions from failing and keeps batch-size calculations from being slightly too optimistic during fuzz testing. It does not change behavior in normal production builds and does not introduce a real-world security vulnerability.
No action required. This is a test-only fuzzing correctness fix. Reviewers may verify that the +1 and +2 adjustments correctly correspond to the extra DER byte per signature under secp256k1_fuzz and that no #[cfg(secp256k1_fuzz)] code leaks into release builds.
Security signals we found
Fuzz-only build configuration change
Weight-estimate correction for non-low-S dummy signatures
Prevents debug assertion failures and batch-limit miscounts in fuzz testing
No production code path altered
Evidence from the diff
The patch adjusts witness weight estimates in lightning/src/events/bump_transaction/mod.rs when the secp256k1_fuzz cfg flag is active. The fuzz signer does not low-S normalize dummy ECDSA signatures, so DER-encoded signatures plus sighash can be one byte larger per signature. The change adds +1 weight for anchor witness estimates and +2 weight for each of the HTLC success/timeout witness and input-output-pair weights. These adjusted values are then used for debug weight assertions and for computing how many HTLCs can be batched before hitting the max transaction weight or user-coin weight budget. The change is gated by #[cfg(secp256k1_fuzz)] and therefore only affects fuzz builds.
Changed components
lightning/src/events/bump_transaction/mod.rsanchor input witness weight estimationHTLC success/timeout witness weight estimationHTLC batching weight limit logicInspect captured patch +23 / −6
diff --git a/lightning/src/events/bump_transaction/mod.rs b/lightning/src/events/bump_transaction/mod.rs
index 6a5e994..79f5ace 100644
--- a/lightning/src/events/bump_transaction/mod.rs
+++ b/lightning/src/events/bump_transaction/mod.rs
@@ -331,7 +331,13 @@ impl<B: BroadcasterInterface, C: CoinSelectionSource, SP: SignerProvider, L: Log
let anchor_input_witness_weight = if channel_type.supports_anchor_zero_fee_commitments() {
EMPTY_WITNESS_WEIGHT
} else {
- ANCHOR_INPUT_WITNESS_WEIGHT
+ let weight = ANCHOR_INPUT_WITNESS_WEIGHT;
+ #[cfg(secp256k1_fuzz)]
+ let weight = {
+ // The secp256k1 fuzz signer does not low-S normalize dummy signatures.
+ weight + 1
+ };
+ weight
};
// First, check if the commitment transaction has sufficient fees on its own.
@@ -547,6 +553,18 @@ impl<B: BroadcasterInterface, C: CoinSelectionSource, SP: SignerProvider, L: Log
} else {
panic!("channel type should be either zero-fee HTLCs, or zero-fee commitments");
};
+ // The secp256k1 fuzz signer emits dummy signatures without low-S normalization, so
+ // DER+sighash can be one byte larger for each of the two HTLC signatures.
+ #[cfg(secp256k1_fuzz)]
+ let (htlc_success_witness_weight, htlc_timeout_witness_weight) =
+ (htlc_success_witness_weight + 2, htlc_timeout_witness_weight + 2);
+ let (htlc_success_input_output_pair_weight, htlc_timeout_input_output_pair_weight) = (
+ chan_utils::aggregated_htlc_success_input_output_pair_weight(channel_type),
+ chan_utils::aggregated_htlc_timeout_input_output_pair_weight(channel_type),
+ );
+ #[cfg(secp256k1_fuzz)]
+ let (htlc_success_input_output_pair_weight, htlc_timeout_input_output_pair_weight) =
+ (htlc_success_input_output_pair_weight + 2, htlc_timeout_input_output_pair_weight + 2);
let max_tx_weight = if channel_type.supports_anchor_zero_fee_commitments() {
// Cap the size of transactions claiming `HolderHTLCOutput` in 0FC channels.
@@ -587,9 +605,9 @@ impl<B: BroadcasterInterface, C: CoinSelectionSource, SP: SignerProvider, L: Log
&htlc_descriptors[broadcasted_htlcs..broadcasted_htlcs + batch_size]
{
let input_output_weight = if htlc_descriptor.preimage.is_some() {
- chan_utils::aggregated_htlc_success_input_output_pair_weight(channel_type)
+ htlc_success_input_output_pair_weight
} else {
- chan_utils::aggregated_htlc_timeout_input_output_pair_weight(channel_type)
+ htlc_timeout_input_output_pair_weight
};
if htlc_weight_sum + input_output_weight >= max_tx_weight - USER_COINS_WEIGHT_BUDGET
{
@@ -649,9 +667,8 @@ impl<B: BroadcasterInterface, C: CoinSelectionSource, SP: SignerProvider, L: Log
{
Ok(selection) => selection,
Err(()) => {
- let htlcs_to_remove = USER_COINS_WEIGHT_BUDGET.div_ceil(
- chan_utils::aggregated_htlc_timeout_input_output_pair_weight(channel_type),
- );
+ let htlcs_to_remove =
+ USER_COINS_WEIGHT_BUDGET.div_ceil(htlc_timeout_input_output_pair_weight);
batch_size = batch_size.checked_sub(htlcs_to_remove as usize).ok_or(())?;
if batch_size == 0 {
return Err(());
Why this scored 15/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.