Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@ MAINNET_RPC_URL=
# S5_RECIDIVISM_THRESHOLD=3 # slashes in window to trigger S5 jail (default: 3)
# S5_JAIL_DURATION=604800 # jail duration in seconds (default: 604800 = 7 days)
# S6_NO_PARTICIPATION_THRESHOLD=3 # no-participation events before S6 fires (default: 3)
# S5_GLOBAL_WINDOW=604800 # seconds over which S5 counts partial slashes from ALL slasher contracts (default: 604800 = 7 days; 0 with threshold 0 = off)
# S5_GLOBAL_THRESHOLD=6 # partial slashes in that window to trigger S5 jail (default: 6; 0 with window 0 = off)
3 changes: 2 additions & 1 deletion Developer/design/MECHANISM_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,14 @@ Grounding: everything below references `foundry/src/` as of `develop`.
| S2 | Missed T1/T2 aggregation submission | Aggregator | On-chain (already implemented) | Partial |
| S3 | Score deviation beyond threshold from cross-auditor median (per model, per GI) | Auditor | On-chain comparison at `finalizeEvaluation` — requires median computed on-chain | Partial → Full on repeat |
| S4 | Invalid aggregation: submitted T1/T2 CID whose recomputation from inputs fails verification | Aggregator | Off-chain challenge + on-chain dispute (S4 is the main dispute-resolution consumer) | Full |
| S5 | Repeated liveness failures (≥ `r` partial slashes within `w` GIs) | Both | On-chain counter | Full + jail/blacklist |
| S5 | Repeated liveness failures (≥ `r` partial slashes within `w` GIs from one slasher contract, or ≥ `r_g` within a time window `w_g` across all models; issue No. 193) | Both | On-chain counter | Full + jail/blacklist |
| S6 | Registration without capacity (registers, never participates, across models) | Both | On-chain counter | Partial, escalating |

### Penalty tiers

- **Partial slash:** fixed fraction of `minStake` (e.g., 25–50%), not the flat `minStake()` used today — today's flat amount fully unstakes a floor-staked validator for one missed vote, which over-punishes liveness faults. Make the fraction a DAO parameter.
- **Full slash:** entire slashable stake (active + unbonding) + blacklist. Reserved for provable malice (S4) and recidivism (S5).
- *Implementation note (issue No. 193 review):* the S5 escalation in `DinValidatorStake.slashPartial` slashes `MIN_STAKE` (not the entire slashable stake) and jails for `s5JailDuration` (7 days by default) rather than blacklisting. Recorded here, not changed; aligning the two is a mechanism decision.
- **Jailing:** `_syncValidatorStatus` already produces a Jailed state below min stake; keep — jailed validators cannot register until they top up.

### Slashed-stake destination (resolved, P3-4.2)
Expand Down
12 changes: 6 additions & 6 deletions Developer/design/adversarial-threat-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ entry in `foundry/test/P3Adversarial.t.sol`.
|---|---|
| **Attacker** | Validator repeatedly misses S1 votes (or S2 submissions) across GIs |
| **Capability** | Registers each GI, never votes; tolerates a partial slash each time |
| **Protocol response today** | `slashPartial` in `DinValidatorStake.sol:294` maintains a per-validator, **per-slasher-contract** rolling GI list (`_partialSlashGIs[validator][msg.sender]`). When slashes within the last `s5RecidivismWindow` (default: 5) GIs for that slasher reach `s5RecidivismThreshold` (default: 3), the slash is escalated to full `MIN_STAKE` and the validator is jailed for `s5JailDuration` (default: 7 days). `isValidatorActive` returns `false` while jailed, blocking re-registration. |
| **Protocol response today** | `slashPartial` in `DinValidatorStake.sol:340` maintains a per-validator, **per-slasher-contract** rolling GI list (`_partialSlashGIs[validator][msg.sender]`). When slashes within the last `s5RecidivismWindow` (default: 5) GIs for that slasher reach `s5RecidivismThreshold` (default: 3), the slash is escalated to full `MIN_STAKE` and the validator is jailed for `s5JailDuration` (default: 7 days). `isValidatorActive` returns `false` while jailed, blocking re-registration. |
| **Expected test outcome** | **DEFENDED** — the test runs the attacker through exactly `s5RecidivismThreshold` GIs of missed votes (3 missed GIs). Escalation fires on the **3rd miss** (the threshold-th slash, not threshold+1). After the 3rd slash, `isValidatorActive` is `false` and a 4th registration attempt reverts. |
| **Trust assumption** | S5 counter is per-slasher-contract (per model). A validator who spreads misses across different models' task contracts (≤ `s5RecidivismThreshold − 1` per model per window) never reaches the escalation threshold for any single model. This is a gap in the multi-model case (see gaps-before-audit). |

Expand Down Expand Up @@ -139,10 +139,10 @@ entry in `foundry/test/P3Adversarial.t.sol`.
| Field | Value |
|---|---|
| **Attacker** | Validator registered across multiple models, spreading misses so no single model's S5 ring reaches threshold |
| **Capability** | Two or more models' task contracts are separate slashers; `_partialSlashGIs[validator][msg.sender]` is namespaced per slasher (`DinValidatorStake.sol:309`). With default threshold 3, ≤ 2 misses per model per window avoids S5 on every individual model. |
| **Protocol response today** | None — S5 is per-slasher-contract, not aggregate across models. A validator can miss `(s5RecidivismThreshold − 1) × N` total votes across N models before any single model escalates. |
| **Expected test outcome** | **KNOWN GAP** ([#193](https://github.com/InfiniteZeroFoundation/DevNet/issues/193)) — the test sets up two task contracts (two models), has a validator miss threshold−1 votes on each, and asserts S5 never fires and `isValidatorActive` remains true. |
| **Trust assumption** | S5 counter would need to be aggregated across all models (or moved to a global platform-level counter) to close this gap. |
| **Capability** | Two or more models' task contracts are separate slashers; `_partialSlashGIs[validator][msg.sender]` is namespaced per slasher (`DinValidatorStake.sol:355`). With default threshold 3, ≤ 2 misses per model per window avoids S5 on every individual model. |
| **Protocol response today** | Two-level S5 ([#193](https://github.com/InfiniteZeroFoundation/DevNet/issues/193)): besides the per-slasher GI ring, `slashPartial` counts every partial slash for the validator in one time-windowed ring shared by all slasher contracts (`_partialSlashTimes`; `s5GlobalWindow` 7 days, `s5GlobalThreshold` 6 by default). Reaching either threshold escalates (full `MIN_STAKE` + jail) and emits `ValidatorEscalatedS5Global` when only the global level fired. The same ring also adds up one model's S1 (auditor contract) and S2 (coordinator) misses. |
| **Expected test outcome** | **DEFENDED above the global threshold.** With defaults, a validator escalates on its 6th partial slash within 7 days, however the misses are spread (`SlashingInvariants.t.sol` `test_s5Global_escalatesAcrossSlashers`, `test_s5Global_addsUpTwoSlashersOfOneModel`). Below it the gap is bounded, not closed: up to 5 misses across models in a window only pay their partial slashes. PR #218's `test_knownGap_perSlasherS5Evasion` (2 models × 2 misses = 4 < 6) still passes as written; when it rebases it should spread misses until the global count reaches `s5GlobalThreshold` and assert escalation (`test_defended_…`). |
| **Trust assumption** | `s5GlobalWindow` / `s5GlobalThreshold` are calibrated (placeholders until [#155](https://github.com/InfiniteZeroFoundation/DevNet/issues/155)). On a proxy upgraded from an earlier version both read 0 (global level off) until the owner sets them. |

---

Expand All @@ -169,7 +169,7 @@ entry in `foundry/test/P3Adversarial.t.sol`.
| Decentralized dispute adjudication | [#181](https://github.com/InfiniteZeroFoundation/DevNet/issues/181) | Row 7 (owner suppresses dispute) and bond-locked-forever variant | `resolveDispute` gated by multi-sig, DAO committee, or fraud-proof; timeout added for unresolved disputes |
| Dispute timeout for unresolved disputes | [#181](https://github.com/InfiniteZeroFoundation/DevNet/issues/181) | Row 7 (owner never resolves → bond locked) | `expireDispute` (or a new function) reachable when `d.resolved == false` after a deadline |
| BlockFLow duplicate-update scoring | [#39](https://github.com/InfiniteZeroFoundation/DevNet/issues/39) | Row 10 (duplicate-update gaming) | BlockFLow fold-in scoring implemented |
| Per-slasher S5 ring evasion | [#193](https://github.com/InfiniteZeroFoundation/DevNet/issues/193) | Row 11 (multi-model S5 evasion) | S5 counter aggregated globally or per-validator across models |
| Per-slasher S5 ring evasion | [#193](https://github.com/InfiniteZeroFoundation/DevNet/issues/193) | Row 11 (multi-model S5 evasion) | **Flipped by task_061026_20 Part C:** per-validator global ring across all slashers (7 days / 6 by default). Misses below the global threshold remain bounded by partial slashes; thresholds pending #155 |
| Mainnet VRF for dispute and batch seeds | [#178](https://github.com/InfiniteZeroFoundation/DevNet/issues/178) | Rows 2, 6, 8 (trust assumption TA-4) | `lockDisputeSeed`, `lockAggSeed` and `lockAuditSeed` upgraded to VRF or multi-party commit-reveal |

---
Expand Down
2 changes: 1 addition & 1 deletion Developer/tasks/task_061026_20.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ There are two ways back out of the warn band: a fold that actually saves bytes,
- 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. `DinValidatorStakeV2` inherits them. `UpgradeValidation.t.sol` and the `DeployPlatform.t.sol` upgrade tests must stay green.
- **Storage.** Append the new variables before `__gap` (`:174`, 50 slots) and shrink the gap by the slots used. *(Amended at PR No. 236: `__gap` stays at 50. Nothing on `develop` is deployed yet, so per the "Core rules" in `storage_layout.md` new variables go above `__gap` without shrinking it, as `07fd597` did for `DinEmission`.)* `DinValidatorStakeV2` inherits them. `UpgradeValidation.t.sol` and the `DeployPlatform.t.sol` upgrade tests must stay green.
- **Setter.** `setS5GlobalParams(window, threshold)`, validated like `setS5RecidivismParams` (`:583-594`).
- **Defaults.** `s5GlobalWindow = 7 days` and `s5GlobalThreshold = 6` (2 × `s5RecidivismThreshold`). These are placeholders until #155.
- Set them in `initialize`.
Expand Down
2 changes: 1 addition & 1 deletion Documentation/public/roles/dinrep.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Optimism Sepolia is a public chain, so there is no anvil step. Instead you need

- **Funds.** The address needs Optimism Sepolia ETH. A full deploy is 22 transactions: seven implementations, seven proxies, and eight wiring calls. Each proxy creates its own ProxyAdmin inside its constructor, so the ProxyAdmins need no transactions of their own.

- **Tokenomics parameters.** `DeployPlatform.s.sol` reads 11 tokenomics settings from the environment: `DIN_PER_ETH`, `MINT_CAP`, the four `EMISSION_*` keys, `MIN_STAKE`, the three `S5_*` keys and `S6_NO_PARTICIPATION_THRESHOLD`. Any key that isn't set falls back to the in-code default; for example, `MINT_CAP` defaults to `0`, which means uncapped. Set the values chosen for this network in `.env.sepolia_op_devnet` (loaded above) or in `foundry/.env` (forge loads it automatically). The script logs an `[INFO] <KEY> not set` line for each missing key and ends with a `--- Effective tokenomics ---` block. See [DeployPlatform.md](../../technical/contracts/foundry/script/DeployPlatform.md) for every key and its default.
- **Tokenomics parameters.** `DeployPlatform.s.sol` reads 13 tokenomics settings from the environment: `DIN_PER_ETH`, `MINT_CAP`, the four `EMISSION_*` keys, `MIN_STAKE`, the three `S5_RECIDIVISM_*`/`S5_JAIL_DURATION` keys, the two `S5_GLOBAL_*` keys and `S6_NO_PARTICIPATION_THRESHOLD`. Any key that isn't set falls back to the in-code default; for example, `MINT_CAP` defaults to `0`, which means uncapped. Set the values chosen for this network in `.env.sepolia_op_devnet` (loaded above) or in `foundry/.env` (forge loads it automatically). The script logs an `[INFO] <KEY> not set` line for each missing key and ends with a `--- Effective tokenomics ---` block. See [DeployPlatform.md](../../technical/contracts/foundry/script/DeployPlatform.md) for every key and its default.

**2. Deploy:**

Expand Down
Loading
Loading