新着:HeliusがLight Protocolを買収
Solanaトランザクションのバージョニング:レガシー、v0、v1
ブログ/基礎

Solanaトランザクションのバージョニング:レガシー、v0、v1

リサーチャーXのLostin
読了時間:3分

Solanaトランザクション形式の略史

Solanaには、レガシー、v0、v1という3つのトランザクション形式があります。大まかな目的はすべて同じです。つまり、バリデータがオンチェーンプログラムに対して実行する署名、アカウント、命令をまとめます。

時代とともに変化したのはワイヤ形式、つまり、これらの情報をどのようにシリアライズし、ネットワーク経由で送信するかです。

各形式は、Solanaアプリケーションが複雑になるにつれて明らかになった制約に対処するために導入されました。同時に、古い形式も引き続き動作できるよう、後方互換性が維持されています。

3つの形式はすべてネットワーク上で共存しており、開発者は構築するトランザクションに最適な形式を使用できます。

レガシートランザクション

元の形式は、トランザクションのバージョニングより前から存在し、バージョン識別子を含まないため、レガシーと呼ばれています。

最大サイズが1,232バイトなのは、Solanaの当初のネットワーク設計に由来します。トランザクションは、IPv6の最小MTUである1,280バイト以内に収まるよう設計され、ネットワークのオーバーヘッドを差し引くと1,232バイトが残りました。

これは小さなトランザクションには適していましたが、アプリケーションの成長に伴い、容量の制約が厳しくなりました。トランザクションに直接含まれる各アカウントアドレスは32バイト、各Ed25519署名はさらに64バイトを占有します。

そのため実際には、アカウント数の多いトランザクションでは、ランタイムのアカウントロック上限に達するよりもはるかに前に、バイト数の上限を使い切る可能性がありました。

トランザクションv0

この制約を緩和する最初の試みがトランザクションv0です。2022年10月にmainnet-betaで稼働を開始しました。v0では1,232バイトの上限を引き上げるのではなく、**Address Lookup Tables(ALT)**が導入されました。

ALTはアカウントアドレスをオンチェーンに保存します。これによりv0トランザクションは、各32バイトの公開鍵を繰り返す代わりに、1バイトのインデックスで参照できます。そのため、既存のパケットサイズ制約を維持しながら、ランタイムの64アカウント上限まで利用できます。

v0はレガシー形式を段階的に拡張したものです。基本的なメッセージと命令のエンコーディングを維持しながら、0x80バージョンプレフィックスとアドレスルックアップセクションを追加しました。

バージョニングの導入により、新しいトランザクション形式がレガシートランザクションを置き換えるのではなく、共存できるフレームワークも確立されました。

ALTは差し迫ったアカウントサイズの問題を解決しましたが、新たな複雑さももたらしました。アプリケーションはオンチェーンでルックアップテーブルを作成・維持する必要があり、バリデータは実行前にそのエントリを解決しなければなりません。また、RPCサービスとインデクサーは、完全なトランザクションを再構築するために解決済みアドレスを必要とします。

生のトランザクションが参照するルックアップテーブルが後に閉じられた場合、過去のデコードもはるかに難しくなります。したがってv0はアカウントリストを効果的に圧縮しましたが、その代わり、トランザクションのワイヤ表現に外部のオンチェーン依存関係を持ち込みました。

ALTはSolana開発者に広く採用されました。v1トランザクションの導入直前に行われた調査では、v0トランザクションの約62%が少なくとも1つのALTを参照していることが分かりました。

優先手数料の後付け

優先手数料も2022年にSolanaへ導入されました。v0トランザクションがメインネットで有効になる前ですが、その時点ですでにv0形式自体は設計済みでした。いずれかのトランザクション形式を再設計する代わりに、Solanaは既存のCompute Budget Programを通じて手数料設定を追加しました。トランザクションにSetComputeUnitLimitやSetComputeUnitPriceなどの命令を含めることで、ユーザーはコンピュート予算を要求し、スケジューリングの優先度を高める追加手数料を設定できました。形式ごとに個別の手数料メカニズムを用意せず、レガシーとv0の両方で同様に機能しました。

