From 1f35f3c3dad24490894de32c64bda311ee4a15b0 Mon Sep 17 00:00:00 2001 From: YaoGalteland Date: Sun, 24 Mar 2024 12:01:58 +0100 Subject: [PATCH] remove duplicate code, simplify code --- .../src/sinsemilla/chip/hash_to_point.rs | 322 +++++++----------- 1 file changed, 118 insertions(+), 204 deletions(-) diff --git a/halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs b/halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs index c9c4bdb470..799cebc9dc 100644 --- a/halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs +++ b/halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs @@ -15,20 +15,24 @@ use group::ff::{PrimeField, PrimeFieldBits}; use pasta_curves::{arithmetic::CurveAffine, pallas}; use std::ops::Deref; - +// Define an enum that can hold either type +#[derive(Debug, Clone)] +pub enum EccPointQ<'a> { + PublicPoint(pallas::Affine), + PrivatePoint(&'a NonIdentityEccPoint), +} impl SinsemillaChip where Hash: HashDomains, Fixed: FixedPoints, Commit: CommitDomains, { - /// [Specification](https://p.z.cash/halo2-0.1:sinsemilla-constraints?partial). #[allow(non_snake_case)] #[allow(clippy::type_complexity)] - pub(super) fn hash_message( + pub(super) fn hash_message_backward_compatibility<'a>( &self, region: &mut Region<'_, pallas::Base>, - Q: pallas::Affine, + Q: EccPointQ<'a>, message: & { - let (offset, x_a, y_a) = if self.config.is_zsa_variant { - self.public_initialization_zsa(region, Q)? - } else { - self.public_initialization(region, Q)? - }; - + let (offset, x_a, y_a) = self.Q_initialization(region, Q.clone())?; let (x_a, y_a, zs_sum) = self.hash_all_pieces(region, offset, message, x_a, y_a)?; #[cfg(test)] @@ -63,9 +62,18 @@ where .map(|piece| piece.field_elem().map(|elem| (elem, piece.num_words()))) .collect(); + let value_Q = match Q { + EccPointQ::PublicPoint(p) => { + // Wrap the EpAffine in Value to match the types + halo2_proofs::circuit::Value::known(p.clone()) + } + EccPointQ::PrivatePoint(p) => p.point(), + }; + field_elems .zip(x_a.value().zip(y_a.value())) - .assert_if_known(|(field_elems, (x_a, y_a))| { + .zip(value_Q) + .assert_if_known(|((field_elems, (x_a, y_a)), value_Q)| { // Get message as a bitstring. let bitstring: Vec = field_elems .iter() @@ -81,7 +89,7 @@ where // incomplete addition with negligible probability. let expected_point = bitstring .chunks(K) - .fold(Q.to_curve(), |acc, chunk| (acc + S(chunk)) + acc); + .fold(value_Q.to_curve(), |acc, chunk| (acc + S(chunk)) + acc); let actual_point = pallas::Affine::from_xy(x_a.evaluate(), y_a.evaluate()).unwrap(); expected_point.to_affine() == actual_point @@ -97,13 +105,92 @@ where )) } + #[allow(non_snake_case)] + /// Assign the coordinates of the initial point `Q` + fn Q_initialization( + &self, + region: &mut Region<'_, pallas::Base>, + Q: EccPointQ, + ) -> Result<(usize, X, Y), Error> { + let config = self.config().clone(); + let mut offset = 0; + + match Q { + // Assign the coordinates of the initial public point `Q`, if Q is EccPointQ::PublicPoint + // | offset | x_A | q_sinsemilla4 | fixed_y_q | + // ----------------------------------------------- + // | 0 | x_Q | 1 | y_Q | + EccPointQ::PublicPoint(q) => { + // Process pallas::Affine point, Q is public + // Get the `x`- and `y`-coordinates of the starting `Q` base. + let x_q = *q.coordinates().unwrap().x(); + let y_q = *q.coordinates().unwrap().y(); + + // Constrain the initial y_a to equal the y-coordinate of the domain's `Q` + // using the q_sinsemilla4 selector. + let y_a: Y = { + // Enable `q_sinsemilla4` on the first row. + config.q_sinsemilla4.enable(region, offset)?; + region.assign_fixed( + || "fixed y_q", + config.fixed_y_q, + offset, + || Value::known(y_q), + )?; + + Value::known(y_q.into()).into() + }; + + // Constrain the initial x_a to equal the x-coordinate of the domain's `Q`. + let x_a: X = { + let x_a = region.assign_advice_from_constant( + || "fixed x_q", + config.double_and_add.x_a, + offset, + x_q.into(), + )?; + + x_a.into() + }; + Ok((offset, x_a, y_a)) + } + // Assign the coordinates of the initial private point `Q`, if Q is EccPointQ::PrivatePoint + // | offset | x_A | x_P | q_sinsemilla4 | + // -------------------------------------- + // | 0 | | y_Q | | + // | 1 | x_Q | | 1 | + EccPointQ::PrivatePoint(q) => { + // Process NonIdentityEccPoint, Q is private + let y_a: Y = { + // Enable `q_sinsemilla4` on the second row. + config.q_sinsemilla4.enable(region, offset + 1)?; + let q_y: AssignedCell, pallas::Base> = q.y().into(); + let y_a: AssignedCell, pallas::Base> = + q_y.copy_advice(|| "fixed y_q", region, config.double_and_add.x_p, offset)?; + + y_a.value_field().into() + }; + offset += 1; + + let x_a: X = { + let q_x: AssignedCell, pallas::Base> = q.x().into(); + let x_a = + q_x.copy_advice(|| "fixed x_q", region, config.double_and_add.x_a, offset)?; + + x_a.into() + }; + Ok((offset, x_a, y_a)) + } + } + } + /// [Specification](https://p.z.cash/halo2-0.1:sinsemilla-constraints?partial). #[allow(non_snake_case)] #[allow(clippy::type_complexity)] - pub(super) fn hash_message_with_private_init( + pub(super) fn hash_message( &self, region: &mut Region<'_, pallas::Base>, - Q: &NonIdentityEccPoint, + Q: pallas::Affine, message: & { - let (offset, x_a, y_a) = self.private_initialization(region, Q)?; - - let (x_a, y_a, zs_sum) = self.hash_all_pieces(region, offset, message, x_a, y_a)?; - - // FIXME: try to avoid duplication with a very similar code block in `hash_message` method - // - it's basically the same code except the following lines: - // - // hash_message_with_private_init: - // ... - // .zip(Q.point()) - // .assert_if_known(|((field_elems, (x_a, y_a)), Q)| { - // ... - // - // hash_message: - // ... - // .assert_if_known(|(field_elems, (x_a, y_a))| { - // ... - #[cfg(test)] - #[allow(non_snake_case)] - // Check equivalence to result from primitives::sinsemilla::hash_to_point - { - use crate::sinsemilla::primitives::{K, S_PERSONALIZATION}; - - use group::{prime::PrimeCurveAffine, Curve}; - use pasta_curves::arithmetic::CurveExt; - - let field_elems: Value> = message - .iter() - .map(|piece| piece.field_elem().map(|elem| (elem, piece.num_words()))) - .collect(); - - field_elems - .zip(x_a.value().zip(y_a.value())) - .zip(Q.point()) - .assert_if_known(|((field_elems, (x_a, y_a)), Q)| { - // Get message as a bitstring. - let bitstring: Vec = field_elems - .iter() - .flat_map(|(elem, num_words)| { - elem.to_le_bits().into_iter().take(K * num_words) - }) - .collect(); - - let hasher_S = pallas::Point::hash_to_curve(S_PERSONALIZATION); - let S = |chunk: &[bool]| hasher_S(&lebs2ip_k(chunk).to_le_bytes()); - - // We can use complete addition here because it differs from - // incomplete addition with negligible probability. - let expected_point = bitstring - .chunks(K) - .fold(Q.to_curve(), |acc, chunk| (acc + S(chunk)) + acc); - let actual_point = - pallas::Affine::from_xy(x_a.evaluate(), y_a.evaluate()).unwrap(); - expected_point.to_affine() == actual_point - }); - } - - x_a.value() - .zip(y_a.value()) - .error_if_known_and(|(x_a, y_a)| x_a.is_zero_vartime() || y_a.is_zero_vartime())?; - Ok(( - NonIdentityEccPoint::from_coordinates_unchecked(x_a.0, y_a), - zs_sum, - )) - } - - #[allow(non_snake_case)] - fn public_initialization( - &self, - region: &mut Region<'_, pallas::Base>, - Q: pallas::Affine, - ) -> Result<(usize, X, Y), Error> { - let config = self.config().clone(); - let offset = 0; - - // Get the `x`- and `y`-coordinates of the starting `Q` base. - let x_q = *Q.coordinates().unwrap().x(); - let y_q = *Q.coordinates().unwrap().y(); - - // 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)?; - region.assign_fixed( - || "fixed y_q", - config.fixed_y_q, - offset, - || Value::known(y_q), - )?; - - Value::known(y_q.into()).into() - }; - - // Constrain the initial x_q to equal the x-coordinate of the domain's `Q`. - let x_a: X = { - let x_a = region.assign_advice_from_constant( - || "fixed x_q", - config.double_and_add.x_a, - offset, - x_q.into(), - )?; - - x_a.into() - }; - - Ok((offset, x_a, y_a)) - } - - #[allow(non_snake_case)] - /// Assign the coordinates of the initial public point `Q` - /// - /// | offset | x_A | x_P | q_sinsemilla4 | - /// -------------------------------------- - /// | 0 | | y_Q | | - /// | 1 | x_Q | | 1 | - fn public_initialization_zsa( - &self, - region: &mut Region<'_, pallas::Base>, - Q: pallas::Affine, - ) -> Result<(usize, X, Y), Error> { - let config = self.config().clone(); - let mut offset = 0; - - // Get the `x`- and `y`-coordinates of the starting `Q` base. - let x_q = *Q.coordinates().unwrap().x(); - let y_q = *Q.coordinates().unwrap().y(); - - // 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 second row. - config.q_sinsemilla4.enable(region, offset + 1)?; - let y_a: AssignedCell, pallas::Base> = region - .assign_advice_from_constant( - || "fixed y_q", - config.double_and_add.x_p, - offset, - y_q.into(), - )?; - - y_a.value_field().into() - }; - offset += 1; - - // Constrain the initial x_q to equal the x-coordinate of the domain's `Q`. - let x_a: X = { - let x_a = region.assign_advice_from_constant( - || "fixed x_q", - config.double_and_add.x_a, - offset, - x_q.into(), - )?; - - x_a.into() - }; - - Ok((offset, x_a, y_a)) + self.hash_message_backward_compatibility(region, EccPointQ::PublicPoint(Q), message) } + /// [Specification](https://p.z.cash/halo2-0.1:sinsemilla-constraints?partial). #[allow(non_snake_case)] - /// Assign the coordinates of the initial private point `Q` - /// - /// | offset | x_A | x_P | q_sinsemilla4 | - /// -------------------------------------- - /// | 0 | | y_Q | | - /// | 1 | x_Q | | 1 | - fn private_initialization( + #[allow(clippy::type_complexity)] + pub(super) fn hash_message_with_private_init( &self, region: &mut Region<'_, pallas::Base>, Q: &NonIdentityEccPoint, - ) -> Result<(usize, X, Y), Error> { - let config = self.config().clone(); - let mut offset = 0; - - // Assign `x_Q` and `y_Q` in the region and constrain the initial x_a, lambda_1, lambda_2, - // x_p, y_Q using the q_sinsemilla4 selector. - let y_a: Y = { - // Enable `q_sinsemilla4` on the second row. - config.q_sinsemilla4.enable(region, offset + 1)?; - let q_y: AssignedCell, pallas::Base> = Q.y().into(); - let y_a: AssignedCell, pallas::Base> = - q_y.copy_advice(|| "fixed y_q", region, config.double_and_add.x_p, offset)?; - - y_a.value_field().into() - }; - offset += 1; - - let x_a: X = { - let q_x: AssignedCell, pallas::Base> = Q.x().into(); - let x_a = q_x.copy_advice(|| "fixed x_q", region, config.double_and_add.x_a, offset)?; - - x_a.into() - }; - - Ok((offset, x_a, y_a)) + message: &>::Message, + ) -> Result< + ( + NonIdentityEccPoint, + Vec>>, + ), + Error, + > { + self.hash_message_backward_compatibility(region, EccPointQ::PrivatePoint(Q), message) } #[allow(clippy::type_complexity)]