Skip to content

Three dormant/no-op mechanisms awaiting a product decision: disableModel kill-switch, rejectModel fee handling, S6 slashing (M-2, L-4, BL-31) #224

Description

@Abidoyesimze

Three separate findings, batched because they share the same shape: each is a mechanism that exists in some form (an admin function, a fee flow, a slashing path) but nobody has made — or at least documented — the decision on whether/how it's actually meant to be used. None are exploitable bugs; all three just need someone with product/mechanism-design authority to decide, so the code (or the docs) can be made to match.

M-2: DINModelRegistry.disableModel() doesn't reach the live task contracts

modelDisabled[modelId] (DINModelRegistry.sol:119) only gates requestModelRegistration/requestManifestUpdate inside the registry itself (L151, L330). Confirmed by inspection against develop @ 740a613: DINTaskCoordinator.sol has zero references to DINModelRegistry, and DINTaskAuditor.sol's only two references are doc comments, not functional checks. A model the DIN-Representative has disabled — e.g., because its task contracts have a bug wrongfully slashing honest validators — keeps running full GIs completely unaffected. The task contracts' own doc table calls this a "kill-switch," which it currently isn't.

Decision needed: wire the task contracts to check DINModelRegistry.modelDisabled(modelId) before state-changing calls (real fix, requires threading the model ID + registry address into contracts that don't currently know about the registry at all), or explicitly document disableModel as registry-metadata-only so operators stop treating it as a circuit breaker.

L-4: rejectModel() never refunds the registration fee

Checked the full flow: feePaid is recorded at requestModelRegistration() (DINModelRegistry.sol:197) and is never refunded on either outcome — not on rejectModel() (L261-271) and not on approveModel() either (L217-258 doesn't touch feePaid at all). This consistency suggests the fee is intentionally a pay-to-apply cost regardless of outcome (anti-spam), not an oversight specific to rejection — but this was never written down anywhere as a deliberate choice.

Decision needed: confirm non-refundable-regardless-of-outcome is the intended design and document it (in DINModelRegistry.md and/or the dinrep role docs), so a future reader doesn't "fix" it into an inconsistent partial-refund state.

BL-31: S6 no-participation slashing is implemented, tested, but never called

DinValidatorStake.recordNoParticipation() (S6: escalating 10%-per-breach slash past a threshold, L333-354) is fully implemented and has its own tests. Every call site in both task contracts is a comment explaining why it's deliberately not called (DINTaskAuditor.sol:1391, DINTaskCoordinator.sol:1185) — slashPartial (S1/S2) already penalizes the same missed-work behavior, and calling both would double-penalize past MIN_STAKE. Confirmed: zero functional call sites anywhere in foundry/src/.

Decision needed: either S6 is meant to cover a gap S1/S2 don't (e.g., a validator registered but never assigned to any batch, so S1/S2 never trigger on them at all) and needs wiring to that specific case, or it's superseded dead code that should be removed along with its threshold parameter and event, per the slashing taxonomy (#38).

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions