Solana 开发 必须要知道的 核心 概念
原文:Solana核心概念(本文对原文代码进行了深入讲解)
想深入或者了解与Solana
相关的开发或知识,可以先看看这些核心知识有没有掌握,这些是成为 Solana
开发者的重要基础。Solana
在 rust
生态,以及区块链世界中都有着举足轻重的地位,而后大多数新的区块链项目都开始纷纷使用rust
语言进行编写,可以说想进入区块链世界不得不学习rust
语言。所以我们可以从Solana
入手,从不了解到熟练,我们一起开始吧:
Account
在Solana中,"Everythin is an Account" 类似Linux世界里面把所有的资源都抽象成"文件"一样。
Solana
作为一个分布式区块链系统,所有的信息都存储在Account
对象中,如合约(Solana叫Onchain Program), 账号信息,合约中存储的内容等都是存储在一个个Account
对象中。
Account
的定义如下:
pub struct Account {
/// 账户中的lamports数量
pub lamports: u64,
/// 此账户持有的数据
#[serde(with = "serde_bytes")]
pub data: Vec<u8>,
/// 拥有此账户的程序。如果可执行,加载此账户的程序。
pub owner: Pubkey,
/// 此账户的数据包含已加载的程序(现在为只读)
pub executable: bool,
/// 此账户下一次欠租金的纪元
pub rent_epoch: Epoch,
}
lamports
:这是账户中的资金单位这是为了纪念计算机科学家莱斯利·兰伯特(Leslie Lamport),他对分布式系统和区块链技术中的一些基本概念做出了重要贡献
,Solana的最小货币单位。1 SOL = 1,000,000,000lamports
。这个字段表明了账户拥有的lamports
数量。data
:这是账户存储的数据。在Solana
中,账户不仅可以持有资金,还可以持有与智能合约相关的数据或其他任何数据。这个字段就是用来存储这些数据的,它的内容和格式完全取决于账户的用途和设计。owner
:这是一个Pubkey
(公钥),标识了拥有这个账户的程序的公钥。在Solana
中,账户不只是用户的,也可以是程序的。如果一个账户是某个程序的一部分,那么这个字段就会指向那个程序的公钥。executable
:这是一个布尔值,指示账户是否包含可执行的程序。如果这个字段为true
,则表明这个账户是一个智能合约账户,里面包含了可执行的代码。rent_epoch
:这表示账户下一次需要支付租金的时代(epoch)。Solana通过租金机制来避免数据存储的无限膨胀,所有账户都需要定期支付租金以保留其数据存储空间。这个字段标记了账户下一次支付租金的时间。
账号和签名
Solana
的签名系统使用的是 Ed25519 ,说人话就是: Ed25519
是一种计算快,安全性高,且生成的签名内容小的一种不对称加密算法。新一代公链几乎都支持这个算法。
所以,我们用户理解的Solana
的账号,就是一串Ed25519
的私钥,各种钱包里面的助记词,会被转换成随机数种子, 再用随机数种子来生成一个私钥,所以助记词最终也是换算成私钥。所以用户账号的本质就是私钥,而用户账号的地址 则是这私钥对应的公钥,优于公钥是二进制的,为了可读性,将其进行Base58编码后的值,就是这个账号的地址。 如:HawRVHh7t4d3H3bitWHFt25WhhoDmbJMCfWdESQQoYEy
把这里的公钥和私钥放一起,就是所谓的Keypair
,或者叫公私钥对。假设这里把私钥进行加密,并由用户来设置密码, 公钥作为这个私钥的索引。就实现了一个简单的钱包系统了。
通过用户选择的公钥,加上密码,得到对应的私钥,再用私钥去操作的他的账号
交易
交易就是链外数据和链上数据产生的一次交互。比如发起一笔转账,在StepN 一个基于区块链的移动健康和健身应用,它允许用户通过运动来赚取加密货币
里面发起一次Claim (领取)通常指的是用户对其赚取的奖励或代币进行领取的操作
动作。 交易是对多个交易指令的打包,所以起内容主要就是各个交易指令,以及相应指令对应的发起人和签名。
Transaction
的定义为:
pub struct Message {
/// 消息头,标识已签名和只读的`account_keys`。
/// 头部值只描述静态的`account_keys`,它们不描述通过地址表查找加载的任何额外账户键。
pub header: MessageHeader,
/// 此交易加载的账户列表。
#[serde(with = "short_vec")]
pub account_keys: Vec<Pubkey>,
/// 最近区块的区块哈希。
pub recent_blockhash: Hash,
/// 指令调用一个指定程序,按顺序执行,
/// 如果全部成功,则在一个原子交易中提交。
///
/// # 注意
///
/// 程序索引必须索引到消息`account_keys`列表中,因为程序id不能从查找表中动态加载。
///
/// 账户索引必须索引到通过以下三个键列表的连接构造的地址列表中:
/// 1) 消息`account_keys`
/// 2) 从`writable`查找表索引加载的键的有序列表
/// 3) 从`readable`查找表索引加载的键的有序列表
#[serde(with = "short_vec")]
pub instructions: Vec<CompiledInstruction>,
/// 用于加载此交易的额外账户的地址表查找列表
#[serde(with = "short_vec")]
pub address_table_lookups: Vec<MessageAddressTableLookup>,
}
pub enum VersionedMessage {
Legacy(LegacyMessage),
V0(v0::Message),
}
pub struct VersionedTransaction {
/// 签名列表
#[serde(with = "short_vec")]
pub signatures: Vec<Signature>,
/// 要签名的消息。
pub message: VersionedMessage,
}
- 交易(Transaction) :在
Solana
中,一次交易是由用户发起的,可以包含多个指令(Instructions)。每个指令可以调用区块链上的程序(智能合约),以执行特定的操作,比如转账、更改账户状态等。 - 消息(Message) :交易中包含的消息部分,定义了交易的具体内容,包括所涉及的账户、最近的区块哈希、以及要执行的一系列指令等。
Message 结构体
header
:消息头部,包含有关签名和只读账户的信息。这些信息帮助Solana网络确定哪些账户需要签名,哪些是只读的。account_keys
:这是一个账户公钥列表,交易中涉及的所有账户(包括发送者、接收者、以及任何相关的智能合约账户)都会在这里列出。recent_blockhash
:最近一个区块的哈希值,这用于确保交易的时效性,防止重放攻击。instructions
:这是交易的核心部分,每个CompiledInstruction
定义了一个指令,包括指令的目标程序、需要传递给程序的账户列表、以及程序需要的输入数据。address_table_lookups
:这用于加载额外的账户信息,Solana引入地址表是为了优化交易数据的大小,通过地址表可以引用更多的账户而不需要在每个交易中完全列出它们的公钥。
CompiledInstruction 结构体
program_id_index
:这是一个索引,指向account_keys
列表中的一个元素,标识将执行此指令的程序(智能合约)的账户。accounts
:这是一个账户索引列表,指出哪些账户将被传递给程序。这些索引也指向account_keys
列表中的元素。data
:程序的输入数据,这是一串字节,其内容和格式取决于程序的设计。
VersionedMessage 和 VersionedTransaction
-
VersionedMessage
:提供了消息格式的不同版本支持,以便未来对协议进行升级和扩展。 -
VersionedTransaction
:signatures
:所有必要的签名集合,每个签名对应于交易中涉及的一个或多个需要签名的账户。message
:实际的消息内容,可以是Legacy
或V0
格式,根据版本的不同,消息的处理和解析方式可能会有所不同。
交易指令
上面说到的交易指令又是什么呢?先来看下定义:
pub struct CompiledInstruction {
/// 交易键数组中的索引,指示执行此指令的程序账户。
pub program_id_index: u8,
/// 有序的索引到交易键数组中,指示哪些账户传递给程序。
#[serde(with = "short_vec")]
pub accounts: Vec<u8>,
/// 程序输入数据。
#[serde(with = "short_vec")]
pub data: Vec<u8>,
}
program_id_index
:这是一个索引,指向交易中的account_keys
数组,表示执行这个指令的程序账户的位置。这个程序就是智能合约,负责处理这个指令。accounts
:这是一个账户索引列表,指向account_keys
数组,表明这个指令涉及到哪些账户。这些账户可能是需要读取或写入数据的账户,也可能是需要转账lamports的账户。data
:这是传递给程序的输入数据。它是一串字节,具体的格式和含义由程序定义。程序会根据这些数据来执行相应的逻辑。
合约
合约分为两类,一类是普通合约一类是系统合约,前者在Solana中称为On Chain Program
后者称为Native Program
其实本质都是类似其他公链上所说的合约。
系统合约
系统合约是由节点在部署的时候生成的,普通用户无法更新,他们像普通合约一样,可以被其他合约或者RPC进行调用
系统合约有
- System Program: 创建账号,转账等作用
- BPF Loader Program: 部署和更新合约
- Vote program: 创建并管理用户POS代理投票的状态和奖励
- ...
普通合约
一般我们说的合约都是普通合约,或者叫 On Chain Program
。普通合约是由用户开发并部署,Solana
官方也有 一些官方开发的合约,如Token
、ATA账号
等合约。
当用户通过BPF Loader Program
部署一个新合约的时候,新合约Account
中的executable
被标记为true
,表示他是一个可以 被执行的合约账号。不同于有些公链,Solana
上的合约是可以被更新的,也可以被销毁。并且当销毁的时候,用于存储 代码的账号所消耗的资源也会归还给部署者。
合约与Account
在上面的Account
介绍中,我们有个owner
的成员,这个就表示这个Account
是被哪个合约管理的,或者说哪个 合约可以对这个Account进行读写,类似Linux操作系统中,文件属于哪个用户。
比如一般合约,他的Owner
都是BPF Loader
:
而存放我们代币余额的内容的ower都是Token合约:
对应的代币为:
租约
Solana
的资金模型中,每个 Solana
账户在区块链上存储数据的费用称为租金
。 这种基于时间和空间的费用来保持账户及其数据在区块链上的活动为节点提供相应的收入。
所有 Solana
账户 都需要保持足够高的 LAMPORT
余额,才能免除租金并保留在 Solana
区块链上。
当账户不再有足够的 LAMPORTS
来支付租金时,它将通过称为垃圾收集的过程从网络中删除。
注意:租金与交易费用不同。 支付租金(或保存在账户中)以将数据存储在 Solana
区块链上。 而交易费用是为了处理网络上的指令而支付的。
租金率
Solana
租金率是在网络范围内设置的,主要基于每年每字节设置的 LAMPORTS
。 目前,租金率为静态金额并存储在 Rent
系统变量中。
免租
保持最低 LAMPORT
余额超过 2 年租金的账户被视为“免租金”,不会产生租金。
每次账户余额减少时,都会检查该账户是否仍免租金。 导致账户余额低于租金豁免阈值的交易将会失败。
垃圾收集
未保持租金豁免状态或余额不足以支付租金的帐户将通过称为垃圾收集的过程从网络中删除。 完成此过程是为了帮助减少不再使用/维护的数据的网络范围存储。
关于租约的提案
转载自:https://juejin.cn/post/7351336619595563048