Crypto OS
职业 Track

T2 · Solana / SVM Engineer

在一条把代码与数据彻底分开的链上,写出能被别人集成的程序。

面向 Rust 与高性能链方向

本页里的「T2」指这条 Track。课程章节一律写成「第 T10 章」并带链接,两者不是一回事——章节 T2 讲的是密码学基础。

这条 Track 适合谁

你从 EVM 过来,一直在用 EVM 的脑子套 Solana。 你会下意识找「合约的存储在哪」,找不到就开始别扭。你知道有 PDA 这个东西,但说不清它和一个普通账户在链上到底差在哪一个字节。

你写得动 Rust,但写不出安全的账户校验。 借用检查器你早就打通了,宏也能看懂。可你的指令函数里,账户校验要么全靠框架的属性宏(你不确定它到底检查了什么),要么靠自己手写几个 require(你不确定漏了什么)。

你在 Devnet 跑得很顺,一上主网就开始出怪事。 交易偶发失败、账户创建的租金不够、同一笔请求被重复执行、账户空间不够又不知道怎么扩。这些在本地验证器上一次都没出现过。

你能写程序,但交不出别人能用的东西。 你的项目只有一个测试脚本,别人要集成就得读你的 Rust 源码去猜账户顺序。你没写过一个客户端 SDK。

分界线是这一句:你交付的不是一个程序,是一个别人不读你源码也能正确调用的程序。

前置

Part B 全 36 章读完最好。要现在就开始,知识图谱里通向这条 Track 的最短路径是:

  1. 第 T1–T3 章
  2. 第 T4 章
  3. 第 T5 章
  4. 第 T6 章
  5. 第 T10 章
  6. 第 T11 章
依赖关系取自 graph.ts。Indexer 那条分支另算,见下表最后一行
必须先读图谱里的理由
第 T1 章第 T3 章状态、签名、交易生命周期。这三章不分链,是所有后续内容的地基
第 T4 章 · RPC 是什么第 T5 章的直接前置。Solana 的高吞吐会把 RPC 的限速与数据完整性问题放大,这一章必须在前面
第 T5 章 · EVM Account Model图谱里第 T6 章的直接前置。这不是笔误:你要先有一个「账户即状态索引」的参照系,才看得出 Solana 把什么东西拆开了
第 T6 章 · Solana Account Model这条 Track 的真正起点。程序不存数据、交易必须提前声明它会碰哪些账户,两句话决定了后面一切
第 T10 章 · Solana Program图谱里依赖第 T6 章。PDA 与 CPI 在这里第一次成为你要自己写的东西
第 T11 章 · Token Program图谱里依赖第 T10 章。共享程序加各自账户这个模型,是 Solana 生态可组合性的来源
第 T13 章第 T17 章只有你要做 Indexer 那个 topic 时才需要。图谱里第 T17 章的依赖链一路回到第 T9 章,绕不过去

你会学什么

  1. Rust
  2. Anchor
  3. PDA
  4. CPI
  5. Token-2022
  6. Indexer
  7. DeFi

七个 topic 分成四组。

一、Rust 与框架:语言不是难点,账户校验才是

Rust 这一关的重点不在所有权,而在你能不能读懂框架的属性宏展开成了什么检查。框架帮你生成的校验通常包括:账户是不是签名者、是不是可写、owner 是不是预期的程序、PDA 的种子能不能推导出这个地址、账户空间够不够。

这些检查任何一条没生成,就得你自己补。这条 Track 要求你能对着一个指令说出:这里一共做了几项校验、每一项防的是什么、以及少了哪一项会被怎样利用。

不用框架手写一遍指令入口,是这条 Track 里性价比最高的一次练习。写过一次,你就再也不会把属性宏当黑盒。

二、PDA 与 CPI:程序的地址空间和它的对外调用

PDA 要真正吃透三件事:种子的设计(谁决定、是否可被他人抢先创建)、bump 的规范取值、以及「这个 PDA 由哪个程序签名」意味着什么。绝大多数 Solana 的严重问题都可以追到一句「这个账户我以为只有我的程序能写」。

CPI 这边是对称的问题:你调别人时要确认调的确实是那个程序,别人调你时要确认权限没有被顺带传递。跨程序调用的深度限制和账户传递规则,在设计接口时就要考虑,不能等到跑不通再改。

三、Token-2022 与生态集成:你的程序要和谁共处

