Skip to content

Commit

Permalink
Fibonacci Stark Prover (#59)
Browse files Browse the repository at this point in the history
* prover sub crate created

* working on fold function

* merge

* test working

* fold test completed

* next_fri_layer function

* Dependencies removed

* using iterator step_by

* fmt

* reordering fri functions

* fri_decommit init

* evaluate_vec in polynomial and reference in evaluate

* using evaluate_vec

* evaluate_vec changed to evaluate_slice

* evaluate_slice changed

* fri_commitment

* fri continuation

* comment moved

* fri_decommit_layers

* comments added

* polynomial.rs merge confilct

* adapting to the new code

* conflicts solved

* append in transcript

* insert last_evaluation in transcript

* beta from transcript.challenge()

* test: generating subgroups

* prover sub crate created

* Save work in progress

* Add first iteration of function to get composition polynomials from trace and air

* Add test for get_composition_poly

* Add get_coefficients function

* Tidy up code

* Add docs

* Fix tests

* Add u128_prime field and make get_composition_poly return a Polynomial data structure

* Fixes from rebasing

* Apply clippy suggestions

* Make functions pub crate

* Tidy up code

* Tidy up code

* Minor fixes

* Use U384 instead of U128

* Tidy up code and remove unnecessary u128 field element module

* generate_vec_roots

* generate_vec_roots in lib

* Return trace polynomial from get_composition_poly

* coset_factor

* Add coset evaluation and fri commitment steps

* Add result to get_cp_and_tp

* Change error description and module name

* Add decommitment step

* Start filling the stark proof struct

* Small comments

* Add first verifier step

* Switch to hardcoded fibonacci trace

* Start FRI verification step

* More progress

* Improve code, change field to 17 for testing purposes

* Fix FRI operation

* Go back to fibonacci example with test passing

* Refactor functions that use fiat shamir to take in a transcript

* Add TODO

* Add comments

* Moved field definition to lib, removed duplicated definitions

* Renamed types

* Simplified operations

* Refactor roots of unity generator

* Small refactor

* Refactor roots of unity generator

* Update comment

* Extracted FRI

* Refactor verify

* Refactor clippy

* Re ordered prover

* cargo fmt

* fix roots of unity

* Remove air

* Prover -> Stark

* Move folders

* Uncomment tests, remove unused code

* Fix fri_functions tests

* Remove fri_merkle_tree module, move to mod.rs

* Clippy

* Remove TODOs

---------

Co-authored-by: Pablo Deymonnaz <deymonnaz@gmail.com>
Co-authored-by: Mariano Nicolini <mariano.nicolini.91@gmail.com>
Co-authored-by: Javier Chatruc <jrchatruc@gmail.com>
Co-authored-by: MauroFab <maurotoscano2@gmail.com>
  • Loading branch information
5 people authored Mar 2, 2023
1 parent 5ebbd30 commit 3c681a3
Show file tree
Hide file tree
Showing 20 changed files with 819 additions and 8 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
members = [
"math",
"crypto",
"proving-system/stark",
]
2 changes: 1 addition & 1 deletion crypto/src/fiat_shamir/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
mod transcript;
pub mod transcript;
10 changes: 5 additions & 5 deletions crypto/src/fiat_shamir/transcript.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
use sha3::{Digest, Sha3_256};

struct Transcript {
pub struct Transcript {
hasher: Sha3_256,
}

impl Transcript {
#[allow(unused)]
fn new() -> Self {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
hasher: Sha3_256::new(),
}
}

#[allow(unused)]
fn append(&mut self, new_data: &[u8]) {
pub fn append(&mut self, new_data: &[u8]) {
self.hasher.update(&mut new_data.to_owned());
}

#[allow(unused)]
fn challenge(&mut self) -> [u8; 32] {
pub fn challenge(&mut self) -> [u8; 32] {
let mut result_hash = [0_u8; 32];
result_hash.copy_from_slice(&self.hasher.finalize_reset());
self.hasher.update(result_hash);
Expand Down
2 changes: 1 addition & 1 deletion crypto/src/merkle_tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::hash::traits::IsCryptoHash;
use self::{merkle::MerkleTree, proof::Proof};

pub mod merkle;
mod proof;
pub mod proof;
mod utils;

pub type U64F = U64PrimeField<0xFFFF_FFFF_0000_0001_u64>;
Expand Down
17 changes: 17 additions & 0 deletions crypto/src/merkle_tree/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,29 @@ use lambdaworks_math::{
traits::ByteConversion,
};

#[derive(Debug, Clone)]
pub struct Proof<F: IsField, H: IsCryptoHash<F>> {
pub value: FieldElement<F>,
pub merkle_path: Vec<(FieldElement<F>, bool)>,
pub hasher: H,
}

impl<F: IsField, H: IsCryptoHash<F>> Proof<F, H> {
pub fn verify(&self, root_hash: FieldElement<F>) -> bool {
let mut hashed_value = self.hasher.hash_one(self.value.clone());

for (sibling_node, is_left) in self.merkle_path.iter().rev() {
if *is_left {
hashed_value = self.hasher.hash_two(hashed_value, sibling_node.clone());
} else {
hashed_value = self.hasher.hash_two(sibling_node.clone(), hashed_value);
}
}

root_hash == hashed_value
}
}

impl<F, H> ByteConversion for Proof<F, H>
where
F: IsField,
Expand Down
5 changes: 5 additions & 0 deletions math/src/field/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,11 @@ where
&self.value
}

// Returns the representative of the value stored
pub fn representative(&self) -> F::BaseType {
F::representative(self.value.clone())
}

/// Returns the multiplicative inverse of `self`
pub fn inv(&self) -> Self {
Self {
Expand Down
4 changes: 4 additions & 0 deletions math/src/field/extensions/cubic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ where
fn from_base_type(x: [FieldElement<Q::BaseField>; 3]) -> [FieldElement<Q::BaseField>; 3] {
x
}

fn representative(_x: Self::BaseType) -> Self::BaseType {
todo!()
}
}

#[cfg(test)]
Expand Down
4 changes: 4 additions & 0 deletions math/src/field/extensions/quadratic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ where
fn from_base_type(x: [FieldElement<Q::BaseField>; 2]) -> [FieldElement<Q::BaseField>; 2] {
x
}

fn representative(_x: Self::BaseType) -> Self::BaseType {
todo!()
}
}

#[cfg(test)]
Expand Down
5 changes: 5 additions & 0 deletions math/src/field/fields/u384_prime_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ where
fn from_base_type(x: Self::BaseType) -> Self::BaseType {
MontgomeryAlgorithms::cios(&x, &C::R2, &C::MODULUS, &C::MU)
}

// TO DO: Add tests for representatives
fn representative(x: Self::BaseType) -> Self::BaseType {
MontgomeryAlgorithms::cios(&x, &U384::from_u64(1), &C::MODULUS, &C::MU)
}
}

impl<C> ByteConversion for FieldElement<MontgomeryBackendPrimeField<C>>
Expand Down
4 changes: 4 additions & 0 deletions math/src/field/fields/u64_prime_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ impl<const MODULUS: u64> IsField for U64PrimeField<MODULUS> {
fn from_base_type(x: u64) -> u64 {
Self::from_u64(x)
}

fn representative(x: u64) -> u64 {
x
}
}

impl<const MODULUS: u64> Copy for U64FieldElement<MODULUS> {}
Expand Down
4 changes: 4 additions & 0 deletions math/src/field/test_fields/u64_test_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ impl<const MODULUS: u64> IsField for U64TestField<MODULUS> {
fn from_base_type(x: u64) -> u64 {
Self::from_u64(x)
}

fn representative(x: u64) -> u64 {
x
}
}

impl<const MODULUS: u64> IsTwoAdicField for U64TestField<MODULUS> {
Expand Down
3 changes: 3 additions & 0 deletions math/src/field/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,7 @@ pub trait IsField: Debug + Clone {
/// Takes as input an element of BaseType and returns the internal representation
/// of that element in the field.
fn from_base_type(x: Self::BaseType) -> Self::BaseType;

// Returns the representative of the value stored
fn representative(a: Self::BaseType) -> Self::BaseType;
}
53 changes: 52 additions & 1 deletion math/src/polynomial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::ops;
/// as a vector of coefficients `[c_0, c_1, ... , c_n]`
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Polynomial<FE> {
coefficients: Vec<FE>,
pub coefficients: Vec<FE>,
}

impl<F: IsField> Polynomial<FieldElement<F>> {
Expand Down Expand Up @@ -160,6 +160,32 @@ impl<F: IsField> Polynomial<FieldElement<F>> {
}
}

// TODO: This is not an optimal implementation, it should use FFT to interpolate.
pub fn compose<F>(
poly_1: &Polynomial<FieldElement<F>>,
poly_2: &Polynomial<FieldElement<F>>,
) -> Polynomial<FieldElement<F>>
where
F: IsField,
{
let max_degree: u64 = (poly_1.degree() * poly_2.degree()) as u64;

let mut interpolation_points = vec![];
for i in 0_u64..max_degree + 1 {
interpolation_points.push(FieldElement::<F>::from(i));
}

let values: Vec<_> = interpolation_points
.iter()
.map(|value| {
let intermediate_value = poly_2.evaluate(value);
poly_1.evaluate(&intermediate_value)
})
.collect();

Polynomial::interpolate(interpolation_points.as_slice(), values.as_slice())
}

impl<F: IsField> ops::Add<&Polynomial<FieldElement<F>>> for &Polynomial<FieldElement<F>> {
type Output = Polynomial<FieldElement<F>>;

Expand Down Expand Up @@ -232,6 +258,21 @@ impl<F: IsField> ops::Mul<Polynomial<FieldElement<F>>> for Polynomial<FieldEleme
}
}

impl<F: IsField> ops::Mul<FieldElement<F>> for Polynomial<FieldElement<F>> {
type Output = Polynomial<FieldElement<F>>;

fn mul(self, multiplicand: FieldElement<F>) -> Polynomial<FieldElement<F>> {
let new_coefficients = self
.coefficients
.iter()
.map(|value| value * &multiplicand)
.collect();
Polynomial {
coefficients: new_coefficients,
}
}
}

#[cfg(test)]
mod tests {
use crate::field::fields::u64_prime_field::U64PrimeField;
Expand Down Expand Up @@ -494,4 +535,14 @@ mod tests {
let p = Polynomial::interpolate(&[FE::new(0)], &[FE::new(0)]);
assert_eq!(FE::new(0), p.evaluate(&FE::new(0)));
}

#[test]
fn composition_works() {
let p = Polynomial::new(&[FE::new(0), FE::new(2)]);
let q = Polynomial::new(&[FE::new(0), FE::new(0), FE::new(1)]);
assert_eq!(
compose(&p, &q),
Polynomial::new(&[FE::new(0), FE::new(0), FE::new(2)])
);
}
}
1 change: 1 addition & 0 deletions math/src/unsigned_integer/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::fmt::Debug;

pub type U384 = UnsignedInteger<6>;
pub type U256 = UnsignedInteger<4>;
pub type U128 = UnsignedInteger<2>;

/// A big unsigned integer in base 2^{64} represented
/// as fixed-size array `limbs` of `u64` components.
Expand Down
12 changes: 12 additions & 0 deletions proving-system/stark/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "lambdaworks-stark"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rand = "0.8.5"
lambdaworks-math = { path = "../../math" }
lambdaworks-crypto = { path = "../../crypto"}
thiserror = "1.0.38"
10 changes: 10 additions & 0 deletions proving-system/stark/src/fri/fri_commitment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pub use super::{FriMerkleTree, Polynomial, F, FE};

pub struct FriCommitment<FE> {
pub poly: Polynomial<FE>,
pub domain: Vec<FE>,
pub evaluation: Vec<FE>,
pub merkle_tree: FriMerkleTree,
}

pub type FriCommitmentVec<FE> = Vec<FriCommitment<FE>>;
94 changes: 94 additions & 0 deletions proving-system/stark/src/fri/fri_decommit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use super::FE;
use crate::{fri::fri_commitment::FriCommitmentVec, PrimeField};
pub use lambdaworks_crypto::fiat_shamir::transcript::Transcript;
use lambdaworks_crypto::merkle_tree::DefaultHasher;

use lambdaworks_crypto::merkle_tree::proof::Proof;

#[derive(Debug, Clone)]
pub struct FriDecommitment {
pub layer_merkle_paths: Vec<(
Proof<PrimeField, DefaultHasher>,
Proof<PrimeField, DefaultHasher>,
)>,
pub last_layer_evaluation: FE,
}

// verifier chooses a randomness and get the index where
// they want to evaluate the poly
// TODO: encapsulate the return type of this function in a struct.
// This returns a list of authentication paths for evaluations on points and their symmetric counterparts.
pub fn fri_decommit_layers(
commit: &FriCommitmentVec<FE>,
index_to_verify: usize,
) -> FriDecommitment {
let mut index = index_to_verify;

let mut layer_merkle_paths = vec![];

// with every element of the commit, we look for that one in
// the merkle tree and get the corresponding element
for commit_i in commit {
let length_i = commit_i.domain.len();
index %= length_i;
let evaluation_i = commit_i.evaluation[index].clone();
let auth_path = commit_i.merkle_tree.get_proof(&evaluation_i).unwrap();

// symmetrical element
let index_sym = (index + length_i / 2) % length_i;
let evaluation_i_sym = commit_i.evaluation[index_sym].clone();
let auth_path_sym = commit_i.merkle_tree.get_proof(&evaluation_i_sym).unwrap();

layer_merkle_paths.push((auth_path, auth_path_sym));
}

// send the last element of the polynomial
let last = commit.last().unwrap();
let last_evaluation = last.poly.coefficients[0].clone();

FriDecommitment {
layer_merkle_paths,
last_layer_evaluation: last_evaluation,
}
}

// Integration test:
// * get an arbitrary polynomial
// * have a domain containing roots of the unity (# is power of two)
// p = 65_537
// * apply FRI commitment
// * apply FRI decommitment
// assert:
// * evaluations of the polynomials coincide with calculations from the decommitment
// * show a fail example: with a monomial

#[cfg(test)]
mod tests {
use crate::fri::U64PrimeField;
use lambdaworks_math::field::element::FieldElement;
use std::collections::HashSet;
const PRIME_GENERATOR: (u64, u64) = (0xFFFF_FFFF_0000_0001_u64, 2717_u64);
pub type F = U64PrimeField<{ PRIME_GENERATOR.0 }>;
pub type FeGoldilocks = FieldElement<F>;

#[test]
fn test() {
let subgroup_size = 1024_u64;
let generator_field = FeGoldilocks::new(PRIME_GENERATOR.1);
let exp = (PRIME_GENERATOR.0 - 1) / subgroup_size;
let generator_of_subgroup = generator_field.pow(exp);
let mut numbers = HashSet::new();

let mut i = 0;
for exp in 0..1024_u64 {
i += 1;
let ret = generator_of_subgroup.pow(exp);
numbers.insert(*ret.value());
println!("{ret:?}");
}

let count = numbers.len();
println!("count: {count}");
println!("iter: {i}");
}
}
Loading

0 comments on commit 3c681a3

Please sign in to comment.