Solana 是一个高性能的区块链平台,采用独特的技术架构实现高吞吐量和低延迟。其核心技术包括:
- Proof of History (POH) 算法确保交易顺序和全局时钟
- Leader Rotation Schedule 和 Tower BFT 共识机制提高区块出块速率
- Turbine 机制通过 Reed-solomon 编码优化大区块传播。
- Solana Virtual Machine (SVM) 和 Sealevel 并行执行引擎加快交易执行速度。
这些都是 Solana 实现高性能的架构设计,但同时也带了一些问题,如网络宕机、交易失败、MEV 问题、状态增长过快和中心化问题,我们在本文中着重阐述了 Proof of History (POH) 这种新机制。
Solana面临的另一个潜在不利因素是竞争环境。以太坊的 Layer-2 解决方案(如 Optimism 和 Arbitrum)正在获得越来越多的关注,它们在保持以太坊主网安全和去中心化的同时,提供了更快、更便宜的交易。
Solana 网络设计
在 Solana 网络中,在任意时间点上都会有一个节点被指定为“Leader”(领导者),它的任务是生成一个“历史证明”(Proof of History, PoH)序列,从而为网络提供全局一致的读取并验证事件的时间顺序。Leader 的角色是将用户发送的消息进行排序,以便其他节点可以高效处理这些消息,最大化交易吞吐量。
具体流程如下:
- 消息排序与交易执行:Leader 会接收用户消息,将它们按顺序排列,然后在当前的系统状态上执行这些交易。当前状态保存在 RAM 中。
- 状态签名发布:Leader 将执行后的交易结果和最终状态的签名发布给名为“验证者”(Verifiers)的复制节点。
- 验证与投票:Verifier 节点会执行相同的交易来验证结果,并对该状态生成签名作为确认。这些确认签名最终成为共识算法的投票,以确定一致的网络状态。
在无网络分区的情况下,网络中在任意时间点只有一个 Leader。每个 Verifier 节点的硬件能力与 Leader 相同,且可以通过基于“权益证明”(Proof of Stake, PoS)的选举机制成为新的 Leader。在这种 PoS 机制下,网络通常会优先选择“一致性”而非“可用性”。尤其是在发生网络分区的情况下,更会优先选择“一致性”。
POH 算法
在 Solana 的白皮书中,“历史证明”(Proof of History,PoH)是一种创新的时间证明机制,用于在区块链系统中验证事件发生的顺序和时间流逝。它通过一个不可预测的加密函数来生成一系列哈希值,确保这些哈希值的生成必须按顺序进行,从而提供了一种无需信任的时间记录。
PoH 的工作原理
计算过程
假设我们从一个随机的初始值(例如,“随机初始值”)开始,采用 sha256 作为哈希函数,每次计算都将上一次的输出作为新的输入。过程如下:
- 初始化:选择一个随机的字符串,作为初始输入,比如
“随机初始值”
。 - 第一步计算:将
“随机初始值”
输入到 sha256 函数,得到输出hash1
。 - 第二步计算:将
hash1
作为输入,再次输入到 sha256,得到输出hash2
。 - 第三步计算:将
hash2
作为输入,继续输入到 sha256,得到hash3
。
此时,我们得到了一个顺序的哈希链表结构:hash1 -> hash2 -> hash3 -> ...
。
序列发布
为了减少数据量,我们可以选择性地发布部分哈希值。例如,我们只发布索引为 1
、200
和 300
处的哈希值:
- 索引 1:
hash1
是从初始值生成的第一个哈希 - 索引 200:
hash200
是hash199
的输出 - 索引 300:
hash300
是hash299
的输出
发布这些索引点意味着这些哈希值只能通过顺序计算得到,而不能提前预测出 hash200
或 hash300
的值。因为在抗碰撞的哈希函数(如 sha256)下,无法通过任何方法预知某一特定位置的哈希值,必须一步一步地从初始值计算到指定位置。
PoH 时间流逝的证明
假设我们想验证在索引 1 和索引 300 之间确实经过了一段时间。由于 hash300
需要从初始值经过 300 次哈希计算才能得出,所以验证方可以通过重新计算前 300 个哈希值来确认 hash300
的值。如果验证者确实得到了相同的 hash300
,则可以确定时间确实在这段过程中流逝了,而不是快速地在一步中得到所有的哈希值。
应用实例
区块链中的数据记录:在区块链系统中,节点可以将交易数据或事件加入这个哈希链。例如,在索引 250 处,某个交易事件发生了,这个事件的哈希值 event_hash
被加入序列,即 hash250 = sha256(append(hash249, event_hash))
。这样可以确保这个事件的发生时间是在 hash249
之后,hash251
之前。
验证数据的顺序和时间:其他节点可以通过重新计算哈希序列来验证事件的时间顺序,确保事件在指定时间发生且无法篡改。
PoH 验证过程的并行化
PoH 序列可以通过多核计算机来并行验证。由于生成序列的时间较长,但验证只需检查是否每一步都正确,因此验证过程可以通过分割序列来加速。
假设我们有两个核(Core 1 和 Core 2),每个核验证不同的序列片段:
- Core 1 验证索引 200 到 300 的序列,通过
hash199
计算得到hash200
,然后再从hash299
计算出hash300
。 - Core 2 验证索引 300 到 400 的序列,通过
hash299
得到hash300
,再从hash399
得到hash400
。
这种方式可以通过现代 GPU 实现极大的并行性。例如,一台拥有 4000 个核的 GPU 可以将序列分成 4000 个片段,每个核负责验证其中一个片段。这样,多个核可以同时工作,验证整个序列所需的时间会大大减少。
验证时间的预期计算如下:
- 总哈希数 / 单核每秒哈希速率:这是单核顺序验证所需的时间。
- 总哈希数 / (每核每秒哈希速率 × 核数):这是多核并行验证所需的时间,显著低于单核验证。
水平扩展的实现
在 Solana 的 PoH 体系中,可以通过将每个 Generator 的序列状态与其他 Generator 的状态进行混合,从而实现多个 PoH Generator 的同步。通过这种方式,可以实现 PoH Generator 的水平扩展,而不需要使用分片(sharding)。为了重建系统中所有事件的完整顺序,必须获得所有 Generator 的输出。
假设有两个 PoH Generator A 和 B:
- Generator A:记录哈希
hash1a
,随后接收来自 B 的状态hash1b
。因此,hash2a 将依赖于hash1b
。 - Generator B:记录哈希
hash1b
,随后接收来自 A 的状态hash1a
。因此,hash2b 将依赖于hash1a
。
这种同步是可传递的。如果有三个 Generator A、B 和 C,且 A 和 B、B 和 C 分别同步。即使 A 和 C 没有直接同步,A 和 C 之间的依赖关系可以通过 B 来推导出来。
通过周期性地同步 Generator,每个 Generator 可以处理一部分外部流量。这使得整个系统能够处理更多的事件,同时由于 Generator 之间的网络延迟,牺牲了一定的时间精确度。在同步窗口内,可以通过某种确定性函数(例如哈希值的大小)来对事件进行全局排序。
图中展示了两个 Generator 如何插入彼此的输出状态,并记录这些操作。粗体的生成哈希表示来自对方的状态已被混合进各自的序列中,颜色的变化则表示序列因对方的数据而被修改。
PoH 下的交易流程
在 Solana 的交易流架构图中,在一个称为 Leader Rotation Schedule 的轮换机制下,会在所有的链上验证者 Validator 中,产生一个 Leader 节点,该 Leader 节点收集交易并且进行排序执行,生成 PoH 序列,之后会生成一个区块传播给其它节点。
为了避免 Leader 节点处产生单点故障,因此引入了时间限制。在 Solana 中时间单位是以 epoch 进行划分,每个 epoch 包含432,000 个 slot(时隙),每个 slot 持续 400ms,在每一个 slot 中,轮换系统会在每个 slot 内分配一个 Leader 节点,Leader 节点必须在给定的 slot 时间内发布区块(400ms),否则,就会跳过这个 slot,重新选举下一个 slot 的 Leader 节点。
PoH 计算开销
如果每秒生成 4000 个哈希,会额外产生 160KB 的数据量。为了验证这些哈希值的正确性,需要使用一台具有 4000 个核心的 GPU,并大约需要 0.25 到 0.75 毫秒的时间完成验证。
防篡改(一致性)保障
场景设置
假设我们有一个 PoH Generator(例如节点 A),它生成了以下事件序列:
- 索引 10:起始哈希
hash10a
- 索引 20:事件
Event1
发生,生成hash20a
- 索引 30:事件
Event2
发生,生成hash30a
- 索引 40:事件
Event3
发生,生成hash40a
恶意节点的攻击
一个恶意的 PoH Generator(节点 B)试图伪造序列顺序,将事件按不同的顺序排列(如反向顺序)。它创建了以下序列:
- 索引 10:起始哈希
hash10b
- 索引 20:事件
Event3
,生成hash20b
- 索引 30:事件
Event2
,生成hash30b
- 索引 40:事件
Event1
,生成hash40b
这种伪造的序列可能导致网络中其他节点误认为事件发生的顺序是 Event3 -> Event2 -> Event1
。
防御机制
为了防止此类攻击,每个事件在生成时会附加一个引用(或签名),表明它所依赖的上一个哈希值。这样就可以确保事件在序列中是按正确顺序插入的。
- 事件
Event1
:生成hash20a
,同时包含hash10a
(上一个有效哈希)的引用。 - 事件
Event2
:生成hash30a
,同时包含hash20a
的引用。 - 事件
Event3
:生成hash40a
,同时包含hash30a
的引用。
通过这种方式,事件 Event3
必须依赖于 Event2
的哈希结果,而 Event2
依赖于 Event1
。任何试图更改事件顺序的行为都会导致这些依赖关系不匹配,从而被检测出来。
签名验证
为了进一步确保安全,客户端在提交事件时,还会对事件数据和上一个哈希进行签名。假设 Event1
被签名为 sign(Event1 data + hash10a, Client Private Key)
。其他节点在验证时,可以使用客户端的公钥检查签名,以确保事件确实来自正确的来源,且引用了正确的前一个哈希。
PoH 攻击及防御模式
1 Reversal Attack
场景描述
假设在 PoH 系统中,事件按以下顺序发生并记录:
- 事件 A:生成
hash_A
- 事件 B:生成
hash_B
- 事件 C:生成
hash_C
攻击者想要将事件的顺序颠倒,例如,伪造一个顺序为 C -> B -> A
的恶意序列,以改变事件发生的顺序,使得事件 A 看起来是在事件 C 之后发生的。
防御机制
为了实现这个逆转,攻击者需要在事件 B 之后才开始恶意序列的生成。然而,PoH 系统的设计是分布式的,多个节点共同维护事件的顺序。每个节点在事件发生时会广播其记录的事件顺序。因此,当一个节点看到恶意节点试图颠倒顺序时,它可以通过其他节点的广播信息来验证事件的原始顺序,识别出这是一个伪造序列,从而拒绝恶意节点的逆转攻击。
2 Speed Attack
场景描述
假设系统中有两个 PoH Generator:
- Generator A:高带宽 Generator,接收大量事件并将其混入序列中。
- Generator B:低带宽但高速的 Generator,定期与 Generator A 同步。
攻击者试图加速生成伪造序列来超过正常的 PoH Generator,从而生成一个比真实序列更新且包含伪造事件的序列。
防御机制
由于 Generator B 生成的是一个次级序列并且定期与 Generator A 同步,攻击者在伪造序列时不仅需要赶上 Generator A 的速度,还需要同步 Generator B 的次级序列。这一额外的同步过程给攻击者增加了难度。攻击者必须同时以高速生成主序列和次级序列,否则无法成功生成一个比真实序列更快的伪造版本。此外,Generator A 和 B 的同步机制使得系统更难被单点突破,增加了攻击的复杂性。
3 Long Range Attack
场景描述
长期攻击发生在攻击者获取了过期的私钥的情况下。例如,假设攻击者得到了某用户的旧私钥,试图使用这个私钥生成一个伪造的历史记录,并使其看起来像是系统中真实的一部分。这可能包括一系列过往交易,甚至是整个区块链的历史。
防御机制
在 PoH 系统中,历史记录是按时间顺序生成的,且每一个区块的生成需要真实的时间流逝。恶意用户即使获得了旧私钥,也必须从时间上重现整个历史记录。这意味着,攻击者必须以与真实 PoH 系统相同的速度生成所有历史事件的哈希序列,否则无法赶上当前系统的进度。例如,如果真实系统的 PoH Generator 每天生成 86400 个哈希(每秒一个),那么攻击者也必须保持同样的速度,才能在生成伪造历史时不被系统识别出来。
此外,PoH 系统使用单一时间源,所有节点都依赖同一条历史记录来确保一致性。这种设计使得伪造历史记录的难度更大,因为任何伪造的历史都需要同步所有节点的共识,这几乎是不可能实现的。
进一步防御措施 PoRep(复制证明)
结合 PoRep,PoH 不仅验证时间的流逝,还验证存储空间的使用。恶意节点需要耗费与真实网络相同的存储资源和时间才能伪造历史记录。这种双重防御确保了恶意节点无法在时间或空间上轻易伪造历史。
附录
更多资料可参考: