Transactions in well-known cryptocurrencies such as Bitcoin and Ethereum are publicly recorded and permanently stored in the blockchain. While blockchain’s decentralization provides transparency, it can also compromise privacy. In a centralized model, privacy is easily achieved as long as the central party is trusted. In traditional banking services, a customer’s transaction is kept confidential unless there is a legal reason to disclose it. However, achieving both decentralization and privacy can be challenging because transactions must be visible to everyone for proper verification of transaction history.

One way to hide transaction history in coins is by using a mixing service. The mixing service receives coins from multiple users and combines them to obscure the destination of each transaction. Nevertheless, there are drawbacks: maintaining privacy can be challenging when there are few users. Additionally, the mixing service has access to all information and may potentially steal coins if it behaves maliciously.

Zerocash↗, proposed by Ben-Sasson et al., is a decentralized anonymous payment scheme and is considered one of the privacy coins. It allows a user to send coins **without revealing the destination or amount in a decentralized manner** using zero-knowledge proofs.

In this post, we will discuss the structure of Zerocash and its real-world applications, specifically Zcash↗.

## Backgrounds

In this section, we will introduce basic cryptographic protocols which are building blocks of Zerocash.

## Commitment Scheme

A **commitment scheme** is a cryptographic protocol that enables a party to commit to a message or value without revealing the value itself, and later reveal it in a verifiable manner. This is useful in scenarios where a party needs to prove that they made a commitment to a particular message at an earlier time without revealing the message until a later time.

Commitment schemes can be constructed using a cryptographic hash function. To commit a message $m$, it is enough to reveal $\text{com} = h(r || m)$, where $r$ is a randomness, $h$ is a cryptographic hash function, and $||$ denotes a concatenation operation. Due to preimage resistance property, it is impossible to recover $m$ from $\text{com}$. We will denote a commitment for a message $m$ with a given randomness $r$ as $\text{com}_r(m)$.

## Pseudorandom Function

A **pseudorandom function (PRF)** is a function that takes input $x$ and outputs a string that *looks* random. The PRF is initialized with a seed, which is generally public. It is deterministic, so if the seed and $x$ are the same, the output is always the same. Additionally, if $x$ is unknown, the output is unpredictable.

The PRF can also be constructed from a cryptographic hash function. For instance, $\text{PRF}_{\text{seed}}(x) = h(\text{seed} || x)$ is unpredictable.

## Zero-Knowledge Proof and zk-SNARK

A **zero-knowledge proof** is a cryptographic technique that allows a prover to convince a verifier of the truth of a statement to a verifier without revealing any additional information beyond the truth of the statement. This technique enables the prover to exhibit knowledge of a confidential value to the verifier without disclosing that value or any related details. Zero-knowledge proofs must satisfy three properties:

**Completeness.**If the statement is true, then an honest prover should be able to convince the verifier of its truth.**Soundness.**If the statement is false, then no cheating prover should be able to convince the verifier of its truth.**Zero-knowledgeness.**The proof should not reveal any additional information beyond the truth of the statement.

Intuitively, the prover can convince their knowledge of $x$ such that $f(x) = y$ without revealing $x$ itself to the verifier.

zk-SNARK is a type of zero-knowledge–proof protocol that has one additional feature called **succintness** (i.e., proof size and verifier time are sublinear). Zerocash uses zk-SNARK for zero-knowledge proofs. Even without knowledge of how zk-SNARK works, one can understand the structure of Zerocash based on the aforementioned properties. Therefore, we will omit further details. Check Intro to Zero-Knowledge: A Guided Tour of the ZK Landscape, Part 1↗ in our blog if interested.

## UTXO: Transactions in Bitcoin

To gain a better understanding of the Zerocash protocol, it is important to first examine how transactions work in Bitcoin. In Bitcoin, the amount of assets each user holds is managed in **unspent transaction output (UTXO) model**. In a typical banking system, each user has a balance and the amount of the balance increases or decreases depending on deposits/withdrawals. This model is called the account model. However, in the UTXO model, individual units of coins named as UTXOs are transferred between users.

Assume that Alice and Bob earn 6.25 BTC from mining, respectively. Then each mining result is recorded in transaction. In other words, Alice and Bob each have 6.25 BTC UTXO. UTXO lists for each user are shown below:

- Alice : 6.25 BTC,
- Bob : 6.25 BTC.

