通过以cell为中心的纠删码/网络编码改进列的传播-网络通信

本文探讨了在以太坊Fusaka分叉后,如何通过改进列的传播来提高blob吞吐量。文章提出了几种基于纠删码(如RS和RLNC)和cell为中心的gossip协议,以优化列的传播效率。不同的方案在编码方式和gossip验证机制上有所差异,旨在实现更高效的网络传输和数据重建,同时考虑了计算成本、带宽效率和安全性。

Fusaka 分叉将引入基于 1D 列的数据可用性采样,其中每个样本都是一个由来自所有 blob 的单元格和证明组成的。在 Fusaka 之后,改进列的传播是进一步提高 blob 吞吐量的主要途径之一,到目前为止,为此提出的工具有从基于推送的 gossip 转向声明和拉取/推送混合,并引入某种形式的编码,如 RSRLNC。在这篇文章中,我们探讨了多种方法来实现后者,同时也使单个单元格成为传播的基本单元。感谢 @asn, @dankrad, @potuz, @MarcoPolo , @vbuterin 的讨论和反馈。

目标

  1. 高效传播。对列传播应用某种形式的纠删码,以在延迟-带宽权衡空间中解锁更好的曲线。
  2. 以单元格为中心的 gossip****。使单元格成为传播的单元:

    • 让节点可以使用 mempool blobs 来参与传播,即使它们只有其中(非连续的)一个子集(例如,即使在拉取单个 blob 时)
    • возможно 最终让我们转移到 1 条消息 = 1 个网络数据包。例如,有 256 列,1 个单元格 = 1 KB,即使使用其 KZG 证明也能放入一个数据包中。

请注意,2. 是一个相当强的约束。如果我们只想改进 CL 传播路径,而不考虑与 EL mempool 和分布式 blob 发布的交互,这将大大简化某些设计,因为我们可以使用更少、更大的块。但是,以下探索将此约束视为既定事实。

在文档的其余部分,除非另有说明,否则我们默认假设总共有 256 列和 128 个 blob。

方法概述

我们探索的设计主要在两个维度上有所不同:

  1. 编码方案: 如何引入冗余。我们始终将单元格视为编码方案的基本单元,并考虑两种可能性,这两种可能性都包括列中单元格的线性组合:

    • Reed-Solomon (RS) 编码: 将标准纠删码垂直应用于列(类似于 EIP-7594 中水平用于行的方式)。
    • 随机线性网络编码 (RLNC): 列中原始单元格的随机线性组合。
  2. Gossip 验证机制: 节点如何确保在重建之前,在 gossip 期间接收和转发的块的完整性和真实性。例如,我们可以将 DataColumnSidecar 分解成块并对其进行编码,但只有在重建整个消息后才能验证它,而我们需要每个块都是可独立验证的。我们通过以下方式之一确保可验证性:

    • KZG 证明验证: 每个单元格,包括编码后的单元格,都带有其 KZG 证明,该证明在 gossip 期间进行验证。编码单元格的 KZG 证明也是原始证明的线性组合,针对原始承诺的线性组合进行验证,利用 KZG线性
    • 单元格根验证: 该块提交到所有单元格根,包括原始单元格根和编码单元格根。每个列的单元格根列表都分布在各自的子网中,并针对块中的承诺进行验证。Gossip 验证包括针对相关列表的简单检查。这种方法不允许在传播期间进行重新编码,因为我们只能验证其根已提交并分发的单元格。
    • Pedersen: 提议者使用 Pedersen 向量承诺来提交到基本单元格,并且线性组合针对这些承诺进行同态验证。这是这篇文章中的方法,适用于列传播。

RS

从总体上看,我们通过 RS 编码(与水平扩展使用的相同)垂直扩展列,并 gossip 单个单元格。我们考虑两种方式,它们在确保传输块的完整性的方式上有所不同:

  1. RS + KZG:单元格带有 kzg 证明,在转发之前进行验证
  2. RS + 单元格根:发布者以简单的方式提交到单元格,例如,在相关子网中发送单元格根列表,并在块中提交到这些根。转发只需要根据这些根进行检查,而无需进行证明验证。

第二种选择对发布者来说计算成本要低得多,因为发布者不需要进行扩展证明的昂贵计算。

RS + KZG

每个 DataColumnSidecar 子网都替换为一个 CellSidecar 子网,其中所有对应于_垂直扩展_列的单元格都进行 gossip。此外,我们有一个单一的、全局的 HeaderSidecar 子网,用于 gossip 带有所有 kzg 承诺(以及包含证明)的签名头。HeaderSidecar 主题将具有高度的网格,例如 30,以确保它尽可能快地传播。我们可以在这里接受高重复因子,因为消息只有几 KB(大部分大小是由于 kzg_commitments,对于 128 个 blob 约为 6 KB)


class CellSidecar(Container)
    cell: Cell
    row_index: RowIndex
    column_index: ColumnIndex
    kzg_proof: KZGProof

class HeaderSidecar(Container):
    signed_block_header: SignedBeaconBlockHeader
    kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
    kzg_commitments_inclusion_proof: Vector[Bytes32, KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH]
CellSidecar 主题 (子网)

cell_sidecar_{subnet_id}

  1. 如果 subnet_idcompute_subnet_for_cell_sidecar(column_index) 不匹配,则拒绝
  2. CellSidecar 验证暂停,直到收到 BeaconBlockHeaderSidecar
  3. 一旦获得 row_indexkzg_commitment,如果 kzg 证明验证失败,则拒绝

在等待匹配的 BeaconBlockHeaderSidecar 时,既不能进行消息传递也不能进行转发。因此,每当对等方发送我们无法验证的单元格时,我们会向他们请求 HeaderSidecar(这些非常小,我们可以限制发送的并行请求数量)。我们还可以降低在超时后不响应此请求的对等方的分数,以惩罚发送不可验证的单元格。

收到 HeaderSidecar 后,可以灵活地安排所有 kzg 证明验证,以最大限度地利用批量验证的优势。

HeaderSidecar 主题(全局)

header_sidecar

  • 如果来自未来的 slot 或不是来自大于最新最终确定 slotslot,则忽略
  • 如果包含证明无效,则拒绝
  • 如果标头签名无效,则拒绝
  • 如果当前最终确定检查点不是 block_header.parent_root 的祖先,则拒绝
  • 如果它不是具有有效标头签名和边车包含证明的元组 (block_header.slot, block_header.proposer_index, sidecar.index) 的第一个边车,则忽略
  • 如果提议者不是由 block_header.parent_root 定义的当前混洗的上下文中 block_header.slot 的预期提议者,则拒绝。
扩展单元格的 KZG 承诺

HeaderSidecar 仅包含原始单元格的 kzg 承诺。验证扩展单元格需要我们插入原始承诺,即计算 C' = M_{RS} \cdot C 其中 C = (C_1, \dots, C_{128}),原始承诺,而 M_{RS} 是 RS 矩阵。这需要 128 个(每个承诺一个)G1 中的 128 点 MSM,大约 ~130 毫秒多线程(在 zka.lc 上每个 128 点 MSM 约 1 毫秒)。请注意,该计算每个节点只需要完成一次,即在首次收到 HeaderSidecar 时。

原则上,我们可以避免在每个节点上重复计算。HeaderSidecar 中的 kzg_commitments 可以包含_所有承诺,包括扩展承诺。需要验证这些承诺与原始承诺的一致性,但这可以通过选择随机标量向量 r 并检查 r^TC' = (r^T M\{RS}) \cdot C 来更快地完成,而不是重新计算。这只需要两个 G1 中的 128 点 MSM,约 2 毫秒。当然,权衡在于带宽,因为这将意味着 HeaderSidecar 中另外 ~6 KB。

计算成本

最昂贵的操作是插值原始证明以计算扩展证明。对于每列,这与我们讨论的 KZG 承诺扩展的操作完全相同,即 128 个(每个证明一个)G1 中的 128 点 MSM,以计算 \pi_j' = M_{RS} \cdot \pi_j 其中 \pi_j = (\pi_{1j}, \dots, \pi_{128j}),列 j的原始证明向量。提议者必须_在关键路径中为每一列_执行此操作,或者换句话说,它必须_为每个证明执行一个 128 点 MSM这似乎非常昂贵,因为它必须计算 128 \times 256 = 32768 个证明。使用来自 zka.lc 的数字,即使是多线程,也需要 ~34 秒。通过用 2 个 FFT 替换与 M\{RS} 的乘法可以进一步加速 ~3 倍,但这仍然使我们远离可接受的成本。此外请注意,即使我们放弃将 CellSidecar 放入单个数据包的想法,我们也不能低于 128 列,这只会使成本减半。