これは実用的で後方互換性のある解決策でしたが、特に洗練されたものではありませんでした。優先手数料とコンピュート上限は本来トランザクションレベルのメタデータですが、レガシーとv0には専用フィールドがありません。そのため、プログラム呼び出しとして命令ストリームに後付けされました。つまり、トランザクションからCompute Budget Programを参照する必要があり、設定が命令のバイト数を消費し、バリデータはトランザクションの取り込み時にそれらの命令を調べて手数料とリソース設定を取得しなければなりません。

したがってv0は、Solanaのトランザクション形式を全面的に再設計したものではありません。主な目的はAddress Lookup Tablesによってアカウントアドレスのボトルネックを解消しつつ、レガシーメッセージの構造をほぼ維持することでした。Compute Budget命令によって両形式で優先手数料をサポートする互換性のある方法がすでに提供されていたため、v0の対象範囲をさらに広げる理由はほとんどありませんでした。

トランザクションサイズの拡大

その後、Solanaはトランザクションの取り込みをUDPからQUICへ移行しました。QUICのストリームは、単一のMTUサイズのペイロードに制約されません。そのため、当初の1,232バイトという上限は、もはや転送上の要件ではなくなりました。新しいv1形式を導入するため、2025年に2つのSIMDが提案されました。SIMD-0296はv1トランザクションの最大サイズを従来の約3.3倍にあたる4,096バイトへ引き上げ、SIMD-0385は追加容量とより効率的なバリデータ取り込みを前提に、まったく新しいワイヤ形式を定義しました。

追加された容量により、v1ではALTを完全に排除できます。32バイトのアドレスを64アカウント分格納しても最大2,048バイトで済むため、完全なアカウントリストをそのままインラインで保持できます。

Solana Foundationの分析によると、既存のv0トランザクションの大半をアップグレードする際、すべてのアドレスをインライン化する影響は限定的です。50%は増加量が420バイト未満、90%は1,400バイト未満です。ALTを多用するトランザクションでも、v1の4,096バイト上限には十分な余裕が残ります。

さらに重要なのは、v1が単に大きなv0トランザクションではないことです。ワイヤ形式が大幅に再編されています。署名は末尾へ移動し、リソース設定はCompute Budget命令からトランザクション設定セクションへ移り、可変長の命令ペイロードは固定幅のヘッダーから分離されました。これらの変更により、バリデータによるトランザクションの解析と優先順位付けが低コストかつ容易になります。

4,096バイトという大きな上限により、アドレス圧縮だけでは対応できなかったワークロードも扱えます。ゼロ知識証明、ネストされたマルチシグ、切り詰められていないWinternitz署名、BLS署名、その他の暗号処理では、数百から数千バイトの命令データが必要になる場合があります。たとえば、Token-2022 Confidential Balancesは、複数のトランザクションに分割せず、単一のアトミックなv1トランザクションとして構築できるようになりました。

特筆すべき点として、初期のv1形式ではSolanaのコンピュート、署名、命令数、64アカウントの上限は引き上げられません。主にサイズのボトルネックを解消し、トランザクションのワイヤ上の表現方法を再設計しています。

Anzaが提案したSIMD-0596では、v1のアカウントロック上限を64から96へ引き上げます。署名者、プログラムID、書き込み可能アカウント、読み取り専用アカウントを含め、トランザクションが参照するすべてのアカウントが対象です。本稿執筆時点で、この提案は審査中です。レガシーとv0トランザクションの上限は引き続き64です。

形式の仕様と比較

上限レガシーv0v1
最大サイズ1,232バイト1,232バイト4,096バイト
アカウントアドレス約32、サイズに依存64、ルックアップテーブル経由64、インライン
Address Lookup Tables非対応対応非対応
重複アドレス許可許可拒否
プレフィックスなし0x800x81
優先手数料SetComputeUnitPrice命令、CUあたりのマイクロlamportsSetComputeUnitPrice命令、CUあたりのマイクロlamportsconfig.priorityFeeLamports、合計lamports
コンピュートユニット上限SetComputeUnitLimit命令SetComputeUnitLimit命令config.computeUnitLimit
ヒープサイズRequestHeapFrame命令RequestHeapFrame命令config.heapSize
ロード済みアカウントの上限SetLoadedAccountsDataSizeLimit命令SetLoadedAccountsDataSizeLimit命令config.loadedAccountsDataSizeLimit

