Early rate$2,400 of senior audit time for $500. Early members keep the rate as it climbs.$2,400 of senior audit time for $500See how →
F-2026-0004·sybil

Juror selection ignores stake magnitude, so splitting stake improves capture odds

Mitigatedescrowarbitrationdispute-resolution
TL;DR

Panel selection weights jurors by tier only, ignoring stake above the tier minimum. Capital is better spent as many minimum-stake identities, letting a party buy a controlling share of the panel deciding their own dispute.

Severity
HIGH
Impact
HIGH
Likelihood
MEDIUM
Method
MManual review
CAT.
Complexity
MEDIUM
Exploitability
MEDIUM
02Section · Description

Description

Panel selection is intended to be stake-weighted, so that selection probability tracks economic exposure: P(juror_i) = stake_i x weight_i / sum(stake_j x weight_j).

The implementation weights by tier alone:

solidity
uint256 weight = ArbitrationPoolLib.tierWeight(jurors[addr].tier); // 1 / 3 / 10
uint256 roll = uint256(keccak256(abi.encodePacked(seed, slot, attempt, "w"))) % MAX_TIER_WEIGHT;
if (roll >= weight) continue;

Stake above a tier's minimum contributes nothing to selection probability. Capital is therefore best deployed as many minimum-stake identities rather than one large one, which is the opposite of the intended incentive. _isEligibleForCase excludes only the literal buyer and seller addresses; there is no relation, cluster, or funding-source check anywhere, and stake() is permissionless with no maturation period, so an attacker may stake after observing a dispute and before the panel is drawn.

Vulnerable Scenario: The following steps illustrate the issue:

  1. A party to a dispute controls 500,000 cNGN.
  2. Deployed as one Expert juror, it yields a single entry in the eligible set at tier weight 10.
  3. Deployed as twenty Community jurors at 25,000 cNGN each, it yields twenty entries at tier weight 1.
  4. On a Community-band case the twenty entries give twenty independent chances at each of the three panel slots, against the single Expert entry's one. Splitting is strictly better.
  5. With two of three votes sufficient to determine the ruling, seating two of the twenty identities decides the case.
03Section · Impact

Impact

A party can buy a controlling share of the panel deciding their own dispute for a fraction of the amount at stake, defeating the guarantee that no party can influence who decides their case. Slashing does not deter it: a captured majority is by definition the winning side, so it collects fees and slash redistribution rather than losing stake.

Community tier is the most exposed, having the lowest stake per identity.

04Section · Recommendation

Recommendation

Selection weight is not where this closes, and implementing the intended stake x tierWeight formula on its own does not close it. Panel seats are drawn without replacement, so one controller holding a given stake as a single identity can occupy at most one of three seats, while the same stake split across many identities can occupy all three. Weighting by stake equalises influence per unit of capital but places no bound on seats per controller, so splitting remains strictly better for capture and the attack above survives unchanged. With the stake x tierWeight formula applied, a split controller still takes roughly 57% of panel seats against a counterparty holding identical capital, where parity would be about 50%.

Bind identity where stake enters the system. Require an attestation at stake() that the pool can verify, so the documented one-identity-per-person rule is enforced on-chain rather than assumed off-chain, and add a stake maturation period so an address cannot become eligible after a dispute is already visible.

If on-chain identity binding is not viable, record in docs/PROTOCOL_SPEC.md §5 that the protocol has no on-chain sybil resistance and that panel independence rests entirely on off-chain KYC at juror onboarding, so anyone relying on the independence guarantee can see what actually enforces it.

05Section · Resolution

Resolution

Mitigated. The selection mechanism itself is unchanged: with equal capital, splitting still takes 82% of seats and held a 2-of-3 majority in 15 of 15 draws. What is new is admission control — staking requires an attestor-signed identity and identityOwner binds one identity to one wallet permanently. That is a real barrier, but it relocates the control from the selection algorithm to attestor policy, which is off-chain.

Related: F-2026-0013 reaches the same panel capture using legitimately attested jurors, so attestation alone does not close this route.

06Section · Affected files

Affected files

  • contracts/lib/ArbitrationPoolSelectLib.sol#L102-L118
  • contracts/lib/ArbitrationPoolSelectLib.sol#L163
  • contracts/lib/ArbitrationPoolLib.sol#L72-L76
  • contracts/ArbitrationPool.sol#L454-L477
Status
Mitigated
F-2026-0004