Motokoプログラミング4

Motokoプログラミング3の続きである。

actor_reference

このサンプルを見ていく前に、いくつか用語を見ていく。

principalとは何か?

What is a principa?を参考に。

principalとは要するに、あなたのIDのことで、これ自体は公開鍵。これが、デプロイしたキャニスター、Cyclesウォレットなどあらゆるところで使われる。

Cyclesとは、要するにICPを使うためのコストで、これが足りないとキャニスターの実行ができなくなるらしい。

Actor referenceとは何か?

https://internetcomputer.org/docs/current/motoko/main/language-manual#actor-referencesのActor referencesに説明がある。

要するに、既存のactorを文字列で参照することができ、「actor “文字列”」の形で荒らされる。そして、そのタイプとしては、actor { … }というものである。

全く機能としては異なるが、通常の変数定義は以下のように書く。

var a: Nat;

同様に、actor referenceの場合は、

actor "文字列": actor { ... }

などとなり、actor { … }型のactor “文字列”への参照、ということらしい。これはあくまで参照なので、この参照をどこかの変数に保持しておく。

サンプルの説明

https://github.com/dfinity/examples/tree/master/motoko/actor_referenceに、このサンプルの説明がある。以下は、ほぼ機械翻訳。

この例では,アクター参照を使用して,あるプリンシパルのテキスト表現をアクター型の値に変換し,アクターとして型付けされ,そのインターフェースの共有関数を呼び出して通信できるようにする簡単な方法を示しています。

アクター参照は,生のプリンシパル識別子への型付けされたインタフェースを提供するために,必要な場合にのみ,控えめに使用されるべきです。アクター参照は、与えられたプリンシパルが与えられたインタフェースに従うことを、プログラマがコンパイラに保証することになるので、安全でないとみなされる。与えられたプリンシパルが与えられたインターフェイスに従うことを保証するのは、コンパイラではなく、プログラマの責任です。

警告 警告:不正なインターフェースを提供すると、その後のアクターとの通信でシリアライズエラー(メモリエラーではない)が発生する可能性があります。

この例では、1つのMotokoアクタを定義しています。main.moは、ICという名前を、テキストのアクタ参照 “aaaaa-aa “に対するインタフェースをアサートして得られたアクタにバインドしています。これは、よく知られている(つまり、システムが提供する)管理キャニスターのテキスト形式のIDで、通常、IC上のキャニスターのインストール、トップアップ、その他の管理に使用されます。

管理キャニスターの完全なインターフェースは、Interface Computer Interface Specificationで提供されています。このおもちゃの例では、指定された操作のサブセットだけが必要で、Candid subtypingにより、完全な仕様で説明されているよりも情報が少ないタイプでインポートすることさえ可能です。より多くの操作へのアクセスを提供するために、元のCandidシグネチャの適切なMotoko翻訳で、アクタータイプにそれらを追加するだけです。

このアクタは、アクタのサイクルバランスの半分を燃やすために、そのローカルICアクタ参照を使用して、一時的なキャニスタを提供、作成、照会、停止、削除する、単一の燃焼メソッドを公開します。

このICのアプリケーションは説明のためのものであり、必ずしも有用ではありません。

サンプルコードのコメント

サンプルのコードにコメント付けしてみる。

import Nat "mo:base/Nat";
import Cycles "mo:base/ExperimentalCycles";
import Debug "mo:base/Debug";


actor {

  // Use an actor reference to access the well-known, virtual
  // IC management canister with specified Principal "aaaaa-aa",
  // asserting its interface type
  // NB: this is a smaller supertype of the full interface at
  //     https://sdk.dfinity.org/docs/interface-spec/index.html#ic-management-canister
  let IC =
    /* アクターへのリファレンス:そのアクターの「型」
       「型」は実際のアクターのサブセットを定義している
     */
    actor "aaaaa-aa" : actor {

      // キャニスターを作成する
      create_canister : {
          // richer in ic.did
          // 非同期でprincipal型のキャニスターIDを返す
        } -> async { canister_id : Principal };

      // キャニスターのCyclesを返す
      canister_status : { canister_id : Principal } ->
        async { // richer in ic.did
          cycles : Nat
        };

      // キャニスターをストップする
      stop_canister : { canister_id : Principal } -> async ();

      // キャニスターを削除する
      delete_canister : { canister_id : Principal } -> async ();
    };

  // Burn half of this actor's cycle balance by provisioning,
  // creating, stopping and deleting a fresh canister
  // (without ever installing any code)
  public func burn() : async () {
    // このアクターのCyclesを取得
    Debug.print("balance before: " # Nat.toText(Cycles.balance()));

    // この部分は、このアクターの持つ量の半分をさらに追加するという意味ではなく、
    // 半分の量を引き続く呼び出し(キャニスター作成)のために使うという意味らしい。
    // 使ってしまった後は、このアクターのCyclesが半分に減る。
    Cycles.add(Cycles.balance()/2);

    // キャニスターを作成する
    let cid = await IC.create_canister({});

    // 作成したキャニスターのCyclesを取得して表示する
    let status = await IC.canister_status(cid);
    Debug.print("cycles: " # Nat.toText(status.cycles));

    // キャニスターをストップして削除する
    await IC.stop_canister(cid);
    await IC.delete_canister(cid);

    // このアクターの現在のCyclesを表示
    Debug.print("balance after: " # Nat.toText(Cycles.balance()));
  };

};