Task: Contract Follow-ups: DINTaskAuditor Size (#201 A4), Dual-Role Guard (#180), Cross-Model S5 (#193)
ID: task_061026_20
Assigned to: Umer Majeed (@umermjd11)
Created: 2026-10-06
Status: Open (assigned)
Dates: Oct 6 – Oct 9, 2026 (about 3 working days; per-Part estimates below). Parts A and C can start right away. Part B goes with Part A (same contract and byte budget). If anything slips, say so early in the tracking discussion.
Repo: https://github.com/InfiniteZeroFoundation/DevNet
Base branch: develop. The spec was written against a1fcce2 (2026-10-05). The task contracts and DinValidatorStake are unchanged since 740a613, where the source plan was verified. Re-pull before starting.
Roadmap ref: P3-6.3a (gas/size), P3-6.3b (audit preparation, open findings), P3-4.2 (penalty tiers, S5)
Source plan: Developer/tasks-plan/umermjd11/task-plan-051026-1.md, reviewed in PR #219 (verification review, reviewer decisions, decisions applied)
Companion task: task_061026_21 (full-GI alignment). See Coordination.
Tracking:
- Part A: #201 Part A, item 4 (the
DINTaskAuditorreview). #201 Part B stays open. - Part B: #180 (closes, pending review).
- Part C: #193 (closes, pending review).
- Discussion #230 (progress updates, questions, PR links)
| Item | State |
|---|---|
| #201 A4 | DINTaskAuditor is 22,718 B runtime (1,858 B margin, so the CI gate warns). It has 16 public mappings. Four of them either have no reader outside the contract or duplicate an existing view |
| #180 | registerDINAuditor (DINTaskAuditor.sol:667-698) and registerDINaggregator (DINTaskCoordinator.sol:401-434) have no cross-role check. The PR #182 review ruled dual-role not intended (Developer/design/adversarial-threat-model.md, Judgment call 2; Row 6 = KNOWN GAP). Stale NatSpec remains at DinValidatorStake.sol:121, :451 and :462 |
| #193 | The S5 ring is _partialSlashGIs[validator][msg.sender] (DinValidatorStake.sol:146; slashPartial at :294-322), so it is counted per slasher contract. That splits S1 (auditor contract) and S2 (coordinator) even within one model. Developer/design/MECHANISM_DESIGN.md:81 defines S5 per validator, across both roles |
| Part | PR | Touches | Conflicts with | Rule |
|---|---|---|---|---|
| A | PR 1, commit 1 | DINTaskAuditor.sol (getter visibility, folds), dincli/abis/DINTaskAuditor.json, DINTaskAuditor.md §3 |
B (same contract and budget); task_061026_21 Part B (DINTaskAuditor bytes) |
Start right away |
| B | PR 1, commit 2 | DINTaskAuditor.registerDINAuditor, IDINTaskCoordinator + new error (DINShared.sol), DinValidatorStake.sol NatSpec, tests, threat model Row 6 |
A; task_061026_21 Part B (DINShared.sol enum) |
Same PR as A, so the gate proves both fit |
| C | PR 2 | DinValidatorStake.sol (S5 storage + slashPartial), S5 tests, deploy-script S5 keys, DinValidatorStake.md, MECHANISM_DESIGN.md, threat model Row 11 |
— | In parallel with PR 1, since it's a different contract |
PR #218 (P3Adversarial.t.sol, #154 Part 2) encodes Row 6 and Row 11 as test_knownGap_dualRoleRegistration / test_knownGap_perSlasherS5Evasion. If it merges first, PR 1 and PR 2 each flip their row's test to test_defended_…. Otherwise PR #218 rebases and flips them. No test may be left asserting a closed gap. Row 11's test calls slashPartial directly as each task contract, which makes it a good regression check for Part C.
Rebase each PR on develop after the previous one merges; no stacked branches.
Shrink the contract in place, with no library, split or deploy change. Only getter visibility changes: no state-changing function, event or storage slot changes. Prototyped in the plan and re-measured in the PR #219 review (via_ir, 200 runs):
Getter → internal |
Readers outside the contract | Saving |
|---|---|---|
auditBatches |
none (only a comment, RewardEngine.t.sol:895). getAuditorsBatch already returns the batch |
−100 B |
dinAuditors |
none. getDINtaskAuditors already returns the list |
−83 B |
Is_testdataCIDs_Assigned |
none. It is written at :1028 and read only by its own guard at :1026 |
−58 B |
auditorGIWeight |
none | −79 B |
| The four together (Decision 1) | −320 B → 22,398 B (2,178 B margin) |
Keep public: rewardClaimed, testDataDisputes and giRewardSnapshot. task_061026_21's claim and dispute commands read them.
Scope:
- Make the four getters
internal. No test reads them, so no test changes. - Fold review: required. #201 item 4 asks for "the same review" of per-phase duplication that the coordinator got. Measure folds of the near-duplicate dispute and reassignment paths. Commit a fold only if it shrinks the contract: in task_021026_19,
via_irmade the coordinator's commit/reveal/finalize folds grow it (+177 B / +166 B). Report every measurement in the PR, including the ones not taken. - Regenerate
dincli/abis/DINTaskAuditor.json(dincli system dump-abi --artifact foundry/out/DINTaskAuditor.sol/DINTaskAuditor.json --output dincli/abis --official) and update theDINTaskAuditor.md§3 tables. - Check PR #29's subgraph for reads of the four getters, and post an ABI note there. Don't push to that branch.
DINTaskAuditor budget across both tasks (Decision 1). Measured on a1fcce2:
| Step | Runtime | Margin | Band |
|---|---|---|---|
develop |
22,718 B | 1,858 B | warn |
| + Part A | 22,398 B | 2,178 B | ok |
| + Part B (+102 B) | 22,500 B | 2,076 B | ok (28 B above the warn line) |
| + task_061026_21 Part B's every-batch commitment check (+75 B) | 22,575 B | 2,001 B | warn (still far above the 1,024 B fail line) |
There are two ways back out of the warn band: a fold that actually saves bytes, and removing Is_testdataCIDs_Assigned once task_061026_21's AuditTestDataAssigned coordinator state guards the double-set. Whichever of the two tasks lands second reports the combined size.
- The four getters are
internal, with no change to state-changing functions, events or storage layout - A size table from one build of the implementation, plus every fold measured (kept or not)
- Bundled ABI regenerated,
DINTaskAuditor.md§3 updated, ABI note posted on PR #29 -
cd foundry && npm ci && forge build && python ../.github/scripts/contract_size_gate.py && forge testis green, includingUpgradeValidation.t.sol.pytest -m "not integration"is green
Estimate: 1 day.
Decision 2: a per-address guard in registerDINAuditor. A second address with its own stake gets around it (Sybil), but the attack then costs a second stake and is visible on-chain.
- Where. Aggregator registration (states 6–7) always comes before auditor registration (8–9) (
DINShared.sol:17-20), so the auditor side is the only place the guard can fire:Addif (dintaskcoordinatorContract.isDINAggregator(_GI, msg.sender)) revert TA_DualRoleNotAllowed();
isDINAggregator(uint256,address) returns (bool)toIDINTaskCoordinator(DINShared.sol:110-121). The coordinator's public mappingisDINAggregator(DINTaskCoordinator.sol:34) already provides it. AddTA_DualRoleNotAllowedtoDINShared.sol. - Cost: +102 B on
DINTaskAuditor, measured. The coordinator doesn't change. - NatSpec:
DinValidatorStake.sol:451and:462say "(not yet enforced)". Both are enforced now, in the two registration functions.:121says "decremented at endGI time". The decrement now happens inreleaseGIRegistrationSlots.
- Tests (next to the
StakingEnforcement.t.solregistration tests):- an aggregator registering as an auditor in the same GI reverts with
TA_DualRoleNotAllowed - the same address registering as an auditor in a different GI succeeds
- a different address with its own stake succeeds, which documents the Sybil limit
- an aggregator registering as an auditor in the same GI reverts with
- Docs:
- threat model Row 6 changes from KNOWN GAP to DEFENDED (with the Sybil note), and its stale line refs are refreshed (
:386→:401,:660→:667) - in
DINTaskAuditor.md, update the registration checks and the §13 caveat - add a
DINShared.mderror row
- threat model Row 6 changes from KNOWN GAP to DEFENDED (with the Sybil note), and its stale line refs are refreshed (
- Guard, interface entry and error added, with the three tests
- NatSpec fixed at
DinValidatorStake.sol:121,:451,:462 - Threat model Row 6 updated (and PR #218's Row 6 test flipped if it has merged)
- The gate is green, and the PR shows the A + B size
Estimate: 0.5 day.
Decision 3: a two-level check.
- The per-slasher GI ring stays as it is, with the same per-model escalation and the same tests.
- Add a per-validator ring of
block.timestampvalues,_partialSlashTimes[validator], withs5GlobalWindow(seconds) ands5GlobalThreshold.- Timestamps only increase, across every slasher, so the ascending-order trim that forced per-contract keying (
DinValidatorStake.sol:136-146) stays safe. slashPartialescalates (fullMIN_STAKEslash + jail) when either level reaches its threshold, and then clears both rings.
- Timestamps only increase, across every slasher, so the ascending-order trim that forced per-contract keying (
- This also closes the same-model split: one validator's S1 and S2 misses now add up.
Scope:
- Storage. Append the new variables before
__gap(:174, 50 slots) and shrink the gap by the slots used. (Amended at PR No. 236:__gapstays at 50. Nothing ondevelopis deployed yet, so per the "Core rules" instorage_layout.mdnew variables go above__gapwithout shrinking it, as07fd597did forDinEmission.)DinValidatorStakeV2inherits them.UpgradeValidation.t.soland theDeployPlatform.t.solupgrade tests must stay green. - Setter.
setS5GlobalParams(window, threshold), validated likesetS5RecidivismParams(:583-594). - Defaults.
s5GlobalWindow = 7 daysands5GlobalThreshold = 6(2 ×s5RecidivismThreshold). These are placeholders until #155.- Set them in
initialize. - On an upgraded proxy, 0 means off; no
reinitializer. Nothing ondevelopis deployed, and DevNet 2.0 is a freshDeployPlatform.s.solrun. - Add optional deploy-script env keys
S5_GLOBAL_WINDOW/S5_GLOBAL_THRESHOLD, matching the existingS5_*keys. Add them toDeployPlatform.mdand its env-override tests.
- Set them in
- Event.
ValidatorEscalatedS5gets a level flag, or a sibling event is added, so indexers can tell which level fired. - Tests:
- two task contracts each slash one validator (threshold − 1) times within the window, and the global level escalates
- when the window expires, the global count resets
- the existing S5 tests (
SlashingInvariants.t.sol:488,:518,:540) andtest_crossModelGICollision_slashPartialDoesNotUnderflow(PR146SlashingRegression.t.sol:321) stay green - the default and override deploy tests cover the new keys
- Docs:
DinValidatorStake.md- the
Developer/design/MECHANISM_DESIGN.mdS5 row - threat model Row 11 (KNOWN GAP → DEFENDED)
- record, but don't change, the separate discrepancy:
MECHANISM_DESIGN.md:87says S5 is "entire slashable stake + blacklist", while the code does aMIN_STAKEslash + as5JailDurationjail (7 days)
- Two-level S5 with setter, defaults, "0 means off", event and deploy-script keys
- The new tests, with the existing S5 and upgrade-validation tests green
- Docs updated (and PR #218's Row 11 test flipped if it has merged)
Estimate: 1.5 days.
DINShared.sol. Part B addsisDINAggregatortoIDINTaskCoordinator. task_061026_21 Part B edits theGIstatesenum. The sections differ, and whichever PR merges second rebases.dincli/abis/DINTaskAuditor.json. Both tasks regenerate it. The second to merge regenerates on top.- The
DINTaskAuditorbudget is tracked in Part A's table. Whichever task lands second reports the combined size. - The #180 guard and the integration suite. The suite uses separate accounts for each role (aggregators 11–22, auditors 50–58), so it isn't affected. task_061026_21 adds a negative check once Part B lands.
- #193 doesn't affect a single-model GI.
- #201 Part B (slash reason for committed-but-unrevealed): a mechanism decision tied to #155 / #38.
- #194, #78, #181, #178: each needs a design note or a decision first, or is mainnet-grade.
- dincli, the integration suite and the GI docs: task_061026_21.
- Don't split
DINTaskAuditorto win bytes. If the budget can't be met with the levers above, stop and raise it in the tracking discussion.