## STARK VM
### STARK
> Scalable Transparent ARgument of Knowledge, which applies to any SNARK (or even interactive SARK) that has at most a polylogarithmic prover overhead and no trusted setup.
정의가 넓은데 그 중 특히 AET, AIR, FRI 를 사용하는 STARK를 다룬다.
### AIR and AET
#### Arithmetic Intermediate Representation (AIR) constraints
- Boundary constraints: $\mathbb{F}^{\mathsf{w}} \rightarrow \mathbb{F}$
- 함수 반환은 0 이면 만족, 아니면 불만족.
- Transition constraints: $\mathbb{F}^{2\mathsf{w}} \rightarrow \mathbb{F}$
- (지금 상태, 다음 상태) 를 인자로 받기 때문에 2w 개의 인자
- 상태는 레지스터, 메모리 등
#### Algebraic Execution Trace (AET)
T개의 명령으로 구성된 프로그램이라고 하면 T+1 개의 행의 AET가 나온다.
1. 초기 상태
2. 1개의 명령을 실행할 때마다 1개의 행 추가.
The AET is _integral_ if all Arithmetic Intermediate Representation (AIR) constraints are satisfied.
### Commitment
p = 2^64 - 2^32 + 1. prime
전체 Field generator $g = 1753635133440165772$
$g ^{2^{32}} = 1$
Trace domain의 generator $\omicron = g^{order/height}$
Fri domain은 $c \cdot o^i$
BaseField.
$\mathbb{F}_{p^3} \cong \frac{\mathbb{F}[X]}{\langle X^3 -X + 1\rangle}$
64비트가 작기 때문에 Extension column 부터는 Extension Field의 원소들을 채운다.
하나의 제약당 두 개의 random 이 필요.
아래의 Nonlinear combination 다항식이 FRI의 입력이 된다.
$$\sum_{i=0} \alpha_i \cdot q_i(X) + \beta_i \cdot X^{\mathsf{d} - b_i} \cdot q_i(X) \enspace$$
$\alpha_i$ 가 필요한 이유에 대한 설명
https://aszepieniec.github.io/stark-anatomy/fri#compiling-a-polynomial-iop
### FRI
Quotient polynomial이 low-degree polynomial 임을 증명하는 방법.
- "low-degree"란 $\rho |D|$ 이하의 차수를 의미.
- FRI에서 folding을 수행해도 각 단계의 `(Hamming distance)/전체 길이` 비율은 유지되거나 증가한다. 마지막 단계에서 얻어진 상수항(Field element)이 예상값과 같으면 원래 다항식을 낮은 차수로, 다르면 높은 차수로 판정한다. 이 과정에서 $\rho$ 확률로 잘못된 판정을 할 수 있다. 보안 수준을 높이기 위해 여러 인덱스에 대해 이 과정을 반복하며, i번 반복하면 전체 오류 확률은 $\rho^i$ 로 감소하여 security bits를 증가시킨다.
### 폰 노이만 구조
1. 메모리에서 다음 명령을 읽는다.
2. 메모리 또는 표준 입력에서 데이터를 읽는다.
3. 명령에 따라 레지스터를 업데이트한다.
4. 메모리 또는 표준 출력에 데이터를 쓴다.

