# What is EZKL?

# EZKL makes zero-knowledge easier

ezkl takes a high-level description of your program and sets up a zero-knowledge prover and verifier. Our focus is on programs that are expressed as pytorch AI/ML models and other computational graphs. After setup, the prover can prove statements such as the following.

"I ran this publicly available neural network on some private data and it produced this output"

"I ran my private neural network on some public data and it produced this output"

"I correctly ran this publicly available neural network on some public data and it produced this output"

These proofs can be trusted by anyone with a copy of the verifier, and verified directly on Ethereum and compatible chains. ezkl can be used directly from Python; see this colab notebook and the python bindings docs. It can also be used from the command line.

ezkl can prove an MNIST-sized inference in less than a second and under 180mb of memory and verify it on the Ethereum Virtual Machine (or on the command line, or in the browser using wasm).

You can install the Python version with pip install ezkl.

ezkl can be used to move large and complex computations off-chain in a way that is easy to program (you can write your own functions in Python) and manage. You are not limited to a pre-defined set of functions, there is no limit on input size (using hashing), and there is no centralized sequencer.

For more details on how to use ezkl, we invite you to explore the docs and check out the repo, especially the notebooks.


# Zero-knowledge proofs

A zero-knowledge proof is a programmable digital signature. In a traditional digital signature scheme, we have a secret key, a public message, and a signature algorithm that was set up in advance and everyone knows. These are applied to the inputs to produce a signature and a public message, which is then verified by a counterparty (such as a blockchain).

A zero-knowledge proof allows us to replace the secret key and public message with arbitrary private and public inputs, and it lets us run any program we like on them, not just a fixed signature algorithm.

Just as with a digital signature, there are three parties: one to define the setup, one to prove, and one to verify.

# The life cycle of a proof

There are three steps in the life of an ezkl proof: Setup, Prove, and Verify. Each step is generally performed by a different party.

# Setup

Setup is invoked with ezkl setup at the cli or ezkl.setup() in Python. It defines what constitutes a proof and how that proof will be verified, setting the rules of the game. Setup is performed by the application developer, who then deploys the resulting artifacts to production.

The inputs to setup are:

  • the model (as an onnx file)
  • the structured reference string which is a common, public piece of cryptographic data shared among proof setups of the same size
  • various flags, settings, and options for tuning and added functionality

The outputs of setup are:

  • the proving key
  • the verification key, and
  • the circuit settings: serialized flags, settings, and options, and a few numbers that describe the shape of the resulting circuit.

Before setup can run, the settings need to be generated with gen-settings and optionally calibrate-settings, and the model must be compiled.

ezkl.gen_settings(onnx_filename, settings_filename)
ezkl.calibrate_settings(
    input_filename, onnx_filename, settings_filename, "resources")
res = ezkl.compile_model(model_path, compiled_model_path, settings_path)
res = ezkl.setup(
        compiled_model_path,
        vk_path,
        pk_path,
        srs_path,
        settings_path,
    )

# Prove

Prove, invoked with ezkl prove at the cli or ezkl.prove() in Python, is called by the prover, often on the client. The prover is making a claim that it knows some inputs (which might include model parameters), such that when the model (chosen during setup) is run on them, produces certain outputs. The prove command computes a cryptographic proof of that claim, which can then be believed by any verifier.

The inputs to prove are:

  • the witness data for the claim: an (input, output) pair (x,y) such that model(input) = output (this pair can be produced from x using the gen-witness command)
  • the model (as a compiled model file, made from an onnx file)
  • the proving key
  • the structured reference string, and
  • the circuit settings.

The outputs of prove are:

  • the proof file.
res = ezkl.gen_witness(data_path, compiled_model_path, witness_path, settings_path = settings_path)

res = ezkl.prove(
        witness_path,
        compiled_model_path,
        pk_path,
        proof_path,
        srs_path,
        "evm",
        "single",
        settings_path,
    )

# Verify

ezkl can produce an EVM verifier contract which takes only the proof as input, and this is the normal use case.

res = ezkl.create_evm_verifier(
        vk_path,
        params_path,
        settings_filename,
        sol_code_path,
        abi_path,
    )

# assuming anvil is running
res = ezkl.deploy_evm(
    address_path,
    sol_code_path,
    'http://127.0.0.1:3030'
)

res = ezkl.verify_evm(
    proof_path,
    addr,
    "http://127.0.0.1:3030"
)

Verification can also be invoked with ezkl verify at the cli, ezkl.verify() in Python, or with WASM. It checks the correctness of the cryptographic proof produced by the prover.

The inputs to (non-EVM) verify are:

  • the proof file
  • the verification key
  • the circuit settings, and
  • the structured reference string

# Contributing 🌎

If you're interested in contributing and are unsure where to start, reach out to one of the maintainers on our Telegram group or our Discord.

More broadly: