1
0
Fork 0

GitBook: [#8] Describing Tree Update witness

This commit is contained in:
Justin Martin 2021-10-14 19:46:41 +00:00 committed by gitbook-bot
parent cd4d273ef8
commit 4beb783e73
No known key found for this signature in database
GPG Key ID: 07D2180C7B12D0FF
1 changed files with 37 additions and 5 deletions

View File

@ -22,7 +22,7 @@ Registering a withdrawal is the essentially the same as registering a deposit, e
The `registerDeposit` and `registerWithdrawal` contract methods of the Tornado Trees contract each emit a corresponding event, `DepositData` and `WithdrawalData` containing the same values as were included in the computed hash, plus an additional event field indication the order in which they entered into the queue.
### Chunked Merkle Tree updates in Zero Knowledge
### Chunked Merkle Tree Update
Standard Merkle Trees are fairly expensive to store and update, especially if you want to commit to a large number of leaves. Depositing a note into the Tornado.cash deposit contracts can cost upwards of 1.2M gas, which can be hundreds of dollars worth of ETH if depositing on Ethereum mainnet. Most of this gas cost results from simply inserting a commitment into the deposit contract Merkle Tree.
@ -54,7 +54,7 @@ The leaves of the tree are then populated from left to right with the leaf hashe
Now that we have the "old root", we can proceed to take a chunk of pending events (256 of them), compute their Poseidon hashes, and insert them into the tree. After updating the non-leaf nodes up to the root, we will have the "new root".
Next, we need to collect a list of path elements starting from the right-most non-zero leaf in the tree, as well as an array of `0/1` bits indicating whether each path element is to the left or right of its parent node.
Next, we need to collect a list of path elements starting from the subtree leaf, as well as an array of `0/1` bits indicating whether each path element is to the left or right of its parent node.
#### Computing the Args Hash
@ -64,13 +64,45 @@ Construct a message that is the concatenation of these fields:
1. The old root label (32 bytes)
2. The new root label (32 bytes)
3. The path indices as bits, left-padded with zeroes (4 bytes)
3. The path indices as an integer, left-padded with zeroes (4 bytes)
4. For each event
1. The commitment/nullifier hash (32 bytes)
2. The Tornado instance address (20 bytes)
3. The block number (4 bytes)
Compute the SHA-256 hash of this message, and then compute its modulus against the BN128 group modulus found in the `SNARK_FIELD` constant of the Tornado Trees contract.
Compute the SHA-256 hash of this message, and then modulus the hash against the BN128 group modulus found in the `SNARK_FIELD` constant of the Tornado Trees contract.
#### Inputs to a Tree Update Proof
### Generating a Merkle Tree Update Witness
#### Inputs to a Tree Update Witness
The [Batch Tree Update](https://github.com/tornadocash/tornado-trees/blob/master/circuits/BatchTreeUpdate.circom) circuit takes a single public input, which is the resulting SHA-256 args hash in the BN128 field.
The additional private inputs for a Tree Update witness are:
1. The old root
2. The new root
3. The path indices as an integer, left-padded with zeroes
4. An array of path elements
5. For the pending events being inserted
1. An array of commitment/nullifier hashes
2. An array of Tornado instance addresses
3. An array of block numbers
#### Proven Claim
To prove that we have updated the tree correctly, we don't have to provide a proof that spans the entire tree. Instead, we can prove just that a subtree of the 8-level chunk size, with a list of specified leaves, was added in place of the left-most zero leaf of a 12-level tree.
#### Proving the Args Hash
Instead of specifying all of the inputs publicly, we can take the Args Hash that we computed earlier and compare it to the result of computing the same hash within the ZK circuit, using the private inputs. This makes for a much more efficient proof verification execution.
#### Build the Subtree
Taking the three fields of each pending event in the order (instance, commitment/nullifier, block number), we compute the Poseidon hash of each leaf. We then construct the Merkle Tree just for the subtree that we're updating. Since the subtree is full, we don't need to worry about any zero leaves.
#### Verify the Subtree Insertion
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.