diff --git a/halo2_gadgets/src/sinsemilla.rs b/halo2_gadgets/src/sinsemilla.rs index f0857a1313..996868acae 100644 --- a/halo2_gadgets/src/sinsemilla.rs +++ b/halo2_gadgets/src/sinsemilla.rs @@ -88,6 +88,19 @@ pub trait SinsemillaInstructions Result<(Self::NonIdentityPoint, Vec), Error>; + /// Same as hash_to_point, but continuing from the given initial point. + /// The initial point can be the hashing domain, or the hash of a previous + /// message to append to. + /// + #[allow(non_snake_case)] + #[allow(clippy::type_complexity)] + fn append_hash_to_point( + &self, + layouter: impl Layouter, + Q: Self::NonIdentityPoint, + message: Self::Message, + ) -> Result<(Self::NonIdentityPoint, Vec), Error>; + /// Extracts the x-coordinate of the output of a Sinsemilla hash. fn extract(point: &Self::NonIdentityPoint) -> Self::X; } @@ -577,7 +590,6 @@ pub(crate) mod tests { meta, advices[..5].try_into().unwrap(), advices[2], - lagrange_coeffs[0], lookup, range_check, ); @@ -585,7 +597,6 @@ pub(crate) mod tests { meta, advices[5..].try_into().unwrap(), advices[7], - lagrange_coeffs[1], lookup, range_check, ); diff --git a/halo2_gadgets/src/sinsemilla/chip.rs b/halo2_gadgets/src/sinsemilla/chip.rs index de4bbb1b68..0f88bd9ce9 100644 --- a/halo2_gadgets/src/sinsemilla/chip.rs +++ b/halo2_gadgets/src/sinsemilla/chip.rs @@ -46,7 +46,7 @@ where /// the y-coordinate of the domain $Q$. q_sinsemilla4: Selector, /// Fixed column used to load the y-coordinate of the domain $Q$. - fixed_y_q: Column, + y_q: Column, /// Logic specific to merged double-and-add. double_and_add: DoubleAndAdd, /// Advice column used to load the message. @@ -152,7 +152,6 @@ where meta: &mut ConstraintSystem, advices: [Column; 5], witness_pieces: Column, - fixed_y_q: Column, lookup: (TableColumn, TableColumn, TableColumn), range_check: LookupRangeCheckConfig, ) -> >::Config { @@ -165,7 +164,7 @@ where q_sinsemilla1: meta.complex_selector(), q_sinsemilla2: meta.fixed_column(), q_sinsemilla4: meta.selector(), - fixed_y_q, + y_q: advices[0], double_and_add: DoubleAndAdd { x_a: advices[0], x_p: advices[1], @@ -203,10 +202,10 @@ where // https://p.z.cash/halo2-0.1:sinsemilla-constraints?partial meta.create_gate("Initial y_Q", |meta| { let q_s4 = meta.query_selector(config.q_sinsemilla4); - let y_q = meta.query_fixed(config.fixed_y_q, Rotation::cur()); + let y_q = meta.query_advice(config.y_q, Rotation::cur()); // Y_A = (lambda_1 + lambda_2) * (x_a - x_r) - let Y_A_cur = Y_A(meta, Rotation::cur()); + let Y_A_cur = Y_A(meta, Rotation::next()); // 2 * y_q - Y_{A,0} = 0 let init_y_q_check = y_q * two - Y_A_cur; @@ -321,6 +320,20 @@ where ) } + #[allow(non_snake_case)] + #[allow(clippy::type_complexity)] + fn append_hash_to_point( + &self, + mut layouter: impl Layouter, + Q: Self::NonIdentityPoint, + message: Self::Message, + ) -> Result<(Self::NonIdentityPoint, Vec), Error> { + layouter.assign_region( + || "hash_to_point", + |mut region| self.append_hash_message(&mut region, Q.clone(), &message), + ) + } + fn extract(point: &Self::NonIdentityPoint) -> Self::X { point.x() } diff --git a/halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs b/halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs index 25be0efb36..781259f423 100644 --- a/halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs +++ b/halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs @@ -24,6 +24,7 @@ where Fixed: FixedPoints, Commit: CommitDomains, { + /// [Specification](https://p.z.cash/halo2-0.1:sinsemilla-constraints?partial). #[allow(non_snake_case)] #[allow(clippy::type_complexity)] @@ -42,9 +43,46 @@ where Vec>>, ), Error, + > { + let (x_a, y_a) = self.init_fixed(region, Q)?; + self.append_message(region, x_a, y_a, message) + } + + /// [Specification](https://p.z.cash/halo2-0.1:sinsemilla-constraints?partial). + #[allow(non_snake_case)] + #[allow(clippy::type_complexity)] + pub(super) fn append_hash_message( + &self, + region: &mut Region<'_, pallas::Base>, + Q: NonIdentityEccPoint, + message: &>::Message, + ) -> Result< + ( + NonIdentityEccPoint, + Vec>>, + ), + Error, + > { + let (x_a, y_a) = self.init_variable(region, Q)?; + self.append_message(region, x_a, y_a, message) + } + + #[allow(non_snake_case)] + #[allow(clippy::type_complexity)] + fn init_fixed( + &self, + region: &mut Region<'_, pallas::Base>, + Q: pallas::Affine, + ) -> Result< + (X, Y), + Error, > { let config = self.config().clone(); - let mut offset = 0; + let offset = 0; // Get the `x`- and `y`-coordinates of the starting `Q` base. let x_q = *Q.coordinates().unwrap().x(); @@ -52,21 +90,24 @@ where // Constrain the initial x_a, lambda_1, lambda_2, x_p using the q_sinsemilla4 // selector. - let mut y_a: Y = { + let y_a: Y = { // Enable `q_sinsemilla4` on the first row. config.q_sinsemilla4.enable(region, offset)?; - region.assign_fixed( + + region.assign_advice_from_constant( || "fixed y_q", - config.fixed_y_q, + config.y_q, offset, - || Value::known(y_q), + y_q, )?; Value::known(y_q.into()).into() }; + let offset = 1; + // Constrain the initial x_q to equal the x-coordinate of the domain's `Q`. - let mut x_a: X = { + let x_a: X = { let x_a = region.assign_advice_from_constant( || "fixed x_q", config.double_and_add.x_a, @@ -77,6 +118,76 @@ where x_a.into() }; + Ok((x_a, y_a)) + } + + + #[allow(non_snake_case)] + #[allow(clippy::type_complexity)] + fn init_variable( + &self, + region: &mut Region<'_, pallas::Base>, + Q: NonIdentityEccPoint, + ) -> Result< + (X, Y), + Error, + > { + let config = self.config().clone(); + let offset = 0; + + // Constrain the initial x_a, lambda_1, lambda_2, x_p using the q_sinsemilla4 + // selector. + let y_a: Y = { + // Enable `q_sinsemilla4` on the first row. + config.q_sinsemilla4.enable(region, offset)?; + + let y_q = Q.y().copy_advice(|| "initial y_q", region, config.y_q, offset)?; + y_q.value_field().into() + }; + + let offset = 1; + + // Constrain the initial x_q to equal the x-coordinate of the domain's `Q`. + let x_a: X = { + let x_q = Q.x().copy_advice(|| "initial x_q", region, config.double_and_add.x_a, offset)?; + let x_q: AssignedCell, _> = x_q.into(); + x_q.into() + }; + + Ok((x_a, y_a)) + } + + #[allow(non_snake_case)] + #[allow(clippy::type_complexity)] + fn append_message( + &self, + region: &mut Region<'_, pallas::Base>, + x_q: X, + y_q: Y, + message: &>::Message, + ) -> Result< + ( + NonIdentityEccPoint, + Vec>>, + ), + Error, + > { + let config = self.config().clone(); + let mut offset = 1; + + // TODO: a better way to test with these values. + #[cfg(test)] + let x_q_val = x_q.value().unwrap().evaluate(); + #[cfg(test)] + let y_q_val = y_q.0.unwrap().evaluate(); + + let mut x_a = x_q; + let mut y_a = y_q; + let mut zs_sum: Vec>> = Vec::new(); // Hash each piece in the message. @@ -132,6 +243,11 @@ where use group::{prime::PrimeCurveAffine, Curve}; use pasta_curves::arithmetic::CurveExt; + let Q = pallas::Affine::from_xy( + x_q_val, + y_q_val + ).unwrap(); + let field_elems: Value> = message .iter() .map(|piece| piece.field_elem().map(|elem| (elem, piece.num_words()))) diff --git a/halo2_gadgets/src/sinsemilla/merkle.rs b/halo2_gadgets/src/sinsemilla/merkle.rs index aa6c420bd3..fbcc58bda0 100644 --- a/halo2_gadgets/src/sinsemilla/merkle.rs +++ b/halo2_gadgets/src/sinsemilla/merkle.rs @@ -236,11 +236,6 @@ pub mod tests { let constants = meta.fixed_column(); meta.enable_constant(constants); - // NB: In the actual Action circuit, these fixed columns will be reused - // by other chips. For this test, we are creating new fixed columns. - let fixed_y_q_1 = meta.fixed_column(); - let fixed_y_q_2 = meta.fixed_column(); - // Fixed columns for the Sinsemilla generator lookup table let lookup = ( meta.lookup_table_column(), @@ -254,7 +249,6 @@ pub mod tests { meta, advices[5..].try_into().unwrap(), advices[7], - fixed_y_q_1, lookup, range_check, ); @@ -264,7 +258,6 @@ pub mod tests { meta, advices[..5].try_into().unwrap(), advices[2], - fixed_y_q_2, lookup, range_check, ); diff --git a/halo2_gadgets/src/sinsemilla/merkle/chip.rs b/halo2_gadgets/src/sinsemilla/merkle/chip.rs index dcedb6b042..3b4eb2007e 100644 --- a/halo2_gadgets/src/sinsemilla/merkle/chip.rs +++ b/halo2_gadgets/src/sinsemilla/merkle/chip.rs @@ -523,6 +523,19 @@ where chip.hash_to_point(layouter, Q, message) } + #[allow(non_snake_case)] + #[allow(clippy::type_complexity)] + fn append_hash_to_point( + &self, + layouter: impl Layouter, + Q: Self::NonIdentityPoint, + message: Self::Message, + ) -> Result<(Self::NonIdentityPoint, Vec>), Error> { + let config = self.config().sinsemilla_config.clone(); + let chip = SinsemillaChip::::construct(config); + chip.append_hash_to_point(layouter, Q, message) + } + fn extract(point: &Self::NonIdentityPoint) -> Self::X { SinsemillaChip::::extract(point) } diff --git a/halo2_proofs/src/circuit.rs b/halo2_proofs/src/circuit.rs index 9451735d54..fffd907709 100644 --- a/halo2_proofs/src/circuit.rs +++ b/halo2_proofs/src/circuit.rs @@ -125,6 +125,19 @@ where } } +impl From> for AssignedCell, F> +where + for<'v> Assigned: From<&'v F>, +{ + fn from(val: AssignedCell) -> Self { + AssignedCell { + value: val.value().map(|v| v.into()), + cell: val.cell, + _marker: Default::default(), + } + } +} + impl AssignedCell, F> { /// Evaluates this assigned cell's value directly, performing an unbatched inversion /// if necessary. diff --git a/halo2_proofs/src/circuit/value.rs b/halo2_proofs/src/circuit/value.rs index 50b30c2676..8707a640c6 100644 --- a/halo2_proofs/src/circuit/value.rs +++ b/halo2_proofs/src/circuit/value.rs @@ -113,6 +113,12 @@ impl Value { inner: self.inner.zip(other.inner), } } + + // TODO: for tests only like #[cfg(test)] + /// Unwrap, only for tests. + pub fn unwrap(self) -> V { + self.inner.unwrap() + } } impl Value<(V, W)> {