The DIN staking mechanism is the protocol's validator collateral system. It provides:
- economic commitment from validators;
- validator eligibility checks for Aggregators and Auditors;
- slashing for faults and non-performance;
- delayed exits through unbonding;
- emergency isolation of dangerous validators through blacklisting.
This mechanism is centered on DinValidatorStake.sol, but it only works correctly when coordinated with:
DinCoordinator.solDINTaskCoordinator.solDINTaskAuditor.sol- governance or DAO-controlled admin processes
The key architectural principle is simple:
validator participation should be controlled by lifecycle state, not only by token balance.
That matters especially for exits, slashing, blacklisting, and controlled unblacklisting.
Target design for what comes next (governable parameters, per-model stake, jailing, slashed-stake destination) lives in
Developer/design/staking-design.md; this document describes what is implemented ondevelop.
stateDiagram-v2
[*] --> Active : stake ≥ MIN_STAKE (default 10 DIN)
Active --> Exiting : unstake() → pending withdrawal\n(UNBONDING_PERIOD, default 7 days, still slashable)
Active --> Exiting : slashed below MIN_STAKE
Exiting --> Active : re-stake above floor
Exiting --> [*] : claimUnstaked() after unbonding
Active --> Jailed : S5 recidivism escalation\n(or jailValidator by a slasher)
Jailed --> Active : reactivate() after jail period\n(stake ≥ MIN_STAKE)
Active --> Blacklisted : owner blacklistValidator()
Exiting --> Blacklisted : owner blacklistValidator()
Jailed --> Blacklisted : owner blacklistValidator()
Blacklisted --> Active : owner unblacklistValidator()\n(via status sync; Jailed if jail still running)
_syncValidatorStatus() recomputes status on every state-modifying call: Blacklisted is sticky until unblacklisted; Jailed is sticky while jailedUntil is in the future; pending withdrawals force Exiting; activeStake ≥ MIN_STAKE gives Active; a nonzero balance below the floor is Exiting; zero is None.
| Mechanic | Behaviour on develop (foundry/src/) |
|---|---|
| Stake entry | stake(amount) requires amount ≥ MIN_STAKE (default 10 DIN, owner-settable via setMinStake), rejects blacklisted callers, transfers DIN in, syncs status |
| Unbonding | unstake(amount) moves stake to a single pending withdrawal with withdrawAvailableAt = now + UNBONDING_PERIOD (default 7 days, owner-settable, not retroactive); claimUnstaked() pays out after maturity |
| Slashability window | slashableStakeOf = activeStake + pendingWithdrawals — exit does not escape slashing until the claim |
| Slashing | Non-blocking: caps at the slashable balance, consumes active stake before pending withdrawals, returns the actual amount, emits ValidatorSlashed. Only authorised slasher contracts may call it |
| Slashed-stake destination | 50% burned (DinToken.burn), 50% to slashTreasury (DinTreasury, wired at deploy); if the treasury is unset, both halves are burned |
| Liveness faults (S1/S2) | slashPartial: an audit vote that was never revealed (S1) or a T1/T2 aggregation CID that was never revealed (S2) costs minStake × s1/s2SlashFractionBps (default 30%). Committing without revealing counts the same as not committing; whether it should cost more is open in issue #201 |
| Recidivism (S5) | The same task contract recording s5RecidivismThreshold (3) partial slashes within s5RecidivismWindow (5) GIs escalates to a full MIN_STAKE slash plus a s5JailDuration (7 days) jail |
| Full-severity faults | slash(minStake): aggregator bad consensus, S3 audit-score deviation (behind s3SlashingEnabled, off by default), S4 invalid aggregation / fresh-subgroup timeout |
| No-participation (S6) | recordNoParticipation exists (escalating 10%-per-breach slash past a threshold of 3) but no task contract calls it; parked for testnet per #224, Oct 26 decision, because S1/S2 plus S5 cover missed work. Storage and parameters remain intact; revisit after testnet only for a demonstrated gap without double penalties |
| Eligibility gates | Task contracts require isValidatorActive() at registration, commit/reveal and submission, and filter inactive registrants at batch creation. When configured they also enforce a per-model stake floor (getModelStakeMin) and a concurrent-registration cap ((stake / minStake) × maxConcurrentRegistrationsPerStakeUnit) |
| Blacklist | blacklistValidator/unblacklistValidator are direct owner() (DIN-Representative) actions; blacklist freezes stake/unstake/claimUnstaked while funds remain slashable |
Per-contract references: DinValidatorStake.md, DINTaskCoordinator.md, DINTaskAuditor.md, DinCoordinator.md.
The core staking and validator-lifecycle contract. It:
- holds validator DIN stake;
- tracks
activeStake,pendingWithdrawals, and validator status; - enforces
MIN_STAKE; - enforces delayed withdrawals with an unbonding period;
- keeps pending withdrawals slashable;
- exposes
isValidatorActive()for downstream eligibility checks; - splits every slash 50% burn / 50% treasury (the treasury half is also burned if no treasury is set), tracks S5 recidivism (
slashPartial) and jails on escalation; - stores the governable parameters task contracts read (per-model stake floors, concurrent-registration cap, S5/S6 settings) and validators' X25519 encryption keys;
- allows emergency blacklist and restoration through direct owner authority.
Manages Aggregator participation and task-round execution. It should:
- use
isValidatorActive()when checking whether a validator may register or keep participating; - call
slashPartial()for missed submissions (S2) andslash()for bad consensus or S4 dispute outcomes; - refuse blacklisted, jailed, or exiting validators through the shared stake state.
Manages Auditor participation. It should:
- use
isValidatorActive()as the role eligibility gate; - call
slashPartial()for missed votes (S1) andslash()for S3 score deviation; - refuse blacklisted, jailed, or exiting validators via stake state.
The coordinator is the admin-facing control point for staking-related policy wiring. In the current architecture it:
- mints DIN against ETH deposits (faucet) and for the emission subsidy (
mintEmission, fromDinEmission), under an optionalmintCap; - forwards faucet ETH to
DinFeeRouter; - registers or removes authorized slasher contracts on
DinValidatorStake.
Blacklist and unblacklist actions are not routed through DinCoordinator in the current implementation. They are direct owner() actions on DinValidatorStake.
Governance should not directly act as a slasher for routine faults. Routine faults belong in task contracts via slash().
Governance is the likely long-term authority for blacklist and unblacklist decisions because those are not normal scoring penalties. They are exceptional trust and safety interventions affecting protocol admission and exit rights.
In the current contracts, this authority is represented by the owner() of DinValidatorStake. In practice that owner can later be:
- a DAO-owned multisig;
- a security council;
- a timelocked governance executor;
- or another governance-controlled owner after ownership transfer.
- DIN token
- 18 decimals
DinValidatorStakeis the intended source of truth forMIN_STAKE- downstream contracts should consume
minStake()orisValidatorActive() - downstream contracts should not rely on independent local stake-threshold copies for access control
Validators should be considered eligible for new work only when:
status == Active
A validator is not eligible when it is:
ExitingJailedBlacklisted- or otherwise below active threshold
This is critical because a validator with high raw balance may still be ineligible if it already requested exit.
- Validator acquires DIN.
- Validator approves
DinValidatorStake. - Validator calls
stake(amount). - DIN is transferred into the stake contract.
activeStakeincreases._syncValidatorStatus()sets validator state.- If stake is sufficient and no exit is pending, validator becomes
Active.
Once active, validators register for protocol roles:
- Aggregators through
DINTaskCoordinator - Auditors through
DINTaskAuditor
Those role contracts should:
- check
isValidatorActive(msg.sender); - reject validators that are exiting, jailed, or blacklisted;
- avoid relying only on raw
getStake().
- Validator misses work, submits invalid work, or fails a consensus rule.
- Authorized task contract determines slash amount and reason.
- Task contract calls
DinValidatorStake.slashPartial()(liveness faults, with S5 recidivism escalation) orslash()(full-severity faults). - Slash is applied first against
activeStake, then againstpendingWithdrawalsif needed. - Half the slashed DIN is burned, half sent to
DinTreasury(or also burned if no treasury is set); validator status is resynchronized (or set toJailedon S5 escalation). - Validator may lose
Activestatus if stake falls too low or if exit is already in progress.
- Validator calls
unstake(amount). - Funds move from
activeStaketopendingWithdrawals. withdrawAvailableAtis set.- Validator becomes
Exiting. - Validator cannot be selected for new work.
- Pending amount remains slashable until claimed.
- After the unbonding period ends, validator calls
claimUnstaked().
- An exceptional risk is identified.
- Governance-authorized actor initiates blacklist action.
- Stake contract marks validator
Blacklisted. - Validator becomes ineligible until the stake contract owner later restores them through
unblacklistValidator(). - Existing in-contract funds remain trapped and still slashable unless governance policy explicitly allows a later release path.
These two mechanisms should not be treated as the same thing.
Use slashing for normal protocol faults such as:
- non-submission;
- invalid aggregation;
- invalid audit behavior;
- measurable liveness failures;
- deterministic correctness failures.
Slashing is operational, expected, and usually triggered automatically by task logic.
Use blacklisting for exceptional cases such as:
- repeated malicious behavior across rounds;
- exploit attempts;
- sybil abuse that ordinary slashing does not contain;
- compromised validator keys still being used;
- governance-determined fraudulent operation;
- legal, sanctions, or emergency trust-and-safety requirements if the protocol adopts such policies.
Blacklisting is an emergency governance action, not a routine scoring penalty.
The current codebase now supports a basic blacklist lifecycle, but governance process and policy are still incomplete.
In DinValidatorStake.sol:
blacklistValidator(address)exists;unblacklistValidator(address)exists;- both are restricted by
onlyOwner; - blacklisted validators cannot
stake(),unstake(), orclaimUnstaked(); _syncValidatorStatus()preservesBlacklisteduntilunblacklistValidator()clears it;- slashing can still reduce blacklisted funds.
- no governance review flow on-chain;
- no automatic jail-to-blacklist escalation (jailing comes only from S5 escalation; blacklisting is a manual owner action);
- no release policy for funds locked under blacklist status.
DinValidatorStake still uses DIN_COORDINATOR for slasher management, but blacklist and unblacklist authority now lives directly on the stake contract owner. This is the intended current model: the DIN-Representative's single admin key now; on-chain DIN-DAO governance is deferred to post-mainnet.
This section describes the recommended production-grade blacklist and unblacklist design.
The most likely operational split is:
Task contracts- handle routine slashing and validator-performance penalties
Security council or emergency multisig- can trigger urgent blacklist actions
DAO governance / timelocked admin- confirms, reverses, or finalizes blacklist status
- authorizes unblacklist decisions
This separation is useful because blacklisting is too powerful to leave inside routine liveness logic.
Blacklist and unblacklist should remain onlyOwner on DinValidatorStake for now, with ownership held by the DIN admin and later transferred to a DAO-controlled owner.
Recommended authority:
- current phase: DIN admin / DIN-Representative as stake contract owner;
- later phase: DAO multisig, timelock, or governance executor as stake contract owner;
- optional emergency process layered off-chain before the owner executes blacklist or restoration.
The reason is that blacklist and unblacklist are governance-grade trust and safety decisions, not routine validator-liveness operations.
- Risk is detected by monitoring, community reports, or protocol operators.
- Evidence is reviewed off-chain by the authorized governance or security actor.
- Stake contract owner calls
blacklistValidator(validator). - Stake contract sets status to
Blacklisted. - Validator instantly becomes ineligible for new work.
- Task contracts naturally stop accepting the validator because
isValidatorActive()becomes false. - If needed, slashers may still apply pending penalties to remaining slashable funds.
After emergency action, governance should decide one of:
- maintain blacklist permanently;
- apply additional slashing or other penalties;
- restore validator through unblacklisting if the case was mistaken or remediated.
If governance decides to restore the validator:
- Governance or DAO admin approves restoration.
- Stake contract owner calls
unblacklistValidator(validator). - Stake contract removes blacklist status.
_syncValidatorStatus()recalculates the right state.- Validator becomes:
Activeif sufficiently staked and not exiting;Exitingif stake remains but active conditions are not met;Noneif no usable stake remains.
- Validator must re-enter normal protocol flows subject to role-contract checks.
Important:
- unblacklisting should restore eligibility only if the validator still satisfies normal staking conditions;
- it should not bypass
MIN_STAKE, pending withdrawal state, or jail state; - it should not auto-register the validator in downstream task rounds.
Should coordinate with blacklist state as follows:
- use
isValidatorActive()before registration; - use
isValidatorActive()before accepting submissions or selecting validators; - exclude blacklisted validators from candidate pools;
- continue using
slash()for routine misconduct, not blacklist.
If a validator is blacklisted mid-lifecycle:
- future participation should stop automatically via active-status checks;
- any round-specific cleanup logic may still be needed in task-contract state.
Should follow the same pattern:
- no new Auditor participation if validator is blacklisted;
- no reliance on raw stake balance alone;
- blacklist is treated as immediate ineligibility.
Should remain responsible for:
- slasher authorization;
- validator stake contract wiring.
Blacklist and unblacklist do not currently pass through DinCoordinator.
Off-chain systems are likely to initiate blacklist proposals because they can aggregate evidence that is hard to compute on-chain, such as:
- repeated malicious pattern analysis;
- exploit forensics;
- identity-level sybil clustering;
- key-compromise reports;
- community or governance disputes.
That means blacklist decisions will often begin off-chain and end on-chain through the stake contract owner.
- Aggregator misses a submission.
DINTaskCoordinatorslashes the validator.- Validator remains in the protocol if enough stake remains.
Use case: routine liveness enforcement.
- Validator repeatedly submits bad work across rounds.
- Task contracts apply normal slashes.
- Governance or DIN admin determines behavior is no longer tolerable.
- Stake contract owner blacklists validator.
Use case: escalation from economic penalties to governance exclusion.
- Validator requests unstake.
- Funds move to
pendingWithdrawals. - Before claim, evidence of fault is proven.
- Slasher contract penalizes the pending withdrawal.
Use case: unbonding preserves slashability without needing blacklist.
- Security operators detect key compromise.
- Governance or emergency multisig blacklists validator immediately.
- Validator cannot keep participating while the incident is investigated.
- Later, governance may choose permanent removal or restoration under a new operational policy.
Use case: blacklist as emergency containment, not routine punishment.
- Validator is blacklisted due to bad evidence or false attribution.
- Governance review finds the blacklist incorrect.
- Governance triggers
unblacklistValidator(). - Validator returns only to the status supported by remaining stake state.
Use case: unblacklist as controlled recovery, not automatic forgiveness.
For a well-thought blacklist system, the policy should state at least:
- who may propose a blacklist;
- who may execute a blacklist;
- whether emergency blacklists bypass timelock;
- how evidence is recorded off-chain or on-chain;
- who may unblacklist;
- whether unblacklisting requires a delay or formal vote;
- whether blacklisted funds remain slashable;
- whether blacklisted funds may ever be withdrawn;
- whether a blacklisted validator must re-register in task contracts after restoration.
Without those rules, blacklist power becomes governance ambiguity rather than a security mechanism.
- Make all role contracts rely on
isValidatorActive()rather than raw balance checks for access control. - Define governance policy for emergency blacklist, review, and restoration.
- Decide whether blacklisted pending withdrawals remain forever frozen or can later be released by governance.
- Plan ownership transfer of
DinValidatorStakefrom the current admin to a DAO-controlled owner or timelock.
The DIN staking mechanism now has a working owner-controlled blacklist and unblacklist path alongside unbonding and slashability.
A production-grade approach should continue to treat:
- slashing as routine automated enforcement;
- blacklisting as exceptional governance isolation;
- unblacklisting as a stricter, review-based restoration path.
That separation keeps validator operations predictable while preserving a strong emergency control when a validator becomes too risky to leave in normal circulation.