Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Bump arkworks to version 0.5.0 #6871

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

winderica
Copy link

Description

Problem*

(N/A)

Summary*

This PR bumps arkworks to version 0.5.0, where noir_grumpkin is additionally replaced by ark-grumpkin.

The MSRV is also updated to 1.75.0 due to the requirement of the new arkworks.

Additional Context

Note that two of the dependencies still rely on arkworks 0.4.0:

  • light-poseidon: There is already a PR that updates arkworks, which is thus used in
    light-poseidon = { git = "https://github.com/Lightprotocol/light-poseidon", rev = "c3bb062" }
  • zkhash: Unfortunately I failed to find the repository for zkhash and couldn't directly contribute to it. Instead of maintaining a fork, I tentatively copied its implementation into
    mod zkhash {
    use ark_ff::PrimeField;
    use std::sync::Arc;
    #[derive(Clone, Debug)]
    pub struct Poseidon2Params<F: PrimeField> {
    pub(crate) t: usize, // statesize
    pub(crate) d: usize, // sbox degree
    pub(crate) rounds_f_beginning: usize,
    pub(crate) rounds_p: usize,
    #[allow(dead_code)]
    pub(crate) rounds_f_end: usize,
    pub(crate) rounds: usize,
    pub(crate) mat_internal_diag_m_1: Vec<F>,
    pub(crate) _mat_internal: Vec<Vec<F>>,
    pub(crate) round_constants: Vec<Vec<F>>,
    }
    impl<F: PrimeField> Poseidon2Params<F> {
    #[allow(clippy::too_many_arguments)]
    pub fn new(
    t: usize,
    d: usize,
    rounds_f: usize,
    rounds_p: usize,
    mat_internal_diag_m_1: &[F],
    mat_internal: &[Vec<F>],
    round_constants: &[Vec<F>],
    ) -> Self {
    assert!(d == 3 || d == 5 || d == 7 || d == 11);
    assert_eq!(rounds_f % 2, 0);
    let r = rounds_f / 2;
    let rounds = rounds_f + rounds_p;
    Poseidon2Params {
    t,
    d,
    rounds_f_beginning: r,
    rounds_p,
    rounds_f_end: r,
    rounds,
    mat_internal_diag_m_1: mat_internal_diag_m_1.to_owned(),
    _mat_internal: mat_internal.to_owned(),
    round_constants: round_constants.to_owned(),
    }
    }
    }
    #[derive(Clone, Debug)]
    pub struct Poseidon2<F: PrimeField> {
    pub(crate) params: Arc<Poseidon2Params<F>>,
    }
    impl<F: PrimeField> Poseidon2<F> {
    pub fn new(params: &Arc<Poseidon2Params<F>>) -> Self {
    Poseidon2 { params: Arc::clone(params) }
    }
    pub fn permutation(&self, input: &[F]) -> Vec<F> {
    let t = self.params.t;
    assert_eq!(input.len(), t);
    let mut current_state = input.to_owned();
    // Linear layer at beginning
    self.matmul_external(&mut current_state);
    for r in 0..self.params.rounds_f_beginning {
    current_state = self.add_rc(&current_state, &self.params.round_constants[r]);
    current_state = self.sbox(&current_state);
    self.matmul_external(&mut current_state);
    }
    let p_end = self.params.rounds_f_beginning + self.params.rounds_p;
    for r in self.params.rounds_f_beginning..p_end {
    current_state[0].add_assign(&self.params.round_constants[r][0]);
    current_state[0] = self.sbox_p(&current_state[0]);
    self.matmul_internal(&mut current_state, &self.params.mat_internal_diag_m_1);
    }
    for r in p_end..self.params.rounds {
    current_state = self.add_rc(&current_state, &self.params.round_constants[r]);
    current_state = self.sbox(&current_state);
    self.matmul_external(&mut current_state);
    }
    current_state
    }
    fn sbox(&self, input: &[F]) -> Vec<F> {
    input.iter().map(|el| self.sbox_p(el)).collect()
    }
    fn sbox_p(&self, input: &F) -> F {
    let mut input2 = *input;
    input2.square_in_place();
    match self.params.d {
    3 => {
    let mut out = input2;
    out.mul_assign(input);
    out
    }
    5 => {
    let mut out = input2;
    out.square_in_place();
    out.mul_assign(input);
    out
    }
    7 => {
    let mut out = input2;
    out.square_in_place();
    out.mul_assign(&input2);
    out.mul_assign(input);
    out
    }
    _ => {
    panic!()
    }
    }
    }
    fn matmul_m4(&self, input: &mut [F]) {
    let t = self.params.t;
    let t4 = t / 4;
    for i in 0..t4 {
    let start_index = i * 4;
    let mut t_0 = input[start_index];
    t_0.add_assign(&input[start_index + 1]);
    let mut t_1 = input[start_index + 2];
    t_1.add_assign(&input[start_index + 3]);
    let mut t_2 = input[start_index + 1];
    t_2.double_in_place();
    t_2.add_assign(&t_1);
    let mut t_3 = input[start_index + 3];
    t_3.double_in_place();
    t_3.add_assign(&t_0);
    let mut t_4 = t_1;
    t_4.double_in_place();
    t_4.double_in_place();
    t_4.add_assign(&t_3);
    let mut t_5 = t_0;
    t_5.double_in_place();
    t_5.double_in_place();
    t_5.add_assign(&t_2);
    let mut t_6 = t_3;
    t_6.add_assign(&t_5);
    let mut t_7 = t_2;
    t_7.add_assign(&t_4);
    input[start_index] = t_6;
    input[start_index + 1] = t_5;
    input[start_index + 2] = t_7;
    input[start_index + 3] = t_4;
    }
    }
    fn matmul_external(&self, input: &mut [F]) {
    let t = self.params.t;
    match t {
    2 => {
    // Matrix circ(2, 1)
    let mut sum = input[0];
    sum.add_assign(&input[1]);
    input[0].add_assign(&sum);
    input[1].add_assign(&sum);
    }
    3 => {
    // Matrix circ(2, 1, 1)
    let mut sum = input[0];
    sum.add_assign(&input[1]);
    sum.add_assign(&input[2]);
    input[0].add_assign(&sum);
    input[1].add_assign(&sum);
    input[2].add_assign(&sum);
    }
    4 => {
    // Applying cheap 4x4 MDS matrix to each 4-element part of the state
    self.matmul_m4(input);
    }
    8 | 12 | 16 | 20 | 24 => {
    // Applying cheap 4x4 MDS matrix to each 4-element part of the state
    self.matmul_m4(input);
    // Applying second cheap matrix for t > 4
    let t4 = t / 4;
    let mut stored = [F::zero(); 4];
    for l in 0..4 {
    stored[l] = input[l];
    for j in 1..t4 {
    stored[l].add_assign(&input[4 * j + l]);
    }
    }
    for i in 0..input.len() {
    input[i].add_assign(&stored[i % 4]);
    }
    }
    _ => {
    panic!()
    }
    }
    }
    fn matmul_internal(&self, input: &mut [F], mat_internal_diag_m_1: &[F]) {
    let t = self.params.t;
    match t {
    2 => {
    // [2, 1]
    // [1, 3]
    let mut sum = input[0];
    sum.add_assign(&input[1]);
    input[0].add_assign(&sum);
    input[1].double_in_place();
    input[1].add_assign(&sum);
    }
    3 => {
    // [2, 1, 1]
    // [1, 2, 1]
    // [1, 1, 3]
    let mut sum = input[0];
    sum.add_assign(&input[1]);
    sum.add_assign(&input[2]);
    input[0].add_assign(&sum);
    input[1].add_assign(&sum);
    input[2].double_in_place();
    input[2].add_assign(&sum);
    }
    4 | 8 | 12 | 16 | 20 | 24 => {
    // Compute input sum
    let mut sum = input[0];
    input.iter().skip(1).take(t - 1).for_each(|el| sum.add_assign(el));
    // Add sum + diag entry * element to each element
    for i in 0..input.len() {
    input[i].mul_assign(&mat_internal_diag_m_1[i]);
    input[i].add_assign(&sum);
    }
    }
    _ => {
    panic!()
    }
    }
    }
    fn add_rc(&self, input: &[F], rc: &[F]) -> Vec<F> {
    input
    .iter()
    .zip(rc.iter())
    .map(|(a, b)| {
    let mut r = *a;
    r.add_assign(b);
    r
    })
    .collect()
    }
    }
    }

Please let me know if you are fine with the solutions. I am happy to make changes based on your feedback :)

Documentation*

Check one:

  • No documentation needed.
  • Documentation included in this PR.
  • [For Experimental Features] Documentation to be submitted in a separate PR.

PR Checklist*

  • I have tested the changes locally.
  • I have formatted the changes with Prettier and/or cargo fmt on default settings.

Copy link
Contributor

Thank you for your contribution to the Noir language.

Please do not force push to this branch after the Noir team have started review of this PR. Doing so will only delay us merging your PR as we will need to start the review process from scratch.

Thanks for your understanding.

@winderica winderica changed the title Bump arkworks to version 0.5.0 chore: Bump arkworks to version 0.5.0 Dec 19, 2024
@TomAFrench
Copy link
Member

Awesome, thank you for this PR! Ideally we'd have a crates.io release of light-poseidon before merging this as we're looking to remove our git dependencies.

We'll probably take a proper look at getting this merged in the new year.

@winderica
Copy link
Author

Thanks! In fact, we are also replacing git dependencies with crates.io ones in sonobe, and acvm is among the last few libraries that are still git-based forks, which is why I opened this PR 😄

I will watch light-poseidon and update this PR once a new version is released!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants