diff --git a/halo2_proofs/src/dev.rs b/halo2_proofs/src/dev.rs index 35fce1daf6..d38228f980 100644 --- a/halo2_proofs/src/dev.rs +++ b/halo2_proofs/src/dev.rs @@ -15,9 +15,11 @@ use crate::{ arithmetic::{FieldExt, Group}, circuit, plonk::{ - permutation, Advice, Any, Assigned, Assignment, Challenge, Circuit, Column, ColumnType, - ConstraintSystem, Error, Expression, Fixed, FloorPlanner, Instance, Phase, Selector, - VirtualCell, + permutation, + sealed::{self, SealedPhase}, + Advice, Any, Assigned, Assignment, Challenge, Circuit, Column, ColumnType, + ConstraintSystem, Error, Expression, FirstPhase, Fixed, FloorPlanner, Instance, Phase, + Selector, VirtualCell, }, poly::Rotation, }; @@ -307,6 +309,14 @@ pub struct MockProver { // A range of available rows for assignment and copies. usable_rows: Range, + + current_phase: sealed::Phase, +} + +impl MockProver { + fn in_phase(&self, phase: P) -> bool { + self.current_phase == phase.to_sealed() + } } impl Assignment for MockProver { @@ -315,6 +325,10 @@ impl Assignment for MockProver { NR: Into, N: FnOnce() -> NR, { + if !self.in_phase(FirstPhase) { + return; + } + assert!(self.current_region.is_none()); self.current_region = Some(Region { name: name().into(), @@ -327,6 +341,10 @@ impl Assignment for MockProver { } fn exit_region(&mut self) { + if !self.in_phase(FirstPhase) { + return; + } + self.regions.push(self.current_region.take().unwrap()); } @@ -335,6 +353,10 @@ impl Assignment for MockProver { A: FnOnce() -> AR, AR: Into, { + if !self.in_phase(FirstPhase) { + return; + } + if let Some(region) = self.current_region.as_mut() { region .annotations @@ -347,6 +369,10 @@ impl Assignment for MockProver { A: FnOnce() -> AR, AR: Into, { + if !self.in_phase(FirstPhase) { + return Ok(()); + } + if !self.usable_rows.contains(&row) { return Err(Error::not_enough_rows_available(self.k)); } @@ -395,25 +421,44 @@ impl Assignment for MockProver { A: FnOnce() -> AR, AR: Into, { - if !self.usable_rows.contains(&row) { - return Err(Error::not_enough_rows_available(self.k)); - } + if self.in_phase(FirstPhase) { + if !self.usable_rows.contains(&row) { + return Err(Error::not_enough_rows_available(self.k)); + } - if let Some(region) = self.current_region.as_mut() { - region.update_extent(column.into(), row); - region - .cells - .entry((column.into(), row)) - .and_modify(|count| *count += 1) - .or_default(); + if let Some(region) = self.current_region.as_mut() { + region.update_extent(column.into(), row); + region + .cells + .entry((column.into(), row)) + .and_modify(|count| *count += 1) + .or_default(); + } } - *self - .advice - .get_mut(column.index()) - .and_then(|v| v.get_mut(row)) - .ok_or(Error::BoundsFailure)? = - CellValue::Assigned(to().into_field().evaluate().assign()?); + match to().into_field().evaluate().assign() { + Ok(to) => { + let value = self + .advice + .get_mut(column.index()) + .and_then(|v| v.get_mut(row)) + .ok_or(Error::BoundsFailure)?; + if let CellValue::Assigned(value) = value { + // Inconsistent assignment between different phases. + if value != &to { + return Err(Error::Synthesis); + } + } else { + *value = CellValue::Assigned(to); + } + } + Err(err) => { + // Propagate `assign` error if the column is in current phase. + if self.in_phase(column.column_type().phase) { + return Err(err); + } + } + } Ok(()) } @@ -431,6 +476,10 @@ impl Assignment for MockProver { A: FnOnce() -> AR, AR: Into, { + if !self.in_phase(FirstPhase) { + return Ok(()); + } + if !self.usable_rows.contains(&row) { return Err(Error::not_enough_rows_available(self.k)); } @@ -461,6 +510,10 @@ impl Assignment for MockProver { right_column: Column, right_row: usize, ) -> Result<(), crate::plonk::Error> { + if !self.in_phase(FirstPhase) { + return Ok(()); + } + if !self.usable_rows.contains(&left_row) || !self.usable_rows.contains(&right_row) { return Err(Error::not_enough_rows_available(self.k)); } @@ -475,6 +528,10 @@ impl Assignment for MockProver { from_row: usize, to: circuit::Value>, ) -> Result<(), Error> { + if !self.in_phase(FirstPhase) { + return Ok(()); + } + if !self.usable_rows.contains(&from_row) { return Err(Error::not_enough_rows_available(self.k)); } @@ -487,6 +544,10 @@ impl Assignment for MockProver { } fn get_challenge(&self, challenge: Challenge) -> circuit::Value { + if self.current_phase <= challenge.phase { + return circuit::Value::unknown(); + } + circuit::Value::known(self.challenges[challenge.index()]) } @@ -581,9 +642,18 @@ impl MockProver { challenges, permutation, usable_rows: 0..usable_rows, + current_phase: FirstPhase.to_sealed(), }; - ConcreteCircuit::FloorPlanner::synthesize(&mut prover, circuit, config, constants)?; + for current_phase in prover.cs.phases() { + prover.current_phase = current_phase; + ConcreteCircuit::FloorPlanner::synthesize( + &mut prover, + circuit, + config.clone(), + constants.clone(), + )?; + } let (cs, selector_polys) = prover.cs.compress_selectors(prover.selectors.clone()); prover.cs = cs; diff --git a/halo2_proofs/src/plonk/circuit.rs b/halo2_proofs/src/plonk/circuit.rs index 964ec7bf24..da4cf5995d 100644 --- a/halo2_proofs/src/plonk/circuit.rs +++ b/halo2_proofs/src/plonk/circuit.rs @@ -76,6 +76,12 @@ pub(crate) mod sealed { } } + impl SealedPhase for Phase { + fn to_sealed(self) -> Phase { + self + } + } + /// Sealed trait to help keep `Phase` private. pub trait SealedPhase { fn to_sealed(self) -> Phase; @@ -496,7 +502,7 @@ impl TableColumn { #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct Challenge { index: usize, - phase: sealed::Phase, + pub(crate) phase: sealed::Phase, } impl Challenge {