版本化交易

摘要

Solana 有三种交易格式:legacy、v0 和 v1。v0 新增了地址查找表(ALT),可通过 1 字节索引引用账户。v1 将大小限制提升至 4,096 字节,将资源限制移入消息本身,并移除了 ALT。

Solana 支持三种交易格式:legacyv0v1。以下将分别从三个方面介绍每种格式:字节在网络上的布局方式、账户的引用方式,以及资源限制的来源。

v1 激活状态

v1 格式目前尚未在任何集群上激活。计划随 Agave v4.2 一同激活。solana-test-validator 4.2+ 版本支持在本地测试 v1 交易。现有应用请查阅为 v1 做准备

格式对比

限制legacyv0v1
最大交易大小1,232 字节1,232 字节4,096 字节
账户地址~32,受大小限制64,通过查找表64,内联
地址查找表不支持支持不支持
资源限制ComputeBudget 指令ComputeBudget 指令消息配置

跳转至:Legacy · v0 · v1

Legacy 格式

这是原始格式,目前仍是大多数工具的默认格式。它没有任何版本前缀:交易的第一个字节是签名数组的 compact-u16 计数,消息的第一个字节是 num_required_signatures,其高位始终未置位。

Legacy 线格式布局

字段大小描述
num_signaturescompact-u16签名数量
signaturesnum_signatures x 64 字节Ed25519 签名
header3 字节MessageHeader — 第一个字节的版本位未置位
num_account_keyscompact-u16账户密钥数量
account_keysnum_account_keys x 32 字节公钥,全部内联
recent_blockhash32 字节生命周期指定符
num_instructionscompact-u16指令数量
instructions可变长度每条指令连续序列化

每个可变长度数组都以 compact-u16 长度为前缀:值 0–127 使用 1 字节,较大值使用 2–3 字节。关于单条指令的布局及大小计算示例,请参阅交易二进制格式

Legacy 中的账户

每个账户在 account_keys 中以完整的 32 字节公钥写出,指令通过 1 字节索引引用该数组中的账户。无法引用未在交易中明确列出的账户,这也是 legacy 交易在耗尽 1,232 字节前大约只能容纳 32 个账户的原因。

Legacy 中的资源限制

计算单元限制、已加载账户数据大小限制、堆大小和优先费均通过在交易中包含 ComputeBudget 程序指令来请求设置。每条指令占用一个指令 slot 和 150 个计算单元。省略这些指令是安全的:运行时将回退到默认值——每条指令 200,000 CU(上限 1.4M)、64 MiB 数据大小限制、32 KiB 堆,以及零优先费。

V0 格式

v0 是在 legacy 消息基础上新增了两项内容:一个 0x80 版本前缀字节,以及附加在指令之后的 address_table_lookups 数组。此前的所有内容与 legacy 格式字节完全相同。

V0 线格式布局

字段大小描述
num_signaturescompact-u16签名数量
signaturesnum_signatures x 64 字节Ed25519 签名
0x801 字节版本前缀字节 — 消息的第一个字节
header3 字节消息头(与传统相同)
num_account_keyscompact-u16静态账户密钥数量
static_account_keysnum_account_keys x 32 字节交易中直接出现的密钥
recent_blockhash32 字节生命周期指定符
num_instructionscompact-u16指令数量
instructions可变长度与传统格式相同
address_table_lookupscompact-u16 + 可变长度ALT 引用(见下文)

每个地址表查找项包含:

字段大小描述
account_key32 字节ALT 账户的公钥
writable_indexescompact-u16 + N × 1 字节ALT 中可写账户的索引
readonly_indexescompact-u16 + N × 1 字节ALT 中只读账户的索引

地址查找表

ALT 是一个链上账户,最多可存储 256 个公钥。通过引用 ALT,交易可以使用 1 字节索引而非 32 字节公钥来包含额外账户,从而显著减少每个账户的开销。

在运行时,执行开始前,validator 会将所有 ALT 引用解析为完整公钥。解析后的地址会追加到静态账户密钥后,形成完整的账户密钥列表。ALT 解析账户的顺序与静态账户一致:可写查找项在只读查找项之前。

地址查找表只影响账户在链上传输时的引用方式。在执行时,运行时会将所有索引解析为完整账户地址。ALT 解析账户只能是可写或只读(非签名者);不能作为签名者。

v0 中的资源限制

与 legacy 相同:使用 ComputeBudget 指令,省略时采用相同的默认值。

V1 格式

v1 将大小限制提升至 4,096 字节,并围绕交易配置对消息结构进行了重构:资源限制从 ComputeBudget 指令中移出,转移到消息本身的固定位置字段中。这使得网络可以通过单次固定偏移读取来按优先费对交易进行排序,而无需扫描并反序列化指令列表。

V1 线格式布局

字段大小描述
0x811 字节版本前缀字节 — 交易的第一个字节
header3 字节MessageHeader(与 legacy 相同)
config_mask4 字节u32 小端位掩码,标记哪些配置值存在
recent_blockhash32 字节生命周期指定符
num_instructions1 字节固定宽度计数,最大 64
num_addresses1 字节固定宽度计数,最大 64
addressesN x 32 字节账户地址,全部内联 — 无查找表引用
config_values0–20 字节每个已置位的掩码位对应一个值,按位顺序排列(见下文)
instruction_headersN x 4 字节每条指令:program_id_index(u8)、num_accounts(u8)、data_len(u16 小端)
instruction_payloads可变长度每条指令:账户索引,然后是 instruction data
signaturesN x 64 字节位于末尾,无长度前缀 — 数量来自 header

编写解码器时,有两处与 legacy 和 v0 的结构差异值得注意。计数使用固定宽度的 u8 字段而非 compact-u16,且指令被拆分为两段:先是所有固定大小的 header,然后是所有可变长度的 payload,而非每条指令连续存放。

v1 中的账户:无地址查找表

v1 有意移除了 ALT 支持。64 个原始地址共 2,048 字节,在 4,096 字节限制内绰绰有余,因此每个地址均如 legacy 一样内联存储。如果您的应用依赖查找表,迁移至 v1 意味着需要将这些地址内联。

v1 中的资源限制:交易配置

配置由一个 u32 位掩码加上每个已置位字段的固定宽度值组成:

字段宽度备注
0–1优先费u64lamport 总量 — 两位同时置位
2计算单元限制u32
3已加载账户数据大小限制u32
4请求的堆大小u32

未知位将被拒绝。由于消息已签名,无法识别的配置字段不能被静默丢弃。

优先费是 lamport 总量,而非单价

在 legacy 和 v0 中,优先费通过 SetComputeUnitPrice每计算单元的 micro-lamport 数设置,再乘以计算单元限制。在 v1 中,优先费是以 lamport 为单位的绝对总量 — 无需乘法,无需取整。请勿将按 CU 计算的算术逻辑迁移过来。总费用公式其余部分不变:(signatures × lamports_per_signature) + priority_fee

ComputeBudget 指令在 v1 中为空操作

v1 交易不会拒绝 ComputeBudget 指令 — 它会忽略这些指令的配置作用。这些指令仍会作为成功的空操作执行,消耗 150 个计算单元和 64 个指令 slot 中的一个,但对预算没有任何影响。构建 v1 交易时请将其去除,读取 v1 交易时也无需扫描这些指令:相关值已存放在消息配置中。

配置字段必须显式设置

对于发送方而言,最重要的行为变化是:与 legacy 和 v0 不同,v1 交易必须显式设置计算单元限制和已加载账户数据大小限制,否则交易将失败。

未设置字段legacy / v0v1省略时的后果
计算单元限制每条指令 200k,最大 1.4M0 CU立即失败,计算单元耗尽
已加载账户数据大小64 MiB0 字节第一个加载的账户触发 MaxLoadedAccountsDataSizeExceeded
优先费00
堆大小32 KiB32 KiB

推荐做法是:先以两个限制均设为最大值进行一次模拟,然后将返回的 unitsConsumedloadedAccountsDataSize 写回配置中,并将数据大小向上取整到下一个 32 KiB 页边界以留出余量(区块费用模型按 32 KiB 页计费,因此低于下一个页边界的余量是免费的)。

为 v1 做准备

v1 不仅影响交易的发送,也影响交易的读取。当 v1 激活后,任何未选择支持的客户端在调用 getTransactiongetBlock 时,将开始在 v1 交易上报错:

  • getTransactiongetBlock 传入 maxSupportedTransactionVersion: 1 — JSON 整数 1,而非字符串 "1"。传入 0 与省略该参数的效果相同,同样会在 v1 交易上失败,因此在 v0 推出期间已更新的代码库仍需修改此值。
  • getTransaction 在处理 v1 交易时将返回错误 -32015,且一笔 v1 交易将导致整个 getBlock 响应失败并返回相同错误 — 不会有部分结果。
  • blockSubscribe 将发出 block: null 并停止推进,因此将其解读为空区块的消费者将从第一个 v1 slot 起静默落后。
  • getSignaturesForAddress 从不检查交易体,因此 v1 签名可正常列出。
  • 选择支持后,响应中将在 v1 交易的消息里包含一个 transactionConfig 对象(legacy 和 v0 交易中完全不存在该对象)。通过扫描 ComputeBudget 指令来推算优先费或计算单元限制的处理流程,将对每笔 v1 交易静默报告零值
  • 在客户端解码交易时请使用 encoding: 'base64',对于超过 1,232 字节的交易在使用 sendTransaction/simulateTransaction 时也应如此 — base58 编码仍受旧大小限制约束。
  • 客户端库支持需要较新版本:@solana/kit 8.0+、Agave 4.2.x 世代的 Rust crate,或 web3.js v3。web3.js v1 从 1.99.0 起支持读取 v1,但无法构建或发送 v1 交易。

完整迁移指南 — 包括客户端库支持、流式(Geyser/gRPC)版本检测及模拟行为 — 请参阅交易格式 v1 升级页面

Is this page helpful?

©️ 2026 Solana 基金会版权所有