When starting a node with pruning enabled (e.g. -prune=550) on a node with full chain history or pruning many files at once, FindFilesToPrune and FindFilesToPruneManual previously called PruneOneBlockFile once per file.
Because PruneOneBlockFile iterated over the entire m_block_index for each file, this was an O(M * N) operation (M = files to prune, N = total blocks). When pruning a full chain (~5,770 files and ~972k blocks), this triggered ~5.6 billion iterations and froze the node running on a Pi 5 for ~10 minutes on startup while holding cs_main.
This PR replaces PruneOneBlockFile with PruneBlockFiles(const std::set<int>&), which collects all candidate files and prunes them in a single O(M + N) pass over m_block_index using a compact dynamic bitfield (std::vector<bool>). Also skips entries without block or undo data to avoid unneeded checks and prevent file 0 pruning from dirtying header-only or already-pruned index entries. The new signature is analogous to UnlinkPrunedFiles({file_number});.
Benchmarked with ~972k blocks and 5,770 files to prune:
- x86_64 (i7-8665U @ 1.90 GHz): 75.1s -> 14.7ms (~5,100x speedup)
- ARM64 (Raspberry Pi 5): 606.0s -> 26.3ms (>3,500x speedup)
Add unit test blockmanager_prune_block_files_batch to verify batch pruning, empty set no-ops, and clean dirty-set behavior. Update caller tests in blockmanager_tests and rescan_tests.