レガシートランザクションの仕様

シリアライズされたレガシートランザクションは、compact-u16形式の署名数で始まり、その後に64バイトのEd25519署名が続きます。

次にメッセージが続きます。メッセージには3バイトのヘッダー、compact-lengthプレフィックス付きアカウントリスト、32バイトの直近のblockhash、compact-lengthプレフィックス付きのコンパイル済み命令配列が含まれます。

レガシートランザクションにはバージョンプレフィックスがありません。

コード
NumSignatures (compact-u16)

Signatures [[u8; 64]]
  -- Length = NumSignatures

MessageHeader (u8, u8, u8)
  -- (NumRequiredSignatures,
      NumReadonlySignedAccounts,
      NumReadonlyUnsignedAccounts)

NumAccountKeys (compact-u16)

AccountKeys [[u8; 32]]
  -- Length = NumAccountKeys

RecentBlockhash [u8; 32]

NumInstructions (compact-u16)

Instructions [CompiledInstruction]
  -- Length = NumInstructions

CompiledInstruction:
  ProgramIdIndex (u8)

  NumInstructionAccounts (compact-u16)

  InstructionAccountIndexes [u8]
    -- Length = NumInstructionAccounts

  InstructionDataLength (compact-u16)

  InstructionData [u8]
    -- Length = InstructionDataLength

重要な特徴は、各CompiledInstructionが、次の命令が始まる前に完全な形でシリアライズされることです。

アカウントインデックスとデータの配列は可変長で、そのサイズはcompact-u16プレフィックスで定義されます。

トランザクションv0の仕様

トランザクションv0は、レガシーのワイヤ形式をほぼすべて維持しています。トランザクションは引き続き署名数と署名で始まりますが、メッセージはバージョンプレフィックス0x80で始まります。

その後、メッセージはレガシーと同じ3バイトのヘッダー、静的アカウントリスト、blockhash、コンパイル済み命令形式を使用し、最後にAddress Lookup Tableセクションを追加します。

これにより、v0トランザクションは1,232バイトのトランザクション上限を維持しながら、ALTを通じて追加のアカウントをロードできます。

コード
NumSignatures (compact-u16)

Signatures [[u8; 64]]
  -- Length = NumSignatures

VersionByte (u8)
  -- 0x80

MessageHeader (u8, u8, u8)

NumStaticAccountKeys (compact-u16)

StaticAccountKeys [[u8; 32]]
  -- Length = NumStaticAccountKeys

RecentBlockhash [u8; 32]

NumInstructions (compact-u16)

Instructions [CompiledInstruction]
  -- Same encoding as legacy

NumAddressTableLookups (compact-u16)

AddressTableLookups [MessageAddressTableLookup]
  -- Length = NumAddressTableLookups

MessageAddressTableLookup:
  AccountKey [u8; 32]
    -- Address of the lookup table account

  NumWritableIndexes (compact-u16)

  WritableIndexes [u8]
    -- Indices into the lookup table

  NumReadonlyIndexes (compact-u16)

  ReadonlyIndexes [u8]
    -- Indices into the lookup table

静的アカウントキーとロード済みアドレスの違いが重要です。メッセージへ直接シリアライズされるのは静的キーだけです。ALT経由で参照されるアドレスは実行時に解決され、有効なアカウントリストへ追加されます。

ALTを使用するv0トランザクションでは、バリデータはまず、参照される各ルックアップテーブルをBankとAccountsDBからロードし、そのアカウントが有効なAddress Lookup Tableであることを検証して状態をデシリアライズし、要求されたインデックスを完全な32バイトアドレスへ解決します。その後、それらのアドレスをトランザクションの静的アカウントキーと統合します。この解決処理が完了して初めて、バリデータは読み取り/書き込みロックと他のトランザクションとの競合を判断するために必要な完全なアカウントリストを取得できます。

トランザクションv1の仕様

シリアライズされたv1トランザクションは、バージョンバイト0x81で始まります。その後に、レガシー形式の3バイトのメッセージヘッダー、32ビットのトランザクション設定マスク、32バイトの有効期間指定子、命令数とアドレス数、32バイトアドレスの完全な配列、設定値、固定サイズの命令ヘッダー、連続した命令ペイロード、最後に署名が続きます。

コード
VersionByte (u8)
  -- 0x81

LegacyHeader (u8, u8, u8)

TransactionConfigMask (u32)
  -- Bitmask describing which configuration values are present

LifetimeSpecifier [u8; 32]

NumInstructions (u8)

NumAddresses (u8)

Addresses [[u8; 32]]
  -- Length = NumAddresses

ConfigValues [[u8; 4]]
  -- Length = popcount(TransactionConfigMask)
  -- Multi-word values, such as priority fee, consume multiple entries

InstructionHeaders [(u8, u8, u16)]
  -- Length = NumInstructions
  -- (ProgramAccountIndex,
      NumInstructionAccounts,
      NumInstructionDataBytes)

InstructionPayloads [InstructionPayload]
  -- Length = NumInstructions

InstructionPayload:
  InstructionAccountIndexes [u8]
    -- Length = NumInstructionAccounts

  InstructionData [u8]
    -- Length = NumInstructionDataBytes

Signatures [[u8; 64]]
  -- Length = LegacyHeader.NumRequiredSignatures

そのため、v1はいくつかの根本的な点で以前の形式と異なります。バージョン識別子はバイト0に置かれ、署名は末尾へ移動して固有の長さプレフィックスが不要になります。アカウント数と命令数は固定幅のu8値になり、リソース設定はトランザクションへ直接エンコードされます。また、固定サイズの命令ヘッダーが可変長のペイロードから分離されます。レガシーやv0とは異なり、v1はAddress Lookup Tablesを使用しません。

トランザクションv1は、Compute Budgetプログラムの設定命令をトランザクションヘッダー内のフィールドに置き換えます。初期のTransactionConfigMaskでは、トランザクションの以下の項目を宣言できます。

  • lamports単位の優先手数料の合計
  • コンピュートユニット上限
  • ロード済みアカウントデータのサイズ上限
  • 要求するヒープサイズ

セットされた各ビットは、対応する4バイトの設定値を示します。64ビットの優先手数料は、マスクの2つの位置を占有します。このマスクは、将来のトランザクションバージョンで拡張できるよう設計されています。

トランザクションの例

Solanaの各トランザクション形式を比較するため、最も単純で実用的な例として、あるアドレスから別のアドレスへのSOL送金を取り上げます。

このサンプルトランザクションには、単一の署名者、つまり送信者がいます。System Programを1回呼び出し、受信者へ1,000 lamportsを送金します。送信者、受信者、System Programという3つのアドレスを参照します。

レガシーおよびv0トランザクション

レガシーとv0トランザクションは、最上位のレイアウトがほぼ同じです。どちらも署名数で始まり、その直後に署名自体が続きます。その後にトランザクションのメッセージが続きます。単一署名者による送金では、最初のバイトは01で、署名が1つであることを示します。その後に送信者の64バイトEd25519署名が続きます。

メッセージには、3バイトのヘッダー、アカウントアドレス、直近のblockhash(有効期間指定子)、コンパイル済み命令が含まれます。

送信者はアドレス0、受信者はアドレス1、System Programはアドレス2です。送金命令はインデックス2のプログラムを参照し、アカウントインデックス00 01をプログラムへ渡します。

レガシー形式とv0形式の違いはわずかです。

  • レガシーメッセージは3バイトのメッセージヘッダーから直接始まります。一方、v0メッセージはバージョンバイト0x80で始まり、その後にヘッダーが続きます。
  • v0では、命令の後にAddress Lookup Tableセクションも追加されます。この単純な例ではルックアップテーブルを使用しないため、このセクションはルックアップ数がゼロであることを示す1つの00バイトで構成されます。

このSOL送金の例は、レガシートランザクションでは215バイト、v0では217バイトになります。v0では、バージョンプレフィックスに1バイト、空のルックアップテーブル配列に1バイトが追加されます。トランザクションの残りの部分は同一です。

送信者が署名するのはメッセージだけです。レガシーの例では、署名の対象はバイト65〜214です。v0ではバイト65〜216が対象となり、0x80メッセージバージョンプレフィックスから始まります。

以下の表は、v0トランザクション形式を使用した、2つのアドレス間のSOL送金例のバイトレイアウトを詳しく示しています。

オフセットサイズフィールド値の例説明
01 B署名数01署名1つ、compact-u16
1–6464 B署名0<Ed25519 signature>送信者の署名
651 Bバージョンプレフィックス80バージョン付きメッセージ、バージョン0
66–683 Bメッセージヘッダー01 00 01必須署名者1、読み取り専用の署名済みアカウント0、読み取り専用の未署名アカウント1
691 B静的アカウント数03インラインアカウント3つ
70–10132 Bアカウント0<sender pubkey>送信者/手数料支払者、書き込み可能な署名者
102–13332 Bアカウント1<recipient pubkey>受信者、書き込み可能、未署名
134–16532 Bアカウント211111111111111111111111111111111System Program、読み取り専用、未署名
166–19732 B直近のblockhash<recent blockhash>トランザクションの有効期間
1981 B命令数01命令1つ
1991 BプログラムIDインデックス02System Program
2001 B命令アカウント数02命令アカウント2つ
201–2022 Bアカウントインデックス00 01送信者、受信者
2031 Bデータ長0C12バイト
204–2074 B送金ディスクリミネータ02 00 00 00SystemInstruction::Transfer
208–2158 BLamportsE8 03 00 00 00 00 00 001,000 lamports
2161 Bアドレステーブルのルックアップ数00ALTルックアップなし
合計217 B

リソース設定

レガシーとv0トランザクションでは、リソース設定はCompute Budget Programへの命令として表現されます。一般的なものにはSetComputeUnitLimitとSetComputeUnitPriceがあります。必要に応じてSetLoadedAccountsDataSizeLimitとRequestHeapFrameも使用されます。

最小構成のトランザクション例には、これらの命令は含まれていません。代わりに、ランタイムがデフォルト値を提供します。

コンピュートユニット価格を指定しない場合、優先手数料はありません。ロード済みアカウントデータの上限には、ランタイムの最大値がデフォルトで適用されます。

トランザクションの取り込み時に、バリデータが命令リストを走査してCompute Budget命令を探す必要があるという非効率性は、更新されたv1形式を導入する動機の1つです。v1では、この情報が代わりにTransactionConfigMaskとConfigValuesへ移されます。

そもそも、トランザクションレベルの設定を命令として表現すること自体にも、一定のオーバーヘッドがあります。レガシーとv0には、要求するコンピュート量や優先手数料の専用フィールドがなかったため、これらの設定はCompute Budget Programを通じて後付けされました。トランザクションからそのプログラムIDを参照する必要があり、各設定が命令リストの容量を消費します。また、プログラム呼び出し自体はアプリケーション処理を行わないにもかかわらず、コンピュートを消費します。

v1では代わりに、これらの値をワイヤ形式内のネイティブな領域へ配置します。余分な命令のオーバーヘッドを回避し、リソース設定を低コストにするとともに、バリデータが容易に確認できるようにします。

トランザクションv1

v1トランザクションは、署名数と署名で始まるのではなく、バージョンバイト0x81から直接始まります。

次に3バイトのヘッダーが続き、その後に新しい4バイトのTransactionConfigMask、有効期間指定子(通常はblockhashですが、nonceの場合もあります)、命令数とアドレス数、アカウントアドレス、設定値、命令、最後に署名が続きます。

オフセットサイズフィールド値の例説明
01 Bバージョンバイト81v1トランザクション
1–33 Bレガシーヘッダー01 00 01必須署名1、読み取り専用の署名済みアカウント0、読み取り専用の未署名アカウント1
4–74 Bトランザクション設定マスク0C 00 00 000x0000000C:ビット2と3がセットされ、2つの4バイト設定値を指定
8–3932 B有効期間指定子<recent blockhash>直近のblockhash(またはnonce)
401 B命令数01System Program命令1つ
411 Bアドレス数03送信者、受信者、System Program
42–7332 Bアドレス0<sender pubkey>送信者、手数料支払者、書き込み可能な署名者
74–10532 Bアドレス1<recipient pubkey>受信者、書き込み可能な未署名アカウント
106–13732 Bアドレス211111111111111111111111111111111System Program、読み取り専用、未署名
138–1414 B設定値:コンピュートユニット上限10 27 00 00リトルエンディアンu32で10,000 CU(マスクのビット2に対応)
142–1454 B設定値:ロード済みアカウントデータのサイズ上限00 00 01 00リトルエンディアンu32で65,536バイト/64 KiB(マスクのビット3に対応)
146–1494 B命令:ヘッダー02 02 0C 00プログラムインデックス2、アカウント2つ、データ12バイト
150–1512 B命令:アカウントインデックス00 01アドレス0=送信者、アドレス1=受信者
152–1554 B命令:送金ディスクリミネータ02 00 00 00SystemInstruction::Transfer
156–1638 B命令:Lamports例:E8 03 00 00 00 00 00 00リトルエンディアンu64で1,000 lamports
164–22764 B署名0<Ed25519 signature>バイト0〜155に対する送信者の署名
合計228 B

署名検証

署名検証は、トランザクションがバリデータへ到達した際に最初に実行される処理の1つです。無効な署名を持つトランザクションは、スケジューラへ到達する前にフィルタリングして破棄する必要があります。可変長メッセージの後ろに署名を追加するとアクセスが難しくなるように見えますが、実際にはそうではありません。

高コストなEd25519検証を実行する前に、バリデータが必要とするのは、トランザクションの各部分がどこで始まり、どこで終わるかを特定することだけです。v1形式は、この処理を低コストかつ決定論的に行えるよう設計されています。

最初のバイト0x81により、トランザクションがv1であることを即座に識別できます。後続のヘッダーにはnum_required_signaturesが含まれるため、バリデータは64バイトの署名がいくつあるべきかを最初から把握できます。

その後、AgaveのTransactionViewパーサーは、オフセットを記録しながらトランザクション内を前方へ走査します。固定サイズのフィールドを読み取り、NumAddressesを使用してアドレス配列をスキップします。

設定マスクを使用して設定セクションのサイズを判定し、各4バイトの命令ヘッダーを読み取って、対応する命令ペイロードのサイズを把握します。

パーサーが最後の命令ペイロードを通過すると、現在位置が署名オフセットとして記録されます。その位置以降では、Agaveは正確にnum_required_signatures × 64バイトを想定します。

実装では、署名数とパケット内の最初の署名のバイトオフセットを含むSignatureFrameを保存します。トランザクションの署名対象メッセージは、v1メッセージの先頭から記録された署名オフセットまでのバイト範囲です。そのためバリデータは、トランザクションを実行したりアカウントをロードしたりせずに、高コストなEd25519検証を実行できます。

v1では、署名配列の後に末尾バイトを置くことも禁止されています。パーサーが署名へ到達すると、残りのバイト数は署名数によって一意に決まります。SIMD-0385では、必須署名者ごとに正確に1つの64バイト署名があり、最後の署名以降にデータが存在しないことを求めています。

改善された命令解析

トランザクションv1のレイアウトでは、命令をより簡単に解析できます。レガシーとv0トランザクションで後方の命令を特定するには、パーサーは先行する命令を順に走査し、その長さをデコードして、各可変長ペイロードをスキップする必要があります。

v1では、固定幅の命令ヘッダーと可変長のペイロードを分離しています。 各命令はまず、プログラムアカウントインデックス、命令アカウント数、命令データ長を含む4バイトのヘッダーを持ちます。これらのヘッダーは、アカウントインデックスとデータペイロードより前にまとめられます。これにより、すべての命令の簡潔な説明を最初に取得できます。命令セクションのフレーム化とスキップが低コストになり、命令データを解析せずに末尾の署名配列のオフセットを判定しやすくなります。

