package unit; import java.util.*; /** * substrate-0001: CWE-407 is_exposed_in_era full table scan * * Simulates: * Slow: iter_prefix(era).any(|(validator, exposure)| exposure.others.iter().any(|i| i.who == who)) * Fast: ErasValidatorPrefs::contains_key (validator fast-path) + targeted scan */ public class IsExposedAlgorithm { static class Result { final long ops; final boolean exposed; Result(long ops, boolean exposed) { this.ops = ops; this.exposed = exposed; } } /** * Slow: scan all validators, scan all nominators per validator. * Mirrors: ErasStakers::iter_prefix(era).any(|(v, exp)| v==who || exp.others.iter().any(...)) */ static Result slowIsExposed(String who, String[] validators, Map nominators) { long ops = 0; for (String validator : validators) { ops++; if (validator.equals(who)) return new Result(ops, true); String[] noms = nominators.getOrDefault(validator, new String[0]); for (String nom : noms) { ops++; if (nom.equals(who)) return new Result(ops, true); } } return new Result(ops, false); } /** * Fast: O(1) validator set check, then targeted nominator scan. * Mirrors: ErasValidatorPrefs::contains_key(era, who) fast path * + nominator reverse index lookup. */ static Result fastIsExposed(String who, Set validatorSet, Map nominators, Map> nominatorToValidators) { long ops = 1; // O(1) set lookup if (validatorSet.contains(who)) return new Result(ops, true); // Nominator reverse-index lookup: O(K) where K = validators who nominated List validatorsForNominator = nominatorToValidators.getOrDefault(who, Collections.emptyList()); ops += validatorsForNominator.size(); return new Result(ops, !validatorsForNominator.isEmpty()); } static void bench() { int N_VALIDATORS = 300; int N_NOMINATORS_PER = 256; // Target nominator is the last entry of the last validator (worst case slow path) String who = "nominator_" + (N_VALIDATORS - 1) + "_" + (N_NOMINATORS_PER - 1); String[] validators = new String[N_VALIDATORS]; Map nominators = new HashMap<>(); Set validatorSet = new HashSet<>(); Map> reverseIndex = new HashMap<>(); for (int v = 0; v < N_VALIDATORS; v++) { validators[v] = "validator_" + v; validatorSet.add(validators[v]); String[] noms = new String[N_NOMINATORS_PER]; for (int n = 0; n < N_NOMINATORS_PER; n++) { noms[n] = "nominator_" + v + "_" + n; reverseIndex.computeIfAbsent(noms[n], k -> new ArrayList<>()).add(validators[v]); } nominators.put(validators[v], noms); } Result slow = slowIsExposed(who, validators, nominators); Result fast = fastIsExposed(who, validatorSet, nominators, reverseIndex); System.out.println("is_exposed_in_era N_VALIDATORS=" + N_VALIDATORS + " N_NOMINATORS_PER=" + N_NOMINATORS_PER); System.out.println(" slow ops: " + slow.ops + " exposed=" + slow.exposed); System.out.println(" fast ops: " + fast.ops + " exposed=" + fast.exposed); double speedup = (double) slow.ops / fast.ops; System.out.printf(" speedup: %.1fx%n", speedup); assert slow.exposed == fast.exposed : "result mismatch"; assert slow.ops > fast.ops * 100 : "expected >100x speedup, got " + speedup; System.out.println("1/1 PASS"); } public static void main(String[] args) { bench(); } }