在发布步骤中消除如此高的计算成本正是原始 blobs 的证明计算已通过将其外包给 tx 发送者而从关键路径中移出的原因。另一方面,垂直扩展的证明计算既不能外包,也不能像我们从 mempool 获取 blobs 那样预先计算,因为我们需要所有将进入块的 blobs 才能执行此操作。

RS + 单元格根

让我们考虑一种不同的方法,通过使用一种更简单的方式来确保单元格的完整性,从而避免了计算扩展证明的高成本。纠删码仅限于单元格,因此 gossip 验证不需要 KZG 证明验证(甚至对于原始单元格也不需要)。相反,它依赖于针对 cell_roots 列表进行检查,该列表作为 ColumnSidecar 的一部分在同一主题中单独发送。反过来,这是针对 HeaderSidecar 中包含的列边车根列表进行验证,并在块中提交。


## 列主题

## 没有 kzg 证明
class CellSidecar(Container)
    cell: Cell
    row_index: RowIndex
    column_index: ColumnIndex

## 具有所有单元格承诺并携带证明的新边车
class ColumnSidecar(Container):
    column_index: ColumnIndex
    cell_roots: List[Root, 2 * MAX_BLOB_COMMITMENTS_PER_BLOCK]
    kzg_proofs: List[KZGProof, MAX_BLOB_COMMITMENTS_PER_BLOCK]

## 全局主题

## 添加对列边车的承诺
class HeaderSidecar(Container):
    signed_block_header: SignedBeaconBlockHeader
    column_sidecar_roots: List[Root, NUMBER_OF_COLUMNS]
    column_sidecar_roots_inclusion_proof: Vector[Bytes32, COLUMN_SIDECAR_ROOTS_INCLUSION_PROOF_DEPTH]
    kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
    kzg_commitments_inclusion_proof: Vector[Bytes32, KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH]

class BeaconBlockBody(Container):
    ...
    # 需要一个更好的名字
    column_sidecar_roots_root: Root

与之前一样,我们在全局主题中传播 HeaderSidecar。除了之前的内容外,它现在还包含一个根列表 column_sidecar_roots,以及针对 header.body_root 的包含证明 column_sidecar_roots_inclusion_proofBeaconBlockBody 本身仅包含对 column_sidecar_roots 的承诺,即 column_sidecar_roots_root = hash_tree_root(column_sidecar_roots)

在给定列的子网中,我们传播:

  1. column_sidecar,包含 cell_roots,提交到给定列的所有单元格,包括扩展单元格的单元格,并且还包含所有 kzg_proofs。对于 gossip 验证,我们检查 hash_tree_root(column_sidecar) == header_sidecar.column_sidecar_roots[column_sidecar.column_index]。请注意,kzg_proofs _未_作为 gossip 验证的一部分进行验证。
  2. 对应于此列的 cell_sidecar。对于 gossip 验证,我们检查 column_sidecar.cell_roots[cell_sidecar.row_index] == hash_tree_root(cell_sidecar.cell)

一些注意事项:

  • 发布者不需要进行任何繁重的计算,只需对单元格进行简单的纠删码(不在群中)和散列。
  • 由于我们针对 cell_roots 验证单元格边车(本身又针对 column_sidecar_roots 验证),因此我们不需要验证 kzg_proof 作为转发的一部分,因此 kzg 验证不会减慢传播速度。
  • 在认为列可用(并证明)之前,我们需要验证列的所有 kzg 证明。kzg 证明包含在 ColumnSidecar 中,并且可以在收到相应的单元格后立即进行验证。由于 kzg 证明验证与转发分离,因此可以在最大限度地利用批处理的同时完成此操作。
  • 就像在 RS + KZG 方案中一样,与 getBlobs 的交互是理想的:当我们从 mempool 检索 blob 时,我们会为每列获得一个完整的 CellSidecar,我们可以立即传播
  • RS + KZG 方案的所有好处也适用于此处。
