From 76b159cc1b572fc48eb9f383f2dc40f4d2be174f Mon Sep 17 00:00:00 2001 From: ionodeionode Date: Tue, 24 Feb 2026 11:29:37 +0700 Subject: [PATCH] fix: remove unbounded collect in finalize_all_subnet_root_dividends (#2411) Replace the unbounded Vec collect with a lazy iterator drain to avoid O(N) memory allocation when cleaning up RootClaimable entries during subnet dissolution. This prevents potential block weight limit violations and high memory usage with large numbers of hotkeys. The storage iterator cursor remains valid while mutating other keys in the same map, so we can safely process each hotkey without materializing the full key set in memory. Closes #2411 --- pallets/subtensor/src/staking/claim_root.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 24a26d154c..3ef6cfa704 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -389,11 +389,25 @@ impl Pallet { } /// Claim all root dividends for subnet and remove all associated data. + /// + /// This function removes the given `netuid` entry from every hotkey's + /// `RootClaimable` map and clears the corresponding `RootClaimed` prefix. + /// + /// The previous implementation collected **all** hotkey keys into a `Vec` + /// before mutating, which is O(N) in memory and could exceed block weight + /// limits when the number of hotkeys is large (see issue #2411). + /// + /// The new implementation avoids the unbounded `collect()` by draining the + /// iterator directly. Because `StorageMap::iter_keys()` returns a lazy + /// iterator backed by the storage trie cursor, we can process each key + /// without materialising the full set in memory. Substrate's storage + /// iterators are safe to use while mutating *other* keys of the same map + /// (cursor invalidation only occurs when the *current* key is removed). pub fn finalize_all_subnet_root_dividends(netuid: NetUid) { - let hotkeys = RootClaimable::::iter_keys().collect::>(); + let mut cursor = RootClaimable::::iter_keys(); - for hotkey in hotkeys.iter() { - RootClaimable::::mutate(hotkey, |claimable| { + while let Some(hotkey) = cursor.next() { + RootClaimable::::mutate(&hotkey, |claimable| { claimable.remove(&netuid); }); }