Skip to content

Commit

Permalink
Stark: Remove degree adjustment from composition poly (#563)
Browse files Browse the repository at this point in the history
* remove degree adjustment

* remove unnecessary challenges

* rename coefficients variables

* remove degree adjustment from docs

* remove whitespaces

---------

Co-authored-by: Mauro Toscano <[email protected]>
  • Loading branch information
schouhy and MauroToscano authored Sep 21, 2023
1 parent 1da50bc commit 21cc017
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 137 deletions.
5 changes: 2 additions & 3 deletions docs/src/starks/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ Both prover and verifier compute the following.

- The interpolation domain: the vector $D_S=(1, g, \dots, g^{2^n-1})$.
- The Low Degree Extension $D_{\text{LDE}} =(h, h\omega, h\omega^2,\dots, h\omega^{2^{n+l} - 1})$. Recall $2^l$ is the blowup factor.
- Let $d_k^T := 2^n (\deg(P_k^T) - 1)$ and let $d^B := 2^n$. Let $d := 2^{n + 1}$. Notice that $d^B \leq d$ and $d_k^T \leq d$ for all $k$. This holds because we assume all transition constraint polynomials are at most cubic.

### Notation of important operations
#### Vector commitment scheme
Expand Down Expand Up @@ -98,7 +97,7 @@ In our cases the sets $A$ will be of the form $A=(f(a), f(ab), f(ab^2), \dots, f
- Compute $B_j := \frac{t_j - P^B_j}{Z_j^B}$.
- Compute $C_k := \frac{P^T_k(t_1, \dots, t_m, t_1(gX), \dots, t_m(gX))}{Z_k^T}$.
- Compute the _composition polynomial_
$$H := \sum_{k} (\alpha_k^T X^{d - d_k^T} + \beta_k^T)C_k + \sum_j (\alpha_j^BX^{d - d^B}+\beta_j^B)B_j$$
$$H := \sum_{k} \beta_k^TC_k + \sum_j \beta_j^BB_j$$
- Decompose $H$ as
$$H = H_1(X^2) + XH_2(X^2)$$
- Compute commitments $[H_1]$ and $[H_2]$.
Expand Down Expand Up @@ -227,7 +226,7 @@ Check that $\text{Keccak256}(x || y)$ has $c$ leading zeroes.
- Compute $b_j := \frac{\tau_j^z - P^B_j(z)}{Z_j^B(z)}$
- Compute $c_k := \frac{P^T_k(\tau_1^z, \dots, \tau_m^z, \tau_1^{gz}, \dots, \tau_m^{gz})}{Z_k^T(z)}$
- Verify
$$h = \sum_{k} (\alpha_k^T z^{d - d_k^T} + \beta_k^T)c_k + \sum_j (\alpha_j^B z^{d - d^B}+\beta_j^B)b_j$$
$$h = \sum_{k} \beta_k^Tc_k + \sum_j \beta_j^Bb_j$$

#### Step 3: Verify FRI

Expand Down
11 changes: 5 additions & 6 deletions docs/src/starks/recap.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,14 @@ $$
How does \\(C\\) encode the transition constraints? We mentioned above that these are satisfied if the polynomial in the numerator vanishes in the elements \\(\{g^0, g^1, g^2, g^3, g^4, g^5\}\\). As with \\(B\\), this is the same as showing that \\(C(x)\\) is a polynomial instead of a rational function.

### Constructing \\(H\\)
With the boundary and transition constraint polynomials in hand, we build the `composition polynomial` \\(H\\) as follows: The verifier will sample four numbers \\(\alpha_1, \alpha_2, \beta_1, \beta_2\\) and \\(H\\) will be
With the boundary and transition constraint polynomials in hand, we build the `composition polynomial` \\(H\\) as follows: The verifier will sample four numbers \\(\beta_1, \beta_2\\) and \\(H\\) will be

$$
H(x) = B(x) (\alpha_1 x^{D - deg(B)} + \beta_1) + C(x) (\alpha_2 x^{D - deg(C)} + \beta_2)
H(x) = \beta_1 B(x) + \beta_2 C(x)
$$

where \\(D\\) is the smallest power of two greater than the degrees of both \\(B\\) and \\(C\\), so for example if \\(deg(B) = 3\\) and \\(deg(C) = 6\\), then \\(D = 8\\).

Why not just take \\(H(x) = B(x) + C(x)\\)? The reason for the alphas and betas is to make the resulting \\(H\\) be always different and unpredictable for the prover, so they can't precompute stuff beforehand. The \\(x^{D - deg(...)}\\) term is there to adjust the degree of the constraints. This ensures the soundness of the protocol according to the [ethSTARK documentation](https://eprint.iacr.org/2021/582.pdf).
Why not just take \\(H(x) = B(x) + C(x)\\)? The reason for the betas is to make the resulting \\(H\\) be always different and unpredictable for the prover, so they can't precompute stuff beforehand.

With what we discussed above, showing that the constraints are satisfied is equivalent to saying that `H` is a polynomial and not a rational function (we are simplifying things a bit here, but it works for our purposes).

Expand All @@ -167,7 +166,7 @@ After commiting to `H`, the prover needs to show that `H` was constructed correc
Because the boundary and transition constraints are a public part of the protocol, the verifier knows them, and thus the only thing it needs to compute the evaluation \\((z)\\) by itself are the three trace evaluations mentioned above. Because it asked the prover for them, it can check both sides of the equation:

$$
H(z) = B(z) (\alpha_1 z^{D - deg(B)} + \beta_1) + C(z) (\alpha_2 z^{D - deg(C)} + \beta_2)
H(z) = \beta_1 B(z) + \beta_2 C(z)
$$

and be convinced that \\(H\\) was constructed correctly.
Expand Down Expand Up @@ -231,7 +230,7 @@ We summarize below the steps required in a STARK proof for both prover and verif
- Take the evaluations \\(H(z)\\), \\(H(x_0)\\), \\(t(z)\\), \\(t(zg)\\), \\(t(zg^2)\\) and \\(t(x_0)\\) the prover provided.
- Reconstruct the evaluations \\(B(z)\\) and \\(C(z)\\) from the trace evaluations we were given. Check that the claimed evaluation \\(H(z)\\) the prover gave us actually satisfies
$$
H(z) = B(z) (\alpha_1 z^{D - deg(B)} + \beta_1) + C(z) (\alpha_2 z^{D - deg(C)} + \beta_2)
H(z) = \beta_1 B(z) + \beta_2 C(z)
$$
- Check that the claimed evaluation \\(Deep(x_0)\\) the prover gave us actually satisfies
$$
Expand Down
6 changes: 3 additions & 3 deletions docs/src/starks/under_the_hood.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ We will once again use the fibonacci example as an ilustration. Recall from the
- Take the evaluation $H(z)$ along with the trace evaluations the prover provided.
- Reconstruct the evaluations $B(z)$ and $C(z)$ from the trace evaluations. Check that the claimed evaluation $H(z)$ the prover gave us actually satisfies
$$
H(z) = B(z) (\alpha_1 z^{D - deg(B)} + \beta_1) + C(z) (\alpha_2 z^{D - deg(C)} + \beta_2)
H(z) = \beta_1 B(z) + \beta_2 C(z)
$$
- Take the evaluations $H(x_0)$ and $t(x_0)$.
- Check that the claimed evaluation $Deep(x_0)$ the prover gave us actually satisfies
Expand Down Expand Up @@ -94,13 +94,13 @@ let constraint_evaluations = evaluator.evaluate(
This function call will return the evaluations of the boundary terms

$$
B_i(x) (\alpha_i x^{D - deg(B)} + \beta_i)
\beta_i^B B_i(x)
$$

and constraint terms

$$
C_i(x) (\alpha_i x^{D - deg(C)} + \beta_i)
\beta_i^T C_i(x)
$$

for every $i$. The `constraint_evaluations` value returned is a `ConstraintEvaluationTable` struct, which is nothing more than a big list of evaluations of each polynomial required to construct `H`.
Expand Down
59 changes: 12 additions & 47 deletions provers/stark/src/constraints/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ impl<F: IsFFTField, A: AIR + AIR<Field = F>> ConstraintEvaluator<F, A> {
&self,
lde_trace: &TraceTable<F>,
domain: &Domain<F>,
alpha_and_beta_transition_coefficients: &[(FieldElement<F>, FieldElement<F>)],
alpha_and_beta_boundary_coefficients: &[(FieldElement<F>, FieldElement<F>)],
transition_coefficients: &[FieldElement<F>],
boundary_coefficients: &[FieldElement<F>],
rap_challenges: &A::RAPChallenges,
) -> ConstraintEvaluationTable<F>
where
Expand Down Expand Up @@ -72,14 +72,6 @@ impl<F: IsFFTField, A: AIR + AIR<Field = F>> ConstraintEvaluator<F, A> {
.collect::<Vec<Vec<FieldElement<F>>>>();

let trace_length = self.air.trace_length();
let composition_poly_degree_bound = self.air.composition_poly_degree_bound();
let boundary_term_degree_adjustment = composition_poly_degree_bound - trace_length;
// Maybe we can do this more efficiently by taking the offset's power and then using successors for roots of unity
let d_adjustment_power = domain
.lde_roots_of_unity_coset
.iter()
.map(|d| d.pow(boundary_term_degree_adjustment))
.collect::<Vec<FieldElement<F>>>();

#[cfg(all(debug_assertions, not(feature = "parallel")))]
let boundary_polys: Vec<Polynomial<FieldElement<F>>> = Vec::new();
Expand Down Expand Up @@ -108,13 +100,12 @@ impl<F: IsFFTField, A: AIR + AIR<Field = F>> ConstraintEvaluator<F, A> {
let boundary_eval_iter = 0..domain.lde_roots_of_unity_coset.len();

let boundary_evaluation = boundary_eval_iter
.zip(&d_adjustment_power)
.map(|(i, d)| {
.map(|i| {
(0..number_of_b_constraints)
.zip(alpha_and_beta_boundary_coefficients)
.fold(FieldElement::zero(), |acc, (index, (alpha, beta))| {
.zip(boundary_coefficients)
.fold(FieldElement::zero(), |acc, (index, beta)| {
acc + &boundary_zerofiers_inverse_evaluations[index][i]
* (alpha * d + beta)
* beta
* &boundary_polys_evaluations[index][i]
})
})
Expand All @@ -136,28 +127,6 @@ impl<F: IsFFTField, A: AIR + AIR<Field = F>> ConstraintEvaluator<F, A> {
let transition_exemptions_evaluations =
evaluate_transition_exemptions(transition_exemptions, domain);
let num_exemptions = self.air.context().num_transition_exemptions;
let context = self.air.context();
let max_transition_degree = *context.transition_degrees.iter().max().unwrap();

#[cfg(feature = "parallel")]
let degree_adjustments_iter = (1..=max_transition_degree).into_par_iter();

#[cfg(not(feature = "parallel"))]
let degree_adjustments_iter = 1..=max_transition_degree;

let degree_adjustments: Vec<Vec<FieldElement<F>>> = degree_adjustments_iter
.map(|transition_degree| {
domain
.lde_roots_of_unity_coset
.iter()
.map(|d| {
let degree_adjustment = composition_poly_degree_bound
- (trace_length * (transition_degree - 1));
d.pow(degree_adjustment)
})
.collect()
})
.collect();

let blowup_factor_order = u64::from(blowup_factor.trailing_zeros());

Expand Down Expand Up @@ -212,22 +181,20 @@ impl<F: IsFFTField, A: AIR + AIR<Field = F>> ConstraintEvaluator<F, A> {
.iter()
.zip(&self.air.context().transition_exemptions)
.zip(&self.air.context().transition_degrees)
.zip(alpha_and_beta_transition_coefficients)
.zip(transition_coefficients)
.fold(
FieldElement::zero(),
|acc, (((eval, exemption), degree), (alpha, beta))| {
|acc, (((eval, exemption), _), beta)| {
#[cfg(feature = "parallel")]
let zerofier = zerofier.clone();

if *exemption == 0 {
acc + zerofier
* (alpha * &degree_adjustments[degree - 1][i] + beta)
* eval
acc + zerofier * beta * eval
} else {
//TODO: change how exemptions are indexed!
if num_exemptions == 1 {
acc + zerofier
* (alpha * &degree_adjustments[degree - 1][i] + beta)
* beta
* eval
* &transition_exemptions_evaluations[0][i]
} else {
Expand All @@ -247,7 +214,7 @@ impl<F: IsFFTField, A: AIR + AIR<Field = F>> ConstraintEvaluator<F, A> {
.expect("is there");

acc + zerofier
* (alpha * &degree_adjustments[degree - 1][i] + beta)
* beta
* eval
* &transition_exemptions_evaluations[index][i]
}
Expand Down Expand Up @@ -295,9 +262,7 @@ impl<F: IsFFTField, A: AIR + AIR<Field = F>> ConstraintEvaluator<F, A> {
.zip(constraint_coeffs)
.fold(
FieldElement::<F>::zero(),
|acc, (((ev, degree), inv), (alpha, beta))| {
acc + ev * (alpha * degree + beta) * inv
},
|acc, (((ev, _), inv), (_, beta))| acc + ev * beta * inv,
)
}
}
Expand Down
35 changes: 8 additions & 27 deletions provers/stark/src/prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ fn round_2_compute_composition_polynomial<F, A>(
air: &A,
domain: &Domain<F>,
round_1_result: &Round1<F, A>,
transition_coeffs: &[(FieldElement<F>, FieldElement<F>)],
boundary_coeffs: &[(FieldElement<F>, FieldElement<F>)],
transition_coefficients: &[FieldElement<F>],
boundary_coefficients: &[FieldElement<F>],
) -> Round2<F>
where
F: IsFFTField,
Expand All @@ -241,8 +241,8 @@ where
let constraint_evaluations = evaluator.evaluate(
&round_1_result.lde_trace,
domain,
transition_coeffs,
boundary_coeffs,
transition_coefficients,
boundary_coefficients,
&round_1_result.rap_challenges,
);

Expand Down Expand Up @@ -641,42 +641,23 @@ where
#[cfg(feature = "instruments")]
let timer2 = Instant::now();

// <<<< Receive challenges: 𝛼_j^B
let boundary_coeffs_alphas = batch_sample_challenges(
air.boundary_constraints(&round_1_result.rap_challenges)
.constraints
.len(),
&mut transcript,
);
// <<<< Receive challenges: 𝛽_j^B
let boundary_coeffs_betas = batch_sample_challenges(
let boundary_coefficients = batch_sample_challenges(
air.boundary_constraints(&round_1_result.rap_challenges)
.constraints
.len(),
&mut transcript,
);
// <<<< Receive challenges: 𝛼_j^T
let transition_coeffs_alphas =
batch_sample_challenges(air.context().num_transition_constraints, &mut transcript);
// <<<< Receive challenges: 𝛽_j^T
let transition_coeffs_betas =
let transition_coefficients =
batch_sample_challenges(air.context().num_transition_constraints, &mut transcript);

let boundary_coeffs: Vec<_> = boundary_coeffs_alphas
.into_iter()
.zip(boundary_coeffs_betas)
.collect();
let transition_coeffs: Vec<_> = transition_coeffs_alphas
.into_iter()
.zip(transition_coeffs_betas)
.collect();

let round_2_result = round_2_compute_composition_polynomial(
&air,
&domain,
&round_1_result,
&transition_coeffs,
&boundary_coeffs,
&transition_coefficients,
&boundary_coefficients,
);

// >>>> Send commitments: [H₁], [H₂]
Expand Down
63 changes: 12 additions & 51 deletions provers/stark/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ where
A: AIR<Field = F>,
{
z: FieldElement<F>,
boundary_coeffs: Vec<(FieldElement<F>, FieldElement<F>)>,
transition_coeffs: Vec<(FieldElement<F>, FieldElement<F>)>,
boundary_coeffs: Vec<FieldElement<F>>,
transition_coeffs: Vec<FieldElement<F>>,
trace_term_coeffs: Vec<Vec<FieldElement<F>>>,
gamma_even: FieldElement<F>,
gamma_odd: FieldElement<F>,
Expand Down Expand Up @@ -88,31 +88,14 @@ where
// ===================================

// These are the challenges alpha^B_j and beta^B_j
// >>>> Send challenges: 𝛼_j^B
let boundary_coeffs_alphas = batch_sample_challenges(
air.boundary_constraints(&rap_challenges).constraints.len(),
transcript,
);
// >>>> Send challenges: 𝛽_j^B
let boundary_coeffs_betas = batch_sample_challenges(
let boundary_coeffs = batch_sample_challenges(
air.boundary_constraints(&rap_challenges).constraints.len(),
transcript,
);
// >>>> Send challenges: 𝛼_j^T
let transition_coeffs_alphas =
batch_sample_challenges(air.context().num_transition_constraints, transcript);
// >>>> Send challenges: 𝛽_j^T
let transition_coeffs_betas =
let transition_coeffs =
batch_sample_challenges(air.context().num_transition_constraints, transcript);
let boundary_coeffs: Vec<_> = boundary_coeffs_alphas
.into_iter()
.zip(boundary_coeffs_betas)
.collect();

let transition_coeffs: Vec<_> = transition_coeffs_alphas
.into_iter()
.zip(transition_coeffs_betas)
.collect();

// <<<< Receive commitments: [H₁], [H₂]
transcript.append(&proof.composition_poly_root);
Expand Down Expand Up @@ -221,8 +204,6 @@ fn step_2_verify_claimed_composition_polynomial<F: IsFFTField, A: AIR<Field = F>
//let n_trace_cols = air.context().trace_columns;
// special cases.
let trace_length = air.trace_length();
let composition_poly_degree_bound = air.composition_poly_degree_bound();
let boundary_term_degree_adjustment = composition_poly_degree_bound - trace_length;
let number_of_b_constraints = boundary_constraints.constraints.len();

// Following naming conventions from https://www.notamonadtutorial.com/diving-deep-fri/
Expand Down Expand Up @@ -251,12 +232,11 @@ fn step_2_verify_claimed_composition_polynomial<F: IsFFTField, A: AIR<Field = F>

FieldElement::inplace_batch_inverse(&mut boundary_c_i_evaluations_den).unwrap();

let boundary_degree_z = challenges.z.pow(boundary_term_degree_adjustment);
let boundary_quotient_ood_evaluation: FieldElement<F> = boundary_c_i_evaluations_num
.iter()
.zip(&boundary_c_i_evaluations_den)
.zip(&challenges.boundary_coeffs)
.map(|((num, den), (alpha, beta))| num * den * (alpha * &boundary_degree_z + beta))
.map(|((num, den), beta)| num * den * beta)
.fold(FieldElement::<F>::zero(), |acc, x| acc + x);

let transition_ood_frame_evaluations = air.compute_transition(
Expand All @@ -276,38 +256,19 @@ fn step_2_verify_claimed_composition_polynomial<F: IsFFTField, A: AIR<Field = F>
.map(|poly| poly.evaluate(&challenges.z))
.collect::<Vec<FieldElement<F>>>();

let max_degree = air
.context()
.transition_degrees()
.iter()
.max()
.expect("has maximum degree");
let degree_adjustments = (1..=*max_degree)
.map(|transition_degree| {
let degree_adjustment =
composition_poly_degree_bound - (trace_length * (transition_degree - 1));
challenges.z.pow(degree_adjustment)
})
.collect::<Vec<FieldElement<F>>>();
let unity = &FieldElement::one();
let transition_c_i_evaluations_sum = transition_ood_frame_evaluations
.iter()
.zip(&air.context().transition_degrees)
.zip(&air.context().transition_exemptions)
.zip(&challenges.transition_coeffs)
.fold(
FieldElement::zero(),
|acc, (((eval, degree), except), (alpha, beta))| {
let except = except
.checked_sub(1)
.map(|i| &exemption[i])
.unwrap_or(unity);
acc + &denominator
* eval
* (alpha * &degree_adjustments[degree - 1] + beta)
* except
},
);
.fold(FieldElement::zero(), |acc, (((eval, _), except), beta)| {
let except = except
.checked_sub(1)
.map(|i| &exemption[i])
.unwrap_or(unity);
acc + &denominator * eval * beta * except
});

let composition_poly_ood_evaluation =
&boundary_quotient_ood_evaluation + transition_c_i_evaluations_sum;
Expand Down

0 comments on commit 21cc017

Please sign in to comment.