Skip to content

Transactional privacy with Zeth

The decentralised nature of blockchain systems — like Autonity Ledger Systems — and the need for system validators to access transaction details in order to keep the system secure (i.e. reach consensus over a valid state), raise the following question: is it possible to hide the details of transactions processed by the distributed ledger, while preserving its initial security guarantees?

Building on years of research, the Zeth protocol answers this question in the affirmative by proposing a variant of Zerocash that works on top of smart contract enabled blockchains such as Ethereum and Autonity.

This user guide documents the research-quality reference implementation of the protocol and provides all the necessary steps to carry out privacy-preserving transactions on Autonity.

Warning

We gently remind the reader that this user guide documents a research-quality piece of code. As such it is not yet suitable to be used in production environments!

Protocol overview:

The Zeth protocol uses efficient non-interactive zero-knowledge proof systems called zk-SNARKs in order to generate cryptographic proofs — for each transaction — showing that transactions are compliant with the systems' rules (i.e. no transaction is double-spending funds, no value is created etc.) without exposing the transaction details.


While Zeth is extensively documented (see References), we briefly describe the main ideas behind the protocol, and invite the interested reader to consult the referenced documents for further details on the protocol.


Zeth implements a private payment mechanism by running a mixing smart contract on which members of the Autonity network, wanting to transact privately, deposit their funds in the form of notes. Being structured pieces of data (with an owner, value etc.), Zeth notes can then be spent by their owners in order to create new ones (for potentially different users). This ability to spend notes to create new ones — similarly to UTXO-based blockchains — allows users to transact with each other. While no restriction is set on the value of notes (e.g. we can have notes of value 0.00001 and/or 9990000999 if users want to), the current protocol parameters require that users spend at most two input notes to create at most two output notes in every single Zeth transaction. Moreover, it is necessary for the transaction to be balanced (i.e. the sum of the input values needs to be equal to the sum of the output values) to make sure that no value is created by that transaction. In brief, "note ownership" here is defined by knowledge of a "secret key" associated to a "public key". Anyone wanting to spend their notes needs to use their "secret key" to do so, while transferring funds requires to use the recipient's "public key" to derive the new notes. (In the following this "public key" is referred to as the Zeth payment address, while the "secret key" will be called the Zeth private address.)

All these checks — along with many more — are cryptographically enforced by requiring users to generate a zk-SNARK for each of their transactions. This zero-knowledge proof of computational integrity is then sent on-chain, certifying that the transfer carried out satisfies all the security checks required for the protocol to be secure.

Once notes are spent, and new ones are created, the details of the newly created notes (i.e. their value etc.) are encrypted with the recipient's public key (in a way that protects his anonymity against other network members) and sent to the chain along with the zero-knowledge proof. Upon receipt of the zero-knowledge proof and encrypted data, the Zeth smart contract verifies the proof (to make sure that the transfer is compliant with the system's security rules — and rejects the transfer otherwise), and broadcasts the encrypted data if the proof is deemed valid. Broadcasting the encrypted data allows users to "scan" the blockchain and try to decrypt the data broadcast by the smart contract. In this way, users can discover any transfers for which they are the recipient. This mechanism protects the recipient's anonymity.

All in all, Zeth can be considered as a "privacy preserving ledger on top of Autonity" allowing to carry out payments. As such, not only Zeth transactions hide the amount transferred and the recipient, but using Zeth also hides one's overall wealth on the blockchain. As the total value of unspent Zeth notes can only be retrieved by the notes' owner (other network participants do no know if/when a Zeth user has received Zeth notes), only partial information about users' balances is exposed via the Autonity account balance which is publicly accessible via the blockchain state.

Software overview:

The image below represents the various software components involved in Zeth, as well as a high level run-though of the protocol.

flow

In short, Zeth relies on three pieces of software:

  1. The zeth client: Acts as a wallet that manages the Zeth secrets of the user (Zeth private address, transfer details etc.). This component interacts with both the blockchain and the prover_server, and provides a Command Line Interface (CLI) to receive and process user inputs.
  2. The prover_server: This piece of software receives the (private) details of a transfer from the wallet, and generates the associated zero-knowledge proof and cryptographic commitments. The received zero-knowledge proof and commitments are then sent on-chain, along with the encrypted payment details, to be processed by the Zeth contracts.
  3. The Zeth contracts: Set of smart contracts deployed on the blockchain system that are used to:
    • verify the zero-knowledge proof to assess the validity of the transfer (without knowing the underlying secret data)
    • keep track of the cryptographic commitments
    • communicate received payments to the recipient(s) via an encrypted broadcast emulating a confidential receiver-anonymous channel

Importantly, both the client zeth and prover_server are run on the user machine. The smart contracts are deployed to and executed on the blockchain.


In the following you will find all the instructions to run the Zeth software stack and execute privacy-preserving asset transfers on the Autonity Bakerloo testnet.

Info

In order to make this tutorial easier to follow, we make use of the zeth_helper CLI tool, which performs some useful operations on an Ethereum-like system like Autonity. We will use it throughout this documentation to make sure your environment is properly configured. Importantly however, this tool is only here to provide additional help, it is not part of the core Zeth stack, and it is not suitable to be used in production environments. As this tool is only here to help you through this documentation, feel free to ignore the zeth_helper commands if you already are an Autonity veteran!

Assets supported:

Zeth can be used to transact with a wide class of digital assets. In fact, Zeth can be used to either:

  1. Transact Autons: Zeth can be used to transfer AUT on Autonity.
  2. Transact smart contract defined tokens: Zeth can be used to transfer any ERC20/ERC223 tokens.

Roadmap

ERC777 token transfers are not yet supported, but enabling their support is part of our Roadmap. Supporting this token standard will further widen the domain of applications of Zeth

Prerequisites

System requirements

This user guide primarily targets x86_64 Linux and macOS platforms. While we use docker extensively in the following sections, some instructions may not be supported on different systems. As such, we recommend using virtualization techniques (VMs etc.) to emulate a compliant system architecture if needed

Before proceeding, please note that the following tools are used throughout this documentation. Users should ensure that they are correctly installed at the given versions:

Once the system complies with all the requirements above, fetch the repository by:

  • Using git to fetch the source code from Github
    git clone git@github.com:clearmatics/zeth.git
    cd zeth
    
    or
  • Using wget to fetch the last release of the software
    # See releases: https://github.com/clearmatics/zeth/releases
    wget https://github.com/clearmatics/zeth/archive/v0.5.tar.gz
    # Verify the checksum
    shasum v0.5.tar.gz
    # Must return: a95ad017eddf8c578cf37e73892647f2cb1a0d63  v0.5.tar.gz
    # Decompress the received archive to a directory named "zeth"
    mkdir zeth && tar xf v0.5.tar.gz -C zeth --strip-components 1
    

Once in the Zeth repository, set up your environment by running:

. ./setup_env.sh

Configure and start the prover server

Below, we provide the commands to start the prover_server in a docker container. The interested reader may want to read the project's README to find out more about how to run this piece of software natively on a x86_64 Linux or macOS system.


For the zero-knowledge proofs to be generated and verified in Zeth, it is necessary to generate a keypair. This keypair must be generated in a secure way to make sure that the system remains secure. While we have implemented a multi-party computation protocol to generate the Zeth keypair securely, we include below a keypair that we will use throughout this documentation.

To download the keypair, click here and here. Then save the files in the zeth repository you fetched at the previous step, and run the following commands:

cd $ZETH && ./scripts/recover_keypair

Here $ZETH is the environment variable that contains the path to the zeth repository on your machine (it is set by the setup_env.sh script above). The recover_keypair script combines the keypair files, decompress the keypair into the $ZETH/zeth_setup directory and deletes the fetched archives. Make sure that the keypair hash digest returned by the script is: 80be1a34bd4e7c03adb7de8e5e9d3a5ee81b062e zeth_setup/keypair.bin

Warning

While we have generated this keypair honestly, please do not use it in production systems. As we do not want you to trust us, we have implemented a Multi-Party Computation (MPC) protocol to securely (and collectively) generate a keypair that can be used for real-life scenarios