They wish to send 1 BTC to Charles. Then, Alice’s transaction input is the 6.25 BTC UTXO she mined, and outputs are 1 BTC UTXO for Charles and 5.25 BTC UTXO for Alice. She retrieves a change by sending the remaining coin to herself. Now, 6.25 BTC transaction output is spent and a new 5.25 BTC UTXO is created. Sames goes for Bob. UTXO lists for each user are shown below:

- Alice : 5.25 BTC,
- Bob : 5.25 BTC,
- Charles : 1 BTC, 1 BTC.

Finally, Charles wants to send 1.5 BTC to Daniel. Then Charles’s new transaction inputs are two 1 BTC UTXO, and the outputs are that 1.5 BTC UTXO belong to Daniel and 0.5 BTC UTXO belong to Charles. UTXO lists for each user are shown below:

- Alice : 5.25 BTC,
- Bob : 5.25 BTC,
- Charles : 0.5 BTC,
- Daniel : 1.5 BTC.

Graphical overview of transactions example. |

A transaction is considered valid if 1) UTXO inputs have not been used before, 2) the sum of inputs and outputs are equal (transaction fee is ignored for simplicity), and 3) the transaction is generated by senders. The first two conditions can be easily verified. To verify the third condition, a digital signature for the transaction is embedded in the transaction. If the UTXO inputs belong to more than one owner, then multi-signature↗ is embedded.

## Zerocash Protocol

A transaction in the Zerocash protocol is based on a transaction in Bitcoin. To clarify, since the Zerocash protocol also uses the UTXO model, we only need to fulfill the three aforementioned conditions. However, it may be impossible to satisfy all of these conditions when the destination and even the amount are hidden. For instance, it may seem strange to verify a sum of inputs and a sum of outputs are equal without revealing the amounts. Nevertheless, this is made possible through the use of **zero-knowledge proofs**.

It has been mentioned that a prover can convince a verifier of their knowledge of $x$ such that $f(x) = y$ without revealing $x$ itself to the verifier using zero-knowledge proof. Therefore, once we represent all conditions of a transaction in equations, the sender can prove the validity of their transaction, revealing sensitive information. We will discuss how to set the conditions into equations. The simplification below will give us a bigger picture of the protocol, and then we’ll break it down to understand the details of a Zerocash transaction.

## Protocol (Simplified)

This is the simplified version of the Zerocash protocol introduced in the Zerocash paper. In this protocol, the value of the coin is fixed. If Alice wants to mint a new coin $c$, Alice does the following:

- Alice samples a random serial number $sn$ and a trapdoor $r$, computes a coin commitment $cm = \text{com}_r(sn)$. Now a new coin $c := (r,sn,cm)$ is defined.
- A corresponding mint transaction $\text{tx}_\text{Mint}$ containing $cm$ is sent to the ledger. Note that neither $sn$ nor $r$ will be sent.

If an Alice wants to spend $c$, Alice does the following:

- Alice posts transaction $\text{tx}_\text{Spend}$ containing the coin’s serial number $sn$ and a zk-SNARK proof $\pi$ of her knowledge of $r$ such that $\text{com}_r(sn)$ appears in the list of coin commitments. The transaction is valid if $sn$ does not appear in past spending transactions.

Maintaining the list of coin commitments in a Merkle tree↗ enables efficient membership checks while ensuring anonymity through the zero-knowledgeness of zk-SNARK. Users can prove their knowledge of $r$ without revealing which coin corresponds to $\text{com}_r(sn)$ in the list of coin commitments.

## Protocol (Exact)

While the simplified version helps to understand the basic concepts of Zerocash, it has several limitations. Firstly, assuming that one coin corresponds to one dollar, if a user wants to transfer 100 dollars, they must post 100 transactions, which is inefficient and leaks the amount. Secondly, transferring a coin is not naturally defined. In this procedure, Alice can prove that coin $c$ was minted by herself. However, if Alice wishes to transfer a $c$ to Bob, the only possible way is to inform Bob of $r$ and $sn$. In this scenario, Alice may spend $c$ before Bob, and even if she does not, if Bob spends $c$, Alice will become aware of it by examining the $sn$ in the transaction.

The exact protocol includes several methods to prevent these flaws. Let’s check out the protocol.

