Skip to content

Latest commit

 

History

History
164 lines (111 loc) · 22.6 KB

task_100926_12.md

File metadata and controls

164 lines (111 loc) · 22.6 KB

Task: P3 Wiring Completion — Per-Model Stake Enforcement, Dispute-Bond Treasury Forwarding, Network-Fee Floor (#37, #38 partial, #43, #78)

ID: task_100926_12 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. Roadmap ref: P3-5.1 (staking), P3-4.2/4.3 (slashing treasury routing), P3-5.3 (fees/treasury), P3 gas floor Tracking: #37 (closes, pending review), #43 (closes, pending review), #78 (closes, pending review), #38 (partial — this task closes only its treasury-forwarding item; see task_100926_13 for the rest) · Discussion #137 (progress updates, questions, PR links)


Context

This task and its sibling, task_100926_13.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. The audit found the pending work clusters into two groups: things where the design decision is already made and only the contract wiring is missing (this task), and things that still need real slashing-economics/simulation work before or alongside the code (task_100926_13). Splitting on that line keeps this task fast and low-risk, and keeps the heavier judgment calls together in one place for review.

All three items here share a property: the DAO-settable storage or destination contract already exists on develop — nothing here is greenfield design, it's finishing wiring that a prior PR explicitly deferred as follow-up work.


Scope for this task

1. Per-model stake enforcement + concurrent-registration cap (#37)

Background: DinValidatorStake.sol already has modelMinStakeBounds[modelId] (a {min, max} struct, onlyOwner-settable via setModelStakeBounds) and maxConcurrentRegistrationsPerStakeUnit (onlyOwner-settable via setMaxConcurrentRegistrationsPerStakeUnit) — both landed in #65 as inert storage, explicitly flagged as follow-up in that PR's description and in Umer's reply to you on this issue (2026-07-25): "DINTaskCoordinator doesn't need to know its own modelId or look anything up yet; wiring that lookup (and solving the 'task contract doesn't know its registry ID' problem) is explicit follow-up work." This task is that follow-up.

a) Per-model stake floor. registerDINaggregator (DINTaskCoordinator.sol:286) and registerDINAuditor (DINTaskAuditor.sol:607) currently only check dinvalidatorStakeContract.isValidatorActive(msg.sender). Add a stake-floor check reading modelMinStakeBounds[modelId]:

  • Give each task contract a way to know its own modelId (an immutable set in the constructor is the simplest fix, since the model owner already knows the ID DINModelRegistry.registerModel returned before deploying the task contracts — check foundry/script/ for where task contracts get deployed and thread it through there).
  • Open question to resolve in your PR description, not silently: the design (MECHANISM_DESIGN §3) describes the model owner setting their own per-model requirement, bounded by DAO min/max — but the only setter that exists today, setModelStakeBounds, is onlyOwner on DinValidatorStake (i.e., DAO-set, not model-owner-set), and there's no separate "chosen value within bounds" storage slot. The simplest correct wiring is to treat modelMinStakeBounds[modelId].min directly as the effective per-model floor in the registration check (getStake(msg.sender) >= max(minStake(), modelMinStakeBounds[modelId].min)), leaving a model-owner-facing setter (bounded by .min/.max) as an explicit fast-follow rather than building it here. If you think it's small enough to include now, propose it — otherwise note the deferral explicitly.
  • When modelMinStakeBounds[modelId] is unset (min == 0 && max == 0), behavior must be unchanged from today (no floor beyond minStake()) — this is the same "not yet configured" sentinel pattern #65 already uses elsewhere.

b) Concurrent-registration cap. This is harder because it's cross-contract: a validator's concurrent registrations span every model's DINTaskCoordinator/DINTaskAuditor, not just one. There's no shared counter today. Proposed approach — confirm or refine in your PR description:

  • Add a per-validator counter on DinValidatorStake (e.g., activeRegistrationCount[address]), incremented/decremented by calls from task contracts.
  • Reuse the existing slasher-authorization trust boundary (onlySlasherContract, wired via DinCoordinator.setDINTaskCoordinatorAsSlasher/setDINTaskAuditorAsSlasher) rather than inventing a new allowlist — a task contract already authorized as a slasher is also trusted to report its own registrations.
  • Increment in registerDINaggregator/registerDINAuditor; decrement when that GI's participation for the validator concludes (endGI, or wherever the equivalent lifecycle end is for aggregators/auditors — check both contracts, they may need different hook points).
  • Cap check: activeRegistrationCount[msg.sender] < (getStake(msg.sender) / minStake()) * maxConcurrentRegistrationsPerStakeUnit is the shape implied by the parameter's name and MECHANISM_DESIGN §3's example ("one 10-DIN stake cannot back 20 tasks") — confirm the exact formula in your PR description since this isn't specified to the bit anywhere.
  • When maxConcurrentRegistrationsPerStakeUnit == 0 (unset), behavior must be unchanged (no cap).

Tests: round-trip + bounds-validation tests already exist from #65 for the storage itself — add enforcement tests: a validator below the model floor cannot register; a validator at the concurrent-registration cap cannot register for a further GI; existing registration behavior is unchanged when either value is left at its zero/unset default.

2. Dispute-bond and reward-treasury forwarding (#38 partial, #43)

Background: Three TODO(task_210726_5): forward to DinTreasury stubs remain live in source, confirmed today:

  • DINTaskCoordinator.sol:131 — treasuryAccrued (dispute-bond forfeiture accumulator)
  • DINTaskCoordinator.sol:1067 — resolveDispute's upheld-dispute bounty top-up
  • DINTaskCoordinator.sol:1079 — resolveDispute's frivolous-dispute forfeiture
  • DINTaskAuditor.sol:58 — treasuryAccrued (reward-settlement treasury bps share)

DinTreasury.sol has existed since #51 (merged 2026-08-07) and the dispute bond's DIN denomination was confirmed by #133 (merged 2026-09-10) — nothing is blocking this except that nobody's wired it yet. This closes both #38's dispute-payout-stub item and all of #43's remaining scope, since they're the same code path.

a) Frivolous-dispute forfeiture (DINTaskCoordinator.sol:1079) and DINTaskAuditor's reward-settlement treasury share (DINTaskAuditor.sol:58) — straightforward, mirror the existing pattern. DinValidatorStake.slash() already does exactly this split (DinValidatorStake.sol:238-245): burnAmount = actualAmount / 2, IBurnableToken(DIN_TOKEN).burn(burnAmount), then DIN_TOKEN.safeTransfer(slashTreasury, treasuryAmount). Both task contracts already hold the tokens in question (the bond, the reward pool's treasury share) directly — no cross-contract pull is needed, just burn+push using the same call shape. Keep treasuryAccrued as a running cumulative counter for observability (emit an event on each forward), but the actual tokens should move immediately rather than sit in the contract.

