Chiquito is a high-level structured language for implementing zero knowledge proof applications.
Chiquito is being implemented in the DSL Working Group of PSE, Ethereum Foundation.
Most ZKP DSLs are based on writing constraints, witness generation and some abstration for DRY like templates or gadgets.
Chiquito allows the developer to think in more high-level and structured abstractions that most ZKP DSLs, while not sacrificing performance.
Chiquito starts from the idea that every zero knowledge proof represents a program (the setup), which can have many computations (the trace) that is proven for a certain input, output and intermediate values (the witness).
The main structured abstraction in chiquito is the step. Any computation can be divided in individual steps. A program is represented by a circuit that has one or many step types, a particular computation is represented as a series of step instances that can have arbitrary order.
A step type constains:
A chiquito circuit contains a trace function that for a given input will generate the step instances in a particular order and use the step type witness generation.
Another important piece of Chiquito are the signals. They represent elements of the witness.
There are several types:
a == a.rot(2)
which means that a
is equial to a
in the next of the next step (super rotation +2
).a == a.next()
but you could not assert a == a.prev()
. Forward signal has the advantage of allowing for witness size optimizations.Chiquito has mmany more features, but these are enough to start writing basic circuits.
Currently Halo2 backend is implemented, but we are looking into implementing other backends.
Chiquito comes in two flavors: ChiquitoCore (Rust) and PyChiquito (Python), so you can write Chiquito applications in either Rust or Python. PyChiquito, and any other language interface in the future, uses ChiquitoCore for most of its work, so adding new languages is easy.
PLONKish-specific features:
Planned:
In research:
But better see for yourself:
class FiboStep(StepType):
def setup(self: FiboStep):
self.c = self.internal("c")
self.constr(eq(self.circuit.a + self.circuit.b, self.c))
self.transition(eq(self.circuit.b, self.circuit.a.next()))
self.transition(eq(self.c, self.circuit.b.next()))
def wg(self: FiboStep, args: Tuple[int, int]):
a_value, b_value = args
self.assign(self.circuit.a, F(a_value))
self.assign(self.circuit.b, F(b_value))
self.assign(self.c, F(a_value + b_value))
class Fibonacci(Circuit):
def setup(self: Fibonacci):
self.a: Queriable = self.forward("a")
self.b: Queriable = self.forward("b")
self.fibo_step = self.step_type(FiboStep(self, "fibo_step"))
self.pragma_num_steps(11)
def trace(self: Fibonacci, args: Any):
self.add(self.fibo_step, (1, 1))
a = 1
b = 2
for i in range(1, 11):
self.add(self.fibo_step, (a, b))
prev_a = a
a = b
b += prev_a
fibo = Fibonacci()
fibo_witness = fibo.gen_witness(None)
fibo.halo2_mock_prover(fibo_witness)
This is explained in more detail in the tutorial, but you can see how concise and clear it is.