Sequencer 选择加入、发现与通信

  • LimeChain
  • 发布于 2024-05-01 12:57
  • 阅读 107

本文主要介绍了在以太坊 L1 上,验证者如何注册成为 L2 rollup 的 sequencer,以及 sequencer 如何被发现和通信。文章详细描述了 sequencer 注册、激活、变更、停用的流程,并探讨了在 rollup 合约中实现确定性回退选择算法、乐观选择算法等关键机制,以支持基于 L1 验证者的 sequencer 选择。

排序器选择加入、发现和通信

TLDR

  • L1 提议者通过两步流程选择加入成为排序器 - 注册和激活。排序器在激活后的某个时间延迟后才有资格被选为排序器。
  • 为了验证请求,注册需要提议者验证器 BLS 密钥对签名的消息。此外,注册需要证明验证器仍然处于活动状态。
  • 注册的排序器会公布一个 RPC url 用于连接,以及一个执行层地址,在排序时代表它们。
  • 排序交易被乐观地接受,rollup 需要决定是否对排序强制执行进一步的排序器资格验证,或者实施挑战期。如果 Ethereum L1 允许从执行层直接访问 L1 提议者的 BLS 公钥,则挑战期可以替换为提交时对排序器选择的验证。
  • 如果 L1 提议者退出 L1 验证器集合,任何参与者都可以触发强制停用。

选择加入机制

为了使 L1 提议者有资格被选为 rollup 的排序器,他们必须将自己注册为 L2 排序器。注册过程包括:

  • 注册,公布他们的 RPC url、他们的验证器 BLS 公钥和他们的代表地址。
  • 在满足 rollup 定义的激活标准(例如,质押)后,激活注册的排序器

这两个操作都在 L1 上通过智能合约执行。

排序器注册合约

排序器注册合约是跟踪当前注册排序器、它们的元数据和它们状态的合约。

interface ISequencerRegistry {
    struct ValidatorProof {
        uint64 currentEpoch;
        uint64 activationEpoch;
        uint64 exitEpoch;
        uint256 validatorIndex;
        bool slashed;
        uint256 proofSlot;
        bytes sszProof;
    }

    struct Sequencer {
        bytes pubkey;
        bytes metadata;
        address signer;
        uint256 activationBlock;
        uint256 deactivationBlock;
    }

    /**
     *  注册排序器,但不激活它们。
     *
     *  授权操作。需要验证器通过其 BLS 公钥对摘要进行签名。
     *  需要 EIP-2537 才能验证签名。
     *
     *  Rollup 合约在强制执行主要或次要选择时将使用签名者地址。
     *  签名必须验证验证器,并且必须是对授权哈希的签名。
     *  授权哈希必须可由合约验证,并且必须包含一个 nonce 以防止重放攻击。
     *  nonce 的推导是实现者的决定,但可以像增量计数器一样简单。
     *  实现必须检查 authHash 是否为 keccak256(protocol_version,contract_address,chain_id,nonce)。
     *
     *  @param signer - 将通过其签名代表排序器的 secp256k1 钱包
     *  @param metadata - 排序器的元数据 - 包括但不限于版本和端点 url
     *  @param authHash - 授权哈希 - keccak256(protocol_version,contract_address,chain_id,nonce)。授权签名是通过对这些字节进行签名创建的。
     *  @param signature - 验证器密钥对 authHash 执行的签名
     *  @param validatorProof - 验证验证器在信标链状态树中存在的所需的所有数据
     */
    function register(
        address signer,
        bytes calldata metadata,
        bytes32 authHash,
        bytes calldata signature,
        ValidatorProof calldata validatorProof
    ) external;

    /**
     *  更改排序器签名者和/或元数据。
     *
     *  授权操作。适用与 `register` 中类似的要求
     *
     *  @param signer - 将通过其签名代表排序器的新钱包
     *  @param metadata - 排序器的新元数据 - 包括但不限于版本和端点 url
     *  @param authHash - 授权哈希 - keccak256(protocol_version,contract_address,chain_id,nonce)。授权签名是通过对这些字节进行签名创建的。
     *  @param signature - 验证器密钥对 authHash 执行的签名
     */
    function changeRegistration(address signer, bytes calldata metadata, bytes32 authHash, bytes calldata signature)
        external;

    /**
     *  激活排序器,完成注册过程
     *  实施者必须确保排序器在更改其状态之前满足激活(即,质押)要求。
     *
     *  @param pubkey - 验证器 BLS12-381 公钥 - 48 字节
     *  @param validatorProof - 验证验证器在信标链状态树中存在的所需的所有数据
     */
    function activate(bytes calldata pubkey, ValidatorProof calldata validatorProof) external;

    /**
     *  停用排序器。
     *
     *  授权操作。适用与 `register` 中类似的要求。
     *  质押过程的实施者必须确保排序器在分配提款之前不再处于活动状态
     *
     *  @param authHash - 授权哈希 - keccak256(protocol_version,contract_address,chain_id,nonce)。授权签名是通过对这些字节进行签名创建的。
     *  @param signature - 验证器密钥对 authHash 执行的签名
     */
    function deactivate(bytes32 authHash, bytes calldata signature) external;

    /**
     *  强制停用排序器。
     *
     *  调用者必须提供验证器不再处于活动状态或已被削减的证明。
     *
     *  @param pubkey - 验证器 BLS12-381 公钥 - 48 字节
     *  @param validatorProof - 验证验证器在信标链状态树中存在和状态所需的所有数据
     */
    function forceDeactivate(bytes calldata pubkey, ValidatorProof calldata validatorProof) external;

    /**
     *  用于获取由此公钥标识的排序器的资格状态
     *  @param pubkey - 验证器 BLS12-381 公钥 - 48 字节
     */
    function isEligible(bytes calldata pubkey) external view returns (bool);

    /**
     *  返回由此公钥标识的排序器的已保存数据
     *  @param pubkey - 验证器 BLS12-381 公钥 - 48 字节
     */
    function statusOf(bytes calldata pubkey) external view returns (Sequencer memory metadata);

    /**
     *  用于获取具有此签名者地址的排序器的激活状态
     *  @param signer - 排序器的关联签名者地址
     */
    function isEligibleSigner(address signer) external view returns (bool);

    /**
     *  返回排序器按索引的数据
     */
    function sequencerByIndex(uint256 index)
        external
        view
        returns (address signer, bytes memory metadata, bytes memory pubkey);

    /**
     *  激活后排序器获得排序资格的区块数
     */
    function activationTimeout() external view returns (uint8);

    /**
     *  停用后排序器变为不具备排序资格的区块数
     */
    function deactivationPeriod() external view returns (uint8);

    /**
     *  返回此区块号的排序器总数
     */
    function eligibleCountAt(uint256 blockNum) external view returns (uint256);

    /**
     *     返回用于授权摘要的协议版本。
     */
    function protocolVersion() external view returns (uint8);
}

授权

存储库中的某些功能只能由 L1 验证器本身触发。通过其 BLS 密钥对生成的签名 - signature - 用于验证验证器。签名超过 32 字节 - authHash

实现可以选择 authHash 是什么。一个建议是 keccak256(protocol_version,contract_address,chain_id,nonce,function_identifier,params_hash),其中:

  • protocol_version - 授权协议的版本
  • contract_address - 旨在授权此消息的合约地址 - 注册表合约
  • chain_id - 预期网络的 EIP155 链 ID - mainnet
  • nonce - 用作防重放攻击机制的数字。
  • function_identifier - 预期函数的功能标识符
  • params_hash - 函数参数的 keccak256 哈希 - 例如 signermetadata

实现必须通过对 signatureauthHash 的签名恢复过程来恢复调用者的 48 字节 BLS12 公钥 - pubkey请注意,在包含 EIP-2573 之前(目前计划用于 Pectra),无法进行 BLS 签名恢复。

接下来,合约必须检查验证器在当前验证器集合中的存在。实现可以通过两步过程来实现:

  1. 通过在 Dencun 升级中使用 EIP4788 引入的 BEACON_ROOTS_ADDRESS 合约,获取父信标链哈希树根 - beaconRoot,用于通过 proofSlot 参数指定的插槽。
  2. 通过 SSZ Merkle 包含多重证明 验证 pubkey 属于具有 beaconRoot 和提供的 validatorProof 的活动验证器,并执行共识规范中指定的 is_slashable_validator 函数的检查。

注册

L1 提议者可以通过触发 register 方法来启动注册过程。此方法需要授权,并且应符合上面概述的授权协议。

成功检查后,实现必须为排序器创建一个条目 - Sequencer。它应该将其 signer 地址和 metadata 字节与 pubkey 相关联。signer 是新排序器将用于操作的地址。它将是排序和提交预确认时预期的发送者地址。

metadata是有关于排序器的信息,以及外部参与者如何连接到它。 它应该包括但不限于版本和端点url。

激活

注册的排序器必须先激活才能有资格被选中。注册和激活之间的分离允许 rollup 协议嵌入特定的激活规则。这种激活规则的一个例子可以是排序器发布的最小质押或激活延迟。

