新消息:Helius 收购 Light Protocol
Solana v1.18 更新
博客/更新

关于 Solana v1.18 更新,你需要了解的一切

Developer Experience EngineerX 上的 0xIchigoLinkedIn 上的 0xIchigoGitHub 上的 0xIchigo
阅读需 19 分钟

衷心感谢 Rex St. John 和 Mike MacCana 审阅本文。

简介

Solana 1.18 更新获得绝大多数节点采用,是一个重要的里程碑。它带来了一系列改进和新功能,旨在提升网络的性能、可靠性和效率。其中最引人注目的变化之一是引入中央调度器。这个新调度器旨在简化交易处理,并确保优先级计算更加准确高效。对运行时环境和程序部署等方面的其他改进,也有助于在网络负载达到峰值时提供更可靠的性能。

本文将探讨 1.18 版本带来的更新和改进。我们会分析这些变化背后的动机、新功能的具体细节,以及它们对网络改进的预期影响。无论你是验证者运营者、开发者,还是普通 Solana 用户,这份全面的 1.18 更新概览都将提供必要信息,帮助你理解并充分利用这些新改进带来的优势。

首先,我们必须介绍推动这些变化的新成立开发公司 Anza,以及它在 Solana 持续开发中扮演的角色。

Anza 是什么?

Anza 是一家新成立的软件开发公司,由 Solana Labs 的前高管和核心工程师创立。它的成立是一项旨在强化 Solana 生态系统的战略举措,目标是提高其可靠性、去中心化程度和网络韧性。Anza 致力于通过开发关键基础设施、为核心协议作出贡献,以及推动新工具创新来增强 Solana 生态系统。

创始团队包括 Jeff Washington、Stephen Akridge、Jed Halfon、Amber Christiansen、Pankaj Garg、Jon Cinque,以及多位来自 Solana Labs 的核心工程师。

Anza 专注于开发和完善 Solana 验证者客户端,并创建了 Agave——Solana Labs 验证者客户端的一个分叉。Anza 的目标不止于开发自己的验证者客户端,还致力于推动整个生态系统的改进,其中包括开发 Token Extensions 和定制 Rust / Clang 工具链。Anza 通过推动开放协作的开发方式,致力于加速和改进 Solana 生态系统。

Agave 是什么?

如上一节简要提到的,Agave 是由 Anza 主导的 Solana Labs 验证者客户端分叉。在这里,“分叉”是指 Anza 开发团队采用 Solana Labs 仓库中的现有代码,并开启一条独立于原始代码库的新开发路径。这样,Anza 就能为 Solana Labs 客户端实现自己的改进、功能和优化。

迁移过程

客户端向 Anza GitHub 组织的迁移始于 3 月 1 日。最初,Agave 会镜像 Solana Labs 仓库,为社区留出适应时间。在此期间,Anza 将负责关闭拉取请求(PR),并将相关议题迁移到 Agave 仓库。Agave 与 Solana Labs 客户端的 1.17 和 1.18 版本在功能上将完全相同。Anza 计划在今年夏季发布 Agave v2.0,届时将归档 Solana Labs 客户端,并建议整个网络全部迁移到新的 Agave 客户端。

从 Solana Labs 到 Agave 的迁移过程已在其 GitHub 上公开跟踪。

Agave 运行时

Agave 运行时继承了 Solana 虚拟机(SVM)的基础架构,是执行 Sealevel 运行时所定义核心功能的基石。 

Solana 协议将运行时定义为处理交易和更新账户数据库状态的关键组件。Agave 和 Firedancer 客户端采用并进一步完善了这一规范。SVM 的核心能力是并行执行所有 Solana 程序并修改账户状态。

要理解交易处理和 1.18 中即将出现的变化,银行这一概念至关重要。银行既是一套逻辑,也代表特定时间点的账本状态。它如同一个复杂的控制器,负责管理账户数据库、跟踪客户端账户、管理程序执行,并维护 Solana 账本的完整性与推进。银行封装了给定区块中所含交易产生的状态,相当于该时间点的账本快照。

