Open chancehudson opened 3 weeks ago
Triton VM comes with a code generator for circuits evaluations. This code generator can output both Rust code and tasm (Triton VM assembly). You can find the code generator for tasm here.
It assumes that your inputs (a
and b
variables in your example) live in memory though. So the code generator will not produce code using the divine
instruction.
This means that you just need to be able to compile your ar1cs
definitions to the Constraints
definition in Triton VM, and then Triton VM can do the rest.
Haha, i figured ya'll had thought of this already. Using memory is probably a better idea than using secret inputs. I was planning to extend the user input to support this, but I'll include this with memory pre-loading instead.
Two questions:
This means that you just need to be able to compile your ar1cs definitions to the Constraints definition in Triton VM, and then Triton VM can do the rest.
So to clarify, I can define a constraint between memory indices? An r1cs constraint should always be a (max) degree 2 polynomial so it should be possible to convert to a stark constraint.
I'm probably going to dig into implementing triton stuff in a couple weeks. I have some more immediate plans around integrating r1cs provers, and building an ide in a hackathon 🤞.
The first two transition constraints relating to the processor table in Triton VM are defined like so[^1]:
pub fn transition_constraints(
circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
let constant = |c: u64| circuit_builder.b_constant(c);
let curr_base_row = |col: ProcessorBaseTableColumn| {
circuit_builder.input(CurrentBaseRow(col.master_base_table_index()))
};
let next_base_row = |col: ProcessorBaseTableColumn| {
circuit_builder.input(NextBaseRow(col.master_base_table_index()))
};
// constraints common to all instructions
let clk_increases_by_1 = next_base_row(CLK) - curr_base_row(CLK) - constant(1);
let is_padding_is_0_or_does_not_change =
curr_base_row(IsPadding) * (next_base_row(IsPadding) - curr_base_row(IsPadding));
let instruction_independent_constraints =
vec![clk_increases_by_1, is_padding_is_0_or_does_not_change];
So you specify relations between "current row" and "next row", and it's then implicit that these rows live somewhere in memory. Then when you generate the TASM code that evaluates all these AIR constraints, you specify where in memory these rows live, or, you specify that their position is only known at run-time, and the generated code will then read their positions from the stack and evaluate all constraints and store the result in memory.
So yeah, I guess it is somewhat like defining constraints between memory cells, but you get a lot more context. It's better to think of this as defining relations between inputs, which are the rows in your algebraic execution trace.
You can think of the ConstraintCircuitBuilder
as a type in Rust that captures some of the capabilities of the ar1cs
language.
[^1]: Code copied from (https://github.com/TritonVM/triton-vm/blob/master/triton-vm/src/table/processor_table.rs#L3844)
This is great, i didn't think it would be possible to define a constraint in such a concrete way.
The
tasm
target can supportar1cs
based functions by wrapping them in assembly automatically. The key is automatically building the input variables in a structured way.Consider this
ar1cs
function:a
andb
are variables being constrained by the provided equations.one
is equal to 1.The value of
a
is defined by the caller. The value ofb
is defined by the current scope and returned.This might be expressed by the compiler in tasm as so: