IC的P2P层,如何实现安全可扩展性?
D Plus CommunityInternet Computer使开发者可以开发Canisters组成面向C端用户的Dapp,任何开发者都可以在IC上重新构想去中心化网络服务、DeFi、社交Dapp、NFT、游戏等应用,因此在Internet Computer在最初设计时就考虑到自身承载泛平台所需的安全、可靠、和可拓展性。
可拓展性一直是一个非常重要的因素,它主要依赖于IC网络中的消息分发的效率。网络越大,承载的Dapp越多,分发的消息就越多。为此Internet Computer采用分片技术将网络划分为多个子网,每个子网都可以被视为是一个IC区块链,它们通过选定节点组成子网运行组成Dapp的Canisters,IC P2P层是同一子网节点之间实现安全、可靠、可扩展通行的Layer。
Internet Computer协议有四个主要Layer组成:
- 执行管理层用于确定性软件消息的安全环境;
- 消息路由层在子网之间的路由用户和系统生成的消息,管理应用程序的输入和输出队列,并调度消息以供执行;
- 共识层选择和排序从用户和不同子网接收的消息,以创建可以在窜地到消息路由层之前进行公正和最终确定的区块;
- 点对点(P2P)层从用户以及同一子网中的其他节点收集和发布消息,P2P层将接收到的消息广播到子网的其他节点,以确保平台的安全、可靠和弹性。
IC P2P层要实现的是安全性、性能和可扩展性:Internet Computer的设计是即使是存在恶意节点时也能确保安全运行,因此IC P2P层和协议皆在确保即使存在最多有三分之一恶意节点的情况下也能保持运行。IC P2P层与其他传统区块链的P2P层设计不同,IC 的P2P层增加了复杂性和性能的权衡。在以下会说到IC P2P层如何以最小的性能开销实现安全目标,同时使子网能够扩展。
此外IC P2P层为消息提供了独特的优先级机制,这样不仅可以更快的传送重要消息,并通过不发送不需要的消息来节省带宽。
在这篇博文中,将涉及IC P2P层的以下几个方面:
- 要求;
- 基本原则;
- 与Dapp程序组件的交互;
- 数据结构;
- Gossip分布式协议;
- 宽带和内存注意事项。
IC?P2P层负责发送上面Layer(例如共识)创建的Artifacts,并负责接受、验证、处理和分发来自同一子网中其他节点以及来自用户的Artifacts。IC P2P层保证,如果有正确节点想其对点节点发送Artifacts,则该Artifacts最终被子网中需要它的所有正确节点接收,这可以看作是可靠广播的一种特殊情况。这是为IC共识算法量身定制的,具有优先级,在某些网络假设下,它提供限时交付。
Dfinity希望IC P2P层在以下要求下提供这种保证:
- 尽管存在拜占庭故障,但保证限时/最终交付;
- 为不同的Dapp程序组件/对点保留资源;
- 有界资源(包括CPU、内存、硬盘);
- 不同Artifacts的优先级;
- 高效率;
- DOS/垃圾邮件弹性;
- 加密、真实性和完整性;
IC P2P层使用Gossip分布式机制在子网中分发消息,gossip协议的原理就是将收到的消息或创建的消息发送给子网中的对点方,P2P中的对点方由覆盖网络拓扑(组成网络之间设备的分布情况以及连接状态)确定,一切都保证在O(diameter)hops中传递,如果覆盖是无向连接的,则所有节点遵循协议,并且不会丢弃任何消息。
IC P2P层被设计为在即使在拜占庭节点存在的情况下也具有容错能力,IC?P2P层在设计时就考虑了在子网中会出现此类节点的可能性,所以IC?P2P层保证即使子网中多大三分之一的节点是拜占庭节点也能正确有效的运行:如果一个节点表现出恶意(不遵守协议、试图伤害其他节点或用户)或者如果它表现出一些错误行为(不响应、不分发Artifacts,遭受严重的网络延迟),基于IC P2P层的容错能力只要不超过三分之一的是拜占庭节点该子网也能正确有效运行。
在考虑拜占庭节点时,Dfinity希望避免几个问题,第一种是所谓的eclipse攻击,其中某个节点的所有对点方是恶意或是有故障的。恶意节点可以串通并选择正确节点看到的Artifacts,并将该节点与网络的其余部分断开。因为会验证消息的真实性,恶意节点无法用欺骗消息欺骗诚实节点,但连接性问题仍然存在。为了避免这种情况,必须使用覆盖来保证与足够多的对点节点的连接,一个节点可以连接子网中的所有其他节点,形成一个完整的图,从而提供完美的eclipse攻击保护。对于较大的子网,例如托管NNS的子网采用稀疏覆盖。
因为想保证在一定时间内所有依赖它们的节点都收到Artifacts,所以gossip分布式协议需要确保这些Artifacts被交付,尽管可能存在故障链接和节点问题,但是gossip分布式协议通常基于传播模式会导致带宽的亢余和浪费,因此减少这些开销的方式设计此类协议非常重要。
Artifacts可能会很大,如果这些Artifacts从多个对点多次发送,则会导致严重的带宽浪费,这类似于不得不从多个朋友那里一遍又一遍的听到相同的谣言,在这个比喻中,你的朋友可以先问你是否听说过最新消息,而不是告诉你谣言,在上下文中,这对应于发送广告,广告是小消息,仅包含Artifacts的元数据和一些验证它们的方法,但不包含其内容。每个节点从至少一个对点方请求它需要的Artifacts。在IC的设计中,将从询问一个对点开始,但是如果遇到问题,可能会向另一个对点询问相同的Artifacts,这可能会重复,直到找到一个没有故障的诚实对点节点。
广告包括由gossip分布式协议及其应用程序组件用于完整性验证(完整性哈希)和用于决策(帮助组件对Artifacts进行优先级排序的属性)的字段。
一个节点可能会收到多个广告,因此必须选择首先请求哪些Artifacts,每个广告都包含一些创建它客户端组件提供的属性。例如共识Artifacts包含一个height属性,它告诉相应Artifacts的区块高度。共识还为gossip提供了优先功能,它接受一个广告(及其属性)并返回一个优先级值(最低的是 drop,这意味着不需要这个Artifacts,最高的是ftch now,这意味着最高优先级的Artifacts)。如果现在共识现在处于高度10,它可能更喜欢该高度10的Artifacts而不是高度11和12的Artifacts,这取决于它们的类型以及可能的其他状态参数(作为一般规则,它更喜欢相同高度而不是不同的高度)。IC的P2P层采用这些优先级值来确定应该首先请求哪个Artifacts。
P2P商店在Artifacts中接收Artifacts。它通知共识和其他客户端组件有关Artifacts池中的更改,然后应用程序组件确定其关于Artifacts池内容的下一步操作。Artifacts池中包含每一个应用程序组件的所有可用Artifacts。
通过将Artifacts分类为已验证或未验证,后一类用于尚未验证的Artifacts。验证意味着由客户端组件检查Artifacts,例如通过验证签名。如果需要可以将客户端的Artifacts池持久化到非易失性存储中,这样做的目的是为了共识Artifacts。
在上面可以看到一个节点为gossip分布式协议保存了哪些数据结构。左侧是Artifacts池,它分为已验证和未验证部分,未验证部分包含那些尚未验证的Artifacts。每个未验证部分的大小是有界的,以防阻止恶意节点填满Artifacts池从而导致资源泄漏和拒绝服务攻击,但足够大以确保协议在正常情况下正确运行。
此外对于每个对点,维护其上下文,这有助于跟踪了收到了哪些广告,向谁请求了哪些广告:
- 广告队列是从该对点方接收到的所有广告的优先队列,它们按照优先级排序;
- 请求的集合包含已经从该对点请求相应Artifacts的所有广告;
- 接收检查缓存用于阻止对最近收到的Artifacts的请求。
以下是gossip分布式协议处理的主要事件:
- Artifacts池中的新Artifacts(由客户端组件在本地添加);
- 处理从对点方收到的新广告;
- 处理从对点方收到的新Artifacts;
- 恢复和重新连接问题。
Artifact池中的新工件(由客户端组件在本地添加):
当一个节点从客户端组件接收到一个新的Artifacts时,它会为它创建一个广告,并将这个广告发送给它所有的对点方。
处理从对点方收到的新广告:
当节点从对点节点收到广告时,它首先检查相应Artifacts是否已由节点本身下载或创建,如果不是,此广告的优先级高于?drop?,则会将广告添加到发送它的对点方的广告队列中。如果Artifacts池的未验证部分中有足够的空间供对点方使用,会专门为此对点方i调用一个名为?download_next(i)的函数。该函数获取最高优先级的广告(基于优先级函数分配的优先级别),并从该对点方请求相应的Artifacts。因此,没有必要请求刚刚收到广告的Artifacts,只请求具有最高优先级的Artifacts。
请求Artifacts后,相应的广告会从广告队列移动到向其请求Artifacts的对点方的请求集合。
可能在多个对点方的广告队列中会拥有相同的广告,因为多个对点方可能发送了相同的广告。广告将会保留在其他对点方的广告队列中,直到收到实际的Artifacts件为止。在Artifacts请求上设置超时是为了防止无响应或缓慢的对点方。这有助于保证有限的时间交付。我们将避免从我们已经提出请求的对点请求Artifacts,因为它可能行为不端,在这种情况下,我们可以看到其他对点方是否已经发布了相同的Artifacts,如果是,我们将在尝试无响应的对点方之前尝试从它们哪里获取它。
处理从对点方收到的新Artifacts:
当我们从对点方收到Artifacts时,我们首先通过检查相应的广告是否在对点方的请求集中来确保它是被请求的。然后我们使用相应的完整性哈希验证其完整性。如果这些检查中的任何一项失败,则意味着该对等方行为不端。最终,我们从所有广告队列和请求的集合中删除该广告。我们将Artifacts添加到发送它的对点方的未验证池中。我们将把它留在那里让客户端组件检查和验证它。我们还将工件的散列添加到称为接收检查的小缓存中,每个对点维护,以忽略同一Artifacts的进一步广告。这样做是为了应用程序组件提供一些宽限期来更新它们的优先级函数,这样即使Artifacts从Artifacts池中的未验证部分中删除,它们也不会再次请求相同的广告。如果我们在未经验证的Artifacts池中仍有空间供此对点体使用,我们将根据优先级使用?download_next(i)?函数从中请求下一个Artifacts。
传输和连接管理:
在 gossip 组件下方,有一个传输组件,用于维护对点之间的实际网络连接。传输组件负责保持连接稳定。对于瞬态连接问题和拥塞情况,它有自己的缓冲区。它有一个内部机制来确保连接不会挂起并检测比平常更长的延迟。这对于提供限时交付很重要。具有自己的第 7 层标头的传输帧gossip消息,其中包含一些由传输组件用于维护流和报告错误的元数据字段。目前,传输在对点之间使用多个 TCP 流。
传输以适应这种分布式对点网络的方式使用 TLS 1.3,没有证书颁发机构层次结构作为信任根。相反,信任根是为节点提供自签名证书的注册表,以便节点可以对其对点方进行身份验证。
如果 TCP 连接中断,Transport 会定期尝试重新连接(只要相应的对点方仍在节点分配的覆盖层上)。当重新建立连接时,我们会清空相应的传输队列并开始接收新消息。
由于所有数据结构都有大小限制,这也适用于传输缓冲区。如果这样的缓冲区已满,或者如果我们一直在等待重新连接,传输最终会通知接收器gossip组件潜在的消息丢失。然后接收方可以发送重传请求。重传请求是gossip分布式协议的消息,它有一个过滤器来告诉接收者请求的发送者看到的最新Artifacts。可能是在发送此请求时其它对点已经成功发送了相同的广告,因此节点可能不需要在它错过的所有消息上赶上相应的对点。
收到重传请求后,发送方节点根据请求中包含过滤器发送所有相关广告。传输这些广告发送给接收者。如果在此过程中队列再次变满,则会将发送剩余广告的另一个重传请求。
总而言之,IC P2P层保证了子网中Artifacts的有限交付。它使用广告-请求-Artifacts模式和覆盖拓扑来减少带宽开销,并为客户端组件提供优先级 API,以确保首先交付最高优先级的Artifacts。该协议具有容错性,并且考虑到了拒绝服务攻击和其他威胁。