Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ authors = ["Jason Park", "Varun Thakore"]
language = "en"
multilingual = false
src = "src"
title = "stwo-book"
title = "Stwo Book"

[preprocessor.admonish]
command = "mdbook-admonish"
Expand Down
11 changes: 10 additions & 1 deletion src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
- [Main Components](cairo-air/main-components/index.md)
- [`ADD` Opcode Walkthrough](cairo-air/add-opcode/index.md)

- [How Does It Work?](how-it-works/index.md)
- [Stwo: Under the Hood](how-it-works/index.md)

- [Mersenne Primes](how-it-works/mersenne-prime.md)

Expand Down Expand Up @@ -61,6 +61,15 @@
- [FRI Prover](how-it-works/circle-fri/fri_prover.md)
- [FRI Verifier](how-it-works/circle-fri/fri_verifier.md)

- [Polynomial Commitment Scheme](how-it-works/pcs/index.md)
- [Technical Overview](how-it-works/pcs/overview.md)
- [Polynomial Commitment Scheme Prover](how-it-works/pcs/prover.md)
- [Polynomial Commitment Scheme Verifier](how-it-works/pcs/verifier.md)

- [Proof Generation and Verification](how-it-works/stark_proof/index.md)
- [STARK Prover](how-it-works/stark_proof/prove.md)
- [STARK Verifier](how-it-works/stark_proof/verify.md)

- [Lookups](how-it-works/lookups.md)

- [Awesome Stwo](awesome-stwo/index.md)
Expand Down
2 changes: 0 additions & 2 deletions src/how-it-works/circle-polynomials/columns.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ Here, `bit_reverse` performs a naive bit-reversal permutation on the `column`.

## Secure Field Columns

<!-- TODO: add figure to showing secure columns -->

An element of the secure field (`SecureField` = `QM31`) cannot be stored in a single `BaseField` column because it is a quartic extension of `M31`. Instead, each secure field element is represented by four base field coordinates and stored in four consecutive columns.

