見出し画像

【Solanaコアコンセプト②】 トランザクションと命令(インストラクション)

この記事は、こちらの公式を翻訳・編集したものです。

1 はじめに

1 トランザクションと命令(Instruction)

Solanaでは、ネットワークとの相互やりとりにトランザクションを送信します。

トランザクションには一つまたは複数の命令が含まれ、各命令処理される特定の操作を表します。

命令の実行ロジックはSolanaネットワークにデプロイされたプログラムに保存されています。

各プログラムは自身の命令セットを保存します。

2 トランザクションの性質

以下にトランザクションの実行についての重要な詳細を示します。

①実行順序
トランザクションに複数の命令が含まれている場合、命令は追加された順序で処理されます。

②原子性
トランザクションは原子性を持ち、すべての命令が正常に処理されるか全体として失敗します。
トランザクション内のいずれかの命令が失敗すると、命令は一切実行されません。

補足
この原始性は非常に重要です。これにより、特定の処理だけ実行されてしまうという心配は必要なくなります。

簡単に言うと、トランザクション一つまたは複数の命令を処理するためのリクエストと考えることができます。

トランザクションを封筒と考えるとイメージが湧きやすいです。

各命令封筒の中に入れる書類です。

その書類を処理するために封筒を郵送するように、ネットワーク上で命令を処理するためにトランザクションを送信します。

2 主要ポイント

1 仕組み

Solanaのトランザクションは、ネットワーク上の様々なプログラムと対話する命令で構成されています。

各命令は特定の操作を表します。

2 命令の構成

各命令は、①命令を実行するプログラム②命令に必要なアカウント、および③命令の実行に必要なデータを指定します。

3 性質① 実行順序

トランザクション内の命令はリストされた順序で処理されます。

4 性質② 原子性

トランザクションは原子性を持ちます。

すべての命令が正常に処理されるか、トランザクション全体が失敗します。

5 最大サイズ

トランザクションの最大サイズ1,232バイトです。

3 基本的な例

1 図による理解

以下は、送信者から受信者へSOLを転送する単一の命令を含むトランザクションを表す図です。

Solanaの個々の「ウォレット」システムプログラムによって所有されるアカウントです。

補足
上の詳細はこちらの記事にバッチリ書いてあります。

Solanaアカウントモデルの一環として、アカウントを所有するプログラムのみアカウント上のデータを変更することが許可されています。

したがって、「ウォレット」アカウントからSOLを転送するには、システムプログラム転送命令を呼び出すためのトランザクションを送信する必要があります。

送信者のアカウントは、トランザクションでの署名者(is_signer)として含まれる必要があり、そのラムポートバランスの減少を承認します。

送信者と受信者のアカウントは変更可能(is_writable)でなければならないため、指令は両方のアカウントのラムポートバランスを変更します。

トランザクションが送信されると、システムプログラムが呼び出されて転送指令を処理します。

その後、システムプログラム送信者と受信者のアカウントのラムポートバランスをそれぞれ更新します。

2 コードでの確認

こちらは、SystemProgram.transferメソッドを使用してSOL転送指令を構築する方法のSolana Playgroundの例です。

https://beta.solpg.io/656a0ea7fb53fa325bfd0c3e

スクリプトを実行し、コンソールにログされたトランザクションの詳細を確認してください。

以下のセクションでは、内部で何が起こっているのかの詳細を順を追って説明します。

4 トランザクション

1 トランザクションの構成要素

Solanaのトランザクションは以下から成り立っています。

① 署名
トランザクションに含まれる署名の配列

② メッセージ
原子的に処理される命令のリスト

2 トランザクションメッセージの構造

トランザクションメッセージの構造は以下の通りです。

① メッセージヘッダー
署名者
読み取り専用アカウントの数を指定します。

② アカウントアドレス
トランザクション上の命令に必要なアカウントアドレスの配列

③ 最近のブロックハッシュ
トランザクションのタイムスタンプとして機能します。

④ 命令
実行される命令の配列。

3 トランザクションサイズ

3ー1 ネットワーク上のサイズについて

Solanaネットワークは、IPv6のMTUサイズ制約に一致する1280バイト最大伝送単位(MTU)サイズを採用しています。

補足
MTU(Maximum Transmission Unit)とは、ネットワークで一回に送信できる最大のデータサイズのことです。

https://www.rtpro.yamaha.co.jp/RT/docs/ipsec/pdf/various-mtu.pdf

