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 field | Goldilocks |
| inner challenge extension | degree 2 binomial extension |
| inner PCS | two-adic FRI PCS |
| inner MMCS | Poseidon2 Merkle tree |
| Poseidon2 state width | 12 |
| digest elements | 4 |
| challenger overwrite width / duplex threshold | 12 field elements |
| MMCS leaf-hash input chunk | 4 field elements |
| FRI folding | binary |
| demo blowup | $2^1=2$ |
| demo queries | 100 |
| demo query PoW | 16 bits |
| outer system | pinned 2024-era Plonky2 revision |
| outer circuit config | standard_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:
不需要 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:
固定源码中 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。开发者维护:
- native prover 使用的 AIR;
- 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_m = \alpha^{m-1}c_0+\alpha^{m-2}c_1+\cdots+c_{m-1}.\]源码的 assert_zero(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 的前缀,然后执行:
若采样时仍有未处理输入,哪怕不足 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。必须区分
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:
这揭示了一个明确的 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:
它不是一组可表示任意高次 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$:
- transcript 吸收 $C_i$;
- 采样 fold challenge $\beta_i$。
然后:
- 检查/吸收 PoW witness;
- 从 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;它 没有 在这些挑战之前执行
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:
- 采样 opening-batch challenge $\rho$;
- 吸收每个 FRI commitment 并采样 $\beta_i$;
- 验证 PoW;
- 采样 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:
- 把三列视为 $a(X),b(X),c(X)$;
- 承诺 trace LDE;
- 用 $\alpha$ 合并五类 AIR constraints;
- 承诺 quotient;
- 在 $\zeta,\zeta_{\mathrm{next}}$ 打开;
- 用 two-adic FRI PCS 证明 openings。
得到
\[\pi_3.\]17.3 outer proof
Plonky2 circuit:
- 把 $\pi_3$ 的代数字段作为 targets,并把 host
degree_bits固定进 circuit shape; - 用 Poseidon2 gadget 重建 transcript;
- 验证所有 MMCS/FRI paths;
- 在 targets 上重新执行 Fibonacci AIR verifier;
- 连接 quotient identity;
- 为整个 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 里直接使用:
源码也留有 “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;
Pcs、Mmcs与 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,而是重新做:
- proof schema mapping;
- transcript conformance;
- domain/PCS verifier;
- ZK randomization checks;
- lookup/batch logic;
- security parameter binding;
- differential tests;
- 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
关键设计:
- 用单一 AIR IR 生成 native 与 recursive evaluation,避免双份手写;
VerifierSpec是可哈希、可版本化、可公开绑定的对象;- proof decoder 先做严格 shape validation,再分配 circuit;
- transcript 有 conformance test vectors;
- PCS/FRI/lookup 每个 backend 各有明确 support matrix;
- outer public inputs 有版本化 schema;
- 生产前做安全界计算与独立审计。
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. 一手资料
- QED Protocol Plonky2.5 仓库
- 固定快照 0f4e51b
- 固定 Developer Guide:AIR setup
- 固定 verifier circuit source(selectors、FRI transcript)
- 固定 normalized-coordinate selectors
- 固定 bridge AIR folder
- 固定 Poseidon2 challenger(overwrite width 12)
- 固定 MMCS leaf hashing(chunk 4)
- 固定 proof schema 与 host
degree_bits - 固定 Poseidon2 gate
- 当前 Plonky3 主仓库
上一篇:Plonky2 递归原理与工程
下一篇:Plonky3 模块化架构
返回:总目录