ROOTVIELROOTVIEL

Agentic anonymization module

Product · not yet on mainnet

Rootzk

Rootzk lets you move value privately on a public blockchain — deposit, transfer and withdraw so the ledger no longer links your operations to each other or to you — and lets an AI assistant help you do it without ever touching your keys or secrets.

Rootzk is the anonymization strand of the ROOTVIEL ecosystem — the module through which agents reason about and operate privacy under strict confinement. It is the privacy counterpart to Rootchain, which covers agentic security in Web3. Rootzk asks “how can an agent help with private, verifiable value flows without ever holding a secret?”; Rootchain asks “how do agents secure on-chain systems?”.

Status · A built product at protocol freeze 1.10.0, deployed and exercised end-to-end on a public testnet (Sepolia, with DEV proving artifacts) but not yet on mainnet. An internal security audit was performed and its findings fixed; production soundness still depends on a multi-party trusted-setup ceremony (not yet run) and an independent external audit (not yet done). Until both, treat strong claims as UNKNOWN.

Rootzk — zero-knowledge shielded-pool module

The problem

Why private value flows are hard on a public chain

A public blockchain records every transfer — amounts, addresses, timing — permanently. Anyone can follow the money: who paid whom, how much, and when. For many legitimate needs (payroll, treasury moves, donations, or simply not broadcasting your net worth) that permanent public linkage is the problem.

Rootzk provides a shielded pool where funds can be held, transferred and withdrawn so the public ledger no longer links your operations to each other or to you — using zero-knowledge proofs, while you keep custody of your keys. An agent can help you operate it, without ever handling your secrets.

Who it is for

  • People and teams who need legitimate on-chain privacy: payroll and treasury, donations, or trading that should not telegraph intent.
  • Builders who want a pool with selective, provable compliance — prove a range, or membership in a curated association (“clean”) set — rather than all-or-nothing secrecy.
  • Researchers studying how an agentic layer can assist private operations under strict confinement.

See it work

One operation, end to end

Follow one flow before the mechanics — a complete operation, from putting funds in to taking them out.

  1. 1

    Deposit

    public

    You move funds into the pool. A one-time 1% protocol entry fee is taken at deposit, so the note commits to the net amount — and that net is what the in-circuit conservation preserves from then on. A commitment that hides your secret becomes a leaf in a public Merkle tree, and from now on you blend into every deposit of the same shape.

  2. 2

    Private transfer

    private

    Inside the pool you pay someone: a 2→2 join-split spends your notes and creates a note for them plus your change. The ledger shows two nullifiers and two new commitments — not amounts, not who paid whom.

  3. 3

    Prove, don’t reveal

    private

    Optionally you prove facts about your funds — an amount is within a range, or they belong to an approved association set (proof of innocence) — without disclosing the amount or your history.

  4. 4

    Withdraw

    public

    Later you withdraw to a fresh address by proving you own an unspent leaf under the tree root and publishing a nullifier. The payout is public; the link back to your deposit is not.

One operation, end to endDocumented architecture
AGENT PLANECRYPTOGRAPHIC ENVIRONMENT & CHAINsecrets never cross left ←PUBLICIntentthe user’s goal:“send 1 privately”REFPlanagent chooses aroute over handlesPRIVATEProveopens the note,builds the proofPUBLICGatechecks the outputfor traceabilityPUBLICSubmitrelayer sends,decoupled txPUBLICSettleverify + updatepublic state
public (on-chain / visible)private (secret material)opaque reference (reveals nothing)

The agent decides what to do but never holds the secret that would let it act: opening the note and building the proof happen in a separate prover environment. Verification and the promotion to a settled operation are done on-chain and by deterministic components — not by the agent.

How it works

Notes, commitments, nullifiers — and why each exists

The pool is built from a few interlocking ideas. Each one solves a specific problem; together they let you spend privately without a trusted operator.

A note is a private record of value you own. To put it on-chain without revealing it, you publish a commitment — a hash that binds your secret — which becomes a leaf in a shared Merkle tree. The tree lets you later prove your leaf is in it without pointing to which one.

Spending needs a way to stop you from spending the same note twice — but checking that must not reveal which note you used, or privacy collapses. That is the job of the nullifier: a value derived from the same secret in a different way, published when you spend. The contract records it; a second spend would publish the same nullifier and be rejected. The check is on the nullifier, never on the note — so nothing identifies the leaf you consumed.

How a deposit and a withdrawal stay unlinkableProtocol mechanism
DEPOSITWITHDRAW (later)Private inputssecret, nullifier ∈ FcommitmentPoseidon(secret, nullifier)leaf · carries amountPoseidon(commitment, amount)Merkle treePoseidon · depth 20anonymity setroot · 100-root windowappendZK membership proofowns an unspent leaf — reveals nonenullifierHash → setblocks double-spendingpayout → recipientrecipient bound in the proofproveobserver cannot link deposit ↔ withdrawal

The commitment binds secret and nullifier; the published nullifierHash is derived from them in a different order, so the value seen at withdrawal cannot be matched to the one seen at deposit. Unlinkability is real only against an on-chain observer, and only as large as the anonymity set — a separate, measured quantity. Illustrative; a valid proof attests to membership under its assumptions, never that the funds are legitimate.