これは、UDPを通じてクラスタ情報の高速かつ信頼性のある伝送を保証するためです。

補足
UDP(User Datagram Protocol)はトランスポート層で動作するプロトコルです。
TCPに比べると信頼性が落ちるものの、高速に転送を行うことができます。

3ー2 トランザクションのサイズについて

IPv6用のヘッダー(40バイト)フラグメントヘッダー(8バイト)を考慮します。

その結果、パケットデータ(シリアライズされたトランザクションなど)には1232バイトが利用可能です。

補足
1280バイトから、40バイト8バイトを引いたのですね。

これは、Solanaトランザクションの合計サイズが1232バイトに制限されていることを意味します。

署名とメッセージの組み合わせは、この制限を超えることはできません。

3ー3 署名とメッセージ

① 署名
各署名は64バイトを必要とします。

署名の数はトランザクションの要件によって異なる場合があります。

②メッセージ
メッセージには命令アカウント追加のメタデータが含まれ、各アカウントには32バイトが必要です。

アカウントとメタデータの合計サイズは、トランザクションに含まれる命令によって異なる場合があります。

4 メッセージヘッダー

メッセージヘッダーは、トランザクションのアカウントアドレス配列に含まれるアカウントの権限を指定します。

これは3バイトで構成され、それぞれがu8整数を含んでおり、以下を指定します。

①トランザクションに必要な署名の数

②署名が必要な読み取り専用アカウントアドレスの数

③署名が不要な読み取り専用アカウントアドレスの数

5 コンパクト配列形式

トランザクションメッセージのコンテキストにおけるコンパクト配列は、以下の形式でシリアライズされた配列を指します。

5ー1 配列の長さ

配列の長さは、『コンパクト-u16』でエンコードされます。

5ー2 個々のアイテム

エンコードされた長さの後に、配列の個々のアイテムが順にリストされます。

このエンコーディング方法は、トランザクションメッセージ内のアカウントアドレス命令の配列長さを指定するために使用されます。

補足
この部分がポイントだと思います。
これはアカウントアドレス命令(Instruction)長さを指定するために使われています。

6 アカウントアドレスの配列

トランザクションメッセージには、トランザクション内の命令に必要なすべてのアカウントアドレスを含む配列が含まれます。

この配列は、アカウントアドレスの数を『コンパクト-u16』でエンコードすることから始まります。

その後、アカウントの権限によって順序付けられたアドレスが続きます。

メッセージヘッダーのメタデータは、各セクションのアカウント数を決定するために使用されます。

① 書き込み可能署名者のアカウント

② 読み取り専用署名者のアカウント

③ 書き込み可能署名者でないアカウント

④ 読み取り専用署名者でないアカウント

7 最近のブロックハッシュ

すべてのトランザクションには、トランザクションのタイムスタンプとして機能する最近のブロックハッシュが含まれます。

ブロックハッシュは、重複を防ぎ古いトランザクションを排除するために使用されます。

トランザクションのブロックハッシュの最大有効期間は150ブロック(ブロック時間が400msと仮定して約1分)です。

トランザクションのブロックハッシュが最新のブロックハッシュより150ブロック古い場合、それは期限切れと見なされます。

これは、特定の時間枠内に処理されなかったトランザクションは決して実行されないことを意味します。

補足
この部分、すごく大事です。
他のチェーンでは、トランザクションが成功も失敗もせずに漂ってしまうケースが起こります。
そのため、多くの場合、dappsでその対応が必要です。
開発者側からすると「きちんと処理が失敗してくれる」のは大きなプラスです。

最新のブロックハッシュと、そのブロックハッシュが有効である最後のブロックの高さを取得するには、getLatestBlockhash RPCメソッドを使用できます。

https://beta.solpg.io/661a06e1cffcf4b13384d046

8 命令の配列

トランザクションメッセージには、処理をリクエストするすべての命令の配列が含まれます。

トランザクションメッセージ内の命令は、『CompiledInstruction』と言う形式です。

アカウントアドレスの配列と同様に、このコンパクト配列は命令の数『コンパクト-u16』でエンコードすることから始まります。

その後に命令の配列が続きます。

配列内の各命令は、次の情報を指定します。

8ー1 プログラムID

命令を処理するオンチェーンプログラム識別します。

これは、アカウントアドレス配列内のアカウントアドレスを指すu8インデックスとして表されます。

8ー2 アカウントアドレスインデックスのコンパクト配列

