概要
Solanaには3つのトランザクションフォーマットがあります:legacy、v0、v1。v0は1バイトインデックスでアカウントを参照するアドレスルックアップテーブル(ALT)を追加します。v1はサイズ制限を4,096バイトに引き上げ、リソース制限をメッセージ自体に組み込み、ALTを廃止します。
Solanaは3つのトランザクションフォーマットをサポートしています:legacy、v0、v1。それぞれについて、ワイヤー上のバイト配置、アカウントの参照方法、リソース制限の出所という3つの観点から説明します。
フォーマット比較
| 制限 | legacy | v0 | v1 |
|---|---|---|---|
| 最大トランザクションサイズ | 1,232バイト | 1,232バイト | 4,096バイト |
| アカウントアドレス | ~32個、サイズ制限あり | 64個、ルックアップテーブル経由 | 64個、インライン |
| アドレスルックアップテーブル | 非対応 | 対応 | 非対応 |
| リソース制限 | ComputeBudget instructions | ComputeBudget instructions | メッセージ設定 |
Legacyフォーマット
オリジナルのフォーマットであり、ほとんどのツールでいまだにデフォルトとして使用されています。バージョンプレフィックスは一切なく、トランザクションの最初のバイトはシグネチャ配列のcompact-u16カウント、メッセージの最初のバイトはnum_required_signaturesであり、その最上位ビットは常に未設定です。
Legacyワイヤーレイアウト
| フィールド | サイズ | 説明 |
|---|---|---|
num_signatures | compact-u16 | シグネチャの数 |
signatures | num_signatures x 64バイト | Ed25519シグネチャ |
header | 3バイト | MessageHeader — 最初のバイトはバージョンビット未設定 |
num_account_keys | compact-u16 | アカウントキーの数 |
account_keys | num_account_keys x 32バイト | 公開鍵、すべてインライン |
recent_blockhash | 32バイト | 有効期限指定子 |
num_instructions | compact-u16 | instructionsの数 |
instructions | 可変長 | 各instructionを連続してシリアライズ |
可変長配列はすべてcompact-u16長プレフィックスが付きます:値0〜127は1バイト、より大きい値は2〜3バイトです。instruction単位のレイアウトとサイズ計算の例については、トランザクションバイナリフォーマットを参照してください。
Legacyのアカウント
すべてのアカウントはaccount_keys内に32バイトの完全な公開鍵として書き込まれ、instructionsは1バイトのインデックスでその配列を参照します。トランザクションに明示的に記載されていないアカウントを参照する方法はなく、これがlegacyトランザクションを1,232バイトを使い切る前におよそ32アカウントに制限する要因です。
Legacyのリソース制限
コンピュートユニット制限、ロード済みアカウントデータサイズ制限、ヒープサイズ、優先手数料はすべて、トランザクションにComputeBudgetプログラムのinstructionsを含めることでリクエストされます。それぞれがinstructionスロットを1つと150コンピュートユニットを消費します。省略しても安全です:ランタイムはデフォルト値(instruction当たり200,000 CU、上限1.4M、データサイズ制限64 MiB、ヒープ32 KiB、優先手数料ゼロ)にフォールバックします。
V0フォーマット
v0はlegacyメッセージに2つの要素を加えたものです:0x80バージョンプレフィックスバイトと、instructionsの後に追加されるaddress_table_lookups配列です。それ以前の部分はlegacyとバイト単位で同一です。
V0ワイヤーレイアウト
| フィールド | サイズ | 説明 |
|---|---|---|
num_signatures | compact-u16 | シグネチャの数 |
signatures | num_signatures x 64バイト | Ed25519シグネチャ |
0x80 | 1バイト | バージョンプレフィックスバイト — メッセージの最初のバイト |
header | 3バイト | MessageHeader(レガシーと同じ) |
num_account_keys | compact-u16 | 静的アカウントキーの数 |
static_account_keys | num_account_keys x 32バイト | トランザクション内に文字通り表示されるキー |
recent_blockhash | 32バイト | 有効期限指定子 |
num_instructions | compact-u16 | instructionsの数 |
instructions | 可変長 | レガシーと同じ形式 |
address_table_lookups | compact-u16 + 可変 | ALT参照(下記参照) |
各アドレステーブルルックアップエントリには以下が含まれます:
| フィールド | サイズ | 説明 |
|---|---|---|
account_key | 32バイト | ALTアカウントの公開鍵 |
writable_indexes | compact-u16 + N x 1バイト | 書き込み可能アカウント用のALTへのインデックス |
readonly_indexes | compact-u16 + N x 1バイト | 読み取り専用アカウント用のALTへのインデックス |
アドレスルックアップテーブル
ALTは最大256個の公開鍵を保存するオンチェーンアカウントです。ALTを参照することで、トランザクションは32バイトの公開鍵の代わりに1バイトのインデックスを使用して追加のアカウントを含めることができ、アカウントごとのオーバーヘッドを大幅に削減します。
実行開始前のランタイムにおいて、validatorはすべてのALT参照を完全な公開鍵に解決します。解決されたアドレスは静的アカウントキーに追加され、完全なアカウントキーリストを形成します。ALTで解決されたアカウントは、静的アカウントと同じ順序に従います。書き込み可能なルックアップが読み取り専用ルックアップの前に配置されます。
アドレスルックアップテーブルは、オンワイヤトランザクションでアカウントがどのように参照されるかにのみ影響します。実行時には、ランタイムがすべてのインデックスを完全なアカウントアドレスに解決します。ALTで解決されたアカウントは書き込み可能または読み取り専用(非署名者)のみ可能で、署名者にはなれません。
v0のリソース制限
legacyから変更なし:ComputeBudget instructions、省略時のデフォルト値も同様。
V1フォーマット
v1はサイズ制限を4,096バイトに引き上げ、トランザクション設定を中心にメッセージを再構築します:リソース制限はComputeBudget instructionsから外れ、メッセージ自体の固定位置フィールドに移動します。これにより、ネットワークはinstructionリストをスキャン・デシリアライズせず、単一の固定オフセット読み取りでトランザクションを優先手数料でランク付けできます。
V1ワイヤーレイアウト
| フィールド | サイズ | 説明 |
|---|---|---|
0x81 | 1バイト | バージョンプレフィックスバイト — トランザクションの最初のバイト |
header | 3バイト | MessageHeader(legacyと同様) |
config_mask | 4バイト | どの設定値が存在するかを示すu32 LEビットマスク |
recent_blockhash | 32バイト | 有効期限指定子 |
num_instructions | 1バイト | 固定幅カウント、最大64 |
num_addresses | 1バイト | 固定幅カウント、最大64 |
addresses | N x 32バイト | アカウントアドレス、すべてインライン — ルックアップテーブル参照なし |
config_values | 0〜20バイト | 設定されたマスクビットごとに1つの値、ビット順(下記参照) |
instruction_headers | N x 4バイト | instruction単位:program_id_index(u8)、num_accounts(u8)、data_len(u16 LE) |
instruction_payloads | 可変長 | instruction単位:アカウントインデックス、続いてinstruction data |
signatures | N x 64バイト | 末尾に配置、長さプレフィックスなし — カウントはヘッダーから取得 |
デコーダーを実装する際に注意すべきlegacyおよびv0との構造上の相違点が2つあります。カウントはcompact-u16ではなく固定幅のu8フィールドであること、そしてinstructionsは各instructionを連続して配置するのではなく、固定サイズのヘッダーをすべて先に並べ、その後に可変長ペイロードをすべて並べるという2つのランクに分割されることです。
v1のアカウント:アドレスルックアップテーブルなし
v1はALTサポートを意図的に廃止しています。64個の生アドレスは2,048バイトであり、4,096バイト制限内に十分収まるため、legacyと同様にすべてのアドレスをインラインで記述します。アプリケーションがルックアップテーブルに依存している場合、v1への移行はそれらのアドレスをインライン化することを意味します。
v1のリソース制限:トランザクション設定
設定はu32ビットマスクに続いて、そのビットが設定された各フィールドの固定幅値で構成されます:
| ビット | フィールド | 幅 | 備考 |
|---|---|---|---|
| 0〜1 | 優先手数料 | u64 | 合計lamport数 — 両ビットを同時に設定 |
| 2 | コンピュートユニット制限 | u32 | |
| 3 | ロード済みアカウントデータサイズ制限 | u32 | |
| 4 | 要求ヒープサイズ | u32 |
未知のビットは拒否されます。メッセージは署名されているため、未認識の設定フィールドを暗黙的に破棄することはできません。
優先手数料は単価ではなく合計lamport数
legacyおよびv0では、優先手数料はSetComputeUnitPriceを通じてコンピュートユニット当たりのマイクロlamport数として設定され、コンピュートユニット制限と乗算されます。v1ではlamport単位の絶対合計値となり、乗算も丸めも不要です。CU単位の演算をそのまま持ち込まないでください。合計手数料の計算式はその他の点では変わりません:(signatures × lamports_per_signature) + priority_fee。
v1でのComputeBudget instructionsはno-op
v1トランザクションはComputeBudget instructionsを拒否しません — 設定においては無視されます。これらは引き続き成功したno-opとして実行され、150コンピュートユニットと64あるinstructionスロットのうちの1つを消費しますが、バジェットには何の影響も与えません。v1トランザクションを構築する際はこれらを取り除き、v1トランザクションを読み取る際はスキャンを止めてください:値はメッセージ設定に格納されています。
設定フィールドは明示的に指定する必要があります
送信者にとって最も重要な動作上の変更点:legacyおよびv0とは異なり、v1トランザクションはコンピュートユニット制限とロード済みアカウントデータサイズ制限を明示的に設定しなければならず、設定しない場合トランザクションは失敗します。
| 未設定フィールド | legacy / v0 | v1 | 省略時の症状 |
|---|---|---|---|
| コンピュートユニット制限 | instruction当たり200k、最大1.4M | 0 CU | 即時失敗、予算不足 |
| ロード済みアカウントデータサイズ | 64 MiB | 0バイト | 最初にロードされたアカウントでMaxLoadedAccountsDataSizeExceeded |
| 優先手数料 | 0 | 0 | — |
| ヒープサイズ | 32 KiB | 32 KiB | — |
推奨されるアプローチは、両方の制限を最大値にして一度シミュレーションを実行し、返されたunitsConsumedとloadedAccountsDataSizeを設定に書き戻すことです。データサイズは余裕を持たせるために次の32 KiBページに切り上げてください(ブロックコストモデルは32 KiBページ単位で課金されるため、次のページ境界未満の余裕はコストがかかりません)。
v1への移行準備
v1はトランザクションの読み取りも変更します。送信だけではありません。v1が有効化されると、オプトインせずにgetTransactionやgetBlockを呼び出すクライアントはv1トランザクションで失敗し始めます:
getTransactionおよびgetBlockにmaxSupportedTransactionVersion: 1— 文字列"1"ではなくJSON整数の1— を渡してください。0を渡すとパラメーターを省略した場合と同様にv1トランザクションで失敗します。v0のロールアウト時にコードベースを更新していても、値を変更する必要があります。getTransactionはv1トランザクションに対してエラー-32015で失敗し、getBlockレスポンスに1つでもv1トランザクションがあると、同じエラーでレスポンス全体が失敗します — 部分的な結果は返されません。blockSubscribeはblock: nullを返して進行を停止するため、これを空のブロックとして読み取るコンシューマーは最初のv1 slot以降から静かに遅延します。getSignaturesForAddressはトランザクション本体を検査しないため、v1のシグネチャは通常通り一覧表示されます。- オプトイン済みのレスポンスには、v1トランザクションのメッセージに
transactionConfigオブジェクトが含まれます(legacyおよびv0では完全に省略されます)。ComputeBudget instructionsをスキャンして優先手数料やコンピュート制限を導出するパイプラインは、すべてのv1トランザクションに対してゼロを静かに報告します。 - クライアント側でトランザクションをデコードする場合、および1,232バイトを超えるトランザクションの
sendTransaction/simulateTransactionにはencoding: 'base64'を使用してください — base58エンコーディングは旧来のサイズ上限のままです。 - クライアントライブラリのサポートには最新バージョンが必要です:
@solana/kit8.0以降、Agave 4.2.x世代のRustクレート、またはweb3.js v3。web3.js v1は1.99.0以降でv1を読み取れますが、構築または送信はできません。
クライアントライブラリのサポート、ストリーミング(Geyser/gRPC)のバージョン検出、シミュレーション動作を含む完全な移行ガイドについては、トランザクションフォーマットv1アップグレードページを参照してください。
Is this page helpful?