# Halo2 Circuits ## Groth16 Uses R1CS Arithmetization R1CS encodes an arithmetic computation (addition and multiplication). $\text{Public Inputs: } (1, x_1, \dots, x_m)$ $\text{Private Inputs: } (x_{m + 1}, \dots, x_n)$ $\text{Constraints: }(A_1B_1 = C_1, \dots, A_kB_k = C_k)$ **Variables** $x_i$ are stored in two vectors (public and private). A **constraint** is a degree-1 or degree-2 polynomial equation: $\text{polynomial}_A * \text{polynomial}_B = \text{polynomial}_C$ $A_j, B_j, C_j$ are each a linear combination (degree-0 or degree-1 polynomial) of the constraint system variables $x_i$: $A_j, B_j, C_j = \sum_{i \in [1, n]}c_ix_i$. $A_jB_j$ is a degree-1 or degree-2 polynomial. **Addition** is encoded as a degree-1 constraint: $$(x_1 + x_2) * (1) = (x_3)$$ **Linear combinations** are encoded as a degree-1 constraint: $$(x_1 + 2x_2 + 3x_3) * (1) = (x_4)$$ **Multiplication** is encoded as a degree-1 constraint: $$ (x_1) * (x_2) = (x_3) \\ (x_1 + x_2) * (2x_3 + 3x_4) = (x_5) $$ ## Halo2 uses Plonk Arithmetization Plonk encodes an arithmetic computation plus variable copying and lookup tables. Variables are laid out in a **matrix** and can have one of three types: - **Advice** - private input - **Instance** - public input - **Fixed** - constant value Each **column** in a matrix stores variables of a single type. ![](https://i.imgur.com/pqP6ysL.png) </br> Halo2/Plonk use **custom constraints** - each constraint defines an arithmetic operation/operations on the matrix values which is not limited to the R1CS form $AB = C$. Example custom constraints: $$ x_1^{(i)} + x_2^{(i)} = x_3^{(i)} \\ x_1^{(i)} + x_2^{(i)} = x_1^{(i + 1)} \\ 3x_1^{(i)} + 5x_2^{(i)}x_2^{(i)}x_3^{(i)} = \text{pi}^{(i)} $$ Note that constraints can have degree greater than 2 (e.g. $x_1x_1x_1$) and variables are identified using there position in the matrix (e.g. $x_1^{(i)}$ is the variable in row $i$ of column $1$). </br> The Halo2 **constraint system** defines the matrix and custom constraints. ![](https://i.imgur.com/fmlgRGD.png) Each constraint $j$ is associated with a **selector** $s_j$ whose value in each row $s_j^{(i)} \in \{0, 1\}$ turns constraint $j$ on/off. Each constraint $j$ is associated with a **fixed column** in the matrix $(s_j^{(1)}, \dots, s_j^{(n)})$ which determines whether the constraint is enabled/disabled in each matrix row. ![](https://i.imgur.com/Whc3m2y.png) Each circuit's constraint system must be **configured** before you can synthesize its values: - Define columns - Define constraints - Define value copies - Define lookup tables ```rust // Circuit stores public and private inputs. struct MyCircuit { pub_inputs: PublicInputs, priv_inputs: PrivateInputs, } // Circuit has two private inputs. struct PrivateInputs { a: Value, b: Value, } // Circuit has no public inputs. struct PublicInputs; // `MyCircuit`'s configuration. struct MyConfig { // Two advice columns. advice: [Column<Advice>; 2], // One constraint (addition). s_add: Selector, } impl Circuit for MyCircuit { type Config = MyConfig; type FloorPlanner = SimpleFloorPlanner; fn configure(cs: &mut ConstraintSystem) -> Self::Config { // Create columns: let a_1 = cs.advice_column(); let a_2 = cs.advice_column(); // Create constraints: let s_add = cs.selector(); cs.create_gate("add", |cs| { // `Rotation::cur` is the row where `s_add` is enabled. let a = cs.query_advice(a_1, Rotation::cur()); let b = cs.query_advice(a_2, Rotation::cur()); // `Rotation::next` is the next row. let c = cs.query_advice(a_1, Rotation::next()); // `a + b - c = 0` Constraints::with_selector(s_add, a + b - c) }); MyConfig { advice: [a_1, a_2], s_add, } } fn synthesize(&self, config: Self::Config, mut layouter: impl Layouter) -> Result<(), Error> { let [col_1, col_2] = config.advice; let _c = layouter.assign_region("a + b = c", |mut region| { // Row where `s_add` is enabled has a relative row index of `0` in the region. let offset = 0; // Turn addition constraint on using `s_add` selector. config.s_add.enable(region, offset)?; // Allocate values. let a = region.assign_advice("a", config.advice[0], offset, || self.priv_inputs.a)?; let b = region.assign_advice("b", config.advice[1], offset, || self.priv_inputs.b)?; region.assign_advice("c", config.advice[0], offset + 1, || { a.value().zip(b.value()).map(|(a, b)| a + b) }) })?; Ok(()) } } ```