Solana基础概念【下】

本文介绍了Solana比较重要的一些核心概念和基本原理,包括Solan程序,交易,指令等等

参考文档: HackQuest社区:https://www.hackquest.io/zh Solana中文文档: https://www.solana-cn.com/SolanaDocumention/home.html

更多更及时的文章请访问作者博客 原文链接:https://leapwhale.com/zh/article/q84o82vf

Solana 程序

Solana 程序,在其他链上叫做智能合约,是所有链上活动的基础。任何开发者都可以在 Solana 链上编写以及部署程序。每个程序都是一个链上账户,用于存储可执行逻辑,并组织成特定的功能,称为指令。链上的一切活动,从去中心化金融(DeFi),到非同质化代币(NFT),再到社交媒体,链上游戏,都由Solana程序所驱动。

 

Solana 程序模型的显着特征之一是代码和数据的分离。程序存储在程序账户中,它是无状态的,这意味着它们不会在内部存储任何状态,但它是可执行的executable,会执行相应的逻辑。相反,它们需要操作的所有数据都存储在单独的数据帐户中,这些帐户在 Transaction 交易中通过引用传递给程序账户,因为它本身是不可执行的。

image-20241027162354688

 

Solana中将程序和状态分离的设计,使得程序可以独立于状态进行开发、测试、部署和升级,提高了程序的可重用性和可扩展性。相反在以太坊中,智能合约和状态是绑定到一起的,合约的升级是一件非常麻烦的事情,必须通过代理模式间接实现逻辑和状态的分离,才可以进行逻辑的升级,并且在新的智能合约中,新增变量的处理要非常小心,避免存储布局 Layout 冲突,覆盖掉旧变量。

 

程序分类

Solana程序通常可以分为以下两种:

On-chain Programs:这些是部署在 Solana 上的用户编写的程序,由开发者在 Solana 网络上根据具体业务场景开发的程序。它们可以通过升级权限进行升级,该权限通常是部署程序的帐户或者指定的其他账户。

Native programs:这些是集成到 Solana 核心模块中的程序。它们提供了验证节点(validator)运行所需的基本功能。native programs 只能通过网络范围内的软件更新进行升级。常见的原生程序有:

System Program:这是Solana最基础的原生程序之一。它负责管理新账户的创建和SOL代币在账户之间的转移。System Program的功能包括:

  • 创建新账户
  • 分配账户存储空间
  • 转移SOL代币
  • 管理账户所有权

BPF Loader Program 这个程序负责加载和执行其他程序。它将编译后的程序代码加载到Solana运行时环境中,使其可以被执行。BPF Loader的主要功能包括:

  • 部署新程序
  • 加载程序指令
  • 管理程序升级

Vote program:这个程序在Solana的共识机制中扮演重要角色。它管理验证者节点的投票过程,包括:

  • 记录验证者投票

  • 管理投票账户

  • 计算验证者的权益和奖励

    Solana Program Libraries - SPL:虽然SPL不是单一的程序,而是一系列标准化程序的集合,但它们也被视为原生程序的一部分。SPL定义了许多重要的链上活动标准,包括:

  • 代币创建和管理(如SPL Token程序)

  • 代币交换(如SPL Token Swap程序)

  • 借贷协议

  • 质押池管理

  • 链上域名解析服务(如SPL Name Service)

 

其中 System Program 这个程序负责管理建立新账户以及在两个账户之间转账SOL。Solana SPL 程序定义了一系列的链上活动,其中包括针对代币的创建,交换,借贷,以及创建质押池,维护链上域名解析服务等。

 

系统程序

默认情况下,所有新帐户都归系统程序所有。系统程序执行几个关键任务,例如:

New Account Creation: Only the System Program can create new accounts.

  • 创建新帐户:只有系统程序可以创建新帐户。
  • 空间分配:设置每个账户的数据字段的字节容量。
  • 分配程序所有权:系统程序创建帐户后,可以将指定的程序所有者重新分配给其他程序帐户。这就是自定义程序获取系统程序创建的新帐户的所有权的方式。 在Solana上,“钱包”只是系统程序拥有的帐户。钱包的lamport余额是账户拥有的SOL金额。

System Account

只有系统程序拥有的帐户才能用作交易费用支付方。

 

数据帐户

Solana程序是“无状态的”,这意味着程序帐户仅包含程序的可执行字节码。若要存储和修改其他数据,必须创建新帐户。这些帐户通常称为“数据帐户”。

数据帐户可以存储所有者程序代码中定义的任何任意数据。

Data Account

请注意,只有系统程序可以创建新帐户。一旦系统程序创建了一个帐户,它就可以将新帐户的所有权转移到另一个程序。

换句话说,为自定义程序创建数据帐户需要两个步骤:

  1. 调用系统程序创建一个帐户,然后将所有权转移给自定义程序
  2. 调用现在拥有该帐户的自定义程序,然后初始化程序代码中定义的帐户数据

此数据帐户创建过程通常抽象为单个步骤,但了解基础过程很有帮助。

 

如何编写程序

这里我们看一个简单的 solana 程序,这是 Rust 编写的 hello world 程序,实现了简单的日志打印。通常我们将程序写在lib.rs文件中:

// 引入 Solana 程序的相关依赖
use solana_program::{
    account_info::AccountInfo,
    entrypoint,
    entrypoint::ProgramResult,
    pubkey::Pubkey,
    msg
};

// 程序入口点
entrypoint!(process_instruction);

// 指令处理逻辑
pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8]
) -> ProgramResult{
    msg!("Hello, world!");

    Ok(())
}

所有的程序都有一个单独的入口点,类似于 Rust 中的main函数,指令的执行就是从这里开始的(即process_instruction),参数须包括:

  • program_id: pubkey (程序ID,即程序地址)
  • accounts: AccountInfo数组,指令所涉及的账户集合。
  • instruction_data: byte array字节数组,即指令所需的参数,该例子中并没有用到。

 

在实际的项目中,通常不会把所有逻辑都写在lib.rs文件中,为了更清晰的划分功能模块,大部分程序遵循以下架构:

image-20241027162746638

 

交易与指令

在Solana上,我们发送交易与网络进行交互。交易包括一个或多个 指令,每个指令代表一个要处理的具体操作。指令的执行逻辑存储在部署到Solana网络的程序上,每个程序都存储自己的指令集。

  • 交易:是一组原子性的操作,代表对区块链状态的一系列更改,包括转账代币、调用程序、更新账户状态等。每个交易都具有唯一的签名,并由一个或多个指令组成。交易费用的支付通常使用 Solana 的原生代币 SOL。
  • 签名:每个交易都必须由一个或多个账户的私钥进行签名,以确保交易的身份和完整性。
  • 指令:是交易中的一条具体指令,包含执行指令所需的具体数据,可以包括执行指令的程序唯一标识 program_id、账户列表、指令参数、配置信息等,用于执行一个特定的操作。

 

以下是有关如何执行交易的关键细节:

  • 执行顺序:如果交易包含多条指令,则按照指令添加到交易中的顺序进行处理。
  • 原子性:交易是原子性的,这意味着它要么完全完成,所有指令都成功处理,要么完全失败。如果交易中的任何指令失败,则不会执行任何指令。

简单来说,可以将交易视为处理一条或多条指令的请求。

 

交易

Solana交易由以下组成:

  1. 签名:交易中包含的签名数组。
  2. 消息:要原子性处理的指令列表。

交易格式

交易消息的结构包括:

交易消息

 

在进行一笔转账交易后我们可以在区块链浏览器查看相关操作,就可以看见一笔转账交易包含了三个指令:

  • Set Compute Unit Price: 设置单个CU的价格
  • Set Compute Unit Limit:设置最多能消耗的CU的数量
  • Transfer: 进行一次转账

image-20241027164243649

 

交易大小

Solana 网络坚持 1280 字节的最大传输单元 (MTU) 大小,符合IPv6 MTU大小限制,以确保通过 UDP 快速可靠地传输集群信息。在考虑必要的标头(IPv6 为 40 字节,分段为8 字节)后,仍有 1232 个字节可用于数据包的数据,例如序列化交易。

这意味着 Solana 交易的总大小限制为 1232 字节。签名和消息的组合不能超过此限制。

  • 签名:每个签名需要 64 字节。签名的数量可能会有所不同,具体取决于交易的要求。
  • 消息:消息包括说明、帐户和其他元数据,每个帐户需要 32 个字节。账户和元数据的组合大小可能会有所不同,具体取决于交易中包含的指令。

交易格式

 

交易费用

执行一个交易就需要 Compute unit。 如果你熟悉 EVM,CU(Compute unit)就像是gas fee

当然如果你不熟悉也没关系,Solana 就像个由多个节点连接组成的公共巨型计算机,节点运行者往往需要投入大量的物理资源(如CPU, GPU)来维持巨型计算机的稳定运行,为了奖励节点运行者处理链上大量的交易维持网络的稳定,gas费将做为他们贡献的补偿。

当然 CU 的存在还有一些别的目的,比如:

1.通过对交易引入实际成本,减少网络垃圾

2.设定每笔交易的最低费用金额,为网络提供长期的经济稳定性

因此,当用户在链上发送一笔交易时,往往需要支付一笔手续费用于处理交易中所包含的指令。

 

CU最大限制

由于每笔交易中所包含的指令调用数量和数据量的不同,每笔交易都设定了最大的CU限制——”compute budget”以确保单笔交易的数据量不会过大从而造成网络的拥堵。

每条指令的执行都会消耗不同数量的CU,在消耗了大量的CU后(即消耗的CU已经超出了”compute budget”所限定的最大CU),指令运行将停止并返回错误,从而导致交易失败。

 

交易费

在一笔转账交易中,我们可以看到其中包含了对于CU limitCU price的设置。

指令Set Compute Unit Price中,可以看到compute budget 程序将每CU的价格设定为 50000 lamports (1 SOL = 1000,000,000 lamports)

指令Set Compute Unit Limit中,compute budget程序将该笔交易的CU消耗上限设置为200,000. 当一笔交易所有的指令CU消耗超过了200,000时,交易将会失败。

手续费的计算公式为: CU数量 * CU价格 = 手续费用

 

交易的确认

一笔交易在根据在solana网络上的确认程度可以分为以下几类主要状态:

'processed': 查询已通过连接节点获得1次确认的最新区块
'confirmed': 查询已通过集群获得1次确认的最新区块
'finalized': 查询已由集群完成的最新区块

image-20241027164415058

 

   

指令

指令是链上处理特定操作的请求,是 程序中最小的连续执行逻辑单元。

在构建要添加到交易中的指令时,每个指令必须包括以下信息:

  • 程序地址:指定被调用的程序。
  • 账户:列出指令读取或写入的每个账户,包括使用 AccountMeta 结构体的其他程序。
  • 指令数据:一个字节数组,用于指定要调用程序上的指令处理程序,以及指令处理程序所需的任何其他数据(函数参数)。

交易指令

 

账户元

对于指令所需的每个账户,必须指定以下信息:

  • pubkey:账户的链上地址
  • is_signer:指定是否需要该帐户作为交易的签署者
  • is_writable:指定是否修改帐户数据

这些信息被称为AccountMeta账户元。

AccountMeta

通过指定指令所需的所有账户,以及每个账户是否可写,交易可以并行处理。

例如,两个不包含写入相同状态的任何账户的交易可以同时执行。

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
加密鲸拓
加密鲸拓
现Golang 后台开发,Web3 技术爱好者,承接各类合作,欢迎联系