Skip to content

[staging] Tornado Cash integration — validate before dev (reindex required)#2008

Closed
Zeugh-eth wants to merge 17 commits into
devfrom
integration/tornado-cash
Closed

[staging] Tornado Cash integration — validate before dev (reindex required)#2008
Zeugh-eth wants to merge 17 commits into
devfrom
integration/tornado-cash

Conversation

@Zeugh-eth

Copy link
Copy Markdown
Member

Draft / staging branch. Bundles the full Tornado Cash integration on top of the dev-based integration head so it can be deployed and validated as a unit (its own Ponder instance + whitelabel frontend) before anything reaches dev. Do not merge until the checklist passes.

⚠️ Ponder infra impact (read first)

Area Change Action needed
ponder.schema.ts UNCHANGED — writes only to existing accountPower + votingPowerHistory tables. No new tables/columns. No migration
ponder.config.ts / config/torn.config.ts TORN token + governor registered as contracts by the base integration (#2002). This bundle adds no new contracts, chains, or event sources. The TornadoVault is an in-handler address constant for lock classification — not a watched Ponder contract. none beyond base
Indexing logic (src/indexer/torn/erc20.ts Transfer handler + new eventHandlers/delegation.ts helper) CHANGEDdelegatedSupply now includes the Vault and per-account voting power is derived from lock/unlock Transfers. FULL REINDEX required (recompute of historical data — not a schema/architecture change)

No hot-path RPC calls were added (per the anticapture-indexer skill).

What's bundled

Validation checklist (gate before → dev)

  • pnpm --filter=@anticapture/indexer typecheck && pnpm --filter=@anticapture/indexer lint
  • Full reindex completes without crashes; proposal count = 67
  • torn-reconcile.ts in full mode (API_BASE=...) → 0 FAIL (esp. delegatedSupply ≈ Σ lockedBalance, sampled accountPower.votingPower == lockedBalance(account))
  • Deploy preview with a dedicated Ponder instance + the TORN whitelabel frontend; smoke-test every page
  • Resolve treasury accounting (see INTEGRATION.md — governor commingles treasury + locks)

Deferred (notes in apps/indexer/src/indexer/torn/INTEGRATION.md)

Re-vote tally drift (needs indexer test) · treasury accounting · staking rewards · vote-extension tracking · proposal-target detector (#2005).

Whitelabel

Intended to be served via its own whitelabel frontend — requires whitelabel: {} + hostnames in shared/dao-config/torn.ts (see RESEARCH.md §2; hostnames alone 404s via isWhitelabelDao).

pikonha and others added 14 commits June 28, 2026 18:58
…p roadmap

Companion to INTEGRATION.md. Documents the Tornado Cash custom-governor
anatomy, the indexer->API->dashboard data funnel, on-chain parameter
verification, and a prioritized next-steps roadmap (proposal-target
verification for the 2026-06-25 Proposal 67 attack, per-account voting
power, and correctness fixes raised in review).
- Tech funnel: correct to API -> Gateful -> @anticapture/client -> Dashboard
  (Kubb reads Gateful's live OpenAPI; kubb.config.ts:29-55).
- White-label: require whitelabel:{} in addition to hostnames, or the
  /whitelabel/[daoId] route 404s via isWhitelabelDao (whitelabel.ts:37-41).
…kfill

- delegatedSupply: note locks route to TornadoVault post-v2; indexer
  watches governor only (erc20.ts:150-159) -> add gap #6 (watch Vault too).
- Date-scope the March 2026 backfill counts; flag proposals 66-67 (incl.
  the June 2026 attack) need a re-run.
- Frame Proposal 67 as a vanity-prefix spoof (shared first 15 hex chars,
  diverges at 16th); detector in §5 must match leading prefixes, not typos.
- Funnel: state governor-only delegatedSupply today (erc20.ts:150-159) and
  mark TornadoVault transfer tracking as TODO (consistent with §4 gap #6).
P2 voting-power roadmap: trigger lockedBalance snapshot on BOTH
RewardUpdateSuccessful and RewardUpdateFailed (updateRewards modifier
emits one or the other via try/catch, but lock/unlock changes balance
either way); reconcile with governor+Vault transfers for pre-v3 history.
…ow-up)

- TORNClient.calculateQuorum: count forVotes + againstVotes (matches the
  on-chain Governance.state() quorum rule); update status docstring.
- dao-config: proposal threshold 1,000 TORN (was 25,000); voting delay 75s
  (was ~1 block/12s); changeVote=true (re-voting overwrites the prior
  receipt on-chain).

Refs review on #2002.
- rules.logic: "All Votes Cast" (was "For") to match calculateQuorum now
  counting forVotes + againstVotes — dashboard no longer explains the
  opposite quorum rule.
- VOTE_MUTABILITY: LOW + copy reflecting that TORN allows changing votes
  (consistent with rules.changeVote=true); drop stale requirements.
- attackExposure GOV_FRONTEND_RESILIENCE: remove the 'immutable votes'
  claim (votes are mutable) — keeps the unhardened-interface point.
Tornado voting power = lockedBalance (no DelegateVotesChanged). Derive it
from lock/unlock TORN Transfers in erc20.ts:
- Treat governor (pre-v2) AND TornadoVault (post-v2) as lock sinks; the
  non-sink side is the user whose voting power changes. Fixes delegatedSupply
  which previously tracked governor-only and missed ~2.6M TORN in the Vault.
- New lockedVotingPowerChanged handler populates accountPower.votingPower +
  votingPowerHistory (mirrors delegatedVotesChanged); deterministic, no
  hot-path RPC calls (indexer-skill safe).

Requires a full reindex. Re-vote tally fix deferred (needs indexer test) —
see INTEGRATION.md. Verify with the reconciliation smoke test.
TreasuryAddresses[TORN] is empty; governor commingles DAO treasury with
pre-v2 user locks so its balance isn't a clean treasury figure. Document
the correct approach + flag to confirm attack-profitability uses the
on-chain liquid-treasury call vs the indexed metric.
@vercel

vercel Bot commented Jun 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
anticapture-storybook Ready Ready Preview, Comment Jun 30, 2026 1:13am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
anticapture Ignored Ignored Jun 30, 2026 1:13am

Request Review

@railway-app

railway-app Bot commented Jun 30, 2026

Copy link
Copy Markdown

This PR was not deployed automatically as @Zeugh-eth does not have access to the Railway project.

In order to get automatic PR deploys, please add @Zeugh-eth to your workspace on Railway.

scripts/torn-reconcile.ts diffs each TORN datapoint against on-chain
truth (params, quorum semantics, custody, per-account voting power).
On-chain-only by default; full reconciliation with API_BASE set. Exit 1
on any mismatch — use as the deploy gate after reindex.
@Zeugh-eth

Copy link
Copy Markdown
Member Author

🔍 Proposal 67 — independent security verification (supports the launch)

The attack that motivates this integration is now verified with Foundry fork tests in blockful/dao-proposals#89 (src/tornado/proposals/67/):

  • Contentgovernance()/staking() on the proposal's target are attacker vanity look-alikes (share the 7-byte prefix with the real contracts, but ≠ them).
  • Powers — only the attacker can nullifyBalance (zero relayer stakes / kill-switch); the real DAO governance is locked out.
  • Treasury — the attacker cannot move treasury assets by any path (withdraw / transferFrom / unlock / upgrade all revert); the real governance proxy can drain the full ~7.32M TORN (contrast).

→ Confirms protocol attack, not treasury — on-chain-verified, backing the article/thread claims. A human-readable report (audit.html) ships in that PR.

7 fork tests, all green (forge test --match-path "src/tornado/proposals/67/**"). Requires a non-censoring MAINNET_RPC_URL (some public RPCs block Tornado calls).

@pikonha pikonha closed this Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants