本文提出了一种基于门票的 blob mempool 设计,通过垂直分片 mempool 和门票分配机制,实现 DoS 防护和提前采样,从而降低峰值带宽需求,并避免单独的 blob 包含层。门票通过 1559 机制出售,并可为上链交易退款。文章讨论了多种设计方案和权衡,包括签名、KZG 证明验证和哈希校验,并分析了在基于汇总的未来可能面临的挑战。
===== 开始 Markdown 内容 ===== 一年前修改一年前修改
核心思想是:
现在暂时不担心如何分配票证,只关注如何利用它们构建 blob 内存池。为此,我们只需要共识层以某种方式知道这些票证。假设 BeaconState 在循环数组 state.blob_tickets_record 中记录接下来 SLOTS_PER_EPOCH 个 Slot 的票证持有者。具体来说,state.blob_tickets_record[slot % SLOTS_PER_EPOCH] 以 BLS 公钥的形式记录 slot 的票证持有者。
class BeaconState(Container):
...
blob_tickets_record: Vector[List[BLSPubkey, MAX_BLOBS_PER_BLOCK], SLOTS_PER_EPOCH]
Blob 内存池完全存在于 CL p2p 网络上,包含一个全局主题 blob_transaction_envelope,用于共享 blob 交易,以及 NUMBER_OF_COLUMNS 个 mempool_cells 主题,用于共享与这些交易相关的 blob 样本及证明。在 Slot N 发送这些主题中的消息需要持有 Slot N 的票证,可以根据记录的公钥进行检查,即 pubkeys = state.blob_tickets_record[N % SLOTS_PER_EPOCH]。具体来说,消息必须包含 ticket_index 并使用 pubkeys[ticket_index] 签名。
blob_transaction_envelope
class BlobTransactionEnvelope(Container):
transaction: Transaction
kzg_commitments: List[KZGCommitments, MAX_BLOBS_PER_TRANSACTION]
slot: Slot
ticket_indices: List[uint8, MAX_BLOBS_PER_TRANSACTION]
class SignedBlobTransactionEnvelope(Container):
message: BlobTransactionEnvelope
signature: BLSSignature
注意:如果我们有 SSZ 编码的交易,可以从 BlobTransactionEnvelope 中移除 kzg_commitments,因为我们可以直接访问版本化哈希,并对照 cell 中的 KZG 承诺进行检查。如果没有,我们还需要将对应于 kzg_commitments 的版本化哈希提供给 EL,并让其检查它们是否与 transaction 中的一致。
设 blob_tickets = state.blob_tickets[slot % SLOTS_PER_EPOCH]。在转发之前,我们基本上只检查消息是否正确由未使用票证的持有者签署,要求:
ticket_indices 的票证持有者是同一人,即对于 ticket_indices 中的所有 ticket_index,blob_tickets[ticket_index].pubkey 相同。ticket_indices 中的每个 ticket_index,这是为该 ticket_index 收到的第一条有效消息。signature 是由 pubkey 对 message 的有效签名,其中 pubkey 是拥有所有票证的唯一公钥,即 blob_tickets[ticket_index].pubkey(对于 ticket_indices 中的任意 ticket_index)。mempool_cells_{subnet_id}
class CellSidecar(Container)
cell: Cell
index: CellIndex
kzg_proof: KZGProof
kzg_commitment: KZGCommitment
class MempoolCells(Container):
cell_sidecars: List[CellSidecar, MAX_BLOBS_PER_TRANSACTION]
slot: Slot
ticket_indices: List[uint8, MAX_BLOBS_PER_TRANSACTION]
class SignedMempoolCells(Container):
message: MempoolCells
signature: BLSSignature
注意:与 CellSidecar 相比,开销为 105 字节(主要来自签名),当 NUM_COLUMNS = 128 时约为 5%,当 NUM_COLUMNS = 256 时约为 10%。与在单个(签名)消息中发送带证明的 blob 相比,开销仅来自每个 cell 中的 kzg_commitment、cell_index、slot 和 ticket_index,每个 cell 总共 65 字节。
在转发之前,我们执行与 BlobTransactionEnvelope 主题完全相同的检查,基于 slot 和 ticket_indices,相当于检查消息是否正确由该 Slot 未使用票证的持有者签署。我们还要检查子网是否正确:
compute_subnet_for_cell_sidecar(cell_sidecar.cell_index) == subnet_id在 gossip 时,我们不要求以下检查,但要使 MempoolCells 对象有效(连同 gossip 检查),则需要这些检查:
cell_sidecar.cell 是通过 cell_sidecar.kzg_proof 在 cell_sidecar.cell_index 处打开 cell_sidecar.kzg_commitment 的结果。换句话说,我们不要求验证 cell sidecar 本身。原因在于,验证单个 cell 虽然相当便宜(约 3ms),但效率远低于批量验证(例如,整个 blob 约 16ms)。为了防止 DoS,检查签名就足够了。完整的验证可以在以后进行,一旦某个 blob 的所有 cell 都已检索到(或者,客户端可以自由安排验证时间)。只有当证明被验证后,这些 cell 才最终被视为有效。
由于验证签名比验证 cell 证明“仅”快约 2-3 倍,另一种设计可能是不对 MempoolCells 进行签名(节省 96 KB,约 10%),而是立即验证证明,无需等待批量验证。但是,节点只有在知道对应的 BlobTransactionEnvelope 时才会验证并转发 MempoolCells 对象,因为在这种设计中,签名验证(即通过票证限制内存池)只会发生在该对象中。
另一种设计是重新引入少量带宽开销,但完全消除验证开销:在 BlobTransactionEnvelope 中包含一个字段 mempool_cells_hashes: List[Root, NUMBER_OF_COLUMNS],存储与交易关联的所有 MempoolCells 对象的哈希。这样,一旦节点拥有了 BlobTransactionEnvelope,它就可以转发任何哈希匹配的 CellSidecar,而无需等待 KZG 证明验证,验证可以在方便批量处理时进行。这样就没有验证开销,而带宽开销仅为每个 cell 32 字节(假设最坏情况是单个 blob 的交易),比签名方式少 3 倍。
总结,至少有三种可能的设计,权衡如下:
| 验证开销 | 带宽开销 | 需要信封才能转发 cell | |
|---|---|---|---|
| 对 cell 签名 | 1ms | 每个 cell 96 KB | 否 |
| 验证 KZG 证明 | 2-3ms | 0 | 是 |
| 对照信封中的哈希检查 | 0 | 每个 cell 32 KB | 是 |
票证可以通过 EL 上的系统合约出售,实现类似 1559 的机制,很像 EIP-7002(EL 触发的提款)使用的机制。然而,可能更适合批量出售票证,时间跨度超过一个 Slot。例如,我们可以出售
目标和最大值可以设置为与 blob 交易费用市场相同的值,因为我们希望内存池有足够的容量来支持网络吞吐量,但不超过:理想情况下,我们不希望那些通过内存池但最终没有上链的交易。例如,最终我们可能每个 Slot 有 128 张票证的目标,以及 256 张的最大值。
注意:这种方法的一个弱点是“如果某个 Slot 被错过,IL 票证就被浪费了。我们可以为错过的 Slot 退款票证,但那样区块提议者可能会做诸如购买所有票证并故意错过该 Slot 之类的事情,如果它无法以足够利润转售它们的话。”
票证分配机制必须确保 DoS 抵抗性:我们只允许有限数量的 blob 交易进入内存池,因此只能分配有限数量的票证。如果我们能提前知道哪些发送者会将 blob 上链,我们就可以将票证分配给他们。但实际中我们当然没有这种先见之明,可能不得不为票证定价。
为了避免阻止 blob 交易发送者使用内存池,我们可以对上链的票证进行退款,同时不放弃 DoS 抵抗性:只有当你将相应的交易上链时,票证才是免费的,此时交易已经支付了费用,并且我们允许它进入内存池是可以接受的。然而,每当其交易上链时就退款票证可能会被滥用:通常每个 epoch 提交少量 blob 交易的人可以在一段时间内购买所有内存池票证,远远超出其自身需求,然后通过将其(真实的)blob 交易直接发送给构建者来慢慢获得退款。虽然这不算严重的攻击,但它仍然允许某人暂时阻止其他人访问内存池,这并不理想。为了防止这种情况,我们可以缩短退款期限。如果设置为一个 Slot,意味着你只有在 Slot N+1 上链交易时才能获得 Slot N 票证的退款,此时攻击向量就不再存在。本质上,你只有在证明自己确实有正当理由在 Slot N 使用内存池时才能获得退款。
先前的帖子探讨了协议出售 blob 票证的想法,这既可以限制对 blob 内存池的访问,也可以授予包含权利。想法是一石二鸟:获得一个抗 DoS 的垂直分片 blob 内存池,并确保它用于提前对 blob 交易进行采样,而不是在区块传播的关键路径上。
这个解决方案似乎非常适合当前的世界,其中排序主要由 L1 之外完成,因此 blob 提交对时间不敏感,因为它只是在已经存在的 Rollup 层面上确认链上信息。然而,在一个充满 Based Rollup 的未来,尤其是基于且原生的 Rollup 不断交互的情况下,事情可能会变得更加复杂。到那时,blob 交易允许访问 L1 和所有此类 L2 的统一状态,并且 blob 交易的包含时间和排序在这个扩展状态中与今天常规 L1 状态一样重要,例如,一个区块中的第一笔 blob 交易可能套利 L1 和多个 L2 上的 AMM。
在这样的世界中,提前出售 blob 交易的包含权利将类似于现在将某种部分区块构建神圣化,因为每个 blob 本质上是统一状态的部分区块。对于部分区块构建,由于状态争用导致的潜在问题范围很大,而且系统是否会崩溃回一个购买大多数票证的超级构建者尚不清楚。此外,时间游戏现在也适用于 blob 交易(再次强调,它们基本上就是这个统一状态的交易),因此几乎没有理由期望内存池传播会在整个 Slot 内发生,而不是尽可能晚地发生。 ===== 结束 Markdown 内容 =====
- 原文链接: hackmd.io/PnHbLie4Tm6vz-...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!
作者暂未设置收款二维码