文章

11. Plonky2.5 跨系统递归桥:在 Plonky2 中验证旧版 Plonky3 STARK

11. Plonky2.5 跨系统递归桥:在 Plonky2 中验证旧版 Plonky3 STARK

重要边界:这里的 Plonky2.5 是一个具体的第三方 2024 proof-of-concept,不是 Polygon/Plonky3 官方定义的通用“第 2.5 代协议”,也不自动兼容 2026 年当前 Plonky3 proof format。

1. 名称与证据链

QED Protocol 仓库对项目的原话定位是:

verify Plonky3 STARK proofs in a Plonky2 SNARK

其拓扑是:

\[\boxed{ \pi_3 \xrightarrow{ \text{Plonky3 verifier encoded in circuit} } \pi_{2.5} \equiv \pi_2. }\]
  • $\pi_3$:一份特定旧版 Plonky3 Uni-STARK proof;
  • 中间箭头:Plonky2 circuit 内的 Plonky3 verifier;
  • $\pi_2$:证明这个 verifier circuit 满足的 Plonky2 proof。

“2.5”没有引入新的:

  • 有限域;
  • 多项式承诺定理;
  • AIR 语言;
  • PLONK permutation argument。

它引入的是一套 cross-verifier gadgets

flowchart TB
    T["Plonky3 execution trace"] --> P3["Plonky3 STARK prover"]
    P3 --> PI3["Inner proof π_3"]
    PI3 --> V25["Plonky3 verifier<br/>用 Plonky2 targets 重写"]
    AIR["同一 AIR 约束的<br/>Plonky2 版本"] --> V25
    V25 --> C2["Plonky2 verifier circuit"]
    C2 --> P2["Outer Plonky2 proof π_2"]

2. 快照中固定的技术栈

本文核对的仓库快照为:

\[\texttt{0f4e51bd601279b6f77a35f0fe817e7dbc21ccea},\]

最后提交日期为 2024-03-28,crate version 为 0.1.0

该快照固定或明显假定:

选择
inner arithmetization旧版 Plonky3 Uni-STARK / AIR
inner base fieldGoldilocks
inner challenge extensiondegree 2 binomial extension
inner PCStwo-adic FRI PCS
inner MMCSPoseidon2 Merkle tree
Poseidon2 state width12
digest elements4
challenger overwrite width / duplex threshold12 field elements
MMCS leaf-hash input chunk4 field elements
FRI foldingbinary
demo blowup$2^1=2$
demo queries100
demo query PoW16 bits
outer systempinned 2024-era Plonky2 revision
outer circuit configstandard_recursion_config

这不是“Plonky3 所有组合的通用 verifier”。例如它没有在同一个抽象接口中覆盖:

  • Mersenne31 + Circle PCS;
  • BabyBear/KoalaBear degree-4 extensions;
  • 当前高元 FRI;
  • current batch-STARK / LogUp;
  • WHIR;
  • current ZK proof layout。

3. 为什么这座桥在数学上可行

3.1 基础域相同

两边都用 Goldilocks:

\[p=2^{64}-2^{32}+1.\]

因此 inner proof 中一个基础域元素 $a$ 可以直接成为 outer circuit 的一个 Target

\[a_{\mathrm{P3}} \longmapsto t_a\in\mathbb F_p.\]

不需要 limb decomposition,也不需要证明 $a$ 是另一个大素数域中的 canonical residue。

3.2 扩域次数也对齐

快照设

\[\texttt{EXT\_DEGREE}=2.\]

把 inner challenge

\[u=u_0+u_1\theta\]

表示为两个 Plonky2 targets:

\[\operatorname{ExtTarget}(u) =(t_{u_0},t_{u_1}).\]

outer circuit 提供:

  • extension add/sub;
  • extension mul;
  • inverse/division;
  • Frobenius;
  • two-adic generators。

3.3 最大的不兼容是 hash

Plonky2 原生递归主线使用 Poseidon;目标旧版 Plonky3 proof 使用 Poseidon2。

因此桥必须在 Plonky2 中新增:

  • Goldilocks Poseidon2 permutation;
  • Poseidon2Gate
  • Poseidon2 Merkle compression;
  • Poseidon2 duplex challenger。

如果只做到“域相同”,但 transcript hash 不同,验证者采样的

\[\alpha,\zeta,\beta_i,q_j\]

都会不同,proof 必然无法验证。

3.4 AIR 与 PLONKish 也不相同

inner statement 用 AIR:

\[C_i( \mathbf t_r, \mathbf t_{r+1}, \text{selectors} )=0.\]

outer proof system 用 PLONKish custom gates。桥的工作是:

\[\text{AIR verifier arithmetic} \longrightarrow \text{Plonky2 gate constraints}.\]

