As of recent weeks, Tezos for the first time has a public testnet running a protocol that allows users to take part in privacy-preserving transactions. Along with this comes a client hardcoded to work with an example contract making use of new Michelson primitives that verify zkSNARKs with Zcash’s Sapling cryptography. The client currently only works with this one contract, although multiple versions of the contract may be originated on-chain. This contract represents a simplistic version of something like a private form of XTZ, hereafter referred to as “ztez”. First, let’s run through how it works on a high level (a more detailed description is available in Nomadic Labs’s documentation):
Its storage is a
sapling_stateand it has one entrypoint that takes as a parameter a pair of a
key_hashfor forwarding funds upon withdrawal. Both
sapling_statetypes are opaque from the standpoint of Michelson and must be constructed and introspected, respectively, off-chain as is done here by the client.
The Sapling transaction is constructed by one of three client commands that take sender and recipient addresses and generates a zkSNARK. At least one of sender and recipient must be a Sapling address generated with an additional client command. Sapling addresses are prefixed with
zet1and correspond to UTXOs in a specific
sapling_state. When both sender and recipient are
zet1addresses, the zkSNARK is written to disk locally and can be passed to the example ztez contract from any transparent account at a later time.
When the sender is a
KT1address the amount proven in the zkSNARK is negative and a corresponding balance in XTZ is included with the Sapling transaction. When the recipient is a
KT1address, the amount proven in the zkSNARK is positive and it’s paired with the transparent address to receive a corresponding XTZ transfer. In both cases the example ztez contract is called immediately rather than writing the transaction to disk with a separate forge command.
It should be noted that the Sapling circuits have no notion of Tezos address types and are agnostic to the denomination of the balance they’re proving. The client described here only takes
KT1addresses in its shielding command in order to bundle XTZ inside the
sapling_transactiontype and does not allow this to be distinct from the amount proven in the zkSNARK. This means that the example ztez contract should never fail in the case of deposits.
The client can also check the balance of a given
zet1account in a given version of the example ztez contract. No functionality is currently provided to use viewing keys distinct from spend authority as would be necessary for regulatory compliance.
Looking forward to how these features can be generalized for use on mainnet in the future, a few points should be made:
As a smart contract platform where native transactions and fee payments are transparent, one of the most compelling use cases for privacy on Tezos lies in private tokens. This means generalizing the Sapling-related client commands and decoupling them from any specific contract.
Ztez could still be implemented by depositing and withdrawing wrapped XTZ compliant with a transparent token standard like FA1.2 or FA2. This would also allow the client to work with a ztez contract that has a floating exchange rate with XTZ due to, e.g., accrual of rewards payments from having delegated its balance.
This would require a modification of the current testnet client’s shielding command in order to allow users to specify the amount proven by the Sapling circuit so that it could be a different amount than transferred in XTZ. The ratio of these two amounts would represent the exchange rate between the private token and underlying transparent token, which could be determined by calling an entrypoint analogous to
%getTotalSupplyin the FA1 standard. Deposits submitted without the correct amount of XTZ would then fail.
Even more than economic incentives, the key to building large anonymity sets is usability. We see this in Zcash, which has recently seen its number of private transactions double month to month after releasing its first two mobile wallet.
It’s not reasonable to expect all users of private tokens to use a CLI client. At the same time, interaction with private token contracts requires an unusually large amount of off-chain computation: specialized wallets with the capability of generating zero knowledge proofs must be used to forge transactions off-chain as well as scan private contracts’ storage and decrypt transactions using viewing keys to show balances.
Much of the purpose of a private token standard should be to enable wallet integration. This can take a form similar to an off-chain version of the current TZIP-10 wallet interaction standard proposed by the Papers team. Ideally privacy schemes should ship with libraries that implement the basic functionality of forging transactions and querying balances in a standardized manner – including bindings to common languages – so that they can be easily integrated by a variety of wallet and tooling engineers.
Given the speed of innovation in zero knowledge cryptography and the fact that many private transaction schemes may not even require upgrades to the protocol, any private token standard should be agnostic to specific privacy-preserving technology (as is the case for zkERC20). This can work with a metadata standard (TZIP-16) to specify the cryptographic engine and storage location.
Private tokens employing different cryptographic engines may only be able to represent a subset of entrypoints included in a standard. For example, the Sapling circuits are only suitable for bearer instruments (in most of the world securities are issued and registered by custodians who are legally required to track ownership and approve transfers, tasks which are impossible with Sapling’s open address set and lack of administrator features) whereas other schemes may allow managed ledgers and therefore also be usable for private security tokens. Still others, e.g. Aztec 2.0, may even be able to represent multiple tokens within the same anonymity set and therefore be used for semi-fungible tokens that have so far been too thinly traded to achieve meaningful privacy. One can imagine optional entrypoints analogous to those in the FA1.2 and FA2 standards for these more advanced forms of private assets.
Finally, a private token standard should be interoperable with future layer two standards Tezos may see in the future. Some cryptographic engines may batch private transactions as part of zkRollups as certain zero knowledge schemes can see significant performance improvements with this approach.
While it’s exciting to finally see private transactions on the horizon for Tezos, considerable work still needs to be done in order for them to be usable by contract and tooling developers. Important open questions remain:
- What features are important for private assets?
- How can we more easily and securely onboard new users when so much off-chain machinery is required?
Sapling is a good start for representing private tokens on Tezos. Thankfully we’re also adding Michelson instructions for operations on the BLS12-381 pairing-based curve that can be used to implement future zero knowledge schemes capable of representing other types of assets without the necessity of a protocol upgrade.