Zero Knowledge Token Transfer Service: A Tech Overview
This blog describes our new marketplace offering, the Zero Knowledge Token Transfer Service. The new service leverages Zero Knowledge Proofs to allow users to transfer ERC20 tokens confidentially and anonymously.
The underlying zero knowledge proof system is Σ-Bullets, proposed by Benedikt Bünz etc. in the paper Zether. The details about Σ-Bullets is outside the scope of this post, but they essentially allow Bulletproofs to work with Sigma protocols. Zether enables confidential token transfer between parties and allows for on chain verification of these transfers. We actually use the anonymous extension of Zether as proposed by Benjamin Diamond from JPMorgan’s Quorum team, who adds anonymity to the confidential token transfers.
The service is currently available as a Kaleido Labs Service. This post covers a brief on why we chose Zether, how the service works, best practices for use and some gotchas to be aware of.
Need for Privacy
One of the greatest strengths of blockchain is data immutability, which is provided by having a decentralized group of parties maintaining a shared ledger. As a result, all data on the blockchain are shared with each party having a complete copy. In addition, all transaction execution are performed locally on each node, so transaction payload is also shared with every participant in the network.
In many use cases, especially with enterprise use cases, privacy is the topmost requirement. How to stay true to the blockchain’s architectural integrity while offering privacy becomes the topic of many leading research groups and technical communities alike.
Privacy is a broad concept and means different things to different people. At a high level, privacy encapsulates two fundamental concerns: confidentiality and anonymity. Confidentiality refers to the need to conceal the transaction details, in terms of what amount is being paid. Anonymity refers to the need to conceal the parties of the transaction, in terms of who is paying who.
Several approaches exist to address privacy requirements from different layers. They are applicable to different types of use cases.
- Data Isolation: by keeping private data to only the transaction parties, this approach allows only a subset of the blockchain network nodes to be able to execute and validate the private transactions. As a result, the state is fragmented and there is no global consensus on a shared state.
This is applicable in use cases where transactions are independent and no global state is required. For instance, in trade finance, each import/export deal is independent of another deal. By sending private transactions only among parties of the finance deal, data privacy is achieved. This is also called off-chain transactions.
A hash of the transaction payload is submitted to the blockchain as a normal transaction for all nodes in the network to execute and validate, as audit trail and security protection.
This technique is employed by both Enterprise Ethereum Alliance’s (EEA) private transaction standard, and Hyperledger Fabric private data collections. More details are available in our blog “Enterprise Blockchain Protocols: A Technical Analysis of Ethereum vs Fabric”.
- Trusted Computation: the idea here is that execution of the transaction is performed by a party, or a group of parties, that can convince the rest of the network’s participants that the computation is done honestly and data has not been manipulated. Because the transaction data itself is kept off-chain, and only the proof or attestation is submitted to the chain for verification, privacy is achieved.
Because proof is submitted as a regular transaction and verification is performed by all nodes in the network, a shared global state is maintained making this approach ideal for use cases that require a shared global view of the blockchain state, such as tokens based dApps.
Specific technologies for trusted computation include zero knowledge proof, trusted execution environment, and multi-party computation. Details of these technologies are beyond the scope of this blog. Details on how to utilize them in blockchains are available in EEA’s Off-chain Trusted Compute Specification.
Of the different technologies for trusted computation, zero knowledge proof is the most utilized in production. zkSNARK based Zcash has been running zero knowledge proof based transactions since October 2016. In recent years, a number of projects have sprung up from both academia and startups to apply ZKP technologies to Ethereum, such as Zokrates by Cornell University and AZTEC by the startup sharing the same name.
In early 2019, researchers from Stanford University and Visa jointly published the Zether protocol, a range proof based zero knowledge proof technology for Ethereum. In the following sections we will introduce some details of the protocol and Kaleido’s integration effort to make it part of the enterprise blockchain full-stack architecture.
Zether and its anonymity extension
Our current blockchain stack is mainly comprised of ethereum based networks like Geth, Quorum and Pantheon. Quorum and Pantheon provide support for EEA-compliant private transactions, which means verification of the private state is not possible to anyone who is not part of the transaction.
Zero Knowledge Proof systems allow for sharing of encrypted private state to everyone. Parties can submit transactions which include information (encrypted) to transition current state to new private state (encrypted) along with a proof which verifies that such state transition was a valid one.
Zether can be readily integrated into any account based smart contract platforms like Ethereum without any changes to the design of the platform. A basic Zether integration for confidential transfer of tokens works roughly like this,
- Zether smart contract (ZSC) is deployed on the network. There is one to one mapping between an ERC20 contract and the corresponding ZSC.
- In ZSC, accounts are identified by public keys.
- ZSC maintains mappings between accounts and encrypted balances.
- On ZSC, users can
- Fund an account with ERC20 tokens and obtain equal amount of shielded tokens (Let’s call them ZTH) in exchange. The ZSC is designated as the escrow of the ERC20 tokens.
- Transfer ZTH tokens from one account to another.
- Withdraw to exchange ZTH token from an account back to ERC20 tokens, at which point the ZSC releases the ERC20 tokens back to the original funding Ethereum account.
- To do a confidential transfer, users must
- Hold a private key corresponding to the account in ZSC.
- Must have enough ZTH balance to transfer.
- Create a confidential transfer by encrypting the value to send using both their public key and receiver’s public key, and a zero knowledge range proof which proves that the encrypted value is positive, and the user’s balance after the transfer is positive.
- The transaction with the payload is sent to ZSC, which updates the encrypted state of both the sender and the receiver after verifying the proof.
- To do a withdraw, users must
- Create a zero knowledge proof for the knowledge of the balance the account holds without revealing the corresponding private key.
- Send the transaction to ZSC with the proof, at which point the ZSC credits the equal amount of ERC20 tokens back to user’s Ethereum address.
Above description leaves out prevention of possible front-running attacks and how they are mitigated in the Zether design. Details can be found in the Zether paper. The paper also proposes how to make confidential transfer anonymous. That proposal is enhanced and extended by Ben Diamond from Quorum team in his anonymous Zether work. The transfers are modified to additionally encrypt 0 by public keys of the additional participants (let’s call them the decoys), along with a random number. Note that the anonymity feature comes at a cost, with the proof generation and verification time being O(N*logN), and proof size O(N), where N is the size of the anonymity set.
The Zether protocol applies transactions in epochs. Epochs allow for protection against front-running and replay types of attacks. A transfer or a burn proof can be successfully verified only in the same epoch that the proof is generated for. Since proofs are generated against current state of sender, a proof becomes invalid if sender’s state changes due to an incoming transfer. The result is there would be many failed transfer transactions due to failed proof verifications.
To prevent against this, epoch is introduced in Zether transactions processing. All transfers are put into pending state during an epoch and are only applied in a future epoch whenever the account is trying to spend the funds.
While epoch solves the state management problem between the proof generator and the proof verifier, it also requires careful arrangement of transfer transactions in the client application. Let’s use the diagram below to illustrate.
Epoch length in the above example is configured to the equivalent of three block periods. Transfer transaction 1 starts at time ts1 and ends at te1. Transfer transaction 2 starts at time ts2 and ends at te2. Same is true for transaction 3.
Notice that the proof for the first two transactions were generated during epoch 1, and both transactions were submitted before epoch 1 ended. Both transactions will be successfully verified as expected.
For transaction #3, however, the proof generation was started during epoch 1 and did not complete until epoch 2, but verification is performed during epoch 2. As a result, it was marked as invproof verifier inside the ZSC contract. The transaction is marked as failed by the EVM.
Epoch length is an important parameter which depends on block intervals, proof generation and verification times. Epoch length should definitely be greater than the sum of proof generation time, proof verification time and propagation time. This ensures that epoch used during the proof generation is the same one that is used during verification by ZSC. Epoch length should also be greater than the block period, should allow for multiple blocks to be published within an epoch duration. More on epochs later in best practices section.
Zero Knowledge Token Transfer
Kaleido’s Zero Knowledge Tokens service can be added from the marketplace or the service catalog via the “Add -> Add Services” button in the environment dashboard.
This is a Kaleido member service, so each member of the consortium can provision their own instance in the Kaleido environment. In the initial release, the service is available to Quorum with both Raft and IBFT as consensus algorithm. Support for other protocols will come soon.
Once successfully provisioned, a user can interact with the service in the Kaleido service dashboard UI. Click on the service instance entry in the “Member Services” section to navigate to the service dashboard.
Notice a few key concepts as you step through the workflow:
- Shield ERC20 contracts: shielding an ERC20 contract deploys a ZSC which is tied to the ERC20 contract in order to call it to perform token operations such as transfer() and transferFrom().
- Shield Ethereum accounts: shielding an Ethereum account generates a special key pair, called Shielded Account, used by the ZSC to hold ZTH balances. The public key of this type of account is a 128-byte hexadecimal string, broken into an array of two 64-bytes long hex strings. Unlike the ethereum addresses, which is the first 20-bytes of the hash of the original public key, shielded accounts use the original public key as the identifier.
- Public Supply: refers to the total supply of the ERC20 tokens in the contract. This value is determined, as per the ERC20 specification, by the initial supply plus minted amount minus burned amount.
- Shielded Supply: refers to the total supply of the ZTH tokens in the ZSC. This value is determined by the sum of all funding operations minus the withdrawals.
Whenever the current balance of a shielded account needs to be acquired, such as when generating a transfer proof, or a withdraw proof, or querying for balance, a computation intensive step is required. This is a CPU-bound operation. To avoid the service to tie up CPUs and prevent other workloads to be processed in the shared infrastructure, the service currently limits total shielded balance to a relatively low number of 5000. This should still be enough to allow early tier kickers to try out the design and workflow, in order to determine if the functionalities meet their use case requirements. The Kaleido Labs team is working on improving the architecture so as to remove this temporary limitation soon.
Due to the epoch parameter being sensitive to the consensus protocol, bit of tuning is necessary to ensure they are set at the optimal value for the consensus. As a result the new Zero Knowledge Tokens service is offered on Quorum only at the moment. Of course our team is working on making it more widely available to other protocol providers.
The trickiest part of Zether is alignment with epoch. Our team at the moment can not completely guarantee that transfer transactions are submitted at the optimal point in time relative to the current epoch. As a result, you may experience transfer failures, especially on IBFT given its longer block period (10 seconds by default). We recommend trying it again which usually fixes the issue. If they continue to fail, please do not hesitate to contact Kaleido with a ticket.
Current Performance and Bottlenecks
A good performance indicator for the service would be shielded transfer transactions throughput. By design, currently only one transfer-out transaction can be made from a single account in an epoch. But an account can receive multiple transfer-in transactions in an epoch. Then the maximum possible achievable throughput would be number-of-accounts/epoch-length.
The epoch length is lower bounded by proof generation and verification time. The block gasLimit restricts the number of transactions inside the block. Either higher block gasLimit or having more blocks within an epoch will allow more transactions to go through. Kaleido already sets the targetGasLimit to maximum value, so block gasLimit will not be the performance bottleneck. For anonymity set of size 8, which we recommend, takes approximately 2200 ms to generate proof and 135 ms to verify. The gas consumption is around 27 million. [source: AnonZether]
We are currently working on benchmarking the proof generation and verification time on our platform with different resource settings and will update our findings. This would be part of a separate post on performance of Zero Knowledge Token Transfer Service.
Gotchas and workarounds
There are several gotchas and suggested workarounds to help you understand and use the service better. This might not cover everything but feel free to reach out if you are stuck while using the service.
If your applications are hitting transaction failures during a transfer, most likely it’s due to proof generation vs. proof verification end up in different epochs. To mitigate this, we recommend two techniques:
- Make sure when the current state is downloaded from ZSC, it’s as close to the beginning of the current epoch as possible. Epoch length is currently set to 15 seconds for both Raft and IBFT. This is subject to change in the future but it will be clearly documented. Every Zether epoch starts at a multiple of 15 seconds since the Unix time Epoch.
- Capture the failure and retry in the next epoch. Time until next epoch can be given by
ceil(currentTimeInSeconds/ ZSC_EPOCH_LENGTH) * ZSC_EPOCH_LENGTH – currentTimeInSeconds
The service currently allows users to generate only one shielded account (elgamal pair) per Ethereum account. This is a design decision in Kaleido to make the user experience easy to follow. The underlying ZSC implementation allows multiple elgamal accounts to be mapped to a single Ethereum account. So it’s conceivable that in the future this will be also enabled in the Kaleido service. If you have a use case that requires a single Ethereum account to be mapped to multiple shielded accounts, we would like to hear from you. The easiest way to contact us about such requirements is clicking the Contact Us button in the product.
Finally, you need to register the Ethereum account to shielded account mapping on ZSC contract before you can fund it and do transfer and withdraw from sender account. The withdraw operation will transfer the ERC20 funds to the Ethereum account to which the shielded account (elgamal public key) was registered with.
Anonymity is as good as the anonymity set and there is definitely a tradeoff between performance and size of anonymity set. The best case scenario will be to use all registered accounts in every transfer transaction but it would downgrade the performance and transfer might even fail if proof generation and verification times cross epoch boundary. The current implementation only allows anonymity sets of size of 2^k for k>=1. This is for efficient proof generation. For anonymous transfers, it is good practice to include keys that are funded. As it is easy to rule out accounts which were never funded, as decoys. It is also good practice to only withdraw from an account once it has been part of good amount of transfer transactions.
Besides private ERC20 token transfers, which is applicable in many enterprise use case scenarios, such as private payments, Zether can also be used in other patterns of applications such as blind auctions. The Kaleido Labs team will continue to collaborate with the open source community to make such patterns click-button easy to adopt.
Lastly, it is worth pointing out that the private token transfers pattern adopted by Kaleido’s Zero Knowledge Token Transfer service is agnostic to the specific underlying zero knowledge protocols. In the future, support for other protocols may be added to allow a variety of choices for clients with different skill sets.
If you have any questions about this service or Kaleido’s solutions, feel free to contact us.