值得信赖的区块链资讯!
L2 – 深入理解zkSync电路预处理
zkSync使用PLONK零知识证明算法生成证明。证明的生成逻辑是通过增强的bellman库实现。matter-labs开源了相关的代码,请查看plonk-release分支。zkSync虽然采用PLONK零知识证明算法,但是电路的搭建开发采用的R1CS形式。为了生成zkSync的电路证明,逻辑上需要两步:1/电路转换 2/ PLONK证明计算。电路转换就是zkSync电路预处理过程。本文重点分析zkSync电路预处理过程。
https://github.com/matter-labs/bellman.git
文章中涉及的代码的最后一个提交信息如下:
commit f551a55d83d2ea604b2dbfe096fd9dcfdaedb189 (HEAD -> master)
Merge: 06c10c0 87d8496
Author: Alexander <[email protected]>
Date: Tue Oct 13 17:06:24 2020 +0200
Merge pull request #30 from matter-labs/plonk_release_cpu_flags_fix
Fix CPU flags in blake2s
一切从core/prover/src/plonk_step_by_step_prover.rs的next_round函数开始。在已知电路的情况下,通过如下的三步计算生成证明:
1. let setup = SetupForStepByStepProver::prepare_setup_for_step_by_step_prover(
instance.clone(),
self.config.download_setup_from_network,
)
2. let vk = PlonkVerificationKey::read_verification_key_for_main_circuit(block_size)
3. let verified_proof = precomp
.setup
.gen_step_by_step_proof_using_prepared_setup(instance, &vk)
第一步就是预处理,第二步是获取verification密钥,第三步开始生成证明。其中instance就是FranklinCircuit对象。核心逻辑是prepare_setup_for_step_by_step_prover函数。该函数实现在core/models/src/prover_utils/mod.rs中:
pub fn prepare_setup_for_step_by_step_prover<C: Circuit<Engine> + Clone>(
circuit: C,
download_setup_file: bool,
) -> Result<Self, failure::Error> {
let hints = transpile(circuit.clone())?;
let setup_polynomials = setup(circuit, &hints)?;
let size = setup_polynomials.n.next_power_of_two().trailing_zeros();
let setup_power_of_two = std::cmp::max(size, SETUP_MIN_POW2); // for exit circuit
let key_monomial_form = Some(get_universal_setup_monomial_form(
setup_power_of_two,
download_setup_file,
)?);
Ok(SetupForStepByStepProver {
setup_power_of_two,
setup_polynomials,
hints,
key_monomial_form,
})
}
电路预处理的两个步骤:1/ Transpile 2/ Setup。Transpile是将电路从R1CS形式,转换成PLONK表示形式。从Transpile讲起。
Transpile
transpile函数实现在src/plonk/mod.rs,实现电路的转换,从R1CS转换到PLONK形式。
pub fn transpile<E: Engine, C: crate::Circuit<E>>(circuit: C) -> Result<Vec<(usize, TranspilationVariant)>, SynthesisError> {
let mut transpiler = Transpiler::<E, PlonkCsWidth4WithNextStepParams>::new();
circuit.synthesize(&mut transpiler).expect("sythesize into traspilation must succeed");
let hints = transpiler.into_hints();
Ok(hints)
}
仔细看看Transpiler的定义:
pub struct Transpiler<E: Engine, P: PlonkConstraintSystemParams<E>> {
current_constraint_index: usize,
current_plonk_input_idx: usize,
current_plonk_aux_idx: usize,
scratch: HashSet<crate::cs::Variable>,
deduplication_scratch: HashMap<crate::cs::Variable, usize>,
transpilation_scratch_space: Option<TranspilationScratchSpace<E>>,
hints: Vec<(usize, TranspilationVariant)>,
n: usize,
}
pub enum TranspilationVariant {
IntoQuadraticGate,
IntoAdditionGate(LcTransformationVariant),
MergeLinearCombinations(MergeLcVariant, LcTransformationVariant),
IntoMultiplicationGate((LcTransformationVariant, LcTransformationVariant, LcTransformationVariant))
}
特别注意Transpiler中的hints,是电路的每个门(模块)的类型。其实Transpile是电路转换的预处理,Transpile的结果就是获取hints。一个R1CS电路是如何转换成PLONK电路的核心逻辑在Transpile的enforce函数。熟悉R1CS形式的小伙伴知道,一个R1CS的约束形式是A*B=C。其中A/B/C都有可能是多个变量的线性组合。
let (a_has_constant, a_constant_term, a_lc_is_empty, a_lc) = deduplicate_and_split_linear_term::<E, Self>(a(crate::LinearCombination::zero()), &mut self.deduplication_scratch);
let (b_has_constant, b_constant_term, b_lc_is_empty, b_lc) = deduplicate_and_split_linear_term::<E, Self>(b(crate::LinearCombination::zero()), &mut self.deduplication_scratch);
let (c_has_constant, c_constant_term, c_lc_is_empty, c_lc) = deduplicate_and_split_linear_term::<E, Self>(c(crate::LinearCombination::zero()), &mut self.deduplication_scratch);
对输入的每一个R1CS约束,查看A/B/C的类型,是否是固定值,是否是多变量组合。针对A/B/C的不同组合,确定不同的转换逻辑。以C*LC=C为例:
(true, false, true) | (false, true, true) => {
…
let (_, _, hint) = enforce_lc_as_gates(
self,
lc,
multiplier,
free_constant_term,
false,
&mut space
).expect("must allocate LCs as gates for constraint like c0 * LC = c1");
…
}
以(A0) * (B0 + Blc) = C0为例,其中,A0,B0,C0都是固定值。整个约束可以变换为:
A0*B0-C0 + A0*Blc = 0
这个形式是个通用形式,也就是enforce_lc_as_gates需要解决的问题。A0*B0-C0就是free_constant_term,A0就是multiplier,Blc就是lc。
介绍具体的转换逻辑之前,介绍一下TranspilationScratchSpace。TranspilationScratchSpace存储一个门电路对应的相关信息,包括变量,系数等等。
struct TranspilationScratchSpace<E: Engine> {
scratch_space_for_vars: Vec<PlonkVariable>,
scratch_space_for_coeffs: Vec<E::Fr>,
scratch_space_for_booleans: Vec<bool>,
}
注意的是,一个约束的系数比变量的个数要多。

