Thanks to Haichen Shen, Peter Garamvölgyi, Jonas Theis, and Luke Parker for comments.

EIP-7503 proposes an in-protocol private transfer feature for Ethereum. The EIP is slightly under-specified and never got traction among core devs (for concerns around lower than ideal bit security and no post-quantum security) however its key idea is quite simple and brilliant; Funds that are sent to a random address are effectively burned, and if a user can prove knowledge of pre-image of an address they can safely re-mint its balance. Now since we can do the poof in ZK, enshrining this in protocol will enable users to move their funds to new accounts without linking it directly to the origin of funds.

The key benefit of EIP-7503 comparing to Tornado Cash and other out of protocol privacy solutions is the plausible deniability, since depositing into the privacy pool looks like sending funds to a random address, it is undistinguishable from all other transfers to fresh accounts without outgoing transfers.

Simplified, Generalized, and Compliant

On Ethereum, the withdrawal from the privacy pool requires minting new coins, so this has to be implemented with a new transaction type. However, for a rollup the logic can be implemented in the canonical bridge, and could be generalized to bridged ERC20 tokens too (not native tokens). The proposal is for L2s to add wormhole_transfer_ETH(amount, dest_address, proof_of_burn, nullifier, blocked_addr_list_root) and wormhole_transfer_ERC20(token, amount, dest_address, nullifier, proof_of_burn, blocked_addr_list_root) functions to the their canonical bridge to “mint” new ETH/tokens with proper proof of burn.

Moreover, under EIP-7503 every user of the protocol gets the same privacy pool, with no ability to disassociate from a specific subset of potentially bad actors. Following the lead of http://privacypools.com/, we adopt a proof of burn that takes a list of blocked deposits as public input. Sophisticated entities will provide updated blocked address lists that users can utilize to disassociate from bad actors.

<aside> ⚠️

Under a vanilla design for proof of disassociation, the complexity of the proofs for well behaving users may blow up after a hack that distribute funds to many addresses, as a result making the list of bad addresses super large. This part of design needs more investigation.

</aside>

The Protocol

Deposit

draw a random secret and send funds to address sha256(MAGIC_ADDRESS | secret)[12:].

<aside> 💡

Best practice is to use a deterministic pseudo-random algorithm to derive secret from the private key pk, so they don’t have to custody secret moving forward; e.g. secret = sha256(MAGIC_SECRET | pk | nonce) for increasing nonce.

</aside>

Circuit Constraints

<aside> 💡

The constraints are described for ETH only but can be easily adopted for ERC20 tokens.

</aside>

<aside> 💡

The purpose of the first constraint is to avoid the risk of withdrawal transactions being snipped in mempool.

</aside>

public input: nullifier, withdrawal_nullifier, block_hash, amount, dest_address