Solana内部解析 第1部分:什么是本地链上程序及其重要性?

  • Sec3dev
  • 发布于 2022-01-01 14:12
  • 阅读 33

这篇文章详细介绍了Solana的本地链上程序,包括其作用、系统程序、代币程序、质押程序等的具体功能和调用方式。同时探讨了跨程序调用的机制以及安全性考量,提供了程序ID一览和相关代码示例,是对Solana链上程序的深度解析。

Solana 有一些内置的(原生链上)程序(例如 system_program, spl_token, stake, vote, ed25519 等),提供基本的指令并且一般是可信的。

在这篇文章中,我们介绍这些程序的内部机制,并强调一些细节。

原生 Solana 程序列表

每个 Solana 程序(包括原生和用户部署的智能合约)都有一个唯一的 program_id,对应程序的公钥(pubkey)。

以下是原生 Solana 程序及其对应的 program_ids 列表

原生 Solana 程序 程序 ID
system_program 11111111111111111111111111111111
stake Stake11111111111111111111111111111111111111
vote Vote111111111111111111111111111111111111111
config Config1111111111111111111111111111111111111
BPFLoaderUpgradeab1e BPFLoaderUpgradeab1e11111111111111111111111
Ed25519 Ed25519SigVerify111111111111111111111111111
Secp256k1 KeccakSecp256k11111111111111111111111111111

此外,spl_tokenspl_associated_token_account 也是官方 Solana 程序,使用频率较高,因此我们也考虑它们:

官方 Solana 程序 程序 ID
spl_token TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
spl_associated_token_account ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL

跨程序调用

当一个程序调用另一个程序时,被调用程序的 program_id 将会通过以下两种函数之一传递给调用:

  • solana_program::program::invoke

  • solana_program::program::invoke_signed

注意:在内部,invoke 调用 invoke_signed,但没有 signer_seeds

program_idinstruction 的第一个参数,如下所定义:

invokeinvoke_signed 内,首先进行 RefCell 检查,以确保账户 RefCells 与请求一致:

由于嵌套循环,RefCell 检查可能会消耗计算单元。

为了避免这个开销,dapps 可能选择使用 invoke_uncheckedinvoke_signed_unchecked,即 invokeinvoke_signed 的未检查版本,不会检查 RefCells。然而,请自行承担使用 invoke_uncheckedinvoke_signed_unchecked 的风险:仅在确认指令中使用的账户与 account_infos 中的账户一致时使用。

Solana 原生加载器和 BPF 加载器

在 Solana 中,每个账户,包括 Solana 程序都有一个所有者。当在跨程序调用中调用 Solana 程序时,其所有者用于处理指令(invokeinvoke_signed)。对于 Solana 程序,它们的所有者可以是 native loader 或 BPF 加载器。

native loaderNativeLoader1111111111111111111111111111111)是一个特殊程序,是大多数原生 Solana 程序的所有者(那些 program_ids 以 111111111111111111111111111 结尾)。

当在跨程序调用中调用原生 Solana 程序时,使用原生加载器将原生程序加载到 Solana 运行时并处理指令。

原生加载器还是三个 BPF 加载器的所有者:

  • BPFLoaderUpgradeab1e (可升级的 Solana BPF 加载器。可升级 BPF 加载器负责部署、升级和执行 BPF 程序。)
  • BPFLoader2 (最新的 Solana BPF 加载器。BPF 加载器负责加载、最终确定和执行 BPF 程序。)
  • BPFLoader(最初和现在已弃用的 Solana BPF 加载器)

大多数 Solana 智能合约使用可升级 BPF 加载器来部署程序,因此它们的所有者是 BPFLoaderUpgradeab1e,并且可以进行升级(由在程序部署时设置的升级权限控制)。

一些 Solana 程序是不可变的,因为它们是由 BPFLoader2BPFLoader 加载的。

例如,SPL Token spl_token 和关联代币 spl_associated_token_account 程序由 BPFLoader2 加载,并且它们是不可变的。

system_program 程序

系统程序 可能是被调用最频繁的程序,通常使用以下两条指令调用:

  • system_instruction::create_account
  • system_instruction::transfer

系统程序提供多个重要功能:

  1. 创建新账户
  2. 分配账户数据
  3. 将账户分配给拥有程序
  4. 从系统程序拥有的账户转移 lamports
  5. 支付交易费用

