diff --git a/runtime/parachains/src/paras_inherent.rs b/runtime/parachains/src/paras_inherent.rs index db0ed5ec9043..8f34181d1149 100644 --- a/runtime/parachains/src/paras_inherent.rs +++ b/runtime/parachains/src/paras_inherent.rs @@ -672,7 +672,7 @@ where DisputedBitfield::from(bitvec) } -/// Select a random subset +/// Select a random subset, with preference for certain indices. /// /// Adds random items to the set until all candidates /// are tried or the remaining weight is depleted. @@ -682,16 +682,39 @@ where fn random_sel Weight>( rng: &mut rand_chacha::ChaChaRng, selectables: Vec, + mut preferred_indices: Vec, weight_fn: F, weight_limit: Weight, ) -> (Weight, Vec) { if selectables.is_empty() { return (0 as Weight, Vec::new()) } - let mut indices = (0..selectables.len()).into_iter().collect::>(); + // all indices that are not part of the preferred set + let mut indices = (0..selectables.len()) + .into_iter() + .filter(|idx| !preferred_indices.contains(idx)) + .collect::>(); let mut picked_indices = Vec::with_capacity(selectables.len().saturating_sub(1)); let mut weight_acc = 0 as Weight; + + while !preferred_indices.is_empty() { + // randomly pick an index from the preferred set + let pick = rng.gen_range(0..preferred_indices.len()); + // remove the index from the available set of preferred indices + let preferred_idx = preferred_indices.swap_remove(pick); + + // preferred indices originate from outside + if let Some(item) = selectables.get(preferred_idx) { + let updated = weight_acc + weight_fn(item); + if updated > weight_limit { + continue + } + weight_acc = updated; + picked_indices.push(preferred_idx); + } + } + while !indices.is_empty() { // randomly pick an index let pick = rng.gen_range(0..indices.len()); @@ -699,11 +722,12 @@ fn random_sel Weight>( let idx = indices.swap_remove(pick); let item = &selectables[idx]; - weight_acc += weight_fn(item); + let updated = weight_acc + weight_fn(item); - if weight_acc > weight_limit { - break + if updated > weight_limit { + continue } + weight_acc = updated; picked_indices.push(idx); } @@ -743,6 +767,16 @@ fn apply_weight_limit( return total } + // Prefer code upgrades, they tend to be large and hence stand no chance to be picked + // late while maintaining the weight bounds + let preferred_indices = candidates + .iter() + .enumerate() + .filter_map(|(idx, candidate)| { + candidate.candidate.commitments.new_validation_code.as_ref().map(|_code| idx) + }) + .collect::>(); + // There is weight remaining to be consumed by a subset of candidates // which are going to be picked now. if let Some(remaining_weight) = remaining_weight.checked_sub(total_bitfields_weight) { @@ -750,6 +784,7 @@ fn apply_weight_limit( random_sel::::Hash>, _>( rng, candidates.clone(), + preferred_indices, |c| backed_candidate_weight::(c), remaining_weight, ); @@ -772,6 +807,7 @@ fn apply_weight_limit( let (total, indices) = random_sel::( rng, bitfields.clone(), + vec![], |_| <::WeightInfo as WeightInfo>::enter_bitfields(), remaining_weight, ); @@ -928,6 +964,10 @@ fn sanitize_backed_candidates(parent_hash: T::Hash) -> [u8; 32] { const CANDIDATE_SEED_SUBJECT: [u8; 32] = *b"candidate-seed-selection-subject"; let vrf_random = CurrentBlockRandomness::::random(&CANDIDATE_SEED_SUBJECT[..]).0; @@ -1010,6 +1050,7 @@ fn limit_disputes( let (acc_remote_disputes_weight, indices) = random_sel::( rng, d, + vec![], |v| <::WeightInfo as WeightInfo>::enter_variable_disputes(*v), remaining_weight, );