We assume that all commands below are run in the zeth repository (the path in the $ZETH variable).

  1. Zeth-prover image (built from Dockerfile-prover):

    docker pull clearmatics/zeth-prover:0.5
    # Verify the checksum
    docker images clearmatics/zeth-prover:0.5 --digests --no-trunc
    # The returned image ID must be: sha256:d911e3f4cd57c3327cbca9584d39ec4b9bbe0eb0d1b6f49cfce1c1e582351c7d
    

  2. Start the Zeth prover and use the keypair fetched at previous steps:

    docker run --rm -ti -p 50051:50051 \
        -v "$(pwd)"/zeth_setup:/root/zeth_setup/ \
        --name prover_server clearmatics/zeth-prover:0.5 \
        prover_server
    

Info

If you wish to start the container in "daemon" mode (i.e. start the process in the background), add the --detach flag to the command above. You can then see the running containers by running docker ps

Using the Zeth client

Warning

As research-grade software, the Zeth wallet should not be relied upon to manage secret data in a secure way. Do not use it with any data that secures real assets (such as real-life Autonity secret keys)

The Zeth client is a set of simple tools, accessible via a single zeth command, providing deployment and very basic wallet functionalities. It must be executed in a directory containing configuration data for the network (i.e. prover_server RPC endpoint, Autonity node RPC endpoint, Zeth mixer contract address, etc) as well as public and private data for the current Zeth user (Autonity and Zeth address details, Zeth notes, etc).

The commands below contain instructions to setup client directories. It is possible to create multiple virtual users on a single machine by setting up multiple client directories. This is an effective way of experimenting with the system, to send and receive funds. The client directories are self-contained so that the zeth command runs in the context of the current Zeth user and is separated from any other virtual users on the system.

Setup the client and install dependencies

Enter the client directory in the Zeth repository, and setup the project:

cd client
python3 -m venv env
source env/bin/activate
make setup

Activating the virtualenv

While the virtualenv is active (indicated by the (env) prompt prefix), the zeth command will be available on the command line. To enter the virtualenv from a new shell, use source $ZETH/client/env/bin/activate

After running these commands, all the wallet's functionalities are available as subcommands in the zeth CLI. Several example commands are provided below. For more information and a full list of commands and available options, please run zeth --help.

Autonity accounts

The following commands will assume that the user has control of a funded Autonity account. We recommend that users run the script zeth_helper eth-gen-address to generate a new Cryptographic IDentity (CID) for each Zeth user, (stored unencrypted in the client directory as eth-private-key and eth-address). This new address can then be funded from a pre-existing Autonity account and used for experimenting with Zeth, without reference to the original Autonity account. Contact the Clearmatics team to receive funds to an Autonity account

Deploy Zeth to the testnet

Deployers only

This section is only relevant for Zeth contract deployers. If you want to use an existing Zeth instance jump here

  1. Create the deployer's client directory, create the client's keys, and initialise it for the Autonity Bakerloo testnet:

    mkdir deployer
    cd deployer
    zeth_helper eth-gen-address
    

  2. Create a file eth-network in the deployer directory, by running the following command, and making sure to replace <rpc-endpoint> by the RPC endpoint of the node (on the Autonity Bakerloo testnet) you would like to connect to:

    zeth_helper eth-gen-network-config autonity-bakerloo \
        --eth-rpc-endpoint <rpc-endpoint>
    

The command will create the following eth-network file:

{
  "name": "autonity-bakerloo",
  "endpoint": "<rpc-endpoint>"
}

Network configuration and address files

As described above, zeth_helper eth-gen-address creates files eth-private-key and eth-address holding the Autonity private key and address for the client. The network configuration file eth-network holds information about the Autonity network and how to connect to it (additional network configuration options can be used to pass custom TLS certificates for instance. For more details, please run zeth_helper eth-gen-network-config --help). Further invocations of the client use this file to query the network and submit transactions

  1. Fund the deployer's Autonity address (contained in the file eth-address).

  2. Compile and deploy the contracts:

    zeth deploy
    

Using Zeth with an ERC token

To use Zeth for ERC tokens transfers, please make sure that the ERC token contract you would like to use is already deployed on Autonity, find its address, and add the following flag to the deploy command above --token-address <your-token-address>. For further details, see the usage zeth deploy --help

  1. Make the instance file (zeth-instance) public, or share it with future users of the contract. This file contains the ABI of the contract, as well as its address on the Autonity network and will be used to configure the wallet of the contract's users.

