1. 引言
Polygon zkEVM中,设计了多种抗故障机制:
- force batch:抗 trusted sequencer对L2交易审查 机制
- force verification:抗 trusted aggregator故障不生成proof 机制
- emergency state紧急状态:抗系统故障升级策略
2. force batch
Polygon zkEVM中,用户必须依赖trusted sequencer来执行其L2交易。但当其交易被trusted sequencer审查不被执行时,用户可将其交易包含在某forced batch内。
所谓forced batch,是指:
- 一组L2交易
- 用户将这些L2交易commit到L1,来公开声明其想要这些交易被执行的意图
PolygonZkEVM.sol 合约内有上图所示的forcedBatches
mapping,其中会存储用户所提交的想要强制执行的transaction batches。forcedBatches
mapping像一块不可变的布告栏,其中forced batches是带时间戳的,并在被包含在某sequence之前发布。
// Queue of forced batches with their associated data
// ForceBatchNum --> hashedForcedBatchData
// hashedForcedBatchData: hash containing the necessary information to force a batch:
// keccak256(keccak256(bytes transactions), bytes32 globalExitRoot, unint64 minForcedTimestamp)
mapping(uint64 => bytes32) public forcedBatches;
// Queue of batches that defines the virtual state
// SequenceBatchNum --> SequencedBatchData
mapping(uint64 => SequencedBatchData) public sequencedBatches;
trusted sequencer可在未来的sequences内包含这些forced batches,以维护其作为trusted entity的身份。否则,用户可证明其正被审查,该trusted sequencer的trusted身份将被撤销。
尽管trusted sequencer被激励对forcedBatches
mapping中发布的forced batches进行sequence,但这并不能保证forced batches内各交易执行的finality。
为了确保trusted sequencer出现故障时的finality,L1 PolygonZkEVM.sol合约内具有一个名为sequenceForceBatches
的替代batch sequencing函数。该函数允许任何人对已发布一段时间(由公共常量forceBatchTimeout
指定)但尚未sequence的forced batches进行sequence。该超时设置为5天。
任何用户都可通过直接调用forceBatch
函数来发布要强制执行的batch:
/**
* @notice Allows a sequencer/user to force a batch of L2 transactions.
* This should be used only in extreme cases where the trusted sequencer does not work as expected
* Note The sequencer has certain degree of control on how non-forced and forced batches are ordered
* In order to assure that users force transactions will be processed properly, user must not sign any other transaction
* with the same nonce
* @param transactions L2 ethereum transactions EIP-155 or pre-EIP-155 with signature:
* @param polAmount Max amount of pol tokens that the sender is willing to pay
*/
function forceBatch(
bytes calldata transactions,
uint256 polAmount
) public virtual isSenderAllowedToForceBatches
为成功发布某forced batch到forcedBatches
mapping中,需满足以下条件,否则该交易将被revert:
- 合约必须不处于紧急状态
- 必须允许force batches
polAmount
参数,必须高于POL fee per batch。前期为用户愿意为其forced batch发布所支付的手续费对应的最大数量的POL tokens。发布forced batch的手续费为sequence的手续费的100倍:_batchFee * 100
。由于在发布forced batch时已支付了手续费,因此后续sequence该forced batch时,无需再支付。- 该交易字节长度必须小于
_MAX_FORCE_BATCH_BYTE_LENGTH
常量值,当前被设置为5000。
forcedBatches
mapping内的forced batch是按其force batch index为key,其value为:
forcedBatches[lastForceBatch] = keccak256(
abi.encodePacked(
keccak256(transactions),
lastGlobalExitRoot,
uint64(block.timestamp), //为L1区块timestamp,表示该forced batch所发布的时间。
blockhash(block.number - 1)
)
);
lastForceBatch
变量值,会随着所发布的每个forced batch而递增,用作forced batch计数器,从而提供特定的index number。
// Update forcedBatches mapping
lastForceBatch++;
基于优化原因,forcedBatches
mapping内仅存储forced batch的commitment,由于可从交易calldata中恢复出forced batch,因此可确保数据可用性。
极端情况下,trusted sequencer掉线,任何用户都可使用sequenceForceBatches
函数来sequence forced batches:
//v1版本
/**
* @notice Allows anyone to sequence forced Batches if the trusted sequencer has not done so in the timeout period
* @param batches Struct array which holds the necessary data to append force batches
*/
function sequenceForceBatches(
ForcedBatchData[] calldata batches
) external isForceBatchAllowed ifNotEmergencyState
//v2版本
/**
* @notice Allows anyone to sequence forced Batches if the trusted sequencer has not done so in the timeout period
* @param batches Struct array which holds the necessary data to append force batches
*/
function sequenceForceBatches(
BatchData[] calldata batches
) external virtual isSenderAllowedToForceBatches
sequenceForceBatches
函数 与 sequenceBatches
函数类似,但当启用了batch forcing时,可由任何人调用。
sequenceForceBatches
函数会判断所提交的sequence内的每个batch是否已发布到forcedBatches
mapping内,且超过了forceBatchTimeout
时长。【由于已在发布forced batch时支付了手续费,此时无需再次支付手续费。】
若该sequence内的所有forced batches满足了所有sequence条件,则会像常规方式那样将该sequence添加到sequencedBatches
mapping内。最终会生成SequenceForceBatches
事件。
emit SequenceForceBatches(currentBatchSequenced);
注意:
- 使用
sequenceForceBatches
所sequence的forced batches sequences,永远不会达成某trusted state,因此在节点的本地trusted L2 state 和 L1PolygonZkEVM.sol
合约内所commit的virtual L2 state 之间会存在分叉。 - 节点软件探测到该分叉情况, 并对其进行处理——会基于从L1获取的L2 state,对其本地的L2 state进行重组。
- 当有sequence forced batch sequence时,trusted L2 state与virtual L2 state的差别为:
3. force verification
若无活跃且正常工作的Sequencer,系统无法达成L2 state finality,若无活跃且正常工作的aggregator,也将没有finality。
trusted aggregator的缺席或失效,则意味着L2 state transitions将永远无法更新到L1。为此,L1 PolygonZkEVM.sol
合约内有个名为verifyBatches
的函数,使得任何人都可aggregate sequences of batches。
/**
* @notice Allows an aggregator to verify multiple batches
* @param rollupID Rollup identifier
* @param pendingStateNum Init pending state, 0 if consolidated state is used
* @param initNumBatch Batch which the aggregator starts the verification
* @param finalNewBatch Last batch aggregator intends to verify
* @param newLocalExitRoot New local exit root once the batch is processed
* @param newStateRoot New State root once the batch is processed
* @param beneficiary Address that will receive the verification reward
* @param proof Fflonk proof
*/
function verifyBatches(
uint32 rollupID,
uint64 pendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
address beneficiary,
bytes32[24] calldata proof
) external ifNotEmergencyState {
verifyBatches
的函数 与verifyBatchesTrustedAggregator
函数的参数一样,但verifyBatches
的函数内增加了对所聚合sequence的2个额外约束,以及一个新的L2 pending state。
除需满足verifyBatchesTrustedAggregator
函数内的条件之外,verifyBatches
的函数内还需额外满足如下条件:
- 合约必须不处于紧急状态
- trusted aggregator必须已超时。其中
trustedAggregatorTimeout
由合约管理员配置
// Check if the trusted aggregator timeout expired,
// Note that the sequencedBatches struct must exists for this finalNewBatch, if not newAccInputHash will be 0
if (
rollup.sequencedBatches[finalNewBatch].sequencedTimestamp +
trustedAggregatorTimeout >
block.timestamp
) {
revert TrustedAggregatorTimeoutNotExpired();
}
若满足所有条件,verifyBatches
函数会验证计算完整性的ZKP proof。但与verifyBatchesTrustedAggregator
不同,若其验证成功,该verified sequence并不会被立即聚合,而是将该verified sequence 添加到 pendingStateTransitions
mapping中,并在pendingStateTimeout
之后才聚合。
mapping(uint256 pendingStateNum => PendingState) pendingStateTransitions;
/**
* @notice Struct to store the pending states
* Pending state will be an intermediary state, that after a timeout can be consolidated, which means that will be added
* to the state root mapping, and the global exit root will be updated
* This is a protection mechanism against soundness attacks, that will be turned off in the future
* @param timestamp Timestamp where the pending state is added to the queue
* @param lastVerifiedBatch Last batch verified batch of this pending state
* @param exitRoot Pending exit root
* @param stateRoot Pending state root
*/
struct PendingState {
uint64 timestamp;
uint64 lastVerifiedBatch;
bytes32 exitRoot;
bytes32 stateRoot;
}
verified batch sequences仍保持在名为Pending state的中间状态,其state transition并未consolidated固化。当处于Pending state时,新的L2 state root 以及 合约的新GlobalExitRoot 均不会添加到mapping(uint64 batchNum => bytes32) batchNumToStateRoot;
中。
lastPendingState
变量用于跟踪待固化的pending state transitions的number,并用作pendingStateTransitions
mapping的key。一旦其ZKP proof验证通过,Aggregator会收到aggregation奖励。
下图展示了,当某batch sequence被通过verifyBatches
函数聚合时,从batch角度的L2 Stages timeline,以及触发进入下一L2 state stage的动作:
处于pending state的batch sequences,对该协议的正确性和合理性没有任何影响。Non-forced batch sequences会在pending sequences之前被验证,且并不是所有sequences都会进入pending state。
lastVerifiedBatch
变量会跟踪最新验证和聚合的batch index。因此,即使某batch sequence吹捧说已验证,可通过调用getLastVerifiedBatch
函数来查询最新的verified batch index。
若有任何pending state transitions,该函数返回的为该pending state中的最后batch index,否则返回lastVerifiedBatch
。
/**
* @notice Get the last verified batch
*/
function _getLastVerifiedBatch(
RollupData storage rollup
) internal view returns (uint64) {
if (rollup.lastPendingState > 0) {
return
rollup
.pendingStateTransitions[rollup.lastPendingState]
.lastVerifiedBatch;
} else {
return rollup.lastVerifiedBatch;
}
}
当调用sequenceBatches
函数时,会调用内部函数 _tryConsolidatePendingState(rollup);
来固化该pending state。若自该pending batches验证之后,超过了pendingStateTimeout
,则会固化该pending state。因为其已验证通过,此时无需再检查其ZKP proof。
/**
* @notice Allows a sequencer to send multiple batches
* @param batches Struct array which holds the necessary data to append new batches to the sequence
* @param maxSequenceTimestamp Max timestamp of the sequence. This timestamp must be inside a safety range (actual + 36 seconds).
* This timestamp should be equal or higher of the last block inside the sequence, otherwise this batch will be invalidated by circuit.
* @param initSequencedBatch This parameter must match the current last batch sequenced.
* This will be a protection for the sequencer to avoid sending undesired data
* @param l2Coinbase Address that will receive the fees from L2
* note Pol is not a reentrant token
*/
function sequenceBatches(
BatchData[] calldata batches,
uint64 maxSequenceTimestamp,
uint64 initSequencedBatch,
address l2Coinbase
) public virtual onlyTrustedSequencer {
设计该机制用于帮助探测任何ZKP proof验证系统的soundness漏洞,同时确保L2资产不被恶意用户转移出去。
4. emergency state
紧急状态,是指‘PolygonZkEVM.sol’ 和 ‘PolygonZkEVMBridge.sol’ 共识合约状态,当激活了紧急状态时,将终止batch sequencing和bridge操作。激活紧急状态的目的是让Polygon 团队解决soundness漏洞或任何合约bug。其为保护Polygon zkEVM内用户资产的安全措施。
当处于紧急状态时,如下函数将被禁用:
- sequenceBatches
- verifyBatches
- forceBatch
- sequenceForceBatches
- proveNonDeterministicPendingState
因此紧急状态时,Sequencer将无法sequence batches。同时,trusted Aggregator可固化额外的state transitions,或,覆盖某被证明non-deterministic的pending state transition。
当具有2个不同L2 state root values的相同batches sequence被验证通过时,则会出现non-deterministic的pending state transition。该状态出现意味着计算完整性ZKP proof验证中存在soundness漏洞。
4.1 何时激活紧急状态?
紧急状态仅可由如下2个合约函数触发:
- 1)由合约owner调用
activateEmergencyState
函数激活。/** * @notice Function to activate emergency state, which also enables the emergency mode on both PolygonRollupManager and PolygonZkEVMBridge contracts * If not called by the owner must not have been aggregated in a _HALT_AGGREGATION_TIMEOUT period and an emergency state was not happened in the same period */ function activateEmergencyState() external {
- 2)在
HALT AGGREGATION TIMEOUT
(1周)超时之后,可由任何人调用。计时自sequencedBatchNum
参数被sequence但未被verify开始。- 该状态意味着没人可聚合batch sequences。其目的是临时停止协议,直到aggregation恢复。
此外,任何人可使用proveNonDeterministicPendingState
函数来触发紧急状态,但其需要证明某pending state是non-deterministic的:
/**
* @notice Allows activate the emergency state if its possible to prove a different state root given the same batches
* @param rollupID Rollup identifier
* @param initPendingStateNum Init pending state, 0 if consolidated state is used
* @param finalPendingStateNum Final pending state, that will be used to compare with the newStateRoot
* @param initNumBatch Batch which the aggregator starts the verification
* @param finalNewBatch Last batch aggregator intends to verify
* @param newLocalExitRoot New local exit root once the batch is processed
* @param newStateRoot New State root once the batch is processed
* @param proof Fflonk proof
*/
function proveNonDeterministicPendingState(
uint32 rollupID,
uint64 initPendingStateNum,
uint64 finalPendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
bytes32[24] calldata proof
) external ifNotEmergencyState {
4.2 覆盖某pending state
若某soundness漏洞被利用,Trusted Aggregator具有覆盖某non-deterministic pending state的能力。
为发起覆盖,使用overridePendingState
函数。由于Trusted Aggregator是本系统内的可信实体,当存在non-deterministic pending state时,仅Trusted Aggregator提供的L2 state root被认为是有效的可用于固化的。
/**
* @notice Allows the trusted aggregator to override the pending state
* if it's possible to prove a different state root given the same batches
* @param rollupID Rollup identifier
* @param initPendingStateNum Init pending state, 0 if consolidated state is used
* @param finalPendingStateNum Final pending state, that will be used to compare with the newStateRoot
* @param initNumBatch Batch which the aggregator starts the verification
* @param finalNewBatch Last batch aggregator intends to verify
* @param newLocalExitRoot New local exit root once the batch is processed
* @param newStateRoot New State root once the batch is processed
* @param proof Fflonk proof
*/
function overridePendingState(
uint32 rollupID,
uint64 initPendingStateNum,
uint64 finalPendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
bytes32[24] calldata proof
) external onlyRole(_TRUSTED_AGGREGATOR_ROLE) {
为成功覆盖某pending state,Trusted Aggregator必须提交可 以与proveNonDeterministicPendingState
内相同方式验证的proof。若该proof验证通过,则会抹掉该pending state transition,且直接固化新的state transition。
总之,紧急状态的激活方式为如下3种:
- 1)当合约owner认为有必要可触发
- 2)aggregation活动停止超过
HALT_AGGREGATION_TIMEOUT
- 3)任何人证明了某pending state是non-deterministic的。
参考资料
[1] Force batches
[2] Force verification
[3] Emergency state
附录:Polygon Hermez 2.0 zkEVM系列博客
- ZK-Rollups工作原理
- Polygon zkEVM——Hermez 2.0简介
- Polygon zkEVM网络节点
- Polygon zkEVM 基本概念
- Polygon zkEVM Prover
- Polygon zkEVM工具——PIL和CIRCOM
- Polygon zkEVM节点代码解析
- Polygon zkEVM的pil-stark Fibonacci状态机初体验
- Polygon zkEVM的pil-stark Fibonacci状态机代码解析
- Polygon zkEVM PIL编译器——pilcom 代码解析
- Polygon zkEVM Arithmetic状态机
- Polygon zkEVM中的常量多项式
- Polygon zkEVM Binary状态机
- Polygon zkEVM Memory状态机
- Polygon zkEVM Memory Align状态机
- Polygon zkEVM zkASM编译器——zkasmcom
- Polygon zkEVM哈希状态机——Keccak-256和Poseidon
- Polygon zkEVM zkASM语法
- Polygon zkEVM可验证计算简单状态机示例
- Polygon zkEVM zkASM 与 以太坊虚拟机opcode 对应集合
- Polygon zkEVM zkROM代码解析(1)
- Polygon zkEVM zkASM中的函数集合
- Polygon zkEVM zkROM代码解析(2)
- Polygon zkEVM zkROM代码解析(3)
- Polygon zkEVM公式梳理
- Polygon zkEVM中的Merkle tree
- Polygon zkEVM中Goldilocks域元素circom约束
- Polygon zkEVM Merkle tree的circom约束
- Polygon zkEVM FFT和多项式evaluate计算的circom约束
- Polygon zkEVM R1CS与Plonk电路转换
- Polygon zkEVM中的子约束系统
- Polygon zkEVM交易解析
- Polygon zkEVM 审计及递归证明
- Polygon zkEVM发布公开测试网2.0
- Polygon zkEVM测试集——创建合约交易
- Polygon zkEVM中的Recursive STARKs
- Polygon zkEVM的gas定价
- Polygon zkEVM zkProver基本设计原则 以及 Storage状态机
- Polygon zkEVM bridge技术文档
- Polygon zkEVM Trustless L2 State Management 技术文档
- Polygon zkEVM中的自定义errors
- Polygon zkEVM RPC服务
- Polygon zkEVM Prover的 RPC功能
- Polygon zkEVM PIL技术文档
- Polygon zkEVM递归证明技术文档(1)【主要描述了相关工具 和 证明的组合、递归以及聚合】
- Polygon zkEVM递归证明技术文档(2)—— Polygon zkEVM架构设计
- Polygon zkEVM递归证明技术文档(3)——代码编译及运行
- Polygon zkEVM递归证明技术文档(4)—— C12 PIL Description
- Polygon zkEVM递归证明技术文档(5)——附录:借助SNARKjs和PIL-STARK实现proof composition
- eSTARK:Polygon zkEVM的扩展STARK协议——支持lookup、permutation、copy等arguments(1)
- eSTARK:Polygon zkEVM的扩展STARK协议——支持lookup、permutation、copy等arguments(2)
- eSTARK:Polygon zkEVM的扩展STARK协议——支持lookup、permutation、copy等arguments(3)
- Polygon zkEVM的Dragon Fruit和Inca Berry升级
- Polygon zkEVM协议治理、升级及其流程
- Polygon zkEVM 节点软件release日志
- Polygon zkEVM bridge服务 release日志
- Polygon zkEVM DataStreamer
- Polygon zkEVM Goldilocks域各项运算性能
- Polygon zkEVM Hexens审计报告解读
- Polygon zkEVM Spearbit审计报告解读(2022年12月版本)
- Polygon zkEVM Spearbit审计报告解读(2023年1月版本)
- Polygon zkEVM Spearbit审计报告解读(2023年3月版本)
- Polygon zkEVM ROM Spearbit审计报告解读(2023年6月Dragon Fruit升级版本)
- Polygon zkEVM ROM Spearbit审计报告解读(2023年8月calldata bug修复)
- Polygon zkEVM PIL-STARK Spearbit审计报告解读(2023年3月版)
- Polygon zkEVM PIL-STARK Spearbit审计报告解读(2023年6月版)
- Polygon zkEVM 合约 Spearbit审计报告解读(2023年3月版)
- Polygon zkEVM的Etrog和Elderberry升级
- Polygon zkEVM 2024年3月22日网络宕机事故报告