java-topology/defects/substrate/unit/IsExposedAlgorithm.java

96 lines
3.8 KiB
Java

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<String, String[]> 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<String> validatorSet,
Map<String, String[]> nominators,
Map<String, List<String>> 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<String> 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<String, String[]> nominators = new HashMap<>();
Set<String> validatorSet = new HashSet<>();
Map<String, List<String>> 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();
}
}