扩展能力带来的不是新功能列表,而是新的假设失效:转账可能触发钩子、可能收取手续费、可能被冻结、元数据可能就在 mint 账户里。你的程序如果假设「转账就是余额加减」,接上带扩展的代币就会算错账。

这一组的核心训练是:列出你的程序对代币做了哪些隐含假设,然后逐条问「哪个扩展会让它不成立」。

四、Indexer 与 DeFi:让链上发生的事变成可查询的事

Solana 的数据形态和 EVM 不一样:没有 EVM 那种日志模型,你要从交易的指令、内层指令和账户变化里还原语义。第 T17 章讲的重组、断点续传、幂等三个问题在这里一个都不少,只是载体换了。

DeFi 这一项的目标不是自己做一个协议(那是 Track T4),而是能正确地集成:读得懂主流程序的账户布局,算得对一次兑换的预期产出,处理得了交易在链上失败的情况。

毕业作品

一个部署在主网的 Anchor 程序,附客户端 SDK。

毕业项目分工明确:毕业项目要一个横向完整的系统,这条 Track 只要一个程序,但要求它能被别人正确使用。「附客户端 SDK」这五个字是这份作品的全部重量所在——它把「我写完了」变成「别人用得上」。

别人能不能跑起来。 clone 之后,一条命令装依赖,一条命令在本地验证器上跑完全部测试。README 写清楚需要的工具链版本——这条链的工具链版本敏感度比多数人预期的高,不写版本等于不能复现。

再给一段不超过 20 行的示例代码,展示 SDK 最主要的那个调用。

SDK 让调用方不需要知道账户顺序。 调用方传业务参数,SDK 负责推导 PDA、组装账户列表、处理关联账户是否已存在、估算所需租金。

验收方式很直接:让一个没读过你 Rust 代码的人,只看 SDK 的类型定义,完成一次成功调用。 他要是得回来问你「第三个账户传什么」,这一条不算过。

账户校验有一份清单,并且每一条都有一个会失败的测试。 逐条列出这个程序做了哪些校验,每条配一个「故意传错账户,交易必须失败」的用例。

只测成功路径的测试套件,证明不了任何安全性质。

失败与重试路径是设计过的,不是撞出来的。 至少覆盖:账户已存在、租金不足、账户空间不够、同一请求被重复提交、交易过期需要重发。

每一种都要说清 SDK 这一侧怎么表现——是重试、是报错、还是幂等地返回成功。

主网部署要小额,而且要有升级与停机的说明。 用你自己的钱,金额控制在出问题也不心疼的范围。写清楚升级权限在谁手里、你打算什么时候交出或销毁它、以及出事时怎么让程序停下来。

只交 Devnet 部署加一份「为什么还不该上主网」,在评审里同样成立。

怎样判断自己达标了

常见误区

「Devnet 跑通了就等于能上主网」

Devnet 的拥塞模式、RPC 行为、账户租金环境都和主网不同。主网上你会遇到:交易因为过期而消失、优先费不够导致长时间不进块、RPC 返回的确认状态和你以为的不一样。

正确做法:把「交易可能永远不确认」当成默认情况来设计客户端。发送后必须有明确的查询与重发策略,而不是发完就当成功。

「用了框架就不用管账户校验了」

框架生成的是你声明的检查,不是你需要的检查。少写一个属性,就少一项校验,而编译器不会提醒你。

正确做法:把账户校验清单当成一份独立的文档来写,逐条对照代码确认它真的存在。每一条都配一个故意传错账户的失败用例——这份用例集就是你的校验清单的可执行版本。

「PDA 就是个地址,随便设个种子就行」

种子设计等于权限设计。种子里少放一个维度,两个本该隔离的对象就会共用同一个账户;种子里放了用户可控且不受约束的值,别人就能抢先占掉你将来要用的地址。

正确做法:先写出「这个 PDA 代表什么,谁有资格让它存在」,再从这句话反推种子。把对象之间的归属关系也编进种子,让「拿错账户」在地址推导阶段就不成立。

「Solana 上没有重入,所以不用管调用顺序」

没有 EVM 那种回调式重入,不等于没有顺序问题。CPI 会把执行权交出去,转账钩子会触发别人的代码,而你的账户此刻可能正处在中间状态。

正确做法和第 T28 章那条肌肉记忆完全一样:先把自己的账记清楚,最后才和外界打交道。 换了一条链,这条规则一个字都不用改。

接下来

本页目录