# 学习 Solana

Original: <https://lifelonglearn.ing/learn-solana>

Author: [奶爸](https://lifelonglearn.ing/users/3b04650c-8231-499b-8ef0-381fc4d7efb0)

Published: 2024-02-26T16:05:38Z

Updated: 2026-09-27T06:57:43Z

Version: 1

最近在学习 Solana，Solana 是一个 TPS \~3000 的高性能区块链，对比 Ethereum 这种 TPS \~200 的链，加上其低廉的矿工费，属于公链中比较强势的。

相对于 Ethereum 的 ECDSA 算法，Solana 采用的是 Ed25519 算法，账户地址是 Ed25519 的公钥的 base58 编码。Solana 每个 Account 都有他的 Owner，Owner 和 Holder 是不同的，比如一个 Data Account 他的 Owner 是一个 Program Account，但是他的 Holder 是地址对应的私钥，只有 Owner 有权限操作 Account。

我在 devnet 测试时就遇到过这样一个问题，随便填充的一个私钥对应的地址是一个 PDA，我尝试使用`system_instruction::transfer` 转出地址上的 SOL 时失败了，因为我只是 Holder 而 Owner 是一个 Program Account，[这里](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly9zb2xhbmEuc3RhY2tleGNoYW5nZS5jb20vcXVlc3Rpb25zLzQ3ODAvZG9lcy1hLXBkYS1oYXZlLXRvLWJlLW93bmVkLWJ5LWl0cy1zaWduZXI=)有个相关问题。

## Raw Transaction 解析

```plaintext
Solana Transaction: 提交到 pushTansaction RPC 接口的 base64 解码后的 16 进制分割
  |- Signature 部分
       |- Length: 有多少个签名，长度为 1 个 byte
       |- SignatueN: N 个长度为 64 位的 Ed25519 签名
  |- Message 部分（签名部分是对整个 Message 部分的签名）
       |- Message Header: 长度为 3 个 byte
            |- 有签名的 Account 数量: 1 个 byte
            |- 有签名的只读 Account 数量: 1 个 byte
            |- 无签名的只读 Account 数量: 1 个 byte
       |- Account Addresses
            |- Length: 本交易内涉及多少个账户，长度为 1 个 byte
            |- AddressN: N 个长度为 32 位的钱包地址（Ed25519 公钥）或 ProgramID
       |- Recent Blockhash: 近期的 Blockhash，如果 Block 过老节点会拒绝此交易
       |- Instructions
            |- Length: 本交易内有多少个 Instruction，长度为 1 个 byte
            |- InstructionN: N 个长度不确定的指令，通过对应字段长度逐字节读入
                  |- Program ID Index: Program ID 在 Account Addresses 中的位置，长度为 1 个 byte
                  |- Account Address Indexes: 本指令涉及的地址索引
                        |- Length: 有多少个地址索引，长度为 1 个 byte
                        |- IndexN: Address 在 Account Addresses 中的位置，长度为 1 个 byte
                  |- Instruction Data 不定长的调用数据
                        |- Length: 调用数据长度，长度为 1 个 byte
                        |- Data: bincode 编码的调用 Program 用的数据
```

我制作了一个 [Solana 交易解析工具](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly9uYWliYS1hcmNoaXZlZC5naXRodWIuaW8vc29sYW5hLXRyYW5zYWN0aW9uLWRlY29kZXIv)，把 base64/hex 的 raw transaction data 粘贴进去即可解析出上述结构。

Transaction Fee 由 `Account Addresses` 列表中的第一个地址支付。

## 智能合约

一般使用 [Anchor](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly9naXRodWIuY29tL2NvcmFsLXh5ei9hbmNob3I=) 进行开发，[Solana Playground](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly9iZXRhLnNvbHBnLmlvLw==) 就很够用了，`solana airdrop 5 -> build -> deploy -> run` 非常丝滑，如果左下角一个默认生成的钱包地址不够用，可以在 client 的 typescript 脚本里面自己再生成一些钱包。

```typescript
const another_wallet = web3.Keypair.fromSeed(Uint8Array.from(Array<number>(32).fill(112)));
```

奶爸只是做了些 Tutorials，测试了下账户读写权限看了看交易结构，后面有过深度使用后再继续分享。

## 常见问题

与 EVM 系链相比，Solana 有哪些独特的地方？

* Solana 账户没有 Nonce，同区块内不同的交易根据地址列表读写情况并行执行，交易内的 Instructions 原子化执行。
* 同一个区块内的交易通常是[先到先服务](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNzI2MDU0ODIvYXJlLXR4cy1pbi10aGUtc2FtZS1ibG9jay1vcmRlcmVk)，[设置优先费用](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly9zb2xhbmEuY29tL2RvY3MvaW50cm8vdHJhbnNhY3Rpb25fZmVlcyNwcmlvcml0aXphdGlvbi1mZWU=)可以使交易优先排序
* Solana 没有类似 Solidity 合约中的 Event，只有一个 `msg!` 可以打印一些字符串，[这里](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly9naXRodWIuY29tL3NvbGFuYS1sYWJzL3NvbGFuYS9pc3N1ZXMvMTQwNzY=)是相关 Issue
* Solana 一般不在合约中校验类似 `eth_personalSign`、`eth_signTypedData` 消息签名，而是通过交易的 Signer 来校验权限
* 节点没有提供类似 EVM 节点 `debug_` NS，无法追踪交易到每一步调用的输入输出