Zeth operations

In the following, we list the various functionalities of the zeth wallet, and finish each sub-section with a simplified example. All examples are linked together and provide a more illustrative approach to the Zeth protocol. To that end, we will use the now-famous Alice, Bob and Charlie trio to illustrate the zeth wallet functionalities.

Info

Run zeth --help to see all the functionalities of the zeth CLI

Create a new Zeth user

New Zeth users only

This section is only relevant to new Zeth users. If you already have a Zeth address, then jump to the next step

  1. Create a user directory to contain the client configuration and state:

    mkdir <your-name>
    cd <your-name>
    

  2. Initialise the client to point to the correct network and Zeth mixer instance:

    • Create the eth-network file, as for the deployer, by running the following command and replace <rpc-endpoint> by the RPC endpoint of the node (on the Autonity Bakerloo testnet) you would like to connect to:
      zeth_helper eth-gen-network-config autonity-bakerloo \
          --eth-rpc-endpoint <rpc-endpoint>
      
    • Copy zeth-instance shared by the contract deployer:
      cp <path-to-zeth-instance-file> .
      

Note: The Bakerloo testnet comes with a set of pre-deployed Zeth smart contracts. If you wish to interact with these, please download the testnet zeth-instance file, by running:

wget https://bakerloo.autonity.network/protocol-suite/assets/zeth-instance
# Verify the checksum
shasum zeth-instance
# Must return: a9df6a68b231ee93bc0dfc80eb95949ad4ae8097 zeth-instance
or by clicking here and moving the downloaded file into the Zeth user directory created at the previous step (see: Step 1).

What is the instance file?

The instance file contains the ABI of the contract as well as its address on the Autonity network. It will be used by your Zeth wallet to point to the right contract on the network when carrying out Zeth transactions

  1. Generate an Autonity address for the user:
    zeth_helper eth-gen-address
    

and fund the address contained in the file eth-address.

  1. Generate a Zeth address:
    zeth gen-address
    

What is a Zeth address?

Your Zeth address will be written unencrypted in two files: zeth-address.priv and zeth-address.pub. The pub file is intended to be shared publicly, or sent to people who may be willing to transact with you. In short, this address is the "payment address" containing two public keys that other users will use to send you funds privately. The priv file, however, represents the "private address" and must be kept secret. It contains two secret keys that will enable you to know when you receive funds from someone, and manipulate them. For more information on the Zeth address, please refer to Section 1.4 of the Zeth protocol specifications

  1. Share your Zeth "payment address" (zeth-address.pub) with parties willing to transact with you, and/or receive Zeth payment addresses from other parties you wish to transact with.

Example - Chapter 1

Charlie is a tech-savvy person, always trying new things and eager to impress his friends Alice and Bob. His new endeavor is to initiate his friends to Zeth in order to show them how to do privacy-preserving state transitions on Autonity. To that end, Charlie has deployed the Zeth contracts on the Autonity Bakerloo testnet, and has published the zeth-instance on his personal webpage (at https://charlie-tech-personal.mock/zeth-instance). Along with this file, Charlie listed a few testnet miner nodes IPs — one of which being 172.16.212.67, exposing an Autonity RPC endpoint on port 8545

After being convinced by her friend Charlie, Alice decides to give Zeth a try. To that end, she types the following commands in her terminal:

Alice

(env) $ mkdir alice && cd alice
(env) $ zeth_helper eth-gen-network-config autonity-bakerloo --eth-rpc-endpoint 172.16.212.67:8545
(env) $ wget https://charlie-tech-personal.mock/zeth-instance
(env) $ zeth_helper eth-gen-address
(env) $ zeth gen-address

Finally, she publishes her payment address on her charity website (at https://alice-charity.mock/zeth-address.pub) and writes a message inviting donations to be made using her Zeth payment address

After some discussions, Charlie also convinced Bob to use Zeth with him and Alice. Bob followed this tutorial using Charlie's help, but decided to connect to his testnet node — 172.16.217.125 — instead

The zeth mix command

Once the client has been set up as described above, Zeth transactions can be issued with the zeth mix command. This is a generic command that supports any combination of:

  • deposits of public funds (i.e. deposit Autons from your Autonity account or ERC20/223 tokens) onto the Zeth mixer contract, creating new Zeth notes (potentially created for other users)

  • transfers of funds via "pouring" the value of sender-owned Zeth notes to newly created Zeth notes (potentially owned by other users)

  • withdrawal of funds from Zeth (i.e. recover Autons or ERC20/223 tokens)

The examples below illustrate some simple operations. For further information, see the output of zeth mix --help, and the references given at the end of this section.

Note that the zeth mix command requires a running prover_server in order to generate the zero-knowledge proofs that are included in the blockchain transactions. The proof generation will take several seconds, after which the Zeth transaction will be sent to the blockchain.

Depositing funds

Depositing funds on the mixer contract is done by passing the amount to deposit to the --vin flag of the zeth mix command:

zeth mix --out zeth-address.pub,2 --vin 2 --wait
where zeth-address.pub is the the user's Zeth payment address.

The type of asset manipulated depends on the asset used during the deployment of the Zeth contract. Depositing Autons on the Zeth contract will only work if your Autonity balance is higher than the amount deposited. Depositing ERC20/223 tokens, however, will also require that the Zeth contract is allowed to transfer at least the amount of tokens you deposit. If you don't know how to set the contract's allowance, you can use zeth_helper token-approve (use --help to see the usage).

Specifying Zeth public addresses

Here the value zeth-address.pub,2 of the --out flag is the filename of the user's public key who will own the new note of value 2. The address data itself (i.e. the human-readable content of zeth-address.pub) can also be specified here. This allows Zeth addresses to be exchanged as plain text and pasted into files or onto the command line directly. If the target Zeth payment address is omitted (for example, --out 2), it defaults to the sender's own public address (written in the file zeth-address.pub in the current directory)

Asynchronous operations

The --wait flag above will cause the client to wait for the transfer transaction to be mined on the Autonity network. Where the --wait flag is omitted, the sync command, described below can be used to obtain the latest state of the network and reflect this in the client's local data

Example - Chapter 2

After creating his Zeth address and initialising his wallet (as in the previous steps of this tutorial), Bob decides to convert most of his Autonity account balance of AUT into Zeth notes. Having 20 AUT in his balance, he decides to deposit 19 AUT on the Zeth contract, and keep 1 AUT in order to pay the gas cost of the various smart contracts he will call from this account in the future. Additionally, Bob wants to split his 19 AUT into two notes of respective value 2 AUT and 17 AUT

As such, Bob runs the following command:

Bob

(env) $ zeth mix \
            --out zeth-address.pub,2  \
            --out zeth-address.pub,17 \
            --vin 19 --wait

Listing unspent Zeth notes

The set of (unspent) notes owned by the current user can be listed as follows:

zeth ls-notes
# b1a2feaf: value=2, addr=0

This command will refer to the notes by using their truncated commitment values (b1a2feaf here).

Example - Chapter 3

Some time after depositing his funds, Bob sees the two Zeth notes he created.

Bob

(env) $ zeth ls-notes
bf940eaf: value=2, addr=0
feaeacb9: value=17, addr=1

Transferring assets between Zeth users

In order to spend notes to transfer funds, users need to spend one or two of their Zeth notes in order to create new notes (one — or both — being owned by the recipient). This can done by using the --in and --out flags of the zeth mix command. These flags respectively take the input notes to spend, and the recipients and values of the output notes to create:

zeth mix \
    --in b1a2feaf \
    --out <peer-payment-address>,0.5 \
    --out <my-payment-address>,1.5 \
    --wait

Here, <peer-payment-address> is the Zeth payment address of the recipient, while <my-payment-address> is the sender's Zeth payment address (in zeth-address.pub).

Spending Zeth Notes

Zeth notes must be fully consumed when they are spent. In the example command above, the note b1a2feaf (with value 2) is split, with 0.5 being sent to the peer as a new Zeth note, and 1.5 returned to the user (again, as new Zeth note). When the transaction is successfully added to the blockchain, the Zeth note b1a2feaf will be locally marked as "spent" and can no longer be used (trying to double-spend this note will violate some cryptographic checks on the Zeth smart contracts leading to a rejection of the transaction)

Example - Chapter 4

Eager to surprise and encourage his friend Alice (who is trying to raise funds for her charity), Bob decides to donate 7 AUT to her. Fortunately, he fetched her payment address some days ago and added it to his local recipient list (via wget -c https://alice-charity.mock/zeth-address.pub --output-document recipients/alice-payment-address.pub). To donate, Bob "splits" his Zeth note feaeacb9 of "face-value" 17 AUT, into one note of value 7 AUT for his friend Alice, and takes the change back to himself via a new note of value 10 AUT:

Bob

(env) $ zeth mix \
            --in feaeacb9 \
            --out recipients/alice-payment-address.pub,7 \
            --out zeth-address.pub,10 \

Bob is very happy, he just gave his friend 7 AUT. Alice has no clue she just received a donation, but she is about to figure this out!

Synchronising with the network

Clients must periodically "sync" with the network. During this process, the wallet tries to decrypt the encrypted data broadcast by the Zeth contracts in order to determine if the user received new payments. In the event of a successful decryption, the wallet updates its local state in order to keep track of the newly received funds. This is performed with the zeth sync command.

In the case of the recipient above, likely output from the zeth sync command will be of the form:

zeth sync
# SYNCHING blocks (24 - 38)
#  NEW NOTE: 21aafa0f: value=0.5E, addr=1
# SYNCED to 38

See the output of zeth sync --help for further details.

Example - Chapter 5

Having been away for some time, Alice decides to check if she received any donations at all over the past days. To check whether she has received any funds via Zeth, she syncs her wallet with the chain by running zeth sync. Her wallet scans the chain, tries to decrypt the encrypted messages broadcast by the Zeth contracts, and after some time... Surprise! She received a donation of 7 AUT from someone! In fact, she now has a Zeth note of 7 AUT, as shown by her wallet:

Alice

(env) $ zeth ls-notes
a7a73eff: value=7, addr=2

Alice is now more motivated than ever to continue her charity project, and hopes to receive more donations in the future.

Withdrawing funds

While it is encouraged to keep transferring funds via Zeth and not withdrawing (to improve one's transactional privacy — and minimise information leakages), it is often necessary to withdraw funds from Zeth. In order to do that, users need to spend an input note in order to recover funds publicly. This can be done by using the --vout flag of the zeth mix command — that can be used in combination with all other flags presented above, e.g:

zeth mix --in 21aafa0f \
       --out <my-payment-address>,0.25 \
       --vout 0.25

Example - Chapter 6: The end

After hearing about Zeth, more people decided to join the network and use the protocol. Among them, Eve, a computer retailer. That's perfect, Alice needs to invest in her project and was about to buy one! She can use the Zeth note she received and buy a computer (6 AUT worth) from Eve, and withdraw the rest of her funds in AUT to convert them to fiat and buy a desktop in the nearest shop:

Alice

(env) $ zeth mix \
            --in a7a73eff \
            --out eve-payment-address.pub,6 \
            --vout 1

She is now fully equipped and ready to start to work!

The end.

Advanced commands

In the section above, all commands have been introduced one after the other in order to illustrate the various functionalities of the zeth wallet. Nevertheless, all the flags of the zeth mix command can be used together to create more complex payment flows. While providing a comprehensive list of such payments is outside of the scope of this documentation, we list a few such commands below as examples, and advise the reader to experiment further with the protocol.

Multiple transfers from public funds

# Charlie can pay both Alice and Bob simultaneously from public funds
zeth mix \
    --vin 10 \
    --out alice-payment-address.pub,0.33 \
    --out bob-payment-address.pub,9.67 \

Multiple transfers from Zeth notes

# Charlie can pay both Alice and Bob simultaneously from owned notes
zeth mix \
    --in aef79ef2 \
    --in fff7bef7 \
    --out alice-payment-address.pub,0.33 \
    --out bob-payment-address.pub,9.67 \

"Top up" transfer

# zeth ls-notes
# aef79ef2: value=2.783, addr=9

# Charlie can "top up" his Zeth notes from public funds in a transfer
zeth mix \
    --vin 0.217 \
    --in aef79ef2 \
    --out alice-payment-address.pub,3 \

and many more...

References

For further details and information about the Zeth protocol, please consult:

Finally, additional materials on Clearmatics Research can be found on the Clearmatics Research website.


Help

If you need help, you can: