Skip to content

Commit

Permalink
remove duplicate code, simplify code
Browse files Browse the repository at this point in the history
  • Loading branch information
YaoGalteland committed Mar 24, 2024
1 parent 32c21b9 commit 1f35f3c
Showing 1 changed file with 118 additions and 204 deletions.
322 changes: 118 additions & 204 deletions halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Hash, Commit, Fixed> SinsemillaChip<Hash, Commit, Fixed>
where
Hash: HashDomains<pallas::Affine>,
Fixed: FixedPoints<pallas::Affine>,
Commit: CommitDomains<pallas::Affine, Fixed, Hash>,
{
/// [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>(

Check warning on line 32 in halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs

View workflow job for this annotation

GitHub Actions / Clippy (beta)

the following explicit lifetimes could be elided: 'a

warning: the following explicit lifetimes could be elided: 'a --> halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs:32:55 | 32 | pub(super) fn hash_message_backward_compatibility<'a>( | ^^ ... 35 | Q: EccPointQ<'a>, | ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes = note: `-W clippy::needless-lifetimes` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::needless_lifetimes)]` help: elide the lifetimes | 32 ~ pub(super) fn hash_message_backward_compatibility( 33 | &self, 34 | region: &mut Region<'_, pallas::Base>, 35 ~ Q: EccPointQ<'_>, |

Check warning on line 32 in halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs

View workflow job for this annotation

GitHub Actions / Clippy (beta)

the following explicit lifetimes could be elided: 'a

warning: the following explicit lifetimes could be elided: 'a --> halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs:32:55 | 32 | pub(super) fn hash_message_backward_compatibility<'a>( | ^^ ... 35 | Q: EccPointQ<'a>, | ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes = note: `-W clippy::needless-lifetimes` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::needless_lifetimes)]` help: elide the lifetimes | 32 ~ pub(super) fn hash_message_backward_compatibility( 33 | &self, 34 | region: &mut Region<'_, pallas::Base>, 35 ~ Q: EccPointQ<'_>, |
&self,
region: &mut Region<'_, pallas::Base>,
Q: pallas::Affine,
Q: EccPointQ<'a>,
message: &<Self as SinsemillaInstructions<
pallas::Affine,
{ sinsemilla::K },
Expand All @@ -41,12 +45,7 @@ where
),
Error,
> {
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)]
Expand All @@ -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())

Check warning on line 68 in halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs

View workflow job for this annotation

GitHub Actions / Clippy (beta)

using `clone` on type `EpAffine` which implements the `Copy` trait

warning: using `clone` on type `EpAffine` which implements the `Copy` trait --> halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs:68:57 | 68 | halo2_proofs::circuit::Value::known(p.clone()) | ^^^^^^^^^ help: try removing the `clone` call: `p` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy = note: `-W clippy::clone-on-copy` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::clone_on_copy)]`

Check failure on line 68 in halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs

View workflow job for this annotation

GitHub Actions / Clippy (MSRV)

using `clone` on type `pasta_curves::EpAffine` which implements the `Copy` trait

error: using `clone` on type `pasta_curves::EpAffine` which implements the `Copy` trait --> halo2_gadgets/src/sinsemilla/chip/hash_to_point.rs:68:57 | 68 | halo2_proofs::circuit::Value::known(p.clone()) | ^^^^^^^^^ help: try removing the `clone` call: `p` | = note: `-D clippy::clone-on-copy` implied by `-D warnings` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
}
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<bool> = field_elems
.iter()
Expand All @@ -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
Expand All @@ -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<pallas::Base>, Y<pallas::Base>), 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<pallas::Base> = {
// 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<pallas::Base> = {
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<pallas::Base> = {
// Enable `q_sinsemilla4` on the second row.
config.q_sinsemilla4.enable(region, offset + 1)?;
let q_y: AssignedCell<Assigned<pallas::Base>, pallas::Base> = q.y().into();
let y_a: AssignedCell<Assigned<pallas::Base>, 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<pallas::Base> = {
let q_x: AssignedCell<Assigned<pallas::Base>, 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: &<Self as SinsemillaInstructions<
pallas::Affine,
{ sinsemilla::K },
Expand All @@ -116,202 +203,29 @@ where
),
Error,
> {
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<Vec<_>> = 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<bool> = 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<pallas::Base>, Y<pallas::Base>), 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<pallas::Base> = {
// 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<pallas::Base> = {
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<pallas::Base>, Y<pallas::Base>), 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<pallas::Base> = {
// Enable `q_sinsemilla4` on the second row.
config.q_sinsemilla4.enable(region, offset + 1)?;
let y_a: AssignedCell<Assigned<pallas::Base>, 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<pallas::Base> = {
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<pallas::Base>, Y<pallas::Base>), 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<pallas::Base> = {
// Enable `q_sinsemilla4` on the second row.
config.q_sinsemilla4.enable(region, offset + 1)?;
let q_y: AssignedCell<Assigned<pallas::Base>, pallas::Base> = Q.y().into();
let y_a: AssignedCell<Assigned<pallas::Base>, 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<pallas::Base> = {
let q_x: AssignedCell<Assigned<pallas::Base>, 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: &<Self as SinsemillaInstructions<
pallas::Affine,
{ sinsemilla::K },
{ sinsemilla::C },
>>::Message,
) -> Result<
(
NonIdentityEccPoint,
Vec<Vec<AssignedCell<pallas::Base, pallas::Base>>>,
),
Error,
> {
self.hash_message_backward_compatibility(region, EccPointQ::PrivatePoint(Q), message)
}

#[allow(clippy::type_complexity)]
Expand Down

0 comments on commit 1f35f3c

Please sign in to comment.