b) Upheld-dispute bounty top-up (DINTaskCoordinator.sol:1067) — not a simple push, flag this explicitly. Unlike (a), this needs DinTreasury to pay out to reward a correct disputer, and DinTreasury.withdrawETH/withdrawERC20 are both onlyOwner (DinTreasury.sol:43,55) — a task contract can't call either. This is a real access-control gap, not a wiring oversight. Options to weigh (pick one, or propose another, and say why in the PR description):

  • Extend DinTreasury with an authorized-withdrawer role (mirroring the slasher-authorization pattern already used elsewhere) so registered task contracts can pull a bounty.
  • Fund the bounty from a different source entirely (e.g., a small carve-out from the GI reward pool, or the disputed party's own forfeited stake) rather than DinTreasury.
  • Keep bounty as an off-chain/manual DAO action for now and just make the on-chain state (disputeBondClaimable credit) correct — i.e., treat the bounty top-up as genuinely out of scope for this task and say so. This sub-item touches DinTreasury.sol's access control, which is shared platform infrastructure — flag it for Umer's review regardless of which option you pick, before merging.

Tests: conservation checks (forfeited bond amount always reaches burn+treasury, never stranded or double-counted); confirm SlashingInvariants.t.sol-style fuzz coverage extends cleanly to the new forwarding calls; a test exercising the upheld-bounty path with whichever option (b) resolves to.

3. Network-fee floor completion (#78)

Background: Developer/design/gas-simulation-network-fee.md is complete except one line it flags itself: commitAuditScore's gas cost (the commit half of commit-then-reveal, added by #63 after this doc's baseline) isn't priced, only revealAuditScore is. The recommended floor (275,916 gas × gas_price_ETH, auditor path) also isn't consumed anywhere in contracts/config yet.

  • Extend foundry/test/GasSimulation.t.sol (see its existing test_gas_s2_auditScore_* tests, GasSimulation.t.sol:307-350) with a commitAuditScore measurement at the same LOW/MID/HIGH participation scale as the rest of the suite.
  • Update Developer/design/gas-simulation-network-fee.md's recommended floor to include the commit-half cost, and mark its "Open items" caveat resolved.
  • Land the resulting number as an actual DAO-settable parameter (a networkFee/feeFloor-style constant with a setter, matching the existing governable-parameter pattern from MIN_STAKE/UNBONDING_PERIOD in #65) somewhere it's actually read — confirm with Umer where that consumption point should be if it isn't obvious from the existing fee-router/model-registration flow.

Tests: new gas-measurement test passes and reports a number in the PR description; if a setter is added, a round-trip test for it (matching the setMinStake/setUnbondingPeriod test shape).


Deliverables

  • modelId known to DINTaskCoordinator/DINTaskAuditor; per-model stake floor enforced in registerDINaggregator/registerDINAuditor
  • Concurrent-registration cap enforced via a new per-validator counter on DinValidatorStake, gated by the existing slasher-authorization trust boundary
  • Both unset-default cases (modelMinStakeBounds unset, maxConcurrentRegistrationsPerStakeUnit == 0) leave registration behavior unchanged from today
  • Frivolous-dispute forfeiture and DINTaskAuditor's reward-settlement treasury share both forward to DinTreasury immediately (burn/treasury split mirroring DinValidatorStake.slash()), not just accrue locally
  • Upheld-dispute bounty top-up: a resolved approach (new DinTreasury role, alternate funding source, or explicit out-of-scope call), flagged for Umer's review given it touches DinTreasury's access control
  • commitAuditScore gas cost measured and folded into the network-fee-floor recommendation; the floor lands as a real DAO-settable contract parameter
  • forge build && forge test green, full suite
  • PR description(s) reference #37, #38, #43, #78 as appropriate and note every open-question resolution called out above

Submission

Three largely-independent pieces of work touching different files — submit as separate PRs, one per issue closed (#37, then #43/#38's shared treasury-forwarding piece, then #78), rather than one large PR. This matches how #124-127 were handled and makes review tractable given (2)'s DinTreasury access-control question needs its own focused look.

Notes

  • Flag any change to DINTaskCoordinator.sol/DINTaskAuditor.sol/DinValidatorStake.sol/DinTreasury.sol for Umer's review given cryptoeconomic sensitivity, same as prior tasks in this area (task_210726_5, task_210726_6, task_100926_11).
  • Re-pull develop and re-run forge test right before opening each PR — this area of the codebase has had five merges in the last 48 hours (#110, #117, #129, #131, #133, #134).
  • If either open question in (1b) or (2b) turns out to be bigger than expected once you're in the code, say so early rather than guessing — both are flagged above as genuinely undecided, not just unwritten.

Audit (shared with task_100926_13)

Pre-task audit: P3 issues #36-43, #75, #78 (2026-09-10)

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.


What changed between the 2026-09-08 round and today

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).


Per-issue audit

#36 — P3 umbrella (mechanism design tracking)

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.

#37 — P3 staking

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.

#38 — P3 slashing

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.

#39 — P3 scoring

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.

#40 — P3 auditing

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.

#41 — P3 rewards

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.

#42 — P3 tokenomics

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).

#43 — P3 fees & treasury

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).

#75 — Fair-launch distribution (long-running)

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.

#78 — Gas simulation for network fee

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.


Closure recommendations

  • 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.