Subtree Update Circuit
Details of subtree update circuit
Input Encodings
Almost all inputs (listed below) are as defined in the JoinSplit Circuit.
The only special encoding we need for this circuit is to deal with the fact that accumulatorHash is a 256-bit value, which is not guaranteed to fit in an element.
Another public input for the circuit is the path to the root of the subtree spanning our batch update. The way this is encoded requires a bit more detail about the commitment tree's structure.
Our note commitment tree is quaternary - each branch node has four children, not two. That means the "path" is not a sequence of booleans (left vs right), but a sequence of numbers between 0 and 4. For binary trees, usually people use the fact that the big-endian binary representation of the index of a given leaf is the path - starting at the root, each bit when read left-to-right tells us which way to go. This would be a D - log2(B) bit unsigned integer, where D is the depth of the tree and B is the batch size.
For our quaternary tree, this is almost identical - the only difference is that we read the big-endian bit representation of the index two bits at a time, giving us a sequence of two-bit numbers 0-4 which tell us which child to traverse instead of a sequence of bits. If our tree is depth D then this will be a 2*D - log2(B) bit number. In our case, D = 16 and B = 16, which means our path will take up 28 bits.
This gives us plenty of room so we can pack the 3 most-significant bits of accumulatorHash into the path's field element. The resulting encoding is as follows:
accumulatorHashis the element represented by the 253 least-significant bits of the "actual" 256-bitaccumulatorHashencodedPathAndHashis the element represented by the following bits, from most-significant to least-significant:254 - (2*D - log2(B)) - 30bitsthe 3 most-significant bits of the actual
accumulatorHashthe
2*D - log2(B)bits of the path from the root of the commitment tree to the root of the subtree spanning the batch insertion
Statement to Prove
The Subtree Update circuit has the following public inputs:
accumulatorHash- the 253 least-significant bits of the hash at the tip of theaccumulatorQueueon-chainencodedPathAndHash- a bit-packed field element containing the path from the root of the commitment tree to the root of the subtree spanning the batch and the 3 most-significant bits ofaccumulatorHash.For notation's sake, I will use
encodedPathAndHash.hashBitsto refer to the 3 hash bits, andencodedPathAndHash.pathto refer to the2*D - log2(B)path bits
oldRoot- the old root of the commitment treenewRoot- the claimed new root of the commitment tree
We also define the constant EMPTY_SUBTREE_ROOT, which is the root of the depth-log2(B) subtree containing B zeros.
And requires the following private data:
SubtreeMembershipProof- a depth2*D - log2(B)-bit Merkle membership proof againstnewRootwhose claimedleafis the root of the subtreeEmptySubtreeMembershipProof- a depth2*D - log2(B)-bit Merkle membership proof againstoldRootwhose claimedleafisEMPTY_SUBTREE_ROOT.notes- a sizeBarrayEncodedNotefor each leaf in the tree. For note commitment insertions, the corresponding entry is ignored. These are encoded as defined in the encodings section.leaves- a sizeBarray containing the leaves of the subtree - that is, the note commitments of every note to be inserted, no matter which insertion case it is.bitmap-Bbits indicating which insertions are note insertions and which insertions are note commitment insertions. Ifbitmap[i] == 1, then theith insertion should be treated as anoteinsertion, not a note commitment insertion.preimage- a sizeB*256bit array containing every insertion digest concatenated together.
We prove that the following conditions hold true, converting to/from big-endian bit representation as needed:
for the
ithbitofbitmap:bitis0or1if
bit == 1, thenpreimage[i*256..(i+1)*256] = sha256(notes[i])leaves[i] = noteCommit(note), wherenoteCommitcomputes the note commitment of a note.
otherwise,
preimage[i*256 + 2..(i+1)*256] = leaves[i]
encodedPathAndHash.hashBits || accumulatorHash = sha256(preimage)where
||denotes concatenation
SubtreeMembershipProof.leafis the root of the depth-log4(B)subtree whose leaves areleaves.EmptySubtreeMembershipProof.leafis the root of an empty depth-log4(B)subtree.EmptySubtreeMembershipProof.pathrepresents the same path asencodedPathAndHash.pathSubtreeMembershipProof.pathrepresents the same path asencodedPathAndHash.pathEmptySubtreeMembershipProofis a valid Merkle membership proof againstoldRoot. This asserts that the subtree was empty before the insertion.SubtreeMembershipProofis a valid Merkle membership proof againstnewRoot. This asserts thatnewRootis the correct root resulting from the subtree update.
Last updated