Repository navigation
fix(contracts): position-stable batch seat assignment (issue #243) - #253
robertocarlous wants to merge 6 commits into
Conversation
|
Cc: @umeradl |
Abidoyesimze
left a comment
There was a problem hiding this comment.
Went through this properly - rebuilt, ran the full suite twice to be safe, hand-traced the actual algorithm rather than just trusting the description.
Solid fix, correctly solves the problem it set out to solve.
- forge build + forge test: 504 passed, 0 failed, both runs
- size gate matches claims exactly: TaskCoordinator 23,482B (1,094B margin, same warn band as develop), TaskAuditor 21,342B (3,234B margin, ok)
- doc links all resolve
Traced the new closed-form batch-count math (t1cnt = mLen/T1_MODELS_PER_BATCH + partial term, capped by rLen/T1_AGGREGATORS_PER_BATCH) against the old greedy while-loop for model-limited, validator-limited, and zero-model cases by hand - they agree in every case, including the T2 boundary (any floor-division remainder is necessarily smaller than the divisor, so "cap triggered means no T2" falls out of the arithmetic the same way on both old and new code).
Also traced the position-stable reserve-fill loop: the reserve pointer only moves forward and the primary/reserve regions are disjoint, so nobody can get double-assigned or silently dropped.
Checked the dead "if (!success) revert" removal - grepped through DINTaskAuditor.createAuditorsBatches end to end, it genuinely never returns false, so that branch really was unreachable.
The three new tests use vm.snapshot/vm.revertTo to run the identical scenario with and without one unstake and assert every other seat is byte-identical across both runs. That's the right way to actually prove position-stability, not just "it doesn't crash."
Two things worth raising before merge, neither blocking:
-
Batch count is now computed purely from the registered count and model count, before checking which registered validators are actually active. If enough registered validators have gone inactive by call time that the reserve can't cover every vacated seat, the whole call now reverts (TC_NotEnoughValidators / TA_NotEnoughAuditors). The old code sized batches off the already-active-filtered pool, so it would've gracefully formed fewer batches in that same scenario instead of reverting outright. Not a security issue, just a liveness/UX tradeoff none of the three new tests exercise (all three only unstake exactly one validator). Worth a test for "enough inactive to exhaust the reserve," and worth confirming the fail-closed behavior here is intentional.
-
_activeAggregatorPool / _activeAuditorPool have zero callers now but weren't deleted. I actually tested this instead of guessing - removed one locally and rebuilt, size was byte-for-byte identical, so the optimizer already eliminates them and it costs nothing on-chain. Still, worth cleaning up since the issue #249 review already flagged this exact pattern as something to delete rather than leave behind, and this contract's margin is tight enough that dead source just confuses the next reader.
No conflicts with my own open PRs - different call sites in everything we both touch.
Security review: no vulnerabilities. No new reentrancy surface, no privilege-boundary change (both entry points stay onlyOwner), and every new unchecked block is guarded by a preceding bounds check before the arithmetic runs.
Abidoyesimze
left a comment
There was a problem hiding this comment.
Approving - nothing blocking from my review above. The two notes I left (reserve-exhaustion liveness edge case, dead helper cleanup) are follow-up suggestions, not merge blockers.
…undation#243) Batch layout is now derived from the fixed per-GI registration list (dinAggregators[_GI] / dinAuditors[_GI]) instead of the live active pool, so an unstake between seed-lock and batch creation forfeits only the leaver's own seat — every other validator's batch and position is determined solely by the fixed list and the locked seed. Key changes: - autoCreateTier1AndTier2: shuffle fixed regList in-place; closed-form batch count (t1cnt = mLen/T1_MODELS_PER_BATCH + partial-batch term, capped by rLen/T1_AGGREGATORS_PER_BATCH); position-stable seat fill scans reserve from totalSeats..rLen-1 to replace inactive primaries. - createAuditorsBatches (DINTaskAuditor): same three-step pattern — shuffle fixed regList, closed-form batchCnt, position-stable fill. - DINTaskCoordinator.createAuditorsBatches wrapper: removed dead bool-success check (DINTaskAuditor always reverts or returns true; the branch was unreachable); saves 75 bytes, putting the contract 1094 B below EIP-170 and passing the CI size gate. - New test: BatchAssignmentStability.t.sol — three snapshot/revert tests verify that unstaking exactly one validator displaces only that validator's own seat and leaves all others unchanged. - Docs: adversarial-threat-model.md Row 2 updated, DINTaskCoordinator.md §6.3/§6.4 rewritten, DINTaskAuditor.md createAuditorsBatches section updated, BACK_LOG.md BL-26 flipped to Fixed. - Tasks: task specs 23–28 added for issues InfiniteZeroFoundation#243–InfiniteZeroFoundation#248.
…ssignmentStabilityTest PR No. 272 added a per-batch testDataCommitments check to setTestDataAssignedFlag; the stability test's _runToSeedLocked helper was missing the placeholder assignment step before that call.
b285168 to
09eaab8
Compare
|
Rebased onto Conflicts resolved:
Test fix: PR No. 272 added a per-batch Rebased commits:
Full |
Resolve conflicts in DINTaskCoordinator.sol (NatSpec: keep position-stable description, drop incorrect AggSeedLocked state reference — lockAggSeed emits that event, it is not a GI state), DINTaskCoordinator.md (keep correct LMSevaluationClosed state name from develop, keep PR's position-stable algorithm description), and DINTaskAuditor.md (keep correct LMSclosed state name from develop, keep PR's position-stable algorithm description).
…k_061026_21 PlaceholderTestData.sol was removed in PR No. 273 (revert of task_061026_21 GI-states richer states). BatchAssignmentStability.t.sol imported it for a per-batch commitment check that only existed in that reverted feature; setTestDataAssignedFlag no longer requires per-batch assignAuditTestDataset calls, so the import and call are simply removed.
Closes #243
Summary
Batch layout is now derived from the fixed per-GI registration list
(dinAggregators[_GI] / dinAuditors[_GI]) instead of the live active pool,
so an unstake between seed-lock and batch creation forfeits only the
leaver's own seat — every other validator's batch and position is
determined solely by the fixed list and the locked seed.
Key changes:
batch count (t1cnt = mLen/T1_MODELS_PER_BATCH + partial-batch term,
capped by rLen/T1_AGGREGATORS_PER_BATCH); position-stable seat fill
scans reserve from totalSeats..rLen-1 to replace inactive primaries.
shuffle fixed regList, closed-form batchCnt, position-stable fill.
bool-success check (DINTaskAuditor always reverts or returns true;
the branch was unreachable); saves 75 bytes, putting the contract
1094 B below EIP-170 and passing the CI size gate.
tests verify that unstaking exactly one validator displaces only
that validator's own seat and leaves all others unchanged.
§6.3/§6.4 rewritten, DINTaskAuditor.md createAuditorsBatches section
updated, BACK_LOG.md BL-26 flipped to Fixed.