halo2_gadgets/sha256/table16/
spread_table.rs

1use super::{util::*, AssignedBits};
2
3use group::ff::{Field, PrimeField};
4use halo2_proofs::{
5    circuit::{Chip, Layouter, Region, Value},
6    pasta::pallas,
7    plonk::{Advice, Column, ConstraintSystem, Error, TableColumn},
8    poly::Rotation,
9};
10use std::convert::TryInto;
11use std::marker::PhantomData;
12
13const BITS_7: usize = 1 << 7;
14const BITS_10: usize = 1 << 10;
15const BITS_11: usize = 1 << 11;
16const BITS_13: usize = 1 << 13;
17const BITS_14: usize = 1 << 14;
18
19/// An input word into a lookup, containing (tag, dense, spread)
20#[derive(Copy, Clone, Debug)]
21pub(super) struct SpreadWord<const DENSE: usize, const SPREAD: usize> {
22    pub tag: u8,
23    pub dense: [bool; DENSE],
24    pub spread: [bool; SPREAD],
25}
26
27/// Helper function that returns tag of 16-bit input
28pub fn get_tag(input: u16) -> u8 {
29    let input = input as usize;
30    if input < BITS_7 {
31        0
32    } else if input < BITS_10 {
33        1
34    } else if input < BITS_11 {
35        2
36    } else if input < BITS_13 {
37        3
38    } else if input < BITS_14 {
39        4
40    } else {
41        5
42    }
43}
44
45impl<const DENSE: usize, const SPREAD: usize> SpreadWord<DENSE, SPREAD> {
46    pub(super) fn new(dense: [bool; DENSE]) -> Self {
47        assert!(DENSE <= 16);
48        SpreadWord {
49            tag: get_tag(lebs2ip(&dense) as u16),
50            dense,
51            spread: spread_bits(dense),
52        }
53    }
54
55    pub(super) fn try_new<T: TryInto<[bool; DENSE]> + std::fmt::Debug>(dense: T) -> Self
56    where
57        <T as TryInto<[bool; DENSE]>>::Error: std::fmt::Debug,
58    {
59        assert!(DENSE <= 16);
60        let dense: [bool; DENSE] = dense.try_into().unwrap();
61        SpreadWord {
62            tag: get_tag(lebs2ip(&dense) as u16),
63            dense,
64            spread: spread_bits(dense),
65        }
66    }
67}
68
69/// A variable stored in advice columns corresponding to a row of [`SpreadTableConfig`].
70#[derive(Clone, Debug)]
71pub(super) struct SpreadVar<const DENSE: usize, const SPREAD: usize> {
72    pub _tag: Value<u8>,
73    pub dense: AssignedBits<DENSE>,
74    pub spread: AssignedBits<SPREAD>,
75}
76
77impl<const DENSE: usize, const SPREAD: usize> SpreadVar<DENSE, SPREAD> {
78    pub(super) fn with_lookup(
79        region: &mut Region<'_, pallas::Base>,
80        cols: &SpreadInputs,
81        row: usize,
82        word: Value<SpreadWord<DENSE, SPREAD>>,
83    ) -> Result<Self, Error> {
84        let tag = word.map(|word| word.tag);
85        let dense_val = word.map(|word| word.dense);
86        let spread_val = word.map(|word| word.spread);
87
88        region.assign_advice(
89            || "tag",
90            cols.tag,
91            row,
92            || tag.map(|tag| pallas::Base::from(tag as u64)),
93        )?;
94
95        let dense =
96            AssignedBits::<DENSE>::assign_bits(region, || "dense", cols.dense, row, dense_val)?;
97
98        let spread =
99            AssignedBits::<SPREAD>::assign_bits(region, || "spread", cols.spread, row, spread_val)?;
100
101        Ok(SpreadVar {
102            _tag: tag,
103            dense,
104            spread,
105        })
106    }
107
108    pub(super) fn without_lookup(
109        region: &mut Region<'_, pallas::Base>,
110        dense_col: Column<Advice>,
111        dense_row: usize,
112        spread_col: Column<Advice>,
113        spread_row: usize,
114        word: Value<SpreadWord<DENSE, SPREAD>>,
115    ) -> Result<Self, Error> {
116        let tag = word.map(|word| word.tag);
117        let dense_val = word.map(|word| word.dense);
118        let spread_val = word.map(|word| word.spread);
119
120        let dense = AssignedBits::<DENSE>::assign_bits(
121            region,
122            || "dense",
123            dense_col,
124            dense_row,
125            dense_val,
126        )?;
127
128        let spread = AssignedBits::<SPREAD>::assign_bits(
129            region,
130            || "spread",
131            spread_col,
132            spread_row,
133            spread_val,
134        )?;
135
136        Ok(SpreadVar {
137            _tag: tag,
138            dense,
139            spread,
140        })
141    }
142}
143
144#[derive(Clone, Debug)]
145pub(super) struct SpreadInputs {
146    pub(super) tag: Column<Advice>,
147    pub(super) dense: Column<Advice>,
148    pub(super) spread: Column<Advice>,
149}
150
151#[derive(Clone, Debug)]
152pub(super) struct SpreadTable {
153    pub(super) tag: TableColumn,
154    pub(super) dense: TableColumn,
155    pub(super) spread: TableColumn,
156}
157
158#[derive(Clone, Debug)]
159pub(super) struct SpreadTableConfig {
160    pub input: SpreadInputs,
161    pub table: SpreadTable,
162}
163
164#[derive(Clone, Debug)]
165pub(super) struct SpreadTableChip<F: Field> {
166    config: SpreadTableConfig,
167    _marker: PhantomData<F>,
168}
169
170impl<F: Field> Chip<F> for SpreadTableChip<F> {
171    type Config = SpreadTableConfig;
172    type Loaded = ();
173
174    fn config(&self) -> &Self::Config {
175        &self.config
176    }
177
178    fn loaded(&self) -> &Self::Loaded {
179        &()
180    }
181}
182
183impl<F: PrimeField> SpreadTableChip<F> {
184    pub fn configure(
185        meta: &mut ConstraintSystem<F>,
186        input_tag: Column<Advice>,
187        input_dense: Column<Advice>,
188        input_spread: Column<Advice>,
189    ) -> <Self as Chip<F>>::Config {
190        let table_tag = meta.lookup_table_column();
191        let table_dense = meta.lookup_table_column();
192        let table_spread = meta.lookup_table_column();
193
194        meta.lookup(|meta| {
195            let tag_cur = meta.query_advice(input_tag, Rotation::cur());
196            let dense_cur = meta.query_advice(input_dense, Rotation::cur());
197            let spread_cur = meta.query_advice(input_spread, Rotation::cur());
198
199            vec![
200                (tag_cur, table_tag),
201                (dense_cur, table_dense),
202                (spread_cur, table_spread),
203            ]
204        });
205
206        SpreadTableConfig {
207            input: SpreadInputs {
208                tag: input_tag,
209                dense: input_dense,
210                spread: input_spread,
211            },
212            table: SpreadTable {
213                tag: table_tag,
214                dense: table_dense,
215                spread: table_spread,
216            },
217        }
218    }
219
220    pub fn load(
221        config: SpreadTableConfig,
222        layouter: &mut impl Layouter<F>,
223    ) -> Result<<Self as Chip<F>>::Loaded, Error> {
224        layouter.assign_table(
225            || "spread table",
226            |mut table| {
227                // We generate the row values lazily (we only need them during keygen).
228                let mut rows = SpreadTableConfig::generate::<F>();
229
230                for index in 0..(1 << 16) {
231                    let mut row = None;
232                    table.assign_cell(
233                        || "tag",
234                        config.table.tag,
235                        index,
236                        || {
237                            row = rows.next();
238                            Value::known(row.map(|(tag, _, _)| tag).unwrap())
239                        },
240                    )?;
241                    table.assign_cell(
242                        || "dense",
243                        config.table.dense,
244                        index,
245                        || Value::known(row.map(|(_, dense, _)| dense).unwrap()),
246                    )?;
247                    table.assign_cell(
248                        || "spread",
249                        config.table.spread,
250                        index,
251                        || Value::known(row.map(|(_, _, spread)| spread).unwrap()),
252                    )?;
253                }
254
255                Ok(())
256            },
257        )
258    }
259}
260
261impl SpreadTableConfig {
262    fn generate<F: PrimeField>() -> impl Iterator<Item = (F, F, F)> {
263        (1..=(1 << 16)).scan((F::ZERO, F::ZERO, F::ZERO), |(tag, dense, spread), i| {
264            // We computed this table row in the previous iteration.
265            let res = (*tag, *dense, *spread);
266
267            // i holds the zero-indexed row number for the next table row.
268            match i {
269                BITS_7 | BITS_10 | BITS_11 | BITS_13 | BITS_14 => *tag += F::ONE,
270                _ => (),
271            }
272            *dense += F::ONE;
273            if i & 1 == 0 {
274                // On even-numbered rows we recompute the spread.
275                *spread = F::ZERO;
276                for b in 0..16 {
277                    if (i >> b) & 1 != 0 {
278                        *spread += F::from(1 << (2 * b));
279                    }
280                }
281            } else {
282                // On odd-numbered rows we add one.
283                *spread += F::ONE;
284            }
285
286            Some(res)
287        })
288    }
289}
290
291#[cfg(test)]
292mod tests {
293    use super::{get_tag, SpreadTableChip, SpreadTableConfig};
294    use rand::Rng;
295
296    use group::ff::PrimeField;
297    use halo2_proofs::{
298        circuit::{Layouter, SimpleFloorPlanner, Value},
299        dev::MockProver,
300        pasta::Fp,
301        plonk::{Advice, Circuit, Column, ConstraintSystem, Error},
302    };
303
304    #[test]
305    fn lookup_table() {
306        /// This represents an advice column at a certain row in the ConstraintSystem
307        #[derive(Copy, Clone, Debug)]
308        pub struct Variable(Column<Advice>, usize);
309
310        struct MyCircuit {}
311
312        impl<F: PrimeField> Circuit<F> for MyCircuit {
313            type Config = SpreadTableConfig;
314            type FloorPlanner = SimpleFloorPlanner;
315
316            fn without_witnesses(&self) -> Self {
317                MyCircuit {}
318            }
319
320            fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
321                let input_tag = meta.advice_column();
322                let input_dense = meta.advice_column();
323                let input_spread = meta.advice_column();
324
325                SpreadTableChip::configure(meta, input_tag, input_dense, input_spread)
326            }
327
328            fn synthesize(
329                &self,
330                config: Self::Config,
331                mut layouter: impl Layouter<F>,
332            ) -> Result<(), Error> {
333                SpreadTableChip::load(config.clone(), &mut layouter)?;
334
335                layouter.assign_region(
336                    || "spread_test",
337                    |mut gate| {
338                        let mut row = 0;
339                        let mut add_row = |tag, dense, spread| -> Result<(), Error> {
340                            gate.assign_advice(
341                                || "tag",
342                                config.input.tag,
343                                row,
344                                || Value::known(tag),
345                            )?;
346                            gate.assign_advice(
347                                || "dense",
348                                config.input.dense,
349                                row,
350                                || Value::known(dense),
351                            )?;
352                            gate.assign_advice(
353                                || "spread",
354                                config.input.spread,
355                                row,
356                                || Value::known(spread),
357                            )?;
358                            row += 1;
359                            Ok(())
360                        };
361
362                        // Test the first few small values.
363                        add_row(F::ZERO, F::from(0b000), F::from(0b000000))?;
364                        add_row(F::ZERO, F::from(0b001), F::from(0b000001))?;
365                        add_row(F::ZERO, F::from(0b010), F::from(0b000100))?;
366                        add_row(F::ZERO, F::from(0b011), F::from(0b000101))?;
367                        add_row(F::ZERO, F::from(0b100), F::from(0b010000))?;
368                        add_row(F::ZERO, F::from(0b101), F::from(0b010001))?;
369
370                        // Test the tag boundaries:
371                        // 7-bit
372                        add_row(F::ZERO, F::from(0b1111111), F::from(0b01010101010101))?;
373                        add_row(F::ONE, F::from(0b10000000), F::from(0b0100000000000000))?;
374                        // - 10-bit
375                        add_row(
376                            F::ONE,
377                            F::from(0b1111111111),
378                            F::from(0b01010101010101010101),
379                        )?;
380                        add_row(
381                            F::from(2),
382                            F::from(0b10000000000),
383                            F::from(0b0100000000000000000000),
384                        )?;
385                        // - 11-bit
386                        add_row(
387                            F::from(2),
388                            F::from(0b11111111111),
389                            F::from(0b0101010101010101010101),
390                        )?;
391                        add_row(
392                            F::from(3),
393                            F::from(0b100000000000),
394                            F::from(0b010000000000000000000000),
395                        )?;
396                        // - 13-bit
397                        add_row(
398                            F::from(3),
399                            F::from(0b1111111111111),
400                            F::from(0b01010101010101010101010101),
401                        )?;
402                        add_row(
403                            F::from(4),
404                            F::from(0b10000000000000),
405                            F::from(0b0100000000000000000000000000),
406                        )?;
407                        // - 14-bit
408                        add_row(
409                            F::from(4),
410                            F::from(0b11111111111111),
411                            F::from(0b0101010101010101010101010101),
412                        )?;
413                        add_row(
414                            F::from(5),
415                            F::from(0b100000000000000),
416                            F::from(0b010000000000000000000000000000),
417                        )?;
418
419                        // Test random lookup values
420                        let mut rng = rand::thread_rng();
421
422                        fn interleave_u16_with_zeros(word: u16) -> u32 {
423                            let mut word: u32 = word.into();
424                            word = (word ^ (word << 8)) & 0x00ff00ff;
425                            word = (word ^ (word << 4)) & 0x0f0f0f0f;
426                            word = (word ^ (word << 2)) & 0x33333333;
427                            word = (word ^ (word << 1)) & 0x55555555;
428                            word
429                        }
430
431                        for _ in 0..10 {
432                            let word: u16 = rng.gen();
433                            add_row(
434                                F::from(u64::from(get_tag(word))),
435                                F::from(u64::from(word)),
436                                F::from(u64::from(interleave_u16_with_zeros(word))),
437                            )?;
438                        }
439
440                        Ok(())
441                    },
442                )
443            }
444        }
445
446        let circuit: MyCircuit = MyCircuit {};
447
448        let prover = match MockProver::<Fp>::run(17, &circuit, vec![]) {
449            Ok(prover) => prover,
450            Err(e) => panic!("{:?}", e),
451        };
452        assert_eq!(prover.verify(), Ok(()));
453    }
454}