Use multiple actors

在本教程中,您将创建一个包含多个参与者的项目。 目前,您只能在 Motoko 文件中定义一个参与者,并且单个参与者始终编译为单个容器智能合约。 但是,您可以创建具有多个参与者的项目,并且可以从同一个 dfx.json 配置文件构建多个容器。

在本教程中,您将为同一项目中的三个actor创建单独的程序文件。 该项目定义了以下不相关的参与者:

  • assistant actor 提供了在待办事项列表中添加和显示任务的功能。

    为简单起见,本教程的代码示例仅包含添加待办事项和显示已添加的待办事项列表的功能。此容器的更完整版本(具有将项目标记为完整和从列表中删除项目的附加功能)可在examples中找到简单的待办事项清单

  • rock_paper_scissors actor 提供了一个函数,用于在硬编码的剪刀石头布比赛中确定获胜者。

    此代码示例说明了在带有硬编码播放器和选项的 Motoko 程序中 switchcase 的基本用法。

  • daemon 角色提供了启动和停止守护进程的模拟函数。

    此代码示例仅分配一个变量并打印消息以用于演示目的。

开始之前

在开始本教程之前,请验证以下内容:

  • 您已按照下载并安装中的说明下载并安装了 {sdk-short-name} 包。

  • 您已停止在您的计算机上运行的任何本地容器执行环境。

完成本教程大约需要 20 分钟。

创建一个新项目

要为本教程创建一个新项目:

  1. 如果您还没有打开一个终端外壳,请在您的本地计算机上打开一个终端外壳。

  2. 更改为您用于 Internet Computer 项目的文件夹(如果您正在使用一个)。

  3. 通过运行以下命令创建一个新项目:

    dfx new multiple_actors
  4. 通过运行以下命令切换到您的项目目录:

    cd multiple_actors

修改默认配置

您已经看到创建一个新项目会在您的项目目录中添加一个默认的 dfx.json 配置文件。 对于本教程,您需要在此文件中添加部分以指定每个容器的位置,这些容器定义了您要构建的actor。

修改默认的 dfx.json 配置文件:

  1. 在文本编辑器中打开 dfx.json 配置文件,然后将默认的 multiple_actors 容器名称和源目录更改为 assistant

    例如,在 canisters 键下:

        "assistant": {
          "main": "src/assistant/main.mo",
          "type": "motoko"
        },

    因为您要向配置文件的这个 canisters 部分添加设置,所以还必须在包含 assistant 主源代码文件和容器的位置的大括号之后添加一个逗号 类型。

  2. 从文件中删除 multiple_actors_assets 部分。

  3. 为“rock_paper_scissors”容器添加新容器名称、源代码位置和容器类型,并在“assistant”容器定义下方为“daemon”程序文件添加新容器名称、源代码位置和容器类型。

    进行更改后,dfx.json 文件的 canisters 部分应类似于以下内容:

    {
      "canisters": {
        "assistant": {
          "main": "src/assistant/main.mo",
          "type": "motoko"
        },
        "rock_paper_scissors": {
          "main": "src/rock_paper_scissors/main.mo",
          "type": "motoko"
        },
        "daemon": {
          "main": "src/daemon/main.mo",
          "type": "motoko"
        }
      },
      "defaults": {
        "build": {
          "packtool": ""
        }
      },
      "dfx": "0.9.2",
      "networks": {
        "local": {
          "bind": "127.0.0.1:8000",
          "type": "ephemeral"
        }
      },
      "version": 1
    }

    您可以按原样保留其他部分。

  4. 保存您的更改并关闭 dfx.json 文件以继续。

  5. 通过运行以下命令,更改默认源文件目录的名称以匹配 dfx.json 配置文件中指定的名称:

    cp -r src/multiple_actors/ src/assistant/
  6. 复制 assistant 源文件目录,通过运行以下命令为 rock_paper_scissors 角色创建主容器文件:

    cp -r src/assistant/ src/rock_paper_scissors/
  7. 复制 assistant 源文件目录,通过运行以下命令为 daemon 角色创建主容器文件:

    cp -r src/assistant/ src/daemon/

修改默认容器

现在,src 目录中有三个单独的目录,每个目录都有一个模板 main.mo 文件。 在本教程中,您将用不同的 actor 替换每个模板 main.mo 文件中的内容。