它并不把整个 inner trace 重新嵌入 outer circuit;只嵌入 inner verifier 对少量 OOD openings 的检查

4. proof 作为 private witness

桥先把 JSON 反序列化成 inner proof 数据,再根据它构造 outer circuit shape。只有 proof 的域元素字段被分配为 virtual targets;degree_bits 是 host 侧 usize

\[\pi_3 = ( \mathsf{Commitments}, \mathsf{OpenedValues}, \mathsf{OpeningProof}; \mathsf{degreeBits}_{\mathrm{host}} ).\]

固定源码中 Proof<Target>::add_virtual_to 直接执行

1
let degree_bits = config.degree_bits;

set_witness 不为它设置任何 target。因此 degree_bits 不是 prover 在既定 circuit 中可选择的 private witness;它在 build time 决定 domain 规模及相关 allocation shape,并最终被 outer circuit digest/verifier data 固定。部署时仍应由 verifier policy 选择或 allowlist 该 circuit identity,不能让任意输入 proof 为线上验证者临时生成新 shape。

4.1 commitments

快照 proof 有:

\[C_T \quad\text{和}\quad C_Q,\]

分别是:

  • trace commitment;
  • quotient chunks commitment。

每个 commitment 是 4 个 Goldilocks digest elements。

4.2 opened values

包含:

\[\begin{aligned} \mathbf t(\zeta)&=\texttt{trace\_local},\\ \mathbf t(\zeta_{\mathrm{next}}) &=\texttt{trace\_next},\\ \mathbf q_j(\zeta) &=\texttt{quotient\_chunks}[j]. \end{aligned}\]

4.3 opening proof

包含:

  • 每个 batch/query 的 opened rows;
  • MMCS authentication paths;
  • FRI commit-phase commitments;
  • 每轮 sibling value/path;
  • 名为 final_poly 的 final constant extension value;
  • PoW witness。

除 host 侧 degree_bits 外,上述 proof targets 默认是 outer private witness。若需要向外界声明 inner public values、trace length 或 AIR identity,必须另行注册并约束 outer public inputs。

5. AIR 必须手工重写一遍

5.1 inner Fibonacci AIR

示例 trace 每行有三列:

\[(a_r,b_r,c_r).\]

约束为:

\[\begin{aligned} a_r+b_r-c_r&=0,\\ a_0-1&=0,\\ b_0-1&=0,\\ a_{r+1}-b_r&=0,\\ b_{r+1}-c_r&=0. \end{aligned}\]

最后两条只在 transition rows 上启用。

5.2 Plonky3 写法

native AIR builder 提供:

  • main.current/next
  • when_first_row
  • when_transition
  • assert_zero/assert_eq

5.3 Plonky2.5 写法

桥定义自己的 Air trait:

1
2
3
4
5
6
7
8
9
trait Air {
    fn name(&self) -> String;
    fn width(&self) -> usize;
    fn eval(
        &self,
        folder: &mut VerifierConstraintFolder<Target>,
        cb: &mut CircuitBuilder,
    );
}

同一组公式必须改写成 extension-target operations:

\[\operatorname{p3\_ext\_add}(a,b)=c,\]

以及 filtered assertions:

\[\begin{aligned} s_{\mathrm{first}}(\zeta)(a(\zeta)-1)&=0,\\ s_{\mathrm{trans}}(\zeta) \bigl(a(\zeta_{\mathrm{next}})-b(\zeta)\bigr)&=0. \end{aligned}\]

5.4 双份 AIR 是主要维护风险

桥没有自动把任意 Plonky3 Air<AB> 编译成 Plonky2 circuit。开发者维护:

  1. native prover 使用的 AIR;
  2. recursive verifier 使用的 AIR 重写。

若两份有任何差异:

\[\mathcal R_{\mathrm{native}} \ne \mathcal R_{\mathrm{bridge}},\]

outer proof 证明的就不是原应用以为的 relation。

审计时至少要做:

  • 约束逐条映射;
  • selector 语义比对;
  • 列下标/布局比对;
  • public-value schema 比对;
  • property tests 与差分测试;
  • 对故意破坏每条约束的 negative tests。

6. 约束随机折叠

桥中的 VerifierConstraintFolder 维护 accumulator:

\[A_0=0, \qquad A_{i+1}=\alpha A_i+c_i.\]

最后

\[A_m = \alpha^{m-1}c_0+\alpha^{m-2}c_1+\cdots+c_{m-1}.\]

源码的 assert_zero(x) 实质是:

\[\texttt{accumulator} \leftarrow \texttt{accumulator}\cdot\alpha+x.\]

filtered assertion 则先乘 selector:

