Randomize order of inputs from `OutputSweeper`
What changed, and why it matters
This commit makes a small privacy improvement to how the OutputSweeper selects which funds to spend. Previously, the order of inputs in a sweep transaction was predictable. Now the code puts them into a hash set and back into a list, which shuffles their order. There is no security bug being fixed here; it is a hardening/privacy tweak.
No security action required. Reviewers may note that HashSet iteration order is not cryptographically random and is deterministic per process, so the privacy gain is marginal as the commit message itself states. If stronger randomization is desired later, an EntropySource-backed shuffle should be considered.
Security signals we found
Privacy hardening: input order randomization
No memory safety, cryptographic, or consensus issue present in diff
No bug fix or vulnerability remediation evident
Evidence from the diff
The change converts a Vec collected from filtered SpendableOutputDescriptor references into a HashSet, then collects it back into a Vec before calling spend_outputs. The stated goal is to randomize input ordering for privacy, relying on HashSet iteration order rather than adding a rand dependency or EntropySource. The change also incidentally deduplicates descriptors. No vulnerability is patched.
Changed components
lightning/src/util/sweep.rsOutputSweeper::construct_maybe_spendable_outputs_handlerInspect captured patch +7 / −1
diff --git a/lightning/src/util/sweep.rs b/lightning/src/util/sweep.rs
index b72dddb..6268a05 100644
--- a/lightning/src/util/sweep.rs
+++ b/lightning/src/util/sweep.rs
@@ -574,13 +574,19 @@ where
let cur_height = sweeper_state.best_block.height;
let cur_hash = sweeper_state.best_block.block_hash;
- let respend_descriptors: Vec<&SpendableOutputDescriptor> = sweeper_state
+ let respend_descriptors_set: HashSet<&SpendableOutputDescriptor> = sweeper_state
.outputs
.iter()
.filter(|o| filter_fn(*o, cur_height))
.map(|o| &o.descriptor)
.collect();
+ // we first collect into a set to avoid duplicates and to "randomize" the order
+ // in which outputs are spent. Then we collect into a vec as that is what
+ // `spend_outputs` requires.
+ let respend_descriptors: Vec<&SpendableOutputDescriptor> =
+ respend_descriptors_set.into_iter().collect();
+
// Generate the spending transaction and broadcast it.
if !respend_descriptors.is_empty() {
let spending_tx = self
Why this scored 19/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.