基本的な依存関係

Dapp デザインのよくあるアプローチは、ある Canister スマートコントラクト(あるいは単に Canister と呼ぶ)でデータを計算または保存し、それを別の Canister で使用するというものです。 たとえ基盤となるスマートコントラクトが異なる言語で書かれていても、異なる Canister のスマートコントラクトで定義された関数を共有して使用できることは、Internet Computer で動作する Dapps を構築する際の重要な戦略となります。 このチュートリアルでは、ある言語(今回の例では Motoko)で関数を書き、そのデータを別の言語(ここではRust)で使用する方法を紹介します。

両方の Canister スマートコントラクトは同じプロジェクトに含まれています。

  • Motoko Canister は オペレーションの結果を保存するための cell 変数を持つ Actor を作成します。

  • mul 関数は自然数を入力として受け取り、入力値を3倍にして、結果を cell 変数に格納します。

  • Rust Canister は cell 変数の現在の値を返すシンプルな read 関数を持ちます。呼び出されるメソッドが query であっても、Canister 間の呼び出しを行う関数は update メソッドであるべきであることに注意してください。

はじめる前に

プロジェクトをはじめる前に、以下を確認してください:

  • インターネットに接続しており、ローカルの macOS または Linux コンピュータでターミナルにアクセスできること。

  • Rust のインストール方法 にあるように、Rust プログラミング言語と Cargo が OS にダウンロードされ、インストールされていること。

    curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

    Rust のバージョンは 1.46.0 より新しい必要があります。

  • ダウンロードとインストールの説明に従って、DFINITY Canister Software Development Kit (SDK) パッケージのダウンロードとインストールが済んでいること。

  • cmake のインストールが済んでいること。例えば、macOS では Homebrew を使って以下のコマンドを実行します:

    brew install cmake

    Homebrew をインストールする方法については、Homebrew のドキュメントを参照してください。

  • コンピュータ上の ローカル Canister 実行環境 プロセスが停止していること。

ローカルコンピュータ上で新たなターミナルを開く方法や、ターミナルでコマンドを実行する方法や、インストールパッケージを確認する方法がわからない場合には、初めての人のための準備を参照してください。 既に必要な前提条件を満たしている場合には、新しいプロジェクトの作成 へと進んでください。

このチュートリアルは、完走に約 20 分かかります。

新しいプロジェクトの作成

新しいプロジェクトを作る手順は以下になります:

  1. ローカル PC でターミナルを開きます。

  2. 以下のコマンドを実行し、新しいプロジェクトを作成します。

    dfx new --type=rust rust_deps
  3. 以下のコマンドで、プロジェクトディレクトリに移動します。

    cd rust_deps

デフォルトプロジェクトの変更

このチュートリアルを完了するためには、以下の手順を踏む必要があります。

Canister の初期設定の変更

このサンプルプロジェクトは、Motoko Canister と Rust Canister の2つの Canister で構成されているため、デフォルトの dfx.json 設定ファイルを変更し、Motoko Canister と Rust Canister の両方をビルドするための情報を含める必要があります。

dfx.json を変更するには、以下のようにします:

  1. 自分がプロジェクトのルートディレクトリにいることを確認します。

  2. テキストエディタで dfx.json を開きます。

  3. canisters.rust_deps セクションの上に、Motoko プログラムをビルドするための設定を挿入します。

    例えば、canisters セクションの中で、新しい multiply_deps キーを以下のように追加します。

    "multiply_deps": {
      "main": "src/multiply_deps/main.mo",
      "type": "motoko"
    }
  4. rust_depsdependencies の設定を追加します。 dependencies の設定では、ある Canister から関数をインポートし、別の Canister で使用することができます。 このチュートリアルでは、Motoko で書かれた multiply_deps Canister から関数をインポートし、Rust で書かれた rust_deps Canister から使用することを考えます。 +rust_deps のフィールドは以下のようになります。

    "rust_deps": {
      "candid": "src/rust_deps/rust_deps.did",
      "package": "rust_deps",
      "type": "rust",
      "dependencies": [
        "multiply_deps"
      ]
    }
  5. ファイルから rust_deps_assets の設定をすべて削除します。

    このチュートリアルの Dapp では、フロントエンドの asset を使用していないので、設定ファイルからこれらの設定を削除することができます。

    また、このチュートリアルでは defaultsdfx のキーも削除することができます。

    これらの不要な設定を削除した後の設定ファイルは次のようになります:

    {
      "canisters": {
        "multiply_deps": {
          "main": "src/multiply_deps/main.mo",
          "type": "motoko"
        },
        "rust_deps": {
          "candid": "src/rust_deps/rust_deps.did",
          "package": "rust_deps",
          "type": "rust",
          "dependencies": [
            "multiply_deps"
          ]
        }
      },
      "networks": {
        "local": {
          "bind": "127.0.0.1:8000",
          "type": "ephemeral"
        }
      },
      "version": 1
    }
  6. 変更内容を保存し、dfx.json ファイルを閉じて次に進みます。

Motoko Canister スマートコントラクトの実装

次のステップでは、src/multiply_deps/main.mo にファイルを作成して、mulread 関数を実装するコードを作成します。

Motoko のソースコードを書くには以下のようにします。

  1. Motoko Canister のためのディレクトリを作成します。

    mkdir multiply_deps
  2. src/multiply_deps/main.mo ファイルを作成し、テキストエディタで開きます。

  3. 以下のサンプルコードを main.mo ファイル内にコピー&ペーストします。

    actor Multiply {
    
        var cell : Nat = 1;
    
        public func mul(n:Nat) : async Nat { cell *= n*3; cell };
    
        public query func read() : async Nat {
            cell
        };
    }
  4. 変更を保存してファイルを閉じ、次に進みます。

