Solana 交易是怎么组装的?签名、消息与指令表的 1232 字节 图 1
Solana 交易是怎么组装的?签名、消息与指令表的 1232 字节 · 图 1

结论先说

一笔 Solana 交易没有隐藏的中间状态:它就是一个签名数组加上一条消息(message),序列化后的总大小不能超过 1232 字节——这个上限来自 IPv6 最小传输单元 1280 字节减去 48 字节网络包头。消息内部只有四样东西:消息头、账户地址表、近期区块哈希和一组编译后的指令。所有后续讨论都围绕这四样东西展开,机制以官方文档为准。这与以太坊那种“目标地址加一段 calldata”的交易形态是两种设计路线,费用侧的差别可以对照Solana 计算预算怎么调?CU 上限、单价与优先费的关系

消息头的三个计数:把账户表切成四段

消息里的 account_keys 是一串 32 字节地址,它本身不带权限标签。权限靠消息头里的三个计数来划分:需要签名的账户数、已签名可读账户数、未签名可读账户数。三个数把地址表顺序切成四组——已签名可写、已签名只读、未签名可写、未签名只读。第一个账户永远是费用支付者,且必须同时是签名者可写账户,否则整笔交易在结构校验阶段就被拒绝。这样设计的好处是:节点不执行任何代码,光看消息头和地址表就能判断谁授权了这笔交易、它可能改动哪些账户。也正是这份显式声明,让运行时能把互不触碰同一可写账户的指令并行执行,这是 Solana 并行调度的地基。

编译指令:只存索引,不存地址

交易消息的四段结构:消息头、账户表、区块哈希与指令索引

每条编译指令(CompiledInstruction)记录程序在地址表中的索引、该指令要用到的账户在表中的下标列表,以及一段指令数据。注意指令里存的是下标而不是地址——同一地址在一条交易里只出现一次,指令靠索引引用它。这就解释了一个常见现象:一笔涉及几十上百个账户的复杂 DeFi 交易,如果全部地址内联写进消息会撑爆 1232 字节,v0 版本消息因此引入地址查找表(ALT),把常用地址打包到链上账户里,交易只引用表的索引,需要执行时才展开。地址查找表的读法可看Solana地址查找表ALT怎么读?

近期区块哈希:一个字段干两件事

recent_blockhash 是一个 32 字节哈希,同时承担时间戳和去重两个职责。时间戳职责:证明这笔交易是最近构造的。官方流水线圈定了区块哈希在验证者区块哈希队列中的年龄上限为 150 个 slot,超龄交易以区块哈希未找到错误被拒绝,唯一例外是配置了有效持久化 nonce 账户的交易,原理见Solana Durable Nonce是什么?。去重职责:验证者维护状态缓存,同一消息哈希的交易若已被处理过,第二次提交会收到已处理错误,不需要像以太坊那样靠账户序列号防重放。这两个职责合在一起解释了 Solana 上最常见的用户困惑——离线签名的交易不会永远有效,过期后必须换一个新的区块哈希重新签名,而不是原样重发,细节见Solana 交易过期是什么原因?

签名验的是消息字节

签名数组与地址表前部按位置一一对应:第 i 个签名必须能用第 i 个账户地址验证通过,算法是 Ed25519,签名的对象是整条消息的字节,而不是逐条指令。多签交易就是多个地址对同一段字节分别签名。校验发生在交易进入银行流水线的签名验证阶段,任何一个签名无效,整个数据包被丢弃。对用户的实际含义是:交易一旦组装签名完成,任何一个字段被改动都会让全部签名作废——这也是为什么钱包弹窗里让你签的就是最终上链的字节本身。

常见误区

第一,把 1232 字节当成“交易条数限制”:它限制的是单笔交易的序列化体积,指令多、账户多都会占用它,批量逻辑要靠程序内部循环或查找表压缩。第二,把区块哈希当成确认数:它只是有效期的锚点,不代表交易被打包。第三,以为指令之间可以互相等待结果:同一笔交易里的指令按顺序原子执行,要么全部生效要么全部回滚,不存在中间态。

小结

Solana 交易是一份自描述的静态清单:签名数组证明授权,消息头声明权限,地址表圈定影响范围,指令表写下要做的事,区块哈希限定有效期。1232 字节的体积约束塑造了它的一切工程取舍——查找表、程序派生地址、批量程序都是绕开这份预算的手段。理解了一笔交易的骨架,再看费用、并行执行和交易失败排查都有了参照。本文为机制说明,依据官方文档整理,不构成投资建议。