In circuit generation mode, the
has_witness field of
RunState is set to the default
CircuitGeneration, and the program of the user is ran to completion.
During the execution, the different snarky functions called on
RunState will create internal variables as well as constraints.
Variables can be created via the
compute() function, which takes two arguments:
TypeCreationtoggle, which is either set to
Unsafe. We will describe this below.
- A closure representing the actual computation behind the variable. This computation will only take place when real values are computed, and can be non-deterministic (e.g. random, or external values provided by the user). Note that the closure takes one argument: a
WitnessGeneration, a structure that allows you to read the runtime values of any variables that were previously created in your program.
compute() function also needs a type hint to understand what type of snarky type it is creating.
It then performs the following steps:
- creates enough
CVarto hold the value to be created
- retrieves the auxiliary data needed to create the snarky type (TODO: explain auxiliary data) and create the
snarky variableout of the
CVars and the auxiliary data
- if the
TypeCreationis set to
Checked, call the
check()function on the snarky type (which will constrain the value created), if it is set to
Unsafedo nothing (in which case we’re trusting that the value cannot be malformed, this is mostly used internally and it is highly-likely that users directly making use of
Unsafeare writing bugs)
At this point we only created variables to hold future values, and made sure that they are constrained.
The actual values will fill the room created by the
CVar only during the witness generation.
All other functions exposed by the API are basically here to operate on variables and create constraints in doing so.