1use std::fmt;
2use std::marker::PhantomData;
3
4use ff::Field;
5use rustc_hash::FxHashMap;
6
7use crate::circuit::AssignedCell;
8use crate::{
9 circuit::{
10 layouter::{RegionColumn, RegionLayouter, SyncDeps, TableLayouter},
11 table_layouter::{compute_table_lengths, SimpleTableLayouter},
12 Cell, Layouter, Region, RegionIndex, Table, Value,
13 },
14 plonk::{
15 Advice, Any, Assigned, Assignment, Challenge, Circuit, Column, Error, Fixed, FloorPlanner,
16 Instance, Selector, TableColumn,
17 },
18};
19
20#[derive(Debug)]
26pub struct SimpleFloorPlanner;
27
28impl FloorPlanner for SimpleFloorPlanner {
29 fn synthesize<F: Field, CS: Assignment<F> + SyncDeps, C: Circuit<F>>(
30 cs: &mut CS,
31 circuit: &C,
32 config: C::Config,
33 constants: Vec<Column<Fixed>>,
34 ) -> Result<(), Error> {
35 let layouter = SingleChipLayouter::new(cs, constants)?;
36 circuit.synthesize(config, layouter)
37 }
38}
39
40pub struct SingleChipLayouter<'a, F: Field, CS: Assignment<F> + 'a> {
42 cs: &'a mut CS,
43 constants: Vec<Column<Fixed>>,
44 columns: FxHashMap<RegionColumn, usize>,
49 table_columns: Vec<TableColumn>,
51 _marker: PhantomData<F>,
52}
53
54impl<'a, F: Field, CS: Assignment<F> + 'a> fmt::Debug for SingleChipLayouter<'a, F, CS> {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 f.debug_struct("SingleChipLayouter")
57 .field("columns", &self.columns)
59 .finish()
60 }
61}
62
63impl<'a, F: Field, CS: Assignment<F>> SingleChipLayouter<'a, F, CS> {
64 pub fn new(cs: &'a mut CS, constants: Vec<Column<Fixed>>) -> Result<Self, Error> {
66 let ret = SingleChipLayouter {
67 cs,
68 constants,
69 columns: FxHashMap::default(),
71 table_columns: vec![],
72 _marker: PhantomData,
73 };
74 Ok(ret)
75 }
76}
77
78impl<'a, F: Field, CS: Assignment<F> + 'a + SyncDeps> Layouter<F>
79 for SingleChipLayouter<'a, F, CS>
80{
81 type Root = Self;
82
83 fn assign_region<A, AR, N, NR>(&mut self, name: N, assignment: A) -> Result<AR, Error>
84 where
85 A: FnOnce(Region<'_, F>) -> Result<AR, Error>,
86 N: Fn() -> NR,
87 NR: Into<String>,
88 {
89 self.cs.enter_region(name);
114 let mut region = SingleChipLayouterRegion::new(self, 0.into()); let result = {
116 let region: &mut dyn RegionLayouter<F> = &mut region;
117 assignment(region.into())
118 }?;
119 let constants_to_assign = region.constants;
120 self.cs.exit_region();
121
122 if self.constants.is_empty() {
125 if !constants_to_assign.is_empty() {
126 return Err(Error::NotEnoughColumnsForConstants);
127 }
128 } else {
129 let constants_column = self.constants[0];
130 let next_constant_row = self
131 .columns
132 .entry(Column::<Any>::from(constants_column).into())
133 .or_default();
134 for (constant, advice) in constants_to_assign {
135 self.cs.assign_fixed(
136 constants_column,
138 *next_constant_row,
139 constant,
140 );
141 self.cs.copy(
142 constants_column.into(),
143 *next_constant_row,
144 advice.column,
145 advice.row_offset, );
147 *next_constant_row += 1;
148 }
149 }
150
151 Ok(result)
152 }
153
154 fn assign_table<A, N, NR>(&mut self, name: N, mut assignment: A) -> Result<(), Error>
155 where
156 A: FnMut(Table<'_, F>) -> Result<(), Error>,
157 N: Fn() -> NR,
158 NR: Into<String>,
159 {
160 self.cs.enter_region(name);
163 let mut table = SimpleTableLayouter::new(self.cs, &self.table_columns);
164 {
165 let table: &mut dyn TableLayouter<F> = &mut table;
166 assignment(table.into())
167 }?;
168 let default_and_assigned = table.default_and_assigned;
169 self.cs.exit_region();
170
171 let first_unused = compute_table_lengths(&default_and_assigned)?;
174
175 for column in default_and_assigned.keys() {
177 self.table_columns.push(*column);
178 }
179
180 for (col, (default_val, _)) in default_and_assigned {
181 self.cs
185 .fill_from_row(col.inner(), first_unused, default_val.unwrap())?;
186 }
187
188 Ok(())
189 }
190
191 fn constrain_instance(&mut self, cell: Cell, instance: Column<Instance>, row: usize) {
192 self.cs.copy(
193 cell.column,
194 cell.row_offset, instance.into(),
196 row,
197 );
198 }
199
200 fn get_challenge(&self, challenge: Challenge) -> Value<F> {
201 self.cs.get_challenge(challenge)
202 }
203
204 fn next_phase(&mut self) {
205 self.cs.next_phase();
206 }
207
208 fn get_root(&mut self) -> &mut Self::Root {
209 self
210 }
211
212 fn push_namespace<NR, N>(&mut self, name_fn: N)
213 where
214 NR: Into<String>,
215 N: FnOnce() -> NR,
216 {
217 self.cs.push_namespace(name_fn)
218 }
219
220 fn pop_namespace(&mut self, gadget_name: Option<String>) {
221 self.cs.pop_namespace(gadget_name)
222 }
223}
224
225struct SingleChipLayouterRegion<'r, 'a, F: Field, CS: Assignment<F> + 'a> {
226 layouter: &'r mut SingleChipLayouter<'a, F, CS>,
227 region_index: RegionIndex,
228 constants: Vec<(Assigned<F>, Cell)>,
230}
231
232impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> fmt::Debug
233 for SingleChipLayouterRegion<'r, 'a, F, CS>
234{
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 f.debug_struct("SingleChipLayouterRegion")
237 .field("layouter", &self.layouter)
238 .field("region_index", &self.region_index)
239 .finish()
240 }
241}
242
243impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> SingleChipLayouterRegion<'r, 'a, F, CS> {
244 fn new(layouter: &'r mut SingleChipLayouter<'a, F, CS>, region_index: RegionIndex) -> Self {
245 SingleChipLayouterRegion {
246 layouter,
247 region_index,
248 constants: vec![],
249 }
250 }
251}
252
253impl<'r, 'a, F: Field, CS: Assignment<F> + 'a + SyncDeps> RegionLayouter<F>
254 for SingleChipLayouterRegion<'r, 'a, F, CS>
255{
256 fn enable_selector<'v>(
257 &'v mut self,
258 annotation: &'v (dyn Fn() -> String + 'v),
259 selector: &Selector,
260 offset: usize,
261 ) -> Result<(), Error> {
262 self.layouter.cs.enable_selector(
263 annotation, selector,
264 offset, )
266 }
267
268 fn name_column<'v>(
269 &'v mut self,
270 annotation: &'v (dyn Fn() -> String + 'v),
271 column: Column<Any>,
272 ) {
273 self.layouter.cs.annotate_column(annotation, column);
274 }
275
276 fn assign_advice<'v>(
277 &mut self,
278 column: Column<Advice>,
280 offset: usize,
281 to: Value<Assigned<F>>, ) -> AssignedCell<&'v Assigned<F>, F> {
283 let value = self.layouter.cs.assign_advice(
284 column, offset, to,
286 );
287
288 AssignedCell {
289 value,
290 cell: Cell {
291 row_offset: offset,
293 column: column.into(),
294 },
295 _marker: PhantomData,
296 }
297 }
298
299 fn assign_advice_from_constant<'v>(
300 &'v mut self,
301 _annotation: &'v (dyn Fn() -> String + 'v),
302 column: Column<Advice>,
303 offset: usize,
304 constant: Assigned<F>,
305 ) -> Result<Cell, Error> {
306 let advice = self
307 .assign_advice(column, offset, Value::known(constant))
308 .cell;
309 self.constrain_constant(advice, constant)?;
310
311 Ok(advice)
312 }
313
314 fn assign_advice_from_instance<'v>(
315 &mut self,
316 _annotation: &'v (dyn Fn() -> String + 'v),
317 instance: Column<Instance>,
318 row: usize,
319 advice: Column<Advice>,
320 offset: usize,
321 ) -> Result<(Cell, Value<F>), Error> {
322 let value = self.layouter.cs.query_instance(instance, row)?;
323
324 let cell = self
325 .assign_advice(advice, offset, value.map(|v| Assigned::Trivial(v)))
326 .cell;
327
328 self.layouter.cs.copy(
329 cell.column,
330 cell.row_offset, instance.into(),
332 row,
333 );
334
335 Ok((cell, value))
336 }
337
338 fn instance_value(
339 &mut self,
340 instance: Column<Instance>,
341 row: usize,
342 ) -> Result<Value<F>, Error> {
343 self.layouter.cs.query_instance(instance, row)
344 }
345
346 fn assign_fixed(
347 &mut self,
348 column: Column<Fixed>,
350 offset: usize,
351 to: Assigned<F>,
352 ) -> Cell {
353 self.layouter.cs.assign_fixed(
354 column, offset, to,
356 );
357
358 Cell {
359 row_offset: offset,
361 column: column.into(),
362 }
363 }
364
365 fn constrain_constant(&mut self, cell: Cell, constant: Assigned<F>) -> Result<(), Error> {
366 self.constants.push((constant, cell));
367 Ok(())
368 }
369
370 fn constrain_equal(&mut self, left: Cell, right: Cell) {
371 self.layouter.cs.copy(
372 left.column,
373 left.row_offset, right.column,
375 right.row_offset, );
377 }
378
379 fn get_challenge(&self, challenge: Challenge) -> Value<F> {
380 self.layouter.cs.get_challenge(challenge)
381 }
382
383 fn next_phase(&mut self) {
384 self.layouter.cs.next_phase();
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use halo2curves::pasta::vesta;
391
392 use super::SimpleFloorPlanner;
393 use crate::{
394 dev::MockProver,
395 plonk::{Advice, Circuit, Column, Error},
396 };
397
398 #[test]
399 fn not_enough_columns_for_constants() {
400 struct MyCircuit {}
401
402 impl Circuit<vesta::Scalar> for MyCircuit {
403 type Config = Column<Advice>;
404 type FloorPlanner = SimpleFloorPlanner;
405 type Params = ();
406
407 fn params(&self) -> Self::Params {}
408 fn without_witnesses(&self) -> Self {
409 MyCircuit {}
410 }
411
412 fn configure(meta: &mut crate::plonk::ConstraintSystem<vesta::Scalar>) -> Self::Config {
413 meta.advice_column()
414 }
415
416 fn synthesize(
417 &self,
418 config: Self::Config,
419 mut layouter: impl crate::circuit::Layouter<vesta::Scalar>,
420 ) -> Result<(), crate::plonk::Error> {
421 layouter.assign_region(
422 || "assign constant",
423 |mut region| {
424 region.assign_advice_from_constant(
425 || "one",
426 config,
427 0,
428 vesta::Scalar::one(),
429 )
430 },
431 )?;
432
433 Ok(())
434 }
435 }
436
437 let circuit = MyCircuit {};
438 assert!(matches!(
439 MockProver::run(3, &circuit, vec![]).unwrap_err(),
440 Error::NotEnoughColumnsForConstants,
441 ));
442 }
443}