概要
各CPIは基本コストとして約1,000 CU、さらにシリアライゼーションコストがかかります。アカウントデータは呼び出し先の実行前後で同期されます。PDA署名は呼び出し元のプログラムIDを使用します。戻りデータは1,024バイトに制限されています。
CPIコストモデル
CPIのコストは、同じトランザクション計算バジェット(共有メーター)から引き出されます。各
invoke / invoke_signed 呼び出しの完全なコスト計算式:
total_cpi_cost = invocation_cost+ instruction_data_cost+ account_meta_cost (SIMD-0339 only)+ account_info_cost (SIMD-0339 only)+ per_account_data_cost (for each non-executable account)+ callee_execution_cost
コストの内訳
| コスト要素 | 計算式 | ソース |
|---|---|---|
| 呼び出し(固定) | invoke_units = 1,000 CU (SIMD-0339では946) | CPIエントリで課金 |
| instruction dataのシリアライゼーション | instruction_data_len / cpi_bytes_per_unit | cpi_bytes_per_unit = 250。translate_instructionで課金。 |
| アカウントメタのシリアライゼーション (SIMD-0339) | (num_account_metas * 34) / cpi_bytes_per_unit | 各 AccountMeta は34バイト(32 pubkey + 1 is_signer + 1 is_writable)。translate_instructionで課金。 |
| アカウント情報の変換 (SIMD-0339) | (num_account_infos * 80) / cpi_bytes_per_unit | ACCOUNT_INFO_BYTE_SIZE = 80バイト(32 key + 32 owner + 8 lamports + 8 data_len)。translate_account_infosで課金。 |
| アカウントごとのデータ | account_data_len / cpi_bytes_per_unit | CallerAccount::from_account_infoおよび実行可能アカウントでアカウントごとに課金。 |
| 呼び出し先の実行 | 呼び出し先プログラムが消費するCU | 呼び出し先の実行中に共有メーターから差し引かれます。 |
コスト計算の例
100バイトのinstruction data、5つのアカウントメタ、5つのアカウント情報(それぞれ1,000バイトのデータ)を持つCPI、SIMD-0339が有効な場合:
invocation_cost = 946instruction_data_cost = 100 / 250 = 0 (integer division)account_meta_cost = (5 * 34) / 250 = 0account_info_cost = (5 * 80) / 250 = 1per_account_data_cost = 5 * (1000 / 250) = 20total (before callee) = 967 CUs
アカウントデータの同期
アカウントの状態は、CPI中の2つのポイントで呼び出し元と呼び出し先の間で同期されます。これにより、両者がアカウントデータの一貫したビューを確認できます。
CPI前の同期(呼び出し元から呼び出し先へ)
呼び出し先が実行される前に、
update_callee_account
は呼び出し元の実行中の変更を呼び出し先のアカウントビューにコピーします:
| フィールド | 方向 | タイミング |
|---|---|---|
| Lamports | 呼び出し元 -> 呼び出し先 | 値が呼び出し先の現在のビューと異なる場合 |
| データ長 | 呼び出し元 -> 呼び出し先 | 呼び出し元がアカウントをリサイズした場合。original_data_len + MAX_PERMITTED_DATA_INCREASE(10 KiB)を超えてはいけません。 |
| データ内容 | 呼び出し元 -> 呼び出し先 | アカウントのデータが変更可能な場合(can_data_be_changedが通過) |
| オーナー | 呼び出し元 -> 呼び出し先 | 最後に設定されるため、古いオーナーの下でデータ/lamportの変更が許可されます |
CPI後の同期(呼び出し先から呼び出し元へ)
呼び出し先が戻った後、
update_caller_account
は呼び出し先の変更を呼び出し元のビューにコピーします。これは書き込み可能としてマークされたアカウントに対してのみ実行されます:
| フィールド | 方向 | タイミング |
|---|---|---|
| Lamports | 呼び出し先 -> 呼び出し元 | 常にコピーされます |
| オーナー | 呼び出し先 -> 呼び出し元 | 常にコピーされます |
| データ長 | 呼び出し先 -> 呼び出し元 | 変更された場合。VMデータスライスポインタとシリアル化された長さフィールドが更新されます。アカウントが縮小された場合、解放されたメモリはゼロ化されます。新しい長さがoriginal_data_len + MAX_PERMITTED_DATA_INCREASEを超える場合、InvalidReallocを返します。 |
| データ内容 | 呼び出し先 -> 呼び出し元 | 呼び出し先のデータバッファから呼び出し元のシリアル化されたデータ領域にコピーされます |
Reallocの制限
アカウントデータは、CPI中に現在のトップレベル命令の開始時点でのアカウントのデータ長を超えて
MAX_PERMITTED_DATA_INCREASE
= 10,240バイト(10 KiB)までリサイズできます。この制限は
update_callee_account
(CPI前)と
update_caller_account
(CPI後)の両方で適用されます。これを超えると*rsInvalidRealloc*が返されます。
PDA署名
*rsinvoke_signed*が呼び出されると、ランタイムは提供されたseedと呼び出し元のプログラムIDからPDAアドレスを導出します。これは
translate_signers_rustで行われます:
- 署名者seed配列が検証されます:最大
MAX_SIGNERS(16)個の署名者seedセット。 - 各seedセットが検証されます:セットあたり最大
MAX_SEEDS(16)個のseed、各seedは最大MAX_SEED_LEN(32)バイト。 Pubkey::create_program_addressがseedと呼び出し元のプログラムID(呼び出し先ではない)で呼び出されます。- seedが有効なPDAを生成しない場合(つまり、結果のポイントがed25519曲線上にある場合)、CPIは
BadSeedsで失敗します。 - 導出されたPDA
pubkeyが収集され、有効な署名者として
prepare_next_instructionに渡されます。権限チェック中に、呼び出し先アカウントが署名者としてマークされ、そのpubkeyがこれらの導出されたPDAのいずれかと一致する場合、署名者チェックは成功します。
PDAは呼び出し先のプログラムIDではなく、呼び出し元のプログラムIDを使用して導出されます。これは、PDAを所有するプログラム(導出に使用されたIDを持つプログラム)のみがその代わりに署名できることを意味します。プログラムは他のプログラムから導出されたPDAに対して署名することはできません。
リターンデータ
プログラムはリターンデータメカニズムを使用して、呼び出し元にデータを返すことができます。これは2つのsyscallを使用します:
sol_set_return_data: 現在の命令に対して最大MAX_RETURN_DATA(1,024)バイトのリターンデータを設定します。コストはdata_len / cpi_bytes_per_unit + syscall_base_costCUです。sol_get_return_data: 最後に実行された命令によって設定されたリターンデータを読み取ります。データとそれを設定したプログラムIDを返します。コストは(data_len + 32) / cpi_bytes_per_unit + syscall_base_costCU(プログラムIDの32バイト)です。
リターンデータはトランザクションごとに保存され、sol_set_return_dataを呼び出す各命令によって上書きされます。各プログラム呼び出しの開始時に、ランタイムはリターンデータをリセットして空にします。CPIが戻った後、呼び出し元は呼び出し先(または呼び出し先が呼び出した任意のプログラム)が最後に設定したリターンデータを読み取ることができます。
リターンデータは1,024バイトに制限されています。呼び出しチェーンでsol_set_return_dataを呼び出した最後のプログラムのみが、呼び出し元に表示される内容を決定します。呼び出し先がリターンデータを設定するさらなるCPIを行う場合、呼び出し先自身のリターンデータは上書きされます。
Is this page helpful?