Polygon zkEVM 抗故障机制

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 和 L1 PolygonZkEVM.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系列博客

  • 9
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值