Filecoin的区块链同步

发布时间:2019年02月25日 价值:20000.00 / 共识:20

Chain Syncing Filecoin的区块链同步

什么是Filecoin中的链同步?

链同步是一个Filecoin节点运行以将其内部链状态与来自网络的新块以及它自己挖掘的新块同步的过程。节点以两种不同的模式同步:syncing和caught up。链同步更新链数据的本地存储和当前最重的观察链的区块链头部。

“同步”模式和“追赶”模式是两个不同的过程。“同步”模式或“初始同步”是当节点远远落后于网络其余部分时触发的过程。一旦节点“头部”足够远,该过程就终止。“同步”完成后,“追赶”同步过程就开始了。此过程使节点与网络的其余部分保持同步,并仅在节点关闭时终止。

接口

  1. type Syncer struct {
  2. // The heaviest known tipset in the network.
  3. head TipSet
  4. // The interface for accessing and putting tipsets into local storage
  5. store ChainStore
  6. // The known genesis tipset
  7. genesis TipSet
  8. // the current mode the syncer is in
  9. syncMode SyncMode
  10. // TipSets known to be invalid
  11. bad BadTipSetCache
  12. // handle to the block sync service
  13. bsync BlockSync
  14. // peer heads
  15. // Note: clear cache on disconnects
  16. peerHeads map[PeerID]Cid
  17. }
  18. const BootstrapPeerThreshold = 5
  19. // InformNewHead informs the syncer about a new potential tipset
  20. // This should be called when connecting to new peers, and additionally
  21. // when receiving new blocks from the network
  22. func (syncer *Syncer) InformNewHead(from PeerID, head TipSet) {
  23. switch syncer.syncMode {
  24. case Bootstrap:
  25. go SyncBootstrap(from, head)
  26. case CaughtUp:
  27. go syncer.SyncCaughtUp(blk)
  28. }
  29. }
  30. // SyncBootstrap is used to synchronise your chain when first joining
  31. // the network, or when rejoining after significant downtime.
  32. func (syncer *Syncer) SyncBootstrap() {
  33. syncer.syncLock.Lock()
  34. defer syncer.syncLock.Unlock()
  35. syncer.peerHeads[from] = head
  36. if len(syncer.peerHeads) < BootstrapPeerThreshold {
  37. // not enough peers to sync yet...
  38. return
  39. }
  40. selectedHead := selectHead(syncer.peerHeads)
  41. cur := selectedHead
  42. var blockSet BlockSet
  43. for head.Height() > 0 {
  44. // NB: GetBlocks validates that the blocks are in-fact the ones we
  45. // requested, and that they are correctly linked to eachother. It does
  46. // not validate any state transitions
  47. blks := syncer.bsync.GetBlocks(head, RequestWidth)
  48. blockSet.Insert(blks)
  49. head = blks.Last().Parents()
  50. }
  51. genesis := blockSet.GetByHeight(0)
  52. if genesis != syncer.genesis {
  53. // TODO: handle this...
  54. Error("We synced to the wrong chain!")
  55. return
  56. }
  57. // Fetch all the messages for all the blocks in this chain
  58. // There are many ways to make this more efficient. For now, do the dumb thing
  59. blockSet.ForEach(func(b Block) {
  60. // FetchMessages should use bitswap to fetch any messages we don't have locally
  61. FetchMessages(b)
  62. })
  63. // Now, to validate some state transitions
  64. base := genesis
  65. for i := 1; i < selectedHead.Height(); i++ {
  66. next := blockSet.GetByHeight(i)
  67. if !ValidateTransition(base, next) {
  68. // TODO: do something productive here...
  69. Error("invalid state transition")
  70. return
  71. }
  72. }
  73. blockSet.PersistTo(syncer.store)
  74. syncer.head = bset.Head()
  75. syncer.syncMode = CaughtUp
  76. }
  77. func selectHead(heads map[PeerID]TipSet) TipSet {
  78. headsArr := toArray(heads)
  79. sel := headsArr[0]
  80. for i := 1; i < len(headsArr); i++ {
  81. cur := headsArr[i]
  82. if cur.IsAncestorOf(sel) {
  83. continue
  84. }
  85. if sel.IsAncestorOf(cur) {
  86. sel = cur
  87. continue
  88. }
  89. nca := NearestCommonAncestor(cur, sel)
  90. if sel.Height() - nca.Height() > ForkLengthThreshold {
  91. // TODO: handle this better than refusing to sync
  92. Fatal("Conflict exists in heads set")
  93. }
  94. if cur.Weight() > sel.Weight() {
  95. sel = cur
  96. }
  97. }
  98. return sel
  99. }
  100. // SyncCaughtUp is used to stay in sync once caught up to
  101. // the rest of the network.
  102. func (syncer *Syncer) SyncCaughtUp(maybeHead TipSet) error {
  103. chain, err := syncer.collectChainCaughtUp(maybeHead)
  104. if err != nil {
  105. return err
  106. }
  107. // possibleTs enumerates possible tipsets that are the union
  108. // of tipsets from the chain and the store
  109. for _, ts := range possibleTs(chain[1:]) {
  110. if err := consensus.Validate(ts, store); err != nil {
  111. return err
  112. }
  113. syncer.store.PutTipSet(ts)
  114. if consenus.Weight(ts) > consensus.Weight(head) {
  115. syncer.head = ts
  116. }
  117. }
  118. return nil
  119. }
  120. func (syncer *Syncer) collectChainCaughtUp(maybeHead TipSet) (Chain, error) {
  121. // fetch tipset and messages via bitswap
  122. ts := tipsetFromCidOverNet(newHead)
  123. var chain Chain
  124. for {
  125. if !consensus.Punctual(ts) {
  126. syncer.bad.InvalidateChain(chain)
  127. syncer.bad.InvalidateTipSet(ts)
  128. return nil, errors.New("tipset forks too far back from head")
  129. }
  130. chain.InsertFront(ts)
  131. if syncer.store.Contains(ts) {
  132. // Store has record of this tipset.
  133. return chain, nil
  134. }
  135. parent := ts.ParentCid()
  136. ts, err = tipsetFromCidOverNet(parent)
  137. if err != nil {
  138. return nil, err
  139. }
  140. }
  141. }