如果一个lc不超过4个变量的组合,一个PLONK约束就可以表示。也就是不需要乘法,只需要5个加法(其中有个固定值)。
如果超过4个变量,需要多个门进行组合。额外需要的门数计算如下:
let cycles = ((lc.0.len() – P::STATE_WIDTH) + (P::STATE_WIDTH – 2)) / (P::STATE_WIDTH – 1);
除了第一个门能支持4个变量外,其他额外的门只能处理3个变量(P::STATE_WIDTH – 1)。门与门之间通过中间变量进行“连接”:

setup函数实现在src/plonk/mod.rs。setup主要是计算sigma函数和门系数函数。事实上,这两个函数“就是”PLONK算法对应的电路表示。
pub fn setup<E: Engine, C: crate::Circuit<E>>(
circuit: C,
hints: &Vec<(usize, TranspilationVariant)>
) -> Result<SetupPolynomials<E, PlonkCsWidth4WithNextStepParams>, SynthesisError> {
use crate::plonk::better_cs::cs::Circuit;
let adapted_curcuit = AdaptorCircuit::<E, PlonkCsWidth4WithNextStepParams, _>::new(circuit, &hints);
let mut assembly = self::better_cs::generator::GeneratorAssembly::<E, PlonkCsWidth4WithNextStepParams>::new();
adapted_curcuit.synthesize(&mut assembly)?;
assembly.finalize();
let worker = Worker::new();
assembly.setup(&worker)
}
AdaptorCircuit是”R1CS“电路的Wrapper。AdaptorCircuit除了包括一个R1CS的Circuit外,还包括Transpile生成的hints信息。GeneratorAssembly实现了PLONK算法对应的门管理的逻辑。简单的说,一个R1CS的Circuit的Synthesize的过程如下图:

Adaptor实现了R1CS的ContraintSystem接口,并在enforce函数实现了R1CS电路转换。GeneratorAssembly中维护了PLONK电路对应的门的形式。GeneratorAssembly的finalize函数将电路的门的个数补齐到2的幂次。
特别注意的是,PLONK电路并不是完全和论文中一致。在bellman的实现中,一个门电路采用的是四个变量:a,b,c和d,而不是三个变量:a,b和c。
pub struct PlonkCsWidth4WithNextStepParams;
impl<E: Engine> PlonkConstraintSystemParams<E> for PlonkCsWidth4WithNextStepParams {
const STATE_WIDTH: usize = 4;
const HAS_CUSTOM_GATES: bool = false;
const CAN_ACCESS_NEXT_TRACE_STEP: bool = true;
type StateVariables = [Variable; 4];
type ThisTraceStepCoefficients = [E::Fr; 6];
type NextTraceStepCoefficients = [E::Fr; 1];
type CustomGateType = NoCustomGate;
}
PlonkCsWidth4WithNextStepParams结构体中的STATE_WIDTH就是一个门对应的变量个数。
GeneratorAssembly的setup函数从门的表示,获取sigma函数和门对应的系数多项式。
pub fn setup(self, worker: &Worker) -> Result<SetupPolynomials<E, PlonkCsWidth4WithNextStepParams>, SynthesisError> {
assert!(self.is_finalized);
let n = self.n;
let num_inputs = self.num_inputs;
let [sigma_1, sigma_2, sigma_3, sigma_4] = self.make_permutations(&worker);
let ([q_a, q_b, q_c, q_d, q_m, q_const],
[q_d_next]) = self.make_selector_polynomials(&worker)?;
drop(self);
let sigma_1 = sigma_1.ifft(&worker);
let sigma_2 = sigma_2.ifft(&worker);
let sigma_3 = sigma_3.ifft(&worker);
let sigma_4 = sigma_4.ifft(&worker);
let q_a = q_a.ifft(&worker);
let q_b = q_b.ifft(&worker);
let q_c = q_c.ifft(&worker);
let q_d = q_d.ifft(&worker);
let q_m = q_m.ifft(&worker);
let q_const = q_const.ifft(&worker);
let q_d_next = q_d_next.ifft(&worker);
let setup = SetupPolynomials::<E, PlonkCsWidth4WithNextStepParams> {
n,
num_inputs,
selector_polynomials: vec![q_a, q_b, q_c, q_d, q_m, q_const],
next_step_selector_polynomials: vec![q_d_next],
permutation_polynomials: vec![sigma_1, sigma_2, sigma_3, sigma_4],
_marker: std::marker::PhantomData
};
Ok(setup)
}
逻辑比较清晰明了,计算permutation和selector多项式的点值表示,并换算到系数表示。make_selector_polynomials函数实现selector多项式的点值计算,相对简单。详细说说permutation的计算过程。
partitions数组的长度为所有变量的个数,记录的每个变量在每个门对应的位置。举个例子:
partitions[3] = ((0, 1),(3, 2))
第三个变量用在第一个门的第一个变量(0,1)和第三个门的第二个变量(3,2)。partitions帮助找出了使用同一个变量的所有的门的信息。通过partitions的信息可以构造sigma函数。
每个门由4个变量组成,每个门的每个变量的位置有独立连续的编号,如下图所示:

for (i, partition) in partitions.into_iter().enumerate().skip(1) { //枚举所有的变量
// copy-permutation should have a cycle around the partition
…
let permutation = rotate(partition.clone());
permutations[i] = permutation.clone();
for (original, new) in partition.into_iter() //构造同一变量对应的门的位置对
.zip(permutation.into_iter())
{
let new_zero_enumerated = new.1 – 1;
let mut new_value = domain_elements[new_zero_enumerated]; //新对应的门的变量的编号
new_value.mul_assign(&non_residues[new.0]); //乘以偏移,得到统一编号
// check to what witness polynomial the variable belongs
let place_into = match original.0 { //获取同一个变量对应的原有位置信息
0 => {
sigma_1.as_mut()
},
1 => {
sigma_2.as_mut()
},
2 => {
sigma_3.as_mut()
},
3 => {
sigma_4.as_mut()
},
_ => {
unreachable!()
}
};
let original_zero_enumerated = original.1 – 1;
place_into[original_zero_enumerated] = new_value; //进行置换
}
}
sigma_1/sigma_2/sigma_3/sigma_4就是需要求取的“置换”函数。整个逻辑如下图所示:

总结:
zkSync虽然采用PLONK零知识证明算法,但是电路的搭建开发采用的R1CS形式。zkSync电路处理包括:1/电路转换 2/PLONK证明计算。Transpile实现了电路的格式转换。电路转换的目的是获取:1/sigma函数 2/ 门系数多项式。
比推快讯
更多 >>- 美参议员要求审查特朗普关联加密交易,涉阿联酋 5 亿美元投资国家安全风险
- ETH 核心贡献者披露 Tomasz 卸任内幕:以太坊基金会长期存在权力斗争
- 下周宏观展望:美联储纪要与 PCE 重磅来袭,最高法院或就特朗普关税案发声
- 分析师:比特币基差回归中性区间,衍生品多头需求降温
- 美知名主持人 Savannah 母亲遭绑架并被索要比特币赎金,案件引发全美关注
- 数据:Hyperliquid 平台鲸鱼当前持仓 29.32 亿美元,多空持仓比为 0.93
- 马斯克旗下 X 将于数周内上线加密货币与股票交易功能
- Benchmark 将 Coinbase 目标价下调 37%至 267 美元,仍维持买入评级
- BTC OG 内幕巨鲸再次抛售 ETH,已向 CEX 存入超 26 万枚 ETH
- CZ:今年遭遇中文竞品 FUD,社区分裂不利于币价,呼吁专心建设
- 加密恐慌指数跌至 8,市场冷清依旧
- X 产品负责人再谈“智能标签”:X 不会直接负责执行交易或充当经纪商
- CZ:今年主要有家中文竞品在花钱黑我们,其他友商的竞争还算专业
- Galaxy Digital 高管:加密市场不会出现 V 型复苏,经历一段震荡期后才会逐步上涨
- Coinbase CEO:启用更多 x402 结账流程可使客服人员高效完成工作
- Polymarket 上预测本周六美国政府停摆概率降至 2%
- Binance 前上币负责人 Chase:比特币 2026 年必将创下历史新高
- 数据:310.01 枚 BTC 从匿名地址转出,经中转后流入 Kraken
- 持有 11 年比特币的巨鲸从币安提取 5300 万美元并偿还贷款
- 数据:过去 1 小时 Binance 净流出 4,814.61 万 USDT
- 当前主流 CEX、DEX 资金费率显示市场再度全面转向看空
- 数据:12 万枚 COMP 从 Coinbase Prime 转出,价值约 239 万美元
- 摩根大通:美元走弱将利好全球股市,而非拖累风险资产
- 数据:435.91 枚 BTC 从 Binance 转出,价值约 2071 万美元
- 长期做空 BTC巨鲸撤销 55,125 美元抄底BTC 限价买单
- 数据:过去 24 小时全网爆仓 3.23 亿美元,多单爆仓 4,926.27 万美元,空单爆仓 2.74 亿美元
- Vitalik:对预测市场现状感到担忧,应努力将其推向广义对冲用例
- 持有 11 年比特币 OG 向币安存入 5000 枚 BTC,价值 3.84 亿美元
- BTC OG 内幕巨鲸向 Binance 存入 5000 枚 BTC
- Vitalik Buterin:预测市场应转向风险对冲,而非短期投机
- 何一回应用户封控争议:涉 VPN 触发风控,账户转为提现模式
- 数据:4.85 万枚 SOL 从 Binance 转出,价值约 421 万美元
- 持有 6500 万美元资产的 Jake Paul 旗下基金投资 OpenAI 及 Polymarket
- 麻吉加仓 ETH 多单,现浮盈 15 万美元
- Zhu Su:加密货币未来几年可能将显著跑赢美股七巨头
- 比特币回升突破 7 万美元
- 数据:BTC 突破 70000 美元
- BTC 突破 70000 USDT,24H 涨幅 4.71%
- 本周美国以太坊现货 ETF 累计净流出 1.612 亿美元
- 数据:若 ETH 跌破 1,971 美元,主流 CEX 累计多单清算强度将达 8.39 亿美元
- X 产品负责人:即将允许用户直接基于时间线交易股票和加密货币
- 数据:218.6 枚 BTC 从 Bitstamp 转出,价值约 1.52 亿美元
- ARK Invest 再度买入约 1500 万美元 Coinbase 股票,结束此前减持
- HTX DAO 发布 2025 年度报告:生态建设、权益体系完善及全球化拓展持续推进
- 数据:ETH 全网合约持仓量 24h 增长 6.7%
- Ju.com 春节“主流币 8 折打新”首期 BTC 收官:逾万人参与,超募 740%
- 某鲸鱼向 MEXC 存入 200 万枚 PIPPIN,已实现盈利 360 万美元
- 先锋领航集团 CRCL 持仓已浮亏逾 4 亿美元
- F2Pool 联创 Wang Chun:聪明钱现在正买入比特币
- X 产品负责人:计划更新 API 政策,以阻止未经用户同意创建费用池的应用
比推专栏
更多 >>- Happy new year【Horse success】|0213Asian
- Was it finished?|0206 Asian
- 围猎以太坊多头:「巨鲸」们暴亏 70 亿美元,正被集体围观
- Challenge,risk And chances|0130 Asian
- Meta 豪赌 AI:砸钱 1350 亿美元,2026 的扎克伯格,值得相信么?
- Variables: Terrible snowstorm|0128 Asian
- 英特尔「生死线」时刻:在 ICU 门前,陈立武如何清算遗产并开启自救?
- 從1月13號到今天,提前到5100|0126Asian
- You Should work HARDER in 2026|0120 Asian
- 硅谷最聪明那群人的「终极推演」:2026,我们应该「All-In」什么?
观点
比推热门文章
- ETH 核心贡献者披露 Tomasz 卸任内幕:以太坊基金会长期存在权力斗争
- 下周宏观展望:美联储纪要与 PCE 重磅来袭,最高法院或就特朗普关税案发声
- 分析师:比特币基差回归中性区间,衍生品多头需求降温
- 美知名主持人 Savannah 母亲遭绑架并被索要比特币赎金,案件引发全美关注
- 数据:Hyperliquid 平台鲸鱼当前持仓 29.32 亿美元,多空持仓比为 0.93
- 马斯克旗下 X 将于数周内上线加密货币与股票交易功能
- Benchmark 将 Coinbase 目标价下调 37%至 267 美元,仍维持买入评级
- BTC OG 内幕巨鲸再次抛售 ETH,已向 CEX 存入超 26 万枚 ETH
- CZ:今年遭遇中文竞品 FUD,社区分裂不利于币价,呼吁专心建设
- 加密恐慌指数跌至 8,市场冷清依旧
比推 APP



