FAQ
質問は StackExchangeに投稿してください。
バークレーパケットフィルタ(BPF)
Solanaのオンチェーンプログラムは、 LLVMコンパイラインフラストラクチャを介して 実行可能リンク形式(ELF) にコンパイルされ、 バークレーパケットフィルタ(BPF) バイトコードのバリエーションを含んでいます。
SolanaはLLVMコンパイラインフラストラクチャを使用しているため、プログラムはLLVMのBPFバックエンドをターゲットにできるあらゆるプログラミング言語で記述することができます。
BPFは効率的な 命令セットを提供し、インタープリタ型仮想マシンで実行することも、効率的なジャストインタイムコンパイルされたネイティブ命令として実行することもできます。
メモリマップ
Solana SBFプログラムで使用される仮想アドレスメモリマップは固定されており、以下のようにレイアウトされています
- プログラムコードは0x100000000から開始
- スタックデータは0x200000000から開始
- ヒープデータは0x300000000から開始
- プログラム入力パラメータは0x400000000から開始
上記の仮想アドレスは開始アドレスですが、プログラムはメモリマップのサブセットにのみアクセスできます。プログラムがアクセス権を与えられていない仮想アドレスの読み書きを試みると、パニックが発生し、
AccessViolation
エラーが返され、違反を試みたアドレスとサイズが含まれます。
InvalidAccountData
このプログラムエラーはさまざまな理由で発生する可能性があります。通常、プログラムが予期していないアカウントをプログラムに渡した場合に発生します。これは、instructionsの中で間違った位置にアカウントがある場合や、実行されるinstructionsと互換性のないアカウントである場合に起こります。
プログラムの実装でも、クロスプログラムinstructionsを実行する際に、呼び出すプログラムのアカウントを提供し忘れた場合にこのエラーが発生することがあります。
InvalidInstructionData
このプログラムエラーは、instructionをデシリアライズしようとしている間に発生する可能性があります。渡された構造体がinstructionと完全に一致するか確認してください。フィールド間にパディングがある場合があります。プログラムがRustのPack
トレイトを実装している場合は、instructionタイプT
をパッキングおよびアンパッキングして、プログラムが期待する正確なエンコーディングを確認してみてください。
MissingRequiredSignature
一部のinstructionsではアカウントが署名者である必要があります。アカウントが署名されていることが期待されているのに署名されていない場合、このエラーが返されます。
プログラムの実装では、署名されたプログラムアドレスを必要とするクロスプログラム呼び出しを実行する際にも、invoke_signed
に渡された署名者シードがプログラムアドレスの作成に使用された署名者シードと一致しない場合、このエラーが発生することがありますcreate_program_address
。
Stack
SBFは可変スタックポインタの代わりにスタックフレームを使用します。各スタックフレームのサイズは4KBです。
プログラムがそのスタックフレームサイズを違反すると、コンパイラはオーバーランを警告として報告します。
例えば:
Error: Function _ZN16curve25519_dalek7edwards21EdwardsBasepointTable6create17h178b3d2411f7f082E Stack offset of -30728 exceeded max offset of -4096 by 26632 bytes, please minimize large stack variables
このメッセージは、どのシンボルがスタックフレームを超過しているかを識別しますが、名前はマングルされている可能性があります。
Rustシンボルをデマングルするにはrustfiltを使用してください。
上記の警告はRustプログラムから来たものなので、デマングルされたシンボル名は:
rustfilt _ZN16curve25519_dalek7edwards21EdwardsBasepointTable6create17h178b3d2411f7f082Ecurve25519_dalek::edwards::EdwardsBasepointTable::create
エラーではなく警告が報告される理由は、プログラムがその機能を使用していなくても、依存するクレートにはスタックフレームの制限に違反する機能が含まれている可能性があるためです。プログラムが実行時にスタックサイズに違反すると、AccessViolation
エラーが報告されます。
SBFスタックフレームは0x200000000
から始まる仮想アドレス範囲を占めます。
ヒープサイズ
プログラムはRust alloc
APIを通じてランタイムヒープにアクセスできます。高速な割り当てを容易にするため、シンプルな32KBのバンプヒープが使用されています。このヒープは
free
や realloc
をサポートしていません。
内部的には、プログラムは仮想アドレス0x300000000から始まる32KBのメモリ領域にアクセスでき、プログラムの特定のニーズに基づいてカスタムヒープを実装することができます。
Rustプログラムはカスタムglobal_allocator
を定義することで、ヒープを直接実装します
ローダー
プログラムはランタイムローダーによってデプロイされ実行されます。現在サポートされているローダーはBPF LoaderとBPF loader deprecatedの2つです
ローダーは異なるアプリケーションバイナリインターフェースをサポートしている場合があるため、開発者は同じローダー向けにプログラムを作成し、デプロイする必要があります。あるローダー用に書かれたプログラムが別のローダーにデプロイされると、通常はプログラムの入力パラメータのデシリアライズの不一致により
AccessViolation
エラーが発生します。
実用的な目的では、プログラムは常に最新のBPFローダーを対象に作成されるべきであり、最新のローダーはコマンドラインインターフェースとJavaScript APIのデフォルトとなっています。
デプロイメント
SBFプログラムのデプロイメントは、BPF共有オブジェクトをプログラムアカウントのデータにアップロードし、そのアカウントを実行可能としてマークするプロセスです。クライアントはBPF共有オブジェクトを小さな部分に分割し、Write
instructionsのインストラクションデータとしてローダーに送信します。ローダーはそのデータをプログラムのアカウントデータに書き込みます。すべての部分が受信されると、クライアントはFinalize
インストラクションをローダーに送信し、ローダーはSBFデータが有効であることを検証し、プログラムアカウントを実行可能としてマークします。プログラムアカウントが実行可能としてマークされると、その後のトランザクションはそのプログラムが処理するためのinstructionsを発行することができます。
実行可能なSBFプログラムに対して命令が送られると、ローダーはプログラムの実行環境を構成し、プログラムの入力パラメータをシリアライズし、プログラムのエントリポイントを呼び出し、発生したエラーを報告します。
詳細については、プログラムのデプロイを参照してください。
入力パラメータのシリアライズ
SBFローダーはプログラム入力パラメータをバイト配列にシリアライズし、それをプログラムのエントリポイントに渡します。プログラムはオンチェーンでそれをデシリアライズする責任があります。非推奨のローダーと現在のローダーの違いの一つは、入力パラメータが、様々なパラメータが整列されたバイト配列内の整列されたオフセットに配置されるようにシリアライズされることです。これにより、デシリアライズ実装はバイト配列を直接参照し、プログラムに整列されたポインタを提供することができます。
最新のローダーは、プログラム入力パラメータを以下のようにシリアライズします(すべてのエンコーディングはリトルエンディアンです):
- 8バイトの符号なしアカウント数
- 各アカウントについて
- 1バイトは、これが重複アカウントかどうかを示します。重複でない場合は値は0xff、そうでなければ値は重複しているアカウントのインデックスです。
- 重複の場合:7バイトのパディング
- 重複でない場合:
- 1バイトのブール値、アカウントが署名者である場合はtrue
- 1バイトのブール値、アカウントが書き込み可能である場合はtrue
- 1バイトのブール値、アカウントが実行可能である場合はtrue
- 4バイトのパディング
- アカウントの公開鍵の32バイト
- アカウントの所有者の公開鍵の32バイト
- アカウントが所有するlamportの8バイト符号なし数
- アカウントデータのバイト数の8バイト符号なし数
- xバイトのアカウントデータ
- 10kバイトのパディング、reallocに使用
- オフセットを8バイトに整列させるための十分なパディング
- 8バイトのrent epoch
- instruction dataの符号なしバイト数の8バイト
- xバイトのinstruction data
- プログラムIDの32バイト
Is this page helpful?