1139 words
6 minutes
深入理解Solana账户与交易机制
一、为什么 Solana 的账户模型如此特别?
Solana 与 EVM 的最大不同,是账户不仅是资产容器,还是数据存储单元。
它更像是一种“文件系统模型”,每个账户都是一个独立的文件:
- Program Account —— 类似可执行文件(智能合约逻辑)。
- Data Account —— 存储数据的账户,由特定程序控制读写权限。
- System Account —— 系统级账户,用于创建新账户或转账 SOL。
在 Solana 上,一笔交易的执行过程更像是:
“程序被加载进多个账户的上下文中,然后对其中的账户进行读写操作。”
二、账户的基本结构
每个账户在链上的核心字段如下:
| 字段 | 含义 |
|---|---|
| lamports | 账户余额,单位为 lamports(1 SOL = 1e9 lamports) |
| owner | 该账户由哪个 Program 拥有(例如 SystemProgram) |
| data | 原始二进制数据,由程序自定义结构 |
| executable | 是否是可执行账户(即 Program) |
| rentEpoch | 租金周期,用于回收长期闲置账户 |
你可以理解为:
“每个账户既能存钱,又能存数据,还能指定由谁来控制它。”
三、交易的三层结构:Transaction → Message → Instruction
Solana 的交易不是单条指令,而是一组“批处理操作”。
它包含多个指令(Instruction),每条指令由一个 Program 执行。
1️⃣ Transaction
最外层结构,包含签名、公钥集合和消息。
2️⃣ Message
打包了一系列要执行的指令(Instructions),以及这些指令涉及的账户。
3️⃣ Instruction
单个操作,由:
- Program ID(哪个程序执行)
- Accounts(涉及哪些账户)
- Data(操作参数)
整体结构如下:
Transaction
└── Message
├── AccountKeys
├── RecentBlockhash
└── Instructions[]
├── ProgramID
├── AccountIndices[]
└── Data[]
四、Solana 的签名机制
Solana 使用 Ed25519 签名算法。
每笔交易必须由至少一个签名者授权(通常是付款方的账户私钥)。
签名的目标数据是 Message 的哈希,而不是交易体。
这样做的好处是可快速验证交易签名的正确性,且支持批量签名验证。
简单来说:签名的是 Message,广播的是 Transaction。
五、构造一笔转账交易(Go 实战)
现在我们通过 Go SDK 来构造并发送一笔简单的 SOL 转账。
导入依赖
import (
"context"
"fmt"
"log"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/rpc"
"github.com/gagliardetto/solana-go/rpc/jsonrpc"
"github.com/gagliardetto/solana-go/text"
)
构建核心逻辑
func main() {
ctx := context.Background()
client := rpc.New(rpc.DevNet_RPC)
// 1️⃣ 加载本地私钥(从 solana-keygen 生成的钱包)
sender, err := solana.PrivateKeyFromSolanaKeygenFile("/Users/alan/.config/solana/devnet.json")
if err != nil {
log.Fatalf("加载钱包失败: %v", err)
}
receiver := solana.MustPublicKeyFromBase58("接收者钱包地址")
// 2️⃣ 构造转账指令
instruction := solana.NewInstruction(
solana.SystemProgramID,
solana.MustToAccountMeta(sender.PublicKey(), true, true),
solana.MustToAccountMeta(receiver, false, true),
).SetData(
solana.MustEncodeSystemTransfer(
solana.NewSystemTransferInstruction(
sender.PublicKey(),
receiver,
1_000_000_000, // 转账 1 SOL = 1e9 lamports
),
),
)
// 3️⃣ 获取最新区块哈希
recent, err := client.GetRecentBlockhash(ctx, rpc.CommitmentFinalized)
if err != nil {
log.Fatalf("获取区块哈希失败: %v", err)
}
// 4️⃣ 构造交易消息
tx, err := solana.NewTransaction(
[]solana.Instruction{instruction},
recent.Value.Blockhash,
sender.PublicKey(),
)
if err != nil {
log.Fatalf("创建交易失败: %v", err)
}
// 5️⃣ 签名交易
_, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey {
if sender.PublicKey().Equals(key) {
return &sender
}
return nil
})
if err != nil {
log.Fatalf("签名失败: %v", err)
}
// 6️⃣ 广播交易
sig, err := client.SendTransaction(ctx, tx)
if err != nil {
log.Fatalf("广播失败: %v", err)
}
fmt.Println("✅ 交易已提交,签名哈希:", sig)
}
六、用 Go 工具查看交易结构
SDK 提供了一个非常有用的调试工具 text.NewEncoder():
encoder := text.NewEncoder(os.Stdout)
encoder.Encode(tx.Message)
输出结构大致如下:
Message {
recent_blockhash: 7dMjeDkBmw6axbiTzA8npPQfLszDQAJhz3fWQ4XgG9b5
account_keys: [
1. Sender
2. Receiver
3. SystemProgram
]
instructions: [
0: Program(SystemProgram) -> Transfer 1 SOL
]
}
这能帮助我们理解交易内部的账户引用关系,非常适合调试复杂合约调用。
七、RPC 与 gRPC 接口的使用
Solana 节点支持两种交互方式:
| 模式 | 协议 | 适合场景 |
|---|---|---|
| JSON-RPC | HTTP(S) | 简单查询与交易提交 |
| gRPC | Binary over HTTP/2 | 高并发、低延迟的链上同步与监听 |
在 Go SDK 中:
rpc.New()→ JSON-RPC 客户端jsonrpc.NewClient()→ gRPC 模式客户端
例如,我们可以通过 gRPC 订阅区块事件:
grpcClient := jsonrpc.NewClient("https://api.devnet.solana.com:443")
stream, err := grpcClient.SubscribeSlotUpdates(ctx)
if err != nil {
log.Fatalf("订阅失败: %v", err)
}
for {
slotUpdate, err := stream.Recv()
if err != nil {
log.Fatalf("接收错误: %v", err)
}
fmt.Printf("新区块 Slot: %v\n", slotUpdate.Slot)
}
这种方式非常适合写区块监听器、价格同步服务等需要实时响应的业务。
八、小结
| 模块 | 内容 |
|---|---|
| 账户模型 | 通用账户体系、数据可存储与执行控制 |
| 交易结构 | Transaction → Message → Instruction |
| 签名机制 | Ed25519,对 Message 哈希签名 |
| Go 实战 | 构造与发送转账交易 |
| 通信接口 | JSON-RPC + gRPC 双模式支持 |
深入理解Solana账户与交易机制
https://blog.ithuo.net/posts/solana-accounts-and-transactions/