没有 Blob 数据的提议者

RS + KZG 案例以及 Fusaka 协议中,_以某种方式_知道与 blob txs 列表关联的 blobs 可用,但没有下载它们的提议者只需包含这些 txs,以及相应的 KZG 承诺,并完全依赖分布式 blob 发布来在 CL 上传播实际的 blob 数据(无论是单元格还是数据列边车)。例如,如果一个委员会证明了 mempool txs 的可用性,情况可能就是这样。

另一方面,此协议不允许提议者这样做,因为为了创建 column_sidecar,需要具有单元格(或更准确地说,cell_roots)和 kzg_proofs,而这些对于cell_sidecar 传播的发生是必需的。请注意,column_sidecar 也必须在块中提交,因此提议者确实需要在块创建时拥有它们。

RLNC

在这里,我们考虑两种方法来采用这个提案来实现我们列传播的目标。在这两种情况下,我们都使用单元格级别的块,主要的区别在于我们如何在传播期间验证块:一种选择是重用 KZG 证明,另一种选择是坚持使用 Pedersen 承诺,如帖子中所述。

RLNC + KZG

我们可以利用 KZG 承诺和证明的线性性质来为列传播启用 RLNC,同时保持与原始 KZG 行承诺相关的重组块的可验证性:

  • 由于我们最初以单元格为中心的 gossip 目标,块对应于单元格。它们通过 kzg 证明进行传输和验证,因此对块进行随机线性组合也需要对关联的证明进行相同的线性组合,以生成重组块的证明。
  • 验证一个块需要计算相应的 KZG 承诺,它是原始承诺的线性组合,具有给定的标量。
