STARK 數學推導完整指南:從 FRI 協議到 STARK 系統的深度密碼學分析

本文深入分析 STARK(Scalable Transparent Argument of Knowledge)的數學推導基礎,特別是 FRI(Fast Reed-Solomon Interactive Oracle Proof of Proximity)協議的核心原理。從 Reed-Solomon 編碼、FRI 折疊操作到完整 STARK 系統架構,提供完整的數學推導與 Python 實現程式碼。

STARK 數學推導完整指南:從 FRI 協議到 STARK 系統的深度密碼學分析

前言

說實話,第一次接觸 STARK 的時候,我差點把手上的咖啡噴到筆電上。

這什麼鬼東西?低密度多項式?線性化誤差?迭加論證?每個名詞都認識,但串在一起就變成天書。後來我決定從頭開始,一個數學公式一個公式地啃,最後總算把整個系統的邏輯串起來了。

今天我想把這個學習過程記錄下來,用人話解釋 STARK 怎麼從一個複雜的數學問題,變成一個可以實際運作的零知識證明系統。如果你也被 STARK 的複雜度折磨過,這篇文就是為你寫的。

第一章:STARK vs SNARK 的核心差異

1.1 為什麼需要 STARK?

先說說背景。ZK-SNARK 很優雅,只需要幾百毫秒就能生成證明,驗證時間更是快到不行。但它有個致命的弱點:需要可信設置(Trusted Setup)

什麼是可信設置?想像一下,你要去銀行開戶,但銀行要求你必須相信某個「初始用戶」是好人,而且他已經把所有機密資訊處理乾淨了。這不是開玩笑嗎?

Groth16 需要用到那個「有毒廢料」(Powers of Tau),PLONK 也需要通用設置。如果你用的設置被人動過手腳,整個系統的安全性就報銷了。

STARK 就是要解決這個問題。STARK 的全名是 Scalable Transparent Argument of Knowledge,關鍵字是「Transparent」——透明。它不需要可信設置,任何人都可以驗證設置過程的公平性。

1.2 STARK 的三大優勢

SNARK 的問題:
- 需要可信設置,有「有毒廢料」風險
- 電路需要固定大小,不夠靈活
- 密碼學假設較強(配對假設)

STARK 的解決方案:
- 無需可信設置,數學上可證明透明性
- 可增量計算電路,支援大型計算
- 只依賴哈希假設(量子抗性!)
- 抗量子計算攻擊

代價是:STARK 的證明體積比 SNARK 大得多。大多少?可能差個 10-100 倍。但別忘了,ZK-SNARK 的設置可是有被污染的風險的。

第二章:FRI 協議——STARK 的核心

2.1 為什麼需要 FRI?

在解釋 FRI 之前,先說說 STARK 要證明的問題:低度測試(Low-Degree Testing, LDT)

假設你聲稱「這是一個度數 ≤ d 的多項式」,但其實是個度數高達 1000d 的多項式。怎麼驗證?直接把整個多項式拿出來檢查當然可以,但那樣就太慢了。

FRI 的全名是 Fast Reed-Solomon Interactive Oracle Proof of Proximity,它提供了一個聰明的方法:透過隨機採樣來機率性地驗證多項式的度數

2.2 Reed-Solomon 編碼

FRI 的第一步是把「原始多項式」編碼成更長的「Reed-Solomon 碼字」。

假設我們有一個定義在域 $\mathbb{F}$ 上的多項式 $f(X)$,度數為 $d$,我們要在 $n$ 個點上評估它,其中 $n > d$。

原始多項式:f(X),度數 d = 3
評估點集合:D = {x₀, x₁, x₂, ..., x_{n-1}},n = 8

Reed-Solomon 碼字:
C = (f(x₀), f(x₁), f(x₂), ..., f(x₇))

這樣做的意義是什麼?如果原始多項式真的是度數 ≤ d,那麼這個碼字有特殊的結構——任何偏離這個結構的「假的」多項式,都會在隨機採樣中被檢測出來。

2.3 度數測試的直覺

想像你在驗證一個人是否真的會彈鋼琴。你不可能讓他演奏完整首曲子(那太慢了),所以你說:「給我彈個C大調音階。」

如果他真的會彈鋼琴,音階對他來說輕而易舉。如果他根本不會,隨便給他個任務也會露餡。

FRI 的度數測試就是这个原理:透過隨機的「快速測試」,來機率性地驗證多項式的度數上限

2.4 FRI 的遞歸結構

FRI 的核心是這個優雅的遞歸過程:

第 0 層(原始層):
- 輸入:代數中層代碼(Algebraic Intermediate Representation, AIR)
- 多項式 f₀(X),度數 ≤ d₀

第 1 層:
- 對 f₀ 的評估值進行 Reed-Solomon 編碼
- 使用「折疊」操作得到 f₁(X)
- 新多項式 f₁(X) 的度數降至 ≤ d₀/2

第 2 層:
- 對 f₁ 的評估值進行 Reed-Solomon 編碼
- 使用「折疊」操作得到 f₂(X)
- 新多項式 f₂(X) 的度數降至 ≤ d₀/4

... 持續折疊直到度數變成常數

最後一層:
- fₖ(X) 的度數 ≤ 2(線性或二次)
- 直接驗證這個簡單的多項式

2.5 折疊操作的數學推導

這是最燒腦的部分。假設我們有一個度數 ≤ d 的多項式 $f(X)$,我們想把它「折疊」成一個度數 ≤ d/2 的多項式 $f'(X)$。

選擇兩個評估點

首先,Prover 和 Verifier 協定選擇兩個點 $\alpha$ 和 $-\alpha$。注意這兩個點關於原點對稱。

定義折疊函數

對於每個同餘類 $[x] = \{x, -x\}$,我們把 $(f(x), f(-x))$ 折疊成一個新的值:

$$

f'(x^2) = \frac{f(x) + f(-x)}{2} + \alpha \cdot \frac{f(x) - f(-x)}{2x}

$$

這個公式看起來很複雜,讓我拆解一下:

為什麼這個折疊是正確的?

假設 $f(X)$ 是個度數 ≤ d 的多項式。把 $f(X)$ 分解為偶數部分和奇數部分:

$$

f(X) = f{even}(X^2) + X \cdot f{odd}(X^2)

$$

其中:

代入折疊公式:

$$

f'(x^2) = f{even}(x^2) + \alpha \cdot f{odd}(x^2)

$$

這是一個關於 $Y = x^2$ 的多項式,其度數上限是 max(deg($f{even}$), deg($f{odd}$)) ≤ d/2。成功了!

2.6 Python 實現折疊操作

from finite_field import FiniteField

def fri_fold(f_values: list, alpha: FiniteField, points: list):
    """
    FRI 折疊操作
    
    Args:
        f_values: f 在點集合 [x, -x, x', -x', ...] 上的評估值
        alpha: 隨機挑戰係數
        points: 對應的 x 值(假設按對 [x, -x] 排列)
    
    Returns:
        折疊後的新評估值列表
    """
    n = len(f_values)
    assert n % 2 == 0, "f_values 數量必須為偶數"
    
    folded_values = []
    
    for i in range(0, n, 2):
        x = points[i]
        f_x = f_values[i]
        f_neg_x = f_values[i + 1]
        
        # 折疊公式
        # f'(x^2) = (f(x) + f(-x))/2 + alpha * (f(x) - f(-x))/(2x)
        even_part = (f_x + f_neg_x) * x.inverse() * FiniteField(2).inverse()
        odd_part = (f_x - f_neg_x) * x.inverse() * alpha * FiniteField(2).inverse()
        
        folded_value = even_part + odd_part
        folded_values.append(folded_value)
    
    return folded_values

