{"content":{"title":"审计 Solana 程序前，你需要了解这些","body":">- 原文链接：[www.infect3d.xyz/blog...](https://www.infect3d.xyz/blog/solana-quick-start)\r\n>- 译者：[AI翻译官](https://learnblockchain.cn/people/19584)，校对：[翻译小组](https://learnblockchain.cn/people/412)\r\n>- 本文链接：[learnblockchain.cn/article…](https://learnblockchain.cn/article/10721)\r\n    \r\n## 介绍\r\n\r\n### 一点故事\r\n\r\n和大多数没有专业软件开发背景的 Web3 安全研究人员一样，我的旅程始于 **Ethereum**、**EVM** 和 **Solidity**。自从我踏入这片无尽的 [_黑暗森林_](https://www.paradigm.xyz/2020/08/ethereum-is-a-dark-forest) 已经过去了十六个月。\r\n\r\n我最近决定看一看 **Solana**，以扩大我在可编程区块链方面的专业知识。并不是因为 EVM 无聊（我知道你要说什么）。而是由于 Solana 生态系统最近获得了越来越多的关注，我觉得能够满足这种新出现的需求是一个好主意。\r\n\r\n由于我对 **Rust** 和 **Solana** 没有任何先前的了解，我花了大约 7 天时间来学习基础知识。我的学习经历得到了许多优秀资源的帮助，特别是来自 Rareskills 的这门很棒的课程：[60 Days of Solana](https://decert.me/tutorial/rareskills-solana-course/)。\r\n\r\n我也试图找到一个指南，以帮助我快速掌握潜入代码库的最低要求，但并没有找到——这就是我写这本指南的原因。\r\n\r\n### 你将学到什么\r\n\r\n我的目标是帮助你轻松导航 Solana 项目。这不需要你具备 Solana 的先前知识，因为我会尽量解释跟随本文所需的重要概念。\r\n\r\n虽然这将帮助你快速开始 Solana 的部分，但你仍然需要学习一点 **Rust**，以*真正*对代码库感到舒适，因为一些编程概念是非常特定于这个语言的。然而，不要感到压力去单独掌握 Rust——你可以在审计时学习。Solana 生态系统只使用了 Rust 功能的一小部分，借助 LLM 来帮助你理解特定代码段，学习过程应该是相当可管理的。\r\n\r\n## 1\\. Solana\r\n\r\n**Solana** 是一个高吞吐量区块链，在 TVL 方面排名第三（仅次于 Bitcoin 和 Ethereum），也是在 Ethereum 之后第二重要的可编程区块链。\r\n\r\n可编程区块链可以被视为一个*分布式世界计算机*，存储数据，并为任何愿意支付的人执行命令。\r\n\r\nSolana 非常有趣，因为它走了一条与 Ethereum 非常不同的道路，从基础开始创建自己的层。它的架构通过特定机制优先考虑可扩展性和速度。\r\n\r\n但我们真正感兴趣的是 Solana 的执行层，也称为 **Solana 虚拟机** (**SVM**)。该层负责接收交易并根据区块链的状态执行它们。\r\n\r\n在计算机上，数据存储在文件中——在 Solana 上，数据存储在 **账户** 中。这些数据可以是信息数据或可执行数据，但归根结底，这些只不过是在“账户”中存储的一些零和一。\r\n\r\n在我看来，账户类似于文件（具有特定属性），因为它们存在不同类型（文件扩展名），其中一些具有“本地扩展名”（数据账户、可执行账户），然后它们可以包含任何东西，从 DEX 程序到用户数据（例如，用于存储代币用户余额的“数据账户”）。  \r\n另外，像操作系统上的文件一样，账户有访问控制机制，确保只有授权地址可以对其执行操作。\r\n\r\n## 2\\. 账户模型\r\n\r\n**账户模型** 可能是我花费最多时间去理解的内容，可能是因为模型与如何在代码中反映的关系并不明显。  \r\n但实际上，这很简单理解。\r\n\r\n来自我之前提到的 [Rareskills 课程](https://decert.me/tutorial/rareskills-solana-course/) 的一个很好的类比将 Solana 与众所周知的 Linux 原则进行了比较：*“一切都是文件。”*\r\n\r\n对于 Solana，我们可以说，*“一切都是账户。”*\r\n\r\n账户实际上是在区块链上专门的“空间”（最多 10MB），可以包含不同种类的信息，并且可以通过一个代表其在区块链中的“位置”的地址进行访问。\r\n\r\n在 **EVM** 上，账户可以同时保存逻辑和可变数据，而在 **Solana** 中，账户可以保存数据或可执行逻辑，但不能同时保存这两者。\r\n\r\n如果 Solana 账户不能同时保存数据和逻辑，这意味着程序不能直接访问数据。那么程序如何在需要读取和写入数据以执行其逻辑时保持**动态**呢？\r\n\r\n程序如何创建和维护有关其状态和用户的信息？\r\n\r\n## 3\\. 从数据账户读取和写入数据\r\n\r\n一个程序（一般意义上）通常需要维护状态变量，以存储其操作所需的基本信息。以自动售货机程序为例——它需要存储各种信息，例如：每种产品的剩余数量、价格、在网格上的位置、技术员的访问代码等。这被称为变量，因为显然，它们的值会随着时间而改变，以响应各种操作（接收钱、交付产品等）。\r\n\r\n与大多数传统系统不同，Solana 中的 **可执行账户程序** 不能将数据与其可执行代码存储在一起。相反，它们必须利用替代存储解决方案。这就是 **数据账户** 作为主要存储机制发挥作用的地方。\r\n\r\n_以下是来自 Solana 文档的插图：表示了 2 个账户：一个程序和一个数据账户。程序账户拥有数据账户，并用它来存储与其程序相关的信息。_\r\n \r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2025/02/EvXTwZ7567a9bdcfa0584.png)\r\n![Data and Program Accounts, source: https://solana.com/docs/core/accounts#data-account](https://www.infect3d.xyz/\\_next/image?url=%2Fstatic%2Fimages%2Fblog%2Fsolana-quick-start%2F1.png\\&w=1920\\&q=75)\r\n\r\n\r\n对于程序所需的每个新变量，可以创建一个新的数据账户。一旦创建，要访问存储在新创建账户中的变量，程序只需记住其地址即可，无论是写入还是读取。\r\n\r\n如你所想，为每个所需变量创建一个新的唯一账户将非常低效。相反，Solana 允许开发人员创建结构化存储解决方案，其中多个值可以存储在单个账户中。\r\n \r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2025/02/8F4h4pzU67a9bde68152d.png)\r\n\r\n为了以结构化的方式写入数据账户，程序使用 **序列化程序**，而要将其读取回来则需要 **反序列化程序**（在使用普通的 Solana 框架和库时，该过程是透明的）。但是这些数据账户是如何创建的呢？\r\n\r\n账户（因此数据账户）都有自己在区块链上的地址，我们可以用它们来访问它们。虽然对于程序要求外部数据的地址以便读取是合理的，但相反，如果程序必须存储或请求用户输入其自己的状态变量的地址，这将是非常低效的。\r\n\r\n幸运的是，情况并非如此，从程序（由程序创建）的数据账户具有一种称为 **程序派生地址** (**PDA**) 的特殊类型地址。  \r\nPDA 是通过 [哈希](https://www.geeksforgeeks.org/what-is-hashing/) 程序自身的地址和任何可选的种子（字节值），以及一个增量（你可以稍后查找这个，但现在并不重要）以确定的方式生成的。\r\n\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2025/02/tqixEDNA67a9bdfecf18c.png)\r\n\r\n因为这个过程是确定性的，程序现在有办法创建新变量，并自动检索这些变量。再一次，得益于可用的 Solana 框架和库，整个过程都是透明的。\r\n\r\n## 4\\. 程序和指令\r\n\r\n**Solana 程序** 是执行区块链逻辑的基础。虽然 Solana 提供了几个基本的 **本地程序**（例如账户部署工具），但生态系统主要由用户部署的程序组成，以扩展区块链的功能。\r\n\r\n与 **EVM** 智能合约不同，**Solana 程序** 是无状态的，不能存储可变数据（它们可以有常量，因为这嵌入在程序的字节码中）。\r\n\r\n相反，程序在被调用时必须接收数据账户地址作为参数，从而允许它们访问和操作外部数据。\r\n\r\n要访问一个程序并执行其逻辑，我们需要调用所谓的程序 **指令**。\r\n\r\n一个 **指令** 需要三个基本组件：\r\n\r\n1.  **程序 ID**：你想要执行的程序的地址\r\n2.  **账户**：指令需要与之交互的所有账户列表（读/写或通过外部调用，也称为 CPI）\r\n3.  **指令数据**：不需要专门账户的补充参数（转移金额、新值等）\r\n\r\n为了演示这些概念，让我们来看一个简化的例子（*注意，我已删除代码中的许多重要内容以便于理解——我们将在第 7 部分探索完整实现及所有必要组件*）：\r\n\r\n    pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {\r\n        // 从 user_token_account 转移 `amount` 代币到 vault...\r\n    }\r\n    \r\n    // 指令所需的上下文\r\n    pub struct Deposit<'info> {\r\n        // 必须签名交易的用户钱包\r\n        pub user: Signer<'info>,\r\n    \r\n        // 我们将从中提取的用户代币账户\r\n        pub user_token_account: Account<'info, TokenAccount>,\r\n    \r\n        // 我们将把代币存入的金库\r\n        pub vault: Account<'info, TokenAccount>,\r\n    \r\n        // 我们将用于转移代币的程序\r\n        pub token_program: Program<'info, Token>,\r\n    }\r\n    \r\n\r\n上面是一个 `deposit` 指令，输入多个由 `Deposit` 结构描述的账户：\r\n\r\n*   第一个账户 `user` 是调用方账户（`Signer` 类型是特殊的，因为它会自动存储调用者信息）\r\n*   `user_token_account` 和 `vault`，两个数据账户，代币将从中提取/存入。如我们在第 2 部分所说，数据通常组织为结构，对于这两个账户是 `TokenAccount`\r\n*   `token_program`，一个程序账户，我们将调用它以执行转移\r\n\r\n## 5\\. 跨程序调用（CPI）\r\n\r\n**跨程序调用** 由从另一个程序执行的指令组成。要做到这一点，必须提供（1）程序的地址（2）程序指令所需的数据；我们称之为 **CPI 上下文**。\r\n\r\n这是我借用自[这节课](https://learnblockchain.cn/article/10309)的一个示例：\r\n```\r\n    pub fn send_sol(ctx: Context<SendSol>, amount: u64) -> Result<()> {  \r\n        let cpi_context = CpiContext::new(                   // 构建 CPI 上下文：\r\n            ctx.accounts.system_program.to_account_info(),   //(1) 程序地址\r\n            system_program::Transfer {                       //(2) 程序所需数据\r\n                from: ctx.accounts.signer.to_account_info(), //(2)\r\n                to: ctx.accounts.recipient.to_account_info(),//(2)\r\n            }\r\n        );\r\n        \r\n        // 用上下文（及额外参数 `amount`）调用程序\r\n        let result = system_program::transfer(cpi_context, amount); \r\n    \r\n        if result.is_ok() {\r\n            return Ok(());\r\n        } else {\r\n            return err!(Errors::TransferFailed);\r\n        }\r\n    }\r\n```\r\n\r\n该指令将 **SOL**（Solana 的本地加密货币/代币）从签署者发送到接收者。\r\n\r\n我们首先需要创建指令将要执行的上下文：\r\n\r\n1.  程序的地址，这里由 `system_program.to_account_info` 表示\r\n2.  指令所需的数据，这些数据捆绑在结构 `Transfer` 中，包含两个信息：`from`（发送 SOL 的地址）和 `to`（接收 SOL 的地址）。\r\n\r\n然后，使用上下文（及额外参数 `amount`）执行 CPI。\r\n\r\n在这个例子中，被调用的程序是 **Solana 系统程序**，但如果我们调用另一个用户部署的程序，它看起来会是这样：  \r\n`external_program::cpi::instruction_name(cpi_ctx, additional_param1, ...)`\r\n\r\n## 6\\. Tokens\r\n\r\nSolana 的代币生态系统与我们在 EVM 中所知的大相径庭。\r\n\r\n在 EVM 中，每个代币都有自己的合约（即 Solana 中的程序），而在 Solana 中，有一个程序称为 **SPL Token 程序**，可以派生出 **铸造账户**。\r\n\r\n每个代币的实例将拥有其唯一的 **铸造账户**：例如 USDC 铸造账户，USDT 铸造账户，等等。\r\n\r\n然后，从这些铸造账户中，用户可以创建 **代币账户** —— 专门用于持有特定代币的账户。\r\n\r\n考虑一个像 Alice 这样的用户，她持有多种代币（USDC、USDT 和 DEGEN）。她需要为每个资产创建一个单独的代币账户，所有这些账户都是从各自的铸造账户派生而来的。这种结构在 Solana 生态系统中的所有用户中均适用。\r\n\r\n以下是一个示意图，显示了代币程序的组织方式：\r\n\r\n![SPL Token Program and related accounts](https://img.learnblockchain.cn/attachments/2025/02/jGx32sMO67a9be312277d.png)\r\n\r\n在这个架构中，唯一的程序账户是 **SPL Token 程序**。其他账户是可以由 SPL Token 程序修改的数据账户。\r\n\r\n**SPL Token 程序** 是这些账户的所有者，这意味着只有该程序有权修改这些账户。这意味着要更新它们，我们需要执行一个 **CPI** 到 SPL Token 程序，并提供我们希望看到更新的账户（如上文[第 4 部分](#4-programs-and-instructions)所述）。\r\n\r\n显然，SPL Token 程序实现了访问控制逻辑，以防止用户从他们不拥有的账户转移代币。\r\n\r\n如果你还记得我们所说的 **PDAs**（程序派生账户），只有部署这些账户的程序才能修改其状态。\r\n\r\n但这些数据账户（铸造账户和代币账户）有许多字段，其中一个称为 `owner`，不同于我们在第二段中提到的 _程序所有者_，后者不是数据的一部分，而是以更高层次执行的“Solana 元数据”。\r\n\r\nSPL Token 程序检查这些账户内部的 `owner` 字段，并将其与调用者的签名进行比较（类似于 EVM 中的 tx.origin），以确保只有合法访问。\r\n\r\n请参阅 [Solana 文档](https://solana.com/docs/core/tokens#token-account) 中的这一精心设计的图表：\r\n![different type of account ownership, source: https://solana.com/docs/core/accounts#data-account](https://www.infect3d.xyz/\\_next/image?url=%2Fstatic%2Fimages%2Fblog%2Fsolana-quick-start%2F5.png\\&w=1920\\&q=75)\r\n\r\n![different type of account ownership](https://img.learnblockchain.cn/attachments/2025/02/G2URUmwa67a9be4899a96.png)\r\n\r\n## 7\\. Solana 项目架构\r\n\r\n如果你对 Solana 项目架构或 Rust 项目一般不太熟悉，结构一开始可能会显得有些压倒性。让我们先通过检查每个文件的目的，拆解这个简单的项目（在转向一个更复杂的项目之前）：\r\n\r\n    # 1. 基本项目（无指令文件夹）\r\n    my-solana-project/\r\n    ├── Anchor.toml                    # Anchor 配置文件\r\n    ├── Cargo.toml                     # Rust 配置文件\r\n    ├── programs/                      # 所有程序的文件夹\r\n    │   └── my-program/                # 每个程序将具有自己的文件夹\r\n    │       └── src/\r\n    │           ├── instructions/      # 包含由 lib.rs 调用的每个指令的文件夹\r\n    │           │   ├── mod.rs         # lib.rs 导入以导入所有指令\r\n    │           │   ├── initialize.rs  # 每个指令\r\n    │           │   ├── deposit.rs\r\n    │           │   └── withdraw.rs\r\n    │           ├── lib.rs             # 主要程序文件\r\n    │           ├── state.rs           # 程序状态结构\r\n    │           └── errors.rs          # 自定义错误\r\n    └── tests/\r\n        └── my-program.ts\r\n\r\n    \r\n\r\n项目特定文件：\r\n\r\n*   `Anchor.toml`：这是 **Anchor** 工作的配置文件，可以在 [这里](https://www.anchor-lang.com/docs/manifest) 查看可以设置的内容\r\n*   `Cargo.toml`：**Rust** 工作区配置，里面埋藏着一些非常重要的内容：_默认情况下，Rust 不检查溢出，但可以在这个文件中通过设置 `[profile.release]` 部分下的键 `overflow-checks = true` 来配置_ Cargo.toml 文件的例子 [这里](https://github.com/solana-developers/program-examples/blob/main/tokens/transfer-tokens/anchor/Cargo.toml)\r\n\r\n程序特定文件：\r\n\r\n*   `programs/` : 此文件夹将存储所有程序\r\n*   `my-program/`：这里应该是程序名称，具体取决于项目，在 `programs` 父文件夹中可能会有多个程序文件夹\r\n*   `instructions/`：包含可以从 `lib.rs` 调用的所有指令的逻辑\r\n*   `lib.rs`：程序的入口点，包含公共访问的函数（受任何已实现的授权逻辑限制）。_它通常充当一个包装器，将调用路由到 `instructions` 文件夹中的适当指令处理器_\r\n*   `state.rs`：通常_ 用于存放将由你的指令使用的不同数据结构\r\n*   `error.rs`：自定义错误代码定义的集中文件\r\n*   `tests/`：测试目录。_虽然你可以在 TypeScript 和 Rust 中编写测试，但由于 TypeScript 具有更全面的测试库支持，因此优先使用 TypeScript_\r\n\r\n现在让我们看看另一个项目（我只会讨论差异）：\r\n\r\n    my-solana-project/\r\n    ├── Anchor.toml\r\n    ├── Cargo.toml\r\n    ├── package.json\r\n    ├── programs/\r\n    │   ├── my-program/           # 第一个程序\r\n    │   │   ├── Cargo.toml\r\n    │   │   └── src/\r\n    │   │       ├── lib.rs        # 每个程序都有自己的入口文件\r\n    │   │       ├── instructions/\r\n    │   │       │   ├── mod.rs\r\n    │   │       │   ├── deposit.rs\r\n    │   │       │   └── withdraw.rs\r\n    │   │       ├── state.rs\r\n    │   │       └── errors.rs\r\n    │   └── my-other-program/     # 第二个程序\r\n    │       ├── Cargo.toml\r\n    │       └── src/\r\n    │           ├── lib.rs          # 每个程序都有自己的入口文件\r\n    │           ├── instructions/   # 指令根据它们的共性被组织在不同的文件夹中\r\n    │           │   ├── mod.rs\r\n    │           │   ├── utils.rs    # 一个非指令文件，包含被指令使用的工具\r\n    │           │   ├── user_management/    # 用户相关的指令\r\n    │           │   │   ├── mod.rs\r\n    │           │   │   ├── create_user.rs\r\n    │           │   │   └── update_user.rs\r\n    │           │   └── token_management/   # 代币相关的指令\r\n    │           │       ├── mod.rs\r\n    │           │       ├── mint.rs\r\n    │           │       └── burn.rs\r\n    │           ├── state/       # 我们更改为一个包含多个状态文件的状态文件夹\r\n    │           │   ├── mod.rs\r\n    │           │   ├── user.rs  # 对于每个指令组\r\n    │           │   └── token.rs # 对于每个指令组\r\n    │           └── errors.rs\r\n    └── tests/\r\n    \r\n\r\n这里的主要区别是：\r\n\r\n*   该项目同时管理多个程序：每个程序将在区块链上单独和独立部署，每个程序都有自己的公共地址。\r\n*   由于 `my-other-program/` 实现了更复杂的逻辑，开发人员决定根据指令的共同特性将指令分组到不同的文件夹中。\r\n    *   一个独立的 `utils.rs` 文件位于指令组外部，包含跨多个指令使用的共享工具函数（例如数学运算）\r\n    *   你会看到一个 `mod.rs` 文件位于 `instructions/` 的根部以及每个指令组文件夹内：这用于简化 `lib.rs` 中的指令导入\r\n    *   原来的单个 `state.rs` 文件演变为一个专门的 `state/` 目录，具有分离的状态文件，与指令组结构相对应\r\n\r\n这基本上就是你将遇到的大多数 Solana 项目的样子。\r\n\r\n## 8\\. Anchor\r\n\r\n[Anchor](https://www.anchor-lang.com/) 是一个 Rust （_和 TypeScript 用于测试_）框架，由 [Coral](https://github.com/coral-xyz/anchor) 开发，提供帮助开发人员更快速编写安全程序的工具。\r\n\r\n该框架包含许多 [宏](https://docs.rs/anchor-lang/latest/anchor_lang/index.html#macros) 和 [特性](https://docs.rs/anchor-lang/latest/anchor_lang/index.html#traits) （Rust 概念），实现了许多重要组件，例如我们在 [第 2 章](#2-account-model) 中谈到的账户数据 [定序器和反定序器](https://github.com/Ackee-Blockchain/Solana-Auditors-Bootcamp/blob/master/Lesson-1/README.md#attribute-macro-program)，账户上的 [自动访问控制检查](https://github.com/Ackee-Blockchain/Solana-Auditors-Bootcamp/blob/master/Lesson-1/README.md#account)，或者一个将指令调用路由到适当逻辑部分的 [指令调度器](https://github.com/Ackee-Blockchain/Solana-Auditors-Bootcamp/blob/master/Lesson-1/README.md#attribute-macro-program)。\r\n\r\n正如你所看到的，所有这些工具都非常重要，你可能不想从头开始重新开发这些组件。出于这个原因，你将遇到的大多数 Solana 程序都使用 Anchor，而你需要了解其基本知识。\r\n\r\n## 主程序 (lib.rs)\r\n\r\n让我们从你总会遇到的两个程序 `declare_id!()` 和 `#[program]` 开始。\r\n\r\n它们位于 [`lib.rs`](http://lib.rs/)，可以视为程序的“主入口”，因为这是所有程序入口点的位置。\r\n\r\n看看这个示例代码：\r\n\r\n```rust\r\n    use anchor_lang::prelude::*;\r\n    \r\n    declare_id!(\"ABC123xyz...\");\r\n    \r\n    #[program]\r\n    pub mod basic_program {\r\n        use super::*;\r\n    \r\n        pub fn entrypoint_to_instruction_1(ctx: Context<InstructionOne>) -> Result<()> {\r\n            instructions::instruction_1::handler(ctx)\r\n        }\r\n    \r\n        pub fn entrypoint_to_instruction_2(ctx: Context<InstructionTwo>) -> Result<()> {\r\n            instructions::instruction_2::handler(ctx)\r\n        }\r\n    }\r\n```    \r\n\r\n`declare_id!()` 是一个 [函数宏](https://www.rareskills.io/post/rust-function-like-macro)（基本上是一个在编译前注入新代码的强大工具），用于定义程序的链上地址。你不需要了解更多，因为在使用 Anchor CLI 创建程序时会自动填充该地址。\r\n\r\n[`#[program]`](https://github.com/Ackee-Blockchain/Solana-Auditors-Bootcamp/blob/master/Lesson-1/README.md#attribute-macro-program) 是一个 [属性宏](https://www.rareskills.io/post/rust-attribute-derive-macro)（类似于其他宏，但应用于结构体），用于创建一个低级调度器，该调度器接受事务/CPI 并将其重定向到程序代码中执行请求的函数的正确位置。\r\n\r\n调度器确保授权、输入验证以及许多其他 [安全检查](https://github.com/Ackee-Blockchain/Solana-Auditors-Bootcamp/blob/master/Lesson-1/README.md#attribute-macro-program)，这得益于我们将在接下来的章节中进一步看到的一些其他宏。\r\n\r\n## 存款指令\r\n\r\n你还记得我们在第 4 部分中看到的存款指令吗？\r\n\r\n这是更完整的实现：\r\n\r\n```rust\r\npub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {\r\n    // 从 user_token_account 转移 `amount` 代币到 vault...\r\n}\r\n\r\n#[derive(Accounts)]\r\npub struct Deposit<'info> {\r\n    // 用户的钱包，必须签署交易\r\n    #[account(mut)]\r\n    pub user: Signer<'info>,\r\n    \r\n    // 用户的代币账户，我们将从中提取\r\n    #[account(\r\n        mut,\r\n        constraint = user_token_account.owner == user.key()\r\n    )]\r\n    pub user_token_account: Account<'info, TokenAccount>,\r\n    \r\n    // 我们将存入代币的程序库\r\n    #[account(\r\n        mut,\r\n        seeds = [b\"vault\"],\r\n        bump\r\n    )]\r\n    pub vault: Account<'info, TokenAccount>,\r\n    \r\n    // 我们将使用的 SPL 代币程序\r\n    pub token_program: Program<'info, Token>,\r\n}\r\n```\r\n\r\n// 第一步观察的是指令的输入内容：\r\n\r\n*   `Context<Deposit>` 是一个包装器，包含执行指令所需的所有账户\r\n*   `amount: u64` 是要存入的代币数量\r\n\r\n`Deposit` 结构在指令下方描述，我们可以看到涉及相当数量的宏：\r\n\r\n*   第一个是 `#[derive(Accounts)]` 宏，它在 Anchor 中用于定义和验证指令在执行过程中将要交互的账户，支持 Solana 的并行事务处理。该宏根据它们的 [类型](https://www.anchor-lang.com/docs/account-types) (Account, Signer, UncheckedAccount, Program) 实现账户的验证检查。\r\n*   第二个是 `#[account()]` 宏：如你所见，可以向该宏提供不同的参数。这些参数称为 [账户约束](https://github.com/Ackee-Blockchain/Solana-Auditors-Bootcamp/blob/master/Lesson-1/README.md#account-constraints)，因为它们将添加额外的检查，每当程序访问这些账户时都会进行验证。\r\n\r\n我们来看看一些约束：\r\n\r\n*   例如，`user` 账户具有 `mut` 约束，指示程序该账户是可变的。如果没有此约束，程序在任何时间修改账户时都会回滚。\r\n*   我们可以在下面看到下一个：`constraint = user_token_account.owner == user.key()`。此约束确保 `user_token_account` 的所有者字段和 `user` 的公共地址具有相同的值。这实际上是一种访问控制，确保只有交易的签署者被授权调用具有此特定 `user_token_account` 的指令。\r\n*   接下来我们看到的是 `seeds = [b\"vault\"]`：这特定于 PDA（程序派生账户）。这添加了检查，以确保提供的 `vault` 账户是我们当前调用的程序创建的，并且是用以下种子派生的。\r\n*   `bump` 只是派生 PDA 时的一个附加参数。\r\n\r\n// 你可以在 [他们的文档](https://www.anchor-lang.com/docs/account-constraints) 或 Ackee Blockchain 的 [这门课程](https://github.com/Ackee-Blockchain/Solana-Auditors-Bootcamp/blob/master/Lesson-1/README.md#account-constraints) 中找到所有 Anchor 约束。\r\n\r\n## 9\\. 学习资源\r\n\r\n感谢你花时间阅读本指南！\r\n\r\n我希望它能为你提供一个扎实的基础，开始探索 Solana 项目。请记住，这只是开始，学习的最佳方式是深入实际代码库，并将我们在这里讨论的概念与真实实现相结合。\r\n\r\n如果你发现本指南对你有帮助，请随时与其他研究者分享。我也非常感谢任何 [反馈或建议](https://twitter.com/messages/compose?recipient_id=1436561550010093574) 来改进，因为这有助于使资源对所有人都更好。\r\n\r\n对于那些希望深入了解 Solana 审计的人，我收集了一些有趣的资源：\r\n\r\n*   [Solana  系列文章](https://learnblockchain.cn/categories/solana/featured2/)\r\n*   [60 天的 Solana (RareSkills)](https://decert.me/tutorial/rareskills-solana-course/)\r\n*   [为 Solidity 开发者准备的基础 Rust](https://decert.me/tutorial/rareskills-solana-course/chapter_6)\r\n*   [为 EVM 开发者理解 Solana](https://solana.com/developers/evm-to-svm)\r\n*   [Solana 开发者课程](https://decert.me/tutorial/sol-dev/)\r\n*   [Ackee Solana 审计员训练营 (repo)](https://github.com/Ackee-Blockchain/Solana-Auditors-Bootcamp/tree/master)\r\n*   [Ackee Solana 审计员训练营 (视频)](https://www.youtube.com/playlist?list=PLzUrW5H8-hDdU-pzHjZrgupi5Wis6zWNJ)\r\n*   [Solana 审计和安全资源 (不再积极维护)](https://github.com/sannykim/solsec)\r\n*   [Anchor 文档](https://www.anchor-lang.com/)\r\n*   [Solana 程序安全的背包客指南](https://learnblockchain.cn/article/7690)\r\n*   [Ottersec 示例公共审计报告 (选择“Solana”作为链)](https://www.notion.so/a296e98838aa4fdb8f3b192663400772?pvs=21)\r\n\r\n祝审计愉快！\r\n\r\n\r\n> 我是 [AI 翻译官](https://learnblockchain.cn/people/19584)，为大家转译优秀英文文章，如有翻译不通的地方，在[这里](https://github.com/lbc-team/Pioneer/blob/master/translations/10721.md)修改，还请包涵～"},"author":{"user":"https://learnblockchain.cn/people/25767","address":null},"history":"bafkreid3e64x43fhgzg7pfdbfymtf6mfsxgp3q6fioy3s2j2vdtq74xelu","timestamp":1739178057,"version":1}