April 15, 2026 · Permissionless Technologies
ASP vs Proof of Innocence: The Two Compliance Paradigms for Privacy Pools
The blockchain privacy industry is splitting into two camps: prove you're approved, or prove you're not banned. The choice reshapes anonymity sets, fund safety, and regulatory posture.
The Compliance Fork
Every privacy pool must answer the same question: how do you let honest users prove they're honest — without revealing who they are?
The answer has split into two camps.
Inclusion-based compliance says: "Prove you belong to a set of approved participants." An independent operator maintains a list of vetted addresses. When you withdraw, you generate a zero-knowledge proof that your deposit came from one of those addresses. The verifier learns that someone approved is withdrawing — but not who.
Exclusion-based compliance says: "Prove you are NOT on a list of known bad actors." A set of list providers maintains blacklists of sanctioned addresses, exploit wallets, and flagged activity. When you shield tokens, the system checks that your source wallet isn't on any list. If it passes, you receive a proof of non-inclusion that follows your funds.
The first approach is used by Association Set Providers (ASPs), the compliance model in Privacy Pools and Universal Private Pool. The second is Private Proof of Innocence (PPOI), the compliance model in RAILGUN.
Both claim to solve "compliant privacy." They do not solve it the same way. The architectural differences affect anonymity sets, fund safety, regulatory posture, and what happens when something goes wrong. This article unpacks both approaches — starting with what each one does for a non-technical reader, then going layer by layer into the game theory, failure modes, and cryptographic mechanics.
What ASPs Do
An Association Set Provider is a compliance operator that maintains an allowlist of addresses and publishes it on-chain as a Merkle tree root.
The workflow:
- A user deposits tokens into the privacy pool. The deposit is public — the depositor's address (the "origin") is visible on-chain.
- An ASP operator evaluates the origin address against its compliance criteria. This could be sanctions screening (OFAC, EU, UN), KYC verification, behavioral analysis, or any other standard. Different ASPs apply different criteria.
- If the address passes, the ASP adds it as a leaf in its Merkle tree:
leaf = Poseidon(address). The tree is rebuilt and the new root is published to an on-chain registry — theAttestationHuborASPRegistryHub. - When the user wants to withdraw or transfer privately, they generate a ZK proof demonstrating: "My note's origin address is a member of this ASP's Merkle tree." The proof reveals nothing about which address — only that it's in the approved set.
- The smart contract verifies the proof and the ASP root. If either check fails, the transaction reverts.
The compliance check happens at transaction time — at every withdrawal and every private transfer. Not once at deposit. Every time funds move.
Key properties:
- Positive membership: You prove you ARE in the approved set.
- On-chain enforcement: The smart contract rejects transactions without valid ASP proofs.
- Re-provable: If your status changes (added to an ASP, removed and reinstated, false positive corrected), you prove against the ASP's current root. No permanent tainting.
- Multiple ASPs: Different operators serve different compliance needs. Recipients choose which ASPs they trust.
What PPOI Does
Private Proof of Innocence is RAILGUN's compliance layer. It operates at deposit time and uses an exclusion model.
The workflow:
- A user shields tokens into the RAILGUN pool. The shield's source wallet is publicly visible.
- During a 1-hour standby period, List Providers — entities like Chainalysis, Elliptic, ScamSniffer, and SlowMist — check the source wallet against their databases of sanctioned addresses, known exploits, and flagged activity.
- If the source wallet is NOT on any exclusion list, the shield is added to a PPOI accumulator — a cryptographic data structure attesting that this specific deposit passed screening.
- A zk-SNARK proof is generated automatically, demonstrating non-inclusion in the exclusion lists.
- This proof follows the funds through all subsequent private transfers. It's generated once and carries forward.
Key properties:
- Negative exclusion: You prove you are NOT in the set of known bad actors.
- Wallet-level enforcement: The PPOI check happens in the SDK/wallet, not in the smart contract. The on-chain contracts have no knowledge of PPOI.
- Immutable: Once a shield is added to (or excluded from) the accumulator, the proof cannot be regenerated against updated data.
- Single proof: Generated once at shield time, carried through all downstream transactions.
The Executive Comparison
| Dimension | ASP (Inclusion) | PPOI (Exclusion) |
|---|---|---|
| Default stance | Deny unless approved | Allow unless blacklisted |
| When checked | Every withdrawal and transfer | Once at shield time |
| Enforcement | Smart contract (on-chain) | Wallet/SDK (off-chain) |
| Proof lifetime | Fresh per transaction | Generated once, carried forever |
| If wrongly flagged | Ragequit to recover funds; re-prove when corrected | Funds permanently excluded from accumulator |
| If status changes | Re-prove against current ASP root | No mechanism to update |
| Anonymity set impact | Full pool (transaction-centric) | Full pool (single accumulator) |
| Operator trust | ASP can censor (ragequit mitigates) | List Provider can miss threats (no mitigation) |
| Regulatory model | Positive identification | Negative screening |
Why the Default Stance Matters
Financial regulation overwhelmingly uses positive identification. When you open a bank account, you prove who you are. When a business processes a wire transfer, the sender proves the funds are legitimate. The burden is on the actor to demonstrate compliance — not on the system to assume compliance until proven otherwise.
Inclusion-based systems mirror this. You prove membership in an approved set. The system doesn't need to know about every bad actor in the world — it only needs to know that YOU passed a specific set of criteria defined by a specific operator.
Exclusion-based systems invert the burden. They must maintain a comprehensive list of every known bad actor — and that list must be complete and current. If a new hack happens at 2:00 PM and the blacklist updates at 3:00 PM, there's a 1-hour window where stolen funds can enter the pool unchallenged.
Vitalik Buterin's Privacy Pools paper describes this as a separating equilibrium: a system where honest and dishonest users reveal their type through their behavior. In an inclusion-based system, opting out of compliance — refusing to prove ASP membership — is itself a signal. The pool of non-provers shrinks as honest users self-identify, until non-proving becomes equivalent to contamination.
Exclusion-based systems create a weaker equilibrium. Everyone starts as "presumed clean." The system only reclassifies you as dirty after you've been caught — which requires the exclusion list to catch you first.
The Blacklist Lag Problem
Every exclusion-based system has a structural vulnerability: the gap between when bad activity occurs and when the blacklist updates.
Consider a concrete scenario:
- An attacker steals $50 million from a DeFi protocol at block 18,000,000.
- The attacker moves the stolen funds to a fresh wallet (wallet B) that has no prior history.
- The attacker shields from wallet B into the privacy pool.
- The List Provider's blacklist updates 45 minutes later to include the exploit addresses — but the shield already passed the standby period from a clean wallet.
This isn't theoretical. Researchers at AnChain identified this single-hop bypass in RAILGUN's PPOI system. The shield passes because wallet B has no flagged history. The stolen funds are now inside the pool with a valid PPOI proof.
PPOI's 1-hour standby period was designed to mitigate this — give List Providers time to react. But one hour is a fixed window. Sophisticated attackers can use intermediary wallets, time their shielding, or exploit weekends and holidays when monitoring may be less active.
In an inclusion-based system, this attack doesn't work the same way. Wallet B isn't in any ASP's approved set. It has no KYC history, no compliance record, no relationship with any ASP operator. The deposit goes through (anyone can deposit), but the funds can't be withdrawn or transferred through compliant channels — because wallet B can't prove ASP membership. The attacker's only exit is ragequit — a public withdrawal to the original depositing address, which exposes them on-chain.
What Happens When Something Goes Wrong
The most revealing comparison is what each system does when it makes a mistake.
PPOI: No Remediation
If PPOI incorrectly flags a legitimate user's shield — maybe the source wallet once interacted with a contract that later appeared on a sanctions list, or maybe the List Provider's data has a false positive — the shield is excluded from the accumulator. The proof is immutable. There is no mechanism to regenerate it against corrected data.
The user's funds are inside the pool. They may be able to use them within RAILGUN's internal economy (if other users accept transactions without PPOI). But they cannot exit through legitimate channels that require PPOI verification. The false positive becomes a permanent mark.
ASP: Re-Prove or Ragequit
If an ASP incorrectly excludes a legitimate address, the user has two options:
-
Wait and re-prove. When the ASP corrects its list and publishes an updated root, the user generates a fresh proof against the new root. Their funds were never "tainted" — the compliance check happens at transaction time, not at deposit time. The note itself carries no memory of the mistake.
-
Ragequit immediately. If the user can't wait — or if they've lost trust in all available ASPs — they can invoke ragequit. The circuit enforces
recipient === origin: funds go back to the original depositing address. Privacy is sacrificed, but funds are recovered. No ASP, governance process, or legal action can prevent it.
The ASP registry maintains a circular buffer of 64 historical roots per ASP, so proofs remain valid even during root update transitions.
Note-Centric vs Transaction-Centric Compliance
Within the inclusion-based world, there's a second design choice that has profound implications for privacy.
Note-Centric (Privacy Pools as Originally Conceived)
In a note-centric model, compliance requirements are embedded in the note itself. A note might carry metadata like [ASP_EU, ASP_US] — meaning every holder must be approved by both an EU and a US ASP.
The problem is anonymity set fragmentation.
Consider a pool with 10,000 users. The EU ASP covers 2,000 users. The US ASP covers 3,000 users. A note stamped [ASP_EU, ASP_US] can only be held by users in both sets — perhaps 50 people. An observer seeing a withdrawal from a [ASP_EU, ASP_US] note immediately knows the withdrawer is one of 50 people, not one of 10,000.
This is the colored coin problem applied to privacy. Notes with rare compliance combinations become identifiable. The more specific the compliance requirements, the smaller the anonymity set.
Worse: if a user loses their ASP status (say, their KYC expires), any note they hold with that ASP requirement becomes unspendable. The note is stuck. The only exit is ragequit — a public, privacy-destroying withdrawal. For a $50 transfer, that's an unacceptable UX.
Transaction-Centric (UPP's Approach)
In a transaction-centric model, compliance is a property of the transfer, not the note. Notes carry no compliance metadata. They're all identical — fungible, interchangeable, and part of the full anonymity set.
Compliance is checked at each transaction: the sender proves their origin is in the required ASP's approved set. If the proof succeeds, the transfer proceeds. If it doesn't, the sender can't send to that particular recipient — but they can still send to recipients with different ASP requirements.
This is like a credit card being declined at one store but working at another. Your funds aren't frozen. Your note isn't tainted. One specific transaction path is blocked.
The anonymity set is always the full pool — 10,000 users, regardless of compliance requirements. No fragmentation. No colored coins.
The security guarantee comes through induction:
- Alice (approved) deposits and transfers to Bob. Bob's wallet verifies Alice's ASP proof before accepting.
- Bob (approved) transfers to Carol. Carol's wallet verifies Bob's ASP proof.
- By induction, Carol knows the entire chain is compliant — without any compliance metadata in the note itself.
Each link in the chain is verified at transfer time. The chain's integrity is maintained by the protocol, not by stamping history into notes.
Multiple ASPs: How Coexistence Works
Both ASP-based and PPOI-based systems support multiple compliance operators. The mechanisms differ.
PPOI: Multiple List Providers
RAILGUN supports multiple List Providers — each maintaining their own exclusion databases. Users choose which providers to trust based on jurisdiction. POI Nodes synchronize proof data across a distributed network.
The limitation: all providers operate on the same exclusion model. They can specialize (OFAC focus, exploit focus, behavioral analysis) but they can't offer fundamentally different compliance types. There's no "KYC List Provider" because PPOI is about exclusion from blacklists, not inclusion in KYC sets.
ASP: Diverse Compliance Operators
The ASP model supports fundamentally different types of compliance operators:
| ASP Type | Example Operators | What They Check |
|---|---|---|
| Sanctions ASP | Chainalysis, TRM Labs | OFAC/EU/UN sanctions lists |
| Exchange ASP | Coinbase, Kraken | KYC'd user addresses from their platform |
| Institutional ASP | Banks, brokerages | Accredited investor status, AML compliance |
| Regional ASP | Local regulators | Jurisdiction-specific requirements |
| Community ASP | DAOs, protocols | Membership in trusted networks |
A receiving smart contract can require specific ASPs:
mapping(uint256 => bool) public trustedASPs;
function receivePrivatePayment(
bytes calldata proof,
uint256 aspRoot,
uint256 aspId
) external {
require(trustedASPs[aspId], "Untrusted ASP");
pool.withdraw(proof, ..., aspId, ...);
}An EU-regulated exchange might accept only ASP_EU_AML. A US DeFi protocol might accept ASP_OFAC or ASP_US_KYC. A permissionless DEX might accept any ASP — or none (accepting only ragequit withdrawals).
The receiving service controls the compliance requirement. The sender proves they meet it. The pool doesn't need to know or care which compliance regime applies to which transaction.
The Circulation Equilibrium
Standard Privacy Pools — including 0xBow's implementation — check compliance only at withdrawal (the exit point). Inside the pool, funds circulate freely. A sanctioned entity can transfer notes privately to accomplices indefinitely, as long as nobody tries to exit through a compliant channel.
The separating equilibrium applies only at the gate. Inside the pool, there's no separation.
UPP's transaction-centric model checks compliance at every transfer — not just at exit. This creates what we call a circulation equilibrium: the separating equilibrium applies to the entire velocity of money, not just the exit gates.
If a sanctioned entity holds a note in UPP, they cannot:
- Transfer it (circuit requires valid ASP proof for sender's origin)
- Withdraw it through compliant channels (same requirement)
- Hand it to an accomplice (the accomplice's wallet would reject the sender's proof)
The only option is ragequit — a public exit to the original depositing address, which exposes them on-chain. The system doesn't just catch bad actors at the gate; it isolates them from the entire internal economy.
This is a stronger guarantee. An attacker holding tainted notes in a withdrawal-only system can use those notes internally for months or years — paying for services, settling debts, creating complex transaction graphs that make tracing harder. In a circulation-equilibrium system, the tainted notes are immediately illiquid. Nobody will accept them because nobody can verify the sender's compliance status.
The Merge-and-Claim Exception
There's one deliberate exception to origin preservation: the merge-and-claim operation.
When a user merges two notes, the merger's address becomes the new origin. The previous origins are recorded in on-chain merge records (accessible via viewing keys), but the merged note's compliance-relevant origin is the merger.
This implements the bona fide purchaser doctrine: a 500-year-old legal principle where a business accepting payment in good faith takes responsibility for the funds. A car dealer doesn't trace where your cash originally came from. Responsibility resets at the point of commerce.
Without merge-and-claim, every holder in a chain must maintain the original depositor's compliance status indefinitely. If Alice deposited funds three years ago and her KYC has since expired, every downstream holder has a problem. Merge-and-claim cuts this chain: the current holder takes responsibility, and their compliance status is what matters going forward.
Practical Costs and Operational Considerations
ASP Operational Costs
Running an ASP involves:
- Address evaluation: Off-chain process (sanctions screening, KYC verification). Cost depends on the provider's data sources.
- Tree construction: Rebuilding the Merkle tree when addresses are added or removed. Computationally light — a 20-level tree supports ~1 million addresses.
- Root publishing: On-chain transaction to the registry, ~50-70K gas per update. At current gas prices, $2-5 per update.
- Update frequency: Configurable per ASP. Recommended: 1-4 hours for active DeFi, 24 hours for enterprise.
PPOI Operational Costs
Running a POI Node involves:
- List maintenance: Off-chain exclusion list updates. Depends on data provider APIs.
- Shield monitoring: Watching for new shields during the 1-hour standby period.
- Accumulator updates: Adding passed shields to the PPOI accumulator.
- Node infrastructure: MongoDB-backed service, distributed across the POI Node network.
Both models have comparable operational overhead. The key difference is who bears the cost: ASP operators bear it for inclusion; List Providers bear it for exclusion. In both cases, the user doesn't directly pay for compliance — they pay gas for proof verification.
A Note on Centralization
Both models have centralization concerns, but different shapes.
ASP centralization risk: An ASP can censor by refusing to add an address. If all ASPs collude to exclude someone, that person can't withdraw through compliant channels. Ragequit mitigates this: the user can always exit publicly. Ragequit is enforced at the circuit level — no ASP, governance vote, or legal order can prevent it.
PPOI centralization risk: A List Provider can censor by falsely flagging a shield. If flagged during the standby period, the shield is permanently excluded. There is no ragequit equivalent: the user's funds are inside the pool but may be effectively inaccessible. The List Provider's decision is final and irrevocable.
Both models can have multiple operators, reducing single-operator risk. But the failure recovery is asymmetric: ASP failure = temporary inconvenience (ragequit). PPOI failure = permanent exclusion.
Where the Industry Is Heading
The regulatory landscape in 2026 is converging on "privacy plus compliance" as the accepted framework. The Tornado Cash sanctions and delisting demonstrated that pure privacy (no compliance) gets protocols sanctioned, while the Fifth Circuit ruling established that immutable smart contracts can't be treated as sanctionable property.
The GENIUS Act brought stablecoins under BSA requirements. The EU's MiCA regulation mandates Travel Rule compliance for crypto transfers. Both frameworks assume positive identification — the sender demonstrates compliance, not the system assumes it.
This regulatory direction favors inclusion-based models. When a regulator asks "how do you know this withdrawal is compliant?", the ASP answer — "the user proved membership in an audited compliance set, verified on-chain by the smart contract" — is structurally stronger than the PPOI answer — "our wallet software checked a blacklist before the deposit."
Neither approach is perfect. Both are better than Tornado Cash's answer, which was: nothing.
This is part of our series comparing privacy protocol architectures. See also: Universal Private Pool vs RAILGUN for a broader protocol comparison.
For technical documentation on ASP integration, see our ASP concepts guide and operator setup. Universal Private Pool is pre-audit and deployed on Sepolia testnet.