命令に必要な各アカウントに対してアカウントアドレス配列を指すu8インデックスの配列です。

8ー3 不透明なu8データのコンパクト配列

呼び出されるプログラムに固有のu8バイト配列

このデータは、プログラム上で呼び出される命令と、その命令が必要とする追加データ(関数引数など)を指定します。

8ー4 コンパクト配列形式の重要性について

このように、Solanaのトランザクションメッセージでは、コンパクト配列形式を使用して効率的にデータをエンコードします。

そのため、ネットワークを介して迅速かつ確実にトランザクションを送信します。

各命令は、特定のプログラムそのプログラムによる処理を必要とするアカウントを正確に指定するため、この情報は非常に重要です。

9 トランザクション構造の例

以下は、単一のSOL転送指令を含むトランザクションの構造の例です。

ヘッダー、アカウントキー、ブロックハッシュ、命令に加えて、トランザクションの署名が含まれているメッセージの詳細を示しています。

9ー1 ヘッダー

アカウントキーの配列での読み書き署名者の権限を指定するために使用されるデータが含まれます。

9ー2 アカウントキー配列

トランザクション上のすべての指令に対するアカウントアドレスを含む配列です。

9ー3 最近のブロックハッシュ

トランザクションが作成された時にトランザクションに含まれるブロックハッシュです。

9ー4 命令

トランザクション上のすべての命令を含む配列です。

各命令内のaccountsおよびprogramIdIndexは、インデックスによってアカウントキーズ配列を参照します。

9ー5 署名

トランザクション上の命令によって署名者として必要とされるすべてのアカウントの署名を含む配列です。

署名は、アカウントに対応する秘密鍵を使用してトランザクションメッセージに署名することで生成されます。

この例は、Solanaプラットフォーム上でのトランザクションの動作とその構成要素を理解するのに役立ちます。

それぞれの部分がどのように相互作用し、トランザクションが効果的に処理されるかを示しています。

10 命令

1 概要

命令は、チェーン上で特定のアクションを処理するためのリクエストです。

プログラム内の最小の連続した実行ロジック単位です。

トランザクションに追加する命令を構築する際には、各命令に以下の情報が含まれている必要があります。

1ー1 プログラムアドレス

呼び出されるプログラムを指定します。

1ー2 アカウント

AccountMeta構造体を使用して、命令が読み取りまたは書き込みを行うすべてのアカウント(他のプログラムを含む)をリストします。

1ー3 命令データ

プログラム上で呼び出される命令ハンドラを指定するバイト配列、および命令ハンドラに必要な追加データ(関数引数)

2 AccountMeta

命令に必要な各アカウントについて、以下の情報が指定されている必要があります。

① pubkey
アカウントのオンチェーンアドレス

② is_signer
トランザクションで署名者として必要かどうかを指定します

③ is_writable: アカウントデータが変更されるかどうかを指定します

この情報は「AccountMeta」として参照されます。

3 書き込み可能性について

命令に必要なすべてのアカウントを指定し、それぞれのアカウントが書き込み可能かどうかを明記することで、トランザクションは並列処理されます。

例えば、同じ状態に書き込むアカウントを含まない二つのトランザクションは同時に実行されることができます。

補足
ここは大事なところだと思います。
書き込まないのであれば、状態が変わらないので、複数のトランザクションを同時に実行しても支障がありません。

4 命令構造の例

以下は、アカウントキー、プログラムID、および命令に必要なデータの詳細を含むSOL転送命令の構造の例です。

4ー1 keys

命令に必要な各アカウントのAccountMetaを含みます。

4ー2 programId

命令が呼び出される実行ロジックを含むプログラムのアドレス。

4ー3 data

バイトのバッファとしての命令データ

11 拡張例

プログラム命令を構築するための詳細は、通常クライアントライブラリによって抽象化されます。

利用できない場合は、常に命令を手動で構築することができます。

1 手動でのSOL転送

以下は、SOL転送命令を手動で構築する方法のSolana Playgroundの例です。

2 ライブラリを使ったSOL転送

内部的には、SystemProgram.transferメソッドを使用するシンプルな例は、上記のより詳細な例と機能的に等価です。

SystemProgram.transferメソッドは、命令に必要な各アカウントの命令データバッファAccountMetaを作成する詳細を単純に抽象化します。


サポートをしていただけたらすごく嬉しいです😄 いただけたサポートを励みに、これからもコツコツ頑張っていきます😊