1
0
Fork 0

GitBook: [#10] Finish describing Tornado Trees

This commit is contained in:
Justin Martin 2021-10-16 16:57:01 +00:00 committed by gitbook-bot
parent 4beb783e73
commit 1e513d9f27
No known key found for this signature in database
GPG Key ID: 07D2180C7B12D0FF
2 changed files with 18 additions and 15 deletions

View File

@ -4,23 +4,11 @@ Behind the Tornado.cash front-end sits a number of [Circom](https://docs.circom.
### How ZK Circuits Work
#### SNARKs and PLONK
#### SNARKs and GROTH16
Before trying to understand how Tornado.cash works under the hood, you first need to understand Zero Knowledge circuits, how they're constructed, and how proofs are generated client-side, then verified on-chain. While there are a [few different types](https://en.wikipedia.org/wiki/Zero-knowledge_proof#Zero_knowledge_types) of ZK systems, Tornado.cash relies upon a variant known as "succinct non-interactive arguments of knowledge" (SNARK), specifically a variant called [PLONK](https://eprint.iacr.org/2019/953).
Before trying to understand how Tornado.cash works under the hood, you first need to understand Zero Knowledge circuits, how they're constructed, and how proofs are generated client-side, then verified on-chain. While there are a [few different types](https://en.wikipedia.org/wiki/Zero-knowledge_proof#Zero_knowledge_types) of ZK systems, Tornado.cash relies upon a variant known as "succinct non-interactive arguments of knowledge" (SNARK), specifically a variant called GROTH16.
If you want to develop a deep understanding of how PLONK works, there is a [great explanation](https://vitalik.ca/general/2019/09/22/plonk.html) by none other than Vitalik Buterin himself. If you're not a math nerd, but can follow along with a bit of math talk, Vitalik's explanation can be best summarized as:
> ... the "fancy cryptography" it relies on is one single standardized component, called a "polynomial commitment" ... A polynomial commitment is a short object that "represents" a polynomial, and allows you to verify evaluations of that polynomial, without needing to actually contain all of the data in the polynomial. That is, if someone gives you a commitment $$c$$representing $$P(x)$$, they can give you a proof that can convince you, for some specific $$z$$what the value of $$P(z)$$ is.
>
> So how do the commitments themselves work? ... A trusted-setup procedure generates a set of elliptic curve points $$G, G * s, G * s^2 \ldots G * s^n$$, as well as $$G^2 * s$$ where $$G$$ and $$G_2$$ are the generators of two elliptic curve groups and `𝑠` is a secret that is forgotten once the procedure is finished.
>
> These points are published and considered to be "the proving key" of the scheme; anyone who needs to make a polynomial commitment will need to use these points. A commitment to a degree-d polynomial is made by multiplying each of the first d+1 points in the proving key by the corresponding coefficient in the polynomial, and adding the results together.
>
> Given a program $$P$$, you convert it into a circuit, and generate a set of equations ..., then convert this set of equations into a single polynomial equation. You also generate from the circuit a list of copy constraints. ... To generate a proof, you compute the values of all the wires and convert them into three polynomials. ... There is a set of equations between the polynomials that need to be checked; you can do this by making commitments to the polynomials, opening them at some random $$z$$, and running the equations on these evaluations instead of the original polynomials. The proof itself is just a few commitments and openings and can be checked with a few equations.
>
> **And that's all there is to it!**
Simple, right? Great.
#### Circom and snarkjs
@ -46,7 +34,7 @@ In short, the R1CS is a set of polynomial constraints which any proof generated
Now, depending on what you're using Tornado.cash for, you might not want any witnesses. However, don't worry, if everything is working correctly, all of the witnesses to your interactions with Tornado.cash will be aggressively compacted, and their bodies disposed of as you please.
In the context of a PLONK circuit, a witness is the set of values that need to be generated from the inputs to the circuit, based on the circuit design, to satisfy all of the constraints imposed by the circuit. You can think of the witness generator produced by Circom as a circuit-specific decompression function which runs your inputs through the circuit, and snapshots all of the various intermediate values that are produced along the way.
In the context of a SNARK circuit, a witness is the set of values that need to be generated from the inputs to the circuit, based on the circuit design, to satisfy all of the constraints imposed by the circuit. You can think of the witness generator produced by Circom as a circuit-specific decompression function which runs your inputs through the circuit, and snapshots all of the various intermediate values that are produced along the way.
With this expanded form generated from your inputs, you know which values must be assigned to the constraints specified by the R1CS in order to construct a valid proof.

View File

@ -106,3 +106,18 @@ Taking the three fields of each pending event in the order (instance, commitment
Lastly, we verify that inserting the subtree root at the proposed position results in the old root transforming to the new root. This works essentially the same way as for the [Merkle Tree Check](core-deposit-circuit.md#computing-the-witness) in the core deposit circuit, except using Poseidon instead of MiMC.
The [Merkle Tree Updater](https://github.com/tornadocash/tornado-trees/blob/master/circuits/MerkleTreeUpdater.circom) first verifies that the specified path contains a zero leaf by computing what the root would be given the path elements, path indices, and a zero leaf. It compares this against the specified old root, and then repeats that process again with the proposed subtree leaf, comparing the resulting root to the new root.
### Completing the Tree Update
With the witness generated, and the proof generated from it, we can now call the corresponding `updateDepositTree` or `updateWithdrawalTree` method on the Tornado Trees contract.
We pass the update method the proof, the args hash, the old root, the new root, path indices, and a list of events that we're batch inserting. The update method then verifies that:
1. The old root specified is the current root of the corresponding tree
2. The specified merkle path points to the first available subtree zero leaf
3. The specified args hash corresponds to the supplied inputs and proposed events
4. The proof is valid according to the circuit verifier, using the args hash as public input
If these preconditions are met, then each inserted event is deleted from the queue. The corresponding contract fields indicating the current and previous roots are updated, as well as a pointer to the last event leaf.
There is a special branch of logic in this method which also handles migrating events from the Tornado Trees V1 contract. After the initial migration, these paths are never visited, and can be safely ignored.