The lack of privacy on Ethereum is a well known problem. Any data published on Ethereum is visible to everyone, implying the lack of confidentiality. The address that published this data is also visible. The address by itself does not reveal identity unless this can be linked back to the owner, implying only pseudo-anonymity.

As a result, despite its promise of immutability and decentralisation, Ethereum is still not a viable platform for enterprise applications due to lack of privacy. EY, driven by the vision of its Global Blockchain Head Paul Brody, believes that enterprises can achieve previously unattainable network effects by building applications on public blockchains but lack of privacy makes that impractical. Solving this was the motivation for the creation of Nightfall.

What is Nightfall?

Nightfall enables transfer of fungible and non-fungible tokens between parties such that the value/token id of the token remains confidential and the recipient remains anonymous. It uses zk-SNARKS. I won’t go into the details of how zk-SNARKs work. For anyone interested to know more about zk-SNARKs, this and this are a good place to start.

Nightfall currently uses ERC-20 token standard for fungible tokens and ERC-721 token standard for non-fungible tokens.

High Level Architecture diagram of Nightfall core

At its core, Nightfall can be subdivided in to 6 sub-protocols

Mint ERC-20 Token Commitment Transfer ERC-20 Token Commitment Burn ERC-20 Token Commitment Mint ERC-721 Token Commitment Transfer ERC-721 Token Commitment Burn ERC-721 Token Commitment

Mint an ERC-20 or ERC-721 Token Commitment:

Mint converts a publicly visible ERC-20 (fungible) or ERC-721 (non-fungible) token into a token commitment that holds similar value or token id as that of the token and the public key of the intended commitment owner. A commitment is a cryptographic primitive that binds the value held within while also hiding it. Confidentiality of value and recipient is attained in this manner.

How to mint?

When Alice wants to mint a token commitment of a public ERC-20 or ERC-721 token that she owns, she generates a zk-SNARK proof π on her offchain server using public inputs and private inputs (witness). She then submits it to the shield contract, a π that says,

she created a commitment Z_A by hashing value/token id of token α , public key of intended commitment owner pk_A and salt σ (to provide commitment uniqueness),

Inputs for proof generation (done off chain) : * Witness (secret inputs) - pk_A, σ

* Public inputs - α, Z_A

The shield contract on receipt of the mint transaction,

verifies the proof by calling verify function in the verifier contract along with the public inputs and only on successful verification,

calls transfer function of ERC-20 or ERC-721 contract to transfer token value/token id α from Alice to the address of the shield contract, and adds the commitment Z_A to its commitments merkle tree

Inputs for proof verification (done on chain) : * Public inputs - α, Z_A

What everybody on the blockchain sees?

That Alice minted a token commitment Z_A holding a value/token id α.

Transfer an ERC-20 or ERC-721 Token Commitment:

Transfer enables the transfer of a token commitment between two parties by nullifying the initial commitment(s) and creating new commitment(s) that holds the same token value(s)/token id(s) as well as the public key of the new intended commitment owner(s).

First, I will explain ERC-20 transfer as it is relatively more complex. ERC-721 is straight forward to understand after.

How to transfer an ERC-20 Commitment?

Alice currently owns two commitments Z’_A and Z’’_A that hold 10α and 5α respectively and she wants to transfer 12α to Bob. She generates a zk-SNARK proof π on her off-chain server and submits it to the shield contract from a one off Ethereum address, a π that says,

she knows the secret inputs of two token commitments Z’_A and Z’’_A by concatenating and hashing the values 10α and 5α , her public key pk_A , and salt σ_1 and σ_2 associated with the commitments, such that

2. that these token commitments exist in the commitments merkle tree. Showing where in the commitments merkle tree these commitments are present reveals which commitments are being used. Instead she proves that starting with each of the Z’_A and Z’’_A she can hash repeatedly (depth of tree -1 times) to produce the root of the commitments merkle tree, R . She does this by repetitively concatenating with the sibling node at each level and hashing.

3. that she created nullifiers for Z’_A and Z’’_A using her secret key sk_A and corresponding commitment salts σ_1 and σ_2 , such that

4. that the public key pk_A used in token commitments Z’_A and Z’’_A is derived from the same secret key sk_A used in their respective nullifiers, such that

5. that she created two new token commitments Z’’’_A and Z_B for her and Bob using their public keys pk_A and pk_B respectively, such that

6. that values of commitments nullified is equal to the values of new commitments created, 10α + 5α = 3α + 12α

7. that the most significant bits of each of 10α, 5α, 3α, 12α are all zero. This prevents the output values 3α and 12α from exceeding the maximum bit-lengths accepted by the circuit (overflow check).

Inputs for proof generation (done off chain) : * Witness (secret inputs) - 10α, 5α, 3α, 12α, pk_A, pk_B, σ_1, σ_2, σ_3, σ_4, sk_A, Z’_A, Z’’_A, path from commitment to root of the commitments merkle tree

* Public inputs - N_10α, N_5α, Z’’’_A, Z_B, R

The shield contract on receipt of the transfer transaction,

verifies the proof by calling verify function in the verifier contract along with the public inputs and only on successful verification,

adds N_10α and N_5α to its nullifiers list adds Z’’’_A and Z_B to its commitments merkle tree

Inputs for proof verification (done on chain) : * Public inputs - N_10α, N_5α, Z’’’_A, Z_B, R

Bob is informed of Z_B , 12α , and σ_4 through off chain communication.

What everybody on the blockchain sees?

that a new Etheruem address (that could probably be traced to Alice) has nullified two amongst many token commitments that it either minted or received. They can also see that it created two new token commitments Z’’’_A and Z_B . But they can’t see who owns these new token commitments or which two token commitments were spent. Also, the values spent and transferred are not revealed.

How to transfer an ERC-721 Commitment ?

When Alice wants to transfer α held in token commitment Z_A to Bob, she generates a zk-SNARK proof π on her offchain server and submits it to the shield contract from a one off Ethereum address, a π that says,

she knows the inputs of a token commitment Z_A by concatenating and hashing the token id α , her public key pk_A , and salt σ associated with the commitment, such that

2. that this token commitment exists in the commitments merkle tree, by showing that starting with Z_A she can hash repeatedly (depth of tree -1 times) to produce the root of the commitments merkle tree, R . She does this by repetitively concatenating with the sibling node at each level and hashing.

3. that she created a nullifier for Z_A using her secret key sk_A and corresponding commitment salt σ , such that

4. that the public key pk_A used in token commitment Z_A is derived from the same secret key sk_A used in its nullifier, such that

5. that she created a new token commitment Z_B for Bob with the same token id α , Bob;s public key pk_B and salt σ’ , such that

Inputs for proof generation (done off chain) : * Witness (secret inputs) - α, pk_A, pk_B, σ, σ’, sk_A, Z_A, path from commitment to root of the commitments merkle tree

* Public inputs - N_α, Z_B, R

The shield contract on receipt of the transfer transaction,

verifies the proof by calling verify function in the verifier contract along with the public inputs and only on successful verification,

adds N_α to its nullifiers list adds Z_B to its commitments merkle tree

Inputs for proof verification (done on chain) : * Public inputs - N_α, Z_B, R

Bob is informed of Z_B , α , and σ’ through off chain communication.

What everybody on the blockchain sees?

that a new Etheruem address (that could probably be traced to Alice) has nullified a token commitment that it either minted or received. They can also see that it created a new token commitment Z_B . But they can’t see who owns this new token commitment or which token commitment was spent. Also, the token id transferred is not revealed.

A one off Ethereum address is used to send transfer transactions to the shield contract. It provides only pseudo-anonymity for the sender of the transaction because the sender will need to pay for the transaction costs and it is likely that this gas could have been received from another of sender’s known address and can be traced to it.

Burn an ERC-20 or ERC-721 Token Commitment:

Burn converts an ERC-20 or ERC-721 token commitment into a publicly visible ERC-20 or ERC-721 token that holds similar value/token id respectively. This does the opposite in function to what a mint does.

How to burn?

When Alice wants to burn a token commitment Z_A that she owns, she generates a zk-SNARK proof π on her offchain server and submits it to the shield contract, a π that says,

she knows the inputs of a token commitment Z_A by concatenating and hashing the token id α , her public key pk_A , and salt σ associated with the commitment, such that

2. that this token commitment exists in the commitments merkle tree, by showing that starting with Z_A she can hash repeatedly (depth of tree -1 times) to produce the root of the commitments merkle tree, R . She does this by repetitively concatenating with the sibling node at each level and hashing.

3. that she created a nullifier for Z_A using her secret key sk_A , such that

4. that the public key pk_A used in token commitment Z_A is derived from the same secret key sk_A used in its nullifier, such that

5. that the address payTo_address Alice wants to send the public ERC-20 or ERC-721 token to is equal to the private address payTo_address_private , such that, payTo_address == payTo_address_private . This check ensures that no miners/intermediaries, cannot replace the payTo_address address with their own address



Inputs for proof generation (done off chain) : * Witness (secret inputs) - pk_A, σ, sk_A, Z_A, path from Z_A to root of merkle tree to hash to R, payTo_address_private

* Public inputs - α, N_α, R, payTo_address

The shield contract on receipt of the burn transaction,

verifies the proof by calling verify function in the verifier contract along with the public inputs and only on successful verification,

adds N_α to its nullifiers list calls transfer function of ERC-20 or ERC-721 contract, to transfer a value/token id of α from address of the shield contract to the intended target address specified by Alice (it could be Alice’s or somebody else’s),

Inputs for proof verification (done on chain) : * Public inputs - α, N_α, R

What everybody on the blockchain sees?

that a new Etheruem address (that could probably be traced to Alice) has nullified a token commitment that it either minted or received. They can also see that a value/token id of α has been received by Alice/somebody else. But they can’t see which token commitment was spent.

Conclusion

Nightfall has been put in the public domain here on 31 May 2019 and its whitepaper can be found here.

It is an experimental solution and still being actively developed. This is not intended to be a production-ready application and do not recommend anyone to use it as such. The project hopes that people will feel motivated to contribute their own ideas and improvements.

Also, in the future, aggregation of proofs is being considered to bring down costs of verification.

Great to be part of Team Nightfall, with thanks to Paul Brody, Michael Connor, Duncan Westland, Quentin Drouot

Further Reading (if interested in knowing more about ZKP)