\[x\leftarrow s(\zeta)x.\]

只要 native prover 与 bridge 对 constraints 使用完全相同的次序,Horner 方向不同于

\[\sum_i\alpha^ic_i\]

也没有问题;问题在于两端顺序必须一致。

7. trace domain 与三个 selectors

设 inner trace domain 为

\[H=g\langle\omega\rangle, \qquad |H|=N.\]

在 OOD point $\zeta$,verifier 需要:

\[s_{\mathrm{first}}(\zeta),\quad s_{\mathrm{last}}(\zeta),\quad s_{\mathrm{transition}}(\zeta),\quad \widehat Z_H(\zeta)^{-1}.\]

固定 QED 源码返回的 first/last 值不是归一化后等于 1 的 Lagrange basis,而是 未归一化的 vanishing filters。令

\[U=\frac{X}{g}, \qquad \widehat Z_H(X)=U^N-1 =g^{-N}(X^N-g^N).\]

这里 $\widehat Z_H$ 是源码在 normalized coordinate $U$ 中使用的 zeroifier;它与通常写作 $Z_H(X)=X^N-g^N$ 的 domain zeroifier 只差非零常数 $g^{-N}$。源码计算的是

\[\boxed{ \begin{aligned} \widetilde s_{\mathrm{first}}(X) &=\frac{\widehat Z_H(X)}{U-1},\\ \widetilde s_{\mathrm{last}}(X) &=\frac{\widehat Z_H(X)}{U-\omega^{-1}},\\ \widetilde s_{\mathrm{transition}}(X) &=U-\omega^{-1},\\ \mathsf{inv\_zeroifier}(X) &=\widehat Z_H(X)^{-1}. \end{aligned} }\]

所以 first/last filters 在非目标 trace rows 为 0、在目标行只保证非零,并不保证值为 1。对应的 normalized Lagrange polynomials 才是

\[L_{\mathrm{first}}(X) =\frac{\widetilde s_{\mathrm{first}}(X)}{N}, \qquad L_{\mathrm{last}}(X) =\frac{\widetilde s_{\mathrm{last}}(X)}{N\omega}.\]

transition filter 在最后一行 $U=\omega^{-1}$ 时为 0,在其他 trace rows 非零。AIR prover 和 bridge verifier 都必须采用同一套未归一化尺度;不能单方面替换成 normalized Lagrange basis,而不同时调整 quotient identity。

bridge 用 domain object 在 circuit 中计算这些 extension targets,而不是让 prover 任意提供 selectors。

8. quotient identity 的核心检查

inner prover 构造折叠约束 polynomial:

\[C_\alpha(X)\]

并令

\[Q(X) = \frac{C_\alpha(X)}{\widehat Z_H(X)}.\]

bridge 在 $\zeta$ 上计算:

\[C_\alpha(\zeta) \cdot \widehat Z_H(\zeta)^{-1}\]

并连接到重组的 quotient:

\[\boxed{ C_\alpha(\zeta)\widehat Z_H(\zeta)^{-1} = Q(\zeta). }\]

源码对应:

1
2
3
folded_constraints
    × selectors.inv_zeroifier
    == recomposed_quotient

这一步只在 PCS 已经证明:

  • trace openings 属于 $C_T$;
  • quotient openings 属于 $C_Q$;
  • 相关 codewords 低度

之后才有意义。

9. quotient chunks 如何重组

设 quotient 被拆到互不相交的 domains:

\[D_0,\ldots,D_{k-1}.\]

令每个 domain 的 vanishing polynomial 为

\[Z_{D_j}(X).\]

对第 $i$ 个 chunk 定义基函数

\[\Lambda_i(X) = \prod_{j\ne i} \frac{ Z_{D_j}(X) }{ Z_{D_j}(x_i) },\]

其中 $x_i$ 是 $D_i$ 的一个固定首点。

它满足:

  • 在 $D_i$ 上按构造归一;
  • 在其他 $D_j$ 上因 $Z_{D_j}=0$ 而消失。

于是

\[Q(\zeta) = \sum_{i=0}^{k-1} \Lambda_i(\zeta)q_i(\zeta).\]

bridge 源码先计算每个 zps[i],再把每个 chunk 的 extension-basis coefficients 重组并求和。

flowchart LR
    Q0["q_0(ζ)"] --> L0["乘 Λ_0(ζ)"]
    Q1["q_1(ζ)"] --> L1["乘 Λ_1(ζ)"]
    QK["q_k(ζ)"] --> LK["乘 Λ_k(ζ)"]
    L0 --> S["求和"]
    L1 --> S
    LK --> S
    S --> Q["Q(ζ)"]

10. Poseidon2 duplex challenger

10.1 固定状态参数

