Task: P3 Slashing Depth, Aggregator-Recomputation Disputes & Reward/Tokenomics Economics (#38 remainder, #41, #42)
ID: task_100926_13
Assigned to: Robbert Abimbola (@robertocarlous)
Created: 2026-09-10
Status: Open (assigned)
Repo: https://github.com/InfiniteZeroFoundation/DevNet
Base branch: develop — spec written against commit ef41a0d (2026-09-10); re-pull before starting regardless of when you begin. Depends on task_100926_12 §1 for its shared-registration-counter infrastructure (see §2 below) — check whether that's merged before starting §2.
Roadmap ref: P3-4.1/4.2/4.3 (slashing depth, disputes), P3-5.2 (reward-split simulation), P3-5.2 (emission contract)
Tracking: #38 (closes, pending review — the remainder not covered by task_100926_12 §2), #41 (closes, pending review), #42 (closes, pending review) · Discussion #138 (progress updates, questions, PR links)
This task and its sibling, task_100926_12.md, come out of a 2026-09-10 audit of ten open P3 issues (#36-43, #75, #78) — the full audit is appended at the bottom of both files, unchanged. task_100926_12 covers the parts of this pending work where the design decision is already made and only wiring is missing; this task covers what's left, which genuinely needs either a slashing-economics judgment call or a simulation-first sequencing (reward-split percentages before the emission contract that depends on them). Expect more back-and-forth on this task than a typical wiring task — several sections below ask you to propose a number or a mechanism, not just implement a spec'd one.
This is a larger, more design-adjacent task than usual. If any section below turns out to be bigger in practice than it reads here, say so early and propose splitting it into its own follow-up rather than absorbing scope creep — same instinct Similoluwa used splitting #121's original ask into #124-127.
Background (MECHANISM_DESIGN §4):
| # | Condition | Role | Current state |
|---|---|---|---|
| S1 | Missed audit vote | Auditor | Flat minStake() slash, AUD_NO_VOTE — DINTaskAuditor.sol:1268 |
| S2 | Missed T1/T2 submission | Aggregator | Flat minStake() slash — DINTaskCoordinator.sol:829 (slashAggregators) |
| S3 | Score deviation from median | Auditor | Already wired, gated behind s3SlashingEnabled (default false) — DINTaskAuditor.sol:151,1229-1279. Not in scope here; enabling it is an operational decision pending empirical threshold data, not a code change. |
| S5 | Recidivism (≥r partial slashes within w GIs) | Both | Spec-only, no code |
| S6 | Registration without capacity (registers, never participates, across models) | Both | Spec-only, no code |
a) S1/S2 → DAO-settable partial fraction. Today's flat minStake() "fully unstakes a floor-staked validator for one missed vote," which MECHANISM_DESIGN §4 explicitly calls over-punishment for a liveness fault. Add a DAO-settable fraction (matching the s3DeviationThreshold/setS3DeviationThreshold pattern already in DINTaskAuditor.sol — a public settable var + setter + event), applied to both slashAggregators (DINTaskCoordinator.sol) and slashAuditors's AUD_NO_VOTE branch (DINTaskAuditor.sol). The exact fraction (MECHANISM_DESIGN suggests 25-50% as an example, not a final number) is your call to propose in the PR description — this is explicitly still an open decision (§9 item 2), not something to treat as settled.
b) S5 — recidivism counter. Needs a per-validator count of partial slashes within a rolling window of w GIs, escalating to a full slash + jail on the r-th occurrence. Open design questions to resolve and note in your PR description:
- Where does the counter live?
DinValidatorStake.slash()is the single choke point for every slash call today, but it doesn't currently distinguish partial from full severity — you'll likely need to extendslash()'s signature (or add a parallel bookkeeping call) so the counter only increments on partial slashes. - What "within
wGIs" means concretely — a rolling GI-index window needs each slash call to carry a GI number, whichDinValidatorStake.slash()doesn't take today (it's called from task contracts that know their own GI but don't currently pass it through). - Escalation: once the recidivism threshold is hit, does the next triggering call become a full slash automatically, or does it require a separate step? Prefer automatic if it fits cleanly, but call out the tradeoff.
c) S6 — registration-without-capacity counter. "Registers, never participates, across models" implies a cross-task-contract counter, same shape as task_100926_12 §1(b)'s concurrent-registration-cap counter. Check whether task_100926_12 has merged before starting this — if its activeRegistrationCount-style counter on DinValidatorStake exists, extend it (or add a sibling counter) to also track a "registered but didn't submit anything" flag per registration, rather than building a second independent tracking mechanism. If task_100926_12 hasn't merged yet, propose the shared shape now and note the eventual merge point in your PR description.
Tests: partial-slash-fraction round-trip + a fuzz test confirming the fraction is applied correctly at the boundary (matches SlashingInvariants.t.sol's existing style); S5 escalation test (r-th partial slash within the window triggers full slash + jail); S6 counter test (registers-without-participating across ≥2 GIs triggers escalating partial slash, per the "partial, escalating" tier in the taxonomy table).
Background: #40 closed 2026-09-10 with one line folded into this issue: "Aggregator auditing: T1/T2 outputs verifiable by recomputation; any validator may open an S4 dispute." DINTaskCoordinator.openDispute/resolveDispute already exist as a scaffold (from #66) — bond custody, dispute window, fresh-subgroup reassignment excluding the accused (_assignFreshSubgroup) — but adjudication is onlyOwner (centralized human judgment call), not an on-chain recomputation check. This is distinct from PR #110's dispute mechanism, which is test-data-integrity-specific (DINTaskAuditor.disputeTestData/resolveDispute) and doesn't touch aggregation outputs at all.
This needs a short design note before code — "verifiable by recomputation" for T1/T2 aggregation isn't specified anywhere beyond that one sentence, and aggregation itself is deliberately off-chain (per the existing gas-simulation design doc's baseline: "aggregation logic is off-chain — only the final CID goes on-chain"). Concretely, propose an answer to: what does a validator submit to prove a T1/T2 CID is invalid, and what does the contract check on-chain to adjudicate it without re-running the aggregation itself (which it structurally can't, since the inputs and computation are off-chain)? A plausible shape: the fresh subgroup reassigned by _assignFreshSubgroup re-runs aggregation off-chain and submits a new CID; if it matches the disputed one, the dispute is frivolous (existing forfeiture path from task_100926_12 §2a applies); if it doesn't, the original is invalid (upheld path applies). Confirm or refine this with Umer before implementing — this is the one item in either task where the design isn't already settled by a prior decision.
Tests: once the mechanism is agreed, a dispute-and-recompute-mismatch test and a dispute-and-recompute-match (frivolous) test, following EncryptedTestData.t.sol's three-stage structure as a model for how PR #110 organized a similar staged-reveal flow.
Background: The 60/20/15/5 client/auditor/aggregator/treasury split shipped in #64 was never validated against the P3-5.2 income-stability model this issue's own scope calls for: "simulate in P3-5.2 at 10-50 validators / 100-500 clients." Developer/issues/validator-reward-mechanism/simulation.md and client-reward-mechanism/simulation.md are simulation plans, not completed simulations.
- Model steady-state validator income at target / 10× / 0.1× participation, across the 10-50 validator / 100-500 client spec-scale range, for the current 60/20/15/5 split.
- Deliverable: a
Developer/design/*.mdwriteup (same shape asgas-simulation-network-fee.md— self-contained, states its own scope, methodology, and a clear recommendation) confirming the current split or proposing an adjustment with numbers behind it. - If the simulation surfaces a split-percentage change, that's a
DINTaskAuditor.RewardSplitDAO-settable-parameter update (already governable, no new contract code needed) — flag the recommended values, don't just leave them as a doc conclusion nobody applies. - Also sweep BL-14 (bps-split rounding dust not swept to treasury) into this work if it's a small addition on top of whatever settlement-path code you're already touching for the simulation's validation; otherwise leave it as a separate small follow-up and say so.
Background: MECHANISM_DESIGN §9 item 5 records geometric decay per epoch and a GI-lifecycle-tied inflation guard as a resolved design decision, but no corresponding contract exists in foundry/src/ — today's reward pool (depositRewards, #64) is entirely externally funded, there's no protocol-emitted subsidy top-up at all.
Sequence this after §3 — the emission contract's decay-rate/epoch-length numbers should be checked against §3's income-stability simulation rather than picked independently, since they're feeding the same validator-income model.
- New contract (or extension of an existing platform contract — propose which, following the existing Transparent Proxy pattern used by
DinTreasury/DinFairLaunchDistributor): per-GI reward subsidy on a geometric decay schedule, tied to GI lifecycle (decays per completed GI, not wall-clock time — confirm this matches the "GI-lifecycle-tied inflation guard" phrasing in §9 item 5). - Mints via
DinToken/DinCoordinator's existing mint path (checkmintCap/totalMinted/faucetRetiredfrom #60 — the emission contract's minting should respect the same cap machinery, not bypass it). - Steady-state target: emission → 0 as protocol fees mature to fund the reward pool on their own (per this issue's own framing) — the decay schedule should make this explicit (a final epoch or an asymptotic tail, your call, state which and why).
- DAO-settable decay rate/epoch length, matching every other governable parameter in this codebase.
Tests: decay-schedule unit tests (emission amount at epoch N matches the geometric formula); an integration test funding a GI's reward pool partly from emission and partly from external depositRewards, confirming no double-counting; a mint-cap-respecting test (emission can't push totalMinted past mintCap, and is a no-op post-retireFaucet).
- S1/S2 slash a DAO-settable partial fraction of
minStake(), not the flat amount, with the fraction proposed and justified in the PR description - S5 recidivism counter + escalation to full slash, with the counter-placement and windowing design resolved and noted
- S6 registration-without-capacity counter, sharing infrastructure with task_100926_12 §1(b) where possible
- A design note for S4's recomputation-verification mechanism, confirmed with Umer before implementation; then the mechanism itself wired into
DINTaskCoordinator's existing dispute scaffold -
Developer/design/*.mdreward-split simulation writeup with a concrete recommendation (confirm current 60/20/15/5 or propose new values) - If the simulation recommends new split values,
DINTaskAuditor.RewardSplitupdated to match - New emission contract: per-GI decaying subsidy, GI-lifecycle-tied, respecting
mintCap/faucetRetired, DAO-settable decay parameters -
forge build && forge testgreen, full suite - PR description(s) reference #38, #41, #42 as appropriate and record every design decision made along the way (partial-slash fraction, S4 mechanism, split percentages, decay schedule)
Four fairly distinct pieces of work (slashing tiers, S4 dispute mechanism, reward simulation, emission contract) — submit as separate PRs rather than one large PR, in roughly the order listed (§1 and §2 can go in parallel; §4 should follow §3, not precede it). Given the S4 mechanism (§2) genuinely needs a design decision first, don't start writing that PR's contract code until it's confirmed — same instruction as task_100926_11's §0 blocking-dependency note, just for a design confirmation instead of a merge.
- Flag any change to
DINTaskCoordinator.sol/DINTaskAuditor.sol/DinValidatorStake.solfor Umer's review given cryptoeconomic sensitivity, same as prior tasks in this area (task_210726_5, task_210726_6, task_100926_11). - Re-pull
developand re-runforge testright before opening each PR — this area of the codebase has had five merges in the last 48 hours (#110, #117, #129, #131, #133, #134), and task_100926_12 will also be touching adjacent code concurrently. - §2 (S4 recomputation) is the one item in either task without a settled design — don't guess past what's written above; ask.
Scope of this audit: #36, #37, #38, #39, #40, #41, #42, #43, #75, #78 — all OPEN as of 2026-09-10 (before this task split #38 and closed #40).
Method: Read each issue's full body + comment history, cross-checked against git log/merged PRs since the 2026-09-08 status-check round, current develop source (foundry/src/*.sol), and a live forge test run of SlashingInvariants.t.sol.
Status-check comments posted 2026-09-10 (Sept 8 comment was stale on these four): #36, #38, #40, #41. #37/#39/#42/#43/#78's 2026-09-08 comments are still accurate — not re-posted. #75 already had a fresh 2026-09-10 comment (PR #77 merge) before this audit started.
Five PRs merged in that window that matter to this issue set:
| PR | Merged | Title | Issues affected |
|---|---|---|---|
| #110 | 2026-09-09 | Real encrypted test-data key distribution + staged dispute resolution (Closes #40) |
#40 (directly), #38 (adjacent — new test-data-specific dispute path, not the general one #38 tracks) |
| #116 | 2026-09-09 | import-deployments drops dinTreasury/dinFeeRouter (BL-8) |
none of these 10 directly |
| #117 | 2026-09-10 | Realign SlashingInvariants.t.sol with burn/treasury split |
#38 (clears its "3 failing tests" item — confirmed 13/13 passing via a fresh forge test run) |
| #129 | 2026-09-10 | Nonce collision fix (BL-1) | none of these 10 |
| #133 | 2026-09-10 | Dispute bond denomination → DIN (BL-12, docs-only) | #38, #36 (§9 item 7 denomination half) |
| #134 | 2026-09-10 | Claim-pull reward settlement, O(1) endGI (BL-10) |
#41, #36 |
| #131 | 2026-09-10 | Weighted aggregator reward settlement + docs/gas-benchmark (#126, #127) | #41, #36 — fixes a real stranded-funds bug in #134's interim isRewardableAggregator flat-split |
Confirmed by direct inspection (not just PR descriptions): foundry/src/DINTaskCoordinator.sol now has aggregatorWeight/totalAggregatorWeight mappings incremented per finalized batch, isRewardableAggregator is gone; DINTaskCoordinator.sol/DINTaskAuditor.sol still carry three TODO(task_210726_5): forward to DinTreasury stubs (PR #133 only touched a NatSpec comment, no functional change).
Verdict: stays open, umbrella issue. Not independently closable — closes when all seven children close. Real movement this round: #38 and #41 both cleared concrete items; #40 is one item away from closable. MECHANISM_DESIGN §9 open-decision count: 6 of 14 now open (was 7) — item 7 (dispute bond) split-resolved, denomination done, size/window still open.
Verdict: no change, stays open. No merged PR touched this since 2026-09-08. Outstanding, confirmed unchanged: per-model stake bounds (modelMinStakeBounds) and the concurrent-registration cap exist as storage on DinValidatorStake but are read nowhere in DINTaskCoordinator/DINTaskAuditor registration — zero enforcement; withdrawal queue still single-pending-only (accepted gap, not a blocker). Task candidate: wire the two existing-but-unenforced storage values into GI registration checks. Self-contained, contract-only, well-specified — good task_12/13 candidate. → task_100926_12, §1.
Verdict: stays open, but two items closed this round (see comment posted today). Confirmed via direct forge test that SlashingInvariants.t.sol is 13/13 passing (was 3 failing). Dispute-bond denomination resolved. Scope also now explicitly includes #40's leftover line (aggregator auditing / S4 recomputation dispute — folded in 2026-09-10, #40 closed as a result). Still outstanding and unchanged: S1/S2 flat-minStake() slash (not a DAO-settable fraction), S3 is fully wired but gated behind s3SlashingEnabled (default false) pending empirical threshold validation — an enablement decision, not missing code, S5/S6 spec-only (no code, need a per-validator counter), general dispute path (DINTaskCoordinator.openDispute/resolveDispute) still onlyOwner, no recomputation-based verification (the freshly-folded S4 gap), and its bond-forfeiture/bounty payout still stubbed (confirmed: treasuryAccrued + 2 more TODO(task_210726_5) comments live in source today). BL-11 (grindable redo entropy), BL-15 (resolved disputes can't reopen) also still open. Split across both tasks: treasury-forwarding piece → task_100926_12 §2; partial-slash tiers, S5/S6, and the S4 recomputation-dispute item → task_100926_13 §1-2.
Verdict: no change, stays open. No merged PR touched this since 2026-09-08. P3-SCR validation workstream is done (#62); scenario-driven redesign (separating eligibility from utility) hasn't started beyond a design doc (Developer/issues/scoring-mechanism.md) and a reference contribution_mode implementation that #62 explicitly scoped as not-production. Not a good near-term task candidate — this is a design-first item (redesign needs agreement before code), not a wiring/bugfix task like the others. Left out of both task_12 and task_13.
CLOSED 2026-09-10. PR #110 (Closes #40) merged 2026-09-09 — real encrypted-key scheme now on develop, matching the issue's own scope item verbatim. The one remaining scope line ("aggregator auditing: T1/T2 outputs verifiable by recomputation, S4 dispute") was architecturally #38's territory (DINTaskCoordinator's dispute path), not something specific to auditor evaluation — PR #110 never touched that contract. Per Umer's direction, folded that line explicitly into #38's Scope section (edited 2026-09-10) and closed #40 with a closing comment pointing to #38 for the rest. No standalone task_12/13 work — its only open line is now task_100926_13 §2.
Verdict: stays open, but the headline gap closed this round. BL-10 (unbounded settlement loop, this issue's own biggest flagged risk) is fully resolved via #134 + #131, including a real stranded-funds bug fix in the aggregator path found along the way (task_100926_11's scope). Remaining: BL-14 (rounding dust not swept to treasury — cosmetic) and the reward-split-percentage simulation (60/20/15/5 defaults were never validated against the P3-5.2 income-stability model this issue calls for). → task_100926_13, §3.
Verdict: no change, stays open. No merged PR touched this since 2026-09-08. Supply/burn/treasury mechanics are done; no emission contract exists in foundry/src/ at all — the "geometric decay per epoch" design decision in MECHANISM_DESIGN §9 has no corresponding code. Task candidate — the largest single gap in this whole set: a new emission contract (per-GI reward subsidy on a decay schedule) is greenfield work, not a fix to existing code. → task_100926_13, §4 (sequenced after §3's simulation, since the emission contract shouldn't be built before the split percentages it feeds are at least sketched).
Verdict: no change, stays open. #133 touched a fee-table doc line (dispute bond → DIN, cosmetic given #38 already covers the substance) but didn't touch this issue's real outstanding item. Confirmed still live in source: DINTaskCoordinator.treasuryAccrued and its two TODO(task_210726_5): forward to DinTreasury stubs — dispute-bond forwarding to DinTreasury still isn't wired despite the contract existing since #51 (merged 2026-08-07, over a month ago). No dynamic-fee base rate/multiplier code exists anywhere. → task_100926_12, §2 (same code path as #38's treasury-forwarding item).
Verdict: no change needed, already current. Comment from earlier today (2026-09-10, before this audit) already covers PR #77's merge. Long-running tracking issue by design — next slice (indexer-backed eligibility snapshot) is explicitly blocked on P4-IDX1/IDX2, out of scope for either task.
Verdict: no change, stays open, low urgency. No merged PR touched this since 2026-09-08. The simulation itself is complete and documented as such; what's missing is (a) pricing commitAuditScore gas (added by #63 after the simulation's baseline) and (b) actually wiring the recommended floor into a contract constant. → task_100926_12, §3.
- Closed 2026-09-10: #40 — one leftover line folded into #38's scope, then closed.
- Not closable yet, real work remains: #37, #38, #41, #42, #43, #78 — see task_100926_12/13 for the plan.
- Not closable, not a near-term task either: #39 (needs a design decision before code, not a wiring task) — left out of both tasks.
- No action, tracking issue by design: #75.