테이블이 여러 개이다. 프로세서, 메모리, 프로그램, 입력, 출력.
1. 개별 테이블 제약.
2. 테이블간의 관계에 대한 제약.
### Permutation Arguments
주장: 두 열은 순서만 다르다. 즉, 중복을 허용하는 집합으로서 같다.
$\lbrace c_ i \rbrace_ i = \lbrace k_ i\rbrace_ i$
예: $\lbrace 1, 3, 1, 2 \rbrace = \lbrace 1, 1, 2, 3 \rbrace$
**The transition constraint**
$\prod_i(\alpha - c_i) = \prod_i(\alpha - k_i)$
$\alpha$ 를 이용해 `2 * 3 = 1 * 6` 이렇게 다른 집합의 곱이 같아지는 것을 막는다.
예시
| $c_i$ | $e$ |
| ----- | -------------------------------------------------- |
| $w$ | $\alpha - w$ |
| $x$ | $(\alpha - w)(\alpha - x)$ |
| $y$ | $(\alpha - w)(\alpha - x)(\alpha - y)$ |
| $z$ | $(\alpha - w)(\alpha - x)(\alpha - y)(\alpha - z)$ |
| $k_i$ | $e$ |
| ----- | -------------------------------------------------- |
| $x$ | $\alpha - x$ |
| $y$ | $(\alpha - x)(\alpha - y)$ |
| $w$ | $(\alpha - x)(\alpha - y)(\alpha - w)$ |
| $z$ | $(\alpha - x)(\alpha - y)(\alpha - w)(\alpha - z)$ |
증명 개요
- Schwartz-zippel lemma로 $\alpha$에 방정식의 해가 나올 확률은 $height/|F|$.
Reference: [stark-brainfuck/docs/engine.md at master · aszepieniec/stark-brainfuck · GitHub](https://github.com/aszepieniec/stark-brainfuck/blob/master/docs/engine.md#permutation-arguments)
### Evaluation Arguments
주장: 한 테이블의 행들은 다른 테이블 행들의 서브리스트이다. 순서 보존.
$(c_i)_{i \vert j_{i}= 1} = (k_j)_j$
- $j_i = 1$인 항들만 짧은 테이블의 칼럼에 포함됨.
- 순서까지 같음을 보이기 위해 먼저 나온 항일수록 $\alpha$ 의 차수가 높다.
**The transition constraint**
큰 테이블
$\forall i > 0 : e_ {i} = \jmath_ {i} \cdot (\alpha e_ {i-1} + c_ i) + (1 - \jmath_ {i}) \cdot e_ {i-1}$
- $\jmath_i = 1$ 일 때만 기존 $e_{i-1}$ 에 $\alpha$ 를 곱하고 이번 행의 $c_i$ 를 더해준다.
작은 테이블
$\forall j > 0 : e_j = \alpha e_{j-1} + k_j$
- 모든 행에서 기존 $e_{i-1}$ 에 $\alpha$ 를 곱하고 이번 행의 $c_i$ 를 더해준다.
예시
큰 테이블
| $\jmath_ i$ | $c_ i$ | $e_ i$ |
| ----------- | ------ | --------------------------- |
| 0 | $u$ | 0 |
| 1 | $x$ | $x$ |
| 0 | $v$ | $x$ |
| 1 | $y$ | $\alpha x + y$ |
| 0 | $w$ | $\alpha x + y$ |
| 1 | $z$ | $\alpha^2 x + \alpha y + z$ |
작은 테이블
| $c_ j$ | $e_ j$ |
| ------ | --------------------------- |
| $x$ | $x$ |
| $y$ | $\alpha x + y$ |
| $z$ | $\alpha^2 x + \alpha y + z$ |
### Table Relations with Zero-Knowledge
문제: Permutation argument, Evaluation argument 는 Terminal 값으로 정보를 누출한다.
해결: Prover는 초기값을 1이 아닌 임의의 수로 두고 Verifier에게 보내지 않는다.
$k \cdot a \cdot \cdot b = k \cdot b \cdot a$ 이므로 초기값 k는 임의의 수여도 Permutation argument 는 성립한다. 대신 두 칼럼의 초기값은 같아야 한다. 이에 대한 제약은 아래의 **Difference Constraints** 에서 설명한다.
### STARK Engine 작동 순서
1. Execution trace의 데이터로 테이블의 base column들을 채운다. 테이블의 길이가 모두 $2^n$ 으로 같게 padding row도 넣는다.
2. 최대 차수의 randomizer polynomial를 랜덤하게 뽑는다. 이를 FRI domain에서 계산해서 _randomizer codeword_ 를 얻는다.
3. 테이블의 base 칼럼들을 보간(interpolate)한 뒤, 이를 FRI domain에서 계산해서 _base codewords_ 를 얻는다.
1. 이 때 interpolation의 x좌표로 $2^n$ 개의 Trace domain의 원소들과 추가되는 randomizer 행들이 쓰인다.
2. Trace domain은 $w^{2^n} = 1$ 을 만족하는 원소들이다.
3. FRI domain은 위와 교집합이 없도록 $o w^i$ 를 만족하는 원소들이다.
4. Zip the randomizer and base codewords 후 머클 트리를 구한다.
1. zip: 여러 원소들을 비트 단위로 이어붙임을 의미.
5. 머클 트리 루트를 Verifier에게 보내고 랜덤 상수 $a,b,c, \alpha, ...$ 등의 challenge를 얻는다.
6. challenge를 이용해 테이블의 extension columns를 채운다.
7. 테이블의 extension 칼럼들을 보간(interpolate)한 뒤, 이를 FRI domain에서 계산해서 _extension codewords_ 를 얻는다.
8. 모든 AIR 제약에 대한 _Quotient codewords_ 를 계산한다.
1. Quotient codewords는 Quotient polynomial을 FRI domain에서 평가한 값들을 NTT(Number Theoretic Fourier Transform) 를 통해 계산함을 의미한다.
2. Quotient polynomial 은 아래에서 자세히 알아보자.
9. Zip the extension codewords 후 두번째 머클 트리를 구한다.
10. Verifier에게 머클 루트를 보내고 1 + 2b + 2e + 2q 개의 랜덤 상수, _weights_ 를 받는다. b는 base codewords의 개수, e는 extension codewords의 개수, q는 quotient codewords의 개수를 말한다.
11. A nonlinear combination 을 계산한다.
1. 각 codeword는 $g(X) = \sum_{i=0}^{n-1} \alpha_i \cdot f_i(X) + \beta_i \cdot X^{2^k-d_i-1} \cdot f_i(X) \enspace$ 식으로 전부 더한다.
2. randomizer는 하나의 weight만 사용해서 위에 더해준다.
12. 이 nonlinear combination codeword 의 머클 트리를 계산한다.
13. Verifier에게 머클 루트를 보내고 correct combination인지 체크할 t 개의 인덱스를 받는다.
14. 해당 인덱스들을 3개의 트리에서 open한다. weights와 codewords를 내적한 값과 nonlinear combination 중 해당 인덱스의 leaf node가 같은지 검증한다.
15. The nonlinear combination codeword 에 대해서 FRI를 실행해서 각각의 열이 low-degree polynomial임을 보인다.
### Quotient polynomials
i번째 열에 대한 다항식을 c_i(x) 라 하자.
**Boundary constraint**
첫번째 행에 대해서만 성립하는 제약
$$\frac{f(c_0(x)), ... , c_n(x))}{x - 1}$$
```python
for point, table in zip(points, self.tables):
# boundary
for constraint, bound in zip(table.boundary_constraints_ext(challenges), table.boundary_quotient_degree_bounds(challenges)):
eval = constraint.evaluate(point)
quotient = eval / \
(self.xfield.lift(self.fri.domain(index)) - self.xfield.one())
```
**Transition constraint**
transition constraint에는 point $X$, next_point $X_{next}$ 다 들어와야 evaluate 가능
- N: Table height
- $\omicron^{-1}$ 은 맨 마지막 행에 대한 Trace domain의 원소. 다음 행이 없기 때문에 Transition constraint가 만족하지 않으므로 분모에서 제외한다.
$$
\frac{f(c_0(x)), ... , c_n(x), c_0(\omicron x), ..., c_n(\omicron x))(X - \omicron^{-1})}{x^N - 1}
$$
**Terminal constraint**
마지막 행에 대해서만 성립하는 제약.
$$\frac{f(c_0(x)), ... , c_n(x)) - T}{x - \omicron^{-1}}$$
```python
# terminal
for constraint, bound in zip(table.terminal_constraints_ext(challenges, terminals), table.terminal_quotient_degree_bounds(challenges, terminals)):
eval = constraint.evaluate(point)
quotient = eval / \
(self.xfield.lift(self.fri.domain(index)) -
self.xfield.lift(table.omicron.inverse()))
```
**Difference constraint**
서로 다른 두 칼럼이 첫번째 행에 대해서 같다는 제약.
$$\frac{f(c_0(x)), ... , c_n(x)) - g(c_0(x)), ... , c_n(x))}{x - 1}$$
```python
for arg in self.permutation_arguments:
quotient = arg.evaluate_difference(
points) / (self.xfield.lift(self.fri.domain(index)) - self.xfield.one())
```
**Fiat-shamir**
1. base_tree.root()
1. -> extension column의 random linear combination을 위한 challenges 샘플링
2. extension_tree.root()
3. terminals
1. self.processor_table.instruction_permutation_terminal
2. self.processor_table.memory_permutation_terminal
3. self.processor_table.input_evaluation_terminal
4. self.processor_table.output_evaluation_terminal
5. self.instruction_table.evaluation_terminal
6. -> weight_seed 샘플링
4. combination_tree.root()
1. -> indices_seed 샘플링: FRI 검사할 인덱스를 뽑기 위한 랜덤 시드
### Table
**def interpolate_columns**
- randomizers: 다항식을 통해 노출되는 정보가 없게 하기 위한 랜덤 행
- domain이 $\omicron$과 겹치지 않게 주의
- x축은 $x = \omega^{2i+1}$ 로 잡아서 $\omega$ 의 짝수 제곱인 $\omicron$ 과 겹치지 않게 함.
**def extend**
기존 테이블에 extension column을 추가한다.
## The Programming Language: Brainfuck
Turing-complete. 명령어는 총 8개이다.
- `[` 는 현재 메모리 셀이 0 이면 대응하는 `]` 으로 점프. mv가 0이 아니면 `ip = ip + 2`.
- `]` 는 현재 메모리 셀이 0이 아니면 대응하는 `]` 으로 점프.
- `+` 현재 메모리 셀에 1을 더한다.
- `-` 현재 메모리 셀에서 1을 뺀다.
- `<` 현재 메모리 포인터에서 1을 뺀다.
- `>`현재 메모리 포인터에서 1을 더한다.
- `.` 현재 메모리 셀의 값을 출력한다.
- `,` 현재 메모리 셀에 유저 입력을 저장한다.
**Prime Field Brainfuck**
기존 Brainfuck은 modulo 256을 사용하는데 여기서는 modulo $p = 2^{64} - 2^{32} + 1$를 사용한다. 따라서 p는 최대 메모리 셀의 개수, 최대 프로그램 명령의 개수이고 메모리에 저장되는 값은 $F_p$ 의 원소이다.
**Brainfuck ISA**
Brainfuck은 그 자체로는 instruction set architecture(ISA)가 아니다.
왜냐하면 `[` 와 `]` 명령이 서로의 위치에 의존적이다. 따라서 `[` 와 `]` 다음에 jump 할 주소를 넣어주도록 컴파일해주자.
### Compiling Brianfuck Programs
**Pushdown automaton**
스택을 활용한 컴파일.
https://en.wikipedia.org/wiki/Pushdown_automaton
`c` 를 지금까지 출력에 쓴 기호의 총 개수라고 하자.
1. `[` 를 만나면
1. `c`를 스택에 푸시한다.
2. `[` 와 placeholder `*` 를 출력한다.
2. `]` 를 만나면
1. 스택에서 한 번 pop한다. 이 값을 `i`라고 하자.
2. `i+1`에 있는 `*`를 `c+2`로 덮어쓴다.
3. `]` 와 `i+2` 를 출력한다.
3. 다른 기호들은 출력에 그대로 쓴다.
예시
```
++>,<[>+.<-]
```
컴파일 과정 중 `[`, `]` 만 보자.
1. `]` 를 만났을 때
1. 지금까지 5개를 출력했으므로 5를 스택에 푸시
2. 인덱스 5에 `[`, 6에 `*` 를 출력
2. `]` 를 만났을 때
1. 스택에서 5를 pop.
2. 인덱스 11까지 썼으므로 현재 c = 12. 따라서 6에 있는 `*`를 14로 덮어쓴다.
3. `]` 와 `7` 을 출력한다.
컴파일 결과
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| + | + | > | , | < | [ | 14 | > | + | . | < | - | ] | 7 |
## Brainfuck VM
VM의 상태는 두 개의 레지스터와 메모리로 이뤄진다.
- ip: instruction pointer
- mp: memory pointer
VM은 전이 함수(state transition function)를 정의한다.
- `program[ip] == '['` 이면
- `data[mp] == 0` 이면 점프. 즉 `ip = program[ip + 1]`
- `program[ip + 1]` 의 의미: `[` 의 다음에는 목적 주소가 적혀있다.
- 아니면, 목적 주소를 건너뛰어라. 즉 `ip = ip + 2`
- `program[ip] == ']'` 이면
- `data[mp] != 0` 이면 점프. 즉 `ip = program[ip + 1]`
- 아니면, 목적 주소를 건너뛰어라. 즉 `ip = ip + 2`
- `program[ip] == '<'` 이면 mp = mp - 1 and ip = ip + 1
- `program[ip] == '>'` 이면 mp = mp + 1 and ip = ip + 1
- `program[ip] == '+'` 이면 `data[mp] = data[mp] + 1` and ip = ip + 1
- `program[ip] == '-'` 이면 `data[mp] = data[mp] - 1` and ip = ip + 1
- `program[ip] == '.'` 이면 `data[mp]`를 출력 and ip = ip + 1
- `program[ip] == ','` 이면 `data[mp]`에 입력값을 저장 and ip = ip + 1
NOTE
- binary operation이 없음. 예를 들어, `+` 에 operand가 없음
## Arithmetization of Brainfuck VM
**Registers**
- `clk` - clock. 현재까지 실행된 명령 수.
- `ip` - instruction pointer. `ci` 에 로드할 프로그램 주소.
- `ci` - 현재 명령. `ip` 위치의 프로그램 값
- `ni` - 다음 명령. `ip+1` 위치의 프로그램 값, `ip`가 마지막이라면 0.
- `mp` - memory pointer. `mv`에 로드할 메모리 주소
- `mv` - memory value. `mp` 에 위치한 메모리 값
- `inv` - inverse. `mv` 가 0이면 0, 아니면 `mv`의 곱셈 역원.
**Tables**
- Memory Table 프로세서는 메모리를 읽고 쓴다.
- Instruction Table
- Input Table
- Output Table

