Solana基础概念

本文介绍了Solana比较重要的一些概念,包括共识机制、账户模型、程序、交易、指令等等

参考文档: https://www.hackquest.io/zh

https://www.solana-cn.com/SolanaDocumention/home.html

 

Solana VS ETH

一、共识机制

Solana

  • Proof of History (PoH): PoH 是一种创新的时间记录机制,通过加密散列函数生成一个可验证的时间顺序,使得区块链中的事件可以按时间线性顺序排列。这减少了节点之间的时间同步需求,使得节点能够迅速达成共识,而无需等待整个网络确认。
  • Proof of Stake (PoS): 验证者需要抵押一定数量的 SOL 代币,持有更多代币的验证者有更高的概率被选中生成区块。PoS 提供了经济激励,确保验证者行为诚实。
  • PoH 确保区块的时间戳和顺序,PoS 则确保网络的安全性和抗攻击性,两者结合使得网络能够达到每秒数千笔交易处理速度

以太坊

  • 从 PoW 到 PoS 的转变: 以太坊 2.0 的升级(The Merge)将共识机制从能源密集的 PoW 转为 PoS,大幅降低了能耗。验证者通过锁定 ETH 参与共识,取代了矿工的角色,提升了网络的安全性和可持续性。

 

二、交易处理能力

Solana

  • 并行处理: 通过将交易分解为多个子集,利用不同的验证节点并行处理,提高了吞吐量。Solana 的架构支持高达每秒数千笔交易,平均出块时间为 400 毫秒,平均每秒 2000+ 笔交易,在高负载时保持较低的交易费用。
  • 低延迟和低费用: Solana 的架构设计使其能够快速确认交易,通常在一秒以内完成。即使在高负载下,交易费用仍保持低廉,适合需要高频交易的应用。

以太坊

  • 串行执行: 以太坊的智能合约在虚拟机(EVM)中按顺序执行,每笔交易必须等待前一笔完成才能开始。虽然这种方式确保了状态的一致性,但也限制了以太坊吞吐量长期维持在每秒15笔~30笔,以牺牲性能换来安全性和一致性。
  • Layer 2 和 Rollup: Layer 2 解决方案通过将大量交易在链下处理,然后将结果提交到主链,显著提高了交易速度和降低了成本。Rollup 是其中一种常见的技术,能够将数百笔交易合并为一个批次,能够将燃料费减少多达100倍。

 

三、交易费用(Gas费)

Solana它的交易费用是根据交易的复杂度和大小动态计算的,这意味着,交易费用会根据交易的执行成本而变化,而不是根据网络上的交易量变化。Solana 的平均交易费用通常低于 0.01 美元,平均为 0.00025 美元,这使得进行小额交易更具成本效益。

以太坊 的交易费用因网络拥堵而波动,这是一种纯粹的市场机制,网络中交易拥堵情况下你的交易要想被确认,就需要支付高昂的手续费。目前一笔转账交易的手续费大概在 1 ~ 10 美元左右。

 

四、智能合约

Solana中一切皆账户,它的智能合约(Solana 中称之为程序program,后续也统一使用“程序”这一术语)也是账户,但细分为可执行账户数据账户,前者存储程序的代码,用来执行特定的逻辑,后者存储状态,即程序运行时的数据。这种分离的模式,使得程序的升级更加简单,因为程序本身无状态,可以直接升级为新的代码逻辑。

以太坊 的智能合约本身就包含了合约的逻辑代码,以及状态数据。因此合约部署之后,就不支持直接的升级,只能通过代理的方式间接升级,即重新部署一套合约代码,生成新的合约地址,代理再指向这个新的合约地址。

 

五、账户

Solana中一切皆账户,它的账户就像一个容器(或者电脑中的文件夹),可以包含程序代码、状态数据以及账户元数据。按照功能可划分为可执行账户数据账户,前者为存储程序代码的账户,也称为程序账户。后者包括普通用户账户和其他非程序账户,这些账户存储了用户的余额、交易历史和其他相关数据,但它们本身不包含程序代码。对于熟悉以太坊账户的同学来说,这种划分方式也许有点奇怪,不过随着了解的深入,就会认识到 Solana 的单一账户模型,使得多个交易能够并行处理,这正是 Solana 高性能的基础。

以太坊 分为EOA账户智能合约,前者是普通用户在以太坊网络中的账户,用于存储以太币(ETH)和进行交易。后者是包含智能合约代码和状态的账户,这些账户由合约创建并部署在以太坊区块链上。通过这两种账户类型的结合,以太坊提供了一个灵活且功能强大的去中心化应用开发平台。

 

共识机制

PoH 工作流程

Proof of History (PoH) 是 Solana 区块链的核心创新之一,它为区块链提供了一个去中心化的时间源。PoH 本质上是一个高频可验证延迟函数 (VDF),它能够创建历史记录,证明某个事件发生在特定时刻之前或之后。

我们看一个简化版的 PoH 工作流程,从一个随机值开始,运行 hash 函数,并将输出(output)作为输入(input)再次运行该函数。记录函数执行的次数(index)以及每次调用的结果(output)。次数,提供了顺序和时间两个维度的支持;将输出作为输入,依次头尾相连,形成了一条完整的证据链。

随机值的选择,可以选择纽约时报当天的标题,或者其他的事实。

image-20241027152102800

 

hashN 代表实际的 Hash 输出,Solana 中每个交易都与前一笔交易的哈希相连接,形成一个由交易构成的链,当一定数量的交易都被执行并构成了PoH 链后,它们被打包到一个区块(slot)中。

image-20241027152111317

 

只要选择的哈希函数是抗碰撞的,这个哈希集和就只能被单线程顺序计算出来。这满足了在 index 为 300 时,如果不通过算法实际运行 300 次,无法获得这样的结果的设定。

因此,我们可以从数据结构中推测出从 index 0 到 index 300 真实的过去的时间。这样以来,在网络中尽管每个节点的时间戳可能会有不一致,但是我们通过 hash 的次数(比如300),就变相的替代了时间,于是网络中有了全局一致的时间钟,并保持了交易的顺序。

 

PoH 的特性和优势

  1. 去中心化时间戳: PoH 提供了一个不依赖中心化时间服务器的时间戳机制。
  2. 顺序保证: 由于每个事件都嵌入到连续的哈希链中,事件的顺序得到了密码学保证。
  3. 高效验证: 验证者可以并行验证 PoH 链,大大提高了验证速度。
  4. 减少网络开销: 节点间不需要频繁同步时间,减少了网络通信。
  5. 提高交易处理速度: PoH 允许 Solana 在不牺牲安全性的情况下实现高吞吐量。

 

PoH 的广播和验证

  1. 区块生成: Leader 节点生成包含 PoH 序列和交易的区块。
  2. 并行验证: 其他节点接收区块后,可以将 PoH 序列分割成多个部分,在 GPU 上并行验证。
  3. 快速共识: 由于 PoH 提供了时间和顺序保证,节点可以快速就区块的有效性达成共识。
  4. 动态调整: Solana 可以根据网络条件动态调整 PoH 的哈希频率,以适应不同的硬件能力。

 

PoH 应用

假设我们正在观察 Solana 网络中的一个时间段,我们将看到 PoH 是如何与其他组件协同工作的。

1. PoH 生成器启动

  • 假设当前的 PoH 生成器(通常是当前的 leader 节点)开始工作。
  • 初始哈希值:0x1234...(这可能来自之前的状态或预定义的起始点)

2. PoH 序列生成

PoH 生成器开始连续进行哈希操作:

Index 0: Hash(0x1234...) = 0xabcd...
Index 1: Hash(0xabcd...) = 0xef01...
Index 2: Hash(0xef01...) = 0x2345...
...

3. 交易到达

  • 在 Index 50 时,一个交易 T1 到达:"Alice 发送 5 SOL 给 Bob"
  • T1 的哈希值为 0x7890...

4. 交易嵌入 PoH

PoH 生成器将 T1 嵌入到 PoH 序列中:

Index 50: Hash(0x...|| 0x7890...) = 0xdead...
Index 51: Hash(0xdead...) = 0xbeef...
...