def fri_commit_phase(prover_secret: list, degree_bound: int, 
                     expansion_factor: int = 4):
    """
    FRI 提交階段
    
    Args:
        prover_secret: Prover 知道的多項式係數
        degree_bound: 聲稱的度數上限 d
        expansion_factor: 擴展因子(通常為 4 或 8)
    
    Returns:
        代碼字的層級列表
    """
    domain_size = expansion_factor * degree_bound
    
    # 在指定大小的域上評估多項式
    # 選擇 2^k 個點,便於二分
    domain_size = 1 << (domain_size - 1).bit_length()
    
    # 構造評估域
    omega = FiniteField.generator()  # 群的生成元
    g = omega ** ((FiniteField.modulus - 1) // domain_size)
    domain = [g ** i for i in range(domain_size)]
    
    # 評估原始多項式
    f_values = [evaluate_polynomial(prover_secret, x) for x in domain]
    
    # 提交代碼字
    layers = [f_values]
    current_degree = degree_bound
    
    alpha = FiniteField.random()  # 初始化隨機種子
    
    while current_degree > 2:
        # 隨機挑戰
        alpha = FiniteField.hash_to_field(str(alpha) + str(layers[-1]))
        
        # 折疊
        folded = fri_fold(layers[-1], alpha, domain)
        layers.append(folded)
        
        # 更新域(每折疊一次,域大小減半)
        domain = domain[:len(domain)//2]
        current_degree = (current_degree + 1) // 2
    
    return layers

2.7 折疊過程的可視化

讓我用一個具體例子展示整個過程:

假設原始多項式:f₀(X) = 1 + 2X + 3X² + 4X³
度數:d₀ = 3

展開係數:4(每層的代碼字大小是度數的 4 倍)

第 0 層(度數 3):
┌────────────────────────────────────────┐
│ f₀(x₀)  f₀(x₁)  f₀(x₂)  f₀(x₃)  ...  │
│ f₀(-x₀) f₀(-x₁) f₀(-x₂) f₀(-x₃) ...  │
└────────────────────────────────────────┘
                    ↓ 折疊
第 1 層(度數 ≤ 1):
┌────────────────────────────────────────┐
│ f₁(x₀²)  f₁(x₁²)  f₁(x₂²)  ...      │
└────────────────────────────────────────┘
                    ↓ 折疊
第 2 層(度數 ≤ 0):
┌────────────────────────────────────────┐
│ f₂(x₀⁴)  f₂(x₁⁴)  ...               │
└────────────────────────────────────────┘

2.8 錯誤分析

FRI 的安全性分析基於以下觀察:

如果多項式真的是低度的

如果原始多項式的度數 ≤ d,經過 k 層折疊後,Prover 總是能給出與 Verifier 的隨機種子一致的代碼字。Verifier 會接受。

如果多項式是高度的呢?

假設原始多項式的度數是 d + ε(比聲稱的要高)。在折疊過程中:

經過 k 層折疊後,高度多項式能堅持到底的機率只有 $(1/2)^k$。

這個 $(1/2)^k$ 的失敗率就是 FRI 的折線距離(Fold Distance)——真低度代碼和假高自由度代碼之間的距離。

第三章:STARK 的完整系統架構

3.1 STARK 的五大步驟

STARK 證明系統的流程可以分為五個主要步驟:

┌─────────────────────────────────────────────────────────────────┐
│                    STARK 證明系統流程                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 算術化(Arithmetization)                                   │
│     ↓                                                           │
│  2. 低度測試(Low-Degree Testing)——FRI                         │
│     ↓                                                           │
│  3. 線性化與多項式協議(Linearization & Polynomial IOP)        │
│     ↓                                                           │
│  4. 在線多項式抽樣(Online Polynomial Sampling)                │
│     ↓                                                           │
│  5. 最終抽樣與承諾(Mercury)                                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

3.2 第一步:算術化

算術化是將計算問題轉換為多項式約束問題的過程。

假設我們要證明「我知道 $x$ 使得 $f(x) = y$」,其中 $f$ 是某個已知的算術電路。

約束系統表示

把電路中的每條線表示為一個變量 $w1, w2, ..., w_n$。每個乘法閘和加法閘產生一個約束:

加法約束:w_out = w_in1 + w_in2
乘法約束:w_out = w_in1 × w_in2

把這些約束轉換為一個多項式約束:

$$

qL(X) \cdot WL(X) + qR(X) \cdot WR(X) + qO(X) \cdot WO(X) + qM(X) \cdot WL(X) \cdot WR(X) + qC(X) = 0

$$

其中:

3.3 第二步:FRI 協議

這就是第二章詳細解釋的內容。Verifier 需要確認這些多項式的度數確實 ≤ d。

3.4 第三步:線性化

當 Prover 發送一個多項式 $f(X)$ 時,Verifier 不能直接檢查整個多項式。STARK 採用「隨機抽樣」的方法:

  1. Prover 發送多項式的 commitments(承諾)
  2. Verifier 發送隨機挑戰 $\alpha$
  3. Prover 根據 $\alpha$ 計算新多項式
  4. 重複直到足夠安全

3.5 第四步:在線抽樣

當 Prover 已經承諾了多項式之後,Verifier 需要「強制」Prover 在特定的隨機點上打開這些多項式。

具體來說:

3.6 第五步:Mercury 轉換

Mercury 是把「多項式協議」轉換成「字面協議」的技術。

簡單來說,Mercury 把「我承諾了一個低度多項式」這個陳述,轉換成「這串數字是某個低度多項式在這些點上的評估值」這個可驗證的陳述。

第四章:完整程式碼實現

4.1 有限域實現

STARK 需要在大質數域上運算。讓我先實現一個簡化的有限域類:

class FiniteField:
    """簡化的有限域實現"""
    
    MODULUS = 2**128 - 2**97 + 1  # Goldilocks 質數
    
    def __init__(self, value):
        self.value = value % self.MODULUS
    
    @staticmethod
    def generator():
        """Goldilocks 域的生成元"""
        return FiniteField(7)
    
    def __add__(self, other):
        return FiniteField(self.value + other.value)
    
    def __sub__(self, other):
        return FiniteField(self.value - other.value)
    
    def __mul__(self, other):
        return FiniteField(self.value * other.value)
    
    def inverse(self):
        """模逆元"""
        return FiniteField(pow(self.value, -1, self.MODULUS))
    
    def __pow__(self, exponent):
        return FiniteField(pow(self.value, exponent, self.MODULUS))
    
    def __eq__(self, other):
        return self.value == other.value
    
    def __repr__(self):
        return f"FF({self.value})"

4.2 STARK 驗證器實現

class STARKVerifier:
    """STARK 驗證器核心邏輯"""
    
    def __init__(self, security_level: int = 128):
        self.security_level = security_level
        self.Queries = 40  # 隨機查詢次數
    
    def verify_fri(self, commitments: list, proofs: list, 
                   degree_bound: int, alpha: FiniteField) -> bool:
        """
        驗證 FRI 證明
        
        Args:
            commitments: 每層的承諾列表
            proofs: 每層的折疊值列表
            degree_bound: 聲稱的度數上限
            alpha: 隨機挑戰
        
        Returns:
            True 如果驗證通過
        """
        current_bound = degree_bound
        
        for i, (commitment, proof) in enumerate(zip(commitments, proofs)):
            # 檢查承諾的長度是否正確
            expected_length = current_bound * 4  # expansion factor = 4
            if len(commitment) != expected_length:
                return False
            
            # 檢查折疊的一致性
            # 在實際實現中,這裡會涉及配對檢查或其他密碼學驗證
            for j in range(0, len(commitment), 2):
                x = commitment[j]
                f_x = proof[j // 2]
                # 驗證折疊公式
                # f'(x^2) = (f(x) + f(-x))/2 + alpha * (f(x) - f(-x))/(2x)
                # 這裡簡化了,實際需要完整實現
                pass
            
            # 更新度數上限
            current_bound = (current_bound + 1) // 2
            
            # 如果已經到了常數層,直接驗證
            if current_bound <= 2:
                return self.verify_final_layer(proof)
        
        return False
    
    def verify_final_layer(self, final_values: list) -> bool:
        """驗證 FRI 的最後一層(度數 ≤ 2)"""
        # 最後一層應該只有幾個值
        # 直接檢查它們是否構成一個低次多項式
        # 這裡使用 Vandermonde 矩陣求解
        return len(final_values) <= 4
    
    def sample_random_points(self, num_queries: int, 
                             domain_size: int) -> list:
        """採樣隨機查詢點"""
        import random
        return [random.randint(0, domain_size - 1) 
                for _ in range(num_queries)]

4.3 STARK 證明器實現

class STARKProver:
    """STARK 證明器核心邏輯"""
    
    def __init__(self, security_level: int = 128):
        self.security_level = security_level
        self.expansion_factor = 4
    
    def prove(self, witness: list, constraints: list) -> dict:
        """
        生成 STARK 證明
        
        Args:
            witness: Prover 知道的所有私有值
            constraints: 電路的約束列表
        
        Returns:
            完整的 STARK 證明
        """
        proof = {
            'fri_layers': [],
            'polynomial_queries': [],
            'decommitments': []
        }
        
        # 步驟 1:構造 trace 多項式
        trace_polynomial = self.construct_trace_polynomial(witness)
        
        # 步驟 2:構造約束多項式
        constraint_polynomial = self.construct_constraint_polynomial(
            trace_polynomial, constraints
        )
        
        # 步驟 3:運行 FRI 協議
        fri_proof = self.run_fri_protocol(constraint_polynomial)
        proof['fri_layers'] = fri_proof
        
        # 步驟 4:生成隨機查詢點的 decommitment
        queries = self.sample_random_queries()
        proof['decommitment'] = self.decommit(trace_polynomial, queries)
        
        return proof
    
    def construct_trace_polynomial(self, witness: list) -> list:
        """構造 trace 多項式"""
        # 使用拉格朗日插值構造多項式
        # 使得多項式在特定點上等於 witness 的值
        n = len(witness)
        domain = [FiniteField.generator() ** i for i in range(n)]
        return lagrange_interpolate(witness, domain)
    
    def run_fri_protocol(self, polynomial: list) -> list:
        """運行完整的 FRI 協議"""
        fri_layers = []
        current_poly = polynomial
        degree = len(polynomial) - 1
        
        while degree > 2:
            # 承諾當前層
            commitment = self.commit_layer(current_poly)
            fri_layers.append(commitment)
            
            # 接收 Verifier 的挑戰
            alpha = FiniteField.random()
            
            # 折疊
            current_poly = self.fold_polynomial(current_poly, alpha)
            degree = (degree + 1) // 2
        
        # 添加最後一層
        fri_layers.append(current_poly)
        
        return fri_layers
    
    def fold_polynomial(self, values: list, alpha: FiniteField) -> list:
        """折疊多項式"""
        n = len(values)
        assert n % 2 == 0
        
        folded = []
        for i in range(0, n, 2):
            # f'(x^2) = (f(x) + f(-x))/2 + alpha * (f(x) - f(-x))/(2x)
            f_x = values[i]
            f_neg_x = values[i + 1]
            
            # 計算偶數和奇數部分
            even = (f_x + f_neg_x) * FiniteField(2).inverse()
            odd = (f_x - f_neg_x) * FiniteField(2).inverse() * alpha
            
            folded.append(even + odd)
        
        return folded

第五章:安全性分析

5.1 資訊理論安全性

STARK 的核心安全保證是資訊理論的(Information-Theoretic)。這意味著即使 Prover 有無限的計算能力,也無法欺騙 Verifier。

為什麼?因為 Verifier 的挑戰是隨機選擇的,而且只有一個「正確」的響應能通過驗證。Prover 猜對這個隨機挑戰的機率是完全可以忽略的。

5.2 具體的安全參數

假設我們想要 128 位的安全性:

FRI 層數:log₂(degree_bound) ≈ 30 層(假設度數上限 = 2³⁰)
查詢次數:40 次
每層的折線距離:1/2

總的失敗機率:(1/2)³⁰ × (1/4⁰) ≈ 2⁻⁵⁰

這個失敗機率遠低於 2⁻¹²⁸ 的目標。

5.3 量子抗性

STARK 只依賴於兩個密碼學假設:

  1. 哈希函數的抗碰撞性
  2. Fréchet 不等式(關於 Reed-Solomon 碼字的距離)

這兩個假設都被認為是量子抗性的。相比之下,ZK-SNARK 依賴的配對假設在量子電腦面前可能不再安全。

SNARK vs STARK 密碼學假設比較:

ZK-SNARK(以 Groth16 為例):
- 配對假設(Bilinear Diffie-Hellman)
- 指數知識假設(Knowledge of Exponent)
- 量子脆弱性:✓(Shor's 算法可破解)

STARK:
- 哈希函數抗碰撞
- Reed-Solomon 碼字距離
- 量子抗性:✓(Grover 算法只能提供二次加速)

第六章:實際應用場景

6.1 zkRollup 中的應用

STARK 最著名的應用是 StarkExStarkNet

StarkEx 是 StarkWare 開發的 Layer 2 引擎,用於交易和支付。目前已經處理了數萬億美元的交易量。

StarkNet 是完全去中心化的 Layer 2 網路,使用 Cairo 語言編寫智能合約。

6.2 為什麼 StarkNet 選擇 STARK?

StarkNet 選擇 STARK 而不是 SNARK 有幾個原因:

1. 無需可信設置
   - StarkNet 是完全去中心化的
   - 如果用 SNARK,就需要某種形式的信任假設

2. 電路靈活性
   - STARK 支援動態電路大小
   - 適合通用智能合約執行環境

3. 量子抗性
   - 長期來看,STARK 的密碼學假設更安全

6.3 效能對比

讓我用一個簡化的例子來比較:

假設要證明一筆涉及 1000 個 DeFi 操作的交易:

指標Groth16 (SNARK)STARK
證明大小~200 bytes~45 KB
驗證時間~3 ms~10 ms
證明時間~2 s~30 s
可信設置需要不需要
量子抗性

這個對比展示了 STARK 的取捨:更長的驗證時間和更大的證明體積,換取更好的安全屬性

結語

STARK 的數學很漂亮,但坦白說,實現一個真正的 STARK 系統是極其複雜的。

幸好現在有很多開源庫可以直接用:

如果你想深入研究,我建議:

  1. 先從理論入手,把 FRI 的數學推導搞清楚
  2. 實際跑一下現有的 STARK 庫
  3. 理解「為什麼」比「怎麼做」更重要

這條路不好走,但我覺得值得。理解 STARK 的過程,其實就是在理解密碼學和計算複雜性理論最前沿的知識。


參考文獻

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!