每个银行都配有执行交易所需的缓存和引用,可从先前的快照或创世区块初始化。在验证者处理交易的银行阶段,银行用于组装区块,之后再验证其完整性。其生命周期包括加载账户、处理交易、冻结银行以最终确定状态,以及最终将其设为根,从而确保其永久性。

概括来说,Agave 运行时中的交易处理引擎负责加载、编译和执行程序。它使用即时(JIT)编译,缓存已编译的程序,以优化执行效率并减少不必要的重复编译。程序在部署前会编译为 eBPF 格式。随后,运行时使用 rBPF 工具包创建 eBPF 虚拟机,将 eBPF 即时编译为 x86_64 机器码指令,充分利用可用硬件。这能确保程序高效执行。

1.18 更新引入了中央交易调度器,它与 Agave 运行时带来的运行效率提升紧密相关。通过改进交易经由银行进行编译、执行和管理的方式,1.18 更新让调度流程更加精简高效,进而缩短交易处理时间并提升吞吐量。新的 Agave 运行时及其客户端是这些改进的基础,因此在深入了解新调度器的复杂细节前,我们必须先建立大致的认识。 

如果你想进一步了解 Agave 运行时,建议阅读 Joe Caulfield 关于该主题的文章。文章探讨得非常深入,并在各处提供了实用的代码片段。

更高效的交易调度器

当前实现

在交易处理流水线中,交易数据包首先通过数据包入口进入系统。随后,这些数据包在 SigVerify 阶段接受签名验证。此步骤确保每笔交易均有效并已获得发送者授权。

签名验证后,交易会被发送到银行阶段。银行阶段有六个线程,其中两个专门处理来自交易处理单元(TPU)或 Gossip 的投票交易,另外四个专门处理非投票交易。各线程相互独立,并从一个共享通道接收数据包。也就是说,SigVerify 会分批发送数据包,每个线程则从共享通道提取交易,并将其存入本地缓冲区。 

本地缓冲区接收交易、确定其优先级,并据此进行排序。该队列会动态更新,持续反映交易状态和网络需求的实时变化。交易加入队列时,系统会重新评估其顺序,确保最高优先级的交易最先准备好接受处理。

这个过程会持续进行,而这些交易数据包如何处理,取决于验证者在领导者时间表中的位置。如果验证者近期没有被安排成为领导者,它会将数据包转发给即将上任的领导者,然后丢弃这些数据包。当验证者接近预定的领导者时隙(约还有 ~20 个时隙)时,它会继续转发数据包,但不再将其丢弃。这是为了确保当其他领导者未处理这些数据包时,验证者可以将其纳入自己的某个区块。当验证者距离成为领导者还有 2 个时隙时,它会开始保留数据包——接收后暂不处理,以便在成为领导者时处理。

在区块生产期间,每个线程从本地队列中取出前 128 笔交易,尝试获取锁,然后检查、加载、执行、记录并提交这些交易。如果获取锁失败,稍后会重试该交易。下面详细说明每个步骤:

  • 加锁:此步骤检查线程可以为哪些交易获取锁。每笔交易都会读取和写入一定数量的账户,因此验证者需要确保不存在任何冲突
  • 检查:此步骤检查交易是否过旧或是否已被处理。请注意,银行设有状态缓存,用于跟踪最近 150–300 个时隙的交易
  • 加载:此步骤加载执行指定交易所需的账户,同时检查费用支付者是否确实有能力支付费用,以及调用的程序是否有效。简单来说,此步骤会加载账户并执行一些初始设置
  • 执行:此步骤执行每笔交易
  • 记录:已执行交易的结果会发送至历史证明服务进行哈希处理。交易签名会在此处发出
  • 提交:如果记录步骤成功,交易便会提交。此步骤还会将变更传播回账户系统,使当前时隙或后续时隙中的未来交易能够获得各账户的更新视图‍
  • 解锁:解除第一步中为各账户设置的锁

银行阶段使用多迭代器方法创建这些交易批次。多迭代器是一种编程模式,允许通过多个序列同时遍历一个数据集。可以想象有多位读者阅读同一本书,每个人从不同章节开始,并相互协调,确保他们对内容的理解可能互相干扰时,不会同时阅读同一页。在银行阶段,这些“读者”就是迭代器,而“书”则是等待处理的交易集合。多迭代器的目标是高效筛选交易,将其分成可以在没有锁冲突的情况下处理的批次。

首先,交易会根据优先级序列化到一个向量中。这为多迭代器提供了结构化序列,以便将交易划分为互不冲突的批次。多迭代器从序列化向量的起点开始,在交易互不冲突的连接处放置迭代器。通过这种方式,它会创建由 128 笔交易组成且不存在任何读写或写写冲突的批次。如果某笔交易与当前正在形成的批次发生冲突,系统会跳过该交易且不作标记,以便在后续冲突不再存在的批次中将其纳入。随着交易持续处理,这个迭代过程会动态调整。 

成功形成一个批次后,系统会执行其中的交易;如果执行成功,则将其记录到历史证明服务中,并广播到网络。

当前实现的问题

当前实现在多个方面可能会受到性能影响,导致交易处理出现潜在瓶颈,并造成优先级排序不一致。这些挑战主要源于银行阶段的架构和系统内部的交易处理方式。

一个根本问题是,处理非投票交易的四个独立线程各自对线程内的交易优先级有不同认识。这种差异可能导致交易排序出现抖动或不一致。当所有高优先级交易都发生冲突时,这些差异会更加明显。由于各线程实际上是从 SigVerify 的共享通道中随机提取数据包,因此每个线程获得的交易集合都是随机的。在热门 NFT 铸造等竞争激烈的事件中,许多高优先级交易很可能分布在多个银行阶段线程中。这会导致线程间的锁冲突,因而成为问题。各线程基于不同的优先级集合运行,可能争相处理这些高优先级交易,并因锁获取失败而在无意中浪费处理时间。

可以把银行阶段想象成一支管弦乐团,每个线程分别代表弦乐、铜管、木管和打击乐声部。理想情况下,指挥会协调这些声部,确保演奏和谐一致。但当前系统就像一支没有指挥却试图演奏复杂乐曲的乐团。每个声部各奏各的,经常彼此冲突。高优先级交易就像所有声部都试图同时演奏的独奏段落,最终造成混乱。这种缺乏协调的情况表明,Solana 的交易处理需要一个中央“指挥”,像指挥带领乐团一样确保效率与和谐。

新交易调度器

1.18 更新引入了中央调度线程,取代了之前由四个独立银行线程分别管理交易优先级和处理的模型。在新结构中,中央调度器是 SigVerify 阶段交易的唯一接收方。它会构建优先级队列,并使用依赖关系图来管理交易优先级和处理。

这种依赖关系图称为 prio-graph。它是一个有向无环图,会随着新交易的加入进行惰性求值。交易被插入图中形成执行链,随后按照时间优先级顺序弹出。处理相互冲突的交易时,最先插入的交易始终具有更高优先级。在上面的示例中,我们有从 A 到 H 的交易。请注意,交易 A 和 E 在各自的链中优先级最高,且互不冲突。调度器从左向右移动,分批处理交易:

交易 A 和 E 作为第一批处理,随后是 B 和 F,再之后是 C、D、G,最后一批是 H。可以看到,优先级最高的交易位于图的顶部(即最左侧)。调度器按优先级降序检查交易时,会识别其中的冲突。如果某笔交易与优先级更高的交易冲突,图中便会创建一条边来表示这种依赖关系(例如,C 和 D 与 B 冲突)。

