diff --git a/src/circuit/gadget/sinsemilla/chip.rs b/src/circuit/gadget/sinsemilla/chip.rs index 018d1f766..876c7dcbb 100644 --- a/src/circuit/gadget/sinsemilla/chip.rs +++ b/src/circuit/gadget/sinsemilla/chip.rs @@ -1,13 +1,16 @@ -use super::super::ecc::chip::EccChip; +use super::super::ecc::chip::{EccChip, EccConfig}; use super::{CommitDomains, HashDomains, SinsemillaInstructions}; use crate::constants::OrchardFixedBases; use halo2::{ arithmetic::{CurveAffine, FieldExt}, circuit::{Cell, Layouter}, - plonk::Error, + plonk::{Error, Selector}, }; +mod generator_table; +use generator_table::*; + /// A structure containing a cell and its assigned value. #[derive(Clone, Debug)] pub struct CellValue { diff --git a/src/circuit/gadget/sinsemilla/chip/generator_table.rs b/src/circuit/gadget/sinsemilla/chip/generator_table.rs new file mode 100644 index 000000000..2952e83f7 --- /dev/null +++ b/src/circuit/gadget/sinsemilla/chip/generator_table.rs @@ -0,0 +1,112 @@ +use super::super::ecc::chip::EccChip; +use crate::primitives::sinsemilla::{K, S_PERSONALIZATION}; +use halo2::{ + arithmetic::{CurveAffine, CurveExt, FieldExt}, + circuit::Layouter, + plonk::{Column, ConstraintSystem, Error, Expression, Fixed}, + poly::Rotation, +}; + +use group::Curve; + +/// Table containing independent generators S[0..2^k] +#[derive(Clone, Debug)] +pub(super) struct GeneratorTable { + table_idx: Column, + table_x: Column, + table_y: Column, +} + +impl GeneratorTable { + pub(super) fn configure( + meta: &mut ConstraintSystem, + m: Expression, + x_p: Expression, + y_p: Expression, + ) -> Self { + let table_idx = meta.fixed_column(); + let table_idx_cur = meta.query_fixed(table_idx, Rotation::cur()); + let table_x = meta.fixed_column(); + let table_x_cur = meta.query_fixed(table_x, Rotation::cur()); + let table_y = meta.fixed_column(); + let table_y_cur = meta.query_fixed(table_y, Rotation::cur()); + + meta.lookup(&[m, x_p, y_p], &[table_idx_cur, table_x_cur, table_y_cur]); + + GeneratorTable { + table_idx, + table_x, + table_y, + } + } + + // Generates S[0..2^k] as 2^k independent, verifiably random generators of the group. + // Loads these generators into a lookup table along with their indices. + // Uses SWU hash-to-curve. + fn generate(&self) -> impl Iterator { + let (init_x, init_y) = get_s_by_idx::(0).to_affine().get_xy().unwrap(); + + (1..=(1 << K)).scan( + (C::Base::default(), init_x, init_y), + move |(idx, x, y), i| { + // We computed this table row in the previous iteration. + let res = (*idx, *x, *y); + + // i holds the zero-indexed row number for the next table row. + *idx = C::Base::from_u64(i as u64); + + let (new_x, new_y) = get_s_by_idx::(i).to_affine().get_xy().unwrap(); + + *x = new_x; + *y = new_y; + + Some(res) + }, + ) + } + + pub(super) fn load( + &self, + layouter: &mut impl Layouter>, + ) -> Result<(), Error> { + layouter.assign_region( + || "generator_table", + |mut gate| { + // We generate the row values lazily (we only need them during keygen). + let mut rows = self.generate::(); + + for index in 0..(1 << K) { + let mut row = None; + gate.assign_fixed( + || "table_idx", + self.table_idx, + index, + || { + row = rows.next(); + row.map(|(idx, _, _)| idx).ok_or(Error::SynthesisError) + }, + )?; + gate.assign_fixed( + || "table_x", + self.table_x, + index, + || row.map(|(_, x, _)| x).ok_or(Error::SynthesisError), + )?; + gate.assign_fixed( + || "table_y", + self.table_y, + index, + || row.map(|(_, _, y)| y).ok_or(Error::SynthesisError), + )?; + } + Ok(()) + }, + ) + } +} + +/// Get generator S by index +pub fn get_s_by_idx(idx: u32) -> C::Curve { + let hash = C::CurveExt::hash_to_curve(S_PERSONALIZATION); + hash(&idx.to_le_bytes()) +}