`openCase` admits unauthenticated, attacker-authored escrows
openCase trusts state self-reported by the escrow it is validating, with no check against the factory. A hostile contract can open cases at will, burning protocol-paid VRF requests, locking genuine jurors onto fake panels and draining the fee pool.
Description
The pool holds no reference to an approved factory or escrow registry. openCase accepts an
arbitrary address and treats that contract's self-reported state as authoritative:
if (escrow.arbitrator() != address(this)) revert NotEscrowArbitrator();if (escrow.status() != ESCROW_STATUS_DISPUTED) revert NotDisputed();...uint256 dealValue = escrow.disputeBaseAmount();bool commercial = escrow.commercial();
Both guards are attested by the contract being validated. A hostile contract returns the
pool's own address and status 3. CaseAlreadyExists is sidestepped by returning a fresh
disputedMilestoneIndex each call. dealValue and commercial — which choose the tier lane and
the fee size — are likewise attacker-chosen.
The fabricated case consumes a genuine VRF request, locks a real panel's activeAssignments, and
on tally pays every matching juror the tier fee from the global feePool — up to 35,000 cNGN per juror in the commercial lane — which requires Expert jurors to be staked,
since that lane also gates eligibility to tier 2; against a Community-only registry the drain is
6,000 cNGN per case — for a case that contributed no compensation budget. Payment is
credited to juror stake and is withdrawable via unstake.
Where the attacker also controls the eligible jurors in the targeted lane, this is deterministic
rather than probabilistic: SelectLib#L94-L96 returns the entire eligible set with no sampling
when eligibleCount <= panelSize.
Impact
Unconditionally: anyone can burn protocol-paid VRF requests and lock full panels of genuine
jurors, removing them from real disputes, while driving feePool toward zero. Genuine jurors on
later legitimate disputes are then paid partially or not at all (JurorFeePartiallyPaid).
Conditionally on lane control: the drained fees become attacker-owned, withdrawable stake, and
their collateral is never slashed because the controlled panel votes unanimously.
Recommendation
Store an immutable or explicitly governed set of factory addresses and verify the submitted escrow
is registered by one of them with this pool recorded as its arbitrator; or require one-time escrow
registration with reciprocal dependency checks at creation. Snapshot the fee-driving fields
(dealValue, commercial, parties) from authenticated factory/escrow state, never from the
submitted contract's return values.
Resolution
Fixed. Escrow provenance is now checked against the factory registry; a fabricated escrow is rejected before it can consume a VRF request or lock a panel.
Affected files
ArbitrationPool.sol#L545-L568lib/ArbitrationPoolSelectLib.sol#L92-L96lib/ArbitrationPoolTallyLib.sol#L179-L193