From 889e5926ceeaf8d7cd624df2822a72076ff4a83b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 13 Sep 2023 18:48:56 +0200 Subject: [PATCH 01/67] bus: draft API --- gadgets/src/bus.rs | 192 ++++++++++++++++++++++++++++++++++++++++++++ gadgets/src/lib.rs | 1 + gadgets/src/util.rs | 15 +++- 3 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 gadgets/src/bus.rs diff --git a/gadgets/src/bus.rs b/gadgets/src/bus.rs new file mode 100644 index 0000000000..bd982d0b72 --- /dev/null +++ b/gadgets/src/bus.rs @@ -0,0 +1,192 @@ +//! LogUp chip + +use eth_types::Field; +use halo2_proofs::{ + arithmetic::FieldExt, + circuit::{Region, Value}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Phase, VirtualCells}, + poly::Rotation, +}; +use std::marker::PhantomData; + +use crate::util::{query_expression, Expr}; + +/// BusConfig +#[derive(Default)] +pub struct BusConfig { + _marker: PhantomData, +} + +impl BusConfig { + /// Create a new bus. + pub fn new(meta: &mut ConstraintSystem, terms: &[Expression]) -> BusConfig { + BusConfig::default() + } +} + +/// BusBuilder +#[derive(Default)] +pub struct BusBuilder { + terms: Vec>, +} + +impl BusBuilder { + /// Connect a port to the bus. + pub fn connect_port>(&mut self, meta: &mut ConstraintSystem, port: &BP) { + let term = port.create_term(meta); + self.terms.push(term); + } + + /// Return the collected terms. + pub fn terms(self) -> Vec> { + self.terms + } +} + +/// BusPort prepares a term to be added to the bus. +pub trait BusPort { + /// The term to add to the bus. This expression must be fully constrained on all rows. + fn create_term(&self, meta: &mut ConstraintSystem) -> Expression; +} + +/// BusPort to access the bus. It manages its own helper column. +pub struct BusPortSingle { + helper: Column, + multi: BusPortMulti, +} + +impl BusPortSingle { + /// Create a new bus port with a single access. + pub fn put(meta: &mut ConstraintSystem, op: BusOp, value: Expression) -> Self { + let helper = meta.advice_column(); + let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); + + let multi = BusPortMulti::new(helper_expr, vec![(op, value.clone())]); + + Self { helper, multi } + } +} + +impl BusPort for BusPortSingle { + fn create_term(&self, meta: &mut ConstraintSystem) -> Expression { + self.multi.create_term(meta) + } +} + +/// A bus operation. +pub enum BusOp { + /// Put an item. The expression evaluates to 0 or the number of copies. + Put(Expression), + /// Take an item. The expression evaluates to 0 or 1. + Take(Expression), +} + +impl BusOp { + /// The expression of the count of items to put or take. + pub fn expr(&self) -> Expression { + match self { + BusOp::Put(e) => e.clone(), + BusOp::Take(e) => -e.clone(), + } + } +} + +/// BusPort to access the bus. The most flexible port. The helper cell is provided by the caller. It +/// supports multiple put/take accesses, as long as only one is active at a time. +pub struct BusPortMulti { + helper: Expression, + ops: Vec<(BusOp, Expression)>, +} + +impl BusPortMulti { + /// Put one out of several possible items to the bus. + /// The operations `ops` must be mutually exclusives (only one `count` is non-zero at a time) + /// across all puts and takes. + pub fn put(helper: Expression, ops: Vec<(BusOp, Expression)>) -> Self { + BusPortMulti { helper, ops } + } + + /// Create a new bus port. + fn new(helper: Expression, ops: Vec<(BusOp, Expression)>) -> Self { + BusPortMulti { helper, ops } + } +} + +impl BusPort for BusPortMulti { + fn create_term(&self, meta: &mut ConstraintSystem) -> Expression { + let rand = 1.expr(); // TODO + + meta.create_gate("bus access", |meta| { + self.ops + .iter() + .map(|(count, value)| { + // If count != 0, then helper = 1 / (rand + value) + count.expr() * (self.helper.clone() * (rand.clone() + value.clone()) - 1.expr()) + }) + .collect::>() + }); + + let count_sum = self + .ops + .iter() + .fold(0.expr(), |acc, (count, _)| acc + count.expr()); + + self.helper.clone() * count_sum + } +} + +#[cfg(test)] +mod tests { + use super::*; + use halo2_proofs::{ + arithmetic::FieldExt, + circuit::{Layouter, SimpleFloorPlanner, Value}, + dev::MockProver, + halo2curves::bn256::Fr, + plonk::ConstraintSystem, + }; + + #[test] + fn test_bus() { + let cs = &mut ConstraintSystem::::default(); + + let mut bus_builder = BusBuilder::::default(); + + // Circuit 1. + let port = BusPortSingle::put(cs, BusOp::Put(1.expr()), 2.expr()); + bus_builder.connect_port(cs, &port); + + // Circuit 2. + let port = BusPortSingle::put(cs, BusOp::Put(1.expr()), 2.expr()); + bus_builder.connect_port(cs, &port); + + // Global bus connection. + let bus_check = BusConfig::new(cs, &bus_builder.terms()); + } +} + + +/* + +sum( (1 / item) for each value ) == 0 + +item = RLC(beta, [ 1, circuit_tag, RLC(alpha, x), y, z, … ] ) + 1, RW, address, value + 1, COPY, src, dst, len + … + ++0*item ++3*item +-item +-item +-item + + +Bus Check: +- on each row, sum_next = sum_current + term_circuit1 + term_circuit2 + … + term_circut10 +- if is_last, sum_current == 0 + +Circuit 1: +term_circuit1 * value == 1 + +*/ \ No newline at end of file diff --git a/gadgets/src/lib.rs b/gadgets/src/lib.rs index c997babb15..7383126e63 100644 --- a/gadgets/src/lib.rs +++ b/gadgets/src/lib.rs @@ -22,6 +22,7 @@ pub mod monotone; pub mod mul_add; pub mod range; pub mod util; +pub mod bus; use eth_types::Field; use halo2_proofs::{ diff --git a/gadgets/src/util.rs b/gadgets/src/util.rs index e7e2b069ca..26b88e0df0 100644 --- a/gadgets/src/util.rs +++ b/gadgets/src/util.rs @@ -3,7 +3,7 @@ use eth_types::{ evm_types::{GasCost, OpcodeId}, U256, }; -use halo2_proofs::{arithmetic::FieldExt, plonk::Expression}; +use halo2_proofs::{arithmetic::FieldExt, plonk::{Expression, ConstraintSystem, VirtualCells}}; /// Returns the sum of the passed in cells pub mod sum { @@ -222,6 +222,19 @@ pub fn expr_from_u16>(u16s: &[E]) -> Expression { value } +/// Query an expression from the constraint system. +pub fn query_expression( + meta: &mut ConstraintSystem, + mut f: impl FnMut(&mut VirtualCells) -> T, +) -> T { + let mut expr = None; + meta.create_gate("Query expression", |meta| { + expr = Some(f(meta)); + Some(0.expr()) + }); + expr.unwrap() +} + /// Returns 2**by as FieldExt pub fn pow_of_two(by: usize) -> F { F::from(2).pow(&[by as u64, 0, 0, 0]) From 09f1586b13e3465117c4f3341fdf0a367ef87d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Thu, 14 Sep 2023 00:18:04 +0200 Subject: [PATCH 02/67] bus: Bus constraints and BusPortDual --- gadgets/src/bus.rs | 236 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 188 insertions(+), 48 deletions(-) diff --git a/gadgets/src/bus.rs b/gadgets/src/bus.rs index bd982d0b72..c8a8efc529 100644 --- a/gadgets/src/bus.rs +++ b/gadgets/src/bus.rs @@ -4,7 +4,7 @@ use eth_types::Field; use halo2_proofs::{ arithmetic::FieldExt, circuit::{Region, Value}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Phase, VirtualCells}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Phase, VirtualCells}, poly::Rotation, }; use std::marker::PhantomData; @@ -12,28 +12,70 @@ use std::marker::PhantomData; use crate::util::{query_expression, Expr}; /// BusConfig -#[derive(Default)] pub struct BusConfig { + enabled: Column, + is_first: Column, + is_last: Column, + acc: Column, _marker: PhantomData, } impl BusConfig { /// Create a new bus. - pub fn new(meta: &mut ConstraintSystem, terms: &[Expression]) -> BusConfig { - BusConfig::default() + pub fn new(cs: &mut ConstraintSystem, terms: &[Expression]) -> Self { + let enabled = cs.fixed_column(); + let is_first = cs.fixed_column(); + let is_last = cs.fixed_column(); + let acc = cs.advice_column(); + + cs.create_gate("bus sum check", |cs| { + let enabled = cs.query_fixed(enabled, Rotation::cur()); + let is_first = cs.query_fixed(is_first, Rotation::cur()); + let is_last = cs.query_fixed(is_last, Rotation::cur()); + + let acc_next = cs.query_advice(acc, Rotation::next()); + let acc = cs.query_advice(acc.clone(), Rotation::cur()); + + let sum = terms.iter().fold(0.expr(), |acc, term| acc + term.clone()); + let next_or_zero = (1.expr() - is_last) * acc_next; + + [ + // If is_first, then initialize: `acc = ∑terms`. + is_first * (acc.clone() - sum.clone()), + // If not is_last, then accumulate: `acc_next = acc + ∑terms` + // If is_last, then the final sum is zero: `0 = acc + ∑terms` + enabled * (next_or_zero - (acc.clone() + sum)), + ] + }); + + Self { + enabled, + is_first, + is_last, + acc, + _marker: PhantomData, + } } } /// BusBuilder -#[derive(Default)] pub struct BusBuilder { + rand: Expression, terms: Vec>, } impl BusBuilder { + /// Create a new bus. + pub fn new(rand: Expression) -> Self { + Self { + rand, + terms: vec![], + } + } + /// Connect a port to the bus. pub fn connect_port>(&mut self, meta: &mut ConstraintSystem, port: &BP) { - let term = port.create_term(meta); + let term = port.create_term(meta, self.rand.clone()); self.terms.push(term); } @@ -43,93 +85,189 @@ impl BusBuilder { } } +/// A bus operation. +pub struct BusOp { + count: Expression, + value: Expression, +} + +impl BusOp { + /// Put an item. The expression evaluates to 0 or the number of copies. + pub fn put(count: Expression, value: Expression) -> Self { + Self { count, value } + } + + /// Take an item. The expression evaluates to 0 or 1. + pub fn take(count: Expression, value: Expression) -> Self { + Self { + count: -count, + value, + } + } + + /// The expression of the count of items to put or take. + pub fn count(&self) -> Expression { + self.count.clone() + } + + /// The expression of the value to put or take. + pub fn value(&self) -> Expression { + self.value.clone() + } +} + /// BusPort prepares a term to be added to the bus. pub trait BusPort { /// The term to add to the bus. This expression must be fully constrained on all rows. - fn create_term(&self, meta: &mut ConstraintSystem) -> Expression; + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression; } -/// BusPort to access the bus. It manages its own helper column. +// ---------------------------------------- + +/// An access to the bus. pub struct BusPortSingle { - helper: Column, - multi: BusPortMulti, + helper: Expression, + op: BusOp, } impl BusPortSingle { /// Create a new bus port with a single access. - pub fn put(meta: &mut ConstraintSystem, op: BusOp, value: Expression) -> Self { - let helper = meta.advice_column(); - let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); + pub fn new(helper: Expression, op: BusOp) -> Self { + Self { helper, op } + } +} - let multi = BusPortMulti::new(helper_expr, vec![(op, value.clone())]); +impl BusPort for BusPortSingle { + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { + let term = self.op.count() * self.helper.clone(); + + meta.create_gate("bus access", |_| { + // Verify that `term = count / (rand + value)`. + // + // If `count = 0`, then `term = 0` by definition. In that case, the helper cell is not + // constrained, so it can be used for something else. + [term.clone() * (rand + self.op.value()) - self.op.count()] + }); - Self { helper, multi } + term } } -impl BusPort for BusPortSingle { - fn create_term(&self, meta: &mut ConstraintSystem) -> Expression { - self.multi.create_term(meta) +// ---------------------------------------- + +/// A bus port with two accesses. BusPortDual uses only one witness cell, however the degree of +/// input expressions is more limited than with BusPortSingle. +pub struct BusPortDual { + helper: Expression, + ops: [BusOp; 2], +} + +impl BusPortDual { + /// Create a new bus port with two accesses. + pub fn new(helper: Expression, ops: [BusOp; 2]) -> Self { + Self { helper, ops } } } -/// A bus operation. -pub enum BusOp { - /// Put an item. The expression evaluates to 0 or the number of copies. - Put(Expression), - /// Take an item. The expression evaluates to 0 or 1. - Take(Expression), +impl BusPort for BusPortDual { + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { + let rv_0 = rand.clone() + self.ops[0].value(); + let rv_1 = rand.clone() + self.ops[1].value(); + + // With witness: helper = 1 / rv_0 / rv_1 + + // term_0 = count_0 * helper * rv_1 + let count_0 = self.ops[0].count(); + let term_0 = count_0.clone() * self.helper.clone() * rv_1.clone(); + + // term_1 = count_1 * helper * rv_0 + let count_1 = self.ops[1].count(); + let term_1 = count_1.clone() * self.helper.clone() * rv_0.clone(); + + // Verify that: + // term_0 == count_0 / rv_0 + // term_0 * rv_0 - count_0 == 0 + // And: + // term_1 == count_1 / rv_1 + // term_1 * rv_1 - count_1 == 0 + // + // In case both count_0 and count_1 are zero, then the helper cell is not constrained, so it + // can be used for something else. + meta.create_gate("bus access (dual)", |_| { + [ + term_0.clone() * rv_0 - count_0, + term_1.clone() * rv_1 - count_1, + ] + }); + + term_0 + term_1 + } } -impl BusOp { - /// The expression of the count of items to put or take. - pub fn expr(&self) -> Expression { - match self { - BusOp::Put(e) => e.clone(), - BusOp::Take(e) => -e.clone(), - } +// ---------------------------------------- + +/// BusPort to access the bus. It manages its own helper column. +pub struct BusPortColumn { + helper: Column, + multi: BusPortMulti, +} + +impl BusPortColumn { + /// Create a new bus port with a single access. + pub fn put(meta: &mut ConstraintSystem, op: BusOp) -> Self { + let helper = meta.advice_column(); + let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); + + let multi = BusPortMulti::new(helper_expr, vec![op]); + + Self { helper, multi } + } +} + +impl BusPort for BusPortColumn { + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { + self.multi.create_term(meta, rand) } } +// ---------------------------------------- + /// BusPort to access the bus. The most flexible port. The helper cell is provided by the caller. It /// supports multiple put/take accesses, as long as only one is active at a time. pub struct BusPortMulti { helper: Expression, - ops: Vec<(BusOp, Expression)>, + ops: Vec>, } impl BusPortMulti { /// Put one out of several possible items to the bus. /// The operations `ops` must be mutually exclusives (only one `count` is non-zero at a time) /// across all puts and takes. - pub fn put(helper: Expression, ops: Vec<(BusOp, Expression)>) -> Self { + pub fn put(helper: Expression, ops: Vec>) -> Self { BusPortMulti { helper, ops } } /// Create a new bus port. - fn new(helper: Expression, ops: Vec<(BusOp, Expression)>) -> Self { + fn new(helper: Expression, ops: Vec>) -> Self { BusPortMulti { helper, ops } } } impl BusPort for BusPortMulti { - fn create_term(&self, meta: &mut ConstraintSystem) -> Expression { + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { let rand = 1.expr(); // TODO - meta.create_gate("bus access", |meta| { + meta.create_gate("bus access", |_| { self.ops .iter() - .map(|(count, value)| { + .map(|op| { // If count != 0, then helper = 1 / (rand + value) - count.expr() * (self.helper.clone() * (rand.clone() + value.clone()) - 1.expr()) + op.count() * (self.helper.clone() * (rand.clone() + op.value()) - 1.expr()) }) .collect::>() }); - let count_sum = self - .ops - .iter() - .fold(0.expr(), |acc, (count, _)| acc + count.expr()); + let count_sum = self.ops.iter().fold(0.expr(), |acc, op| acc + op.count()); self.helper.clone() * count_sum } @@ -143,21 +281,24 @@ mod tests { circuit::{Layouter, SimpleFloorPlanner, Value}, dev::MockProver, halo2curves::bn256::Fr, - plonk::ConstraintSystem, + plonk::{ConstraintSystem, FirstPhase}, }; #[test] fn test_bus() { let cs = &mut ConstraintSystem::::default(); + cs.advice_column(); // Bypass illogical validation. - let mut bus_builder = BusBuilder::::default(); + let rand = cs.challenge_usable_after(FirstPhase); + let rand_expr = query_expression(cs, |cs| cs.query_challenge(rand)); + let mut bus_builder = BusBuilder::::new(rand_expr); // Circuit 1. - let port = BusPortSingle::put(cs, BusOp::Put(1.expr()), 2.expr()); + let port = BusPortColumn::put(cs, BusOp::put(1.expr(), 2.expr())); bus_builder.connect_port(cs, &port); // Circuit 2. - let port = BusPortSingle::put(cs, BusOp::Put(1.expr()), 2.expr()); + let port = BusPortColumn::put(cs, BusOp::put(1.expr(), 2.expr())); bus_builder.connect_port(cs, &port); // Global bus connection. @@ -165,7 +306,6 @@ mod tests { } } - /* sum( (1 / item) for each value ) == 0 @@ -189,4 +329,4 @@ Bus Check: Circuit 1: term_circuit1 * value == 1 -*/ \ No newline at end of file +*/ From 089fd738a35e81dce1a66af83b8dac6f2820c2ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 19 Sep 2023 14:36:10 +0200 Subject: [PATCH 03/67] bus: organize modules --- gadgets/src/bus.rs | 310 +---------------------------------- gadgets/src/bus/bus_chip.rs | 88 ++++++++++ gadgets/src/bus/bus_multi.rs | 37 +++++ gadgets/src/bus/bus_port.rs | 141 ++++++++++++++++ gadgets/src/bus/tests.rs | 36 ++++ 5 files changed, 310 insertions(+), 302 deletions(-) create mode 100644 gadgets/src/bus/bus_chip.rs create mode 100644 gadgets/src/bus/bus_multi.rs create mode 100644 gadgets/src/bus/bus_port.rs create mode 100644 gadgets/src/bus/tests.rs diff --git a/gadgets/src/bus.rs b/gadgets/src/bus.rs index c8a8efc529..2f9a59df57 100644 --- a/gadgets/src/bus.rs +++ b/gadgets/src/bus.rs @@ -1,310 +1,16 @@ -//! LogUp chip +//! Bus module. -use eth_types::Field; -use halo2_proofs::{ - arithmetic::FieldExt, - circuit::{Region, Value}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Phase, VirtualCells}, - poly::Rotation, -}; -use std::marker::PhantomData; +/// Bus chip that check the integrity of all bus accesses. +pub mod bus_chip; -use crate::util::{query_expression, Expr}; +/// A chip to access the bus. +pub mod bus_port; -/// BusConfig -pub struct BusConfig { - enabled: Column, - is_first: Column, - is_last: Column, - acc: Column, - _marker: PhantomData, -} - -impl BusConfig { - /// Create a new bus. - pub fn new(cs: &mut ConstraintSystem, terms: &[Expression]) -> Self { - let enabled = cs.fixed_column(); - let is_first = cs.fixed_column(); - let is_last = cs.fixed_column(); - let acc = cs.advice_column(); - - cs.create_gate("bus sum check", |cs| { - let enabled = cs.query_fixed(enabled, Rotation::cur()); - let is_first = cs.query_fixed(is_first, Rotation::cur()); - let is_last = cs.query_fixed(is_last, Rotation::cur()); - - let acc_next = cs.query_advice(acc, Rotation::next()); - let acc = cs.query_advice(acc.clone(), Rotation::cur()); - - let sum = terms.iter().fold(0.expr(), |acc, term| acc + term.clone()); - let next_or_zero = (1.expr() - is_last) * acc_next; - - [ - // If is_first, then initialize: `acc = ∑terms`. - is_first * (acc.clone() - sum.clone()), - // If not is_last, then accumulate: `acc_next = acc + ∑terms` - // If is_last, then the final sum is zero: `0 = acc + ∑terms` - enabled * (next_or_zero - (acc.clone() + sum)), - ] - }); - - Self { - enabled, - is_first, - is_last, - acc, - _marker: PhantomData, - } - } -} - -/// BusBuilder -pub struct BusBuilder { - rand: Expression, - terms: Vec>, -} - -impl BusBuilder { - /// Create a new bus. - pub fn new(rand: Expression) -> Self { - Self { - rand, - terms: vec![], - } - } - - /// Connect a port to the bus. - pub fn connect_port>(&mut self, meta: &mut ConstraintSystem, port: &BP) { - let term = port.create_term(meta, self.rand.clone()); - self.terms.push(term); - } - - /// Return the collected terms. - pub fn terms(self) -> Vec> { - self.terms - } -} - -/// A bus operation. -pub struct BusOp { - count: Expression, - value: Expression, -} - -impl BusOp { - /// Put an item. The expression evaluates to 0 or the number of copies. - pub fn put(count: Expression, value: Expression) -> Self { - Self { count, value } - } - - /// Take an item. The expression evaluates to 0 or 1. - pub fn take(count: Expression, value: Expression) -> Self { - Self { - count: -count, - value, - } - } - - /// The expression of the count of items to put or take. - pub fn count(&self) -> Expression { - self.count.clone() - } - - /// The expression of the value to put or take. - pub fn value(&self) -> Expression { - self.value.clone() - } -} - -/// BusPort prepares a term to be added to the bus. -pub trait BusPort { - /// The term to add to the bus. This expression must be fully constrained on all rows. - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression; -} - -// ---------------------------------------- - -/// An access to the bus. -pub struct BusPortSingle { - helper: Expression, - op: BusOp, -} - -impl BusPortSingle { - /// Create a new bus port with a single access. - pub fn new(helper: Expression, op: BusOp) -> Self { - Self { helper, op } - } -} - -impl BusPort for BusPortSingle { - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { - let term = self.op.count() * self.helper.clone(); - - meta.create_gate("bus access", |_| { - // Verify that `term = count / (rand + value)`. - // - // If `count = 0`, then `term = 0` by definition. In that case, the helper cell is not - // constrained, so it can be used for something else. - [term.clone() * (rand + self.op.value()) - self.op.count()] - }); - - term - } -} - -// ---------------------------------------- - -/// A bus port with two accesses. BusPortDual uses only one witness cell, however the degree of -/// input expressions is more limited than with BusPortSingle. -pub struct BusPortDual { - helper: Expression, - ops: [BusOp; 2], -} - -impl BusPortDual { - /// Create a new bus port with two accesses. - pub fn new(helper: Expression, ops: [BusOp; 2]) -> Self { - Self { helper, ops } - } -} - -impl BusPort for BusPortDual { - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { - let rv_0 = rand.clone() + self.ops[0].value(); - let rv_1 = rand.clone() + self.ops[1].value(); - - // With witness: helper = 1 / rv_0 / rv_1 - - // term_0 = count_0 * helper * rv_1 - let count_0 = self.ops[0].count(); - let term_0 = count_0.clone() * self.helper.clone() * rv_1.clone(); - - // term_1 = count_1 * helper * rv_0 - let count_1 = self.ops[1].count(); - let term_1 = count_1.clone() * self.helper.clone() * rv_0.clone(); - - // Verify that: - // term_0 == count_0 / rv_0 - // term_0 * rv_0 - count_0 == 0 - // And: - // term_1 == count_1 / rv_1 - // term_1 * rv_1 - count_1 == 0 - // - // In case both count_0 and count_1 are zero, then the helper cell is not constrained, so it - // can be used for something else. - meta.create_gate("bus access (dual)", |_| { - [ - term_0.clone() * rv_0 - count_0, - term_1.clone() * rv_1 - count_1, - ] - }); - - term_0 + term_1 - } -} - -// ---------------------------------------- - -/// BusPort to access the bus. It manages its own helper column. -pub struct BusPortColumn { - helper: Column, - multi: BusPortMulti, -} - -impl BusPortColumn { - /// Create a new bus port with a single access. - pub fn put(meta: &mut ConstraintSystem, op: BusOp) -> Self { - let helper = meta.advice_column(); - let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); - - let multi = BusPortMulti::new(helper_expr, vec![op]); - - Self { helper, multi } - } -} - -impl BusPort for BusPortColumn { - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { - self.multi.create_term(meta, rand) - } -} - -// ---------------------------------------- - -/// BusPort to access the bus. The most flexible port. The helper cell is provided by the caller. It -/// supports multiple put/take accesses, as long as only one is active at a time. -pub struct BusPortMulti { - helper: Expression, - ops: Vec>, -} - -impl BusPortMulti { - /// Put one out of several possible items to the bus. - /// The operations `ops` must be mutually exclusives (only one `count` is non-zero at a time) - /// across all puts and takes. - pub fn put(helper: Expression, ops: Vec>) -> Self { - BusPortMulti { helper, ops } - } - - /// Create a new bus port. - fn new(helper: Expression, ops: Vec>) -> Self { - BusPortMulti { helper, ops } - } -} - -impl BusPort for BusPortMulti { - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { - let rand = 1.expr(); // TODO - - meta.create_gate("bus access", |_| { - self.ops - .iter() - .map(|op| { - // If count != 0, then helper = 1 / (rand + value) - op.count() * (self.helper.clone() * (rand.clone() + op.value()) - 1.expr()) - }) - .collect::>() - }); - - let count_sum = self.ops.iter().fold(0.expr(), |acc, op| acc + op.count()); - - self.helper.clone() * count_sum - } -} +/// A variant of bus port for mutually exclusive accesses. +pub mod bus_multi; #[cfg(test)] -mod tests { - use super::*; - use halo2_proofs::{ - arithmetic::FieldExt, - circuit::{Layouter, SimpleFloorPlanner, Value}, - dev::MockProver, - halo2curves::bn256::Fr, - plonk::{ConstraintSystem, FirstPhase}, - }; - - #[test] - fn test_bus() { - let cs = &mut ConstraintSystem::::default(); - cs.advice_column(); // Bypass illogical validation. - - let rand = cs.challenge_usable_after(FirstPhase); - let rand_expr = query_expression(cs, |cs| cs.query_challenge(rand)); - let mut bus_builder = BusBuilder::::new(rand_expr); - - // Circuit 1. - let port = BusPortColumn::put(cs, BusOp::put(1.expr(), 2.expr())); - bus_builder.connect_port(cs, &port); - - // Circuit 2. - let port = BusPortColumn::put(cs, BusOp::put(1.expr(), 2.expr())); - bus_builder.connect_port(cs, &port); - - // Global bus connection. - let bus_check = BusConfig::new(cs, &bus_builder.terms()); - } -} +mod tests; /* diff --git a/gadgets/src/bus/bus_chip.rs b/gadgets/src/bus/bus_chip.rs new file mode 100644 index 0000000000..33dbb533d9 --- /dev/null +++ b/gadgets/src/bus/bus_chip.rs @@ -0,0 +1,88 @@ +use halo2_proofs::{ + arithmetic::FieldExt, + plonk::{Advice, Column, ConstraintSystem, Expression, Fixed}, + poly::Rotation, +}; +use std::marker::PhantomData; + +use crate::util::Expr; + +/// BusConfig +pub struct BusConfig { + enabled: Column, + is_first: Column, + is_last: Column, + acc: Column, + _marker: PhantomData, +} + +impl BusConfig { + /// Create a new bus. + pub fn new(cs: &mut ConstraintSystem, terms: &[Expression]) -> Self { + let enabled = cs.fixed_column(); + let is_first = cs.fixed_column(); + let is_last = cs.fixed_column(); + let acc = cs.advice_column(); + + cs.create_gate("bus sum check", |cs| { + let enabled = cs.query_fixed(enabled, Rotation::cur()); + let is_first = cs.query_fixed(is_first, Rotation::cur()); + let is_last = cs.query_fixed(is_last, Rotation::cur()); + + let acc_next = cs.query_advice(acc, Rotation::next()); + let acc = cs.query_advice(acc.clone(), Rotation::cur()); + + let sum = terms.iter().fold(0.expr(), |acc, term| acc + term.clone()); + let next_or_zero = (1.expr() - is_last) * acc_next; + + [ + // If is_first, then initialize: `acc = ∑terms`. + is_first * (acc.clone() - sum.clone()), + // If not is_last, then accumulate: `acc_next = acc + ∑terms` + // If is_last, then the final sum is zero: `0 = acc + ∑terms` + enabled * (next_or_zero - (acc.clone() + sum)), + ] + }); + + Self { + enabled, + is_first, + is_last, + acc, + _marker: PhantomData, + } + } +} + +/// BusBuilder +pub struct BusBuilder { + rand: Expression, + terms: Vec>, +} + +impl BusBuilder { + /// Create a new bus. + pub fn new(rand: Expression) -> Self { + Self { + rand, + terms: vec![], + } + } + + /// Connect a port to the bus. + pub fn connect_port>(&mut self, meta: &mut ConstraintSystem, port: &BP) { + let term = port.create_term(meta, self.rand.clone()); + self.terms.push(term); + } + + /// Return the collected terms. + pub fn terms(self) -> Vec> { + self.terms + } +} + +/// BusPort prepares a term to be added to the bus. +pub trait BusPort { + /// The term to add to the bus. This expression must be fully constrained on all rows. + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression; +} diff --git a/gadgets/src/bus/bus_multi.rs b/gadgets/src/bus/bus_multi.rs new file mode 100644 index 0000000000..e4f65e626e --- /dev/null +++ b/gadgets/src/bus/bus_multi.rs @@ -0,0 +1,37 @@ +use super::{ + bus_chip::BusPort, + bus_port::{BusOp, BusPortSingle}, +}; +use crate::util::Expr; +use halo2_proofs::{ + arithmetic::FieldExt, + plonk::{ConstraintSystem, Expression}, +}; + +/// BusPort to access the bus. The most flexible port. The helper cell is provided by the caller. It +/// supports multiple put/take accesses, as long as only one is active at a time. +pub struct BusPortMulti { + helper: Expression, + ops: Vec>, +} + +impl BusPortMulti { + /// Put one out of several possible items to the bus. + /// The operations `ops` must be mutually exclusives (only one `count` is non-zero at a time) + /// across all puts and takes. + pub fn put(helper: Expression, ops: Vec>) -> Self { + BusPortMulti { helper, ops } + } +} + +impl BusPort for BusPortMulti { + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { + self.ops + .iter() + .map(|op| { + BusPortSingle::new(self.helper.clone(), op.clone()).create_term(meta, rand.clone()) + }) + .reduce(|acc, term| acc + term) + .unwrap_or(0.expr()) + } +} diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs new file mode 100644 index 0000000000..d2cdc6da83 --- /dev/null +++ b/gadgets/src/bus/bus_port.rs @@ -0,0 +1,141 @@ +use super::bus_chip::BusPort; +use crate::util::query_expression; +use halo2_proofs::{ + arithmetic::FieldExt, + plonk::{Advice, Column, ConstraintSystem, Expression}, + poly::Rotation, +}; + +/// A bus operation. +#[derive(Clone)] +pub struct BusOp { + count: Expression, + value: Expression, +} + +impl BusOp { + /// Put an item. The expression evaluates to 0 or the number of copies. + pub fn put(count: Expression, value: Expression) -> Self { + Self { count, value } + } + + /// Take an item. The expression evaluates to 0 or 1. + pub fn take(count: Expression, value: Expression) -> Self { + Self { + count: -count, + value, + } + } + + /// The expression of the count of items to put or take. + pub fn count(&self) -> Expression { + self.count.clone() + } + + /// The expression of the value to put or take. + pub fn value(&self) -> Expression { + self.value.clone() + } +} + +/// A chip to access to the bus. +pub struct BusPortSingle { + helper: Expression, + op: BusOp, +} + +impl BusPortSingle { + /// Create a new bus port with a single access. + pub fn new(helper: Expression, op: BusOp) -> Self { + Self { helper, op } + } +} + +impl BusPort for BusPortSingle { + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { + let term = self.op.count() * self.helper.clone(); + + meta.create_gate("bus access", |_| { + // Verify that `term = count / (rand + value)`. + // + // If `count = 0`, then `term = 0` by definition. In that case, the helper cell is not + // constrained, so it can be used for something else. + [term.clone() * (rand + self.op.value()) - self.op.count()] + }); + + term + } +} + +/// A chip with two accesses to the bus. BusPortDual uses only one witness cell, however the +/// degree of input expressions is more limited than with BusPortSingle. +pub struct BusPortDual { + helper: Expression, + ops: [BusOp; 2], +} + +impl BusPortDual { + /// Create a new bus port with two accesses. + pub fn new(helper: Expression, ops: [BusOp; 2]) -> Self { + Self { helper, ops } + } +} + +impl BusPort for BusPortDual { + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { + let rv_0 = rand.clone() + self.ops[0].value(); + let rv_1 = rand.clone() + self.ops[1].value(); + + // With witness: helper = 1 / rv_0 / rv_1 + + // term_0 = count_0 * helper * rv_1 + let count_0 = self.ops[0].count(); + let term_0 = count_0.clone() * self.helper.clone() * rv_1.clone(); + + // term_1 = count_1 * helper * rv_0 + let count_1 = self.ops[1].count(); + let term_1 = count_1.clone() * self.helper.clone() * rv_0.clone(); + + // Verify that: + // term_0 == count_0 / rv_0 + // term_0 * rv_0 - count_0 == 0 + // And: + // term_1 == count_1 / rv_1 + // term_1 * rv_1 - count_1 == 0 + // + // In case both count_0 and count_1 are zero, then the helper cell is not constrained, so it + // can be used for something else. + meta.create_gate("bus access (dual)", |_| { + [ + term_0.clone() * rv_0 - count_0, + term_1.clone() * rv_1 - count_1, + ] + }); + + term_0 + term_1 + } +} + +/// A chip to access the bus. It manages its own helper column. +pub struct BusPortColumn { + helper: Column, + port: BusPortSingle, +} + +impl BusPortColumn { + /// Create a new bus port with a single access. + pub fn put(meta: &mut ConstraintSystem, op: BusOp) -> Self { + let helper = meta.advice_column(); + let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); + + let port = BusPortSingle::new(helper_expr, op); + + Self { helper, port } + } +} + +impl BusPort for BusPortColumn { + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { + self.port.create_term(meta, rand) + } +} diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs new file mode 100644 index 0000000000..82e6ec6136 --- /dev/null +++ b/gadgets/src/bus/tests.rs @@ -0,0 +1,36 @@ +use crate::util::{query_expression, Expr}; +use eth_types::Field; +use halo2_proofs::{ + arithmetic::FieldExt, + circuit::{Layouter, Region, SimpleFloorPlanner, Value}, + dev::MockProver, + halo2curves::bn256::Fr, + plonk::{ + Advice, Column, ConstraintSystem, Error, Expression, FirstPhase, Fixed, Phase, VirtualCells, + }, + poly::Rotation, +}; +use std::marker::PhantomData; + +use super::{bus_chip::*, bus_multi::*, bus_port::*}; + +#[test] +fn test_bus() { + let cs = &mut ConstraintSystem::::default(); + cs.advice_column(); // Bypass illogical validation. + + let rand = cs.challenge_usable_after(FirstPhase); + let rand_expr = query_expression(cs, |cs| cs.query_challenge(rand)); + let mut bus_builder = BusBuilder::::new(rand_expr); + + // Circuit 1. + let port = BusPortColumn::put(cs, BusOp::put(1.expr(), 2.expr())); + bus_builder.connect_port(cs, &port); + + // Circuit 2. + let port = BusPortColumn::put(cs, BusOp::put(1.expr(), 2.expr())); + bus_builder.connect_port(cs, &port); + + // Global bus connection. + let bus_check = BusConfig::new(cs, &bus_builder.terms()); +} From 90e2e5354b4556758b7d379e7d6f4a7637e29295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 19 Sep 2023 14:52:43 +0200 Subject: [PATCH 04/67] bus: type safety with BusTerm --- gadgets/src/bus/bus_chip.rs | 29 ++++++++++++++++++++++++----- gadgets/src/bus/bus_multi.rs | 15 ++++++++++----- gadgets/src/bus/bus_port.rs | 12 ++++++------ 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/gadgets/src/bus/bus_chip.rs b/gadgets/src/bus/bus_chip.rs index 33dbb533d9..8817752f30 100644 --- a/gadgets/src/bus/bus_chip.rs +++ b/gadgets/src/bus/bus_chip.rs @@ -7,6 +7,23 @@ use std::marker::PhantomData; use crate::util::Expr; +/// A term of the bus sum check. +#[derive(Clone)] +pub struct BusTerm(Expression); + +impl BusTerm { + /// Wrap an expression to indicate that it was properly constructed as a bus term. + pub fn verified(term: Expression) -> Self { + Self(term) + } +} + +impl Expr for BusTerm { + fn expr(&self) -> Expression { + self.0.clone() + } +} + /// BusConfig pub struct BusConfig { enabled: Column, @@ -18,7 +35,7 @@ pub struct BusConfig { impl BusConfig { /// Create a new bus. - pub fn new(cs: &mut ConstraintSystem, terms: &[Expression]) -> Self { + pub fn new(cs: &mut ConstraintSystem, terms: &[BusTerm]) -> Self { let enabled = cs.fixed_column(); let is_first = cs.fixed_column(); let is_last = cs.fixed_column(); @@ -32,7 +49,9 @@ impl BusConfig { let acc_next = cs.query_advice(acc, Rotation::next()); let acc = cs.query_advice(acc.clone(), Rotation::cur()); - let sum = terms.iter().fold(0.expr(), |acc, term| acc + term.clone()); + let sum = terms + .iter() + .fold(0.expr(), |acc, term| acc + term.0.clone()); let next_or_zero = (1.expr() - is_last) * acc_next; [ @@ -57,7 +76,7 @@ impl BusConfig { /// BusBuilder pub struct BusBuilder { rand: Expression, - terms: Vec>, + terms: Vec>, } impl BusBuilder { @@ -76,7 +95,7 @@ impl BusBuilder { } /// Return the collected terms. - pub fn terms(self) -> Vec> { + pub fn terms(self) -> Vec> { self.terms } } @@ -84,5 +103,5 @@ impl BusBuilder { /// BusPort prepares a term to be added to the bus. pub trait BusPort { /// The term to add to the bus. This expression must be fully constrained on all rows. - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression; + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> BusTerm; } diff --git a/gadgets/src/bus/bus_multi.rs b/gadgets/src/bus/bus_multi.rs index e4f65e626e..c32df3f26e 100644 --- a/gadgets/src/bus/bus_multi.rs +++ b/gadgets/src/bus/bus_multi.rs @@ -1,5 +1,5 @@ use super::{ - bus_chip::BusPort, + bus_chip::{BusPort, BusTerm}, bus_port::{BusOp, BusPortSingle}, }; use crate::util::Expr; @@ -25,13 +25,18 @@ impl BusPortMulti { } impl BusPort for BusPortMulti { - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { - self.ops + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> BusTerm { + let term = self + .ops .iter() .map(|op| { - BusPortSingle::new(self.helper.clone(), op.clone()).create_term(meta, rand.clone()) + BusPortSingle::new(self.helper.clone(), op.clone()) + .create_term(meta, rand.clone()) + .expr() }) .reduce(|acc, term| acc + term) - .unwrap_or(0.expr()) + .unwrap_or(0.expr()); + + BusTerm::verified(term) } } diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index d2cdc6da83..714d8c4015 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,4 +1,4 @@ -use super::bus_chip::BusPort; +use super::bus_chip::{BusPort, BusTerm}; use crate::util::query_expression; use halo2_proofs::{ arithmetic::FieldExt, @@ -52,7 +52,7 @@ impl BusPortSingle { } impl BusPort for BusPortSingle { - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> BusTerm { let term = self.op.count() * self.helper.clone(); meta.create_gate("bus access", |_| { @@ -63,7 +63,7 @@ impl BusPort for BusPortSingle { [term.clone() * (rand + self.op.value()) - self.op.count()] }); - term + BusTerm::verified(term) } } @@ -82,7 +82,7 @@ impl BusPortDual { } impl BusPort for BusPortDual { - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> BusTerm { let rv_0 = rand.clone() + self.ops[0].value(); let rv_1 = rand.clone() + self.ops[1].value(); @@ -112,7 +112,7 @@ impl BusPort for BusPortDual { ] }); - term_0 + term_1 + BusTerm::verified(term_0 + term_1) } } @@ -135,7 +135,7 @@ impl BusPortColumn { } impl BusPort for BusPortColumn { - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> Expression { + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> BusTerm { self.port.create_term(meta, rand) } } From 4e6b81fc7da64a02c4d63a6ec0172bd04689387d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 19 Sep 2023 15:01:38 +0200 Subject: [PATCH 05/67] bus: calculate witness --- gadgets/src/bus/bus_port.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 714d8c4015..9db30175ba 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -2,6 +2,7 @@ use super::bus_chip::{BusPort, BusTerm}; use crate::util::query_expression; use halo2_proofs::{ arithmetic::FieldExt, + circuit::Value, plonk::{Advice, Column, ConstraintSystem, Expression}, poly::Rotation, }; @@ -49,6 +50,11 @@ impl BusPortSingle { pub fn new(helper: Expression, op: BusOp) -> Self { Self { helper, op } } + + /// Return the witness that must be assigned to the helper cell. + pub fn helper_witness(value: F, rand: Value) -> Value { + rand.map(|rand| (rand + value).invert().unwrap_or(F::zero())) + } } impl BusPort for BusPortSingle { @@ -58,6 +64,8 @@ impl BusPort for BusPortSingle { meta.create_gate("bus access", |_| { // Verify that `term = count / (rand + value)`. // + // With witness: helper = 1 / (rand + value) + // // If `count = 0`, then `term = 0` by definition. In that case, the helper cell is not // constrained, so it can be used for something else. [term.clone() * (rand + self.op.value()) - self.op.count()] @@ -79,6 +87,15 @@ impl BusPortDual { pub fn new(helper: Expression, ops: [BusOp; 2]) -> Self { Self { helper, ops } } + + /// Return the witness that must be assigned to the helper cell. + pub fn helper_witness(values: [F; 2], rand: Value) -> Value { + rand.map(|rand| { + ((rand + values[0]) * (rand + values[1])) + .invert() + .unwrap_or(F::zero()) + }) + } } impl BusPort for BusPortDual { @@ -132,6 +149,8 @@ impl BusPortColumn { Self { helper, port } } + + // TODO: assign function. } impl BusPort for BusPortColumn { From 058aa4d8ed361a2c218e568b977fd4a8adc2da48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 19 Sep 2023 16:56:44 +0200 Subject: [PATCH 06/67] bus: witness generation of the bus accumulator --- gadgets/src/bus/bus_chip.rs | 60 ++++++++++++--- gadgets/src/bus/bus_port.rs | 35 +++++---- gadgets/src/bus/tests.rs | 144 ++++++++++++++++++++++++++++++++---- 3 files changed, 203 insertions(+), 36 deletions(-) diff --git a/gadgets/src/bus/bus_chip.rs b/gadgets/src/bus/bus_chip.rs index 8817752f30..67304409f0 100644 --- a/gadgets/src/bus/bus_chip.rs +++ b/gadgets/src/bus/bus_chip.rs @@ -1,6 +1,7 @@ use halo2_proofs::{ arithmetic::FieldExt, - plonk::{Advice, Column, ConstraintSystem, Expression, Fixed}, + circuit::{Region, Value}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, SecondPhase}, poly::Rotation, }; use std::marker::PhantomData; @@ -25,21 +26,21 @@ impl Expr for BusTerm { } /// BusConfig -pub struct BusConfig { +#[derive(Clone)] +pub struct BusConfig { enabled: Column, is_first: Column, is_last: Column, acc: Column, - _marker: PhantomData, } -impl BusConfig { +impl BusConfig { /// Create a new bus. - pub fn new(cs: &mut ConstraintSystem, terms: &[BusTerm]) -> Self { + pub fn new(cs: &mut ConstraintSystem, terms: &[BusTerm]) -> Self { let enabled = cs.fixed_column(); let is_first = cs.fixed_column(); let is_last = cs.fixed_column(); - let acc = cs.advice_column(); + let acc = cs.advice_column_in(SecondPhase); cs.create_gate("bus sum check", |cs| { let enabled = cs.query_fixed(enabled, Rotation::cur()); @@ -55,8 +56,8 @@ impl BusConfig { let next_or_zero = (1.expr() - is_last) * acc_next; [ - // If is_first, then initialize: `acc = ∑terms`. - is_first * (acc.clone() - sum.clone()), + // If is_first, then initialize: `acc = 0`. + is_first * acc.clone(), // If not is_last, then accumulate: `acc_next = acc + ∑terms` // If is_last, then the final sum is zero: `0 = acc + ∑terms` enabled * (next_or_zero - (acc.clone() + sum)), @@ -68,9 +69,50 @@ impl BusConfig { is_first, is_last, acc, - _marker: PhantomData, } } + + /// Assign the helper witness. + pub fn assign( + &self, + region: &mut Region<'_, F>, + n_rows: usize, + terms: &[Value], + ) -> Result<(), Error> { + region.assign_fixed( + || "Bus_is_first", + self.is_first, + 0, + || Value::known(F::one()), + )?; + + region.assign_fixed( + || "Bus_is_last", + self.is_last, + n_rows - 1, + || Value::known(F::one()), + )?; + + for offset in 0..n_rows { + region.assign_fixed( + || "Bus_enable", + self.enabled, + offset, + || Value::known(F::one()), + )?; + } + + let mut acc = Value::known(F::zero()); + + for offset in 0..n_rows { + region.assign_advice(|| "Bus_acc", self.acc, offset, || acc)?; + + if let Some(term) = terms.get(offset) { + acc = acc + term; + } + } + Ok(()) + } } /// BusBuilder diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 9db30175ba..a359aea177 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -2,8 +2,8 @@ use super::bus_chip::{BusPort, BusTerm}; use crate::util::query_expression; use halo2_proofs::{ arithmetic::FieldExt, - circuit::Value, - plonk::{Advice, Column, ConstraintSystem, Expression}, + circuit::{Region, Value}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, SecondPhase}, poly::Rotation, }; @@ -40,6 +40,7 @@ impl BusOp { } /// A chip to access to the bus. +#[derive(Clone)] pub struct BusPortSingle { helper: Expression, op: BusOp, @@ -52,8 +53,8 @@ impl BusPortSingle { } /// Return the witness that must be assigned to the helper cell. - pub fn helper_witness(value: F, rand: Value) -> Value { - rand.map(|rand| (rand + value).invert().unwrap_or(F::zero())) + pub fn helper_witness(value: Value, rand: Value) -> Value { + (rand + value).map(|x| x.invert().unwrap_or(F::zero())) } } @@ -89,12 +90,8 @@ impl BusPortDual { } /// Return the witness that must be assigned to the helper cell. - pub fn helper_witness(values: [F; 2], rand: Value) -> Value { - rand.map(|rand| { - ((rand + values[0]) * (rand + values[1])) - .invert() - .unwrap_or(F::zero()) - }) + pub fn helper_witness(values: [Value; 2], rand: Value) -> Value { + ((rand + values[0]) * (rand + values[1])).map(|x| x.invert().unwrap_or(F::zero())) } } @@ -134,6 +131,7 @@ impl BusPort for BusPortDual { } /// A chip to access the bus. It manages its own helper column. +#[derive(Clone)] pub struct BusPortColumn { helper: Column, port: BusPortSingle, @@ -141,8 +139,8 @@ pub struct BusPortColumn { impl BusPortColumn { /// Create a new bus port with a single access. - pub fn put(meta: &mut ConstraintSystem, op: BusOp) -> Self { - let helper = meta.advice_column(); + pub fn new(meta: &mut ConstraintSystem, op: BusOp) -> Self { + let helper = meta.advice_column_in(SecondPhase); let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); let port = BusPortSingle::new(helper_expr, op); @@ -150,7 +148,18 @@ impl BusPortColumn { Self { helper, port } } - // TODO: assign function. + /// Assign the helper witness. + pub fn assign( + &self, + region: &mut Region<'_, F>, + offset: usize, + value: Value, + rand: Value, + ) -> Result, Error> { + let helper = BusPortSingle::helper_witness(value, rand); + region.assign_advice(|| "BusPort_helper", self.helper, offset, || helper)?; + Ok(helper) + } } impl BusPort for BusPortColumn { diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 82e6ec6136..44cd56f890 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -6,7 +6,8 @@ use halo2_proofs::{ dev::MockProver, halo2curves::bn256::Fr, plonk::{ - Advice, Column, ConstraintSystem, Error, Expression, FirstPhase, Fixed, Phase, VirtualCells, + Advice, Challenge, Circuit, Column, ConstraintSystem, Error, Expression, FirstPhase, Fixed, + Phase, VirtualCells, }, poly::Rotation, }; @@ -16,21 +17,136 @@ use super::{bus_chip::*, bus_multi::*, bus_port::*}; #[test] fn test_bus() { - let cs = &mut ConstraintSystem::::default(); - cs.advice_column(); // Bypass illogical validation. + test_circuit(); +} + +#[derive(Clone)] +struct TestCircuitConfig { + enabled: Column, + count1: Column, + port1: BusPortColumn, + count2: Column, + port2: BusPortColumn, + bus_check: BusConfig, + rand: Challenge, + _marker: PhantomData, +} + +#[derive(Default, Clone)] +struct TestCircuit { + n_rows: usize, + _marker: PhantomData, +} + +impl Circuit for TestCircuit { + type Config = TestCircuitConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(cs: &mut ConstraintSystem) -> Self::Config { + cs.advice_column(); // Bypass illogical validation. + + let enabled = cs.fixed_column(); + let enabled_expr = query_expression(cs, |cs| cs.query_fixed(enabled, Rotation::cur())); + + let rand = cs.challenge_usable_after(FirstPhase); + let rand_expr = query_expression(cs, |cs| cs.query_challenge(rand)); + let mut bus_builder = BusBuilder::::new(rand_expr); + + let value = 2.expr(); - let rand = cs.challenge_usable_after(FirstPhase); - let rand_expr = query_expression(cs, |cs| cs.query_challenge(rand)); - let mut bus_builder = BusBuilder::::new(rand_expr); + // Circuit 1. + let count1 = cs.advice_column(); + let count1_expr = query_expression(cs, |cs| cs.query_advice(count1, Rotation::cur())); + let port1 = BusPortColumn::new( + cs, + BusOp::put(enabled_expr.clone() * count1_expr, value.clone()), + ); + bus_builder.connect_port(cs, &port1); - // Circuit 1. - let port = BusPortColumn::put(cs, BusOp::put(1.expr(), 2.expr())); - bus_builder.connect_port(cs, &port); + // Circuit 2. + let count2 = cs.advice_column(); + let count2_expr = query_expression(cs, |cs| cs.query_advice(count2, Rotation::cur())); + let port2 = BusPortColumn::new(cs, BusOp::take(enabled_expr * count2_expr, value)); + bus_builder.connect_port(cs, &port2); - // Circuit 2. - let port = BusPortColumn::put(cs, BusOp::put(1.expr(), 2.expr())); - bus_builder.connect_port(cs, &port); + // Global bus connection. + let bus_check = BusConfig::new(cs, &bus_builder.terms()); + + TestCircuitConfig { + enabled, + bus_check, + count1, + port1, + count2, + port2, + rand, + _marker: PhantomData, + } + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let rand = layouter.get_challenge(config.rand); + + layouter.assign_region( + || "witness", + |mut region| { + let value = Value::known(F::from(2)); + + for offset in 0..self.n_rows { + region.assign_fixed( + || "Port_enable", + config.enabled, + offset, + || Value::known(F::one()), + )?; + } + + let mut terms = vec![Value::known(F::zero()); self.n_rows]; + + // Circuit 1. + let off1 = 1; + region.assign_advice( + || "count1", + config.count1, + off1, + || Value::known(F::one()), + )?; + let h1 = config.port1.assign(&mut region, off1, value, rand)?; + terms[off1] = terms[off1] + h1; + + // Circuit 2. + let off2 = 3; + region.assign_advice( + || "count2", + config.count2, + off2, + || Value::known(F::one()), + )?; + let h2 = config.port2.assign(&mut region, off2, value, rand)?; + terms[off2] = terms[off2] - h2; + + config.bus_check.assign(&mut region, self.n_rows, &terms)?; + + Ok(()) + }, + ) + } +} - // Global bus connection. - let bus_check = BusConfig::new(cs, &bus_builder.terms()); +fn test_circuit() { + let circuit = TestCircuit:: { + n_rows: 10, + _marker: PhantomData, + }; + let k = 10; + let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); + prover.assert_satisfied_par() } From 59a703940782e61029d76223828944136654157e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 19 Sep 2023 18:04:43 +0200 Subject: [PATCH 07/67] bus: BusAssigner --- gadgets/src/bus.rs | 3 ++ gadgets/src/bus/bus_builder.rs | 83 ++++++++++++++++++++++++++++++++++ gadgets/src/bus/bus_chip.rs | 55 +++++----------------- gadgets/src/bus/bus_multi.rs | 3 +- gadgets/src/bus/bus_port.rs | 10 ++-- gadgets/src/bus/tests.rs | 82 ++++++++++++++------------------- 6 files changed, 140 insertions(+), 96 deletions(-) create mode 100644 gadgets/src/bus/bus_builder.rs diff --git a/gadgets/src/bus.rs b/gadgets/src/bus.rs index 2f9a59df57..954ef1a31d 100644 --- a/gadgets/src/bus.rs +++ b/gadgets/src/bus.rs @@ -3,6 +3,9 @@ /// Bus chip that check the integrity of all bus accesses. pub mod bus_chip; +/// The bus builder collects all the ports into a bus. +pub mod bus_builder; + /// A chip to access the bus. pub mod bus_port; diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs new file mode 100644 index 0000000000..83380ee981 --- /dev/null +++ b/gadgets/src/bus/bus_builder.rs @@ -0,0 +1,83 @@ +use halo2_proofs::{ + arithmetic::FieldExt, + circuit::Value, + plonk::{ConstraintSystem, Expression}, +}; + +use super::bus_chip::BusTerm; + +/// BusPort prepares a term to be added to the bus. +pub trait BusPort { + /// The term to add to the bus. This expression must be fully constrained on all rows. + fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> BusTerm; +} + +/// BusBuilder +pub struct BusBuilder { + rand: Expression, + terms: Vec>, +} + +impl BusBuilder { + /// Create a new bus. + pub fn new(rand: Expression) -> Self { + Self { + rand, + terms: vec![], + } + } + + /// Connect a port to the bus. + pub fn connect_port>(&mut self, meta: &mut ConstraintSystem, port: &BP) { + let term = port.create_term(meta, self.rand.clone()); + self.terms.push(term); + } + + /// Return the collected terms. + pub fn build(self) -> Vec> { + self.terms + } +} + +/// BusAssigner +pub struct BusAssigner { + terms: Vec, + all_assigned: bool, +} + +impl BusAssigner { + /// Create a new bus assigner with a maximum number of rows. + pub fn new(n_rows: usize) -> Self { + Self { + terms: vec![F::zero(); n_rows], + all_assigned: true, + } + } + + /// Put a term value to the bus. + pub fn put_term(&mut self, offset: usize, term: Value) { + assert!( + offset < self.terms.len(), + "offset={offset} out of bounds n_rows={}", + self.terms.len() + ); + if term.is_none() { + self.all_assigned = false; + } + term.map(|t| self.terms[offset] += t); + } + + /// Take a term value from the bus. + pub fn take_term(&mut self, offset: usize, term: Value) { + self.put_term(offset, -term); + } + + /// Return the collected terms. + pub fn terms(&self) -> Value<&[F]> { + if self.all_assigned { + Value::known(&self.terms) + } else { + Value::unknown() + } + } +} diff --git a/gadgets/src/bus/bus_chip.rs b/gadgets/src/bus/bus_chip.rs index 67304409f0..b658135545 100644 --- a/gadgets/src/bus/bus_chip.rs +++ b/gadgets/src/bus/bus_chip.rs @@ -1,12 +1,10 @@ +use crate::util::Expr; use halo2_proofs::{ arithmetic::FieldExt, circuit::{Region, Value}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, SecondPhase}, poly::Rotation, }; -use std::marker::PhantomData; - -use crate::util::Expr; /// A term of the bus sum check. #[derive(Clone)] @@ -77,7 +75,7 @@ impl BusConfig { &self, region: &mut Region<'_, F>, n_rows: usize, - terms: &[Value], + terms: Value<&[F]>, ) -> Result<(), Error> { region.assign_fixed( || "Bus_is_first", @@ -102,48 +100,19 @@ impl BusConfig { )?; } - let mut acc = Value::known(F::zero()); + terms.map(|terms| { + let mut acc = F::zero(); - for offset in 0..n_rows { - region.assign_advice(|| "Bus_acc", self.acc, offset, || acc)?; + for offset in 0..n_rows { + region + .assign_advice(|| "Bus_acc", self.acc, offset, || Value::known(acc)) + .unwrap(); - if let Some(term) = terms.get(offset) { - acc = acc + term; + if let Some(term) = terms.get(offset) { + acc = acc + term; + } } - } + }); Ok(()) } } - -/// BusBuilder -pub struct BusBuilder { - rand: Expression, - terms: Vec>, -} - -impl BusBuilder { - /// Create a new bus. - pub fn new(rand: Expression) -> Self { - Self { - rand, - terms: vec![], - } - } - - /// Connect a port to the bus. - pub fn connect_port>(&mut self, meta: &mut ConstraintSystem, port: &BP) { - let term = port.create_term(meta, self.rand.clone()); - self.terms.push(term); - } - - /// Return the collected terms. - pub fn terms(self) -> Vec> { - self.terms - } -} - -/// BusPort prepares a term to be added to the bus. -pub trait BusPort { - /// The term to add to the bus. This expression must be fully constrained on all rows. - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> BusTerm; -} diff --git a/gadgets/src/bus/bus_multi.rs b/gadgets/src/bus/bus_multi.rs index c32df3f26e..cc4e28bce7 100644 --- a/gadgets/src/bus/bus_multi.rs +++ b/gadgets/src/bus/bus_multi.rs @@ -1,5 +1,6 @@ use super::{ - bus_chip::{BusPort, BusTerm}, + bus_builder::BusPort, + bus_chip::BusTerm, bus_port::{BusOp, BusPortSingle}, }; use crate::util::Expr; diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index a359aea177..cc6f04cb9a 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,4 +1,4 @@ -use super::bus_chip::{BusPort, BusTerm}; +use super::{bus_builder::BusPort, bus_chip::BusTerm}; use crate::util::query_expression; use halo2_proofs::{ arithmetic::FieldExt, @@ -130,14 +130,14 @@ impl BusPort for BusPortDual { } } -/// A chip to access the bus. It manages its own helper column. +/// A chip to access the bus. It manages its own helper column and gives one access per row. #[derive(Clone)] -pub struct BusPortColumn { +pub struct BusPortChip { helper: Column, port: BusPortSingle, } -impl BusPortColumn { +impl BusPortChip { /// Create a new bus port with a single access. pub fn new(meta: &mut ConstraintSystem, op: BusOp) -> Self { let helper = meta.advice_column_in(SecondPhase); @@ -162,7 +162,7 @@ impl BusPortColumn { } } -impl BusPort for BusPortColumn { +impl BusPort for BusPortChip { fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> BusTerm { self.port.create_term(meta, rand) } diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 44cd56f890..68e016f3c4 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -1,19 +1,15 @@ use crate::util::{query_expression, Expr}; -use eth_types::Field; use halo2_proofs::{ arithmetic::FieldExt, - circuit::{Layouter, Region, SimpleFloorPlanner, Value}, + circuit::{Layouter, SimpleFloorPlanner, Value}, dev::MockProver, halo2curves::bn256::Fr, - plonk::{ - Advice, Challenge, Circuit, Column, ConstraintSystem, Error, Expression, FirstPhase, Fixed, - Phase, VirtualCells, - }, + plonk::{Advice, Challenge, Circuit, Column, ConstraintSystem, Error, FirstPhase, Fixed}, poly::Rotation, }; use std::marker::PhantomData; -use super::{bus_chip::*, bus_multi::*, bus_port::*}; +use super::{bus_builder::*, bus_chip::*, bus_port::*}; #[test] fn test_bus() { @@ -24,9 +20,8 @@ fn test_bus() { struct TestCircuitConfig { enabled: Column, count1: Column, - port1: BusPortColumn, - count2: Column, - port2: BusPortColumn, + port1: BusPortChip, + port2: BusPortChip, bus_check: BusConfig, rand: Challenge, _marker: PhantomData, @@ -58,30 +53,28 @@ impl Circuit for TestCircuit { let value = 2.expr(); - // Circuit 1. + // Circuit 1 puts values dynamically. let count1 = cs.advice_column(); - let count1_expr = query_expression(cs, |cs| cs.query_advice(count1, Rotation::cur())); - let port1 = BusPortColumn::new( - cs, - BusOp::put(enabled_expr.clone() * count1_expr, value.clone()), - ); + let count1_expr = enabled_expr.clone() + * query_expression(cs, |cs| cs.query_advice(count1, Rotation::cur())); + + let port1 = BusPortChip::new(cs, BusOp::put(count1_expr, value.clone())); bus_builder.connect_port(cs, &port1); - // Circuit 2. - let count2 = cs.advice_column(); - let count2_expr = query_expression(cs, |cs| cs.query_advice(count2, Rotation::cur())); - let port2 = BusPortColumn::new(cs, BusOp::take(enabled_expr * count2_expr, value)); + // Circuit 2 takes one value per row. + let count2_expr = enabled_expr * 1.expr(); + + let port2 = BusPortChip::new(cs, BusOp::take(count2_expr, value)); bus_builder.connect_port(cs, &port2); // Global bus connection. - let bus_check = BusConfig::new(cs, &bus_builder.terms()); + let bus_check = BusConfig::new(cs, &bus_builder.build()); TestCircuitConfig { enabled, bus_check, count1, port1, - count2, port2, rand, _marker: PhantomData, @@ -109,31 +102,26 @@ impl Circuit for TestCircuit { )?; } - let mut terms = vec![Value::known(F::zero()); self.n_rows]; - - // Circuit 1. - let off1 = 1; - region.assign_advice( - || "count1", - config.count1, - off1, - || Value::known(F::one()), - )?; - let h1 = config.port1.assign(&mut region, off1, value, rand)?; - terms[off1] = terms[off1] + h1; - - // Circuit 2. - let off2 = 3; - region.assign_advice( - || "count2", - config.count2, - off2, - || Value::known(F::one()), - )?; - let h2 = config.port2.assign(&mut region, off2, value, rand)?; - terms[off2] = terms[off2] - h2; - - config.bus_check.assign(&mut region, self.n_rows, &terms)?; + let mut bus_assigner = BusAssigner::new(self.n_rows); + + // Circuit 1 puts the value once, but it is worth `count` times. + { + let count = Value::known(F::from(self.n_rows as u64)); + let offset = 3; // can be anywhere. + region.assign_advice(|| "count1", config.count1, offset, || count)?; + let term = config.port1.assign(&mut region, offset, value, rand)?; + bus_assigner.put_term(offset, count * term); + } + + // Circuit 2 takes the value once per row. + for offset in 0..self.n_rows { + let term = config.port2.assign(&mut region, offset, value, rand)?; + bus_assigner.take_term(offset, term); + } + + config + .bus_check + .assign(&mut region, self.n_rows, bus_assigner.terms())?; Ok(()) }, From 8fac6a9e3ff10b63945932e8c0f921cd0e739f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 19 Sep 2023 18:43:56 +0200 Subject: [PATCH 08/67] bus: rename value to message --- gadgets/src/bus/bus_port.rs | 64 +++++++++++++++++-------------------- gadgets/src/bus/tests.rs | 12 +++---- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index cc6f04cb9a..7ce4606a66 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -11,21 +11,18 @@ use halo2_proofs::{ #[derive(Clone)] pub struct BusOp { count: Expression, - value: Expression, + message: Expression, } impl BusOp { /// Put an item. The expression evaluates to 0 or the number of copies. - pub fn put(count: Expression, value: Expression) -> Self { - Self { count, value } + pub fn put(count: Expression, message: Expression) -> Self { + Self { count, message } } /// Take an item. The expression evaluates to 0 or 1. - pub fn take(count: Expression, value: Expression) -> Self { - Self { - count: -count, - value, - } + pub fn take(count: Expression, message: Expression) -> Self { + Self::put(-count, message) } /// The expression of the count of items to put or take. @@ -33,9 +30,9 @@ impl BusOp { self.count.clone() } - /// The expression of the value to put or take. - pub fn value(&self) -> Expression { - self.value.clone() + /// The expression of the message to put or take. + pub fn message(&self) -> Expression { + self.message.clone() } } @@ -53,8 +50,8 @@ impl BusPortSingle { } /// Return the witness that must be assigned to the helper cell. - pub fn helper_witness(value: Value, rand: Value) -> Value { - (rand + value).map(|x| x.invert().unwrap_or(F::zero())) + pub fn helper_witness(message: Value, rand: Value) -> Value { + (rand + message).map(|x| x.invert().unwrap_or(F::zero())) } } @@ -63,13 +60,13 @@ impl BusPort for BusPortSingle { let term = self.op.count() * self.helper.clone(); meta.create_gate("bus access", |_| { - // Verify that `term = count / (rand + value)`. + // Verify that `term = count / (rand + message)`. // - // With witness: helper = 1 / (rand + value) + // With witness: helper = 1 / (rand + message) // // If `count = 0`, then `term = 0` by definition. In that case, the helper cell is not // constrained, so it can be used for something else. - [term.clone() * (rand + self.op.value()) - self.op.count()] + [term.clone() * (rand + self.op.message()) - self.op.count()] }); BusTerm::verified(term) @@ -90,39 +87,38 @@ impl BusPortDual { } /// Return the witness that must be assigned to the helper cell. - pub fn helper_witness(values: [Value; 2], rand: Value) -> Value { - ((rand + values[0]) * (rand + values[1])).map(|x| x.invert().unwrap_or(F::zero())) + pub fn helper_witness(messages: [Value; 2], rand: Value) -> Value { + ((rand + messages[0]) * (rand + messages[1])).map(|x| x.invert().unwrap_or(F::zero())) } } impl BusPort for BusPortDual { fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> BusTerm { - let rv_0 = rand.clone() + self.ops[0].value(); - let rv_1 = rand.clone() + self.ops[1].value(); + let rm_0 = rand.clone() + self.ops[0].message(); + let rm_1 = rand.clone() + self.ops[1].message(); - // With witness: helper = 1 / rv_0 / rv_1 + // With witness: helper = 1 / rm_0 / rm_1 - // term_0 = count_0 * helper * rv_1 + // term_0 = count_0 * helper * rm_1 let count_0 = self.ops[0].count(); - let term_0 = count_0.clone() * self.helper.clone() * rv_1.clone(); + let term_0 = count_0.clone() * self.helper.clone() * rm_1.clone(); - // term_1 = count_1 * helper * rv_0 + // term_1 = count_1 * helper * rm_0 let count_1 = self.ops[1].count(); - let term_1 = count_1.clone() * self.helper.clone() * rv_0.clone(); + let term_1 = count_1.clone() * self.helper.clone() * rm_0.clone(); // Verify that: - // term_0 == count_0 / rv_0 - // term_0 * rv_0 - count_0 == 0 - // And: - // term_1 == count_1 / rv_1 - // term_1 * rv_1 - count_1 == 0 + // term_0 == count_0 / (rand + message_0) + // term_0 * rm_0 - count_0 == 0 + // + // And the same for term_1. // // In case both count_0 and count_1 are zero, then the helper cell is not constrained, so it // can be used for something else. meta.create_gate("bus access (dual)", |_| { [ - term_0.clone() * rv_0 - count_0, - term_1.clone() * rv_1 - count_1, + term_0.clone() * rm_0 - count_0, + term_1.clone() * rm_1 - count_1, ] }); @@ -153,10 +149,10 @@ impl BusPortChip { &self, region: &mut Region<'_, F>, offset: usize, - value: Value, + message: Value, rand: Value, ) -> Result, Error> { - let helper = BusPortSingle::helper_witness(value, rand); + let helper = BusPortSingle::helper_witness(message, rand); region.assign_advice(|| "BusPort_helper", self.helper, offset, || helper)?; Ok(helper) } diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 68e016f3c4..4bc1d2c22d 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -51,20 +51,20 @@ impl Circuit for TestCircuit { let rand_expr = query_expression(cs, |cs| cs.query_challenge(rand)); let mut bus_builder = BusBuilder::::new(rand_expr); - let value = 2.expr(); + let message = 2.expr(); // Circuit 1 puts values dynamically. let count1 = cs.advice_column(); let count1_expr = enabled_expr.clone() * query_expression(cs, |cs| cs.query_advice(count1, Rotation::cur())); - let port1 = BusPortChip::new(cs, BusOp::put(count1_expr, value.clone())); + let port1 = BusPortChip::new(cs, BusOp::put(count1_expr, message.clone())); bus_builder.connect_port(cs, &port1); // Circuit 2 takes one value per row. let count2_expr = enabled_expr * 1.expr(); - let port2 = BusPortChip::new(cs, BusOp::take(count2_expr, value)); + let port2 = BusPortChip::new(cs, BusOp::take(count2_expr, message)); bus_builder.connect_port(cs, &port2); // Global bus connection. @@ -91,7 +91,7 @@ impl Circuit for TestCircuit { layouter.assign_region( || "witness", |mut region| { - let value = Value::known(F::from(2)); + let message = Value::known(F::from(2)); for offset in 0..self.n_rows { region.assign_fixed( @@ -109,13 +109,13 @@ impl Circuit for TestCircuit { let count = Value::known(F::from(self.n_rows as u64)); let offset = 3; // can be anywhere. region.assign_advice(|| "count1", config.count1, offset, || count)?; - let term = config.port1.assign(&mut region, offset, value, rand)?; + let term = config.port1.assign(&mut region, offset, message, rand)?; bus_assigner.put_term(offset, count * term); } // Circuit 2 takes the value once per row. for offset in 0..self.n_rows { - let term = config.port2.assign(&mut region, offset, value, rand)?; + let term = config.port2.assign(&mut region, offset, message, rand)?; bus_assigner.take_term(offset, term); } From 962095c6f4ef07291671a8d2426f82bbeae5d786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 19 Sep 2023 18:47:34 +0200 Subject: [PATCH 09/67] bus: remove "multi" variant --- gadgets/src/bus.rs | 28 ----------------------- gadgets/src/bus/bus_multi.rs | 43 ------------------------------------ gadgets/src/bus/bus_port.rs | 4 +++- 3 files changed, 3 insertions(+), 72 deletions(-) delete mode 100644 gadgets/src/bus/bus_multi.rs diff --git a/gadgets/src/bus.rs b/gadgets/src/bus.rs index 954ef1a31d..0e638bad5a 100644 --- a/gadgets/src/bus.rs +++ b/gadgets/src/bus.rs @@ -9,33 +9,5 @@ pub mod bus_builder; /// A chip to access the bus. pub mod bus_port; -/// A variant of bus port for mutually exclusive accesses. -pub mod bus_multi; - #[cfg(test)] mod tests; - -/* - -sum( (1 / item) for each value ) == 0 - -item = RLC(beta, [ 1, circuit_tag, RLC(alpha, x), y, z, … ] ) - 1, RW, address, value - 1, COPY, src, dst, len - … - -+0*item -+3*item --item --item --item - - -Bus Check: -- on each row, sum_next = sum_current + term_circuit1 + term_circuit2 + … + term_circut10 -- if is_last, sum_current == 0 - -Circuit 1: -term_circuit1 * value == 1 - -*/ diff --git a/gadgets/src/bus/bus_multi.rs b/gadgets/src/bus/bus_multi.rs deleted file mode 100644 index cc4e28bce7..0000000000 --- a/gadgets/src/bus/bus_multi.rs +++ /dev/null @@ -1,43 +0,0 @@ -use super::{ - bus_builder::BusPort, - bus_chip::BusTerm, - bus_port::{BusOp, BusPortSingle}, -}; -use crate::util::Expr; -use halo2_proofs::{ - arithmetic::FieldExt, - plonk::{ConstraintSystem, Expression}, -}; - -/// BusPort to access the bus. The most flexible port. The helper cell is provided by the caller. It -/// supports multiple put/take accesses, as long as only one is active at a time. -pub struct BusPortMulti { - helper: Expression, - ops: Vec>, -} - -impl BusPortMulti { - /// Put one out of several possible items to the bus. - /// The operations `ops` must be mutually exclusives (only one `count` is non-zero at a time) - /// across all puts and takes. - pub fn put(helper: Expression, ops: Vec>) -> Self { - BusPortMulti { helper, ops } - } -} - -impl BusPort for BusPortMulti { - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> BusTerm { - let term = self - .ops - .iter() - .map(|op| { - BusPortSingle::new(self.helper.clone(), op.clone()) - .create_term(meta, rand.clone()) - .expr() - }) - .reduce(|acc, term| acc + term) - .unwrap_or(0.expr()); - - BusTerm::verified(term) - } -} diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 7ce4606a66..2d0c212ca2 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -45,6 +45,7 @@ pub struct BusPortSingle { impl BusPortSingle { /// Create a new bus port with a single access. + /// The helper cell can be used for something else if op.count is zero. pub fn new(helper: Expression, op: BusOp) -> Self { Self { helper, op } } @@ -73,8 +74,9 @@ impl BusPort for BusPortSingle { } } -/// A chip with two accesses to the bus. BusPortDual uses only one witness cell, however the +/// A chip with two accesses to the bus. BusPortDual uses only one helper cell, however the /// degree of input expressions is more limited than with BusPortSingle. +/// The helper cell can be used for something else if both op.count are zero. pub struct BusPortDual { helper: Expression, ops: [BusOp; 2], From 60593a2b9acc0a30cafd21dc230483cb3e21a5dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 20 Sep 2023 14:51:03 +0200 Subject: [PATCH 10/67] bus: implement batch inversion --- gadgets/src/bus/bus_builder.rs | 19 +++++++---- gadgets/src/bus/bus_port.rs | 60 ++++++++++++++++++++++++++++++++-- gadgets/src/bus/tests.rs | 42 ++++++++++++++++++++---- 3 files changed, 106 insertions(+), 15 deletions(-) diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs index 83380ee981..ec867926b1 100644 --- a/gadgets/src/bus/bus_builder.rs +++ b/gadgets/src/bus/bus_builder.rs @@ -42,7 +42,7 @@ impl BusBuilder { /// BusAssigner pub struct BusAssigner { terms: Vec, - all_assigned: bool, + unknown: bool, } impl BusAssigner { @@ -50,7 +50,7 @@ impl BusAssigner { pub fn new(n_rows: usize) -> Self { Self { terms: vec![F::zero(); n_rows], - all_assigned: true, + unknown: false, } } @@ -61,10 +61,15 @@ impl BusAssigner { "offset={offset} out of bounds n_rows={}", self.terms.len() ); + if self.unknown { + return; + } if term.is_none() { - self.all_assigned = false; + self.unknown = true; + self.terms.clear(); + } else { + term.map(|t| self.terms[offset] += t); } - term.map(|t| self.terms[offset] += t); } /// Take a term value from the bus. @@ -74,10 +79,10 @@ impl BusAssigner { /// Return the collected terms. pub fn terms(&self) -> Value<&[F]> { - if self.all_assigned { - Value::known(&self.terms) - } else { + if self.unknown { Value::unknown() + } else { + Value::known(&self.terms) } } } diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 2d0c212ca2..8a64049685 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -3,6 +3,7 @@ use crate::util::query_expression; use halo2_proofs::{ arithmetic::FieldExt, circuit::{Region, Value}, + halo2curves::group::ff::BatchInvert, plonk::{Advice, Column, ConstraintSystem, Error, Expression, SecondPhase}, poly::Rotation, }; @@ -54,6 +55,11 @@ impl BusPortSingle { pub fn helper_witness(message: Value, rand: Value) -> Value { (rand + message).map(|x| x.invert().unwrap_or(F::zero())) } + + /// Return the denominator of the helper cell, to be inverted. + pub fn helper_denom(message: Value, rand: Value) -> Value { + rand + message + } } impl BusPort for BusPortSingle { @@ -146,8 +152,8 @@ impl BusPortChip { Self { helper, port } } - /// Assign the helper witness. - pub fn assign( + /// Assign the helper witness based on a message. + pub fn assign_message( &self, region: &mut Region<'_, F>, offset: usize, @@ -158,6 +164,17 @@ impl BusPortChip { region.assign_advice(|| "BusPort_helper", self.helper, offset, || helper)?; Ok(helper) } + + /// Assign the helper witness based on a precomputed term (without count). + pub fn assign_term( + &self, + region: &mut Region<'_, F>, + offset: usize, + term: Value, + ) -> Result<(), Error> { + region.assign_advice(|| "BusPort_helper", self.helper, offset, || term)?; + Ok(()) + } } impl BusPort for BusPortChip { @@ -165,3 +182,42 @@ impl BusPort for BusPortChip { self.port.create_term(meta, rand) } } + +/// TermBatch calculates helper witnesses, in batches for better performance. +pub struct HelperBatch { + denoms: Vec<(F, INFO)>, + unknown: bool, +} + +impl HelperBatch { + /// Create a new term batch. + pub fn new() -> Self { + Self { + denoms: vec![], + unknown: false, + } + } + + /// Add a helper denominator to the batch. Some `info` can be attached for later use. + pub fn add_denom(&mut self, denom: Value, info: INFO) { + if self.unknown { + return; + } + if denom.is_none() { + self.unknown = true; + self.denoms.clear(); + } else { + denom.map(|denom| self.denoms.push((denom, info))); + } + } + + /// Return the inverse of all denominators and their associated info. + pub fn invert(mut self) -> Value> { + if self.unknown { + Value::unknown() + } else { + self.denoms.iter_mut().map(|(d, _)| d).batch_invert(); + Value::known(self.denoms) + } + } +} diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 4bc1d2c22d..0169847bd6 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -104,19 +104,49 @@ impl Circuit for TestCircuit { let mut bus_assigner = BusAssigner::new(self.n_rows); - // Circuit 1 puts the value once, but it is worth `count` times. + // Circuit 1 puts a message on some row. { + // Put `count` copies of the same message. let count = Value::known(F::from(self.n_rows as u64)); let offset = 3; // can be anywhere. region.assign_advice(|| "count1", config.count1, offset, || count)?; - let term = config.port1.assign(&mut region, offset, message, rand)?; + + // Set the helper cell. + let term = config + .port1 + .assign_message(&mut region, offset, message, rand)?; + + // Report the term to the global bus. bus_assigner.put_term(offset, count * term); } - // Circuit 2 takes the value once per row. - for offset in 0..self.n_rows { - let term = config.port2.assign(&mut region, offset, message, rand)?; - bus_assigner.take_term(offset, term); + // Circuit 2 takes one message per row. + { + // This uses a batching method rather than row-by-row. + let mut helper_batch = HelperBatch::new(); + + // First pass: run circuit steps. + for offset in 0..self.n_rows { + // … do normal circuit assignment logic … + + // Collect the message(s) of this step into the batch. + let denom = BusPortSingle::helper_denom(message, rand); + helper_batch.add_denom(denom, offset); + } + + // Final pass: assign the bus witnesses. + helper_batch.invert().map(|terms| { + // The batch has converted the messages into bus terms. + for (term, offset) in terms { + let term = Value::known(term); + + // Set the helper cell. + config.port2.assign_term(&mut region, offset, term).unwrap(); + + // Report the term to the global bus. + bus_assigner.take_term(offset, term); + } + }); } config From ec90302022aae742771f42bbdcf8ddb7050972a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 20 Sep 2023 16:15:36 +0200 Subject: [PATCH 11/67] bus: simplify batch assignment --- gadgets/src/bus/bus_port.rs | 81 ++++++++++++++++++++++++++++++++++--- gadgets/src/bus/tests.rs | 26 +++++------- 2 files changed, 86 insertions(+), 21 deletions(-) diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 8a64049685..3b94d07ab9 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,4 +1,7 @@ -use super::{bus_builder::BusPort, bus_chip::BusTerm}; +use super::{ + bus_builder::{BusAssigner, BusPort}, + bus_chip::BusTerm, +}; use crate::util::query_expression; use halo2_proofs::{ arithmetic::FieldExt, @@ -57,7 +60,7 @@ impl BusPortSingle { } /// Return the denominator of the helper cell, to be inverted. - pub fn helper_denom(message: Value, rand: Value) -> Value { + fn helper_denom(message: Value, rand: Value) -> Value { rand + message } } @@ -175,6 +178,11 @@ impl BusPortChip { region.assign_advice(|| "BusPort_helper", self.helper, offset, || term)?; Ok(()) } + + /// The column of the helper cell. + pub fn column(&self) -> Column { + self.helper + } } impl BusPort for BusPortChip { @@ -184,14 +192,14 @@ impl BusPort for BusPortChip { } /// TermBatch calculates helper witnesses, in batches for better performance. -pub struct HelperBatch { +struct HelperBatch { denoms: Vec<(F, INFO)>, unknown: bool, } impl HelperBatch { /// Create a new term batch. - pub fn new() -> Self { + fn new() -> Self { Self { denoms: vec![], unknown: false, @@ -199,7 +207,7 @@ impl HelperBatch { } /// Add a helper denominator to the batch. Some `info` can be attached for later use. - pub fn add_denom(&mut self, denom: Value, info: INFO) { + fn add_denom(&mut self, denom: Value, info: INFO) { if self.unknown { return; } @@ -212,7 +220,7 @@ impl HelperBatch { } /// Return the inverse of all denominators and their associated info. - pub fn invert(mut self) -> Value> { + fn invert(mut self) -> Value> { if self.unknown { Value::unknown() } else { @@ -221,3 +229,64 @@ impl HelperBatch { } } } + +/// PortAssigner computes and assigns terms into helper cells and the bus. +pub struct PortAssigner { + rand: Value, + batch: HelperBatch, isize, Value)>, +} + +impl PortAssigner { + /// Create a new PortAssigner. + pub fn new(rand: Value) -> Self { + Self { + rand, + batch: HelperBatch::new(), + } + } + + /// Put a message. + pub fn put_message( + &mut self, + offset: usize, + column: Column, + rotation: isize, + count: Value, + message: Value, + ) { + let denom = BusPortSingle::helper_denom(message, self.rand); + self.batch + .add_denom(denom, (offset, column, rotation, count)); + } + + /// Take a message. + pub fn take_message( + &mut self, + offset: usize, + column: Column, + rotation: isize, + count: Value, + message: Value, + ) { + self.put_message(offset, column, rotation, -count, message); + } + + /// Assign the helper cells and report the terms to the bus. + pub fn finish(self, region: &mut Region<'_, F>, bus_assigner: &mut BusAssigner) { + self.batch.invert().map(|terms| { + // The batch has converted the messages into bus terms. + for (term, (offset, column, rotation, count)) in terms { + let term = Value::known(term); + + // Set the helper cell. + let cell_offset = (offset as isize + rotation) as usize; + region + .assign_advice(|| "BusPort_helper", column, cell_offset, || term) + .unwrap(); + + // Report the term to the global bus. + bus_assigner.put_term(offset, count * term); + } + }); + } +} diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 0169847bd6..4cc832f913 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -122,31 +122,27 @@ impl Circuit for TestCircuit { // Circuit 2 takes one message per row. { + let count = Value::known(F::one()); + // This uses a batching method rather than row-by-row. - let mut helper_batch = HelperBatch::new(); + let mut port_assigner = PortAssigner::new(rand); // First pass: run circuit steps. for offset in 0..self.n_rows { // … do normal circuit assignment logic … // Collect the message(s) of this step into the batch. - let denom = BusPortSingle::helper_denom(message, rand); - helper_batch.add_denom(denom, offset); + port_assigner.take_message( + offset, + config.port2.column(), + 0, + count, + message, + ); } // Final pass: assign the bus witnesses. - helper_batch.invert().map(|terms| { - // The batch has converted the messages into bus terms. - for (term, offset) in terms { - let term = Value::known(term); - - // Set the helper cell. - config.port2.assign_term(&mut region, offset, term).unwrap(); - - // Report the term to the global bus. - bus_assigner.take_term(offset, term); - } - }); + port_assigner.finish(&mut region, &mut bus_assigner); } config From d24995263a47915f06a239a0b6bf04e2d553c6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 20 Sep 2023 16:59:42 +0200 Subject: [PATCH 12/67] bus: BusOp for expr and values --- gadgets/src/bus/bus_port.rs | 60 ++++++++++++++++++------------------- gadgets/src/bus/tests.rs | 14 ++++----- 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 3b94d07ab9..9318a5b3b9 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,3 +1,5 @@ +use std::ops::Neg; + use super::{ bus_builder::{BusAssigner, BusPort}, bus_chip::BusTerm, @@ -11,31 +13,40 @@ use halo2_proofs::{ poly::Rotation, }; +/// A bus operation, as expressions for circuit config. +pub type BusOpExpr = BusOp>; + +/// A bus operation, as values for circuit assignment. +pub type BusOpVal = BusOp>; + /// A bus operation. #[derive(Clone)] -pub struct BusOp { - count: Expression, - message: Expression, +pub struct BusOp { + count: T, + message: T, } -impl BusOp { +impl BusOp +where + T: Clone + Neg, +{ /// Put an item. The expression evaluates to 0 or the number of copies. - pub fn put(count: Expression, message: Expression) -> Self { + pub fn put(count: T, message: T) -> Self { Self { count, message } } /// Take an item. The expression evaluates to 0 or 1. - pub fn take(count: Expression, message: Expression) -> Self { + pub fn take(count: T, message: T) -> Self { Self::put(-count, message) } /// The expression of the count of items to put or take. - pub fn count(&self) -> Expression { + pub fn count(&self) -> T { self.count.clone() } /// The expression of the message to put or take. - pub fn message(&self) -> Expression { + pub fn message(&self) -> T { self.message.clone() } } @@ -44,13 +55,13 @@ impl BusOp { #[derive(Clone)] pub struct BusPortSingle { helper: Expression, - op: BusOp, + op: BusOpExpr, } impl BusPortSingle { /// Create a new bus port with a single access. /// The helper cell can be used for something else if op.count is zero. - pub fn new(helper: Expression, op: BusOp) -> Self { + pub fn new(helper: Expression, op: BusOpExpr) -> Self { Self { helper, op } } @@ -88,12 +99,12 @@ impl BusPort for BusPortSingle { /// The helper cell can be used for something else if both op.count are zero. pub struct BusPortDual { helper: Expression, - ops: [BusOp; 2], + ops: [BusOpExpr; 2], } impl BusPortDual { /// Create a new bus port with two accesses. - pub fn new(helper: Expression, ops: [BusOp; 2]) -> Self { + pub fn new(helper: Expression, ops: [BusOpExpr; 2]) -> Self { Self { helper, ops } } @@ -146,7 +157,7 @@ pub struct BusPortChip { impl BusPortChip { /// Create a new bus port with a single access. - pub fn new(meta: &mut ConstraintSystem, op: BusOp) -> Self { + pub fn new(meta: &mut ConstraintSystem, op: BusOpExpr) -> Self { let helper = meta.advice_column_in(SecondPhase); let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); @@ -245,30 +256,17 @@ impl PortAssigner { } } - /// Put a message. - pub fn put_message( + /// Assign a message. + pub fn set_op( &mut self, offset: usize, column: Column, rotation: isize, - count: Value, - message: Value, + op: BusOpVal, ) { - let denom = BusPortSingle::helper_denom(message, self.rand); + let denom = BusPortSingle::helper_denom(op.message(), self.rand); self.batch - .add_denom(denom, (offset, column, rotation, count)); - } - - /// Take a message. - pub fn take_message( - &mut self, - offset: usize, - column: Column, - rotation: isize, - count: Value, - message: Value, - ) { - self.put_message(offset, column, rotation, -count, message); + .add_denom(denom, (offset, column, rotation, op.count())); } /// Assign the helper cells and report the terms to the bus. diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 4cc832f913..fcf4dc795f 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -91,8 +91,6 @@ impl Circuit for TestCircuit { layouter.assign_region( || "witness", |mut region| { - let message = Value::known(F::from(2)); - for offset in 0..self.n_rows { region.assign_fixed( || "Port_enable", @@ -107,6 +105,7 @@ impl Circuit for TestCircuit { // Circuit 1 puts a message on some row. { // Put `count` copies of the same message. + let message = Value::known(F::from(2)); let count = Value::known(F::from(self.n_rows as u64)); let offset = 3; // can be anywhere. region.assign_advice(|| "count1", config.count1, offset, || count)?; @@ -122,22 +121,21 @@ impl Circuit for TestCircuit { // Circuit 2 takes one message per row. { - let count = Value::known(F::one()); - // This uses a batching method rather than row-by-row. let mut port_assigner = PortAssigner::new(rand); // First pass: run circuit steps. for offset in 0..self.n_rows { // … do normal circuit assignment logic … + let count = Value::known(F::one()); + let message = Value::known(F::from(2)); - // Collect the message(s) of this step into the batch. - port_assigner.take_message( + // Collect the bus operations into the batch. + port_assigner.set_op( offset, config.port2.column(), 0, - count, - message, + BusOp::take(count, message), ); } From b2bd7f604cde88d8b507c29909ff1db76fb2fc83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 20 Sep 2023 17:10:52 +0200 Subject: [PATCH 13/67] bus: reverse count/message order --- gadgets/src/bus/bus_port.rs | 22 +++++++++++----------- gadgets/src/bus/tests.rs | 14 +++++++------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 9318a5b3b9..b7995cc128 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -22,8 +22,8 @@ pub type BusOpVal = BusOp>; /// A bus operation. #[derive(Clone)] pub struct BusOp { - count: T, message: T, + count: T, } impl BusOp @@ -31,24 +31,24 @@ where T: Clone + Neg, { /// Put an item. The expression evaluates to 0 or the number of copies. - pub fn put(count: T, message: T) -> Self { - Self { count, message } + pub fn put(message: T, count: T) -> Self { + Self { message, count } } /// Take an item. The expression evaluates to 0 or 1. - pub fn take(count: T, message: T) -> Self { - Self::put(-count, message) - } - - /// The expression of the count of items to put or take. - pub fn count(&self) -> T { - self.count.clone() + pub fn take(message: T, count: T) -> Self { + Self::put(message, -count) } - /// The expression of the message to put or take. + /// The message to put or take. pub fn message(&self) -> T { self.message.clone() } + + /// The number of copies of the message to put (if positive) or take (if negative). + pub fn count(&self) -> T { + self.count.clone() + } } /// A chip to access to the bus. diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index fcf4dc795f..3969221701 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -22,7 +22,7 @@ struct TestCircuitConfig { count1: Column, port1: BusPortChip, port2: BusPortChip, - bus_check: BusConfig, + bus_config: BusConfig, rand: Challenge, _marker: PhantomData, } @@ -58,21 +58,21 @@ impl Circuit for TestCircuit { let count1_expr = enabled_expr.clone() * query_expression(cs, |cs| cs.query_advice(count1, Rotation::cur())); - let port1 = BusPortChip::new(cs, BusOp::put(count1_expr, message.clone())); + let port1 = BusPortChip::new(cs, BusOp::put(message.clone(), count1_expr)); bus_builder.connect_port(cs, &port1); // Circuit 2 takes one value per row. let count2_expr = enabled_expr * 1.expr(); - let port2 = BusPortChip::new(cs, BusOp::take(count2_expr, message)); + let port2 = BusPortChip::new(cs, BusOp::take(message, count2_expr)); bus_builder.connect_port(cs, &port2); // Global bus connection. - let bus_check = BusConfig::new(cs, &bus_builder.build()); + let bus_config = BusConfig::new(cs, &bus_builder.build()); TestCircuitConfig { enabled, - bus_check, + bus_config, count1, port1, port2, @@ -135,7 +135,7 @@ impl Circuit for TestCircuit { offset, config.port2.column(), 0, - BusOp::take(count, message), + BusOp::take(message, count), ); } @@ -144,7 +144,7 @@ impl Circuit for TestCircuit { } config - .bus_check + .bus_config .assign(&mut region, self.n_rows, bus_assigner.terms())?; Ok(()) From 2bafc2643d51fca54712ec59988f6eb2115d5634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 25 Sep 2023 12:16:29 +0200 Subject: [PATCH 14/67] bus: draft TableBus --- gadgets/src/bus/bus_builder.rs | 1 + gadgets/src/bus/bus_chip.rs | 11 ++- gadgets/src/bus/bus_port.rs | 3 +- gadgets/src/bus/tests.rs | 2 +- zkevm-circuits/src/evm_circuit.rs | 26 ++++++- zkevm-circuits/src/evm_circuit/execution.rs | 3 +- zkevm-circuits/src/lib.rs | 1 + zkevm-circuits/src/super_circuit.rs | 41 ++++++++++ zkevm-circuits/src/table_bus.rs | 83 +++++++++++++++++++++ zkevm-circuits/src/util.rs | 2 +- 10 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 zkevm-circuits/src/table_bus.rs diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs index ec867926b1..1ad476a3fd 100644 --- a/gadgets/src/bus/bus_builder.rs +++ b/gadgets/src/bus/bus_builder.rs @@ -13,6 +13,7 @@ pub trait BusPort { } /// BusBuilder +#[derive(Debug)] pub struct BusBuilder { rand: Expression, terms: Vec>, diff --git a/gadgets/src/bus/bus_chip.rs b/gadgets/src/bus/bus_chip.rs index b658135545..6f758c45dc 100644 --- a/gadgets/src/bus/bus_chip.rs +++ b/gadgets/src/bus/bus_chip.rs @@ -1,3 +1,5 @@ +use std::marker::PhantomData; + use crate::util::Expr; use halo2_proofs::{ arithmetic::FieldExt, @@ -7,7 +9,7 @@ use halo2_proofs::{ }; /// A term of the bus sum check. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BusTerm(Expression); impl BusTerm { @@ -77,6 +79,12 @@ impl BusConfig { n_rows: usize, terms: Value<&[F]>, ) -> Result<(), Error> { + /*assert_eq!( + region.global_offset(0), + 0, + "The bus requires a global region" + );*/ + region.assign_fixed( || "Bus_is_first", self.is_first, @@ -101,6 +109,7 @@ impl BusConfig { } terms.map(|terms| { + assert!(terms.len() <= n_rows, "Bus terms out-of-bound"); let mut acc = F::zero(); for offset in 0..n_rows { diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index b7995cc128..378cf72ea8 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -283,7 +283,8 @@ impl PortAssigner { .unwrap(); // Report the term to the global bus. - bus_assigner.put_term(offset, count * term); + let global_offset = offset; // region.global_offset(offset); + bus_assigner.put_term(global_offset, count * term); } }); } diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 3969221701..3885b2458d 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -127,8 +127,8 @@ impl Circuit for TestCircuit { // First pass: run circuit steps. for offset in 0..self.n_rows { // … do normal circuit assignment logic … - let count = Value::known(F::one()); let message = Value::known(F::from(2)); + let count = Value::known(F::one()); // Collect the bus operations into the batch. port_assigner.set_op( diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 7d999e1b58..6cfbb04cc2 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -1,6 +1,7 @@ //! The EVM circuit implementation. #![allow(missing_docs)] +use gadgets::bus::bus_builder::BusBuilder; use halo2_proofs::{ circuit::{Cell, Layouter, SimpleFloorPlanner, Value}, plonk::*, @@ -90,14 +91,28 @@ pub struct EvmCircuitExports { pub withdraw_root: (Cell, Value), } +// Implement this marker trait. Its method is never called. impl SubCircuitConfig for EvmCircuitConfig { - type ConfigArgs = EvmCircuitConfigArgs; + type ConfigArgs = Unreachable; + #[allow(clippy::too_many_arguments)] + fn new(_: &mut ConstraintSystem, _: Self::ConfigArgs) -> Self { + unreachable!() + } +} + +/// This type guarantees that SubCircuitConfig::new() is never called. +pub struct Unreachable { + _private: (), +} + +impl EvmCircuitConfig { /// Configure EvmCircuitConfig #[allow(clippy::too_many_arguments)] - fn new( + pub fn new( meta: &mut ConstraintSystem, - Self::ConfigArgs { + bus_builder: &mut BusBuilder, + EvmCircuitConfigArgs { challenges, tx_table, rw_table, @@ -110,13 +125,14 @@ impl SubCircuitConfig for EvmCircuitConfig { modexp_table, ecc_table, pow_of_rand_table, - }: Self::ConfigArgs, + }: EvmCircuitConfigArgs, ) -> Self { let fixed_table = [(); 4].map(|_| meta.fixed_column()); let byte_table = [(); 1].map(|_| meta.fixed_column()); let execution = Box::new(ExecutionConfig::configure( meta, challenges, + bus_builder, &fixed_table, &byte_table, &tx_table, @@ -453,6 +469,7 @@ impl Circuit for EvmCircuit { fn configure(meta: &mut ConstraintSystem) -> Self::Config { let challenges = Challenges::construct(meta); let challenges_expr = challenges.exprs(meta); + let mut bus_builder = BusBuilder::::new(challenges_expr.lookup_input()); let rw_table = RwTable::construct(meta); let tx_table = TxTable::construct(meta); let bytecode_table = BytecodeTable::construct(meta); @@ -468,6 +485,7 @@ impl Circuit for EvmCircuit { ( EvmCircuitConfig::new( meta, + &mut bus_builder, EvmCircuitConfigArgs { challenges: challenges_expr, tx_table, diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index b5355ff363..faffe7bae1 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -26,7 +26,7 @@ use crate::{ }; use bus_mapping::util::read_env_var; use eth_types::{Field, ToLittleEndian}; -use gadgets::util::not; +use gadgets::{util::not, bus::bus_builder::BusBuilder}; use halo2_proofs::{ arithmetic::FieldExt, circuit::{Layouter, Region, Value}, @@ -370,6 +370,7 @@ impl ExecutionConfig { pub(crate) fn configure( meta: &mut ConstraintSystem, challenges: Challenges>, + bus_builder: &mut BusBuilder, fixed_table: &dyn LookupTable, byte_table: &dyn LookupTable, tx_table: &dyn LookupTable, diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index 9dd67c3c55..e291213693 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -41,6 +41,7 @@ pub mod modexp_circuit; pub mod state_circuit; pub mod super_circuit; pub mod table; +pub mod table_bus; #[cfg(any(feature = "test", test))] pub mod test_util; diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 58acb3e708..9613d52d2c 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -80,6 +80,7 @@ use crate::{ MptTable, PoseidonTable, PowOfRandTable, RlpFsmRlpTable as RlpTable, RwTable, SigTable, TxTable, U16Table, U8Table, }, + table_bus::{self, TableBusCircuit, TableBusConfig}, tx_circuit::{TxCircuit, TxCircuitConfig, TxCircuitConfigArgs}, util::{circuit_stats, log2_ceil, Challenges, SubCircuit, SubCircuitConfig}, witness::{block_convert, Block, Transaction}, @@ -93,6 +94,10 @@ use bus_mapping::{ mock::BlockData, }; use eth_types::{geth_types::GethData, Field}; +use gadgets::bus::{ + bus_builder::{BusAssigner, BusBuilder}, + bus_chip::BusConfig, +}; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, halo2curves::bn256::Fr, @@ -104,6 +109,8 @@ use snark_verifier_sdk::CircuitExt; /// Configuration of the Super Circuit #[derive(Clone)] pub struct SuperCircuitConfig { + bus_config: BusConfig, + table_bus: TableBusConfig, block_table: BlockTable, mpt_table: MptTable, rlp_table: RlpTable, @@ -165,6 +172,8 @@ impl SubCircuitConfig for SuperCircuitConfig { }; let challenges_expr = challenges.exprs(meta); + let mut bus_builder = BusBuilder::::new(challenges_expr.lookup_input()); + let tx_table = TxTable::construct(meta); log_circuit_info(meta, "tx table"); let rw_table = RwTable::construct(meta); @@ -328,6 +337,7 @@ impl SubCircuitConfig for SuperCircuitConfig { let evm_circuit = EvmCircuitConfig::new( meta, + &mut bus_builder, EvmCircuitConfigArgs { challenges: challenges_expr.clone(), tx_table: tx_table.clone(), @@ -367,12 +377,20 @@ impl SubCircuitConfig for SuperCircuitConfig { ); log_circuit_info(meta, "ecc circuit"); + // Table to Bus. + let table_bus = TableBusConfig::new(meta, &mut bus_builder, &rw_table); + + // The Bus. + let bus_config = BusConfig::new(meta, &bus_builder.build()); + #[cfg(feature = "onephase")] if meta.max_phase() != 0 { log::warn!("max_phase: {}", meta.max_phase()); } SuperCircuitConfig { + bus_config, + table_bus, block_table, mpt_table, tx_table, @@ -420,6 +438,8 @@ pub struct SuperCircuit< const MAX_INNER_BLOCKS: usize, const MOCK_RANDOMNESS: u64, > { + /// TableBus Circuit + pub table_bus: TableBusCircuit, /// EVM Circuit pub evm_circuit: EvmCircuit, /// State Circuit @@ -559,6 +579,7 @@ impl< } fn new_from_block(block: &Block) -> Self { + let table_bus = TableBusCircuit::new(); let evm_circuit = EvmCircuit::new_from_block(block); let state_circuit = StateCircuit::new_from_block(block); let tx_circuit = TxCircuit::new_from_block(block); @@ -575,6 +596,7 @@ impl< #[cfg(feature = "zktrie")] let mpt_circuit = MptCircuit::new_from_block(block); SuperCircuit:: { + table_bus, evm_circuit, state_circuit, tx_circuit, @@ -624,6 +646,9 @@ impl< challenges: &crate::util::Challenges>, layouter: &mut impl Layouter, ) -> Result<(), Error> { + let n_rows = 10; // TODO. + let mut bus_assigner = BusAssigner::new(n_rows); + log::debug!("assigning evm_circuit"); self.evm_circuit .synthesize_sub(&config.evm_circuit, challenges, layouter)?; @@ -691,6 +716,22 @@ impl< .synthesize_sub(&config.mpt_circuit, challenges, layouter)?; } + self.table_bus.synthesize_sub( + &config.table_bus, + challenges, + layouter, + &mut bus_assigner, + )?; + + layouter.assign_region( + || "bus", + |mut region| { + config + .bus_config + .assign(&mut region, n_rows, bus_assigner.terms()) + }, + )?; + log::debug!("super circuit synthesize_sub done"); Ok(()) } diff --git a/zkevm-circuits/src/table_bus.rs b/zkevm-circuits/src/table_bus.rs new file mode 100644 index 0000000000..aa2f1672e9 --- /dev/null +++ b/zkevm-circuits/src/table_bus.rs @@ -0,0 +1,83 @@ +//! The TableBus circuit puts items from a table to the bus. + +use std::marker::PhantomData; + +use eth_types::Field; +use gadgets::bus::{ + bus_builder::{BusAssigner, BusBuilder}, + bus_port::{BusOp, BusPortChip, PortAssigner}, +}; +use halo2_proofs::{ + circuit::{Layouter, Value}, + plonk::{ConstraintSystem, Error}, +}; + +use crate::{table::LookupTable, util::query_expression}; + +/// TableBusConfig +#[derive(Clone)] +pub struct TableBusConfig { + port: BusPortChip, +} + +impl TableBusConfig { + /// Create a new TableBus circuit. + pub fn new( + meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder, + table: &dyn LookupTable, + ) -> Self { + let exprs = query_expression(meta, |meta| table.table_exprs(meta)); + + let count = exprs[0].clone(); + let message = exprs[1].clone(); + // TODO: multi-column message. + + let port = BusPortChip::new(meta, BusOp::put(message, count)); + bus_builder.connect_port(meta, &port); + + Self { port } + } +} + +/// TableBusCircuit +#[derive(Clone, Default, Debug)] +pub struct TableBusCircuit { + _marker: PhantomData, +} + +impl TableBusCircuit { + /// Create a new TableBus circuit. + pub fn new() -> Self { + Self { + _marker: PhantomData, + } + } + + /// Make the assignments to the circuit + pub fn synthesize_sub( + &self, + config: &TableBusConfig, + challenges: &crate::util::Challenges>, + layouter: &mut impl Layouter, + bus_assigner: &mut BusAssigner, + ) -> Result<(), Error> { + layouter.assign_region( + || "TableBus", + |mut region| { + let rand = challenges.lookup_input(); + let mut port_assigner = PortAssigner::new(rand); + + let message = Value::known(F::zero()); + let count = Value::known(F::zero()); + port_assigner.set_op(0, config.port.column(), 0, BusOp::take(message, count)); + + port_assigner.finish(&mut region, bus_assigner); + + Ok(()) + }, + )?; + + Ok(()) + } +} diff --git a/zkevm-circuits/src/util.rs b/zkevm-circuits/src/util.rs index e4e60de63f..e6dc3ab141 100644 --- a/zkevm-circuits/src/util.rs +++ b/zkevm-circuits/src/util.rs @@ -208,7 +208,7 @@ pub(crate) fn build_tx_log_expression( /// table(s) if any). pub trait SubCircuit { /// Configuration of the SubCircuit. - type Config: SubCircuitConfig; + type Config; /// Returns number of unusable rows of the SubCircuit, which should be /// `meta.blinding_factors() + 1`. From 3ca2865a5e57d36d3405c3b0ae37e2569ebb931a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 25 Sep 2023 20:21:07 +0200 Subject: [PATCH 15/67] bus: draft EVM bytes lookup --- gadgets/src/bus/bus_chip.rs | 8 +- gadgets/src/bus/bus_port.rs | 61 +++++++++++-- zkevm-circuits/src/evm_circuit.rs | 96 ++++++++++++++++++--- zkevm-circuits/src/evm_circuit/execution.rs | 89 +++++++++++++++++-- zkevm-circuits/src/super_circuit.rs | 41 --------- zkevm-circuits/src/table_bus.rs | 78 ++++++----------- 6 files changed, 252 insertions(+), 121 deletions(-) diff --git a/gadgets/src/bus/bus_chip.rs b/gadgets/src/bus/bus_chip.rs index 6f758c45dc..c55c3e6593 100644 --- a/gadgets/src/bus/bus_chip.rs +++ b/gadgets/src/bus/bus_chip.rs @@ -4,7 +4,7 @@ use crate::util::Expr; use halo2_proofs::{ arithmetic::FieldExt, circuit::{Region, Value}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, SecondPhase}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, ThirdPhase}, poly::Rotation, }; @@ -26,7 +26,7 @@ impl Expr for BusTerm { } /// BusConfig -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BusConfig { enabled: Column, is_first: Column, @@ -40,7 +40,7 @@ impl BusConfig { let enabled = cs.fixed_column(); let is_first = cs.fixed_column(); let is_last = cs.fixed_column(); - let acc = cs.advice_column_in(SecondPhase); + let acc = cs.advice_column_in(ThirdPhase); cs.create_gate("bus sum check", |cs| { let enabled = cs.query_fixed(enabled, Rotation::cur()); @@ -108,6 +108,8 @@ impl BusConfig { )?; } + println!("XXX bus enabled up to row {}", n_rows - 1); + terms.map(|terms| { assert!(terms.len() <= n_rows, "Bus terms out-of-bound"); let mut acc = F::zero(); diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 378cf72ea8..4cfa4607fe 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,4 +1,4 @@ -use std::ops::Neg; +use std::{collections::HashMap, ops::Neg}; use super::{ bus_builder::{BusAssigner, BusPort}, @@ -9,7 +9,7 @@ use halo2_proofs::{ arithmetic::FieldExt, circuit::{Region, Value}, halo2curves::group::ff::BatchInvert, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, SecondPhase}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, ThirdPhase}, poly::Rotation, }; @@ -20,7 +20,7 @@ pub type BusOpExpr = BusOp>; pub type BusOpVal = BusOp>; /// A bus operation. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BusOp { message: T, count: T, @@ -52,7 +52,7 @@ where } /// A chip to access to the bus. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BusPortSingle { helper: Expression, op: BusOpExpr, @@ -149,7 +149,7 @@ impl BusPort for BusPortDual { } /// A chip to access the bus. It manages its own helper column and gives one access per row. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BusPortChip { helper: Column, port: BusPortSingle, @@ -158,7 +158,7 @@ pub struct BusPortChip { impl BusPortChip { /// Create a new bus port with a single access. pub fn new(meta: &mut ConstraintSystem, op: BusOpExpr) -> Self { - let helper = meta.advice_column_in(SecondPhase); + let helper = meta.advice_column_in(ThirdPhase); let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); let port = BusPortSingle::new(helper_expr, op); @@ -245,6 +245,7 @@ impl HelperBatch { pub struct PortAssigner { rand: Value, batch: HelperBatch, isize, Value)>, + bus_op_counter: BusOpCounter, } impl PortAssigner { @@ -253,6 +254,7 @@ impl PortAssigner { Self { rand, batch: HelperBatch::new(), + bus_op_counter: BusOpCounter::new(), } } @@ -264,13 +266,19 @@ impl PortAssigner { rotation: isize, op: BusOpVal, ) { + self.bus_op_counter.set_op(&op); + let denom = BusPortSingle::helper_denom(op.message(), self.rand); self.batch .add_denom(denom, (offset, column, rotation, op.count())); } /// Assign the helper cells and report the terms to the bus. - pub fn finish(self, region: &mut Region<'_, F>, bus_assigner: &mut BusAssigner) { + pub fn finish( + self, + region: &mut Region<'_, F>, + bus_assigner: &mut BusAssigner, + ) -> BusOpCounter { self.batch.invert().map(|terms| { // The batch has converted the messages into bus terms. for (term, (offset, column, rotation, count)) in terms { @@ -287,5 +295,44 @@ impl PortAssigner { bus_assigner.put_term(global_offset, count * term); } }); + self.bus_op_counter + } +} + +/// OpCounter tracks the messages taken, to help generating the puts. +#[derive(Clone, Debug, Default)] +pub struct BusOpCounter { + counts: HashMap, Value>, +} + +impl BusOpCounter { + /// Create a new OpCounter. + pub fn new() -> Self { + Self::default() + } + + /// Report an operation. + pub fn set_op(&mut self, op: &BusOpVal) { + op.message().map(|message| { + self.counts + .entry(Self::to_key(message)) + .and_modify(|c| *c = *c + op.count()) + .or_insert_with(|| op.count()); + }); + } + + /// Count the number of copies of this messages taken. + pub fn count_of_message(&self, message: Value) -> Value { + let mut count = Value::known(F::zero()); + message.map(|message| { + if let Some(c) = self.counts.get(&Self::to_key(message)) { + count = *c; + } + }); + count + } + + fn to_key(message: F) -> Vec { + Vec::from(message.to_repr().as_ref()) } } diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 6cfbb04cc2..c2d7445fa1 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -1,10 +1,15 @@ //! The EVM circuit implementation. #![allow(missing_docs)] -use gadgets::bus::bus_builder::BusBuilder; +use gadgets::bus::{ + bus_builder::{BusAssigner, BusBuilder}, + bus_chip::BusConfig, + bus_port::{BusOp, BusOpCounter, PortAssigner}, +}; use halo2_proofs::{ circuit::{Cell, Layouter, SimpleFloorPlanner, Value}, plonk::*, + poly::Rotation, }; mod execution; @@ -26,7 +31,8 @@ use crate::{ BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, LookupTable, ModExpTable, PowOfRandTable, RwTable, SigTable, TxTable, }, - util::{SubCircuit, SubCircuitConfig}, + table_bus::LookupBusConfig, + util::{query_expression, SubCircuit, SubCircuitConfig}, }; use bus_mapping::evm::OpcodeId; use eth_types::Field; @@ -41,6 +47,9 @@ use witness::Block; pub struct EvmCircuitConfig { fixed_table: [Column; 4], byte_table: [Column; 1], + enable_table: Column, + bus: BusConfig, + table_to_bus: LookupBusConfig, pub(crate) execution: Box>, // External tables tx_table: TxTable, @@ -111,7 +120,6 @@ impl EvmCircuitConfig { #[allow(clippy::too_many_arguments)] pub fn new( meta: &mut ConstraintSystem, - bus_builder: &mut BusBuilder, EvmCircuitConfigArgs { challenges, tx_table, @@ -129,10 +137,17 @@ impl EvmCircuitConfig { ) -> Self { let fixed_table = [(); 4].map(|_| meta.fixed_column()); let byte_table = [(); 1].map(|_| meta.fixed_column()); + let enable_table = meta.fixed_column(); + + let mut bus_builder = BusBuilder::::new(challenges.lookup_input()); + + let table_to_bus = + Self::configure_table_to_bus(meta, &mut bus_builder, &byte_table, enable_table); + let execution = Box::new(ExecutionConfig::configure( meta, challenges, - bus_builder, + &mut bus_builder, &fixed_table, &byte_table, &tx_table, @@ -148,6 +163,8 @@ impl EvmCircuitConfig { &pow_of_rand_table, )); + let bus = BusConfig::new(meta, &bus_builder.build()); + meta.annotate_lookup_any_column(byte_table[0], || "byte_range"); fixed_table.iter().enumerate().for_each(|(idx, &col)| { meta.annotate_lookup_any_column(col, || format!("fix_table_{idx}")) @@ -167,6 +184,9 @@ impl EvmCircuitConfig { Self { fixed_table, byte_table, + enable_table, + bus, + table_to_bus, execution, tx_table, rw_table, @@ -181,6 +201,17 @@ impl EvmCircuitConfig { pow_of_rand_table, } } + + fn configure_table_to_bus( + meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder, + byte_table: &dyn LookupTable, + enabled: Column, + ) -> LookupBusConfig { + let byte_expr = query_expression(meta, |meta| byte_table.table_exprs(meta)[0].clone()); + let enabled = query_expression(meta, |meta| meta.query_fixed(enabled, Rotation::cur())); + LookupBusConfig::new(meta, bus_builder, byte_expr, enabled) + } } impl EvmCircuitConfig { @@ -208,19 +239,41 @@ impl EvmCircuitConfig { } /// Load byte table - pub fn load_byte_table(&self, layouter: &mut impl Layouter) -> Result<(), Error> { + pub fn load_byte_table( + &self, + challenges: &crate::util::Challenges>, + layouter: &mut impl Layouter, + bus_assigner: &mut BusAssigner, + bus_op_counter: &BusOpCounter, + ) -> Result<(), Error> { layouter.assign_region( || "byte table", |mut region| { + let mut port_assigner = PortAssigner::new(challenges.lookup_input()); + for offset in 0..256 { + let value = Value::known(F::from(offset as u64)); + + region.assign_fixed(|| "", self.byte_table[0], offset, || value)?; + region.assign_fixed( || "", - self.byte_table[0], + self.enable_table, + offset, + || Value::known(F::one()), + )?; + + let count = bus_op_counter.count_of_message(value); + self.table_to_bus.assign( + &mut region, + &mut port_assigner, offset, - || Value::known(F::from(offset as u64)), + BusOp::put(value, count), )?; } + port_assigner.finish(&mut region, bus_assigner); + Ok(()) }, ) @@ -294,6 +347,9 @@ impl EvmCircuit { } } + // It must fit the byte table. + num_rows = num_rows.max(256); + // It must have one row for EndBlock and at least one unused one num_rows + 2 } @@ -345,12 +401,32 @@ impl SubCircuit for EvmCircuit { layouter: &mut impl Layouter, ) -> Result<(), Error> { let block = self.block.as_ref().unwrap(); + let num_rows = Self::get_num_rows_required(block); config.load_fixed_table(layouter, self.fixed_table_tags.clone())?; - config.load_byte_table(layouter)?; config.pow_of_rand_table.assign(layouter, challenges)?; - let export = config.execution.assign_block(layouter, block, challenges)?; + + let mut bus_assigner = BusAssigner::new(num_rows); + + let (export, bus_op_counter) = + config + .execution + .assign_block(layouter, &mut bus_assigner, block, challenges)?; self.exports.borrow_mut().replace(export); + + if let Some(bus_op_counter) = bus_op_counter { + config.load_byte_table(challenges, layouter, &mut bus_assigner, &bus_op_counter)?; + } + + layouter.assign_region( + || "EVM_Bus", + |mut region| { + config + .bus + .assign(&mut region, num_rows, bus_assigner.terms()) + }, + )?; + Ok(()) } } @@ -469,7 +545,6 @@ impl Circuit for EvmCircuit { fn configure(meta: &mut ConstraintSystem) -> Self::Config { let challenges = Challenges::construct(meta); let challenges_expr = challenges.exprs(meta); - let mut bus_builder = BusBuilder::::new(challenges_expr.lookup_input()); let rw_table = RwTable::construct(meta); let tx_table = TxTable::construct(meta); let bytecode_table = BytecodeTable::construct(meta); @@ -485,7 +560,6 @@ impl Circuit for EvmCircuit { ( EvmCircuitConfig::new( meta, - &mut bus_builder, EvmCircuitConfigArgs { challenges: challenges_expr, tx_table, diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index faffe7bae1..3207e73658 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -22,11 +22,18 @@ use crate::{ witness::{Block, Call, ExecStep, Transaction}, }, table::{LookupTable, RwTableTag, TxReceiptFieldTag}, + table_bus::LookupBusConfig, util::{query_expression, Challenges, Expr}, }; use bus_mapping::util::read_env_var; use eth_types::{Field, ToLittleEndian}; -use gadgets::{util::not, bus::bus_builder::BusBuilder}; +use gadgets::{ + bus::{ + bus_builder::{BusAssigner, BusBuilder}, + bus_port::{BusOp, BusPortChip, PortAssigner, BusOpCounter}, + }, + util::not, +}; use halo2_proofs::{ arithmetic::FieldExt, circuit::{Layouter, Region, Value}, @@ -260,6 +267,7 @@ pub(crate) struct ExecutionConfig { q_step_last: Selector, advices: [Column; STEP_WIDTH], step: Step, + bus_port: BusPortChip, pub(crate) height_map: HashMap, stored_expressions_map: HashMap>>, instrument: Instrument, @@ -533,6 +541,8 @@ impl ExecutionConfig { let cell_manager = step_curr.cell_manager.clone(); + let bus_port = Self::configure_bus(meta, bus_builder, q_usable, &cell_manager); + let config = Self { q_usable, q_step, @@ -541,6 +551,7 @@ impl ExecutionConfig { num_rows_inv, q_step_first, q_step_last, + bus_port, advices, // internal states begin_tx_gadget: configure_gadget!(), @@ -912,6 +923,25 @@ impl ExecutionConfig { }); } + fn configure_bus( + meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder, + q_usable: Selector, + cell_manager: &CellManager, + ) -> BusPortChip { + let q_usable = query_expression(meta, |meta| meta.query_selector(q_usable)); + + for column in cell_manager.columns().iter() { + if let CellType::LookupByte = column.cell_type { + let port = BusPortChip::new(meta, BusOp::take(column.expr(), q_usable)); + bus_builder.connect_port(meta, &port); + return port; + // TODO: support all columns. + } + } + unreachable!() + } + #[allow(clippy::too_many_arguments)] fn configure_lookup( meta: &mut ConstraintSystem, @@ -1028,11 +1058,13 @@ impl ExecutionConfig { pub fn assign_block( &self, layouter: &mut impl Layouter, + bus_assigner: &mut BusAssigner, block: &Block, challenges: &Challenges>, - ) -> Result>, Error> { + ) -> Result<(EvmCircuitExports>, Option>), Error> { let mut is_first_time = true; - + let mut bus_op_counter = None; + layouter.assign_region( || "Execution step", |mut region| { @@ -1046,6 +1078,9 @@ impl ExecutionConfig { )?; return Ok(()); } + + let mut port_assigner = PortAssigner::new(challenges.lookup_input()); + let mut offset = 0; // Annotate the EVMCircuit columns within it's single region. @@ -1121,6 +1156,7 @@ impl ExecutionConfig { } self.assign_exec_step( &mut region, + &mut port_assigner, offset, block, transaction, @@ -1158,6 +1194,7 @@ impl ExecutionConfig { } self.assign_same_exec_step_in_range( &mut region, + &mut port_assigner, offset, last_row, block, @@ -1180,6 +1217,7 @@ impl ExecutionConfig { log::trace!("assign last EndBlock at offset {}", offset); self.assign_exec_step( &mut region, + &mut port_assigner, offset, block, &dummy_tx, @@ -1209,6 +1247,8 @@ impl ExecutionConfig { || Value::known(F::zero()), )?; + bus_op_counter = Some(port_assigner.finish(&mut region, bus_assigner)); + log::debug!("assign for region done at offset {}", offset); Ok(()) }, @@ -1232,9 +1272,9 @@ impl ExecutionConfig { .evm_word() .map(|r| rlc::value(&block.withdraw_root.to_le_bytes(), r)); - Ok(EvmCircuitExports { + Ok((EvmCircuitExports { withdraw_root: (final_withdraw_root_cell, withdraw_root_rlc.into()), - }) + }, bus_op_counter)) } fn annotate_circuit(&self, region: &mut Region) { @@ -1274,10 +1314,40 @@ impl ExecutionConfig { region.name_column(|| "Copy_Constr_const", self.constants); } + fn assign_bus_ports( + &self, + region: &mut CachedRegion<'_, '_, F>, + port_assigner: &mut PortAssigner, + offset_begin: usize, + offset_end: usize, + ) { + for column in self.step.cell_manager.columns() { + if let CellType::LookupByte = column.cell_type { + for offset in offset_begin..offset_end { + let byte = region.get_advice( + offset, + self.advices[column.index].index(), + Rotation::cur(), + ); + println!("XXX offset={} byte={:?}", offset, byte); + let count = Value::known(F::one()); + port_assigner.set_op( + offset, + self.bus_port.column(), + 0, + BusOp::take(Value::known(byte), count), + ); + } + break; // TODO: support all columns at all rotations. + } + } + } + #[allow(clippy::too_many_arguments)] fn assign_same_exec_step_in_range( &self, region: &mut Region<'_, F>, + port_assigner: &mut PortAssigner, offset_begin: usize, offset_end: usize, block: &Block, @@ -1310,6 +1380,8 @@ impl ExecutionConfig { offset_end, )?; + self.assign_bus_ports(region, port_assigner, offset_begin, offset_end); + Ok(()) } @@ -1317,6 +1389,7 @@ impl ExecutionConfig { fn assign_exec_step( &self, region: &mut Region<'_, F>, + port_assigner: &mut PortAssigner, offset: usize, block: &Block, transaction: &Transaction, @@ -1353,7 +1426,11 @@ impl ExecutionConfig { )?; } - self.assign_exec_step_int(region, offset, block, transaction, call, step, true) + self.assign_exec_step_int(region, offset, block, transaction, call, step, true)?; + + self.assign_bus_ports(region, port_assigner, offset, offset + height); + + Ok(()) } #[allow(clippy::too_many_arguments)] diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 9613d52d2c..58acb3e708 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -80,7 +80,6 @@ use crate::{ MptTable, PoseidonTable, PowOfRandTable, RlpFsmRlpTable as RlpTable, RwTable, SigTable, TxTable, U16Table, U8Table, }, - table_bus::{self, TableBusCircuit, TableBusConfig}, tx_circuit::{TxCircuit, TxCircuitConfig, TxCircuitConfigArgs}, util::{circuit_stats, log2_ceil, Challenges, SubCircuit, SubCircuitConfig}, witness::{block_convert, Block, Transaction}, @@ -94,10 +93,6 @@ use bus_mapping::{ mock::BlockData, }; use eth_types::{geth_types::GethData, Field}; -use gadgets::bus::{ - bus_builder::{BusAssigner, BusBuilder}, - bus_chip::BusConfig, -}; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, halo2curves::bn256::Fr, @@ -109,8 +104,6 @@ use snark_verifier_sdk::CircuitExt; /// Configuration of the Super Circuit #[derive(Clone)] pub struct SuperCircuitConfig { - bus_config: BusConfig, - table_bus: TableBusConfig, block_table: BlockTable, mpt_table: MptTable, rlp_table: RlpTable, @@ -172,8 +165,6 @@ impl SubCircuitConfig for SuperCircuitConfig { }; let challenges_expr = challenges.exprs(meta); - let mut bus_builder = BusBuilder::::new(challenges_expr.lookup_input()); - let tx_table = TxTable::construct(meta); log_circuit_info(meta, "tx table"); let rw_table = RwTable::construct(meta); @@ -337,7 +328,6 @@ impl SubCircuitConfig for SuperCircuitConfig { let evm_circuit = EvmCircuitConfig::new( meta, - &mut bus_builder, EvmCircuitConfigArgs { challenges: challenges_expr.clone(), tx_table: tx_table.clone(), @@ -377,20 +367,12 @@ impl SubCircuitConfig for SuperCircuitConfig { ); log_circuit_info(meta, "ecc circuit"); - // Table to Bus. - let table_bus = TableBusConfig::new(meta, &mut bus_builder, &rw_table); - - // The Bus. - let bus_config = BusConfig::new(meta, &bus_builder.build()); - #[cfg(feature = "onephase")] if meta.max_phase() != 0 { log::warn!("max_phase: {}", meta.max_phase()); } SuperCircuitConfig { - bus_config, - table_bus, block_table, mpt_table, tx_table, @@ -438,8 +420,6 @@ pub struct SuperCircuit< const MAX_INNER_BLOCKS: usize, const MOCK_RANDOMNESS: u64, > { - /// TableBus Circuit - pub table_bus: TableBusCircuit, /// EVM Circuit pub evm_circuit: EvmCircuit, /// State Circuit @@ -579,7 +559,6 @@ impl< } fn new_from_block(block: &Block) -> Self { - let table_bus = TableBusCircuit::new(); let evm_circuit = EvmCircuit::new_from_block(block); let state_circuit = StateCircuit::new_from_block(block); let tx_circuit = TxCircuit::new_from_block(block); @@ -596,7 +575,6 @@ impl< #[cfg(feature = "zktrie")] let mpt_circuit = MptCircuit::new_from_block(block); SuperCircuit:: { - table_bus, evm_circuit, state_circuit, tx_circuit, @@ -646,9 +624,6 @@ impl< challenges: &crate::util::Challenges>, layouter: &mut impl Layouter, ) -> Result<(), Error> { - let n_rows = 10; // TODO. - let mut bus_assigner = BusAssigner::new(n_rows); - log::debug!("assigning evm_circuit"); self.evm_circuit .synthesize_sub(&config.evm_circuit, challenges, layouter)?; @@ -716,22 +691,6 @@ impl< .synthesize_sub(&config.mpt_circuit, challenges, layouter)?; } - self.table_bus.synthesize_sub( - &config.table_bus, - challenges, - layouter, - &mut bus_assigner, - )?; - - layouter.assign_region( - || "bus", - |mut region| { - config - .bus_config - .assign(&mut region, n_rows, bus_assigner.terms()) - }, - )?; - log::debug!("super circuit synthesize_sub done"); Ok(()) } diff --git a/zkevm-circuits/src/table_bus.rs b/zkevm-circuits/src/table_bus.rs index aa2f1672e9..4056e971df 100644 --- a/zkevm-circuits/src/table_bus.rs +++ b/zkevm-circuits/src/table_bus.rs @@ -5,79 +5,51 @@ use std::marker::PhantomData; use eth_types::Field; use gadgets::bus::{ bus_builder::{BusAssigner, BusBuilder}, - bus_port::{BusOp, BusPortChip, PortAssigner}, + bus_port::{BusOp, BusOpVal, BusPortChip, PortAssigner}, }; use halo2_proofs::{ - circuit::{Layouter, Value}, - plonk::{ConstraintSystem, Error}, + circuit::{Layouter, Region, Value}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression}, + poly::Rotation, }; -use crate::{table::LookupTable, util::query_expression}; +use crate::util::query_expression; -/// TableBusConfig -#[derive(Clone)] -pub struct TableBusConfig { +/// LookupBus exposes a table as a lookup through the bus. +#[derive(Clone, Debug)] +pub struct LookupBusConfig { port: BusPortChip, + count: Column, } -impl TableBusConfig { - /// Create a new TableBus circuit. +impl LookupBusConfig { + /// Create a new LookupBus circuit from the expressions of message and count. pub fn new( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, - table: &dyn LookupTable, + message: Expression, + enabled: Expression, ) -> Self { - let exprs = query_expression(meta, |meta| table.table_exprs(meta)); + let count = meta.advice_column(); + let count_expr = query_expression(meta, |meta| meta.query_advice(count, Rotation::cur())); - let count = exprs[0].clone(); - let message = exprs[1].clone(); - // TODO: multi-column message. - - let port = BusPortChip::new(meta, BusOp::put(message, count)); + let port = BusPortChip::new(meta, BusOp::put(message, enabled * count_expr)); bus_builder.connect_port(meta, &port); - Self { port } + Self { port, count } } -} -/// TableBusCircuit -#[derive(Clone, Default, Debug)] -pub struct TableBusCircuit { - _marker: PhantomData, -} - -impl TableBusCircuit { - /// Create a new TableBus circuit. - pub fn new() -> Self { - Self { - _marker: PhantomData, - } - } - - /// Make the assignments to the circuit - pub fn synthesize_sub( + /// Assign a lookup operation. + pub fn assign( &self, - config: &TableBusConfig, - challenges: &crate::util::Challenges>, - layouter: &mut impl Layouter, - bus_assigner: &mut BusAssigner, + region: &mut Region<'_, F>, + port_assigner: &mut PortAssigner, + offset: usize, + op: BusOpVal, ) -> Result<(), Error> { - layouter.assign_region( - || "TableBus", - |mut region| { - let rand = challenges.lookup_input(); - let mut port_assigner = PortAssigner::new(rand); - - let message = Value::known(F::zero()); - let count = Value::known(F::zero()); - port_assigner.set_op(0, config.port.column(), 0, BusOp::take(message, count)); - - port_assigner.finish(&mut region, bus_assigner); - - Ok(()) - }, - )?; + region.assign_advice(|| "LookupBus", self.count, offset, || op.count())?; + port_assigner.set_op(offset, self.port.column(), 0, op); Ok(()) } } From da5855fad9412d0f428532cb3d707e2087d9f639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 26 Sep 2023 22:11:49 +0200 Subject: [PATCH 16/67] bus: working byte lookup --- gadgets/src/bus.rs | 6 +++ gadgets/src/bus/bus_chip.rs | 4 -- .../src/bus/bus_lookup.rs | 32 ++++++----- gadgets/src/bus/bus_port.rs | 53 ++++++++++--------- gadgets/src/bus/util.rs | 10 ++++ zkevm-circuits/src/evm_circuit.rs | 35 ++++++++---- zkevm-circuits/src/evm_circuit/execution.rs | 20 +++---- zkevm-circuits/src/lib.rs | 1 - 8 files changed, 95 insertions(+), 66 deletions(-) rename zkevm-circuits/src/table_bus.rs => gadgets/src/bus/bus_lookup.rs (65%) create mode 100644 gadgets/src/bus/util.rs diff --git a/gadgets/src/bus.rs b/gadgets/src/bus.rs index 0e638bad5a..455d84ce37 100644 --- a/gadgets/src/bus.rs +++ b/gadgets/src/bus.rs @@ -9,5 +9,11 @@ pub mod bus_builder; /// A chip to access the bus. pub mod bus_port; +/// A chip to expose a lookup table on a bus. +pub mod bus_lookup; + +/// Utility functions. +mod util; + #[cfg(test)] mod tests; diff --git a/gadgets/src/bus/bus_chip.rs b/gadgets/src/bus/bus_chip.rs index c55c3e6593..7232930e3b 100644 --- a/gadgets/src/bus/bus_chip.rs +++ b/gadgets/src/bus/bus_chip.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use crate::util::Expr; use halo2_proofs::{ arithmetic::FieldExt, @@ -108,8 +106,6 @@ impl BusConfig { )?; } - println!("XXX bus enabled up to row {}", n_rows - 1); - terms.map(|terms| { assert!(terms.len() <= n_rows, "Bus terms out-of-bound"); let mut acc = F::zero(); diff --git a/zkevm-circuits/src/table_bus.rs b/gadgets/src/bus/bus_lookup.rs similarity index 65% rename from zkevm-circuits/src/table_bus.rs rename to gadgets/src/bus/bus_lookup.rs index 4056e971df..d214eb9604 100644 --- a/zkevm-circuits/src/table_bus.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -1,29 +1,28 @@ //! The TableBus circuit puts items from a table to the bus. -use std::marker::PhantomData; +use crate::util::query_expression; -use eth_types::Field; -use gadgets::bus::{ - bus_builder::{BusAssigner, BusBuilder}, - bus_port::{BusOp, BusOpVal, BusPortChip, PortAssigner}, +use super::{ + bus_builder::BusBuilder, + bus_port::{BusOp, BusOpF, BusPortChip, PortAssigner}, + util::from_isize, }; use halo2_proofs::{ - circuit::{Layouter, Region, Value}, + circuit::{Region, Value}, + halo2curves::FieldExt, plonk::{Advice, Column, ConstraintSystem, Error, Expression}, poly::Rotation, }; -use crate::util::query_expression; - -/// LookupBus exposes a table as a lookup through the bus. +/// BusLookup exposes a table as a lookup through the bus. #[derive(Clone, Debug)] -pub struct LookupBusConfig { +pub struct BusLookupConfig { port: BusPortChip, count: Column, } -impl LookupBusConfig { - /// Create a new LookupBus circuit from the expressions of message and count. +impl BusLookupConfig { + /// Create a new BusLookup circuit from the expressions of message and count. pub fn new( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, @@ -45,9 +44,14 @@ impl LookupBusConfig { region: &mut Region<'_, F>, port_assigner: &mut PortAssigner, offset: usize, - op: BusOpVal, + op: BusOpF, ) -> Result<(), Error> { - region.assign_advice(|| "LookupBus", self.count, offset, || op.count())?; + region.assign_advice( + || "BusLookup", + self.count, + offset, + || Value::known(from_isize::(op.count())), + )?; port_assigner.set_op(offset, self.port.column(), 0, op); Ok(()) diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 4cfa4607fe..022a9ad9d2 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -3,6 +3,7 @@ use std::{collections::HashMap, ops::Neg}; use super::{ bus_builder::{BusAssigner, BusPort}, bus_chip::BusTerm, + util::from_isize, }; use crate::util::query_expression; use halo2_proofs::{ @@ -14,39 +15,40 @@ use halo2_proofs::{ }; /// A bus operation, as expressions for circuit config. -pub type BusOpExpr = BusOp>; +pub type BusOpExpr = BusOp, Expression>; /// A bus operation, as values for circuit assignment. -pub type BusOpVal = BusOp>; +pub type BusOpF = BusOp, isize>; /// A bus operation. #[derive(Clone, Debug)] -pub struct BusOp { - message: T, - count: T, +pub struct BusOp { + message: M, + count: C, } -impl BusOp +impl BusOp where - T: Clone + Neg, + M: Clone, + C: Clone + Neg, { /// Put an item. The expression evaluates to 0 or the number of copies. - pub fn put(message: T, count: T) -> Self { + pub fn put(message: M, count: C) -> Self { Self { message, count } } /// Take an item. The expression evaluates to 0 or 1. - pub fn take(message: T, count: T) -> Self { + pub fn take(message: M, count: C) -> Self { Self::put(message, -count) } /// The message to put or take. - pub fn message(&self) -> T { + pub fn message(&self) -> M { self.message.clone() } /// The number of copies of the message to put (if positive) or take (if negative). - pub fn count(&self) -> T { + pub fn count(&self) -> C { self.count.clone() } } @@ -244,8 +246,8 @@ impl HelperBatch { /// PortAssigner computes and assigns terms into helper cells and the bus. pub struct PortAssigner { rand: Value, - batch: HelperBatch, isize, Value)>, - bus_op_counter: BusOpCounter, + batch: HelperBatch, isize, isize)>, + bus_op_counter: BusOpCounter, } impl PortAssigner { @@ -264,7 +266,7 @@ impl PortAssigner { offset: usize, column: Column, rotation: isize, - op: BusOpVal, + op: BusOpF, ) { self.bus_op_counter.set_op(&op); @@ -278,7 +280,7 @@ impl PortAssigner { self, region: &mut Region<'_, F>, bus_assigner: &mut BusAssigner, - ) -> BusOpCounter { + ) -> BusOpCounter { self.batch.invert().map(|terms| { // The batch has converted the messages into bus terms. for (term, (offset, column, rotation, count)) in terms { @@ -292,6 +294,7 @@ impl PortAssigner { // Report the term to the global bus. let global_offset = offset; // region.global_offset(offset); + let count = Value::known(from_isize::(count)); bus_assigner.put_term(global_offset, count * term); } }); @@ -301,18 +304,18 @@ impl PortAssigner { /// OpCounter tracks the messages taken, to help generating the puts. #[derive(Clone, Debug, Default)] -pub struct BusOpCounter { - counts: HashMap, Value>, +pub struct BusOpCounter { + counts: HashMap, isize>, } -impl BusOpCounter { +impl BusOpCounter { /// Create a new OpCounter. pub fn new() -> Self { Self::default() } /// Report an operation. - pub fn set_op(&mut self, op: &BusOpVal) { + pub fn set_op(&mut self, op: &BusOpF) { op.message().map(|message| { self.counts .entry(Self::to_key(message)) @@ -321,18 +324,16 @@ impl BusOpCounter { }); } - /// Count the number of copies of this messages taken. - pub fn count_of_message(&self, message: Value) -> Value { - let mut count = Value::known(F::zero()); + /// Count the number of copies of this messages put (positive) or taken (negative). + pub fn count_of_message(&self, message: Value) -> isize { + let mut count = 0; message.map(|message| { - if let Some(c) = self.counts.get(&Self::to_key(message)) { - count = *c; - } + count = *self.counts.get(&Self::to_key(message)).unwrap_or(&0); }); count } - fn to_key(message: F) -> Vec { + fn to_key(message: F) -> Vec { Vec::from(message.to_repr().as_ref()) } } diff --git a/gadgets/src/bus/util.rs b/gadgets/src/bus/util.rs new file mode 100644 index 0000000000..574715627c --- /dev/null +++ b/gadgets/src/bus/util.rs @@ -0,0 +1,10 @@ +use std::ops::Neg; + +/// Convert an isize to a field element. +pub fn from_isize + Neg>(x: isize) -> F { + if x < 0 { + -F::from((-x) as u64) + } else { + F::from(x as u64) + } +} diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index c2d7445fa1..ea0d05acd0 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -1,10 +1,14 @@ //! The EVM circuit implementation. #![allow(missing_docs)] -use gadgets::bus::{ - bus_builder::{BusAssigner, BusBuilder}, - bus_chip::BusConfig, - bus_port::{BusOp, BusOpCounter, PortAssigner}, +use gadgets::{ + bus::{ + bus_builder::{BusAssigner, BusBuilder}, + bus_chip::BusConfig, + bus_lookup::BusLookupConfig, + bus_port::{BusOp, BusOpCounter, PortAssigner}, + }, + util::Expr, }; use halo2_proofs::{ circuit::{Cell, Layouter, SimpleFloorPlanner, Value}, @@ -31,7 +35,6 @@ use crate::{ BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, LookupTable, ModExpTable, PowOfRandTable, RwTable, SigTable, TxTable, }, - table_bus::LookupBusConfig, util::{query_expression, SubCircuit, SubCircuitConfig}, }; use bus_mapping::evm::OpcodeId; @@ -49,7 +52,7 @@ pub struct EvmCircuitConfig { byte_table: [Column; 1], enable_table: Column, bus: BusConfig, - table_to_bus: LookupBusConfig, + table_to_bus: BusLookupConfig, pub(crate) execution: Box>, // External tables tx_table: TxTable, @@ -207,10 +210,10 @@ impl EvmCircuitConfig { bus_builder: &mut BusBuilder, byte_table: &dyn LookupTable, enabled: Column, - ) -> LookupBusConfig { + ) -> BusLookupConfig { let byte_expr = query_expression(meta, |meta| byte_table.table_exprs(meta)[0].clone()); let enabled = query_expression(meta, |meta| meta.query_fixed(enabled, Rotation::cur())); - LookupBusConfig::new(meta, bus_builder, byte_expr, enabled) + BusLookupConfig::new(meta, bus_builder, byte_expr, enabled) } } @@ -244,11 +247,19 @@ impl EvmCircuitConfig { challenges: &crate::util::Challenges>, layouter: &mut impl Layouter, bus_assigner: &mut BusAssigner, - bus_op_counter: &BusOpCounter, + bus_op_counter: &BusOpCounter, ) -> Result<(), Error> { + let mut closure_count = 0; + layouter.assign_region( || "byte table", |mut region| { + // TODO: deal with this some other way. + closure_count += 1; + if closure_count == 1 { + return Ok(()); + } + let mut port_assigner = PortAssigner::new(challenges.lookup_input()); for offset in 0..256 { @@ -268,7 +279,7 @@ impl EvmCircuitConfig { &mut region, &mut port_assigner, offset, - BusOp::put(value, count), + BusOp::put(value, -count), )?; } @@ -276,7 +287,9 @@ impl EvmCircuitConfig { Ok(()) }, - ) + )?; + assert_eq!(closure_count, 2, "assign_region behavior changed"); + Ok(()) } } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 3207e73658..ede82c1c5a 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -22,7 +22,6 @@ use crate::{ witness::{Block, Call, ExecStep, Transaction}, }, table::{LookupTable, RwTableTag, TxReceiptFieldTag}, - table_bus::LookupBusConfig, util::{query_expression, Challenges, Expr}, }; use bus_mapping::util::read_env_var; @@ -30,7 +29,7 @@ use eth_types::{Field, ToLittleEndian}; use gadgets::{ bus::{ bus_builder::{BusAssigner, BusBuilder}, - bus_port::{BusOp, BusPortChip, PortAssigner, BusOpCounter}, + bus_port::{BusOp, BusOpCounter, BusPortChip, PortAssigner}, }, util::not, }; @@ -1061,10 +1060,10 @@ impl ExecutionConfig { bus_assigner: &mut BusAssigner, block: &Block, challenges: &Challenges>, - ) -> Result<(EvmCircuitExports>, Option>), Error> { + ) -> Result<(EvmCircuitExports>, Option), Error> { let mut is_first_time = true; let mut bus_op_counter = None; - + layouter.assign_region( || "Execution step", |mut region| { @@ -1272,9 +1271,12 @@ impl ExecutionConfig { .evm_word() .map(|r| rlc::value(&block.withdraw_root.to_le_bytes(), r)); - Ok((EvmCircuitExports { - withdraw_root: (final_withdraw_root_cell, withdraw_root_rlc.into()), - }, bus_op_counter)) + Ok(( + EvmCircuitExports { + withdraw_root: (final_withdraw_root_cell, withdraw_root_rlc.into()), + }, + bus_op_counter, + )) } fn annotate_circuit(&self, region: &mut Region) { @@ -1329,13 +1331,11 @@ impl ExecutionConfig { self.advices[column.index].index(), Rotation::cur(), ); - println!("XXX offset={} byte={:?}", offset, byte); - let count = Value::known(F::one()); port_assigner.set_op( offset, self.bus_port.column(), 0, - BusOp::take(Value::known(byte), count), + BusOp::take(Value::known(byte), 1), ); } break; // TODO: support all columns at all rotations. diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index e291213693..9dd67c3c55 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -41,7 +41,6 @@ pub mod modexp_circuit; pub mod state_circuit; pub mod super_circuit; pub mod table; -pub mod table_bus; #[cfg(any(feature = "test", test))] pub mod test_util; From c59a2878a3fec510177847d6cdcefc752dc5e3f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 26 Sep 2023 22:35:12 +0200 Subject: [PATCH 17/67] bus: connect API --- gadgets/src/bus/bus_lookup.rs | 8 ++++---- gadgets/src/bus/bus_port.rs | 13 ++++++++++++- gadgets/src/bus/tests.rs | 10 ++++++---- zkevm-circuits/src/evm_circuit.rs | 15 ++++++--------- zkevm-circuits/src/evm_circuit/execution.rs | 4 ++-- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index d214eb9604..17acd37ac7 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -22,8 +22,8 @@ pub struct BusLookupConfig { } impl BusLookupConfig { - /// Create a new BusLookup circuit from the expressions of message and count. - pub fn new( + /// Create and connect a new BusLookup circuit from the expressions of message and count. + pub fn connect( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, message: Expression, @@ -32,8 +32,8 @@ impl BusLookupConfig { let count = meta.advice_column(); let count_expr = query_expression(meta, |meta| meta.query_advice(count, Rotation::cur())); - let port = BusPortChip::new(meta, BusOp::put(message, enabled * count_expr)); - bus_builder.connect_port(meta, &port); + let port = + BusPortChip::connect(meta, bus_builder, BusOp::put(message, enabled * count_expr)); Self { port, count } } diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 022a9ad9d2..caf11691df 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, ops::Neg}; use super::{ - bus_builder::{BusAssigner, BusPort}, + bus_builder::{BusAssigner, BusBuilder, BusPort}, bus_chip::BusTerm, util::from_isize, }; @@ -158,6 +158,17 @@ pub struct BusPortChip { } impl BusPortChip { + /// Create and connect a new bus port with a single access. + pub fn connect( + meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder, + op: BusOpExpr, + ) -> Self { + let port = Self::new(meta, op); + bus_builder.connect_port(meta, &port); + port + } + /// Create a new bus port with a single access. pub fn new(meta: &mut ConstraintSystem, op: BusOpExpr) -> Self { let helper = meta.advice_column_in(ThirdPhase); diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 3885b2458d..40f027b3cb 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -58,14 +58,16 @@ impl Circuit for TestCircuit { let count1_expr = enabled_expr.clone() * query_expression(cs, |cs| cs.query_advice(count1, Rotation::cur())); - let port1 = BusPortChip::new(cs, BusOp::put(message.clone(), count1_expr)); - bus_builder.connect_port(cs, &port1); + let port1 = BusPortChip::connect( + cs, + &mut bus_builder, + BusOp::put(message.clone(), count1_expr), + ); // Circuit 2 takes one value per row. let count2_expr = enabled_expr * 1.expr(); - let port2 = BusPortChip::new(cs, BusOp::take(message, count2_expr)); - bus_builder.connect_port(cs, &port2); + let port2 = BusPortChip::connect(cs, &mut bus_builder, BusOp::take(message, count2_expr)); // Global bus connection. let bus_config = BusConfig::new(cs, &bus_builder.build()); diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index ea0d05acd0..27d96a6cea 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -1,14 +1,11 @@ //! The EVM circuit implementation. #![allow(missing_docs)] -use gadgets::{ - bus::{ - bus_builder::{BusAssigner, BusBuilder}, - bus_chip::BusConfig, - bus_lookup::BusLookupConfig, - bus_port::{BusOp, BusOpCounter, PortAssigner}, - }, - util::Expr, +use gadgets::bus::{ + bus_builder::{BusAssigner, BusBuilder}, + bus_chip::BusConfig, + bus_lookup::BusLookupConfig, + bus_port::{BusOp, BusOpCounter, PortAssigner}, }; use halo2_proofs::{ circuit::{Cell, Layouter, SimpleFloorPlanner, Value}, @@ -213,7 +210,7 @@ impl EvmCircuitConfig { ) -> BusLookupConfig { let byte_expr = query_expression(meta, |meta| byte_table.table_exprs(meta)[0].clone()); let enabled = query_expression(meta, |meta| meta.query_fixed(enabled, Rotation::cur())); - BusLookupConfig::new(meta, bus_builder, byte_expr, enabled) + BusLookupConfig::connect(meta, bus_builder, byte_expr, enabled) } } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index ede82c1c5a..57fcd6412c 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -932,8 +932,8 @@ impl ExecutionConfig { for column in cell_manager.columns().iter() { if let CellType::LookupByte = column.cell_type { - let port = BusPortChip::new(meta, BusOp::take(column.expr(), q_usable)); - bus_builder.connect_port(meta, &port); + let port = + BusPortChip::connect(meta, bus_builder, BusOp::take(column.expr(), q_usable)); return port; // TODO: support all columns. } From 2a3acf9787ebd5201553c3be513b7a7d352c4cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 26 Sep 2023 23:12:59 +0200 Subject: [PATCH 18/67] bus: hiding details --- gadgets/src/bus/bus_lookup.rs | 3 +-- gadgets/src/bus/bus_port.rs | 24 +++++++++++++++------ zkevm-circuits/src/evm_circuit.rs | 4 ++-- zkevm-circuits/src/evm_circuit/execution.rs | 8 ++----- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index 17acd37ac7..c905f4692d 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -52,8 +52,7 @@ impl BusLookupConfig { offset, || Value::known(from_isize::(op.count())), )?; - - port_assigner.set_op(offset, self.port.column(), 0, op); + self.port.assign(port_assigner, offset, op); Ok(()) } } diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index caf11691df..5d4ecfedd8 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -179,6 +179,11 @@ impl BusPortChip { Self { helper, port } } + /// Assign an operation. + pub fn assign(&self, port_assigner: &mut PortAssigner, offset: usize, op: BusOpF) { + port_assigner.set_op(offset, self.helper, 0, op); + } + /// Assign the helper witness based on a message. pub fn assign_message( &self, @@ -202,11 +207,6 @@ impl BusPortChip { region.assign_advice(|| "BusPort_helper", self.helper, offset, || term)?; Ok(()) } - - /// The column of the helper cell. - pub fn column(&self) -> Column { - self.helper - } } impl BusPort for BusPortChip { @@ -335,8 +335,18 @@ impl BusOpCounter { }); } - /// Count the number of copies of this messages put (positive) or taken (negative). - pub fn count_of_message(&self, message: Value) -> isize { + /// Count how many times a message was taken (net of puts). + pub fn count_takes(&self, message: Value) -> isize { + (-self.count_ops(message)).max(0) + } + + /// Count how many times a message was put (net of takes). + pub fn count_puts(&self, message: Value) -> isize { + self.count_ops(message).max(0) + } + + /// Count how many times a message was put (net positive) or taken (net negative). + fn count_ops(&self, message: Value) -> isize { let mut count = 0; message.map(|message| { count = *self.counts.get(&Self::to_key(message)).unwrap_or(&0); diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 27d96a6cea..300a9f59b4 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -271,12 +271,12 @@ impl EvmCircuitConfig { || Value::known(F::one()), )?; - let count = bus_op_counter.count_of_message(value); + let count = bus_op_counter.count_takes(value); self.table_to_bus.assign( &mut region, &mut port_assigner, offset, - BusOp::put(value, -count), + BusOp::put(value, count), )?; } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 57fcd6412c..ec6db681e0 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -1331,12 +1331,8 @@ impl ExecutionConfig { self.advices[column.index].index(), Rotation::cur(), ); - port_assigner.set_op( - offset, - self.bus_port.column(), - 0, - BusOp::take(Value::known(byte), 1), - ); + self.bus_port + .assign(port_assigner, offset, BusOp::take(Value::known(byte), 1)); } break; // TODO: support all columns at all rotations. } From 0979a31a62a5d530066d9f754ad9cb8a5acfdd51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 26 Sep 2023 23:50:56 +0200 Subject: [PATCH 19/67] bus: cover all byte lookups --- gadgets/src/bus/bus_port.rs | 35 +++++++++++-- zkevm-circuits/src/evm_circuit/execution.rs | 54 +++++++++++---------- 2 files changed, 61 insertions(+), 28 deletions(-) diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 5d4ecfedd8..8c3fb0efaa 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,5 +1,3 @@ -use std::{collections::HashMap, ops::Neg}; - use super::{ bus_builder::{BusAssigner, BusBuilder, BusPort}, bus_chip::BusTerm, @@ -13,6 +11,7 @@ use halo2_proofs::{ plonk::{Advice, Column, ConstraintSystem, Error, Expression, ThirdPhase}, poly::Rotation, }; +use std::{collections::HashMap, ops::Neg}; /// A bus operation, as expressions for circuit config. pub type BusOpExpr = BusOp, Expression>; @@ -170,7 +169,7 @@ impl BusPortChip { } /// Create a new bus port with a single access. - pub fn new(meta: &mut ConstraintSystem, op: BusOpExpr) -> Self { + fn new(meta: &mut ConstraintSystem, op: BusOpExpr) -> Self { let helper = meta.advice_column_in(ThirdPhase); let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); @@ -215,6 +214,36 @@ impl BusPort for BusPortChip { } } +/// A chip to access the bus. It manages its own helper columns and gives multiple accesses per row. +#[derive(Clone, Debug)] +pub struct BusPortMulti { + // TODO: implement with as few helper columns as possible. + ports: Vec>, +} + +impl BusPortMulti { + /// Create and connect a new bus port with multiple accesses. + pub fn connect( + meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder, + ops: Vec>, + ) -> Self { + let ports = ops + .into_iter() + .map(|op| BusPortChip::connect(meta, bus_builder, op)) + .collect(); + Self { ports } + } + + /// Assign operations. + pub fn assign(&self, port_assigner: &mut PortAssigner, offset: usize, ops: Vec>) { + assert_eq!(self.ports.len(), ops.len()); + for (port, op) in self.ports.iter().zip(ops) { + port.assign(port_assigner, offset, op); + } + } +} + /// TermBatch calculates helper witnesses, in batches for better performance. struct HelperBatch { denoms: Vec<(F, INFO)>, diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index ec6db681e0..d5c9e8a045 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -29,7 +29,7 @@ use eth_types::{Field, ToLittleEndian}; use gadgets::{ bus::{ bus_builder::{BusAssigner, BusBuilder}, - bus_port::{BusOp, BusOpCounter, BusPortChip, PortAssigner}, + bus_port::{BusOp, BusOpCounter, BusPortMulti, PortAssigner}, }, util::not, }; @@ -266,7 +266,7 @@ pub(crate) struct ExecutionConfig { q_step_last: Selector, advices: [Column; STEP_WIDTH], step: Step, - bus_port: BusPortChip, + bus_port: BusPortMulti, pub(crate) height_map: HashMap, stored_expressions_map: HashMap>>, instrument: Instrument, @@ -927,18 +927,17 @@ impl ExecutionConfig { bus_builder: &mut BusBuilder, q_usable: Selector, cell_manager: &CellManager, - ) -> BusPortChip { + ) -> BusPortMulti { let q_usable = query_expression(meta, |meta| meta.query_selector(q_usable)); - for column in cell_manager.columns().iter() { - if let CellType::LookupByte = column.cell_type { - let port = - BusPortChip::connect(meta, bus_builder, BusOp::take(column.expr(), q_usable)); - return port; - // TODO: support all columns. - } - } - unreachable!() + let ops = cell_manager + .columns() + .iter() + .filter(|column| column.cell_type == CellType::LookupByte) + .map(|column| BusOp::take(column.expr(), q_usable.clone())) + .collect::>(); + + BusPortMulti::connect(meta, bus_builder, ops) } #[allow(clippy::too_many_arguments)] @@ -1323,19 +1322,24 @@ impl ExecutionConfig { offset_begin: usize, offset_end: usize, ) { - for column in self.step.cell_manager.columns() { - if let CellType::LookupByte = column.cell_type { - for offset in offset_begin..offset_end { - let byte = region.get_advice( - offset, - self.advices[column.index].index(), - Rotation::cur(), - ); - self.bus_port - .assign(port_assigner, offset, BusOp::take(Value::known(byte), 1)); - } - break; // TODO: support all columns at all rotations. - } + let column_indexes = self + .step + .cell_manager + .columns() + .iter() + .filter(|column| column.cell_type == CellType::LookupByte) + .map(|column| self.advices[column.index].index()) + .collect::>(); + + for offset in offset_begin..offset_end { + let ops = column_indexes + .iter() + .map(|column_index| { + let byte = region.get_advice(offset, *column_index, Rotation::cur()); + BusOp::take(Value::known(byte), 1) + }) + .collect::>(); + self.bus_port.assign(port_assigner, offset, ops); } } From 9c2c4394d223fb1ca7d46a5feb62ee9f814df139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 27 Sep 2023 00:13:17 +0200 Subject: [PATCH 20/67] bus: adapt test --- gadgets/src/bus/tests.rs | 75 +++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 40f027b3cb..acbe57c222 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -4,12 +4,12 @@ use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, dev::MockProver, halo2curves::bn256::Fr, - plonk::{Advice, Challenge, Circuit, Column, ConstraintSystem, Error, FirstPhase, Fixed}, + plonk::{Challenge, Circuit, Column, ConstraintSystem, Error, Fixed, SecondPhase}, poly::Rotation, }; use std::marker::PhantomData; -use super::{bus_builder::*, bus_chip::*, bus_port::*}; +use super::{bus_builder::*, bus_chip::*, bus_lookup::BusLookupConfig, bus_port::*}; #[test] fn test_bus() { @@ -19,10 +19,9 @@ fn test_bus() { #[derive(Clone)] struct TestCircuitConfig { enabled: Column, - count1: Column, - port1: BusPortChip, - port2: BusPortChip, bus_config: BusConfig, + bus_lookup: BusLookupConfig, + port2: BusPortChip, rand: Challenge, _marker: PhantomData, } @@ -43,26 +42,20 @@ impl Circuit for TestCircuit { fn configure(cs: &mut ConstraintSystem) -> Self::Config { cs.advice_column(); // Bypass illogical validation. + cs.advice_column_in(SecondPhase); let enabled = cs.fixed_column(); let enabled_expr = query_expression(cs, |cs| cs.query_fixed(enabled, Rotation::cur())); - let rand = cs.challenge_usable_after(FirstPhase); + let rand = cs.challenge_usable_after(SecondPhase); let rand_expr = query_expression(cs, |cs| cs.query_challenge(rand)); let mut bus_builder = BusBuilder::::new(rand_expr); let message = 2.expr(); // Circuit 1 puts values dynamically. - let count1 = cs.advice_column(); - let count1_expr = enabled_expr.clone() - * query_expression(cs, |cs| cs.query_advice(count1, Rotation::cur())); - - let port1 = BusPortChip::connect( - cs, - &mut bus_builder, - BusOp::put(message.clone(), count1_expr), - ); + let bus_lookup = + BusLookupConfig::connect(cs, &mut bus_builder, message.clone(), enabled_expr.clone()); // Circuit 2 takes one value per row. let count2_expr = enabled_expr * 1.expr(); @@ -75,8 +68,7 @@ impl Circuit for TestCircuit { TestCircuitConfig { enabled, bus_config, - count1, - port1, + bus_lookup, port2, rand, _marker: PhantomData, @@ -104,47 +96,44 @@ impl Circuit for TestCircuit { let mut bus_assigner = BusAssigner::new(self.n_rows); + // This uses a batching method rather than row-by-row. + let mut port_assigner = PortAssigner::new(rand); + // Circuit 1 puts a message on some row. { - // Put `count` copies of the same message. + // Do normal circuit assignment logic, and obtain a message. let message = Value::known(F::from(2)); - let count = Value::known(F::from(self.n_rows as u64)); - let offset = 3; // can be anywhere. - region.assign_advice(|| "count1", config.count1, offset, || count)?; - // Set the helper cell. - let term = config - .port1 - .assign_message(&mut region, offset, message, rand)?; + // Set the `count` of copies of the same message. + let count = self.n_rows as isize; + let offset = 3; // can be anywhere. - // Report the term to the global bus. - bus_assigner.put_term(offset, count * term); + // Assign an operation to the port of this circuit, and to the shared bus. + config.bus_lookup.assign( + &mut region, + &mut port_assigner, + offset, + BusOp::put(message, count), + )?; } // Circuit 2 takes one message per row. { - // This uses a batching method rather than row-by-row. - let mut port_assigner = PortAssigner::new(rand); - // First pass: run circuit steps. for offset in 0..self.n_rows { - // … do normal circuit assignment logic … + // Do normal circuit assignment logic, and obtain a message. let message = Value::known(F::from(2)); - let count = Value::known(F::one()); - - // Collect the bus operations into the batch. - port_assigner.set_op( - offset, - config.port2.column(), - 0, - BusOp::take(message, count), - ); - } - // Final pass: assign the bus witnesses. - port_assigner.finish(&mut region, &mut bus_assigner); + // Assign an operation to the port of this circuit, and to the shared bus. + config + .port2 + .assign(&mut port_assigner, offset, BusOp::take(message, 1)); + } } + // Final pass: assign the bus witnesses. + port_assigner.finish(&mut region, &mut bus_assigner); + config .bus_config .assign(&mut region, self.n_rows, bus_assigner.terms())?; From 9b8fb6bf91d584bf33f45efb5183cfd2480b4851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 27 Sep 2023 00:50:43 +0200 Subject: [PATCH 21/67] bus: safer "connect" API --- gadgets/src/bus/bus_builder.rs | 10 +++-- gadgets/src/bus/bus_port.rs | 80 +++++++++++++--------------------- 2 files changed, 38 insertions(+), 52 deletions(-) diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs index 1ad476a3fd..e571eb58bb 100644 --- a/gadgets/src/bus/bus_builder.rs +++ b/gadgets/src/bus/bus_builder.rs @@ -28,9 +28,13 @@ impl BusBuilder { } } - /// Connect a port to the bus. - pub fn connect_port>(&mut self, meta: &mut ConstraintSystem, port: &BP) { - let term = port.create_term(meta, self.rand.clone()); + /// The random challenge used to encode messages. + pub fn rand(&self) -> Expression { + self.rand.clone() + } + + /// Add a term to the bus. + pub fn add_term(&mut self, term: BusTerm) { self.terms.push(term); } diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 8c3fb0efaa..78cdd17789 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -8,10 +8,10 @@ use halo2_proofs::{ arithmetic::FieldExt, circuit::{Region, Value}, halo2curves::group::ff::BatchInvert, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, ThirdPhase}, + plonk::{Advice, Column, ConstraintSystem, Expression, ThirdPhase}, poly::Rotation, }; -use std::{collections::HashMap, ops::Neg}; +use std::{collections::HashMap, marker::PhantomData, ops::Neg}; /// A bus operation, as expressions for circuit config. pub type BusOpExpr = BusOp, Expression>; @@ -55,15 +55,23 @@ where /// A chip to access to the bus. #[derive(Clone, Debug)] pub struct BusPortSingle { - helper: Expression, op: BusOpExpr, + helper: Expression, } impl BusPortSingle { /// Create a new bus port with a single access. /// The helper cell can be used for something else if op.count is zero. - pub fn new(helper: Expression, op: BusOpExpr) -> Self { - Self { helper, op } + pub fn connect( + meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder, + op: BusOpExpr, + helper: Expression, + ) -> Self { + let port = Self { op, helper }; + let term = port.create_term(meta, bus_builder.rand()); + bus_builder.add_term(term); + port } /// Return the witness that must be assigned to the helper cell. @@ -99,14 +107,22 @@ impl BusPort for BusPortSingle { /// degree of input expressions is more limited than with BusPortSingle. /// The helper cell can be used for something else if both op.count are zero. pub struct BusPortDual { - helper: Expression, ops: [BusOpExpr; 2], + helper: Expression, } impl BusPortDual { /// Create a new bus port with two accesses. - pub fn new(helper: Expression, ops: [BusOpExpr; 2]) -> Self { - Self { helper, ops } + pub fn connect( + meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder, + ops: [BusOpExpr; 2], + helper: Expression, + ) -> Self { + let port = Self { ops, helper }; + let term = port.create_term(meta, bus_builder.rand()); + bus_builder.add_term(term); + port } /// Return the witness that must be assigned to the helper cell. @@ -153,65 +169,31 @@ impl BusPort for BusPortDual { #[derive(Clone, Debug)] pub struct BusPortChip { helper: Column, - port: BusPortSingle, + _marker: PhantomData, } impl BusPortChip { - /// Create and connect a new bus port with a single access. + /// Create a new bus port with a single access. pub fn connect( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, op: BusOpExpr, ) -> Self { - let port = Self::new(meta, op); - bus_builder.connect_port(meta, &port); - port - } - - /// Create a new bus port with a single access. - fn new(meta: &mut ConstraintSystem, op: BusOpExpr) -> Self { let helper = meta.advice_column_in(ThirdPhase); let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); - let port = BusPortSingle::new(helper_expr, op); + BusPortSingle::connect(meta, bus_builder, op, helper_expr); - Self { helper, port } + Self { + helper, + _marker: PhantomData, + } } /// Assign an operation. pub fn assign(&self, port_assigner: &mut PortAssigner, offset: usize, op: BusOpF) { port_assigner.set_op(offset, self.helper, 0, op); } - - /// Assign the helper witness based on a message. - pub fn assign_message( - &self, - region: &mut Region<'_, F>, - offset: usize, - message: Value, - rand: Value, - ) -> Result, Error> { - let helper = BusPortSingle::helper_witness(message, rand); - region.assign_advice(|| "BusPort_helper", self.helper, offset, || helper)?; - Ok(helper) - } - - /// Assign the helper witness based on a precomputed term (without count). - pub fn assign_term( - &self, - region: &mut Region<'_, F>, - offset: usize, - term: Value, - ) -> Result<(), Error> { - region.assign_advice(|| "BusPort_helper", self.helper, offset, || term)?; - Ok(()) - } -} - -impl BusPort for BusPortChip { - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> BusTerm { - self.port.create_term(meta, rand) - } } /// A chip to access the bus. It manages its own helper columns and gives multiple accesses per row. From f1f63a59013a5fc7ef1b46f6f778971c50746207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 27 Sep 2023 02:05:31 +0200 Subject: [PATCH 22/67] bus: introduce bus_codec --- gadgets/src/bus.rs | 3 ++ gadgets/src/bus/bus_builder.rs | 45 +++++++++------------ gadgets/src/bus/bus_codec.rs | 30 ++++++++++++++ gadgets/src/bus/bus_port.rs | 36 +++++++---------- gadgets/src/bus/tests.rs | 8 ++-- zkevm-circuits/src/evm_circuit.rs | 9 +++-- zkevm-circuits/src/evm_circuit/execution.rs | 2 +- 7 files changed, 78 insertions(+), 55 deletions(-) create mode 100644 gadgets/src/bus/bus_codec.rs diff --git a/gadgets/src/bus.rs b/gadgets/src/bus.rs index 455d84ce37..1013bd4e3d 100644 --- a/gadgets/src/bus.rs +++ b/gadgets/src/bus.rs @@ -12,6 +12,9 @@ pub mod bus_port; /// A chip to expose a lookup table on a bus. pub mod bus_lookup; +/// This module encodes messages into terms. +pub mod bus_codec; + /// Utility functions. mod util; diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs index e571eb58bb..d4d32adfb6 100644 --- a/gadgets/src/bus/bus_builder.rs +++ b/gadgets/src/bus/bus_builder.rs @@ -1,21 +1,14 @@ -use halo2_proofs::{ - arithmetic::FieldExt, - circuit::Value, - plonk::{ConstraintSystem, Expression}, -}; - -use super::bus_chip::BusTerm; +use halo2_proofs::{arithmetic::FieldExt, circuit::Value, plonk::Expression}; -/// BusPort prepares a term to be added to the bus. -pub trait BusPort { - /// The term to add to the bus. This expression must be fully constrained on all rows. - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> BusTerm; -} +use super::{ + bus_chip::BusTerm, + bus_codec::{BusCodecExpr, BusCodecVal}, +}; /// BusBuilder #[derive(Debug)] pub struct BusBuilder { - rand: Expression, + codec: BusCodecExpr, terms: Vec>, } @@ -23,14 +16,14 @@ impl BusBuilder { /// Create a new bus. pub fn new(rand: Expression) -> Self { Self { - rand, + codec: BusCodecExpr::new(rand), terms: vec![], } } - /// The random challenge used to encode messages. - pub fn rand(&self) -> Expression { - self.rand.clone() + /// Return the codec for messages on this bus. + pub fn codec(&self) -> &BusCodecExpr { + &self.codec } /// Add a term to the bus. @@ -46,21 +39,28 @@ impl BusBuilder { /// BusAssigner pub struct BusAssigner { + codec: BusCodecVal, terms: Vec, unknown: bool, } impl BusAssigner { /// Create a new bus assigner with a maximum number of rows. - pub fn new(n_rows: usize) -> Self { + pub fn new(n_rows: usize, codec: BusCodecVal) -> Self { Self { + codec, terms: vec![F::zero(); n_rows], unknown: false, } } - /// Put a term value to the bus. - pub fn put_term(&mut self, offset: usize, term: Value) { + /// Return the codec for messages on this bus. + pub fn codec(&self) -> &BusCodecVal { + &self.codec + } + + /// Add a term value to the bus. + pub fn add_term(&mut self, offset: usize, term: Value) { assert!( offset < self.terms.len(), "offset={offset} out of bounds n_rows={}", @@ -77,11 +77,6 @@ impl BusAssigner { } } - /// Take a term value from the bus. - pub fn take_term(&mut self, offset: usize, term: Value) { - self.put_term(offset, -term); - } - /// Return the collected terms. pub fn terms(&self) -> Value<&[F]> { if self.unknown { diff --git a/gadgets/src/bus/bus_codec.rs b/gadgets/src/bus/bus_codec.rs new file mode 100644 index 0000000000..4950a7e2c1 --- /dev/null +++ b/gadgets/src/bus/bus_codec.rs @@ -0,0 +1,30 @@ +use std::ops::{Add, Mul}; + +use halo2_proofs::{circuit::Value, plonk::Expression}; + +/// A simple message encoder (expressions). +pub type BusCodecExpr = BusCodec>; + +/// A simple message encoder (values). +pub type BusCodecVal = BusCodec>; + +/// A message codec that adds a random value to the message. +#[derive(Clone, Debug)] +pub struct BusCodec { + rand: T, +} + +impl BusCodec +where + T: Clone + Add + Mul, +{ + /// Create a new message codec. + pub fn new(rand: T) -> Self { + Self { rand } + } + + /// Encode a message. + pub fn encode(&self, msg: T) -> T { + self.rand.clone() + msg + } +} diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 78cdd17789..2ab9cbdf13 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,6 +1,7 @@ use super::{ - bus_builder::{BusAssigner, BusBuilder, BusPort}, + bus_builder::{BusAssigner, BusBuilder}, bus_chip::BusTerm, + bus_codec::{BusCodecExpr, BusCodecVal}, util::from_isize, }; use crate::util::query_expression; @@ -69,7 +70,7 @@ impl BusPortSingle { helper: Expression, ) -> Self { let port = Self { op, helper }; - let term = port.create_term(meta, bus_builder.rand()); + let term = port.create_term(meta, bus_builder.codec()); bus_builder.add_term(term); port } @@ -79,14 +80,7 @@ impl BusPortSingle { (rand + message).map(|x| x.invert().unwrap_or(F::zero())) } - /// Return the denominator of the helper cell, to be inverted. - fn helper_denom(message: Value, rand: Value) -> Value { - rand + message - } -} - -impl BusPort for BusPortSingle { - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> BusTerm { + fn create_term(&self, meta: &mut ConstraintSystem, codec: &BusCodecExpr) -> BusTerm { let term = self.op.count() * self.helper.clone(); meta.create_gate("bus access", |_| { @@ -96,7 +90,7 @@ impl BusPort for BusPortSingle { // // If `count = 0`, then `term = 0` by definition. In that case, the helper cell is not // constrained, so it can be used for something else. - [term.clone() * (rand + self.op.message()) - self.op.count()] + [term.clone() * codec.encode(self.op.message()) - self.op.count()] }); BusTerm::verified(term) @@ -120,7 +114,7 @@ impl BusPortDual { helper: Expression, ) -> Self { let port = Self { ops, helper }; - let term = port.create_term(meta, bus_builder.rand()); + let term = port.create_term(meta, bus_builder.codec()); bus_builder.add_term(term); port } @@ -129,12 +123,10 @@ impl BusPortDual { pub fn helper_witness(messages: [Value; 2], rand: Value) -> Value { ((rand + messages[0]) * (rand + messages[1])).map(|x| x.invert().unwrap_or(F::zero())) } -} -impl BusPort for BusPortDual { - fn create_term(&self, meta: &mut ConstraintSystem, rand: Expression) -> BusTerm { - let rm_0 = rand.clone() + self.ops[0].message(); - let rm_1 = rand.clone() + self.ops[1].message(); + fn create_term(&self, meta: &mut ConstraintSystem, codec: &BusCodecExpr) -> BusTerm { + let rm_0 = codec.encode(self.ops[0].message()); + let rm_1 = codec.encode(self.ops[1].message()); // With witness: helper = 1 / rm_0 / rm_1 @@ -267,16 +259,16 @@ impl HelperBatch { /// PortAssigner computes and assigns terms into helper cells and the bus. pub struct PortAssigner { - rand: Value, + codec: BusCodecVal, batch: HelperBatch, isize, isize)>, bus_op_counter: BusOpCounter, } impl PortAssigner { /// Create a new PortAssigner. - pub fn new(rand: Value) -> Self { + pub fn new(codec: BusCodecVal) -> Self { Self { - rand, + codec, batch: HelperBatch::new(), bus_op_counter: BusOpCounter::new(), } @@ -292,7 +284,7 @@ impl PortAssigner { ) { self.bus_op_counter.set_op(&op); - let denom = BusPortSingle::helper_denom(op.message(), self.rand); + let denom = self.codec.encode(op.message()); self.batch .add_denom(denom, (offset, column, rotation, op.count())); } @@ -317,7 +309,7 @@ impl PortAssigner { // Report the term to the global bus. let global_offset = offset; // region.global_offset(offset); let count = Value::known(from_isize::(count)); - bus_assigner.put_term(global_offset, count * term); + bus_assigner.add_term(global_offset, count * term); } }); self.bus_op_counter diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index acbe57c222..94fe2d1f30 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -9,7 +9,9 @@ use halo2_proofs::{ }; use std::marker::PhantomData; -use super::{bus_builder::*, bus_chip::*, bus_lookup::BusLookupConfig, bus_port::*}; +use super::{ + bus_builder::*, bus_chip::*, bus_codec::BusCodecVal, bus_lookup::BusLookupConfig, bus_port::*, +}; #[test] fn test_bus() { @@ -94,10 +96,10 @@ impl Circuit for TestCircuit { )?; } - let mut bus_assigner = BusAssigner::new(self.n_rows); + let mut bus_assigner = BusAssigner::new(self.n_rows, BusCodecVal::new(rand)); // This uses a batching method rather than row-by-row. - let mut port_assigner = PortAssigner::new(rand); + let mut port_assigner = PortAssigner::new(bus_assigner.codec().clone()); // Circuit 1 puts a message on some row. { diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 300a9f59b4..005df48d27 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -4,6 +4,7 @@ use gadgets::bus::{ bus_builder::{BusAssigner, BusBuilder}, bus_chip::BusConfig, + bus_codec::BusCodecVal, bus_lookup::BusLookupConfig, bus_port::{BusOp, BusOpCounter, PortAssigner}, }; @@ -241,7 +242,6 @@ impl EvmCircuitConfig { /// Load byte table pub fn load_byte_table( &self, - challenges: &crate::util::Challenges>, layouter: &mut impl Layouter, bus_assigner: &mut BusAssigner, bus_op_counter: &BusOpCounter, @@ -257,7 +257,7 @@ impl EvmCircuitConfig { return Ok(()); } - let mut port_assigner = PortAssigner::new(challenges.lookup_input()); + let mut port_assigner = PortAssigner::new(bus_assigner.codec().clone()); for offset in 0..256 { let value = Value::known(F::from(offset as u64)); @@ -416,7 +416,8 @@ impl SubCircuit for EvmCircuit { config.load_fixed_table(layouter, self.fixed_table_tags.clone())?; config.pow_of_rand_table.assign(layouter, challenges)?; - let mut bus_assigner = BusAssigner::new(num_rows); + let mut bus_assigner = + BusAssigner::new(num_rows, BusCodecVal::new(challenges.lookup_input())); let (export, bus_op_counter) = config @@ -425,7 +426,7 @@ impl SubCircuit for EvmCircuit { self.exports.borrow_mut().replace(export); if let Some(bus_op_counter) = bus_op_counter { - config.load_byte_table(challenges, layouter, &mut bus_assigner, &bus_op_counter)?; + config.load_byte_table(layouter, &mut bus_assigner, &bus_op_counter)?; } layouter.assign_region( diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index d5c9e8a045..ca97cfad07 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -1077,7 +1077,7 @@ impl ExecutionConfig { return Ok(()); } - let mut port_assigner = PortAssigner::new(challenges.lookup_input()); + let mut port_assigner = PortAssigner::new(bus_assigner.codec().clone()); let mut offset = 0; From 9aa311877b324bbc6868f631981e63318e1c8591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 27 Sep 2023 06:29:30 +0200 Subject: [PATCH 23/67] bus: draft multi-value messages --- gadgets/src/bus/bus_builder.rs | 8 +- gadgets/src/bus/bus_codec.rs | 26 +++++-- gadgets/src/bus/bus_lookup.rs | 2 +- gadgets/src/bus/bus_port.rs | 82 +++++++-------------- gadgets/src/bus/tests.rs | 16 ++-- gadgets/src/bus/util.rs | 44 +++++++++++ zkevm-circuits/src/evm_circuit.rs | 13 ++-- zkevm-circuits/src/evm_circuit/execution.rs | 5 +- 8 files changed, 112 insertions(+), 84 deletions(-) diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs index d4d32adfb6..24739b8bd4 100644 --- a/gadgets/src/bus/bus_builder.rs +++ b/gadgets/src/bus/bus_builder.rs @@ -1,4 +1,4 @@ -use halo2_proofs::{arithmetic::FieldExt, circuit::Value, plonk::Expression}; +use halo2_proofs::{arithmetic::FieldExt, circuit::Value}; use super::{ bus_chip::BusTerm, @@ -14,9 +14,9 @@ pub struct BusBuilder { impl BusBuilder { /// Create a new bus. - pub fn new(rand: Expression) -> Self { + pub fn new(codec: BusCodecExpr) -> Self { Self { - codec: BusCodecExpr::new(rand), + codec, terms: vec![], } } @@ -46,7 +46,7 @@ pub struct BusAssigner { impl BusAssigner { /// Create a new bus assigner with a maximum number of rows. - pub fn new(n_rows: usize, codec: BusCodecVal) -> Self { + pub fn new(codec: BusCodecVal, n_rows: usize) -> Self { Self { codec, terms: vec![F::zero(); n_rows], diff --git a/gadgets/src/bus/bus_codec.rs b/gadgets/src/bus/bus_codec.rs index 4950a7e2c1..20d48262e7 100644 --- a/gadgets/src/bus/bus_codec.rs +++ b/gadgets/src/bus/bus_codec.rs @@ -1,30 +1,40 @@ -use std::ops::{Add, Mul}; +use std::{ + marker::PhantomData, + ops::{Add, Mul}, +}; use halo2_proofs::{circuit::Value, plonk::Expression}; /// A simple message encoder (expressions). -pub type BusCodecExpr = BusCodec>; +pub type BusCodecExpr = BusCodec, Vec>>; /// A simple message encoder (values). -pub type BusCodecVal = BusCodec>; +pub type BusCodecVal = BusCodec, Vec>>; /// A message codec that adds a random value to the message. #[derive(Clone, Debug)] -pub struct BusCodec { +pub struct BusCodec { rand: T, + _marker: PhantomData, } -impl BusCodec +impl BusCodec where T: Clone + Add + Mul, + M: IntoIterator, { /// Create a new message codec. pub fn new(rand: T) -> Self { - Self { rand } + Self { + rand, + _marker: PhantomData, + } } /// Encode a message. - pub fn encode(&self, msg: T) -> T { - self.rand.clone() + msg + pub fn encode(&self, msg: M) -> T { + // TODO: support multiple values. + let first = msg.into_iter().next().unwrap(); + self.rand.clone() + first } } diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index c905f4692d..1e10725627 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -26,7 +26,7 @@ impl BusLookupConfig { pub fn connect( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, - message: Expression, + message: Vec>, enabled: Expression, ) -> Self { let count = meta.advice_column(); diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 2ab9cbdf13..b003c80601 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -2,23 +2,22 @@ use super::{ bus_builder::{BusAssigner, BusBuilder}, bus_chip::BusTerm, bus_codec::{BusCodecExpr, BusCodecVal}, - util::from_isize, + util::{from_isize, HelperBatch}, }; use crate::util::query_expression; use halo2_proofs::{ arithmetic::FieldExt, circuit::{Region, Value}, - halo2curves::group::ff::BatchInvert, plonk::{Advice, Column, ConstraintSystem, Expression, ThirdPhase}, poly::Rotation, }; use std::{collections::HashMap, marker::PhantomData, ops::Neg}; /// A bus operation, as expressions for circuit config. -pub type BusOpExpr = BusOp, Expression>; +pub type BusOpExpr = BusOp>, Expression>; /// A bus operation, as values for circuit assignment. -pub type BusOpF = BusOp, isize>; +pub type BusOpF = BusOp>, isize>; /// A bus operation. #[derive(Clone, Debug)] @@ -82,6 +81,7 @@ impl BusPortSingle { fn create_term(&self, meta: &mut ConstraintSystem, codec: &BusCodecExpr) -> BusTerm { let term = self.op.count() * self.helper.clone(); + let enc = codec.encode(self.op.message()); meta.create_gate("bus access", |_| { // Verify that `term = count / (rand + message)`. @@ -90,7 +90,7 @@ impl BusPortSingle { // // If `count = 0`, then `term = 0` by definition. In that case, the helper cell is not // constrained, so it can be used for something else. - [term.clone() * codec.encode(self.op.message()) - self.op.count()] + [term.clone() * enc - self.op.count()] }); BusTerm::verified(term) @@ -218,45 +218,6 @@ impl BusPortMulti { } } -/// TermBatch calculates helper witnesses, in batches for better performance. -struct HelperBatch { - denoms: Vec<(F, INFO)>, - unknown: bool, -} - -impl HelperBatch { - /// Create a new term batch. - fn new() -> Self { - Self { - denoms: vec![], - unknown: false, - } - } - - /// Add a helper denominator to the batch. Some `info` can be attached for later use. - fn add_denom(&mut self, denom: Value, info: INFO) { - if self.unknown { - return; - } - if denom.is_none() { - self.unknown = true; - self.denoms.clear(); - } else { - denom.map(|denom| self.denoms.push((denom, info))); - } - } - - /// Return the inverse of all denominators and their associated info. - fn invert(mut self) -> Value> { - if self.unknown { - Value::unknown() - } else { - self.denoms.iter_mut().map(|(d, _)| d).batch_invert(); - Value::known(self.denoms) - } - } -} - /// PortAssigner computes and assigns terms into helper cells and the bus. pub struct PortAssigner { codec: BusCodecVal, @@ -330,34 +291,41 @@ impl BusOpCounter { /// Report an operation. pub fn set_op(&mut self, op: &BusOpF) { - op.message().map(|message| { + if let Some(key) = Self::to_key(&op.message()) { self.counts - .entry(Self::to_key(message)) + .entry(key) .and_modify(|c| *c = *c + op.count()) .or_insert_with(|| op.count()); - }); + } } /// Count how many times a message was taken (net of puts). - pub fn count_takes(&self, message: Value) -> isize { + pub fn count_takes(&self, message: &Vec>) -> isize { (-self.count_ops(message)).max(0) } /// Count how many times a message was put (net of takes). - pub fn count_puts(&self, message: Value) -> isize { + pub fn count_puts(&self, message: &Vec>) -> isize { self.count_ops(message).max(0) } /// Count how many times a message was put (net positive) or taken (net negative). - fn count_ops(&self, message: Value) -> isize { - let mut count = 0; - message.map(|message| { - count = *self.counts.get(&Self::to_key(message)).unwrap_or(&0); - }); - count + fn count_ops(&self, message: &Vec>) -> isize { + if let Some(key) = Self::to_key(message) { + *self.counts.get(&key).unwrap_or(&0) + } else { + 0 + } } - fn to_key(message: F) -> Vec { - Vec::from(message.to_repr().as_ref()) + fn to_key(message: &Vec>) -> Option> { + let mut bytes = vec![]; + for v in message { + if v.is_none() { + return None; + } + v.map(|v| bytes.extend_from_slice(v.to_repr().as_ref())); + } + Some(bytes) } } diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 94fe2d1f30..92cee6ae2a 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -10,7 +10,11 @@ use halo2_proofs::{ use std::marker::PhantomData; use super::{ - bus_builder::*, bus_chip::*, bus_codec::BusCodecVal, bus_lookup::BusLookupConfig, bus_port::*, + bus_builder::*, + bus_chip::*, + bus_codec::{BusCodecExpr, BusCodecVal}, + bus_lookup::BusLookupConfig, + bus_port::*, }; #[test] @@ -51,9 +55,9 @@ impl Circuit for TestCircuit { let rand = cs.challenge_usable_after(SecondPhase); let rand_expr = query_expression(cs, |cs| cs.query_challenge(rand)); - let mut bus_builder = BusBuilder::::new(rand_expr); + let mut bus_builder = BusBuilder::::new(BusCodecExpr::new(rand_expr)); - let message = 2.expr(); + let message = vec![2.expr()]; // Circuit 1 puts values dynamically. let bus_lookup = @@ -96,7 +100,7 @@ impl Circuit for TestCircuit { )?; } - let mut bus_assigner = BusAssigner::new(self.n_rows, BusCodecVal::new(rand)); + let mut bus_assigner = BusAssigner::new(BusCodecVal::new(rand), self.n_rows); // This uses a batching method rather than row-by-row. let mut port_assigner = PortAssigner::new(bus_assigner.codec().clone()); @@ -104,7 +108,7 @@ impl Circuit for TestCircuit { // Circuit 1 puts a message on some row. { // Do normal circuit assignment logic, and obtain a message. - let message = Value::known(F::from(2)); + let message = vec![Value::known(F::from(2))]; // Set the `count` of copies of the same message. let count = self.n_rows as isize; @@ -124,7 +128,7 @@ impl Circuit for TestCircuit { // First pass: run circuit steps. for offset in 0..self.n_rows { // Do normal circuit assignment logic, and obtain a message. - let message = Value::known(F::from(2)); + let message = vec![Value::known(F::from(2))]; // Assign an operation to the port of this circuit, and to the shared bus. config diff --git a/gadgets/src/bus/util.rs b/gadgets/src/bus/util.rs index 574715627c..826b88a6a1 100644 --- a/gadgets/src/bus/util.rs +++ b/gadgets/src/bus/util.rs @@ -1,5 +1,10 @@ use std::ops::Neg; +use halo2_proofs::{ + circuit::Value, + halo2curves::group::{ff, ff::BatchInvert}, +}; + /// Convert an isize to a field element. pub fn from_isize + Neg>(x: isize) -> F { if x < 0 { @@ -8,3 +13,42 @@ pub fn from_isize + Neg>(x: isize) -> F { F::from(x as u64) } } + +/// TermBatch calculates helper witnesses, in batches for better performance. +pub struct HelperBatch { + denoms: Vec<(F, INFO)>, + unknown: bool, +} + +impl HelperBatch { + /// Create a new term batch. + pub fn new() -> Self { + Self { + denoms: vec![], + unknown: false, + } + } + + /// Add a helper denominator to the batch. Some `info` can be attached for later use. + pub fn add_denom(&mut self, denom: Value, info: INFO) { + if self.unknown { + return; + } + if denom.is_none() { + self.unknown = true; + self.denoms.clear(); + } else { + denom.map(|denom| self.denoms.push((denom, info))); + } + } + + /// Return the inverse of all denominators and their associated info. + pub fn invert(mut self) -> Value> { + if self.unknown { + Value::unknown() + } else { + self.denoms.iter_mut().map(|(d, _)| d).batch_invert(); + Value::known(self.denoms) + } + } +} diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 005df48d27..c285caed65 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -4,7 +4,7 @@ use gadgets::bus::{ bus_builder::{BusAssigner, BusBuilder}, bus_chip::BusConfig, - bus_codec::BusCodecVal, + bus_codec::{BusCodecExpr, BusCodecVal}, bus_lookup::BusLookupConfig, bus_port::{BusOp, BusOpCounter, PortAssigner}, }; @@ -140,7 +140,7 @@ impl EvmCircuitConfig { let byte_table = [(); 1].map(|_| meta.fixed_column()); let enable_table = meta.fixed_column(); - let mut bus_builder = BusBuilder::::new(challenges.lookup_input()); + let mut bus_builder = BusBuilder::::new(BusCodecExpr::new(challenges.lookup_input())); let table_to_bus = Self::configure_table_to_bus(meta, &mut bus_builder, &byte_table, enable_table); @@ -211,7 +211,7 @@ impl EvmCircuitConfig { ) -> BusLookupConfig { let byte_expr = query_expression(meta, |meta| byte_table.table_exprs(meta)[0].clone()); let enabled = query_expression(meta, |meta| meta.query_fixed(enabled, Rotation::cur())); - BusLookupConfig::connect(meta, bus_builder, byte_expr, enabled) + BusLookupConfig::connect(meta, bus_builder, vec![byte_expr], enabled) } } @@ -271,12 +271,13 @@ impl EvmCircuitConfig { || Value::known(F::one()), )?; - let count = bus_op_counter.count_takes(value); + let message = vec![value]; + let count = bus_op_counter.count_takes(&message); self.table_to_bus.assign( &mut region, &mut port_assigner, offset, - BusOp::put(value, count), + BusOp::put(message, count), )?; } @@ -417,7 +418,7 @@ impl SubCircuit for EvmCircuit { config.pow_of_rand_table.assign(layouter, challenges)?; let mut bus_assigner = - BusAssigner::new(num_rows, BusCodecVal::new(challenges.lookup_input())); + BusAssigner::new(BusCodecVal::new(challenges.lookup_input()), num_rows); let (export, bus_op_counter) = config diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index ca97cfad07..86a504e26e 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -934,7 +934,7 @@ impl ExecutionConfig { .columns() .iter() .filter(|column| column.cell_type == CellType::LookupByte) - .map(|column| BusOp::take(column.expr(), q_usable.clone())) + .map(|column| BusOp::take(vec![column.expr()], q_usable.clone())) .collect::>(); BusPortMulti::connect(meta, bus_builder, ops) @@ -1336,7 +1336,8 @@ impl ExecutionConfig { .iter() .map(|column_index| { let byte = region.get_advice(offset, *column_index, Rotation::cur()); - BusOp::take(Value::known(byte), 1) + let message = vec![Value::known(byte)]; + BusOp::take(message, 1) }) .collect::>(); self.bus_port.assign(port_assigner, offset, ops); From 9ecaafeba76584bd0aa751fc16cde03ad7424643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 29 Sep 2023 13:25:03 +0200 Subject: [PATCH 24/67] bus: attach message type to builder --- gadgets/src/bus/bus_builder.rs | 14 +- gadgets/src/bus/bus_codec.rs | 26 ++-- gadgets/src/bus/bus_lookup.rs | 7 +- gadgets/src/bus/bus_port.rs | 135 +++++++++++--------- gadgets/src/bus/tests.rs | 2 +- zkevm-circuits/src/evm_circuit.rs | 6 +- zkevm-circuits/src/evm_circuit/execution.rs | 6 +- 7 files changed, 108 insertions(+), 88 deletions(-) diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs index 24739b8bd4..d68895853f 100644 --- a/gadgets/src/bus/bus_builder.rs +++ b/gadgets/src/bus/bus_builder.rs @@ -1,20 +1,20 @@ -use halo2_proofs::{arithmetic::FieldExt, circuit::Value}; +use halo2_proofs::{arithmetic::FieldExt, circuit::Value, plonk::Expression}; use super::{ bus_chip::BusTerm, - bus_codec::{BusCodecExpr, BusCodecVal}, + bus_codec::{BusCodec, BusCodecVal}, }; /// BusBuilder #[derive(Debug)] -pub struct BusBuilder { - codec: BusCodecExpr, +pub struct BusBuilder { + codec: BusCodec, M>, terms: Vec>, } -impl BusBuilder { +impl BusBuilder { /// Create a new bus. - pub fn new(codec: BusCodecExpr) -> Self { + pub fn new(codec: BusCodec, M>) -> Self { Self { codec, terms: vec![], @@ -22,7 +22,7 @@ impl BusBuilder { } /// Return the codec for messages on this bus. - pub fn codec(&self) -> &BusCodecExpr { + pub fn codec(&self) -> &BusCodec, M> { &self.codec } diff --git a/gadgets/src/bus/bus_codec.rs b/gadgets/src/bus/bus_codec.rs index 4e42ec135e..6cf5515da5 100644 --- a/gadgets/src/bus/bus_codec.rs +++ b/gadgets/src/bus/bus_codec.rs @@ -5,11 +5,14 @@ use std::{ use halo2_proofs::{circuit::Value, plonk::Expression}; -/// A simple message encoder (expressions). -pub type BusCodecExpr = BusCodec, Vec>>; +/// The default message type is simply a vector of items. +pub type DefaultMsg = Vec; -/// A simple message encoder (values). -pub type BusCodecVal = BusCodec, Vec>>; +/// The codec for the default message type (expressions form). +pub type BusCodecExpr>> = BusCodec, M>; + +/// The codec for the default message type (values form). +pub type BusCodecVal>> = BusCodec, M>; /// A message codec that adds a random value to the message. #[derive(Clone, Debug)] @@ -31,8 +34,11 @@ where } } - /// Encode a message. - pub fn encode(&self, msg: M) -> T { + /// Compress a message into a field element, such that: + /// - the map from message to elements is collision-resistant. + /// - the inverses of the elements are linearly independent. + /// - Elements are non-zero. + pub fn compress(&self, msg: M) -> T { // TODO: support multiple values. let first = msg.into_items().next().unwrap().into(); self.rand.clone() + first @@ -40,7 +46,7 @@ where } /// A trait for messages that can be encoded. -pub trait BusMessage { +pub trait BusMessage: Clone { /// The item iterator type. type IntoIter: Iterator; @@ -51,7 +57,7 @@ pub trait BusMessage { // The default implementation of `BusMessage` for iterators of compatible types. impl BusMessage for I where - I: IntoIterator, + I: IntoIterator + Clone, I::Item: Into, { type IntoIter = std::iter::Map T>; @@ -87,13 +93,13 @@ mod tests { // Using vectors as message type. let codec = BusCodec::new(Fr::one()); let msg = vec![1u64, 2u64, 3u64]; - assert_eq!(codec.encode(msg), Fr::from(2)); + assert_eq!(codec.compress(msg), Fr::from(2)); } { // Using a custom message type. let codec = BusCodec::new(Fr::one()); let msg = TestMessage { a: 1, b: 2 }; - assert_eq!(codec.encode(msg.clone()), Fr::from(2)); + assert_eq!(codec.compress(msg.clone()), Fr::from(2)); } } } diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index 1e10725627..de75c0acc4 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -4,6 +4,7 @@ use crate::util::query_expression; use super::{ bus_builder::BusBuilder, + bus_codec::BusMessage, bus_port::{BusOp, BusOpF, BusPortChip, PortAssigner}, util::from_isize, }; @@ -23,10 +24,10 @@ pub struct BusLookupConfig { impl BusLookupConfig { /// Create and connect a new BusLookup circuit from the expressions of message and count. - pub fn connect( + pub fn connect>>( meta: &mut ConstraintSystem, - bus_builder: &mut BusBuilder, - message: Vec>, + bus_builder: &mut BusBuilder, + message: M, enabled: Expression, ) -> Self { let count = meta.advice_column(); diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index b003c80601..7fbe3df730 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,7 +1,7 @@ use super::{ bus_builder::{BusAssigner, BusBuilder}, bus_chip::BusTerm, - bus_codec::{BusCodecExpr, BusCodecVal}, + bus_codec::{BusCodecExpr, BusCodecVal, BusMessage, DefaultMsg}, util::{from_isize, HelperBatch}, }; use crate::util::query_expression; @@ -14,10 +14,10 @@ use halo2_proofs::{ use std::{collections::HashMap, marker::PhantomData, ops::Neg}; /// A bus operation, as expressions for circuit config. -pub type BusOpExpr = BusOp>, Expression>; +pub type BusOpX = BusOp>; /// A bus operation, as values for circuit assignment. -pub type BusOpF = BusOp>, isize>; +pub type BusOpF = BusOp>, isize>; /// A bus operation. #[derive(Clone, Debug)] @@ -54,43 +54,49 @@ where /// A chip to access to the bus. #[derive(Clone, Debug)] -pub struct BusPortSingle { - op: BusOpExpr, - helper: Expression, -} +pub struct BusPortSingle; -impl BusPortSingle { +impl BusPortSingle { /// Create a new bus port with a single access. /// The helper cell can be used for something else if op.count is zero. - pub fn connect( + pub fn connect>>( meta: &mut ConstraintSystem, - bus_builder: &mut BusBuilder, - op: BusOpExpr, + bus_builder: &mut BusBuilder, + op: BusOpX, helper: Expression, - ) -> Self { - let port = Self { op, helper }; - let term = port.create_term(meta, bus_builder.codec()); + ) { + let term = Self::create_term(meta, bus_builder.codec(), op, helper); bus_builder.add_term(term); - port } /// Return the witness that must be assigned to the helper cell. - pub fn helper_witness(message: Value, rand: Value) -> Value { - (rand + message).map(|x| x.invert().unwrap_or(F::zero())) + /// Prefer using PortAssigner instead. + pub fn helper_witness>>( + codec: &BusCodecVal, + message: M, + ) -> Value { + codec + .compress(message) + .map(|x| x.invert().unwrap_or(F::zero())) } - fn create_term(&self, meta: &mut ConstraintSystem, codec: &BusCodecExpr) -> BusTerm { - let term = self.op.count() * self.helper.clone(); - let enc = codec.encode(self.op.message()); + fn create_term>>( + meta: &mut ConstraintSystem, + codec: &BusCodecExpr, + op: BusOpX, + helper: Expression, + ) -> BusTerm { + let term = op.count() * helper.clone(); + let denom = codec.compress(op.message()); meta.create_gate("bus access", |_| { - // Verify that `term = count / (rand + message)`. + // Verify that `term = count / denom`. // - // With witness: helper = 1 / (rand + message) + // With witness: helper = 1 / denom // // If `count = 0`, then `term = 0` by definition. In that case, the helper cell is not // constrained, so it can be used for something else. - [term.clone() * enc - self.op.count()] + [term.clone() * denom - op.count()] }); BusTerm::verified(term) @@ -100,47 +106,52 @@ impl BusPortSingle { /// A chip with two accesses to the bus. BusPortDual uses only one helper cell, however the /// degree of input expressions is more limited than with BusPortSingle. /// The helper cell can be used for something else if both op.count are zero. -pub struct BusPortDual { - ops: [BusOpExpr; 2], - helper: Expression, -} +pub struct BusPortDual; -impl BusPortDual { +impl BusPortDual { /// Create a new bus port with two accesses. - pub fn connect( + pub fn connect>>( meta: &mut ConstraintSystem, - bus_builder: &mut BusBuilder, - ops: [BusOpExpr; 2], + bus_builder: &mut BusBuilder, + ops: [BusOpX; 2], helper: Expression, - ) -> Self { - let port = Self { ops, helper }; - let term = port.create_term(meta, bus_builder.codec()); + ) { + let term = Self::create_term(meta, bus_builder.codec(), ops, helper); bus_builder.add_term(term); - port } /// Return the witness that must be assigned to the helper cell. - pub fn helper_witness(messages: [Value; 2], rand: Value) -> Value { - ((rand + messages[0]) * (rand + messages[1])).map(|x| x.invert().unwrap_or(F::zero())) + /// Prefer using PortAssigner instead. + pub fn helper_witness>>( + codec: &BusCodecVal, + messages: [M; 2], + ) -> Value { + let [m0, m1] = messages; + (codec.compress(m0) * codec.compress(m1)).map(|x| x.invert().unwrap_or(F::zero())) } - fn create_term(&self, meta: &mut ConstraintSystem, codec: &BusCodecExpr) -> BusTerm { - let rm_0 = codec.encode(self.ops[0].message()); - let rm_1 = codec.encode(self.ops[1].message()); + fn create_term>>( + meta: &mut ConstraintSystem, + codec: &BusCodecExpr, + ops: [BusOpX; 2], + helper: Expression, + ) -> BusTerm { + let denom_0 = codec.compress(ops[0].message()); + let denom_1 = codec.compress(ops[1].message()); - // With witness: helper = 1 / rm_0 / rm_1 + // With witness: helper = 1 / (denom_0 * denom_1) - // term_0 = count_0 * helper * rm_1 - let count_0 = self.ops[0].count(); - let term_0 = count_0.clone() * self.helper.clone() * rm_1.clone(); + // term_0 = count_0 * helper * denom_1 + let count_0 = ops[0].count(); + let term_0 = count_0.clone() * helper.clone() * denom_1.clone(); - // term_1 = count_1 * helper * rm_0 - let count_1 = self.ops[1].count(); - let term_1 = count_1.clone() * self.helper.clone() * rm_0.clone(); + // term_1 = count_1 * helper * denom_0 + let count_1 = ops[1].count(); + let term_1 = count_1.clone() * helper.clone() * denom_0.clone(); // Verify that: - // term_0 == count_0 / (rand + message_0) - // term_0 * rm_0 - count_0 == 0 + // term_0 == count_0 / denom_0 + // term_0 * denom_0 - count_0 == 0 // // And the same for term_1. // @@ -148,8 +159,8 @@ impl BusPortDual { // can be used for something else. meta.create_gate("bus access (dual)", |_| { [ - term_0.clone() * rm_0 - count_0, - term_1.clone() * rm_1 - count_1, + term_0.clone() * denom_0 - count_0, + term_1.clone() * denom_1 - count_1, ] }); @@ -166,10 +177,10 @@ pub struct BusPortChip { impl BusPortChip { /// Create a new bus port with a single access. - pub fn connect( + pub fn connect>>( meta: &mut ConstraintSystem, - bus_builder: &mut BusBuilder, - op: BusOpExpr, + bus_builder: &mut BusBuilder, + op: BusOpX, ) -> Self { let helper = meta.advice_column_in(ThirdPhase); let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); @@ -197,10 +208,10 @@ pub struct BusPortMulti { impl BusPortMulti { /// Create and connect a new bus port with multiple accesses. - pub fn connect( + pub fn connect>>( meta: &mut ConstraintSystem, - bus_builder: &mut BusBuilder, - ops: Vec>, + bus_builder: &mut BusBuilder, + ops: Vec>, ) -> Self { let ports = ops .into_iter() @@ -245,7 +256,7 @@ impl PortAssigner { ) { self.bus_op_counter.set_op(&op); - let denom = self.codec.encode(op.message()); + let denom = self.codec.compress(op.message()); self.batch .add_denom(denom, (offset, column, rotation, op.count())); } @@ -300,17 +311,17 @@ impl BusOpCounter { } /// Count how many times a message was taken (net of puts). - pub fn count_takes(&self, message: &Vec>) -> isize { + pub fn count_takes(&self, message: &DefaultMsg>) -> isize { (-self.count_ops(message)).max(0) } /// Count how many times a message was put (net of takes). - pub fn count_puts(&self, message: &Vec>) -> isize { + pub fn count_puts(&self, message: &DefaultMsg>) -> isize { self.count_ops(message).max(0) } /// Count how many times a message was put (net positive) or taken (net negative). - fn count_ops(&self, message: &Vec>) -> isize { + fn count_ops(&self, message: &DefaultMsg>) -> isize { if let Some(key) = Self::to_key(message) { *self.counts.get(&key).unwrap_or(&0) } else { @@ -318,7 +329,7 @@ impl BusOpCounter { } } - fn to_key(message: &Vec>) -> Option> { + fn to_key(message: &DefaultMsg>) -> Option> { let mut bytes = vec![]; for v in message { if v.is_none() { diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 92cee6ae2a..4aa4cd812f 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -55,7 +55,7 @@ impl Circuit for TestCircuit { let rand = cs.challenge_usable_after(SecondPhase); let rand_expr = query_expression(cs, |cs| cs.query_challenge(rand)); - let mut bus_builder = BusBuilder::::new(BusCodecExpr::new(rand_expr)); + let mut bus_builder = BusBuilder::new(BusCodecExpr::new(rand_expr)); let message = vec![2.expr()]; diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index c285caed65..eb2e7e6b56 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -116,6 +116,8 @@ pub struct Unreachable { _private: (), } +type ByteMsgX = Vec>; + impl EvmCircuitConfig { /// Configure EvmCircuitConfig #[allow(clippy::too_many_arguments)] @@ -140,7 +142,7 @@ impl EvmCircuitConfig { let byte_table = [(); 1].map(|_| meta.fixed_column()); let enable_table = meta.fixed_column(); - let mut bus_builder = BusBuilder::::new(BusCodecExpr::new(challenges.lookup_input())); + let mut bus_builder = BusBuilder::new(BusCodecExpr::new(challenges.lookup_input())); let table_to_bus = Self::configure_table_to_bus(meta, &mut bus_builder, &byte_table, enable_table); @@ -205,7 +207,7 @@ impl EvmCircuitConfig { fn configure_table_to_bus( meta: &mut ConstraintSystem, - bus_builder: &mut BusBuilder, + bus_builder: &mut BusBuilder>, byte_table: &dyn LookupTable, enabled: Column, ) -> BusLookupConfig { diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 86a504e26e..0e511de3e8 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -6,7 +6,7 @@ use super::{ RW_TABLE_LOOKUPS, SIG_TABLE_LOOKUPS, TX_TABLE_LOOKUPS, }, util::{instrumentation::Instrument, CachedRegion, CellManager, StoredExpression}, - EvmCircuitExports, + EvmCircuitExports, ByteMsgX, }; use crate::{ evm_circuit::{ @@ -377,7 +377,7 @@ impl ExecutionConfig { pub(crate) fn configure( meta: &mut ConstraintSystem, challenges: Challenges>, - bus_builder: &mut BusBuilder, + bus_builder: &mut BusBuilder>, fixed_table: &dyn LookupTable, byte_table: &dyn LookupTable, tx_table: &dyn LookupTable, @@ -924,7 +924,7 @@ impl ExecutionConfig { fn configure_bus( meta: &mut ConstraintSystem, - bus_builder: &mut BusBuilder, + bus_builder: &mut BusBuilder>, q_usable: Selector, cell_manager: &CellManager, ) -> BusPortMulti { From a45d8e033577fcf8b19b59a81d4625653fcb44aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 29 Sep 2023 16:50:45 +0200 Subject: [PATCH 25/67] bus: make generic and type-safe with message types --- gadgets/src/bus/bus_builder.rs | 20 +++---- gadgets/src/bus/bus_codec.rs | 15 +++-- gadgets/src/bus/bus_lookup.rs | 8 +-- gadgets/src/bus/bus_port.rs | 65 +++++++++++++-------- zkevm-circuits/src/evm_circuit.rs | 12 ++-- zkevm-circuits/src/evm_circuit/execution.rs | 24 +++++--- zkevm-circuits/src/evm_circuit/table.rs | 5 +- 7 files changed, 87 insertions(+), 62 deletions(-) diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs index d68895853f..5ebf0d7e13 100644 --- a/gadgets/src/bus/bus_builder.rs +++ b/gadgets/src/bus/bus_builder.rs @@ -1,20 +1,20 @@ -use halo2_proofs::{arithmetic::FieldExt, circuit::Value, plonk::Expression}; +use halo2_proofs::{arithmetic::FieldExt, circuit::Value}; use super::{ bus_chip::BusTerm, - bus_codec::{BusCodec, BusCodecVal}, + bus_codec::{BusCodecExpr, BusCodecVal}, }; /// BusBuilder #[derive(Debug)] pub struct BusBuilder { - codec: BusCodec, M>, + codec: BusCodecExpr, terms: Vec>, } impl BusBuilder { /// Create a new bus. - pub fn new(codec: BusCodec, M>) -> Self { + pub fn new(codec: BusCodecExpr) -> Self { Self { codec, terms: vec![], @@ -22,7 +22,7 @@ impl BusBuilder { } /// Return the codec for messages on this bus. - pub fn codec(&self) -> &BusCodec, M> { + pub fn codec(&self) -> &BusCodecExpr { &self.codec } @@ -38,15 +38,15 @@ impl BusBuilder { } /// BusAssigner -pub struct BusAssigner { - codec: BusCodecVal, +pub struct BusAssigner { + codec: BusCodecVal, terms: Vec, unknown: bool, } -impl BusAssigner { +impl BusAssigner { /// Create a new bus assigner with a maximum number of rows. - pub fn new(codec: BusCodecVal, n_rows: usize) -> Self { + pub fn new(codec: BusCodecVal, n_rows: usize) -> Self { Self { codec, terms: vec![F::zero(); n_rows], @@ -55,7 +55,7 @@ impl BusAssigner { } /// Return the codec for messages on this bus. - pub fn codec(&self) -> &BusCodecVal { + pub fn codec(&self) -> &BusCodecVal { &self.codec } diff --git a/gadgets/src/bus/bus_codec.rs b/gadgets/src/bus/bus_codec.rs index 6cf5515da5..d5656aecc6 100644 --- a/gadgets/src/bus/bus_codec.rs +++ b/gadgets/src/bus/bus_codec.rs @@ -5,14 +5,17 @@ use std::{ use halo2_proofs::{circuit::Value, plonk::Expression}; -/// The default message type is simply a vector of items. -pub type DefaultMsg = Vec; +/// The default message type for expressions. +pub type DefaultMsgExpr = Vec>; -/// The codec for the default message type (expressions form). -pub type BusCodecExpr>> = BusCodec, M>; +/// The default message type for values. +pub type DefaultMsgVal = Vec>; -/// The codec for the default message type (values form). -pub type BusCodecVal>> = BusCodec, M>; +/// The codec for expressions. +pub type BusCodecExpr = BusCodec, M>; + +/// The codec for values. +pub type BusCodecVal = BusCodec, M>; /// A message codec that adds a random value to the message. #[derive(Clone, Debug)] diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index de75c0acc4..b41bc16b22 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -5,7 +5,7 @@ use crate::util::query_expression; use super::{ bus_builder::BusBuilder, bus_codec::BusMessage, - bus_port::{BusOp, BusOpF, BusPortChip, PortAssigner}, + bus_port::{BusOp, BusPortChip, PortAssigner, BusOpA}, util::from_isize, }; use halo2_proofs::{ @@ -40,12 +40,12 @@ impl BusLookupConfig { } /// Assign a lookup operation. - pub fn assign( + pub fn assign>>( &self, region: &mut Region<'_, F>, - port_assigner: &mut PortAssigner, + port_assigner: &mut PortAssigner, offset: usize, - op: BusOpF, + op: BusOpA, ) -> Result<(), Error> { region.assign_advice( || "BusLookup", diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 7fbe3df730..18d8568916 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,7 +1,7 @@ use super::{ bus_builder::{BusAssigner, BusBuilder}, bus_chip::BusTerm, - bus_codec::{BusCodecExpr, BusCodecVal, BusMessage, DefaultMsg}, + bus_codec::{BusCodecExpr, BusCodecVal, BusMessage}, util::{from_isize, HelperBatch}, }; use crate::util::query_expression; @@ -17,7 +17,7 @@ use std::{collections::HashMap, marker::PhantomData, ops::Neg}; pub type BusOpX = BusOp>; /// A bus operation, as values for circuit assignment. -pub type BusOpF = BusOp>, isize>; +pub type BusOpA = BusOp; /// A bus operation. #[derive(Clone, Debug)] @@ -194,7 +194,12 @@ impl BusPortChip { } /// Assign an operation. - pub fn assign(&self, port_assigner: &mut PortAssigner, offset: usize, op: BusOpF) { + pub fn assign>>( + &self, + port_assigner: &mut PortAssigner, + offset: usize, + op: BusOpA, + ) { port_assigner.set_op(offset, self.helper, 0, op); } } @@ -221,7 +226,12 @@ impl BusPortMulti { } /// Assign operations. - pub fn assign(&self, port_assigner: &mut PortAssigner, offset: usize, ops: Vec>) { + pub fn assign>>( + &self, + port_assigner: &mut PortAssigner, + offset: usize, + ops: Vec>, + ) { assert_eq!(self.ports.len(), ops.len()); for (port, op) in self.ports.iter().zip(ops) { port.assign(port_assigner, offset, op); @@ -230,15 +240,16 @@ impl BusPortMulti { } /// PortAssigner computes and assigns terms into helper cells and the bus. -pub struct PortAssigner { - codec: BusCodecVal, + +pub struct PortAssigner { + codec: BusCodecVal, batch: HelperBatch, isize, isize)>, - bus_op_counter: BusOpCounter, + bus_op_counter: BusOpCounter, } -impl PortAssigner { +impl>> PortAssigner { /// Create a new PortAssigner. - pub fn new(codec: BusCodecVal) -> Self { + pub fn new(codec: BusCodecVal) -> Self { Self { codec, batch: HelperBatch::new(), @@ -252,7 +263,7 @@ impl PortAssigner { offset: usize, column: Column, rotation: isize, - op: BusOpF, + op: BusOpA, ) { self.bus_op_counter.set_op(&op); @@ -265,8 +276,8 @@ impl PortAssigner { pub fn finish( self, region: &mut Region<'_, F>, - bus_assigner: &mut BusAssigner, - ) -> BusOpCounter { + bus_assigner: &mut BusAssigner, + ) -> BusOpCounter { self.batch.invert().map(|terms| { // The batch has converted the messages into bus terms. for (term, (offset, column, rotation, count)) in terms { @@ -289,20 +300,24 @@ impl PortAssigner { } /// OpCounter tracks the messages taken, to help generating the puts. -#[derive(Clone, Debug, Default)] -pub struct BusOpCounter { +#[derive(Clone, Debug)] +pub struct BusOpCounter { counts: HashMap, isize>, + _marker: PhantomData<(F, M)>, } -impl BusOpCounter { - /// Create a new OpCounter. +impl>> BusOpCounter { + /// Create a new BusOpCounter. pub fn new() -> Self { - Self::default() + Self { + counts: HashMap::new(), + _marker: PhantomData, + } } /// Report an operation. - pub fn set_op(&mut self, op: &BusOpF) { - if let Some(key) = Self::to_key(&op.message()) { + pub fn set_op(&mut self, op: &BusOpA) { + if let Some(key) = Self::to_key(op.message()) { self.counts .entry(key) .and_modify(|c| *c = *c + op.count()) @@ -311,27 +326,27 @@ impl BusOpCounter { } /// Count how many times a message was taken (net of puts). - pub fn count_takes(&self, message: &DefaultMsg>) -> isize { + pub fn count_takes(&self, message: &M) -> isize { (-self.count_ops(message)).max(0) } /// Count how many times a message was put (net of takes). - pub fn count_puts(&self, message: &DefaultMsg>) -> isize { + pub fn count_puts(&self, message: &M) -> isize { self.count_ops(message).max(0) } /// Count how many times a message was put (net positive) or taken (net negative). - fn count_ops(&self, message: &DefaultMsg>) -> isize { - if let Some(key) = Self::to_key(message) { + fn count_ops(&self, message: &M) -> isize { + if let Some(key) = Self::to_key(message.clone()) { *self.counts.get(&key).unwrap_or(&0) } else { 0 } } - fn to_key(message: &DefaultMsg>) -> Option> { + fn to_key(message: M) -> Option> { let mut bytes = vec![]; - for v in message { + for v in message.into_items() { if v.is_none() { return None; } diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index eb2e7e6b56..8096b13f0e 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -40,7 +40,7 @@ use eth_types::Field; use execution::ExecutionConfig; use itertools::Itertools; use strum::IntoEnumIterator; -use table::FixedTableTag; +use table::{ByteMsgV, ByteMsgX, FixedTableTag}; use witness::Block; /// EvmCircuitConfig implements verification of execution trace of a block. @@ -116,8 +116,6 @@ pub struct Unreachable { _private: (), } -type ByteMsgX = Vec>; - impl EvmCircuitConfig { /// Configure EvmCircuitConfig #[allow(clippy::too_many_arguments)] @@ -213,7 +211,7 @@ impl EvmCircuitConfig { ) -> BusLookupConfig { let byte_expr = query_expression(meta, |meta| byte_table.table_exprs(meta)[0].clone()); let enabled = query_expression(meta, |meta| meta.query_fixed(enabled, Rotation::cur())); - BusLookupConfig::connect(meta, bus_builder, vec![byte_expr], enabled) + BusLookupConfig::connect(meta, bus_builder, [byte_expr], enabled) } } @@ -245,8 +243,8 @@ impl EvmCircuitConfig { pub fn load_byte_table( &self, layouter: &mut impl Layouter, - bus_assigner: &mut BusAssigner, - bus_op_counter: &BusOpCounter, + bus_assigner: &mut BusAssigner>, + bus_op_counter: &BusOpCounter>, ) -> Result<(), Error> { let mut closure_count = 0; @@ -273,7 +271,7 @@ impl EvmCircuitConfig { || Value::known(F::one()), )?; - let message = vec![value]; + let message = [value]; let count = bus_op_counter.count_takes(&message); self.table_to_bus.assign( &mut region, diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 0e511de3e8..4ee16c6860 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -6,13 +6,13 @@ use super::{ RW_TABLE_LOOKUPS, SIG_TABLE_LOOKUPS, TX_TABLE_LOOKUPS, }, util::{instrumentation::Instrument, CachedRegion, CellManager, StoredExpression}, - EvmCircuitExports, ByteMsgX, + EvmCircuitExports, }; use crate::{ evm_circuit::{ param::{EVM_LOOKUP_COLS, MAX_STEP_HEIGHT, N_PHASE2_COLUMNS, STEP_WIDTH}, step::{ExecutionState, Step}, - table::Table, + table::{ByteMsgV, ByteMsgX, Table}, util::{ constraint_builder::{ BaseConstraintBuilder, ConstrainBuilderCommon, EVMConstraintBuilder, @@ -934,7 +934,7 @@ impl ExecutionConfig { .columns() .iter() .filter(|column| column.cell_type == CellType::LookupByte) - .map(|column| BusOp::take(vec![column.expr()], q_usable.clone())) + .map(|column| BusOp::take([column.expr()], q_usable.clone())) .collect::>(); BusPortMulti::connect(meta, bus_builder, ops) @@ -1056,10 +1056,16 @@ impl ExecutionConfig { pub fn assign_block( &self, layouter: &mut impl Layouter, - bus_assigner: &mut BusAssigner, + bus_assigner: &mut BusAssigner>, block: &Block, challenges: &Challenges>, - ) -> Result<(EvmCircuitExports>, Option), Error> { + ) -> Result< + ( + EvmCircuitExports>, + Option>>, + ), + Error, + > { let mut is_first_time = true; let mut bus_op_counter = None; @@ -1318,7 +1324,7 @@ impl ExecutionConfig { fn assign_bus_ports( &self, region: &mut CachedRegion<'_, '_, F>, - port_assigner: &mut PortAssigner, + port_assigner: &mut PortAssigner>, offset_begin: usize, offset_end: usize, ) { @@ -1336,7 +1342,7 @@ impl ExecutionConfig { .iter() .map(|column_index| { let byte = region.get_advice(offset, *column_index, Rotation::cur()); - let message = vec![Value::known(byte)]; + let message = [Value::known(byte)]; BusOp::take(message, 1) }) .collect::>(); @@ -1348,7 +1354,7 @@ impl ExecutionConfig { fn assign_same_exec_step_in_range( &self, region: &mut Region<'_, F>, - port_assigner: &mut PortAssigner, + port_assigner: &mut PortAssigner>, offset_begin: usize, offset_end: usize, block: &Block, @@ -1390,7 +1396,7 @@ impl ExecutionConfig { fn assign_exec_step( &self, region: &mut Region<'_, F>, - port_assigner: &mut PortAssigner, + port_assigner: &mut PortAssigner>, offset: usize, block: &Block, transaction: &Transaction, diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index fa9d95d471..47fcd5d433 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -6,10 +6,13 @@ use crate::{ use bus_mapping::{evm::OpcodeId, precompile::PrecompileCalls}; use eth_types::Field; use gadgets::util::Expr; -use halo2_proofs::plonk::Expression; +use halo2_proofs::{circuit::Value, plonk::Expression}; use strum::IntoEnumIterator; use strum_macros::EnumIter; +pub type ByteMsgX = [Expression; 1]; +pub type ByteMsgV = [Value; 1]; + #[derive(Clone, Copy, Debug, EnumIter)] pub enum FixedTableTag { Zero = 0, From 22b67dce5a6fa1b0c20335160dde9fe2bdb9f54b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 2 Oct 2023 12:22:26 +0200 Subject: [PATCH 26/67] bus: support multiple values --- gadgets/src/bus/bus_codec.rs | 66 ++++++++++++++------- gadgets/src/bus/bus_lookup.rs | 2 +- gadgets/src/bus/bus_port.rs | 41 ++++++------- gadgets/src/bus/tests.rs | 4 +- zkevm-circuits/src/evm_circuit.rs | 4 +- zkevm-circuits/src/evm_circuit/execution.rs | 2 +- zkevm-circuits/src/evm_circuit/table.rs | 4 +- 7 files changed, 69 insertions(+), 54 deletions(-) diff --git a/gadgets/src/bus/bus_codec.rs b/gadgets/src/bus/bus_codec.rs index d5656aecc6..93f02c982a 100644 --- a/gadgets/src/bus/bus_codec.rs +++ b/gadgets/src/bus/bus_codec.rs @@ -1,9 +1,7 @@ -use std::{ - marker::PhantomData, - ops::{Add, Mul}, -}; +use std::marker::PhantomData; -use halo2_proofs::{circuit::Value, plonk::Expression}; +use crate::util::Expr; +use halo2_proofs::{circuit::Value, halo2curves::FieldExt, plonk::Expression}; /// The default message type for expressions. pub type DefaultMsgExpr = Vec>; @@ -24,11 +22,7 @@ pub struct BusCodec { _marker: PhantomData, } -impl BusCodec -where - T: Clone + Add + Mul, - M: BusMessage, -{ +impl BusCodec { /// Create a new message codec. pub fn new(rand: T) -> Self { Self { @@ -36,15 +30,37 @@ where _marker: PhantomData, } } +} +impl BusCodec, M> +where + F: FieldExt, + M: BusMessage>, +{ /// Compress a message into a field element, such that: /// - the map from message to elements is collision-resistant. /// - the inverses of the elements are linearly independent. /// - Elements are non-zero. - pub fn compress(&self, msg: M) -> T { - // TODO: support multiple values. - let first = msg.into_items().next().unwrap().into(); - self.rand.clone() + first + pub fn compress(&self, msg: M) -> Expression { + msg.into_items() + .fold(1.expr(), |acc, f| self.rand.clone() * acc + f) + } +} + +impl BusCodec, M> +where + F: FieldExt, + M: BusMessage, +{ + /// Compress a message into a field element, such that: + /// - the map from message to elements is collision-resistant. + /// - the inverses of the elements are linearly independent. + /// - Elements are non-zero. + pub fn compress(&self, msg: M) -> Value { + self.rand.clone().map(|rand| { + msg.into_items() + .fold(F::one(), |acc, f| rand.clone() * acc + f) + }) } } @@ -72,9 +88,8 @@ where #[cfg(test)] mod tests { - use halo2_proofs::halo2curves::bn256::Fr; - use super::*; + use halo2_proofs::halo2curves::bn256::Fr; #[derive(Clone, Debug)] struct TestMessage { @@ -92,17 +107,24 @@ mod tests { #[test] fn test_codec() { + let rand = Value::known(Fr::from(10u64)); { // Using vectors as message type. - let codec = BusCodec::new(Fr::one()); - let msg = vec![1u64, 2u64, 3u64]; - assert_eq!(codec.compress(msg), Fr::from(2)); + let codec = BusCodec::new(rand); + let msg = vec![2u64, 3u64]; + let compressed = codec.compress(msg); + + assert!(!compressed.is_none()); + compressed.map(|c| assert_eq!(c, Fr::from(123))); } { // Using a custom message type. - let codec = BusCodec::new(Fr::one()); - let msg = TestMessage { a: 1, b: 2 }; - assert_eq!(codec.compress(msg.clone()), Fr::from(2)); + let codec = BusCodec::new(rand); + let msg = TestMessage { a: 2, b: 3 }; + let compressed = codec.compress(msg); + + assert!(!compressed.is_none()); + compressed.map(|c| assert_eq!(c, Fr::from(123))); } } } diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index b41bc16b22..d668551759 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -40,7 +40,7 @@ impl BusLookupConfig { } /// Assign a lookup operation. - pub fn assign>>( + pub fn assign> ( &self, region: &mut Region<'_, F>, port_assigner: &mut PortAssigner, diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 18d8568916..ee5869f2fc 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -71,7 +71,7 @@ impl BusPortSingle { /// Return the witness that must be assigned to the helper cell. /// Prefer using PortAssigner instead. - pub fn helper_witness>>( + pub fn helper_witness>( codec: &BusCodecVal, message: M, ) -> Value { @@ -122,7 +122,7 @@ impl BusPortDual { /// Return the witness that must be assigned to the helper cell. /// Prefer using PortAssigner instead. - pub fn helper_witness>>( + pub fn helper_witness>( codec: &BusCodecVal, messages: [M; 2], ) -> Value { @@ -194,7 +194,7 @@ impl BusPortChip { } /// Assign an operation. - pub fn assign>>( + pub fn assign>( &self, port_assigner: &mut PortAssigner, offset: usize, @@ -226,7 +226,7 @@ impl BusPortMulti { } /// Assign operations. - pub fn assign>>( + pub fn assign>( &self, port_assigner: &mut PortAssigner, offset: usize, @@ -247,7 +247,7 @@ pub struct PortAssigner { bus_op_counter: BusOpCounter, } -impl>> PortAssigner { +impl> PortAssigner { /// Create a new PortAssigner. pub fn new(codec: BusCodecVal) -> Self { Self { @@ -306,7 +306,7 @@ pub struct BusOpCounter { _marker: PhantomData<(F, M)>, } -impl>> BusOpCounter { +impl> BusOpCounter { /// Create a new BusOpCounter. pub fn new() -> Self { Self { @@ -317,12 +317,11 @@ impl>> BusOpCounter { /// Report an operation. pub fn set_op(&mut self, op: &BusOpA) { - if let Some(key) = Self::to_key(op.message()) { - self.counts - .entry(key) - .and_modify(|c| *c = *c + op.count()) - .or_insert_with(|| op.count()); - } + let key = Self::to_key(op.message()); + self.counts + .entry(key) + .and_modify(|c| *c = *c + op.count()) + .or_insert_with(|| op.count()); } /// Count how many times a message was taken (net of puts). @@ -337,21 +336,15 @@ impl>> BusOpCounter { /// Count how many times a message was put (net positive) or taken (net negative). fn count_ops(&self, message: &M) -> isize { - if let Some(key) = Self::to_key(message.clone()) { - *self.counts.get(&key).unwrap_or(&0) - } else { - 0 - } + let key = Self::to_key(message.clone()); + *self.counts.get(&key).unwrap_or(&0) } - fn to_key(message: M) -> Option> { + fn to_key(message: M) -> Vec { let mut bytes = vec![]; - for v in message.into_items() { - if v.is_none() { - return None; - } - v.map(|v| bytes.extend_from_slice(v.to_repr().as_ref())); + for f in message.into_items() { + bytes.extend_from_slice(f.to_repr().as_ref()); } - Some(bytes) + bytes } } diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 4aa4cd812f..dab152c514 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -108,7 +108,7 @@ impl Circuit for TestCircuit { // Circuit 1 puts a message on some row. { // Do normal circuit assignment logic, and obtain a message. - let message = vec![Value::known(F::from(2))]; + let message = vec![F::from(2)]; // Set the `count` of copies of the same message. let count = self.n_rows as isize; @@ -128,7 +128,7 @@ impl Circuit for TestCircuit { // First pass: run circuit steps. for offset in 0..self.n_rows { // Do normal circuit assignment logic, and obtain a message. - let message = vec![Value::known(F::from(2))]; + let message = vec![F::from(2)]; // Assign an operation to the port of this circuit, and to the shared bus. config diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 8096b13f0e..2cb0c248a2 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -260,9 +260,9 @@ impl EvmCircuitConfig { let mut port_assigner = PortAssigner::new(bus_assigner.codec().clone()); for offset in 0..256 { - let value = Value::known(F::from(offset as u64)); + let value = F::from(offset as u64); - region.assign_fixed(|| "", self.byte_table[0], offset, || value)?; + region.assign_fixed(|| "", self.byte_table[0], offset, || Value::known(value))?; region.assign_fixed( || "", diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 4ee16c6860..77fe9892e6 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -1342,7 +1342,7 @@ impl ExecutionConfig { .iter() .map(|column_index| { let byte = region.get_advice(offset, *column_index, Rotation::cur()); - let message = [Value::known(byte)]; + let message = [byte]; BusOp::take(message, 1) }) .collect::>(); diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index 47fcd5d433..daa0b76679 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -6,12 +6,12 @@ use crate::{ use bus_mapping::{evm::OpcodeId, precompile::PrecompileCalls}; use eth_types::Field; use gadgets::util::Expr; -use halo2_proofs::{circuit::Value, plonk::Expression}; +use halo2_proofs::plonk::Expression; use strum::IntoEnumIterator; use strum_macros::EnumIter; pub type ByteMsgX = [Expression; 1]; -pub type ByteMsgV = [Value; 1]; +pub type ByteMsgV = [F; 1]; #[derive(Clone, Copy, Debug, EnumIter)] pub enum FixedTableTag { From a1e08266e00fadd100f43558e24627c534c02d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 3 Oct 2023 11:07:23 +0200 Subject: [PATCH 27/67] bus: more flexible PortAssigner for multiport --- gadgets/src/bus/bus_port.rs | 81 +++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index ee5869f2fc..b392dcabdd 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -200,7 +200,37 @@ impl BusPortChip { offset: usize, op: BusOpA, ) { - port_assigner.set_op(offset, self.helper, 0, op); + let cmd = Box::new(BPCCmd { + offset, + column: self.helper, + count: op.count(), + }); + let denom = port_assigner.codec().compress(op.message()); + + port_assigner.bus_op_counter.set_op(&op); + port_assigner.push_command(cmd, denom); + } +} + +struct BPCCmd { + offset: usize, + column: Column, + count: isize, +} + +impl Command for BPCCmd { + fn exec(&self, region: &mut Region<'_, F>, helper: F) -> (usize, F) { + region + .assign_advice( + || "BusPort_helper", + self.column, + self.offset, + || Value::known(helper), + ) + .unwrap(); + + let term = from_isize::(self.count) * helper; + (self.offset, term) } } @@ -239,12 +269,16 @@ impl BusPortMulti { } } -/// PortAssigner computes and assigns terms into helper cells and the bus. +trait Command { + #[must_use = "terms must be added to the bus"] + fn exec(&self, region: &mut Region<'_, F>, helper: F) -> (usize, F); +} +/// PortAssigner computes and assigns terms into helper cells and the bus. pub struct PortAssigner { codec: BusCodecVal, - batch: HelperBatch, isize, isize)>, bus_op_counter: BusOpCounter, + commands: HelperBatch>>, } impl> PortAssigner { @@ -252,24 +286,18 @@ impl> PortAssigner { pub fn new(codec: BusCodecVal) -> Self { Self { codec, - batch: HelperBatch::new(), bus_op_counter: BusOpCounter::new(), + commands: HelperBatch::new(), } } - /// Assign a message. - pub fn set_op( - &mut self, - offset: usize, - column: Column, - rotation: isize, - op: BusOpA, - ) { - self.bus_op_counter.set_op(&op); + /// The codec to compress messages on this bus. + pub fn codec(&self) -> &BusCodecVal { + &self.codec + } - let denom = self.codec.compress(op.message()); - self.batch - .add_denom(denom, (offset, column, rotation, op.count())); + fn push_command(&mut self, cmd: Box>, denom: Value) { + self.commands.add_denom(denom, cmd) } /// Assign the helper cells and report the terms to the bus. @@ -278,23 +306,14 @@ impl> PortAssigner { region: &mut Region<'_, F>, bus_assigner: &mut BusAssigner, ) -> BusOpCounter { - self.batch.invert().map(|terms| { - // The batch has converted the messages into bus terms. - for (term, (offset, column, rotation, count)) in terms { - let term = Value::known(term); - - // Set the helper cell. - let cell_offset = (offset as isize + rotation) as usize; - region - .assign_advice(|| "BusPort_helper", column, cell_offset, || term) - .unwrap(); - - // Report the term to the global bus. - let global_offset = offset; // region.global_offset(offset); - let count = Value::known(from_isize::(count)); - bus_assigner.add_term(global_offset, count * term); + self.commands.invert().map(|commands| { + for (helper, command) in commands { + let (offset, term) = command.exec(region, helper); + bus_assigner.add_term(offset, Value::known(term)); + // TODO: Ensure this is a global offset (need Halo2 support). } }); + self.bus_op_counter } } From aa2f1903b71bac30dc69e019053b6b309a5516ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 3 Oct 2023 12:14:41 +0200 Subject: [PATCH 28/67] bus: refactor BusAssigner and PortAssigner --- gadgets/src/bus.rs | 5 + gadgets/src/bus/bus_builder.rs | 33 +++++- gadgets/src/bus/bus_lookup.rs | 5 +- gadgets/src/bus/bus_port.rs | 119 ++------------------ gadgets/src/bus/port_assigner.rs | 118 +++++++++++++++++++ gadgets/src/bus/tests.rs | 1 + zkevm-circuits/src/evm_circuit.rs | 10 +- zkevm-circuits/src/evm_circuit/execution.rs | 3 +- 8 files changed, 176 insertions(+), 118 deletions(-) create mode 100644 gadgets/src/bus/port_assigner.rs diff --git a/gadgets/src/bus.rs b/gadgets/src/bus.rs index 1013bd4e3d..ff09db92dc 100644 --- a/gadgets/src/bus.rs +++ b/gadgets/src/bus.rs @@ -15,8 +15,13 @@ pub mod bus_lookup; /// This module encodes messages into terms. pub mod bus_codec; +/// This module helps ports with their assignments. +mod port_assigner; + /// Utility functions. mod util; #[cfg(test)] mod tests; + +pub use port_assigner::{BusOpCounter, PortAssigner}; diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs index 5ebf0d7e13..a4e3757293 100644 --- a/gadgets/src/bus/bus_builder.rs +++ b/gadgets/src/bus/bus_builder.rs @@ -40,8 +40,7 @@ impl BusBuilder { /// BusAssigner pub struct BusAssigner { codec: BusCodecVal, - terms: Vec, - unknown: bool, + term_adder: TermAdder, } impl BusAssigner { @@ -49,8 +48,7 @@ impl BusAssigner { pub fn new(codec: BusCodecVal, n_rows: usize) -> Self { Self { codec, - terms: vec![F::zero(); n_rows], - unknown: false, + term_adder: TermAdder::new(n_rows), } } @@ -61,6 +59,31 @@ impl BusAssigner { /// Add a term value to the bus. pub fn add_term(&mut self, offset: usize, term: Value) { + self.term_adder.add_term(offset, term); + } + + /// Return the collected terms. + pub fn terms(&self) -> Value<&[F]> { + self.term_adder.terms() + } +} + +struct TermAdder { + terms: Vec, + unknown: bool, +} + +impl TermAdder { + /// Create a term adder with a maximum number of rows. + fn new(n_rows: usize) -> Self { + Self { + terms: vec![F::zero(); n_rows], + unknown: false, + } + } + + /// Add a term value to the bus. + fn add_term(&mut self, offset: usize, term: Value) { assert!( offset < self.terms.len(), "offset={offset} out of bounds n_rows={}", @@ -78,7 +101,7 @@ impl BusAssigner { } /// Return the collected terms. - pub fn terms(&self) -> Value<&[F]> { + fn terms(&self) -> Value<&[F]> { if self.unknown { Value::unknown() } else { diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index d668551759..e06f012cfb 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -5,8 +5,9 @@ use crate::util::query_expression; use super::{ bus_builder::BusBuilder, bus_codec::BusMessage, - bus_port::{BusOp, BusPortChip, PortAssigner, BusOpA}, + bus_port::{BusOp, BusOpA, BusPortChip}, util::from_isize, + PortAssigner, }; use halo2_proofs::{ circuit::{Region, Value}, @@ -40,7 +41,7 @@ impl BusLookupConfig { } /// Assign a lookup operation. - pub fn assign> ( + pub fn assign>( &self, region: &mut Region<'_, F>, port_assigner: &mut PortAssigner, diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index b392dcabdd..9bc1af7964 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,8 +1,10 @@ use super::{ - bus_builder::{BusAssigner, BusBuilder}, + bus_builder::BusBuilder, bus_chip::BusTerm, bus_codec::{BusCodecExpr, BusCodecVal, BusMessage}, - util::{from_isize, HelperBatch}, + port_assigner::Assigner, + util::from_isize, + PortAssigner, }; use crate::util::query_expression; use halo2_proofs::{ @@ -11,7 +13,7 @@ use halo2_proofs::{ plonk::{Advice, Column, ConstraintSystem, Expression, ThirdPhase}, poly::Rotation, }; -use std::{collections::HashMap, marker::PhantomData, ops::Neg}; +use std::{marker::PhantomData, ops::Neg}; /// A bus operation, as expressions for circuit config. pub type BusOpX = BusOp>; @@ -200,26 +202,26 @@ impl BusPortChip { offset: usize, op: BusOpA, ) { - let cmd = Box::new(BPCCmd { + let cmd = Box::new(BusPortAssigner { offset, column: self.helper, count: op.count(), }); let denom = port_assigner.codec().compress(op.message()); - port_assigner.bus_op_counter.set_op(&op); - port_assigner.push_command(cmd, denom); + port_assigner.track_op(&op); + port_assigner.assign_later(cmd, denom); } } -struct BPCCmd { +struct BusPortAssigner { offset: usize, column: Column, count: isize, } -impl Command for BPCCmd { - fn exec(&self, region: &mut Region<'_, F>, helper: F) -> (usize, F) { +impl Assigner for BusPortAssigner { + fn assign(&self, region: &mut Region<'_, F>, helper: F) -> (usize, F) { region .assign_advice( || "BusPort_helper", @@ -268,102 +270,3 @@ impl BusPortMulti { } } } - -trait Command { - #[must_use = "terms must be added to the bus"] - fn exec(&self, region: &mut Region<'_, F>, helper: F) -> (usize, F); -} - -/// PortAssigner computes and assigns terms into helper cells and the bus. -pub struct PortAssigner { - codec: BusCodecVal, - bus_op_counter: BusOpCounter, - commands: HelperBatch>>, -} - -impl> PortAssigner { - /// Create a new PortAssigner. - pub fn new(codec: BusCodecVal) -> Self { - Self { - codec, - bus_op_counter: BusOpCounter::new(), - commands: HelperBatch::new(), - } - } - - /// The codec to compress messages on this bus. - pub fn codec(&self) -> &BusCodecVal { - &self.codec - } - - fn push_command(&mut self, cmd: Box>, denom: Value) { - self.commands.add_denom(denom, cmd) - } - - /// Assign the helper cells and report the terms to the bus. - pub fn finish( - self, - region: &mut Region<'_, F>, - bus_assigner: &mut BusAssigner, - ) -> BusOpCounter { - self.commands.invert().map(|commands| { - for (helper, command) in commands { - let (offset, term) = command.exec(region, helper); - bus_assigner.add_term(offset, Value::known(term)); - // TODO: Ensure this is a global offset (need Halo2 support). - } - }); - - self.bus_op_counter - } -} - -/// OpCounter tracks the messages taken, to help generating the puts. -#[derive(Clone, Debug)] -pub struct BusOpCounter { - counts: HashMap, isize>, - _marker: PhantomData<(F, M)>, -} - -impl> BusOpCounter { - /// Create a new BusOpCounter. - pub fn new() -> Self { - Self { - counts: HashMap::new(), - _marker: PhantomData, - } - } - - /// Report an operation. - pub fn set_op(&mut self, op: &BusOpA) { - let key = Self::to_key(op.message()); - self.counts - .entry(key) - .and_modify(|c| *c = *c + op.count()) - .or_insert_with(|| op.count()); - } - - /// Count how many times a message was taken (net of puts). - pub fn count_takes(&self, message: &M) -> isize { - (-self.count_ops(message)).max(0) - } - - /// Count how many times a message was put (net of takes). - pub fn count_puts(&self, message: &M) -> isize { - self.count_ops(message).max(0) - } - - /// Count how many times a message was put (net positive) or taken (net negative). - fn count_ops(&self, message: &M) -> isize { - let key = Self::to_key(message.clone()); - *self.counts.get(&key).unwrap_or(&0) - } - - fn to_key(message: M) -> Vec { - let mut bytes = vec![]; - for f in message.into_items() { - bytes.extend_from_slice(f.to_repr().as_ref()); - } - bytes - } -} diff --git a/gadgets/src/bus/port_assigner.rs b/gadgets/src/bus/port_assigner.rs new file mode 100644 index 0000000000..f846b6bdb9 --- /dev/null +++ b/gadgets/src/bus/port_assigner.rs @@ -0,0 +1,118 @@ +use super::{ + bus_builder::BusAssigner, + bus_codec::{BusCodecVal, BusMessage}, + bus_port::BusOpA, + util::HelperBatch, +}; +use halo2_proofs::{ + arithmetic::FieldExt, + circuit::{Region, Value}, +}; +use std::{collections::HashMap, marker::PhantomData}; + +/// Assigners are used to delay the assignment until helper values are computed. +pub trait Assigner { + /// Given the helper value, assign ports and return (offset, term). + #[must_use = "terms must be added to the bus"] + fn assign(&self, region: &mut Region<'_, F>, helper: F) -> (usize, F); +} + +/// PortAssigner computes and assigns terms into helper cells and the bus. +pub struct PortAssigner { + codec: BusCodecVal, + bus_op_counter: BusOpCounter, + assigners: HelperBatch>>, +} + +impl> PortAssigner { + /// Create a new PortAssigner. + pub fn new(codec: BusCodecVal) -> Self { + Self { + codec, + bus_op_counter: BusOpCounter::new(), + assigners: HelperBatch::new(), + } + } + + /// The codec to compress messages on this bus. + pub fn codec(&self) -> &BusCodecVal { + &self.codec + } + + /// Track an operation. + pub fn track_op(&mut self, op: &BusOpA) { + self.bus_op_counter.track_op(op); + } + + /// Execute an assignment later, with the inverse of `denom`. + pub fn assign_later(&mut self, cmd: Box>, denom: Value) { + self.assigners.add_denom(denom, cmd) + } + + /// Assign the helper cells and report the terms to the bus. + pub fn finish( + self, + region: &mut Region<'_, F>, + bus_assigner: &mut BusAssigner, + ) -> BusOpCounter { + self.assigners.invert().map(|commands| { + for (helper, command) in commands { + let (offset, term) = command.assign(region, helper); + bus_assigner.add_term(offset, Value::known(term)); + // TODO: Ensure this is a global offset (need Halo2 support). + } + }); + + self.bus_op_counter + } +} + +/// OpCounter tracks the messages taken, to help generating the puts. +#[derive(Clone, Debug)] +pub struct BusOpCounter { + counts: HashMap, isize>, + _marker: PhantomData<(F, M)>, +} + +impl> BusOpCounter { + /// Create a new BusOpCounter. + pub fn new() -> Self { + Self { + counts: HashMap::new(), + _marker: PhantomData, + } + } + + /// Report an operation. + pub fn track_op(&mut self, op: &BusOpA) { + let key = Self::to_key(op.message()); + self.counts + .entry(key) + .and_modify(|c| *c = *c + op.count()) + .or_insert_with(|| op.count()); + } + + /// Count how many times a message was taken (net of puts). + pub fn count_takes(&self, message: &M) -> isize { + (-self.count_ops(message)).max(0) + } + + /// Count how many times a message was put (net of takes). + pub fn count_puts(&self, message: &M) -> isize { + self.count_ops(message).max(0) + } + + /// Count how many times a message was put (net positive) or taken (net negative). + fn count_ops(&self, message: &M) -> isize { + let key = Self::to_key(message.clone()); + *self.counts.get(&key).unwrap_or(&0) + } + + fn to_key(message: M) -> Vec { + let mut bytes = vec![]; + for f in message.into_items() { + bytes.extend_from_slice(f.to_repr().as_ref()); + } + bytes + } +} diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index dab152c514..ef80fdf9d2 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -15,6 +15,7 @@ use super::{ bus_codec::{BusCodecExpr, BusCodecVal}, bus_lookup::BusLookupConfig, bus_port::*, + PortAssigner, }; #[test] diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 2cb0c248a2..64031371b8 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -6,7 +6,8 @@ use gadgets::bus::{ bus_chip::BusConfig, bus_codec::{BusCodecExpr, BusCodecVal}, bus_lookup::BusLookupConfig, - bus_port::{BusOp, BusOpCounter, PortAssigner}, + bus_port::BusOp, + BusOpCounter, PortAssigner, }; use halo2_proofs::{ circuit::{Cell, Layouter, SimpleFloorPlanner, Value}, @@ -262,7 +263,12 @@ impl EvmCircuitConfig { for offset in 0..256 { let value = F::from(offset as u64); - region.assign_fixed(|| "", self.byte_table[0], offset, || Value::known(value))?; + region.assign_fixed( + || "", + self.byte_table[0], + offset, + || Value::known(value), + )?; region.assign_fixed( || "", diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 77fe9892e6..567d2304ba 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -29,7 +29,8 @@ use eth_types::{Field, ToLittleEndian}; use gadgets::{ bus::{ bus_builder::{BusAssigner, BusBuilder}, - bus_port::{BusOp, BusOpCounter, BusPortMulti, PortAssigner}, + bus_port::{BusOp, BusPortMulti}, + BusOpCounter, PortAssigner, }, util::not, }; From 5fbbc4d348d5b6c669796a23c70321cc75a554c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 3 Oct 2023 14:23:05 +0200 Subject: [PATCH 29/67] bus: simplify API with a single bus_assigner object --- gadgets/src/bus.rs | 2 - gadgets/src/bus/bus_builder.rs | 36 ++++++++++++++++-- gadgets/src/bus/bus_lookup.rs | 7 ++-- gadgets/src/bus/bus_port.rs | 19 +++++----- gadgets/src/bus/port_assigner.rs | 35 ++++-------------- gadgets/src/bus/tests.rs | 10 ++--- gadgets/src/bus/util.rs | 4 ++ zkevm-circuits/src/evm_circuit.rs | 17 +++------ zkevm-circuits/src/evm_circuit/execution.rs | 41 +++++++-------------- 9 files changed, 79 insertions(+), 92 deletions(-) diff --git a/gadgets/src/bus.rs b/gadgets/src/bus.rs index ff09db92dc..782d6467c9 100644 --- a/gadgets/src/bus.rs +++ b/gadgets/src/bus.rs @@ -23,5 +23,3 @@ mod util; #[cfg(test)] mod tests; - -pub use port_assigner::{BusOpCounter, PortAssigner}; diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs index a4e3757293..ae1a6c72e2 100644 --- a/gadgets/src/bus/bus_builder.rs +++ b/gadgets/src/bus/bus_builder.rs @@ -1,8 +1,14 @@ -use halo2_proofs::{arithmetic::FieldExt, circuit::Value}; +use std::mem; + +use halo2_proofs::{ + arithmetic::FieldExt, + circuit::{Region, Value}, +}; use super::{ bus_chip::BusTerm, - bus_codec::{BusCodecExpr, BusCodecVal}, + bus_codec::{BusCodecExpr, BusCodecVal, BusMessage}, + port_assigner::{BusOpCounter, PortAssigner}, }; /// BusBuilder @@ -41,14 +47,18 @@ impl BusBuilder { pub struct BusAssigner { codec: BusCodecVal, term_adder: TermAdder, + bus_op_counter: BusOpCounter, + port_assigner: PortAssigner, } -impl BusAssigner { +impl> BusAssigner { /// Create a new bus assigner with a maximum number of rows. pub fn new(codec: BusCodecVal, n_rows: usize) -> Self { Self { + port_assigner: PortAssigner::new(), codec, term_adder: TermAdder::new(n_rows), + bus_op_counter: BusOpCounter::new(), } } @@ -57,6 +67,23 @@ impl BusAssigner { &self.codec } + /// Return the op counter. + pub fn op_counter(&mut self) -> &mut BusOpCounter { + &mut self.bus_op_counter + } + + /// Return the port assigner. + pub fn port_assigner(&mut self) -> &mut PortAssigner { + &mut self.port_assigner + } + + /// Finish pending assignments in a region. + pub fn finish_ports(&mut self, region: &mut Region<'_, F>) { + let old_port_assigner = mem::replace(&mut self.port_assigner, PortAssigner::new()); + + old_port_assigner.finish(region, self); + } + /// Add a term value to the bus. pub fn add_term(&mut self, offset: usize, term: Value) { self.term_adder.add_term(offset, term); @@ -64,6 +91,9 @@ impl BusAssigner { /// Return the collected terms. pub fn terms(&self) -> Value<&[F]> { + assert_eq!(self.port_assigner.len(), 0, "finish_ports was not called"); + // TODO: better error handling. + self.term_adder.terms() } } diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index e06f012cfb..acf6b5ff2b 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -3,11 +3,10 @@ use crate::util::query_expression; use super::{ - bus_builder::BusBuilder, + bus_builder::{BusAssigner, BusBuilder}, bus_codec::BusMessage, bus_port::{BusOp, BusOpA, BusPortChip}, util::from_isize, - PortAssigner, }; use halo2_proofs::{ circuit::{Region, Value}, @@ -44,7 +43,7 @@ impl BusLookupConfig { pub fn assign>( &self, region: &mut Region<'_, F>, - port_assigner: &mut PortAssigner, + bus_assigner: &mut BusAssigner, offset: usize, op: BusOpA, ) -> Result<(), Error> { @@ -54,7 +53,7 @@ impl BusLookupConfig { offset, || Value::known(from_isize::(op.count())), )?; - self.port.assign(port_assigner, offset, op); + self.port.assign(bus_assigner, offset, op); Ok(()) } } diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 9bc1af7964..16b94372f0 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,10 +1,9 @@ use super::{ - bus_builder::BusBuilder, + bus_builder::{BusBuilder, BusAssigner}, bus_chip::BusTerm, bus_codec::{BusCodecExpr, BusCodecVal, BusMessage}, port_assigner::Assigner, util::from_isize, - PortAssigner, }; use crate::util::query_expression; use halo2_proofs::{ @@ -72,7 +71,7 @@ impl BusPortSingle { } /// Return the witness that must be assigned to the helper cell. - /// Prefer using PortAssigner instead. + /// Prefer using BusAssigner instead. pub fn helper_witness>( codec: &BusCodecVal, message: M, @@ -123,7 +122,7 @@ impl BusPortDual { } /// Return the witness that must be assigned to the helper cell. - /// Prefer using PortAssigner instead. + /// Prefer using BusAssigner instead. pub fn helper_witness>( codec: &BusCodecVal, messages: [M; 2], @@ -198,7 +197,7 @@ impl BusPortChip { /// Assign an operation. pub fn assign>( &self, - port_assigner: &mut PortAssigner, + bus_assigner: &mut BusAssigner, offset: usize, op: BusOpA, ) { @@ -207,10 +206,10 @@ impl BusPortChip { column: self.helper, count: op.count(), }); - let denom = port_assigner.codec().compress(op.message()); + let denom = bus_assigner.codec().compress(op.message()); - port_assigner.track_op(&op); - port_assigner.assign_later(cmd, denom); + bus_assigner.op_counter().track_op(&op); + bus_assigner.port_assigner().assign_later(cmd, denom); } } @@ -260,13 +259,13 @@ impl BusPortMulti { /// Assign operations. pub fn assign>( &self, - port_assigner: &mut PortAssigner, + bus_assigner: &mut BusAssigner, offset: usize, ops: Vec>, ) { assert_eq!(self.ports.len(), ops.len()); for (port, op) in self.ports.iter().zip(ops) { - port.assign(port_assigner, offset, op); + port.assign(bus_assigner, offset, op); } } } diff --git a/gadgets/src/bus/port_assigner.rs b/gadgets/src/bus/port_assigner.rs index f846b6bdb9..f4bea4fd6e 100644 --- a/gadgets/src/bus/port_assigner.rs +++ b/gadgets/src/bus/port_assigner.rs @@ -1,9 +1,4 @@ -use super::{ - bus_builder::BusAssigner, - bus_codec::{BusCodecVal, BusMessage}, - bus_port::BusOpA, - util::HelperBatch, -}; +use super::{bus_builder::BusAssigner, bus_codec::BusMessage, bus_port::BusOpA, util::HelperBatch}; use halo2_proofs::{ arithmetic::FieldExt, circuit::{Region, Value}, @@ -19,42 +14,26 @@ pub trait Assigner { /// PortAssigner computes and assigns terms into helper cells and the bus. pub struct PortAssigner { - codec: BusCodecVal, - bus_op_counter: BusOpCounter, assigners: HelperBatch>>, + _marker: PhantomData, } impl> PortAssigner { /// Create a new PortAssigner. - pub fn new(codec: BusCodecVal) -> Self { + pub fn new() -> Self { Self { - codec, - bus_op_counter: BusOpCounter::new(), assigners: HelperBatch::new(), + _marker: PhantomData, } } - /// The codec to compress messages on this bus. - pub fn codec(&self) -> &BusCodecVal { - &self.codec - } - - /// Track an operation. - pub fn track_op(&mut self, op: &BusOpA) { - self.bus_op_counter.track_op(op); - } - /// Execute an assignment later, with the inverse of `denom`. pub fn assign_later(&mut self, cmd: Box>, denom: Value) { self.assigners.add_denom(denom, cmd) } /// Assign the helper cells and report the terms to the bus. - pub fn finish( - self, - region: &mut Region<'_, F>, - bus_assigner: &mut BusAssigner, - ) -> BusOpCounter { + pub fn finish(self, region: &mut Region<'_, F>, bus_assigner: &mut BusAssigner) { self.assigners.invert().map(|commands| { for (helper, command) in commands { let (offset, term) = command.assign(region, helper); @@ -62,8 +41,10 @@ impl> PortAssigner { // TODO: Ensure this is a global offset (need Halo2 support). } }); + } - self.bus_op_counter + pub fn len(&self) -> usize { + self.assigners.len() } } diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index ef80fdf9d2..bb8702b4f6 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -15,7 +15,6 @@ use super::{ bus_codec::{BusCodecExpr, BusCodecVal}, bus_lookup::BusLookupConfig, bus_port::*, - PortAssigner, }; #[test] @@ -103,9 +102,6 @@ impl Circuit for TestCircuit { let mut bus_assigner = BusAssigner::new(BusCodecVal::new(rand), self.n_rows); - // This uses a batching method rather than row-by-row. - let mut port_assigner = PortAssigner::new(bus_assigner.codec().clone()); - // Circuit 1 puts a message on some row. { // Do normal circuit assignment logic, and obtain a message. @@ -118,7 +114,7 @@ impl Circuit for TestCircuit { // Assign an operation to the port of this circuit, and to the shared bus. config.bus_lookup.assign( &mut region, - &mut port_assigner, + &mut bus_assigner, offset, BusOp::put(message, count), )?; @@ -134,12 +130,12 @@ impl Circuit for TestCircuit { // Assign an operation to the port of this circuit, and to the shared bus. config .port2 - .assign(&mut port_assigner, offset, BusOp::take(message, 1)); + .assign(&mut bus_assigner, offset, BusOp::take(message, 1)); } } // Final pass: assign the bus witnesses. - port_assigner.finish(&mut region, &mut bus_assigner); + bus_assigner.finish_ports(&mut region); config .bus_config diff --git a/gadgets/src/bus/util.rs b/gadgets/src/bus/util.rs index 826b88a6a1..e3572fb25f 100644 --- a/gadgets/src/bus/util.rs +++ b/gadgets/src/bus/util.rs @@ -51,4 +51,8 @@ impl HelperBatch { Value::known(self.denoms) } } + + pub fn len(&self) -> usize { + self.denoms.len() + } } diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 64031371b8..3ca4e2d348 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -7,7 +7,6 @@ use gadgets::bus::{ bus_codec::{BusCodecExpr, BusCodecVal}, bus_lookup::BusLookupConfig, bus_port::BusOp, - BusOpCounter, PortAssigner, }; use halo2_proofs::{ circuit::{Cell, Layouter, SimpleFloorPlanner, Value}, @@ -245,7 +244,6 @@ impl EvmCircuitConfig { &self, layouter: &mut impl Layouter, bus_assigner: &mut BusAssigner>, - bus_op_counter: &BusOpCounter>, ) -> Result<(), Error> { let mut closure_count = 0; @@ -258,8 +256,6 @@ impl EvmCircuitConfig { return Ok(()); } - let mut port_assigner = PortAssigner::new(bus_assigner.codec().clone()); - for offset in 0..256 { let value = F::from(offset as u64); @@ -278,17 +274,16 @@ impl EvmCircuitConfig { )?; let message = [value]; - let count = bus_op_counter.count_takes(&message); + let count = bus_assigner.op_counter().count_takes(&message); self.table_to_bus.assign( &mut region, - &mut port_assigner, + bus_assigner, offset, BusOp::put(message, count), )?; } - port_assigner.finish(&mut region, bus_assigner); - + bus_assigner.finish_ports(&mut region); Ok(()) }, )?; @@ -426,15 +421,13 @@ impl SubCircuit for EvmCircuit { let mut bus_assigner = BusAssigner::new(BusCodecVal::new(challenges.lookup_input()), num_rows); - let (export, bus_op_counter) = + let export = config .execution .assign_block(layouter, &mut bus_assigner, block, challenges)?; self.exports.borrow_mut().replace(export); - if let Some(bus_op_counter) = bus_op_counter { - config.load_byte_table(layouter, &mut bus_assigner, &bus_op_counter)?; - } + config.load_byte_table(layouter, &mut bus_assigner)?; layouter.assign_region( || "EVM_Bus", diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 567d2304ba..9bf1a25078 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -30,7 +30,6 @@ use gadgets::{ bus::{ bus_builder::{BusAssigner, BusBuilder}, bus_port::{BusOp, BusPortMulti}, - BusOpCounter, PortAssigner, }, util::not, }; @@ -1060,15 +1059,8 @@ impl ExecutionConfig { bus_assigner: &mut BusAssigner>, block: &Block, challenges: &Challenges>, - ) -> Result< - ( - EvmCircuitExports>, - Option>>, - ), - Error, - > { + ) -> Result>, Error> { let mut is_first_time = true; - let mut bus_op_counter = None; layouter.assign_region( || "Execution step", @@ -1084,8 +1076,6 @@ impl ExecutionConfig { return Ok(()); } - let mut port_assigner = PortAssigner::new(bus_assigner.codec().clone()); - let mut offset = 0; // Annotate the EVMCircuit columns within it's single region. @@ -1161,7 +1151,7 @@ impl ExecutionConfig { } self.assign_exec_step( &mut region, - &mut port_assigner, + bus_assigner, offset, block, transaction, @@ -1199,7 +1189,7 @@ impl ExecutionConfig { } self.assign_same_exec_step_in_range( &mut region, - &mut port_assigner, + bus_assigner, offset, last_row, block, @@ -1222,7 +1212,7 @@ impl ExecutionConfig { log::trace!("assign last EndBlock at offset {}", offset); self.assign_exec_step( &mut region, - &mut port_assigner, + bus_assigner, offset, block, &dummy_tx, @@ -1252,7 +1242,7 @@ impl ExecutionConfig { || Value::known(F::zero()), )?; - bus_op_counter = Some(port_assigner.finish(&mut region, bus_assigner)); + bus_assigner.finish_ports(&mut region); log::debug!("assign for region done at offset {}", offset); Ok(()) @@ -1277,12 +1267,9 @@ impl ExecutionConfig { .evm_word() .map(|r| rlc::value(&block.withdraw_root.to_le_bytes(), r)); - Ok(( - EvmCircuitExports { - withdraw_root: (final_withdraw_root_cell, withdraw_root_rlc.into()), - }, - bus_op_counter, - )) + Ok(EvmCircuitExports { + withdraw_root: (final_withdraw_root_cell, withdraw_root_rlc.into()), + }) } fn annotate_circuit(&self, region: &mut Region) { @@ -1325,7 +1312,7 @@ impl ExecutionConfig { fn assign_bus_ports( &self, region: &mut CachedRegion<'_, '_, F>, - port_assigner: &mut PortAssigner>, + bus_assigner: &mut BusAssigner>, offset_begin: usize, offset_end: usize, ) { @@ -1347,7 +1334,7 @@ impl ExecutionConfig { BusOp::take(message, 1) }) .collect::>(); - self.bus_port.assign(port_assigner, offset, ops); + self.bus_port.assign(bus_assigner, offset, ops); } } @@ -1355,7 +1342,7 @@ impl ExecutionConfig { fn assign_same_exec_step_in_range( &self, region: &mut Region<'_, F>, - port_assigner: &mut PortAssigner>, + bus_assigner: &mut BusAssigner>, offset_begin: usize, offset_end: usize, block: &Block, @@ -1388,7 +1375,7 @@ impl ExecutionConfig { offset_end, )?; - self.assign_bus_ports(region, port_assigner, offset_begin, offset_end); + self.assign_bus_ports(region, bus_assigner, offset_begin, offset_end); Ok(()) } @@ -1397,7 +1384,7 @@ impl ExecutionConfig { fn assign_exec_step( &self, region: &mut Region<'_, F>, - port_assigner: &mut PortAssigner>, + bus_assigner: &mut BusAssigner>, offset: usize, block: &Block, transaction: &Transaction, @@ -1436,7 +1423,7 @@ impl ExecutionConfig { self.assign_exec_step_int(region, offset, block, transaction, call, step, true)?; - self.assign_bus_ports(region, port_assigner, offset, offset + height); + self.assign_bus_ports(region, bus_assigner, offset, offset + height); Ok(()) } From f5a95fd167019b280331f2002bad4367d4e950e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 6 Oct 2023 12:36:50 +0200 Subject: [PATCH 30/67] bus: lookup two bytes per operation --- gadgets/src/bus/bus_port.rs | 2 +- zkevm-circuits/src/evm_circuit.rs | 103 +++++++++++++------- zkevm-circuits/src/evm_circuit/execution.rs | 42 +++++--- zkevm-circuits/src/evm_circuit/table.rs | 4 +- 4 files changed, 104 insertions(+), 47 deletions(-) diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 16b94372f0..098b029387 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,5 +1,5 @@ use super::{ - bus_builder::{BusBuilder, BusAssigner}, + bus_builder::{BusAssigner, BusBuilder}, bus_chip::BusTerm, bus_codec::{BusCodecExpr, BusCodecVal, BusMessage}, port_assigner::Assigner, diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 3ca4e2d348..e3777712d7 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -48,6 +48,7 @@ use witness::Block; pub struct EvmCircuitConfig { fixed_table: [Column; 4], byte_table: [Column; 1], + dual_byte_table: [Column; 2], enable_table: Column, bus: BusConfig, table_to_bus: BusLookupConfig, @@ -138,12 +139,13 @@ impl EvmCircuitConfig { ) -> Self { let fixed_table = [(); 4].map(|_| meta.fixed_column()); let byte_table = [(); 1].map(|_| meta.fixed_column()); + let dual_byte_table = [(); 2].map(|_| meta.fixed_column()); let enable_table = meta.fixed_column(); let mut bus_builder = BusBuilder::new(BusCodecExpr::new(challenges.lookup_input())); let table_to_bus = - Self::configure_table_to_bus(meta, &mut bus_builder, &byte_table, enable_table); + Self::configure_table_to_bus(meta, &mut bus_builder, &dual_byte_table, enable_table); let execution = Box::new(ExecutionConfig::configure( meta, @@ -185,6 +187,7 @@ impl EvmCircuitConfig { Self { fixed_table, byte_table, + dual_byte_table, enable_table, bus, table_to_bus, @@ -206,12 +209,17 @@ impl EvmCircuitConfig { fn configure_table_to_bus( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder>, - byte_table: &dyn LookupTable, + dual_byte_table: &[Column; 2], enabled: Column, ) -> BusLookupConfig { - let byte_expr = query_expression(meta, |meta| byte_table.table_exprs(meta)[0].clone()); + let message = query_expression(meta, |meta| { + [ + meta.query_fixed(dual_byte_table[0], Rotation::cur()), + meta.query_fixed(dual_byte_table[1], Rotation::cur()), + ] + }); let enabled = query_expression(meta, |meta| meta.query_fixed(enabled, Rotation::cur())); - BusLookupConfig::connect(meta, bus_builder, [byte_expr], enabled) + BusLookupConfig::connect(meta, bus_builder, message, enabled) } } @@ -240,7 +248,26 @@ impl EvmCircuitConfig { } /// Load byte table - pub fn load_byte_table( + pub fn load_byte_table(&self, layouter: &mut impl Layouter) -> Result<(), Error> { + layouter.assign_region( + || "byte table", + |mut region| { + for offset in 0..256 { + region.assign_fixed( + || "", + self.byte_table[0], + offset, + || Value::known(F::from(offset as u64)), + )?; + } + Ok(()) + }, + )?; + Ok(()) + } + + /// Load dual byte table + pub fn load_dual_byte_table( &self, layouter: &mut impl Layouter, bus_assigner: &mut BusAssigner>, @@ -256,31 +283,39 @@ impl EvmCircuitConfig { return Ok(()); } - for offset in 0..256 { - let value = F::from(offset as u64); - - region.assign_fixed( - || "", - self.byte_table[0], - offset, - || Value::known(value), - )?; - - region.assign_fixed( - || "", - self.enable_table, - offset, - || Value::known(F::one()), - )?; - - let message = [value]; - let count = bus_assigner.op_counter().count_takes(&message); - self.table_to_bus.assign( - &mut region, - bus_assigner, - offset, - BusOp::put(message, count), - )?; + for i in 0..256 { + for j in 0..256 { + let offset = (i * 256 + j) as usize; + let message = [F::from(i), F::from(j)]; + + region.assign_fixed( + || "", + self.enable_table, + offset, + || Value::known(F::one()), + )?; + + region.assign_fixed( + || "", + self.dual_byte_table[0], + offset, + || Value::known(message[0]), + )?; + region.assign_fixed( + || "", + self.dual_byte_table[1], + offset, + || Value::known(message[1]), + )?; + + let count = bus_assigner.op_counter().count_takes(&message); + self.table_to_bus.assign( + &mut region, + bus_assigner, + offset, + BusOp::put(message, count), + )?; + } } bus_assigner.finish_ports(&mut region); @@ -359,8 +394,9 @@ impl EvmCircuit { } } - // It must fit the byte table. - num_rows = num_rows.max(256); + // It must fit the dual byte table. + // TODO: Find a way to make this smaller in tests. + num_rows = num_rows.max(256 * 256); // It must have one row for EndBlock and at least one unused one num_rows + 2 @@ -427,7 +463,8 @@ impl SubCircuit for EvmCircuit { .assign_block(layouter, &mut bus_assigner, block, challenges)?; self.exports.borrow_mut().replace(export); - config.load_byte_table(layouter, &mut bus_assigner)?; + config.load_byte_table(layouter)?; + config.load_dual_byte_table(layouter, &mut bus_assigner)?; layouter.assign_region( || "EVM_Bus", diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 9bf1a25078..643b4c22d9 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -930,11 +930,25 @@ impl ExecutionConfig { ) -> BusPortMulti { let q_usable = query_expression(meta, |meta| meta.query_selector(q_usable)); - let ops = cell_manager - .columns() - .iter() - .filter(|column| column.cell_type == CellType::LookupByte) - .map(|column| BusOp::take([column.expr()], q_usable.clone())) + let byte_columns = { + let mut cols = cell_manager + .columns() + .iter() + .filter(|column| column.cell_type == CellType::LookupByte) + .map(|column| column.expr()) + .collect::>(); + if cols.len() % 2 != 0 { + cols.push(0.expr()); + } + cols + }; + + let ops = byte_columns + .chunks(2) + .map(|columns| { + let message = [columns[0].clone(), columns[1].clone()]; + BusOp::take(message, q_usable.clone()) + }) .collect::>(); BusPortMulti::connect(meta, bus_builder, ops) @@ -1316,7 +1330,7 @@ impl ExecutionConfig { offset_begin: usize, offset_end: usize, ) { - let column_indexes = self + let byte_columns = self .step .cell_manager .columns() @@ -1326,14 +1340,20 @@ impl ExecutionConfig { .collect::>(); for offset in offset_begin..offset_end { - let ops = column_indexes - .iter() - .map(|column_index| { - let byte = region.get_advice(offset, *column_index, Rotation::cur()); - let message = [byte]; + let ops = byte_columns + .chunks(2) + .map(|columns| { + let byte_0 = region.get_advice(offset, columns[0], Rotation::cur()); + let byte_1 = if columns.len() == 2 { + region.get_advice(offset, columns[1], Rotation::cur()) + } else { + F::zero() + }; + let message = [byte_0, byte_1]; BusOp::take(message, 1) }) .collect::>(); + self.bus_port.assign(bus_assigner, offset, ops); } } diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index daa0b76679..e7f9ee9f04 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -10,8 +10,8 @@ use halo2_proofs::plonk::Expression; use strum::IntoEnumIterator; use strum_macros::EnumIter; -pub type ByteMsgX = [Expression; 1]; -pub type ByteMsgV = [F; 1]; +pub type ByteMsgX = [Expression; 2]; +pub type ByteMsgV = [F; 2]; #[derive(Clone, Copy, Debug, EnumIter)] pub enum FixedTableTag { From 95390832843fa690e9da57c65e8fc303811ab92c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 6 Oct 2023 14:14:11 +0200 Subject: [PATCH 31/67] bus: faster hashtable. Separate traits for msg Expr/F. --- eth-types/src/lib.rs | 1 + gadgets/src/bus.rs | 2 + gadgets/src/bus/bus_builder.rs | 15 ++- gadgets/src/bus/bus_chip.rs | 8 +- gadgets/src/bus/bus_codec.rs | 22 +++-- gadgets/src/bus/bus_lookup.rs | 10 +- gadgets/src/bus/bus_port.rs | 30 +++--- gadgets/src/bus/port_assigner.rs | 54 ++++++----- gadgets/src/bus/port_multi.rs | 158 +++++++++++++++++++++++++++++++ gadgets/src/bus/tests.rs | 8 +- 10 files changed, 240 insertions(+), 68 deletions(-) create mode 100644 gadgets/src/bus/port_multi.rs diff --git a/eth-types/src/lib.rs b/eth-types/src/lib.rs index d81f455549..95e6f939ec 100644 --- a/eth-types/src/lib.rs +++ b/eth-types/src/lib.rs @@ -54,6 +54,7 @@ pub trait Field: + PrimeField + poseidon_circuit::hash::Hashable + std::convert::From + + std::hash::Hash { } diff --git a/gadgets/src/bus.rs b/gadgets/src/bus.rs index 782d6467c9..d22d506119 100644 --- a/gadgets/src/bus.rs +++ b/gadgets/src/bus.rs @@ -23,3 +23,5 @@ mod util; #[cfg(test)] mod tests; + +use eth_types::Field; diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs index ae1a6c72e2..5538866cc2 100644 --- a/gadgets/src/bus/bus_builder.rs +++ b/gadgets/src/bus/bus_builder.rs @@ -1,15 +1,12 @@ use std::mem; -use halo2_proofs::{ - arithmetic::FieldExt, - circuit::{Region, Value}, -}; - use super::{ bus_chip::BusTerm, - bus_codec::{BusCodecExpr, BusCodecVal, BusMessage}, + bus_codec::{BusCodecExpr, BusCodecVal, BusMessageF}, port_assigner::{BusOpCounter, PortAssigner}, + Field, }; +use halo2_proofs::circuit::{Region, Value}; /// BusBuilder #[derive(Debug)] @@ -18,7 +15,7 @@ pub struct BusBuilder { terms: Vec>, } -impl BusBuilder { +impl BusBuilder { /// Create a new bus. pub fn new(codec: BusCodecExpr) -> Self { Self { @@ -51,7 +48,7 @@ pub struct BusAssigner { port_assigner: PortAssigner, } -impl> BusAssigner { +impl> BusAssigner { /// Create a new bus assigner with a maximum number of rows. pub fn new(codec: BusCodecVal, n_rows: usize) -> Self { Self { @@ -103,7 +100,7 @@ struct TermAdder { unknown: bool, } -impl TermAdder { +impl TermAdder { /// Create a term adder with a maximum number of rows. fn new(n_rows: usize) -> Self { Self { diff --git a/gadgets/src/bus/bus_chip.rs b/gadgets/src/bus/bus_chip.rs index 7232930e3b..3e75a42e40 100644 --- a/gadgets/src/bus/bus_chip.rs +++ b/gadgets/src/bus/bus_chip.rs @@ -1,6 +1,6 @@ +use super::Field; use crate::util::Expr; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Region, Value}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, ThirdPhase}, poly::Rotation, @@ -17,7 +17,7 @@ impl BusTerm { } } -impl Expr for BusTerm { +impl Expr for BusTerm { fn expr(&self) -> Expression { self.0.clone() } @@ -34,7 +34,7 @@ pub struct BusConfig { impl BusConfig { /// Create a new bus. - pub fn new(cs: &mut ConstraintSystem, terms: &[BusTerm]) -> Self { + pub fn new(cs: &mut ConstraintSystem, terms: &[BusTerm]) -> Self { let enabled = cs.fixed_column(); let is_first = cs.fixed_column(); let is_last = cs.fixed_column(); @@ -71,7 +71,7 @@ impl BusConfig { } /// Assign the helper witness. - pub fn assign( + pub fn assign( &self, region: &mut Region<'_, F>, n_rows: usize, diff --git a/gadgets/src/bus/bus_codec.rs b/gadgets/src/bus/bus_codec.rs index 93f02c982a..da956492b6 100644 --- a/gadgets/src/bus/bus_codec.rs +++ b/gadgets/src/bus/bus_codec.rs @@ -1,7 +1,7 @@ -use std::marker::PhantomData; - +use super::Field; use crate::util::Expr; -use halo2_proofs::{circuit::Value, halo2curves::FieldExt, plonk::Expression}; +use halo2_proofs::{circuit::Value, plonk::Expression}; +use std::{cmp::Eq, hash::Hash, marker::PhantomData}; /// The default message type for expressions. pub type DefaultMsgExpr = Vec>; @@ -34,8 +34,8 @@ impl BusCodec { impl BusCodec, M> where - F: FieldExt, - M: BusMessage>, + F: Field, + M: BusMessageExpr, { /// Compress a message into a field element, such that: /// - the map from message to elements is collision-resistant. @@ -49,7 +49,7 @@ where impl BusCodec, M> where - F: FieldExt, + F: Field, M: BusMessage, { /// Compress a message into a field element, such that: @@ -64,6 +64,14 @@ where } } +/// A message as expressions to configure circuits. +pub trait BusMessageExpr: BusMessage> {} +impl BusMessageExpr for M where M: BusMessage> {} + +/// A message as values to be assigned. +pub trait BusMessageF: BusMessage + Eq + Hash {} +impl BusMessageF for M where M: BusMessage + Eq + Hash {} + /// A trait for messages that can be encoded. pub trait BusMessage: Clone { /// The item iterator type. @@ -91,7 +99,7 @@ mod tests { use super::*; use halo2_proofs::halo2curves::bn256::Fr; - #[derive(Clone, Debug)] + #[derive(Clone, Debug, Hash)] struct TestMessage { a: u64, b: u64, diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index acf6b5ff2b..9923eb848f 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -4,13 +4,13 @@ use crate::util::query_expression; use super::{ bus_builder::{BusAssigner, BusBuilder}, - bus_codec::BusMessage, + bus_codec::{BusMessageExpr, BusMessageF}, bus_port::{BusOp, BusOpA, BusPortChip}, util::from_isize, + Field, }; use halo2_proofs::{ circuit::{Region, Value}, - halo2curves::FieldExt, plonk::{Advice, Column, ConstraintSystem, Error, Expression}, poly::Rotation, }; @@ -22,9 +22,9 @@ pub struct BusLookupConfig { count: Column, } -impl BusLookupConfig { +impl BusLookupConfig { /// Create and connect a new BusLookup circuit from the expressions of message and count. - pub fn connect>>( + pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, message: M, @@ -40,7 +40,7 @@ impl BusLookupConfig { } /// Assign a lookup operation. - pub fn assign>( + pub fn assign>( &self, region: &mut Region<'_, F>, bus_assigner: &mut BusAssigner, diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 098b029387..ffe6dd38ae 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,13 +1,13 @@ use super::{ bus_builder::{BusAssigner, BusBuilder}, bus_chip::BusTerm, - bus_codec::{BusCodecExpr, BusCodecVal, BusMessage}, + bus_codec::{BusCodecExpr, BusCodecVal, BusMessageExpr, BusMessageF}, port_assigner::Assigner, util::from_isize, + Field, }; use crate::util::query_expression; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Region, Value}, plonk::{Advice, Column, ConstraintSystem, Expression, ThirdPhase}, poly::Rotation, @@ -60,7 +60,7 @@ pub struct BusPortSingle; impl BusPortSingle { /// Create a new bus port with a single access. /// The helper cell can be used for something else if op.count is zero. - pub fn connect>>( + pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, op: BusOpX, @@ -72,7 +72,7 @@ impl BusPortSingle { /// Return the witness that must be assigned to the helper cell. /// Prefer using BusAssigner instead. - pub fn helper_witness>( + pub fn helper_witness>( codec: &BusCodecVal, message: M, ) -> Value { @@ -81,7 +81,7 @@ impl BusPortSingle { .map(|x| x.invert().unwrap_or(F::zero())) } - fn create_term>>( + fn create_term>( meta: &mut ConstraintSystem, codec: &BusCodecExpr, op: BusOpX, @@ -111,7 +111,7 @@ pub struct BusPortDual; impl BusPortDual { /// Create a new bus port with two accesses. - pub fn connect>>( + pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, ops: [BusOpX; 2], @@ -123,7 +123,7 @@ impl BusPortDual { /// Return the witness that must be assigned to the helper cell. /// Prefer using BusAssigner instead. - pub fn helper_witness>( + pub fn helper_witness>( codec: &BusCodecVal, messages: [M; 2], ) -> Value { @@ -131,7 +131,7 @@ impl BusPortDual { (codec.compress(m0) * codec.compress(m1)).map(|x| x.invert().unwrap_or(F::zero())) } - fn create_term>>( + fn create_term>( meta: &mut ConstraintSystem, codec: &BusCodecExpr, ops: [BusOpX; 2], @@ -176,9 +176,9 @@ pub struct BusPortChip { _marker: PhantomData, } -impl BusPortChip { +impl BusPortChip { /// Create a new bus port with a single access. - pub fn connect>>( + pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, op: BusOpX, @@ -195,7 +195,7 @@ impl BusPortChip { } /// Assign an operation. - pub fn assign>( + pub fn assign>( &self, bus_assigner: &mut BusAssigner, offset: usize, @@ -219,7 +219,7 @@ struct BusPortAssigner { count: isize, } -impl Assigner for BusPortAssigner { +impl Assigner for BusPortAssigner { fn assign(&self, region: &mut Region<'_, F>, helper: F) -> (usize, F) { region .assign_advice( @@ -242,9 +242,9 @@ pub struct BusPortMulti { ports: Vec>, } -impl BusPortMulti { +impl BusPortMulti { /// Create and connect a new bus port with multiple accesses. - pub fn connect>>( + pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, ops: Vec>, @@ -257,7 +257,7 @@ impl BusPortMulti { } /// Assign operations. - pub fn assign>( + pub fn assign>( &self, bus_assigner: &mut BusAssigner, offset: usize, diff --git a/gadgets/src/bus/port_assigner.rs b/gadgets/src/bus/port_assigner.rs index f4bea4fd6e..7b36abf350 100644 --- a/gadgets/src/bus/port_assigner.rs +++ b/gadgets/src/bus/port_assigner.rs @@ -1,12 +1,17 @@ -use super::{bus_builder::BusAssigner, bus_codec::BusMessage, bus_port::BusOpA, util::HelperBatch}; -use halo2_proofs::{ - arithmetic::FieldExt, - circuit::{Region, Value}, +use super::{ + bus_builder::BusAssigner, bus_codec::BusMessageF, bus_port::BusOpA, util::HelperBatch, Field, +}; +use halo2_proofs::circuit::{Region, Value}; +use std::{ + collections::{ + hash_map::Entry::{Occupied, Vacant}, + HashMap, + }, + marker::PhantomData, }; -use std::{collections::HashMap, marker::PhantomData}; /// Assigners are used to delay the assignment until helper values are computed. -pub trait Assigner { +pub trait Assigner { /// Given the helper value, assign ports and return (offset, term). #[must_use = "terms must be added to the bus"] fn assign(&self, region: &mut Region<'_, F>, helper: F) -> (usize, F); @@ -18,7 +23,7 @@ pub struct PortAssigner { _marker: PhantomData, } -impl> PortAssigner { +impl> PortAssigner { /// Create a new PortAssigner. pub fn new() -> Self { Self { @@ -51,11 +56,11 @@ impl> PortAssigner { /// OpCounter tracks the messages taken, to help generating the puts. #[derive(Clone, Debug)] pub struct BusOpCounter { - counts: HashMap, isize>, + counts: HashMap, _marker: PhantomData<(F, M)>, } -impl> BusOpCounter { +impl> BusOpCounter { /// Create a new BusOpCounter. pub fn new() -> Self { Self { @@ -66,11 +71,21 @@ impl> BusOpCounter { /// Report an operation. pub fn track_op(&mut self, op: &BusOpA) { - let key = Self::to_key(op.message()); - self.counts - .entry(key) - .and_modify(|c| *c = *c + op.count()) - .or_insert_with(|| op.count()); + if op.count() == 0 { + return; + } + match self.counts.entry(op.message()) { + Occupied(mut entry) => { + let count = entry.get_mut(); + *count += op.count(); + if *count == 0 { + entry.remove(); + } + } + Vacant(entry) => { + entry.insert(op.count()); + } + }; } /// Count how many times a message was taken (net of puts). @@ -85,15 +100,6 @@ impl> BusOpCounter { /// Count how many times a message was put (net positive) or taken (net negative). fn count_ops(&self, message: &M) -> isize { - let key = Self::to_key(message.clone()); - *self.counts.get(&key).unwrap_or(&0) - } - - fn to_key(message: M) -> Vec { - let mut bytes = vec![]; - for f in message.into_items() { - bytes.extend_from_slice(f.to_repr().as_ref()); - } - bytes + *self.counts.get(message).unwrap_or(&0) } } diff --git a/gadgets/src/bus/port_multi.rs b/gadgets/src/bus/port_multi.rs new file mode 100644 index 0000000000..dfdab88824 --- /dev/null +++ b/gadgets/src/bus/port_multi.rs @@ -0,0 +1,158 @@ + +/// A chip with many accesses to the bus. BusPortDual uses only one helper cell, however the +/// degree of input expressions is more limited than with BusPortSingle. +/// The helper cell can be used for something else if all op.count are zero. +pub struct BusPortDual2; + +impl BusPortDual2 { + /// Create a new bus port with two accesses. + pub fn connect>( + meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder, + ops: Vec>, + helper: Expression, + ) { + let term = Self::create_term(meta, bus_builder.codec(), ops, helper); + bus_builder.add_term(term); + } + + /// Return the witness that must be assigned to the helper cell. + /// Prefer using BusAssigner instead. + pub fn helper_denom>( + codec: &BusCodecVal, + messages: Vec, + ) -> Value { + let m0 = messages[0].clone(); // TODO + let m1 = messages[1].clone(); + codec.compress(m0) * codec.compress(m1) + } + + fn create_term>( + meta: &mut ConstraintSystem, + codec: &BusCodecExpr, + ops: Vec>, + helper: Expression, + ) -> BusTerm { + let denoms = ops + .iter() + .map(|op| codec.compress(op.message())) + .collect::>(); + + let others = Self::product_of_others(denoms.clone()); + + let terms = ops + .iter() + .zip(others) + .map(|(op, others)| op.count() * helper.clone() * others.clone()) + .collect::>(); + + meta.create_gate("bus access (multi)", |_| { + ops.iter() + .zip(denoms) + .zip(terms.iter()) + .map(|((op, denom), term)| { + // Verify that: + // term == count / denom + // term * denom - count == 0 + term.clone() * denom - op.count() + }) + }); + + let total_term = terms.into_iter().reduce(|acc, term| acc + term).unwrap(); + BusTerm::verified(total_term) + } + + fn product_of_others(vals: Vec>) -> Vec> { + // all_after[i] contains the product of all values after vals[i] (non-inclusive). + let all_afters = { + let mut all_after = 1.expr(); + let mut all_afters = Vec::with_capacity(vals.len()); + for val in vals.iter().rev() { + all_afters.push(all_after.clone()); + all_after = all_after * val.clone(); + } + all_afters.reverse(); + all_afters + }; + + let mut all_before = 1.expr(); + let mut all_others = Vec::with_capacity(vals.len()); + for (val, all_after) in vals.into_iter().zip(all_afters) { + all_others.push(all_before.clone() * all_after); + all_before = all_before * val; + } + + all_others + } +} + + +/// A chip to access the bus. It manages its own helper column and gives one access per row. +#[derive(Clone, Debug)] +pub struct BusPortChip2 { + helper: Column, + _marker: PhantomData, +} + +impl BusPortChip2 { + /// Create a new bus port with a single access. + pub fn connect>( + meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder, + ops: Vec>, + ) -> Self { + let helper = meta.advice_column_in(ThirdPhase); + let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); + + BusPortDual2::connect(meta, bus_builder, ops, helper_expr); + + Self { + helper, + _marker: PhantomData, + } + } + + /// Assign an operation. + pub fn assign>( + &self, + bus_assigner: &mut BusAssigner, + offset: usize, + ops: Vec>, + ) { + let messages = ops.iter().map(|op| op.message()).collect(); + let denom = BusPortDual2::helper_denom(bus_assigner.codec(), messages); + + let cmd = Box::new(BusPortAssigner2 { + offset, + column: self.helper, + count: ops[0].count(), // TODO + }); + + for op in &ops { + bus_assigner.op_counter().track_op(&op); + } + bus_assigner.port_assigner().assign_later(cmd, denom); + } +} + +struct BusPortAssigner2 { + offset: usize, + column: Column, + count: isize, +} + +impl Assigner for BusPortAssigner2 { + fn assign(&self, region: &mut Region<'_, F>, helper: F) -> (usize, F) { + region + .assign_advice( + || "BusPort_helper", + self.column, + self.offset, + || Value::known(helper), + ) + .unwrap(); + + let term = from_isize::(self.count) * helper; + (self.offset, term) + } +} diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index bb8702b4f6..9139222a8b 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -1,6 +1,5 @@ use crate::util::{query_expression, Expr}; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Layouter, SimpleFloorPlanner, Value}, dev::MockProver, halo2curves::bn256::Fr, @@ -15,6 +14,7 @@ use super::{ bus_codec::{BusCodecExpr, BusCodecVal}, bus_lookup::BusLookupConfig, bus_port::*, + Field, }; #[test] @@ -23,7 +23,7 @@ fn test_bus() { } #[derive(Clone)] -struct TestCircuitConfig { +struct TestCircuitConfig { enabled: Column, bus_config: BusConfig, bus_lookup: BusLookupConfig, @@ -33,12 +33,12 @@ struct TestCircuitConfig { } #[derive(Default, Clone)] -struct TestCircuit { +struct TestCircuit { n_rows: usize, _marker: PhantomData, } -impl Circuit for TestCircuit { +impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; From 64b5c9e91aa6e458fdf94590ab0ae39821fac04e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 6 Oct 2023 14:45:09 +0200 Subject: [PATCH 32/67] =?UTF-8?q?bus:=20review=20names=20(send,=20?= =?UTF-8?q?=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gadgets/src/bus/bus_lookup.rs | 13 ++++-- gadgets/src/bus/bus_port.rs | 52 ++++++++++++++------- gadgets/src/bus/port_assigner.rs | 18 +++---- gadgets/src/bus/port_multi.rs | 6 +-- gadgets/src/bus/tests.rs | 15 +++--- zkevm-circuits/src/evm_circuit.rs | 24 +++++----- zkevm-circuits/src/evm_circuit/execution.rs | 4 +- 7 files changed, 76 insertions(+), 56 deletions(-) diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index 9923eb848f..061eb8ce2f 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -1,11 +1,11 @@ -//! The TableBus circuit puts items from a table to the bus. +//! The BusLookup chip exposes entries from a table as messages on the bus. use crate::util::query_expression; use super::{ bus_builder::{BusAssigner, BusBuilder}, bus_codec::{BusMessageExpr, BusMessageF}, - bus_port::{BusOp, BusOpA, BusPortChip}, + bus_port::{BusOp, BusOpF, BusPortChip}, util::from_isize, Field, }; @@ -33,8 +33,11 @@ impl BusLookupConfig { let count = meta.advice_column(); let count_expr = query_expression(meta, |meta| meta.query_advice(count, Rotation::cur())); - let port = - BusPortChip::connect(meta, bus_builder, BusOp::put(message, enabled * count_expr)); + let port = BusPortChip::connect( + meta, + bus_builder, + BusOp::send_to_lookups(message, enabled * count_expr), + ); Self { port, count } } @@ -45,7 +48,7 @@ impl BusLookupConfig { region: &mut Region<'_, F>, bus_assigner: &mut BusAssigner, offset: usize, - op: BusOpA, + op: BusOpF, ) -> Result<(), Error> { region.assign_advice( || "BusLookup", diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index ffe6dd38ae..ff633f886c 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -15,10 +15,10 @@ use halo2_proofs::{ use std::{marker::PhantomData, ops::Neg}; /// A bus operation, as expressions for circuit config. -pub type BusOpX = BusOp>; +pub type BusOpExpr = BusOp>; /// A bus operation, as values for circuit assignment. -pub type BusOpA = BusOp; +pub type BusOpF = BusOp; /// A bus operation. #[derive(Clone, Debug)] @@ -32,22 +32,38 @@ where M: Clone, C: Clone + Neg, { - /// Put an item. The expression evaluates to 0 or the number of copies. - pub fn put(message: M, count: C) -> Self { - Self { message, count } + /// Receive a message, with the expectation that it carries a true fact. This can be a + /// cross-circuit call answered by a `send`, or a lookup query answered by a `send_to_lookups`. + /// Enabled must be 0 or 1. + pub fn receive(message: M, enabled: C) -> Self { + Self { + message, + count: -enabled, + } + } + + /// Send a message, with the responsibility to verify that it states a true fact, and the + /// expectation that it is received exactly once somewhere else. Enabled must be 0 or 1. + pub fn send(message: M, enabled: C) -> Self { + Self { + message, + count: enabled, + } } - /// Take an item. The expression evaluates to 0 or 1. - pub fn take(message: M, count: C) -> Self { - Self::put(message, -count) + /// Expose an entry of a lookup table as a bus message, with the responsibility that it is a + /// true fact. It can be received any number of times. This number is the `count`, given as + /// advice, or 0 to disable. + pub fn send_to_lookups(message: M, count: C) -> Self { + Self { message, count } } - /// The message to put or take. + /// The message to send or receive. pub fn message(&self) -> M { self.message.clone() } - /// The number of copies of the message to put (if positive) or take (if negative). + /// The number of copies of the message to send (if positive) or receive (if negative). pub fn count(&self) -> C { self.count.clone() } @@ -63,7 +79,7 @@ impl BusPortSingle { pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, - op: BusOpX, + op: BusOpExpr, helper: Expression, ) { let term = Self::create_term(meta, bus_builder.codec(), op, helper); @@ -84,7 +100,7 @@ impl BusPortSingle { fn create_term>( meta: &mut ConstraintSystem, codec: &BusCodecExpr, - op: BusOpX, + op: BusOpExpr, helper: Expression, ) -> BusTerm { let term = op.count() * helper.clone(); @@ -114,7 +130,7 @@ impl BusPortDual { pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, - ops: [BusOpX; 2], + ops: [BusOpExpr; 2], helper: Expression, ) { let term = Self::create_term(meta, bus_builder.codec(), ops, helper); @@ -134,7 +150,7 @@ impl BusPortDual { fn create_term>( meta: &mut ConstraintSystem, codec: &BusCodecExpr, - ops: [BusOpX; 2], + ops: [BusOpExpr; 2], helper: Expression, ) -> BusTerm { let denom_0 = codec.compress(ops[0].message()); @@ -181,7 +197,7 @@ impl BusPortChip { pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, - op: BusOpX, + op: BusOpExpr, ) -> Self { let helper = meta.advice_column_in(ThirdPhase); let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); @@ -199,7 +215,7 @@ impl BusPortChip { &self, bus_assigner: &mut BusAssigner, offset: usize, - op: BusOpA, + op: BusOpF, ) { let cmd = Box::new(BusPortAssigner { offset, @@ -247,7 +263,7 @@ impl BusPortMulti { pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, - ops: Vec>, + ops: Vec>, ) -> Self { let ports = ops .into_iter() @@ -261,7 +277,7 @@ impl BusPortMulti { &self, bus_assigner: &mut BusAssigner, offset: usize, - ops: Vec>, + ops: Vec>, ) { assert_eq!(self.ports.len(), ops.len()); for (port, op) in self.ports.iter().zip(ops) { diff --git a/gadgets/src/bus/port_assigner.rs b/gadgets/src/bus/port_assigner.rs index 7b36abf350..c44190cc1b 100644 --- a/gadgets/src/bus/port_assigner.rs +++ b/gadgets/src/bus/port_assigner.rs @@ -1,5 +1,5 @@ use super::{ - bus_builder::BusAssigner, bus_codec::BusMessageF, bus_port::BusOpA, util::HelperBatch, Field, + bus_builder::BusAssigner, bus_codec::BusMessageF, bus_port::BusOpF, util::HelperBatch, Field, }; use halo2_proofs::circuit::{Region, Value}; use std::{ @@ -53,7 +53,7 @@ impl> PortAssigner { } } -/// OpCounter tracks the messages taken, to help generating the puts. +/// OpCounter tracks the messages received, to help generating the corresponding sends. #[derive(Clone, Debug)] pub struct BusOpCounter { counts: HashMap, @@ -69,8 +69,8 @@ impl> BusOpCounter { } } - /// Report an operation. - pub fn track_op(&mut self, op: &BusOpA) { + /// Record an operation that went on the bus. + pub fn track_op(&mut self, op: &BusOpF) { if op.count() == 0 { return; } @@ -88,17 +88,17 @@ impl> BusOpCounter { }; } - /// Count how many times a message was taken (net of puts). - pub fn count_takes(&self, message: &M) -> isize { + /// Count how many times a message was received (net of sends). + pub fn count_receives(&self, message: &M) -> isize { (-self.count_ops(message)).max(0) } - /// Count how many times a message was put (net of takes). - pub fn count_puts(&self, message: &M) -> isize { + /// Count how many times a message was sent (net of receives). + pub fn count_sent(&self, message: &M) -> isize { self.count_ops(message).max(0) } - /// Count how many times a message was put (net positive) or taken (net negative). + /// Count how many times a message was sent (net positive) or received (net negative). fn count_ops(&self, message: &M) -> isize { *self.counts.get(message).unwrap_or(&0) } diff --git a/gadgets/src/bus/port_multi.rs b/gadgets/src/bus/port_multi.rs index dfdab88824..24f30d0965 100644 --- a/gadgets/src/bus/port_multi.rs +++ b/gadgets/src/bus/port_multi.rs @@ -9,7 +9,7 @@ impl BusPortDual2 { pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, - ops: Vec>, + ops: Vec>, helper: Expression, ) { let term = Self::create_term(meta, bus_builder.codec(), ops, helper); @@ -30,7 +30,7 @@ impl BusPortDual2 { fn create_term>( meta: &mut ConstraintSystem, codec: &BusCodecExpr, - ops: Vec>, + ops: Vec>, helper: Expression, ) -> BusTerm { let denoms = ops @@ -99,7 +99,7 @@ impl BusPortChip2 { pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, - ops: Vec>, + ops: Vec>, ) -> Self { let helper = meta.advice_column_in(ThirdPhase); let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 9139222a8b..fba9718610 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -59,14 +59,15 @@ impl Circuit for TestCircuit { let message = vec![2.expr()]; - // Circuit 1 puts values dynamically. + // Circuit 1 sends values dynamically. let bus_lookup = BusLookupConfig::connect(cs, &mut bus_builder, message.clone(), enabled_expr.clone()); - // Circuit 2 takes one value per row. + // Circuit 2 receives one value per row. let count2_expr = enabled_expr * 1.expr(); - let port2 = BusPortChip::connect(cs, &mut bus_builder, BusOp::take(message, count2_expr)); + let port2 = + BusPortChip::connect(cs, &mut bus_builder, BusOp::receive(message, count2_expr)); // Global bus connection. let bus_config = BusConfig::new(cs, &bus_builder.build()); @@ -102,7 +103,7 @@ impl Circuit for TestCircuit { let mut bus_assigner = BusAssigner::new(BusCodecVal::new(rand), self.n_rows); - // Circuit 1 puts a message on some row. + // Circuit 1 sends a message on some row. { // Do normal circuit assignment logic, and obtain a message. let message = vec![F::from(2)]; @@ -116,11 +117,11 @@ impl Circuit for TestCircuit { &mut region, &mut bus_assigner, offset, - BusOp::put(message, count), + BusOp::send_to_lookups(message, count), )?; } - // Circuit 2 takes one message per row. + // Circuit 2 receives one message per row. { // First pass: run circuit steps. for offset in 0..self.n_rows { @@ -130,7 +131,7 @@ impl Circuit for TestCircuit { // Assign an operation to the port of this circuit, and to the shared bus. config .port2 - .assign(&mut bus_assigner, offset, BusOp::take(message, 1)); + .assign(&mut bus_assigner, offset, BusOp::receive(message, 1)); } } diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index e3777712d7..aaa7a5d3eb 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -49,9 +49,9 @@ pub struct EvmCircuitConfig { fixed_table: [Column; 4], byte_table: [Column; 1], dual_byte_table: [Column; 2], - enable_table: Column, bus: BusConfig, - table_to_bus: BusLookupConfig, + bus_lookup: BusLookupConfig, + enable_bus_lookup: Column, pub(crate) execution: Box>, // External tables tx_table: TxTable, @@ -140,12 +140,12 @@ impl EvmCircuitConfig { let fixed_table = [(); 4].map(|_| meta.fixed_column()); let byte_table = [(); 1].map(|_| meta.fixed_column()); let dual_byte_table = [(); 2].map(|_| meta.fixed_column()); - let enable_table = meta.fixed_column(); + let enable_bus_lookup = meta.fixed_column(); let mut bus_builder = BusBuilder::new(BusCodecExpr::new(challenges.lookup_input())); - let table_to_bus = - Self::configure_table_to_bus(meta, &mut bus_builder, &dual_byte_table, enable_table); + let bus_lookup = + Self::configure_bus_lookup(meta, &mut bus_builder, &dual_byte_table, enable_bus_lookup); let execution = Box::new(ExecutionConfig::configure( meta, @@ -188,9 +188,9 @@ impl EvmCircuitConfig { fixed_table, byte_table, dual_byte_table, - enable_table, bus, - table_to_bus, + bus_lookup, + enable_bus_lookup, execution, tx_table, rw_table, @@ -206,7 +206,7 @@ impl EvmCircuitConfig { } } - fn configure_table_to_bus( + fn configure_bus_lookup( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder>, dual_byte_table: &[Column; 2], @@ -290,7 +290,7 @@ impl EvmCircuitConfig { region.assign_fixed( || "", - self.enable_table, + self.enable_bus_lookup, offset, || Value::known(F::one()), )?; @@ -308,12 +308,12 @@ impl EvmCircuitConfig { || Value::known(message[1]), )?; - let count = bus_assigner.op_counter().count_takes(&message); - self.table_to_bus.assign( + let count = bus_assigner.op_counter().count_receives(&message); + self.bus_lookup.assign( &mut region, bus_assigner, offset, - BusOp::put(message, count), + BusOp::send_to_lookups(message, count), )?; } } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 643b4c22d9..e77cc795ed 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -947,7 +947,7 @@ impl ExecutionConfig { .chunks(2) .map(|columns| { let message = [columns[0].clone(), columns[1].clone()]; - BusOp::take(message, q_usable.clone()) + BusOp::receive(message, q_usable.clone()) }) .collect::>(); @@ -1350,7 +1350,7 @@ impl ExecutionConfig { F::zero() }; let message = [byte_0, byte_1]; - BusOp::take(message, 1) + BusOp::receive(message, 1) }) .collect::>(); From e98765b4e99d4ab30055b754f53c25cc47395283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Sat, 7 Oct 2023 20:46:45 +0200 Subject: [PATCH 33/67] bus: separate enabled and count --- gadgets/src/bus/bus_lookup.rs | 5 +- gadgets/src/bus/bus_port.rs | 88 ++++++++++++++------- gadgets/src/bus/tests.rs | 14 ++-- zkevm-circuits/src/evm_circuit.rs | 2 +- zkevm-circuits/src/evm_circuit/execution.rs | 6 +- 5 files changed, 75 insertions(+), 40 deletions(-) diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index 061eb8ce2f..7813910093 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -27,8 +27,8 @@ impl BusLookupConfig { pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, - message: M, enabled: Expression, + message: M, ) -> Self { let count = meta.advice_column(); let count_expr = query_expression(meta, |meta| meta.query_advice(count, Rotation::cur())); @@ -36,7 +36,8 @@ impl BusLookupConfig { let port = BusPortChip::connect( meta, bus_builder, - BusOp::send_to_lookups(message, enabled * count_expr), + enabled, + BusOp::send_to_lookups(message, count_expr), ); Self { port, count } diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index ff633f886c..a346e262dd 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -12,7 +12,7 @@ use halo2_proofs::{ plonk::{Advice, Column, ConstraintSystem, Expression, ThirdPhase}, poly::Rotation, }; -use std::{marker::PhantomData, ops::Neg}; +use std::marker::PhantomData; /// A bus operation, as expressions for circuit config. pub type BusOpExpr = BusOp>; @@ -30,30 +30,28 @@ pub struct BusOp { impl BusOp where M: Clone, - C: Clone + Neg, + C: Count, { /// Receive a message, with the expectation that it carries a true fact. This can be a /// cross-circuit call answered by a `send`, or a lookup query answered by a `send_to_lookups`. - /// Enabled must be 0 or 1. - pub fn receive(message: M, enabled: C) -> Self { + pub fn receive(message: M) -> Self { Self { message, - count: -enabled, + count: C::neg_one(), } } /// Send a message, with the responsibility to verify that it states a true fact, and the - /// expectation that it is received exactly once somewhere else. Enabled must be 0 or 1. - pub fn send(message: M, enabled: C) -> Self { + /// expectation that it is received exactly once somewhere else. + pub fn send(message: M) -> Self { Self { message, - count: enabled, + count: C::one(), } } /// Expose an entry of a lookup table as a bus message, with the responsibility that it is a - /// true fact. It can be received any number of times. This number is the `count`, given as - /// advice, or 0 to disable. + /// true fact. It can be received any number of times. This number is the `count` advice. pub fn send_to_lookups(message: M, count: C) -> Self { Self { message, count } } @@ -69,51 +67,78 @@ where } } +/// Trait usable as BusOp count (Expression or isize). +pub trait Count: Clone { + /// 1 + fn one() -> Self; + /// -1 + fn neg_one() -> Self; +} + +impl Count for Expression { + fn one() -> Self { + Self::Constant(F::one()) + } + fn neg_one() -> Self { + Self::Constant(-F::one()) + } +} + +impl Count for isize { + fn one() -> Self { + 1 + } + fn neg_one() -> Self { + -1 + } +} + /// A chip to access to the bus. #[derive(Clone, Debug)] pub struct BusPortSingle; impl BusPortSingle { /// Create a new bus port with a single access. - /// The helper cell can be used for something else if op.count is zero. + /// The helper cell can be used for something else when not enabled. pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, + enabled: Expression, op: BusOpExpr, helper: Expression, ) { - let term = Self::create_term(meta, bus_builder.codec(), op, helper); + let term = Self::create_term(meta, bus_builder.codec(), enabled, op, helper); bus_builder.add_term(term); } /// Return the witness that must be assigned to the helper cell. - /// Prefer using BusAssigner instead. + /// Very slow. Prefer `PortAssigner::assign_later` instead. pub fn helper_witness>( codec: &BusCodecVal, - message: M, + op: BusOpF, ) -> Value { codec - .compress(message) - .map(|x| x.invert().unwrap_or(F::zero())) + .compress(op.message()) + .map(|denom| from_isize::(op.count()) * denom.invert().unwrap_or(F::zero())) } fn create_term>( meta: &mut ConstraintSystem, codec: &BusCodecExpr, + enabled: Expression, op: BusOpExpr, helper: Expression, ) -> BusTerm { - let term = op.count() * helper.clone(); - let denom = codec.compress(op.message()); + let term = enabled.clone() * helper.clone(); meta.create_gate("bus access", |_| { - // Verify that `term = count / denom`. + // Verify that `term = enabled * count / compress(message)`. // - // With witness: helper = 1 / denom + // With witness: helper = count / compress(message) // - // If `count = 0`, then `term = 0` by definition. In that case, the helper cell is not + // If `enabled = 0`, then `term = 0` by definition. In that case, the helper cell is not // constrained, so it can be used for something else. - [term.clone() * denom - op.count()] + [term.clone() * codec.compress(op.message()) - enabled * op.count()] }); BusTerm::verified(term) @@ -130,10 +155,11 @@ impl BusPortDual { pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, + enabled: Expression, ops: [BusOpExpr; 2], helper: Expression, ) { - let term = Self::create_term(meta, bus_builder.codec(), ops, helper); + let term = Self::create_term(meta, bus_builder.codec(), enabled, ops, helper); bus_builder.add_term(term); } @@ -150,9 +176,12 @@ impl BusPortDual { fn create_term>( meta: &mut ConstraintSystem, codec: &BusCodecExpr, + enabled: Expression, ops: [BusOpExpr; 2], helper: Expression, ) -> BusTerm { + // TODO: no need for the count in the constraint. + // TODO: only one constraint for both ops. let denom_0 = codec.compress(ops[0].message()); let denom_1 = codec.compress(ops[1].message()); @@ -197,12 +226,13 @@ impl BusPortChip { pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, + enabled: Expression, op: BusOpExpr, ) -> Self { let helper = meta.advice_column_in(ThirdPhase); let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); - BusPortSingle::connect(meta, bus_builder, op, helper_expr); + BusPortSingle::connect(meta, bus_builder, enabled, op, helper_expr); Self { helper, @@ -236,17 +266,18 @@ struct BusPortAssigner { } impl Assigner for BusPortAssigner { - fn assign(&self, region: &mut Region<'_, F>, helper: F) -> (usize, F) { + fn assign(&self, region: &mut Region<'_, F>, inversed_denom: F) -> (usize, F) { + let term = from_isize::(self.count) * inversed_denom; + region .assign_advice( || "BusPort_helper", self.column, self.offset, - || Value::known(helper), + || Value::known(term), ) .unwrap(); - let term = from_isize::(self.count) * helper; (self.offset, term) } } @@ -263,11 +294,12 @@ impl BusPortMulti { pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, + enabled: Expression, ops: Vec>, ) -> Self { let ports = ops .into_iter() - .map(|op| BusPortChip::connect(meta, bus_builder, op)) + .map(|op| BusPortChip::connect(meta, bus_builder, enabled.clone(), op)) .collect(); Self { ports } } diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index fba9718610..57f3a2bf3b 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -61,13 +61,15 @@ impl Circuit for TestCircuit { // Circuit 1 sends values dynamically. let bus_lookup = - BusLookupConfig::connect(cs, &mut bus_builder, message.clone(), enabled_expr.clone()); + BusLookupConfig::connect(cs, &mut bus_builder, enabled_expr.clone(), message.clone()); // Circuit 2 receives one value per row. - let count2_expr = enabled_expr * 1.expr(); - - let port2 = - BusPortChip::connect(cs, &mut bus_builder, BusOp::receive(message, count2_expr)); + let port2 = BusPortChip::connect( + cs, + &mut bus_builder, + enabled_expr, + BusOp::receive(message), + ); // Global bus connection. let bus_config = BusConfig::new(cs, &bus_builder.build()); @@ -131,7 +133,7 @@ impl Circuit for TestCircuit { // Assign an operation to the port of this circuit, and to the shared bus. config .port2 - .assign(&mut bus_assigner, offset, BusOp::receive(message, 1)); + .assign(&mut bus_assigner, offset, BusOp::receive(message)); } } diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index aaa7a5d3eb..10e2bbcc02 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -219,7 +219,7 @@ impl EvmCircuitConfig { ] }); let enabled = query_expression(meta, |meta| meta.query_fixed(enabled, Rotation::cur())); - BusLookupConfig::connect(meta, bus_builder, message, enabled) + BusLookupConfig::connect(meta, bus_builder, enabled, message) } } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index e77cc795ed..96ec15810c 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -947,11 +947,11 @@ impl ExecutionConfig { .chunks(2) .map(|columns| { let message = [columns[0].clone(), columns[1].clone()]; - BusOp::receive(message, q_usable.clone()) + BusOp::receive(message) }) .collect::>(); - BusPortMulti::connect(meta, bus_builder, ops) + BusPortMulti::connect(meta, bus_builder, q_usable, ops) } #[allow(clippy::too_many_arguments)] @@ -1350,7 +1350,7 @@ impl ExecutionConfig { F::zero() }; let message = [byte_0, byte_1]; - BusOp::receive(message, 1) + BusOp::receive(message) }) .collect::>(); From ddf3218b6f646654cf4c62634af60b9115c68a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Sat, 7 Oct 2023 21:29:04 +0200 Subject: [PATCH 34/67] bus: safer and lower degree bus check --- gadgets/src/bus/bus_chip.rs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/gadgets/src/bus/bus_chip.rs b/gadgets/src/bus/bus_chip.rs index 3e75a42e40..face22ff25 100644 --- a/gadgets/src/bus/bus_chip.rs +++ b/gadgets/src/bus/bus_chip.rs @@ -28,7 +28,6 @@ impl Expr for BusTerm { pub struct BusConfig { enabled: Column, is_first: Column, - is_last: Column, acc: Column, } @@ -37,35 +36,36 @@ impl BusConfig { pub fn new(cs: &mut ConstraintSystem, terms: &[BusTerm]) -> Self { let enabled = cs.fixed_column(); let is_first = cs.fixed_column(); - let is_last = cs.fixed_column(); let acc = cs.advice_column_in(ThirdPhase); cs.create_gate("bus sum check", |cs| { + let not_last = cs.query_fixed(enabled, Rotation::next()); let enabled = cs.query_fixed(enabled, Rotation::cur()); let is_first = cs.query_fixed(is_first, Rotation::cur()); - let is_last = cs.query_fixed(is_last, Rotation::cur()); let acc_next = cs.query_advice(acc, Rotation::next()); let acc = cs.query_advice(acc.clone(), Rotation::cur()); + // The sum of terms on the current row. let sum = terms .iter() .fold(0.expr(), |acc, term| acc + term.0.clone()); - let next_or_zero = (1.expr() - is_last) * acc_next; + let next_or_zero = not_last * acc_next; + let diff_or_zero = enabled * (next_or_zero - acc.clone()); [ // If is_first, then initialize: `acc = 0`. - is_first * acc.clone(), - // If not is_last, then accumulate: `acc_next = acc + ∑terms` - // If is_last, then the final sum is zero: `0 = acc + ∑terms` - enabled * (next_or_zero - (acc.clone() + sum)), + is_first * acc, + // If not last, the terms go into accumulator: `∑terms + acc = acc_next` + // If last, the final accumulator is zero: `∑terms + acc = 0` + // If not enabled, the terms add up to zero: `∑terms = 0` + sum - diff_or_zero, ] }); Self { enabled, is_first, - is_last, acc, } } @@ -90,13 +90,6 @@ impl BusConfig { || Value::known(F::one()), )?; - region.assign_fixed( - || "Bus_is_last", - self.is_last, - n_rows - 1, - || Value::known(F::one()), - )?; - for offset in 0..n_rows { region.assign_fixed( || "Bus_enable", From c324383bf960e0fd17401d35e066c4e110731d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Sun, 8 Oct 2023 21:14:55 +0200 Subject: [PATCH 35/67] bus: port batched --- Cargo.lock | 1 + gadgets/Cargo.toml | 1 + gadgets/src/bus.rs | 3 + gadgets/src/bus/bus_lookup.rs | 6 +- gadgets/src/bus/bus_port.rs | 200 +++++++++--------------- gadgets/src/bus/port_multi.rs | 280 +++++++++++++++++++++------------- gadgets/src/bus/tests.rs | 4 +- 7 files changed, 255 insertions(+), 240 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3f3367c12..07bace5d9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2245,6 +2245,7 @@ dependencies = [ "digest 0.7.6", "eth-types", "halo2_proofs", + "itertools", "rand", "rand_xorshift", "sha3 0.7.3", diff --git a/gadgets/Cargo.toml b/gadgets/Cargo.toml index 1ae6a29100..0e39cdc928 100644 --- a/gadgets/Cargo.toml +++ b/gadgets/Cargo.toml @@ -11,6 +11,7 @@ sha3 = "0.7.2" eth-types = { path = "../eth-types" } digest = "0.7.6" strum = "0.24" +itertools = "0.10.3" [dev-dependencies] rand_xorshift = "0.3" diff --git a/gadgets/src/bus.rs b/gadgets/src/bus.rs index d22d506119..5dba18b5bf 100644 --- a/gadgets/src/bus.rs +++ b/gadgets/src/bus.rs @@ -9,6 +9,9 @@ pub mod bus_builder; /// A chip to access the bus. pub mod bus_port; +/// A chip to access the bus, with batching. +pub mod port_multi; + /// A chip to expose a lookup table on a bus. pub mod bus_lookup; diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index 7813910093..336c4aa713 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -5,7 +5,7 @@ use crate::util::query_expression; use super::{ bus_builder::{BusAssigner, BusBuilder}, bus_codec::{BusMessageExpr, BusMessageF}, - bus_port::{BusOp, BusOpF, BusPortChip}, + bus_port::{BusOp, BusOpF, PortChip}, util::from_isize, Field, }; @@ -18,7 +18,7 @@ use halo2_proofs::{ /// BusLookup exposes a table as a lookup through the bus. #[derive(Clone, Debug)] pub struct BusLookupConfig { - port: BusPortChip, + port: PortChip, count: Column, } @@ -33,7 +33,7 @@ impl BusLookupConfig { let count = meta.advice_column(); let count_expr = query_expression(meta, |meta| meta.query_advice(count, Rotation::cur())); - let port = BusPortChip::connect( + let port = PortChip::connect( meta, bus_builder, enabled, diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index a346e262dd..ca53cc98bd 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -93,12 +93,49 @@ impl Count for isize { } } -/// A chip to access to the bus. +/// A chip to access the bus. It manages its own helper column and gives one access per row. #[derive(Clone, Debug)] -pub struct BusPortSingle; +pub struct PortChip { + helper: Column, + _marker: PhantomData, +} -impl BusPortSingle { +impl PortChip { /// Create a new bus port with a single access. + pub fn connect>( + meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder, + enabled: Expression, + op: BusOpExpr, + ) -> Self { + let helper = meta.advice_column_in(ThirdPhase); + let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); + + Port::connect(meta, bus_builder, enabled, op, helper_expr); + + Self { + helper, + _marker: PhantomData, + } + } + + /// Assign an operation. + pub fn assign>( + &self, + bus_assigner: &mut BusAssigner, + offset: usize, + op: BusOpF, + ) { + Port::assign(bus_assigner, offset, op, self.helper, 0); + } +} + +/// Functions to add an operation to the bus. +#[derive(Clone, Debug)] +pub struct Port; + +impl Port { + /// Create a new bus port with a single operation. /// The helper cell can be used for something else when not enabled. pub fn connect>( meta: &mut ConstraintSystem, @@ -111,15 +148,24 @@ impl BusPortSingle { bus_builder.add_term(term); } - /// Return the witness that must be assigned to the helper cell. - /// Very slow. Prefer `PortAssigner::assign_later` instead. - pub fn helper_witness>( - codec: &BusCodecVal, + /// Assign an operation. + pub fn assign>( + bus_assigner: &mut BusAssigner, + offset: usize, op: BusOpF, - ) -> Value { - codec - .compress(op.message()) - .map(|denom| from_isize::(op.count()) * denom.invert().unwrap_or(F::zero())) + helper: Column, + rotation: isize, + ) { + let cmd = Box::new(PortAssigner { + offset, + helper, + rotation, + count: op.count(), + }); + let denom = bus_assigner.codec().compress(op.message()); + + bus_assigner.op_counter().track_op(&op); + bus_assigner.port_assigner().assign_later(cmd, denom); } fn create_term>( @@ -129,7 +175,7 @@ impl BusPortSingle { op: BusOpExpr, helper: Expression, ) -> BusTerm { - let term = enabled.clone() * helper.clone(); + let term = helper * enabled.clone(); meta.create_gate("bus access", |_| { // Verify that `term = enabled * count / compress(message)`. @@ -138,142 +184,40 @@ impl BusPortSingle { // // If `enabled = 0`, then `term = 0` by definition. In that case, the helper cell is not // constrained, so it can be used for something else. - [term.clone() * codec.compress(op.message()) - enabled * op.count()] + [term.clone() * codec.compress(op.message()) - op.count() * enabled] }); BusTerm::verified(term) } -} - -/// A chip with two accesses to the bus. BusPortDual uses only one helper cell, however the -/// degree of input expressions is more limited than with BusPortSingle. -/// The helper cell can be used for something else if both op.count are zero. -pub struct BusPortDual; - -impl BusPortDual { - /// Create a new bus port with two accesses. - pub fn connect>( - meta: &mut ConstraintSystem, - bus_builder: &mut BusBuilder, - enabled: Expression, - ops: [BusOpExpr; 2], - helper: Expression, - ) { - let term = Self::create_term(meta, bus_builder.codec(), enabled, ops, helper); - bus_builder.add_term(term); - } /// Return the witness that must be assigned to the helper cell. - /// Prefer using BusAssigner instead. + /// Very slow. Prefer `PortAssigner::assign_later` instead. pub fn helper_witness>( codec: &BusCodecVal, - messages: [M; 2], - ) -> Value { - let [m0, m1] = messages; - (codec.compress(m0) * codec.compress(m1)).map(|x| x.invert().unwrap_or(F::zero())) - } - - fn create_term>( - meta: &mut ConstraintSystem, - codec: &BusCodecExpr, - enabled: Expression, - ops: [BusOpExpr; 2], - helper: Expression, - ) -> BusTerm { - // TODO: no need for the count in the constraint. - // TODO: only one constraint for both ops. - let denom_0 = codec.compress(ops[0].message()); - let denom_1 = codec.compress(ops[1].message()); - - // With witness: helper = 1 / (denom_0 * denom_1) - - // term_0 = count_0 * helper * denom_1 - let count_0 = ops[0].count(); - let term_0 = count_0.clone() * helper.clone() * denom_1.clone(); - - // term_1 = count_1 * helper * denom_0 - let count_1 = ops[1].count(); - let term_1 = count_1.clone() * helper.clone() * denom_0.clone(); - - // Verify that: - // term_0 == count_0 / denom_0 - // term_0 * denom_0 - count_0 == 0 - // - // And the same for term_1. - // - // In case both count_0 and count_1 are zero, then the helper cell is not constrained, so it - // can be used for something else. - meta.create_gate("bus access (dual)", |_| { - [ - term_0.clone() * denom_0 - count_0, - term_1.clone() * denom_1 - count_1, - ] - }); - - BusTerm::verified(term_0 + term_1) - } -} - -/// A chip to access the bus. It manages its own helper column and gives one access per row. -#[derive(Clone, Debug)] -pub struct BusPortChip { - helper: Column, - _marker: PhantomData, -} - -impl BusPortChip { - /// Create a new bus port with a single access. - pub fn connect>( - meta: &mut ConstraintSystem, - bus_builder: &mut BusBuilder, - enabled: Expression, - op: BusOpExpr, - ) -> Self { - let helper = meta.advice_column_in(ThirdPhase); - let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); - - BusPortSingle::connect(meta, bus_builder, enabled, op, helper_expr); - - Self { - helper, - _marker: PhantomData, - } - } - - /// Assign an operation. - pub fn assign>( - &self, - bus_assigner: &mut BusAssigner, - offset: usize, op: BusOpF, - ) { - let cmd = Box::new(BusPortAssigner { - offset, - column: self.helper, - count: op.count(), - }); - let denom = bus_assigner.codec().compress(op.message()); - - bus_assigner.op_counter().track_op(&op); - bus_assigner.port_assigner().assign_later(cmd, denom); + ) -> Value { + codec + .compress(op.message()) + .map(|denom| from_isize::(op.count()) * denom.invert().unwrap_or(F::zero())) } } -struct BusPortAssigner { +struct PortAssigner { offset: usize, - column: Column, + helper: Column, + rotation: isize, count: isize, } -impl Assigner for BusPortAssigner { +impl Assigner for PortAssigner { fn assign(&self, region: &mut Region<'_, F>, inversed_denom: F) -> (usize, F) { let term = from_isize::(self.count) * inversed_denom; region .assign_advice( || "BusPort_helper", - self.column, - self.offset, + self.helper, + (self.offset as isize + self.rotation) as usize, || Value::known(term), ) .unwrap(); @@ -286,7 +230,7 @@ impl Assigner for BusPortAssigner { #[derive(Clone, Debug)] pub struct BusPortMulti { // TODO: implement with as few helper columns as possible. - ports: Vec>, + ports: Vec>, } impl BusPortMulti { @@ -299,7 +243,7 @@ impl BusPortMulti { ) -> Self { let ports = ops .into_iter() - .map(|op| BusPortChip::connect(meta, bus_builder, enabled.clone(), op)) + .map(|op| PortChip::connect(meta, bus_builder, enabled.clone(), op)) .collect(); Self { ports } } diff --git a/gadgets/src/bus/port_multi.rs b/gadgets/src/bus/port_multi.rs index 24f30d0965..3f78f6c798 100644 --- a/gadgets/src/bus/port_multi.rs +++ b/gadgets/src/bus/port_multi.rs @@ -1,158 +1,224 @@ +use super::{ + bus_builder::{BusAssigner, BusBuilder}, + bus_chip::BusTerm, + bus_codec::{BusCodecExpr, BusCodecVal, BusMessageExpr, BusMessageF}, + bus_port::{BusOpExpr, BusOpF}, + port_assigner::Assigner, + util::from_isize, + Field, +}; +use crate::util::{query_expression, Expr}; +use halo2_proofs::{ + circuit::{Region, Value}, + plonk::{Advice, Column, ConstraintSystem, Expression, ThirdPhase}, + poly::Rotation, +}; +use itertools::Itertools; +use std::{marker::PhantomData, ops::Mul}; + +/// PortBatchedChip does multiple bus operations per row. It manages its own helper column. +#[derive(Clone, Debug)] +pub struct PortBatchedChip { + helper: Column, + _marker: PhantomData, +} + +impl PortBatchedChip { + /// Create a new bus port with multiple operations. + pub fn connect>( + meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder, + enabled: Expression, + ops: Vec>, + ) -> Self { + let helper = meta.advice_column_in(ThirdPhase); + let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); -/// A chip with many accesses to the bus. BusPortDual uses only one helper cell, however the -/// degree of input expressions is more limited than with BusPortSingle. -/// The helper cell can be used for something else if all op.count are zero. -pub struct BusPortDual2; + PortBatched::connect(meta, bus_builder, enabled, ops, helper_expr); -impl BusPortDual2 { - /// Create a new bus port with two accesses. + Self { + helper, + _marker: PhantomData, + } + } + + /// Assign an operation. + pub fn assign>( + &self, + bus_assigner: &mut BusAssigner, + offset: usize, + ops: Vec>, + ) { + PortBatched::assign(bus_assigner, offset, ops, self.helper, 0); + } +} + +/// Functions to add multiple operations to the bus, using only one helper cell. However, the degree +/// of input expressions is more limited than with the simple Port. +pub struct PortBatched; + +impl PortBatched { + /// Create a new bus port with multiple operations. + /// The helper cell can be used for something else when not enabled. pub fn connect>( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder, + enabled: Expression, ops: Vec>, helper: Expression, ) { - let term = Self::create_term(meta, bus_builder.codec(), ops, helper); + let term = Self::create_term(meta, bus_builder.codec(), enabled, ops, helper); bus_builder.add_term(term); } - /// Return the witness that must be assigned to the helper cell. - /// Prefer using BusAssigner instead. - pub fn helper_denom>( - codec: &BusCodecVal, - messages: Vec, - ) -> Value { - let m0 = messages[0].clone(); // TODO - let m1 = messages[1].clone(); - codec.compress(m0) * codec.compress(m1) + /// Assign an operation. + pub fn assign>( + bus_assigner: &mut BusAssigner, + offset: usize, + ops: Vec>, + helper: Column, + rotation: isize, + ) { + let (numer, denom) = Self::helper_fraction(bus_assigner.codec(), &ops); + + let cmd = Box::new(PortBatchedAssigner { + offset, + helper, + rotation, + numer, + }); + + for op in &ops { + bus_assigner.op_counter().track_op(&op); + } + bus_assigner.port_assigner().assign_later(cmd, denom); } fn create_term>( meta: &mut ConstraintSystem, codec: &BusCodecExpr, + enabled: Expression, ops: Vec>, helper: Expression, ) -> BusTerm { - let denoms = ops - .iter() - .map(|op| codec.compress(op.message())) - .collect::>(); - - let others = Self::product_of_others(denoms.clone()); - - let terms = ops - .iter() - .zip(others) - .map(|(op, others)| op.count() * helper.clone() * others.clone()) - .collect::>(); + // term = helper, or 0 when not enabled. + let total_term = helper * enabled.clone(); meta.create_gate("bus access (multi)", |_| { - ops.iter() - .zip(denoms) - .zip(terms.iter()) - .map(|((op, denom), term)| { - // Verify that: - // term == count / denom - // term * denom - count == 0 - term.clone() * denom - op.count() - }) + // denoms[i] = compress(messages[i]) + let denoms = ops + .iter() + .map(|op| codec.compress(op.message())) + .collect::>(); + + // other_denoms[i] = ∏ denoms[j] for j!=i + // all_denoms = ∏ denoms[j] for all j + let (other_denoms, all_denoms) = Self::product_of_others(denoms, 1.expr()); + + // counts_times_others = ∑ count[i] * other_denoms[i] + let counts_times_others = ops + .iter() + .zip_eq(other_denoms.into_iter()) + .map(|(op, other)| op.count() * other) + .reduce(|acc, term| acc + term) + .unwrap_or(0.expr()); + + // Verify that: term = enabled * ∑ counts[i] / compress(messages[i]) + // + // With witness: helper = ∑ counts[i] / compress(messages[i]) + // + // If `enabled = 0`, then `term = 0` by definition. In that case, the helper cell is not + // constrained, so it can be used for something else. + [total_term.clone() * all_denoms - counts_times_others * enabled] }); - let total_term = terms.into_iter().reduce(|acc, term| acc + term).unwrap(); BusTerm::verified(total_term) } - fn product_of_others(vals: Vec>) -> Vec> { - // all_after[i] contains the product of all values after vals[i] (non-inclusive). + /// Return the witness that must be assigned to the helper cell, as (numerator, denominator). + fn helper_fraction>( + codec: &BusCodecVal, + ops: &[BusOpF], + ) -> (F, Value) { + // denoms[i] = compress(messages[i]) + let denoms = { + let mut denoms = Vec::with_capacity(ops.len()); + for op in ops { + let denom = codec.compress(op.message()); + if denom.is_none() { + return (F::zero(), Value::unknown()); + } else { + denom.map(|denom| denoms.push(denom)); + } + } + denoms + }; + + // other_denoms[i] = ∏ denoms[j] for j!=i + // all_denoms = ∏ denoms[j] for all j + let (other_denoms, all_denoms) = Self::product_of_others(denoms, F::one()); + + // helper = ∑ counts[i] / compress(messages[i]) + // = (∑ counts[i] * other_denoms[i]) / all_denoms + let numer = ops + .iter() + .zip_eq(other_denoms.into_iter()) + .map(|(op, others)| from_isize::(op.count()) * others) + .reduce(|sum, term| sum + term) + .unwrap_or(F::zero()); + + (numer, Value::known(all_denoms)) + } + + /// Return products such that `others[i] = ∏ values[j] for j!=i`, and the product of all values. + fn product_of_others(values: Vec, one: T) -> (Vec, T) + where + T: Mul + Clone, + { + // all_afters[i] contains the product of all values after values[i] (non-inclusive). let all_afters = { - let mut all_after = 1.expr(); - let mut all_afters = Vec::with_capacity(vals.len()); - for val in vals.iter().rev() { + let mut all_after = one.clone(); + let mut all_afters = Vec::with_capacity(values.len()); + for value in values.iter().rev() { all_afters.push(all_after.clone()); - all_after = all_after * val.clone(); + all_after = all_after * value.clone(); } all_afters.reverse(); all_afters }; - let mut all_before = 1.expr(); - let mut all_others = Vec::with_capacity(vals.len()); - for (val, all_after) in vals.into_iter().zip(all_afters) { + // all_before at step i contains the product of all values before vals[i] (non-inclusive). + let mut all_before = one; + let mut all_others = Vec::with_capacity(values.len()); + for (value, all_after) in values.into_iter().zip(all_afters) { all_others.push(all_before.clone() * all_after); - all_before = all_before * val; + all_before = all_before * value; } - all_others + (all_others, all_before) } } - -/// A chip to access the bus. It manages its own helper column and gives one access per row. -#[derive(Clone, Debug)] -pub struct BusPortChip2 { +struct PortBatchedAssigner { + offset: usize, helper: Column, - _marker: PhantomData, -} - -impl BusPortChip2 { - /// Create a new bus port with a single access. - pub fn connect>( - meta: &mut ConstraintSystem, - bus_builder: &mut BusBuilder, - ops: Vec>, - ) -> Self { - let helper = meta.advice_column_in(ThirdPhase); - let helper_expr = query_expression(meta, |meta| meta.query_advice(helper, Rotation::cur())); - - BusPortDual2::connect(meta, bus_builder, ops, helper_expr); - - Self { - helper, - _marker: PhantomData, - } - } - - /// Assign an operation. - pub fn assign>( - &self, - bus_assigner: &mut BusAssigner, - offset: usize, - ops: Vec>, - ) { - let messages = ops.iter().map(|op| op.message()).collect(); - let denom = BusPortDual2::helper_denom(bus_assigner.codec(), messages); - - let cmd = Box::new(BusPortAssigner2 { - offset, - column: self.helper, - count: ops[0].count(), // TODO - }); - - for op in &ops { - bus_assigner.op_counter().track_op(&op); - } - bus_assigner.port_assigner().assign_later(cmd, denom); - } + rotation: isize, + numer: F, } -struct BusPortAssigner2 { - offset: usize, - column: Column, - count: isize, -} +impl Assigner for PortBatchedAssigner { + fn assign(&self, region: &mut Region<'_, F>, inversed_denom: F) -> (usize, F) { + let term = self.numer * inversed_denom; -impl Assigner for BusPortAssigner2 { - fn assign(&self, region: &mut Region<'_, F>, helper: F) -> (usize, F) { region .assign_advice( - || "BusPort_helper", - self.column, - self.offset, - || Value::known(helper), + || "PortBatched_helper", + self.helper, + (self.offset as isize + self.rotation) as usize, + || Value::known(term), ) .unwrap(); - let term = from_isize::(self.count) * helper; (self.offset, term) } } diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 57f3a2bf3b..c71110deef 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -27,7 +27,7 @@ struct TestCircuitConfig { enabled: Column, bus_config: BusConfig, bus_lookup: BusLookupConfig, - port2: BusPortChip, + port2: PortChip, rand: Challenge, _marker: PhantomData, } @@ -64,7 +64,7 @@ impl Circuit for TestCircuit { BusLookupConfig::connect(cs, &mut bus_builder, enabled_expr.clone(), message.clone()); // Circuit 2 receives one value per row. - let port2 = BusPortChip::connect( + let port2 = PortChip::connect( cs, &mut bus_builder, enabled_expr, From 15595d728f745aa253e245f09837cf4b4c83ead2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 9 Oct 2023 22:01:08 +0200 Subject: [PATCH 36/67] bus: calculate degree --- gadgets/src/bus/bus_port.rs | 35 +++++++++++++---- gadgets/src/bus/port_multi.rs | 71 +++++++++++++++++++++++------------ 2 files changed, 74 insertions(+), 32 deletions(-) diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index ca53cc98bd..b24f870281 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -168,6 +168,18 @@ impl Port { bus_assigner.port_assigner().assign_later(cmd, denom); } + /// Return the degree of the constraints given these inputs. + pub fn degree>( + codec: &BusCodecExpr, + enabled: Expression, + op: BusOpExpr, + helper: Expression, + ) -> usize { + let term = helper * enabled.clone(); + let [constraint] = Self::constraint(codec, enabled, op, term); + constraint.degree() + } + fn create_term>( meta: &mut ConstraintSystem, codec: &BusCodecExpr, @@ -178,18 +190,27 @@ impl Port { let term = helper * enabled.clone(); meta.create_gate("bus access", |_| { - // Verify that `term = enabled * count / compress(message)`. - // - // With witness: helper = count / compress(message) - // - // If `enabled = 0`, then `term = 0` by definition. In that case, the helper cell is not - // constrained, so it can be used for something else. - [term.clone() * codec.compress(op.message()) - op.count() * enabled] + Self::constraint(codec, enabled, op, term.clone()) }); BusTerm::verified(term) } + fn constraint>( + codec: &BusCodecExpr, + enabled: Expression, + op: BusOpExpr, + term: Expression, + ) -> [Expression; 1] { + // Verify that `term = enabled * count / compress(message)`. + // + // With witness: helper = count / compress(message) + // + // If `enabled = 0`, then `term = 0` by definition. In that case, the helper cell is not + // constrained, so it can be used for something else. + [term * codec.compress(op.message()) - op.count() * enabled] + } + /// Return the witness that must be assigned to the helper cell. /// Very slow. Prefer `PortAssigner::assign_later` instead. pub fn helper_witness>( diff --git a/gadgets/src/bus/port_multi.rs b/gadgets/src/bus/port_multi.rs index 3f78f6c798..08f38b0119 100644 --- a/gadgets/src/bus/port_multi.rs +++ b/gadgets/src/bus/port_multi.rs @@ -94,6 +94,18 @@ impl PortBatched { bus_assigner.port_assigner().assign_later(cmd, denom); } + /// Return the degree of the constraints given these inputs. + pub fn degree>( + codec: &BusCodecExpr, + enabled: Expression, + ops: Vec>, + helper: Expression, + ) -> usize { + let total_term = helper * enabled.clone(); + let [constraint] = Self::constraint(codec, enabled, ops, total_term); + constraint.degree() + } + fn create_term>( meta: &mut ConstraintSystem, codec: &BusCodecExpr, @@ -105,36 +117,45 @@ impl PortBatched { let total_term = helper * enabled.clone(); meta.create_gate("bus access (multi)", |_| { - // denoms[i] = compress(messages[i]) - let denoms = ops - .iter() - .map(|op| codec.compress(op.message())) - .collect::>(); - - // other_denoms[i] = ∏ denoms[j] for j!=i - // all_denoms = ∏ denoms[j] for all j - let (other_denoms, all_denoms) = Self::product_of_others(denoms, 1.expr()); - - // counts_times_others = ∑ count[i] * other_denoms[i] - let counts_times_others = ops - .iter() - .zip_eq(other_denoms.into_iter()) - .map(|(op, other)| op.count() * other) - .reduce(|acc, term| acc + term) - .unwrap_or(0.expr()); - - // Verify that: term = enabled * ∑ counts[i] / compress(messages[i]) - // - // With witness: helper = ∑ counts[i] / compress(messages[i]) - // - // If `enabled = 0`, then `term = 0` by definition. In that case, the helper cell is not - // constrained, so it can be used for something else. - [total_term.clone() * all_denoms - counts_times_others * enabled] + Self::constraint(codec, enabled, ops, total_term.clone()) }); BusTerm::verified(total_term) } + fn constraint>( + codec: &BusCodecExpr, + enabled: Expression, + ops: Vec>, + total_term: Expression, + ) -> [Expression; 1] { + // denoms[i] = compress(messages[i]) + let denoms = ops + .iter() + .map(|op| codec.compress(op.message())) + .collect::>(); + + // other_denoms[i] = ∏ denoms[j] for j!=i + // all_denoms = ∏ denoms[j] for all j + let (other_denoms, all_denoms) = Self::product_of_others(denoms, 1.expr()); + + // counts_times_others = ∑ count[i] * other_denoms[i] + let counts_times_others = ops + .iter() + .zip_eq(other_denoms.into_iter()) + .map(|(op, other)| op.count() * other) + .reduce(|acc, term| acc + term) + .unwrap_or(0.expr()); + + // Verify that: term = enabled * ∑ counts[i] / compress(messages[i]) + // + // With witness: helper = ∑ counts[i] / compress(messages[i]) + // + // If `enabled = 0`, then `term = 0` by definition. In that case, the helper cell is not + // constrained, so it can be used for something else. + [total_term * all_denoms - counts_times_others * enabled] + } + /// Return the witness that must be assigned to the helper cell, as (numerator, denominator). fn helper_fraction>( codec: &BusCodecVal, From c797b8717f35747e587d0c2841f363789c640ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 10 Oct 2023 10:41:26 +0200 Subject: [PATCH 37/67] bus: remove byte lookups --- zkevm-circuits/src/evm_circuit.rs | 27 ++------------------- zkevm-circuits/src/evm_circuit/execution.rs | 11 --------- 2 files changed, 2 insertions(+), 36 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 10e2bbcc02..cabd0c45c8 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -47,7 +47,6 @@ use witness::Block; #[derive(Clone, Debug)] pub struct EvmCircuitConfig { fixed_table: [Column; 4], - byte_table: [Column; 1], dual_byte_table: [Column; 2], bus: BusConfig, bus_lookup: BusLookupConfig, @@ -138,7 +137,6 @@ impl EvmCircuitConfig { }: EvmCircuitConfigArgs, ) -> Self { let fixed_table = [(); 4].map(|_| meta.fixed_column()); - let byte_table = [(); 1].map(|_| meta.fixed_column()); let dual_byte_table = [(); 2].map(|_| meta.fixed_column()); let enable_bus_lookup = meta.fixed_column(); @@ -152,7 +150,6 @@ impl EvmCircuitConfig { challenges, &mut bus_builder, &fixed_table, - &byte_table, &tx_table, &rw_table, &bytecode_table, @@ -168,7 +165,8 @@ impl EvmCircuitConfig { let bus = BusConfig::new(meta, &bus_builder.build()); - meta.annotate_lookup_any_column(byte_table[0], || "byte_range"); + meta.annotate_lookup_any_column(dual_byte_table[0], || "dual_byte_table_0"); + meta.annotate_lookup_any_column(dual_byte_table[1], || "dual_byte_table_1"); fixed_table.iter().enumerate().for_each(|(idx, &col)| { meta.annotate_lookup_any_column(col, || format!("fix_table_{idx}")) }); @@ -186,7 +184,6 @@ impl EvmCircuitConfig { Self { fixed_table, - byte_table, dual_byte_table, bus, bus_lookup, @@ -247,25 +244,6 @@ impl EvmCircuitConfig { ) } - /// Load byte table - pub fn load_byte_table(&self, layouter: &mut impl Layouter) -> Result<(), Error> { - layouter.assign_region( - || "byte table", - |mut region| { - for offset in 0..256 { - region.assign_fixed( - || "", - self.byte_table[0], - offset, - || Value::known(F::from(offset as u64)), - )?; - } - Ok(()) - }, - )?; - Ok(()) - } - /// Load dual byte table pub fn load_dual_byte_table( &self, @@ -463,7 +441,6 @@ impl SubCircuit for EvmCircuit { .assign_block(layouter, &mut bus_assigner, block, challenges)?; self.exports.borrow_mut().replace(export); - config.load_byte_table(layouter)?; config.load_dual_byte_table(layouter, &mut bus_assigner)?; layouter.assign_region( diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 96ec15810c..3e917345bf 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -379,7 +379,6 @@ impl ExecutionConfig { challenges: Challenges>, bus_builder: &mut BusBuilder>, fixed_table: &dyn LookupTable, - byte_table: &dyn LookupTable, tx_table: &dyn LookupTable, rw_table: &dyn LookupTable, bytecode_table: &dyn LookupTable, @@ -660,7 +659,6 @@ impl ExecutionConfig { Self::configure_lookup( meta, fixed_table, - byte_table, tx_table, rw_table, bytecode_table, @@ -958,7 +956,6 @@ impl ExecutionConfig { fn configure_lookup( meta: &mut ConstraintSystem, fixed_table: &dyn LookupTable, - byte_table: &dyn LookupTable, tx_table: &dyn LookupTable, rw_table: &dyn LookupTable, bytecode_table: &dyn LookupTable, @@ -999,14 +996,6 @@ impl ExecutionConfig { }); } } - for column in cell_manager.columns().iter() { - if let CellType::LookupByte = column.cell_type { - meta.lookup_any("Byte lookup", |meta| { - let byte_table_expression = byte_table.table_exprs(meta)[0].clone(); - vec![(column.expr(), byte_table_expression)] - }); - } - } } pub fn get_num_rows_required(&self, block: &Block) -> usize { From 5ce5fe8e0fe696f3e92d5b24ac764273d6f6aaf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 10 Oct 2023 18:36:20 +0200 Subject: [PATCH 38/67] bus: introduce multi-table message --- gadgets/src/bus/bus_lookup.rs | 4 +- gadgets/src/bus/port_multi.rs | 2 +- gadgets/src/bus/tests.rs | 6 +- zkevm-circuits/src/evm_circuit.rs | 70 ++++++++++++++------- zkevm-circuits/src/evm_circuit/execution.rs | 18 +++--- zkevm-circuits/src/evm_circuit/table.rs | 43 ++++++++++++- 6 files changed, 104 insertions(+), 39 deletions(-) diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index 336c4aa713..9f93fd1f7a 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -17,12 +17,12 @@ use halo2_proofs::{ /// BusLookup exposes a table as a lookup through the bus. #[derive(Clone, Debug)] -pub struct BusLookupConfig { +pub struct BusLookupChip { port: PortChip, count: Column, } -impl BusLookupConfig { +impl BusLookupChip { /// Create and connect a new BusLookup circuit from the expressions of message and count. pub fn connect>( meta: &mut ConstraintSystem, diff --git a/gadgets/src/bus/port_multi.rs b/gadgets/src/bus/port_multi.rs index 08f38b0119..5cf0c9d931 100644 --- a/gadgets/src/bus/port_multi.rs +++ b/gadgets/src/bus/port_multi.rs @@ -139,7 +139,7 @@ impl PortBatched { // all_denoms = ∏ denoms[j] for all j let (other_denoms, all_denoms) = Self::product_of_others(denoms, 1.expr()); - // counts_times_others = ∑ count[i] * other_denoms[i] + // counts_times_others = ∑ counts[i] * other_denoms[i] let counts_times_others = ops .iter() .zip_eq(other_denoms.into_iter()) diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index c71110deef..9331c78ec9 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -12,7 +12,7 @@ use super::{ bus_builder::*, bus_chip::*, bus_codec::{BusCodecExpr, BusCodecVal}, - bus_lookup::BusLookupConfig, + bus_lookup::BusLookupChip, bus_port::*, Field, }; @@ -26,7 +26,7 @@ fn test_bus() { struct TestCircuitConfig { enabled: Column, bus_config: BusConfig, - bus_lookup: BusLookupConfig, + bus_lookup: BusLookupChip, port2: PortChip, rand: Challenge, _marker: PhantomData, @@ -61,7 +61,7 @@ impl Circuit for TestCircuit { // Circuit 1 sends values dynamically. let bus_lookup = - BusLookupConfig::connect(cs, &mut bus_builder, enabled_expr.clone(), message.clone()); + BusLookupChip::connect(cs, &mut bus_builder, enabled_expr.clone(), message.clone()); // Circuit 2 receives one value per row. let port2 = PortChip::connect( diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index cabd0c45c8..24475e0d80 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -5,7 +5,7 @@ use gadgets::bus::{ bus_builder::{BusAssigner, BusBuilder}, bus_chip::BusConfig, bus_codec::{BusCodecExpr, BusCodecVal}, - bus_lookup::BusLookupConfig, + bus_lookup::BusLookupChip, bus_port::BusOp, }; use halo2_proofs::{ @@ -40,7 +40,7 @@ use eth_types::Field; use execution::ExecutionConfig; use itertools::Itertools; use strum::IntoEnumIterator; -use table::{ByteMsgV, ByteMsgX, FixedTableTag}; +use table::{FixedTableTag, Lookup, MsgExpr, MsgF}; use witness::Block; /// EvmCircuitConfig implements verification of execution trace of a block. @@ -49,7 +49,7 @@ pub struct EvmCircuitConfig { fixed_table: [Column; 4], dual_byte_table: [Column; 2], bus: BusConfig, - bus_lookup: BusLookupConfig, + bus_lookup: [BusLookupChip; 2], enable_bus_lookup: Column, pub(crate) execution: Box>, // External tables @@ -142,8 +142,13 @@ impl EvmCircuitConfig { let mut bus_builder = BusBuilder::new(BusCodecExpr::new(challenges.lookup_input())); - let bus_lookup = - Self::configure_bus_lookup(meta, &mut bus_builder, &dual_byte_table, enable_bus_lookup); + let bus_lookup = Self::configure_bus_lookup( + meta, + &mut bus_builder, + enable_bus_lookup, + &dual_byte_table, + &fixed_table, + ); let execution = Box::new(ExecutionConfig::configure( meta, @@ -205,18 +210,38 @@ impl EvmCircuitConfig { fn configure_bus_lookup( meta: &mut ConstraintSystem, - bus_builder: &mut BusBuilder>, - dual_byte_table: &[Column; 2], + bus_builder: &mut BusBuilder>, enabled: Column, - ) -> BusLookupConfig { - let message = query_expression(meta, |meta| { - [ - meta.query_fixed(dual_byte_table[0], Rotation::cur()), - meta.query_fixed(dual_byte_table[1], Rotation::cur()), - ] - }); + dual_byte_table: &[Column; 2], + fixed_table: &[Column; 4], + ) -> [BusLookupChip; 2] { let enabled = query_expression(meta, |meta| meta.query_fixed(enabled, Rotation::cur())); - BusLookupConfig::connect(meta, bus_builder, enabled, message) + + let byte_lookup = { + let message = query_expression(meta, |meta| { + MsgExpr::Bytes([ + meta.query_fixed(dual_byte_table[0], Rotation::cur()), + meta.query_fixed(dual_byte_table[1], Rotation::cur()), + ]) + }); + BusLookupChip::connect(meta, bus_builder, enabled.clone(), message) + }; + + let fixed_lookup = { + let message = query_expression(meta, |meta| { + MsgExpr::Lookup(Lookup::Fixed { + tag: meta.query_fixed(fixed_table[0], Rotation::cur()), + values: [ + meta.query_fixed(fixed_table[1], Rotation::cur()), + meta.query_fixed(fixed_table[2], Rotation::cur()), + meta.query_fixed(fixed_table[3], Rotation::cur()), + ], + }) + }); + BusLookupChip::connect(meta, bus_builder, enabled.clone(), message) + }; + + [byte_lookup.clone(), fixed_lookup] } } @@ -248,7 +273,8 @@ impl EvmCircuitConfig { pub fn load_dual_byte_table( &self, layouter: &mut impl Layouter, - bus_assigner: &mut BusAssigner>, + bus_assigner: &mut BusAssigner>, + bus_lookup: &BusLookupChip, ) -> Result<(), Error> { let mut closure_count = 0; @@ -262,9 +288,10 @@ impl EvmCircuitConfig { } for i in 0..256 { + let b0 = F::from(i); for j in 0..256 { let offset = (i * 256 + j) as usize; - let message = [F::from(i), F::from(j)]; + let b1 = F::from(j); region.assign_fixed( || "", @@ -277,17 +304,18 @@ impl EvmCircuitConfig { || "", self.dual_byte_table[0], offset, - || Value::known(message[0]), + || Value::known(b0), )?; region.assign_fixed( || "", self.dual_byte_table[1], offset, - || Value::known(message[1]), + || Value::known(b1), )?; + let message = MsgF::Bytes([b0, b1]); let count = bus_assigner.op_counter().count_receives(&message); - self.bus_lookup.assign( + bus_lookup.assign( &mut region, bus_assigner, offset, @@ -441,7 +469,7 @@ impl SubCircuit for EvmCircuit { .assign_block(layouter, &mut bus_assigner, block, challenges)?; self.exports.borrow_mut().replace(export); - config.load_dual_byte_table(layouter, &mut bus_assigner)?; + config.load_dual_byte_table(layouter, &mut bus_assigner, &config.bus_lookup[0])?; layouter.assign_region( || "EVM_Bus", diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 3e917345bf..d730633490 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -12,7 +12,7 @@ use crate::{ evm_circuit::{ param::{EVM_LOOKUP_COLS, MAX_STEP_HEIGHT, N_PHASE2_COLUMNS, STEP_WIDTH}, step::{ExecutionState, Step}, - table::{ByteMsgV, ByteMsgX, Table}, + table::{MsgExpr, MsgF, Table}, util::{ constraint_builder::{ BaseConstraintBuilder, ConstrainBuilderCommon, EVMConstraintBuilder, @@ -377,7 +377,7 @@ impl ExecutionConfig { pub(crate) fn configure( meta: &mut ConstraintSystem, challenges: Challenges>, - bus_builder: &mut BusBuilder>, + bus_builder: &mut BusBuilder>, fixed_table: &dyn LookupTable, tx_table: &dyn LookupTable, rw_table: &dyn LookupTable, @@ -922,7 +922,7 @@ impl ExecutionConfig { fn configure_bus( meta: &mut ConstraintSystem, - bus_builder: &mut BusBuilder>, + bus_builder: &mut BusBuilder>, q_usable: Selector, cell_manager: &CellManager, ) -> BusPortMulti { @@ -944,7 +944,7 @@ impl ExecutionConfig { let ops = byte_columns .chunks(2) .map(|columns| { - let message = [columns[0].clone(), columns[1].clone()]; + let message = MsgExpr::Bytes([columns[0].clone(), columns[1].clone()]); BusOp::receive(message) }) .collect::>(); @@ -1059,7 +1059,7 @@ impl ExecutionConfig { pub fn assign_block( &self, layouter: &mut impl Layouter, - bus_assigner: &mut BusAssigner>, + bus_assigner: &mut BusAssigner>, block: &Block, challenges: &Challenges>, ) -> Result>, Error> { @@ -1315,7 +1315,7 @@ impl ExecutionConfig { fn assign_bus_ports( &self, region: &mut CachedRegion<'_, '_, F>, - bus_assigner: &mut BusAssigner>, + bus_assigner: &mut BusAssigner>, offset_begin: usize, offset_end: usize, ) { @@ -1338,7 +1338,7 @@ impl ExecutionConfig { } else { F::zero() }; - let message = [byte_0, byte_1]; + let message = MsgF::Bytes([byte_0, byte_1]); BusOp::receive(message) }) .collect::>(); @@ -1351,7 +1351,7 @@ impl ExecutionConfig { fn assign_same_exec_step_in_range( &self, region: &mut Region<'_, F>, - bus_assigner: &mut BusAssigner>, + bus_assigner: &mut BusAssigner>, offset_begin: usize, offset_end: usize, block: &Block, @@ -1393,7 +1393,7 @@ impl ExecutionConfig { fn assign_exec_step( &self, region: &mut Region<'_, F>, - bus_assigner: &mut BusAssigner>, + bus_assigner: &mut BusAssigner>, offset: usize, block: &Block, transaction: &Transaction, diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index e7f9ee9f04..b1d9218aeb 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -5,13 +5,50 @@ use crate::{ }; use bus_mapping::{evm::OpcodeId, precompile::PrecompileCalls}; use eth_types::Field; -use gadgets::util::Expr; +use gadgets::{bus::bus_codec::BusMessage, util::Expr}; use halo2_proofs::plonk::Expression; use strum::IntoEnumIterator; use strum_macros::EnumIter; -pub type ByteMsgX = [Expression; 2]; -pub type ByteMsgV = [F; 2]; +#[derive(Clone, Debug)] +pub(crate) enum MsgExpr { + Bytes([Expression; 2]), + Lookup(Lookup), +} + +impl BusMessage> for MsgExpr { + type IntoIter = std::vec::IntoIter>; + + fn into_items(self) -> Self::IntoIter { + match self { + Self::Bytes([byte0, byte1]) => { + let tag = 0.expr(); + vec![tag, byte0, byte1].into_iter() + } + Self::Lookup(lookup) => { + let tag = (1 + (lookup.table() as u64)).expr(); + let mut inputs = lookup.input_exprs(); + inputs.insert(0, tag); + inputs.into_iter() + } + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum MsgF { + Bytes([F; 2]), +} + +impl BusMessage for MsgF { + type IntoIter = std::vec::IntoIter; + + fn into_items(self) -> Self::IntoIter { + match self { + Self::Bytes([b0, b1]) => vec![F::zero(), b0, b1].into_iter(), + } + } +} #[derive(Clone, Copy, Debug, EnumIter)] pub enum FixedTableTag { From 3bb7ccb39859003766e380a1c2f00514fcaee750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 10 Oct 2023 18:45:04 +0200 Subject: [PATCH 39/67] bus: simplify lookup table API --- gadgets/src/bus/bus_lookup.rs | 14 ++++++++- gadgets/src/bus/tests.rs | 49 ++++++++++++++++--------------- zkevm-circuits/src/evm_circuit.rs | 9 +----- 3 files changed, 39 insertions(+), 33 deletions(-) diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index 9f93fd1f7a..f0070cdeaa 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -43,8 +43,20 @@ impl BusLookupChip { Self { port, count } } - /// Assign a lookup operation. + /// Assign a lookup operation from message. The count is discovered from the BusAssigner. pub fn assign>( + &self, + region: &mut Region<'_, F>, + bus_assigner: &mut BusAssigner, + offset: usize, + message: M, + ) -> Result<(), Error> { + let count = bus_assigner.op_counter().count_receives(&message); + self.assign_op(region, bus_assigner, offset, BusOp::send_to_lookups(message, count)) + } + + /// Assign a lookup operation from message and count. + pub fn assign_op>( &self, region: &mut Region<'_, F>, bus_assigner: &mut BusAssigner, diff --git a/gadgets/src/bus/tests.rs b/gadgets/src/bus/tests.rs index 9331c78ec9..2537c8c6bd 100644 --- a/gadgets/src/bus/tests.rs +++ b/gadgets/src/bus/tests.rs @@ -64,12 +64,7 @@ impl Circuit for TestCircuit { BusLookupChip::connect(cs, &mut bus_builder, enabled_expr.clone(), message.clone()); // Circuit 2 receives one value per row. - let port2 = PortChip::connect( - cs, - &mut bus_builder, - enabled_expr, - BusOp::receive(message), - ); + let port2 = PortChip::connect(cs, &mut bus_builder, enabled_expr, BusOp::receive(message)); // Global bus connection. let bus_config = BusConfig::new(cs, &bus_builder.build()); @@ -105,24 +100,6 @@ impl Circuit for TestCircuit { let mut bus_assigner = BusAssigner::new(BusCodecVal::new(rand), self.n_rows); - // Circuit 1 sends a message on some row. - { - // Do normal circuit assignment logic, and obtain a message. - let message = vec![F::from(2)]; - - // Set the `count` of copies of the same message. - let count = self.n_rows as isize; - let offset = 3; // can be anywhere. - - // Assign an operation to the port of this circuit, and to the shared bus. - config.bus_lookup.assign( - &mut region, - &mut bus_assigner, - offset, - BusOp::send_to_lookups(message, count), - )?; - } - // Circuit 2 receives one message per row. { // First pass: run circuit steps. @@ -137,6 +114,30 @@ impl Circuit for TestCircuit { } } + // Circuit 1 sends a message on some row. + { + // Do normal circuit assignment logic, and obtain a message. + let message = vec![F::from(2)]; + + // The bus assigner counted `n_rows` messages from Circuit 2. + assert_eq!( + bus_assigner.op_counter().count_receives(&message), + self.n_rows as isize, + ); + + // Provide an entry of the table to the bus. + let offset = 3; // can be anywhere. + config.bus_lookup.assign( + &mut region, + &mut bus_assigner, + offset, + message.clone(), + )?; + + // The bus assigner counted that all lookups were satisfied. + assert_eq!(bus_assigner.op_counter().count_receives(&message), 0,); + } + // Final pass: assign the bus witnesses. bus_assigner.finish_ports(&mut region); diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 24475e0d80..7279223b88 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -6,7 +6,6 @@ use gadgets::bus::{ bus_chip::BusConfig, bus_codec::{BusCodecExpr, BusCodecVal}, bus_lookup::BusLookupChip, - bus_port::BusOp, }; use halo2_proofs::{ circuit::{Cell, Layouter, SimpleFloorPlanner, Value}, @@ -314,13 +313,7 @@ impl EvmCircuitConfig { )?; let message = MsgF::Bytes([b0, b1]); - let count = bus_assigner.op_counter().count_receives(&message); - bus_lookup.assign( - &mut region, - bus_assigner, - offset, - BusOp::send_to_lookups(message, count), - )?; + bus_lookup.assign(&mut region, bus_assigner, offset, message)?; } } From 0761f43b449fc536c41a40d05d9965e3d7b9f3d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 10 Oct 2023 19:29:27 +0200 Subject: [PATCH 40/67] bus: expose the fixed table --- zkevm-circuits/src/evm_circuit.rs | 28 +++++++++++++++++++++---- zkevm-circuits/src/evm_circuit/table.rs | 8 ++++++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 7279223b88..f32df05ebb 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -22,6 +22,7 @@ pub(crate) mod util; #[cfg(any(feature = "test", test))] pub(crate) mod test; +use self::table::Table; #[cfg(any(feature = "test", test, feature = "test-circuits"))] pub use self::EvmCircuit as TestEvmCircuit; @@ -137,7 +138,7 @@ impl EvmCircuitConfig { ) -> Self { let fixed_table = [(); 4].map(|_| meta.fixed_column()); let dual_byte_table = [(); 2].map(|_| meta.fixed_column()); - let enable_bus_lookup = meta.fixed_column(); + let enable_bus_lookup = meta.fixed_column(); // TODO: replace with q_usable, or BusConfig.enabled? let mut bus_builder = BusBuilder::new(BusCodecExpr::new(challenges.lookup_input())); @@ -246,9 +247,11 @@ impl EvmCircuitConfig { impl EvmCircuitConfig { /// Load fixed table - pub fn load_fixed_table( + pub(crate) fn load_fixed_table( &self, layouter: &mut impl Layouter, + bus_assigner: &mut BusAssigner>, + bus_lookup: &BusLookupChip, fixed_table_tags: Vec, ) -> Result<(), Error> { layouter.assign_region( @@ -261,15 +264,26 @@ impl EvmCircuitConfig { for (column, value) in self.fixed_table.iter().zip_eq(row) { region.assign_fixed(|| "", *column, offset, || Value::known(value))?; } + + region.assign_fixed( + || "", + self.enable_bus_lookup, + offset, + || Value::known(F::one()), + )?; + + let message = MsgF::Lookup(Table::Fixed, row.to_vec()); + bus_lookup.assign(&mut region, bus_assigner, offset, message)?; } + bus_assigner.finish_ports(&mut region); Ok(()) }, ) } /// Load dual byte table - pub fn load_dual_byte_table( + pub(crate) fn load_dual_byte_table( &self, layouter: &mut impl Layouter, bus_assigner: &mut BusAssigner>, @@ -450,7 +464,6 @@ impl SubCircuit for EvmCircuit { let block = self.block.as_ref().unwrap(); let num_rows = Self::get_num_rows_required(block); - config.load_fixed_table(layouter, self.fixed_table_tags.clone())?; config.pow_of_rand_table.assign(layouter, challenges)?; let mut bus_assigner = @@ -464,6 +477,13 @@ impl SubCircuit for EvmCircuit { config.load_dual_byte_table(layouter, &mut bus_assigner, &config.bus_lookup[0])?; + config.load_fixed_table( + layouter, + &mut bus_assigner, + &config.bus_lookup[1], + self.fixed_table_tags.clone(), + )?; + layouter.assign_region( || "EVM_Bus", |mut region| { diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index b1d9218aeb..f15ac57bc4 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -36,8 +36,9 @@ impl BusMessage> for MsgExpr { } #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum MsgF { +pub(crate) enum MsgF { Bytes([F; 2]), + Lookup(Table, Vec), } impl BusMessage for MsgF { @@ -46,6 +47,11 @@ impl BusMessage for MsgF { fn into_items(self) -> Self::IntoIter { match self { Self::Bytes([b0, b1]) => vec![F::zero(), b0, b1].into_iter(), + MsgF::Lookup(table, mut inputs) => { + let tag = F::from(1 + (table as u64)); + inputs.insert(0, tag); + inputs.into_iter() + } } } } From 2b1508e23e822440d37bf17854cccda434a9f216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 11 Oct 2023 13:31:36 +0200 Subject: [PATCH 41/67] bus: introduce third phase cells --- zkevm-circuits/src/evm_circuit.rs | 4 +++- zkevm-circuits/src/evm_circuit/execution.rs | 13 +++++++------ zkevm-circuits/src/evm_circuit/param.rs | 13 ++++++++++--- zkevm-circuits/src/evm_circuit/util.rs | 10 +++++++++- .../src/evm_circuit/util/constraint_builder.rs | 11 +++++++++++ .../src/evm_circuit/util/instrumentation.rs | 4 ++++ .../src/evm_circuit/util/math_gadget/test_util.rs | 6 +++--- 7 files changed, 47 insertions(+), 14 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index f32df05ebb..37d2a9dfcd 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -708,7 +708,7 @@ mod evm_circuit_stats { evm_circuit::{ param::{ LOOKUP_CONFIG, N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, N_PHASE2_COLUMNS, - N_PHASE2_COPY_COLUMNS, + N_PHASE2_COPY_COLUMNS, N_PHASE3_COLUMNS, }, step::ExecutionState, table::FixedTableTag, @@ -891,6 +891,8 @@ mod evm_circuit_stats { N_PHASE1_COLUMNS, storage_2, N_PHASE2_COLUMNS, + storage_3, + N_PHASE3_COLUMNS, storage_perm, N_COPY_COLUMNS, storage_perm_2, diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index d730633490..5649bb726f 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -10,7 +10,7 @@ use super::{ }; use crate::{ evm_circuit::{ - param::{EVM_LOOKUP_COLS, MAX_STEP_HEIGHT, N_PHASE2_COLUMNS, STEP_WIDTH}, + param::{EVM_LOOKUP_COLS, MAX_STEP_HEIGHT, N_PHASE2_COLUMNS, N_PHASE3_COLUMNS, STEP_WIDTH}, step::{ExecutionState, Step}, table::{MsgExpr, MsgF, Table}, util::{ @@ -405,9 +405,9 @@ impl ExecutionConfig { .iter() .enumerate() .map(|(n, _)| { - if n < EVM_LOOKUP_COLS { + if n < EVM_LOOKUP_COLS + N_PHASE3_COLUMNS { meta.advice_column_in(ThirdPhase) - } else if n < EVM_LOOKUP_COLS + N_PHASE2_COLUMNS { + } else if n < EVM_LOOKUP_COLS + N_PHASE3_COLUMNS + N_PHASE2_COLUMNS { meta.advice_column_in(SecondPhase) } else { meta.advice_column_in(FirstPhase) @@ -1290,6 +1290,7 @@ impl ExecutionConfig { ("EVM_lookup_ecc", ECC_TABLE_LOOKUPS), ("EVM_lookup_pow_of_rand", POW_OF_RAND_TABLE_LOOKUPS), ("EVM_adv_phase2", N_PHASE2_COLUMNS), + ("EVM_adv_phase3", N_PHASE3_COLUMNS), ("EVM_copy", N_COPY_COLUMNS), ("EVM_lookup_byte", N_BYTE_LOOKUPS), ("EVM_adv_phase1", N_PHASE1_COLUMNS), @@ -1312,7 +1313,7 @@ impl ExecutionConfig { region.name_column(|| "Copy_Constr_const", self.constants); } - fn assign_bus_ports( + fn assign_byte_lookups( &self, region: &mut CachedRegion<'_, '_, F>, bus_assigner: &mut BusAssigner>, @@ -1384,7 +1385,7 @@ impl ExecutionConfig { offset_end, )?; - self.assign_bus_ports(region, bus_assigner, offset_begin, offset_end); + self.assign_byte_lookups(region, bus_assigner, offset_begin, offset_end); Ok(()) } @@ -1432,7 +1433,7 @@ impl ExecutionConfig { self.assign_exec_step_int(region, offset, block, transaction, call, step, true)?; - self.assign_bus_ports(region, bus_assigner, offset, offset + height); + self.assign_byte_lookups(region, bus_assigner, offset, offset + height); Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index dee6d8b6af..d82e097b80 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -7,7 +7,7 @@ use halo2_proofs::{ use std::collections::HashMap; // Step dimension -pub(crate) const STEP_WIDTH: usize = 140; +pub(crate) const STEP_WIDTH: usize = 150; /// Step height pub const MAX_STEP_HEIGHT: usize = 21; /// The height of the state of a step, used by gates that connect two @@ -18,9 +18,16 @@ pub(crate) const STEP_STATE_HEIGHT: usize = 1; /// Number of Advice Phase2 columns in the EVM circuit pub(crate) const N_PHASE2_COLUMNS: usize = 7; +/// Number of Advice Phase3 columns, used by Bus ports. +pub const N_PHASE3_COLUMNS: usize = 10; + /// Number of Advice Phase1 columns in the EVM circuit -pub(crate) const N_PHASE1_COLUMNS: usize = - STEP_WIDTH - EVM_LOOKUP_COLS - N_PHASE2_COLUMNS - N_COPY_COLUMNS - N_BYTE_LOOKUPS; +pub(crate) const N_PHASE1_COLUMNS: usize = STEP_WIDTH + - EVM_LOOKUP_COLS + - N_PHASE3_COLUMNS + - N_PHASE2_COLUMNS + - N_COPY_COLUMNS + - N_BYTE_LOOKUPS; // Number of copy columns pub(crate) const N_COPY_COLUMNS: usize = 2; diff --git a/zkevm-circuits/src/evm_circuit/util.rs b/zkevm-circuits/src/evm_circuit/util.rs index fb43c30fd4..89605ae772 100644 --- a/zkevm-circuits/src/evm_circuit/util.rs +++ b/zkevm-circuits/src/evm_circuit/util.rs @@ -2,7 +2,7 @@ use crate::{ evm_circuit::{ param::{ LOOKUP_CONFIG, N_BYTES_MEMORY_ADDRESS, N_BYTES_U64, N_BYTE_LOOKUPS, N_COPY_COLUMNS, - N_PHASE2_COLUMNS, N_PHASE2_COPY_COLUMNS, + N_PHASE2_COLUMNS, N_PHASE2_COPY_COLUMNS, N_PHASE3_COLUMNS, }, table::Table, }, @@ -290,6 +290,7 @@ impl StoredExpression { pub(crate) enum CellType { StoragePhase1, StoragePhase2, + StoragePhase3, StoragePermutation, StoragePermutationPhase2, LookupByte, @@ -314,6 +315,7 @@ impl CellType { match phase { 0 => CellType::StoragePhase1, 1 => CellType::StoragePhase2, + 2 => CellType::StoragePhase3, _ => unreachable!(), } } @@ -381,6 +383,12 @@ impl CellManager { } } + // Mark columns used for Phase3. + for _ in 0..N_PHASE3_COLUMNS { + columns[column_idx].cell_type = CellType::StoragePhase3; + column_idx += 1; + } + // Mark columns used for copy constraints on phase2 for _ in 0..N_PHASE2_COPY_COLUMNS { meta.enable_equality(advices[column_idx]); diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 3bbcf030e9..1f78cdf1e9 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -469,6 +469,17 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { cell } + #[allow(clippy::let_and_return)] + fn query_cell_phase3(&mut self) -> Cell { + let cell = self.query_cell_with_type(CellType::StoragePhase3); + #[cfg(not(feature = "onephase"))] + assert_eq!( + cell.column.column_type(), + &halo2_proofs::plonk::Advice::new(halo2_proofs::plonk::ThirdPhase) + ); + cell + } + pub(crate) fn query_copy_cell(&mut self) -> Cell { self.query_cell_with_type(CellType::StoragePermutation) } diff --git a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs index 96cd6eaaf2..46586f8ce6 100644 --- a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs +++ b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs @@ -70,6 +70,9 @@ impl Instrument { CellType::StoragePhase2 => { report.storage_2 = data_entry; } + CellType::StoragePhase3 => { + report.storage_3 = data_entry; + } CellType::StoragePermutation => { report.storage_perm = data_entry; } @@ -130,6 +133,7 @@ pub(crate) struct ExecStateReport { pub(crate) state: ExecutionState, pub(crate) storage_1: StateReportRow, pub(crate) storage_2: StateReportRow, + pub(crate) storage_3: StateReportRow, pub(crate) storage_perm: StateReportRow, pub(crate) storage_perm_2: StateReportRow, pub(crate) byte_lookup: StateReportRow, diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs index 3c160480d4..5ab70d2723 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs @@ -4,7 +4,7 @@ use strum::IntoEnumIterator; use crate::{ evm_circuit::{ - param::{MAX_STEP_HEIGHT, N_PHASE2_COLUMNS, STEP_WIDTH}, + param::{MAX_STEP_HEIGHT, N_PHASE2_COLUMNS, N_PHASE3_COLUMNS, STEP_WIDTH}, step::{ExecutionState, Step}, table::{FixedTableTag, Table}, util::{ @@ -123,9 +123,9 @@ impl> Circuit for UnitTestMathGadgetBaseC .iter() .enumerate() .map(|(n, _)| { - if n < lookup_column_count { + if n < lookup_column_count + N_PHASE3_COLUMNS { meta.advice_column_in(ThirdPhase) - } else if n < lookup_column_count + N_PHASE2_COLUMNS { + } else if n < lookup_column_count + N_PHASE3_COLUMNS + N_PHASE2_COLUMNS { meta.advice_column_in(SecondPhase) } else { meta.advice_column_in(FirstPhase) From 9a4732ed3a4ffd1b7b14b5abdc9dc2a6204aa145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 11 Oct 2023 14:26:32 +0200 Subject: [PATCH 42/67] bus: track bus lookup expressions --- zkevm-circuits/src/evm_circuit/execution.rs | 45 ++++++++++++++++--- .../evm_circuit/util/constraint_builder.rs | 31 ++++++++++++- .../evm_circuit/util/math_gadget/test_util.rs | 2 +- 3 files changed, 68 insertions(+), 10 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 5649bb726f..6e1c37f8f9 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -5,7 +5,7 @@ use super::{ N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, POW_OF_RAND_TABLE_LOOKUPS, RW_TABLE_LOOKUPS, SIG_TABLE_LOOKUPS, TX_TABLE_LOOKUPS, }, - util::{instrumentation::Instrument, CachedRegion, CellManager, StoredExpression}, + util::{instrumentation::Instrument, CachedRegion, CellManager, StoredExpression, constraint_builder::StepBusOp}, EvmCircuitExports, }; use crate::{ @@ -266,9 +266,10 @@ pub(crate) struct ExecutionConfig { q_step_last: Selector, advices: [Column; STEP_WIDTH], step: Step, - bus_port: BusPortMulti, + bytes_port: BusPortMulti, pub(crate) height_map: HashMap, stored_expressions_map: HashMap>>, + bus_ops_map: HashMap>>, instrument: Instrument, // internal state gadgets begin_tx_gadget: Box>, @@ -510,6 +511,7 @@ impl ExecutionConfig { }); let mut stored_expressions_map = HashMap::new(); + let mut bus_ops_map = HashMap::new(); macro_rules! configure_gadget { () => { @@ -531,6 +533,7 @@ impl ExecutionConfig { &step_curr, &mut height_map, &mut stored_expressions_map, + &mut bus_ops_map, &mut instrument, )) })() @@ -539,7 +542,7 @@ impl ExecutionConfig { let cell_manager = step_curr.cell_manager.clone(); - let bus_port = Self::configure_bus(meta, bus_builder, q_usable, &cell_manager); + let bytes_port = Self::configure_bus(meta, bus_builder, q_usable, &cell_manager); let config = Self { q_usable, @@ -549,7 +552,7 @@ impl ExecutionConfig { num_rows_inv, q_step_first, q_step_last, - bus_port, + bytes_port, advices, // internal states begin_tx_gadget: configure_gadget!(), @@ -653,6 +656,7 @@ impl ExecutionConfig { step: step_curr, height_map, stored_expressions_map, + bus_ops_map, instrument, }; @@ -693,6 +697,7 @@ impl ExecutionConfig { step_curr: &Step, height_map: &mut HashMap, stored_expressions_map: &mut HashMap>>, + bus_ops_map: &mut HashMap>>, instrument: &mut Instrument, ) -> G { // Configure the gadget with the max height first so we can find out the actual @@ -706,7 +711,7 @@ impl ExecutionConfig { G::EXECUTION_STATE, ); cb.annotation(G::NAME, |cb| G::configure(cb)); - let (_, _, _, height) = cb.build(); + let (_, _, _, _, height) = cb.build(); height }; @@ -732,6 +737,7 @@ impl ExecutionConfig { step_next, height_map, stored_expressions_map, + bus_ops_map, instrument, G::NAME, G::EXECUTION_STATE, @@ -754,6 +760,7 @@ impl ExecutionConfig { step_next: &Step, height_map: &mut HashMap, stored_expressions_map: &mut HashMap>>, + bus_ops_map: &mut HashMap>>, instrument: &mut Instrument, name: &'static str, execution_state: ExecutionState, @@ -772,7 +779,7 @@ impl ExecutionConfig { instrument.on_gadget_built(execution_state, &cb); - let (state_selector, constraints, stored_expressions, _) = cb.build(); + let (state_selector, constraints, stored_expressions, bus_ops, _) = cb.build(); debug_assert!( !height_map.contains_key(&execution_state), "execution state already configured" @@ -784,6 +791,9 @@ impl ExecutionConfig { "execution state already configured" ); stored_expressions_map.insert(execution_state, stored_expressions); + bus_ops_map.insert(execution_state, bus_ops); + + // TODO: create ports for the bus ops. // Enforce the logic for this opcode let sel_step: &dyn Fn(&mut VirtualCells) -> Expression = @@ -1344,7 +1354,7 @@ impl ExecutionConfig { }) .collect::>(); - self.bus_port.assign(bus_assigner, offset, ops); + self.bytes_port.assign(bus_assigner, offset, ops); } } @@ -1385,6 +1395,8 @@ impl ExecutionConfig { offset_end, )?; + // TODO: assign_bus_lookups needed? + self.assign_byte_lookups(region, bus_assigner, offset_begin, offset_end); Ok(()) @@ -1433,6 +1445,8 @@ impl ExecutionConfig { self.assign_exec_step_int(region, offset, block, transaction, call, step, true)?; + self.assign_bus_lookups(region, bus_assigner, offset, step)?; + self.assign_byte_lookups(region, bus_assigner, offset, offset + height); Ok(()) @@ -1679,6 +1693,23 @@ impl ExecutionConfig { Ok(assigned_stored_expressions) } + fn assign_bus_lookups( + &self, + region: &mut CachedRegion<'_, '_, F>, + bus_assigner: &mut BusAssigner>, + offset: usize, + step: &ExecStep, + ) -> Result<(), Error> { + for bus_op in self + .bus_ops_map + .get(&step.execution_state) + .unwrap_or_else(|| panic!("Execution state unknown: {:?}", step.execution_state)) + { + // TODO: assign bus op. + } + Ok(()) + } + fn check_rw_lookup( assigned_stored_expressions: &[(String, F)], offset: usize, diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 1f78cdf1e9..68ecda7a1c 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -2,7 +2,7 @@ use crate::{ evm_circuit::{ param::STACK_CAPACITY, step::{ExecutionState, Step}, - table::{FixedTableTag, Lookup, RwValues}, + table::{FixedTableTag, Lookup, MsgExpr, RwValues, Table}, util::{Cell, RandomLinearCombination, Word}, }, table::{ @@ -16,7 +16,10 @@ use bus_mapping::{ util::{KECCAK_CODE_HASH_EMPTY, POSEIDON_CODE_HASH_EMPTY}, }; use eth_types::{Field, ToLittleEndian, ToScalar, ToWord}; -use gadgets::util::{and, not, sum}; +use gadgets::{ + bus::bus_port::BusOpExpr, + util::{and, not, sum}, +}; use halo2_proofs::{ circuit::Value, plonk::{ @@ -297,6 +300,12 @@ pub(crate) struct Constraints { pub(crate) not_step_last: Vec<(&'static str, Expression)>, } +#[derive(Clone, Debug)] +pub struct StepBusOp { + op: BusOpExpr>, + helper: Cell, +} + pub(crate) struct EVMConstraintBuilder<'a, F> { pub max_degree: usize, pub(crate) curr: Step, @@ -312,6 +321,7 @@ pub(crate) struct EVMConstraintBuilder<'a, F> { conditions: Vec>, constraints_location: ConstraintLocation, stored_expressions: Vec>, + bus_ops: Vec>, pub(crate) max_inner_degree: (&'static str, usize), #[cfg(feature = "debug-annotations")] annotations: Vec, @@ -362,6 +372,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { conditions: Vec::new(), constraints_location: ConstraintLocation::Step, stored_expressions: Vec::new(), + bus_ops: Vec::new(), max_inner_degree: ("", 0), annotations: Vec::new(), } @@ -376,6 +387,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { Expression, Constraints, Vec>, + Vec>, usize, ) { let exec_state_sel = self.curr.execution_state_selector([self.execution_state]); @@ -383,6 +395,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { exec_state_sel, self.constraints, self.stored_expressions, + self.bus_ops, self.curr.cell_manager.get_height(), ) } @@ -1649,11 +1662,25 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { } } + fn add_bus_lookup(&mut self, lookup: Lookup) { + // TODO: support all types. + if lookup.table() != Table::Fixed { + return; + } + + let op = BusOpExpr::receive(MsgExpr::Lookup(lookup)); + let helper = self.query_cell_phase3(); + self.bus_ops.push(StepBusOp { op, helper }); + } + pub(crate) fn add_lookup(&mut self, name: &str, lookup: Lookup) { let lookup = match self.condition_expr_opt() { Some(condition) => lookup.conditional(condition), None => lookup, }; + + self.add_bus_lookup(lookup.clone()); + let compressed_expr = self.split_expression( "Lookup compression", rlc::expr(&lookup.input_exprs(), self.challenges.lookup_input()), diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs index 5ab70d2723..9d90149757 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs @@ -144,7 +144,7 @@ impl> Circuit for UnitTestMathGadgetBaseC ExecutionState::STOP, ); let math_gadget_container = G::configure_gadget_container(&mut cb); - let (state_selector, constraints, stored_expressions, _) = cb.build(); + let (state_selector, constraints, stored_expressions, bus_ops, _) = cb.build(); if !constraints.step.is_empty() { let step_constraints = constraints.step; From ae635b734d4511b531fabc0c0edc245e8c6fb5ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Thu, 12 Oct 2023 21:12:05 +0200 Subject: [PATCH 43/67] bus: fixed lookups --- gadgets/src/bus/bus_lookup.rs | 7 +- zkevm-circuits/src/evm_circuit.rs | 14 ++- zkevm-circuits/src/evm_circuit/execution.rs | 69 +++++++----- zkevm-circuits/src/evm_circuit/util.rs | 101 +++++++++++++++++- .../evm_circuit/util/constraint_builder.rs | 8 +- .../evm_circuit/util/math_gadget/test_util.rs | 2 +- 6 files changed, 162 insertions(+), 39 deletions(-) diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index f0070cdeaa..d7659d5323 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -52,7 +52,12 @@ impl BusLookupChip { message: M, ) -> Result<(), Error> { let count = bus_assigner.op_counter().count_receives(&message); - self.assign_op(region, bus_assigner, offset, BusOp::send_to_lookups(message, count)) + self.assign_op( + region, + bus_assigner, + offset, + BusOp::send_to_lookups(message, count), + ) } /// Assign a lookup operation from message and count. diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 37d2a9dfcd..9eabc9db9a 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -241,7 +241,7 @@ impl EvmCircuitConfig { BusLookupChip::connect(meta, bus_builder, enabled.clone(), message) }; - [byte_lookup.clone(), fixed_lookup] + [byte_lookup, fixed_lookup] } } @@ -254,9 +254,17 @@ impl EvmCircuitConfig { bus_lookup: &BusLookupChip, fixed_table_tags: Vec, ) -> Result<(), Error> { + let mut closure_count = 0; + layouter.assign_region( || "fixed table", |mut region| { + // TODO: deal with this some other way. + closure_count += 1; + if closure_count == 1 { + return Ok(()); + } + for (offset, row) in std::iter::once([F::zero(); 4]) .chain(fixed_table_tags.iter().flat_map(|tag| tag.build())) .enumerate() @@ -279,7 +287,9 @@ impl EvmCircuitConfig { bus_assigner.finish_ports(&mut region); Ok(()) }, - ) + )?; + assert_eq!(closure_count, 2, "assign_region behavior changed"); + Ok(()) } /// Load dual byte table diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 6e1c37f8f9..b4c98c4599 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -5,7 +5,7 @@ use super::{ N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, POW_OF_RAND_TABLE_LOOKUPS, RW_TABLE_LOOKUPS, SIG_TABLE_LOOKUPS, TX_TABLE_LOOKUPS, }, - util::{instrumentation::Instrument, CachedRegion, CellManager, StoredExpression, constraint_builder::StepBusOp}, + util::{instrumentation::Instrument, CachedRegion, CellManager, StepBusOp, StoredExpression}, EvmCircuitExports, }; use crate::{ @@ -511,7 +511,7 @@ impl ExecutionConfig { }); let mut stored_expressions_map = HashMap::new(); - let mut bus_ops_map = HashMap::new(); + let mut bus_ops_map: HashMap>> = HashMap::new(); macro_rules! configure_gadget { () => { @@ -523,6 +523,7 @@ impl ExecutionConfig { (|| { Box::new(Self::configure_gadget( meta, + bus_builder, advices, q_usable, q_step, @@ -542,7 +543,7 @@ impl ExecutionConfig { let cell_manager = step_curr.cell_manager.clone(); - let bytes_port = Self::configure_bus(meta, bus_builder, q_usable, &cell_manager); + let bytes_port = Self::configure_bytes_port(meta, bus_builder, q_usable, &cell_manager); let config = Self { q_usable, @@ -687,6 +688,7 @@ impl ExecutionConfig { #[allow(clippy::too_many_arguments)] fn configure_gadget>( meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder>, advices: [Column; STEP_WIDTH], q_usable: Selector, q_step: Column, @@ -728,6 +730,7 @@ impl ExecutionConfig { Self::configure_gadget_impl( meta, + bus_builder, q_usable, q_step, num_rows_until_next_step, @@ -751,6 +754,7 @@ impl ExecutionConfig { #[allow(clippy::too_many_arguments)] fn configure_gadget_impl( meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder>, q_usable: Selector, q_step: Column, num_rows_until_next_step: Column, @@ -791,9 +795,18 @@ impl ExecutionConfig { "execution state already configured" ); stored_expressions_map.insert(execution_state, stored_expressions); - bus_ops_map.insert(execution_state, bus_ops); + bus_ops_map.insert(execution_state, bus_ops.clone()); - // TODO: create ports for the bus ops. + { + let step_enabled = query_expression(meta, |meta| { + let q_usable = meta.query_selector(q_usable); + let q_step = meta.query_advice(q_step, Rotation::cur()); + q_usable * q_step * state_selector.clone() + }); + for op in bus_ops { + op.connect(meta, bus_builder, step_enabled.clone()); + } + } // Enforce the logic for this opcode let sel_step: &dyn Fn(&mut VirtualCells) -> Expression = @@ -930,7 +943,7 @@ impl ExecutionConfig { }); } - fn configure_bus( + fn configure_bytes_port( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder>, q_usable: Selector, @@ -1358,6 +1371,22 @@ impl ExecutionConfig { } } + fn assign_bus_lookups( + &self, + region: &mut CachedRegion<'_, '_, F>, + bus_assigner: &mut BusAssigner>, + offset: usize, + step: &ExecStep, + ) { + let bus_ops = self + .bus_ops_map + .get(&step.execution_state) + .unwrap_or_else(|| panic!("Execution state unknown: {:?}", step.execution_state)); + for bus_op in bus_ops { + bus_op.assign(region, bus_assigner, offset); + } + } + #[allow(clippy::too_many_arguments)] fn assign_same_exec_step_in_range( &self, @@ -1395,10 +1424,13 @@ impl ExecutionConfig { offset_end, )?; - // TODO: assign_bus_lookups needed? - + // TODO: accelerate repeated bus assignments. self.assign_byte_lookups(region, bus_assigner, offset_begin, offset_end); + for offset in offset_begin..offset_end { + self.assign_bus_lookups(region, bus_assigner, offset, step); + } + Ok(()) } @@ -1445,10 +1477,10 @@ impl ExecutionConfig { self.assign_exec_step_int(region, offset, block, transaction, call, step, true)?; - self.assign_bus_lookups(region, bus_assigner, offset, step)?; - self.assign_byte_lookups(region, bus_assigner, offset, offset + height); + self.assign_bus_lookups(region, bus_assigner, offset, step); + Ok(()) } @@ -1693,23 +1725,6 @@ impl ExecutionConfig { Ok(assigned_stored_expressions) } - fn assign_bus_lookups( - &self, - region: &mut CachedRegion<'_, '_, F>, - bus_assigner: &mut BusAssigner>, - offset: usize, - step: &ExecStep, - ) -> Result<(), Error> { - for bus_op in self - .bus_ops_map - .get(&step.execution_state) - .unwrap_or_else(|| panic!("Execution state unknown: {:?}", step.execution_state)) - { - // TODO: assign bus op. - } - Ok(()) - } - fn check_rw_lookup( assigned_stored_expressions: &[(String, F)], offset: usize, diff --git a/zkevm-circuits/src/evm_circuit/util.rs b/zkevm-circuits/src/evm_circuit/util.rs index 89605ae772..6b81091458 100644 --- a/zkevm-circuits/src/evm_circuit/util.rs +++ b/zkevm-circuits/src/evm_circuit/util.rs @@ -11,7 +11,11 @@ use crate::{ witness::{Block, ExecStep, Rw, RwMap}, }; use bus_mapping::state_db::CodeDB; -use eth_types::{Address, ToLittleEndian, ToWord, U256}; +use eth_types::{Address, Field, ToLittleEndian, ToWord, U256}; +use gadgets::bus::{ + bus_builder::{BusAssigner, BusBuilder}, + bus_port::{BusOpExpr, BusOpF, Port}, +}; use halo2_proofs::{ arithmetic::FieldExt, circuit::{AssignedCell, Region, Value}, @@ -33,6 +37,8 @@ pub(crate) mod precompile_gadget; pub use gadgets::util::{and, not, or, select, sum}; +use super::table::{MsgExpr, MsgF}; + #[derive(Clone, Debug)] pub(crate) struct Cell { // expression for constraint @@ -89,6 +95,69 @@ impl Expr for &Cell { self.expression.clone() } } + +#[derive(Clone, Debug)] +pub struct StepBusOp { + op: BusOpExpr>, + helper: Cell, +} + +impl StepBusOp { + pub(crate) fn connect( + self, + meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder>, + enabled: Expression, + ) { + Port::connect(meta, bus_builder, enabled, self.op, self.helper.expr()); + } + + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + bus_assigner: &mut BusAssigner>, + offset: usize, + ) { + let count = region.eval(offset, self.op.count()); + if count.is_zero_vartime() { + return; + } + assert_eq!(count, -F::one(), "count must be 0 or -1"); + + let message = Self::eval_msg(region, offset, self.op.message()); + + Port::assign( + bus_assigner, + offset, + BusOpF::receive(message), + self.helper.column, + self.helper.rotation as isize, + ); + } + + /// Evaluate a message from expressions of the content of a region. + fn eval_msg( + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + message: MsgExpr, + ) -> MsgF { + match message { + MsgExpr::Bytes(exprs) => { + let values = exprs.map(|expr| region.eval(offset, expr)); + MsgF::Bytes(values) + } + MsgExpr::Lookup(lookup) => { + let values = lookup + .input_exprs() + .into_iter() + .map(|expr| region.eval(offset, expr)) + .collect(); + MsgF::Lookup(lookup.table(), values) + } + } + } +} + pub struct CachedRegion<'r, 'b, F: FieldExt> { region: &'r mut Region<'b, F>, advice: Vec>, @@ -179,6 +248,36 @@ impl<'r, 'b, F: FieldExt> CachedRegion<'r, 'b, F> { res } + pub fn eval(&self, offset: usize, expr: Expression) -> F { + let value = expr.evaluate( + &|scalar| Value::known(scalar), + &|_| unimplemented!("selector column"), + &|fixed_query| { + Value::known(self.get_fixed( + offset, + fixed_query.column_index(), + fixed_query.rotation(), + )) + }, + &|advice_query| { + Value::known(self.get_advice( + offset, + advice_query.column_index(), + advice_query.rotation(), + )) + }, + &|_| unimplemented!("instance column"), + &|challenge| *self.challenges().indexed()[challenge.index()], + &|a| -a, + &|a, b| a + b, + &|a, b| a * b, + &|a, scalar| a * Value::known(scalar), + ); + let mut f = F::zero(); + value.map(|v| f = v); + f + } + pub fn get_fixed(&self, _row_index: usize, _column_index: usize, _rotation: Rotation) -> F { unimplemented!("fixed column"); } diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 68ecda7a1c..c2b5548d78 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -29,7 +29,7 @@ use halo2_proofs::{ }; use itertools::Itertools; -use super::{rlc, CachedRegion, CellType, StoredExpression}; +use super::{rlc, CachedRegion, CellType, StepBusOp, StoredExpression}; // Max degree allowed in all expressions passing through the ConstraintBuilder. // It aims to cap `extended_k` to 2, which allows constraint degree to 2^2+1, @@ -300,12 +300,6 @@ pub(crate) struct Constraints { pub(crate) not_step_last: Vec<(&'static str, Expression)>, } -#[derive(Clone, Debug)] -pub struct StepBusOp { - op: BusOpExpr>, - helper: Cell, -} - pub(crate) struct EVMConstraintBuilder<'a, F> { pub max_degree: usize, pub(crate) curr: Step, diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs index 9d90149757..8615c936d2 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs @@ -144,7 +144,7 @@ impl> Circuit for UnitTestMathGadgetBaseC ExecutionState::STOP, ); let math_gadget_container = G::configure_gadget_container(&mut cb); - let (state_selector, constraints, stored_expressions, bus_ops, _) = cb.build(); + let (state_selector, constraints, stored_expressions, _bus_ops, _) = cb.build(); if !constraints.step.is_empty() { let step_constraints = constraints.step; From 1ef2a58cacb48a4f500e6e295c065132dd456df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 13 Oct 2023 05:34:16 +0200 Subject: [PATCH 44/67] bus: RW lookup in EVM --- gadgets/src/bus/bus_builder.rs | 5 + gadgets/src/bus/port_assigner.rs | 2 +- zkevm-circuits/src/evm_circuit.rs | 107 +++++++++++++++++- .../evm_circuit/util/constraint_builder.rs | 2 +- zkevm-circuits/src/table.rs | 30 ++++- 5 files changed, 137 insertions(+), 9 deletions(-) diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs index 5538866cc2..0a5afcc297 100644 --- a/gadgets/src/bus/bus_builder.rs +++ b/gadgets/src/bus/bus_builder.rs @@ -59,6 +59,11 @@ impl> BusAssigner { } } + /// Return the number of rows where the bus must be enabled. + pub fn n_rows(&self) -> usize { + self.term_adder.terms.len() + } + /// Return the codec for messages on this bus. pub fn codec(&self) -> &BusCodecVal { &self.codec diff --git a/gadgets/src/bus/port_assigner.rs b/gadgets/src/bus/port_assigner.rs index c44190cc1b..29a79c76c5 100644 --- a/gadgets/src/bus/port_assigner.rs +++ b/gadgets/src/bus/port_assigner.rs @@ -11,7 +11,7 @@ use std::{ }; /// Assigners are used to delay the assignment until helper values are computed. -pub trait Assigner { +pub trait Assigner: Send + Sync { /// Given the helper value, assign ports and return (offset, term). #[must_use = "terms must be added to the bus"] fn assign(&self, region: &mut Region<'_, F>, helper: F) -> (usize, F); diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 9eabc9db9a..eb5210bd2e 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -22,9 +22,12 @@ pub(crate) mod util; #[cfg(any(feature = "test", test))] pub(crate) mod test; -use self::table::Table; #[cfg(any(feature = "test", test, feature = "test-circuits"))] pub use self::EvmCircuit as TestEvmCircuit; +use self::{ + table::{RwValues, Table}, + witness::{Rw, RwRow}, +}; pub use crate::witness; use crate::{ @@ -49,7 +52,7 @@ pub struct EvmCircuitConfig { fixed_table: [Column; 4], dual_byte_table: [Column; 2], bus: BusConfig, - bus_lookup: [BusLookupChip; 2], + bus_lookup: [BusLookupChip; 3], enable_bus_lookup: Column, pub(crate) execution: Box>, // External tables @@ -148,6 +151,7 @@ impl EvmCircuitConfig { enable_bus_lookup, &dual_byte_table, &fixed_table, + &rw_table, ); let execution = Box::new(ExecutionConfig::configure( @@ -214,7 +218,8 @@ impl EvmCircuitConfig { enabled: Column, dual_byte_table: &[Column; 2], fixed_table: &[Column; 4], - ) -> [BusLookupChip; 2] { + rw_table: &RwTable, + ) -> [BusLookupChip; 3] { let enabled = query_expression(meta, |meta| meta.query_fixed(enabled, Rotation::cur())); let byte_lookup = { @@ -241,7 +246,34 @@ impl EvmCircuitConfig { BusLookupChip::connect(meta, bus_builder, enabled.clone(), message) }; - [byte_lookup, fixed_lookup] + let rw_lookup = { + let rw_enabled = query_expression(meta, |meta| { + meta.query_fixed(rw_table.q_enable, Rotation::cur()) + }); + + let message = query_expression(meta, |meta| { + let mut query = |col| meta.query_advice(col, Rotation::cur()); + + MsgExpr::Lookup(Lookup::Rw { + counter: query(rw_table.rw_counter), + is_write: query(rw_table.is_write), + tag: query(rw_table.tag), + values: RwValues { + id: query(rw_table.id), + address: query(rw_table.address), + field_tag: query(rw_table.field_tag), + storage_key: query(rw_table.storage_key), + value: query(rw_table.value), + value_prev: query(rw_table.value_prev), + aux1: query(rw_table.aux1), + aux2: query(rw_table.aux2), + }, + }) + }); + BusLookupChip::connect(meta, bus_builder, rw_enabled, message) + }; + + [byte_lookup, fixed_lookup, rw_lookup] } } @@ -348,6 +380,64 @@ impl EvmCircuitConfig { assert_eq!(closure_count, 2, "assign_region behavior changed"); Ok(()) } + + /// Load RW table + pub(crate) fn load_rw_table( + &self, + layouter: &mut impl Layouter, + bus_assigner: &mut BusAssigner>, + bus_lookup: &BusLookupChip, + rws: &[Rw], + rw_n_rows: usize, + challenge_evm_word: Value, + ) -> Result<(), Error> { + let mut closure_count = 0; + layouter.assign_region( + || "fixed table", + |mut region| { + // TODO: deal with this some other way. + closure_count += 1; + if closure_count == 1 { + return Ok(()); + } + // Skip the first phase. + if challenge_evm_word.is_none() { + return Ok(()); + } + + for (offset, row) in RwTable::iter_active_rows(rws, rw_n_rows, challenge_evm_word) { + // Same format as `Lookup::input_exprs()` + let values: [_; 12] = [ + Value::known(F::one()), // TODO: can remove the "enabled" field. + row.rw_counter, + row.is_write, + row.tag, + row.id, + row.address, + row.field_tag, + row.storage_key, + row.value, + row.value_prev, + row.aux1, + row.aux2, + ]; + let mut inputs = Vec::with_capacity(values.len()); + for value in values { + assert!(!value.is_none(), "RW values must be known here"); + value.map(|f| inputs.push(f)); + } + + let message = MsgF::Lookup(Table::Rw, inputs); + bus_lookup.assign(&mut region, bus_assigner, offset, message)?; + } + + bus_assigner.finish_ports(&mut region); + Ok(()) + }, + )?; + assert_eq!(closure_count, 2, "assign_region behavior changed"); + Ok(()) + } } /// Tx Circuit for verifying transaction signatures @@ -494,6 +584,15 @@ impl SubCircuit for EvmCircuit { self.fixed_table_tags.clone(), )?; + config.load_rw_table( + layouter, + &mut bus_assigner, + &config.bus_lookup[2], + &block.rws.table_assignments(), + block.circuits_params.max_rws, + challenges.evm_word(), + )?; + layouter.assign_region( || "EVM_Bus", |mut region| { diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index c2b5548d78..3f9b55fb7b 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1658,7 +1658,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { fn add_bus_lookup(&mut self, lookup: Lookup) { // TODO: support all types. - if lookup.table() != Table::Fixed { + if lookup.table() != Table::Fixed && lookup.table() != Table::Rw { return; } diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 177aa4988b..828b4452aa 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2,9 +2,12 @@ use crate::{ copy_circuit::util::number_or_hash_to_field, - evm_circuit::util::{ - constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, - rlc, + evm_circuit::{ + table::MsgF, + util::{ + constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, + rlc, + }, }, exp_circuit::param::{OFFSET_INCREMENT, ROWS_PER_STEP}, impl_expr, @@ -25,6 +28,7 @@ use core::iter::once; use eth_types::{sign_types::SignData, Field, ToLittleEndian, ToScalar, ToWord, Word, U256}; use gadgets::{ binary_number::{BinaryNumberChip, BinaryNumberConfig}, + bus::bus_builder::BusAssigner, util::{and, not, split_u256, split_u256_limb64, Expr}, }; use halo2_proofs::{ @@ -647,9 +651,29 @@ impl RwTable { ] { region.assign_advice(|| "assign rw row on rw table", column, offset, || value)?; } + Ok(()) } + /// Iterate over the active rows of the RwTable: (offset, values). + pub(crate) fn iter_active_rows( + rws: &[Rw], + n_rows: usize, + challenge: Value, + ) -> impl Iterator>)> { + // TODO: we could avoid the copies in table_assignments_prepad and table_assignment. + + let (rows, pad_len) = RwMap::table_assignments_prepad(rws, n_rows); + + rows.into_iter() + .enumerate() + .skip(pad_len) + .map(move |(offset, row)| { + let row = row.table_assignment(challenge); + (offset, row) + }) + } + /// Assign the `RwTable` from a `RwMap`, following the same /// table layout that the State Circuit uses. pub fn load( From 62dba19e9fc3854285daf0684b0628f1c5e38a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 13 Oct 2023 07:03:45 +0200 Subject: [PATCH 45/67] bus: trouble-shooting --- gadgets/src/bus/bus_chip.rs | 33 +++++++++++++++++-- gadgets/src/bus/bus_lookup.rs | 2 ++ gadgets/src/bus/bus_port.rs | 6 ++++ gadgets/src/bus/port_assigner.rs | 5 +++ gadgets/src/bus/port_multi.rs | 2 ++ zkevm-circuits/src/evm_circuit.rs | 21 +++++------- .../evm_circuit/util/constraint_builder.rs | 4 +++ zkevm-circuits/src/table.rs | 23 +++++-------- 8 files changed, 66 insertions(+), 30 deletions(-) diff --git a/gadgets/src/bus/bus_chip.rs b/gadgets/src/bus/bus_chip.rs index face22ff25..bc6be3f0d8 100644 --- a/gadgets/src/bus/bus_chip.rs +++ b/gadgets/src/bus/bus_chip.rs @@ -1,7 +1,7 @@ -use super::Field; +use super::{bus_builder::BusAssigner, bus_codec::BusMessageF, Field}; use crate::util::Expr; use halo2_proofs::{ - circuit::{Region, Value}, + circuit::{Layouter, Region, Value}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, ThirdPhase}, poly::Rotation, }; @@ -63,6 +63,10 @@ impl BusConfig { ] }); + cs.annotate_lookup_any_column(enabled, || "Bus_enabled"); + cs.annotate_lookup_any_column(is_first, || "Bus_is_first"); + cs.annotate_lookup_any_column(acc, || "Bus_acc"); + Self { enabled, is_first, @@ -70,7 +74,30 @@ impl BusConfig { } } - /// Assign the helper witness. + /// Assign the accumulator values from a BusAssigner. + pub fn finish_assigner>( + &self, + layouter: &mut impl Layouter, + bus_assigner: BusAssigner, + ) -> Result<(), Error> { + let mut closure_count = 0; + layouter.assign_region( + || "Bus_accumulator", + |mut region| { + // TODO: deal with this some other way. + closure_count += 1; + if closure_count == 1 { + return Ok(()); + } + + self.assign(&mut region, bus_assigner.n_rows(), bus_assigner.terms()) + }, + )?; + assert_eq!(closure_count, 2, "assign_region behavior changed"); + Ok(()) + } + + /// Assign the accumulator values, from the sum of terms per row. pub fn assign( &self, region: &mut Region<'_, F>, diff --git a/gadgets/src/bus/bus_lookup.rs b/gadgets/src/bus/bus_lookup.rs index d7659d5323..4157242622 100644 --- a/gadgets/src/bus/bus_lookup.rs +++ b/gadgets/src/bus/bus_lookup.rs @@ -40,6 +40,8 @@ impl BusLookupChip { BusOp::send_to_lookups(message, count_expr), ); + meta.annotate_lookup_any_column(count, || "BusLookup_count"); + Self { port, count } } diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index b24f870281..0a70cc6f2b 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -113,6 +113,8 @@ impl PortChip { Port::connect(meta, bus_builder, enabled, op, helper_expr); + meta.annotate_lookup_any_column(helper, || "Port_helper"); + Self { helper, _marker: PhantomData, @@ -156,6 +158,10 @@ impl Port { helper: Column, rotation: isize, ) { + if op.count() == 0 { + return; // Leave the helper cell at 0. + } + let cmd = Box::new(PortAssigner { offset, helper, diff --git a/gadgets/src/bus/port_assigner.rs b/gadgets/src/bus/port_assigner.rs index 29a79c76c5..dcb7a087b5 100644 --- a/gadgets/src/bus/port_assigner.rs +++ b/gadgets/src/bus/port_assigner.rs @@ -102,4 +102,9 @@ impl> BusOpCounter { fn count_ops(&self, message: &M) -> isize { *self.counts.get(message).unwrap_or(&0) } + + /// Return true if all messages received have been sent. + pub fn is_complete(&self) -> bool { + self.counts.is_empty() + } } diff --git a/gadgets/src/bus/port_multi.rs b/gadgets/src/bus/port_multi.rs index 5cf0c9d931..73dc0fa123 100644 --- a/gadgets/src/bus/port_multi.rs +++ b/gadgets/src/bus/port_multi.rs @@ -36,6 +36,8 @@ impl PortBatchedChip { PortBatched::connect(meta, bus_builder, enabled, ops, helper_expr); + meta.annotate_lookup_any_column(helper, || "PortBatched_helper"); + Self { helper, _marker: PhantomData, diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index eb5210bd2e..d40882f8ff 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -26,7 +26,7 @@ pub(crate) mod test; pub use self::EvmCircuit as TestEvmCircuit; use self::{ table::{RwValues, Table}, - witness::{Rw, RwRow}, + witness::Rw, }; pub use crate::witness; @@ -279,7 +279,7 @@ impl EvmCircuitConfig { impl EvmCircuitConfig { /// Load fixed table - pub(crate) fn load_fixed_table( + fn load_fixed_table( &self, layouter: &mut impl Layouter, bus_assigner: &mut BusAssigner>, @@ -325,7 +325,7 @@ impl EvmCircuitConfig { } /// Load dual byte table - pub(crate) fn load_dual_byte_table( + fn load_dual_byte_table( &self, layouter: &mut impl Layouter, bus_assigner: &mut BusAssigner>, @@ -382,7 +382,7 @@ impl EvmCircuitConfig { } /// Load RW table - pub(crate) fn load_rw_table( + fn load_rw_table( &self, layouter: &mut impl Layouter, bus_assigner: &mut BusAssigner>, @@ -593,14 +593,11 @@ impl SubCircuit for EvmCircuit { challenges.evm_word(), )?; - layouter.assign_region( - || "EVM_Bus", - |mut region| { - config - .bus - .assign(&mut region, num_rows, bus_assigner.terms()) - }, - )?; + if !bus_assigner.op_counter().is_complete() { + log::warn!("Incomplete bus assignment."); + log::debug!("Missing bus ops: {:?}", bus_assigner.op_counter()); + } + config.bus.finish_assigner(layouter, bus_assigner)?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 3f9b55fb7b..937457a820 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1661,6 +1661,10 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { if lookup.table() != Table::Fixed && lookup.table() != Table::Rw { return; } + // TODO: support conditional lookups. + if matches!(lookup, Lookup::Conditional(_, _)) { + return; + } let op = BusOpExpr::receive(MsgExpr::Lookup(lookup)); let helper = self.query_cell_phase3(); diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 828b4452aa..f746c7be53 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2,12 +2,9 @@ use crate::{ copy_circuit::util::number_or_hash_to_field, - evm_circuit::{ - table::MsgF, - util::{ - constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, - rlc, - }, + evm_circuit::util::{ + constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, + rlc, }, exp_circuit::param::{OFFSET_INCREMENT, ROWS_PER_STEP}, impl_expr, @@ -28,7 +25,6 @@ use core::iter::once; use eth_types::{sign_types::SignData, Field, ToLittleEndian, ToScalar, ToWord, Word, U256}; use gadgets::{ binary_number::{BinaryNumberChip, BinaryNumberConfig}, - bus::bus_builder::BusAssigner, util::{and, not, split_u256, split_u256_limb64, Expr}, }; use halo2_proofs::{ @@ -663,15 +659,12 @@ impl RwTable { ) -> impl Iterator>)> { // TODO: we could avoid the copies in table_assignments_prepad and table_assignment. - let (rows, pad_len) = RwMap::table_assignments_prepad(rws, n_rows); + let (rows, _pad_len) = RwMap::table_assignments_prepad(rws, n_rows); - rows.into_iter() - .enumerate() - .skip(pad_len) - .map(move |(offset, row)| { - let row = row.table_assignment(challenge); - (offset, row) - }) + rows.into_iter().enumerate().map(move |(offset, row)| { + let row = row.table_assignment(challenge); + (offset, row) + }) } /// Assign the `RwTable` from a `RwMap`, following the same From 3829be559545d7805ffb7cf60e54dff9f4a136b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 13 Oct 2023 07:36:52 +0200 Subject: [PATCH 46/67] bus: support conditional lookups --- gadgets/src/bus/bus_port.rs | 12 ++++++++++-- zkevm-circuits/src/evm_circuit/table.rs | 11 +++++++++++ .../src/evm_circuit/util/constraint_builder.rs | 17 +++++++---------- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 0a70cc6f2b..1e44d66fc1 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -12,7 +12,7 @@ use halo2_proofs::{ plonk::{Advice, Column, ConstraintSystem, Expression, ThirdPhase}, poly::Rotation, }; -use std::marker::PhantomData; +use std::{marker::PhantomData, ops::Mul}; /// A bus operation, as expressions for circuit config. pub type BusOpExpr = BusOp>; @@ -56,6 +56,14 @@ where Self { message, count } } + /// Apply a boolean condition to this operation. + pub fn conditional(self, condition: C) -> Self { + Self { + message: self.message, + count: self.count * condition, + } + } + /// The message to send or receive. pub fn message(&self) -> M { self.message.clone() @@ -68,7 +76,7 @@ where } /// Trait usable as BusOp count (Expression or isize). -pub trait Count: Clone { +pub trait Count: Clone + Mul { /// 1 fn one() -> Self; /// -1 diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index f15ac57bc4..883acbb5d6 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -408,6 +408,17 @@ impl Lookup { } } + /// Return the inner lookup (not Conditional), and the condition (or constant 1). + pub(crate) fn unconditional(self) -> (Self, Expression) { + match self { + Self::Conditional(cond, lookup) => { + let (lookup, cond2) = lookup.unconditional(); + (lookup, cond * cond2) + } + _ => (self, 1.expr()), + } + } + pub(crate) fn input_exprs(&self) -> Vec> { match self { Self::Fixed { tag, values } => [vec![tag.clone()], values.to_vec()].concat(), diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 937457a820..5aa44c4dd5 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1657,16 +1657,9 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { } fn add_bus_lookup(&mut self, lookup: Lookup) { - // TODO: support all types. - if lookup.table() != Table::Fixed && lookup.table() != Table::Rw { - return; - } - // TODO: support conditional lookups. - if matches!(lookup, Lookup::Conditional(_, _)) { - return; - } + let (lookup, condition) = lookup.unconditional(); - let op = BusOpExpr::receive(MsgExpr::Lookup(lookup)); + let op = BusOpExpr::receive(MsgExpr::Lookup(lookup)).conditional(condition); let helper = self.query_cell_phase3(); self.bus_ops.push(StepBusOp { op, helper }); } @@ -1677,7 +1670,11 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { None => lookup, }; - self.add_bus_lookup(lookup.clone()); + // TODO: support all types. + if lookup.table() == Table::Fixed || lookup.table() == Table::Rw { + self.add_bus_lookup(lookup); + return; + } let compressed_expr = self.split_expression( "Lookup compression", From 40be2d02c9cae1303e16539c908e6de239a95966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 13 Oct 2023 07:53:42 +0200 Subject: [PATCH 47/67] bus: remove Fixed and RW lookups --- zkevm-circuits/src/evm_circuit/param.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index d82e097b80..af5cc95518 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -7,7 +7,7 @@ use halo2_proofs::{ use std::collections::HashMap; // Step dimension -pub(crate) const STEP_WIDTH: usize = 150; +pub(crate) const STEP_WIDTH: usize = 140; /// Step height pub const MAX_STEP_HEIGHT: usize = 21; /// The height of the state of a step, used by gates that connect two @@ -19,7 +19,7 @@ pub(crate) const STEP_STATE_HEIGHT: usize = 1; pub(crate) const N_PHASE2_COLUMNS: usize = 7; /// Number of Advice Phase3 columns, used by Bus ports. -pub const N_PHASE3_COLUMNS: usize = 10; +pub const N_PHASE3_COLUMNS: usize = 18; /// Number of Advice Phase1 columns in the EVM circuit pub(crate) const N_PHASE1_COLUMNS: usize = STEP_WIDTH @@ -67,13 +67,13 @@ pub(crate) const LOOKUP_CONFIG: &[(Table, usize)] = &[ ]; /// Fixed Table lookups done in EVMCircuit -pub const FIXED_TABLE_LOOKUPS: usize = 10; +pub const FIXED_TABLE_LOOKUPS: usize = 0; /// Tx Table lookups done in EVMCircuit pub const TX_TABLE_LOOKUPS: usize = 4; /// Rw Table lookups done in EVMCircuit -pub const RW_TABLE_LOOKUPS: usize = 8; +pub const RW_TABLE_LOOKUPS: usize = 0; /// Bytecode Table lookups done in EVMCircuit pub const BYTECODE_TABLE_LOOKUPS: usize = 8; From dea35284c6a1ca0fd7c1d6f462657009892fe173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 13 Oct 2023 11:51:12 +0200 Subject: [PATCH 48/67] bus: support high-degree lookups --- gadgets/src/bus/bus_port.rs | 10 +++++ zkevm-circuits/src/evm_circuit.rs | 6 +-- zkevm-circuits/src/evm_circuit/execution.rs | 2 +- zkevm-circuits/src/evm_circuit/table.rs | 37 ++++++++++++++-- zkevm-circuits/src/evm_circuit/util.rs | 25 +---------- .../evm_circuit/util/constraint_builder.rs | 43 +++++++++++++++---- 6 files changed, 83 insertions(+), 40 deletions(-) diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 1e44d66fc1..81272b1324 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -194,6 +194,16 @@ impl Port { constraint.degree() } + /// Return the maximum (message_degree, count_degree), given the degrees of the constraint + /// system and `enabled`. + pub fn max_degrees(max_degree: usize, enabled_degree: usize) -> (usize, usize) { + let helper_degree = 1; + ( + max_degree - enabled_degree - helper_degree, + max_degree - enabled_degree, + ) + } + fn create_term>( meta: &mut ConstraintSystem, codec: &BusCodecExpr, diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index d40882f8ff..2ab06ea5c5 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -224,7 +224,7 @@ impl EvmCircuitConfig { let byte_lookup = { let message = query_expression(meta, |meta| { - MsgExpr::Bytes([ + MsgExpr::bytes([ meta.query_fixed(dual_byte_table[0], Rotation::cur()), meta.query_fixed(dual_byte_table[1], Rotation::cur()), ]) @@ -234,7 +234,7 @@ impl EvmCircuitConfig { let fixed_lookup = { let message = query_expression(meta, |meta| { - MsgExpr::Lookup(Lookup::Fixed { + MsgExpr::lookup(Lookup::Fixed { tag: meta.query_fixed(fixed_table[0], Rotation::cur()), values: [ meta.query_fixed(fixed_table[1], Rotation::cur()), @@ -254,7 +254,7 @@ impl EvmCircuitConfig { let message = query_expression(meta, |meta| { let mut query = |col| meta.query_advice(col, Rotation::cur()); - MsgExpr::Lookup(Lookup::Rw { + MsgExpr::lookup(Lookup::Rw { counter: query(rw_table.rw_counter), is_write: query(rw_table.is_write), tag: query(rw_table.tag), diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 707b1f98d5..50d8683663 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -970,7 +970,7 @@ impl ExecutionConfig { let ops = byte_columns .chunks(2) .map(|columns| { - let message = MsgExpr::Bytes([columns[0].clone(), columns[1].clone()]); + let message = MsgExpr::bytes([columns[0].clone(), columns[1].clone()]); BusOp::receive(message) }) .collect::>(); diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index b83c3b7615..edd789b178 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -13,7 +13,37 @@ use strum_macros::EnumIter; #[derive(Clone, Debug)] pub(crate) enum MsgExpr { Bytes([Expression; 2]), - Lookup(Lookup), + Lookup(Table, Vec>), +} + +impl MsgExpr { + pub fn bytes(bytes: [Expression; 2]) -> Self { + Self::Bytes(bytes) + } + + pub fn lookup(lookup: Lookup) -> Self { + Self::Lookup(lookup.table(), lookup.input_exprs()) + } + + pub fn map_exprs(self, f: impl FnMut(Expression) -> Expression) -> Self { + match self { + Self::Bytes(bytes) => Self::Bytes(bytes.map(f)), + Self::Lookup(table, inputs) => Self::Lookup(table, inputs.into_iter().map(f).collect()), + } + } + + pub fn map_values(self, f: impl FnMut(Expression) -> F) -> MsgF { + match self { + Self::Bytes(exprs) => { + let values = exprs.map(f); + MsgF::Bytes(values) + } + Self::Lookup(table, exprs) => { + let values = exprs.into_iter().map(f).collect(); + MsgF::Lookup(table, values) + } + } + } } impl BusMessage> for MsgExpr { @@ -25,9 +55,8 @@ impl BusMessage> for MsgExpr { let tag = 0.expr(); vec![tag, byte0, byte1].into_iter() } - Self::Lookup(lookup) => { - let tag = (1 + (lookup.table() as u64)).expr(); - let mut inputs = lookup.input_exprs(); + Self::Lookup(table, mut inputs) => { + let tag = (1 + table as u64).expr(); inputs.insert(0, tag); inputs.into_iter() } diff --git a/zkevm-circuits/src/evm_circuit/util.rs b/zkevm-circuits/src/evm_circuit/util.rs index 0bc0258953..f81af48149 100644 --- a/zkevm-circuits/src/evm_circuit/util.rs +++ b/zkevm-circuits/src/evm_circuit/util.rs @@ -125,7 +125,8 @@ impl StepBusOp { } assert_eq!(count, -F::one(), "count must be 0 or -1"); - let message = Self::eval_msg(region, offset, self.op.message()); + let eval = |expr| region.eval(offset, expr); + let message = self.op.message().map_values(eval); Port::assign( bus_assigner, @@ -135,28 +136,6 @@ impl StepBusOp { self.helper.rotation as isize, ); } - - /// Evaluate a message from expressions of the content of a region. - fn eval_msg( - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - message: MsgExpr, - ) -> MsgF { - match message { - MsgExpr::Bytes(exprs) => { - let values = exprs.map(|expr| region.eval(offset, expr)); - MsgF::Bytes(values) - } - MsgExpr::Lookup(lookup) => { - let values = lookup - .input_exprs() - .into_iter() - .map(|expr| region.eval(offset, expr)) - .collect(); - MsgF::Lookup(lookup.table(), values) - } - } - } } pub struct CachedRegion<'r, 'b, F: FieldExt> { diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 4e7b1a6c7b..4999283707 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -17,7 +17,7 @@ use bus_mapping::{ }; use eth_types::{Field, ToLittleEndian, ToScalar, ToWord}; use gadgets::{ - bus::bus_port::BusOpExpr, + bus::bus_port::{BusOpExpr, Port}, util::{and, not, sum}, }; use halo2_proofs::{ @@ -1675,14 +1675,6 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { } } - fn add_bus_lookup(&mut self, lookup: Lookup) { - let (lookup, condition) = lookup.unconditional(); - - let op = BusOpExpr::receive(MsgExpr::Lookup(lookup)).conditional(condition); - let helper = self.query_cell_phase3(); - self.bus_ops.push(StepBusOp { op, helper }); - } - pub(crate) fn add_lookup(&mut self, name: &str, lookup: Lookup) { let lookup = match self.condition_expr_opt() { Some(condition) => lookup.conditional(condition), @@ -1703,6 +1695,39 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.store_expression(name, compressed_expr, CellType::Lookup(lookup.table())); } + fn add_bus_lookup(&mut self, lookup: Lookup) { + let (lookup, condition) = lookup.unconditional(); + + // Reduce the degree of the inputs and condition. + let (msg_degree, count_degree) = Port::max_degrees(MAX_DEGREE, IMPLICIT_DEGREE); + + let message = MsgExpr::lookup(lookup) + .map_exprs(|expr| self.store_any_expression("Bus message", expr, msg_degree)); + + let condition = self.store_any_expression("Bus count", condition, count_degree); + + // Allocate an operation to an helper cell. + let op = BusOpExpr::receive(message).conditional(condition); + let helper = self.query_cell_phase3(); + self.bus_ops.push(StepBusOp { op, helper }); + } + + /// Reduce the degree of `expr` to `target_degree`. Split and store the expression if needed. + fn store_any_expression( + &mut self, + name: &'static str, + expr: Expression, + target_degree: usize, + ) -> Expression { + let expr = self.split_expression(name, expr, MAX_DEGREE - IMPLICIT_DEGREE); + if expr.degree() <= target_degree { + expr + } else { + let cell_type = CellType::storage_for_expr(&expr); + self.store_expression(name, expr, cell_type) + } + } + pub(crate) fn store_expression( &mut self, name: &str, From 0b8d655fa860aee18eae698ebdeebaf7658eb43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 13 Oct 2023 17:17:55 +0200 Subject: [PATCH 49/67] bus: generalize table entries --- zkevm-circuits/src/evm_circuit.rs | 55 ++++++--------------- zkevm-circuits/src/evm_circuit/execution.rs | 3 +- zkevm-circuits/src/evm_circuit/table.rs | 36 +++++++++++++- zkevm-circuits/src/table.rs | 10 ++-- 4 files changed, 56 insertions(+), 48 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 2ab06ea5c5..6990f5341d 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -24,10 +24,7 @@ pub(crate) mod util; pub(crate) mod test; #[cfg(any(feature = "test", test, feature = "test-circuits"))] pub use self::EvmCircuit as TestEvmCircuit; -use self::{ - table::{RwValues, Table}, - witness::Rw, -}; +use self::{table::RwValues, witness::Rw}; pub use crate::witness; use crate::{ @@ -312,8 +309,7 @@ impl EvmCircuitConfig { || Value::known(F::one()), )?; - let message = MsgF::Lookup(Table::Fixed, row.to_vec()); - bus_lookup.assign(&mut region, bus_assigner, offset, message)?; + bus_lookup.assign(&mut region, bus_assigner, offset, MsgF::fixed(row))?; } bus_assigner.finish_ports(&mut region); @@ -368,8 +364,12 @@ impl EvmCircuitConfig { || Value::known(b1), )?; - let message = MsgF::Bytes([b0, b1]); - bus_lookup.assign(&mut region, bus_assigner, offset, message)?; + bus_lookup.assign( + &mut region, + bus_assigner, + offset, + MsgF::bytes([b0, b1]), + )?; } } @@ -400,38 +400,15 @@ impl EvmCircuitConfig { if closure_count == 1 { return Ok(()); } - // Skip the first phase. - if challenge_evm_word.is_none() { - return Ok(()); - } - - for (offset, row) in RwTable::iter_active_rows(rws, rw_n_rows, challenge_evm_word) { - // Same format as `Lookup::input_exprs()` - let values: [_; 12] = [ - Value::known(F::one()), // TODO: can remove the "enabled" field. - row.rw_counter, - row.is_write, - row.tag, - row.id, - row.address, - row.field_tag, - row.storage_key, - row.value, - row.value_prev, - row.aux1, - row.aux2, - ]; - let mut inputs = Vec::with_capacity(values.len()); - for value in values { - assert!(!value.is_none(), "RW values must be known here"); - value.map(|f| inputs.push(f)); + // Only in second phase. + challenge_evm_word.map(|challenge| { + for (offset, row) in RwTable::iter_table(rws, rw_n_rows, challenge) { + bus_lookup + .assign(&mut region, bus_assigner, offset, MsgF::rw(row)) + .unwrap(); } - - let message = MsgF::Lookup(Table::Rw, inputs); - bus_lookup.assign(&mut region, bus_assigner, offset, message)?; - } - - bus_assigner.finish_ports(&mut region); + bus_assigner.finish_ports(&mut region); + }); Ok(()) }, )?; diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 50d8683663..e6d14dabc2 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -1364,8 +1364,7 @@ impl ExecutionConfig { } else { F::zero() }; - let message = MsgF::Bytes([byte_0, byte_1]); - BusOp::receive(message) + BusOp::receive(MsgF::bytes([byte_0, byte_1])) }) .collect::>(); diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index edd789b178..938b5ce09c 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -2,6 +2,7 @@ pub use crate::table::TxContextFieldTag; use crate::{ evm_circuit::step::{ExecutionState, ResponsibleOp}, impl_expr, + witness::RwRow, }; use bus_mapping::{evm::OpcodeId, precompile::PrecompileCalls}; use eth_types::Field; @@ -70,14 +71,45 @@ pub(crate) enum MsgF { Lookup(Table, Vec), } +impl MsgF { + pub fn bytes(bytes: [F; 2]) -> Self { + Self::Bytes(bytes) + } + + pub fn fixed(row: [F; 4]) -> Self { + Self::Lookup(Table::Fixed, row.to_vec()) + } + + pub fn rw(row: RwRow) -> Self { + // Same format as `Lookup::input_exprs()` + Self::Lookup( + Table::Rw, + vec![ + F::one(), // TODO: can remove the "enabled" field. + row.rw_counter, + row.is_write, + row.tag, + row.id, + row.address, + row.field_tag, + row.storage_key, + row.value, + row.value_prev, + row.aux1, + row.aux2, + ], + ) + } +} + impl BusMessage for MsgF { type IntoIter = std::vec::IntoIter; fn into_items(self) -> Self::IntoIter { match self { Self::Bytes([b0, b1]) => vec![F::zero(), b0, b1].into_iter(), - MsgF::Lookup(table, mut inputs) => { - let tag = F::from(1 + (table as u64)); + Self::Lookup(table, mut inputs) => { + let tag = F::from(1 + table as u64); inputs.insert(0, tag); inputs.into_iter() } diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index e23815c0cb..ff858bb94d 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -651,18 +651,18 @@ impl RwTable { Ok(()) } - /// Iterate over the active rows of the RwTable: (offset, values). - pub(crate) fn iter_active_rows( + /// Iterate over the entries of the table, and their position in the circuit: (offset, values). + pub(crate) fn iter_table( rws: &[Rw], n_rows: usize, - challenge: Value, - ) -> impl Iterator>)> { + challenge: F, + ) -> impl Iterator)> { // TODO: we could avoid the copies in table_assignments_prepad and table_assignment. let (rows, _pad_len) = RwMap::table_assignments_prepad(rws, n_rows); rows.into_iter().enumerate().map(move |(offset, row)| { - let row = row.table_assignment(challenge); + let row = row.table_assignment_aux(challenge); (offset, row) }) } From dfc36b1f4dab5ae1a863acb25190db2b0b59dfb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 20 Oct 2023 19:30:28 +0200 Subject: [PATCH 50/67] bus: tx table --- zkevm-circuits/src/copy_circuit/dev.rs | 1 + zkevm-circuits/src/evm_bus.rs | 75 +++++++++++++++++ zkevm-circuits/src/evm_circuit.rs | 31 ++++++- zkevm-circuits/src/evm_circuit/table.rs | 18 ++++- .../evm_circuit/util/constraint_builder.rs | 2 +- zkevm-circuits/src/lib.rs | 1 + zkevm-circuits/src/pi_circuit/dev.rs | 1 + zkevm-circuits/src/super_circuit.rs | 15 +++- zkevm-circuits/src/table.rs | 81 ++++++++++--------- zkevm-circuits/src/tx_circuit.rs | 48 +++++++++-- 10 files changed, 219 insertions(+), 54 deletions(-) create mode 100644 zkevm-circuits/src/evm_bus.rs diff --git a/zkevm-circuits/src/copy_circuit/dev.rs b/zkevm-circuits/src/copy_circuit/dev.rs index 239d847ecb..59f79804fd 100644 --- a/zkevm-circuits/src/copy_circuit/dev.rs +++ b/zkevm-circuits/src/copy_circuit/dev.rs @@ -58,6 +58,7 @@ impl Circuit for CopyCircuit { self.external_data.max_calldata, 0, // chain id &challenge_values, + |_, _| {}, )?; config.0.rw_table.load( diff --git a/zkevm-circuits/src/evm_bus.rs b/zkevm-circuits/src/evm_bus.rs new file mode 100644 index 0000000000..26c59bb0ce --- /dev/null +++ b/zkevm-circuits/src/evm_bus.rs @@ -0,0 +1,75 @@ +use eth_types::Field; +use gadgets::bus::{ + bus_builder::{BusAssigner, BusBuilder}, + bus_lookup::BusLookupChip, +}; +use halo2_proofs::{ + circuit::Layouter, + plonk::{ConstraintSystem, Error}, + poly::Rotation, +}; +use itertools::Itertools; + +use crate::{ + evm_circuit::table::{Lookup, MsgExpr, MsgF}, + table::TxTable, + util::query_expression, +}; + +#[derive(Clone, Debug)] +pub struct EVMBus { + tx_lookup: BusLookupChip, +} + +impl EVMBus { + pub fn configure( + meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder>, + tx_table: TxTable, + ) -> Self { + let tx_lookup = { + let tx_enabled = query_expression(meta, |meta| { + meta.query_fixed(tx_table.q_enable, Rotation::cur()) + }); + let message = query_expression(meta, |meta| { + MsgExpr::lookup(Lookup::Tx { + id: meta.query_advice(tx_table.tx_id, Rotation::cur()), + field_tag: meta.query_fixed(tx_table.tag, Rotation::cur()), + index: meta.query_advice(tx_table.index, Rotation::cur()), + value: meta.query_advice(tx_table.value, Rotation::cur()), + }) + }); + BusLookupChip::connect(meta, bus_builder, tx_enabled, message) + }; + + Self { tx_lookup } + } + + pub fn assign( + &self, + layouter: &mut impl Layouter, + bus_assigner: &mut BusAssigner>, + tx_messages: Vec<(usize, MsgF)>, + ) -> Result<(), Error> { + let mut closure_count = 0; + layouter.assign_region( + || "EVM Bus Tables", + |mut region| { + closure_count += 1; + if closure_count == 1 { + return Ok(()); // TODO: deal with this some other way. + } + + for (offset, message) in tx_messages.iter().unique_by(|(o, _)| *o) { + self.tx_lookup + .assign(&mut region, bus_assigner, *offset, message.clone())?; + } + + bus_assigner.finish_ports(&mut region); + Ok(()) + }, + )?; + assert_eq!(closure_count, 2, "assign_region behavior changed"); + Ok(()) + } +} diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 6990f5341d..8e4d6b18dd 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -28,6 +28,7 @@ use self::{table::RwValues, witness::Rw}; pub use crate::witness; use crate::{ + evm_bus::EVMBus, evm_circuit::param::{MAX_STEP_HEIGHT, STEP_STATE_HEIGHT}, table::{ BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, LookupTable, @@ -50,6 +51,7 @@ pub struct EvmCircuitConfig { dual_byte_table: [Column; 2], bus: BusConfig, bus_lookup: [BusLookupChip; 3], + evm_bus: EVMBus, // TODO: move out to super circuit. enable_bus_lookup: Column, pub(crate) execution: Box>, // External tables @@ -151,6 +153,8 @@ impl EvmCircuitConfig { &rw_table, ); + let evm_bus = EVMBus::configure(meta, &mut bus_builder, tx_table.clone()); + let execution = Box::new(ExecutionConfig::configure( meta, challenges, @@ -193,6 +197,7 @@ impl EvmCircuitConfig { dual_byte_table, bus, bus_lookup, + evm_bus, enable_bus_lookup, execution, tx_table, @@ -393,7 +398,7 @@ impl EvmCircuitConfig { ) -> Result<(), Error> { let mut closure_count = 0; layouter.assign_region( - || "fixed table", + || "bus RW table", |mut region| { // TODO: deal with this some other way. closure_count += 1; @@ -531,12 +536,25 @@ impl SubCircuit for EvmCircuit { (num_rows_required_for_execution_steps, total_rows) } - /// Make the assignments to the EvmCircuit + // TODO: remove. fn synthesize_sub( &self, config: &Self::Config, challenges: &crate::util::Challenges>, layouter: &mut impl Layouter, + ) -> Result<(), Error> { + self.synthesize_sub2(config, challenges, layouter, vec![]) + } +} + +impl EvmCircuit { + /// Make the assignments to the EvmCircuit + pub fn synthesize_sub2( + &self, + config: &EvmCircuitConfig, + challenges: &crate::util::Challenges>, + layouter: &mut impl Layouter, + tx_messages: Vec<(usize, MsgF)>, ) -> Result<(), Error> { let block = self.block.as_ref().unwrap(); let num_rows = Self::get_num_rows_required(block); @@ -570,6 +588,10 @@ impl SubCircuit for EvmCircuit { challenges.evm_word(), )?; + config + .evm_bus + .assign(layouter, &mut bus_assigner, tx_messages)?; + if !bus_assigner.op_counter().is_complete() { log::warn!("Incomplete bus assignment."); log::debug!("Missing bus ops: {:?}", bus_assigner.op_counter()); @@ -738,6 +760,8 @@ impl Circuit for EvmCircuit { let (config, challenges) = config; let challenges = challenges.values(&layouter); + let mut tx_messages = vec![]; + config.tx_table.load( &mut layouter, &block.txs, @@ -745,6 +769,7 @@ impl Circuit for EvmCircuit { block.circuits_params.max_calldata, block.chain_id, &challenges, + |offset, message| tx_messages.push((offset, message)), )?; block.rws.check_rw_counter_sanity(); config.rw_table.load( @@ -781,7 +806,7 @@ impl Circuit for EvmCircuit { &challenges, )?; - self.synthesize_sub(&config, &challenges, &mut layouter) + self.synthesize_sub2(&config, &challenges, &mut layouter, tx_messages) } } diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index 938b5ce09c..f4f92fda4c 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -12,7 +12,7 @@ use strum::IntoEnumIterator; use strum_macros::EnumIter; #[derive(Clone, Debug)] -pub(crate) enum MsgExpr { +pub enum MsgExpr { Bytes([Expression; 2]), Lookup(Table, Vec>), } @@ -66,7 +66,7 @@ impl BusMessage> for MsgExpr { } #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub(crate) enum MsgF { +pub enum MsgF { Bytes([F; 2]), Lookup(Table, Vec), } @@ -100,6 +100,16 @@ impl MsgF { ], ) } + + pub fn tx(id: F, field_tag: F, index: F, value: F) -> Self { + Self::Lookup(Table::Tx, vec![ + F::one(), // TODO: can remove the "enabled" field. + id, + field_tag, + index, + value, + ]) + } } impl BusMessage for MsgF { @@ -253,7 +263,7 @@ impl FixedTableTag { } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, EnumIter)] -pub(crate) enum Table { +pub enum Table { Fixed, Tx, Rw, @@ -306,7 +316,7 @@ impl RwValues { } #[derive(Clone, Debug)] -pub(crate) enum Lookup { +pub enum Lookup { /// Lookup to fixed table, which contains several pre-built tables such as /// range tables or bitwise tables. Fixed { diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 4999283707..253faa9471 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1682,7 +1682,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { }; // TODO: support all types. - if lookup.table() == Table::Fixed || lookup.table() == Table::Rw { + if [Table::Fixed, Table::Rw, Table::Tx].contains(&lookup.table()) { self.add_bus_lookup(lookup); return; } diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index 9dd67c3c55..da60a9ee9c 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -41,6 +41,7 @@ pub mod modexp_circuit; pub mod state_circuit; pub mod super_circuit; pub mod table; +mod evm_bus; #[cfg(any(feature = "test", test))] pub mod test_util; diff --git a/zkevm-circuits/src/pi_circuit/dev.rs b/zkevm-circuits/src/pi_circuit/dev.rs index 51fee3dd05..f9176524ba 100644 --- a/zkevm-circuits/src/pi_circuit/dev.rs +++ b/zkevm-circuits/src/pi_circuit/dev.rs @@ -97,6 +97,7 @@ impl>, layouter: &mut impl Layouter, ) -> Result<(), Error> { + let mut tx_messages = vec![]; + + log::debug!("assigning tx_circuit"); + self.tx_circuit.synthesize_sub2( + &config.tx_circuit, + challenges, + layouter, + |offset, message| tx_messages.push((offset, message)), + )?; + log::debug!("assigning evm_circuit"); self.evm_circuit - .synthesize_sub(&config.evm_circuit, challenges, layouter)?; + .synthesize_sub2(&config.evm_circuit, challenges, layouter, tx_messages)?; if !challenges.lookup_input().is_none() { let is_mock_prover = format!("{:?}", challenges.lookup_input()) == *"Value { inner: Some(0x207a52ba34e1ed068be1e33b0bc39c8ede030835f549fe5c0dbe91dce97d17d2) }"; @@ -646,9 +656,6 @@ impl< log::debug!("assigning bytecode_circuit"); self.bytecode_circuit .synthesize_sub(&config.bytecode_circuit, challenges, layouter)?; - log::debug!("assigning tx_circuit"); - self.tx_circuit - .synthesize_sub(&config.tx_circuit, challenges, layouter)?; log::debug!("assigning sig_circuit"); self.sig_circuit .synthesize_sub(&config.sig_circuit, challenges, layouter)?; diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index ff858bb94d..89d4c30b1a 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2,9 +2,12 @@ use crate::{ copy_circuit::util::number_or_hash_to_field, - evm_circuit::util::{ - constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, - rlc, + evm_circuit::{ + table::MsgF, + util::{ + constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, + rlc, + }, }, exp_circuit::param::{OFFSET_INCREMENT, ROWS_PER_STEP}, impl_expr, @@ -228,6 +231,7 @@ impl TxTable { max_calldata: usize, chain_id: u64, challenges: &Challenges>, + mut provide_msg: impl FnMut(usize, MsgF) -> (), ) -> Result>, Error> { assert!( txs.len() <= max_txs, @@ -245,42 +249,47 @@ impl TxTable { ); } - fn assign_row( - region: &mut Region<'_, F>, - offset: usize, - q_enable: Column, - advice_columns: &[Column], - tag: &Column, - row: &[Value; 4], - msg: &str, - ) -> Result, Error> { - let mut value_cell = None; - for (index, column) in advice_columns.iter().enumerate() { - let cell = region.assign_advice( + let mut assign_row = + |region: &mut Region<'_, F>, + offset: usize, + q_enable: Column, + advice_columns: &[Column], + tag: &Column, + row: &[Value; 4], + msg: &str| + -> Result, Error> { + let mut value_cell = None; + for (index, column) in advice_columns.iter().enumerate() { + let cell = region.assign_advice( + || format!("tx table {msg} row {offset}"), + *column, + offset, + || row[if index > 0 { index + 1 } else { index }], + )?; + // tx_id, index, value + if index == 2 { + value_cell = Some(cell); + } + } + region.assign_fixed( + || format!("tx table q_enable row {offset}"), + q_enable, + offset, + || Value::known(F::one()), + )?; + region.assign_fixed( || format!("tx table {msg} row {offset}"), - *column, + *tag, offset, - || row[if index > 0 { index + 1 } else { index }], + || row[1], )?; - // tx_id, index, value - if index == 2 { - value_cell = Some(cell); - } - } - region.assign_fixed( - || format!("tx table q_enable row {offset}"), - q_enable, - offset, - || Value::known(F::one()), - )?; - region.assign_fixed( - || format!("tx table {msg} row {offset}"), - *tag, - offset, - || row[1], - )?; - Ok(value_cell.unwrap()) - } + row[0].zip(row[1]).zip(row[2]).zip(row[3]).map( + |(((id, field_tag), index), value)| { + provide_msg(offset, MsgF::tx(id, field_tag, index, value)); + }, + ); + Ok(value_cell.unwrap()) + }; layouter.assign_region( || "tx table", diff --git a/zkevm-circuits/src/tx_circuit.rs b/zkevm-circuits/src/tx_circuit.rs index a9fa3414ca..476a98f376 100644 --- a/zkevm-circuits/src/tx_circuit.rs +++ b/zkevm-circuits/src/tx_circuit.rs @@ -13,7 +13,10 @@ mod test; pub use dev::TxCircuitTester as TestTxCircuit; use crate::{ - evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, + evm_circuit::{ + table::MsgF, + util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, + }, sig_circuit::SigCircuit, table::{ BlockContextFieldTag::{CumNumTxs, NumAllTxs, NumTxs}, @@ -1694,9 +1697,15 @@ impl TxCircuitConfig { } /// Assign 1st empty row with tag = Null - fn assign_null_row(&self, region: &mut Region<'_, F>, offset: &mut usize) -> Result<(), Error> { + fn assign_null_row( + &self, + region: &mut Region<'_, F>, + provide_msg: &mut impl FnMut(usize, MsgF) -> (), + offset: &mut usize, + ) -> Result<(), Error> { self.assign_common_part( region, + provide_msg, *offset, None, 1, @@ -1716,6 +1725,7 @@ impl TxCircuitConfig { fn assign_fixed_rows( &self, region: &mut Region<'_, F>, + provide_msg: &mut impl FnMut(usize, MsgF) -> (), offset: &mut usize, tx: &Transaction, sign_data: &SignData, @@ -1952,6 +1962,7 @@ impl TxCircuitConfig { tx_value_cells.push(self.assign_common_part( region, + provide_msg, *offset, Some(tx), tx_id_next, @@ -2145,6 +2156,7 @@ impl TxCircuitConfig { fn assign_calldata_rows( &self, region: &mut Region<'_, F>, + provide_msg: &mut impl FnMut(usize, MsgF) -> (), offset: &mut usize, tx: &Transaction, next_tx: Option<&Transaction>, @@ -2168,6 +2180,7 @@ impl TxCircuitConfig { self.assign_common_part( region, + provide_msg, *offset, Some(tx), tx_id_next, @@ -2209,6 +2222,7 @@ impl TxCircuitConfig { fn assign_common_part( &self, region: &mut Region<'_, F>, + provide_msg: &mut impl FnMut(usize, MsgF) -> (), offset: usize, tx: Option<&Transaction>, tx_id_next: usize, @@ -2244,18 +2258,21 @@ impl TxCircuitConfig { )?; // fixed columns + let tag_f = F::from(usize::from(tag) as u64); for (col_anno, col, col_val) in [ ("q_enable", self.tx_table.q_enable, F::one()), - ("tag", self.tx_table.tag, F::from(usize::from(tag) as u64)), + ("tag", self.tx_table.tag, tag_f), ] { region.assign_fixed(|| col_anno, col, offset, || Value::known(col_val))?; } // 1st phase columns + let tx_id_f = F::from(tx_id as u64); + let index_f = F::from(index); for (col_anno, col, col_val) in [ // note that tx_table.index is not assigned in this function - ("tx_id", self.tx_table.tx_id, F::from(tx_id as u64)), - ("tx_index", self.tx_table.index, F::from(index)), + ("tx_id", self.tx_table.tx_id, tx_id_f), + ("tx_index", self.tx_table.index, index_f), ("tx_type", self.tx_type, F::from(u64::from(tx_type))), ( "is_l1_msg", @@ -2269,6 +2286,8 @@ impl TxCircuitConfig { let tx_value_cell = region.assign_advice(|| "tx_value", self.tx_table.value, offset, || value)?; + value.map(|value| provide_msg(offset, MsgF::tx(tx_id_f, tag_f, index_f, value))); + Ok(tx_value_cell) } @@ -2545,6 +2564,7 @@ impl TxCircuit { config: &TxCircuitConfig, challenges: &crate::util::Challenges>, layouter: &mut impl Layouter, + mut provide_msg: impl FnMut(usize, MsgF) -> (), start_l1_queue_index: u64, sign_datas: Vec, padding_txs: &[Transaction], @@ -2566,7 +2586,7 @@ impl TxCircuit { // 1. Empty entry region.assign_fixed(|| "q_first", config.q_first, 0, || Value::known(F::one()))?; - config.assign_null_row(&mut region, &mut offset)?; + config.assign_null_row(&mut region, &mut provide_msg, &mut offset)?; // 2. Assign all tx fields except for call data let get_tx = |i: usize| { @@ -2641,6 +2661,7 @@ impl TxCircuit { tx_value_cells.extend_from_slice( config.assign_fixed_rows( &mut region, + &mut provide_msg, &mut offset, tx, sign_data, @@ -2669,6 +2690,7 @@ impl TxCircuit { .find(|tx| !tx.call_data.is_empty()); config.assign_calldata_rows( &mut region, + &mut provide_msg, &mut offset, tx, next_tx, @@ -2744,6 +2766,19 @@ impl SubCircuit for TxCircuit { config: &Self::Config, challenges: &crate::util::Challenges>, layouter: &mut impl Layouter, + ) -> Result<(), Error> { + self.synthesize_sub2(config, challenges, layouter, |_, _| {}) + } +} + +impl TxCircuit { + /// Make the assignments to the TxCircuit + pub fn synthesize_sub2( + &self, + config: &TxCircuitConfig, + challenges: &crate::util::Challenges>, + layouter: &mut impl Layouter, + provide_msg: impl FnMut(usize, MsgF) -> (), ) -> Result<(), Error> { assert!(self.txs.len() <= self.max_txs); @@ -2800,6 +2835,7 @@ impl SubCircuit for TxCircuit { config, challenges, layouter, + provide_msg, self.start_l1_queue_index, sign_datas, &padding_txs, From d4c26c27206df93c7f05f05504638b34f1bea5e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 20 Oct 2023 21:19:02 +0200 Subject: [PATCH 51/67] bus: couple RW table and bus assign --- gadgets/src/bus/bus_chip.rs | 20 +--- gadgets/src/util.rs | 32 +++++- zkevm-circuits/src/copy_circuit/dev.rs | 3 +- zkevm-circuits/src/evm_bus.rs | 59 ++++++++--- zkevm-circuits/src/evm_circuit.rs | 134 +++++-------------------- zkevm-circuits/src/pi_circuit/dev.rs | 2 +- zkevm-circuits/src/state_circuit.rs | 35 +++++-- zkevm-circuits/src/super_circuit.rs | 22 ++-- zkevm-circuits/src/table.rs | 37 ++++--- zkevm-circuits/src/util.rs | 2 +- 10 files changed, 174 insertions(+), 172 deletions(-) diff --git a/gadgets/src/bus/bus_chip.rs b/gadgets/src/bus/bus_chip.rs index bc6be3f0d8..80f7ae06f6 100644 --- a/gadgets/src/bus/bus_chip.rs +++ b/gadgets/src/bus/bus_chip.rs @@ -1,5 +1,5 @@ use super::{bus_builder::BusAssigner, bus_codec::BusMessageF, Field}; -use crate::util::Expr; +use crate::util::{assign_global, Expr}; use halo2_proofs::{ circuit::{Layouter, Region, Value}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, ThirdPhase}, @@ -80,21 +80,11 @@ impl BusConfig { layouter: &mut impl Layouter, bus_assigner: BusAssigner, ) -> Result<(), Error> { - let mut closure_count = 0; - layouter.assign_region( + assign_global( + layouter, || "Bus_accumulator", - |mut region| { - // TODO: deal with this some other way. - closure_count += 1; - if closure_count == 1 { - return Ok(()); - } - - self.assign(&mut region, bus_assigner.n_rows(), bus_assigner.terms()) - }, - )?; - assert_eq!(closure_count, 2, "assign_region behavior changed"); - Ok(()) + |mut region| self.assign(&mut region, bus_assigner.n_rows(), bus_assigner.terms()), + ) } /// Assign the accumulator values, from the sum of terms per row. diff --git a/gadgets/src/util.rs b/gadgets/src/util.rs index 26b88e0df0..6d3cffd7ed 100644 --- a/gadgets/src/util.rs +++ b/gadgets/src/util.rs @@ -3,7 +3,11 @@ use eth_types::{ evm_types::{GasCost, OpcodeId}, U256, }; -use halo2_proofs::{arithmetic::FieldExt, plonk::{Expression, ConstraintSystem, VirtualCells}}; +use halo2_proofs::{ + arithmetic::FieldExt, + circuit::{Layouter, Region}, + plonk::{ConstraintSystem, Error, Expression, VirtualCells}, +}; /// Returns the sum of the passed in cells pub mod sum { @@ -235,6 +239,32 @@ pub fn query_expression( expr.unwrap() } +/// Assign into the global circuit. This is a wrapper around `Layouter::assign_region`, but the +/// closure is called only once, with a region spanning the entire circuit. +pub fn assign_global( + layouter: &mut impl Layouter, + name: N, + mut assignment: A, +) -> Result +where + F: FieldExt, + AR: Default, + A: FnMut(Region<'_, F>) -> Result, + N: Fn() -> NR, + NR: Into, +{ + let mut closure_count = 0; + let ret = layouter.assign_region(name, |region| { + closure_count += 1; + if closure_count == 1 { + return Ok(AR::default()); + } + assignment(region) + }); + assert_eq!(closure_count, 2, "assign_region behavior changed"); + ret +} + /// Returns 2**by as FieldExt pub fn pow_of_two(by: usize) -> F { F::from(2).pow(&[by as u64, 0, 0, 0]) diff --git a/zkevm-circuits/src/copy_circuit/dev.rs b/zkevm-circuits/src/copy_circuit/dev.rs index 59f79804fd..4b1c5f3308 100644 --- a/zkevm-circuits/src/copy_circuit/dev.rs +++ b/zkevm-circuits/src/copy_circuit/dev.rs @@ -53,16 +53,17 @@ impl Circuit for CopyCircuit { config.0.tx_table.load( &mut layouter, + |_, _| {}, &self.external_data.txs, self.external_data.max_txs, self.external_data.max_calldata, 0, // chain id &challenge_values, - |_, _| {}, )?; config.0.rw_table.load( &mut layouter, + |_, _| {}, &self.external_data.rws.table_assignments(), self.external_data.max_rws, challenge_values.evm_word(), diff --git a/zkevm-circuits/src/evm_bus.rs b/zkevm-circuits/src/evm_bus.rs index 26c59bb0ce..5167aa991b 100644 --- a/zkevm-circuits/src/evm_bus.rs +++ b/zkevm-circuits/src/evm_bus.rs @@ -11,13 +11,14 @@ use halo2_proofs::{ use itertools::Itertools; use crate::{ - evm_circuit::table::{Lookup, MsgExpr, MsgF}, - table::TxTable, - util::query_expression, + evm_circuit::table::{Lookup, MsgExpr, MsgF, RwValues}, + table::{RwTable, TxTable}, + util::{assign_global, query_expression}, }; #[derive(Clone, Debug)] pub struct EVMBus { + rw_lookup: BusLookupChip, tx_lookup: BusLookupChip, } @@ -25,8 +26,36 @@ impl EVMBus { pub fn configure( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder>, - tx_table: TxTable, + rw_table: &RwTable, + tx_table: &TxTable, ) -> Self { + let rw_lookup = { + let rw_enabled = query_expression(meta, |meta| { + meta.query_fixed(rw_table.q_enable, Rotation::cur()) + }); + + let message = query_expression(meta, |meta| { + let mut query = |col| meta.query_advice(col, Rotation::cur()); + + MsgExpr::lookup(Lookup::Rw { + counter: query(rw_table.rw_counter), + is_write: query(rw_table.is_write), + tag: query(rw_table.tag), + values: RwValues { + id: query(rw_table.id), + address: query(rw_table.address), + field_tag: query(rw_table.field_tag), + storage_key: query(rw_table.storage_key), + value: query(rw_table.value), + value_prev: query(rw_table.value_prev), + aux1: query(rw_table.aux1), + aux2: query(rw_table.aux2), + }, + }) + }); + BusLookupChip::connect(meta, bus_builder, rw_enabled, message) + }; + let tx_lookup = { let tx_enabled = query_expression(meta, |meta| { meta.query_fixed(tx_table.q_enable, Rotation::cur()) @@ -42,24 +71,30 @@ impl EVMBus { BusLookupChip::connect(meta, bus_builder, tx_enabled, message) }; - Self { tx_lookup } + Self { + rw_lookup, + tx_lookup, + } } pub fn assign( &self, layouter: &mut impl Layouter, bus_assigner: &mut BusAssigner>, + rw_messages: Vec<(usize, MsgF)>, tx_messages: Vec<(usize, MsgF)>, ) -> Result<(), Error> { - let mut closure_count = 0; - layouter.assign_region( + assign_global( + layouter, || "EVM Bus Tables", |mut region| { - closure_count += 1; - if closure_count == 1 { - return Ok(()); // TODO: deal with this some other way. + // RW table. + for (offset, message) in rw_messages.iter().unique_by(|(o, _)| *o) { + self.rw_lookup + .assign(&mut region, bus_assigner, *offset, message.clone())?; } + // TX table. for (offset, message) in tx_messages.iter().unique_by(|(o, _)| *o) { self.tx_lookup .assign(&mut region, bus_assigner, *offset, message.clone())?; @@ -68,8 +103,6 @@ impl EVMBus { bus_assigner.finish_ports(&mut region); Ok(()) }, - )?; - assert_eq!(closure_count, 2, "assign_region behavior changed"); - Ok(()) + ) } } diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 8e4d6b18dd..9562fce373 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -24,7 +24,6 @@ pub(crate) mod util; pub(crate) mod test; #[cfg(any(feature = "test", test, feature = "test-circuits"))] pub use self::EvmCircuit as TestEvmCircuit; -use self::{table::RwValues, witness::Rw}; pub use crate::witness; use crate::{ @@ -34,7 +33,7 @@ use crate::{ BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, LookupTable, ModExpTable, PowOfRandTable, RwTable, SigTable, TxTable, }, - util::{query_expression, SubCircuit, SubCircuitConfig}, + util::{assign_global, query_expression, SubCircuit, SubCircuitConfig}, }; use bus_mapping::evm::OpcodeId; use eth_types::Field; @@ -50,7 +49,7 @@ pub struct EvmCircuitConfig { fixed_table: [Column; 4], dual_byte_table: [Column; 2], bus: BusConfig, - bus_lookup: [BusLookupChip; 3], + bus_lookup: [BusLookupChip; 2], evm_bus: EVMBus, // TODO: move out to super circuit. enable_bus_lookup: Column, pub(crate) execution: Box>, @@ -150,10 +149,9 @@ impl EvmCircuitConfig { enable_bus_lookup, &dual_byte_table, &fixed_table, - &rw_table, ); - let evm_bus = EVMBus::configure(meta, &mut bus_builder, tx_table.clone()); + let evm_bus = EVMBus::configure(meta, &mut bus_builder, &rw_table, &tx_table); let execution = Box::new(ExecutionConfig::configure( meta, @@ -220,8 +218,7 @@ impl EvmCircuitConfig { enabled: Column, dual_byte_table: &[Column; 2], fixed_table: &[Column; 4], - rw_table: &RwTable, - ) -> [BusLookupChip; 3] { + ) -> [BusLookupChip; 2] { let enabled = query_expression(meta, |meta| meta.query_fixed(enabled, Rotation::cur())); let byte_lookup = { @@ -248,34 +245,7 @@ impl EvmCircuitConfig { BusLookupChip::connect(meta, bus_builder, enabled.clone(), message) }; - let rw_lookup = { - let rw_enabled = query_expression(meta, |meta| { - meta.query_fixed(rw_table.q_enable, Rotation::cur()) - }); - - let message = query_expression(meta, |meta| { - let mut query = |col| meta.query_advice(col, Rotation::cur()); - - MsgExpr::lookup(Lookup::Rw { - counter: query(rw_table.rw_counter), - is_write: query(rw_table.is_write), - tag: query(rw_table.tag), - values: RwValues { - id: query(rw_table.id), - address: query(rw_table.address), - field_tag: query(rw_table.field_tag), - storage_key: query(rw_table.storage_key), - value: query(rw_table.value), - value_prev: query(rw_table.value_prev), - aux1: query(rw_table.aux1), - aux2: query(rw_table.aux2), - }, - }) - }); - BusLookupChip::connect(meta, bus_builder, rw_enabled, message) - }; - - [byte_lookup, fixed_lookup, rw_lookup] + [byte_lookup, fixed_lookup] } } @@ -288,17 +258,10 @@ impl EvmCircuitConfig { bus_lookup: &BusLookupChip, fixed_table_tags: Vec, ) -> Result<(), Error> { - let mut closure_count = 0; - - layouter.assign_region( + assign_global( + layouter, || "fixed table", |mut region| { - // TODO: deal with this some other way. - closure_count += 1; - if closure_count == 1 { - return Ok(()); - } - for (offset, row) in std::iter::once([F::zero(); 4]) .chain(fixed_table_tags.iter().flat_map(|tag| tag.build())) .enumerate() @@ -320,9 +283,7 @@ impl EvmCircuitConfig { bus_assigner.finish_ports(&mut region); Ok(()) }, - )?; - assert_eq!(closure_count, 2, "assign_region behavior changed"); - Ok(()) + ) } /// Load dual byte table @@ -332,17 +293,10 @@ impl EvmCircuitConfig { bus_assigner: &mut BusAssigner>, bus_lookup: &BusLookupChip, ) -> Result<(), Error> { - let mut closure_count = 0; - - layouter.assign_region( + assign_global( + layouter, || "byte table", |mut region| { - // TODO: deal with this some other way. - closure_count += 1; - if closure_count == 1 { - return Ok(()); - } - for i in 0..256 { let b0 = F::from(i); for j in 0..256 { @@ -381,44 +335,7 @@ impl EvmCircuitConfig { bus_assigner.finish_ports(&mut region); Ok(()) }, - )?; - assert_eq!(closure_count, 2, "assign_region behavior changed"); - Ok(()) - } - - /// Load RW table - fn load_rw_table( - &self, - layouter: &mut impl Layouter, - bus_assigner: &mut BusAssigner>, - bus_lookup: &BusLookupChip, - rws: &[Rw], - rw_n_rows: usize, - challenge_evm_word: Value, - ) -> Result<(), Error> { - let mut closure_count = 0; - layouter.assign_region( - || "bus RW table", - |mut region| { - // TODO: deal with this some other way. - closure_count += 1; - if closure_count == 1 { - return Ok(()); - } - // Only in second phase. - challenge_evm_word.map(|challenge| { - for (offset, row) in RwTable::iter_table(rws, rw_n_rows, challenge) { - bus_lookup - .assign(&mut region, bus_assigner, offset, MsgF::rw(row)) - .unwrap(); - } - bus_assigner.finish_ports(&mut region); - }); - Ok(()) - }, - )?; - assert_eq!(closure_count, 2, "assign_region behavior changed"); - Ok(()) + ) } } @@ -543,7 +460,7 @@ impl SubCircuit for EvmCircuit { challenges: &crate::util::Challenges>, layouter: &mut impl Layouter, ) -> Result<(), Error> { - self.synthesize_sub2(config, challenges, layouter, vec![]) + self.synthesize_sub2(config, challenges, layouter, vec![], vec![]) } } @@ -554,6 +471,7 @@ impl EvmCircuit { config: &EvmCircuitConfig, challenges: &crate::util::Challenges>, layouter: &mut impl Layouter, + rw_messages: Vec<(usize, MsgF)>, tx_messages: Vec<(usize, MsgF)>, ) -> Result<(), Error> { let block = self.block.as_ref().unwrap(); @@ -579,18 +497,9 @@ impl EvmCircuit { self.fixed_table_tags.clone(), )?; - config.load_rw_table( - layouter, - &mut bus_assigner, - &config.bus_lookup[2], - &block.rws.table_assignments(), - block.circuits_params.max_rws, - challenges.evm_word(), - )?; - config .evm_bus - .assign(layouter, &mut bus_assigner, tx_messages)?; + .assign(layouter, &mut bus_assigner, rw_messages, tx_messages)?; if !bus_assigner.op_counter().is_complete() { log::warn!("Incomplete bus assignment."); @@ -761,23 +670,26 @@ impl Circuit for EvmCircuit { let challenges = challenges.values(&layouter); let mut tx_messages = vec![]; - config.tx_table.load( &mut layouter, + |offset, message| tx_messages.push((offset, message)), &block.txs, block.circuits_params.max_txs, block.circuits_params.max_calldata, block.chain_id, &challenges, - |offset, message| tx_messages.push((offset, message)), )?; + + let mut rw_messages = vec![]; block.rws.check_rw_counter_sanity(); config.rw_table.load( &mut layouter, + |offset, message| rw_messages.push((offset, message)), &block.rws.table_assignments(), block.circuits_params.max_rws, challenges.evm_word(), )?; + config .bytecode_table .dev_load(&mut layouter, block.bytecodes.values(), &challenges)?; @@ -806,7 +718,13 @@ impl Circuit for EvmCircuit { &challenges, )?; - self.synthesize_sub2(&config, &challenges, &mut layouter, tx_messages) + self.synthesize_sub2( + &config, + &challenges, + &mut layouter, + rw_messages, + tx_messages, + ) } } diff --git a/zkevm-circuits/src/pi_circuit/dev.rs b/zkevm-circuits/src/pi_circuit/dev.rs index f9176524ba..cd9816b186 100644 --- a/zkevm-circuits/src/pi_circuit/dev.rs +++ b/zkevm-circuits/src/pi_circuit/dev.rs @@ -92,12 +92,12 @@ impl StateCircuitConfig { fn assign_par( &self, layouter: &mut impl Layouter, + mut provide_msg: impl FnMut(usize, MsgF) -> (), rows: &[Rw], n_rows: usize, // 0 means dynamically calculated from `rows`. updates: &MptUpdates, @@ -694,6 +695,13 @@ impl StateCircuitConfig { padding_length ); + randomness.map(|challenge| { + // Only in second phase. + for (offset, row) in rows.iter().enumerate() { + provide_msg(offset, MsgF::rw(row.table_assignment_aux(challenge))); + } + }); + // Assigning to same columns in different regions should be avoided. // Here we use one single region to assign `overrides` to both rw table and // other parts. @@ -928,12 +936,30 @@ impl SubCircuit for StateCircuit { ) } + /// powers of randomness for instance columns + fn instance(&self) -> Vec> { + vec![] + } + /// Make the assignments to the StateCircuit fn synthesize_sub( &self, config: &Self::Config, challenges: &Challenges>, layouter: &mut impl Layouter, + ) -> Result<(), Error> { + self.synthesize_sub2(config, challenges, layouter, |_, _| {}) + } +} + +impl StateCircuit { + /// Make the assignments to the StateCircuit + pub fn synthesize_sub2( + &self, + config: &StateCircuitConfig, + challenges: &Challenges>, + layouter: &mut impl Layouter, + mut provide_msg: impl FnMut(usize, MsgF) -> (), ) -> Result<(), Error> { config.load_aux_tables(layouter)?; @@ -959,6 +985,7 @@ impl SubCircuit for StateCircuit { if is_parallel_assignment { return config.assign_par( layouter, + provide_msg, &self.rows, self.n_rows, &self.updates, @@ -990,6 +1017,7 @@ impl SubCircuit for StateCircuit { } config.rw_table.load_with_region( &mut region, + &mut provide_msg, &self.rows, self.n_rows, randomness, @@ -1027,11 +1055,6 @@ impl SubCircuit for StateCircuit { }, ) } - - /// powers of randomness for instance columns - fn instance(&self) -> Vec> { - vec![] - } } fn queries(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig) -> Queries { diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index d669e19710..5e9a9cbe22 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -624,9 +624,17 @@ impl< challenges: &crate::util::Challenges>, layouter: &mut impl Layouter, ) -> Result<(), Error> { - let mut tx_messages = vec![]; + log::debug!("assigning state_circuit"); + let mut rw_messages = vec![]; + self.state_circuit.synthesize_sub2( + &config.state_circuit, + challenges, + layouter, + |offset, message| rw_messages.push((offset, message)), + )?; log::debug!("assigning tx_circuit"); + let mut tx_messages = vec![]; self.tx_circuit.synthesize_sub2( &config.tx_circuit, challenges, @@ -635,8 +643,13 @@ impl< )?; log::debug!("assigning evm_circuit"); - self.evm_circuit - .synthesize_sub2(&config.evm_circuit, challenges, layouter, tx_messages)?; + self.evm_circuit.synthesize_sub2( + &config.evm_circuit, + challenges, + layouter, + rw_messages, + tx_messages, + )?; if !challenges.lookup_input().is_none() { let is_mock_prover = format!("{:?}", challenges.lookup_input()) == *"Value { inner: Some(0x207a52ba34e1ed068be1e33b0bc39c8ede030835f549fe5c0dbe91dce97d17d2) }"; @@ -665,9 +678,6 @@ impl< log::debug!("assigning modexp_circuit"); self.modexp_circuit .synthesize_sub(&config.modexp_circuit, challenges, layouter)?; - log::debug!("assigning state_circuit"); - self.state_circuit - .synthesize_sub(&config.state_circuit, challenges, layouter)?; log::debug!("assigning copy_circuit"); self.copy_circuit .synthesize_sub(&config.copy_circuit, challenges, layouter)?; diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 89d4c30b1a..faa7f5e096 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -28,7 +28,7 @@ use core::iter::once; use eth_types::{sign_types::SignData, Field, ToLittleEndian, ToScalar, ToWord, Word, U256}; use gadgets::{ binary_number::{BinaryNumberChip, BinaryNumberConfig}, - util::{and, not, split_u256, split_u256_limb64, Expr}, + util::{and, assign_global, not, split_u256, split_u256_limb64, Expr}, }; use halo2_proofs::{ arithmetic::FieldExt, @@ -226,12 +226,12 @@ impl TxTable { pub fn load( &self, layouter: &mut impl Layouter, + mut provide_msg: impl FnMut(usize, MsgF) -> (), // TODO: use. txs: &[Transaction], max_txs: usize, max_calldata: usize, chain_id: u64, challenges: &Challenges>, - mut provide_msg: impl FnMut(usize, MsgF) -> (), ) -> Result>, Error> { assert!( txs.len() <= max_txs, @@ -660,45 +660,42 @@ impl RwTable { Ok(()) } - /// Iterate over the entries of the table, and their position in the circuit: (offset, values). - pub(crate) fn iter_table( - rws: &[Rw], - n_rows: usize, - challenge: F, - ) -> impl Iterator)> { - // TODO: we could avoid the copies in table_assignments_prepad and table_assignment. - - let (rows, _pad_len) = RwMap::table_assignments_prepad(rws, n_rows); - - rows.into_iter().enumerate().map(move |(offset, row)| { - let row = row.table_assignment_aux(challenge); - (offset, row) - }) - } - /// Assign the `RwTable` from a `RwMap`, following the same /// table layout that the State Circuit uses. pub fn load( &self, layouter: &mut impl Layouter, + mut provide_msg: impl FnMut(usize, MsgF) -> (), rws: &[Rw], n_rows: usize, challenges: Value, ) -> Result<(), Error> { - layouter.assign_region( + assign_global( + layouter, || "rw table", - |mut region| self.load_with_region(&mut region, rws, n_rows, challenges), + |mut region| { + self.load_with_region(&mut region, &mut provide_msg, rws, n_rows, challenges) + }, ) } pub(crate) fn load_with_region( &self, region: &mut Region<'_, F>, + provide_msg: &mut impl FnMut(usize, MsgF) -> (), rws: &[Rw], n_rows: usize, challenges: Value, ) -> Result<(), Error> { let (rows, _) = RwMap::table_assignments_prepad(rws, n_rows); + + challenges.map(|challenge| { + // Only in second phase. + for (offset, row) in rows.iter().enumerate() { + provide_msg(offset, MsgF::rw(row.table_assignment_aux(challenge))); + } + }); + for (offset, row) in rows.iter().enumerate() { self.assign(region, offset, &row.table_assignment(challenges))?; } diff --git a/zkevm-circuits/src/util.rs b/zkevm-circuits/src/util.rs index afb9c2818a..2bbb96654c 100644 --- a/zkevm-circuits/src/util.rs +++ b/zkevm-circuits/src/util.rs @@ -17,7 +17,7 @@ use halo2_proofs::plonk::SecondPhase; use crate::{evm_circuit::util::rlc, table::TxLogFieldTag, witness}; use eth_types::{Field, ToAddress, Word}; pub use ethers_core::types::{Address, U256}; -pub use gadgets::util::Expr; +pub use gadgets::util::{assign_global, Expr}; /// A wrapper of is_zero in gadgets which gives is_zero at any rotation pub mod is_zero; From 8bf98781e5d10977b8d7ae6d48b028dc40ef3514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 20 Oct 2023 23:40:43 +0200 Subject: [PATCH 52/67] bus: move lookups to super_circuit --- zkevm-circuits/src/evm_bus.rs | 4 +- zkevm-circuits/src/evm_circuit.rs | 147 +++++++++----------- zkevm-circuits/src/evm_circuit/execution.rs | 15 +- zkevm-circuits/src/evm_circuit/param.rs | 6 +- zkevm-circuits/src/super_circuit.rs | 43 +++++- 5 files changed, 108 insertions(+), 107 deletions(-) diff --git a/zkevm-circuits/src/evm_bus.rs b/zkevm-circuits/src/evm_bus.rs index 5167aa991b..0653dccb72 100644 --- a/zkevm-circuits/src/evm_bus.rs +++ b/zkevm-circuits/src/evm_bus.rs @@ -17,12 +17,12 @@ use crate::{ }; #[derive(Clone, Debug)] -pub struct EVMBus { +pub struct EVMBusLookups { rw_lookup: BusLookupChip, tx_lookup: BusLookupChip, } -impl EVMBus { +impl EVMBusLookups { pub fn configure( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder>, diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 9562fce373..b1cd477097 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -27,7 +27,7 @@ pub use self::EvmCircuit as TestEvmCircuit; pub use crate::witness; use crate::{ - evm_bus::EVMBus, + evm_bus::EVMBusLookups, evm_circuit::param::{MAX_STEP_HEIGHT, STEP_STATE_HEIGHT}, table::{ BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, LookupTable, @@ -48,14 +48,10 @@ use witness::Block; pub struct EvmCircuitConfig { fixed_table: [Column; 4], dual_byte_table: [Column; 2], - bus: BusConfig, bus_lookup: [BusLookupChip; 2], - evm_bus: EVMBus, // TODO: move out to super circuit. enable_bus_lookup: Column, pub(crate) execution: Box>, // External tables - tx_table: TxTable, - rw_table: RwTable, bytecode_table: BytecodeTable, block_table: BlockTable, copy_table: CopyTable, @@ -71,10 +67,6 @@ pub struct EvmCircuitConfig { pub struct EvmCircuitConfigArgs { /// Challenge pub challenges: crate::util::Challenges>, - /// TxTable - pub tx_table: TxTable, - /// RwTable - pub rw_table: RwTable, /// BytecodeTable pub bytecode_table: BytecodeTable, /// BlockTable @@ -122,10 +114,9 @@ impl EvmCircuitConfig { #[allow(clippy::too_many_arguments)] pub fn new( meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder>, EvmCircuitConfigArgs { challenges, - tx_table, - rw_table, bytecode_table, block_table, copy_table, @@ -141,25 +132,18 @@ impl EvmCircuitConfig { let dual_byte_table = [(); 2].map(|_| meta.fixed_column()); let enable_bus_lookup = meta.fixed_column(); // TODO: replace with q_usable, or BusConfig.enabled? - let mut bus_builder = BusBuilder::new(BusCodecExpr::new(challenges.lookup_input())); - let bus_lookup = Self::configure_bus_lookup( meta, - &mut bus_builder, + bus_builder, enable_bus_lookup, &dual_byte_table, &fixed_table, ); - let evm_bus = EVMBus::configure(meta, &mut bus_builder, &rw_table, &tx_table); - let execution = Box::new(ExecutionConfig::configure( meta, challenges, - &mut bus_builder, - &fixed_table, - &tx_table, - &rw_table, + bus_builder, &bytecode_table, &block_table, ©_table, @@ -171,15 +155,11 @@ impl EvmCircuitConfig { &pow_of_rand_table, )); - let bus = BusConfig::new(meta, &bus_builder.build()); - meta.annotate_lookup_any_column(dual_byte_table[0], || "dual_byte_table_0"); meta.annotate_lookup_any_column(dual_byte_table[1], || "dual_byte_table_1"); fixed_table.iter().enumerate().for_each(|(idx, &col)| { meta.annotate_lookup_any_column(col, || format!("fix_table_{idx}")) }); - tx_table.annotate_columns(meta); - rw_table.annotate_columns(meta); bytecode_table.annotate_columns(meta); block_table.annotate_columns(meta); copy_table.annotate_columns(meta); @@ -193,13 +173,9 @@ impl EvmCircuitConfig { Self { fixed_table, dual_byte_table, - bus, bus_lookup, - evm_bus, enable_bus_lookup, execution, - tx_table, - rw_table, bytecode_table, block_table, copy_table, @@ -456,11 +432,11 @@ impl SubCircuit for EvmCircuit { // TODO: remove. fn synthesize_sub( &self, - config: &Self::Config, - challenges: &crate::util::Challenges>, - layouter: &mut impl Layouter, + _config: &Self::Config, + _challenges: &crate::util::Challenges>, + _layouter: &mut impl Layouter, ) -> Result<(), Error> { - self.synthesize_sub2(config, challenges, layouter, vec![], vec![]) + unimplemented!("use synthesize_sub2") } } @@ -471,42 +447,26 @@ impl EvmCircuit { config: &EvmCircuitConfig, challenges: &crate::util::Challenges>, layouter: &mut impl Layouter, - rw_messages: Vec<(usize, MsgF)>, - tx_messages: Vec<(usize, MsgF)>, + bus_assigner: &mut BusAssigner>, ) -> Result<(), Error> { let block = self.block.as_ref().unwrap(); - let num_rows = Self::get_num_rows_required(block); config.pow_of_rand_table.assign(layouter, challenges)?; - let mut bus_assigner = - BusAssigner::new(BusCodecVal::new(challenges.lookup_input()), num_rows); - - let export = - config - .execution - .assign_block(layouter, &mut bus_assigner, block, challenges)?; + let export = config + .execution + .assign_block(layouter, bus_assigner, block, challenges)?; self.exports.borrow_mut().replace(export); - config.load_dual_byte_table(layouter, &mut bus_assigner, &config.bus_lookup[0])?; + config.load_dual_byte_table(layouter, bus_assigner, &config.bus_lookup[0])?; config.load_fixed_table( layouter, - &mut bus_assigner, + bus_assigner, &config.bus_lookup[1], self.fixed_table_tags.clone(), )?; - config - .evm_bus - .assign(layouter, &mut bus_assigner, rw_messages, tx_messages)?; - - if !bus_assigner.op_counter().is_complete() { - log::warn!("Incomplete bus assignment."); - log::debug!("Missing bus ops: {:?}", bus_assigner.op_counter()); - } - config.bus.finish_assigner(layouter, bus_assigner)?; - Ok(()) } } @@ -615,7 +575,14 @@ use crate::util::Challenges; use crate::util::MockChallenges as Challenges; impl Circuit for EvmCircuit { - type Config = (EvmCircuitConfig, Challenges); + type Config = ( + EvmCircuitConfig, + BusConfig, + EVMBusLookups, + Challenges, + RwTable, + TxTable, + ); type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { @@ -625,8 +592,11 @@ impl Circuit for EvmCircuit { fn configure(meta: &mut ConstraintSystem) -> Self::Config { let challenges = Challenges::construct(meta); let challenges_expr = challenges.exprs(meta); + let mut bus_builder = BusBuilder::new(BusCodecExpr::new(challenges_expr.lookup_input())); let rw_table = RwTable::construct(meta); + rw_table.annotate_columns(meta); let tx_table = TxTable::construct(meta); + tx_table.annotate_columns(meta); let bytecode_table = BytecodeTable::construct(meta); let block_table = BlockTable::construct(meta); let q_copy_table = meta.fixed_column(); @@ -637,26 +607,25 @@ impl Circuit for EvmCircuit { let modexp_table = ModExpTable::construct(meta); let ecc_table = EccTable::construct(meta); let pow_of_rand_table = PowOfRandTable::construct(meta, &challenges_expr); - ( - EvmCircuitConfig::new( - meta, - EvmCircuitConfigArgs { - challenges: challenges_expr, - tx_table, - rw_table, - bytecode_table, - block_table, - copy_table, - keccak_table, - exp_table, - sig_table, - modexp_table, - ecc_table, - pow_of_rand_table, - }, - ), - challenges, - ) + let config = EvmCircuitConfig::new( + meta, + &mut bus_builder, + EvmCircuitConfigArgs { + challenges: challenges_expr, + bytecode_table, + block_table, + copy_table, + keccak_table, + exp_table, + sig_table, + modexp_table, + ecc_table, + pow_of_rand_table, + }, + ); + let evm_lookups = EVMBusLookups::configure(meta, &mut bus_builder, &rw_table, &tx_table); + let bus = BusConfig::new(meta, &bus_builder.build()); + (config, bus, evm_lookups, challenges, rw_table, tx_table) } fn synthesize( @@ -665,12 +634,16 @@ impl Circuit for EvmCircuit { mut layouter: impl Layouter, ) -> Result<(), Error> { let block = self.block.as_ref().unwrap(); + let num_rows = Self::get_num_rows_required(block); - let (config, challenges) = config; + let (config, bus, evm_lookups, challenges, rw_table, tx_table) = config; let challenges = challenges.values(&layouter); + let mut bus_assigner = + BusAssigner::new(BusCodecVal::new(challenges.lookup_input()), num_rows); + let mut tx_messages = vec![]; - config.tx_table.load( + tx_table.load( &mut layouter, |offset, message| tx_messages.push((offset, message)), &block.txs, @@ -682,7 +655,7 @@ impl Circuit for EvmCircuit { let mut rw_messages = vec![]; block.rws.check_rw_counter_sanity(); - config.rw_table.load( + rw_table.load( &mut layouter, |offset, message| rw_messages.push((offset, message)), &block.rws.table_assignments(), @@ -718,13 +691,17 @@ impl Circuit for EvmCircuit { &challenges, )?; - self.synthesize_sub2( - &config, - &challenges, - &mut layouter, - rw_messages, - tx_messages, - ) + self.synthesize_sub2(&config, &challenges, &mut layouter, &mut bus_assigner)?; + + evm_lookups.assign(&mut layouter, &mut bus_assigner, rw_messages, tx_messages)?; + + if !bus_assigner.op_counter().is_complete() { + log::warn!("Incomplete bus assignment."); + log::debug!("Missing bus ops: {:?}", bus_assigner.op_counter()); + } + bus.finish_assigner(&mut layouter, bus_assigner)?; + + Ok(()) } } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index e6d14dabc2..26a83a9b5d 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -382,9 +382,6 @@ impl ExecutionConfig { meta: &mut ConstraintSystem, challenges: Challenges>, bus_builder: &mut BusBuilder>, - fixed_table: &dyn LookupTable, - tx_table: &dyn LookupTable, - rw_table: &dyn LookupTable, bytecode_table: &dyn LookupTable, block_table: &dyn LookupTable, copy_table: &dyn LookupTable, @@ -666,9 +663,6 @@ impl ExecutionConfig { Self::configure_lookup( meta, - fixed_table, - tx_table, - rw_table, bytecode_table, block_table, copy_table, @@ -981,9 +975,6 @@ impl ExecutionConfig { #[allow(clippy::too_many_arguments)] fn configure_lookup( meta: &mut ConstraintSystem, - fixed_table: &dyn LookupTable, - tx_table: &dyn LookupTable, - rw_table: &dyn LookupTable, bytecode_table: &dyn LookupTable, block_table: &dyn LookupTable, copy_table: &dyn LookupTable, @@ -1001,9 +992,9 @@ impl ExecutionConfig { let name = format!("{table:?}"); meta.lookup_any(Box::leak(name.into_boxed_str()), |meta| { let table_expressions = match table { - Table::Fixed => fixed_table, - Table::Tx => tx_table, - Table::Rw => rw_table, + Table::Fixed => unreachable!("Fixed table is on the bus"), + Table::Tx => unreachable!("TX table is on the bus"), + Table::Rw => unreachable!("RW table is on the bus"), Table::Bytecode => bytecode_table, Table::Block => block_table, Table::Copy => copy_table, diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index 0348a316ee..9cc9f09550 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -7,7 +7,7 @@ use halo2_proofs::{ use std::collections::HashMap; // Step dimension -pub(crate) const STEP_WIDTH: usize = 140; +pub(crate) const STEP_WIDTH: usize = 130; /// Step height pub const MAX_STEP_HEIGHT: usize = 21; /// The height of the state of a step, used by gates that connect two @@ -19,7 +19,7 @@ pub(crate) const STEP_STATE_HEIGHT: usize = 1; pub(crate) const N_PHASE2_COLUMNS: usize = 7; /// Number of Advice Phase3 columns, used by Bus ports. -pub const N_PHASE3_COLUMNS: usize = 18; +pub const N_PHASE3_COLUMNS: usize = 12; /// Number of Advice Phase1 columns in the EVM circuit pub(crate) const N_PHASE1_COLUMNS: usize = STEP_WIDTH @@ -70,7 +70,7 @@ pub(crate) const LOOKUP_CONFIG: &[(Table, usize)] = &[ pub const FIXED_TABLE_LOOKUPS: usize = 0; /// Tx Table lookups done in EVMCircuit -pub const TX_TABLE_LOOKUPS: usize = 4; +pub const TX_TABLE_LOOKUPS: usize = 0; /// Rw Table lookups done in EVMCircuit pub const RW_TABLE_LOOKUPS: usize = 0; diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 5e9a9cbe22..0a04583c69 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -63,6 +63,7 @@ use crate::{ bytecode_circuit::circuit::{BytecodeCircuit, BytecodeCircuitConfigArgs}, copy_circuit::{CopyCircuit, CopyCircuitConfig, CopyCircuitConfigArgs}, ecc_circuit::{EccCircuit, EccCircuitConfig, EccCircuitConfigArgs}, + evm_bus::EVMBusLookups, evm_circuit::{EvmCircuit, EvmCircuitConfig, EvmCircuitConfigArgs}, exp_circuit::{ExpCircuit, ExpCircuitArgs, ExpCircuitConfig}, keccak_circuit::{ @@ -78,7 +79,7 @@ use crate::{ table::{ BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, ModExpTable, MptTable, PoseidonTable, PowOfRandTable, RlpFsmRlpTable as RlpTable, RwTable, SigTable, - TxTable, U16Table, U8Table, + TxTable, U16Table, U8Table, LookupTable, }, tx_circuit::{TxCircuit, TxCircuitConfig, TxCircuitConfigArgs}, util::{circuit_stats, log2_ceil, Challenges, SubCircuit, SubCircuitConfig}, @@ -93,6 +94,11 @@ use bus_mapping::{ mock::BlockData, }; use eth_types::{geth_types::GethData, Field}; +use gadgets::bus::{ + bus_builder::{BusAssigner, BusBuilder}, + bus_chip::BusConfig, + bus_codec::{BusCodecExpr, BusCodecVal}, +}; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, halo2curves::bn256::Fr, @@ -104,6 +110,8 @@ use snark_verifier_sdk::CircuitExt; /// Configuration of the Super Circuit #[derive(Clone)] pub struct SuperCircuitConfig { + bus: BusConfig, + evm_lookups: EVMBusLookups, block_table: BlockTable, mpt_table: MptTable, rlp_table: RlpTable, @@ -165,9 +173,13 @@ impl SubCircuitConfig for SuperCircuitConfig { }; let challenges_expr = challenges.exprs(meta); + let mut bus_builder = BusBuilder::new(BusCodecExpr::new(challenges_expr.lookup_input())); + let tx_table = TxTable::construct(meta); + tx_table.annotate_columns(meta); log_circuit_info(meta, "tx table"); let rw_table = RwTable::construct(meta); + rw_table.annotate_columns(meta); log_circuit_info(meta, "rw table"); let mpt_table = MptTable::construct(meta); @@ -328,10 +340,9 @@ impl SubCircuitConfig for SuperCircuitConfig { let evm_circuit = EvmCircuitConfig::new( meta, + &mut bus_builder, EvmCircuitConfigArgs { challenges: challenges_expr.clone(), - tx_table: tx_table.clone(), - rw_table, bytecode_table, block_table: block_table.clone(), copy_table, @@ -367,12 +378,18 @@ impl SubCircuitConfig for SuperCircuitConfig { ); log_circuit_info(meta, "ecc circuit"); + let evm_lookups = EVMBusLookups::configure(meta, &mut bus_builder, &rw_table, &tx_table); + + let bus = BusConfig::new(meta, &bus_builder.build()); + #[cfg(feature = "onephase")] if meta.max_phase() != 0 { log::warn!("max_phase: {}", meta.max_phase()); } SuperCircuitConfig { + bus, + evm_lookups, block_table, mpt_table, tx_table, @@ -420,6 +437,8 @@ pub struct SuperCircuit< const MAX_INNER_BLOCKS: usize, const MOCK_RANDOMNESS: u64, > { + /// Number of rows required by the SuperCircuit + num_rows: usize, /// EVM Circuit pub evm_circuit: EvmCircuit, /// State Circuit @@ -559,6 +578,7 @@ impl< } fn new_from_block(block: &Block) -> Self { + let num_rows = Self::get_num_rows_required(block); let evm_circuit = EvmCircuit::new_from_block(block); let state_circuit = StateCircuit::new_from_block(block); let tx_circuit = TxCircuit::new_from_block(block); @@ -575,6 +595,7 @@ impl< #[cfg(feature = "zktrie")] let mpt_circuit = MptCircuit::new_from_block(block); SuperCircuit:: { + num_rows, evm_circuit, state_circuit, tx_circuit, @@ -624,6 +645,9 @@ impl< challenges: &crate::util::Challenges>, layouter: &mut impl Layouter, ) -> Result<(), Error> { + let mut bus_assigner = + BusAssigner::new(BusCodecVal::new(challenges.lookup_input()), self.num_rows); + log::debug!("assigning state_circuit"); let mut rw_messages = vec![]; self.state_circuit.synthesize_sub2( @@ -647,8 +671,7 @@ impl< &config.evm_circuit, challenges, layouter, - rw_messages, - tx_messages, + &mut bus_assigner, )?; if !challenges.lookup_input().is_none() { @@ -708,6 +731,16 @@ impl< .synthesize_sub(&config.mpt_circuit, challenges, layouter)?; } + config + .evm_lookups + .assign(layouter, &mut bus_assigner, rw_messages, tx_messages)?; + + if !bus_assigner.op_counter().is_complete() { + log::warn!("Incomplete bus assignment."); + log::debug!("Missing bus ops: {:?}", bus_assigner.op_counter()); + } + config.bus.finish_assigner(layouter, bus_assigner)?; + log::debug!("super circuit synthesize_sub done"); Ok(()) } From cca3c294a76696f7914b54f7fe24f6cd24abdebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 7 Nov 2023 16:59:33 +0100 Subject: [PATCH 53/67] bus-auto: factorize bus table code --- zkevm-circuits/src/evm_bus.rs | 200 +++++++++++++++++++--------- zkevm-circuits/src/evm_circuit.rs | 8 +- zkevm-circuits/src/super_circuit.rs | 28 ++-- zkevm-circuits/src/table.rs | 2 +- 4 files changed, 150 insertions(+), 88 deletions(-) diff --git a/zkevm-circuits/src/evm_bus.rs b/zkevm-circuits/src/evm_bus.rs index 0653dccb72..c0e0e1aaad 100644 --- a/zkevm-circuits/src/evm_bus.rs +++ b/zkevm-circuits/src/evm_bus.rs @@ -4,11 +4,10 @@ use gadgets::bus::{ bus_lookup::BusLookupChip, }; use halo2_proofs::{ - circuit::Layouter, - plonk::{ConstraintSystem, Error}, + circuit::{Layouter, Region, Value}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, poly::Rotation, }; -use itertools::Itertools; use crate::{ evm_circuit::table::{Lookup, MsgExpr, MsgF, RwValues}, @@ -18,8 +17,8 @@ use crate::{ #[derive(Clone, Debug)] pub struct EVMBusLookups { - rw_lookup: BusLookupChip, - tx_lookup: BusLookupChip, + rw_bus_table: BusTable, + tx_bus_table: BusTable, } impl EVMBusLookups { @@ -29,51 +28,11 @@ impl EVMBusLookups { rw_table: &RwTable, tx_table: &TxTable, ) -> Self { - let rw_lookup = { - let rw_enabled = query_expression(meta, |meta| { - meta.query_fixed(rw_table.q_enable, Rotation::cur()) - }); - - let message = query_expression(meta, |meta| { - let mut query = |col| meta.query_advice(col, Rotation::cur()); - - MsgExpr::lookup(Lookup::Rw { - counter: query(rw_table.rw_counter), - is_write: query(rw_table.is_write), - tag: query(rw_table.tag), - values: RwValues { - id: query(rw_table.id), - address: query(rw_table.address), - field_tag: query(rw_table.field_tag), - storage_key: query(rw_table.storage_key), - value: query(rw_table.value), - value_prev: query(rw_table.value_prev), - aux1: query(rw_table.aux1), - aux2: query(rw_table.aux2), - }, - }) - }); - BusLookupChip::connect(meta, bus_builder, rw_enabled, message) - }; - - let tx_lookup = { - let tx_enabled = query_expression(meta, |meta| { - meta.query_fixed(tx_table.q_enable, Rotation::cur()) - }); - let message = query_expression(meta, |meta| { - MsgExpr::lookup(Lookup::Tx { - id: meta.query_advice(tx_table.tx_id, Rotation::cur()), - field_tag: meta.query_fixed(tx_table.tag, Rotation::cur()), - index: meta.query_advice(tx_table.index, Rotation::cur()), - value: meta.query_advice(tx_table.value, Rotation::cur()), - }) - }); - BusLookupChip::connect(meta, bus_builder, tx_enabled, message) - }; - + let rw_bus_table = BusTable::configure(meta, bus_builder, rw_table); + let tx_bus_table = BusTable::configure(meta, bus_builder, tx_table); Self { - rw_lookup, - tx_lookup, + rw_bus_table, + tx_bus_table, } } @@ -81,24 +40,13 @@ impl EVMBusLookups { &self, layouter: &mut impl Layouter, bus_assigner: &mut BusAssigner>, - rw_messages: Vec<(usize, MsgF)>, - tx_messages: Vec<(usize, MsgF)>, ) -> Result<(), Error> { assign_global( layouter, || "EVM Bus Tables", |mut region| { - // RW table. - for (offset, message) in rw_messages.iter().unique_by(|(o, _)| *o) { - self.rw_lookup - .assign(&mut region, bus_assigner, *offset, message.clone())?; - } - - // TX table. - for (offset, message) in tx_messages.iter().unique_by(|(o, _)| *o) { - self.tx_lookup - .assign(&mut region, bus_assigner, *offset, message.clone())?; - } + self.rw_bus_table.assign(&mut region, bus_assigner)?; + self.tx_bus_table.assign(&mut region, bus_assigner)?; bus_assigner.finish_ports(&mut region); Ok(()) @@ -106,3 +54,131 @@ impl EVMBusLookups { ) } } + +#[derive(Clone, Debug)] +struct BusTable { + enabled: Expression, + message: MsgExpr, + chip: BusLookupChip, +} + +impl BusTable { + fn configure( + meta: &mut ConstraintSystem, + bus_builder: &mut BusBuilder>, + table: &dyn QueryTable, + ) -> Self { + let (enabled, message) = + query_expression(meta, |meta| (table.enabled(meta), table.message(meta))); + BusTable { + enabled: enabled.clone(), + message: message.clone(), + chip: BusLookupChip::connect(meta, bus_builder, enabled, message), + } + } + + fn assign( + &self, + region: &mut Region, + bus_assigner: &mut BusAssigner>, + ) -> Result<(), Error> { + for offset in 0..bus_assigner.n_rows() { + let enabled = eval(region, offset, self.enabled.clone()); + if enabled.is_zero_vartime() { + continue; + } + + let message = self + .message + .clone() + .map_values(|expr| eval(region, offset, expr)); + + self.chip + .assign(region, bus_assigner, offset, message.clone())?; + } + Ok(()) + } +} + +fn eval(region: &Region, offset: usize, expr: Expression) -> F { + // TODO: error handling. + let value = expr.evaluate( + &|scalar| Value::known(scalar), + &|_| unimplemented!("selector column"), + &|fixed_query| { + Value::known( + region + .query_fixed( + Column::new(fixed_query.column_index(), Fixed), + (offset as i32 + fixed_query.rotation().0) as usize, + ) + .unwrap(), + ) + }, + &|advice_query| { + Value::known( + region + .query_advice( + Column::new(advice_query.column_index(), Advice::default()), + (offset as i32 + advice_query.rotation().0) as usize, + ) + .unwrap(), + ) + }, + &|_| unimplemented!("instance column"), + &|_| unimplemented!("challenge"), + &|a| -a, + &|a, b| a + b, + &|a, b| a * b, + &|a, scalar| a * Value::known(scalar), + ); + let mut f = F::zero(); + value.map(|v| f = v); + f +} + +trait QueryTable { + fn enabled(&self, meta: &mut VirtualCells) -> Expression; + fn message(&self, meta: &mut VirtualCells) -> MsgExpr; +} + +impl QueryTable for RwTable { + fn enabled(&self, meta: &mut VirtualCells) -> Expression { + meta.query_fixed(self.q_enable, Rotation::cur()) + } + + fn message(&self, meta: &mut VirtualCells) -> MsgExpr { + let mut query = |col| meta.query_advice(col, Rotation::cur()); + + MsgExpr::lookup(Lookup::Rw { + counter: query(self.rw_counter), + is_write: query(self.is_write), + tag: query(self.tag), + values: RwValues { + id: query(self.id), + address: query(self.address), + field_tag: query(self.field_tag), + storage_key: query(self.storage_key), + value: query(self.value), + value_prev: query(self.value_prev), + aux1: query(self.aux1), + aux2: query(self.aux2), + }, + }) + } +} + +impl QueryTable for TxTable { + fn enabled(&self, meta: &mut VirtualCells) -> Expression { + meta.query_fixed(self.q_enable, Rotation::cur()) + } + + fn message(&self, meta: &mut VirtualCells) -> MsgExpr { + MsgExpr::lookup(Lookup::Tx { + id: meta.query_advice(self.tx_id, Rotation::cur()), + field_tag: meta.query_fixed(self.tag, Rotation::cur()), + index: meta.query_advice(self.index, Rotation::cur()), + value: meta.query_advice(self.value, Rotation::cur()), + }) + } +} diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index b1cd477097..3d09215009 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -642,10 +642,9 @@ impl Circuit for EvmCircuit { let mut bus_assigner = BusAssigner::new(BusCodecVal::new(challenges.lookup_input()), num_rows); - let mut tx_messages = vec![]; tx_table.load( &mut layouter, - |offset, message| tx_messages.push((offset, message)), + |_offset, _message| {}, &block.txs, block.circuits_params.max_txs, block.circuits_params.max_calldata, @@ -653,11 +652,10 @@ impl Circuit for EvmCircuit { &challenges, )?; - let mut rw_messages = vec![]; block.rws.check_rw_counter_sanity(); rw_table.load( &mut layouter, - |offset, message| rw_messages.push((offset, message)), + |_offset, _message| {}, &block.rws.table_assignments(), block.circuits_params.max_rws, challenges.evm_word(), @@ -693,7 +691,7 @@ impl Circuit for EvmCircuit { self.synthesize_sub2(&config, &challenges, &mut layouter, &mut bus_assigner)?; - evm_lookups.assign(&mut layouter, &mut bus_assigner, rw_messages, tx_messages)?; + evm_lookups.assign(&mut layouter, &mut bus_assigner)?; if !bus_assigner.op_counter().is_complete() { log::warn!("Incomplete bus assignment."); diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 0a04583c69..b8b7ca352c 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -77,9 +77,9 @@ use crate::{ sig_circuit::{SigCircuit, SigCircuitConfig, SigCircuitConfigArgs}, state_circuit::{StateCircuit, StateCircuitConfig, StateCircuitConfigArgs}, table::{ - BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, ModExpTable, - MptTable, PoseidonTable, PowOfRandTable, RlpFsmRlpTable as RlpTable, RwTable, SigTable, - TxTable, U16Table, U8Table, LookupTable, + BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, LookupTable, + ModExpTable, MptTable, PoseidonTable, PowOfRandTable, RlpFsmRlpTable as RlpTable, RwTable, + SigTable, TxTable, U16Table, U8Table, }, tx_circuit::{TxCircuit, TxCircuitConfig, TxCircuitConfigArgs}, util::{circuit_stats, log2_ceil, Challenges, SubCircuit, SubCircuitConfig}, @@ -649,22 +649,12 @@ impl< BusAssigner::new(BusCodecVal::new(challenges.lookup_input()), self.num_rows); log::debug!("assigning state_circuit"); - let mut rw_messages = vec![]; - self.state_circuit.synthesize_sub2( - &config.state_circuit, - challenges, - layouter, - |offset, message| rw_messages.push((offset, message)), - )?; + self.state_circuit + .synthesize_sub(&config.state_circuit, challenges, layouter)?; log::debug!("assigning tx_circuit"); - let mut tx_messages = vec![]; - self.tx_circuit.synthesize_sub2( - &config.tx_circuit, - challenges, - layouter, - |offset, message| tx_messages.push((offset, message)), - )?; + self.tx_circuit + .synthesize_sub(&config.tx_circuit, challenges, layouter)?; log::debug!("assigning evm_circuit"); self.evm_circuit.synthesize_sub2( @@ -731,9 +721,7 @@ impl< .synthesize_sub(&config.mpt_circuit, challenges, layouter)?; } - config - .evm_lookups - .assign(layouter, &mut bus_assigner, rw_messages, tx_messages)?; + config.evm_lookups.assign(layouter, &mut bus_assigner)?; if !bus_assigner.op_counter().is_complete() { log::warn!("Incomplete bus assignment."); diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index faa7f5e096..60dd942e56 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -226,7 +226,7 @@ impl TxTable { pub fn load( &self, layouter: &mut impl Layouter, - mut provide_msg: impl FnMut(usize, MsgF) -> (), // TODO: use. + mut provide_msg: impl FnMut(usize, MsgF) -> (), txs: &[Transaction], max_txs: usize, max_calldata: usize, From c258ee43240ba7ba7eff15c3163c0be17a310564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 7 Nov 2023 19:08:37 +0100 Subject: [PATCH 54/67] bus-auto: all tables except Block and Ecc --- zkevm-circuits/src/evm_bus.rs | 241 ++++++++++++++++-- zkevm-circuits/src/evm_circuit.rs | 34 ++- .../evm_circuit/util/constraint_builder.rs | 17 +- zkevm-circuits/src/super_circuit.rs | 32 ++- 4 files changed, 287 insertions(+), 37 deletions(-) diff --git a/zkevm-circuits/src/evm_bus.rs b/zkevm-circuits/src/evm_bus.rs index c0e0e1aaad..cf078043af 100644 --- a/zkevm-circuits/src/evm_bus.rs +++ b/zkevm-circuits/src/evm_bus.rs @@ -1,7 +1,18 @@ +use crate::{ + evm_circuit::table::{Lookup, MsgExpr, MsgF, RwValues}, + table::{ + BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, ModExpTable, + PowOfRandTable, RwTable, SigTable, TxTable, + }, + util::{assign_global, query_expression}, +}; use eth_types::Field; -use gadgets::bus::{ - bus_builder::{BusAssigner, BusBuilder}, - bus_lookup::BusLookupChip, +use gadgets::{ + bus::{ + bus_builder::{BusAssigner, BusBuilder}, + bus_lookup::BusLookupChip, + }, + util::Expr, }; use halo2_proofs::{ circuit::{Layouter, Region, Value}, @@ -9,16 +20,9 @@ use halo2_proofs::{ poly::Rotation, }; -use crate::{ - evm_circuit::table::{Lookup, MsgExpr, MsgF, RwValues}, - table::{RwTable, TxTable}, - util::{assign_global, query_expression}, -}; - #[derive(Clone, Debug)] pub struct EVMBusLookups { - rw_bus_table: BusTable, - tx_bus_table: BusTable, + bus_tables: [BusTable; 11], } impl EVMBusLookups { @@ -27,12 +31,30 @@ impl EVMBusLookups { bus_builder: &mut BusBuilder>, rw_table: &RwTable, tx_table: &TxTable, + bytecode_table: &BytecodeTable, + block_table: &BlockTable, + copy_table: &CopyTable, + keccak_table: &KeccakTable, + exp_table: &ExpTable, + sig_table: &SigTable, + modexp_table: &ModExpTable, + ecc_table: &EccTable, + pow_of_rand_table: &PowOfRandTable, ) -> Self { - let rw_bus_table = BusTable::configure(meta, bus_builder, rw_table); - let tx_bus_table = BusTable::configure(meta, bus_builder, tx_table); Self { - rw_bus_table, - tx_bus_table, + bus_tables: [ + BusTable::configure(meta, bus_builder, rw_table), + BusTable::configure(meta, bus_builder, tx_table), + BusTable::configure(meta, bus_builder, bytecode_table), + BusTable::configure(meta, bus_builder, block_table), + BusTable::configure(meta, bus_builder, copy_table), + BusTable::configure(meta, bus_builder, keccak_table), + BusTable::configure(meta, bus_builder, exp_table), + BusTable::configure(meta, bus_builder, sig_table), + BusTable::configure(meta, bus_builder, modexp_table), + BusTable::configure(meta, bus_builder, ecc_table), + BusTable::configure(meta, bus_builder, pow_of_rand_table), + ], } } @@ -45,8 +67,9 @@ impl EVMBusLookups { layouter, || "EVM Bus Tables", |mut region| { - self.rw_bus_table.assign(&mut region, bus_assigner)?; - self.tx_bus_table.assign(&mut region, bus_assigner)?; + for table in &self.bus_tables { + table.assign(&mut region, bus_assigner)?; + } bus_assigner.finish_ports(&mut region); Ok(()) @@ -182,3 +205,187 @@ impl QueryTable for TxTable { }) } } + +impl QueryTable for BytecodeTable { + fn enabled(&self, meta: &mut VirtualCells) -> Expression { + meta.query_fixed(self.q_enable, Rotation::cur()) + } + + fn message(&self, meta: &mut VirtualCells) -> MsgExpr { + let mut query = |col| meta.query_advice(col, Rotation::cur()); + + MsgExpr::lookup(Lookup::Bytecode { + hash: query(self.code_hash), + tag: query(self.tag), + index: query(self.index), + is_code: query(self.is_code), + value: query(self.value), + push_rlc: query(self.push_rlc), + }) + } +} + +impl QueryTable for BlockTable { + fn enabled(&self, _meta: &mut VirtualCells) -> Expression { + // TODO: may need an enabled selector. Cannot use tag because it is not boolean. + 1.expr() + } + + fn message(&self, meta: &mut VirtualCells) -> MsgExpr { + MsgExpr::lookup(Lookup::Block { + field_tag: meta.query_fixed(self.tag, Rotation::cur()), + number: meta.query_advice(self.index, Rotation::cur()), + value: meta.query_advice(self.value, Rotation::cur()), + }) + } +} + +impl QueryTable for CopyTable { + fn enabled(&self, meta: &mut VirtualCells) -> Expression { + meta.query_fixed(self.q_enable, Rotation::cur()) + * meta.query_advice(self.is_first, Rotation::cur()) + } + + fn message(&self, meta: &mut VirtualCells) -> MsgExpr { + MsgExpr::lookup(Lookup::CopyTable { + is_first: meta.query_advice(self.is_first, Rotation::cur()), // TODO: can be removed. + src_id: meta.query_advice(self.id, Rotation::cur()), + src_tag: self.tag.value(Rotation::cur())(meta), + dst_id: meta.query_advice(self.id, Rotation::next()), + dst_tag: self.tag.value(Rotation::next())(meta), + src_addr: meta.query_advice(self.addr, Rotation::cur()), + src_addr_end: meta.query_advice(self.src_addr_end, Rotation::cur()), + dst_addr: meta.query_advice(self.addr, Rotation::next()), + length: meta.query_advice(self.real_bytes_left, Rotation::cur()), + rlc_acc: meta.query_advice(self.rlc_acc, Rotation::cur()), + rw_counter: meta.query_advice(self.rw_counter, Rotation::cur()), + rwc_inc: meta.query_advice(self.rwc_inc_left, Rotation::cur()), + }) + } +} + +impl QueryTable for KeccakTable { + fn enabled(&self, meta: &mut VirtualCells) -> Expression { + meta.query_fixed(self.q_enable, Rotation::cur()) + * meta.query_advice(self.is_final, Rotation::cur()) + } + + fn message(&self, meta: &mut VirtualCells) -> MsgExpr { + MsgExpr::lookup(Lookup::KeccakTable { + input_rlc: meta.query_advice(self.input_rlc, Rotation::cur()), + input_len: meta.query_advice(self.input_len, Rotation::cur()), + output_rlc: meta.query_advice(self.output_rlc, Rotation::cur()), + }) + } +} + +impl QueryTable for ExpTable { + fn enabled(&self, meta: &mut VirtualCells) -> Expression { + // TODO: only is_step? + meta.query_fixed(self.q_enable, Rotation::cur()) + * meta.query_fixed(self.is_step, Rotation::cur()) + } + + fn message(&self, meta: &mut VirtualCells) -> MsgExpr { + MsgExpr::lookup(Lookup::ExpTable { + base_limbs: [ + meta.query_advice(self.base_limb, Rotation::cur()), + meta.query_advice(self.base_limb, Rotation::next()), + meta.query_advice(self.base_limb, Rotation(2)), + meta.query_advice(self.base_limb, Rotation(3)), + ], + exponent_lo_hi: [ + meta.query_advice(self.exponent_lo_hi, Rotation::cur()), + meta.query_advice(self.exponent_lo_hi, Rotation::next()), + ], + exponentiation_lo_hi: [ + meta.query_advice(self.exponentiation_lo_hi, Rotation::cur()), + meta.query_advice(self.exponentiation_lo_hi, Rotation::next()), + ], + }) + } +} + +impl QueryTable for SigTable { + fn enabled(&self, meta: &mut VirtualCells) -> Expression { + meta.query_fixed(self.q_enable, Rotation::cur()) + } + + fn message(&self, meta: &mut VirtualCells) -> MsgExpr { + let mut query = |col| meta.query_advice(col, Rotation::cur()); + + MsgExpr::lookup(Lookup::SigTable { + msg_hash_rlc: query(self.msg_hash_rlc), + sig_v: query(self.sig_v), + sig_r_rlc: query(self.sig_r_rlc), + sig_s_rlc: query(self.sig_s_rlc), + recovered_addr: query(self.recovered_addr), + is_valid: query(self.is_valid), + }) + } +} + +impl QueryTable for ModExpTable { + fn enabled(&self, meta: &mut VirtualCells) -> Expression { + meta.query_fixed(self.q_head, Rotation::cur()) + } + + fn message(&self, meta: &mut VirtualCells) -> MsgExpr { + MsgExpr::lookup(Lookup::ModExpTable { + base_limbs: [ + meta.query_advice(self.base, Rotation::cur()), + meta.query_advice(self.base, Rotation::next()), + meta.query_advice(self.base, Rotation(2)), + ], + exp_limbs: [ + meta.query_advice(self.exp, Rotation::cur()), + meta.query_advice(self.exp, Rotation::next()), + meta.query_advice(self.exp, Rotation(2)), + ], + modulus_limbs: [ + meta.query_advice(self.modulus, Rotation::cur()), + meta.query_advice(self.modulus, Rotation::next()), + meta.query_advice(self.modulus, Rotation(2)), + ], + result_limbs: [ + meta.query_advice(self.result, Rotation::cur()), + meta.query_advice(self.result, Rotation::next()), + meta.query_advice(self.result, Rotation(2)), + ], + }) + } +} + +impl QueryTable for EccTable { + fn enabled(&self, _meta: &mut VirtualCells) -> Expression { + // TODO: may need an enabled selector. Cannot use op_type because it is not boolean. + 1.expr() + } + + fn message(&self, meta: &mut VirtualCells) -> MsgExpr { + MsgExpr::lookup(Lookup::EccTable { + op_type: meta.query_fixed(self.op_type, Rotation::cur()), + is_valid: meta.query_advice(self.is_valid, Rotation::cur()), + arg1_rlc: meta.query_advice(self.arg1_rlc, Rotation::cur()), + arg2_rlc: meta.query_advice(self.arg2_rlc, Rotation::cur()), + arg3_rlc: meta.query_advice(self.arg3_rlc, Rotation::cur()), + arg4_rlc: meta.query_advice(self.arg4_rlc, Rotation::cur()), + input_rlc: meta.query_advice(self.input_rlc, Rotation::cur()), + output1_rlc: meta.query_advice(self.output1_rlc, Rotation::cur()), + output2_rlc: meta.query_advice(self.output2_rlc, Rotation::cur()), + }) + } +} + +impl QueryTable for PowOfRandTable { + fn enabled(&self, meta: &mut VirtualCells) -> Expression { + meta.query_fixed(self.q_enable, Rotation::cur()) + } + + fn message(&self, meta: &mut VirtualCells) -> MsgExpr { + MsgExpr::lookup(Lookup::PowOfRandTable { + exponent: meta.query_fixed(self.exponent, Rotation::cur()), + pow_of_rand: meta.query_advice(self.pow_of_rand, Rotation::cur()), + }) + } +} diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 3d09215009..520739cb07 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -612,18 +612,32 @@ impl Circuit for EvmCircuit { &mut bus_builder, EvmCircuitConfigArgs { challenges: challenges_expr, - bytecode_table, - block_table, - copy_table, - keccak_table, - exp_table, - sig_table, - modexp_table, - ecc_table, - pow_of_rand_table, + bytecode_table: bytecode_table.clone(), + block_table: block_table.clone(), + copy_table: copy_table.clone(), + keccak_table: keccak_table.clone(), + exp_table: exp_table.clone(), + sig_table: sig_table.clone(), + modexp_table: modexp_table.clone(), + ecc_table: ecc_table.clone(), + pow_of_rand_table: pow_of_rand_table.clone(), }, ); - let evm_lookups = EVMBusLookups::configure(meta, &mut bus_builder, &rw_table, &tx_table); + let evm_lookups = EVMBusLookups::configure( + meta, + &mut bus_builder, + &rw_table, + &tx_table, + &bytecode_table, + &block_table, + ©_table, + &keccak_table, + &exp_table, + &sig_table, + &modexp_table, + &ecc_table, + &pow_of_rand_table, + ); let bus = BusConfig::new(meta, &bus_builder.build()); (config, bus, evm_lookups, challenges, rw_table, tx_table) } diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 253faa9471..e90a9766f5 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1682,7 +1682,22 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { }; // TODO: support all types. - if [Table::Fixed, Table::Rw, Table::Tx].contains(&lookup.table()) { + if [ + Table::Fixed, + Table::Rw, + Table::Tx, + Table::Bytecode, + //Table::Block, // TODO: enabled selector + Table::Copy, + Table::Keccak, + Table::Exp, + Table::Sig, + Table::ModExp, + //Table::Ecc, // TODO: enabled selector + Table::PowOfRand, + ] + .contains(&lookup.table()) + { self.add_bus_lookup(lookup); return; } diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index b8b7ca352c..88d73218ca 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -343,15 +343,15 @@ impl SubCircuitConfig for SuperCircuitConfig { &mut bus_builder, EvmCircuitConfigArgs { challenges: challenges_expr.clone(), - bytecode_table, + bytecode_table: bytecode_table.clone(), block_table: block_table.clone(), - copy_table, + copy_table: copy_table.clone(), keccak_table: keccak_table.clone(), - exp_table, - sig_table, - modexp_table, - ecc_table, - pow_of_rand_table, + exp_table: exp_table.clone(), + sig_table: sig_table.clone(), + modexp_table: modexp_table.clone(), + ecc_table: ecc_table.clone(), + pow_of_rand_table: pow_of_rand_table.clone(), }, ); log_circuit_info(meta, "evm circuit"); @@ -362,7 +362,7 @@ impl SubCircuitConfig for SuperCircuitConfig { let sig_circuit = SigCircuitConfig::new( meta, SigCircuitConfigArgs { - keccak_table, + keccak_table: keccak_table.clone(), sig_table, challenges: challenges_expr.clone(), }, @@ -378,7 +378,21 @@ impl SubCircuitConfig for SuperCircuitConfig { ); log_circuit_info(meta, "ecc circuit"); - let evm_lookups = EVMBusLookups::configure(meta, &mut bus_builder, &rw_table, &tx_table); + let evm_lookups = EVMBusLookups::configure( + meta, + &mut bus_builder, + &rw_table, + &tx_table, + &bytecode_table, + &block_table, + ©_table, + &keccak_table, + &exp_table, + &sig_table, + &modexp_table, + &ecc_table, + &pow_of_rand_table, + ); let bus = BusConfig::new(meta, &bus_builder.build()); From 3393cb6dd363de66136263c8fc5d8bb8582eb9f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 7 Nov 2023 20:53:15 +0100 Subject: [PATCH 55/67] bus-auto: q_enable for Block and Ecc tables --- zkevm-circuits/src/ecc_circuit.rs | 18 ++++++++++++ zkevm-circuits/src/evm_bus.rs | 28 ++++++++----------- .../evm_circuit/util/constraint_builder.rs | 6 ++-- zkevm-circuits/src/pi_circuit.rs | 12 +++++++- zkevm-circuits/src/table.rs | 18 ++++++++++++ zkevm-circuits/src/tx_circuit.rs | 6 ++++ 6 files changed, 68 insertions(+), 20 deletions(-) diff --git a/zkevm-circuits/src/ecc_circuit.rs b/zkevm-circuits/src/ecc_circuit.rs index 998820b83b..3e1aa471c2 100644 --- a/zkevm-circuits/src/ecc_circuit.rs +++ b/zkevm-circuits/src/ecc_circuit.rs @@ -311,6 +311,12 @@ impl EccCircuit { |mut region| { // handle EcAdd ops. for (idx, ec_add_assigned) in assigned_ec_ops.ec_adds_assigned.iter().enumerate() { + region.assign_fixed( + || "assign ecc_table enabled", + config.ecc_table.q_enable, + idx, + || Value::known(F::one()), + )?; region.assign_fixed( || "assign ecc_table op_type", config.ecc_table.op_type, @@ -370,6 +376,12 @@ impl EccCircuit { // handle EcMul ops. for (idx, ec_mul_assigned) in assigned_ec_ops.ec_muls_assigned.iter().enumerate() { let idx = idx + self.max_add_ops; + region.assign_fixed( + || "assign ecc_table enabled", + config.ecc_table.q_enable, + idx, + || Value::known(F::one()), + )?; region.assign_fixed( || "assign ecc_table op_type", config.ecc_table.op_type, @@ -427,6 +439,12 @@ impl EccCircuit { assigned_ec_ops.ec_pairings_assigned.iter().enumerate() { let idx = idx + self.max_add_ops + self.max_mul_ops; + region.assign_fixed( + || "assign ecc_table enabled", + config.ecc_table.q_enable, + idx, + || Value::known(F::one()), + )?; region.assign_fixed( || "assign ecc_table op_type", config.ecc_table.op_type, diff --git a/zkevm-circuits/src/evm_bus.rs b/zkevm-circuits/src/evm_bus.rs index cf078043af..d30b977f62 100644 --- a/zkevm-circuits/src/evm_bus.rs +++ b/zkevm-circuits/src/evm_bus.rs @@ -7,12 +7,9 @@ use crate::{ util::{assign_global, query_expression}, }; use eth_types::Field; -use gadgets::{ - bus::{ - bus_builder::{BusAssigner, BusBuilder}, - bus_lookup::BusLookupChip, - }, - util::Expr, +use gadgets::bus::{ + bus_builder::{BusAssigner, BusBuilder}, + bus_lookup::BusLookupChip, }; use halo2_proofs::{ circuit::{Layouter, Region, Value}, @@ -226,9 +223,8 @@ impl QueryTable for BytecodeTable { } impl QueryTable for BlockTable { - fn enabled(&self, _meta: &mut VirtualCells) -> Expression { - // TODO: may need an enabled selector. Cannot use tag because it is not boolean. - 1.expr() + fn enabled(&self, meta: &mut VirtualCells) -> Expression { + meta.query_fixed(self.q_enable, Rotation::cur()) } fn message(&self, meta: &mut VirtualCells) -> MsgExpr { @@ -243,12 +239,11 @@ impl QueryTable for BlockTable { impl QueryTable for CopyTable { fn enabled(&self, meta: &mut VirtualCells) -> Expression { meta.query_fixed(self.q_enable, Rotation::cur()) - * meta.query_advice(self.is_first, Rotation::cur()) } fn message(&self, meta: &mut VirtualCells) -> MsgExpr { MsgExpr::lookup(Lookup::CopyTable { - is_first: meta.query_advice(self.is_first, Rotation::cur()), // TODO: can be removed. + is_first: meta.query_advice(self.is_first, Rotation::cur()), src_id: meta.query_advice(self.id, Rotation::cur()), src_tag: self.tag.value(Rotation::cur())(meta), dst_id: meta.query_advice(self.id, Rotation::next()), @@ -266,6 +261,7 @@ impl QueryTable for CopyTable { impl QueryTable for KeccakTable { fn enabled(&self, meta: &mut VirtualCells) -> Expression { + // This is a boolean because of constraint "boolean is_final". meta.query_fixed(self.q_enable, Rotation::cur()) * meta.query_advice(self.is_final, Rotation::cur()) } @@ -281,9 +277,8 @@ impl QueryTable for KeccakTable { impl QueryTable for ExpTable { fn enabled(&self, meta: &mut VirtualCells) -> Expression { - // TODO: only is_step? - meta.query_fixed(self.q_enable, Rotation::cur()) - * meta.query_fixed(self.is_step, Rotation::cur()) + // is_step implies q_enable by fixed assignment. + meta.query_fixed(self.is_step, Rotation::cur()) } fn message(&self, meta: &mut VirtualCells) -> MsgExpr { @@ -357,9 +352,8 @@ impl QueryTable for ModExpTable { } impl QueryTable for EccTable { - fn enabled(&self, _meta: &mut VirtualCells) -> Expression { - // TODO: may need an enabled selector. Cannot use op_type because it is not boolean. - 1.expr() + fn enabled(&self, meta: &mut VirtualCells) -> Expression { + meta.query_fixed(self.q_enable, Rotation::cur()) } fn message(&self, meta: &mut VirtualCells) -> MsgExpr { diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index e90a9766f5..bee13a2912 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1687,13 +1687,13 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { Table::Rw, Table::Tx, Table::Bytecode, - //Table::Block, // TODO: enabled selector + Table::Block, Table::Copy, Table::Keccak, Table::Exp, Table::Sig, Table::ModExp, - //Table::Ecc, // TODO: enabled selector + Table::Ecc, Table::PowOfRand, ] .contains(&lookup.table()) @@ -1702,6 +1702,8 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { return; } + unreachable!("All lookups are implement by bus."); + let compressed_expr = self.split_expression( "Lookup compression", rlc::expr(&lookup.input_exprs(), self.challenges.lookup_input()), diff --git a/zkevm-circuits/src/pi_circuit.rs b/zkevm-circuits/src/pi_circuit.rs index 932e07e1f8..dc109ef2cb 100644 --- a/zkevm-circuits/src/pi_circuit.rs +++ b/zkevm-circuits/src/pi_circuit.rs @@ -1331,7 +1331,11 @@ impl PiCircuitConfig { let block_table_columns = >::advice_columns(&self.block_table); - for fixed in [self.q_block_tag, self.is_block_num_txs] { + for fixed in [ + self.q_block_tag, + self.is_block_num_txs, + self.block_table.q_enable, + ] { region.assign_fixed( || "block table all-zero row for fixed", fixed, @@ -1393,6 +1397,12 @@ impl PiCircuitConfig { .into_iter() .zip(tag.iter()) { + region.assign_fixed( + || format!("block table enabled {offset}"), + self.block_table.q_enable, + offset, + || Value::known(F::one()), + )?; region.assign_fixed( || format!("block table row {offset}"), self.block_table.tag, diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 60dd942e56..447a7298d5 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -1255,6 +1255,8 @@ impl From for usize { /// Table with Block header fields #[derive(Clone, Debug)] pub struct BlockTable { + /// Enabled. It is equivalent to tag != Null. + pub q_enable: Column, /// Tag pub tag: Column, /// Index @@ -1267,6 +1269,7 @@ impl BlockTable { /// Construct a new BlockTable pub fn construct(meta: &mut ConstraintSystem) -> Self { Self { + q_enable: meta.fixed_column(), tag: meta.fixed_column(), index: meta.advice_column(), value: meta.advice_column_in(SecondPhase), @@ -1304,6 +1307,12 @@ impl BlockTable { .count(); cum_num_txs += num_txs; for row in block_ctx.table_assignments(num_txs, cum_num_txs, 0, challenges) { + region.assign_fixed( + || format!("block table enabled {offset}"), + self.q_enable, + offset, + || Value::known(F::one()), + )?; region.assign_fixed( || format!("block table row {offset}"), self.tag, @@ -2374,6 +2383,8 @@ impl LookupTable for SigTable { /// - output1_rlc <- success {0, 1} #[derive(Clone, Copy, Debug)] pub struct EccTable { + /// Enabled. It is equivalent to op_type != 0. + pub q_enable: Column, /// Since the current design of the ECC circuit reserves fixed number of rows for EcAdd, EcMul /// and EcPairing ops respectively, we already know the `op_type` for each row. pub op_type: Column, @@ -2443,6 +2454,7 @@ impl EccTable { /// Construct the ECC table. pub(crate) fn construct(meta: &mut ConstraintSystem) -> Self { Self { + q_enable: meta.fixed_column(), op_type: meta.fixed_column(), is_valid: meta.advice_column(), arg1_rlc: meta.advice_column_in(SecondPhase), @@ -2545,6 +2557,12 @@ impl EccTable { || "ecc table dev load", |mut region| { for (i, row) in assignments.iter().enumerate() { + region.assign_fixed( + || format!("ecc table row = {i}, enabled"), + self.q_enable, + i, + || Value::known(F::one()), + )?; region.assign_fixed( || format!("ecc table row = {i}, op_type"), self.op_type, diff --git a/zkevm-circuits/src/tx_circuit.rs b/zkevm-circuits/src/tx_circuit.rs index 476a98f376..6563ad4f6b 100644 --- a/zkevm-circuits/src/tx_circuit.rs +++ b/zkevm-circuits/src/tx_circuit.rs @@ -2534,6 +2534,12 @@ impl TxCircuit { .enumerate() { let row = offset * 3 + j; + region.assign_fixed( + || format!("block table enabled"), + config.block_table.q_enable, + row, + || Value::known(F::one()), + )?; region.assign_fixed( || "block_table.tag", config.block_table.tag, From efad68bf3026267a0bac745fc7d71e019e960cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 8 Nov 2023 00:13:23 +0100 Subject: [PATCH 56/67] bus-auto: remove Execution lookups --- zkevm-circuits/src/evm_circuit.rs | 187 +++++++----------- zkevm-circuits/src/evm_circuit/execution.rs | 96 +-------- zkevm-circuits/src/evm_circuit/param.rs | 67 ------- zkevm-circuits/src/evm_circuit/util.rs | 11 +- .../evm_circuit/util/constraint_builder.rs | 41 +--- .../src/evm_circuit/util/instrumentation.rs | 37 ---- .../evm_circuit/util/math_gadget/test_util.rs | 23 +-- zkevm-circuits/src/super_circuit.rs | 19 +- 8 files changed, 92 insertions(+), 389 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 520739cb07..2f3a4a655c 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -48,41 +48,16 @@ use witness::Block; pub struct EvmCircuitConfig { fixed_table: [Column; 4], dual_byte_table: [Column; 2], + pow_of_rand_table: PowOfRandTable, bus_lookup: [BusLookupChip; 2], enable_bus_lookup: Column, pub(crate) execution: Box>, - // External tables - bytecode_table: BytecodeTable, - block_table: BlockTable, - copy_table: CopyTable, - keccak_table: KeccakTable, - exp_table: ExpTable, - sig_table: SigTable, - modexp_table: ModExpTable, - ecc_table: EccTable, - pow_of_rand_table: PowOfRandTable, } /// Circuit configuration arguments pub struct EvmCircuitConfigArgs { /// Challenge pub challenges: crate::util::Challenges>, - /// BytecodeTable - pub bytecode_table: BytecodeTable, - /// BlockTable - pub block_table: BlockTable, - /// CopyTable - pub copy_table: CopyTable, - /// KeccakTable - pub keccak_table: KeccakTable, - /// ExpTable - pub exp_table: ExpTable, - /// SigTable - pub sig_table: SigTable, - /// ModExpTable - pub modexp_table: ModExpTable, - /// Ecc Table. - pub ecc_table: EccTable, // Power of Randomness Table. pub pow_of_rand_table: PowOfRandTable, } @@ -117,14 +92,6 @@ impl EvmCircuitConfig { bus_builder: &mut BusBuilder>, EvmCircuitConfigArgs { challenges, - bytecode_table, - block_table, - copy_table, - keccak_table, - exp_table, - sig_table, - modexp_table, - ecc_table, pow_of_rand_table, }: EvmCircuitConfigArgs, ) -> Self { @@ -140,35 +107,13 @@ impl EvmCircuitConfig { &fixed_table, ); - let execution = Box::new(ExecutionConfig::configure( - meta, - challenges, - bus_builder, - &bytecode_table, - &block_table, - ©_table, - &keccak_table, - &exp_table, - &sig_table, - &modexp_table, - &ecc_table, - &pow_of_rand_table, - )); + let execution = Box::new(ExecutionConfig::configure(meta, challenges, bus_builder)); meta.annotate_lookup_any_column(dual_byte_table[0], || "dual_byte_table_0"); meta.annotate_lookup_any_column(dual_byte_table[1], || "dual_byte_table_1"); fixed_table.iter().enumerate().for_each(|(idx, &col)| { meta.annotate_lookup_any_column(col, || format!("fix_table_{idx}")) }); - bytecode_table.annotate_columns(meta); - block_table.annotate_columns(meta); - copy_table.annotate_columns(meta); - keccak_table.annotate_columns(meta); - exp_table.annotate_columns(meta); - sig_table.annotate_columns(meta); - modexp_table.annotate_columns(meta); - ecc_table.annotate_columns(meta); - pow_of_rand_table.annotate_columns(meta); Self { fixed_table, @@ -176,14 +121,6 @@ impl EvmCircuitConfig { bus_lookup, enable_bus_lookup, execution, - bytecode_table, - block_table, - copy_table, - keccak_table, - exp_table, - sig_table, - modexp_table, - ecc_table, pow_of_rand_table, } } @@ -582,6 +519,14 @@ impl Circuit for EvmCircuit { Challenges, RwTable, TxTable, + BytecodeTable, + BlockTable, + CopyTable, + KeccakTable, + ExpTable, + SigTable, + ModExpTable, + EccTable, ); type FloorPlanner = SimpleFloorPlanner; @@ -607,19 +552,22 @@ impl Circuit for EvmCircuit { let modexp_table = ModExpTable::construct(meta); let ecc_table = EccTable::construct(meta); let pow_of_rand_table = PowOfRandTable::construct(meta, &challenges_expr); + + bytecode_table.annotate_columns(meta); + block_table.annotate_columns(meta); + copy_table.annotate_columns(meta); + keccak_table.annotate_columns(meta); + exp_table.annotate_columns(meta); + sig_table.annotate_columns(meta); + modexp_table.annotate_columns(meta); + ecc_table.annotate_columns(meta); + pow_of_rand_table.annotate_columns(meta); + let config = EvmCircuitConfig::new( meta, &mut bus_builder, EvmCircuitConfigArgs { challenges: challenges_expr, - bytecode_table: bytecode_table.clone(), - block_table: block_table.clone(), - copy_table: copy_table.clone(), - keccak_table: keccak_table.clone(), - exp_table: exp_table.clone(), - sig_table: sig_table.clone(), - modexp_table: modexp_table.clone(), - ecc_table: ecc_table.clone(), pow_of_rand_table: pow_of_rand_table.clone(), }, ); @@ -639,7 +587,22 @@ impl Circuit for EvmCircuit { &pow_of_rand_table, ); let bus = BusConfig::new(meta, &bus_builder.build()); - (config, bus, evm_lookups, challenges, rw_table, tx_table) + ( + config, + bus, + evm_lookups, + challenges, + rw_table, + tx_table, + bytecode_table, + block_table, + copy_table, + keccak_table, + exp_table, + sig_table, + modexp_table, + ecc_table, + ) } fn synthesize( @@ -650,7 +613,22 @@ impl Circuit for EvmCircuit { let block = self.block.as_ref().unwrap(); let num_rows = Self::get_num_rows_required(block); - let (config, bus, evm_lookups, challenges, rw_table, tx_table) = config; + let ( + config, + bus, + evm_lookups, + challenges, + rw_table, + tx_table, + bytecode_table, + block_table, + copy_table, + keccak_table, + exp_table, + sig_table, + modexp_table, + ecc_table, + ) = config; let challenges = challenges.values(&layouter); let mut bus_assigner = @@ -675,26 +653,19 @@ impl Circuit for EvmCircuit { challenges.evm_word(), )?; - config - .bytecode_table - .dev_load(&mut layouter, block.bytecodes.values(), &challenges)?; - config - .block_table - .dev_load(&mut layouter, &block.context, &block.txs, &challenges)?; - config - .copy_table - .dev_load(&mut layouter, block, &challenges)?; - config - .keccak_table - .dev_load(&mut layouter, &block.sha3_inputs, &challenges)?; - config.exp_table.dev_load(&mut layouter, block)?; - config - .sig_table - .dev_load(&mut layouter, block, &challenges)?; - config - .modexp_table - .dev_load(&mut layouter, &block.get_big_modexp())?; - config.ecc_table.dev_load( + bytecode_table.dev_load(&mut layouter, block.bytecodes.values(), &challenges)?; + + block_table.dev_load(&mut layouter, &block.context, &block.txs, &challenges)?; + + copy_table.dev_load(&mut layouter, block, &challenges)?; + + keccak_table.dev_load(&mut layouter, &block.sha3_inputs, &challenges)?; + exp_table.dev_load(&mut layouter, block)?; + + sig_table.dev_load(&mut layouter, block, &challenges)?; + + modexp_table.dev_load(&mut layouter, &block.get_big_modexp())?; + ecc_table.dev_load( &mut layouter, block.circuits_params.max_ec_ops, &block.get_ec_add_ops(), @@ -722,7 +693,7 @@ mod evm_circuit_stats { use crate::{ evm_circuit::{ param::{ - LOOKUP_CONFIG, N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, N_PHASE2_COLUMNS, + N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, N_PHASE2_COLUMNS, N_PHASE2_COPY_COLUMNS, N_PHASE3_COLUMNS, }, step::ExecutionState, @@ -913,29 +884,7 @@ mod evm_circuit_stats { storage_perm_2, N_PHASE2_COPY_COLUMNS, byte_lookup, - N_BYTE_LOOKUPS, - fixed_table, - LOOKUP_CONFIG[0].1, - tx_table, - LOOKUP_CONFIG[1].1, - rw_table, - LOOKUP_CONFIG[2].1, - bytecode_table, - LOOKUP_CONFIG[3].1, - block_table, - LOOKUP_CONFIG[4].1, - copy_table, - LOOKUP_CONFIG[5].1, - keccak_table, - LOOKUP_CONFIG[6].1, - exp_table, - LOOKUP_CONFIG[7].1, - sig_table, - LOOKUP_CONFIG[8].1, - ecc_table, - LOOKUP_CONFIG[9].1, - pow_of_rand_table, - LOOKUP_CONFIG[10].1 + N_BYTE_LOOKUPS ); } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 26a83a9b5d..529a498e83 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -1,10 +1,5 @@ use super::{ - param::{ - BLOCK_TABLE_LOOKUPS, BYTECODE_TABLE_LOOKUPS, COPY_TABLE_LOOKUPS, ECC_TABLE_LOOKUPS, - EXP_TABLE_LOOKUPS, FIXED_TABLE_LOOKUPS, KECCAK_TABLE_LOOKUPS, MODEXP_TABLE_LOOKUPS, - N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, POW_OF_RAND_TABLE_LOOKUPS, - RW_TABLE_LOOKUPS, SIG_TABLE_LOOKUPS, TX_TABLE_LOOKUPS, - }, + param::{N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS}, util::{ instrumentation::Instrument, CachedRegion, CellManager, Inverter, StepBusOp, StoredExpression, @@ -13,9 +8,9 @@ use super::{ }; use crate::{ evm_circuit::{ - param::{EVM_LOOKUP_COLS, MAX_STEP_HEIGHT, N_PHASE2_COLUMNS, N_PHASE3_COLUMNS, STEP_WIDTH}, + param::{MAX_STEP_HEIGHT, N_PHASE2_COLUMNS, N_PHASE3_COLUMNS, STEP_WIDTH}, step::{ExecutionState, Step}, - table::{MsgExpr, MsgF, Table}, + table::{MsgExpr, MsgF}, util::{ constraint_builder::{ BaseConstraintBuilder, ConstrainBuilderCommon, EVMConstraintBuilder, @@ -24,7 +19,7 @@ use crate::{ }, witness::{Block, Call, ExecStep, Transaction}, }, - table::{LookupTable, RwTableTag, TxReceiptFieldTag}, + table::{RwTableTag, TxReceiptFieldTag}, util::{query_expression, Challenges, Expr}, }; use bus_mapping::util::read_env_var; @@ -376,21 +371,11 @@ pub(crate) struct ExecutionConfig { } impl ExecutionConfig { - #[allow(clippy::too_many_arguments)] #[allow(clippy::redundant_closure_call)] pub(crate) fn configure( meta: &mut ConstraintSystem, challenges: Challenges>, bus_builder: &mut BusBuilder>, - bytecode_table: &dyn LookupTable, - block_table: &dyn LookupTable, - copy_table: &dyn LookupTable, - keccak_table: &dyn LookupTable, - exp_table: &dyn LookupTable, - sig_table: &dyn LookupTable, - modexp_table: &dyn LookupTable, - ecc_table: &dyn LookupTable, - pow_of_rand_table: &dyn LookupTable, ) -> Self { let mut instrument = Instrument::default(); let q_usable = meta.complex_selector(); @@ -406,9 +391,9 @@ impl ExecutionConfig { .iter() .enumerate() .map(|(n, _)| { - if n < EVM_LOOKUP_COLS + N_PHASE3_COLUMNS { + if n < N_PHASE3_COLUMNS { meta.advice_column_in(ThirdPhase) - } else if n < EVM_LOOKUP_COLS + N_PHASE3_COLUMNS + N_PHASE2_COLUMNS { + } else if n < N_PHASE3_COLUMNS + N_PHASE2_COLUMNS { meta.advice_column_in(SecondPhase) } else { meta.advice_column_in(FirstPhase) @@ -661,20 +646,6 @@ impl ExecutionConfig { instrument, }; - Self::configure_lookup( - meta, - bytecode_table, - block_table, - copy_table, - keccak_table, - exp_table, - sig_table, - modexp_table, - ecc_table, - pow_of_rand_table, - &challenges, - &cell_manager, - ); config } @@ -972,49 +943,6 @@ impl ExecutionConfig { BusPortMulti::connect(meta, bus_builder, q_usable, ops) } - #[allow(clippy::too_many_arguments)] - fn configure_lookup( - meta: &mut ConstraintSystem, - bytecode_table: &dyn LookupTable, - block_table: &dyn LookupTable, - copy_table: &dyn LookupTable, - keccak_table: &dyn LookupTable, - exp_table: &dyn LookupTable, - sig_table: &dyn LookupTable, - modexp_table: &dyn LookupTable, - ecc_table: &dyn LookupTable, - pow_of_rand_table: &dyn LookupTable, - challenges: &Challenges>, - cell_manager: &CellManager, - ) { - for column in cell_manager.columns().iter() { - if let CellType::Lookup(table) = column.cell_type { - let name = format!("{table:?}"); - meta.lookup_any(Box::leak(name.into_boxed_str()), |meta| { - let table_expressions = match table { - Table::Fixed => unreachable!("Fixed table is on the bus"), - Table::Tx => unreachable!("TX table is on the bus"), - Table::Rw => unreachable!("RW table is on the bus"), - Table::Bytecode => bytecode_table, - Table::Block => block_table, - Table::Copy => copy_table, - Table::Keccak => keccak_table, - Table::Exp => exp_table, - Table::Sig => sig_table, - Table::ModExp => modexp_table, - Table::Ecc => ecc_table, - Table::PowOfRand => pow_of_rand_table, - } - .table_exprs(meta); - vec![( - column.expr(), - rlc::expr(&table_expressions, challenges.lookup_input()), - )] - }); - } - } - } - pub fn get_num_rows_required(&self, block: &Block) -> usize { // Start at 1 so we can be sure there is an unused `next` row available let mut num_rows = 1; @@ -1293,18 +1221,6 @@ impl ExecutionConfig { fn annotate_circuit(&self, region: &mut Region) { let groups = [ - ("EVM_lookup_fixed", FIXED_TABLE_LOOKUPS), - ("EVM_lookup_tx", TX_TABLE_LOOKUPS), - ("EVM_lookup_rw", RW_TABLE_LOOKUPS), - ("EVM_lookup_bytecode", BYTECODE_TABLE_LOOKUPS), - ("EVM_lookup_block", BLOCK_TABLE_LOOKUPS), - ("EVM_lookup_copy", COPY_TABLE_LOOKUPS), - ("EVM_lookup_keccak", KECCAK_TABLE_LOOKUPS), - ("EVM_lookup_exp", EXP_TABLE_LOOKUPS), - ("EVM_lookup_sig", SIG_TABLE_LOOKUPS), - ("EVM_lookup_modexp", MODEXP_TABLE_LOOKUPS), - ("EVM_lookup_ecc", ECC_TABLE_LOOKUPS), - ("EVM_lookup_pow_of_rand", POW_OF_RAND_TABLE_LOOKUPS), ("EVM_adv_phase2", N_PHASE2_COLUMNS), ("EVM_adv_phase3", N_PHASE3_COLUMNS), ("EVM_copy", N_COPY_COLUMNS), diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index 9cc9f09550..0142ad0bf7 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -1,4 +1,3 @@ -use super::table::Table; use crate::evm_circuit::{step::ExecutionState, EvmCircuit}; use halo2_proofs::{ halo2curves::bn256::Fr, @@ -23,7 +22,6 @@ pub const N_PHASE3_COLUMNS: usize = 12; /// Number of Advice Phase1 columns in the EVM circuit pub(crate) const N_PHASE1_COLUMNS: usize = STEP_WIDTH - - EVM_LOOKUP_COLS - N_PHASE3_COLUMNS - N_PHASE2_COLUMNS - N_COPY_COLUMNS @@ -36,71 +34,6 @@ pub(crate) const N_PHASE2_COPY_COLUMNS: usize = 1; pub(crate) const N_BYTE_LOOKUPS: usize = 26; -/// Amount of lookup columns in the EVM circuit dedicated to lookups. -pub(crate) const EVM_LOOKUP_COLS: usize = FIXED_TABLE_LOOKUPS - + TX_TABLE_LOOKUPS - + RW_TABLE_LOOKUPS - + BYTECODE_TABLE_LOOKUPS - + BLOCK_TABLE_LOOKUPS - + COPY_TABLE_LOOKUPS - + KECCAK_TABLE_LOOKUPS - + EXP_TABLE_LOOKUPS - + SIG_TABLE_LOOKUPS - + MODEXP_TABLE_LOOKUPS - + ECC_TABLE_LOOKUPS - + POW_OF_RAND_TABLE_LOOKUPS; - -/// Lookups done per row. -pub(crate) const LOOKUP_CONFIG: &[(Table, usize)] = &[ - (Table::Fixed, FIXED_TABLE_LOOKUPS), - (Table::Tx, TX_TABLE_LOOKUPS), - (Table::Rw, RW_TABLE_LOOKUPS), - (Table::Bytecode, BYTECODE_TABLE_LOOKUPS), - (Table::Block, BLOCK_TABLE_LOOKUPS), - (Table::Copy, COPY_TABLE_LOOKUPS), - (Table::Keccak, KECCAK_TABLE_LOOKUPS), - (Table::Exp, EXP_TABLE_LOOKUPS), - (Table::Sig, SIG_TABLE_LOOKUPS), - (Table::ModExp, MODEXP_TABLE_LOOKUPS), - (Table::Ecc, ECC_TABLE_LOOKUPS), - (Table::PowOfRand, POW_OF_RAND_TABLE_LOOKUPS), -]; - -/// Fixed Table lookups done in EVMCircuit -pub const FIXED_TABLE_LOOKUPS: usize = 0; - -/// Tx Table lookups done in EVMCircuit -pub const TX_TABLE_LOOKUPS: usize = 0; - -/// Rw Table lookups done in EVMCircuit -pub const RW_TABLE_LOOKUPS: usize = 0; - -/// Bytecode Table lookups done in EVMCircuit -pub const BYTECODE_TABLE_LOOKUPS: usize = 1; - -/// Block Table lookups done in EVMCircuit -pub const BLOCK_TABLE_LOOKUPS: usize = 1; - -/// Copy Table lookups done in EVMCircuit -pub const COPY_TABLE_LOOKUPS: usize = 1; - -/// Keccak Table lookups done in EVMCircuit -pub const KECCAK_TABLE_LOOKUPS: usize = 1; - -/// Exp Table lookups done in EVMCircuit -pub const EXP_TABLE_LOOKUPS: usize = 1; - -/// Sig Table lookups done in EVMCircuit -pub const SIG_TABLE_LOOKUPS: usize = 1; - -/// ModExp Table lookups done in EVMCircuit -pub const MODEXP_TABLE_LOOKUPS: usize = 1; -/// Ecc Table lookups done in EVMCircuit -pub const ECC_TABLE_LOOKUPS: usize = 1; - -/// Power of Randomness lookups done from EVM Circuit. -pub const POW_OF_RAND_TABLE_LOOKUPS: usize = 1; - /// Maximum number of bytes that an integer can fit in field without wrapping /// around. pub(crate) const MAX_N_BYTES_INTEGER: usize = 31; diff --git a/zkevm-circuits/src/evm_circuit/util.rs b/zkevm-circuits/src/evm_circuit/util.rs index f81af48149..ddecb94c0c 100644 --- a/zkevm-circuits/src/evm_circuit/util.rs +++ b/zkevm-circuits/src/evm_circuit/util.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::{ param::{ - LOOKUP_CONFIG, N_BYTES_MEMORY_ADDRESS, N_BYTES_U64, N_BYTE_LOOKUPS, N_COPY_COLUMNS, + N_BYTES_MEMORY_ADDRESS, N_BYTES_U64, N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE2_COLUMNS, N_PHASE2_COPY_COLUMNS, N_PHASE3_COLUMNS, }, table::Table, @@ -373,7 +373,6 @@ pub(crate) enum CellType { StoragePermutation, StoragePermutationPhase2, LookupByte, - Lookup(Table), } impl CellType { @@ -454,14 +453,6 @@ impl CellManager { let mut column_idx = 0; - // Mark columns used for lookups in Phase3 - for &(table, count) in LOOKUP_CONFIG { - for _ in 0usize..count { - columns[column_idx].cell_type = CellType::Lookup(table); - column_idx += 1; - } - } - // Mark columns used for Phase3. for _ in 0..N_PHASE3_COLUMNS { columns[column_idx].cell_type = CellType::StoragePhase3; diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index bee13a2912..83cfd5cf61 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1675,41 +1675,12 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { } } - pub(crate) fn add_lookup(&mut self, name: &str, lookup: Lookup) { + pub(crate) fn add_lookup(&mut self, _name: &str, lookup: Lookup) { let lookup = match self.condition_expr_opt() { Some(condition) => lookup.conditional(condition), None => lookup, }; - - // TODO: support all types. - if [ - Table::Fixed, - Table::Rw, - Table::Tx, - Table::Bytecode, - Table::Block, - Table::Copy, - Table::Keccak, - Table::Exp, - Table::Sig, - Table::ModExp, - Table::Ecc, - Table::PowOfRand, - ] - .contains(&lookup.table()) - { - self.add_bus_lookup(lookup); - return; - } - - unreachable!("All lookups are implement by bus."); - - let compressed_expr = self.split_expression( - "Lookup compression", - rlc::expr(&lookup.input_exprs(), self.challenges.lookup_input()), - MAX_DEGREE - IMPLICIT_DEGREE, - ); - self.store_expression(name, compressed_expr, CellType::Lookup(lookup.table())); + self.add_bus_lookup(lookup); } fn add_bus_lookup(&mut self, lookup: Lookup) { @@ -1755,13 +1726,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { let stored_expression = self.find_stored_expression(&expr, cell_type); match stored_expression { - Some(stored_expression) => { - debug_assert!( - !matches!(cell_type, CellType::Lookup(_)), - "The same lookup is done multiple times", - ); - stored_expression.cell.expr() - } + Some(stored_expression) => stored_expression.cell.expr(), None => { // Even if we're building expressions for the next step, // these intermediate values need to be stored in the current step. diff --git a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs index 46586f8ce6..a947b06ab6 100644 --- a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs +++ b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs @@ -1,6 +1,5 @@ use crate::evm_circuit::{ step::ExecutionState, - table::Table, util::{constraint_builder::EVMConstraintBuilder, CellType}, }; use halo2_proofs::arithmetic::FieldExt; @@ -82,42 +81,6 @@ impl Instrument { CellType::LookupByte => { report.byte_lookup = data_entry; } - CellType::Lookup(Table::Fixed) => { - report.fixed_table = data_entry; - } - CellType::Lookup(Table::Tx) => { - report.tx_table = data_entry; - } - CellType::Lookup(Table::Rw) => { - report.rw_table = data_entry; - } - CellType::Lookup(Table::Bytecode) => { - report.bytecode_table = data_entry; - } - CellType::Lookup(Table::Block) => { - report.block_table = data_entry; - } - CellType::Lookup(Table::Copy) => { - report.copy_table = data_entry; - } - CellType::Lookup(Table::Keccak) => { - report.keccak_table = data_entry; - } - CellType::Lookup(Table::Exp) => { - report.exp_table = data_entry; - } - CellType::Lookup(Table::Sig) => { - report.sig_table = data_entry; - } - CellType::Lookup(Table::ModExp) => { - report.modexp_table = data_entry; - } - CellType::Lookup(Table::Ecc) => { - report.ecc_table = data_entry; - } - CellType::Lookup(Table::PowOfRand) => { - report.pow_of_rand_table = data_entry; - } } } report_collection.push(report); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs index 8615c936d2..cdba2e88d0 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs @@ -9,7 +9,7 @@ use crate::{ table::{FixedTableTag, Table}, util::{ constraint_builder::EVMConstraintBuilder, rlc, CachedRegion, CellType, Expr, - StoredExpression, LOOKUP_CONFIG, + StoredExpression, }, Advice, Column, Fixed, }, @@ -118,14 +118,13 @@ impl> Circuit for UnitTestMathGadgetBaseC let q_usable = meta.selector(); let fixed_table = [(); 4].map(|_| meta.fixed_column()); - let lookup_column_count: usize = LOOKUP_CONFIG.iter().map(|(_, count)| *count).sum(); let advices = [(); STEP_WIDTH] .iter() .enumerate() .map(|(n, _)| { - if n < lookup_column_count + N_PHASE3_COLUMNS { + if n < N_PHASE3_COLUMNS { meta.advice_column_in(ThirdPhase) - } else if n < lookup_column_count + N_PHASE3_COLUMNS + N_PHASE2_COLUMNS { + } else if n < N_PHASE3_COLUMNS + N_PHASE2_COLUMNS { meta.advice_column_in(SecondPhase) } else { meta.advice_column_in(FirstPhase) @@ -156,22 +155,6 @@ impl> Circuit for UnitTestMathGadgetBaseC }); } - let cell_manager = step_curr.cell_manager.clone(); - for column in cell_manager.columns().iter() { - if let CellType::Lookup(table) = column.cell_type { - if table == Table::Fixed { - let name = format!("{table:?}"); - meta.lookup_any(Box::leak(name.into_boxed_str()), |meta| { - let table_expressions = fixed_table.table_exprs(meta); - vec![( - column.expr(), - rlc::expr(&table_expressions, challenges_exprs.lookup_input()), - )] - }); - } - } - } - ( UnitTestMathGadgetBaseCircuitConfig:: { q_usable, diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 88d73218ca..8c9c7a4f27 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -209,12 +209,23 @@ impl SubCircuitConfig for SuperCircuitConfig { log_circuit_info(meta, "ecc table"); let pow_of_rand_table = PowOfRandTable::construct(meta, &challenges_expr); log_circuit_info(meta, "power of randomness table"); + // TODO: move pow_of_rand_table to EVMCircuit. let u8_table = U8Table::construct(meta); log_circuit_info(meta, "u8 table"); let u16_table = U16Table::construct(meta); log_circuit_info(meta, "u16 table"); + bytecode_table.annotate_columns(meta); + block_table.annotate_columns(meta); + copy_table.annotate_columns(meta); + keccak_table.annotate_columns(meta); + exp_table.annotate_columns(meta); + sig_table.annotate_columns(meta); + modexp_table.annotate_columns(meta); + ecc_table.annotate_columns(meta); + pow_of_rand_table.annotate_columns(meta); + assert!(get_num_rows_per_round() == 12); let keccak_circuit = KeccakCircuitConfig::new( meta, @@ -343,14 +354,6 @@ impl SubCircuitConfig for SuperCircuitConfig { &mut bus_builder, EvmCircuitConfigArgs { challenges: challenges_expr.clone(), - bytecode_table: bytecode_table.clone(), - block_table: block_table.clone(), - copy_table: copy_table.clone(), - keccak_table: keccak_table.clone(), - exp_table: exp_table.clone(), - sig_table: sig_table.clone(), - modexp_table: modexp_table.clone(), - ecc_table: ecc_table.clone(), pow_of_rand_table: pow_of_rand_table.clone(), }, ); From 38805ca0033de5295ae7d089d5d6ba07adfd8faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 8 Nov 2023 00:41:45 +0100 Subject: [PATCH 57/67] bus-auto: remove provide_msg mechanism --- zkevm-circuits/src/copy_circuit/dev.rs | 2 - zkevm-circuits/src/evm_circuit.rs | 7 -- zkevm-circuits/src/evm_circuit/param.rs | 2 +- zkevm-circuits/src/evm_circuit/util.rs | 9 +- .../evm_circuit/util/constraint_builder.rs | 2 +- .../evm_circuit/util/math_gadget/test_util.rs | 18 ++-- zkevm-circuits/src/pi_circuit/dev.rs | 1 - zkevm-circuits/src/state_circuit.rs | 25 +---- zkevm-circuits/src/table.rs | 93 +++++++------------ zkevm-circuits/src/tx_circuit.rs | 45 +-------- 10 files changed, 53 insertions(+), 151 deletions(-) diff --git a/zkevm-circuits/src/copy_circuit/dev.rs b/zkevm-circuits/src/copy_circuit/dev.rs index 4b1c5f3308..239d847ecb 100644 --- a/zkevm-circuits/src/copy_circuit/dev.rs +++ b/zkevm-circuits/src/copy_circuit/dev.rs @@ -53,7 +53,6 @@ impl Circuit for CopyCircuit { config.0.tx_table.load( &mut layouter, - |_, _| {}, &self.external_data.txs, self.external_data.max_txs, self.external_data.max_calldata, @@ -63,7 +62,6 @@ impl Circuit for CopyCircuit { config.0.rw_table.load( &mut layouter, - |_, _| {}, &self.external_data.rws.table_assignments(), self.external_data.max_rws, challenge_values.evm_word(), diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 2f3a4a655c..616032fc4c 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -636,7 +636,6 @@ impl Circuit for EvmCircuit { tx_table.load( &mut layouter, - |_offset, _message| {}, &block.txs, block.circuits_params.max_txs, block.circuits_params.max_calldata, @@ -647,23 +646,17 @@ impl Circuit for EvmCircuit { block.rws.check_rw_counter_sanity(); rw_table.load( &mut layouter, - |_offset, _message| {}, &block.rws.table_assignments(), block.circuits_params.max_rws, challenges.evm_word(), )?; bytecode_table.dev_load(&mut layouter, block.bytecodes.values(), &challenges)?; - block_table.dev_load(&mut layouter, &block.context, &block.txs, &challenges)?; - copy_table.dev_load(&mut layouter, block, &challenges)?; - keccak_table.dev_load(&mut layouter, &block.sha3_inputs, &challenges)?; exp_table.dev_load(&mut layouter, block)?; - sig_table.dev_load(&mut layouter, block, &challenges)?; - modexp_table.dev_load(&mut layouter, &block.get_big_modexp())?; ecc_table.dev_load( &mut layouter, diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index 0142ad0bf7..2e45f38235 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -6,7 +6,7 @@ use halo2_proofs::{ use std::collections::HashMap; // Step dimension -pub(crate) const STEP_WIDTH: usize = 130; +pub(crate) const STEP_WIDTH: usize = 121; /// Step height pub const MAX_STEP_HEIGHT: usize = 21; /// The height of the state of a step, used by gates that connect two diff --git a/zkevm-circuits/src/evm_circuit/util.rs b/zkevm-circuits/src/evm_circuit/util.rs index ddecb94c0c..7fb95c4acc 100644 --- a/zkevm-circuits/src/evm_circuit/util.rs +++ b/zkevm-circuits/src/evm_circuit/util.rs @@ -1,10 +1,7 @@ use crate::{ - evm_circuit::{ - param::{ - N_BYTES_MEMORY_ADDRESS, N_BYTES_U64, N_BYTE_LOOKUPS, N_COPY_COLUMNS, - N_PHASE2_COLUMNS, N_PHASE2_COPY_COLUMNS, N_PHASE3_COLUMNS, - }, - table::Table, + evm_circuit::param::{ + N_BYTES_MEMORY_ADDRESS, N_BYTES_U64, N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE2_COLUMNS, + N_PHASE2_COPY_COLUMNS, N_PHASE3_COLUMNS, }, table::RwTableTag, util::{query_expression, Challenges, Expr}, diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 83cfd5cf61..bee25c7642 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -2,7 +2,7 @@ use crate::{ evm_circuit::{ param::STACK_CAPACITY, step::{ExecutionState, Step}, - table::{FixedTableTag, Lookup, MsgExpr, RwValues, Table}, + table::{FixedTableTag, Lookup, MsgExpr, RwValues}, util::{Cell, RandomLinearCombination, Word}, }, table::{ diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs index cdba2e88d0..4df280c5fd 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs @@ -2,18 +2,12 @@ use itertools::Itertools; use std::marker::PhantomData; use strum::IntoEnumIterator; -use crate::{ - evm_circuit::{ - param::{MAX_STEP_HEIGHT, N_PHASE2_COLUMNS, N_PHASE3_COLUMNS, STEP_WIDTH}, - step::{ExecutionState, Step}, - table::{FixedTableTag, Table}, - util::{ - constraint_builder::EVMConstraintBuilder, rlc, CachedRegion, CellType, Expr, - StoredExpression, - }, - Advice, Column, Fixed, - }, - table::LookupTable, +use crate::evm_circuit::{ + param::{MAX_STEP_HEIGHT, N_PHASE2_COLUMNS, N_PHASE3_COLUMNS, STEP_WIDTH}, + step::{ExecutionState, Step}, + table::FixedTableTag, + util::{constraint_builder::EVMConstraintBuilder, CachedRegion, StoredExpression}, + Advice, Column, Fixed, }; #[cfg(not(feature = "onephase"))] diff --git a/zkevm-circuits/src/pi_circuit/dev.rs b/zkevm-circuits/src/pi_circuit/dev.rs index cd9816b186..51fee3dd05 100644 --- a/zkevm-circuits/src/pi_circuit/dev.rs +++ b/zkevm-circuits/src/pi_circuit/dev.rs @@ -92,7 +92,6 @@ impl StateCircuitConfig { fn assign_par( &self, layouter: &mut impl Layouter, - mut provide_msg: impl FnMut(usize, MsgF) -> (), rows: &[Rw], n_rows: usize, // 0 means dynamically calculated from `rows`. updates: &MptUpdates, @@ -695,13 +694,6 @@ impl StateCircuitConfig { padding_length ); - randomness.map(|challenge| { - // Only in second phase. - for (offset, row) in rows.iter().enumerate() { - provide_msg(offset, MsgF::rw(row.table_assignment_aux(challenge))); - } - }); - // Assigning to same columns in different regions should be avoided. // Here we use one single region to assign `overrides` to both rw table and // other parts. @@ -947,19 +939,6 @@ impl SubCircuit for StateCircuit { config: &Self::Config, challenges: &Challenges>, layouter: &mut impl Layouter, - ) -> Result<(), Error> { - self.synthesize_sub2(config, challenges, layouter, |_, _| {}) - } -} - -impl StateCircuit { - /// Make the assignments to the StateCircuit - pub fn synthesize_sub2( - &self, - config: &StateCircuitConfig, - challenges: &Challenges>, - layouter: &mut impl Layouter, - mut provide_msg: impl FnMut(usize, MsgF) -> (), ) -> Result<(), Error> { config.load_aux_tables(layouter)?; @@ -985,7 +964,6 @@ impl StateCircuit { if is_parallel_assignment { return config.assign_par( layouter, - provide_msg, &self.rows, self.n_rows, &self.updates, @@ -1017,7 +995,6 @@ impl StateCircuit { } config.rw_table.load_with_region( &mut region, - &mut provide_msg, &self.rows, self.n_rows, randomness, diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 447a7298d5..0d75cd47de 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2,12 +2,9 @@ use crate::{ copy_circuit::util::number_or_hash_to_field, - evm_circuit::{ - table::MsgF, - util::{ - constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, - rlc, - }, + evm_circuit::util::{ + constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, + rlc, }, exp_circuit::param::{OFFSET_INCREMENT, ROWS_PER_STEP}, impl_expr, @@ -226,7 +223,6 @@ impl TxTable { pub fn load( &self, layouter: &mut impl Layouter, - mut provide_msg: impl FnMut(usize, MsgF) -> (), txs: &[Transaction], max_txs: usize, max_calldata: usize, @@ -249,47 +245,41 @@ impl TxTable { ); } - let mut assign_row = - |region: &mut Region<'_, F>, - offset: usize, - q_enable: Column, - advice_columns: &[Column], - tag: &Column, - row: &[Value; 4], - msg: &str| - -> Result, Error> { - let mut value_cell = None; - for (index, column) in advice_columns.iter().enumerate() { - let cell = region.assign_advice( - || format!("tx table {msg} row {offset}"), - *column, - offset, - || row[if index > 0 { index + 1 } else { index }], - )?; - // tx_id, index, value - if index == 2 { - value_cell = Some(cell); - } - } - region.assign_fixed( - || format!("tx table q_enable row {offset}"), - q_enable, - offset, - || Value::known(F::one()), - )?; - region.assign_fixed( + let assign_row = |region: &mut Region<'_, F>, + offset: usize, + q_enable: Column, + advice_columns: &[Column], + tag: &Column, + row: &[Value; 4], + msg: &str| + -> Result, Error> { + let mut value_cell = None; + for (index, column) in advice_columns.iter().enumerate() { + let cell = region.assign_advice( || format!("tx table {msg} row {offset}"), - *tag, + *column, offset, - || row[1], + || row[if index > 0 { index + 1 } else { index }], )?; - row[0].zip(row[1]).zip(row[2]).zip(row[3]).map( - |(((id, field_tag), index), value)| { - provide_msg(offset, MsgF::tx(id, field_tag, index, value)); - }, - ); - Ok(value_cell.unwrap()) - }; + // tx_id, index, value + if index == 2 { + value_cell = Some(cell); + } + } + region.assign_fixed( + || format!("tx table q_enable row {offset}"), + q_enable, + offset, + || Value::known(F::one()), + )?; + region.assign_fixed( + || format!("tx table {msg} row {offset}"), + *tag, + offset, + || row[1], + )?; + Ok(value_cell.unwrap()) + }; layouter.assign_region( || "tx table", @@ -665,7 +655,6 @@ impl RwTable { pub fn load( &self, layouter: &mut impl Layouter, - mut provide_msg: impl FnMut(usize, MsgF) -> (), rws: &[Rw], n_rows: usize, challenges: Value, @@ -673,29 +662,19 @@ impl RwTable { assign_global( layouter, || "rw table", - |mut region| { - self.load_with_region(&mut region, &mut provide_msg, rws, n_rows, challenges) - }, + |mut region| self.load_with_region(&mut region, rws, n_rows, challenges), ) } pub(crate) fn load_with_region( &self, region: &mut Region<'_, F>, - provide_msg: &mut impl FnMut(usize, MsgF) -> (), rws: &[Rw], n_rows: usize, challenges: Value, ) -> Result<(), Error> { let (rows, _) = RwMap::table_assignments_prepad(rws, n_rows); - challenges.map(|challenge| { - // Only in second phase. - for (offset, row) in rows.iter().enumerate() { - provide_msg(offset, MsgF::rw(row.table_assignment_aux(challenge))); - } - }); - for (offset, row) in rows.iter().enumerate() { self.assign(region, offset, &row.table_assignment(challenges))?; } diff --git a/zkevm-circuits/src/tx_circuit.rs b/zkevm-circuits/src/tx_circuit.rs index 6563ad4f6b..b3f2027801 100644 --- a/zkevm-circuits/src/tx_circuit.rs +++ b/zkevm-circuits/src/tx_circuit.rs @@ -13,10 +13,7 @@ mod test; pub use dev::TxCircuitTester as TestTxCircuit; use crate::{ - evm_circuit::{ - table::MsgF, - util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, - }, + evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, sig_circuit::SigCircuit, table::{ BlockContextFieldTag::{CumNumTxs, NumAllTxs, NumTxs}, @@ -1697,15 +1694,9 @@ impl TxCircuitConfig { } /// Assign 1st empty row with tag = Null - fn assign_null_row( - &self, - region: &mut Region<'_, F>, - provide_msg: &mut impl FnMut(usize, MsgF) -> (), - offset: &mut usize, - ) -> Result<(), Error> { + fn assign_null_row(&self, region: &mut Region<'_, F>, offset: &mut usize) -> Result<(), Error> { self.assign_common_part( region, - provide_msg, *offset, None, 1, @@ -1725,7 +1716,6 @@ impl TxCircuitConfig { fn assign_fixed_rows( &self, region: &mut Region<'_, F>, - provide_msg: &mut impl FnMut(usize, MsgF) -> (), offset: &mut usize, tx: &Transaction, sign_data: &SignData, @@ -1962,7 +1952,6 @@ impl TxCircuitConfig { tx_value_cells.push(self.assign_common_part( region, - provide_msg, *offset, Some(tx), tx_id_next, @@ -2156,7 +2145,6 @@ impl TxCircuitConfig { fn assign_calldata_rows( &self, region: &mut Region<'_, F>, - provide_msg: &mut impl FnMut(usize, MsgF) -> (), offset: &mut usize, tx: &Transaction, next_tx: Option<&Transaction>, @@ -2180,7 +2168,6 @@ impl TxCircuitConfig { self.assign_common_part( region, - provide_msg, *offset, Some(tx), tx_id_next, @@ -2222,7 +2209,6 @@ impl TxCircuitConfig { fn assign_common_part( &self, region: &mut Region<'_, F>, - provide_msg: &mut impl FnMut(usize, MsgF) -> (), offset: usize, tx: Option<&Transaction>, tx_id_next: usize, @@ -2267,12 +2253,10 @@ impl TxCircuitConfig { } // 1st phase columns - let tx_id_f = F::from(tx_id as u64); - let index_f = F::from(index); for (col_anno, col, col_val) in [ // note that tx_table.index is not assigned in this function - ("tx_id", self.tx_table.tx_id, tx_id_f), - ("tx_index", self.tx_table.index, index_f), + ("tx_id", self.tx_table.tx_id, F::from(tx_id as u64)), + ("tx_index", self.tx_table.index, F::from(index)), ("tx_type", self.tx_type, F::from(u64::from(tx_type))), ( "is_l1_msg", @@ -2286,8 +2270,6 @@ impl TxCircuitConfig { let tx_value_cell = region.assign_advice(|| "tx_value", self.tx_table.value, offset, || value)?; - value.map(|value| provide_msg(offset, MsgF::tx(tx_id_f, tag_f, index_f, value))); - Ok(tx_value_cell) } @@ -2570,7 +2552,6 @@ impl TxCircuit { config: &TxCircuitConfig, challenges: &crate::util::Challenges>, layouter: &mut impl Layouter, - mut provide_msg: impl FnMut(usize, MsgF) -> (), start_l1_queue_index: u64, sign_datas: Vec, padding_txs: &[Transaction], @@ -2592,7 +2573,7 @@ impl TxCircuit { // 1. Empty entry region.assign_fixed(|| "q_first", config.q_first, 0, || Value::known(F::one()))?; - config.assign_null_row(&mut region, &mut provide_msg, &mut offset)?; + config.assign_null_row(&mut region, &mut offset)?; // 2. Assign all tx fields except for call data let get_tx = |i: usize| { @@ -2667,7 +2648,6 @@ impl TxCircuit { tx_value_cells.extend_from_slice( config.assign_fixed_rows( &mut region, - &mut provide_msg, &mut offset, tx, sign_data, @@ -2696,7 +2676,6 @@ impl TxCircuit { .find(|tx| !tx.call_data.is_empty()); config.assign_calldata_rows( &mut region, - &mut provide_msg, &mut offset, tx, next_tx, @@ -2772,19 +2751,6 @@ impl SubCircuit for TxCircuit { config: &Self::Config, challenges: &crate::util::Challenges>, layouter: &mut impl Layouter, - ) -> Result<(), Error> { - self.synthesize_sub2(config, challenges, layouter, |_, _| {}) - } -} - -impl TxCircuit { - /// Make the assignments to the TxCircuit - pub fn synthesize_sub2( - &self, - config: &TxCircuitConfig, - challenges: &crate::util::Challenges>, - layouter: &mut impl Layouter, - provide_msg: impl FnMut(usize, MsgF) -> (), ) -> Result<(), Error> { assert!(self.txs.len() <= self.max_txs); @@ -2841,7 +2807,6 @@ impl TxCircuit { config, challenges, layouter, - provide_msg, self.start_l1_queue_index, sign_datas, &padding_txs, From f9ed8d95a37dc164fb8aa77742a5d23f127d0f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 10 Nov 2023 14:18:57 +0100 Subject: [PATCH 58/67] bus-auto: move Byte and Fixed tables to the common chip --- zkevm-circuits/src/evm_bus.rs | 76 +++++++++---- zkevm-circuits/src/evm_circuit.rs | 163 ++++++++-------------------- zkevm-circuits/src/super_circuit.rs | 14 ++- zkevm-circuits/src/table.rs | 63 +++++++++++ 4 files changed, 179 insertions(+), 137 deletions(-) diff --git a/zkevm-circuits/src/evm_bus.rs b/zkevm-circuits/src/evm_bus.rs index d30b977f62..220a6678de 100644 --- a/zkevm-circuits/src/evm_bus.rs +++ b/zkevm-circuits/src/evm_bus.rs @@ -1,8 +1,8 @@ use crate::{ evm_circuit::table::{Lookup, MsgExpr, MsgF, RwValues}, table::{ - BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, ModExpTable, - PowOfRandTable, RwTable, SigTable, TxTable, + BlockTable, BytecodeTable, CopyTable, DualByteTable, EccTable, ExpTable, FixedTable, + KeccakTable, ModExpTable, PowOfRandTable, RwTable, SigTable, TxTable, }, util::{assign_global, query_expression}, }; @@ -13,19 +13,22 @@ use gadgets::bus::{ }; use halo2_proofs::{ circuit::{Layouter, Region, Value}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, + plonk::{ConstraintSystem, Error, Expression, VirtualCells}, poly::Rotation, }; +/// EVMBusLookups makes all lookup tables available on the bus. #[derive(Clone, Debug)] pub struct EVMBusLookups { - bus_tables: [BusTable; 11], + bus_tables: [BusTable; 13], } impl EVMBusLookups { pub fn configure( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder>, + dual_byte_table: &DualByteTable, + fixed_table: &FixedTable, rw_table: &RwTable, tx_table: &TxTable, bytecode_table: &BytecodeTable, @@ -38,23 +41,33 @@ impl EVMBusLookups { ecc_table: &EccTable, pow_of_rand_table: &PowOfRandTable, ) -> Self { + let tables: [&dyn QueryTable; 13] = [ + dual_byte_table, + fixed_table, + rw_table, + tx_table, + bytecode_table, + block_table, + copy_table, + keccak_table, + exp_table, + sig_table, + modexp_table, + ecc_table, + pow_of_rand_table, + ]; Self { - bus_tables: [ - BusTable::configure(meta, bus_builder, rw_table), - BusTable::configure(meta, bus_builder, tx_table), - BusTable::configure(meta, bus_builder, bytecode_table), - BusTable::configure(meta, bus_builder, block_table), - BusTable::configure(meta, bus_builder, copy_table), - BusTable::configure(meta, bus_builder, keccak_table), - BusTable::configure(meta, bus_builder, exp_table), - BusTable::configure(meta, bus_builder, sig_table), - BusTable::configure(meta, bus_builder, modexp_table), - BusTable::configure(meta, bus_builder, ecc_table), - BusTable::configure(meta, bus_builder, pow_of_rand_table), - ], + bus_tables: tables.map(|table| BusTable::configure(meta, bus_builder, table)), } } + /// Assign the answers to all lookups on the bus. + /// + /// This must be called after all other circuits. The lookup queries and the content of the + /// tables must have been assigned already. + /// + /// This function reads the content of the tables, it detects how many times each entry has been + /// looked up, and it assigns the bus operations from the tables to balance these lookups. pub fn assign( &self, layouter: &mut impl Layouter, @@ -129,7 +142,7 @@ fn eval(region: &Region, offset: usize, expr: Expression) -> F { Value::known( region .query_fixed( - Column::new(fixed_query.column_index(), Fixed), + fixed_query.column(), (offset as i32 + fixed_query.rotation().0) as usize, ) .unwrap(), @@ -139,7 +152,7 @@ fn eval(region: &Region, offset: usize, expr: Expression) -> F { Value::known( region .query_advice( - Column::new(advice_query.column_index(), Advice::default()), + advice_query.column(), (offset as i32 + advice_query.rotation().0) as usize, ) .unwrap(), @@ -162,6 +175,31 @@ trait QueryTable { fn message(&self, meta: &mut VirtualCells) -> MsgExpr; } +impl QueryTable for DualByteTable { + fn enabled(&self, meta: &mut VirtualCells) -> Expression { + meta.query_fixed(self.q_enable, Rotation::cur()) + } + + fn message(&self, meta: &mut VirtualCells) -> MsgExpr { + MsgExpr::bytes(self.bytes.map(|col| meta.query_fixed(col, Rotation::cur()))) + } +} + +impl QueryTable for FixedTable { + fn enabled(&self, meta: &mut VirtualCells) -> Expression { + meta.query_fixed(self.q_enable, Rotation::cur()) + } + + fn message(&self, meta: &mut VirtualCells) -> MsgExpr { + let mut query = |col| meta.query_fixed(col, Rotation::cur()); + + MsgExpr::lookup(Lookup::Fixed { + tag: query(self.tag), + values: self.values.map(|col| query(col)), + }) + } +} + impl QueryTable for RwTable { fn enabled(&self, meta: &mut VirtualCells) -> Expression { meta.query_fixed(self.q_enable, Rotation::cur()) diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 616032fc4c..1ae97216d1 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -5,12 +5,10 @@ use gadgets::bus::{ bus_builder::{BusAssigner, BusBuilder}, bus_chip::BusConfig, bus_codec::{BusCodecExpr, BusCodecVal}, - bus_lookup::BusLookupChip, }; use halo2_proofs::{ circuit::{Cell, Layouter, SimpleFloorPlanner, Value}, plonk::*, - poly::Rotation, }; mod execution; @@ -30,27 +28,25 @@ use crate::{ evm_bus::EVMBusLookups, evm_circuit::param::{MAX_STEP_HEIGHT, STEP_STATE_HEIGHT}, table::{ - BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, LookupTable, - ModExpTable, PowOfRandTable, RwTable, SigTable, TxTable, + BlockTable, BytecodeTable, CopyTable, DualByteTable, EccTable, ExpTable, FixedTable, + KeccakTable, LookupTable, ModExpTable, PowOfRandTable, RwTable, SigTable, TxTable, }, - util::{assign_global, query_expression, SubCircuit, SubCircuitConfig}, + util::{assign_global, SubCircuit, SubCircuitConfig}, }; use bus_mapping::evm::OpcodeId; use eth_types::Field; use execution::ExecutionConfig; use itertools::Itertools; use strum::IntoEnumIterator; -use table::{FixedTableTag, Lookup, MsgExpr, MsgF}; +use table::{FixedTableTag, MsgExpr, MsgF}; use witness::Block; /// EvmCircuitConfig implements verification of execution trace of a block. #[derive(Clone, Debug)] pub struct EvmCircuitConfig { - fixed_table: [Column; 4], - dual_byte_table: [Column; 2], + dual_byte_table: DualByteTable, + fixed_table: FixedTable, pow_of_rand_table: PowOfRandTable, - bus_lookup: [BusLookupChip; 2], - enable_bus_lookup: Column, pub(crate) execution: Box>, } @@ -58,6 +54,10 @@ pub struct EvmCircuitConfig { pub struct EvmCircuitConfigArgs { /// Challenge pub challenges: crate::util::Challenges>, + /// The dual byte table. + pub dual_byte_table: DualByteTable, + /// Fixed Table. + pub fixed_table: FixedTable, // Power of Randomness Table. pub pow_of_rand_table: PowOfRandTable, } @@ -92,74 +92,20 @@ impl EvmCircuitConfig { bus_builder: &mut BusBuilder>, EvmCircuitConfigArgs { challenges, + dual_byte_table, + fixed_table, pow_of_rand_table, }: EvmCircuitConfigArgs, ) -> Self { - let fixed_table = [(); 4].map(|_| meta.fixed_column()); - let dual_byte_table = [(); 2].map(|_| meta.fixed_column()); - let enable_bus_lookup = meta.fixed_column(); // TODO: replace with q_usable, or BusConfig.enabled? - - let bus_lookup = Self::configure_bus_lookup( - meta, - bus_builder, - enable_bus_lookup, - &dual_byte_table, - &fixed_table, - ); - let execution = Box::new(ExecutionConfig::configure(meta, challenges, bus_builder)); - meta.annotate_lookup_any_column(dual_byte_table[0], || "dual_byte_table_0"); - meta.annotate_lookup_any_column(dual_byte_table[1], || "dual_byte_table_1"); - fixed_table.iter().enumerate().for_each(|(idx, &col)| { - meta.annotate_lookup_any_column(col, || format!("fix_table_{idx}")) - }); - Self { - fixed_table, dual_byte_table, - bus_lookup, - enable_bus_lookup, - execution, + fixed_table, pow_of_rand_table, + execution, } } - - fn configure_bus_lookup( - meta: &mut ConstraintSystem, - bus_builder: &mut BusBuilder>, - enabled: Column, - dual_byte_table: &[Column; 2], - fixed_table: &[Column; 4], - ) -> [BusLookupChip; 2] { - let enabled = query_expression(meta, |meta| meta.query_fixed(enabled, Rotation::cur())); - - let byte_lookup = { - let message = query_expression(meta, |meta| { - MsgExpr::bytes([ - meta.query_fixed(dual_byte_table[0], Rotation::cur()), - meta.query_fixed(dual_byte_table[1], Rotation::cur()), - ]) - }); - BusLookupChip::connect(meta, bus_builder, enabled.clone(), message) - }; - - let fixed_lookup = { - let message = query_expression(meta, |meta| { - MsgExpr::lookup(Lookup::Fixed { - tag: meta.query_fixed(fixed_table[0], Rotation::cur()), - values: [ - meta.query_fixed(fixed_table[1], Rotation::cur()), - meta.query_fixed(fixed_table[2], Rotation::cur()), - meta.query_fixed(fixed_table[3], Rotation::cur()), - ], - }) - }); - BusLookupChip::connect(meta, bus_builder, enabled.clone(), message) - }; - - [byte_lookup, fixed_lookup] - } } impl EvmCircuitConfig { @@ -167,45 +113,38 @@ impl EvmCircuitConfig { fn load_fixed_table( &self, layouter: &mut impl Layouter, - bus_assigner: &mut BusAssigner>, - bus_lookup: &BusLookupChip, fixed_table_tags: Vec, ) -> Result<(), Error> { assign_global( layouter, || "fixed table", |mut region| { + let table: [Column; 4] = + >::fixed_columns(&self.fixed_table) + .try_into() + .unwrap(); + for (offset, row) in std::iter::once([F::zero(); 4]) .chain(fixed_table_tags.iter().flat_map(|tag| tag.build())) .enumerate() { - for (column, value) in self.fixed_table.iter().zip_eq(row) { + for (column, value) in table.iter().zip_eq(row) { region.assign_fixed(|| "", *column, offset, || Value::known(value))?; } - region.assign_fixed( - || "", - self.enable_bus_lookup, + || "fixed table enabled", + self.fixed_table.q_enable, offset, || Value::known(F::one()), )?; - - bus_lookup.assign(&mut region, bus_assigner, offset, MsgF::fixed(row))?; } - - bus_assigner.finish_ports(&mut region); Ok(()) }, ) } /// Load dual byte table - fn load_dual_byte_table( - &self, - layouter: &mut impl Layouter, - bus_assigner: &mut BusAssigner>, - bus_lookup: &BusLookupChip, - ) -> Result<(), Error> { + fn load_dual_byte_table(&self, layouter: &mut impl Layouter) -> Result<(), Error> { assign_global( layouter, || "byte table", @@ -218,34 +157,24 @@ impl EvmCircuitConfig { region.assign_fixed( || "", - self.enable_bus_lookup, + self.dual_byte_table.q_enable, offset, || Value::known(F::one()), )?; - region.assign_fixed( || "", - self.dual_byte_table[0], + self.dual_byte_table.bytes[0], offset, || Value::known(b0), )?; region.assign_fixed( || "", - self.dual_byte_table[1], + self.dual_byte_table.bytes[1], offset, || Value::known(b1), )?; - - bus_lookup.assign( - &mut region, - bus_assigner, - offset, - MsgF::bytes([b0, b1]), - )?; } } - - bus_assigner.finish_ports(&mut region); Ok(()) }, ) @@ -388,6 +317,8 @@ impl EvmCircuit { ) -> Result<(), Error> { let block = self.block.as_ref().unwrap(); + config.load_dual_byte_table(layouter)?; + config.load_fixed_table(layouter, self.fixed_table_tags.clone())?; config.pow_of_rand_table.assign(layouter, challenges)?; let export = config @@ -395,15 +326,6 @@ impl EvmCircuit { .assign_block(layouter, bus_assigner, block, challenges)?; self.exports.borrow_mut().replace(export); - config.load_dual_byte_table(layouter, bus_assigner, &config.bus_lookup[0])?; - - config.load_fixed_table( - layouter, - bus_assigner, - &config.bus_lookup[1], - self.fixed_table_tags.clone(), - )?; - Ok(()) } } @@ -538,29 +460,32 @@ impl Circuit for EvmCircuit { let challenges = Challenges::construct(meta); let challenges_expr = challenges.exprs(meta); let mut bus_builder = BusBuilder::new(BusCodecExpr::new(challenges_expr.lookup_input())); + + let dual_byte_table = DualByteTable::construct(meta); + let fixed_table = FixedTable::construct(meta); + fixed_table.annotate_columns(meta); let rw_table = RwTable::construct(meta); rw_table.annotate_columns(meta); let tx_table = TxTable::construct(meta); tx_table.annotate_columns(meta); let bytecode_table = BytecodeTable::construct(meta); + bytecode_table.annotate_columns(meta); let block_table = BlockTable::construct(meta); + block_table.annotate_columns(meta); let q_copy_table = meta.fixed_column(); let copy_table = CopyTable::construct(meta, q_copy_table); - let keccak_table = KeccakTable::construct(meta); - let exp_table = ExpTable::construct(meta); - let sig_table = SigTable::construct(meta); - let modexp_table = ModExpTable::construct(meta); - let ecc_table = EccTable::construct(meta); - let pow_of_rand_table = PowOfRandTable::construct(meta, &challenges_expr); - - bytecode_table.annotate_columns(meta); - block_table.annotate_columns(meta); copy_table.annotate_columns(meta); + let keccak_table = KeccakTable::construct(meta); keccak_table.annotate_columns(meta); + let exp_table = ExpTable::construct(meta); exp_table.annotate_columns(meta); + let sig_table = SigTable::construct(meta); sig_table.annotate_columns(meta); + let modexp_table = ModExpTable::construct(meta); modexp_table.annotate_columns(meta); + let ecc_table = EccTable::construct(meta); ecc_table.annotate_columns(meta); + let pow_of_rand_table = PowOfRandTable::construct(meta, &challenges_expr); pow_of_rand_table.annotate_columns(meta); let config = EvmCircuitConfig::new( @@ -568,12 +493,18 @@ impl Circuit for EvmCircuit { &mut bus_builder, EvmCircuitConfigArgs { challenges: challenges_expr, + // Tables assigned by the EVM circuit. + dual_byte_table: dual_byte_table.clone(), + fixed_table: fixed_table.clone(), pow_of_rand_table: pow_of_rand_table.clone(), }, ); + let evm_lookups = EVMBusLookups::configure( meta, &mut bus_builder, + &dual_byte_table, + &fixed_table, &rw_table, &tx_table, &bytecode_table, @@ -587,11 +518,13 @@ impl Circuit for EvmCircuit { &pow_of_rand_table, ); let bus = BusConfig::new(meta, &bus_builder.build()); + ( config, bus, evm_lookups, challenges, + // Tables assigned by external circuits. rw_table, tx_table, bytecode_table, diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 8c9c7a4f27..220aadc248 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -77,9 +77,9 @@ use crate::{ sig_circuit::{SigCircuit, SigCircuitConfig, SigCircuitConfigArgs}, state_circuit::{StateCircuit, StateCircuitConfig, StateCircuitConfigArgs}, table::{ - BlockTable, BytecodeTable, CopyTable, EccTable, ExpTable, KeccakTable, LookupTable, - ModExpTable, MptTable, PoseidonTable, PowOfRandTable, RlpFsmRlpTable as RlpTable, RwTable, - SigTable, TxTable, U16Table, U8Table, + BlockTable, BytecodeTable, CopyTable, DualByteTable, EccTable, ExpTable, FixedTable, + KeccakTable, LookupTable, ModExpTable, MptTable, PoseidonTable, PowOfRandTable, + RlpFsmRlpTable as RlpTable, RwTable, SigTable, TxTable, U16Table, U8Table, }, tx_circuit::{TxCircuit, TxCircuitConfig, TxCircuitConfigArgs}, util::{circuit_stats, log2_ceil, Challenges, SubCircuit, SubCircuitConfig}, @@ -175,6 +175,9 @@ impl SubCircuitConfig for SuperCircuitConfig { let mut bus_builder = BusBuilder::new(BusCodecExpr::new(challenges_expr.lookup_input())); + let dual_byte_table = DualByteTable::construct(meta); + let fixed_table = FixedTable::construct(meta); + fixed_table.annotate_columns(meta); let tx_table = TxTable::construct(meta); tx_table.annotate_columns(meta); log_circuit_info(meta, "tx table"); @@ -354,6 +357,9 @@ impl SubCircuitConfig for SuperCircuitConfig { &mut bus_builder, EvmCircuitConfigArgs { challenges: challenges_expr.clone(), + // Tables assigned by the EVM circuit. + dual_byte_table: dual_byte_table.clone(), + fixed_table: fixed_table.clone(), pow_of_rand_table: pow_of_rand_table.clone(), }, ); @@ -384,6 +390,8 @@ impl SubCircuitConfig for SuperCircuitConfig { let evm_lookups = EVMBusLookups::configure( meta, &mut bus_builder, + &dual_byte_table, + &fixed_table, &rw_table, &tx_table, &bytecode_table, diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 0d75cd47de..b583b98b2b 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -118,6 +118,69 @@ impl> + Copy, const W: usize> LookupTable for [ } } +/// The DualByteTable used to check the range of two bytes at once. +#[derive(Clone, Debug)] +pub struct DualByteTable { + /// Enabled. + // TODO: this could be removed. + pub q_enable: Column, + /// Two byte columns containing the 256*256 combinations of two bytes. + pub bytes: [Column; 2], +} + +impl DualByteTable { + /// Construct a new DualByteTable + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + q_enable: meta.fixed_column(), + bytes: [(); 2].map(|_| meta.fixed_column()), + } + } +} + +/// The table of fixed functions (range checks, XOR, etc). +#[derive(Clone, Debug)] +pub struct FixedTable { + /// Enabled. + // TODO: this could be removed. + pub q_enable: Column, + /// The tag that selects the function. + pub tag: Column, + /// The arguments of the function (or 0). + pub values: [Column; 3], +} + +impl FixedTable { + /// Construct a new FixedTable + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + q_enable: meta.fixed_column(), + tag: meta.fixed_column(), + values: [(); 3].map(|_| meta.fixed_column()), + } + } +} + +impl LookupTable for FixedTable { + fn columns(&self) -> Vec> { + vec![ + self.tag.into(), + self.values[0].into(), + self.values[1].into(), + self.values[2].into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("tag"), + String::from("values[0]"), + String::from("values[1]"), + String::from("values[2]"), + ] + } +} + /// Tag used to identify each field in the transaction in a row of the /// transaction table. #[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter)] From 01d2d2b38d894a9e714f9c0911b3b42dc6f6ccae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 13 Nov 2023 16:55:39 +0100 Subject: [PATCH 59/67] bus-auto: patch to use the right version of halo2 --- Cargo.lock | 40 +++++++++++++--------------------------- Cargo.toml | 12 ++++++++++-- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2716a49483..95184b3b26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,7 +43,7 @@ name = "aggregator" version = "0.1.0" dependencies = [ "ark-std 0.3.0", - "env_logger 0.10.0", + "env_logger", "eth-types", "ethers-core", "halo2_proofs", @@ -484,7 +484,7 @@ name = "bus-mapping" version = "0.1.0" dependencies = [ "ctor", - "env_logger 0.10.0", + "env_logger", "eth-types", "ethers-core", "ethers-providers", @@ -650,7 +650,7 @@ version = "0.1.0" dependencies = [ "ark-std 0.3.0", "bus-mapping", - "env_logger 0.10.0", + "env_logger", "eth-types", "ethers", "ethers-signers", @@ -1465,19 +1465,6 @@ dependencies = [ "syn 2.0.32", ] -[[package]] -name = "env_logger" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - [[package]] name = "env_logger" version = "0.10.0" @@ -2228,7 +2215,7 @@ dependencies = [ name = "geth-utils" version = "0.1.0" dependencies = [ - "env_logger 0.10.0", + "env_logger", "gobuild 0.1.0-alpha.2 (git+https://github.com/scroll-tech/gobuild.git)", "log", ] @@ -2439,13 +2426,12 @@ dependencies = [ [[package]] name = "halo2_proofs" version = "0.2.0" -source = "git+https://github.com/scroll-tech/halo2.git?branch=develop#e3fe25eadd714fd991f35190d17ff0b8fb031188" +source = "git+https://github.com//scroll-tech/halo2.git?branch=bus-auto#3ef65de2a89cafc75f40e433ac44f23a6286357d" dependencies = [ "ark-std 0.3.0", "blake2b_simd", "cfg-if 0.1.10", "crossbeam", - "env_logger 0.8.4", "ff 0.12.1", "group 0.12.1", "halo2curves", @@ -2484,7 +2470,7 @@ dependencies = [ [[package]] name = "halo2wrong" version = "0.1.0" -source = "git+https://github.com/scroll-tech/halo2wrong?branch=halo2-ecc-snark-verifier-0323#939d679cb16abf0e820bd606248661e400328afa" +source = "git+https://github.com/scroll-tech/halo2wrong?branch=bus-auto#68c126b9506d82d0cca8a38c75a86a3d5eb955c7" dependencies = [ "group 0.12.1", "halo2_proofs", @@ -2828,7 +2814,7 @@ name = "integration-tests" version = "0.1.0" dependencies = [ "bus-mapping", - "env_logger 0.10.0", + "env_logger", "eth-types", "ethers", "halo2_proofs", @@ -2945,7 +2931,7 @@ dependencies = [ name = "keccak256" version = "0.1.0" dependencies = [ - "env_logger 0.10.0", + "env_logger", "eth-types", "halo2_proofs", "itertools", @@ -3122,7 +3108,7 @@ dependencies = [ [[package]] name = "maingate" version = "0.1.0" -source = "git+https://github.com/scroll-tech/halo2wrong?branch=halo2-ecc-snark-verifier-0323#939d679cb16abf0e820bd606248661e400328afa" +source = "git+https://github.com/scroll-tech/halo2wrong?branch=bus-auto#68c126b9506d82d0cca8a38c75a86a3d5eb955c7" dependencies = [ "group 0.12.1", "halo2wrong", @@ -3220,7 +3206,7 @@ dependencies = [ name = "mpt-zktrie" version = "0.1.0" dependencies = [ - "env_logger 0.10.0", + "env_logger", "eth-types", "halo2-mpt-circuits", "halo2_proofs", @@ -4833,7 +4819,7 @@ version = "0.0.1" source = "git+https://github.com/scroll-tech/snark-verifier?tag=v0.1.5#bc1d39ae31f3fe520c51dd150f0fefaf9653c465" dependencies = [ "bincode", - "env_logger 0.10.0", + "env_logger", "ethereum-types", "halo2-base", "hex", @@ -5085,7 +5071,7 @@ dependencies = [ "bus-mapping", "clap 3.2.25", "ctor", - "env_logger 0.10.0", + "env_logger", "eth-types", "ethers-core", "ethers-signers", @@ -5889,7 +5875,7 @@ dependencies = [ "criterion", "ctor", "either", - "env_logger 0.10.0", + "env_logger", "eth-types", "ethers-core", "ethers-signers", diff --git a/Cargo.toml b/Cargo.toml index f58834bd12..e2a6b4468a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,14 +61,22 @@ url = "2.2" [patch.crates-io] ethers-core = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } ethers-etherscan = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } + +# Replace all references to halo2 PSE and Scroll repos with a specific branch. +# Note the double slash // hack to make it a different URL. +[patch."https://github.com/scroll-tech/halo2.git"] +halo2_proofs = { git = "https://github.com//scroll-tech/halo2.git", branch = "bus-auto" } [patch."https://github.com/privacy-scaling-explorations/halo2.git"] -halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "develop" } +halo2_proofs = { git = "https://github.com//scroll-tech/halo2.git", branch = "bus-auto" } + [patch."https://github.com/privacy-scaling-explorations/poseidon.git"] poseidon = { git = "https://github.com/scroll-tech/poseidon.git", branch = "scroll-dev-0220" } + [patch."https://github.com/privacy-scaling-explorations/halo2curves.git"] halo2curves = { git = "https://github.com/scroll-tech/halo2curves.git", branch = "0.3.1-derive-serde" } + [patch."https://github.com/privacy-scaling-explorations/halo2wrong.git"] -maingate = { git = "https://github.com/scroll-tech/halo2wrong", branch = "halo2-ecc-snark-verifier-0323" } +maingate = { git = "https://github.com/scroll-tech/halo2wrong", branch = "bus-auto" } # Definition of benchmarks profile to use. [profile.bench] From 29b63a381bc92f26c4e82d9e632b819beb2116d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 15 Nov 2023 12:05:50 +0100 Subject: [PATCH 60/67] bus-demo: clippy --- Cargo.lock | 36 ++++++------------- gadgets/src/bus.rs | 2 +- .../{port_assigner.rs => batch_assigner.rs} | 20 ++++++++--- gadgets/src/bus/bus_builder.rs | 18 +++++----- gadgets/src/bus/bus_chip.rs | 4 +-- gadgets/src/bus/bus_codec.rs | 6 ++-- gadgets/src/bus/bus_port.rs | 6 ++-- gadgets/src/bus/port_multi.rs | 6 ++-- gadgets/src/lib.rs | 2 +- zkevm-circuits/src/evm_circuit.rs | 2 +- 10 files changed, 49 insertions(+), 53 deletions(-) rename gadgets/src/bus/{port_assigner.rs => batch_assigner.rs} (87%) diff --git a/Cargo.lock b/Cargo.lock index 98498de3b8..5a6221c3cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,7 +43,7 @@ name = "aggregator" version = "0.1.0" dependencies = [ "ark-std 0.3.0", - "env_logger 0.10.0", + "env_logger", "eth-types", "ethers-core", "halo2_proofs", @@ -484,7 +484,7 @@ name = "bus-mapping" version = "0.1.0" dependencies = [ "ctor", - "env_logger 0.10.0", + "env_logger", "eth-types", "ethers-core", "ethers-providers", @@ -649,7 +649,7 @@ version = "0.1.0" dependencies = [ "ark-std 0.3.0", "bus-mapping", - "env_logger 0.10.0", + "env_logger", "eth-types", "ethers", "ethers-signers", @@ -1454,19 +1454,6 @@ dependencies = [ "syn 2.0.32", ] -[[package]] -name = "env_logger" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - [[package]] name = "env_logger" version = "0.10.0" @@ -2216,7 +2203,7 @@ dependencies = [ name = "geth-utils" version = "0.1.0" dependencies = [ - "env_logger 0.10.0", + "env_logger", "gobuild 0.1.0-alpha.2 (git+https://github.com/scroll-tech/gobuild.git)", "log", ] @@ -2427,13 +2414,12 @@ dependencies = [ [[package]] name = "halo2_proofs" version = "0.2.0" -source = "git+https://github.com/scroll-tech/halo2.git?branch=develop#e3fe25eadd714fd991f35190d17ff0b8fb031188" +source = "git+https://github.com/scroll-tech/halo2.git?branch=develop#5cf9a3726525810af41ef416e0292ec999b34030" dependencies = [ "ark-std 0.3.0", "blake2b_simd", "cfg-if 0.1.10", "crossbeam", - "env_logger 0.8.4", "ff 0.12.1", "group 0.12.1", "halo2curves", @@ -2795,7 +2781,7 @@ name = "integration-tests" version = "0.1.0" dependencies = [ "bus-mapping", - "env_logger 0.10.0", + "env_logger", "eth-types", "ethers", "halo2_proofs", @@ -2921,7 +2907,7 @@ dependencies = [ name = "keccak256" version = "0.1.0" dependencies = [ - "env_logger 0.10.0", + "env_logger", "eth-types", "halo2_proofs", "itertools 0.11.0", @@ -3148,7 +3134,7 @@ dependencies = [ name = "mpt-zktrie" version = "0.1.0" dependencies = [ - "env_logger 0.10.0", + "env_logger", "eth-types", "halo2-mpt-circuits", "halo2_proofs", @@ -4748,7 +4734,7 @@ version = "0.0.1" source = "git+https://github.com/scroll-tech/snark-verifier?tag=v0.1.5#bc1d39ae31f3fe520c51dd150f0fefaf9653c465" dependencies = [ "bincode", - "env_logger 0.10.0", + "env_logger", "ethereum-types", "halo2-base", "hex", @@ -5019,7 +5005,7 @@ dependencies = [ "bus-mapping", "clap 3.2.25", "ctor", - "env_logger 0.10.0", + "env_logger", "eth-types", "ethers-core", "ethers-signers", @@ -5823,7 +5809,7 @@ dependencies = [ "criterion", "ctor", "either", - "env_logger 0.10.0", + "env_logger", "eth-types", "ethers-core", "ethers-signers", diff --git a/gadgets/src/bus.rs b/gadgets/src/bus.rs index 5dba18b5bf..3ff23314f2 100644 --- a/gadgets/src/bus.rs +++ b/gadgets/src/bus.rs @@ -19,7 +19,7 @@ pub mod bus_lookup; pub mod bus_codec; /// This module helps ports with their assignments. -mod port_assigner; +mod batch_assigner; /// Utility functions. mod util; diff --git a/gadgets/src/bus/port_assigner.rs b/gadgets/src/bus/batch_assigner.rs similarity index 87% rename from gadgets/src/bus/port_assigner.rs rename to gadgets/src/bus/batch_assigner.rs index dcb7a087b5..d8e02c1d73 100644 --- a/gadgets/src/bus/port_assigner.rs +++ b/gadgets/src/bus/batch_assigner.rs @@ -17,14 +17,20 @@ pub trait Assigner: Send + Sync { fn assign(&self, region: &mut Region<'_, F>, helper: F) -> (usize, F); } -/// PortAssigner computes and assigns terms into helper cells and the bus. -pub struct PortAssigner { +/// BatchAssigner computes and assigns terms into helper cells and the bus. +pub struct BatchAssigner { assigners: HelperBatch>>, _marker: PhantomData, } -impl> PortAssigner { - /// Create a new PortAssigner. +impl> Default for BatchAssigner { + fn default() -> Self { + Self::new() + } +} + +impl> BatchAssigner { + /// Create a new BatchAssigner. pub fn new() -> Self { Self { assigners: HelperBatch::new(), @@ -60,6 +66,12 @@ pub struct BusOpCounter { _marker: PhantomData<(F, M)>, } +impl> Default for BusOpCounter { + fn default() -> Self { + Self::new() + } +} + impl> BusOpCounter { /// Create a new BusOpCounter. pub fn new() -> Self { diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs index 0a5afcc297..a55801c10a 100644 --- a/gadgets/src/bus/bus_builder.rs +++ b/gadgets/src/bus/bus_builder.rs @@ -1,9 +1,9 @@ use std::mem; use super::{ + batch_assigner::{BatchAssigner, BusOpCounter}, bus_chip::BusTerm, bus_codec::{BusCodecExpr, BusCodecVal, BusMessageF}, - port_assigner::{BusOpCounter, PortAssigner}, Field, }; use halo2_proofs::circuit::{Region, Value}; @@ -45,14 +45,14 @@ pub struct BusAssigner { codec: BusCodecVal, term_adder: TermAdder, bus_op_counter: BusOpCounter, - port_assigner: PortAssigner, + batch_assigner: BatchAssigner, } impl> BusAssigner { /// Create a new bus assigner with a maximum number of rows. pub fn new(codec: BusCodecVal, n_rows: usize) -> Self { Self { - port_assigner: PortAssigner::new(), + batch_assigner: BatchAssigner::new(), codec, term_adder: TermAdder::new(n_rows), bus_op_counter: BusOpCounter::new(), @@ -74,16 +74,16 @@ impl> BusAssigner { &mut self.bus_op_counter } - /// Return the port assigner. - pub fn port_assigner(&mut self) -> &mut PortAssigner { - &mut self.port_assigner + /// Return the batch assigner. + pub fn batch_assigner(&mut self) -> &mut BatchAssigner { + &mut self.batch_assigner } /// Finish pending assignments in a region. pub fn finish_ports(&mut self, region: &mut Region<'_, F>) { - let old_port_assigner = mem::replace(&mut self.port_assigner, PortAssigner::new()); + let old_batch_assigner = mem::replace(&mut self.batch_assigner, BatchAssigner::new()); - old_port_assigner.finish(region, self); + old_batch_assigner.finish(region, self); } /// Add a term value to the bus. @@ -93,7 +93,7 @@ impl> BusAssigner { /// Return the collected terms. pub fn terms(&self) -> Value<&[F]> { - assert_eq!(self.port_assigner.len(), 0, "finish_ports was not called"); + assert_eq!(self.batch_assigner.len(), 0, "finish_ports was not called"); // TODO: better error handling. self.term_adder.terms() diff --git a/gadgets/src/bus/bus_chip.rs b/gadgets/src/bus/bus_chip.rs index 80f7ae06f6..8f5454d4b5 100644 --- a/gadgets/src/bus/bus_chip.rs +++ b/gadgets/src/bus/bus_chip.rs @@ -44,7 +44,7 @@ impl BusConfig { let is_first = cs.query_fixed(is_first, Rotation::cur()); let acc_next = cs.query_advice(acc, Rotation::next()); - let acc = cs.query_advice(acc.clone(), Rotation::cur()); + let acc = cs.query_advice(acc, Rotation::cur()); // The sum of terms on the current row. let sum = terms @@ -126,7 +126,7 @@ impl BusConfig { .unwrap(); if let Some(term) = terms.get(offset) { - acc = acc + term; + acc += term; } } }); diff --git a/gadgets/src/bus/bus_codec.rs b/gadgets/src/bus/bus_codec.rs index da956492b6..1c51d8e98a 100644 --- a/gadgets/src/bus/bus_codec.rs +++ b/gadgets/src/bus/bus_codec.rs @@ -57,10 +57,8 @@ where /// - the inverses of the elements are linearly independent. /// - Elements are non-zero. pub fn compress(&self, msg: M) -> Value { - self.rand.clone().map(|rand| { - msg.into_items() - .fold(F::one(), |acc, f| rand.clone() * acc + f) - }) + self.rand + .map(|rand| msg.into_items().fold(F::one(), |acc, f| rand * acc + f)) } } diff --git a/gadgets/src/bus/bus_port.rs b/gadgets/src/bus/bus_port.rs index 81272b1324..3d329d0f8d 100644 --- a/gadgets/src/bus/bus_port.rs +++ b/gadgets/src/bus/bus_port.rs @@ -1,8 +1,8 @@ use super::{ + batch_assigner::Assigner, bus_builder::{BusAssigner, BusBuilder}, bus_chip::BusTerm, bus_codec::{BusCodecExpr, BusCodecVal, BusMessageExpr, BusMessageF}, - port_assigner::Assigner, util::from_isize, Field, }; @@ -179,7 +179,7 @@ impl Port { let denom = bus_assigner.codec().compress(op.message()); bus_assigner.op_counter().track_op(&op); - bus_assigner.port_assigner().assign_later(cmd, denom); + bus_assigner.batch_assigner().assign_later(cmd, denom); } /// Return the degree of the constraints given these inputs. @@ -236,7 +236,7 @@ impl Port { } /// Return the witness that must be assigned to the helper cell. - /// Very slow. Prefer `PortAssigner::assign_later` instead. + /// Very slow. Prefer `BatchAssigner::assign_later` instead. pub fn helper_witness>( codec: &BusCodecVal, op: BusOpF, diff --git a/gadgets/src/bus/port_multi.rs b/gadgets/src/bus/port_multi.rs index 73dc0fa123..cb4357c7f5 100644 --- a/gadgets/src/bus/port_multi.rs +++ b/gadgets/src/bus/port_multi.rs @@ -1,9 +1,9 @@ use super::{ + batch_assigner::Assigner, bus_builder::{BusAssigner, BusBuilder}, bus_chip::BusTerm, bus_codec::{BusCodecExpr, BusCodecVal, BusMessageExpr, BusMessageF}, bus_port::{BusOpExpr, BusOpF}, - port_assigner::Assigner, util::from_isize, Field, }; @@ -91,9 +91,9 @@ impl PortBatched { }); for op in &ops { - bus_assigner.op_counter().track_op(&op); + bus_assigner.op_counter().track_op(op); } - bus_assigner.port_assigner().assign_later(cmd, denom); + bus_assigner.batch_assigner().assign_later(cmd, denom); } /// Return the degree of the constraints given these inputs. diff --git a/gadgets/src/lib.rs b/gadgets/src/lib.rs index 7383126e63..83a5661e6a 100644 --- a/gadgets/src/lib.rs +++ b/gadgets/src/lib.rs @@ -13,6 +13,7 @@ pub mod batched_is_zero; pub mod binary_number; +pub mod bus; pub mod comparator; pub mod evm_word; pub mod is_equal; @@ -22,7 +23,6 @@ pub mod monotone; pub mod mul_add; pub mod range; pub mod util; -pub mod bus; use eth_types::Field; use halo2_proofs::{ diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index b1cd477097..aebbeebaa2 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -218,7 +218,7 @@ impl EvmCircuitConfig { ], }) }); - BusLookupChip::connect(meta, bus_builder, enabled.clone(), message) + BusLookupChip::connect(meta, bus_builder, enabled, message) }; [byte_lookup, fixed_lookup] From 3fd44bc1016976b52573f3d0bd25ca74f5416ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 15 Nov 2023 12:23:58 +0100 Subject: [PATCH 61/67] bus-auto: fix clippy --- zkevm-circuits/src/evm_bus.rs | 4 +++- zkevm-circuits/src/evm_circuit.rs | 2 +- zkevm-circuits/src/evm_circuit/execution.rs | 6 ++---- zkevm-circuits/src/super_circuit.rs | 2 +- zkevm-circuits/src/tx_circuit.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/zkevm-circuits/src/evm_bus.rs b/zkevm-circuits/src/evm_bus.rs index 220a6678de..dc15b4695d 100644 --- a/zkevm-circuits/src/evm_bus.rs +++ b/zkevm-circuits/src/evm_bus.rs @@ -24,6 +24,8 @@ pub struct EVMBusLookups { } impl EVMBusLookups { + /// Connect all tables to the bus. + #[allow(clippy::too_many_arguments)] pub fn configure( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder>, @@ -195,7 +197,7 @@ impl QueryTable for FixedTable { MsgExpr::lookup(Lookup::Fixed { tag: query(self.tag), - values: self.values.map(|col| query(col)), + values: self.values.map(query), }) } } diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 1ae97216d1..4ab4d64b49 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -496,7 +496,7 @@ impl Circuit for EvmCircuit { // Tables assigned by the EVM circuit. dual_byte_table: dual_byte_table.clone(), fixed_table: fixed_table.clone(), - pow_of_rand_table: pow_of_rand_table.clone(), + pow_of_rand_table, }, ); diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 529a498e83..e56d7783bc 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -530,7 +530,7 @@ impl ExecutionConfig { let bytes_port = Self::configure_bytes_port(meta, bus_builder, q_usable, &cell_manager); - let config = Self { + Self { q_usable, q_step, constants, @@ -644,9 +644,7 @@ impl ExecutionConfig { stored_expressions_map, bus_ops_map, instrument, - }; - - config + } } pub(crate) fn instrument(&self) -> &Instrument { diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 220aadc248..8eb2d084bd 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -360,7 +360,7 @@ impl SubCircuitConfig for SuperCircuitConfig { // Tables assigned by the EVM circuit. dual_byte_table: dual_byte_table.clone(), fixed_table: fixed_table.clone(), - pow_of_rand_table: pow_of_rand_table.clone(), + pow_of_rand_table, }, ); log_circuit_info(meta, "evm circuit"); diff --git a/zkevm-circuits/src/tx_circuit.rs b/zkevm-circuits/src/tx_circuit.rs index b3f2027801..8c1b88c450 100644 --- a/zkevm-circuits/src/tx_circuit.rs +++ b/zkevm-circuits/src/tx_circuit.rs @@ -2517,7 +2517,7 @@ impl TxCircuit { { let row = offset * 3 + j; region.assign_fixed( - || format!("block table enabled"), + || "block table enabled", config.block_table.q_enable, row, || Value::known(F::one()), From 1e1afa9a9aa4b70793fe2d4960fa2c6ab78c0cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 15 Nov 2023 13:02:48 +0100 Subject: [PATCH 62/67] bus-auto: fmt --- zkevm-circuits/src/evm_circuit/param.rs | 7 ++----- zkevm-circuits/src/evm_circuit/table.rs | 17 ++++++++++------- zkevm-circuits/src/lib.rs | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index 2e45f38235..e5a579be56 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -21,11 +21,8 @@ pub(crate) const N_PHASE2_COLUMNS: usize = 7; pub const N_PHASE3_COLUMNS: usize = 12; /// Number of Advice Phase1 columns in the EVM circuit -pub(crate) const N_PHASE1_COLUMNS: usize = STEP_WIDTH - - N_PHASE3_COLUMNS - - N_PHASE2_COLUMNS - - N_COPY_COLUMNS - - N_BYTE_LOOKUPS; +pub(crate) const N_PHASE1_COLUMNS: usize = + STEP_WIDTH - N_PHASE3_COLUMNS - N_PHASE2_COLUMNS - N_COPY_COLUMNS - N_BYTE_LOOKUPS; // Number of copy columns pub(crate) const N_COPY_COLUMNS: usize = 2; diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index f4f92fda4c..a89c79db32 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -102,13 +102,16 @@ impl MsgF { } pub fn tx(id: F, field_tag: F, index: F, value: F) -> Self { - Self::Lookup(Table::Tx, vec![ - F::one(), // TODO: can remove the "enabled" field. - id, - field_tag, - index, - value, - ]) + Self::Lookup( + Table::Tx, + vec![ + F::one(), // TODO: can remove the "enabled" field. + id, + field_tag, + index, + value, + ], + ) } } diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index da60a9ee9c..42a6b7c808 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -37,11 +37,11 @@ pub mod rlp_circuit_fsm; pub mod sig_circuit; // we don't use this for aggregation //pub mod root_circuit; +mod evm_bus; pub mod modexp_circuit; pub mod state_circuit; pub mod super_circuit; pub mod table; -mod evm_bus; #[cfg(any(feature = "test", test))] pub mod test_util; From 026ae9de020e1afb9d6a7542c4c831b141f02177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Thu, 30 Nov 2023 16:23:07 +0100 Subject: [PATCH 63/67] bus-auto: sync halo2 branch develop --- Cargo.lock | 2 +- Cargo.toml | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1fd0c42dee..23f6317464 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2414,7 +2414,7 @@ dependencies = [ [[package]] name = "halo2_proofs" version = "0.2.0" -source = "git+https://github.com//scroll-tech/halo2.git?branch=bus-auto#8fc91569172fe266554cb945e1d6801670f7bab1" +source = "git+https://github.com/scroll-tech/halo2.git?branch=develop#5b9a3d71a325a9ecbad164aba90a7f6a8550a015" dependencies = [ "ark-std 0.3.0", "blake2b_simd", diff --git a/Cargo.toml b/Cargo.toml index bb52b29a33..4b1fb48f38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,12 +63,8 @@ url = "2.2" ethers-core = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } ethers-etherscan = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } -# Replace all references to halo2 PSE and Scroll repos with a specific branch. -# Note the double slash // hack to make it a different URL. -[patch."https://github.com/scroll-tech/halo2.git"] -halo2_proofs = { git = "https://github.com//scroll-tech/halo2.git", branch = "bus-auto" } [patch."https://github.com/privacy-scaling-explorations/halo2.git"] -halo2_proofs = { git = "https://github.com//scroll-tech/halo2.git", branch = "bus-auto" } +halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "develop" } [patch."https://github.com/privacy-scaling-explorations/poseidon.git"] poseidon = { git = "https://github.com/scroll-tech/poseidon.git", branch = "scroll-dev-0220" } From 0d548295eb0d6a8ea8915a69c4f821ec931fa323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Thu, 7 Dec 2023 14:39:14 +0100 Subject: [PATCH 64/67] bus-auto: fixes after merge --- zkevm-circuits/src/evm_circuit.rs | 2 ++ zkevm-circuits/src/evm_circuit/execution.rs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index e6bdf3ecf1..7c1dd27b5e 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -441,6 +441,7 @@ impl Circuit for EvmCircuit { BlockTable, CopyTable, KeccakTable, + SHA256Table, ExpTable, SigTable, ModExpTable, @@ -557,6 +558,7 @@ impl Circuit for EvmCircuit { block_table, copy_table, keccak_table, + sha256_table, exp_table, sig_table, modexp_table, diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 8cfe1e08e7..f61410233b 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -771,7 +771,7 @@ impl ExecutionConfig { { let step_enabled = query_expression(meta, |meta| { - let q_usable = meta.query_selector(q_usable); + let q_usable = meta.query_fixed(q_usable, Rotation::cur()); let q_step = meta.query_advice(q_step, Rotation::cur()); q_usable * q_step * state_selector.clone() }); @@ -921,7 +921,7 @@ impl ExecutionConfig { q_usable: Column, cell_manager: &CellManager, ) -> BusPortMulti { - let q_usable = query_expression(meta, |meta| meta.query_selector(q_usable)); + let q_usable = query_expression(meta, |meta| meta.query_fixed(q_usable, Rotation::cur())); let byte_columns = { let mut cols = cell_manager From 9c52286b61d63e3b853c2c5a2fb3a5b58eae69f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 8 Dec 2023 23:21:21 +0100 Subject: [PATCH 65/67] bus-auto: support for parallel assign_regions --- gadgets/src/bus/batch_assigner.rs | 10 +- gadgets/src/bus/bus_builder.rs | 68 +++++++-- zkevm-circuits/src/evm_circuit/execution.rs | 149 ++++++++++++-------- 3 files changed, 160 insertions(+), 67 deletions(-) diff --git a/gadgets/src/bus/batch_assigner.rs b/gadgets/src/bus/batch_assigner.rs index d8e02c1d73..f4e873587e 100644 --- a/gadgets/src/bus/batch_assigner.rs +++ b/gadgets/src/bus/batch_assigner.rs @@ -48,8 +48,7 @@ impl> BatchAssigner { self.assigners.invert().map(|commands| { for (helper, command) in commands { let (offset, term) = command.assign(region, helper); - bus_assigner.add_term(offset, Value::known(term)); - // TODO: Ensure this is a global offset (need Halo2 support). + bus_assigner.add_term(region.global_offset(offset), Value::known(term)); } }); } @@ -119,4 +118,11 @@ impl> BusOpCounter { pub fn is_complete(&self) -> bool { self.counts.is_empty() } + + /// Merge another instance of BusOpCounter into self. The counts are accumulated. + pub fn merge(&mut self, other: Self) { + for (key, other_count) in other.counts { + *self.counts.entry(key).or_insert(0) += other_count; + } + } } diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs index a55801c10a..4dfce86daf 100644 --- a/gadgets/src/bus/bus_builder.rs +++ b/gadgets/src/bus/bus_builder.rs @@ -54,12 +54,17 @@ impl> BusAssigner { Self { batch_assigner: BatchAssigner::new(), codec, - term_adder: TermAdder::new(n_rows), + term_adder: TermAdder::new(0, n_rows), bus_op_counter: BusOpCounter::new(), } } - /// Return the number of rows where the bus must be enabled. + /// Return the first offset supported by this BusAssigner. + pub fn start_offset(&self) -> usize { + self.term_adder.start_offset + } + + /// Return the number of rows supported by this BusAssigner. pub fn n_rows(&self) -> usize { self.term_adder.terms.len() } @@ -86,6 +91,11 @@ impl> BusAssigner { old_batch_assigner.finish(region, self); } + fn assert_finished(&self) { + assert_eq!(self.batch_assigner.len(), 0, "finish_ports was not called"); + // TODO: better error handling. + } + /// Add a term value to the bus. pub fn add_term(&mut self, offset: usize, term: Value) { self.term_adder.add_term(offset, term); @@ -93,22 +103,44 @@ impl> BusAssigner { /// Return the collected terms. pub fn terms(&self) -> Value<&[F]> { - assert_eq!(self.batch_assigner.len(), 0, "finish_ports was not called"); - // TODO: better error handling. - + self.assert_finished(); + assert_eq!( + self.start_offset(), + 0, + "cannot use the terms of a BusAssigner fork" + ); self.term_adder.terms() } + + /// Fork this BusAssigner for parallel assignment. + pub fn fork(&self, start_offset: usize, n_rows: usize) -> Self { + Self { + batch_assigner: BatchAssigner::new(), + codec: self.codec.clone(), + term_adder: TermAdder::new(start_offset, n_rows), + bus_op_counter: BusOpCounter::new(), + } + } + + /// Merge a fork of this BusAssigner back into it. + pub fn merge(&mut self, fork: Self) { + fork.assert_finished(); + self.term_adder.merge(fork.term_adder); + self.bus_op_counter.merge(fork.bus_op_counter); + } } struct TermAdder { + start_offset: usize, terms: Vec, unknown: bool, } impl TermAdder { /// Create a term adder with a maximum number of rows. - fn new(n_rows: usize) -> Self { + fn new(start_offset: usize, n_rows: usize) -> Self { Self { + start_offset, terms: vec![F::zero(); n_rows], unknown: false, } @@ -116,10 +148,10 @@ impl TermAdder { /// Add a term value to the bus. fn add_term(&mut self, offset: usize, term: Value) { + let range = self.start_offset..self.start_offset + self.terms.len(); assert!( - offset < self.terms.len(), - "offset={offset} out of bounds n_rows={}", - self.terms.len() + range.contains(&offset), + "offset={offset} out of bounds ({range:?})" ); if self.unknown { return; @@ -128,7 +160,7 @@ impl TermAdder { self.unknown = true; self.terms.clear(); } else { - term.map(|t| self.terms[offset] += t); + term.map(|t| self.terms[offset - self.start_offset] += t); } } @@ -140,4 +172,20 @@ impl TermAdder { Value::known(&self.terms) } } + + /// Merge another TermAdder::terms() into self. + fn merge(&mut self, other: Self) { + if other.unknown { + self.unknown = true; + self.terms.clear(); + } else { + assert!(other.start_offset >= self.start_offset); + assert!(other.start_offset + other.terms.len() <= self.start_offset + self.terms.len()); + let start_index = other.start_offset - self.start_offset; + + for (index, term) in other.terms.into_iter().enumerate() { + self.terms[start_index + index] += term; + } + } + } } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index f61410233b..9a0d2d7e6b 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -1060,8 +1060,7 @@ impl ExecutionConfig { self.q_step, height - 1, || Value::known(F::zero()), - )?; - Ok(height) + ) }; let dummy_tx = Transaction::default(); @@ -1161,13 +1160,15 @@ impl ExecutionConfig { }; // Step1: assign real steps - let (region1_chunk_size, region1_chunk_num) = - chunking_fn("region1", step_assignments.len(), 50); - let mut region1_is_first_time: Vec<(usize, bool)> = (0..region1_chunk_num) - .map(|chunk_idx| (chunk_idx, true)) - .collect(); - let region1_height_sum = layouter - .assign_regions( + { + // Prepare the thread states. + let (region1_chunk_size, region1_chunk_num) = + chunking_fn("region1", step_assignments.len(), 50); + let mut region1_is_first_time: Vec<(usize, bool)> = (0..region1_chunk_num) + .map(|chunk_idx| (chunk_idx, true)) + .collect(); + // Assign in parallel. + let bus_assigner_forks = layouter.assign_regions( || "Execution step region1", region1_is_first_time .iter_mut() @@ -1185,10 +1186,14 @@ impl ExecutionConfig { .sum::(); if *is_first_time { *is_first_time = false; - return assign_shape_fn(&mut region, total_height); + assign_shape_fn(&mut region, total_height)?; + return Ok(None); } let mut offset = 0; + let mut bus_assigner_fork = + bus_assigner.fork(region.global_offset(0), total_height); + // Annotate the EVMCircuit columns within it's single region. self.annotate_circuit(&mut region); @@ -1218,7 +1223,7 @@ impl ExecutionConfig { self.assign_exec_step( &mut region, - bus_assigner, + &mut bus_assigner_fork, offset, block, transaction, @@ -1233,58 +1238,90 @@ impl ExecutionConfig { offset += height; } + + bus_assigner_fork.finish_ports(&mut region); debug_assert_eq!(offset, total_height); - Ok(total_height) + Ok(Some(bus_assigner_fork)) } }) .collect_vec(), - )? - .into_iter() - .sum::(); + )?; + // Merge the thread results. + let mut actual_offset = 0; + for bus_assigner_fork in bus_assigner_forks { + let bus_assigner_fork = bus_assigner_fork.unwrap(); + + // Validate the offsets found by assign_regions. + assert_eq!(actual_offset, bus_assigner_fork.start_offset()); + actual_offset += bus_assigner_fork.n_rows(); - debug_assert_eq!(region1_height, region1_height_sum); + bus_assigner.merge(bus_assigner_fork); + } + assert_eq!(region1_height, actual_offset); + } // part2: assign non-last EndBlock steps when padding needed + { + // Prepare the thread states. + let (region2_chunk_size, region2_chunk_num) = + chunking_fn("region2", region2_height, 300); + let idxs: Vec = (0..region2_height).collect(); + let mut region2_is_first_time = vec![true; region2_chunk_num]; - let (region2_chunk_size, region2_chunk_num) = chunking_fn("region2", region2_height, 300); - let idxs: Vec = (0..region2_height).collect(); - let mut region2_is_first_time = vec![true; region2_chunk_num]; + log::trace!( + "assign non-last EndBlock in range [{},{})", + region1_height, + region1_height + region2_height + ); + let bus_assigner_forks = layouter.assign_regions( + || "Execution step region2", + idxs.chunks(region2_chunk_size) + .zip_eq(region2_is_first_time.iter_mut()) + .map(|(rows, is_first_time)| { + |mut region: Region<'_, F>| { + if *is_first_time { + *is_first_time = false; + assign_shape_fn(&mut region, rows.len())?; + return Ok(None); + } - log::trace!( - "assign non-last EndBlock in range [{},{})", - region1_height, - region1_height + region2_height - ); - layouter.assign_regions( - || "Execution step region2", - idxs.chunks(region2_chunk_size) - .zip_eq(region2_is_first_time.iter_mut()) - .map(|(rows, is_first_time)| { - |mut region: Region<'_, F>| { - if *is_first_time { - *is_first_time = false; - return assign_shape_fn(&mut region, rows.len()); - } - self.assign_same_exec_step_in_range( - &mut region, - bus_assigner, - 0, - rows.len(), - block, - &dummy_tx, - &last_call, - end_block_not_last, - 1, - challenges, - )?; - for row_idx in 0..rows.len() { - self.assign_q_step(&mut region, &inverter, row_idx, 1)?; + let mut bus_assigner_fork = + bus_assigner.fork(region.global_offset(0), rows.len()); + + self.assign_same_exec_step_in_range( + &mut region, + &mut bus_assigner_fork, + 0, + rows.len(), + block, + &dummy_tx, + &last_call, + end_block_not_last, + 1, + challenges, + )?; + for row_idx in 0..rows.len() { + self.assign_q_step(&mut region, &inverter, row_idx, 1)?; + } + bus_assigner_fork.finish_ports(&mut region); + Ok(Some(bus_assigner_fork)) } - Ok(rows.len()) - } - }) - .collect_vec(), - )?; + }) + .collect_vec(), + )?; + // Merge the thread results. + let mut actual_offset = region1_height; + for bus_assigner_fork in bus_assigner_forks { + let bus_assigner_fork = bus_assigner_fork.unwrap(); + + // Validate the offsets found by assign_regions. + assert_eq!(actual_offset, bus_assigner_fork.start_offset()); + actual_offset += bus_assigner_fork.n_rows(); + + bus_assigner.merge(bus_assigner_fork); + } + assert_eq!(region1_height + region2_height, actual_offset); + } // part3: assign the last EndBlock at offset `evm_rows - 1` // This region don't need to be parallelized @@ -1299,7 +1336,8 @@ impl ExecutionConfig { |mut region| { if region3_is_first_time { region3_is_first_time = false; - return assign_shape_fn(&mut region, region3_height); + assign_shape_fn(&mut region, region3_height)?; + return Ok(()); } self.assign_exec_step( &mut region, @@ -1331,7 +1369,8 @@ impl ExecutionConfig { bus_assigner.finish_ports(&mut region); - Ok(2) // region height + assert_eq!(region.global_offset(0), region1_height + region2_height); + Ok(()) }, )?; From 7f1803238223a7d22b810c400a07693be5b55b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Sat, 9 Dec 2023 00:11:30 +0100 Subject: [PATCH 66/67] bus-auto: support for the SHA256 table --- zkevm-circuits/src/evm_bus.rs | 24 +++++++++++++++++++++--- zkevm-circuits/src/evm_circuit.rs | 1 + zkevm-circuits/src/super_circuit.rs | 2 ++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/zkevm-circuits/src/evm_bus.rs b/zkevm-circuits/src/evm_bus.rs index dc15b4695d..6dedd97cda 100644 --- a/zkevm-circuits/src/evm_bus.rs +++ b/zkevm-circuits/src/evm_bus.rs @@ -2,7 +2,7 @@ use crate::{ evm_circuit::table::{Lookup, MsgExpr, MsgF, RwValues}, table::{ BlockTable, BytecodeTable, CopyTable, DualByteTable, EccTable, ExpTable, FixedTable, - KeccakTable, ModExpTable, PowOfRandTable, RwTable, SigTable, TxTable, + KeccakTable, ModExpTable, PowOfRandTable, RwTable, SHA256Table, SigTable, TxTable, }, util::{assign_global, query_expression}, }; @@ -20,7 +20,7 @@ use halo2_proofs::{ /// EVMBusLookups makes all lookup tables available on the bus. #[derive(Clone, Debug)] pub struct EVMBusLookups { - bus_tables: [BusTable; 13], + bus_tables: [BusTable; 14], } impl EVMBusLookups { @@ -37,13 +37,14 @@ impl EVMBusLookups { block_table: &BlockTable, copy_table: &CopyTable, keccak_table: &KeccakTable, + sha256_table: &SHA256Table, exp_table: &ExpTable, sig_table: &SigTable, modexp_table: &ModExpTable, ecc_table: &EccTable, pow_of_rand_table: &PowOfRandTable, ) -> Self { - let tables: [&dyn QueryTable; 13] = [ + let tables: [&dyn QueryTable; 14] = [ dual_byte_table, fixed_table, rw_table, @@ -52,6 +53,7 @@ impl EVMBusLookups { block_table, copy_table, keccak_table, + sha256_table, exp_table, sig_table, modexp_table, @@ -315,6 +317,22 @@ impl QueryTable for KeccakTable { } } +impl QueryTable for SHA256Table { + fn enabled(&self, meta: &mut VirtualCells) -> Expression { + // This is a boolean because of constraint "final_is_bool". + meta.query_fixed(self.q_enable, Rotation::cur()) + * meta.query_advice(self.is_final, Rotation::cur()) + } + + fn message(&self, meta: &mut VirtualCells) -> MsgExpr { + MsgExpr::lookup(Lookup::Sha256Table { + input_rlc: meta.query_advice(self.input_rlc, Rotation::cur()), + input_len: meta.query_advice(self.input_len, Rotation::cur()), + output_rlc: meta.query_advice(self.output_rlc, Rotation::cur()), + }) + } +} + impl QueryTable for ExpTable { fn enabled(&self, meta: &mut VirtualCells) -> Expression { // is_step implies q_enable by fixed assignment. diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 7c1dd27b5e..423b019a87 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -511,6 +511,7 @@ impl Circuit for EvmCircuit { &block_table, ©_table, &keccak_table, + &sha256_table, &exp_table, &sig_table, &modexp_table, diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index ce88802a94..784c808a84 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -230,6 +230,7 @@ impl SubCircuitConfig for SuperCircuitConfig { block_table.annotate_columns(meta); copy_table.annotate_columns(meta); keccak_table.annotate_columns(meta); + sha256_table.annotate_columns(meta); exp_table.annotate_columns(meta); sig_table.annotate_columns(meta); modexp_table.annotate_columns(meta); @@ -411,6 +412,7 @@ impl SubCircuitConfig for SuperCircuitConfig { &block_table, ©_table, &keccak_table, + &sha256_table, &exp_table, &sig_table, &modexp_table, From 5ede0c6c7d664fdc55a5ed1218bb90ca66ff84ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Sat, 9 Dec 2023 00:21:15 +0100 Subject: [PATCH 67/67] bus-auto: clippy --- gadgets/src/bus/port_multi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gadgets/src/bus/port_multi.rs b/gadgets/src/bus/port_multi.rs index cb4357c7f5..f6a349cd55 100644 --- a/gadgets/src/bus/port_multi.rs +++ b/gadgets/src/bus/port_multi.rs @@ -144,7 +144,7 @@ impl PortBatched { // counts_times_others = ∑ counts[i] * other_denoms[i] let counts_times_others = ops .iter() - .zip_eq(other_denoms.into_iter()) + .zip_eq(other_denoms) .map(|(op, other)| op.count() * other) .reduce(|acc, term| acc + term) .unwrap_or(0.expr()); @@ -185,7 +185,7 @@ impl PortBatched { // = (∑ counts[i] * other_denoms[i]) / all_denoms let numer = ops .iter() - .zip_eq(other_denoms.into_iter()) + .zip_eq(other_denoms) .map(|(op, others)| from_isize::(op.count()) * others) .reduce(|sum, term| sum + term) .unwrap_or(F::zero());