diff --git a/src/circuit/floor_planner/single_pass.rs b/src/circuit/floor_planner/single_pass.rs index 82aa8329..9bfad5eb 100644 --- a/src/circuit/floor_planner/single_pass.rs +++ b/src/circuit/floor_planner/single_pass.rs @@ -29,8 +29,9 @@ impl FloorPlanner for SimpleFloorPlanner { cs: &mut CS, circuit: &C, config: C::Config, + constants: Vec>, ) -> Result<(), Error> { - let layouter = SingleChipLayouter::new(cs)?; + let layouter = SingleChipLayouter::new(cs, constants)?; circuit.synthesize(config, layouter) } } @@ -38,6 +39,7 @@ impl FloorPlanner for SimpleFloorPlanner { /// A [`Layouter`] for a single-chip circuit. pub struct SingleChipLayouter<'a, F: Field, CS: Assignment + 'a> { cs: &'a mut CS, + constants: Vec>, /// Stores the starting row for each region. regions: Vec, /// Stores the first empty row for each column. @@ -56,9 +58,10 @@ impl<'a, F: Field, CS: Assignment + 'a> fmt::Debug for SingleChipLayouter<'a, impl<'a, F: Field, CS: Assignment> SingleChipLayouter<'a, F, CS> { /// Creates a new single-chip layouter. - pub fn new(cs: &'a mut CS) -> Result { + pub fn new(cs: &'a mut CS, constants: Vec>) -> Result { let ret = SingleChipLayouter { cs, + constants, regions: vec![], columns: HashMap::default(), _marker: PhantomData, @@ -143,6 +146,8 @@ impl<'a, F: Field, CS: Assignment + 'a> Layouter for SingleChipLayouter<'a struct SingleChipLayouterRegion<'r, 'a, F: Field, CS: Assignment + 'a> { layouter: &'r mut SingleChipLayouter<'a, F, CS>, region_index: RegionIndex, + // The index of the next available row in the first constants column. + next_constant_row: usize, } impl<'r, 'a, F: Field, CS: Assignment + 'a> fmt::Debug @@ -161,6 +166,7 @@ impl<'r, 'a, F: Field, CS: Assignment + 'a> SingleChipLayouterRegion<'r, 'a, SingleChipLayouterRegion { layouter, region_index, + next_constant_row: 0, } } } @@ -202,6 +208,39 @@ impl<'r, 'a, F: Field, CS: Assignment + 'a> RegionLayouter }) } + fn assign_advice_from_constant<'v>( + &'v mut self, + annotation: &'v (dyn Fn() -> String + 'v), + column: Column, + offset: usize, + constant: Assigned, + ) -> Result { + if self.layouter.constants.is_empty() { + return Err(Error::NotEnoughColumnsForConstants); + } + + let advice = self.assign_advice(annotation, column, offset, &mut || Ok(constant))?; + + // For the simple layouter, just assign the fixed constants inside the region + // using the first constants column. self.next_constant_row is updated by this + // function call. + let fixed = self.assign_fixed( + annotation, + self.layouter.constants[0], + // Convert the absolute row into a relative offset within this region, but + // always at an offset that is not before this region (taking advantage of the + // fact that for this single-pass layouter, regions are always layed out in + // increasing row order). + self.next_constant_row + .saturating_sub(*self.layouter.regions[*self.region_index]), + &mut || Ok(constant), + )?; + + self.constrain_equal(advice, fixed)?; + + Ok(advice) + } + fn assign_advice_from_instance<'v>( &mut self, annotation: &'v (dyn Fn() -> String + 'v), @@ -233,12 +272,17 @@ impl<'r, 'a, F: Field, CS: Assignment + 'a> RegionLayouter offset: usize, to: &'v mut (dyn FnMut() -> Result, Error> + 'v), ) -> Result { - self.layouter.cs.assign_fixed( - annotation, - column, - *self.layouter.regions[*self.region_index] + offset, - to, - )?; + let row = *self.layouter.regions[*self.region_index] + offset; + + self.layouter.cs.assign_fixed(annotation, column, row, to)?; + + if let Some(c) = self.layouter.constants.first() { + if c == &column { + // Ensure that the next row we will assign a constant to is always after any + // prior assignments (for simplicity). + self.next_constant_row = cmp::max(self.next_constant_row, row + 1); + } + } Ok(Cell { region_index: self.region_index, diff --git a/src/circuit/layouter.rs b/src/circuit/layouter.rs index 2c6a0e04..8a32e6e5 100644 --- a/src/circuit/layouter.rs +++ b/src/circuit/layouter.rs @@ -166,6 +166,17 @@ impl RegionLayouter for RegionShape { }) } + fn assign_advice_from_constant<'v>( + &'v mut self, + annotation: &'v (dyn Fn() -> String + 'v), + column: Column, + offset: usize, + constant: Assigned, + ) -> Result { + // The rest is identical to witnessing an advice cell. + self.assign_advice(annotation, column, offset, &mut || Ok(constant)) + } + fn assign_advice_from_instance<'v>( &mut self, _: &'v (dyn Fn() -> String + 'v), diff --git a/src/plonk.rs b/src/plonk.rs index af0c7a9b..2a44e26a 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -154,6 +154,9 @@ pub enum Error { NotEnoughRowsAvailable, /// Instance provided exceeds number of available rows InstanceTooLarge, + /// Circuit synthesis requires global constants, but circuit configuration did not + /// call [`ConstraintSystem::enable_constant`] on fixed columns with sufficient space. + NotEnoughColumnsForConstants, } impl ProvingKey { diff --git a/tests/plonk_api.rs b/tests/plonk_api.rs index ef68a429..3f239bc3 100644 --- a/tests/plonk_api.rs +++ b/tests/plonk_api.rs @@ -924,6 +924,7 @@ fn plonk_api() { ], }, ], + constants: [], minimum_degree: None, }, fixed_commitments: [