← Duc V. Le
AsiaCCS 2025

DTL: Data Tumbling Layer

A Composable Unlinkability for Smart Contracts. Users commit data to one tumbling contract, then redeem an encrypted copy into any other on-chain application — with the link to the original commit removed.

Mohsen Minaei1, Pedro Moreno-Sanchez2,1,3, Zhiyong Fang4, Srinivasan Raghuraman1,5, Navid Alamati1, Panagiotis Chatzigiannis1, Ranjit Kumaresan1, Duc V. Le1

1Visa Research  ·  2IMDEA Software Institute  ·  3MPI-SP  ·  4Texas A&M University  ·  5MIT

The linked PDF is arXiv v1 (2503.04260, March 2025). Table 2's Mobius and AMR comparison columns on this page come from the later AsiaCCS version and are not in that PDF.

In three numbers

What one tumbling contract buys

Anonymity set
$2^{20}$

Merkle tree of depth $20$ in every application (§7.1). Anonymous Zether's measured transfer uses a set of 8.

Gas, unlinkable withdraw
258,467

$\mathsf{C}_\mathsf{DTL} \rightarrow \mathsf{C}_\mathsf{Conf}$ (Table 3), against 7,306,703 for an Anonymous Zether transfer with 8 users.

Slowest measured proof
1.32 s

Unlinkable withdraw (Table 3) on a 4-core i5-1137G7 laptop with 8 GB of memory. Groth16, Circom, SnarkJS.

The problem

Privacy tools that only do one job

Ledgers publish senders, receivers and amounts. The privacy add-ons that exist — unlinkable mixers like AMR, confidential payments like Zether, stealth addresses — are each built for one function, usually moving money. None of them can be plugged into other contracts. The paper asks:

How can we design privacy-enhancing solutions that can be composed with existing on-chain applications to enhance user privacy throughout the entire system?

① Commit

User $u_i$ commits data $\mathsf{data}_i$ to the tumbling contract $\mathsf{C}_\mathsf{DTL}$. The contract adds a public key $\mathsf{cpk}$ to a Merkle tree.

② Redeem

Later, possibly from a fresh address, the user proves in zero knowledge that they own some leaf and receives $[\mathsf{data}_j]$, an encrypted copy of the data.

③ Compose

That copy flows straight into another contract: a payment, a confidential-balance contract (Zether), or a weighted vote. One layer, many applications.

Two constructions: $\mathtt{DTL}_\mathsf{fixed}$ when every user commits the same data (a fixed coin, an equal vote), and $\mathtt{DTL}_\mathsf{arb}$ for arbitrary data such as amounts. Both are proved correct, unlinkable, and secure against one-more redemption, theft and slander (Theorems 1 and 2).

Interactive 1 · the tumbler

Commit, redeem, and try to cheat

Commit a few users into the tree, then redeem them to fresh addresses. The right-hand panel is everything the chain shows. Then try the three attacks. Each one is stopped by a specific line of the relation below.

Merkle tree in $\mathsf{C}_\mathsf{DTL}$ (8 of $2^{20}$ leaves drawn)

Recent roots $\mathsf{AccList}_{wdr}$ (demo keeps $k = 4$)