In the full protocol, each user generates addresses. As usual, address is a key pair $(a_{pk}, a_{sk})$ where $a_{pk}$ is a public key and $a_{sk}$ is a private key. The key pair is generated by randomly sampling $a_{sk}$ first and setting $a_{pk} := \text{PRF}_{a_{sk}}(0)$. A user can generate any number of address key pairs.

### Minting

To mint a new coin $c$ with value $v$, Alice follows these steps:

- Alice samples $\rho$ and then the coin’s serial number $sn := \text{PRF}_{a_{sk}}(\rho)$.
- Alice computes $k := \text{com}_r(a_{pk} || \rho)$ for a random $r$, then computes $cm := \text{com}_s(v || k)$ for a random $s$.
- A coin $c := (a_{pk}, v, \rho, r, s, cm)$ and a mint transaction $\text{tx}_{\text{Mint}} := (v, k, s, cm)$ are generated.

Compared to the simplified protocol, the transaction includes the user’s public address. Additionally, the commitment process has become more complex. This nested structure allows anyone to verify that $cm$ is a commitment for a coin value $v$ by checking that $cm = \text{com}_s(v || k)$ **without zk-SNARK proofs**, while the owner and serial number remain hidden.

### Pouring

Coins are spent using the **pour** operation, which works similarly to the UTXO model. The operation takes a set of input coins to be consumed and pours their value into a set of fresh output coins.

The procedure for Alice, with address key pair $(a_{pk}, a_{sk})$, consumes her coin

$c^{\text{old}} := (a_{pk}^{\text{old}}, v^{\text{old}}, \rho^{\text{old}}, r^{\text{old}}, s^{\text{old}}, cm^{\text{old}})$

and produces two new coins:

$c_1^{\text{new}} := (a_{pk,1}^{\text{new}}, v_1^{\text{new}}, \rho_1^{\text{new}}, r_1^{\text{new}}, s_1^{\text{new}}, cm_1^{\text{new}})$ and $c_2^{\text{new}} := (a_{pk,2}^{\text{new}}, v_2^{\text{new}}, \rho_2^{\text{new}}, r_2^{\text{new}}, s_2^{\text{new}}, cm_2^{\text{new}})$

The procedure is as follows.

- Alice samples $\rho_i^{\text{new}}$ for $i \in \{1,2\}$.
- Alice computes $k_i^{\text{new}} := \text{com}_{r_i^{\text{new}}}(a_{pk,i}^{\text{new}} || \rho_i^{\text{new}})$ for a random $r^{\text{new}}_i$ for $i \in \{1,2\}$.
- Alice computes $cm_i^{\text{new}} := \text{com}_{s_i^{\text{new}}}(v_i^{\text{new}} || k_i^{\text{new}})$ for a random $s_i^{\text{new}}$ for $i \in \{1,2\}$.
- Now two new coins $c_1^{\text{new}} := (a_{pk,1}^{\text{new}}, v_1^{\text{new}}, \rho_1^{\text{new}}, r_1^{\text{new}}, s_1^{\text{new}}, cm_1^{\text{new}})$ and $c_2^{\text{new}} := (a_{pk,2}^{\text{new}}, v_2^{\text{new}}, \rho_2^{\text{new}}, r_2^{\text{new}}, s_2^{\text{new}}, cm_2^{\text{new}})$ are produced. Alice produces a zk-SNARK proof $\pi_{\text{Pour}}$ of her knowledge of $c^{\text{old}}, c_1^{\text{new}}, c_2^{\text{new}}$ and $a_{sk}^{\text{old}}$ such that
- The coins are well-formed: $k^{\text{old}}=\text{com}_{r^{\text{old}}}(a_{pk}^{\text{old}})$, $k_1^{\text{new}}=\text{com}_{r_1^{\text{new}}}(a_{pk,1}^{\text{new}})$, $k_2^{\text{new}}=\text{com}_{r_2^{\text{new}}}(a_{pk,2}^{\text{new}})$, $cm^{\text{old}} = \text{com}_{s^{\text{old}}}(v^{\text{old}} || k^{\text{old}})$, $cm_1^{\text{new}} = \text{com}_{s_1^{\text{new}}}(v_1^{\text{new}} || k_1^{\text{new}})$, and $cm_2^{\text{new}} = \text{com}_{s_2^{\text{new}}}(v_2^{\text{new}} || k_2^{\text{new}})$.
- The address secret key matches the public key: $a_{pk}^{\text{old}} = \text{PRF}_{a_{sk}^{\text{old}}}(0)$.
- The serial number is computed correctly: $sn^{\text{old}} := \text{PRF}_{a_{sk}^{\text{old}}}(\rho^{\text{old}}).$
- The coin commitment $cm^{\text{old}}$ appears in the list of coin commitments.
- The vaules add up: $v_1^{\text{new}} + v_2^{\text{new}} = v^{\text{old}}$.

- A resulting pour transaction $\text{tx}_{\text{Pour}} := (sn^{\text{old}}, \text{cm}_1^{\text{new}}, \text{cm}_2^{\text{new}}, \pi_{\text{Pour}})$ is appended to the ledger. The transaction is valid if $sn^{\text{old}}$ has not appeared in past pour transactions.

The below illustration introduces a structure of the coin commitment.

Illustration of the structure of a coin commitment. $pk_{enc}$ refers receiver’s public key in public-key cryptosystem mentioned in point 1. Images from Zerocash paper. |

The statements that zk-SNARK $\pi_{\text{Pour}}$ proves are quite complicated. It is recommended to check each statement carefully. To help the reader understand, here are some **points**:

- If Alice wants to send $c_1^\text{new}$ to Bob, she must first need to know Bob’s address public key and set $a_{pk,1}^{\text{new}}$ as Bob’s address public key. Moreover, she also needs to provide $(v_1^\text{new}, \rho_1^\text{new}, r_1^\text{new}, s_1^\text{new})$. These can be transferred over a private channel or by preparing a public-key cryptosystem beforehand and adding ciphertext $\text{Enc}(v_1^\text{new}, \rho_1^\text{new}, r_1^\text{new}, s_1^\text{new})$ with Bob’s public key in transaction.
- Suppose $(v_1^\text{new}, \rho_1^\text{new}, r_1^\text{new}, s_1^\text{new})$ are provided using public-key cryptosystem. Then in order to receive a coin, Bob should verify the entire pour transactions so that $\text{Enc}(v_1^\text{new}, \rho_1^\text{new}, r_1^\text{new}, s_1^\text{new})$ is decrypted with Bob’s own private key.
- If Alice does not know $a_{sk,1}^{\text{new}}$, then Alice cannot generate valid $\pi_{\text{Pour}}$ to spend $c_1^{\text{new}}$. In particular, it contradicts with the second statement,
*the address secret key matches the public key*. - Assume that $c_1^\text{new}$ belongs to Bob. When Bob spends $c_1^{\text{new}}$, Alice cannot find it, because $sn_1^{\text{new}} = \text{PRF}_{a_{sk,1}^\text{new}}(\rho_1^\text{new})$ is revealed in the transaction, but Alice does not know it.

Now that we understand the protocol, let’s check out a real-life implementation of Zerocash: Zcash.

## Zcash: An Implementation of Zerocash

Zcash↗ is an implementation of Zerocash. Zerocash was announced in 2014, and Zcash was launched in 2016. Since then, it has undergone continuous development. In essence, Zcash follows the approach introduced in Zerocash but with several changes made over the past decade. It has evolved from a simple proof-of-concept implementation to a vast ecosystem with a market capitalization of approximately 400 million dollars.

Zcash offers two types of address: z-addresses and t-addresses. Transactions between t-addresses are transparent, similar to Bitcoin transactions. Transactions between z-addresses are shielded, simliar to Zerocash transactions. It is also possible to send coins from a t-address to a z-address or from a z-address to t-address. In both cases, only the value is revealed on the t-address side.

Zcash has three shielded protocols named **Sprout**, **Sapling**, and **Orchard**. Sprout was the original protocol implemented in the launch of Zcash. Sapling, introduced in 2018, addressed the limitations of the Sprout protocol and offers faster transaction generation and verification. Orchard, the latest protocol upgrade activated in 2022, further enhances privacy and scalability. Orchard utilizes a new zk-SNARK called Halo2↗, which eliminates the need for a trusted setup. This is an improvement over BCTV14↗ in Sprout and Groth16↗ in Sapling. Currently, Sapling is the most widely used shielded protocol.

The amount of ZEC stored in each of Zcash’s three shielded pools since 2016. Images from ELECTRIC COIN CO.↗ |

For more information on the protocol’s structure, please refer to the Zcash Protocol Specification↗.

