Skip to content

Commit 3c681a3

Browse files
pablodeymoentropidelicjrchatrucMauroToscano
authored
Fibonacci Stark Prover (#59)
* 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 <[email protected]> Co-authored-by: Mariano Nicolini <[email protected]> Co-authored-by: Javier Chatruc <[email protected]> Co-authored-by: MauroFab <[email protected]>
1 parent 5ebbd30 commit 3c681a3

File tree

20 files changed

+819
-8
lines changed

20 files changed

+819
-8
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
members = [
44
"math",
55
"crypto",
6+
"proving-system/stark",
67
]

crypto/src/fiat_shamir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
mod transcript;
1+
pub mod transcript;

crypto/src/fiat_shamir/transcript.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
use sha3::{Digest, Sha3_256};
22

3-
struct Transcript {
3+
pub struct Transcript {
44
hasher: Sha3_256,
55
}
66

77
impl Transcript {
8-
#[allow(unused)]
9-
fn new() -> Self {
8+
#[allow(clippy::new_without_default)]
9+
pub fn new() -> Self {
1010
Self {
1111
hasher: Sha3_256::new(),
1212
}
1313
}
1414

1515
#[allow(unused)]
16-
fn append(&mut self, new_data: &[u8]) {
16+
pub fn append(&mut self, new_data: &[u8]) {
1717
self.hasher.update(&mut new_data.to_owned());
1818
}
1919

2020
#[allow(unused)]
21-
fn challenge(&mut self) -> [u8; 32] {
21+
pub fn challenge(&mut self) -> [u8; 32] {
2222
let mut result_hash = [0_u8; 32];
2323
result_hash.copy_from_slice(&self.hasher.finalize_reset());
2424
self.hasher.update(result_hash);

crypto/src/merkle_tree/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::hash::traits::IsCryptoHash;
88
use self::{merkle::MerkleTree, proof::Proof};
99

1010
pub mod merkle;
11-
mod proof;
11+
pub mod proof;
1212
mod utils;
1313

1414
pub type U64F = U64PrimeField<0xFFFF_FFFF_0000_0001_u64>;

crypto/src/merkle_tree/proof.rs

+17
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,29 @@ use lambdaworks_math::{
55
traits::ByteConversion,
66
};
77

8+
#[derive(Debug, Clone)]
89
pub struct Proof<F: IsField, H: IsCryptoHash<F>> {
910
pub value: FieldElement<F>,
1011
pub merkle_path: Vec<(FieldElement<F>, bool)>,
1112
pub hasher: H,
1213
}
1314

15+
impl<F: IsField, H: IsCryptoHash<F>> Proof<F, H> {
16+
pub fn verify(&self, root_hash: FieldElement<F>) -> bool {
17+
let mut hashed_value = self.hasher.hash_one(self.value.clone());
18+
19+
for (sibling_node, is_left) in self.merkle_path.iter().rev() {
20+
if *is_left {
21+
hashed_value = self.hasher.hash_two(hashed_value, sibling_node.clone());
22+
} else {
23+
hashed_value = self.hasher.hash_two(sibling_node.clone(), hashed_value);
24+
}
25+
}
26+
27+
root_hash == hashed_value
28+
}
29+
}
30+
1431
impl<F, H> ByteConversion for Proof<F, H>
1532
where
1633
F: IsField,

math/src/field/element.rs

+5
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,11 @@ where
300300
&self.value
301301
}
302302

303+
// Returns the representative of the value stored
304+
pub fn representative(&self) -> F::BaseType {
305+
F::representative(self.value.clone())
306+
}
307+
303308
/// Returns the multiplicative inverse of `self`
304309
pub fn inv(&self) -> Self {
305310
Self {

math/src/field/extensions/cubic.rs

+4
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ where
127127
fn from_base_type(x: [FieldElement<Q::BaseField>; 3]) -> [FieldElement<Q::BaseField>; 3] {
128128
x
129129
}
130+
131+
fn representative(_x: Self::BaseType) -> Self::BaseType {
132+
todo!()
133+
}
130134
}
131135

132136
#[cfg(test)]

math/src/field/extensions/quadratic.rs

+4
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ where
105105
fn from_base_type(x: [FieldElement<Q::BaseField>; 2]) -> [FieldElement<Q::BaseField>; 2] {
106106
x
107107
}
108+
109+
fn representative(_x: Self::BaseType) -> Self::BaseType {
110+
todo!()
111+
}
108112
}
109113

110114
#[cfg(test)]

math/src/field/fields/u384_prime_field.rs

+5
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ where
151151
fn from_base_type(x: Self::BaseType) -> Self::BaseType {
152152
MontgomeryAlgorithms::cios(&x, &C::R2, &C::MODULUS, &C::MU)
153153
}
154+
155+
// TO DO: Add tests for representatives
156+
fn representative(x: Self::BaseType) -> Self::BaseType {
157+
MontgomeryAlgorithms::cios(&x, &U384::from_u64(1), &C::MODULUS, &C::MU)
158+
}
154159
}
155160

156161
impl<C> ByteConversion for FieldElement<MontgomeryBackendPrimeField<C>>

math/src/field/fields/u64_prime_field.rs

+4
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ impl<const MODULUS: u64> IsField for U64PrimeField<MODULUS> {
5656
fn from_base_type(x: u64) -> u64 {
5757
Self::from_u64(x)
5858
}
59+
60+
fn representative(x: u64) -> u64 {
61+
x
62+
}
5963
}
6064

6165
impl<const MODULUS: u64> Copy for U64FieldElement<MODULUS> {}

math/src/field/test_fields/u64_test_field.rs

+4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ impl<const MODULUS: u64> IsField for U64TestField<MODULUS> {
5050
fn from_base_type(x: u64) -> u64 {
5151
Self::from_u64(x)
5252
}
53+
54+
fn representative(x: u64) -> u64 {
55+
x
56+
}
5357
}
5458

5559
impl<const MODULUS: u64> IsTwoAdicField for U64TestField<MODULUS> {

math/src/field/traits.rs

+3
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,7 @@ pub trait IsField: Debug + Clone {
9393
/// Takes as input an element of BaseType and returns the internal representation
9494
/// of that element in the field.
9595
fn from_base_type(x: Self::BaseType) -> Self::BaseType;
96+
97+
// Returns the representative of the value stored
98+
fn representative(a: Self::BaseType) -> Self::BaseType;
9699
}

math/src/polynomial.rs

+52-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::ops;
66
/// as a vector of coefficients `[c_0, c_1, ... , c_n]`
77
#[derive(Debug, Clone, PartialEq, Eq)]
88
pub struct Polynomial<FE> {
9-
coefficients: Vec<FE>,
9+
pub coefficients: Vec<FE>,
1010
}
1111

1212
impl<F: IsField> Polynomial<FieldElement<F>> {
@@ -160,6 +160,32 @@ impl<F: IsField> Polynomial<FieldElement<F>> {
160160
}
161161
}
162162

163+
// TODO: This is not an optimal implementation, it should use FFT to interpolate.
164+
pub fn compose<F>(
165+
poly_1: &Polynomial<FieldElement<F>>,
166+
poly_2: &Polynomial<FieldElement<F>>,
167+
) -> Polynomial<FieldElement<F>>
168+
where
169+
F: IsField,
170+
{
171+
let max_degree: u64 = (poly_1.degree() * poly_2.degree()) as u64;
172+
173+
let mut interpolation_points = vec![];
174+
for i in 0_u64..max_degree + 1 {
175+
interpolation_points.push(FieldElement::<F>::from(i));
176+
}
177+
178+
let values: Vec<_> = interpolation_points
179+
.iter()
180+
.map(|value| {
181+
let intermediate_value = poly_2.evaluate(value);
182+
poly_1.evaluate(&intermediate_value)
183+
})
184+
.collect();
185+
186+
Polynomial::interpolate(interpolation_points.as_slice(), values.as_slice())
187+
}
188+
163189
impl<F: IsField> ops::Add<&Polynomial<FieldElement<F>>> for &Polynomial<FieldElement<F>> {
164190
type Output = Polynomial<FieldElement<F>>;
165191

@@ -232,6 +258,21 @@ impl<F: IsField> ops::Mul<Polynomial<FieldElement<F>>> for Polynomial<FieldEleme
232258
}
233259
}
234260

261+
impl<F: IsField> ops::Mul<FieldElement<F>> for Polynomial<FieldElement<F>> {
262+
type Output = Polynomial<FieldElement<F>>;
263+
264+
fn mul(self, multiplicand: FieldElement<F>) -> Polynomial<FieldElement<F>> {
265+
let new_coefficients = self
266+
.coefficients
267+
.iter()
268+
.map(|value| value * &multiplicand)
269+
.collect();
270+
Polynomial {
271+
coefficients: new_coefficients,
272+
}
273+
}
274+
}
275+
235276
#[cfg(test)]
236277
mod tests {
237278
use crate::field::fields::u64_prime_field::U64PrimeField;
@@ -494,4 +535,14 @@ mod tests {
494535
let p = Polynomial::interpolate(&[FE::new(0)], &[FE::new(0)]);
495536
assert_eq!(FE::new(0), p.evaluate(&FE::new(0)));
496537
}
538+
539+
#[test]
540+
fn composition_works() {
541+
let p = Polynomial::new(&[FE::new(0), FE::new(2)]);
542+
let q = Polynomial::new(&[FE::new(0), FE::new(0), FE::new(1)]);
543+
assert_eq!(
544+
compose(&p, &q),
545+
Polynomial::new(&[FE::new(0), FE::new(0), FE::new(2)])
546+
);
547+
}
497548
}

math/src/unsigned_integer/element.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::fmt::Debug;
99

1010
pub type U384 = UnsignedInteger<6>;
1111
pub type U256 = UnsignedInteger<4>;
12+
pub type U128 = UnsignedInteger<2>;
1213

1314
/// A big unsigned integer in base 2^{64} represented
1415
/// as fixed-size array `limbs` of `u64` components.

proving-system/stark/Cargo.toml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "lambdaworks-stark"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
rand = "0.8.5"
10+
lambdaworks-math = { path = "../../math" }
11+
lambdaworks-crypto = { path = "../../crypto"}
12+
thiserror = "1.0.38"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
pub use super::{FriMerkleTree, Polynomial, F, FE};
2+
3+
pub struct FriCommitment<FE> {
4+
pub poly: Polynomial<FE>,
5+
pub domain: Vec<FE>,
6+
pub evaluation: Vec<FE>,
7+
pub merkle_tree: FriMerkleTree,
8+
}
9+
10+
pub type FriCommitmentVec<FE> = Vec<FriCommitment<FE>>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use super::FE;
2+
use crate::{fri::fri_commitment::FriCommitmentVec, PrimeField};
3+
pub use lambdaworks_crypto::fiat_shamir::transcript::Transcript;
4+
use lambdaworks_crypto::merkle_tree::DefaultHasher;
5+
6+
use lambdaworks_crypto::merkle_tree::proof::Proof;
7+
8+
#[derive(Debug, Clone)]
9+
pub struct FriDecommitment {
10+
pub layer_merkle_paths: Vec<(
11+
Proof<PrimeField, DefaultHasher>,
12+
Proof<PrimeField, DefaultHasher>,
13+
)>,
14+
pub last_layer_evaluation: FE,
15+
}
16+
17+
// verifier chooses a randomness and get the index where
18+
// they want to evaluate the poly
19+
// TODO: encapsulate the return type of this function in a struct.
20+
// This returns a list of authentication paths for evaluations on points and their symmetric counterparts.
21+
pub fn fri_decommit_layers(
22+
commit: &FriCommitmentVec<FE>,
23+
index_to_verify: usize,
24+
) -> FriDecommitment {
25+
let mut index = index_to_verify;
26+
27+
let mut layer_merkle_paths = vec![];
28+
29+
// with every element of the commit, we look for that one in
30+
// the merkle tree and get the corresponding element
31+
for commit_i in commit {
32+
let length_i = commit_i.domain.len();
33+
index %= length_i;
34+
let evaluation_i = commit_i.evaluation[index].clone();
35+
let auth_path = commit_i.merkle_tree.get_proof(&evaluation_i).unwrap();
36+
37+
// symmetrical element
38+
let index_sym = (index + length_i / 2) % length_i;
39+
let evaluation_i_sym = commit_i.evaluation[index_sym].clone();
40+
let auth_path_sym = commit_i.merkle_tree.get_proof(&evaluation_i_sym).unwrap();
41+
42+
layer_merkle_paths.push((auth_path, auth_path_sym));
43+
}
44+
45+
// send the last element of the polynomial
46+
let last = commit.last().unwrap();
47+
let last_evaluation = last.poly.coefficients[0].clone();
48+
49+
FriDecommitment {
50+
layer_merkle_paths,
51+
last_layer_evaluation: last_evaluation,
52+
}
53+
}
54+
55+
// Integration test:
56+
// * get an arbitrary polynomial
57+
// * have a domain containing roots of the unity (# is power of two)
58+
// p = 65_537
59+
// * apply FRI commitment
60+
// * apply FRI decommitment
61+
// assert:
62+
// * evaluations of the polynomials coincide with calculations from the decommitment
63+
// * show a fail example: with a monomial
64+
65+
#[cfg(test)]
66+
mod tests {
67+
use crate::fri::U64PrimeField;
68+
use lambdaworks_math::field::element::FieldElement;
69+
use std::collections::HashSet;
70+
const PRIME_GENERATOR: (u64, u64) = (0xFFFF_FFFF_0000_0001_u64, 2717_u64);
71+
pub type F = U64PrimeField<{ PRIME_GENERATOR.0 }>;
72+
pub type FeGoldilocks = FieldElement<F>;
73+
74+
#[test]
75+
fn test() {
76+
let subgroup_size = 1024_u64;
77+
let generator_field = FeGoldilocks::new(PRIME_GENERATOR.1);
78+
let exp = (PRIME_GENERATOR.0 - 1) / subgroup_size;
79+
let generator_of_subgroup = generator_field.pow(exp);
80+
let mut numbers = HashSet::new();
81+
82+
let mut i = 0;
83+
for exp in 0..1024_u64 {
84+
i += 1;
85+
let ret = generator_of_subgroup.pow(exp);
86+
numbers.insert(*ret.value());
87+
println!("{ret:?}");
88+
}
89+
90+
let count = numbers.len();
91+
println!("count: {count}");
92+
println!("iter: {i}");
93+
}
94+
}

0 commit comments

Comments
 (0)