モッピー!ポイ活応援ポイントサイト
未分類

PDA

プログラム間の呼び出し

クロスプログラム呼び出し

Solanaランタイムを使用すると、プログラム間呼び出しと呼ばれるメカニズムを介してプログラムが相互に呼び出すことができます。プログラム間の呼び出しは、一方のプログラムが他方の命令を呼び出すことによって実現されます。呼び出されたプログラムは、呼び出されたプログラムが命令の処理を終了するまで停止されます。

たとえば、クライアントは、それぞれが別々のオンチェーンプログラムによって所有されている2つのアカウントを変更するトランザクションを作成できます。

メッセージ= Message :: new(vec![
token_instruction :: pay(&alice_pubkey)、
acme_instruction :: launch_missiles(&bob_pubkey)、
]);
client.send_and_confirm_message(&[&alice_keypair、&bob_keypair]、&message);

クライアントは、代わりに、acmeプログラムtoken がクライアントに代わって命令を便利に呼び出すことを許可する場合があります。

メッセージ= Message :: new(vec![
acme_instruction :: pay_and_launch_missiles(&alice_pubkey、&bob_pubkey)、
]);
client.send_and_confirm_message(&[&alice_keypair、&bob_keypair]、&message);

2つのオンチェーンプログラムtokenacme、それぞれの実装命令pay()とが与えられた場合、 プログラム間呼び出しを発行することによりlaunch_missiles()tokenモジュールで定義された関数の呼び出しでacmeを実装できます。

mod acme {
token_instructionを使用します。
fn launch_missiles(accounts:&[AccountInfo])-> Result <()> {
..。
}
fn pay_and_launch_missiles(accounts:&[AccountInfo])-> Result <()> {
alice_pubkey = accounts .key;とします。
命令= token_instruction :: pay(&alice_pubkey);
invoke(&instruction、accounts)?;
launch_missiles(accounts)?;
}

invoke()Solanaのランタイムに組み込まれており、指定された命令をtoken命令のprogram_id フィールドを介してプログラムにルーティングする役割を果たします。

invoke呼び出し元は、呼び出される命令に必要なすべてのアカウントを渡す必要があることに注意してください。これは、実行可能アカウント(命令のプログラムIDと一致するアカウント)と命令プロセッサに渡されるアカウントの両方を意味します。

を呼び出す前pay()に、ランタイムはacme、が所有するアカウントが変更されていないことを確認する必要がありますtoken。これは、ランタイムのポリシーを、acme呼び出し時のアカウントの現在の状態と、命令のinvoke開始時のアカウントの初期状態に適用することによって行われますacmepay()完了後 、ランタイムは、ランタイムのポリシーを再度適用することtokenによりacme、所有するアカウントが変更されていないことを再度確認する必要がありますが、今回はtokenプログラムIDを使用します。最後に、pay_and_launch_missiles() 完了後、ランタイムはランタイムポリシーをもう一度適用する必要がありますpre_*。通常は適用されますが、更新されたすべての変数を使用します。無効なアカウント変更pay_and_launch_missiles()pay()行わないまで実行した場合 、 pay()無効な変更を行わず、returnsが無効な変更を行わないpay()まで 実行するとpay_and_launch_missiles()、ランタイムはpay_and_launch_missiles()、全体として無効なアカウントの変更が行われなかったと推移的に想定できるため、これらすべてのアカウントの変更をコミットできます。

特権を必要とする指示

ランタイムは、呼び出し元プログラムに付与された特権を使用して、呼び出し先に拡張できる特権を決定します。このコンテキストでの特権は、署名者と書き込み可能なアカウントを指します。たとえば、呼び出し元が処理している命令に署名者または書き込み可能なアカウントが含まれている場合、呼び出し元はその署名者および/または書き込み可能なアカウントも含む命令を呼び出すことができます。

この特権拡張は、プログラムのアップグレードの特別な場合を除いて、プログラムが不変であるという事実に依存しています。

acmeプログラムの場合、ランタイムはトランザクションの署名をtoken命令の署名として安全に扱うことができます。ランタイムは token命令参照 を確認するとalice_pubkeyacme命令内のキーを検索して、そのキーが署名されたアカウントに対応しているかどうかを確認します。この場合、それはそれを行い、それによってtokenプログラムがアリスのアカウントを変更することを許可します。

プログラム署名済みアカウント

プログラムは、プログラムから派生したアドレスを使用して、元のトランザクションで署名されなかった署名済みアカウントを含む命令を発行できます

プログラムから派生したアドレスでアカウントに署名するために、プログラムは invoke_signed()

invoke_signed(
&命令、
アカウント、
&[&[“最初のアドレスシード”]、
&[“2番目のアドレスの最初のシード”、 “2番目のアドレスの2番目のシード”]]、
)?;

深さを呼び出す

プログラム間の呼び出しにより、プログラムは他のプログラムを直接呼び出すことができますが、現在、深さは4に制限されています。

リエントラント

現在、リエントラントは、固定された深さで制限された直接自己再帰に制限されています。この制限により、プログラムが後でコールバックされる可能性があることを知らずに、プログラムが中間状態から別のプログラムを呼び出す可能性がある状況が防止されます。直接再帰により、プログラムはコールバックされた時点でその状態を完全に制御できます。

プログラム派生アドレス

プログラムから派生したアドレスを使用すると、プログラム間で呼び出すときにプログラムで生成された署名を使用できます。

プログラムから派生したアドレスを使用して、プログラムにアカウントに対する権限を付与し、後でその権限を別のアカウントに転送することができます。これが可能なのは、プログラムが権限を与えるトランザクションの署名者として機能できるためです。

たとえば、2人のユーザーがソラナでのゲームの結果に賭けをしたい場合、それぞれのユーザーは賭けの資産を、合意を尊重する仲介者に譲渡する必要があります。現在、仲介プログラムは資産を勝者に譲渡できないため、この仲介をソラナのプログラムとして実装する方法はありません。

この機能は、新しい所有者を決定するイベントが発生するまでアセットをエスクローエージェントに転送する必要があるため、多くのDeFiアプリケーションに必要です。

  • 一致するビッドオーダーとアスクオーダーの間で資産を転送する分散型取引所。
  • 資産を勝者に譲渡するオークション。
  • 賞品を集めて勝者に再配布するゲームまたは予測市場。

プログラムから派生したアドレス:

  1. プログラムがプログラムアドレスと呼ばれる特定のアドレスを制御できるようにして、外部ユーザーがそれらのアドレスの署名を使用して有効なトランザクションを生成できないようにします。
  2. クロスプログラム呼び出しを介して呼び出される命令に存在するプログラムアドレスにプログラムがプログラムで署名できるようにします。

2つの条件が与えられると、ユーザーはオンチェーンアセットの権限をプログラムアドレスに安全に転送または割り当てることができ、プログラムはその裁量でその権限を他の場所に割り当てることができます。

プログラムアドレスの秘密鍵

プログラムアドレスはed25519曲線上にないため、有効な秘密鍵が関連付けられていないため、その署名を生成することはできません。独自の秘密鍵はありませんが、プログラムが署名者としてプログラムアドレスを含む命令を発行するために使用できます。

ハッシュベースで生成されたプログラムアドレス

プログラムアドレスは、256ビットの原像耐性ハッシュ関数を使用してシードのコレクションとプログラムIDから決定論的に導出されます。秘密鍵が関連付けられていないことを確認するために、プログラムアドレスがed25519曲線上にないようにする必要があります。生成中に、アドレスが曲線上にあることが判明した場合、エラーが返されます。シードとプログラムIDの特定のコレクションに対して、これが発生する可能性は約50/50です。これが発生した場合は、別のシードセットまたはシードバンプ(追加の8ビットシード)を使用して、曲線から外れた有効なプログラムアドレスを見つけることができます。

プログラムの決定論的プログラムアドレスは、で作成されたアカウントと同様の派生パスに従い、SystemInstruction::CreateAccountWithSeedで実装されPubkey::create_with_seedます。

参考までに、その実装は次のとおりです。

pub fn create_with_seed(
ベース:&Pubkey、
シード:&str、
program_id:&Pubkey、
)-> Result <Pubkey、SystemError> {
seed.len()> MAX_ADDRESS_SEED_LEN {の場合
Err(SystemError :: MaxSeedLengthExceeded);を返します。
}
Ok(Pubkey :: new(
hashv(&[base.as_ref()、seed.as_ref()、program_id.as_ref()])。as_ref()、
))
}

プログラムは、シードを使用して、任意の数のアドレスを決定論的に導出できます。これらのシードは、アドレスがどのように使用されているかを象徴的に識別できます。

からPubkey::

///派生プログラムアドレスを生成します
/// *シード、キーの派生に使用されるシンボリックキーワード
/// * program_id、アドレスが派生するプログラム
pub fn create_program_address(
シード:&[&[u8]]、
program_id:&Pubkey、
)-> Result <Pubkey、PubkeyError>
///有効なオフカーブ派生プログラムアドレスとそのバンプシードを検索します
/// *シード、キーの派生に使用されるシンボリックキーワード
/// * program_id、アドレスが派生するプログラム
pub fn find_program_address(
シード:&[&[u8]]、
program_id:&Pubkey、
)->オプション<(Pubkey、u8)> {
mutbump_seed = [std :: u8 :: MAX];
for _ in 0..std :: u8 :: MAX {
mut seeds_with_bump = seeds.to_vec();
seeds_with_bump.push(&bump_seed);
Ok(address)= create_program_address(&seeds_with_bump、program_id){
Some((address、bump_seed [0]));を返します。
}
bump_seed [0]-= 1;
}
なし
}

プログラムアドレスの使用

クライアントはこのcreate_program_address関数を使用して宛先アドレスを生成できます。この例ではcreate_program_address(&[&["escrow"]], &escrow_program_id)、が曲線から外れた有効なプログラムアドレスを生成すると仮定し ます。

//エスクローキーを決定論的に導出します
let escrow_pubkey = create_program_address(&[&[“escrow”]]、&escrow_program_id);
//そのキーを使用して転送メッセージを作成します
メッセージ= Message :: new(vec![
token_instruction :: transfer(&alice_pubkey、&escrow_pubkey、1)、
]);
// 1つのトークンをエスクローに転送するメッセージを処理します
client.send_and_confirm_message(&[&alice_keypair]、&message);

プログラムは同じ関数を使用して同じアドレスを生成できます。以下の関数では、プログラムtoken_instruction::transferは、トランザクションに署名するための秘密鍵を持っているかのように、プログラムアドレスからを発行します。

fn transfer_one_token_from_escrow(
program_id:&Pubkey、
アカウント:&[AccountInfo]、
)-> ProgramResult {
//ユーザーが宛先を指定します
alice_pubkey = keyed_accounts .unsigned_key();とします。
//エスクローパブキーを決定論的に導出します。
let escrow_pubkey = create_program_address(&[&[“escrow”]]、program_id);
//転送命令を作成します
命令= token_instruction :: transfer(&escrow_pubkey、&alice_pubkey、1);を許可します。
//ランタイムは、現在のキーから決定論的にキーを導出します
//プログラムIDと指定されたキーワードを実行します。
//派生アドレスが命令で署名済みとしてマークされたキーと一致する場合
//次に、そのキーは署名済みとして受け入れられます。
invoke_signed(&instruction、accounts、&[&[“escrow”]])
}

を使用して生成されたアドレスcreate_program_addressは、カーブ外の有効なプログラムアドレスであるとは限らないことに注意してください。たとえば、シード"escrow2"が有効なプログラムアドレスを生成しないと仮定しましょう。

"escrow2シードとしてを使用して有効なプログラムアドレスを生成するには、を使用して find_program_address、有効な組み合わせが見つかるまで、可能なバンプシードを反復処理します。前の例は次のようになります。

//エスクローキーと有効なバンプシードを見つけます
let(escrow_pubkey2、escrow_bump_seed)= find_program_address(&[&[“escrow2”]]、&escrow_program_id);
//そのキーを使用して転送メッセージを作成します
メッセージ= Message :: new(vec![
token_instruction :: transfer(&alice_pubkey、&escrow_pubkey2、1)、
]);
// 1つのトークンをエスクローに転送するメッセージを処理します
client.send_and_confirm_message(&[&alice_keypair]、&message);

プログラム内では、これは次のようになります。

fn transfer_one_token_from_escrow2(
program_id:&Pubkey、
アカウント:&[AccountInfo]、
)-> ProgramResult {
//ユーザーが宛先を指定します
alice_pubkey = keyed_accounts .unsigned_key();とします。
//エスクローパブキーを繰り返し導出します
let(escrow_pubkey2、bump_seed)= find_program_address(&[&[“escrow2”]]、program_id);
//転送命令を作成します
命令= token_instruction :: transfer(&escrow_pubkey2、&alice_pubkey、1);を許可します。
//生成されたバンプシードをすべてのシードのリストに含めます
invoke_signed(&instruction、accounts、&[&[“escrow2″、&[bump_seed]]])
}

以来find_program_addressへのコール数を繰り返し処理が必要です create_program_address、それはより多く使用することができます 計算予算をオンチェーン使用時。計算コストを削減するには、find_program_addressオフチェーンを使用して、結果のバンプシードをプログラムに渡します。

署名者を必要とする指示

で生成されたアドレスcreate_program_addressとはfind_program_address 、他の公開鍵と区別できません。ランタイムがアドレスがプログラムに属していることを確認する唯一の方法は、プログラムがアドレスの生成に使用されるシードを提供することです。

ランタイムは内部でを呼び出しcreate_program_address、その結果を命令で指定されたアドレスと比較します。

クロスプログラム呼び出しの使用方法の例については、Rust を使用した開発およびCを使用した開発を参照してください。

ABOUT ME
たけ
はじめまして! たけといいます。 20代男性サラリーマンが資産運用で5年で3000万をめざします。 これを読んで自分でも出来るのではないかと思ってくれる人が増えると嬉しいです。 お金を得ることは手段に過ぎません。若いうちに稼いで、自分の時間をより大切なことに使いたいです。 【2019投資戦歴】 投資資金合計 300万 2019年度単年損益(年利) FX 15万(15%) 投信 9万(7%) 株式 4万(8%) ※投信、株式は含み益