Solana/交易/系统程序

Solana 的系统程序 1111111... 是 solana 中最核心的程序, 负责提供基本的区块链账户管理功能. 其主要功能包括创建账户, 余额转移, 数据存储等功能. 通过在交易中调用它, 您可以实现各种复杂功能, 这也是 solana 可编程性的体现.

系统程序所支持的指令列表定义在 https://github.com/solana-program/system/blob/main/interface/src/instruction.rs.

您可以看到, 指令数据被定义为一个枚举类型, 并使用 bincode 编码. Bincode 是一种紧凑型编码方式, 特点是编码对象的大小将等于或小于对象在正在运行的 rust 程序中占用的内存大小.

Bincode 编码规范详见 https://github.com/bincode-org/bincode/blob/trunk/docs/spec.md.

粗略的讲, 我们目前只需要关注 bincode 中以下几条规则:

  • 任何数字以小端序编码.
  • 枚举拥有索引值, 被视作 u32. 编码时首先编码索引值, 然后再编码其值.

以下是对系统程序中前三条主要指令的分析. 余下的指令, 相信您在本节课程结束后有能力自行阅读学习. 加油, 奥里给!

创建账户

作用: ​用于创建一个新的账户, 并将其所有权指定为系统程序. ​

位置: SystemInstruction::CreateAccount

涉及账户:

账户 权限 说明
资金提供者 3 必须拥有足够的 sol 余额来支付创建新账户所需的费用
新账户 1 -

还记得我们是如何表示账户权限的吗? 我们使用两个比特位, 第 0 个比特位表示是否可写, 第 1 个比特位表示是否需要签名.

数据格式:

名称 类型 说明
index u32 约定为 0
lamports u64 分配给新账户的 sol 数量
space u64 新账户分配的存储空间大小, 以字节为单位
owner pubkey 新账户的所有者程序的公钥

分配

作用: ​将账户的所有权转移到指定的程序. ​Solana 的账户模型要求每个账户必须有一个所有者, 我们日常使用的 solana 账户, 其所有者是系统程序.

位置: SystemInstruction::Assign

涉及账户:

账户 权限 说明
目标账户的公钥 3 目标账户的公钥

数据格式:

名称 类型 说明
index u32 约定为 1
owner pubkey 目标程序的公钥

转移

作用: ​从一个账户向另一个账户转移 sol 余额. ​

位置: SystemInstruction::Transfer

涉及账户:

账户 权限 说明
发送方公钥 3 -
接收方公钥 1 -

数据格式:

名称 类型 说明
index u32 约定为 2
lamports u64 要转移的 sol 数量

其它内置程序

Solana 系统程序的执行方式是"基于账户"的, 系统程序通过修改账户的状态和存储的数据来执行任务. 除了系统程序外, solana 还内置了一些其它程序:

  • 代币程序. Solana 的代币程序允许用户创建自己的代币, 进行代币的转移, 铸造和销毁等操作. 如果您参与过 solana 链上代币交易的话, 会频繁与此程序交互. 程序源码: https://github.com/solana-program/token-2022.
  • 质押程序. Solana 的质押程序允许用户将 sol 代币质押到网络中, 从而参与验证过程. 质押机制是 solana 共识算法的一部分, 允许用户通过质押来获取网络的奖励.
  • 租赁程序. Solana 的租赁程序用于管理网络中账户的存储租赁机制. Solana 的账户存储空间是有限的, 因此用户在创建账户时需要支付一定的"租赁费用", 以确保他们的账户数据能够在网络中得到存储.

习题

例: Solana 系统程序的公钥是多少? 请以十六进制表示.

答: Solana 系统程序的公钥是一个全部为 0 的 32 字节数组. 它是一个"黑洞"账户, 理论上没有与之对应的私钥.

import pxsol

pubkey = pxsol.core.PubKey.base58_decode('11111111111111111111111111111111')
assert pubkey.hex() == '0000000000000000000000000000000000000000000000000000000000000000'

例: 如果 ada 要转账 2 sol 给 bob, 他应当如何构造指令数据?

答: 我们可以按照上述规则手工拼凑数据, 也可以借助 pxsol.program 模块完成数据的构建. 此处仅演示第二种方法:

import pxsol

data = pxsol.program.System.transfer(2 * pxsol.denomination.sol)
assert pxsol.base58.encode(data) == '3Bxs3zxH1DZVrsVy'