デフォルトの Rust Canister スマートコントラクトの置き換え

これで Rust Canister が依存する Motoko Canister ができたので、Rust Canister をプロジェクトに追加してみましょう。

デフォルトの Rust Canister を置き換えるには以下のようにします:

  1. 自分がルートディレクトリにいることを確認します。

  2. src/rust_deps/lib.rs ファイルをテキストエディタで開き、内容を削除します。

    次のステップは、Motoko Canister をインポートし、read 関数を実装した Rust プログラムを書くことです。

  3. lib.rs ファイルに以下のコードをコピー&ペーストします:

    use ic_cdk_macros::*;
    use ic_cdk::export::candid;
    
    #[import(canister = "multiply_deps")]
    struct CounterCanister;
    
    #[update]
    async fn read() -> candid::Nat {
        CounterCanister::read().await.0
    }
  4. src/rust_deps/lib.rs を保存して閉じ、先に進みます。

インターフェース記述ファイルの更新

Candid は、Internet Computer で動作する Canister と対話するためのインターフェース記述言語(IDL)です。 Candid ファイルは、Canister が定義する各関数の名前・引数・返し値のフォーマットやデータ型など、Canister のインターフェースを言語に依存しないように記述したものです。

Candid ファイルをプロジェクトに追加することで、Rust で定義されたデータが Internet Computer 上で安全に実行されるために適切に変換されることを保証します。

Candid インターフェース記述言語の構文の詳細は Candid ガイドCandid クレートのドキュメントをご覧ください。

Candid ファイルを更新するには、以下のようにします。

  1. 自分がプロジェクトのルートディレクトリにいることを確認します。

  2. src/rust_deps/rust_deps.did ファイルをテキストエディタで開きます。

  3. read 関数のために、以下のように service を定義します:

    service : {
      "read": () -> (nat);
    }
  4. 変更を保存して deps.did ファイルを閉じ、次に進んでください。

ローカル Canister 実行環境 を立ち上げる

プロジェクトをビルドする前に、ローカル Canister 実行環境 か、Internet Computer メインネットに接続する必要があります。

ローカル Canister 実行環境 を立ち上げるには、以下のようにします:

  1. 自分がプロジェクトのルートディレクトリにいることを確認します。

  2. ローカル Canister 実行環境 をバックグラウンドで立ち上げるために、以下のコマンドを実行します:

    dfx start --clean --background

    プラットフォームやローカルのセキュリティ設定によっては、警告が表示される場合があります。 ネットワーク接続を許可するかどうかの確認画面が表示された場合は、Allow をクリックします。

プロジェクトの登録・ビルド・デプロイ

開発環境で立ち上がっている ローカル Canister 実行環境 に接続した後に、プロジェクトの登録・ビルド・デプロイをローカル環境で行うことができます。 登録・ビルド・デプロイを行うためには、以下のようにします:

  1. プロジェクトのルートディレクトリにいることを確認します。

  2. dfx.json に指定されている Canister を以下のコマンドで、登録・ビルド・デプロイします:

    dfx deploy

    dfx deploy コマンドを実行すると、以下のような実行結果に関しての情報が表示されます。

    Creating a wallet canister on the local network.
    The wallet canister on the "local" network for user "default" is "rwlgt-iiaaa-aaaaa-aaaaa-cai"
    Deploying all canisters.
    Creating canisters...
    Creating canister "multiply_deps"...
    "multiply_deps" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai"
    Creating canister "rust_deps"...
    "rust_deps" canister created with canister id: "ryjl3-tyaaa-aaaaa-aaaba-cai"
    Building canisters...
    Executing: "cargo" "build" "--target" "wasm32-unknown-unknown" "--release" "-p" "rust_deps"
    ...
        Finished release [optimized] target(s) in 5.26s
    Executing: ic-cdk-optimizer -o target/wasm32-unknown-unknown/release/rust_deps.wasm target/wasm32-unknown-unknown/release/rust_deps.wasm
    Installing canisters...
    Creating UI canister on the local network.
    The UI canister on the "local" network is "r7inp-6aaaa-aaaaa-aaabq-cai"
    Installing code for canister multiply_deps, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai
    Installing code for canister rust_deps, with canister_id ryjl3-tyaaa-aaaaa-aaaba-cai
    Deployed canisters.

デプロイした Canister の関数を呼ぶ

Canister のデプロイが成功すると、Canister の関数を呼ぶことでテストできるようになります。

このチュートリアルでは、以下のようにします:

  • mul 関数を呼ぶたびに、cell 変数の値を 3 倍にします。

  • read 関数を呼び出し、cell 変数の現在の値を返します。

デプロイした Canister をテストするために:

  1. デプロイした Canister 上の cell 変数の値を読み取るために Motoko Canister から read 関数を呼び出します。

    dfx canister call multiply_deps read

    このコマンドは、変数 cell の現在の値である1を返します。

    (1 : nat)
  2. 次のコマンドを実行して、入力された引数を3倍する mul 関数を呼び出します。

    dfx canister call multiply_deps mul '(3)'

    このコマンドは、変数 cell の新しい値を返します。

    (9 : nat)
  3. multiply_deps Canister から関数をインポートしている rust_deps Canister の read 関数を呼び出します。

    dfx canister call rust_deps read

    このコマンドは、変数 cell の現在の値を返します。

    (9 : nat)

ローカル Canister 実行環境 を止める

アプリケーションのテストをした後は、ローカル Canister 実行環境 がバックグラウンドで稼働し続けないように、以下の手順で停止します:

  1. ネットワークの稼働状況が表示されている端末で、Control-C を押して ローカル Canister 実行環境 のプロセスを止めてください。

  2. 以下のコマンドを用いて ローカル Canister 実行環境 を停止してください:

    dfx stop