Solana内部结构第4部分:Bank - 关键组件

  • Sec3dev
  • 发布于 2022-01-31 17:25
  • 阅读 24

本文深入探讨了Solana区块链中的Bank模块,重点介绍其在账户和程序状态管理、交易执行和程序调用验证中的关键角色,详述了Bank的生命周期、InvokeContext的处理以及与计算资源的管理等技术细节。

接下来的第三部分:TPU,本文详细讲解了 bank 模块,这是 Solana 区块链的核心组件。

什么是Bank(银行)?

银行模块的重要性不容忽视:

它管理所有账户和程序的状态,执行链上程序,并跟踪它们的进展。

在高层次上,一个 bank 与由单一领导者生成的块相关,每个 bank(除了创世银行)指向一个父银行。

bank 是处理已确认交易的主要入口。在 Bank::process_transactions 中,它创建一个 InvokeContext 来处理每个交易

InvokeContext 详解

invoke_context.process_instruction 是处理每个指令的关键函数,它验证被调用的程序没有不当行为,维护一个缓存以存储编译的指令,并返回使用的计算单位等。

它有几个参数:instruction_datainstruction_accountsprogram_indices(用于检索调用的 program_id)、compute_units_consumed(记录执行指令时使用的计算单位,初始为 0)和 timings(用于执行时间信息)。

要在程序上调用指令,invoke_context.process_instruction 首先使用程序的拥有者加载程序,然后调用程序的入口点。

被调用程序的拥有者是以下之一:

  • 本地加载程序(加载内置程序)
  • 内置程序(例如,系统指令)

如果它是本地加载程序( NativeLoader1111111111111111111111111111111),那么相应的内置程序的入口点 process_instruction 将被调用:

否则,内置程序的入口点 process_instruction 将被调用:

process_instruction 函数接受三个参数作为输入:first_instruction_account(被调用的 program_id)、instruction_data 和 invoke_context 本身:

system_instruction.process_instruction

考虑 system_instruction.process_instruction,它处理以下指令:

使用最频繁的指令是 CreateAccount, TransferAllocate

bpf_loader.process_instruction

bpf_loader.process_instruction 函数用于执行用户部署的智能合约(即,BPF 字节码):

该函数调用 process_instruction_common,创建一个 BpfExecutor 并传递程序数据,然后调用其执行函数:

BpfExecutor.execute 中,它创建一个虚拟机并通过 vm.execute_program_jitvm.execute_program_interpreted 执行程序。

BPF 代码如何执行?

重要的是,BPF 字节码并不是由 Linux 内核执行,而是由一个 BPF 虚拟机EbpfVm)。

默认情况下,use_jit 为假,使用 vm.execute_program_interpreted,即 BPF 代码由虚拟机解释。这也意味着 Solana 有很大的潜力进一步提高性能,例如通过在 Linux 内核中本机执行 BPF 代码(尽管需要更多技术细节和安全保护措施)。

虚拟机是 EbpfVm,在 rbpf 中定义( uBPF 的扩展版本:用于eBPF程序的虚拟机和 JIT 编译器)。

请注意,rbpf 并未经过审计,它包含许多不安全的 Rust 函数块。rbpf 中的任何错误可能导致严重的漏洞,例如整数溢出和内存损坏。有关示例,请参见 BlockSec 的 这篇文章

处理跨程序调用 (CPI)

当被调用程序通过 invokeinvoke_signed 调用另一个程序时,该程序将被加载并调用其入口点。

在内部,这是通过 syscall 调用 sol_invoke_signed_rust 来完成的,此后将再次调用 invoke_context.process_instruction

相应的 BPF 指令是 ebpf::CALL_IMM(参见 vm.rs L939-L972

syscall.function 是从 syscall_registry 中检索的,该注册中心已使用许多内置系统调用进行初始化,如 sol_invoke_signed_csol_invoke_signed_rustsol_create_program_addresssol_keccak256 等。

有关注册系统调用的完整列表,请参见 syscall.rs

transaction_context

在处理 CPI 后,结果(对所有涉及账户的更新)将被复制回调用者:

InvokeContext 具有一个 transaction_context 用于跟踪当前调用上下文,并确保

  • 调用深度限制为计算预算中设定的 max_invoke_depthmax_invoke_depth: 4
  • 不允许重入,除非调用者自调用

验证被调用程序没有不当行为

Solana 在验证指令调用的设计 类似于 事务内存:它先执行指令,然后验证结果,以确保被调用程序没有不当行为。

bank 还负责验证指令和每个 CPI 的结果,以判断是否符合 会计规则,这是一系列对 Solana 非常关键的属性。

具体取决于调用级别,它将调用 verifyverify_and_update(如果验证,则也更新结果):

重要:会计规则

bank 在指令执行前后维护指令账户的状态,并广泛检查规则:

  • 它验证所有 lamports 的总和没有变化:

  • 它验证所有可执行账户没有未解决的引用:

  • 它验证只有账户的拥有者可以更改拥有者,并且只有在账户是可写的且账户不可执行且数据是零初始化或为空的情况下:

  • 不属于程序的账户不能借记账户:

  • 只读和可执行账户的余额可能不会更改:

  • 账户数据大小不得超过最大长度:

  • 只有账户的拥有者可以更改数据的大小:

  • 只有系统程序可以更改数据的大小,并且只有在系统程序拥有账户的情况下:

  • 只有拥有者可以更改账户数据,如果账户是可写的,且如果账户不可执行:

  • 可执行模式是单向的(假->真),只有账户的拥有者可以设置:

  • 没有人可以修改 rent_epoch

银行生命周期

在高层次上,银行的生命周期包括以下阶段:

  1. 开启: 创建一个新的银行,并在银行达到当前节点作为该时隙的领导者的tick计数或节点在该时隙中的所有条目中已经应用了所有交易之前,将交易应用于它。
  2. 提交: 对于一笔交易,只有当交易中的所有指令成功时,账户才会被提交回银行,然后结果将存储到账户存储中。
  3. 冻结: 完成后,银行就可以被冻结。冻结后,无法再应用更多交易或进行状态更改。在冻结步骤中,将应用租金,并各种 sysvar 特殊账户更新到系统的新状态。
  4. 已根植: 冻结后,如果银行得到了适当数量的投票,那么它可以变得根植。在此时,它将无法从链上移除,状态被最终确定。

我们将继续在下一篇文章中介绍 Solana 的架构及其技术组件。


关于 sec3(前称 Soteria)

sec3 是一家安全研究公司,为 Solana 项目为数百万用户提供准备工作。sec3 的启动审计是一次严格的以研究者为主导的代码检查,旨在调查并认证主网等级智能合约;sec3 的持续审计软件平台 X-ray 与 GitHub 集成,逐步扫描拉取请求,帮助项目在部署前巩固代码;sec3 的部署后安全解决方案 WatchTower 确保资金安全。sec3 正在为 Web3 项目构建基于技术的可扩展解决方案,以确保在扩展时协议能够保持安全。

要了解更多关于 sec3 的信息,请访问 https://www.sec3.dev

  • 原文链接: sec3.dev/blog/solana-int...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Sec3dev
Sec3dev
https://www.sec3.dev/