> For the complete documentation index, see [llms.txt](https://docs.intelligenceonchain.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.intelligenceonchain.com/technical-module-guidance/tornado.cash-investigator.md).

# Tornado.Cash Investigator

*How the module works, what its output means, and what it deliberately refuses to do.*

***

## Overview

The Investigator is a guided investigation workstation. Where the Withdrawal Viewer answers *who withdrew* and the Cluster Analyser answers *which recipients look related*, the Investigator supports a full line of enquiry: define a hypothesis, collect the relevant data, see how the candidate set was narrowed, examine why each candidate scored as it did, challenge the automatic clustering, trace funds onward, and export an auditable case file.

The design principle that shapes everything else: **the tool optimises for investigative usefulness and transparency, not for producing the highest possible number of apparent matches.** It does not declare matches. It assembles reviewable evidence and hands the conclusion to the analyst.

***

## What makes it different from the Viewer

The Viewer identifies withdrawals by reading transaction **values** — a transfer out of a pool contract in the right range is a withdrawal. That's sufficient for a recipient table and insufficient for an investigation, because value alone can't tell you the relayer, the fee, the nullifier, or whether the withdrawal was relayed at all, and it blurs the gross denomination with the net amount received.

The Investigator reads the pools' **`Withdrawal` event log** instead:

```
Withdrawal(address to, bytes32 nullifierHash, address indexed relayer, uint256 fee)
```

That gives recipient, nullifier hash, relayer and fee as first-class fields, and lets the *pool contract* establish the denomination rather than inferring it from the transferred amount. Several analyses further down — relayer reuse, fee-percentage behaviour, note-level matching — only exist because of this. It also removes a class of quiet error: net and gross are never conflated, because they're read from different places.

<figure><img src="/files/mEVGRtUmVBkgeSjZpusy" alt=""><figcaption></figcaption></figure>

***

## Evidential posture

This is not a disclaimer bolted on at the end; it's a constraint on the whole system.

Every score, cluster and connection the module produces is a **probabilistic investigative lead**. It does not establish common ownership, control, identity, or a direct relationship between a deposit and a withdrawal. That warning appears on load, before a run, beside every score, next to every automatic cluster, in the conclusion panel, and at the top of every exported report.

The module also constrains its own vocabulary. Terms like *confirmed match*, *identified owner* and *proven connection* are prohibited; the permitted register is *potential connection*, *candidate withdrawal*, *provisional entity*, *investigative lead*, *behavioural similarity*, *corroborating indicator*, and low/moderate/strong **relative** signal. A strong result is described as a strong investigative lead, never as proof. This is enforced by an automated check on the output rather than left to discipline.

"Provisional" is load-bearing throughout. One recipient address is one entity until clustering *proposes* otherwise — and every proposal is reviewable.

***

## The four modes

**Mode 1 — Search for candidate withdrawals.** For when you know the shape of the activity you're looking for but not the addresses. Define per-denomination requirements (minimum, maximum, exact count, mandatory or optional; whether splitting across addresses is permitted), time criteria (date bounds, holding time, time-of-day, day of week, same-hour/same-day grouping, regular intervals, burst activity, FIFO-style sequencing), and protocol criteria (relayer, relayer reuse, fee amount and percentage, gas price and its uniqueness, relayed versus direct, Merkle root, nullifier hash, recipient type, immediate contract interaction).

**Mode 2 — Analyse a known depositor.** Enter an address that made deposits. The module identifies every relevant deposit across the four ETH pools, builds a **denomination fingerprint** and deposit timeline, collects the eligible later withdrawals, ranks candidate entities against the fingerprint, and traces the strongest downstream. You control the corpus scope, the search window relative to the deposits, which pools count, tracing depth, and whether the softer indicators (gas, relayer, fee, downstream) contribute to ranking or appear only as corroboration.

**Mode 3 — Test suspected withdrawal addresses.** Assess a supplied list against a known deposit set. Each address is reported on individually: withdrawals, denominations, totals, timing after the deposits, FIFO compatibility, value compatibility, relayer and fee behaviour, downstream destinations, shared services, shared private sinks, similarity to the other supplied addresses — and, given equal weight, the reasons *against* the hypothesis, the missing evidence, and the alternative explanations.

This mode declares its own limitation prominently: it compares only the addresses you supplied. Its rankings, p-values and candidate counts are **not pool-wide** unless a full withdrawal corpus is loaded alongside. A rank of "first out of three" means very little and the interface says so.

**Mode 4 — Explore.** Browse all withdrawals in a period with no depositor or suspect in hand. Summaries by pool, day and hour; relayer usage; fee and gas-price distributions; repeated recipients; shared downstream destinations; bursts; suspiciously regular intervals; consolidation wallets; probable exchange exits; bridge usage; Tornado re-deposits; and outliers.

Across all modes there is a hard rule about what may *not* merge addresses. Shared use of a popular relayer, an exchange hot wallet, a common router, a common bridge, a similar gas price, the same denomination or a similar withdrawal time can never, alone, cluster two addresses. Those are precisely the things unrelated people have in common.

<figure><img src="/files/lVIQzI9FwAw7nY7kZPgm" alt=""><figcaption></figcaption></figure>

***

## The guided workflow

### Stage 1 — Define the investigation

The module asks what you're investigating, what you have (depositor address, suspected addresses, neither), which denominations and dates are relevant, whether there's an evidential cutoff, how many hops to trace, whether common services should be excluded from clustering, and whether you have an attribution database.

It then restates your configuration in plain language for confirmation — *"You are looking for a possible entity that withdrew two 1 ETH notes and one 10 ETH note between 1 January and 31 March 2024, potentially using multiple recipient addresses…"* — before anything expensive runs.

It also **suggests analyses**, each with a stated reason it applies to your specific criteria *and* an explicit note on what that analysis cannot establish. For example: relayer and fee analysis is suggested because those are behavioural settings that tend to persist across one operator's withdrawals, but the suggestion also states that popular relayers are shared by thousands of unrelated users and only an uncommon one carries weight. A recommendation the investigator can't evaluate is just another black box.

### Stage 2 — Data collection

Progress reporting covers pools queried, blocks searched, events collected and excluded, API retries and errors, rate-limit and cache status, estimated corpus size, and — importantly — which fields were actually available. If roots, relayers, fees or nullifiers couldn't be read for part of the corpus, you're told, because analyses downstream of missing data need to be discounted accordingly.

### Stage 3 — Filtering

The reduction from raw corpus to candidate set is shown step by step: total collected, then after date filtering, pool filtering, denomination filtering, and investigator-defined criteria, ending in unique recipients and provisional entities. Each step reports before, after, removed and why.

The reasoning is simple: an investigator who cannot show how eighty thousand events became eighteen candidates cannot defend the result under challenge.

### Stage 4 — Candidate scoring

Each candidate is scored on components that are computed and displayed **separately**. A composite that can't be decomposed isn't reviewable, and an unreviewable score isn't usable as evidence.

The components are:

| Component                         | What it measures                                                                                                                                     |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Denomination agreement**        | How well the entity's mix of note sizes matches the deposit fingerprint, including how many notes are unmatched and how many extra withdrawals exist |
| **Note-level FIFO compatibility** | Whether each deposit note can be answered by a *distinct later* withdrawal of the same denomination, matched earliest-first                          |
| **Timing rarity**                 | How unusual the entity's timing pattern is relative to other entities in the same corpus — not in isolation                                          |
| **Total-value compatibility**     | Whether the amounts are consistent with the deposit set                                                                                              |
| **Relayer similarity**            | Relayer choice and reuse, weighted against how popular that relayer is across the corpus                                                             |
| **Fee similarity**                | Consistency of the fee percentage accepted — a persistent behavioural setting                                                                        |
| **Downstream convergence**        | Whether funds arrive at the same low-traffic destinations                                                                                            |

Every component reports four things: a relative score, **plain statements supporting it**, **plain statements weakening it**, and whether the underlying data existed at all. The weakening statements are not an afterthought — a note that can't be matched, an address with unrelated extra activity, or a withdrawal preceding its supposed deposit are genuine arguments against the hypothesis and are surfaced as such.

<figure><img src="/files/BPT6aN1vvZLxb5RCsSnE" alt=""><figcaption></figcaption></figure>

The composite carries an explicit `calibrated: false`. **It is never presented as the probability that the candidate is correct**, because it hasn't been calibrated against validated ground truth. It is a relative ranking within the analysed corpus, and results are labelled in relative tiers (*strong investigative lead*, and so on) rather than as percentages of certainty.

Where the scope permits it, candidates also carry an empirical p-value measured against the corpus as its own null population, adjusted for multiple testing using a false-discovery-rate correction — because ranking thousands of entities and reporting the top one's p-value without correction manufactures significance. Every result states its **statistical scope** and **null population**, and flags when the scope isn't valid for inference at all.

### Stage 5 — Cluster review

This is the stage most tools skip, and the reason the module exists.

Clusters are **proposed, never applied automatically**. Before any multi-address entity is finalised, the proposed cluster is shown with its supporting edges, and each edge carries the evidence that produced it: the two addresses, evidence type and strength, the shared sink and its category and in-degree, relayer overlap, fee similarity, timing and gas relationships — and crucially, whether that single edge alone triggered a wider transitive merge.

Edges come only from **shared private sinks**: low-traffic destinations that two entities both reached. Before edges are built, destinations are classified and high-in-degree addresses are excluded as hubs, using both an absolute in-degree ceiling and a proportional one, plus category-based exclusion of known infrastructure. An exchange deposit address cannot produce an edge no matter how many entities touch it. Every exclusion is logged with its in-degree and the reason.

You can then accept a cluster, reject it, strike a single edge, exclude a specific hub, re-run without shared-sink clustering entirely, re-run using only sinks you've approved, and **compare the clustered and unclustered results side by side**. If a finding survives only under one clustering choice, that is something you want to know before you rely on it.

### Stage 6 — Downstream analysis

One or two hops by default, more available. The tracer uses a branching model — it does not simply follow the largest output where funds split — and records native ETH, internal transfers, WETH, ERC-20 transfers, DEX swaps, aggregators, bridges, exchanges, Tornado re-deposits, contract interactions and consolidation paths.

Safeguards run throughout against exchange hot-wallet over-linking, routers, high-in-degree hubs, dust, address poisoning, lookalike addresses, incidental shared services and contract-mediated false convergence.

### Stage 7 — Conclusion

The module does not declare a match. It presents a structured panel:

* **Supporting indicators**, listed individually so each can be checked.
* **Contradictory indicators** — mismatches, extra activity, inconsistent timing, divergent service use.
* **Missing evidence** — what couldn't be read, and what that prevents you concluding.
* **Alternative explanations** — coincidental denomination match, popular relayer, shared exchange infrastructure, different people following similar practices, a service controlling several addresses, an intermediary or OTC broker, an incomplete corpus, a search window that was too narrow.
* **Recommended next steps**, each with a stated reason — trace further, review exchange attribution, compare service deposit identifiers, run OSINT on the addresses, examine how the withdrawal wallets were gassed, widen the window, add a pool, re-run without a contested edge, or request records from an identified service.

The headline explicitly carries **"not proof"**.

<figure><img src="/files/P83B87rh776BBQ26zEwS" alt=""><figcaption></figcaption></figure>

***

## Export and auditability

A run exports a full case file: a primary investigation report plus supporting notes covering methodology, target deposits, the withdrawal corpus, ranked candidates, cluster review, downstream analysis, timeline, service interactions, contradictory evidence, investigator actions, limitations, and the audit log. Structured data accompanies it as CSV and JSON — deposits, withdrawals, candidates, clusters, cluster edges, downstream edges, services, excluded hubs, the analysis configuration and the raw results.

The Markdown is Obsidian-compatible: YAML frontmatter, full addresses in `[[wiki links]]`, full transaction hashes, cross-linked sections, callouts, tags, stable entity and cluster IDs, embedded CSVs, and Mermaid diagrams for note matching, entity clustering, downstream flow, timeline and shared services.

**No truncated addresses appear in any evidential table.** Shortening is reserved for transient progress messages, because a table with a partial address isn't checkable.

The audit log records API errors, retries, incomplete data, missing pool coverage, every exclusion and every investigator action, so the path from raw data to conclusion can be reconstructed by someone who wasn't there.

***

## Analytical safeguards

Stated plainly, because they're the difference between a defensible result and a confident-looking one:

* All four ETH denominations supported end to end.
* Event-level timestamps preserved; UTC retained in the data with the local timezone shown separately.
* Gross denomination and net recipient amount kept distinct; relayer fees recorded separately.
* Denomination never inferred from net value where the pool contract is known.
* Failed transactions excluded.
* Normal and internal transfer representations of the same event deduplicated.
* Source data cached with timestamps; API errors and incomplete data recorded rather than hidden.
* Warnings raised when roots, logs or token transfers are unavailable, when pool coverage is missing, and when the withdrawal corpus may be incomplete.
* Whether the analysis is full-pool or targeted is always stated, along with the search window and every excluded service or hub.
* The evidence behind every cluster edge is preserved, and every automatic cluster can be reviewed and reversed.
* Shared public infrastructure is never treated as evidence of common control.

***

## Limitations

* **It cannot link a deposit to a withdrawal.** Nothing can; the cryptography holds. Every analysis here operates on correlations *around* the mixer, not through it.
* **Scores are relative, not calibrated.** A high composite means unusual within the analysed corpus. Converting that to a probability of being right would require validated ground truth the module does not have, and it declines to pretend otherwise.
* **Corpus completeness governs everything.** A narrow window, a missing pool, or unreadable event data can produce a clean-looking result that means nothing. The module reports these rather than absorbing them silently.
* **Targeted mode is not pool-wide.** Comparing five suspects tells you which of the five fits best, not whether any of them fit.
* **Denomination patterns are reproducible.** Anyone can withdraw two 1 ETH notes and one 10 ETH note. Rarity rises with the number of pools involved, but a matching fingerprint is a starting point, not a finding.
* **Timing correlates with external events.** Many unrelated people withdraw together after the same news. Co-timing is meaningful at tight windows and weak at loose ones.
* **Attribution remains a separate job.** The module can tell you funds reached an exchange. It cannot tell you whose account.

***

## How it relates to the other modules

The Withdrawal Viewer enumerates recipients. The Cluster Analyser groups them by on-chain relationships. The Investigator subsumes both into a hypothesis-driven workflow with event-log-grade data, decomposed scoring, reviewable clustering and exportable case files.

Use the Viewer when the question is *who came out*. Use the Investigator when the question is *does this population contain the entity I'm looking for, and can I defend the answer*.

***

## Scope of this document

This describes methodology, not implementation. Deliberately excluded: infrastructure, endpoints, authentication, credit and rate-limit handling, exact scoring weights and threshold values, and source code. The thresholds in particular are withheld because publishing them tells anyone who wants to avoid the analysis exactly what to stay under.

Everything above should be enough to understand what the module does, judge whether a given output is fit for purpose, and — the point of the whole design — disagree with it on specific, stated grounds.