```rust,no_run,noplayground
Expand Down
2 changes: 0 additions & 2 deletions src/how-it-works/circle-polynomials/secure-evals-and-poly.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ $$p(x, y) = p_a(x, y) + i \cdot p_b(x, y) + u \cdot p_c(x, y) + iu \cdot p_d(x,

where $p_a(x,y)$ is a `CirclePoly` with coefficients $a_j \in \mathsf{M31}$, similarly for $p_b(x, y), p_c(x, y)$ and $p_d(x, y)$. This is implemented as follows:

<!-- TODO: Add a figure or example to explain this implementation. -->

```rust,no_run,noplayground
{{#webinclude https://raw.githubusercontent.com/starkware-libs/stwo/0790eba46b8af5697083d84fb75bd34b08a0b31f/crates/stwo/src/prover/poly/circle/secure_poly.rs 14:14}}
```
Expand Down
18 changes: 15 additions & 3 deletions src/how-it-works/index.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
# How Does It Work?
# Stwo: Under the Hood

<!-- TODO: update this section in the end, add commit used to describe the implementation -->
> This section is for those who want an in-depth explanation of various components of Stwo.
> This section provides an overview of the core components and protocols that make up Stwo. It is intended for readers who want to understand the technical details and inner workings of the system implementation. Throughout this section, we describe the implementation as available in this [commit](https://github.com/starkware-libs/stwo/tree/0790eba46b8af5697083d84fb75bd34b08a0b31f).

The following topics are covered:

- [**Mersenne Primes**](./mersenne-prime.md): Introduction to the Mersenne31 prime field used in Stwo for efficient arithmetic.
- [**Circle Group**](./circle-group.md): Explains the algebraic structure underlying FFT and polynomial operations.
- [**Circle Polynomials**](./circle-polynomials/index.md): Details the representation and evaluation of polynomials in the circle group.
- [**Circle FFT**](./circle-fft/index.md): Describes the fast Fourier transform algorithm adapted for the circle group.
- [**Vector Commitment Scheme (VCS)**](./vcs/index.md): Covers the use of Merkle trees for committing to vectors and enabling efficient proofs of inclusion.
- [**AIR to Composition Polynomial**](./air/index.md): Shows how algebraic constraints are encoded as polynomials for proof generation.
- [**Circle FRI**](./circle-fri/index.md): Explains the FRI protocol for low-degree testing of polynomials over the circle group.
- [**Polynomial Commitment Scheme (PCS)**](./pcs/index.md): Describes the protocol for committing to and opening polynomials with soundness guarantees.
- [**Proof Generation and Verification**](./stark_proof/index.md): Walks through the process of generating and verifying a STARK proof in Stwo.
- [**Lookups**](./lookups.md): Discusses lookup arguments and their implementation in Stwo for efficient constraint checking.
12 changes: 6 additions & 6 deletions src/how-it-works/lookups.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,28 +62,28 @@ Let's walk through how LogUp is implemented in Stwo using a simple example where
First, we create columns in the original trace, where all values are from the preprocessed trace $A$.

<figure id="fig-lookup-implementation-1" style="text-align: center;">
<img src="./lookups-1.png" width="80%" />
<img src="./figures/lookups-1.png" width="80%" />
<figcaption><center><span style="font-size: 0.9em">Figure 1: Create original trace columns that look up values from a preprocessed trace</span></center></figcaption>
</figure>

Then, we add a multiplicity column to the original trace indicating the number of times each value in $A$ appears in the original trace.

<figure id="fig-lookup-implementation-2" style="text-align: center;">
<img src="./lookups-2.png" width="90%" />
<img src="./figures/lookups-2.png" width="90%" />
<figcaption><center><span style="font-size: 0.9em">Figure 2: Add a multiplicity column</span></center></figcaption>
</figure>

Next, we create LogUp columns as part of the interaction trace, one for the preprocessed trace and the multiplicity column, and another for the batch of all lookups.

<figure id="fig-lookup-implementation-3" style="text-align: center;">
<img src="./lookups-3.png" width="100%" />
<img src="./figures/lookups-3.png" width="100%" />
<figcaption><center><span style="font-size: 0.9em">Figure 3: Create LogUp columns</span></center></figcaption>
</figure>

To create a constraint over the LogUp columns, Stwo modifies the LogUp columns to contain the cumulative sum of the fractions in each row. This results in columns that look like the following:

<figure id="fig-lookup-implementation-4" style="text-align: center;">
<img src="./lookups-4.png" width="70%" />
<img src="./figures/lookups-4.png" width="70%" />
<figcaption><center><span style="font-size: 0.9em">Figure 4: Cumulative sum columns</span></center></figcaption>
</figure>

Expand All @@ -94,7 +94,7 @@ $$
$$

<figure id="fig-lookup-implementation-5" style="text-align: center;">
<img src="./lookups-5.png" width="80%" />
<img src="./figures/lookups-5.png" width="80%" />
<figcaption><center><span style="font-size: 0.9em">Figure 5: Constraint over two rows</span></center></figcaption>
</figure>

Expand All @@ -109,7 +109,7 @@ $$
where $\text{avg}$ is a witness value provided by the prover.

<figure id="fig-lookup-implementation-6" style="text-align: center;">
<img src="./lookups-6.png" width="100%" />
<img src="./figures/lookups-6.png" width="100%" />
<figcaption><center><span style="font-size: 0.9em">Figure 6: Trick to not create a separate constraint for the first row</span></center></figcaption>
</figure>

Expand Down
9 changes: 9 additions & 0 deletions src/how-it-works/pcs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Polynomial Commitment Scheme

> This section presents the implementation of the polynomial commitment scheme in Stwo, which is built on top of the FRI protocol described previously. Polynomial commitments are a core cryptographic primitive that enable a prover to commit to a polynomial and later reveal evaluations at specific points.

This section is organized as follows:

- [**Overview**](./overview.md): Describes the polynomial commitment scheme of Stwo.
- [**PCS Prover**](./prover.md): Details the implementation of the prover for the polynomial commitment scheme, including commitment and opening protocol.
- [**PCS Verifier**](./verifier.md): Describes the verifier implementation for checking commitments and evaluation proofs.
76 changes: 76 additions & 0 deletions src/how-it-works/pcs/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Technical Overview


In this section, we describe a polynomial commitment scheme using the FRI protocol covered in the previous section.

## Polynomial Commitment Scheme


A polynomial commitment scheme (PCS) allows a prover to commit to a polynomial and later prove its evaluations at points chosen by the verifier. The verifier can then check that the evaluations are consistent with the committed polynomial. It consists of the following three algorithms:


- $setup(d) \rightarrow pp$: Given an upper bound $d$ on the degree, it outputs public parameters $pp$ used to commit to polynomials of degree less than $d$. For STARKs, the public parameters include the hash functions used for the Merkle commitment scheme and the FRI protocol parameters.


- $commit(pp, f) \rightarrow com_f$: Takes public parameters $pp$ and a polynomial $f$ of degree $< d$, and outputs a commitment $com_f$ to the polynomial. For STARKs, $com_f$ is the root of the Merkle tree which commits to the evaluations of the polynomial $f$ on the evaluation domain.


- $open(\mathcal{P}, \mathcal{V}) \rightarrow 0/1$: An interactive protocol where the prover $\mathcal{P}$ convinces the verifier $\mathcal{V}$ that $f(u) = v$. The verifier outputs 1 (accept) or 0 (reject). For STARKs, this protocol is based on FRI. The verifier will ask the prover to open the polynomial $f$ (committed using the Merkle tree) at point $u$. The prover will send the opening $v$ and define the quotient:
$$
q(x) = \frac{f(x) - v}{x - u}
$$

If the prover sends the correct opening, then $q$ will be a polynomial of bounded degree. The prover then uses the FRI protocol to convince the verifier that $q$ is "close" to some polynomial with a pre-specified degree bound.


For Stwo, we have already described the $commit$ protocol, which evaluates the circle polynomial over a canonical coset and commits to those evaluations using a Merkle tree. The $open$ protocol follows the same idea as the univariate case discussed above, but it is slightly different, as described next.


In Stwo, instead of using the single-point opening as described above, we have two-point openings for the values at point $Q$ and its conjugate $\overline{Q}$. The protocol proceeds as follows:

1. The verifier first receives a Merkle commitment to the evaluations of the original polynomial $f$.
2. The verifier samples a circle point $Q \in C(\mathsf{QM31}) \setminus C(\mathsf{M31})$ and requests the evaluation $f(Q)$ from the prover.
3. The prover sends the purported value $V$, and then both prover and verifier engage in the FRI protocol on the quotient:

$$
q(x, y) = \frac{f(x, y) - l_{Q,V}(x, y)}{v_Q(x, y)}
$$

Here, $l_{Q,V}(x, y)$ is the linear polynomial interpolating $(Q, V)$ and $(\overline{Q}, \overline{V})$, while $v_Q(x, y)$ vanishes at $Q$ and its conjugate $\overline{Q}$. As in the univariate case, if $q(x, y)$ is "close" to a polynomial, then the verifier is convinced that the evaluation claim is correct, i.e., that $f(Q) = V$.


In the above protocol, we are opening the polynomial at a single point $Q$. To open the polynomial at multiple points, we batch the quotients of each point using a random linear combination and then apply the FRI protocol to a single batched quotient.


One key property of a polynomial commitment scheme is binding. Informally, the binding property states that once the prover commits to a polynomial, they cannot open some other polynomial which outputs a different evaluation. For STARKs, the binding property is closely related to out-of-domain sampling, which we will describe next.

## Out of Domain Sampling


Out-of-domain sampling relates to the notion of "closeness" described in the [FRI section](../circle-fri/overview.md#introduction). Informally, the FRI protocol tests whether a function provided by the prover is "close" to some bounded degree polynomial. There are two notions of "closeness":

1. **Unique Decoding Regime**: We operate in this regime if there is at most a single polynomial which is "close" to the function provided by the prover. If the function is "close" to a single polynomial, then we can infer that the function represents that _unique_ polynomial.
2. **List Decoding Regime**: We operate in this regime if there is a list of polynomials which are "close" to the function provided by the prover. In this case, since the function can be "close" to a _list_ of polynomials, we cannot be sure that it represents a unique polynomial.


In practice, we are usually operate in the list decoding regime. So there can be multiple polynomials which are "close" to the function provided by the prover. This affects the binding property of the polynomial commitment scheme, since the function sent by the prover represents a list of polynomials rather than some unique polynomial.


To bind the prover to a unique polynomial from the list, we ask the prover to open the polynomial at an out-of-domain point. This is also referred to as _Domain Extension for Eliminating Pretenders_ (or the _DEEP method_). This is the informal motivation for out-of-domain sampling. For more details, please refer to ["A summary on the FRI low degree test"](https://eprint.iacr.org/2022/1216.pdf).


As we have seen in the [Security Analysis section](../circle-fri/overview.md#security-analysis), we can improve security by increasing the number of verifier queries. But this will lead to more prover work, because the prover will have to send a Merkle decommitment for each verifier query and also increase the proof size. We will now see a method to increase the security of our protocol without significantly increasing the prover's work.

## Proof of Work


The key idea is that rather than increasing the number of verifier queries, we can increase the cost of generating a false proof by a malicious prover by using _proof of work_ or _grinding_.


We add an additional requirement to the FRI protocol: following all the commitments made by the prover, the prover must find a 64-bit nonce that, when hashed together with the state of the hash chain, results in a required number of leading zeros. The number of leading zeros defines a certain amount of work that the prover must perform before generating the randomness representing the queries. As a result, a malicious prover that attempts to generate favorable queries will need to repeat the grinding process every time a commitment is changed. On the other hand, an honest prover only needs to perform the grinding process once.


This is similar to the grinding performed on many blockchains. The nonce found by the prover is sent to the verifier as part of the proof, and in turn the verifier checks its consistency with the state of the hash chain by running the hash function once. The required number of leading zeros is configured by the `pow_bits` parameter.


This effectively reduces the computational power of the cheating prover while only slightly increasing the running time of the honest prover. This is because the honest prover needs to solve the proof-of-work once, while a cheating prover, during the long process of trying to find a false proof, would need to solve many different instances of the proof-of-work.
Loading