5. 继续生成 PoH 和处理交易

  • 更多的交易陆续到达并被嵌入 PoH 序列
  • 例如,在 Index 100 嵌入 T2,Index 150 嵌入 T3,等等

6. 形成区块

  • 假设每 200 个 PoH 条目形成一个区块
  • 当达到 Index 200 时,当前的 PoH 序列和所有嵌入的交易被打包成一个区块

7. 区块广播和验证

  • Leader 节点将这个区块广播给其他验证者节点
  • 验证者接收到区块后,可以并行验证 PoH 序列:
    • 验证者 1 检查 Index 0-66
    • 验证者 2 检查 Index 67-133
    • 验证者 3 检查 Index 134-200

8. 共识和确认

  • 验证者快速确认 PoH 序列的正确性
  • 他们也验证嵌入的交易(T1, T2, T3 等)是否有效
  • 一旦大多数验证者确认,区块被添加到链上

9. Leader 轮换

  • 基于 PoH 序列,网络预先知道下一个 leader 是谁
  • 例如,如果每 1000 个 PoH 条目轮换一次 leader,那么在 Index 1000 时,新的 leader 接管 PoH 生成

10. 时间估算

  • 假设每次哈希操作平均需要 0.1 毫秒
  • 那么 200 个 PoH 条目(一个区块)大约代表 20 毫秒的真实时间
  • 网络可以据此估算交易的确切时间戳

 

实际应用中的优势

  1. 高吞吐量:由于 PoH 提供了时间和顺序保证,Solana 可以并行处理大量交易。
  2. 低延迟:交易几乎立即被嵌入 PoH,提供近乎实时的确认。
  3. 可预测的 leader 选择:网络总是知道谁是下一个 leader,减少了切换开销。
  4. 快速终结性:由于快速的验证过程,交易可以在几秒内达到最终确认。
  5. 精确的时间戳:每个交易都有一个与 PoH 序列关联的精确时间戳。

 

POS 工作流程

先来了解下在 Solana的系统架构中最重要的两种角色:Leader(出块者)和 Validator(验证者)。两者实际上都是质押了 SOL 代币的全节点,只是在不同的出块周期内,Leader 会由不同的全节点来充当,而没有当选 Leader 的全节点会成为 Validator。 所以在选择验证者方面, Solana 采用的是 PoS(权益证明)机制,验证者是通过抵押一定数量的代币来参与网络交易的验证的,持有更多代币的验证者有更大的机会被选中生成新的区块。

image-20241027151758993

 

1.用户发起交易后,会被客户端直接转发给 Leader 节点,或者先被普通节点接收,再立刻转发给 Leader;

2.出块者 Leader 接收网络内全部的待处理交易,一边执行,一边给交易指令排序,制成交易序列(类似区块)。每隔一段时间,Leader 会把排好的交易序列发送给 Validator 验证节点;

3.Validator 按照交易序列(区块)给定的顺序执行交易,产生相应的状态信息State(执行交易会改变节点的状态,比如改变某些账户的余额);

4.每发送 N 个交易序列,Leader 会定期公开本地的状态 State,Validator会将其与自己的 State 作对比,给出 肯定/否定 的投票。这一步就类似于以太坊2.0或其他POS公链里的“检查点”。

5.如果在规定时间内,Leader收集到占全网 2/3 质押权重的节点们给出的肯定票,则此前发布的交易序列和状态 State 就被敲定,“检查点”通过,相当于区块完成最终确认 Finality;

6.一般而言,给出肯定票的 Validator 节点与出块者 Leader 所执行的交易、执行后的状态都是相同的,数据会同步。

 

Leader 选举

在 Solana 的共识协议中,有 Epoch(纪元)和Slot(间隔)两大时间单位。每个 Slot 约为0.4~0.8秒,相当于一个区块的时间间隔。而每个Epoch周期包含43.2万个Slot(区块),长达2~4天。每过4个 Slot(出块周期),Leader节点就会进行一次变更。

image-20241027153707784

 

Solana 使用一种称为 Tower BFT(一种改进的实用拜占庭容错算法)的共识机制,结合 Proof of Stake (PoS) 和 Proof of History (PoH) 来选择和轮换 leader。

  1. Leader 调度:
    • Solana 网络预先确定一个 leader 调度表,通常覆盖未来几天的时间。
    • 这个调度是基于验证者的质押量(stake)按比例分配的。
  2. Leader 轮换:
    • Leader 角色在验证者之间频繁轮换,通常每 1.6秒~3.2秒就会切换。
    • 在一个 "epoch"(大约 2-3 天)内,每个验证者都有机会成为 leader。
  3. 单一 Leader:
    • 在任何给定的时刻,只有一个节点作为 leader
    • Leader 负责生成 PoH 序列和打包交易到区块。
  4. Leader 的职责:
    • 生成 PoH 序列
    • 接收交易并将其打包到区块
    • 广播区块给其他验证者

 

在每个新的Epoch周期开始时,Solana网络会按照各节点的质押权重进行抽选,组成一个出块者Leader轮换名单,“钦定”了未来不同时刻的出块者。也就是说出块者会提前获知他们成为出块者。 具体而言,究竟如何指定 Leader 会考虑诸多因素,比如:

1.质押的代币数量: 在 PoS 中,质押的代币数量是一个关键的考虑因素。Validator 通常倾向于选择质押数量较大的节点,因为这增加了节点被选中为区块生产者的机会。这也有助于确保网络由具有足够利益参与的节点维护。

2.节点的性能: Validator 的节点性能是另一个关键因素。高性能的节点能够更快速地验证和打包交易,有助于维持网络的高吞吐量。Validator 可能会选择性能较好的节点以提高整个网络的效率。

3.网络延迟: Validator 可能会考虑节点之间的网络延迟。选择网络延迟较低的节点有助于减少区块的传播时间,从而提高网络的实时性。

4.节点的可用性: Validator 会关注节点的可用性,确保它们能够稳定运行而不容易出现故障。可靠的节点能够为网络提供更稳定的服务。

 

去中心化考虑

虽然 Solana 在任何时刻只有一个 leader,但这并不意味着系统是中心化的。以下几点说明了 Solana 如何维护去中心化:

  1. 频繁轮换:
    • Leader 角色快速轮换大大降低了单一实体控制网络的风险。
  2. 基于质押的概率选择:
    • 成为 leader 的机会与验证者的质押量成正比,鼓励更多参与者加入网络。
  3. 大量验证者:
    • Solana 网络有数百个活跃验证者,确保了足够的去中心化程度。
  4. 验证者的持续参与:
    • 即使不是 leader,其他验证者也在持续验证交易和区块,维护网络安全。
  5. 惩罚机制:
    • 对于行为不当的验证者(包括 leader),存在削减质押的惩罚机制。
  6. 开放参与:
    • 任何人只要满足硬件要求并质押足够的 SOL,都可以成为验证者。
  7. 治理决策:
    • 网络参数和升级通过去中心化的治理过程决定,而不是由单一实体控制。

 

Solana 要实现的目标是选择一组合适的 Leader,确保网络的安全性、效率和公平性。通过综合考虑这些因素,Solana 能够做出最佳的区块生产决策,从而推动网络的正常运行。

 

小结

现在在了解了基础的细节后,让我们来看看 Solana 的网络整体是如何通过 PoH 和 PoS 运作的:

1.生成交易: 用户创建并广播交易,包含交易的详细信息和数字签名。

2.PoH 链上的排序: 交易的哈希通过数字签名连接到 PoH 链上。由于 PoH 链是有序的,交易也就被排序了。

3.Validator 验证: Validator 负责验证交易的有效性,并选择哪些交易将包含在下一个区块中。Validator 的选择可能基于质押的代币数量、验证者的性能等因素。

4.交易打包成区块: Validator 选择的交易被打包成一个区块,其中包括一个特殊的块生产交易,它包含了当前 PoH 链的哈希以及其他信息。

5.区块传播和确认: 区块广播到整个网络,其他节点验证并确认区块的有效性。确认后,区块和其中包含的交易就被添加到整个区块链中。