challenger 使用 width-12 overwrite duplex;其触发阈值也是 12:

\[\texttt{WIDTH}=12, \qquad |\texttt{input\_buffer}|=12 \Longrightarrow \mathsf{duplex}.\]

challenger 维护:

  • sponge state;
  • input buffer;
  • output buffer。

observe 每次把一个 field element 追加到 input buffer,并清空旧 output buffer。duplex 把现有输入覆盖到 state 的前缀,然后执行:

\[\begin{aligned} s_i&\leftarrow b_i && (0\le i<|b|),\\ \mathbf s &\leftarrow \operatorname{Poseidon2}(\mathbf s). \end{aligned}\]

若采样时仍有未处理输入,哪怕不足 12 个,也先执行一次 duplex;只有“输入为空且 output buffer 非空”时才直接 squeeze。固定实现随后把完整 12-element state 放入 output buffer,并以 pop() 顺序取样。overwrite 位置、squeeze 顺序和 partial-buffer 规则都是 transcript 语义。

这里没有 challenger 的 RATE=4。快照常量 RATE=4 只用于 MerkleTreeMmcs::hash_iter_slices:MMCS 把展平后的 leaf input 每 4 个 field elements 分一块,覆盖 state 前缀并做 Poseidon2 permutation。必须区分

\[\boxed{ \text{challenger duplex threshold}=12 \ne \text{MMCS leaf-hash chunk}=4. }\]

10.2 extension challenge

degree-2 challenge 由两个采样值组成:

\[\alpha = \alpha_0+\alpha_1\theta.\]

10.3 bit challenge

FRI query index 需要 $h$ bits:

\[q\in[0,2^h).\]

bridge 从 challenger sample 派生低 bits,并在 circuit 中使用 u32/bit gadgets 做:

  • shift;
  • xor;
  • and;
  • bit reversal。

这些不是 Rust host integer operations,而必须有对应 constraints;否则 prover 可任意选择 query routing。

11. 为什么必须实现 Poseidon2 custom gate

一份 inner proof 中 Poseidon2 至少用于:

  • trace/quotient MMCS;
  • FRI layer MMCS;
  • challenger。

bridge 的 Poseidon2Gate 在一行 Plonky2 gate 中表达完整 width-12 permutation。

其约束大致覆盖:

\[\begin{aligned} \mathbf s&\leftarrow M_E\mathbf s,\\ \mathbf s&\leftarrow (\mathbf s+\mathbf c)^{\circ d} \quad\text{on full rounds},\\ s_0&\leftarrow(s_0+c)^d \quad\text{on partial rounds},\\ \mathbf s&\leftarrow M_I\mathbf s. \end{aligned}\]

快照常量文件显示 8 组 full-round constants 和 22 个 partial-round constants,state width 12。桥必须和 inner proof 生成端使用完全相同的:

  • round constants;
  • S-box exponent;
  • external/internal matrices;
  • sponge absorb/squeeze convention;
  • digest truncation。

“两边都叫 Poseidon2”不构成兼容性证明。

12. MMCS:一次承诺多个不同高度矩阵

12.1 Mixed Matrix Commitment Scheme

旧版 Plonky3 的 MMCS 不只承诺一棵“单向量 Merkle tree”,而可批量承诺矩阵:

\[M_0,\ldots,M_{s-1},\]

其中

\[\operatorname{height}(M_i)\]

可以不同。

选择相对于最大高度的 global query index

\[q\in[0,2^{h_{\max}}),\]

对高度 $2^{h_i}$ 的矩阵使用:

\[q_i = q\gg(h_{\max}-h_i).\]

也就是丢掉一部分低位,把同一随机 query 映射到较小矩阵。

12.2 bridge 中的 shape

p3_verify_batch 接收:

  • commitment;
  • verifier-known dimensions;
  • global index;
  • 每个 matrix 的 opened row;
  • Merkle proof。

它先把 extension-valued matrix width 展开成基础域 width:

\[w_{\mathrm{base}} = D\cdot w_{\mathrm{ext}}, \qquad D=2.\]

然后调用 Poseidon2 Merkle verifier。

该 verifier 的 leaf hashing 把展平输入按 4 个基础域元素分块;这就是快照 RATE=4 的用途。它不是上一节 challenger 的吸收阈值。binary Merkle compression 则把左右两个 4-element digests 放入 state 的前 8 个位置再置换。

12.3 width 必须由 verifier 知道

若 leaf hash 只是把多行/多矩阵 elements 平铺,digest 本身不说明边界:

\[(a,b)\parallel(c) \quad\text{和}\quad (a)\parallel(b,c)\]

可产生同一个 field-element stream。

