Skip to content

ZK Circuits

There are no constraints on WeaveDB itself. However, when using zkJSON, the database schema and circuit parameters must be chosen carefully, since zk-circuits have strict limits on what they can prove depending on their configuration.

For example, the default value size_val=8 allows only about 190 characters per field, while size_json=256 supports an entire JSON of roughly 6,080 characters. Likewise, setting level=168 makes it possible to use document IDs up to 28 characters in base64, which is enough to hold values such as Ethereum addresses.

These parameters can be increased, but at a cost. Larger values make the zk-circuits grow quickly—sometimes exponentially. Once the circuit requires a power of tau greater than 20, proof generation slows down dramatically and Ethereum gas costs also rise. In practice, the smaller the circuit, the cheaper and faster the proofs will be.

Circuits

There are 5 main circuits, and each circuit is built on top of the preceding one.

JSON.circom

The base building block to prove JSON with an efficient encoding.

  • size_json : JSON size : default 256
  • size_path : path size : default 4
  • size_val : value size : default 8

Collection.circom

A collection proven by a sparse merkle tree (SMT) can contain many JSON documents (2 ** 168 by default).

  • level : collection SMT level : default 168
  • size_json : JSON size : default 256
  • size_path : path size : default 4
  • size_val : value size : default 8

DB.circom

A database proven by a sparse merkle tree (SMT) can contain many collections (2 ** 8 by default).

  • level_col : DB SMT level : default 8
  • level : collection SMT level : default 168
  • size_json : JSON size : default 256
  • size_path : path size : default 4
  • size_val : value size : default 8

Query.circom

Query proves a JSON data insert or update by a single write query.

  • level_col : DB SMT level : default 8
  • level : collection SMT level : default 168
  • size_json : JSON size : default 256

Rollup.circom

Rollup proves batch data transitions.

  • tx_size : max number of queries in a batch : default 10
  • level_col : DB SMT level : default 8
  • level : collection SMT level : default 168
  • size_json : JSON size : default 256

Powers of Tau

The first thing you need to do is to set up a powers of tau by a ceremony. As the power goes up the generation time and the wasm file size increases exponentially, and what power required for each circuit depends on the parameters above. So you need to find the right balance with the parameters of each circuit for your application. For instance, power 20 required for the default Rollup circuit settings takes hours with a normal consumer computer.

To run a ceremony,

yarn ceremony --power 14

Generated files are located at build/pot.

You can also specify entropy and name for the ceremony. Refer to the Circom docs for what they mean.

yarn ceremony --power 14 --name "first contribution" --entropy "some random value"

The same goes with the compiling process below.

Compile Circuit

You can specify the parameters when compiling a circuit. Unspecified parameters will use the default values.

For instance, to compile the JSON circuit,

yarn compile --power 14 --circuit json --size_json 256 --size_path 4 --size_val 8

To compile the Rollup circuit, you might need to increase --max-old-space-size of NodeJS.

yarn compile --power 20 --circuit rollup --tx_size 10 --level_col 8 --level 168 --size_json 256

All the generated files are stored at build/circuits including a Solidity verifier contract.

Concept of Some Parameters

The litepaper explains in detail, but here are brief explanations on size and level.

size

The base unit of size is uint. Circom by default uses BN254, the modulus of 21888242871839275222246405745257275088548364400416034343698204186575808495617 (77 digits), and Solidity's base storage block is uint256 and allows 78 digits. So zkJSON efficiently encodes JSON and packs it into blocks of 76 digits, which is one uint.

path_size=5 means, 5 * 76 digits are allowed for the query path when encoded, and it will be represented within uint[5] in Solidity. on the Solidity side, however, zkJSON uses dynamic arrays uint[], so it will be more space-efficient than the max set size. But the zk-circuits cannot prove data sizes more than the set size.

The default json_size is set 256, which is 256 * 76 digits and should be sufficient for most JSON data.

level

level is the level of the sparse merkle tree (SMT). As the litepaper describes, the level of SMT for Collection determines how many alphanumeric characters each document ID can contain. It's determined by

Number of Characters = (log10(2^Level)) / 2

level=168 can allow 28 characters in document ID. This is significant because document IDs are often used in access control rules of NoSQL databases (with WeaveDB, for instance).

28 characters can fit compressed Ethereum addresses (20 bytes) in Bse64 format.

For DB, level_col determines how many collections the DB can contain. The collection IDs use the direct index numbers and are not converted to an alphanumeric representation, so level_col=8 (2 ** 8 = 256) collections should be sufficient for most applications. But you are free to set a different value.

Default Parameters and Required POT

CircuitPOTsize_jsonsize_pathsize_vallevellevel_coltx_size
JSON1425648
Collection1625648168
DB16256481688
Query172561688
Rollup20256168810

Optimal DB Settings

We have been experimenting with the right parameters and so far the following seems to be our favorite for the DB circuit. We will probably change the default settings soon.

CircuitPOTsize_jsonsize_pathsize_vallevellevel_col
DB162563225618424
  • size_path=32 : about 760 characters in path
  • size_val=256 : about 6080 characters in each field
  • level=184 : max 30 characters in doc ID in base64
  • level_col=24 : max 16777216 collections in a database

This setting is within power=16, which only takes about 3 seconds to generate a proof.