系统程序是所有钱包账户的 所有者

注意:只有账户的所有者才能对账户进行写入访问。如果一个账户没有被程序拥有,程序只能读取其数据并为账户充值(但不能从账户中扣除)。

当账户通过 create_account 创建时,系统程序也是该账户的 默认所有者。然后允许转移 lamports,并且重要的是 分配 账户所有权,即将所有者更改为不同的程序_id。

spl_token 程序

SPL Token 程序 提供用于创建和管理代币的功能(包括可替代代币和不可替代代币,即 NFTs)。

spl_token 通常用于创建新代币、铸造、销毁和分发给用户。以下指令在 Solana 智能合约中经常使用:

  • 创建代币账户:spl_token::instruction::initialize_account
  • 创建代币铸造:spl_token::instruction::initialize_mint
  • 向账户铸造新代币:spl_token::instruction::mint_to
  • 从一个账户转移代币到另一个账户:spl_token::instruction::transfer
  • 通过从账户中移除代币来销毁代币:spl_token::instruction::burn
  • 批准代理人:spl_token::instruction::approve

spl_associated_token_account 程序

关联代币程序 允许用户为他们拥有的每个代币创建主代币账户。内部将用户的钱包地址映射到每个代币铸造的唯一关联代币账户。

具体来说,要为给定的钱包地址和代币铸造创建关联代币账户: create_associated_token_account

stake 程序

Stake 程序 用于创建和管理表示验证者或其委托人权益和奖励的账户。

以下指令经常使用:

  • 用锁定和授权信息初始化权益:stake::instruction::initialize
  • 授权一个密钥以管理权益或提款:stake::instruction::authorize
  • 从权益账户中提取未锁定的 lamports:stake::instruction::withdraw
  • 设置权益锁定:stake::instruction::set_lockup
  • 禁用账户中的权益:stake::instruction::deactivate_stake
  • 合并两个权益账户:stake::instruction::merge
  • 从权益账户中分离出权益到另一个权益账户:stake::instruction::split
  • 将权益委托给特定的投票账户:stake::instruction::delegate_stake

投票程序

投票程序 用于创建和管理跟踪验证者投票状态和奖励的账户。

配置程序

配置程序 用于将配置数据添加到链上,包括允许修改数据的公钥列表。

投票和配置主要由 Solana 验证者使用,因此我们在此省略细节。

ed25519 签名验证程序

Ed25519 签名验证程序 接收一个 ed25519 签名、公钥和消息,并用于验证该消息是否由(相应的私钥)签名。

默认情况下,使用所有可用 CPU 核心并行验证签名。当可用性能库时,签名验证会转移到 GPU。

可以验证多个签名。如果任意签名验证失败,将返回错误。

secp256k1 恢复程序

Secp256k1 恢复程序 用于从已签名消息中恢复 Secp256k1 公钥(ecrecover)。这是为了支持以太坊 / Solana 桥接。

Secp256k1 是流行区块链(例如,比特币、以太坊)用于实现公钥密码学的椭圆曲线名称。该曲线上的所有点都是有效的公钥。

Ed25519 和 Secp256k1 程序在 Solana 中预编译以最大化性能。

从安全角度看

应该注意,尽管这些原生程序通常是可信赖的(且大部分是稳定的),但为了确保安全,了解其假设并以预期的方式使用指令是很重要的。

我们使用 SPL Token 程序的安全案例来说明这一点。

spl-token v3.1.1(于 2021 年 5 月 18 日发布)之前,代币指令代码存在一个漏洞,允许调用任意程序(而不是实际的 spl-token 程序)。修复在 这个提交 中。

修复在所有代币指令函数中添加了 check_program_account,以确保用户提供的 token_program_id 与 spl-token program_id TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA 相同。

check_program_account 函数如下所示:

该漏洞的根本原因在于对 spl-token 程序预期用法与潜在用法之间存在不一致的假设。

因此,为了避免一般性这样的漏洞(从而防御攻击),重要的是

  • 检查这些原生程序的预期用法

  • 添加必要的用户检查

  • 始终使用修复过的版本(例如,spl-token v3.1.1 及以上)。

    • *

关于 sec3(前称 Soteria)

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

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

0 条评论

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