トランザクション設定マスク

v1形式におけるもう1つの主要な追加要素は、4バイトのTransactionConfigMaskです。

レガシーとv0トランザクションでは、Compute Budget Programへの命令を通じてリソースを設定します。たとえば、トランザクションにはSetComputeUnitLimit、SetComputeUnitPrice、SetLoadedAccountsDataSizeLimit命令が含まれる場合があります。トランザクションのリソース要件や優先度を確認するバリデータは、これらの命令を見つけて解釈する必要があります。トランザクションv1では、この情報を命令ストリームからトランザクション形式自体へ移します。

マスクは32ビットのリトルエンディアンビットフィールドです。セットされた各ビットは、トランザクションの後半にあるConfigValuesセクション内の4バイトワードに対応します。

  • ビット0と1:lamports単位の優先手数料の合計(8バイトのリトルエンディアンu64としてエンコード)
  • ビット2:コンピュートユニット上限(4バイトのu32)
  • ビット3:ロード済みアカウントデータのサイズ上限(4バイトのu32)
  • ビット4:要求するヒープサイズ(4バイトのu32)

注:初期のv1仕様で意味が割り当てられているのはビット0〜4だけです。残りのビットは現在未割り当てで、将来のトランザクションレベルの設定フィールド用に予約されています。

このマスクは、どのフィールドが存在し、何バイトを想定すべきかをパーサーへ伝える簡潔なスキーマとして機能します。たとえば、単純なSOL送金にはコンピュートユニット上限とロード済みアカウントデータのサイズ上限が必要ですが、優先手数料やカスタムヒープサイズは不要です。

これら2つのビットがセットされているため、トランザクションのアドレス配列の後に、正確に2つの4バイト設定値が続きます。この例では、20,000 CUの上限と64 KiBのロード済みアカウントデータ上限を要求します。

マスクは、最初の4バイト設定がビット2(コンピュートユニット上限)に属し、2番目がビット3(ロード済みアカウントデータ上限)に属することをバリデータへ伝えます。

レガシーとv0トランザクションでは、コンピュートユニット上限命令を省略すると、暗黙的なコンピュート予算がトランザクションに適用されます。v1では同じデフォルト値を使用しません。未設定のコンピュートユニット上限はゼロになり、未設定のロード済みアカウントデータのサイズ上限もゼロになります。32 KiBのデフォルト値が維持されるのはヒープだけです。そのため、v1トランザクションは必要なリソースを明示的に要求する必要があります。

これらの設定を明確に定義された設定領域へ移すことで、バリデータは命令を検索せず、トランザクションの取り込み時に必要な情報へ直接アクセスできます。また、Compute Budget Program命令はv1トランザクションを設定しなくなります。含まれている場合は無視され、コンピュートユニットを消費しながらno-op命令として処理されます。

これには、容量面での追加メリットもあります。レガシー/v0トランザクションでCompute Budget命令を追加する場合、通常は32バイトのCompute Budget Programアドレスをアカウントリストに含める必要があり、さらにシリアライズされた命令自体も必要です。

まとめ

Solanaの3つのトランザクション形式は、ネットワークの進化を反映しています。レガシーは当初のモデルを確立し、v0はALTによって拡張することで、1,232バイトの上限内でより多くのアカウントを扱えるようにしました。v1は、より大きなトランザクション、簡素な解析、インラインアドレス、ネイティブなリソース設定を軸に、ワイヤ形式をより根本的に再設計しています。

3つの形式はすべて引き続き有効ですが、それぞれがSolanaの発展における異なる段階を表しています。これらを通じて、ネットワーク上のアプリケーション、手数料市場、バリデータ要件の進化に合わせて、トランザクション形式がどのように適応してきたかが分かります。

関連リソース

Heliusを購読

Solana開発の最新情報や新しい記事の公開通知を受け取れます

拡大画像