Skip to content
Open
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
6 changes: 6 additions & 0 deletions Developer/BACK_LOG.md

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Developer/design/adversarial-threat-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ entry in `foundry/test/P3Adversarial.t.sol`.
|---|---|
| **Attacker** | Sybil operator: k identities, each staking MIN_STAKE, targeting k-of-n seats in one batch |
| **Capability** | k × MIN_STAKE total stake; no control of randomness — batch assignment is `_shuffleAddressArray(valPool, keccak256(seed, "AGG_ADDR"))` (`DINTaskCoordinator.sol:582`) and auditor seat assignment is `_shuffleAddressArray(auditorPool, keccak256(seed, "AUD_ADDR"))` (`DINTaskAuditor.sol:871`), where `seed` is the per-GI `aggSeed`/`auditSeed`: anchored to a future block (`block.number + disputeSeedDelay`) at the preceding GI-state transition and locked permissionlessly via `lockAggSeed`/`lockAuditSeed` (issue #156 H-2, PR #191). `autoCreateTier1AndTier2` and `createAuditorsBatches` are still `onlyOwner`, but revert unless the seed is locked, so the block the owner calls in no longer changes the shuffle. |
| **Protocol response today** | No Sybil-specific defence beyond the per-identity stake requirement. Owner block-picking of the seat shuffle is closed by the H-2 fix: the seed is fixed at a block not yet mined when it is anchored, and batch creation reads the stored seed (`test_createAuditorsBatches_timingIndependent` / `test_autoCreateTier1AndTier2_timingIndependent` in `SecurityFindings.t.sol`). Residuals remain (BL-26): the owner can decline to lock a seed they dislike and let it re-anchor after the ~256-block `blockhash` window (a repeatable re-roll while nobody else locks — aggregator/auditor dincli now locks the seed from the validator side to narrow this), and a validator can unstake between lock and batch creation to reshape the active pool. The seed also trusts the sequencer not to grind `blockhash` (TA-4). None of this changes the Sybil-cost framing below — k identities still win seats at the hypergeometric rate. |
| **Protocol response today** | No Sybil-specific defence beyond the per-identity stake requirement. Owner block-picking of the seat shuffle is closed by the H-2 fix: the seed is fixed at a block not yet mined when it is anchored, and batch creation reads the stored seed (`test_createAuditorsBatches_timingIndependent` / `test_autoCreateTier1AndTier2_timingIndependent` in `SecurityFindings.t.sol`). Post-lock pool reshaping is closed by issue #243 (PR [#253](https://github.com/InfiniteZeroFoundation/DevNet/pull/253)): batch layout is now derived from the fixed per-GI registration list (`dinAggregators[_GI]`, `dinAuditors[_GI]`) rather than the live active pool, so an unstake between seed-lock and batch-creation forfeits only the leaver's own seat — every other validator's batch and seat position is determined solely by the fixed list and the locked seed (`test_aggregator_unstakeOnlyDisplacesOwnSeat` in `BatchAssignmentStability.t.sol`). Residual: the owner can still decline to lock a seed they dislike and let it re-anchor after the ~256-block `blockhash` window (a repeatable re-roll while nobody else locks — aggregator/auditor dincli now locks the seed from the validator side to narrow this). The seed also trusts the sequencer not to grind `blockhash` (TA-4). None of this changes the Sybil-cost framing below — k identities still win seats at the hypergeometric rate. |
| **Expected test outcome** | **COST-BOUNDED** — the test sets up n registered identities, k of which are Sybil. P(Sybil controls ≥ majority of a 3-seat batch) is hypergeometric: ≈ 3p² − 2p³ where p = k/n (not k/n). The test asserts the fraction of batches where Sybil controls majority and the total stake cost is k × MIN_STAKE. |
| **Trust assumption** | n (total registered validators) large enough that k × MIN_STAKE to control majority is economically prohibitive relative to poisoned-model gain (TA-3); seat-shuffle seed not ground by the sequencer (TA-4). |

Expand Down Expand Up @@ -166,6 +166,7 @@ entry in `foundry/test/P3Adversarial.t.sol`.
| T1 wrong CID — finalCID not replaced, dissenter not refunded | [#194](https://github.com/InfiniteZeroFoundation/DevNet/issues/194) (#181 covers who adjudicates, not repairing the outcome) | Row 5 (T1 wrong CID) | `settleRecomputation(confirmed=true)` replaces `finalCID` and refunds the honest dissenter |
| Dual-role registration guard | [#180](https://github.com/InfiniteZeroFoundation/DevNet/issues/180) | Row 6 (cross-role + owner block-selection) | **Flipped by task_061026_20 Part B:** per-address guard in `registerDINAuditor` (`TA_DualRoleNotAllowed`). The Sybil variant (second funded address) remains, bounded by stake cost |
| Stronger batch-shuffle seed | [#156](https://github.com/InfiniteZeroFoundation/DevNet/issues/156) | Rows 2, 6 (owner block-selection) | **Flipped by PR #191:** shuffles seeded by the locked, future-block `aggSeed`/`auditSeed` rather than at owner-controlled call time. Residuals (decline-to-lock re-roll, post-lock pool reshaping) tracked in BL-26 |
| Post-lock pool-reshaping gap | [#243](https://github.com/InfiniteZeroFoundation/DevNet/issues/243) | Row 2 (Sybil batch control) | **Flipped by PR [#253](https://github.com/InfiniteZeroFoundation/DevNet/pull/253):** batch layout derived from fixed registration list (`dinAggregators[_GI]`) not the live active pool; an unstake after seed-lock forfeits only the leaver's own seat (position-stable seat assignment). Residual: decline-to-lock re-roll still open (TA-4). Covered by `BatchAssignmentStability.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 |
Expand Down
78 changes: 78 additions & 0 deletions Developer/tasks/task_141026_23.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Task: Fix Batch-Assignment Pool-Reshaping Gap — Shuffle Fixed Registration List, Not Live Active Pool (#243)

**ID:** task_141026_23
**Assigned to:** Unassigned
**Created:** 2026-10-14
**Status:** Open (unassigned)
**Dates:** No fixed deadline. Can start immediately; no cross-task dependency.
**Repo:** https://github.com/InfiniteZeroFoundation/DevNet
**Base branch:** `develop` — spec written against commit `aa76ce18` (2026-10-14); re-pull before starting.
**Roadmap ref:** P3-6 (security hardening before audit); BL-26 (`Developer/BACK_LOG.md`)
**Tracking:**
- [#243](https://github.com/InfiniteZeroFoundation/DevNet/issues/243) (closes on PR merge)
- Post progress updates and the PR link in a comment on #243.

---

## Background

`DINTaskCoordinator.autoCreateTier1AndTier2` and `createAuditorsBatches` build the validator pool by calling `_activeAggregatorPool` / `_activeAuditorPool` **at call time**, after the batch-creation seed is already public. A validator can unstake between the seed lock and the batch-creation call. This changes the active-pool size and causes Fisher-Yates to re-roll the entire batch assignment for every other participant — not just the leaving validator's own seat.

The fix is already available in the codebase: `_assignFreshSubgroup` (the dispute re-eval draw, landed in PR #171) shuffles a **fixed per-GI registration list** with the locked seed and skips inactive entries while filling seats. An unstake under that scheme only drops the leaver's own slot; everyone else's assignment is already committed.

This is a **distinct gap** from the re-roll-by-declining-to-lock residual tracked in issue #178. It requires no GI-abort design (issue #222) as a prerequisite.

---

## Status check at `aa76ce18`

| Contract | Function | Gap |
|---|---|---|
| `DINTaskCoordinator.sol` | `autoCreateTier1AndTier2` (~L714–780) | Calls `_activeAggregatorPool()` at create time after seed is locked |
| `DINTaskCoordinator.sol` | `createAuditorsBatches` (~L820–880) | Calls `_activeAuditorPool()` at create time after seed is locked |
| `DINTaskCoordinator.sol` | `_assignFreshSubgroup` | Correct pattern — uses fixed `dinAggregators[_GI]` list + seed |

---

## Implementation

### 1. Aggregator batch creation

In `autoCreateTier1AndTier2`:

- Replace the `_activeAggregatorPool()` call with `dinAggregators[_GI]` (the fixed list of aggregators who registered for this GI).
- Shuffle `dinAggregators[_GI]` using the locked seed (already available: `aggSeed[_GI]`), following the same pattern as `_assignFreshSubgroup`.
- Walk the shuffled list to fill T1/T2 seats, **skipping** any entry where `!stake.isValidatorActive(addr)` — this is the same skip-inactive pattern `_assignFreshSubgroup` uses.
- An inactive validator (unstaked after registration) gets no seat; everyone else keeps theirs.

### 2. Auditor batch creation

Apply the same change in `createAuditorsBatches`:

- Replace `_activeAuditorPool()` with the per-GI registered auditor list.
- Shuffle with `auditSeed[_GI]`, skip inactive entries.

### 3. No change to shuffle helper

The existing `_shuffleAddressArray(arr, seed)` helper (task_240926_18 Part B) already takes an explicit seed. No new helper needed.

---

## Deliverables

- [ ] `autoCreateTier1AndTier2` shuffles `dinAggregators[_GI]` (or equivalent registered list) with the locked `aggSeed[_GI]`; inactive validators are skipped, not re-rolled
- [ ] `createAuditorsBatches` applies the same pattern with `auditSeed[_GI]` and the per-GI registered auditor list
- [ ] Neither function calls `_activeAggregatorPool` / `_activeAuditorPool` at batch-creation time
- [ ] Tests (in an existing adversarial or integration test file, or a new `BatchAssignment.t.sol`):
- [ ] Unstake between seed lock and `autoCreateTier1AndTier2` does **not** change any other participant's batch seat
- [ ] The unstaked validator gets no seat and the remaining seats are filled from the rest of the registered list
- [ ] Batch assignments with the same seed are identical regardless of when `autoCreateTier1AndTier2` is called (timing independence — same pattern as task_240926_18 Part B's determinism test)
- [ ] `forge build && forge test` green, full suite
- [ ] If bundled ABIs changed, refresh `dincli/abis/`
- [ ] `Developer/BACK_LOG.md` BL-26 status updated to "Fixed — PR #<n>"

## Notes

- Do not change the seed-lock mechanism itself. The seed-lock flow (permissionless `lockAggSeed` / `lockAuditSeed`) is unchanged; this fix is only about what the shuffle reads as its input pool.
- Do not address the re-roll-by-declining-to-lock residual (issue #178) or the GI-abort design (issue #222) in this PR. Note both as out of scope.
- Flag all `DINTaskCoordinator.sol` / `DINTaskAuditor.sol` changes for Umer's review.
91 changes: 91 additions & 0 deletions Developer/tasks/task_141026_24.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Task: On-Chain Deployment Drift Check — verify foundry/deployments/<network>.json matches the chain (#244)

**ID:** task_141026_24
**Assigned to:** Unassigned
**Created:** 2026-10-14
**Status:** Open (unassigned)
**Dates:** No fixed deadline. Must land before the DevNet 3.0 deploy; should also run after every `UpgradePlatform.s.sol` run.
**Repo:** https://github.com/InfiniteZeroFoundation/DevNet
**Base branch:** `develop` — spec written against commit `aa76ce18` (2026-10-14); re-pull before starting.
**Roadmap ref:** P3 (pre-deploy tooling); BL-33 (`Developer/BACK_LOG.md`)
**Tracking:**
- [#244](https://github.com/InfiniteZeroFoundation/DevNet/issues/244) (closes on PR merge)
- Post progress updates and the PR link in a comment on #244.

---

## Background

Nothing currently verifies that `foundry/deployments/<network>.json` (and the `din_info` snapshot written by `dincli system import-deployments`) still matches what is live on-chain. A stale deployments file after a failed or partial upgrade silently routes calls to the wrong implementation, and there is no quick way to detect it without reading raw storage slots by hand.

This task implements a **read-only drift check** that compares the recorded deployments against the live chain and prints `OK` / `DRIFT` per item.

---

## Preferred approach: `dincli system verify-deployment`

A new subcommand added to `dincli/cli/system.py`, following the pattern of the existing `import-deployments` command. It reads `foundry/deployments/<network>.json` (or the `din_info` config file), connects to the configured RPC, and checks the following for each recorded proxy:

### Check 1 — EIP-1967 implementation slot

Read `bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)` from the proxy address and compare against the recorded implementation address.

### Check 2 — EIP-1967 admin slot

Read `bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)` from the proxy address and compare against the recorded `proxyAdmin*` address.

### Check 3 — Runtime code hash

Fetch `eth_getCode(implementation)` and compute its keccak256. Compare against `keccak256` of the `deployedBytecode` field in the corresponding `foundry/out/<Contract>.sol/<Contract>.json` artifact (immutables section masked to zero). Report `DRIFT` if the hashes differ.

### Check 4 — Owner / DIN-Representative

Call `owner()` on each ownable contract and compare against the recorded owner address.

### Check 5 — Wiring

Verify the key cross-contract links:
- `DinCoordinator.dinToken()` == recorded `dinToken` address
- `DinCoordinator.dinValidatorStake()` == recorded `dinValidatorStake` address
- `DinCoordinator.dinModelRegistry()` == recorded `dinModelRegistry` address
- Slasher authorizations on `DinValidatorStake`: each task-coordinator address listed in the deployments JSON is an authorized slasher.

### Output format

```
[OK] DINTaskCoordinator proxy → implementation 0xabc...
[OK] DINTaskCoordinator proxyAdmin 0xdef...
[DRIFT] DINTaskAuditor implementation: recorded 0x111... actual 0x222...
[OK] DinCoordinator owner 0xghi...
[OK] wiring: DinCoordinator.dinToken → 0xjkl...
```

Exits 0 if all checks pass, non-zero on any `DRIFT` or `ERROR`.

---

## Alternative: forge script

If the dincli approach is too heavy for the use case, a forge script `script/VerifyDeployment.s.sol` is acceptable. It should read the same deployments JSON via `vm.readFile` + `stdJson`, perform the same five check categories above, and output to stdout. Either approach closes the issue.

Whichever path is chosen, record the decision in a comment on #244.

---

## Deliverables

- [ ] `dincli system verify-deployment` (or `script/VerifyDeployment.s.sol`) implements all five check categories above
- [ ] Output is plain text, one line per check, `OK` / `DRIFT` / `ERROR`, usable in CI without color codes (`--no-color` flag or auto-detect via `sys.stdout.isatty()`)
- [ ] Exits non-zero on any `DRIFT` or `ERROR`
- [ ] Tests (using the existing `monkeypatch` + `CliRunner` pattern for dincli, or a Foundry integration test for the forge-script path):
- [ ] All-OK run exits 0 and prints only `[OK]` lines
- [ ] A mismatched implementation slot prints `[DRIFT]` and exits non-zero
- [ ] A mismatched owner prints `[DRIFT]` and exits non-zero
- [ ] `Documentation/technical/testing/` or `Documentation/public/guides/` — add a short section describing when and how to run the drift check
- [ ] `Developer/BACK_LOG.md` BL-33 status updated to "Fixed — PR #<n>"

## Notes

- This check is read-only. It must never send transactions or modify any state.
- It covers only what the deployments JSON records. A clean run does not prove nothing else was deployed or authorized outside that file. Say this in the command's help text.
- Do not attempt to auto-repair drift. Report and exit only.
Loading
Loading