因此 dimensions/width 不能由不可信 proof 自报后直接接受。桥的旧实现有若干 “todo: MMCS doesn’t really need width” 风格假设;移植到当前实现时必须采用 verifier-known shape 并做严格长度检查。

13. batch PCS opening 如何压成每个高度一个值

13.1 query 点

对 log-height 为 $h$ 的矩阵,bridge 从 query index 得到 domain point。

若 domain generator 为 $\omega_h$、coset shift 为 $g$,并采用 bit-reversed leaf order,则:

\[x = g\, \omega_h^{ \operatorname{rev}_h(q_h) }.\]

这里的 bit reversal 必须和 inner LDE/Merkle leaf order 完全一致。

13.2 opening quotient

对声称

\[p(z)=v_z\]

及 query 打开的

\[p(x)=v_x,\]

计算

\[d_{p,z}(x) = \frac{v_x-v_z}{x-z}.\]

若 $v_z=p(z)$,它等于多项式

\[\frac{p(X)-p(z)}{X-z}\]

在 $x$ 处的值。

13.3 按高度分别随机合并

同一 FRI instance 可能包含不同高度的 matrices。bridge 为每个 log-height $h$ 维护:

\[R_h = \sum_{(p,z)\in\mathcal B_h} \rho^{j(p,z)} \frac{p(x_h)-p(z)}{x_h-z}.\]

源码用长度 32 的数组按 log_height 存放这些 reduced openings:

\[\texttt{ro}[h]=R_h.\]

这揭示了一个明确的 prototype 上限/假设:相关 log-height 被硬编码在 32 个槽位内。

13.4 为什么不同高度不能先盲目相加

高度不同意味着:

  • evaluation domain 不同;
  • query point $x_h$ 不同;
  • FRI 注入的轮次不同。

所以 $R_h$ 要在 FRI fold chain 到达相应高度时才加入:

\[y_{\mathrm{folded}} \leftarrow y_{\mathrm{folded}}+R_h.\]

14. 二元 FRI verifier 的电路公式

14.1 挑出 sibling

当前 index 为 $q$,其 sibling 为

\[q_{\mathrm{sib}}=q\oplus1,\]

parent index 为

\[q'=q\gg1.\]

proof 给出 sibling evaluation $y_{\mathrm{sib}}$,当前 fold value 为 $y$。根据最低 bit 排成:

\[(y_0,y_1) = \begin{cases} (y,y_{\mathrm{sib}}),&q\bmod2=0,\\ (y_{\mathrm{sib}},y),&q\bmod2=1. \end{cases}\]

14.2 认证二元 leaf

bridge 验证 commitment 中 parent index $q’$ 打开的 leaf 正好包含:

\[(y_0,y_1).\]

每个 extension value 展开为两个基础域 elements,所以 leaf base width 是 4。

14.3 两点插值到挑战

设 coset points 为

\[x_0,\ x_1.\]

通过直线插值在 $\beta$ 上求值:

\[y' = y_0 + \frac{(y_1-y_0)(\beta-x_0)} {x_1-x_0}.\]

这与 even/odd FRI fold 等价,只是用 Lagrange interpolation 形式实现。

14.4 更新 domain

二元 fold 后:

\[q\leftarrow q', \qquad x\leftarrow x^2.\]

逐层重复,最后连接到 proof 中名为 final_poly 的单个 extension element:

\[f_{\mathrm{final}}(X)=c_{\mathrm{final}}, \qquad y_{\mathrm{final}}=c_{\mathrm{final}}.\]

它不是一组可表示任意高次 final polynomial 的 coefficients;每个 query 的最终 folded evaluation 都被连接到同一个 constant extension value。这体现了该旧版 constant-final-value proof shape 的特定假设。

flowchart LR
    I["index q 与 current y"] --> S["用 q mod 2 插入 sibling"]
    S --> M["验证二元 leaf 的 Merkle path"]
    M --> L["在 beta 上做两点插值"]
    L --> U["q = q >> 1<br/>x = x²"]
    U --> N{"还有 FRI 层?"}
    N -->|是| S
    N -->|否| F["连接 final constant value"]

15. FRI challenge 顺序

对每个 commit-phase commitment $C_i$:

  1. transcript 吸收 $C_i$;
  2. 采样 fold challenge $\beta_i$。

然后:

  1. 检查/吸收 PoW witness;
  2. 从 transcript 采样每个 query index。

形式化为:

\[\beta_i = \mathsf{FS}( \text{prefix}, C_0,\ldots,C_i ),\]

\[q_j = \mathsf{FSBits}( \text{all FRI commits}, \text{PoW witness}, j ).\]

15.1 final_poly 没有在 query 前吸收

固定 bridge 的 p3_verify_shape_and_sample_challenges 在吸收全部 FRI commitments 后,直接检查/吸收 PoW 并采样 query indices;它 没有 在这些挑战之前执行

\[\mathsf{Observe}(\texttt{proof.final\_poly}).\]

final_poly 只在逐 query fold 完成后作为一个 constant value 被 equality constraint 使用。因此本节描述的是必须原样重演才能验证历史 proof 的 legacy transcript 边界,不是可推广的 FRI 模板。若 final object 是非恒定 polynomial,或者新协议要求 prover 在 queries 前绑定 final coefficients/commitment,就必须把该对象先吸收再采样 PoW/query challenges;这会改变 transcript,必须同时升级 prover、proof version 与安全分析,不能只改 bridge 一侧。

若 query index 先于 FRI commitments,prover 可只让被查询 branches 一致;若 $\beta_i$ 先于 $C_i$,prover 可针对 challenge 选择当前 layer。

16. 顶层 inner verifier 的 transcript

快照的 __p3_verify_proof__ 可概括为:

sequenceDiagram
    participant C as Plonky2 circuit
    participant T as In-circuit Poseidon2 challenger
    participant W as Inner proof witness
    W->>T: trace commitment C_T
    T-->>C: constraint challenge alpha
    W->>T: quotient commitment C_Q
    T-->>C: OOD point zeta
    C->>C: compute zeta_next
    C->>C: verify batched PCS openings
    C->>C: recompose Q(zeta)
    C->>C: evaluate AIR at opened rows
    C->>C: assert C_alpha(zeta) / Zhat_H(zeta) = Q(zeta)

PCS opening 内又有一层 transcript:

  1. 采样 opening-batch challenge $\rho$;
  2. 吸收每个 FRI commitment 并采样 $\beta_i$;
  3. 验证 PoW;
  4. 采样 query indices。

这两层不是两个独立 hash states;它们在同一 challenger state 上继续演化。

17. Fibonacci 示例端到端

17.1 inner trace

\[(a_0,b_0)=(1,1)\]

开始:

\[\begin{aligned} c_r&=a_r+b_r,\\ a_{r+1}&=b_r,\\ b_{r+1}&=c_r. \end{aligned}\]

前几行:

\[\begin{array}{c|ccc} r&a_r&b_r&c_r\\\hline 0&1&1&2\\ 1&1&2&3\\ 2&2&3&5\\ 3&3&5&8 \end{array}\]

17.2 inner proof

旧版 Plonky3:

  1. 把三列视为 $a(X),b(X),c(X)$;
  2. 承诺 trace LDE;
  3. 用 $\alpha$ 合并五类 AIR constraints;
  4. 承诺 quotient;
  5. 在 $\zeta,\zeta_{\mathrm{next}}$ 打开;
  6. 用 two-adic FRI PCS 证明 openings。

得到

\[\pi_3.\]

17.3 outer proof

Plonky2 circuit:

  1. 把 $\pi_3$ 的代数字段作为 targets,并把 host degree_bits 固定进 circuit shape;
  2. 用 Poseidon2 gadget 重建 transcript;
  3. 验证所有 MMCS/FRI paths;
  4. 在 targets 上重新执行 Fibonacci AIR verifier;
  5. 连接 quotient identity;
  6. 为整个 circuit 生成 $\pi_2$。

最终:

\[\pi_2 \Rightarrow \exists\pi_3: \mathsf{Verify}_{\mathrm{P3,Fib}}(\pi_3)=1.\]

17.4 这个命题没有自动说什么

若 outer public inputs 为空,它不会自动向外界暴露:

  • 最后一个 Fibonacci 数;
  • trace length;
  • inner commitment;
  • inner proof hash。

示例 AIR 把初值固定为 1,但 trace degree/shape 是构建 circuit 时从 proof shape 推导的。部署者必须让 outer circuit identity 和 application policy 固定它接受的 degree/AIR/config。

18. 源码中的硬编码与 TODO

快照不是 generic production verifier,源码可见的限制包括:

18.1 Goldilocks modulus 硬编码

p3_constant 里直接使用:

\[\texttt{0xFFFF\_FFFF\_0000\_0001}.\]

源码也留有 “don’t use hard coded Goldilocks modulus” TODO。

18.2 extension degree 固定为 2

\[\texttt{EXT\_DEGREE}=2.\]

当前 Plonky3 常见 31-bit field 配置多使用更高扩域次数,不能直接套用。

18.3 FRI 只实现 binary fold

proof step 只有一个 sibling value,源码注释也说明若支持其他 arity,proof object 需要改变。

18.4 32 个高度槽

reduced openings 使用:

\[[\operatorname{ExtTarget};32].\]

这不是由通用类型系统推导的最大值。

18.5 quotient allocation 固定形状

检查的 commit 中,virtual opened-values allocator 固定出非常具体的 quotient chunk/basis layout。虽然顶层 config 从输入 proof 的长度计算若干参数,这不表示所有长度组合都被完整支持和测试。

18.6 proof shape 从示例 proof 推导

builder 从已反序列化 proof 的:

  • degree_bits
  • quotient chunks length;
  • FRI commitment count;
  • trace width;
  • Merkle path length

推导 circuit allocation shape。

这适合“为固定 proof family 生成固定 circuit”的原型;不适合让不可信 proof 决定线上 verifier policy。

18.7 AIR 只有简化接口

快照自定义 AIR trait 没有当前 Plonky3 的:

  • public values;
  • preprocessed trace;
  • periodic columns;
  • permutation/lookup trace;
  • batch-STARK cross-AIR interactions。

19. 为什么不能拿它直接验证当前 Plonky3

从 2024 到当前主分支,Plonky3 的核心接口与 proof shape 已明显演进:

  • Uni-STARK transcript 绑定了更多 structural data;
  • PcsMmcs 与 domain abstractions 扩展;
  • FRI 支持 early stopping 与高元 folding;
  • ZK/hiding PCS 增加 randomization commitment/openings;
  • preprocessed/periodic columns 已进入协议;
  • batch-STARK、LogUp terminals 与 shared openings 出现;
  • Circle PCS、WHIR 等后端出现;
  • malformed-proof shape checks 和 error types 加强。

因此:

\[\boxed{ \text{Plonky2.5 commit 0f4e51} \not\equiv \text{current Plonky3 recursive verifier}. }\]

正确的 port 不是改几个 struct field,而是重新做:

  1. proof schema mapping;
  2. transcript conformance;
  3. domain/PCS verifier;
  4. ZK randomization checks;
  5. lookup/batch logic;
  6. security parameter binding;
  7. differential tests;
  8. independent audit。

20. outer proof 的 statement 必须显式设计

20.1 最弱命题

如果所有 inner proof fields 和 AIR identity 都只是 private/fixed build inputs,outer proof 最多说明:

\[\exists \pi_3: \mathsf{Verify}_{A,\theta}(\pi_3)=1,\]

其中 $A$ 与参数 $\theta$ 由 outer circuit 固定。

20.2 应用通常需要

\[\exists\pi_3: \mathsf{Verify}_{A,\theta} (\pi_3,x)=1 \quad\land\quad g(x)=y_{\mathrm{public}}.\]

因此 outer circuit 应:

  • 把 inner public values 加入 transcript;
  • 约束它们进入 AIR verifier;
  • 把必要投影注册为 Plonky2 public inputs;
  • 固定 AIR/config identifier;
  • 固定 inner degree/shape policy。

20.3 proof hash 不能替代 statement

公开

\[H(\pi_3)\]

只能绑定某份 proof bytes,不能告诉 application 这份 proof 声明了什么。仍需解析并绑定 public-value schema。

21. ZK 与“recursive ZKP”措辞

仓库 README 使用 “recursive zkp” 的宣传语境,但 inspected demo outer config 是:

\[\texttt{CircuitConfig::standard\_recursion\_config()},\]

而该 Plonky2 配置默认

\[\texttt{zero\_knowledge=false}.\]

所以更严谨的描述是:这是一个递归 validity wrapper / cross-verifier prototype;inspected demo 的 outer proof 本身不提供 zero-knowledge。

若 inner proof $\pi_3$ 从未单独发布,只在同一可信 proving boundary 内作为 outer private witness,则对 external verifier 的 wrapper privacy 可由下列充分条件表达:

\[\boxed{ \mathsf{ZK}_{\mathrm{wrapper,external}} \Leftarrow \mathsf{ZK}_{\mathrm{outer}} \land \mathsf{PrivateToExternal}(\pi_3) \land \mathsf{SafeProjection}(x_{\mathrm{out}}). }\]

在这种可见性模型下,inner scheme 本身不必是 ZK;outer ZK 隐藏其整个 private witness,包括 $\pi_3$。但若 $\pi_3$ 被单独发布,或先交给不可信 aggregation service,则 outer proof 无法消除该观察者已经获得的 inner leakage,此时才还需要针对该观察者审计 $\mathsf{ZK}_{\mathrm{inner}}$。无论哪种情形,outer public inputs 的 statement projection 都必须只公开应用允许公开的字段。

因此既不能从项目名推导 $\mathsf{ZK}{\mathrm{inner}}\land\mathsf{ZK}{\mathrm{outer}}$,也不能把“inner 必须 ZK”误写成所有 private-wrapper 拓扑的必要条件。

22. 安全检查清单

22.1 配置与 identity

  • inner AIR 是否固定或公开 allowlist?
  • trace width、degree bits 是否由 verifier policy 固定?
  • FRI 参数是否 transcript-bound?
  • hash round constants 是否逐字节匹配?
  • public values 是否进入 transcript 与 outer PI?

22.2 proof shape

  • 所有 vectors 是否有 exact expected length?
  • MMCS matrix widths 是否 verifier-known?
  • quotient chunk 数是否匹配 symbolic degree?
  • FRI layer count 是否匹配 degree/final polynomial?
  • query count 是否强制等于参数?

22.3 arithmetic

  • extension polynomial 是否 irreducible 且与 inner 一致?
  • inverse/division 是否处理零分母?
  • query bits 是否真的 range-constrained?
  • bit reversal 与 LDE order 是否一致?
  • domain shift/generator 是否一致?

22.4 transcript

  • absorb/sample 顺序是否逐项差分测试?
  • commitment、opened values、PoW 是否全被吸收?
  • final object 是否按该 proof version 的规定在 query 前绑定?legacy constant-final-value 的例外是否被明确隔离?
  • base/extension serialization 是否 canonical?
  • 是否有 domain separation 与 length ambiguity?

22.5 隐私与运维

  • inner/outer ZK flags 是否显式?
  • 两个 codebase 的 audit delta 是什么?
  • 依赖 pin 是否可重现?
  • 当前 proof producer 是否仍生成 2024 格式?
  • bridge 是否有独立审计?

23. 如果今天重做“2.5”,合理架构是什么

不要复制 2024 proof struct;应围绕当前 trait 边界生成 verifier circuit:

flowchart TB
    SPEC["固定的 VerifierSpec<br/>field, PCS, hash, AIR, params"] --> GEN["递归 verifier generator"]
    AIR["单一 AIR IR"] --> GEN
    GEN --> NATIVE["native verifier"]
    GEN --> CIRCUIT["Plonky2 或其他 outer circuit"]
    TEST["transcript + differential vectors"] --> NATIVE
    TEST --> CIRCUIT
    NATIVE --> EQ["逐 challenge / 逐 opening 一致性"]
    CIRCUIT --> EQ

关键设计:

  1. 用单一 AIR IR 生成 native 与 recursive evaluation,避免双份手写;
  2. VerifierSpec 是可哈希、可版本化、可公开绑定的对象;
  3. proof decoder 先做严格 shape validation,再分配 circuit;
  4. transcript 有 conformance test vectors;
  5. PCS/FRI/lookup 每个 backend 各有明确 support matrix;
  6. outer public inputs 有版本化 schema;
  7. 生产前做安全界计算与独立审计。

24. Plonky2.5 真正值得学习的地方

尽管它不是当前可直接部署的通用方案,概念上仍展示了四个重要事实。

24.1 proof systems 可以跨层组合

inner 与 outer 不必使用同一种 arithmetization:

\[\text{AIR/STARK inner} \quad+\quad \text{PLONKish/FRI outer}.\]

24.2 field alignment 能极大简化 bridge

Goldilocks-to-Goldilocks 避免了 non-native field;剩余主要成本落在 hash、FRI 和 bit operations。

24.3 transcript compatibility 是协议的一部分

复制高层数学公式不够,必须复制:

  • byte/field encoding;
  • observe order;
  • squeeze schedule;
  • query-bit extraction;
  • tree leaf order。

24.4 递归 verifier 是独立安全关键实现

native verifier 正确,不代表手写 circuit verifier 正确。后者必须被视为另一套密码学实现单独测试和审计。

25. 总结

Plonky2.5 的本质公式不是一种新证明协议,而是:

\[\boxed{ \pi_2 \text{ proves } \mathsf{Verify}_{\mathrm{old\ P3}} \bigl( A,\theta,x,\pi_3 \bigr)=1. }\]

它成功的必要条件是:

\[\begin{aligned} &\text{field arithmetic 一致},\\ &\text{extension representation 一致},\\ &\text{Poseidon2/MMCS 一致},\\ &\text{Fiat–Shamir transcript 一致},\\ &\text{AIR constraint order 一致},\\ &\text{PCS/FRI proof shape 一致},\\ &\text{statement 与 parameters 被 outer proof 绑定}. \end{aligned}\]

它的最大误用风险,是把一个固定的 2024 Fibonacci demo 当成“可验证任意当前 Plonky3 proof 的正式 2.5 协议”。

26. 一手资料


上一篇:Plonky2 递归原理与工程
下一篇:Plonky3 模块化架构
返回:总目录

本文由作者按照 CC BY 4.0 进行授权