新的调度器模型解决了多迭代器方法固有的几个关键问题:

  • 优先级处理的一致性:新系统通过集中接收和调度交易,确保所有交易都按一致的优先级顺序处理。这消除了多个线程对交易优先级认识不同而造成的抖动
  • 减少处理延迟:prio-graph 确保为执行准备的批次极有可能在没有锁冲突的情况下成功,从而缩短处理时间,并减少锁竞争造成的延迟。请注意,这里使用了“极有可能成功”一词——严格来说,prio-graph 创建的批次并非绝对不会因锁而失败,因为它们可能与投票线程发生冲突,不过这是非常罕见的边缘情况
  • 可扩展性和灵活性:这种新调度器设计允许增加线程数量,而无需再担心锁冲突随之增加。这得益于对锁的集中视图,以及在工作线程之间进行更可控的交易分配 

预计 1.18 引入的中央调度器将显著改善交易处理,降低旧系统带来的复杂性和开销。这可能会缩短交易处理时间、提高吞吐量,并增强网络稳定性。由于 1.18 的发布有所延迟,调度器自最初推出以来已经得到改进。例如,交易的预编译验证已移至工作线程,以提升效率。此外,现在的 CU 限制更加合理,估算值与实际值的比率远低于旧调度器。新调度器现在可以使用 CU 对已调度的工作队列进行限流,防止因账户冲突而将过多工作加入队列。

请注意,中央调度器默认不启用,启动验证者时必须使用新的 --block-production-method central-scheduler 标志启用。目前只能选择启用,但它将在未来版本中成为默认调度器。另请注意,可以使用 --block-production-method thread-local-multi-iterator 标志启用旧调度器(目前默认启用,但请不要在未来版本中这样做——中央调度器效率高得多,并且解决了旧调度器存在的问题)。

更有效的优先级计算 

1.18 还完善了交易优先级的确定方式,使资源使用和成本回收更加公平高效。此前,交易优先级主要基于计算预算优先级,有时会导致计算单元定价不理想。这是因为优先级计算没有充分考虑收取的基础费用,可能导致资源定价过低,并影响网络的运行效率。

新方法使用公式 Priority = Fees / (Cost + 1) 调整交易优先级计算,同时考虑交易费用和相关成本。其中,费用表示与指定交易相关的交易费用,成本则表示由 Solana 成本模型确定的计算和资源消耗。在分母中加“1”是一项安全措施,用于防止除以零。 

我们可以进一步拆解该公式,更明确地表示 Fees 和 Cost:

现在,系统会全面计算交易成本,考虑所有相关的计算和运行成本。这确保优先级计算能够反映交易的实际资源消耗。这意味着,如果开发者和用户请求的计算单元更少,就会获得更高优先级。这也意味着,即使没有任何优先费,简单转账也会在队列中获得一定优先级。

改进程序部署

1.18 还从部署可靠性和执行效率方面显著改进了程序部署。 

新更新解决了这样一个问题:在 epoch 最后一个时隙部署的程序,无法正确应用为下一个 epoch 规划的运行时环境变更。因此,在这一过渡期部署的程序会错误地使用旧运行时环境。1.18 调整了部署流程,确保在 epoch 末尾部署的任何程序所使用的运行时环境都与即将到来的 epoch 环境保持一致。

1.18 还通过向 CLI 程序部署命令添加 --with-compute-unit-price 标志,解决了无法为部署交易设置计算单元价格或限制的问题。该标志可与 solana program deploy 和 solana program write-buffer 命令搭配使用。计算单元限制通过模拟每种部署交易来设置,其值为消耗的计算单元数量。  

另一项重要改进涉及大型程序部署所用区块哈希的处理方式。在 1.18 之前,使用 sign_all_messages_and_send 发送的交易会被限制在 100 TPS。对于大型程序,部署交易的数量会达到数千笔。这意味着交易可能延迟,而且由于许多交易一次会延迟 10 秒以上,因此可能使用已过期的区块哈希。1.18 会等到限流延迟结束后,再使用最近的区块哈希为部署交易签名。区块哈希现在每 5 秒刷新一次,因此包含超过 500 笔交易的部署将因使用更新的区块哈希而受益。

此外,1.18 改进了网络处理程序部署和验证交易的方式。此前,由于识别账户状态时出现错误,一些程序会被错误标记为 FailedVerification。这可能会错误标记实际并未通过任何检查失败的程序。如果这些程序不应处于活动状态,现在会被正确识别为 Closed。这一变更可确保只有存在问题的程序才会被标记以供重新检查,并有助于避免不必要的重复验证。

更新程序状态的流程也得到了完善。程序现在可以在部署它们的同一时隙内从 Closed 状态转为活动状态。这意味着程序能够更快、更可靠地投入运行,这在高需求时期至关重要。不过需要注意,这项改进仍受一个时隙的取消部署/重新部署/部署冷却期,以及一个时隙的可见性延迟限制。因此,尽管这些调整有助于更有效地管理网络负载并防止某些类型的拥堵,但并不会显著改变 dApp 开发者的工作流程。

“拥堵补丁”——更好地处理拥堵

测试网版本 1.18.11被称为*“拥堵补丁*”,它提出了应对 Solana 近期拥堵的变更。请注意,此版本并非 1.18 独有,并且已向后移植到 1.17.31。无论如何,我们都必须讨论它。

最大的变化是,在质押加权服务质量(SWQoS)中,QUIC 现在会将质押量极低的对等节点视为未质押对等节点。这是为了解决质押量极少的质押节点可能滥用系统、获得不成比例带宽的问题。此外,现有指标无法显示来自质押节点和未质押节点的数据包分别有多少被下发和限流。因此,系统添加了这些指标,以提高可观测性。通过将 vec 实例替换为 smallvec,系统还优化了数据包分块的处理方式,从而为每个数据包节省一次内存分配。由于流的大小与数据包相当,预计数量很少,因此可以这样做。 

此前在银行阶段,所有数据包都会转发到下一个节点。但 1.18 对此进行了更改,只有来自质押节点的数据包才会被转发。这项更新使 Staked Connections 在未来变得比以往任何时候都更重要,因为它们在优先级计算和交易转发中具有更高权重。

改进文档

1.18 更新还显著改善了 Solana 官方文档的翻译支持,确保全球受众都能更方便地访问文档。更新内容包括升级 Crowdin CLI 和配置,以简化不同语言文档之间的同步;还引入了新的 serve 命令,以便通过 Docusaurus 更好地进行本地测试。文档还改进了静态内容的处理方式,将 PDF 文件直接链接到 GitHub blob,以避免翻译版本中相对路径引发的问题。

对于开发者,更新后的 README 明确说明了参与翻译的流程,包括如何处理必要的环境变量和常见构建错误等问题。持续集成流程也得到了改进,现在仅在稳定渠道构建中包含翻译。这样可以确保只有经过审查的稳定文档才会提供给最终用户。这些变更旨在简化贡献流程、提升官方文档质量,并让所有用户都能获取可靠准确的信息。

总结

在 Anza 的推动下,1.18 更新大幅改进了交易处理、优先级计算、程序部署、官方文档和整体网络性能。随着中央调度器的引入,以及多项旨在解决近期拥堵问题的修复,Solana 能够更好地处理峰值负载,并确保网络高效可靠地运行。Solana 是实现可扩展区块链的最佳希望,而这次更新进一步印证了它的潜力。

如果你读到了这里,感谢你,anon!请务必在下方填写电子邮箱地址,这样你就不会错过 Solana 的任何最新动态。准备好深入探索了吗?立即阅读 Helius 博客上的最新文章,继续你的 Solana 之旅。

其他资源

订阅 Helius

及时了解 Solana 开发的最新动态,并在我们发布新内容时收到更新

放大图片