通过这个过程,Solana 通过 PoH 链的时间有序性和 PoS 的节点验证机制,实现了交易的流转和整个区块链的更新。这样的设计使得 Solana 平均出块时间被压缩至 400 毫秒,且无需 Layer2 即拥有较高速度,手续费亦可忽略不计。

 

账户模型

每个帐户都可以通过其唯一地址进行识别,地址以Ed25519算法生成32个字节的公钥来表示。

在以太坊中,我们将代码与数据、状态直接存储在智能合约中。而 Solana 账户最大的不同就是将两者分开存放在不同的账户上。所以 Solana 账户又分为程序账户数据账户

  • 程序账户(可执行账户):存储不可变的数据,主要用于存储程序的代码(BPF 字节码)。
  • 数据账户(不可执行账户):存储可变的数据,主要用于存储程序的状态。

说明:Solana中的智能合约并不叫“智能合约”,而是“程序program”,尽管它们代表的是相似的概念。为了避免混淆,后续我们将统一使用“程序”这一术语。

 

账户分类

基本账户

  • 数据账户:用于存储各种数据,如用户的 SOL 余额、代币余额、NFT 元数据或游戏状态信息。
  • 程序账户:存储可执行代码,类似于智能合约。一旦部署,这些程序就变成不可变的,可以被其他账户或程序调用执行。
  • 原生账户:这是 Solana 内置的特殊账户,用于执行系统级的功能,如创建新账户或转账 SOL。

 

特殊账户类型

  • PDA(Program Derived Addresses):程序派生地址。PDA 是由程序确定性生成的地址,不与任何私钥相关联。该类账户存储程序的状态,即程序执行过程中存储的数据,跟以太坊的状态是一个概念。
  • ATA(Associated Token Account)账户:关联账户。它是用户与特定的 SPL(Solana Program Library)Token 代币关联的账户,地址是用户主钱包和代币铸造地址的派生,主要作用是允许用户方便管理他们持有的代币。

 

Solana 链上程序是只读或无状态的,即程序的账户(可执行账户)只存储代码,不存储任何状态,程序会把状态存储在其他独立的账户(不可执行账户)中。如果一个程序账户是一个数据账户的所有者,那么它就可以改变数据账户中的状态。

要点

  • 帐户最多可以存储10MB的数据,这些数据主要包含由可执行程序代码或程序状态。
  • 帐户需要以SOL为单位的租赁押金,与存储的数据量成正比,该押金在帐户关闭时可全额退还。
  • 每个帐户都有一个程序“所有者”。只有帐户所有者的程序才能修改其数据或扣除其lamport余额。但是,任何人都可以增加余额。
  • 程序(智能合约)是存储可执行代码的无状态帐户。
  • 数据帐户由程序创建,用于存储和管理程序状态。
  • 本机程序是Solana运行时附带的内置程序。
  • Sysvar帐户是存储网络群集状态的特殊帐户。

 

账户定义

帐户的最大大小为10MB(10兆字节),存储在 Solana 上每个帐户上的数据具有以下结构,称为AccountInfo

image-20241027154916018

对于每个帐户AccountInfo都包含以下字段:

  • lamports: 表示账户余额,lamport 是 Solana 中的基本货币单位,类似于以太坊的wei。
  • data:表示存储的内容,这是一个字节数组,可以包含任意类型的数据,如程序的状态、用户资产信息,以及存储程序的字节码等。
  • owner:表示拥有或管理该账户的程序的公钥。这表示了哪个程序有权对该账户进行操作。如果账户包含的数据是可执行的,那么owner表示加载该账户的程序。
  • executable:表示是否可执行,如果为true,表示该账户中的数据可以被执行,是程序账户。如果为false,表示该账户用于存储普通的数据,而不是可执行代码。
  • rent_epoch:表示下一次该账户将被扣除租金的时期。Solana使用租金机制来防止账户被无限期占用而不使用,避免状态膨胀。

作为Solana账户模型的关键部分,Solana 上的每个账户都有一个指定的“所有者”,特别是一个程序。只有被指定为账户所有者的程序才能修改账户上存储的数据或扣除lamport余额。需要注意的是,虽然只有所有者可以扣除余额,但任何人都可以增加余额。

 

租金机制

Solana 引入了租金概念来管理账户存储和网络资源使用。租金与交易费用不同。用户支付租金以将数据存储在 Solana 区块链上。而交易费用是为了处理网络上的指令而支付的。

  1. 租金的目的:
    • 防止网络存储无限增长
    • 激励用户清理不再需要的账户
    • 补偿验证者存储和处理数据的成本
  2. 租金计算:
    • 基于账户大小(以字节为单位)
    • 当前租金率:每字节每年 0.00000348 SOL(可能会随时间调整)
  3. 租金豁免:
    • 账户余额超过两年租金的账户可以免租金
    • 例如,如果账户大小为 1KB,约需 0.0035 SOL 来获得租金豁免
  4. 租金收取:
    • 每个 epoch(约 2-3 天)收取一次
    • 从账户余额中扣除
  5. 账户关闭:
    • 如果账户余额不足以支付租金,账户可能被关闭
    • 关闭的账户数据将被删除,剩余余额返回到一个指定账户

 

PDA

在Solana区块链中,PDA指的是“程序派生地址”(Program Derived Address)。这是一种特殊类型的地址,由 Solana 的程序生成,而不是由用户的私钥直接派生。PDA的主要目的是允许程序拥有和控制某些数据或资产,而不需要传统的私钥签名

 

为什么需要 PDA?

在区块链中,你需要一个私钥来证明你拥有一个公钥的所有权,同时你才能签字同意这个账户的转账请求。但如果这个账户的所有者不是一个人而是一个去中心化程序,那么把私钥放在这个程序上就不是一个好主意,因为所有程序代码都在链上都是公开的,如果所有人都能看到你的私钥,那么人们就能进行一些恶意操作,比如偷走你的代币。这时我们就需要一个没有私钥的 PDA。 这样程序不需要私钥就能对一个地址进行签名操作

 

私钥、公钥与助记词

Solana 和以太坊一样,有私钥、公钥、助记词三种东西。私钥是私自保管不可示人的,当我们授权某一笔交易时,我们需要通过私钥签名并“授权”该交易。私钥是一串乱码,不好记,与之对应有一串助记词。助记词可以通过算法推出私钥,所以实际上我们在使用钱包时,只要记住助记词。而我们可以通过加密算法从私钥推算出公钥。公钥是可以展示给别人看的,别人通过你的公钥给你转账,同时公钥也是程序的地址,也叫 program_id

要注意的是我们只能从助记词通过加密算法推算出私钥,从私钥推算出公钥,而无法反向从公钥推算出私钥,从私钥推算出助记词。否则我们所有加密地址中的代币都可以被任何人控制了。这其中的过程通过ECDSA算法进行计算,如果希望了解具体的原理可以先从了解 ECDSA算法开始。但是理解 PDA 账户可以不必这么深入,让我们先把这个过程进行一个简化。

image-20241027161341441

 

如上图所示(这是一个简化的图示),每一个 X 轴私钥会在曲线上对应一个 Y轴公钥,**但是 Q 点的 公钥没有对应任何一个 X 轴上的私钥。那么这就意味着这个公钥没有对应的私钥!**这意味着这个公钥不是从私钥派生/衍生(Secret Key Derived)出来的。

这就是 PDA 的原理,“程序派生地址”(Program Derived Address)是没有对应的私钥的,它是由一个程序的program_id和seed派生/衍生出来的,这也是为什么它被称之为“程序派生地址”(Program Derived Address)。有时候我们通过 program_id 和 seed 获得的公钥正好有对应的私钥,那么这种情况下我们就需要重新生成一个公钥,通常是在我们的 program_id 和 seed 之外再加上一个数字(这个数字有个专有名词叫 bump),这个数字从 255 开始,依次往下,直到生成的公钥没有私钥为止。

 

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

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

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

作者:加密鲸拓

版权:此文章版权归 加密鲸拓 所有,如有转载,请注明出处!