### Processor Table
**구성**
- 7 base columns: 7 registers
- 4 extension columns: 다른 네 테이블과의 관계
- `ipa`: Instruction Permutation
- `mpa`: Memory Permutation
- `iea`: Input Evaluation
- `oea`: Output Evaluation
- $a, b, c, \alpha$: Instruction permutation의 random scalar
- $d, e, f, \beta$: Memory permutation의 random scalar
- $\gamma$: Input evaluation의 random scalar
- $\delta$: Output evaluation의 random scalar
제약을 만족하면 각 제약식은 0이 된다.
**Boundary constraints**
Base columns
- `ci` 와 `ni` 를 제외한 레지스터들은 전부 0으로 초기화한다.
- `clk - 0`, `ip - 0`, `mp - 0`, `mv - 0`, `inv - 0`
Extension columns
- `Instruction permuation`, `Memory permutation` 은 random initial이므로 제약 없음.
- `iea`, `oea`은 0으로 초기화.
- `iea - 0`
- `oea - 0`
**Transition constraints**
Disjunctive normal form: OR-of-ANDs
- OR은 제약 다항식의 곱셈, AND는 random coefficient를 이용한 일차 결합으로 표현 후 (제약 = 0)이 성립하도록 만든다.
- $A \rightarrow B$ 를 집합 기호로 표현하면 $\neg A \lor B$
**명령별 제약**
instruction deselector을 곱해서 제약이 특정 명령어에 대해서만 유효하게 만든다.
$\mathsf{ci} = \varphi$ 인 경우에만 적용되게 할 때, 아래 식을 사용한다.
$$
\varsigma_\varphi(\mathsf{ci}) = \mathsf{ci} \prod_ {\phi \in \Phi \backslash \varphi} (\mathsf{ci} - \phi) \enspace
$$
ci = `[`
- 메모리 값이 0이면 점프, 0이 아니면 명령 포인터(ip) 2 증가: `(ip' - ip - 2) * mv + (ip' - ni) * iszero`
- `iszero` 는 $1 - \mathsf{mv} \cdot \mathsf{inv}$ 식으로 `mv` 가 0일 때 1이 된다.
- `ni` 는 다음 명령어고 컴파일할 때 점프할 위치를 넣어뒀다.
- 메모리 포인터 유지: `mp' - mp`
- 메모리 값 유지: `mv' - mv`
ci = `]`
- 메모리 값이 0이 아니면 점프, 0이면 명령 포인터(ip) 2 증가: `(ip' - ip - 2) * iszero + (ip' - ni) * mv`
- 메모리 포인터 유지 `mp' - mp`
- 메모리 값 유지: `mv' - mv`
ci = `<`
- 명령 포인터(ip) 1 증가: `ip' - ip - 1`
- 메모리 포인터 1 감소: `mp' - mp + 1`
- 메모리 값은 제약 없음.
ci = `>`
- 명령 포인터(ip) 1 증가: `ip' - ip - 1`
- 메모리 포인터 1 증가: `mp' - mp - 1`
- 메모리 값은 제약 없음.
ci = `+`
- 명령 포인터(ip) 1 증가: `ip' - ip - 1`
- 메모리 포인터 유지 `mp' - mp`
- 메모리 값 1 증가: `mv' - mv - 1`
ci = `-`
- 명령 포인터(ip) 1 증가: `ip' - ip - 1`
- 메모리 포인터 유지 `mp' - mp`
- 메모리 값 1 감소: `mv' - mv + 1`
ci = `,`
- 명령 포인터(ip) 1 증가: `ip' - ip - 1`
- 메모리 포인터 유지 `mp' - mp`
- 메모리 값은 입력에서 가져오므로 제약 없음.
ci = `.`
- 명령 포인터(ip) 1 증가: `ip' - ip - 1`
- 메모리 포인터 유지 `mp' - mp`
- 메모리 값 유지 `mv' - mv`
instruction에 무관한 제약들
- clock이 1씩 증가: `clk' - clk - 1`
- `inv * (1 - inv * mv)`
- `mv * (1 - inv * mv)`
- 둘 다 필요한 이유: inv = 0 일 때 mv= 0 을 강제하기 위함
The transition constraints for the extension columns
1. The instruction permutation argument
- $\mathsf{ci} \cdot ( \mathsf{ipa} \cdot (a \cdot \mathsf{ip} + b \cdot \mathsf{ci} + c \cdot \mathsf{ni} - \alpha) - \mathsf{ipa}^\star) + (\mathsf{ipa}^\star - \mathsf{ipa}) \cdot \prod_{\varphi \in \Phi} (\mathsf{ci} - \varphi)$
- $\mathsf{ci} \in \Phi$ 일 때의 ip, ci, ni 칼럼은 instruction table과 순서만 다르다.
2. The memory permutation argument
- clk, mp, mv 칼럼이 memory table과 순서만 다르다.
- $\mathsf{mpa} \cdot (d \cdot \mathsf{clk} + e \cdot \mathsf{mp} + f \cdot \mathsf{mv} - \beta) - \mathsf{mpa}^\star$
3. The input evaluation argument
- evaluation argument 이므로 $\gamma$ 를 계속 곱하며 차수를 늘려간다.
- $\varsigma_{‘,’}(\mathsf{ni}) \cdot (\mathsf{iea} \cdot \gamma + \mathsf{mv}) + (\mathsf{ni} - ',') \cdot \mathsf{iea} - \mathsf{iea}^\star$
- instruction deselector $\varsigma$ 가 사용됨. ni가 `,`이면 더한다.
- Q. 마지막 항이 $(iea - iea^*)$ 이렇게 괄호로 묶어야 하는 거 아닌가?
- A. 아니다. $\mathsf{iea}^\star$ 를 우변에 넘기고 좌변이 $\mathsf{iea}^\star$ 와 같아야 한다는 제약으로 보면 더 잘 보인다.
1. The output evaluation argument
- $\varsigma_{‘.’}(\mathsf{ni}) \cdot (\mathsf{oea} \cdot \delta + \mathsf{mv}) + (\mathsf{ni} - '.') \cdot \mathsf{oea} - \mathsf{oea}^\star$
**Terminal constraints**
1. $\mathsf{ipa} \cdot (a \cdot \mathsf{ip} + b \cdot \mathsf{ci} + c \cdot \mathsf{ni} - \alpha) - T_{\mathsf{ipa}}$
2. $\mathsf{mpa} \cdot (d \cdot \mathsf{clk} + e \cdot \mathsf{mp} + f \cdot \mathsf{mv} - \beta) - T_{\mathsf{mpa}}$
3. $\mathsf{iea} - T_{\mathsf{iea}}$
4. $\mathsf{oea} - T_{\mathsf{oea}}$
참고: Constraints는 구현 별로 조금씩 다름.
- https://github.com/aszepieniec/stark-brainfuck/blob/master/code/processor_table.py#L304
- https://github.com/andrewmilson/ministark/blob/main/examples/brainfuck/constraints.rs#L136
**Example: Processor Table**
가정
- $\beta = 3, d = e = f = 1$
- permutation argument init = 7
계산
- $prp_n = init \cdot \prod_{i=0}^n{(\beta - d \cdot \mathsf{clk} - e \cdot \mathsf{mp} - f \cdot \mathsf{mv})}$
- 2번째 행: $7 \cdot (3 - 1 - 0 -1)= 7$
- 3번째 행: $7 \cdot (3 - 2 - 0 - 2) = -7 = -7 +(2^{64} - 2^{32} + 1)$
| clk | ip | ci | ni | mp | mv | mvi | prp |
| --- | --- | --- | --- | --- | --- | ----- | -------------------- |
| 0 | 0 | a+ | a+ | 0 | 0 | 0 | 7 |
| 1 | 1 | a+ | a> | 0 | 1 | 1 | 7 |
| 2 | 2 | a> | a, | 0 | 2 | 2⁻¹ | 18446744069414584314 |
| 3 | 3 | a, | a< | 1 | 0 | 0 | 18446744069414584342 |
| 4 | 4 | a< | a[ | 1 | 97 | 97⁻¹ | 18446744069414584258 |
| 5 | 5 | a[ | 13 | 0 | 2 | 2⁻¹ | 18446744069414590684 |
| 6 | 7 | a> | a+ | 0 | 2 | 2⁻¹ | 18446744069414546143 |
| 7 | 8 | a+ | a. | 1 | 97 | 97⁻¹ | 18446744069414851567 |
| 8 | 9 | a. | a< | 1 | 98 | 98⁻¹ | 18446744069414851567 |
| 9 | 10 | a< | a- | 1 | 98 | 98⁻¹ | 18446744072360704225 |
| 10 | 11 | a- | a] | 0 | 2 | 2⁻¹ | 18446743754179754593 |
| 11 | 12 | a] | 7 | 0 | 1 | 1 | 3467583127008 |
| 12 | 7 | a> | a+ | 0 | 1 | 1 | 18446705926000187233 |
| 13 | 8 | a+ | a. | 1 | 98 | 98⁻¹ | 4577209727650056 |
| 14 | 9 | a. | a< | 1 | 99 | 99⁻¹ | 1839593704143766310 |
| 15 | 10 | a< | a- | 1 | 99 | 99⁻¹ | 5741194161392097408 |
| 16 | 11 | a- | a] | 0 | 1 | 1 | 9586652100225931044 |
| 17 | 12 | a] | 7 | 0 | 0 | 0 | 12634263021116362185 |
| 18 | 14 | 0 | 0 | 0 | 0 | 0 | 7659764256986325571 |
| | | | | | | | 5425144832537830614 |
### Instruction Table
**구성**
- 3 bases columns: `ip`, `ci`, `ni`
- 2 extension columns
- `ppa` processor permutation argument
- `pea` program evaluation argument.
- a, b, c: permutation argument에서 base columns의 weight.
- $\alpha$: permutation argument의 random scalar.
- $\eta$: evaluation argument에서의 random scalar.
만드는 방법
1. program을 순회하며 (ip, ci, ni)를 채운다.
3. Processor Table의 패딩이 아닌 행들의 (ip, ci, ni) 를 한 번 복사해서 아래에 연결한다.
4. `ip` 오름차순으로 정렬한다.
m을 해당 명령이 실행된 횟수라고 할 때, Instruction Table 내에서 각 명령은 m + 1번 반복된다.
제약의 의미
1. Processor가 program 에 적힌 (ip, ci, ni) 대로 실행한다.
2. 프로그램이 실행 도중 바뀌지 않는다.
**Boundary constraints**
- `ip` 는 0부터 시작한다.
- `ip - 0`
**Transition constraints**
base column 제약
- `ip` 는 0 또는 1 증가
- `(ip' - ip)(ip' - ip - 1)`
- `ip` 가 1 증가하면 이번 행의 `ni` 는 다음 행의 `ci'` 와 같다.
- `(ip' - ip)(ni - ci')`
- `ip` 가 그대로 이면 `ci`도 그대로다.
- `(ip' - ip - 1)(ci' - ci)`
- `ip` 가 그대로 이면 `ni` 도 그대로다.
- `(ip' - ip - 1)(ni' - ni)`
extension column 제약
- 패딩 행은 ci = 0.
The permutation argument
- $\mathsf{ci} \cdot (\mathsf{ip} + 1 - \mathsf{ip}^\star) \cdot (\mathsf{ppa}^\star - \mathsf{ppa} \cdot (a \cdot \mathsf{ip}^\star + b \cdot \mathsf{ci}^\star + c \cdot \mathsf{ni}^\star - \alpha))$
- 패딩 행이 아니고 ip가 1 증가하지 않으면 permutation argument 누적
- $\left( \prod_{\varphi \in \Phi} \mathsf{ci} - \varphi \right) \cdot (\mathsf{ppa}^\star - \mathsf{ppa})$
- $\Phi$ 는 instructions set
- ci 가 instructions set의 원소가 아니라면 ppa는 그대로.
- $(\mathsf{ip} - \mathsf{ip}^\star) \cdot (\mathsf{ppa}^\star - \mathsf{ppa})$
- ip가 변화하면 paa는 그대로.
The evaluation argument
다음 행에서 ip 가 바뀔 때만 evaluation argument
- $(\mathsf{ip}^\star - \mathsf{ip} - 1) \cdot (\mathsf{pea}^\star - \mathsf{pea} )$
- $(\mathsf{ip}^\star - \mathsf{ip}) \cdot (\mathsf{pea}^\star - \mathsf{pea} \cdot \eta - (a \cdot \mathsf{ip}^\star + b \cdot \mathsf{ci}^\star + c \cdot \mathsf{ni}^\star))$
**Terminal constraints**
- $ppa - T_{ppa}$
- $T_{ipa} = T_{ppa}$ 이므로 prover에게 받은 $T_{ppa}$ 를 여기에도 사용
- $pea - T_{pea}$
- $T_{pea}$ 는 Verifier가 직접 계산한다.
**Example: Instruction Table**
| ip | ci | ni |
| --- | --- | --- |
| 0 | a+ | a+ |
| 0 | a+ | a+ |
| 1 | a+ | a> |
| 1 | a+ | a> |
| 2 | a> | a, |
| 2 | a> | a, |
| 3 | a, | a< |
| 3 | a, | a< |
| 4 | a< | a[ |
| 4 | a< | a[ |
| 5 | a[ | 13 |
| 5 | a[ | 13 |
| 7 | a> | a+ |
| 7 | a> | a+ |
| 7 | a> | a+ |
| 8 | a+ | a. |
| 8 | a+ | a. |
| 8 | a+ | a. |
| 9 | a. | a< |
| 9 | a. | a< |
| 9 | a. | a< |
| 10 | a< | a- |
| 10 | a< | a- |
| 10 | a< | a- |
| 11 | a- | a] |
| 11 | a- | a] |
| 11 | a- | a] |
| 12 | a] | 7 |
| 12 | a] | 7 |
| 12 | a] | 7 |
### Memory Table
**구성**
- 3 base columns: `clk`, `mp`, `mv`
- 1 extension column: `ppa`
- the permutation argument: Memory Table 과 Processor Table을 연결.
**Boundary constraints**
- `clk = mp = mv = 0`
- Processor Table 에서 동일한 제약이 있으므로 생략.
- `ppa` 에 대한 제약은 없음. Prover가 random 초기값을 하나 뽑기 때문.
**Transition constraints**
The base columns
- 메모리 포인터는 0 또는 1 증가
- `(mp + 1 - mp')(mp - mp')`
- 메모리 포인터가 1 증가하면 새 메모리 값은 0
- `(mp - mp')mv'`
- 메모리 포인터가 변하지 않고 사이클 카운트가 1 증가하면 메모리 값은 변할 수 있다.
- `(mp - mp')(mv' - mv)`
- 메모리 포인터가 변하면 메모리 값은 변하지 않는다.
- `(clk - 1 - clk')(mv' - mv)`
- 사이클 카운트가 1 증가하지 않으면 메모리 값은 변하지 않는다.
The extension column
- `ppa`: a product argument
- `ppa * (d * clk + e * mp + f * mv - beta) - ppa'`
- d, e, f 는 각 칼럼에 대한 random weight.
**Terminal constraints**
The table has one terminal, $T_{ppa}$ sent by the prover.
Processor Table에서 $T_{mpa}$ 와 비슷한 기능.
마지막 행을 제외하고 모든 행의 `(d * clk + e * mp + f * mv - beta)` 를 곱한 값이 마지막 행의 ppa에 위치. 따라서 Terminal constraints 는 아래처럼 마지막 행을 곱해서 확인.
$\mathsf{ppa} \cdot (d \cdot \mathsf{clk} + e \cdot \mathsf{mp} + f \cdot \mathsf{mv} - \beta) - T_{\mathsf{ppa}}$
**Example: Memory Table**
| `clk` | `mp` | `mv` |
| ----- | ---- | ---- |
| 0 | 0 | 0 |
| 1 | 0 | 1 |
| 2 | 0 | 2 |
| 5 | 0 | 2 |
| 6 | 0 | 2 |
| 10 | 0 | 2 |
| 11 | 0 | 1 |
| 12 | 0 | 1 |
| 16 | 0 | 1 |
| 17 | 0 | 0 |
| 18 | 0 | 0 |
| 3 | 1 | 0 |
| 4 | 1 | 97 |
| 7 | 1 | 97 |
| 8 | 1 | 98 |
| 9 | 1 | 98 |
| 13 | 1 | 98 |
| 14 | 1 | 99 |
| 15 | 1 | 99 |
### Input and Output Table
구성
- Base column: `c`
- Extension column: `ea`
**Boundary constraints**
`ea - c`
해설: 첫 행에서 `ea`와 `c` 두 칼럼은 같다.
**Transition constraints**
$$\zeta \cdot ea + c' - ea'$$
해설
- Evaluation argument에 사용되는 challenge $\zeta$ 는 Input table에서의 $\gamma$, Output table에서의 $\delta$ 를 의미.
- 이번 행의 `ea` 에 $\zeta$ 를 곱하고 다음 행의 base column 에 있는 $c'$ 를 더하면 다음 행의 extension column $ea'$ 와 같다.
**Terminal constraint**
$T_{ea} - ea$
Terminal value는 prover가 보내지 않고 verifier가 계산한다.
**Example: Input Table**
- ea: Evaluation Argument
| `input value` | `ea` |
| ------------- | ---- |
| 97 | 97 |
**Example: Output Table**
$\delta = 2$ 가정.
| `output value` | `ea` |
| -------------- | ------------------------ |
| 98 | 98 |
| 99 | $98 \cdot 2 + 99 = 295$ |
### Difference Constraints
두 칼럼이 같은 값에서 시작함을 증명.
영지식성을 위해 Prover가 permutation argument에 쓰일 extension column의 초기값은 랜덤하게 잡는다. 그럼 비교하는 두 열의 초기값은 서로 같아야 3 * 2 * 4 = 6 * 4 * 1 처럼 두 열의 곱이 같다고 서로가 치환이라고 잘못 판단하지 않는다.
예를 들어, Processor Table의 `ipa` 칼럼과 Instruction Table 의 `ppa` 칼럼은 permutation argument 이다. 각 칼럼의 보간 다항식 $f_{ipa}(X), f_{ppa}(X)$ 는 첫번재 행에서 같다. $f_{ipa}(X) - f_{ppa}(X)$ 는 $X = o^{0} = 1$ 에서 0이어야 한다. 따라서 $X - 1$ 로 나누어 떨어진다.
즉, 몫 다항식은 $max(deg(f_{ipa}(X)), deg(f_{ppa}(X))) - 1$ 이하의 차수를 가짐을 증명해야 한다.
Processor Table 의 `mpa` 칼럼과 Memory Table의 `ppa` 칼럼도 마찬가지다.
$\frac{f_{\mathsf{mpa}}(X) - f_{\mathsf{ppa}}(X)}{X-1}$ 는 $\max(\deg(f_{\mathsf{mpa}}(X)), \deg(f_{\mathsf{ppa}}(X))) - 1$ 의 차수를 가짐을 보여야 한다.
## Attack 예시
내가 작성한 서킷의 soundness 체크
예시 공격
Memory value를 다른 값으로 바꾼다는 것은 balance를 바꿀 수도 있다는 것
프로그램
`+><.-><+`
**Processor Table**
여기서 잘못된 것을 찾아보자.
| `clk` | `ip` | `ci` | `ni` | `mp` | `mv` | `inv` |
| ----- | ---- | ---- | ---- | ---- | ----- | ------------- |
| 0 | 0 | `+` | `>` | 0 | 0 | 0 |
| 1 | 1 | `>` | `<` | 0 | 1 | 1 |
| 2 | 2 | `<` | `.` | 1 | 0 | 0 |
| 3 | 3 | `.` | `-` | 0 | **2** | **2** $^{-1}$ |
| 4 | 4 | `-` | `>` | 0 | 2 | 2 $^{-1}$ |
| 5 | 5 | `>` | `<` | 0 | 1 | 1 |
| 6 | 6 | `<` | `+` | 1 | 0 | 0 |
| 7 | 7 | `+` | 0 | 0 | 1 | 1 |
| 8 | 8 | 0 | 0 | 0 | 2 | 2 $^{-1}$ |
**Memory Table**
| `clk` | `mp` | `mv` |
| ----- | ---- | ---- |
| 0 | 0 | 0 |
| 1 | 0 | 1 |
| 5 | 0 | 1 |
| 7 | 0 | 1 |
| 8 | 0 | 2 |
| 3 | 0 | 2 |
| 4 | 0 | 2 |
| 2 | 1 | 0 |
| 6 | 1 | 0 |
**AIR constraints**
1. 첫번째 줄은 `ci`, `ni` 를 제외하면 전부 0이다.
2. 메모리 포인터 `mp` 는 0 또는 1 증가한다.
3. 메모리 포인터가 그대로라면 `clk` 는 점프하고 메모리 값 `mv` 는 그대로 유지되어야 한다.
1. 1 -> 2 이렇게 clock cycle이 늘어나는 게 아니라 1 -> 5 이렇게 2이상 증가하는 경우에는 과거 `mp` 로 돌아가서 `mv` 도 같이 돌아오는 상황. 따라서 `mv` 가 직전 행의 값에서 변하면 안 된다.
문제는 clock cycle `clk`가 8에서 3으로 거꾸로 갔기 때문에 3번 제약을 지키더라도 미래의 `mv` 값을 과거의 `mv`에 넣을 수 있다.
### 정렬이 필요한 이유
Memory Table이 정렬되어 있다면 다음 두 가지 성질을 만족한다.
1. 같은 메모리 포인터에 대한 행들은 contiguous
2. 같은 메모리 포인터에 대한 행들은 clock cycle 오름차순으로 정렬되어 있다.
Memory Table 의 AIR constraints
- Boundary constraint
- 첫번째 행은 전부 0이다.
- Transition constraints
- `mv` 는 `clk`가 1 넘게 증가하는 행에서 변하지 않는다.
- `mp`가 변하면 다음 행의 `mv` 는 0이다.
- Transition constraints of the Processor Table
- `mp` 가 그대로이고 $ci \notin \{+, - \}$ 이라면 `mv`는 변하지 않는다.
### Solution
The transition constraints for the base table
- 메모리 포인터 칼럼은 0 또는 1 증가한다.
- `(mp' - mp) * (mp' - mp - 1)`
- 메모리 포인터가 같은 contiguous region에서, 클락 사이클 카운터는 1씩 증가한다.
- 더미 row로 가능해짐.
- `(mp' - mp - 1) * (clk' - clk - 1)`
- 메모리 포인터가 1 증가한다면 다음 메모리 값은 0이다.
- 해석: 새로운 메모리 포인터로 바뀔 때 mv는 최초에 0.
- `(mp' - mp) * mv'`
- The dummy next 는 0 또는 1이다.
- `d' * (d' - 1)`
- 더미 row이면 메모리 값은 변하지 않는다.
- `d * (mv' - mv)`
- 더미 row이면 메모리 포인터는 변하지 않는다.
- `d * (mp' - mp)`
The transition constraints for the extension column:
$$\mathsf{d} \cdot \mathsf{ppa} + (1 - \mathsf{d}) \cdot \mathsf{ppa} \cdot (d \cdot \mathsf{clk} + e \cdot \mathsf{mp} + f \cdot \mathsf{mv} - \beta) - \mathsf{ppa}^\star$$
해설
dummy row가 아닐 때만 permutation argument column에 곱해진다.
- d = 0 이면 $\mathsf{ppa} \cdot (d \cdot \mathsf{clk} + e \cdot \mathsf{mp} + f \cdot \mathsf{mv} - \beta) = \mathsf{ppa}^\star$
- d = 1 이면 $\mathsf{ppa} = \mathsf{ppa}^\star$
The terminal constraint
위의 transition constraint와 거의 같고 `ppa'` 만 $T_{ppa}$ 로 대체된다.
$$\mathsf{d} \cdot \mathsf{ppa} + (1 - \mathsf{d}) \cdot \mathsf{ppa} \cdot (d \cdot \mathsf{clk} + e \cdot \mathsf{mp} + f \cdot \mathsf{mv} - \beta) - T_{ppa}$$
## 참고
- [RISC Zero Developer Docs](https://dev.risczero.com/proof-system/stark-by-hand)
- [BrainSTARK, Part 0: Introduction | BrainSTARK](https://aszepieniec.github.io/stark-brainfuck/)
- 테이블 예시: [Brainfuck STARK Tutorial — Neptune](https://neptune.cash/learn/brainfuck-tutorial/)
- https://github.com/andrewmilson/ministark
- https://github.com/aszepieniec/stark-brainfuck