修改默认源代码:

  1. 在文本编辑器中打开 src/assistant/main.mo 文件并删除现有内容。

  2. 将以下示例代码复制并粘贴到文件中:

    import Array "mo:base/Array";
    import Nat "mo:base/Nat";
    
    // Define the actor
    actor Assistant {
    
      stable var todos : [ToDo] = [];
      stable var nextId : Nat = 1;
    
      // Define to-do item properties
      type ToDo = {
        id : Nat;
        description : Text;
        completed : Bool;
      };
    
      // Add to-do item utility
      func add(todos : [ToDo], description : Text, id : Nat) : [ToDo] {
        let todo : ToDo = {
          id = id;
          description = description;
          completed = false;
        };
        Array.append(todos, [todo])
    };
    
      // Show to-do item utility
      func show(todos : [ToDo]) : Text {
        var output : Text = "\n___TO-DOs___";
        for (todo : ToDo in todos.vals()) {
          output #= "\n(" # Nat.toText(todo.id) # ") " # todo.description;
          if (todo.completed) { output #= " ✔"; };
        };
        output
      };
    
      public func addTodo (description : Text) : async () {
        todos := add(todos, description, nextId);
        nextId += 1;
      };
    
      public query func showTodos () : async Text {
        show(todos)
      };
    
    };
  3. 保存更改并关闭 main.mo 文件以继续。

  4. 在文本编辑器中打开 src/rock_paper_scissors/main.mo 文件并删除现有内容。

  5. 将以下示例代码复制并粘贴到文件中:

    import I "mo:base/Iter"; 
    
    actor RockPaperScissors {
    
      stable var alice_score : Nat = 0;
      stable var bob_score : Nat = 0;
      stable var alice_last : Choice = #scissors;
      stable var bob_last : Choice = #rock;
    
      type Choice = {
        #rock;
        #paper;
        #scissors;
      };
    
      public func contest() : async Text {
        for (i in I.range(0, 99)) {
          battle_round();
        };
        var winner = "The contest was a draw";
        if (alice_score > bob_score) winner := "Alice won" 
        else if (alice_score < bob_score) winner := "Bob won";
        return (winner);
      };
    
      func battle_round() : () {
        let a = alice(bob_last);
        let b = bob(alice_last);
    
        switch (a, b) {
          case (#rock, #scissors) alice_score += 1;
          case (#rock, #paper) bob_score += 1;
          case (#paper, #scissors) alice_score += 1;
          case (#paper, #rock) bob_score += 1;
          case (#scissors, #paper) alice_score += 1;
          case (#scissors, #rock) bob_score += 1;
          case (#rock, #rock) alice_score += 0;
          case (#paper, #paper) bob_score += 0;
          case (#scissors, #scissors) alice_score += 0;
        };
    
        alice_last := a;
        bob_last := b;
    
        return ();
      };
      
      // Hard-coded players and choices
      func bob(last : Choice) : Choice {
        return #paper;
      };
    
      func alice(last : Choice) : Choice {
        return #rock;
      };
    };
  6. 保存更改并关闭 main.mo 文件以继续。

  7. 在文本编辑器中打开 src/daemon/main.mo 文件并删除现有内容。

  8. 将以下示例代码复制并粘贴到文件中:

    actor Daemon {
      stable var running = false;
    
      public func launch() : async Text {
        running := true;
        debug_show "The daemon process is running";
      };
    
      public func stop(): async Text {
        running := false;
        debug_show "The daemon is stopped";
      };
    };
  9. 保存更改并关闭 main.mo 文件以继续。

启动本地容器执行环境

在本地安装 multiple_actors 项目之前,您需要启动本地容器执行环境。 如果您打算将其部署到 Internet Computer 区块链主网,则可以跳过本节。

启动本地容器执行环境:

  1. 在本地计算机上打开一个新的终端窗口或选项卡。

  2. 如有必要,导航到项目的根目录。

  3. 通过运行以下命令启动本地容器执行环境:

    dfx start
  4. 将显示容器执行操作的终端保持打开状态,并将焦点切换到您在其中创建新项目的原始终端。

部署你的多容器 dapp

要部署多容器 dapp,您需要将容器注册、构建和安装到本地容器执行环境或 Internet Computer 区块链主网。

要在本地部署 dapp:

  1. 如果需要,请检查您是否仍在项目的根目录中。

  2. 通过运行以下命令注册、构建和部署应用程序:

    dfx deploy

要将 dapp 部署到 Internet Computer 区块链主网:

  1. 如果需要,请检查您是否仍在项目的根目录中。

  2. 运行 dfx deploy 命令,指定 --network 选项和在 dfx.json 文件中配置的网络别名。 例如,如果您使用网络别名 ic 指定的 URL 连接到 Internet Computer 主网,您将运行类似于以下的命令:

    dfx deploy --network ic

dfx deploy 命令输出显示有关它执行的操作的信息。 例如,该命令显示在 dfx.json 配置文件中定义的三个容器的特定容器标识符。

Deploying all canisters.
Creating canisters...
Creating canister "assistant"...
"assistant" canister created with canister id: "75hes-oqbaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q"
Creating canister "daemon"...
"daemon" canister created with canister id: "cxeji-wacaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q"
Creating canister "rock_paper_scissors"...
"rock_paper_scissors" canister created with canister id: "7kncf-oidaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q"

通过调用容器来验证部署

您现在在本地容器执行环境或 Internet Computer 区块链主网上部署了三个 canisters 智能合约,并且可以使用 dfx canister call 命令对其进行测试。

要测试您在本地部署的容器:

  1. 使用 dfx canister call 命令通过 addTodo 函数调用容器 assistant,并通过运行以下命令将要添加的任务传递给它:

    dfx canister call assistant addTodo '("Schedule monthly demos")'
  2. 通过运行以下命令,验证该命令是否使用 showTodos 函数返回待办事项列表项:

    dfx canister call assistant showTodos

    该命令返回类似于以下内容的输出:

    ("
    ___TO-DOs___
    (1) Schedule monthly demos")
  3. 使用 dfx canister call 命令通过运行以下命令,使用 contest 函数调用容器 rock_paper_scissors:

    dfx canister call rock_paper_scissors contest

    该命令返回类似于以下内容的硬编码竞赛结果:

    ("Bob won")
  4. 使用 dfx canister call 命令通过运行以下命令,使用 launch 函数调用容器 daemon:

    dfx canister call daemon launch
  5. 验证 mock launch 函数返回“守护进程正在运行”消息“:

    (""The daemon process is running"")

要测试在 Internet Computer 区块链主网上运行的容器,请使用与上述相同的命令,指定 --network 选项和在 dfx.json 文件中配置的网络别名。

停止本地网络

完成 dapp 试验后,您可以停止本地容器执行环境,使其不会继续在后台运行。

要停止本地容器执行环境:

  1. 在显示网络操作的终端中,按 Control-C 中断本地网络进程。

  2. 通过运行以下命令停止本地容器执行环境:

    dfx stop