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!