协议
  1. 基本块: RLNC 的基本单元(块)是一个单元格,原始单元格是我们想要传输的基本块。让 v_{ij} 表示第 i 行和第 j 列的单元格的数据有效载荷(字段元素)。与 v_{ij} 相关联的是其 KZG 证明 \pi_{ij}(证明针对行承诺 C_i 的评估)。
  2. 编码块和表示: 通过 RLNC 传播的任何块 v 都表示该 j 列的基本块的线性组合,v = \sum_i s_i v_{ij}。每个块 v 都与其对应的聚合 KZG 证明 \pi = \sum_i s_i \pi_{ij} 和定义组合的标量向量 s = (s_i)_i 一起传输。对于基本块 v_{ij},标量向量 s 只是由 s_k = 1 如果 k=i 并且 0 否则给出。
  3. RLNC 编码(重新编码): 拥有列 j 的一组输入块 (v_k, \pi_k, s_k)_k 的节点(其中每个 s_k = (s_{ki})_i 因此 v_k = \sum_i s_{ki} v_{ij} 并且 \pi_k = \sum_i s_{ki} \pi_{ij}) 可以创建一个新的编码块。它选择一组随机标量 (r_k)_k 并计算:

    • 编码数据: v = \sum_k r_k v_k。
    • 新标量: 新的标量向量 s' = (s'_i)_i 使得 v = \sum_i s_i' v_{ij}。为此,s'_i = \sum_k r_k s_{ki}。
    • 编码证明: \pi = \sum_k r_k \pi_k。(请注意,这等效于 \pi = \sum_i s'_i \pi_{ij})。
  4. RLNC 数据包: 节点 gossip 包含 (v, \pi, s') 的数据包。为了适应现有的边车结构,这可以表示为 CellSidecar,其中 cell 保持 vkzg_proof 保持 \pi,新的 rlnc_scalars 字段保持 s。保留 column_index,但 row_indexrlnc_scalars 替换,因为数据包表示行的组合。

class CellSidecar(Container):
       cell: Cell
       column_index: ColumnIndex
       kzg_proof: KZGProof
       rlnc_scalars: List[Bytes32, MAX_BLOB_COMMITMENTS_PER_BLOCK]
  1. 验证: 节点接收列 j 的 (v, \pi, s)
    • 它从 HeaderSidecar 中获得原始行承诺 C_i(与 RS + KZG 方法相同)。
    • 它计算组合承诺:C = \sum_i s_i C_i。
    • 它使用对应于 jcolumn_index)的评估点,针对编码数据 v 和组合承诺 C 验证 KZG 证明 \pi。
标量大小和开销

RLNC 的一个问题是传输标量向量 s = (s_i)_i(rlnc_scalars) 的开销。让 N 为基本块的数量(每列的原始行/单元格,例如 128)。

  • 全尺寸标量: 如果 s_i 是完整的 32 字节标量,则开销为 32N 字节,即 128 \times 32 = 4096 字节,成本高昂。
  • 小标量: 在重组块时,我们可以始终从较小的范围(例如,1 字节,0-255)中选择新的标量 r_k。这仍然使我们有很高的概率发送有用的块(如果可能),\ge 1 - 1/256 \approx 99.6%。初始开销较低(例如,N \times 1 = 128 字节,使用 1 字节标量)。
  • 标量膨胀: 重新编码计算 s'_i = \sum_k r_k s_{ki}。如果 s_{ki} 需要 B_s 位,并且 r_k 需要 B_b 位,则 s'_i 大约需要 B_s + B_r + \log_2(m) 位,其中 m 是组合的项数。位长度大致随重新编码跳数 (h) 线性增长。如果从 1 字节标量 (B=8) 开始,并假设传播需要 h=4 跳,则标量在最后一跳可能需要大约 (h+1)B \approx 5 字节(40 位)。对于 N =128,_最后一跳_的总开销将为 128 \times 5 = 640 字节。
减少标量膨胀

我们还可以尝试不同的策略来减少膨胀,例如:

  1. 只有发布者重新编码,以便具有初始冗余,但始终保持相同的 1 字节标量。换句话说,我们将纯粹将随机线性编码用作纠删码的替代品,唯一的优点是我们可以权衡 RS 编码的 FFT 优化及其更强的保证(知道任何 50% 的块_保证_能够重建)以使用更小的标量。特别是,我们得到:

    • 发布者的计算负载降低 32 倍。正如我们已经看到的 RS + KZG 和我们将在下一节中讨论的那样,发布者的负载是这些方案的一个巨大问题
    • 提议者使用的小标量可以提前约定。同样,我们只是用另一个 1 字节标量矩阵替换 RS 代码矩阵。然后,一旦收到带有原始承诺的 HeaderSidecar,就可以只计算一次组合承诺。此外,由于代码是提前知道的,我们不需要在消息中包含标量,这避免了每个单元格 128 B 的开销。
  2. 仅重新编码至少 M 个块,对于某些 M 需要通过实验确定。这里的想法是在传播的早期阶段直接转发块,此时重复的可能性较小,并且仅在最有用时才进行重新编码(与 push-pull 阶段转换 有些相关)。这样,前几跳根本不会膨胀标量。另请注意,进行重新编码的部分原因是它可以让你避免在带宽受限时需要选择哪些块优先:如果你没有带宽受限,你可以始终将拥有的所有块发送给每个对等方,但如果你受到带宽限制,最好发送尽可能多的_重新编码的_块,以最大限度地提高你发送的块对接收者有用的机会。当你还没有很多块并且可以转发所有块时(并且可能接收者的下行链路也没有饱和),重新编码就没有那么有用了。
计算成本
  • 发送者: 要使用系数 r_k 组合 m 个收到的数据包 (v_k, \pi_k, s_k),大部分成本是计算 \pi = \sum r_k \pi_k,这需要一个 m 项 G1 MSM。最坏情况下 m = 128。正如我们在 RS + KZG 案例中已经看到的那样,这在 zka.lc 上大约需要 ~1 毫秒,已经是多线程的。这是对于完整标量。使用 1 字节标量,成本降低 32 倍,接近可忽略不计。
  • 接收者: 要验证收到的数据包 (v, \pi, s),其中 s 具有 m 个非零条目,大部分成本是:

    • 计算 C = \sum_i s_i C_i,这再次需要一个 m 项 G1 MSM (~1 毫秒)
    • 验证 (v, \pi, C) 的 kzg 证明,需要 2 个配对 (~1.5 毫秒)

    请注意,即使我们使用 1 字节标量,这里的 s_i 也不是新生成的标量,因此会受到膨胀的影响,而发送者完成的证明的线性组合直接使用新生成的小标量,因此始终保持相同的加速。但是,即使有一些膨胀,配对也将在此处占据主导地位。

  • 提议者: 要为一列生成 R 个编码块,提议者会产生数据包发送者最坏情况下的成本 R 次,即 R 128 项 G1 MSM。它需要_为每一列执行此操作,因此执行 256 次。为了容忍 ~50% 的块丢失,我们需要 R = 256,因此每列的成本为 256 \times (128\text{-term MSM})。或者,提议者也可以发布原始块(基本单元格 v_{ij} 及其证明 \pi\{ij}),并且仅生成 R = 128 个编码块(或更少,具体取决于所需的冗余是什么)。

使用后一种策略,提议者的成本与 RS + KZG 中的成本相同,这很有意义,因为我们本质上也在做一个 1/2 速率的块纠删码,但代码是随机的。与 RS + KZG 不同,我们甚至无法从 FFT 中受益,因为代码不是以这种方式结构的。但是,如果我们使用 1 字节标量而不是完整(32 字节)标量,则会有一个很大的优势,因为这应该会导致 MSM 大约加速 ~32 倍,从而使成本降至 ~1 秒。

RLNC + Pedersen

现在让我们讨论来自 RLNC ethresearch 帖子的方法,该方法使用 Pedersen 承诺来保证重新编码块的完整性。以下很多内容都是对帖子中的内容的重新陈述,针对传播列的特定情况,并推理出具体的开销。

协议
  1. 基本块和承诺: 基本块仍然是一个单元格 v_{ij},被视为 M=32 个字段元素 (v_{ijk})_k 的向量(对于 256 列)。提议者使用 M=32 个固定生成器点 G_l 为每个基本单元计算一个 Pedersen 向量承诺:C_{ij} = \sum_{l=1}^{32} v_{ijk} G_l。这些原始承诺 C_{0j}, ..., C_{N-1, j}(其中 N=128 是每列的原始单元的数量)可以像 RS + 单元格根 方法的 cell_roots 一样,分布在 ColumnSidecar 中,该 ColumnSidecar 本身在块中提交。

class ColumnSidecar(Container):
       column_index: ColumnIndex
       pedersen_commitments: List[PedersenCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
       kzg_proofs: List[KZGProof, MAX_BLOB_COMMITMENTS_PER_BLOCK]
  1. 编码块和表示: 传播的任何块 v 都表示列 j 的基本块的线性组合,v = \sum_i s_i v_{ij}。
  2. RLNC 编码(重新编码): 一个节点接收输入块 (v_k, s_k)_k(其中 s_k = (s_{ki})_i 定义 v_k = \sum_i s_{ki} v_{ij})。它选择随机标量 (r_k)_k,并计算:

    • 编码数据: v = \sum_k r_k v_k。
    • 新标量: s' = (s'_i)_i 其中 s'_i = \sum_k r_k s_{ki}。
  3. RLNC 数据包: 节点 gossip (v, s'),采用这种形式:

class CellSidecar(Container):
       cell: Cell
       column_index: ColumnIndex
       rlnc_scalars: List[Bytes32, MAX_BLOB_COMMITMENTS_PER_BLOCK]
  1. 验证: 节点接收 (v, s')。

    • 它从 ColumnSidecar 中获取列 j 的原始基本 Pedersen 承诺 C_{ij}。
    • 它基于标量计算预期的组合承诺:C' = \sum_i s'_i C_{ij}。
    • 它直接计算 v 的承诺:C = \sum_{l=1}^{32} v_l G_l。
    • 它验证 C' = C 是否成立。
计算成本
  • 提议者:
    • 必须为每个原始单元(i=0..N-1)计算一个 Pedersen 向量承诺 C_{ij} = \sum_{l=1}^{32} a_{ij,l} G_l。这需要每个原始单元在组中进行一个 M 项(32 项)的 MSM,并且每列需要 128 个这样的 MSM。对于所有 256 列,仍然有 32768 个 MSM,如 RS + KZGRLNC + KZG 案例中一样,但每个 MSM 有 32 项而不是 128 项。仍然非常昂贵。
  • 发送者(中间节点重新编码): 只有便宜的数据和标量算术 (v = \sum r_k v_k, s' = \sum r_k s_k)。没有组算术,成本可忽略不计。
  • 接收者(验证数据包): 组中一个 128 项的 MSM (\sum_i s'_i C_{ij}),以及组中一个 M 项(32 项)的 MSM (\sum_{l=1}^{32} v_l G_l)

几个评论:

  • RLNC + KZG 案例不同,使用 1 字节标量_不会_降低发布者的计算开销,因为这是由于必须计算对原始单元的 Pedersen 承诺,与重组块无关
  • 就像我们已经将 kzg 证明计算外包给 tx 发送者 一样,原则上我们可以外包 Pedersen 承诺的计算,从而使提议者的计算负载非常低。

好处

让我们简要讨论一下这类设计的优点,而不关注由于 RLNCRS 等等造成的任何差异。

发布

要发布 blob 数据,提议者/构建者可以执行以下操作:

  1. 首先发送 HeaderSidecar
  2. 对于每一列,通过将每个单元发送给单个对等方,将所有相应的单元边车分发给该子网中的对等方,例如,如果该块包含 60 个 blobs(因此扩展列有 120 个单元),并且在给定子网中有 20 个对等方,则向每个对等方发送 6 个单元。

这只需要提议者发送原始数据的 4 倍(由于 DAS 的水平扩展,原始数据的 2 倍,然后传播中的冗余,又增加了 2 倍),而不是当前的 16 倍(同样的,由于 blobs 的水平扩展,原始数据的 2 倍,然后每列发送给 8 个对等方)。

请注意,发布步骤不能仅通过从推送转换为声明和拉取来改进,因为来自发布者的所有消息都将被拉取。一个更有趣的比较将是与当前系统进行比较,通过推送发送完整的列,并通过 批量发布 来增强,即优先在每个子网中发布每列的第一个副本。即使这样,为每列至少提供一些冗余也需要发布每列的至少 2 个副本,因此发送 4 倍的数据,就像使用纠删码一样。以相同的带宽成本,对列进行纠删码更加健壮,并且更不容易被一些故障减慢速度。

发布后的传播

总的来说,纠删码使我们能够拥有高水平的冗余和高分支因子( 方法 发布者计算成本 每跳工作量 每个单元格的额外字节
RS + KZG 非常高 ≈ 34s,RS 编码 proofs (32 768 × 128-pt G1 MSMs 或 512 G1 FFTs) 发送:仅转发 <br> 接收:≈ 1.5ms,验证 KZG proof (2 pairings) : +48 B (KZG proof), ~5%
带有 1 字节标量的随机线性码 + KZG 中等 ≈ 1s (32× 更快的 MSMs) 发送:仅转发 <br> 接收:≈ 1.5ms,验证 KZG proof (2 pairings) : +48 B (KZG proof), ~5% (固定码,不传输标量)
RS + 单元格根 (RS 编码单元格,无 group MSMs) 可忽略不计 : 在单元格 sidecar 中: 0 <br> 带有列 sidecar 的总计: ~11%
RLNC + KZG 非常高 ≈ 68s (65 536 × 128-pt G1 MSMs) 发送:128-pt G1 MSM (≈ 1ms) <br> 接收:128-pt G1 MSM + 2 pairings (≈ 2.5ms) 非常高: +4.1 kB (标量向量) +48 B (proof), ~4x 开销
RLNC + KZG (1 字节标量) 中等 ≈ 1s (32× 更快的 MSMs) 发送:低 (带有 1 字节标量的 MSM) <br> 接收:即使有标量膨胀,成本主要由 2 pairings 主导 (≈ 2.5ms) : 最后一个跃点 +640 B,高达 ~60%
RLNC + Pedersen (1 字节标量) 非常高 ≈ 22s (curve25519 中的 32 768 × 32-pt MSMs) 发送:可忽略不计 <br> 接收:≈ 3ms curve25519 中的一个 128-pt + 一个 32-pt MSM 最后一个跃点 +640 B,高达 ~60%
RLNC + Pedersen (1 字节标量,txs 包括 Pedersen 承诺) (无 group MSMs) 发送:可忽略不计 <br> 接收:≈ 3ms curve25519 中的一个 128-pt + 一个 32-pt MSM 最后一个跃点 +640 B,高达 ~60%

附录

KZG 承诺和 proofs 的线性性

KZG 承诺和 proofs 具有非常有用的线性性质,我们在 RS + KZG 和 RLNC + KZG 中都使用了它,以便为扩展/重编码单元格生成 proof:

考虑评估点 (x_j)_j 和 (值, 承诺, proof) 三元组 ((y_{ij})_j, C_i, \pi_i),证明对于所有 j,p_i(x_j) = y_{ij},对于多项式 p_i 使得 [p_i(s)] = C_i。给定标量 (r_i)_i,三元组 ((y_j)_j, C, \pi) = ((\sum_i r_i y_{ij})_j, \sum_i r_i C_i, \sum_i r_i \pi_i) 使得 C = [p(s)] 是对多项式 p(x) = \sum_i r_i p_i(x) 的承诺,并且 \pi 是一个有效的 proof,证明对于所有 j,p(x_j) = y_j。

对于感兴趣的读者,这里有一个简短的解释,主要基于 这个很棒的参考

> 令 I_i(x) = \sum_j y_{ij} \prod_{k\neq j} \frac{x-x_k}{x_j - x_k} 为 (x_j, y_{ij})_j 的插值多项式,Z(x) = \prod_j{(x - x_j)} 为 (x_j)_j 上的消失多项式。p_i(x) 在 (x_j)_j 的 proof 为 \pi_i = [q_i(s)],其中 q_i(x) = \frac{p(x) - I_i(x)}{Z(x)}。
>
> I(x) = \sum_i r_i I_i(x) 是 p(x) = \sum_i r_i p_i(x) 在 (x_j, y_{ij})_j 上的插值多项式,因为 I(x_j) = \sum_i r_i I_i(x_j) = \sum_i r_i p_i(x_j) = p(x_j)。
>
> 那么,生成 p(x) 在 (x_j)_j 的 proof 的商多项式为 q(x) = \frac{p(x) - I(x)}{Z(x)} = \frac{\sum_i r_i p_i(x) - \sum_i r_i I_i(x)}{Z(x)} = \sum_i r_i\frac{p_i(x) - I_i(x)}{Z(x)} = \sum_i r_i q_i(x)。proof 为 [q(x)] = [\sum_i r_iq_i(x)] = \sum_i r_i[q_i(x)] = \sum_i r_i\pi_i。

验证理由

乍一看,单元格 sidecars 似乎很危险,因为它们可以是完全任意的数据片段,不与任何区块或 slot 或提议者相关联。但是,首先请注意,恶意节点不会获得将其 mesh peers 之外传播坏单元格(未与任何区块关联)的能力,因为转发需要等待 HeaderSidecar,并且可能还需要 ColumnSidecar,具体取决于哪个设计,并执行验证以证明该单元格确实与区块相关联。恶意节点可以做的只是向 mesh peer 发送一个坏的单元格 sidecar。

似乎即使这种较弱形式的恶意行为也不可能通过 DataColumnSidecar 实现,至少在不被降分的情况下,因为列 sidecar 包含与 HeaderSidecar 等效的内容,因此可以独立验证。事实上,情况并非如此:列 sidecar 的验证取决于对其包含的已签名信标区块头的验证,而这又依赖于其链:如果你不知道标头所属链的 shuffle,则无法验证它。

具体来说,peer 总是可以向你发送带有有效签名但编造的父根的标头,这样你就永远无法检索其链并确定提议者是否是正确的。因此,你也不能确定它是无效的,并且任何降分最多都必须取决于时间,例如,如果消息长时间无法验证,则降分,因为你无法检索其依赖项。这与节点接收到单元格 sidecar 且无法检索相应的 HeaderSidecar 并且可能无法检索 ColumnSidecar 的情况完全相同。

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

0 条评论

请先 登录 后评论
以太坊中文
以太坊中文
以太坊中文, 用中文传播以太坊的最新进展