use std::collections::HashMap;
use eth_types::Field;
use gadgets::util::Expr;
use halo2_proofs::{
circuit::{AssignedCell, Value},
plonk::{Advice, Column, ConstraintSystem, Error, Expression, VirtualCells},
poly::Rotation,
};
use crate::{
evm_circuit::{table::Table, util::CachedRegion},
util::query_expression,
};
pub(crate) use super::cell_placement_strategy::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) enum CellType {
StoragePhase1,
StoragePhase2,
StoragePermutation,
Lookup(Table),
}
impl CellType {
pub(crate) fn expr_phase<F: Field>(expr: &Expression<F>) -> u8 {
use Expression::*;
match expr {
Challenge(challenge) => challenge.phase() + 1,
Advice(query) => query.phase(),
Constant(_) | Selector(_) | Fixed(_) | Instance(_) => 0,
Negated(a) | Expression::Scaled(a, _) => Self::expr_phase(a),
Sum(a, b) | Product(a, b) => std::cmp::max(Self::expr_phase(a), Self::expr_phase(b)),
}
}
pub(crate) fn storage_for_phase(phase: u8) -> CellType {
match phase {
0 => CellType::StoragePhase1,
1 => CellType::StoragePhase2,
_ => unreachable!(),
}
}
pub(crate) fn storage_for_expr<F: Field>(expr: &Expression<F>) -> CellType {
Self::storage_for_phase(Self::expr_phase::<F>(expr))
}
}
#[derive(Clone, Debug)]
pub struct Cell<F> {
expression: Option<Expression<F>>,
column: Option<Column<Advice>>,
column_idx: usize,
rotation: usize,
}
impl<F: Field> Cell<F> {
pub fn new(
meta: &mut VirtualCells<F>,
column: Column<Advice>,
column_idx: usize,
rotation: usize,
) -> Cell<F> {
Cell {
expression: Some(meta.query_advice(column, Rotation(rotation as i32))),
column: Some(column),
column_idx,
rotation,
}
}
pub fn new_from_cs(
meta: &mut ConstraintSystem<F>,
column: Column<Advice>,
column_idx: usize,
rotation: usize,
) -> Cell<F> {
query_expression(meta, |meta| Cell::new(meta, column, column_idx, rotation))
}
pub(crate) fn assign(
&self,
region: &mut CachedRegion<'_, '_, F>,
offset: usize,
value: Value<F>,
) -> Result<AssignedCell<F, F>, Error> {
region.assign_advice(
|| {
format!(
"Cell column: {:?} and rotation: {}",
self.column, self.rotation
)
},
self.column.expect("wrong operation on value only cell"),
offset + self.rotation,
|| value,
)
}
pub(crate) fn at_offset(&self, meta: &mut ConstraintSystem<F>, offset: i32) -> Self {
Self::new_from_cs(
meta,
self.column.expect("wrong operation on value only cell"),
self.column_idx,
(self.rotation as i32 + offset) as usize,
)
}
}
impl<F> Cell<F> {
pub(crate) fn new_value(column_idx: usize, rotation: usize) -> Self {
Self {
expression: None,
column: None,
column_idx,
rotation,
}
}
pub(crate) fn get_column_idx(&self) -> usize {
self.column_idx
}
pub(crate) fn get_rotation(&self) -> usize {
self.rotation
}
}
impl<F: Field> Expr<F> for Cell<F> {
fn expr(&self) -> Expression<F> {
self.expression
.clone()
.expect("wrong operation on value only cell")
}
}
impl<F: Field> Expr<F> for &Cell<F> {
fn expr(&self) -> Expression<F> {
self.expression
.clone()
.expect("wrong operation on value only cell")
}
}
#[derive(Debug, Clone)]
pub(crate) struct CellColumn {
pub advice: Column<Advice>,
pub cell_type: CellType,
pub idx: usize,
}
impl CellColumn {
pub fn new(advice: Column<Advice>, cell_type: CellType, idx: usize) -> CellColumn {
CellColumn {
advice,
cell_type,
idx,
}
}
pub fn expr<F: Field>(&self, meta: &mut ConstraintSystem<F>) -> Expression<F> {
query_expression(meta, |meta| meta.query_advice(self.advice, Rotation::cur()))
}
pub fn expr_vc<F: Field>(&self, meta: &mut VirtualCells<F>) -> Expression<F> {
meta.query_advice(self.advice, Rotation::cur())
}
}
pub(crate) struct CellPlacement {
pub column: CellColumn,
pub rotation: usize,
}
pub(crate) struct CellPlacementValue {
pub column_idx: usize,
pub rotation: usize,
}
pub(crate) trait CellPlacementStrategy {
type Stats;
type Affinity;
fn on_creation(&mut self, columns: &mut CellManagerColumns);
fn place_cell<F: Field>(
&mut self,
columns: &mut CellManagerColumns,
meta: &mut ConstraintSystem<F>,
cell_type: CellType,
) -> CellPlacement;
fn place_cell_with_affinity<F: Field>(
&mut self,
columns: &mut CellManagerColumns,
meta: &mut ConstraintSystem<F>,
cell_type: CellType,
affinity: Self::Affinity,
) -> CellPlacement;
fn place_cell_value(
&mut self,
columns: &mut CellManagerColumns,
cell_type: CellType,
) -> CellPlacementValue;
fn place_cell_value_with_affinity(
&mut self,
columns: &mut CellManagerColumns,
cell_type: CellType,
affinity: Self::Affinity,
) -> CellPlacementValue;
fn get_height(&self) -> usize;
fn get_stats(&self, columns: &CellManagerColumns) -> Self::Stats;
}
#[derive(Default, Debug, Clone)]
pub(crate) struct CellManagerColumns {
columns: HashMap<CellType, Vec<CellColumn>>,
columns_list: Vec<CellColumn>,
}
impl CellManagerColumns {
pub fn add_column(&mut self, cell_type: CellType, column: Column<Advice>) {
let idx = self.columns_list.len();
let cell_column = CellColumn::new(column, cell_type, idx);
self.columns_list.push(cell_column.clone());
self.columns
.entry(cell_type)
.and_modify(|columns| columns.push(cell_column.clone()))
.or_insert(vec![cell_column]);
}
pub fn get_cell_type_width(&self, cell_type: CellType) -> usize {
if let Some(columns) = self.columns.get(&cell_type) {
columns.len()
} else {
0
}
}
pub fn get_column(&self, cell_type: CellType, column_idx: usize) -> Option<&CellColumn> {
if let Some(columns) = self.columns.get(&cell_type) {
columns.get(column_idx)
} else {
None
}
}
pub fn columns(&self) -> Vec<CellColumn> {
self.columns_list.clone()
}
#[allow(dead_code, reason = "under active development")]
pub fn get_width(&self) -> usize {
self.columns_list.len()
}
}
#[derive(Clone, Debug)]
pub(crate) struct CellManager<S: CellPlacementStrategy> {
columns: CellManagerColumns,
strategy: S,
}
impl<Stats, S: CellPlacementStrategy<Stats = Stats>> CellManager<S> {
pub fn new(mut strategy: S) -> CellManager<S> {
let mut columns = CellManagerColumns::default();
strategy.on_creation(&mut columns);
CellManager { columns, strategy }
}
pub fn query_cell<F: Field>(
&mut self,
meta: &mut ConstraintSystem<F>,
cell_type: CellType,
) -> Cell<F> {
let placement = self.strategy.place_cell(&mut self.columns, meta, cell_type);
Cell::new_from_cs(
meta,
placement.column.advice,
placement.column.idx,
placement.rotation,
)
}
pub fn query_cell_with_affinity<F: Field>(
&mut self,
meta: &mut ConstraintSystem<F>,
cell_type: CellType,
affinity: S::Affinity,
) -> Cell<F> {
let placement =
self.strategy
.place_cell_with_affinity(&mut self.columns, meta, cell_type, affinity);
Cell::new_from_cs(
meta,
placement.column.advice,
placement.column.idx,
placement.rotation,
)
}
pub fn query_cells<F: Field>(
&mut self,
meta: &mut ConstraintSystem<F>,
cell_type: CellType,
count: usize,
) -> Vec<Cell<F>> {
(0..count)
.map(|_| self.query_cell(meta, cell_type))
.collect()
}
pub fn query_cell_value<F>(&mut self, cell_type: CellType) -> Cell<F> {
let placement = self.strategy.place_cell_value(&mut self.columns, cell_type);
Cell::new_value(placement.column_idx, placement.rotation)
}
pub fn query_cell_value_with_affinity<F>(
&mut self,
cell_type: CellType,
affinity: S::Affinity,
) -> Cell<F> {
let placement =
self.strategy
.place_cell_value_with_affinity(&mut self.columns, cell_type, affinity);
Cell::new_value(placement.column_idx, placement.rotation)
}
pub fn get_height(&self) -> usize {
self.strategy.get_height()
}
pub fn columns(&self) -> Vec<CellColumn> {
self.columns.columns()
}
#[allow(dead_code, reason = "under active development")]
pub fn get_width(&self) -> usize {
self.columns.get_width()
}
pub fn get_stats(&self) -> Stats {
self.strategy.get_stats(&self.columns)
}
pub fn get_strategy(&mut self) -> &mut S {
&mut self.strategy
}
}