根本的になにかの知識が足りない。
概要
開発者は、独自のプログラムを作成してSolanaブロックチェーンにデプロイできます。
HelloWorldの例では、プログラムは、書かれて構築され、展開、およびオンチェーンと相互作用しているかを確認するには良い出発点です。
Berkeley Packet Filter(BPF)#
Solanaオンチェーンプログラムは、LLVMコンパイラインフラストラクチャ(要はコンパイラ)を介して、Berkeley Packet Filter(BPF)バイトコードのバリエーションを含むExecutable and Linkable Format(ELF )にコンパイルされます。(わからん。BPFは何かのフェルターで、実行可能なファイルにコンパイルされるということでとりあえず…)
SolanaはLLVMコンパイラインフラストラクチャを使用するため、プログラムはLLVMのBPFバックエンドをターゲットにできる任意のプログラミング言語で記述できます。Solanaは現在、RustおよびC / C ++でのプログラムの記述をサポートしています。
BPFは、インタープリターされた仮想マシンで実行できる効率的な命令セット、または効率的なジャストインタイムでコンパイルされたネイティブ命令を提供します。
メモリマップ#
Solana BPFプログラムで使用される仮想アドレスメモリマップは、次のように固定およびレイアウトされています。
- プログラムコードは0x100000000から始まります
- スタックデータは0x200000000から始まります
- ヒープデータは0x300000000から始まります
- プログラム入力パラメータは0x400000000から始まります
上記の仮想アドレスは開始アドレスですが、プログラムにはメモリマップのサブセットへのアクセスが許可されています。アクセスが許可されていない仮想アドレスに対してプログラムが読み取りまたは書き込みを試みると、プログラムはパニックになり、試行さ AccessViolation
れた違反のアドレスとサイズを含むエラーが返されます。
スタック#
BPFは、可変スタックポインタの代わりにスタックフレームを使用します。各スタックフレームのサイズは4KBです。
プログラムがそのスタックフレームサイズに違反している場合、コンパイラはオーバーランを警告として報告します。
例えば: Error: Function _ZN16curve25519_dalek7edwards21EdwardsBasepointTable6create17h178b3d2411f7f082E Stack offset of -30728 exceeded max offset of -4096 by 26632 bytes, please minimize large stack variables
メッセージは、どのシンボルがスタックフレームを超えているかを識別しますが、RustまたはC ++シンボルの場合、名前が壊れている可能性があります。Rustシンボルをデマングルするには、rustfiltを使用し ます。上記の警告はRustプログラムからのものであるため、デマングルされたシンボル名は次のとおりです。
C ++シンボルをデマングルするにはc++filt
、binutilsから使用します。
エラーではなく警告が報告される理由は、プログラムがその機能を使用していなくても、一部の依存クレートにスタックフレームの制限に違反する機能が含まれている可能性があるためです。プログラムが実行時にスタックサイズに違反すると、AccessViolation
エラーが報告されます。
BPFスタックフレームは、0x200000000から始まる仮想アドレス範囲を占有します。
深さを呼び出す#
プログラムは迅速に実行するように制約されており、これを容易にするために、プログラムの呼び出しスタックは最大深度64フレームに制限されています。
ヒープ#
プログラムは、Cで直接、またはRustAPIを介してランタイムヒープにアクセスでき alloc
ます。高速割り当てを容易にするために、単純な32KBのバンプヒープが使用されます。ヒープはサポートしていないfree
か、realloc
そう賢くそれを使用しています。
内部的には、プログラムは仮想アドレス0x300000000から始まる32KBのメモリ領域にアクセスでき、プログラムの特定のニーズに基づいてカスタムヒープを実装できます。
フロートサポート#
プログラムはRustのfloat操作の限定されたサブセットをサポートします。プログラムがサポートされていないfloat操作を使用しようとすると、ランタイムは未解決のシンボルエラーを報告します。
フロート操作は、ソフトウェアライブラリ、特にLLVMのフロートビルトインを介して実行されます。エミュレートされたソフトウェアにより、整数演算よりも多くの計算単位を消費します。一般に、可能な場合は固定小数点演算が推奨されます。
Solanaプログラムライブラリの数学テストは、いくつかの数学演算のパフォーマンスを報告します:https: //github.com/solana-labs/solana-program-library/tree/master/libraries/math
テストを実行するには、リポジトリを同期して、以下を実行します。
$ cargo test-bpf -- --nocapture --test-threads=1
最近の結果は、float演算は、同等の整数と比較してより多くの命令を必要とすることを示しています。固定小数点の実装は異なる場合がありますが、同等の浮動小数点数よりも少なくなります。
静的書き込み可能データ#
プログラム共有オブジェクトは、書き込み可能な共有データをサポートしていません。プログラムは、同じ共有読み取り専用コードとデータを使用して、複数の並列実行間で共有されます。これは、開発者がプログラムに静的な書き込み可能変数またはグローバル変数を含めてはならないことを意味します。将来的には、書き込み可能なデータをサポートするために、コピーオンライトメカニズムが追加される可能性があります。
署名された部門#
BPF命令セットは、符号付き除算をサポートしていません。署名された除算命令を追加することは考慮事項です。
ローダー#
プログラムはランタイムローダーでデプロイされ、実行されます。現在、サポートされているローダーは2つあります。BPFローダー とBPFローダーは非推奨です。
ローダーはさまざまなアプリケーションバイナリインターフェイスをサポートしている可能性があるため、開発者はプログラムを作成して同じローダーにデプロイする必要があります。あるローダー用に作成されたプログラムが別のローダーにデプロイされた場合、通常 AccessViolation
、プログラムの入力パラメーターの逆シリアル化の不一致が原因でエラーが発生します。
すべての実用的な目的のために、プログラムは常に最新のBPFローダーをターゲットにするように作成する必要があり、最新のローダーがコマンドラインインターフェイスとjavascriptAPIのデフォルトです。
特定のローダー用のプログラムの実装に関する言語固有の情報については、以下を参照してください。
展開#
BPFプログラムの展開は、BPF共有オブジェクトをプログラムアカウントのデータにアップロードし、アカウントを実行可能としてマークするプロセスです。クライアントは、BPF共有オブジェクトを細かく分割し、それらを命令の命令データとして Write
ローダーに送信します。ローダーは、そのデータをプログラムのアカウントデータに書き込みます。すべてのピースが受信されると、クライアントFinalize
はローダーに命令を送信 し、ローダーはBPFデータが有効であることを検証し、プログラムアカウントを実行可能としてマークします。プログラムアカウントが実行可能としてマークされると、後続のトランザクションはそのプログラムに処理するための指示を発行する場合があります。
命令が実行可能BPFプログラムに向けられると、ローダーはプログラムの実行環境を構成し、プログラムの入力パラメーターをシリアル化し、プログラムのエントリーポイントを呼び出し、発生したエラーを報告します。
入力パラメータのシリアル化#
BPFローダーは、プログラム入力パラメーターをバイト配列にシリアル化し、それをプログラムのエントリポイントに渡します。このエントリポイントでは、プログラムがチェーン上で逆シリアル化を行います。非推奨のローダーと現在のローダーの間の変更点の1つは、入力パラメーターがシリアル化され、さまざまなパラメーターが整列されたバイト配列内の整列されたオフセットに収まるようにすることです。これにより、逆シリアル化の実装はバイト配列を直接参照し、プログラムへの整列されたポインターを提供できます。
シリアル化に関する言語固有の情報については、以下を参照してください。
最新のローダーは、プログラム入力パラメーターを次のようにシリアル化します(すべてのエンコードはリトルエンディアンです)。
- 8バイトの符号なしアカウント数
- アカウントごとに
- これが重複アカウントであるかどうかを示す1バイト。重複していない場合、値は0xffです。それ以外の場合、値は重複しているアカウントのインデックスです。
- 重複する場合:7バイトのパディング
- 重複していない場合:
- 1バイトのブール値、アカウントが署名者の場合はtrue
- 1バイトのブール値、アカウントが書き込み可能である場合はtrue
- 1バイトのブール値。アカウントが実行可能である場合はtrue
- 4バイトのパディング
- アカウント公開鍵の32バイト
- アカウントの所有者公開鍵の32バイト
- アカウントが所有する8バイトの符号なしランポート数
- アカウントデータの8バイトの符号なしバイト数
- xバイトのアカウントデータ
- reallocに使用される10kバイトのパディング
- オフセットを8バイトに揃えるのに十分なパディング。
- 8バイトの家賃エポック
- 符号なし数の命令データの8バイト
- xバイトの命令データ
- プログラムIDの32バイト