Solana区块链堪称一款强大利器,能够每秒处理数千笔交易,而且几乎无需支付交易手续费。倘若你是初涉Web3领域的新手,又或是此前在基于EVM的区块链上开展过开发工作,那么这份指南定能助力你理解Solana的基本要素,开启你的Solana开发征程。要是你已然在Solana的基础上进行项目构建,也可将此指南当作参考资料,进一步深化对Solana基础知识的认知。
在这份指南里,我们会详细介绍Solana的基础构成部分,具体涵盖:账户、程序、指令、交易、RPC以及订阅。话不多说,让我们正式开启探索之旅!
简而言之
账户类似于传统操作系统中的文件,在Solana上,多数事物都以账户形式存在,像用户钱包、程序、数据日志,甚至系统程序皆是如此。
程序(在其他区块链上被称作智能合约)属于特定类型的 “可执行” 账户,它们无状态,仅依据用户输入参数执行代码。程序自身无法更新,不过拥有特定权限,可更新其 “掌控” 的其他账户的数据(或状态)。
用户借助RPC向Solana集群签署并提交事务(即程序特定指令的集合),以此告知程序执行何种操作。
Solana采用一种名为历史证明(PoH)的独特共识机制,该机制借助时间和密码学,为发送至网络的交易添加时间戳并排序。
程序和交易签署者授权对链上某些数据账户进行更改,从而更新网络状态。
若想了解更多?那就继续往下阅读吧!👇
索拉纳是什么?
Solana是一个针对速度和成本优化的开源区块链,允许信息和状态在全球范围内同步。Solana使用一个名为“历史证明”的独特权益证明共识版本,该共识使用精细的可验证延迟函数来有效地对发送到网络的交易进行时间戳和排序。根据Anatoly Yakovenko在Solana白皮书中的说法,“它使用加密安全函数,因此无法从输入中预测输出,必须完全执行才能生成输出”(3)。“领导者对用户消息进行排序并对其进行排序,以便系统中的其他节点可以有效地处理它们,从而最大限度地提高吞吐量”(2)。
*这和本文中Solana的运行时一样深入,我们将把重点转移到您如何与Solana互动和构建上。如果您想更深入地了解索拉纳的建筑及其一些设计特征背后的理论,这里有一些很棒的资源:
帐户
与操作系统中的文件一样,帐户用于运行系统、存储数据或状态以及执行代码。
Solana上有3种主要类型的帐户:
- 存储信息和管理网络状态的数据帐户。数据帐户有两种类型:
- 包含可执行代码的程序帐户。我们将在下一节中更多地讨论这些,但这些帐户是无状态的(意味着数据通过它们,但不更新它们)[示例:糖果机v2程序帐户]
- 原生系统帐户(例如,系统程序、投票、押注、BPF加载器)[示例:Solana系统程序]
本文将重点关注#1和#2。如果您想了解有关#3,原生系统帐户的更多信息,请查看docs.solana.com。
简而言之,索拉纳上的几乎所有内容都是一个帐户。最常见的帐户类型是用户钱包,这是一个管理用户SOL余额状态的数据帐户。
每个帐户都有一个唯一的地址或公钥(很像文件路径)和一组相关的元数据:
- lamports:该帐户拥有的lamport数量(64位无符号整数)*
- 所有者:该帐户被分配的程序所有者(所有者程序的字符串地址)。所有者程序控制谁对帐户有写入访问权限——其他程序可以读取另一个帐户的数据,但如果他们试图修改该数据,交易将失败。
- 可执行文件:该帐户是否可以处理指令(布尔值)。可执行值为true的帐户实际上是一个程序(或智能合约)——我们将在下一节中讨论这些。
- 数据:与帐户相关的任何其他数据,存储为原始数据字节数组
- rent_epoch:该帐户将欠租金的下一个时代(64位无符号整数)
*lamports是SOL的最小面额,SOLana的原生代币(代表SOL的十亿分之一)
任何帐户的元数据都可以通过运行getAccountInfo JSON RPC查询或在Solana Explorer上搜索来读取。Solana Explorer允许您搜索任何帐户,并查看有关该帐户类型以及与哪个程序关联的基本详细信息。以下是系统拥有的数据帐户(用户的钱包)的示例:
让我们来可视化所有帐户类型的示例:
这里发生了很多事情,但你在这个数字中看到的一切都是一个帐户。在左侧,我们有一个名为“系统程序”的系统帐户,管理所有用户钱包。我们还有一个可执行的程序帐户,称为Solana SPL代币程序,用户可以用它来铸造、烧录和传输SPL代币。最后,我们有程序衍生帐户。在这种情况下,这些是令牌计划拥有的令牌帐户,并与用户帐户相关联。注意1个用户钱包如何拥有多个代币帐户。
请记住,您可以在Solana Explorer中搜索任何帐户地址,以获取其元数据,并了解它是什么类型的帐户以及它与什么程序相关联。
Lamports和Rent Epoch帐户和帐户数据存储在验证器的内存中,需要在lamports中支付“租金”(在帐户lamports元数据字段中表示)才能保留在那里。您帐户中存储的数据越多,所需的租金就越多(注意:当前最大帐户大小为10 MB)。您可以使用JSON RPC方法计算您的租金要求,getMinimumBalanceForRentExemption。验证者负责定期扫描所有帐户并收取租金--帐户在rent_epochmetadata字段中确定的时代上进行检查。如果帐户降至零灯端口余额,网络将清除该帐户,数据将丢失。在撰写本文时,所有新账户都需要保持免租金状态,这是通过在账户中持有2年的租金来授予的。来源:https://docs.solana.com/developing/programming-model/accounts 纪元是索拉纳时间的衡量标准,目前大约代表2.5天。
程序
在Solana体系中,程序即所谓的智能合约,它们犹如处理信息与请求的引擎,涵盖的范围极为广泛,从代币转移、糖果机铸币,到“Hello World”日志记录,乃至DeFi托管治理等各类事务,皆在其处理范畴之内。
需牢记,Solana程序本质上是被标记为“可执行文件”的无状态账户。正如相关表述:“程序之所以被视为无状态,是因为存储在程序账户中的主要数据是经过编译的BPF代码。”
程序能够借助“跨程序调用”这一方式,来运用或构建其他Solana程序,这一特性通常被称作可组合性。
与众多其他热门区块链不同,Solana的程序具备可升级性。
大多数程序由用户自行创建,不过Solana实验室构建并维护了众多链上程序,这些程序被统称为Solana程序库。
实际操作中,用户必须向程序提供程序特定的指令,程序负责处理这些指令,之后程序便可以调用其他程序,并且(或者)更新其生成并掌控的程序衍生账户(关于PDA的更多内容,详见下文)。
程序衍生地址帐户
程序派生地址(PDA)指的是由特定程序创建并管控的账户。PDA具有以下特性:
• 允许程序对特定地址(即程序地址)进行控制,如此一来,外部用户便无法生成带有这些地址签名的有效交易。
• 允许程序以编程方式对通过跨程序调用所调用的指令中存在的程序地址进行签名。Solana会依据程序以及程序ID所定义的种子,以确定性的方式推导出PDA。若想获取关于生成的程序地址的更多信息,可访问docs.solana。
(来源:https://docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses)
这一特性使得程序能够提供诸如托管账户这类无需信任的服务,从而安全地管理交易、投注或者DeFi协议。若参考上述原理示意图,SPL代币程序会依据用户钱包以及该特定代币的铸币地址,为用户生成PDA。此特定的PDA将存储用户所持有的代币数量等状态或数据信息。
接下来,让我们深入探究程序是如何与指令和交易协同工作的。
指令
指令,从本质上来说,就是告知程序执行特定操作的信息。Solana对指令的定义为“程序中执行逻辑的最小连续单元”。(来源:docs.solana.com/terminology)。
指令由三个部分构成:
• 即将调用的程序的公钥;
• 它将要读取或修改的一系列账户;
• 程序执行所需的任何额外数据的字节数组(此部分因程序而异)。
最后一点尤为关键——指令具有程序特定性。倘若输入了错误的程序ID,或者不清楚传递给程序的预期数据结构,交易就会宣告失败。
在Javascript中,交易指令如下所示:
new TransactionInstruction({
programId: new PublicKey(PROGRAM_ID),
keys: [
{ pubkey: signerKeypair.publicKey, isSigner: true, isWritable: true },
{ pubkey: pda, isSigner: false, isWritable: true },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
],
data: Buffer.from(SOME_ENCODED_DATA),
})
交易
交易是向网络中的程序发送指令的途径。它能够将多条指令打包组合,从而实现多个操作的同时处理。举例来讲,假设您有三条不同的指令:一条指令是让钱包A向钱包B发送1美元的SOL;另一条指令是让钱包B向钱包A发送33美元的USDC;还有一条指令是在交易中记录一则备忘录,声明“钱包A在YYYY - MM - DD这天,与钱包B以1美元兑换33美元”。虽然这些指令能够分别发送至网络,但要是其中一条指令失败,而其他指令成功,会出现什么状况呢?这就可能导致一种情况,一方收到了代币,而另一方却没有。通过将这些指令捆绑成一个数据包,我们就能确保所有交易要么全部成功,要么全部失败。
目前,交易的大小限制为1,232字节。每笔交易必须包含以下内容:
• 一个或多个交易指令的数组:包含要执行的具体操作指令。
• 程序将与之交互的所有帐户地址的数组:明确涉及交易的相关账户。
• 签名数组:我们在交易中传递的部分账户,必须附带签名。正如“这些签名向链上程序表明账户持有人已授权该交易。通常,程序依靠这种授权来允许从账户扣款或修改其数据。”(来源:docs.solana.com)。例如,对于创作者验证NFT的真实性,签名可能也是必要的。
• 最近的块哈希:块哈希本质上类似于一个PoH时间戳。默认情况下,Solana会剔除“旧”交易,以避免重复计算,使得验证者只需依据最近的批次来检查交易,同时也能让用户快速知晓他们的交易是否已处理(若失败则可重试)。在提交交易前获取最近的块哈希,网络就能知晓我们交易的时效性以及何时过期(若有此需求)。
一旦网络接收到交易,就会返回一个交易ID(采用base - 58编码的字符串),通过该ID可查询交易的详细信息。
接下来,我们探讨一下如何将交易提交到网络!
整合所有概念
好了!这里包含了大量信息 —— 您或许已开始察觉到这些基本概念之间存在一些关联,但在将所有零碎部分串联起来之前,让我们快速回顾一下:
• 在Solana上,几乎所有事物皆以账户形式存在,包括用户钱包、程序、系统程序、程序数据等等。
• 程序是无状态的,它们负责处理指令,能够调用其他程序,并改变其掌控的数据账户的状态。
• 指令具有程序特定性,用于告知程序执行何种操作。
• 交易将一条或多条带有签名的指令进行打包,以此授权程序执行一项或多项任务。
• RPC协议使得客户端能够通过节点(例如QuickNode)向Solana的三个集群之一发送读/写请求。
• 客户端可以借助RPC提供商,通过WebSocket请求来订阅网络变化。
下面让我们来看一个具体示例。假设有一个名为“GM”的程序,该程序中有一个计数器,每当用户向这个程序发送“GM”消息时,计数器便会记录。
在上述示例中,客户端创建了一个包含多个交易指令的交易。这些说明是特定于程序的,因此它们需要与通用汽车计划的预期结构相匹配。客户端使用Connection类通过RPC提供商(如QuickNode)签署并将事务发送到Solana集群。RPC提供商将请求路由到集群。程序帐户执行并处理收到的输入指令。程序帐户是无状态的,不会更改,但授权更改PDA的数据,以增加通用汽车计数器的价值。客户端创建的订阅会监听PDA的更改;然后,网络会提醒客户端帐户已被更改。