{"content":{"title":"Solana 基础知识参考指南","body":"## 概述\r\n\r\nSolana 区块链是一个强大的工具，每秒可处理数千笔交易，几乎没有交易费用。如果你对 Web3 感到陌生，或者之前在基于 EVM 的区块链上开发过，这份指南将帮助你理解 Solana 的基本元素，并开启你在 Solana 开发的旅程。如果你已经在 Solana 上进行开发，可以将这份指南作为参考，继续增强你对 Solana 基础知识的理解。\r\n\r\n在本指南中，我们将介绍 Solana 的基本构建块，具体包括：账户、程序、指令、交易、RPC 和订阅。让我们开始吧！\r\n\r\n## TLDR\r\n\r\n- 账户就像传统操作系统中的文件——在 Solana 上，大多数东西都是一个账户：用户钱包、程序、数据日志，甚至系统程序\r\n- 程序（在其他链上称为智能合约）是一种特定类型的 \"可执行\" 账户——它们是无状态的，并且仅根据用户输入参数执行代码。程序没有自我更新的能力，而是具有特定的权限来更新它们 \"拥有\" 的其他账户的数据（或状态）。\r\n- 用户通过 RPC 签署并提交交易（程序特定指令的捆绑）到 Solana 集群，以告知程序该执行什么操作。\r\n- Solana 使用一种称为历史证明（Proof of History, PoH）的独特共识机制，该机制依赖于时间和密码学对发送到网络的交易进行时间戳和排序。\r\n- 程序和交易签名者在链上授权对某些数据账户的更改，以更新网络的状态。\r\n\r\n渴望了解更多？继续阅读！👇\r\n\r\n## Solana 是什么？\r\n\r\nSolana 是一个优化速度和成本的开源区块链，它允许信息和状态在全球范围内同步。Solana 利用一种称为历史证明的独特形式的权益证明共识，该共识使用细粒度可验证延迟函数来有效地对发送到网络的交易进行时间戳和排序。根据 Anatoly Yakovenko 在 [Solana 白皮书](https://solana.com/solana-whitepaper.pdf) 中所说，“它使用了一种密码学上安全的函数，以便输出不能通过输入来预测，并且必须完全执行才能生成输出”（3）。“领导者在系统中对用户消息进行排序，以便其他节点能够有效地处理，从而最大化吞吐量”（2）。\r\n\r\n![](https://img.learnblockchain.cn/2025/02/24/0-c4654c0741499040f14c2b28ad76d580.png)\r\n\r\n\r\n在本文中，我们将对 Solana 的运行时进行的探讨大致到此为止，接下来我们将转向如何与 Solana 互动和构建。如果你想深入了解 Solana 的架构及其某些设计特征背后的理论，这里有几个很好的资源：\r\n\r\n- [Solana 白皮书](https://solana.com/solana-whitepaper.pdf)\r\n- [Anatoly Yakovenko 的 Medium](https://medium.com/@anatolyyakovenko)\r\n- [Solana.com](https://docs.solana.com/cluster/synchronization)\r\n\r\n## 账户\r\n\r\n像操作系统中的文件一样，账户用于运行系统、存储数据或状态以及执行代码。\r\n\r\n在 Solana 上有三种主要类型的账户：\r\n\r\n1. 数据账户，用于存储信息并管理网络的状态。数据账户有两种类型：\r\n   - 系统拥有的账户（通常被认为是钱包） _\\[示例：_ [_用户钱包_](https://explorer.solana.com/address/E645TckHQnDcavVv92Etc6xSWQaq8zzPtPRGBheviRAk) _\\]_\r\n   - 程序派生账户（通常简称为 PDA）——一个常见示例是特定代币的用户代币账户。它由 Solana SPL Token 程序派生。 _\\[示例：_ [_用户代币账户_](https://explorer.solana.com/address/HmXbXp3q1NCuT8DT3EiTbcDcJti4w3aTUd1S7dWkV7wi) _\\]_\r\n2. 程序账户，持有可执行代码。我们将在接下来的部分详细讨论这些账户，但这些账户是无状态的（意味着数据通过它们，但不会更新它们） _\\[示例：_ [_Candy Machine v2 程序账户_](https://explorer.solana.com/address/cndy3Z4yapfJBmL3ShUp5exZKqR3z33thTzeNMm2gRZ) _\\]_\r\n3. 本地系统账户（例如，系统程序、投票、质押、BPF 加载器） _\\[示例：_ [_Solana 系统程序_](https://explorer.solana.com/address/11111111111111111111111111111111) _\\]_\r\n\r\n> 本文将重点讨论 #1 和 #2。如果你想了解更多关于 #3 本地系统账户的信息，请查看 [docs.solana.com](https://docs.solana.com/developing/runtime-facilities/programs)。\r\n\r\n简而言之，Solana 上几乎所有东西都是一个账户。最常见的账户类型是用户钱包，这是一个管理用户 SOL 余额状态的数据账户。\r\n\r\n每个账户都有一个独特的地址或公钥（很像文件路径）以及一组相关的元数据：\r\n\r\n- **lamports**：此账户拥有的 lamports 数量（64 位无符号整数）\\*\r\n- **owner**：分配给此账户的程序所有者（所有者程序的字符串地址）。所有者程序控制谁可以对账户进行写入——其他程序可以读取另一个账户的数据，但如果它们试图修改该数据，事务将失败。\r\n- **executable**：此账户是否可以处理指令（布尔值）。一个执行值为 _true_ 的账户实际上是一个程序（或智能合约）——我们将在下一部分讨论这些。\r\n- **data**：与账户关联的任何附加数据，以原始数据字节数组的形式存储\r\n- **rent\\_epoch**：此账户将欠租的下一个纪元（64 位无符号整数）\r\n\r\nlamports 是 SOL 的最小面额，是 Solana 的原生代币（代表一个 SOL 的十亿分之一）\r\n\r\n可以通过运行 [getAccountInfo JSON RPC 查询](https://www.quicknode.com/docs/solana/getAccountInfo) 或在 [Solana Explorer](https://explorer.solana.com/) 上搜索来读取任何账户的元数据。Solana Explorer 允许你搜索任何账户，并查看有关该账户类型和关联程序的基本详细信息。下面是一个系统拥有数据账户（用户钱包）的示例：\r\n\r\n![](https://img.learnblockchain.cn/2025/02/24/1-b6dad4771c0bb328f571df02e01607c7.png)\r\n\r\n让我们可视化所有账户类型的示例：\r\n\r\n![](https://img.learnblockchain.cn/2025/02/24/2-16c19fb6aaba7e517e667514ac52c108.png)\r\n\r\n这里有很多内容，但你在此图中看到的每个元素都是一个账户。在左侧，我们有一个名为系统程序的系统账户，管理所有用户钱包。我们还拥有一个可执行程序账户，称为 Solana SPL Token Program，用户可以用来铸造、销毁和转移 SPL 代币。最后，我们有程序派生账户。在这种情况下，这些是由 Token Program 拥有并与用户账户关联的代币账户。请注意，一个用户钱包可以拥有多个代币账户。\r\n\r\n请记住，你可以在 Solana Explorer 中搜索任何账户地址，以获取其元数据，并了解它的账户类型及其关联的程序。\r\n\r\n> **Lamports 和租赁纪元**：账户及其所包含的数据存储在验证者的内存中，并要求以 lamports（在账户的 **lamports** 元数据字段中表示）支付“租金”，以维持存储状态。存储在账户中的数据越多，所需的租金就越多 _(注意：当前账户的最大大小为 10MB)_。你可以使用 JSON RPC 方法 [getMinimumBalanceForRentExemption](https://www.quicknode.com/docs/solana/getMinimumBalanceForRentExemption) 计算租赁要求。验证者负责定期扫描所有账户并收取租金——账户在 **_rent\\_epoch_** 元数据字段中标识的纪元进行检查。如果一个账户的 lamport 余额降至零，网络将清除该账户，数据将会丢失。在撰写本文时，所有新账户均需保持免租状态，该状态的获得是通过在账户中持有 2 年的租金。来源：[_https://docs.solana.com/developing/programming-model/accounts_](https://docs.solana.com/developing/programming-model/accounts)。纪元是 Solana 上时间的度量，目前大约代表 2.5 天。\r\n\r\n## 程序\r\n\r\n程序是 Solana 所称的智能合约——它们是处理信息和请求的引擎：从代币转账和 Candy Machine 铸造到 \"hello world\" 日志和 DeFi 托管治理。\r\n\r\n- 请记住，Solana 程序是标记为“可执行”的无状态账户：“程序被视为无状态，因为程序账户中存储的主要数据是编译后的 BPF 代码。”\r\n- 程序可以使用“跨程序调用（Cross-Program Invocation）”来使用或构建其他 Solana 程序。这通常被称为可组合性。\r\n- 与许多其他流行链不同，程序是可升级的。\r\n- 大多数程序都是用户生成的，但 Solana Labs 构建并维护了一些称为 [Solana 程序库](https://spl.solana.com/) 的链上程序。\r\n\r\n实际上，用户必须向程序提供特定于程序的指令，程序处理这些指令，然后程序可能调用其他程序和/或更新它拥有的程序派生账户（PDAs，详细见下文）。\r\n\r\n#### 程序派生地址账户\r\n\r\n程序派生地址（PDA）是由特定程序创建和控制的账户。PDA：\r\n\r\n- 允许程序控制特定的地址，称为程序地址，因此没有外部用户可以生成针对这些地址的有效交易签名。\r\n- 允许程序为通过跨程序调用中出现的程序地址进行编程签名。Solana 根据程序定义的种子和程序 ID 确定性地派生 PDA。有关生成的程序地址的更多信息，请访问 [docs.solana](https://docs.solana.com/developing/programming-model/calling-between-programs#hash-based-generated-program-addresses)。\r\n\r\n来源：[_https://docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses_](https://docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses)\r\n\r\n这使得程序能够提供无信任服务，例如安全管理交易、下注或 DeFi 协议的托管账户。如果我们参考上面的示意图，SPL Token Program 会根据用户的钱包和特定代币的铸造地址为用户生成一个 PDA。该 PDA 将存储用户拥有的该代币的数量或数据的状态。\r\n\r\n让我们深入了解如何使程序与指令和交易协同工作。\r\n\r\n## 指令\r\n\r\n指令的核心是告知程序执行某个操作。Solana 将指令定义为“程序中的最小连续执行逻辑单元。” （来源：[docs.solana.com/terminology](https://docs.solana.com/terminology#instruction)）。\r\n\r\n一条指令由三个部分组成：\r\n\r\n1. 将要调用的程序的公钥，\r\n2. 将要读取或修改的账户数组，以及\r\n3. 任何执行程序所需的附加数据的字节数组（特定于程序）\r\n\r\n最后一点是至关重要的——**指令是特定于程序的**。如果你输入错误的程序 ID 或不清楚程序所需的数据结构，则交易将失败。\r\n\r\n在 Javascript 中，交易指令看起来像这样：\r\n\r\n```\r\nnew TransactionInstruction({\r\n\tprogramId: new PublicKey(PROGRAM_ID),\r\n\tkeys: [\\\r\n\t\t{ pubkey: signerKeypair.publicKey, isSigner: true, isWritable: true },\\\r\n\t\t{ pubkey: pda, isSigner: false, isWritable: true },\\\r\n\t\t{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },\\\r\n\t],\r\n\tdata: Buffer.from(SOME_ENCODED_DATA),\r\n})\r\n\r\n```\r\n\r\n简化调试的日志 \r\n\r\n你现在可以访问 RPC 端点的日志，帮助你更有效地排查问题。如果你在 RPC 调用中遇到问题，只需查看 QuickNode 仪表板中的日志即可快速确定和解决问题。了解有关日志历史限制的更多信息，请访问 [我们的定价页面](https://www.quicknode.com/pricing#features)。\r\n\r\n在此值得一提的是，交易指令所需的账户信息。我们必须提供账户的公钥，该账户是否在交易中是签署者，以及该账户是否将作为交易的一部分被写入（这有助于贡献于 Solana 的快速执行能力）。在上面的示例中，用户签署钱包，数据被写入到一个 PDA。通常还必须包括系统程序账户（例如，用于创建新账户）。最后，我们传入 **SOME\\_ENCODED\\_DATA**。所需的数据类型在程序内部定义。程序必须对输入数据进行反序列化以正确处理它。\r\n\r\n## 交易\r\n\r\n交易是将指令发送到网络上某个程序的手段。交易可以将多个指令打包在一起，以便同时处理多个操作。例如，想象一下当你有三个不同的指令时：一条指令让钱包 A 向钱包 B 发送 1 个 $SOL，一条指令让钱包 B 向钱包 A 发送 33 个 $USDC，以及一条指令记录交易备注，声明 “钱包 A 用 1 个 $SOL 交换了与钱包 B 交易的 33 个 $USDC，日期为 YYYY-MM-DD。”尽管这些可以单独发送到网络，但如果一条指令失败而其他指令成功会发生什么？你将会遇到一种情况，一个人获得他的代币，而另一个人却没有。通过将指令打包在一起，我们可以确保所有的交易要么一起成功，要么一起失败。\r\n\r\n当前交易的大小限制为 1232 字节。每笔交易必须包括以下内容：\r\n\r\n- 一个或多个交易指令的数组\r\n- 程序将要与之交互的所有账户地址的数组\r\n- 一组签名：我们传入交易的账户子集必须包括一个签名。“这些签名在链上程序中表明账户持有人已授权该交易。通常，程序使用授权来允许借记账户或修改数据。”（来源：[docs.solana.com](https://docs.solana.com/developing/programming-model/transactions#signatures)）。例如，这对于创建者验证 NFT 是否真实也可能是必需的。\r\n- 一个最近的区块哈希：区块哈希基本上是一个 PoH 时间戳。默认情况下，Solana 会丢弃“旧”交易，以防止重复计算，允许验证者仅对非常最近的批次进行检查，并使用户快速确认他们的交易是否已处理（或失败，以便他们可以重试）。在提交交易之前获取最近的区块哈希，可以让网络了解我们交易的时效性和该交易何时需要过期（如果需要）。\r\n\r\n一旦网络收到交易，它会返回一个交易 ID（base-58 编码字符串），可用于查找交易详细信息。\r\n\r\n让我们讨论一下如何将你的交易发送到网络上！\r\n\r\n## RPC\r\n\r\nJSON-RPC 是一种以 JSON 编码的远程过程调用协议，使用户能够向 Solana 集群发送信息并从网络获取响应。Solana 目前维护三个网络集群：\r\n\r\n- Devnet：测试应用和方法功能的游乐场\r\n- Testnet：用于全面测试所有最新规划升级的开发环境\r\n- Mainnet Beta：生产环境\r\n\r\n客户端可以通过 Solana 节点对任何三个集群发起读写请求。节点在处理请求到网络方面充当“空中交通管制”。客户端通过 Solana Web3 _Connection_ 类连接到 Solana 节点：\r\n\r\n```\r\nconst HTTP_ENDPOINT = 'https://example.solana-devnet.quiknode.pro/000/'\r\nconst SOLANA_CONNECTION = new Connection(HTTP_ENDPOINT)\r\n\r\n```\r\n\r\n在上述示例中，**HTTP\\_ENDPOINT** 是指向 Solana 节点的 HTTP URL，最终将你的请求路由到三个集群之一。\r\n\r\n每个请求都需要一个 API 端点，以便连接到网络。这就是 QuickNode 登场的地方！你可以使用公共节点或部署和管理自己的基础设施；然而，如果你希望获得 8 倍的响应速度，你可以把繁重的工作留给我们。\r\n\r\n\r\n一旦你建立了与 Solana 集群的 _Connection_，就可以对网络执行许多读写方法。有关 RPC 方法的完整列表和文档可以在 [QuickNode 文档](https://www.quicknode.com/docs/solana) 上找到。尽管每个方法都有其目的，但在本指南中讨论的一些相关方法包括：\r\n\r\n- [sendTransaction](https://www.quicknode.com/docs/solana/sendTransaction) \\- 将交易发送到 Solana 网络\r\n- [getAccountInfo](https://www.quicknode.com/docs/solana/getAccountInfo) \\- 获取与任何账户相关的元数据\r\n- [getLatestBlockhash](https://www.quicknode.com/docs/solana/getLatestBlockhash) \\- 返回网络的最新区块哈希\r\n\r\nHTTP 调用允许我们告诉网络执行某个操作（例如，处理交易及其指令，返回有关账户的当前状态信息等），但是如果我们想让网络在账户状态发生变化时提醒我们怎么办？\r\n\r\n## 订阅\r\n\r\n订阅是一种特殊的 RPC 请求，我们会使用 WebSocket (WSS) 请求，而不是使用 HTTP 端点。Solana Web3 JSON RPC 包含几种订阅方法（例如，[accountSubscribe](https://www.quicknode.com/docs/solana/accountSubscribe)，它将监听账户 lamports 或数据的变化）。然后，我们可以使用回调函数订阅这些事件，以便在网络发生更改时执行某些期望的操作。\r\n\r\n_Connection_ 类构造函数允许我们传递一个可选的 _commitmentOrConfig_。在这个配置中，我们可以传递一个 _wsEndpoint_。这是一个选项，让你提供指向完整节点 JSON RPC Websocket 端点的 URL。如果你创建了一个 QuickNode 端点，你应该能在你的 [端点页面](https://www.quicknode.com/endpoints) 的右侧看到这个选项。\r\n\r\n```\r\nconst HTTP_ENDPOINT = 'https://example.solana-devnet.quiknode.pro/000/'\r\nconst WSS_ENDPOINT = 'wss://example.solana-devnet.quiknode.pro/000/'\r\nconst solanaConnection = new Connection(HTTP_ENDPOINT, {\r\n\twsEndpoint: WSS_ENDPOINT,\r\n})\r\n\r\n```\r\n\r\n如果你不传递 _wsEndpoint_ 会发生什么？好吧，Solana 用一个名为 **makeWebsocketUrl** 的函数来处理这一点，该函数将你端点 URL 中的 _https_ 替换为 _wss_ 或 _http_ 替换为 _ws_（[来源](https://github.com/solana-labs/solana/blob/627d91fb20272afa5ce790fbec01b40a29fb9852/web3.js/src/connection.ts#L2452)）。由于所有 QuickNode HTTP 端点都有相应的 WSS 端点，且使用相同的身份验证令牌，因此如果你不想为 WebSocket 查询使用单独的端点，则可以省略此参数。\r\n\r\n要深入了解如何创建 WebSocket 订阅，请查看我们的 [指南：如何使用 TypeScript 创建 Websocket 订阅到 Solana 区块链](https://www.quicknode.com/guides/solana-development/how-to-create-websocket-subscriptions-to-solana-blockchain-using-typescript)。\r\n\r\n## 整合所有内容\r\n\r\n好的！这是一堆信息——你可能开始看到了这些基本元素之间的某些连接，但在将所有部分联系在一起之前，让我们快速回顾一下：\r\n\r\n- Solana 上几乎每个元素都是一个账户：用户钱包、程序、系统程序、程序数据等。\r\n- 程序是无状态的——它们处理指令，并可以调用其他程序和更改它们拥有的数据账户的状态。\r\n- 指令是特定于程序的，告诉程序该做什么。\r\n- 交易将一个或多个指令与签名打包在一起，以授权程序执行一个或多个任务。\r\n- RPC 协议允许客户端通过节点（如 QuickNode）向 Solana 的三个集群发送读/写请求。\r\n- 客户端可以使用通过 RPC 提供者的 WebSocket 请求订阅网络更改。\r\n\r\n让我们通过一个说明性的示例来说明。想象一下一个名为 “GM” 的程序，它持有一个计数器，用于记录每当用户向程序发送 “GM” 消息时的数量。\r\n\r\n![](https://img.learnblockchain.cn/2025/02/24/4-b82268881919612c1bc1af4ea333d150.png)\r\n\r\n在上述示例中，客户端创建一个交易，其中包含多个交易指令。这些指令是特定于程序的，因此需要与 GM 程序的期望结构相匹配。客户端签署并通过 RPC 提供者（如 QuickNode）使用 _Connection_ 类将交易发送到 Solana 集群。RPC 提供者将请求路由到集群。程序账户执行和处理接收到的输入指令。程序账户是无状态的，并不会改变，但授权对 PDA 中数据的更改以递增 GM 计数器的值。客户端创建的订阅侦听对 PDA 的更改；然后，网络会通知客户端账户已被更改。\r\n\r\n## 总结\r\n\r\n哇！我们刚刚覆盖了很多信息。不要害怕多读几遍，以确保理解这些概念。\r\n\r\n现在你对 Solana 有了更深入的理解，是时候开始构建了！试试我们的一些 [Solana 开发指南](https://www.quicknode.com/guides/solana-development/) 来开始吧。以下是几个示例：\r\n\r\n- [如何在 Solana 上创建和发送（版本化）交易](https://www.quicknode.com/guides/solana-development/how-to-use-versioned-transactions-on-solana)\r\n- [如何使用 TypeScript 在 Solana 上铸造 NFT](https://www.quicknode.com/guides/solana-development/how-to-mint-an-nft-on-solana-using-typescript)\r\n- [如何获取 Solana 上的交易日志](https://www.quicknode.com/guides/solana-development/how-to-get-transaction-logs-on-solana)\r\n\r\n对这些概念有疑问，或者只是想聊聊？在 [Discord](https://discord.gg/quicknode) 或 [Twitter](https://twitter.com/QuickNode) 联系我们，告诉我们你正在构建什么！\r\n\r\n \r\n\r\n## 参考和来源 \r\n\r\n- [https://solanacookbook.com/core-concepts/accounts.html#deep-dive](https://solanacookbook.com/core-concepts/accounts.html#deep-dive)\r\n- [https://www.quicknode.com/guides/solana-development/an-introduction-to-the-solana-account-model](https://www.quicknode.com/guides/solana-development/an-introduction-to-the-solana-account-model)\r\n- [https://medium.com/@anatolyyakovenko](https://medium.com/@anatolyyakovenko)\r\n- [https://docs.solana.com/developing/programming-model/accounts](https://docs.solana.com/developing/programming-model/accounts)\r\n- [https://docs.solana.com/developing/clients/jsonrpc-api#getaccountinfo](https://docs.solana.com/developing/clients/jsonrpc-api#getaccountinfo)\r\n\r\n>- 原文链接： [quicknode.com/guides/sol...](https://www.quicknode.com/guides/solana-development/getting-started/solana-fundamentals-reference-guide)\r\n>- 登链社区 AI 助手，为大家转译优秀英文文章，如有翻译不通的地方，还请包涵～"},"author":{"user":"https://learnblockchain.cn/people/25306","address":null},"history":null,"timestamp":1740450048,"version":1}