--- tags: zkEVM halo2 --- # Getting rid of halo2 meta in zkEVM-ce ## Expression builder Generating expressions with columns is very verbose, we can have a wrap type that wraps around columns and use things that we have currently in the constraint builder and in some expression operators. For example: ```rust= let eb = ExpressionBuilder::default(meta); let index = Column::from(bytecode_table.index, eb); let value = Column::from(bytecode_table.value, eb); let tag = Column::from(bytecode_table.tag, eb); let q_enable = Column::fixed(eb); let q_last = Column::fixed(eb); ``` This new type would have mainly four functions `.rot(N)`, `.cur()`, `.prev()` and `.next()` that would return an "expresion builder" to generate expressions with different methods/operators. A code like this: ```rust= let is_header = |meta: &mut VirtualCells<F>| { not::expr(meta.query_advice(bytecode_table.tag, Rotation::cur())) }; // When is_header -> // assert cur.index == 0 // assert cur.value == cur.length meta.create_gate("Header row", |meta| { let mut cb = BaseConstraintBuilder::default(); cb.require_zero( "cur.index == 0", meta.query_advice(bytecode_table.index, Rotation::cur()), ); cb.require_equal( "cur.value == cur.length", meta.query_advice(bytecode_table.value, Rotation::cur()), meta.query_advice(length, Rotation::cur()), ); cb.gate(and::expr(vec![ meta.query_fixed(q_enable, Rotation::cur()), not::expr(meta.query_fixed(q_last, Rotation::cur())), is_header(meta), ])) }); ``` could be built like this: ```rust= let is_header = tag.cur().not(); // When is_header -> // assert cur.index == 0 // assert cur.value == cur.length meta.create_gate("Header row", |meta| { let mut cb = BaseConstraintBuilder::default(); eb.set_gate_meta(meta); cb.require( "cur.index == 0", index.cur(), // Converted with a Expression::From<ExpressionBuilder> ); cb.requirel( "cur.value == cur.length", value.cur().eq(lenght.cur()) ); cb.gate( q_enable.cur().and([q_last.cur().not(),is_header]), ])) }); ``` We see that the problem might be, that we are not passing meta, to these expressions. We do something ugly with a global there, but this will be make cleaner later. ### Returning from circuit configuration The new wrapper column type could have a method `to_halo2` that returns the wrapped column, so it can be returned in the circuit configuration. ## Gate builder Let's change now the way we create gates, to make the code more readable. ### gb.when Creating a gate now using the constrain builder put the enabling exprasion at the bottom, as we can see in the previos examples: ```rust= meta.create_gate("desc", |meta| { let mut cb = BaseConstraintBuilder::default(); cb.require(A); cb.require(B); cb.gate(ENABLE) }); ``` The proposal would be to have something that puts the `Enable` expression at the beginning, where it is more readable: ```rust gb.when("desc", ENABLE, |meta, cb| { cb.require(A); cb.require(B); }); ``` This secondarly allows us to pass `cb` to the closure and eliminating a line in each gate. And to set the right meta for the expression builder. Also, as with the expression builder, it does not make sense to have other requires types we could just return an array of required expressions: ```rust gb.when("desc", ENABLE, || { [A , B] }); ``` Our example would be: ```rust= let eb = ExpressionBuilder::default(meta); let gb = GateBuilder::default(meta, eb); let is_header = tag.cur().not(); // When is_header -> // assert cur.index == 0 // assert cur.value == cur.length gb.when("Header row", is_header.and([q_enale.cur(), q_last.cur().not()]), || { [ ("cur.index == 0", index.cur()) ("cur.value == cur.length", value.cur().eq(lenght.cur())) ] }); ``` The gate builder will make sure that the `meta` is used when converting to halo2 expressions. ### gb.always For the cases where the gate is a simple expression, we can have a expresion of the type: ```rust gb.always(desc, A); ``` ### Conclusion With the new column types, the expression builder and the gate builders, we get away from halo2 meta and the not readable ways, gates and expressions are built. This put us very close to building halo2 usable rust code that is as readable as the python specs. ## Implicit selector column Another idea, that I have not researched much is to have a wrapper column type that will be automatically enabled in the witness generation for all the rows of the region, this seems very common in our code.