## Attacks on Zcash

There are cases where cryptographic vulnerabilities in the Zcash protocol have been discovered and the protocol has been modified. We will discuss Faerie Gold attack and Internal hash collision attack, which are also mentioned in the protocol specification. Note that these vulnerabilities are already addressed by developers.

### Faerie Gold Attack

In the Zerocash protocol, the coin serial number is determined by $sn = \text{PRF}_{a_{sk}}(\rho)$ with arbitrary chosen $\rho$. If Alice needs send two coins $c_1$ and $c_2$ to Bob but wants to do something maliciously, then she may choose the same $\rho$ for $c_1$ and $c_2$. Then serial numbers $sn_1$ and $sn_2$ are collided. Although both coins are valid, Bob can only use one of them because the serial number in the transaction is checked to see if it has appeared before.

To address this issue, Zcash ensures the uniqueness of $\rho$ by changing the specification. Instead of randomly choosing $\rho$, it is derived from a PRF. The inputs of the PRF are a hash digest $h_{\text{Sig}}$, which differ in each transaction, and the output order index $i$. If $c_1$ and $c_2$ are generated in different transactions, $h_{\text{Sig}}$ values will differ, and if they are generated in the same transaction, the output order index $i$ will differ. Therefore $\rho$ will never collide.

### Internal Hash Collision Attack

Recall the minting process with a new coin $c$ with value $v$ in Zerocash:

- Alice samples $\rho$ and then the coin’s serial number $sn := \text{PRF}_{a_{sk}}(\rho)$.
- Alice computes $k := \text{com}_r(a_{pk} || \rho)$ for a random $r$, then computes $cm := \text{com}_s(v || k)$ for a random $s$.
- A coin $c := (a_{pk}, v, \rho, r, s, cm)$ and a mint transaction $\text{tx}_{\text{Mint}} := (v, k, s, cm)$ are generated.

In the original Zcash design, commitment $cm$ is derived as follows:

- $InternalH = \text{SHA256}(a_{pk} || \rho) \text{ truncated to 128 bits}$
- $k = \text{SHA256}(r || InternalH)$
- $cm = \text{SHA256}(k || 0^{192} || v)$

Since $InternalH$ is only 128-bits, an attacker can find $(a_{pk}, \rho)$ and $(a_{pk}', \rho')$ pairs that have the same $InternalH$ with $O(2^{64})$ computations. Two coins derived from $(a_{pk}, \rho)$ and $(a_{pk}', \rho')$ *looks different*, as their serial numbers $\text{PRF}_{a_{sk}}(\rho)$ and $\text{PRF}_{a_{sk}'}(\rho')$ are different. Therefore an attacker can double-spend them by generating two different zk-SNARK proofs using $(a_{pk}, \rho)$ and $(a_{pk}', \rho')$.

To mitigate this, commitment is changed into $cm = \text{SHA256}(\text{0xB0} || a_{pk} || v || \rho || r)$. With this fix, Zcash commits $a_{pk}, v, \rho$ and $r$ all at once, whereas Zerocash uses a nested commitment scheme where $a_{pk}$ and $k$ are committed first, then $v$. The reason for the nested commitment scheme in Zerocash is to allow mint transactions to be publically verified without requiring zk-SNARK proofs. However, in Zcash, mint and pour transactions are combined and each transfer always uses a zk-SNARK proof. Therefore Zcash does not require the nesting.

## Conclusion

This post discusses Zerocash and its use in Zcash, a cryptocurrency. Zerocash is a protocol that enhances privacy and anonymity in cryptocurrencies, addressing security and privacy concerns. It uses advanced cryptographic techniques, such as zk-SNARKs, to prove transaction validity while anonymizing the sender, receiver, and transaction amount. This feature enables users to preserve the transparency and integrity of the blockchain while concealing the content of their transactions. Moreover, from a researcher’s perspective, it is interesting to investigate the protocol further to identify potential vulnerabilities, such as the Faerie Gold attack.

## About Us

Zellic specializes in securing emerging technologies. Our security researchers have uncovered vulnerabilities in the most valuable targets, from Fortune 500s to DeFi giants.

Developers, founders, and investors trust our security assessments to ship quickly, confidently, and without critical vulnerabilities. With our background in real-world offensive security research, we find what others miss.

Contact us↗ for an audit that’s better than the rest. Real audits, not rubber stamps.