激活通过 activate 方法执行。实施者必须检查是否满足激活规则。与注册类似,强制性检查是验证器是否处于活动状态。其他检查可能包括调用质押合约以检查是否已发布质押。

如果所有检查都通过,则实现必须将排序器 activationBlock 设置为当前区块号,并将其包含在排序器列表中。

注意:虽然排序器处于活动状态,但由于需要传递激活超时,因此尚未有资格被选中。它的作用类似于信标链强制执行的激活期。

更改注册和停用

如果注册的排序器想要更改其注册 - 即更新其元数据 - 他们可以调用授权的 changeRegistration 方法。实现必须将新的 signermetadatapubkey 相关联。

如果注册的排序器想要停止成为排序器,他们可以调用授权的 deactivate 方法。实现必须将排序器 deactivationBlock 更改为当前区块号,以便接口合约可以强制执行提款超时。

注意:虽然排序器已停用,但在停用超时期间过去之前,它仍然可以被选择算法选择。停用的选定排序器将错过他们的插槽。

撤回的验证器

让排序器成为活动的 L1 验证器是基于 vanilla 的排序的一个重要属性。在主要选择中,它确保了对包含排序交易的最高级别的审查抵抗。

一个可能的极端情况是,选择加入的排序器退出 Ethereum 验证器集合或被削减。为了涵盖这种情况,提供了一个公开可用的函数 forceDeactivation。调用者可以提供验证器不再处于活动状态的证明。实现必须将排序器 deactivationBlock 更改为当前区块号。

注意:虽然排序器已停用,但在停用超时期间过去之前,它仍然可以被选择算法选择。停用的选定排序器将错过他们的插槽。

激活和停用超时

为了确保可预测的超前排序器队列,后备选择的随机性选择算法需要在超前期间内稳定符合条件的验证器的总数。这需要强制执行激活和停用超时。选择区块数 X 超时,有效地指定了提议者的超前队列。

实施需要确保它们响应 function eligibleCountAt(uint256 blockNum) 方法,同时考虑新激活排序器的 activationBlock 和激活超时。

同样,后备机制的实现必须考虑在其 deactivationBlock 之后的超时区块内以及停用超时内的排序器。

使排序合约“基于 Vanilla”

所有 rollup 都有它们自己的排序合约。以下段落列出了需要对 rollup 合约进行的修改,以便支持基于 vanilla 的排序。

确定性后备选择

rollup 合约必须实现排序器的后备选择 - 从选择加入的提议者中选择一个随机排序器。

为了使此选择能够被链下复制,并为排序器和追随者提供足够的超前性,选择必须基于预先已知的属性。

一个例子可以是采用当前区块的第 X 个父区块的 blockhash 作为随机性算法的种子。使用 blockhash 是一个很好的选择,因为它的组成部分之一是信标链随机性信标,并且很难操纵。参数 X 指定了 rollup 提供给后续节点的超前性。

为了选择排序器,SequencerRegistry 合约提供了两种方法 - eligibleCountAtsequencerByIndex。通过这两种方法,从当前合格的排序器集合中选择一个随机的活动排序器。与 信标链提议者选择算法 类似,如果选择了已停用的排序器,则算法必须重试,直到选择了活动的合格排序器。

乐观选择算法

基于 vanilla 的 rollup 的主要要求之一是实现主要和后备排序器选择算法。排序器使用这些算法来识别他们是否以及何时被选中成为两种模式之一的排序器。

一种直观的方法是看到 L1 智能合约需要来自排序器的排序交易,这些排序器 1) 在主要选择的情况下不是当前的 L1 提议者,并且 2) 在后备选择的情况下不是选择的排序器。然而,目前在 Ethereum L1 中无法实现这种方法 - 执行层中缺少关于当前区块提议者的信息。

首先,无法通过 SSZ 多重证明来证明当前的提议者,因为 BEACON_ROOTS_ADDRESS 是父区块的 bacon 树哈希根 - 因此关于当前插槽的信息尚未可用。

其次,通过 COINBASE 操作码可用的地址无法与提议者确定性地联系起来。自从合并以来,这个操作码返回区块构建者设置的费用接收者,但这不一定是提议者

这两个障碍都禁止 rollup 合约在进行中拒绝排序提交。实现这种方法需要 L1 硬分叉才能在执行层中公开当前区块提议者。

为了解决这个问题,建议实现一种乐观的选择 - 链下执行选择算法,同时允许在之后证明和/或惩罚不合格排序器的提交。选择这种方法有几个论点。

首先,诚实的追随节点将始终在链下知道谁是有效的排序器,以及他们应该将哪个序列应用于他们的状态。

其次,这种乐观的方法“优化了快乐路径”。如果排序器诚实行事,则只有一个排序器将提交排序交易 - 选择的排序器。

为了解决恶意情况,可以选择两种解决方法。

第一个选择是引入挑战期(乐观 rollup 中已经存在)。如果排序器恶意行事,并且在未被选为排序器时提交排序交易,则可以证明他们的提交不合格(通过 SSZ merkle 多重证明),并且可以惩罚排序器,挑战者会得到奖励。

第二个选择是在后续区块中引入验证函数(或与 zk rollup 的证明交易一起)。此交易旨在为排序器提供资格证明(通过 SSZ merkle 多重证明) - 证明他们是正确选择的排序器。

这两种选择都会给排序流程带来缺陷,但可以立即实施。建议如果/当 Ethereum L1 提供了一种允许执行层访问当前区块提议者的方式时,实现切换到更安全的验证方法。

无论选择哪种乐观选择选项,这两种选择类型都需要它们各自不同的证明资格/不资格的方式。在主要选择的上下文中,证明必须是排序器是/不是排序交易进入的插槽的提议者的 SSZ 多重证明。在后备选择的上下文中,可以通过 L1 智能合约通过执行上一节中概述的确定性后备选择算法来确定性地实现选择验证。

Rollup 合约要求

  • 必须接受所有活动排序器的签名者的所有序列。这些序列必须表明它们旨在用于哪个 rollup 插槽。
  • 必须实现确定性后备选择算法。
  • 在执行选择时必须遵守激活和停用超时。
  • 必须提供提交和验证合格/不合格序列证明的方法。
  • 必须对不合格的提交强制执行惩罚,并对有效的符合条件序列证明提交给予奖励。

质押和与质押合约交互

许多 rollup 都有它们自己的排序合约。这种质押通常用作正确行为的密码经济激励。

与 SequencingRegistry 交互

虽然质押不是 rollup 成为“基于 Vanilla”的要求(也可以使用债券),但建议的 SequencerRegistry 接口在设计时考虑了质押。

首先,注册(通过 register 执行)和激活(通过 activate 执行)之间的分离为质押合约提供了一个有用的入口点。rollup 质押合约只有在满足质押要求时才能允许触发激活。

其次,Sequencer 数据中的 deactivationBlock 字段使质押合约能够强制执行提款条件。

关于质押的注意事项

在本节中,你可以找到在实施 rollup 质押机制时的一些重要注意事项。

首先,基于 vanilla 的排序不依赖于所质押的资产和金额。rollup 决定哪种资产以及应该质押多少资产才能满足激活标准。此外,rollup 设计者可以选择更复杂的质押机制,从而实现质押委托和/或验证器重新质押(即 EigenLayer 风格的重新质押和 LST)。

其次,在发布已停用验证器的质押时,重要的是要考虑最终确定的时间。由于最终确定是检测到大多数不当行为的时间(欺诈证明或证明无效),因此在使质押可提取之前,请务必考虑停用后的宽限期。

排序器发现和通信

与基于 vanilla 的 rollup 交互流程的重要部分是发现后续排序器。在本节中,概述了发现和与后续排序器通信的机制。

排序器发现

为了使钱包或用户能够发现当前排序器以及超前队列中的排序器,他们需要从 L1 信标链和 rollup SequencerRegistry 中获取几条信息。

首先,钱包需要确定接下来的 X 个插槽的已知 L1 提议者 - 其中 X 是由 rollup 团队设置的参数,理想情况下应该是一个 epoch(32 个插槽)。

其次,钱包需要获取当前符合条件的 L1 验证器的列表,并针对下一个 X 个 L1 提议者进行交叉检查。任何匹配都可以被认为是主要选择的排序器 - L1 提议者将成为此插槽的 L2 排序器。

对于 L1 提议者未选择加入成为排序器的插槽,钱包需要运行如上 “确定性后备选择” 部分中指定的确定性后备选择算法。请记住,这些排序器将提前 X 个插槽显示。随机选择的 L2 排序器将是相应插槽的排序器。

最后,钱包必须获取他们想要与之通信的排序器的 metadata 信息。

排序器通信

作为元数据的一部分,排序器必须通信一个 RPC 端点。此端点必须是排序器提交交易的 RPC 端点,并且它应服务的请求应包括但不限于:

  • 发送原始交易
  • 获取预确认承诺或拒绝
  • 任何其他 rollup 特定方法。

随着插槽的变化,排序器的 URL 也会发生变化。这是要由钱包软件处理的复杂性。

  • 原文链接: github.com/LimeChain/bas...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
LimeChain
LimeChain
江湖只有他的大名,没有他的介绍。