Skip to main content

zk-snarks

Before we can dive into the Illium protocol we need to wrap our heads around zk-snarks. Don't worry, while the math behind zk-snarks is super difficult to understand, you don't need to know any math to get a high level overview of what they are and what they do.

In a nutshell, zk-snarks are programmatic cryptographic proofs that allow us to prove just about any statement we desire.

Consider something you're likely already familiar with:

Digital Signatures

Digital signatures and zero-knowledge proofs are fundamental concepts in cryptography. In a digital signature scheme, a signer uses their private key to sign a message. Anyone can verify the signature against the signer's public key, ensuring the authenticity and integrity of the message.

On the other hand, a zero-knowledge proof allows a prover to demonstrate knowledge of a secret to a verifier, without revealing the secret itself.

Both of these concepts are critical for secure communication and transaction verification in a variety of applications.

In code, we might make use of digital signatures as follows:

privateKey, publicKey := GenerateKeyPair()

message := "hello world"

signature := Sign(privateKey, message)

valid := Verify(publicKey, message, signature)

Here the Sign() and Verify() functions are hiding all the complexity of the elliptic curve math behind the digital signature but all you really need to know is what they do.

Zk-snarks are just as easy to understand from this high level.

Back to zk-snarks

Consider a function in code that looks like the following:

func foobar(privateParams, publicParams) bool {
// Some code here
}

In the above code block we have a function, which we're calling foobar, that takes in two sets of parameters, one private and one public, and returns a boolean (either true or false). The body of our function could be anything we want it to be.

Once we define the function body, zk-snarks allow us to prove that we know some combination of private and public parameters that make the function return True without revealing the private parameters (we will reveal the public parameters).

In this sense we can create a zero-knowledge proof for just about any statement we can write in code.

Let's see what this might look like in code:

provingKey, verifyingKey := Compile(foobar)

proof := Prove(provingKey, privateParams, publicParams)

valid := Verify(verifyingKey, publicParams, proof)

In the first line we "compile" the foobar function and produce a keypair which is cryptographically linked to our foobar function. When used in production both the prover and verifier would have prior agreement on the body of the foobar function and could independently calculate the keypair.

In the second line the prover creates a proof using the provingKey and the private and public parameters.

At this point the prover would share the proof and publicParams with the verifier.

And finally, on line 3 the verifier would verify the proof using the verifyingKey and the publicParams.

That's all there is to it. See that wasn't so bad!