Skip to main content

维修服务

维修服务#

修复服务负责检索未能通过Turbine等主要通信协议传送的遗失碎片。 它负责管理下面维修协议部分中描述的协议。

挑战。#

1) 验证节点可能会因为网络故障而无法接收特定的碎片。

2) 考虑一个场景,blockstore包含一组插槽{1, 3, 5}。 那么Blockstore接收到一些插槽7的碎片,其中对于每一个碎片b,b.parent == 6,那么父子关系6 -> 7就存储在blockstore中。 但是,没有办法将这些插槽链到Blockstore中任何一个现有的库中,因此,碎片修复协议不会修复这些插槽。 如果这些插槽恰好是主链的一部分,这将停止该节点上的重放进度。

修复相关基元#

纪元插槽: 每个验证节点都在Gossip上分别广播纪元插槽的各个部分。

  • 储藏:一个以纪元为单位的所有已完成插槽的压缩集。
  • 缓存:最新的N个已完成的插槽的运行长度编码(RLE),从某个插槽M开始,其中N是一个MTU大小的数据包所能容纳的插槽数。

gossip中的Epoch Slots每当验证节点收到一个在epoch内的完整插槽时,就会更新。 已完成的插槽由blockstore检测,并通过通道发送到维修服务。 需要注意的是,我们知道当一个插槽X完成的时候,包含插槽X的纪元必须存在纪元时间表,因为WindowService会拒绝未确认的纪元的碎片。

每完成一个N/2插槽,最老的N/2插槽就会从缓存移到stash中。 RLE的基值M也要更新。

修复请求协议#

修复协议为进步Blockstore的分叉结构做了最好的尝试。

不同的协议策略来解决上述难题。

  1. Shred Repair(解决挑战#1):这是最基本的修复协议,目的是检测和填补账本中的 "漏洞"。 Blockstore会跟踪最新的根插槽。 然后,RepairService会从根插槽开始定期迭代blockstore中的每一个分叉,向验证节点发送修复请求,以获取任何缺失的碎片。 它每次迭代最多会发送一些N修复请求。 碎片修复应该根据领导者的分叉重量来优先修复分叉。 验证节点应该只向在其EpochSlots中标记该插槽已完成的验证节点发送修复请求。 验证节点应该优先修复他们负责通过涡轮重传的每个插槽中的碎片。 验证节点可以计算出他们负责重传的碎片,因为turbine的种子是基于leader id、slot和碎片 index的。

    注意:验证节点只接受当前可验证的epoch (验证节点有领袖时间表的epoch) 内的碎纸片。

  2. Preemptive Slot Repair(解决挑战 #2):这个协议的目标是发现 "孤儿 "插槽的链路关系,这些插槽目前没有链到任何已知的分叉。 碎片修复应该根据领导者的分叉权重来优先修复孤儿插槽。

    • Blockstore将在一个单独的列族中跟踪 "孤儿 "插槽的集合。

    • 修复服务将定期为blockstore中的每个孤儿提出Orphan请求。

      Orphan(orphan)请求--orphan是请求者想知道的孤儿插槽的父母Orphan(orphan)响应--请求的orphan的前N个父母的最高分叉量。

      在收到响应p时,其中p是父插槽中的一些碎片,验证节点将:

      • 如果它还不存在,那么就在区块存储中为p.slot插入一个空的SlotMeta
      • 如果p.slot确实存在,根据父辈更新p的父插槽。

      注意:一旦这些空插槽被添加到区块存储中,Shred Repair协议应该尝试填补这些插槽。

      注意:验证节点只会接受包含当前可验证的epoch (验证节点有领导时间表的epoch) 内的碎片的响应。

验证节点应该尝试将孤儿请求发送给在其EpochSlots中已将该孤儿标记为已完成的验证节点。 如果不存在这样的验证节点,那么就以利害关系加权的方式随机选择一个验证节点。

修复响应协议#

当验证节点收到一个碎片S的请求时,如果他们有碎片,他们就会响应。

当验证节点通过修复响应收到碎纸片时,他们会检查EpochSlots,看看是否有 <= 1/3 的网络已经将这个插槽标记为完成。 如果是这样,他们就会通过其相关的涡轮路径重新提交这个碎片,但前提是这个验证节点之前没有重传过这个碎片。