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

Arrabiata: introduce trait ArrabiataCurve to englobe all data a curve must implement to be used with Arrabiata #2719

Merged
merged 7 commits into from
Dec 19, 2024
48 changes: 32 additions & 16 deletions arrabbiata/src/constraints.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,45 @@
use super::{columns::Column, interpreter::InterpreterEnv};
use crate::{
columns::{Gadget, E},
curve::ArrabbiataCurve,
interpreter::{self, Instruction, Side},
MAX_DEGREE, NUMBER_OF_COLUMNS, NUMBER_OF_PUBLIC_INPUTS,
};
use ark_ff::{Field, PrimeField};
use ark_ec::{short_weierstrass::SWCurveConfig, CurveConfig};
use ark_ff::PrimeField;
use kimchi::circuits::{
expr::{ConstantTerm::Literal, Expr, ExprInner, Operations, Variable},
gate::CurrOrNext,
};
use log::debug;
use num_bigint::BigInt;
use o1_utils::FieldHelpers;
use poly_commitment::commitment::CommitmentCurve;

#[derive(Clone, Debug)]
pub struct Env<Fp: Field> {
pub poseidon_mds: Vec<Vec<Fp>>,
pub struct Env<C: ArrabbiataCurve> {
/// The parameter a is the coefficients of the elliptic curve in affine
/// coordinates.
// FIXME: this is ugly. Let use the curve as a parameter. Only lazy for now.
pub a: BigInt,
pub idx_var: usize,
pub idx_var_next_row: usize,
pub idx_var_pi: usize,
pub constraints: Vec<E<Fp>>,
pub constraints: Vec<E<C::ScalarField>>,
pub activated_gadget: Option<Gadget>,
}

impl<Fp: PrimeField> Env<Fp> {
pub fn new(poseidon_mds: Vec<Vec<Fp>>, a: BigInt) -> Self {
impl<C: ArrabbiataCurve> Env<C>
where
<<C as CommitmentCurve>::Params as CurveConfig>::BaseField: PrimeField,
{
pub fn new() -> Self {
// This check might not be useful
assert!(a < Fp::modulus_biguint().into(), "a is too large");
let a: BigInt = <C as CommitmentCurve>::Params::COEFF_A.to_biguint().into();
assert!(
a < C::ScalarField::modulus_biguint().into(),
"a is too large"
);
Self {
poseidon_mds,
a,
idx_var: 0,
idx_var_next_row: 0,
Expand All @@ -48,10 +55,10 @@ impl<Fp: PrimeField> Env<Fp> {
/// proof.
/// The constraint environment must be instantiated only once, at the last step
/// of the computation.
impl<Fp: PrimeField> InterpreterEnv for Env<Fp> {
impl<C: ArrabbiataCurve> InterpreterEnv for Env<C> {
type Position = (Column, CurrOrNext);

type Variable = E<Fp>;
type Variable = E<C::ScalarField>;

fn allocate(&mut self) -> Self::Position {
assert!(self.idx_var < NUMBER_OF_COLUMNS, "Maximum number of columns reached ({NUMBER_OF_COLUMNS}), increase the number of columns");
Expand Down Expand Up @@ -81,7 +88,7 @@ impl<Fp: PrimeField> InterpreterEnv for Env<Fp> {

fn constant(&self, value: BigInt) -> Self::Variable {
let v = value.to_biguint().unwrap();
let v = Fp::from_biguint(&v).unwrap();
let v = C::ScalarField::from_biguint(&v).unwrap();
let v_inner = Operations::from(Literal(v));
Self::Variable::constant(v_inner)
}
Expand Down Expand Up @@ -180,7 +187,7 @@ impl<Fp: PrimeField> InterpreterEnv for Env<Fp> {
}

fn get_poseidon_mds_matrix(&mut self, i: usize, j: usize) -> Self::Variable {
let v = self.poseidon_mds[i][j];
let v = C::sponge_params().mds[i][j];
let v_inner = Operations::from(Literal(v));
Self::Variable::constant(v_inner)
}
Expand Down Expand Up @@ -304,7 +311,7 @@ impl<Fp: PrimeField> InterpreterEnv for Env<Fp> {
}
}

impl<F: PrimeField> Env<F> {
impl<C: ArrabbiataCurve> Env<C> {
/// Get all the constraints for the IVC circuit, only.
///
/// The following gadgets are used in the IVC circuit:
Expand All @@ -317,7 +324,7 @@ impl<F: PrimeField> Env<F> {
// the computation of the challenges.
// FIXME: add a test checking that whatever the value given in parameter of
// the gadget, the constraints are the same
pub fn get_all_constraints_for_ivc(&self) -> Vec<E<F>> {
pub fn get_all_constraints_for_ivc(&self) -> Vec<E<C::ScalarField>> {
// Copying the instance we got in parameter, and making it mutable to
// avoid modifying the original instance.
let mut env = self.clone();
Expand Down Expand Up @@ -354,7 +361,7 @@ impl<F: PrimeField> Env<F> {
// FIXME: the application should be given as an argument to handle Rust
// zkApp. It is only for the PoC.
// FIXME: the selectors are not added for now.
pub fn get_all_constraints(&self) -> Vec<E<F>> {
pub fn get_all_constraints(&self) -> Vec<E<C::ScalarField>> {
let mut constraints = self.get_all_constraints_for_ivc();

// Copying the instance we got in parameter, and making it mutable to
Expand All @@ -370,3 +377,12 @@ impl<F: PrimeField> Env<F> {
constraints
}
}

impl<C: ArrabbiataCurve> Default for Env<C>
where
<<C as CommitmentCurve>::Params as CurveConfig>::BaseField: PrimeField,
{
fn default() -> Self {
Self::new()
}
}
103 changes: 103 additions & 0 deletions arrabbiata/src/curve.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//! This file defines a trait similar to [kimchi::curve::KimchiCurve] for Pallas and
//! Vesta. It aims to define all the parameters that are needed by a curve to be
//! used in Arrabbiata. For instance, the sponge parameters, the endomorphism
//! coefficients, etc.
//! The goal of this trait is to parametrize the whole library with the
//! different curves.

use ark_ec::short_weierstrass::Affine;
use kimchi::curve::{pallas_endos, vesta_endos};
use mina_curves::pasta::curves::{pallas::PallasParameters, vesta::VestaParameters};
use mina_poseidon::{constants::SpongeConstants, poseidon::ArithmeticSpongeParams};
use poly_commitment::commitment::{CommitmentCurve, EndoCurve};

#[derive(Clone)]
pub struct PlonkSpongeConstants {}

impl SpongeConstants for PlonkSpongeConstants {
const SPONGE_CAPACITY: usize = 1;
const SPONGE_WIDTH: usize = 3;
const SPONGE_RATE: usize = 2;
const PERM_ROUNDS_FULL: usize = 60;
const PERM_ROUNDS_PARTIAL: usize = 0;
const PERM_HALF_ROUNDS_FULL: usize = 0;
const PERM_SBOX: u32 = 5;
const PERM_FULL_MDS: bool = true;
const PERM_INITIAL_ARK: bool = false;
}

/// Represents additional information that a curve needs in order to be used
/// with Arrabbiata.
pub trait ArrabbiataCurve: CommitmentCurve + EndoCurve {
/// A human readable name.
const NAME: &'static str;

// FIXME: use this in the codebase.
// We might want to use different sponge constants for different curves.
// For now, it does use the same constants for both curves.
type SpongeConstants: SpongeConstants;

const SPONGE_CONSTANTS: Self::SpongeConstants;

/// Provides the sponge params to be used with this curve.
fn sponge_params() -> &'static ArithmeticSpongeParams<Self::ScalarField>;

/// Provides the sponge params to be used with the other curve.
fn other_curve_sponge_params() -> &'static ArithmeticSpongeParams<Self::BaseField>;

/// Provides the coefficients for the curve endomorphism, called (q,r) in
/// some places.
fn endos() -> &'static (Self::BaseField, Self::ScalarField);

/// Provides the coefficient for the curve endomorphism over the other
/// field, called q in some places.
fn other_curve_endo() -> &'static Self::ScalarField;
}

impl ArrabbiataCurve for Affine<PallasParameters> {
const NAME: &'static str = "pallas";

type SpongeConstants = PlonkSpongeConstants;

const SPONGE_CONSTANTS: Self::SpongeConstants = PlonkSpongeConstants {};

fn sponge_params() -> &'static ArithmeticSpongeParams<Self::ScalarField> {
crate::poseidon_3_60_0_5_5_fq::static_params()
}

fn other_curve_sponge_params() -> &'static ArithmeticSpongeParams<Self::BaseField> {
crate::poseidon_3_60_0_5_5_fp::static_params()
}

fn endos() -> &'static (Self::BaseField, Self::ScalarField) {
pallas_endos()
}

fn other_curve_endo() -> &'static Self::ScalarField {
&vesta_endos().0
}
}

impl ArrabbiataCurve for Affine<VestaParameters> {
const NAME: &'static str = "vesta";

type SpongeConstants = PlonkSpongeConstants;

const SPONGE_CONSTANTS: Self::SpongeConstants = PlonkSpongeConstants {};

fn sponge_params() -> &'static ArithmeticSpongeParams<Self::ScalarField> {
crate::poseidon_3_60_0_5_5_fp::static_params()
}

fn other_curve_sponge_params() -> &'static ArithmeticSpongeParams<Self::BaseField> {
crate::poseidon_3_60_0_5_5_fq::static_params()
}

fn endos() -> &'static (Self::BaseField, Self::ScalarField) {
vesta_endos()
}

fn other_curve_endo() -> &'static Self::ScalarField {
&pallas_endos().0
}
}
35 changes: 22 additions & 13 deletions arrabbiata/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@
//!
//! Hashing is a crucial part of the IVC scheme. The hash function the
//! interpreter does use for the moment is an instance of the Poseidon hash
//! function with a fixed state size of [POSEIDON_STATE_SIZE]. Increasing the
//! function with a fixed state size of
//! [crate::curve::PlonkSpongeConstants::SPONGE_WIDTH]. Increasing the
//! state size can be considered as it would potentially optimize the
//! number of rounds, and allow hashing more data on one row. We leave this for
//! future works.
Expand Down Expand Up @@ -338,11 +339,11 @@
//! there.

use crate::{
columns::Gadget, MAXIMUM_FIELD_SIZE_IN_BITS, NUMBER_OF_COLUMNS, POSEIDON_ROUNDS_FULL,
POSEIDON_STATE_SIZE,
columns::Gadget, curve::PlonkSpongeConstants, MAXIMUM_FIELD_SIZE_IN_BITS, NUMBER_OF_COLUMNS,
};
use ark_ff::{One, Zero};
use log::debug;
use mina_poseidon::constants::SpongeConstants;
use num_bigint::BigInt;

/// A list of instruction/gadget implemented in the interpreter.
Expand Down Expand Up @@ -830,19 +831,23 @@ pub fn run_ivc<E: InterpreterEnv>(env: &mut E, instr: Instruction) {
Instruction::Poseidon(curr_round) => {
env.activate_gadget(Gadget::Poseidon);
debug!("Executing instruction Poseidon({curr_round})");
if curr_round < POSEIDON_ROUNDS_FULL {
if curr_round < PlonkSpongeConstants::PERM_ROUNDS_FULL {
// Values to be absorbed are 0 when when the round is not zero,
// i.e. when we are processing the rounds.
let values_to_absorb: Vec<E::Variable> = (0..POSEIDON_STATE_SIZE - 1)
let values_to_absorb: Vec<E::Variable> = (0..PlonkSpongeConstants::SPONGE_WIDTH
- 1)
.map(|_i| {
let pos = env.allocate_public_input();
// fetch_value_to_absorb is supposed to return 0 if curr_round != 0.
unsafe { env.fetch_value_to_absorb(pos, curr_round) }
})
.collect();
let round_input_positions: Vec<E::Position> =
(0..POSEIDON_STATE_SIZE).map(|_i| env.allocate()).collect();
let round_output_positions: Vec<E::Position> = (0..POSEIDON_STATE_SIZE)
let round_input_positions: Vec<E::Position> = (0
..PlonkSpongeConstants::SPONGE_WIDTH)
.map(|_i| env.allocate())
.collect();
let round_output_positions: Vec<E::Position> = (0
..PlonkSpongeConstants::SPONGE_WIDTH)
.map(|_i| env.allocate_next_row())
.collect();
// If we are at the first round, we load the state from the environment.
Expand All @@ -856,8 +861,9 @@ pub fn run_ivc<E: InterpreterEnv>(env: &mut E, instr: Instruction) {
.enumerate()
.map(|(i, pos)| {
let res = env.load_poseidon_state(*pos, i);
// Absorb value. The capacity is POSEIDON_STATE_SIZE - 1
if i < POSEIDON_STATE_SIZE - 1 {
// Absorb value. The capacity is
// PlonkSpongeConstants::SPONGE_WIDTH - 1
if i < PlonkSpongeConstants::SPONGE_WIDTH - 1 {
res + values_to_absorb[i].clone()
} else {
res
Expand All @@ -882,7 +888,7 @@ pub fn run_ivc<E: InterpreterEnv>(env: &mut E, instr: Instruction) {

let round = curr_round + idx_round;

let rcs: Vec<E::Variable> = (0..POSEIDON_STATE_SIZE)
let rcs: Vec<E::Variable> = (0..PlonkSpongeConstants::SPONGE_WIDTH)
.map(|i| {
let pos = env.allocate_public_input();
env.get_poseidon_round_constant(pos, round, i)
Expand Down Expand Up @@ -915,7 +921,7 @@ pub fn run_ivc<E: InterpreterEnv>(env: &mut E, instr: Instruction) {
// now, we will save the state at the end of the last round
// and reload it at the beginning of the next Poseidon full
// hash.
if round == POSEIDON_ROUNDS_FULL - 1 {
if round == PlonkSpongeConstants::PERM_ROUNDS_FULL - 1 {
state.iter().enumerate().for_each(|(i, x)| {
unsafe { env.save_poseidon_state(x.clone(), i) };
});
Expand All @@ -924,7 +930,10 @@ pub fn run_ivc<E: InterpreterEnv>(env: &mut E, instr: Instruction) {
state
});
} else {
panic!("Invalid index: it is supposed to be less than {POSEIDON_ROUNDS_FULL}");
panic!(
"Invalid index: it is supposed to be less than {}",
PlonkSpongeConstants::PERM_ROUNDS_FULL
);
}
}
Instruction::NoOp => {}
Expand Down
13 changes: 1 addition & 12 deletions arrabbiata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use strum::EnumCount as _;
pub mod column_env;
pub mod columns;
pub mod constraints;
pub mod curve;
pub mod interpreter;
pub mod logup;
pub mod poseidon_3_60_0_5_5_fp;
Expand Down Expand Up @@ -34,18 +35,6 @@ pub const NUMBER_OF_COLUMNS: usize = 15;
/// to absorb.
pub const NUMBER_OF_PUBLIC_INPUTS: usize = 15 + 2;

/// The low-exponentiation value used by the Poseidon hash function for the
/// substitution box.
///
/// The value is used to perform initialisation checks with the fields.
pub const POSEIDON_ALPHA: u64 = 5;

/// The number of full rounds in the Poseidon hash function.
pub const POSEIDON_ROUNDS_FULL: usize = 60;

/// The number of elements in the state of the Poseidon hash function.
pub const POSEIDON_STATE_SIZE: usize = 3;

/// The maximum number of bits the fields can be.
/// It is critical as we have some assumptions for the gadgets describing the
/// IVC.
Expand Down
7 changes: 5 additions & 2 deletions arrabbiata/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use arrabbiata::{
curve::PlonkSpongeConstants,
interpreter::{self, InterpreterEnv},
witness::Env,
IVC_CIRCUIT_SIZE, MIN_SRS_LOG2_SIZE, POSEIDON_STATE_SIZE,
IVC_CIRCUIT_SIZE, MIN_SRS_LOG2_SIZE,
};
use log::{debug, info};
use mina_curves::pasta::{Fp, Fq, Pallas, Vesta};
use mina_poseidon::constants::SpongeConstants;
use num_bigint::BigInt;
use std::time::Instant;

Expand Down Expand Up @@ -47,7 +49,8 @@ pub fn main() {
let domain_size = 1 << srs_log2_size;

// FIXME: setup correctly the initial sponge state
let sponge_e1: [BigInt; POSEIDON_STATE_SIZE] = std::array::from_fn(|_i| BigInt::from(42u64));
let sponge_e1: [BigInt; PlonkSpongeConstants::SPONGE_WIDTH] =
std::array::from_fn(|_i| BigInt::from(42u64));
// FIXME: make a setup phase to build the selectors
let mut env = Env::<Fp, Fq, Vesta, Pallas>::new(
*srs_log2_size,
Expand Down
7 changes: 3 additions & 4 deletions arrabbiata/src/prover.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! A prover for the folding/accumulation scheme

use crate::proof::Proof;
use ark_ec::AffineRepr;
use crate::{curve::ArrabbiataCurve, proof::Proof};
use ark_ff::PrimeField;

use crate::witness::Env;
Expand All @@ -12,8 +11,8 @@ use crate::witness::Env;
pub fn prove<
Fp: PrimeField,
Fq: PrimeField,
E1: AffineRepr<ScalarField = Fp, BaseField = Fq>,
E2: AffineRepr<ScalarField = Fq, BaseField = Fp>,
E1: ArrabbiataCurve<ScalarField = Fp, BaseField = Fq>,
E2: ArrabbiataCurve<ScalarField = Fq, BaseField = Fp>,
>(
_env: &Env<Fp, Fq, E1, E2>,
) -> Result<Proof, String> {
Expand Down
Loading
Loading