Notes, commitments, the tree and nullifiersDocumented mechanism
PRIVATEsecret, nullifierprivate, in FUNIQUENESS — the spend sidePUBLICnullifierHashPoseidon(nullifier, secret)PUBLICnullifier setpublished at spend (public)derivea repeat ⇒ rejected — the set neversays which leaf it came fromMEMBERSHIP — the deposit sidePUBLICcommitmentPoseidon(secret, null.)PUBLICleaf (+amount)Poseidon(commit, amt)PUBLICMerkle tree → rootmembership proof revealsno leafderiveSame secrets,two hashesdifferent order ⇒no on-chain link(one of several
public (on-chain)private (secret material)

A double-spend is blocked because the second attempt would publish the same nullifierHash, and the set already holds it — the check is on the nullifier, never on the proof or the leaf, so nothing identifies the note that was spent. Different-order hashing is one ingredient of unlinkability, not the whole of it: the overall guarantee also depends on the anonymity set and on behaviour, covered below.

Transfers and withdrawals

A private transfer moves value between people inside the pool. A 2→2 join-split spends two notes and creates two — one for the receiver, one as change — so value never leaves the shielded domain and no amount is revealed. A withdrawal instead exits to a public address, paying out a public amount while keeping the link to your deposit hidden.

Both rely on an in-circuit conservation rule: the value going in must equal the value coming out, exactly, so no proof can mint funds. This is what makes the pool safe to withdraw from.

A transfer inside the shielded domainSynthetic example
SHIELDED DOMAIN · privatePRIVATEsender note APRIVATEsender note Bjoin-split 2→2value conservedstays in the poolPRIVATEreceiver note→ receiver (new commitment)PRIVATEchange note→ senderdelivered via an encrypted memo only the receiver can openPUBLIC LEDGER — what everyone seesnullifier(A)nullifier(B)commitment₁commitment₂proofHidden:the amounts · which input became which output · the sender and receiver identities.An observer sees that two notes were spent and two created — not who paid whom, or how much. Linkage is only as strong as the anonymity set.
public (on-chain)private (note / amount)

A transfer never exits to the public balance, so no amount is revealed — unlike a withdrawal, which pays out a public amount. The sender proves the spend is valid and value-preserving; the receiver can later spend their note the same way.

Join-split conservation — value cannot be mintedCircuit invariant
INPUTSin₁in₂join-split2 → 2, Groth16OUTPUTSout₁out₂sumIn = sumOut + publicAmount + relayerFeeexact · each value < 2¹²⁸ (Num2Bits)✕ broken conservation and negative-output (−v mod p) are rejected

Conservation (INV-02) is what makes the pool safe to withdraw from: no proof can create value. It was tested adversarially — mint-by-broken-conservation and negative-output attempts were rejected by the circuit.

Public vs hidden

What an observer can and cannot see

Privacy here is specific, not absolute. It is worth being exact about who the observer is, what they see, and how operations can still be correlated.

The observer model

Assume a realistic observer: anyone who can read the chain, plus the relayer that submits your transaction. The chain observer sees commitments, nullifiers, roots, proofs and public amounts; the relayer additionally sees a withdrawal’s recipient and amount, by construction.

Public — on the ledger

  • Commitments and the Merkle root
  • Nullifiers of spent notes
  • Proofs, and the fact that an operation happened
  • The public amount of a deposit or withdrawal (a transfer has none)

Hidden — never revealed

  • Which leaf a spend consumed
  • The link between a deposit and a later withdrawal
  • Amounts inside a transfer, and who transferred to whom
  • The note secrets and viewing material

Privacy is a size, not a yes/no

Privacy here is not absolute and not a yes/no property — it is a size. You are hidden among the set of indistinguishable operations of the same shape; a set of one hides nothing. The set grows with usage and with waiting, and shrinks with unique amounts, tight timing, address reuse, or always using the same relayer.

  • Timing — depositing and withdrawing close together narrows the set.
  • Amounts — an unusual amount can be matched across the two sides.
  • Network & behaviour — IP, a reused relayer or a distinctive pattern can correlate operations off-chain, which is outside the protocol’s scope.

The agentic layer

What the agent adds — and what it may never do

Rootzk’s own contribution is the layer that lets an agent operate this safely. Its value is in reasoning; its design is in strictly separating that reasoning from anything that can act.

Planning

Turns an intent (“send 1 privately”) into a concrete sequence of capabilities, choosing denominations and routes.

Evaluating alternatives

Compares ways to reach the goal and weighs their privacy and cost, rather than executing the first option.

Privacy analysis

Runs a proposed output through the privacy gate’s checks and prefers outputs that do not stand out.

Capability coordination

Sequences deposit, transfer, disclosure and withdrawal capabilities, and surfaces what is unavailable instead of hiding a gap.

Separation of duties

Rootzk draws a hard line between reasoning and anything that can move value or touch a secret. Five duties are deliberately held by different parties:

DutyWho does itNote
Reasoning & planningThe agentDecides what to do — over opaque references only.
Secret handling & proof generationA prover environment, outside the agentOpens notes and builds proofs; the agent never sees this material.
AuthorizationDeterministic policy + on-chain gatesDecides whether an action may take effect; the agent cannot self-authorize.
VerificationOn-chain Groth16 verifiers (deterministic)Validate every proof; a reasoner cannot vouch for one.
Promotion to a settled operationThe chainOnly an on-chain receipt makes an operation real.

So the agent is forbidden to see a secret, sign, validate cryptography or promote a result to settled — while proving, verifying and promotion are deliberately done by other parties.

What this boundary is, and is not (threat model)

Be precise about how strong this boundary is. The agent-side confinement is enforced by types, modules and tests — a strong boundary against accidental leakage and against a reasoner (including an LLM) steered by its inputs. It is NOT a sandbox against arbitrary native code in the same process; that is not the threat model. The guarantees that hold against a malicious party come from the separate prover environment and from deterministic on-chain verification — not from in-process isolation.

The confinement membrane — what an agent can never doDocumented architecture
Agent planeAn agent / LLM reasons about privacy and verification —it holds opaque handles only (SecretRef, NoteRef, WitnessRef).Membrane · typed, fail-closed✕ see a secret✕ sign✕ validate cryptography✕ promote to verifiedagent-safe DTO ↑secrets blocked ↑SYSTEM PLANE · the deterministic sliceIntentCapabilityEvidenceVerifierVerifiedOperationSecrets · keys · witnessesnever cross the membrane

The separation is enforced by typing, not by convention: an agent cannot import the membrane that restricts it, and the context accepts only agent-safe observations. The four prohibitions — no secret, no sign, no crypto-validate, no promote — are each asserted by a test.

Privacy gate

Attacking the output before it is broadcast

Before anything is broadcast, a privacy gate attacks the proposed output with its own de-anonymiser and refuses outputs it can trace.

Inputs

  • The proposed public output — nullifiers, commitments, amounts, timing
  • The current pool state and anonymity set
  • A battery of de-anonymisation heuristics

Checks it runs

  • Subset-sum and amount matching
  • Temporal correlation and intersection
  • Gas and arity fingerprints
  • Input-selection and controller-history patterns

Decision

If the output is uniquely traceable or below the anonymity floor, it is rejected; otherwise it is released — with a randomised hold so the gate’s own timing is not a usable signal.

Its limits

The gate is a conservative filter, not a proof of privacy: it can reject what its heuristics catch, but it cannot certify that an accepted output is private. That still depends on the anonymity set and on off-chain behaviour (network, timing, host), which are outside the protocol.

The privacy gate attacks its own output before broadcastDocumented mechanism
ProposedoutputPrivacy gatebuilt-in attacker · reverse_trace12 attacks: subset-sum,timing, intersection, gas…randomized hold (not an oracle)✕ Rejectuniquely traceable / below floorBroadcasterdecouples user from tx.fromChain

The gate is a conservative filter, not a guarantee: it rejects outputs its own attacker can de-anonymise, but it cannot prove an accepted output is private — that still depends on the anonymity-set size and on what happens off-chain (network, timing, host).

Status & limits

Where the product stands

A built product at protocol freeze 1.10.0, deployed and exercised end-to-end on a public testnet (Sepolia, with DEV proving artifacts) but not yet on mainnet. An internal security audit was performed and its findings fixed; production soundness still depends on a multi-party trusted-setup ceremony (not yet run) and an independent external audit (not yet done). Until both, treat strong claims as UNKNOWN.

Exercised on testnet

The pool is deployed on the Sepolia testnet (DEV artifacts) and run end-to-end: a real deposit → a Groth16 proof verified on-chain → a withdrawal paying the exact amounts → a rejected double-spend (“Nullifier used”). This shows the implementation works; it is not a mainnet-readiness claim — the DEV proving keys are forgeable until the ceremony.

Internal audit — done

An internal security audit was performed and its findings fixed at freeze 1.10.0: the controlled-access gate now covers every value path including join-split and shielded (H-01); Merkle path indices are boolean-constrained in all circuits (M-01); the keystore signer both deploy scripts require is restored (L-04); and guardian-set changes are timelocked once governance is active (M-04). An independent external audit is still pending — production soundness remains UNKNOWN until it, and the ceremony, are done.

Why the trusted setup matters

The zero-knowledge proofs are only sound if the one-time setup that generated the proving keys was run honestly — otherwise whoever holds its secret could forge proofs. A production ceremony, with independent contributors, is the primary thing standing between this product and mainnet. Its full procedure is operational detail, below.

Why an external audit matters

Circuits and contracts fail in subtle ways that reading your own code does not catch. An internal audit was done and its findings fixed; an independent external audit is still a precondition for trusting the system with real value, and it has not yet been done — so production soundness is UNKNOWN.

Trust assumptions

  • An honest ceremony (at least one honest contributor)
  • An honest owner — until M-of-N governance is activated
  • One honest RPC for receipts
  • A relayer and an indexer
  • circomlib / snarkjs / ethers behaving as specified

Out of the model

  • Traffic analysis
  • Host compromise
  • Legal coercion
  • Deep chain reorganisations
  • A quantum adversary — BN254 and secp256k1 are not post-quantum

Known gaps

  • Proof-of-innocence membership is available but NOT required on the live withdrawal paths (M-03); if PoI gating is a desired invariant, it must be explicitly wired.
  • By default the owner can upgrade the Diamond instantly; the non-custodial property is conditional on activating M-of-N, owner-managed governance and the 48h timelock. Guardian-set changes are themselves timelocked once governance is active (M-04), and on the testnet pool 2-of-3 governance is activated — the instant owner cut is disabled.
  • The two mainnet blockers remain: the production trusted-setup ceremony has not been run, and an independent external audit (distinct from the internal audit already done) has not been performed — so production soundness is UNKNOWN until both are complete.
  • Reproducibility note: the circuit library is tag-pinned rather than a committed submodule (M-05).

Relation to prior work

Rootzk integrates well-known ideas — Tornado-style pools, join-split, proof-of-innocence, stealth addresses. Its own contribution is the agentic layer: the confinement boundary and the privacy gate. It is not equivalent to any deployed system; a feature-level comparison with RAILGUN, with honest classifications, is in the technical reference below.

Reading the guarantee honestly: a valid proof attests to a statement — conservation, membership, a range, an opening — under its circuit and the pending ceremony’s assumptions. It is never evidence that funds are legitimate, that a withdrawal was willing, or that the privacy a user wanted was achieved. PROVEN, NOT PROVEN and UNKNOWN are kept distinct.

Technical reference

Implementation detail

The specifics an implementer or auditor needs — kept out of the explanation above. Expand what you need.

Build, toolchain & architecture layers
Protocol freeze
1.10.0
Toolchain
circom 2.1.6 / 2.2.3 · snarkjs 0.7.6 · circomlib 2.0.5 · solc 0.8.20 (via_ir) · Groth16 / BN254
Layer 1Implemented

Reasoning layer

Python · ≈33k LOC · standard library only

A read-only-by-default architecture with no third-party runtime dependencies, whose job is to let agents reason about privacy and verification while being structurally unable to see a secret, sign, validate cryptography, or promote a result to a verified fact. The audit catalogue reports it as the most mature and best-verified layer, with 769 secret-confinement tests passing. Those four prohibitions are each asserted by tests, not left to convention.

Layer 2Partial

On-chain protocol

Solidity + circom · EIP-2535 Diamond (11 facets, ≈7.3k LOC) · 12 Groth16 circuits (≈2.2k LOC)

A Tornado-style note pool with amount-carrying notes, a 2→2 join-split, and a persistent shielded domain. Commitments and nullifiers use Poseidon, with an on-chain nullifier set and a Poseidon Merkle tree. Fully implemented at protocol freeze 1.10.0 and exercised end-to-end on the Sepolia testnet (DEV artifacts); not yet on mainnet, with the production trusted-setup ceremony and an independent external audit still pending.

Layer 3Partial

Ceremony / release / freeze tooling

Reproducible freeze fingerprint · independent ceremony verification · release gates

Tooling to govern a future deployment: a reproducible freeze fingerprint, independent verification of the trusted-setup ceremony, and release gates. The machinery exists; a production ceremony has not been run.

Note model — field, hashes and parameters

Let F be the BN254 scalar field (p = 21888242871839275222246405745257275088548364400416034343698204186575808495617). All hashing uses Poseidon (circomlib) — never a bespoke implementation.

commitment
Poseidon(secret, nullifier)
leaf
Poseidon(commitment, amount) — the leaf carries the amount
nullifierHash
Poseidon(nullifier, secret) — the reversed order separates spending from viewing
  • secret, nullifier ∈ F are private; amount < 2^128 (constrained in-circuit).
  • The Merkle tree is Poseidon, depth 20, zero-leaf keccak256("tornado") mod p, with a rolling history of 100 roots.
  • The persistent shielded pool uses three domain separators (DOMAIN_NOTE, DOMAIN_NULL, DOMAIN_OWNER): ownerPk = Poseidon(DOMAIN_OWNER, spendKey); commitment = Poseidon(DOMAIN_NOTE, asset, value, ownerPk, randomness, rho); nullifier = Poseidon(DOMAIN_NULL, rho, spendKey) — so the view key never derives the spend key.
On-chain layer — facets, invariants & governance
FacetFunctionsKey propertyAccess control
AmountFacetdepositAmount, batchDepositAmount, withdrawAmount, emergencyWithdrawAmount, *AuthorizedAmount-carrying note; conservation withdraw + fee ≤ amount; derived changenonReentrant, authorization gate, EIP-712 grant variants
JoinSplitFacetjoinSplit (join_split_v4 verifier, 11 signals)2→2, exact conservation; recipient + relayer bound; msg.sender == relayernonReentrant; authorization gate (fixed, finding H-01)
ShieldedFacetshieldedJoinSplit (V2 verifier, 14 signals)Persistent pool; metadataHash + PROTOCOL_VERSION bound; fee subtracted before netnonReentrant; authorization gate (fixed, H-01)
CoreFacetshieldedTransferV2 (5 signals)Value-preserving re-key (newLeaf = Poseidon(newCommitment, amount), same amount)nonReentrant, authorization gate
ComplianceFacetverifyDisclosureV2 (5 signals), updateAssociationSetSelective range disclosure; proof of innocenceaspOperator role for the association set

An EIP-2535 Diamond proxy with AppStorage in slot 0 and LibDiamond in a namespaced slot; the fallback delegatecalls the facet selected by msg.sig.

Legacy deposits/withdrawals, an on-chain stealth spend, batch withdraw, migrateOut, token paths, a decoy-pool gas sponsor, and a removed zkML mock verifier are present only as stubs that revert — withdrawn by design and documented finding by finding: migrate the intent, not the defect.

Invariants verified on-chain

  • Double-spend prevented by a set of nullifierHashes + marking under nonReentrant, with checks-effects-interactions order.
  • poolId = address(this) and chainId = block.chainid are supplied by the contract (anti-replay across pools and chains).
  • Solvency checked excluding amounts owed to the treasury.

All 11 verifiers are real Groth16 (precompiles 0x06/0x07/0x08, checkField over each public signal; no “return true” shortcut).

Governance & upgrade

DiamondCutFacet
owner-only, instantaneous diamondCut; disabled once governanceThreshold > 0.
TimelockCutFacet
proposal → 48h timelock → M-of-N → execution, with a payload-hash re-check.
VerifierGovernanceFacet
the ONLY path to install verifiers: register (captures codehash) → propose → 48h timelock → M-of-N → EXTCODEHASH re-check at execution → install.
GovernanceFacet
guardian set and M-of-N threshold; owner-managed (a documented trust boundary).
Zero-knowledge circuits & soundness properties
CircuitConstraintsPropertyVerifier
withdraw_amount13,516Withdrawal with change; conservation LessEqThan(130); Num2Bits(128) per valueGroth16VerifierAmount
join_split_v1 / v227,869 / 54,3652→2 and 4→4; exact conservation(baseline)
join_split_v427,960v3 + recipient — closes settlement front-runningJoinSplitVerifierV4
shielded_joinsplit_v1 / v229,046 / 29,277Two-sided conservation; metadataHash + protocolVersion bound (v2)ShieldedJoinSplitVerifierV2
shielded_transfer13,118Re-key; same amount ⇒ value-preservingShieldedTransferVerifier
association_set_membership_v211,434Proof of innocence: commitment in a clean set, bound to the same nullifierHashAssociationVerifierV2
selective_disclosure_v212,353min ≤ amount ≤ max without revealing amountSelectiveDisclosureVerifierV2
Conservation / no-mint (INV-02)
sumIn === sumOut + publicAmount + relayerFee (exact); each value Num2Bits(128). Adversarially tested: mint-by-broken-conservation and negative-output (−v mod p) attempts are rejected by the circuit.
Context binding (INV-09)
poolId/chainId (and recipient/relayer) are public signals supplied by the contract; a proof is valid only for the exact signal vector it was generated with — a mutated signal invalidates it (verified).
Boolean Merkle-index constraint (M-01, fixed)
Each pathIndices[i] is constrained to {0,1} (isRight·(isRight−1) === 0) across all 12 circuits after the fix; before, 4 circuits only bounded the weighted sum. A membership forgery with a non-boolean index was experimentally refuted.
Off-chain services

Relayer

policy and execution are separated; the private key lives in a Node subprocess environment, never as a Python value; a NonceManager serializes nonce assignment (fixing a legacy nonce race); only an on-chain receipt promotes an operation.

Indexer

reorg-aware, anchored to block hash; LANDED_DESPITE_FAILURE reconciliation; reconstructible from the chain.

Wallet

the seed/phrase never enters Python (scrypt→HKDF derivation and proving happen in a Node bridge); artifacts verified by hash; deniability via two independent notes with independent salts.

API

capability-shaped endpoints; FORBIDDEN_FIELDS rejects secret material and infrastructure identifiers at parse; a 64 KiB limit; an anti-replay window.

Trusted setup & ceremony — operational detail

Status: NOT performed

NOT performed. All artifacts are DEV; REAL proving is rejected by code unless trust_level == PRODUCTION_CANDIDATE. This is the primary cryptographic blocker.

  • Policy: a minimum of 3 independent contributors, 5+ preferred — an operational floor, not a cryptographic threshold (an updatable ceremony is secure as long as one honest contributor destroys their contribution).
  • freeze fingerprint: SHA-256 over contract + circuit sources, selectors, storage layout, manifest and toolchain, so audited source = ceremony source = deployed source.
  • Independent transcript verification (an output_hash → input_hash chain, a final beacon, frozen_circuit_match); a TEST ceremony promotes nothing.

Conditional soundness (informal)

Conditional soundness (informal). Groth16 soundness (INV-03) holds if and only if (a) the circuits express the correct properties, (b) the verifiers are real (verified), and (c) an honest ceremony exists (pending). Without (c), proofs are forgeable by whoever holds the toxic waste.

Feature-level comparison with RAILGUN

A row marked “implemented” means the feature is built — not that it equals RAILGUN’s in production. RAILGUN is deployed, audited and multi-token; Rootzk is not yet on mainnet, with no external audit or production ceremony. Feature presence is not parity. Read the classification, not the ticks — and this is a comparison, never a claim of equivalence.

DimensionRAILGUN (reference)RootzkClassification
Shielding / unshieldingYes, multi-tokenYes — ETH, amount-carrying noteImplemented — not yet on mainnet
Private transfersYesShieldedFacet + shieldedTransferV2Implemented
Private DeFiYesAbsentOut of scope
ZK circuitsAudited Groth16 + ceremonyReal Groth16 — no ceremony, no external auditPartial (blocking)
Commitments / nullifiersYesPoseidon; on-chain setImplemented & verified
Merkle treesYesPoseidon, depth 20, 100 rootsImplemented
BroadcastersYesReal relayer, key in a subprocessImplemented
Viewing keys / disclosureYesselective_disclosure_v2, stealth view-keyImplemented
Private PoIYesassociation_set_membership_v2 (not enforced on live withdrawal)Partial
Non-custodialYesKeys never in Python — but owner has instant upgrade by defaultPartial / conditional
GovernanceMultisig / timelockDiamond + 48h timelock + M-of-N; 2-of-3 active on testnet (M-04)Partial
Security auditsMultiple externalInternal done; external pendingPartial

The material difference from RAILGUN is less one of design than of maturity — ceremony, external audit and deployment. Rootzk’s most original engineering contribution is the agentic membrane with typed secret confinement and a privacy gate with a built-in attacker; its cryptographic novelty is low (a careful integration of known ideas: Tornado-style pools, join-split, proof-of-innocence, stealth addresses per ERC-5564).

Background — learn the concepts

Rootzk is built on well-studied cryptography. These Academy lessons explain the ideas from first principles — the honest way to understand what the proofs do and do not guarantee.