同步模式

一个filecoin节点在第一次进入网络时或在被隔离足够长的时间后以同步模式同步。确切的时间段来自共识协议(TODO更具体地说明,例如,这与共识有何关联。准时方法?)。

在syncing模式期间,节点通过安全引导协议了解区块链的最新头部。然后,同步协议同步整个链的块头,并验证它们的链接。然后它获取链的所有消息,并检查块之间的所有状态转换以及块是否已正确创建。如果验证通过,则节点的头部更新为从引导接收的头部。

在这种操作模式下,Filecoin节点不应该挖掘或发送消息,因为它将无法成功生成最重的块或引用链的正确状态以验证消息将按预期执行。

(TODO:应该包括讨论Load()调用,以便在同步模式的“重新唤醒”情况下利用节点上的现有链数据。)

追赶模式

caught up模式在syncing模式完成后Filecoin节点同步。节点将保持此模式,直到它们关闭。新的区块cids通过hello协议或网络的区块pubsub协议从网络传播。节点还获得来自其自己的成功挖掘的块的新块cid。这些cid输入到caught up同步协议。如果这些cid属于存储中已存在的tipset,则它们已经同步并且同步协议完成。如果不是,则同步协议解析对应于输入cids的tipset。它会检查此tipset是否不在其badTipSet缓存中,并且使用共识Punctual方法知道此tipset在链中不会太远。然后通过读取tipset的任何块的区块头中的父cid来解析父tipset。重复上述过程,直到找到错误或存储包含下一个tipset。在错误的情况下,在调用collectTipSetCaughtUp返回之前,坏消息集及其尚未在坏消息集缓存中的子消息集将被添加到缓存中。

在收集到先前同步到存储的祖先tipset的链之后,同步协议将逐个检查新链的每个tipset的有效性。当filecoin网络运行预期一致协议或任何其他多父一致协议时,同步协议不仅必须考虑新链中的tipsets,而且还必须考虑可能的新的最重的tipsets,即新链中的tipsets与存储中的tipsets的并集。在预期共识的情况下,最多只有一个这样的tipset:由新链中同步到的第一个新的tipset和相同父tipset中存储的最大的tipset组成的tipset。

要同步新的tipset,caught up同步协议首先在tipset上运行共识验证检查。如果任何tipset无效,则同步协议完成。如果tipset有效,则syncer会将tipset添加到chain store。然后,同步协议使用一致加权规则检查tipset是否比当前头更重。如果它更重,则链更新节点的状态以考虑新的最重的tipset。

依赖

影响链同步协议的事情。

共识协议

  • 一致协议应该定义一个准时函数:func Punctual([]TipSet chain) bool. Punctual(chain) == true。如果所提供的链没有从当前最佳链的节点视图“太远过去”分支,则准时true,否则为false。
  • 分叉选择规则。这包括加权功能。作为EC的上下文中的一部分,同步器必须考虑作为独立传播的tipset的并集的tipset。

链存储

  • 当前的链同步协议要求链存储从不存储无效的tipset。

开放问题

  • 在syncing模式下安全引导
  • 当第一次SyncBootstrap调用完成后,我们如何处理同步模式下初始引导头和网络头之间的延迟?我们可能需要多个SyncBootstrap调用。它们应该并行化吗?
  • 链存储实现的属性对同步协议的设计和同步协议对拒绝服务(DOS)攻击的抵抗力具有重大影响。例如,如果链存储简单地保持存储节点中的所有块更容易耗尽空间。作为另一个例子,syncer假设存储总是包含最重链的准时祖先。该规范是否应该包含链存储的属性,以便同步协议可以保证一定的DOS抵抗水平?链存储应该完全取决于实现吗?链存储规范应该是同步协议的一部分吗?
  • 分享 收藏
0 条评论
  • 这篇文章暂无评论,赶紧评论一下吧~