What the chain sees · commits

    What the chain sees · redeems

      $\mathsf{TagList}$

      Adversary's best guess

      Start by committing Alice and Bob.
      Relation proved in zero knowledge (Eq. 1)
      ·$\mathsf{MT}.\mathtt{Verify}(i, \mathsf{cpk}, \mathsf{st}, \mathsf{path})$$\mathsf{cpk}$ is a leaf of the committed tree. Which leaf stays hidden.
      ·$\mathsf{cpk} = \mathsf{TAG}.\mathtt{TagKGen}(\mathsf{csk})$The prover knows the secret key behind that leaf.
      ·$\mathsf{tag} = \mathsf{TAG}.\mathtt{TagEval}(\mathsf{csk})$The tag is fixed by the key, so each key yields exactly one tag.
      ·$\mathsf{cpk}' = \mathsf{TAG}.\mathtt{TagKGen}(\mathsf{csk})$As in the fixed case, but $\mathsf{cpk}'$ is used as commitment randomness.
      ·$\mathsf{cpk} = \mathsf{COM}.\mathtt{Commit}(\mathsf{data}; \mathsf{cpk}')$The data is bound to the leaf but hidden by the commitment.
      ·$c = \mathtt{E}.\mathsf{Enc}(\mathsf{ek}, \mathsf{data}; r_\mathsf{enc})$The output is an encryption of the same data. Plaintext output would link redeem to commit.
      ·$P(\mathsf{data}) = 1$An application predicate, e.g. a range proof on an amount.
      Checks by the contract
      ·$\mathsf{st} \in \mathsf{AccList}_{wdr}$The proof's root is one of the $k$ recent roots (see Interactive 4).
      ·$\mathsf{tag} \notin \mathsf{TagList}$This tag has not been redeemed before.

      Hex strings here are illustrative stand-ins produced by a toy hash, not Poseidon/Pedersen outputs. The relation rows follow Eq. 1 ($\mathtt{DTL}_\mathsf{fixed}$) and Eq. 2 ($\mathtt{DTL}_\mathsf{arb}$) in §5.

      Interactive 2 · composed applications

      What each composed route costs

      Every application is a commit into $\mathsf{C}_\mathsf{DTL}$ followed by an unlinkable redeem into somewhere else. Pick a route. Segments are the measured per-transaction gas from Tables 2–4, and totals are sums of those entries.

      • Step 1 · commit into $\mathsf{C}_\mathsf{DTL}$
      • Step 2 · unlinkable redeem
      • Step 3 · reveal (once per candidate)
      • total claimed in §7 text
      Show the numbers as a table

      Interactive 3 · against prior work

      Anonymity set $2^{20}$ without the gas bill

      Mobius pays for its ring at withdrawal time, and Anonymous Zether pays for its anonymity set on every transfer. DTL's verification is a Groth16 check against a Merkle root, so its cost does not grow with the set. Prior-work numbers are the ones reported in those papers, as used in the paper's §7.

        Show the numbers as a table

        Interactive 4 · concurrency (Appendix, Theorem 3)

        How hard is it to knock out a withdrawal?

        Every deposit changes the Merkle root, so the contract keeps the $k$ most recent roots. A withdrawal proves against one of them, say the root at position $i$. An attacker who floods deposits can push that root out of the list. Theorem 3 says this takes exactly $k-i+1$ deposits. With the paper's Ethereum figures (a block gas limit of about $30\text{M}$ and $767{,}565$ gas per deposit), at most $B = 39$ deposits fit in one block.

        Deposits needed · $k-i+1$
        1,000
        Blocks needed · $\lceil (k-i+1)/B \rceil$
        26
        ≈ 6.5 min at 15 s blocks
        Attacker cost · $(k-i+1)\times \mathsf{fee}_{dep}$
        20 ETH
        at the paper's assumed $\mathsf{fee}_{dep}=0.02$ ETH

        $B = \lfloor 30{,}000{,}000 / 767{,}565 \rfloor = 39$. The paper's worked example is $k = 1000$, $i = 1$: at least $26$ blocks (about $6.5$ minutes at $15$-second blocks) and $20$ ETH, which it prices at about \$36,000, to temporarily block one withdrawal. The honest user can just resubmit against a newer root. Block time, fee and gas limit are the paper's assumptions, not measurements.

        Guarantees

        What is proved

        PropertyMeaning (from §1)$\mathtt{DTL}_\mathsf{fixed}$$\mathtt{DTL}_\mathsf{arb}$
        CorrectnessA user who committed data can redeem the same data for use in other applications.Thm 1Thm 2
        No one-more redemptionA user who commits $n$ data inputs cannot redeem more than $n$ outputs.Thm 1Thm 2
        Theft preventionAn adversary cannot use data previously committed by an honest user.Thm 1Thm 2
        Non-slanderabilityAn adversary cannot stop an honest user from redeeming their committed data.Thm 1Thm 2
        UnlinkabilityAn adversary with the contract's state cannot link a user's commit to their redeem. For arbitrary data, it cannot tell whether a specific committed item is the one being redeemed.Thm 1Thm 2

        Assumptions: Theorem 1 assumes a secure zk-SNARK, Merkle tree and tagging scheme. Theorem 2 adds a hiding and binding commitment scheme and IND-CPA encryption. Proofs are in the full version (arXiv 2503.04260).

        Table 1 · Comparison with previous works

        Anonymity setConfidential amountComposabilityAvailabilityApplications

        ● yes   ○ no   ◐ partially provided, i.e. it composes with only a single application. Off-chain solutions rely on servers that can go offline or misbehave, which hurts availability.

        Measured results

        Tables 2–4, as printed

        Groth16 over BN254 with Poseidon for the Merkle tree, Pedersen for tags and commitments, and ElGamal on Baby Jubjub for $\mathtt{DTL}_\mathsf{arb}$. The tree has depth $20$. Measured on a 4-core Intel i5-1137G7 with 8 GB of memory.

        Table 2 · Unlinkable fixed-amount payment. Mobius anonymity set 8; AMR and DTL up to $2^{20}$.
        DTL DepositDTL WithdrawMobius DepositMobius Withdraw(8)AMR DepositAMR Withdraw
        R1CS constraints-8,146---28,275
        Proving time-1.15s-3.254ms-8.325s
        Gas cost767,565233,375105,3462,685,7121,036,580325,051
        Table 3 · Confidential and unlinkable payment. Anonymous Zether anonymity set 8; ours $2^{20}$.
        Transparent deposit
        addr → C_DTL
        Confidential deposit
        C_Conf → C_DTL
        Unlinkable withdraw
        C_DTL → C_Conf
        AZether
        Transfer (8)
        R1CS constraints-8,67111,521-
        Proving time-1.16s1.32s1.9s
        Gas cost767,5651,051,112258,4677,306,703
        Table 4 · Unlinkable weighted voting
        Registration
        addr_voter → C_DTL
        Unlinkable vote
        C_DTL → C_Conf
        Reveal
        addr_i → C_Conf
        R1CS constraints-11,5216,750
        Proving time-1.19s1.03s
        Gas cost767,565258,467291,151

        Where the sources disagree

        1. Gas for confidential → DTL → confidential. The §7 text says the second payment route costs "approximately $1.8$m", and the abstract and conclusion say gas is under $1.8$ million. Table 3's entries for that route sum to $1{,}051{,}112$ + $258{,}467$ = $1{,}309{,}579$, and no table entry gives $1.8$m. The first route's text figure ("approximately $1$m") agrees with its sum, $1{,}026{,}032$. The linked arXiv v1 says "around $1.1$ to $1.8$ million gas". This page charts the table sums and shows the text claim as a marker.
        2. Two proving times for one circuit. The unlinkable withdraw (Table 3) and the unlinkable vote (Table 4) have the same constraint count ($11{,}521$) and the same gas ($258{,}467$), but their proving times are reported as $1.32$s and $1.19$s. Both are shown as printed.
        3. Anonymous Zether's anonymity set. Table 1 lists it as $<2^5$, while §7 quotes its cost for $64$ users ("rising to $36$m"). The $64$-user point is shown as a text-only marker.
        4. Linked PDF version. The PDF linked from this page (arXiv v1) has Table 3 with the Anonymous Zether column, but its Table 2 lacks the Mobius and AMR columns used here.

        Reference

        Cite this work

        @misc{le2025dtldatatumblinglayer,
          title         = {DTL: Data Tumbling Layer. A Composable Unlinkability for Smart Contracts},
          author        = {Mohsen Minaei and Pedro Moreno-Sanchez and Zhiyong Fang and
                           Srinivasan Raghuraman and Navid Alamati and Panagiotis Chatzigiannis and
                           Ranjit Kumaresan and Duc V. Le},
          year          = {2025},
          eprint        = {2503.04260},
          archivePrefix = {arXiv},
          primaryClass  = {cs.CR},
          url           = {https://arxiv.org/abs/2503.04260}
        }

        Source code. The paper has no code or data availability statement, so this page links no implementation. The evaluation (§7) used Circom circuits, SnarkJS for proving and for generating the Solidity verifiers, and the Truffle toolchain on a test network.