Dfinity(ICP)基础开发教程-5

Dfinity 基础开发教程

教程概述

快速启动提供了没有停下来欣赏沿途的风景部署简单的默认应用程序快速路径。

此文章介绍了特定场景,并指出了在每个步骤中执行的操作和详细信息。

如果快速入门和教程不完全符合您的风格,此概览备忘单总结了要遵循的步骤以供快速参考。

安装 DFINITY Canister SDK 后,您需要了解以下所有信息:

  1. 创建一个新项目并切换到项目目录。

    dfx new <project_name> && cd <project_name>>
    
  2. 编辑定义您的服务或应用程序的后端。

  3. 编辑为您的服务或应用程序提供前端的 HTML、JavaScript 和 CSS。

  4. 启动 Internet 计算机进行本地开发或检查与 Internet 计算机的连接以进行网络部署。

  5. 在本地或网络上注册、构建和部署。

    dfx deploy --network <network>
    
  6. 在浏览器中查看您的服务或应用程序。

探索默认项目

如果你开始使用DFINITY SDK并快速启动,你已经看到,用于创建Internet 计算机上运行的应用程序的基本工作流程。现在,让我们通过探索在创建新项目时添加到工作区的默认文件和文件夹来仔细研究该工作流程。

作为预览,下图说明了在您的计算机上本地运行 Internet 计算机时的开发工作流程。

在这里插入图片描述

在开始之前

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

  • 您有互联网连接并可以访问本地 macOS 或 Linux 计算机上的 shell 终端。
  • node.js如果你想在你的项目中包含前端开发的默认模板文件,你已经安装了。
  • 您已经下载并安装了 DFINITY Canister SDK 包
  • 如果您使用 Visual Studio Code 作为 IDE,您已经为 Motoko 安装了 Visual Studio Code 插件。
  • 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。

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

创建一个新项目

Internet 计算机的应用程序作为您创建的项目启动。您可以使用dfx可执行命令行界面 (CLI)创建项目。

要仔细查看默认情况下包含在项目中的文件和文件夹,让我们创建一个新项目来使用。

要创建一个新项目:

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

  2. 如果您使用单独的工作文件夹,请导航到您用于 Internet 计算机项目的文件夹。

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

    dfx new explore_hello
    

    dfx new explore_hello命令会创建一个新explore_hello 项目,包括新项目名称下的默认项目目录结构和项目的新 Git 存储库。如果已经node.js安装在本地,新建项目也会添加一些模板前端代码和依赖。

    为确保项目名称在 JavaScript、Motoko 和其他上下文中使用时有效,您应该只使用字母数字字符和下划线。不能包含破折号或任何特殊字符。

  4. 通过运行以下命令查看默认目录结构:

    ls -l explore_hello
    

    默认情况下,项目目录结构至少包括一个源子目录、一个模板README.md文件和一个默认dfx.json配置文件。

    根据您是否已node.js安装,您的项目目录可能包含以下部分或全部文件:

    explore_hello/
    ├── README.md      # default project documentation
    ├── dfx.json       # project configuration file
    ├── node_modules   # libraries for front-end development
    ├── package-lock.json
    ├── package.json
    ├── src            # source files directory
    │   ├── explore_hello
    │   │   └── main.mo
    │   └── explore_hello_assets
    │       ├── assets
    │       │   ├── logo.png
    │       │   ├── main.css
    │       │   └── sample-asset.txt
    │       └── src
    │           ├── index.html
    │           └── index.js
    └── webpack.config.js
    

    默认项目目录至少包括以下文件夹和文件:

    • README用于在存储库中记录项目的默认文件。
    • dfx.json用于为项目设置可配置选项的默认配置文件。
    • src应用程序所需的所有源文件的默认目录。

    默认src目录包含一个模板main.mo文件,您可以修改或替换该文件以包含您的核心编程逻辑。

    由于本教程侧重于入门的基础知识,因此您将只使用该main.mo文件。如果您已node.js安装,您的项目目录将包含其他文件和目录,您可以使用它们来定义应用程序的前端界面。前端开发和文件assets夹中的模板文件稍后讨论。

查看默认配置

默认情况下,创建新项目会将一些模板文件添加到您的项目目录中。您可以编辑这些模板文件以自定义项目的配置设置并包含您自己的代码以加快开发周期。

要查看项目的默认配置文件:

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

  2. 通过运行以下命令切换到您的项目目录:

    cd explore_hello
    
  3. dfx.json在文本编辑器中打开配置文件以查看默认设置。

    例如:

    {
        "canisters": {
            "explore_hello": {
                "main": "src/explore_hello/main.mo",
                "type": "motoko"
            },
            "explore_hello_assets": {
                "dependencies": [
                    "explore_hello"
                ],
                "frontend": {
                    "entrypoint": "src/explore_hello_assets/src/index.js"
                },
                "source": [
                    "src/explore_hello_assets/assets",
                    "dist/explore_hello_assets/"
                ],
                "type": "assets"
            }
        },
        "defaults": {
            "build": {
                "packtool": ""
            }
        },
        "dfx": "0.7.2",
        "networks": {
            "local": {
                "bind": "127.0.0.1:8000",
                "type": "ephemeral"
            }
        },
        "version": 1
    }
    

    让我们来看看一些默认设置。

    • canisters部分指定explore_hello项目的 WebAssembly 模块的名称是explore_hello.

    • 所述canisters.explore_hellokey指定要编译的主程序位于路径中指定由所述main设置,在这种情况下,src/explore_hello/main.motype设置表示这是一个motoko程序。

    • canisters.explore_hello_assets关于这个项目的前端资产密钥指定配置细节。让我们暂时跳过这些。

    • dfx设置用于标识用于创建项目的软件版本。

    • networks部分指定有关您连接到的网络的信息。默认设置将本地 Internet 计算机网络绑定到本地主机地址127.0.0.1和端口8000

      如果您可以访问其他 Internet 计算机网络提供商,则该networks部分可以包含用于连接到这些提供商的网络别名和 URL。

    您可以保留默认设置。

  4. 关闭dfx.json文件以继续。

查看默认程序代码

新项目总是包含一个模板main.mo源代码文件。您可以编辑此文件以包含您自己的代码以加快开发周期。

让我们看一下默认main.mo模板文件中的示例程序,作为使用 Motoko 编程语言创建简单程序的起点。

要查看项目的默认示例程序:

  1. 通过运行以下命令检查您是否仍在项目目录中:

    pwd
    
  2. src/explore_hello/main.mo在文本编辑器中打开文件并查看模板中的代码:

    actor { 
        public func greet(name : Text) : async Text { 
            return "Hello, " # name # "!"; 
        }; 
    };
    

    让我们来看看这个程序的几个关键元素:

    • 您可能会注意到,此示例代码定义了一个actor而不是main某些编程语言需要的函数。对于 Motoko,该main函数隐含在文件本身中。
    • 虽然传统的“Hello, World!” 程序说明了如何使用printprintln函数打印字符串,该传统程序不能代表在 Internet 计算机上运行的 Motoko 程序的典型用例。
    • 这个示例程序定义了一个actorwith 公共greet函数,而不是一个打印函数,它接受name一个类型为的参数Text
    • 然后程序使用async关键字来指示程序返回一个异步消息,该消息由使用"Hello, "#运算符、name参数和构造的串联文本字符串组成"!"

    稍后我们将更多地探索使用actor对象和异步消息处理的代码。现在,您可以继续下一部分。

  3. 关闭main.mo文件以继续。

启动本地网络

在构建默认项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。

在本地启动网络需要一个dfx.json文件,因此您应该确保您位于项目的根目录中。对于本教程,您应该有两个独立的终端外壳,以便您可以在一个终端中启动和查看网络操作,并在另一个终端中管理您的项目。

在本地启动网络:

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

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

    • 您现在应该打开了两个终端
    • 您应该将项目目录作为您当前的工作目录
  3. 通过运行以下命令在本地计算机上启动 Internet 计算机网络:

    dfx start
    

    根据您的平台和本地安全设置,您可能会看到显示的警告。如果系统提示您允许或拒绝传入网络连接,请单击允许

    启动本地网络后,您有一个终端显示有关网络操作的消息,另一个终端用于执行与项目相关的任务。

  4. 让显示网络操作的终端保持打开状态,并将焦点切换到您创建新项目的终端。

注册容器标识符

在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以向网络注册,以便为您的项目生成唯一的、特定于网络的容器标识符

在快速入门教程中,此步骤是作为dfx deploy命令工作流程的一部分执行的。本教程演示了如何独立执行每个操作。

要为本地网络注册容器标识符:

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

  2. 通过运行以下命令为项目中的容器注册唯一的容器标识符:

    dfx canister create --all
    

    该命令显示dfx.json配置文件中定义的容器的特定于网络的容器标识符。

    Creating a wallet canister on the local network.
    The wallet canister on the "local" network for user "pubs-id" is "rwlgt-iiaaa-aaaaa-aaaaa-cai"
    Creating canister "explore_hello"...
    "explore_hello" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai"
    Creating canister "explore_hello_assets"...
    "explore_hello_assets" canister created with canister id: "ryjl3-tyaaa-aaaaa-aaaba-cai"
    

    由于您已连接到本地运行的 Internet 计算机网络,因此这些容器标识符仅在本地有效,并针对项目存储在.dfx/local/canister_ids.json文件中。

    例如:

    { 
      "explore_hello": { 
        "local": "rrkah-fqaaa-aaaaa-aaaaq-cai" 
      }, 
      "explore_hello_assets": { 
        "local": "ryjl3-tyaaa-aaaaa-aaaba-cai" 
      } 
    }
    

编译程序

现在您已经探索了默认配置设置和程序代码并启动了 Internet Computer 网络,让我们将默认程序编译成一个可执行的 WebAssembly 模块。

要构建程序可执行文件:

  1. 在本地计算机上的终端 shell 中,导航到您的explore_hello项目目录。

    您必须dfx build从项目目录结构中运行该命令。

  2. 通过运行以下命令构建可执行容器:

    dfx build
    

    您应该会看到类似于以下内容的输出:

    Building canisters...
    Building frontend...
    

    由于您连接到本地运行的 Internet 计算机网络,因此该dfx build命令会在项目canisters目录下添加目录.dfx/local/

  3. 通过运行以下命令验证.dfx/local/canisters/explore_hellodfx build命令创建的目录是否包含 WebAssembly 和相关应用程序文件。

    ls -l .dfx/local/canisters/explore_hello/
    

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

    -rw-r--r--  1 pubs  staff     178 Apr  6 14:25 explore_hello.d.ts
    -rw-r--r--  1 pubs  staff      41 Apr  6 14:25 explore_hello.did
    -rw-r--r--  1 pubs  staff     155 Apr  6 14:25 explore_hello.did.js
    -rw-r--r--  1 pubs  staff     142 Apr  6 14:25 explore_hello.js
    -rw-r--r--  1 pubs  staff  157613 Apr  6 14:25 explore_hello.wasm
    

    canisters/explore_hello目录包含以下关键文件:

    • explore_hello.did文件包含主程序的接口描述。
    • explore_hello.did.js文件包含程序中函数的容器接口的 JavaScript 表示。
    • explore_hello.js文件包含程序的容器接口的 JavaScript 表示。
    • explore_hello.wasm文件包含为项目中使用的资产编译的 WebAssembly。

    canisters/explore_hello_assets目录包含用于描述与您的项目相关联的前端资产的类似文件。

    除了canisters/explore_hellocanisters/explore_hello_assets目录中的文件外,该dfx build命令还会创建一个idl目录。

在本地部署项目

您已经看到该dfx build命令在canisters您的项目的目录中创建了多个工件。canister_manifest.json要在 Internet 计算机网络上部署您的程序,需要WebAssembly 模块和文件。

本地部署:

  1. 在本地计算机上的终端 shell 中,导航到您的explore_hello项目目录。

  2. explore_hello通过运行以下命令在本地网络上部署您的项目:

    dfx canister install --all
    

    该命令显示类似于以下内容的输出:

    Installing code for canister explore_hello, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai
    Installing code for canister explore_hello_assets, with canister_id ryjl3-tyaaa-aaaaa-aaaba-cai
    Authorizing our identity (pubs-id) to the asset canister...
    Uploading assets to asset canister...
      /index.html 1/1 (480 bytes)
      /index.js 1/1 (296836 bytes)
      /main.css 1/1 (484 bytes)
      /sample-asset.txt 1/1 (24 bytes)
      /logo.png 1/1 (25397 bytes)
      /index.js.map 1/1 (964679 bytes)
      /index.js.LICENSE.txt 1/1 (499 bytes)
    
  3. 运行dfx canister call命令并通过运行以下命令指定要调用的程序和函数:

    dfx canister call explore_hello greet everyone
    

    此命令指定:

    • explore_hello作为您要调用的容器或应用程序服务的名称。
    • greet作为您要调用的特定方法或函数。
    • everyone作为传递给greet函数的参数。
  4. 验证命令显示greet函数的返回值。

    例如:

    ("Hello, everyone!")
    

查看默认前端

如果您已node.js安装在您的开发环境中,您的项目将包含一个简单的前端示例,该示例使用模板index.jsJavaScript 文件explore_hello在浏览器中访问该程序。

要探索默认的前端模板:

  1. 在本地计算机上打开一个终端 shell(如果您还没有打开),然后导航到您的explore_hello项目目录。

  2. src/explore_hello_assets/src/index.js在文本编辑器中打开文件并查看模板脚本中的代码:

    import { Actor, HttpAgent } from '@dfinity/agent';
    import { idlFactory as explore_hello_idl, canisterId as explore_hello_id } from 'dfx-generated/explore_hello';
    
    const agent = new HttpAgent();
    const explore_hello = Actor.createActor(explore_hello_idl, { agent, canisterId: explore_hello_id });
    
    document.getElementById("clickMeBtn").addEventListener("click", async () => {
      const name = document.getElementById("name").value.toString();
      const greeting = await explore_hello.greet(name);
    
      document.getElementById("greeting").innerText = greeting;
    });
    

    模板index.js文件使用两个import语句显式创建代理实例和explore_hellodfx.

    此文件与模板index.html文件结合使用以显示带有图像资产、输入字段和greet函数按钮的 HTML 页面。

  3. 关闭index.js文件以继续。

  4. 通过运行以下命令查看为项目创建的前端资产:

    ls -l .dfx/local/canisters/explore_hello_assets/
    

    该命令显示类似于以下内容的输出:

    drwxr-xr-x  9 pubs  staff     288 Apr  6 14:25 assets
    -r--r--r--  1 pubs  staff    2931 Dec 31  1969 assetstorage.did
    -r--r--r--  1 pubs  staff  265823 Dec 31  1969 assetstorage.wasm
    -rw-r--r--  1 pubs  staff    3651 Apr  6 14:25 explore_hello_assets.d.ts
    -rw-rw-rw-  1 pubs  staff    2931 Dec 31  1969 explore_hello_assets.did
    -rw-r--r--  1 pubs  staff    4236 Apr  6 14:25 explore_hello_assets.did.js
    -rw-r--r--  1 pubs  staff     149 Apr  6 14:25 explore_hello_assets.js
    -rw-rw-rw-  1 pubs  staff  265823 Dec 31  1969 explore_hello_assets.wasm
    

    这些文件是由dfx build命令使用节点模块和模板index.js文件自动生成的。

  5. 打开浏览器,然后导航到local网络地址和端口号-127.0.0.1:8000/中-specifieddfx.json配置文件。

    要指定您希望 Web 服务器显示canisterIdexplore_hello_assets容器,请使用以下语法将参数和 容器标识符添加到 URL:

    ?canisterId=<YOUR-CANISTER-IDENTIFIER>
    

    例如,完整的 URL 应类似于以下内容:

    http://127.0.0.1:8000/?canisterId=rrkah-fqaaa-aaaaa-aaaaq-cai
    
  6. 验证您是否看到了示例应用程序的 HTML 页面。

    例如:

在这里插入图片描述

  1. 键入问候语,然后单击“**单击我”**以返回问候语。

在这里插入图片描述

停止本地网络

在您完成程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。

要停止本地网络:

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

  2. 通过运行以下命令停止 Internet 计算机网络:

    dfx stop
    

使用 actor 查询

在快速入门中,您第一次看到了一个简单的 Internet 计算机程序,其中包含一个参与者对象和异步消息传递。作为学习编写利用基于参与者的消息传递的程序的下一步,本教程说明了如何修改传统Hello, World!程序以定义参与者,然后在本地网络上部署和测试您的程序。

在你开始之前

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

  • 您已经下载并安装了 DFINITY Canister SDK 包,
  • 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。

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

创建一个新项目

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

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

  2. 如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。

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

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

    cd actor_hello
    

修改默认配置

您看到创建新项目会将默认dfx.json配置文件添加到您的项目目录中。在本教程中,您需要修改一些默认设置以反映您的项目。

修改dfx.json配置文件:

  1. dfx.json在文本编辑器中打开配置文件。

  2. 检查actor_hello项目的默认设置。

  3. 请注意,源文件和输出文件的名称和路径都使用actor_hello项目名称。

    例如,默认容器名称是actor_hello,主程序文件的默认路径是src/actor_hello/main.mo.

    您可以重命名这些文件或目录中的任何一个。但是,如果您进行任何更改,请确保您用于文件系统上的文件和目录的名称与您在dfx.json配置文件中指定的名称相匹配。如果您计划使用默认目录和文件名,则无需更改。

  4. actor_hello_assets从文件中删除所有配置设置。

    本教程的示例程序不使用任何前端资产,因此您可以从配置文件中删除这些设置。

    例如,删除该actor_hello_assets部分后的配置文件如下所示:

    {
      "canisters": {
        "actor_hello": {
          "main": "src/actor_hello/main.mo",
          "type": "motoko"
        }
      },
      "defaults": {
        "build": {
          "packtool": ""
        }
      },
      "dfx": "0.7.2",
      "networks": {
        "local": {
          "bind": "127.0.0.1:8000",
          "type": "ephemeral"
        }
      },
      "version": 1
    }
    
  5. 保存更改并关闭文件以继续。

修改默认程序

您看到创建新项目会创建一个src带有模板main.mo文件的默认目录。在本教程中,您将修改模板代码以创建一个简单的“Hello, World!” 使用演员的程序。

修改默认模板源代码:

  1. 通过运行以下命令切换到项目的源代码目录:

    cd src/actor_hello
    
  2. main.mo在文本编辑器中打开模板文件并删除现有内容。

    下一步是编写一个程序,打印一个类似于传统的“Hello, World!”语句的程序。示例程序。但是,要为 Internet 计算机编译程序,您的程序必须包含一个actor具有public函数的对象。

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

    import Debug "mo:base/Debug";
    actor HelloActor {
       public query func hello() : async () {
          Debug.print ("Hello, World from DFINITY \n");
       }
    };
    

    让我们仔细看看这个简单的程序:

    • 该程序导入一个Debug模块以提供print功能。
    • 该程序使用public query func来定义查询方法,因为在这种情况下,actor_hello程序不会对容器的状态进行任何更改,也不会执行任何会更新您正在访问的数据的操作。
  4. 保存更改并关闭main.mo文件。

使用本地标识符构建程序

您可能只会将这个简单的程序用于一些本地测试。因此,无需在 Internet 计算机网络上保留唯一的容器标识符来保存程序的编译输出。

在这种情况下,您可以在完全不连接 Internet 计算机网络的情况下编译程序。相反,该dfx build命令会创建一个本地、硬编码的容器标识符供您使用。

您可以在测试您的程序时或任何时候您想编译您的程序时使用此本地标识符,而无需在本地启动 Internet 计算机副本进程或连接到远程子网上的副本。

要构建程序可执行文件:

  1. 导航回项目目录的根目录。

  2. 通过运行以下命令,使用本地定义的标识符构建程序:

    dfx build --check
    

    --check选项使您能够在本地构建项目以验证它是否编译并检查生成的文件。由于该dfx build --check命令仅生成一个临时标识符,您应该会看到类似于以下内容的输出:

    Building canisters to check they build ok. Canister IDs might be hard coded.
    Building canisters...xxxxxxxxxx Building canisters to check they build ok. Canister IDs might be hard coded.Building canisters...
    

    如果程序编译成功,您可以检查默认.dfx/local/canisters目录和.dfx/local/canisters/actor_hello/子目录中的输出。

    例如,您可以使用以下tree命令查看创建的文件:

    tree .dfx/local/canisters
    

    该命令显示类似于以下内容的输出

.dfx/local/canisters
├── actor_hello
│   ├── actor_hello.d.ts
│   ├── actor_hello.did
│   ├── actor_hello.did.js
│   ├── actor_hello.js
│   └── actor_hello.wasm
└── idl

2 directories, 5 files

部署项目

您不能将dfx build --check命令的输出部署到任何 Internet 计算机网络。如果要部署此项目,则需要执行以下操作:

  • 连接到 Internet 计算机网络。
  • 注册特定于网络的容器标识符。
  • 部署容器。

让我们更详细地考虑这些步骤。在部署此项目之前,您必须连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。在连接到本地或远程网络后,您还必须生成一个唯一的、特定网络的容器标识符来替换本地定义的标识符。要自己查看所涉及的步骤,让我们将项目部署到本地。

要在本地部署此项目:

  1. 如果需要,打开终端并导航到您的项目目录。

  2. 通过运行以下命令在本地计算机上启动 Internet 计算机网络:

    dfx start --background
    

    对于本教程,您可以使用该--background选项将 Internet 计算机网络作为后台进程启动。使用此选项,您可以继续下一步,而无需在本地计算机上打开另一个终端外壳。

  3. 通过运行以下命令,在本地 Internet 计算机网络上为您的项目生成新的容器标识符:

    dfx canister create actor_hello
    

    您应该会看到类似于以下内容的输出:

    Creating a wallet canister on the local network.
    The wallet canister on the "local" network for user "pubs-id" is "rwlgt-iiaaa-aaaaa-aaaaa-cai"
    Creating canister "actor_hello"...
    "actor_hello" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai"
    

    dfx canister create命令还将特定于网络的容器标识符canister_ids.json存储在.dfx/local目录中的文件中。

    例如:

    {
      "actor_hello": {
        "local": "rrkah-fqaaa-aaaaa-aaaaq-cai"
      }
    }
    
  4. actor_hello通过运行以下命令在本地网络上部署您的项目:

    dfx canister install actor_hello
    

    该命令显示类似于以下内容的输出:

    Installing code for canister actor_hello, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai
    

查询容器

您现在已将程序部署为本地 Internet 计算机网络上的容器,并且可以使用该dfx canister call命令测试您的程序。

要测试您在本地网络上部署的程序:

  1. 用于通过运行以下命令dfx canister call来调用该hello函数:

    dfx canister call actor_hello hello
    
  2. 验证命令返回为hello函数指定的文本以及运行本地网络进程的终端中的检查点消息。

    例如,程序在类似于以下的输出中显示“Hello, World from DFINITY”:

    [Canister rrkah-fqaaa-aaaaa-aaaaq-cai] Hello, World from DFINITY
    

    请注意,如果您在单独的终端而不是在后台运行 Internet 计算机网络,则会在显示网络活动的终端中显示“Hello, World from DFINITY”消息。

停止本地网络

在您完成程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。

要停止本地网络:

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

  2. 通过运行以下命令停止 Internet 计算机网络:

    dfx stop
    

传递文本参数

本教程提供了默认程序的一个简单变体,它允许您将单个文本参数传递给单个 actor,编译代码以创建一个容器,然后检索该参数。

本教程说明了如何使用 Candid 接口描述语言 (IDL) 在终端的命令行上传递参数,以及如何修改程序以允许它接受多个文本参数值。

在你开始之前

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

  • 您已经下载并安装了 DFINITY Canister SDK 包
  • 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。

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

创建一个新项目

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

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

  2. 如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。

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

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

    cd location_hello
    

修改默认配置

创建新项目会将默认dfx.json配置文件添加到您的项目目录中。您应该始终查看文件中的默认设置,以验证信息是否准确反映了您要使用的项目设置。对于本教程,您将修改默认配置以删除未使用的设置。

要修改dfx.json配置文件中的设置:

  1. dfx.json在文本编辑器中打开配置文件。

  2. 检查location_hello项目的默认设置。

  3. 删除所有不必要的配置设置。

    由于本教程不涉及创建任何前端资产,因此您可以location_hello_assets从文件中删除所有配置设置。

  4. 保存更改并关闭文件以继续。

修改默认程序

您看到创建新项目会创建一个src带有模板main.mo文件的默认目录。

修改默认模板源代码:

  1. src/location_hello/main.mo在文本编辑器中打开源代码文件。

  2. 修改默认源代码,用greet函数替换location函数,用name参数替换city参数。

    例如:

    actor {
      public func location(city : Text) : async Text {
        return "Hello, " # city # "!";
      };
    };
    
  3. 保存更改并关闭文件以继续。

启动本地网络

在构建项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。

在本地启动网络需要一个dfx.json文件,因此您应该确保您位于项目的根目录中。对于本教程,您应该有两个独立的终端外壳,以便您可以在一个终端中启动和查看网络操作,并在另一个终端中管理您的项目。

要启动本地网络:

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

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

    • 您现在应该打开了两个终端
    • 您应该将项目目录作为您当前的工作目录
  3. 通过运行以下命令在本地计算机上启动 Internet 计算机网络:

    dfx start
    

    如果系统提示您允许或拒绝传入网络连接,请单击允许

  4. 让显示网络操作的终端保持打开状态,并将焦点切换到您创建项目的原始终端。

注册、构建和部署应用程序

在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署您的应用程序。

在本地部署应用程序:

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

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

    dfx deploy
    

    dfx deploy关于它执行的操作命令输出显示信息。

传递文本参数

您现在已将程序部署为本地 Internet 计算机网络上的容器,并且可以使用dfx canister call命令测试您的程序。

要测试您在本地部署的程序:

  1. 调用location程序中的方法并通过运行以下命令传递city类型参数text

    dfx canister call location_hello location "San Francisco"
    

    由于本例中的参数在San和之间包含一个空格Francisco,因此您需要将参数括在引号中。该命令显示类似于以下内容的输出:

    ("Hello, San Francisco!")
    

    如果参数不包含需要将文本括在引号内的空格,您可以允许 Candid 接口描述语言推断数据类型,如下所示:

    dfx canister call location_hello location Paris
    

    Candid 推断数据类型为Text,并将程序的输出作为文本返回,如下所示:

    ("Hello, Paris!")
    
  2. 调用location程序中的方法并city使用文本参数的 Candid 接口描述语言语法显式传递参数:

    dfx canister call location_hello location '("San Francisco and Paris")'
    

    该命令显示类似于以下内容的输出:

    ("Hello, San Francisco and Paris!")
    

    因为您的程序只接受一个文本参数,所以指定多个字符串只会返回第一个参数。

    例如,如果您尝试此命令:

    dfx canister call location_hello location '("San Francisco","Paris","Rome")'
    

    ("Hello, San Francisco!")返回第一个参数— — 。

修改程序中的源代码

为了扩展您在本教程中学到的知识,您可能想要尝试修改源代码以返回不同的结果。例如,您可能希望修改location函数以返回多个城市名称。

要尝试修改本教程的源代码:

  1. dfx.json在文本编辑器中打开配置文件并将默认location_hello设置更改为favorite_cities.

    对于这一步,您应该修改容器名称和主程序的路径,以便容器使用favorite_cities

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

  3. 通过运行以下命令复制location_hello源文件目录以匹配dfx.json配置文件中指定的名称:

    cp -r src/location_hello src/favorite_cities
    
  4. src/favorite_cities/main.mo在文本编辑器中打开文件。

  5. 复制并粘贴以下代码示例以location使用两个新函数替换该函数。

    例如:

    actor {
    
      public func location(cities : [Text]) : async Text {
        return "Hello, from " # (debug_show cities) # "!";
      };
    
      public func location_pretty(cities : [Text]) : async Text {
        var str = "Hello from ";
        for (city in cities.vals()) {
          str := str # city #", ";
        };
        return str # "bon voyage!";
      }
    };
    

    您可能注意到Text在此代码示例中用方 ( [ ]) 括号括起来。就其本身而言,Text代表一组 UTF-8 字符。类型周围的方括号表示它是该类型的数组。因此,在此上下文中,[Text]表示一组 UTF-8 字符集合,使程序能够接受和返回多个文本字符串。

    代码示例还使用了apply数组操作的基本格式,可以抽象为:

    public func apply<A, B>(fs : [A -> B], xs : [A]) : [B] {
        var ys : [B] = [];
        for (f in fs.vals()) {
          ys := append<B>(ys, map<A, B>(f, xs));
        };
        ys;
    };
    

    有关对数组执行操作的函数的信息,请参阅 Motoko 基础库或Motoko 编程语言参考中对 Array 模块的描述

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

    dfx deploy
    
  7. 通过运行以下命令,调用location程序中的方法并city使用 Candid 接口描述语法传递参数:

    dfx canister call favorite_cities location '(vec {"San Francisco";"Paris";"Rome"})'
    

    该命令使用 Candid 接口描述语法(vec { val1; val2; val3; })返回值向量。有关 Candid 界面描述语言的更多信息,请参阅Candid语言指南。

    此命令显示类似于以下内容的输出:

    dfx canister call favorite_cities location '(vec {"San Francisco";"Paris";"Rome"})'
    
  8. 通过运行以下命令调用location_pretty程序中的方法并city使用接口描述语法传递参数:

    dfx canister call favorite_cities location_pretty '(vec {"San Francisco";"Paris";"Rome"})'
    

    该命令显示类似于以下内容的输出:

    ("Hello from San Francisco, Paris, Rome, bon voyage!")
    

在浏览器中测试功能

容器接口描述语言(通常称为 Candid 或更一般地称为 IDL)提供了一种用于指定容器服务签名的通用语言。Candid 为您提供了一种统一的方式来与用不同语言编写或使用不同工具访问的容器进行交互。例如,无论底层程序是原生 Rust、JavaScript 还是 Motoko,Candid 都提供一致的服务视图。Candid 还支持不同的工具(例如dfx命令行界面和网络神经系统应用程序)来共享服务的通用描述。

基于 actor 的类型签名,Candid 还提供了一个 Web 界面,允许您调用容器函数进行测试和调试。

使用dfx deployordfx canister install命令在本地部署项目后,您可以在浏览器中访问 Candid Web 界面端点。此 Web 界面(Candid UI)以表单形式公开服务描述,使您能够快速查看和测试功能并尝试输入不同的数据类型,而无需编写任何前端代码。

要使用 Candid Web 界面来测试容器功能:

  1. 使用dfx canister id __Candid_UI命令查找与当前项目关联的 Candid UI 容器标识符。

    dfx canister id __Candid_UI
    

    该命令显示 Candid UI 的容器标识符,输出类似于以下内容:

    r7inp-6aaaa-aaaaa-aaabq-cai
    
  2. 复制 Candid UI 容器标识符,以便它在剪贴板中可用。

  3. 如果您已停止 Internet 计算机,请通过运行以下命令在本地重新启动它:

    dfx start --background
    
  4. 打开浏览器并导航到dfx.json配置文件中指定的地址和端口号。

    默认情况下,local网络绑定到127.0.0.1:8000地址和端口号。

  5. canisterIddfx canister id命令返回的必需参数和 Candid UI 容器标识符添加到 URL。

    例如,完整的 URL 应该类似于以下内容,但带有CANDID-UI-CANISTER-IDENTIFIERdfx canister id命令返回的:

    http://127.0.0.1:8000/?canisterId=<CANDID-UI-CANISTER-IDENTIFIER>
    

    浏览器会显示一个表单,供您指定容器标识符或选择 Candid 描述 ( .did) 文件。

    如果您不确定要使用哪个容器标识符,您可以运行该dfx canister id命令来查找特定容器名称的标识符。

  6. 提供容器 ID字段中为您的应用程序指定容器标识符或描述文件,然后单击转到以显示服务描述。

  7. 查看程序中定义的函数调用和类型列表。

  8. 为函数键入适当类型的值,或单击“**随机”**生成值,然后单击“**调用”**或“**查询”**以查看结果。

    请注意,根据数据类型,Candid 界面可能会显示用于测试功能的其他配置设置。例如,如果函数接受一个数组,您可能需要在输入值之前指定数组中的项数。

    在这个例子中,每个函数都接受一个文本字符串数组。因此,您首先选择数组的长度,然后在单击Call之前为每个项目设置值。

    在这里插入图片描述

停止本地网络

在您完成程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。

要停止本地网络:

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

  2. 通过运行以下命令停止 Internet 计算机网络:

    dfx stop
    

增加一个自然数

在本教程中,您将编写一个程序来创建单个参与者并提供一些基本函数来增加计数器并说明值的持久性。

在本教程中,角色名为Counter。程序使用该currentValue变量来包含一个代表计数器当前值的自然数。该程序支持以下函数调用:

  • increment函数调用更新当前值,由1(无返回值)递增。
  • get函数调用查询并返回当前值。
  • set函数调用更新当前值到您指定作为参数的任意数值。

本教程提供了一个简单示例,说明如何通过调用已部署容器上的函数来增加计数器。通过多次调用该函数来增加一个值,您可以验证变量状态(即两次调用之间的变量值)是否持续存在。

在你开始之前

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

  • 您已经下载并安装了 DFINITY Canister SDK 包。。
  • 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。

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

创建一个新项目

要为本教程创建新的项目目录:

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

  2. 如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。

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

    dfx new my_counter
    

    该命令my_counter为您的项目创建一个新项目和 Git 存储库。

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

    cd my_counter
    

修改默认配置

你已经看到,创建一个新的项目增加了一个默认dfx.json的配置文件到您的项目目录。在本教程中,您将修改默认设置,使用不同的名称在您的项目主程序。

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

  1. 打开dfx.json在文本编辑器配置文件,更改默认main的设置main.moincrement_counter.mo

    例如:

    "main": "src/my_counter/increment_counter.mo",
    

    对于本教程,将源文件的名称从main.mo更改为increment_counter.mo仅说明dfx.json配置文件中的设置如何确定要编译的源文件。

    在更复杂的应用程序中,您可能有多个带有依赖项的源文件,您需要使用dfx.json配置文件中的设置来管理这些依赖项。在这样的场景中——在你的dfx.json文件中定义了多个容器和程序——同时命名多个文件main.mo可能会令人困惑。

    您可以保留其余的默认设置。

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

  3. dfx.json通过运行以下命令将源代码目录中的主程序文件的名称更改为与配置文件中指定的名称匹配

    mv src/my_counter/main.mo src/my_counter/increment_counter.mo
    

修改默认程序

到目前为止,您只更改了项目的主程序的名称。下一步是修改代码src/my_counter/increment_counter.mo文件中定义了一个名为一名演员Counter和落实incrementget以及set功能。

修改默认模板源代码:

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

  2. src/my_counter/increment_counter.mo在文本编辑器中打开文件并删除现有内容。

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

    // Create a simple Counter actor.
    actor Counter {
      stable var currentValue : Nat = 0;
    
      // Increment the counter with the increment function.
      public func increment() : async () {
        currentValue += 1;
      };
    
      // Read the counter value with a get function.
      public query func get() : async Nat {
        currentValue
      };
    
      // Write an arbitrary value with a set function.
      public func set(n: Nat) : async () {
        currentValue := n;
      };
    }
    

    跑步

    让我们仔细看看这个示例程序:

    • 您可以看到currentValue此示例中的变量声明包含stable用于指示状态的关键字——可以设置、递增和检索的值——保持不变。

      该关键字确保程序升级时变量的值不变。

    • currentValue变量的声明还指定其类型是自然 ( Nat) 数。

    • 该程序包括两个公共更新方法——theincrementset函数——以及一个查询方法,在本例中为get函数。

  4. 保存更改并关闭文件以继续。

启动本地网络

在构建my_counter项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。

在本地启动网络需要一个dfx.json文件,因此您应该确保您位于项目的根目录中。对于本教程,您应该有两个独立的终端外壳,以便您可以在一个终端中启动和查看网络操作,并在另一个终端中管理您的项目。

在本地启动网络:

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

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

    • 您现在应该打开了两个终端
    • 您应该将项目目录作为您当前的工作目录
  3. 通过运行以下命令在本地计算机上启动 Internet 计算机网络:

    dfx start
    

    启动本地网络后,终端会显示有关网络操作的消息。

  4. 让显示网络操作的终端保持打开状态,并将焦点切换到您创建新项目的原始终端。

注册、构建和部署应用程序

在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署您的应用程序。

在本地部署应用程序:

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

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

    dfx deploy
    

    dfx deploy关于它执行的操作命令输出显示信息。

调用已部署容器上的方法

成功部署容器后,您可以模拟最终用户调用容器提供的方法。在本教程中,您将调用一个get方法来查询计数器的值,一个increment在每次调用时递增计数器的set方法,以及一个传递参数以将计数器更新为您指定的任意值的方法。

要在已部署的容器上测试调用方法:

  1. 运行以下命令以调用该get函数,该函数读取currentValue已部署容器上变量的当前值:

    dfx canister call my_counter get
    

    该命令将currentValue变量的当前值返回为零:

    (0)
    
  2. 运行以下命令以调用increment函数将currentValue已部署容器上的变量值加1:

    dfx canister call my_counter increment
    

    该命令增加变量的值——改变它的状态——但不返回结果。

  3. 重新运行以下命令以获取currentValue已部署容器上变量的当前值:

    dfx canister call my_counter get
    

    该命令将currentValue变量的更新值返回为 1:

    (1)
    
  4. 运行其他命令来试验调用其他方法和使用不同的值。

    例如,尝试类似以下的命令来设置和返回计数器值:

    dfx canister call my_counter set '(987)'
    dfx canister call my_counter get
    

    返回 987 的当前值。

    dfx canister call my_counter increment
    dfx canister call my_counter get
    

    返回 988 的增量值。

在浏览器中测试功能

容器接口描述语言(通常称为 Candid 或更一般地称为 IDL)提供了一种用于指定容器服务签名的通用语言。Candid 为您提供了一种统一的方式来与用不同语言编写或使用不同工具访问的容器进行交互。例如,无论底层程序是原生 Rust、JavaScript 还是 Motoko,Candid 都提供了一致的服务视图。Candid 还支持不同的工具(例如dfx命令行界面和网络神经系统应用程序)来共享服务的通用描述。

基于 actor 的类型签名,Candid 还提供了一个 Web 界面,允许您调用容器函数进行测试和调试。

使用dfx deployordfx canister install命令在本地部署项目后,您可以在浏览器中访问 Candid Web 界面端点。这个 Web 界面(Candid UI)以一种形式公开服务描述,使您能够快速查看和测试功能并尝试输入不同的数据类型,而无需编写任何前端代码。

要使用 Candid Web 界面来测试容器功能:

  1. 使用dfx canister id __Candid_UI命令查找与当前项目关联的 Candid UI 容器标识符。

    dfx canister id __Candid_UI
    

    该命令显示 Candid UI 的容器标识符,输出类似于以下内容:

    r7inp-6aaaa-aaaaa-aaabq-cai
    
  2. 复制 Candid UI 容器标识符,以便它在剪贴板中可用。

  3. 如果您已停止 Internet 计算机,请通过运行以下命令在本地重新启动它:

    dfx start --background
    
  4. 打开浏览器并导航到dfx.json配置文件中指定的地址和端口号。

    默认情况下,local网络绑定到127.0.0.1:8000地址和端口号。

  5. canisterIddfx canister id命令返回的必需参数和 Candid UI 容器标识符添加到 URL。

    例如,完整的 URL 应该类似于以下内容,但带有CANDID-UI-CANISTER-IDENTIFIERdfx canister id命令返回的:

    http://127.0.0.1:8000/?canisterId=<CANDID-UI-CANISTER-IDENTIFIER>
    

    浏览器会显示一个表单,供您指定容器标识符或选择 Candid 描述 ( .did) 文件。

    如果您不确定要使用哪个容器标识符,您可以运行该dfx canister id命令来查找特定容器名称的标识符。

  6. 提供容器 ID字段中为您的应用程序指定容器标识符或描述文件,然后单击转到以显示服务描述。

  7. 查看程序中定义的函数调用和类型列表。

  8. 为函数键入适当类型的值,或单击“**随机”**生成值,然后单击“**调用”**或“**查询”**以查看结果。

    请注意,根据数据类型,Candid 界面可能会显示用于测试功能的其他配置设置。例如,如果函数接受一个数组,您可能需要在输入值之前指定数组中的项数。

在这里插入图片描述

停止本地网络

在您完成程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。

要停止本地网络:

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

  2. 通过运行以下命令停止 Internet 计算机网络:

    dfx stop
    

在计算器函数中使用整数

在本教程中,您将编写一个简单的计算器程序,该程序创建一个带有多个公共入口点函数的单个 actor,以执行基本的算术运算。

在本教程中,角色名为Calc。该程序使用该cell变量包含一个整数,表示计算器操作的当前结果。

该程序支持以下函数调用:

  • add函数调用接受输入和执行加法。
  • sub函数调用接受输入并执行减法。
  • mul函数调用接受输入,并执行乘法。
  • div函数调用接受输入和执行除法。
  • clearall函数清除cell作为先前操作结果存储的值,将该cell值重置为零。

div函数还包括防止程序试图除以零的代码。

在你开始之前

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

  • 您已经下载并安装了 DFINITY Canister SDK 包
  • 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。

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

创建一个新项目

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

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

  2. 如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。

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

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

    cd calc
    

修改默认配置

在本教程中,让我们修改默认dfx.json配置文件,为其主程序使用更具体的名称。

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

  1. dfx.json在文本编辑器中打开配置文件。

  2. main密钥设置从默认main.mo程序名称更改为calc_main.mo

    例如:

    "main": "src/calc/calc_main.mo"
    

    对于本教程,将源文件的名称从main.mo更改为calc_main.mo仅说明dfx.json配置文件中的设置如何确定要编译的源文件。

    在更复杂的应用程序中,您可能有多个源文件而不是单个main程序文件。更复杂的应用程序可能还具有多个源文件之间的特定依赖项,您需要使用dfx.json配置文件中的设置来管理这些源文件。在这样的场景中——在你的dfx.json文件中定义了多个容器和程序——将多个文件全部命名main.mo可能会使你的工作区导航变得更加困难。您为每个程序选择的名称并不重要,但重要的是您在dfx.json文件中设置的名称与文件系统中程序的名称相匹配。

  3. 保存更改并关闭文件以继续。

修改默认程序

对于本教程,您需要使用执行基本算术运算的程序替换默认程序。

替换默认程序:

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

  2. 复制模板main.mo文件以创建一个calc_main.mo通过运行以下命令命名的新文件:

    cp src/calc/main.mo src/calc/calc_main.mo
    
  3. src/calc/calc_main.mo在文本编辑器中打开文件并删除现有内容。

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

    // This single-cell calculator defines one calculator instruction per
    // public entry point (add, sub, mul, div).
    
    // Create a simple Calc actor.
    actor Calc {
      var cell : Int = 0;
    
      // Define functions to add, subtract, multiply, and divide
      public func add(n:Int) : async Int { cell += n; cell };
      public func sub(n:Int) : async Int { cell -= n; cell };
      public func mul(n:Int) : async Int { cell *= n; cell };
      public func div(n:Int) : async ?Int {
        if ( n == 0 ) {
          return null // null indicates div-by-zero error
        } else {
          cell /= n; ?cell
        }
      };
    
      // Clear the calculator and reset to zero
      public func clearall() : async Int {
        if (cell : Int != 0)
          cell -= cell;
        return cell
      };
     };
    

    您可能会注意到此示例代码使用整数 ( Int) 数据类型,使您可以使用正数或负数。如果您想将此计算器代码中的函数限制为仅使用正数,您可以将数据类型更改为仅允许自然 ( Nat) 数据。

  5. 保存更改并关闭文件以继续。

启动本地网络

在构建calc项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。

在本地启动网络需要一个dfx.json文件,因此您应该确保您位于项目的根目录中。对于本教程,您应该有两个独立的终端外壳,以便您可以在一个终端中启动和查看网络操作,并在另一个终端中管理您的项目。

在本地启动网络:

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

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

    • 您现在应该打开了两个终端
    • 您应该将项目目录作为您当前的工作目录
  3. 通过运行以下命令在本地计算机上启动 Internet 计算机网络:

    dfx start
    

    启动本地网络后,终端会显示有关网络操作的消息。

  4. 让显示网络操作的终端保持打开状态,并将焦点切换到您创建新项目的原始终端。

注册、构建和部署应用程序

在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署您的应用程序。

在本地部署应用程序:

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

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

    dfx deploy
    

    dfx deploy关于它执行的操作命令输出显示信息。

验证容器上的计算器功能

您现在已将程序部署为本地 Internet 计算机网络上的容器。您可以使用dfx canister call命令来测试程序。

要测试您已部署的程序:

  1. 使用该dfx canister call命令调用calc容器add函数并通过10运行以下命令将输入参数传递给它:

    dfx canister call calc add '(10)'
    

    当您传递由单引号和括号括起来的参数时,接口描述语言 (IDL) 会解析参数类型,因此您无需手动指定参数类型。

    验证命令返回add函数的预期值。例如,程序显示类似于以下内容的输出:

    (10)
    
  2. 通过运行以下命令调用该mul函数并将输入参数传递给它3

    dfx canister call calc mul '(3)'
    

    验证命令返回mul函数的预期值。例如,程序显示类似于以下内容的输出:

    (30)
    
  3. 通过运行以下命令调用该sub函数并将5type的输入参数传递给它number

    dfx canister call calc sub '(5)'
    

    验证命令返回sub函数的预期值。例如,程序显示类似于以下内容的输出:

    (25)
    
  4. 通过运行以下命令调用该div函数并将输入参数传递给它5

    dfx canister call calc div '(5)'
    

    验证命令返回div函数的预期值。例如,程序显示类似于以下内容的输出:

    (选项 5)
    

    您可能会注意到该div函数返回一个可选结果。程序将结果设为可选,以便div函数null在出现被零除错误的情况下返回。

    因为这个程序中的单元格变量是一个整数,你也可以调用它的函数并指定负输入值。例如,您可以运行以下命令:

    dfx canister call calc mul '(-4)'复制
    

    返回:

    (-20)
    
  5. 调用该clearall函数并验证它是否将cell值重置为零:

    dfx canister call calc clearall
    

    例如,程序显示类似于以下内容的输出:

    (0)
    

在浏览器中测试功能

容器接口描述语言(通常称为 Candid 或更一般地称为 IDL)提供了一种用于指定容器服务签名的通用语言。Candid 为您提供了一种统一的方式来与用不同语言编写或使用不同工具访问的容器进行交互。例如,无论底层程序是原生 Rust、JavaScript 还是 Motoko,Candid 都提供了一致的服务视图。Candid 还支持不同的工具(例如dfx命令行界面和网络神经系统应用程序)来共享服务的通用描述。

基于 actor 的类型签名,Candid 还提供了一个 Web 界面,允许您调用容器函数进行测试和调试。

使用dfx deployordfx canister install命令在本地部署项目后,您可以在浏览器中访问 Candid Web 界面端点。这个 Web 界面(Candid UI)以一种形式公开服务描述,使您能够快速查看和测试功能并尝试输入不同的数据类型,而无需编写任何前端代码。

要使用 Candid Web 界面来测试容器功能:

  1. 使用dfx canister id __Candid_UI命令查找与当前项目关联的 Candid UI 容器标识符。

    dfx canister id __Candid_UI
    

    该命令显示 Candid UI 的容器标识符,输出类似于以下内容:

    r7inp-6aaaa-aaaaa-aaabq-cai
    
  2. 复制 Candid UI 容器标识符,以便它在剪贴板中可用。

  3. 如果您已停止 Internet 计算机,请通过运行以下命令在本地重新启动它:

    dfx start --background
    
  4. 打开浏览器并导航到dfx.json配置文件中指定的地址和端口号。

    默认情况下,local网络绑定到127.0.0.1:8000地址和端口号。

  5. canisterIddfx canister id命令返回的必需参数和 Candid UI 容器标识符添加到 URL。

    例如,完整的 URL 应该类似于以下内容,但带有CANDID-UI-CANISTER-IDENTIFIERdfx canister id命令返回的:

    http://127.0.0.1:8000/?canisterId=<CANDID-UI-CANISTER-IDENTIFIER>
    

    浏览器会显示一个表单,供您指定容器标识符或选择 Candid 描述 ( .did) 文件。

    如果您不确定要使用哪个容器标识符,您可以运行该dfx canister id命令来查找特定容器名称的标识符。

  6. 提供容器 ID字段中为您的应用程序指定容器标识符或描述文件,然后单击转到以显示服务描述。

  7. 查看程序中定义的函数调用和类型列表。

  8. 为函数键入适当类型的值,或单击“**随机”**生成值,然后单击“**调用”**或“**查询”**以查看结果。

    请注意,根据数据类型,Candid 界面可能会显示用于测试功能的其他配置设置。例如,如果函数接受一个数组,您可能需要在输入值之前指定数组中的项数。

在这里插入图片描述

停止本地网络

在您完成程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。

要停止本地网络:

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

  2. 通过运行以下命令停止 Internet 计算机网络:

    dfx stop
    

导入库模块

在本教程中,您将编写一个简单的程序,使您能够存储和查找电话号码。本教程说明了如何导入和使用一些基本的 Motoko 库函数。

在本教程中,Motoko 基本库函数在ListAssocList模块中定义 ,使您能够将列表作为链接的键值对进行处理。在此示例中,是 a namephone与该名称关联的文本字符串。

该程序支持以下函数调用:

  • insert函数接受namephone键值对作为存储在book变量中的输入。
  • lookup函数是一个查询,它使用指定的name键作为输入来查找相关联的电话号码。

在你开始之前

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

  • 您已经下载并安装了 DFINITY Canister SDK 包
  • 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。

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

创建一个新项目

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

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

  2. 如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。

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

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

    cd phonebook
    

修改默认程序

在本教程中,让我们main.mo为简单的电话号码查找程序创建一个新文件。

修改默认模板:

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

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

    // Import standard library functions for lists
    
    import L "mo:base/List";
    import A "mo:base/AssocList";
    
    // The PhoneBook actor.
    actor {
    
        // Type aliases make the rest of the code easier to read.
        public type Name = Text;
        public type Phone = Text;
    
        // The actor maps names to phone numbers.
        flexible var book: A.AssocList<Name, Phone> = L.nil<(Name, Phone)>();
    
        // An auxiliary function checks whether two names are equal.
        func nameEq(l: Name, r: Name): Bool {
            return l == r;
        };
    
        // A shared invokable function that inserts a new entry
        // into the phone book or replaces the previous one.
        public func insert(name: Name, phone: Phone): async () {
            let (newBook, _) = A.replace<Name, Phone>(book, name, nameEq, ?phone);
            book := newBook;
        };
    
        // A shared read-only query function that returns the (optional)
        // phone number corresponding to the person with the given name.
        public query func lookup(name: Name): async ?Phone {
            return A.find<Name, Phone>(book, name, nameEq);
        };
    };
    

    在查看此示例程序时,您可能会注意到以下关键元素:

    • 代码定义NamePhone作为自定义文本类型。创建用户定义的类型可以提高代码的可读性。
    • insert函数是一个更新调用,该lookup函数是一个查询调用。
    • Phone使用?Phone语法将该类型标识为可选值。

启动本地网络

在构建phonebook项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。

在本地启动网络需要一个dfx.json文件,因此您应该确保您位于项目的根目录中。对于本教程,您应该有两个独立的终端外壳,以便您可以在一个终端中启动和查看网络操作,并在另一个终端中管理您的项目。

在本地启动网络:

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

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

    • 您现在应该打开了两个终端
    • 您应该将项目目录作为您当前的工作目录
  3. 通过运行以下命令在本地计算机上启动 Internet 计算机网络:

    dfx start --clean
    

    在本教程中,我们使用该--clean选项以干净的状态启动 Internet 计算机网络。

    此选项会删除任何可能中断正常操作的孤立后台进程或容器标识符。例如,如果您dfx stop在项目之间移动时忘记发出 a ,则可能有一个进程在后台或另一个终端中运行。该--clean选项确保您可以启动 Internet 计算机网络并继续下一步,而无需手动查找和终止任何正在运行的进程。

  4. 让显示网络操作的终端保持打开状态,并将焦点切换到您创建新项目的原始终端。

注册、构建和部署应用程序

在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署您的应用程序。

在本地部署应用程序:

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

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

    dfx deploy phonebook
    

    dfx.json文件提供用于创建应用程序前端入口点和assets容器的默认设置。

    在之前的教程中,我们删除了资产容器的条目,因为我们没有为示例应用程序添加前端。通过消除未使用的文件,该更改使我们的项目工作区保持整洁。但是,没有要求这样做,并且将资产容器描述留在dfx.json文件中也没有坏处。例如,如果您打算稍后添加前端资产,您可能希望将其用作占位符。

    对于本教程,您可以使用该dfx deploy phonebook命令仅部署电话簿后端容器,因为该项目是基于终端的应用程序,不包含任何前端资产。

    加名称和数字

您现在已将程序部署为本地副本网络上的容器,并且可以使用dfx canister call命令测试您的程序。

要测试您在本地副本网络上部署的程序:

  1. 使用该dfx canister call命令phonebook使用该insert函数调用容器,并通过运行以下命令将名称和电话号码传递给它:

    dfx canister call phonebook insert '("Chris Lynn", "01 415 792 1333")'
    
  2. 通过运行以下命令添加第二个名称和号码对:

    dfx canister call phonebook insert '("Maya Garcia", "01 408 395 7276")'
    
  3. lookup通过运行以下命令,验证该命令是否使用该函数返回了与“Chris Lynn”关联的号码:

    dfx canister call phonebook lookup '("Chris Lynn")'
    

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

    (opt "01 415 792 1333")
    
  4. lookup通过运行以下命令,尝试使用与“Maya Garcia”关联的号码调用该函数:

    dfx canister call phonebook lookup '("01 408 395 7276")'
    

    请注意,在这种情况下,该命令会返回,(null)因为电话号码不是与“Maya Garcia”名称条目关联的键。

  5. 尝试lookup通过运行以下命令再次调用该函数以返回“Maya Garcia”和“Chris Lynn”的电话号码:

    dfx canister call phonebook lookup '("Maya Garcia","Chris Lynn")'
    

    因为该程序被编写为为一个键返回一个值,所以该命令仅返回与第一个键相关联的信息,在本例中为 的电话号码Maya Garcia

在浏览器中测试功能

容器接口描述语言(通常称为 Candid 或更一般地称为 IDL)提供了一种用于指定容器服务签名的通用语言。Candid 为您提供了一种统一的方式来与用不同语言编写或使用不同工具访问的容器进行交互。例如,无论底层程序是原生 Rust、JavaScript 还是 Motoko,Candid 都提供了一致的服务视图。Candid 还支持不同的工具(例如dfx命令行界面和网络神经系统应用程序)来共享服务的通用描述。

基于 actor 的类型签名,Candid 还提供了一个 Web 界面,允许您调用容器函数进行测试和调试。

使用dfx deployordfx canister install命令在本地部署项目后,您可以在浏览器中访问 Candid Web 界面端点。这个 Web 界面(Candid UI)以一种形式公开服务描述,使您能够快速查看和测试功能并尝试输入不同的数据类型,而无需编写任何前端代码。

要使用 Candid Web 界面来测试容器功能:

  1. 使用dfx canister id __Candid_UI命令查找与当前项目关联的 Candid UI 容器标识符。

    dfx canister id __Candid_UI
    

    该命令显示 Candid UI 的容器标识符,输出类似于以下内容:

    r7inp-6aaaa-aaaaa-aaabq-cai
    
  2. 复制 Candid UI 容器标识符,以便它在剪贴板中可用。

  3. 如果您已停止 Internet 计算机,请通过运行以下命令在本地重新启动它:

    dfx start --background
    
  4. 打开浏览器并导航到dfx.json配置文件中指定的地址和端口号。

    默认情况下,local网络绑定到127.0.0.1:8000地址和端口号。

  5. canisterIddfx canister id命令返回的必需参数和 Candid UI 容器标识符添加到 URL。

    例如,完整的 URL 应该类似于以下内容,但带有CANDID-UI-CANISTER-IDENTIFIERdfx canister id命令返回的:

    http://127.0.0.1:8000/?canisterId=<CANDID-UI-CANISTER-IDENTIFIER>
    

    浏览器会显示一个表单,供您指定容器标识符或选择 Candid 描述 ( .did) 文件。

    如果您不确定要使用哪个容器标识符,您可以运行该dfx canister id命令来查找特定容器名称的标识符。

  6. 提供容器 ID字段中为您的应用程序指定容器标识符或描述文件,然后单击转到以显示服务描述。

  7. 查看程序中定义的函数调用和类型列表。

  8. 为函数键入适当类型的值,或单击“**随机”**生成值,然后单击“**调用”**或“**查询”**以查看结果。

    请注意,根据数据类型,Candid 界面可能会显示用于测试功能的其他配置设置。例如,如果函数接受一个数组,您可能需要在输入值之前指定数组中的项数。

在这里插入图片描述

修改程序中的源代码

为了扩展您在本教程中学到的知识,您可能想要尝试修改源代码以返回不同的结果。

例如,您可能希望更改源代码,而不是插入和查找当前键值(姓名-电话)对的程序来创建一个程序,该程序存储类似于数据库“记录”的联系信息,其中一个主键与多个字段相关联。在此示例中,您的程序可能允许用户或其他程序添加信息(例如家庭电话号码、手机号码、电子邮件地址和街道地址),并有选择地返回所有或特定字段值。

停止本地网络

在您完成程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。

要停止本地网络:

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

  2. 通过运行以下命令停止 Internet 计算机网络:

    dfx stop
    

使用多个actor

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

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

  • assistant演员提供函数来在待办事项列表中添加和演出任务。

    为简单起见,本教程的代码示例仅包含添加待办事项和显示当前已添加待办事项列表的功能。这个程序的一个更完整的版本 - 带有用于将项目标记为完整和从列表中删除项目的附加功能 - 在示例存储库中作为Simple to-do checklist 提供

  • rock_paper_scissors演员提供了一个硬编码的石头,剪子,布的比赛确定一个胜利者的功能。

    此代码示例演示了基本的使用switchcase与硬编码的球员和选择一个元子程序。

  • daemon演员提供模拟功能,启动和停止守护程序。

    此代码示例只是为演示目的分配一个变量并打印消息

在你开始之前

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

  • 您已经下载并安装了 DFINITY Canister SDK 包
  • 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。

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

创建一个新项目

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

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

  2. 如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。

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

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

    cd multiple_actors
    

修改默认配置

你已经看到,创建一个新的项目增加了一个默认dfx.json的配置文件到您的项目目录。对于本教程,您需要向此文件添加部分以指定定义要构建的演员的每个程序的位置。

修改默认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,并为容器定义daemon下方的程序文件添加新的容器名称、源代码位置和容器类型assistant

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

    {
          
          
      "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.7.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_scissorsactor的主程序文件:

    cp -r src/assistant/ src/rock_paper_scissors/
    
  7. 通过运行以下命令复制assistant源文件目录以创建daemonactor的主程序文件:

    cp -r src/assistant/ src/daemon/
    

修改默认程序

您现在在目录中有三个独立的src目录,每个目录都有一个模板main.mo文件。在本教程中,您将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 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。

在本地启动网络:

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

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

  3. 通过运行以下命令在本地计算机上启动 Internet 计算机网络:

    dfx start
    
  4. 让显示网络操作的终端保持打开状态,并将焦点切换到您创建新项目的原始终端。

注册、构建和部署您的应用程序

连接到在开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署多容器应用程序。

在本地部署应用程序:

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

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

    dfx deploy
    

    如果您有权访问远程运行的 Internet 计算机,则可以通过指定--network选项和dfx.json文件中配置的网络别名来部署到该网络,而不是在本地部署。例如,如果您要连接到由网络别名指定的 URL,ic您将运行类似以下的命令:

    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"
    

通过调用函数验证部署

您现在已将三个程序部署为本地副本网络上的容器,并且可以使用dfx canister call命令测试每个程序。

要测试您已部署的程序:

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

    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命令调用rock_paper_scissors使用该contest函数的容器:

    dfx canister call rock_paper_scissors contest
    

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

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

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

    (""The daemon process is running"")
    

停止本地网络

在您完成程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。

要停止本地网络:

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

  2. 通过运行以下命令停止 Internet 计算机网络:

    dfx stop
    

自定义前端

既然您对如何创建、构建和部署简单程序有了基本的了解,并且熟悉默认项目文件和示例前端,您可能想要开始尝试不同的方式来自定义前端用户体验为您的项目。

本教程演示了如何使用简单的 React 框架为默认示例程序创建新的前端,并指导您完成一些基本修改以自定义显示的界面。后面的教程会扩展这里介绍的技术,但如果您已经知道如何使用 CSS、HTML、JavaScript 和 React 或其他框架来构建您的用户界面,则可以跳过本教程。

本教程说明了如何使用 React 框架来管理容器的文档对象模型 (DOM)。因为 React 有自己自定义的 DOM 语法,所以需要修改 webpack 配置来编译前端代码,前端代码是用 JSX 编写的。有关学习使用 React 和 JSX 的更多信息,请参阅React 网站上的入门

在你开始之前

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

  • 您已node.js安装用于前端开发,并且可以安装npm install在您的项目中使用的包。有关为本地操作系统和包管理器安装 node 的信息,请参阅Node网站。
  • 您已经下载并安装了 DFINITY Canister SDK 包
  • 如果您使用 Visual Studio Code 作为 IDE,您已经为 Motoko 安装了 Visual Studio Code 插件。
  • 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。

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

创建一个新项目

为您的自定义前端应用程序创建一个新的项目目录:

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

  2. 如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。

  3. node.js通过运行以下命令检查您是否已在本地安装:

    which node
    which npm
    

    如果您尚未node.js安装,则应先下载并安装它,然后再继续下一步。有关为本地操作系统和包管理器安装 node 的信息,请参阅Node网站。

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

    dfx new custom_greeting
    

    dfx new custom_greeting命令创建一个新custom_greeting项目。

  5. 通过运行以下命令切换到您的项目目录:

    cd custom_greeting
    

安装 React 框架

如果您以前从未使用过 React,那么在编辑前端代码之前,您可能想浏览React教程或React 网站

要安装所需的框架模块:

  1. 通过运行以下命令安装 React 模块:

    npm install --save react react-dom
    
  2. 通过运行以下命令安装所需的 TypeScript 语言编译器加载器:

    npm install --save typescript ts-loader复制

    {
      "name": "custom_greeting_assets",
      "version": "0.1.0",
      "description": "",
      "keywords": [],
      "scripts": {
        "build": "webpack"
      },
      "devDependencies": {
        "@dfinity/agent": "0.7.2",
        "assert": "2.0.0",
        "buffer": "6.0.3",
        "events": "3.3.0",
        "html-webpack-plugin": "5.3.1",
        "process": "0.11.10",
        "stream-browserify": "3.0.0",
        "terser-webpack-plugin": "5.1.1",
        "util": "0.12.3",
        "webpack": "5.24.4",
        "webpack-cli": "4.5.0"
      },
      "dependencies": {
        "react": "^17.0.2",
        "react-dom": "^17.0.2",
        "ts-loader": "^8.1.0",
        "typescript": "^4.2.4"
      }
    }
    

查看默认配置

在我们对本教程使用 React 进行任何更改之前,让我们回顾一下dfx.json您项目的配置文件中的默认前端设置。

查看默认dfx.json配置文件:

  1. dfx.json在文本编辑器中打开配置文件。

  2. 请注意,该canisters键包含custom_greeting_assets容器的设置。

    {
      "canisters": {
        ...
    
        "custom_greeting_assets": {
          "dependencies": [
            "custom_greeting"
          ],
          "frontend": {
            "entrypoint": "src/custom_greeting_assets/src/index.html"
          },
          "source": [
            "src/custom_greeting_assets/assets",
            "dist/custom_greeting_assets/"
          ],
          "type": "assets"
        }
      }
    }
    

    我们来看看本节中的设置。

    • 项目的前端资产被编译到它们自己的容器中,在本例中,名为custom_greeting_assets.
    • 资产容器默认依赖于项目的主容器。
    • frontend.entrypoint设置指定index.html要用作应用程序入口点的文件(在本例中为文件)的路径。如果您有不同的起点(例如,自定义first-page.html文件),您将修改此设置。
    • 这些source设置指定了您的srcdist目录的路径。该src设置指定用于在构建项目时包含在资产容器中的静态资产的目录。如果您有自定义级联样式表 (CSS) 或 JavaScript 文件,您可以将它们包含在此路径指定的文件夹中。构建项目后,项目资产将从dist设置指定的目录中提供。
    • type设置指定custom_greeting_assets是资产容器而不是程序容器。

    在本教程中,我们将在index.jsx文件中添加 React JavaScript ,但这不需要对dfx.json文件中的默认设置进行任何更改。

  3. 关闭dfx.json文件以继续。

查看默认的前端文件

对于本教程,您将使用默认main.mo程序并且仅通过修改前端来操作应用程序。不过,在进行任何更改之前,让我们先看看项目的默认前端文件中的内容。

查看默认的前端文件:

  1. src/custom_greeting_assets/src/index.html在文本编辑器中打开文件。

    此模板文件是应用程序的默认前端入口点,由文件中的frontend.entrypoint设置指定dfx.json

    此文件包含标准 HTML,其中包含对 CSS 文件和位于src/custom_greeting_assets/assets目录中的图像的引用。默认index.html文件还包括用于显示name参数的输入字段和可点击按钮的标准 HTML 语法。

    这与您在查看默认前端 中看到的默认前端相同

  2. src/custom_greeting_assets/src/index.js在文本编辑器中打开文件。

    import { Actor, HttpAgent } from '@dfinity/agent';
    import { idlFactory as custom_greeting_idl, canisterId as custom_greeting_id } from 'dfx-generated/custom_greeting';
    
    const agent = new HttpAgent();
    const custom_greeting = Actor.createActor(custom_greeting_idl, { agent, canisterId: custom_greeting_id });
    
    document.getElementById("clickMeBtn").addEventListener("click", async () => {
      const name = document.getElementById("name").value.toString();
      const greeting = await custom_greeting.greet(name);
    
      document.getElementById("greeting").innerText = greeting;
    });
    
    • import默认index.js文件中的第一条语句使用 JavaScript 代理库创建一个参与者和一个代理实例。
    • 第二个import语句为custom_greeting要构造的actor 对象准备容器。
    • 接下来的两个语句构造代理和参与者对象。
    • 其余行为默认应用程序提供文档对象处理。
  3. 关闭index.js文件以继续。

修改前端文件

您现在已准备好为默认程序创建新的前端。

准备前端文件:

  1. webpack.config.js在文本编辑器中打开 webpack 配置文件 ( )。

  2. 修改前端进入替换默认index.html使用index.jsx

    entry: {
          
          
      // The frontend.entrypoint points to the HTML file for this build, so we need
      // to replace the extension to `.js`.
      index: path.join(__dirname, info.frontend.entrypoint).replace(/\.html$/, ".jsx"),
    },
    
  3. moduleplugins部分上方添加以下键:

    module: {
          
          
      rules: [
        {
          
           test: /\.(js|ts)x?$/, loader: "ts-loader" }
      ]
    },
    

    此设置使程序能够将ts-loader编译器用于 React JavaScriptindex.jsx文件。请注意,默认webpack.config.js文件中有一个注释部分,您可以修改该部分以添加module密钥。

  4. tsconfig.json在您的项目的根目录中创建一个名为的新文件。

  5. tsconfig.json在文本编辑器中打开文件,然后将以下内容复制并粘贴到文件中:

    {
          
          
        "compilerOptions": {
          
          
          "target": "es2018",        /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
          "lib": ["ES2018", "DOM"],  /* Specify library files to be included in the compilation. */
          "allowJs": true,           /* Allow javascript files to be compiled. */
          "jsx": "react",            /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
        },
        "include": ["src/**/*"],
    }
    
  6. 保存更改并关闭tsconfig.json文件以继续。

  7. src/custom_greeting_assets/src/index.js在文本编辑器中打开默认文件并删除第 7 到 12 行。

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

    // Insert these lines after the import statements for
    // importing an agent and an actor
    import * as React from 'react';
    import { render } from 'react-dom';
    
    // Replace the default index.js content with
    // React JavaScript
    class MyHello extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          name: 'Name',
          message: '',
        };
      }
    
      async doGreet() {
        const greeting = await custom_greeting.greet(this.state.name);
        this.setState({ ...this.state, message: greeting });
      }
    
      onNameChange(ev) {
        this.setState({ ...this.state, name: ev.target.value });
      }
    
      render() {
        return (
          <div style={
         
         { "font-size": "30px" }}>
            <div style={
         
         { "background-color": "yellow" }}>
              <p>Greetings, from DFINITY!</p>
              <p> Type your message in the Name input field, then click <b> Get Greeting</b> to display the result.</p>
            </div>
            <div style={
         
         { "margin": "30px" }}>
              <input id="name" value={this.state.name} onChange={ev => this.onNameChange(ev)}></input>
              <button onClick={() => this.doGreet()}>Get Greeting!</button>
            </div>
            <div>Greeting is: "<span style={
         
         { "color": "blue" }}>{this.state.message}</span>"</div>
          </div>
        );
      }
    }
    
    render(<MyHello />, document.getElementById('app'));
    
  9. 通过运行以下命令将修改后的index.js文件重命名为index.jsx

    mv src/custom_greeting_assets/src/index.js src/custom_greeting_assets/src/index.jsx
    
  10. src/custom_greeting_assets/src/index.html在文本编辑器中打开默认文件,然后将正文内容替换为<div id="app"></div>.

    例如:

    <!doctype html>
    <html lang="en">
     <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width">
        <title>custom_greeting</title>
        <base href="/">
        <link type="text/css" rel="stylesheet" href="main.css" />
     </head>
     <body>
        <div id="app"></div>
     </body>
    </html>
    

启动本地网络

在构建custom_greeting项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。

在本地启动网络:

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

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

  3. 通过运行以下命令在本地计算机上启动 Internet 计算机网络:

    dfx start --background
    

    在本地 Internet 计算机网络完成其启动操作后,您可以继续下一步。

注册、构建和部署应用程序

在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署您的应用程序。

在本地部署应用程序:

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

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

    dfx deploy复制
    

    dfx deploy关于它执行的操作命令输出显示信息。

  3. 将容器的容器标识符复制custom_greeting_assets到剪贴板或记事本应用程序。

    如果您没有记下容器标识符,则可以通过运行以下命令来查找它:

    dfx canister id custom_greeting_assets
    

查看新的前端

您现在可以通过在浏览器中输入资产容器的容器标识符来访问默认程序的新前端。

查看自定义前端:

  1. 打开浏览器并导航到配置文件中local指定的网络地址和端口号dfx.json

    例如,如果您使用本地网络的默认绑定,请导航到127.0.0.1:8000/

    要指定您希望 Web 服务器显示canisterIdcustom_greeting_assets容器,请使用以下语法将参数和 容器标识符添加到 URL:

    ?canisterId=<YOUR-ASSET-CANISTER-IDENTIFIER>
    

    例如,完整 URL 应类似于以下内容,但在部署容器时_canister_identifier_custom_greeting_assets容器返回:

    http://127.0.0.1:8000/?canisterId=cxeji-wacaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q
    
  2. 确认系统提示您键入问候语。

    例如:

    在这里插入图片描述

  3. 将输入字段中的Name替换为您要显示的文本,然后单击Get Greeting以查看结果。

    例如:

在这里插入图片描述

修改前端并测试您的更改

查看前端后,您可能需要进行一些更改。

修改前端:

  1. index.jsx在文本编辑器中打开文件并修改其样式设置。例如,您可能希望通过进行类似于以下的更改来更改字体系列并为输入字段使用占位符:

    // Modify the front-end in the React JavaScript
    class MyHello extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          name: '',
          message: '',
        };
      }
    
      async doGreet() {
        const greeting = await custom_greeting.greet(this.state.name);
        this.setState({ ...this.state, message: greeting });
      }
    
      onNameChange(ev) {
        this.setState({ ...this.state, name: ev.target.value });
      }
    
      render() {
        return (
          <div style={
         
         { "font-family": "sans-serif" }}>
          <div style={
         
         { "font-size": "30px" }}>
              <p>Greetings, from DFINITY!</p>
              <p> Type your message in the Name input field, then click <b> Get Greeting</b> to display the result.</p>
    
            <div style={
         
         { "margin": "30px" }}>
              <input id="name" placeholder="Type text here" value={this.state.name} onChange={ev => this.onNameChange(ev)}></input>
              <button onClick={() => this.doGreet()}>Get Greeting!</button>
            </div>
            <div>Greeting is: "<span style={
         
         { "color": "green" }}>{this.state.message}</span>"</div>
          </div>
        </div>
        );
      }
    }
    
    render(<MyHello />, document.getElementById('app'));
    
  2. 通过运行以下命令,使用您的更改重新构建项目。

    dfx build
    
  3. 通过运行以下命令部署您的项目更改:

    dfx canister install --all --mode reinstall
    
  4. 通过重新加载显示custom_greeting_assets容器的页面在浏览器中查看结果。

    例如:

在这里插入图片描述

  1. 键入一条新消息并查看您的新问候语。例如:

在这里插入图片描述

停止本地网络

在您完成程序前端的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。

要停止本地网络:

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

  2. 通过运行以下命令停止 Internet 计算机网络:

    dfx stop
    

添加样式表

级联样式表是为应用程序定制用户体验的最常见方式之一。本教程说明了如何在使用 React 为项目创建新前端时添加样式表。如果您已经知道如何将级联样式表 (CSS) 添加到基于 React 的项目中,则可以跳过本教程。

此处不做过多赘述

进行容器间的调用

对于开发人员而言,Internet 计算机最重要的功能之一是能够从另一个容器中的程序构建、部署和调用一个容器中的共享功能。这种在容器之间进行调用的功能(有时也称为容器间调用)使您能够在多个应用程序中重用和共享功能。

例如,您可能想要创建一个用于专业网络、组织社区活动或举办筹款活动的应用程序。这些应用程序中的每一个都可能有一个社交组件,使用户能够根据某些标准或共同兴趣(例如朋友和家人或现任和前任同事)识别社交关系。

为了解决这个社交组件,您可能想要创建一个用于存储用户关系的容器,然后编写您的专业网络、社区组织或筹款应用程序来导入和调用容器中定义的用于社交关系的函数。然后,您可以构建其他应用程序来使用社交联系容器或扩展社交联系容器提供的功能,使其对更广泛的其他开发人员社区有用。

基于 Motoko 的 LinkedUp 示例应用程序提供了一个开放专业网络的简单实现,演示了如何在项目中使用容器间调用。

LinkedUp 示例应用程序是使用以下容器实现的:

  • linkedup容器创建并存储为用户的基本个人资料,包括工作经历和教育背景。
  • connectd容器创建并存储用户的连接。
  • linkedup_assets容器存储的前端资产,包括JavaScript的,HTML和CSS文件,定义用户界面

在你开始之前

在构建示例应用程序之前,请验证以下内容:

  • 您已经下载并安装了 DFINITY Canister SDK 包
  • 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。

下载演示

要使用 LinkedUp 示例应用程序试验容器间调用:

  1. 打开终端外壳并切换到您用于 Internet 计算机示例项目的文件夹。

  2. 克隆linkedup存储库。

    git clone https://github.com/dfinity/linkedup.git
    
  3. 更改到linkedup存储库的本地工作目录。

    cd linkedup
    
  4. 通过运行以下命令安装节点模块:

    npm install
    

    如有必要,请通过运行以下命令修复发现的任何漏洞:

    npm audit fix
    
  5. dfx.json在文本编辑器中打开文件并验证dfx设置与dfx您安装的可执行文件的版本号相同。

启动本地网络

在构建linkedup项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。

在本地启动网络:

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

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

  3. 通过运行以下命令在本地计算机上启动 Internet 计算机网络:

    dfx start --background复制

    在本地 Internet 计算机网络完成其启动操作后,您可以继续下一步。

注册容器标识符

在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以向网络注册以生成您项目的唯一容器标识符。

要为本地网络注册容器标识符:

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

  2. 通过运行以下命令为项目注册唯一的容器标识符:

    dfx canister create --all
    

    该命令显示dfx.json配置文件中定义的容器的特定于网络的容器标识符。

    "connectd" canister created with canister id: "75hes-oqbaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q"
    "linkedup" canister created with canister id: "cxeji-wacaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q"
    "linkedup_assets" canister created with canister id: "7kncf-oidaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q"
    

    请记住,由于您在本地运行 Internet 计算机,因此这些标识符仅在本地网络上有效。要在远程网络上部署容器,您必须使用--network命令行选项和特定网络名称或地址连接到该网络,以在该网络上注册标识符。

构建和部署演示项目

要构建和部署 LinkUp 示例应用程序,请执行以下步骤:

  1. pwd如有必要,请通过运行命令检查您是否仍在项目目录中。

  2. 通过运行以下命令构建 LinkedUp 容器:

    dfx build
    
  3. 通过运行以下命令在本地网络上部署项目:

    dfx canister install --all
    

    您应该会看到和容器的容器标识符connectdlinkeduplinkedup_assets带有类似于以下内容的消息:

    Installing code for canister connectd, with canister_id 75hes-oqbaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q
    Installing code for canister linkedup, with canister_id cxeji-wacaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q
    Installing code for canister linkedup_assets, with canister_id 7kncf-oidaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q
    
  4. 复制命令linkedup_assets返回的容器标识符dfx canister install

    在此示例应用程序中,只有linkedup_assets容器包含用于访问应用程序功能的前端资产。因此,要在浏览器中打开应用程序,您需要指定linkedup_assets容器标识符。

  5. linkedup_assets在 Web 浏览器中打开容器。

    例如,如果绑定到默认的 localhost 地址和端口号,则 URL 看起来类似于:

    http://127.0.0.1:8000/?canisterId=7kncf-oidaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q
    

创建配置文件和连接

要运行 LinkedUp 示例应用程序的演示,请执行以下步骤:

  1. 打开浏览器选项卡或窗口。

  2. 键入 Web 服务器主机名、端口和canisterId关键字,然后将linkedup_assets容器标识符粘贴为要显示的 URL。

    127.0.0.1:8000/?canisterId=<ic-identifier-for-linkedup-assets>
    

    浏览器显示一个介绍页面。

    将自动生成公私钥对来建立您访问容器的身份,因此在使用该服务之前无需提供用户名和密码或注册帐户来存储您的身份。

  3. 点击登录

    浏览器显示一个空的个人资料页面。

    在这里插入图片描述

  4. 点击编辑,输入个人资料信息,复制并粘贴头像照片的图片地址,然后点击提交

    在这里插入图片描述

    单击Submit 后,您将拥有一个包含一些可以查看的工作历史记录的个人资料。

    例如:

在这里插入图片描述

添加另一个配置文件

此时,没有其他配置文件要搜索或添加为连接。要试用搜索和连接功能,您可以:

  • 运行一个脚本,用一些额外的配置文件填充示例应用程序。
  • 通过打开私人窗口手动创建配置文件。

对于本教程,您将手动创建另一个配置文件。

要添加具有不同身份的用户配置文件:

  1. 在浏览器窗口的右上角,单击相应的图标以显示浏览器的菜单选项。

    例如,如果您使用的是 Google Chrome,则单击垂直椭圆以显示“更多”菜单。

  2. 如果您使用的是 Google Chrome,请单击“**新建隐身窗口”,如果您使用的是 Firefox,请单击“****新建隐私窗口”,**这样您就可以导航到容器,而无需在与容器的初始浏览器连接中生成用户身份。

  3. 将第一个浏览器会话中的 URL 复制并粘贴到隐私浏览窗口中,然后点击登录

    在这里插入图片描述

    请注意,隐私浏览窗口中没有配置文件,但您的原始配置文件在初始浏览器选项卡中仍然可见。

  4. 点击编辑,输入个人资料信息,复制并粘贴头像照片的图片地址,然后点击提交

在这里插入图片描述

单击Submit 后,您将获得第二个个人资料,其中包含一些可以查看的工作历史记录。

例如:

在这里插入图片描述

  1. 键入您创建的第一个配置文件中的名字或姓氏(例如,如果您为 Maya Garcia 创建了配置文件,请键入 Maya),然后单击“搜索”

在这里插入图片描述

将显示与您的搜索条件匹配的配置文件。

在这里插入图片描述

  1. 从搜索结果中选择联系人,等待显示连接按钮,然后点击连接

在这里插入图片描述

当连接请求完成时,第二个配置文件显示与第一个配置文件的连接。例如:

在这里插入图片描述

  1. 使用您的原始配置文件返回浏览器选项卡。

    如果您想在原始配置文件和您在隐私浏览窗口中创建的配置文件之间创建连接,您可以通过重复搜索、选择和连接步骤来实现。

探索配置文件

既然您已经了解了示例应用程序的基本功能,您就可以了解如何使用配置设置和源文件的一些上下文。

要浏览配置文件:

  1. 切换到linkedup目录,然后打开项目的dfx.json文件。
  2. 请注意,定义了两个主要容器,connectd并且linkedup每个容器都有一个main.mo源文件。
  3. 请注意,linkedup_assets容器main.js以 CSS 和 HTML 文件的形式指定了资产的前端入口点。
  4. 请注意,应用程序使用默认服务器 IP 地址和端口号。

探索连接的源代码

社交关系容器 的源代码connectd组织成以下文件:

  • digraph.mo文件提供了创建顶点和边的有向图以描述用户连接的功能。
  • main.mo包含用于定义与能够由LinkedUp示例应用程序被称为用户简档相关联的连接的演员和按键功能。
  • types.mo文件定义了一个顶点映射到用户身份在使用自定义类型digraphmain程序文件。

探索链接的源代码

具有工作经历和教育背景的专业简介的源代码组织成以下文件:

  • main.mo文件包含 LinkedUp 示例应用程序的角色和关键函数。
  • types.mo文件定义了自定义类型,这些自定义类型描述了mainlinkedup容器的程序文件中导入和使用的用户身份和配置文件字段。
  • utils.mo文件提供了帮助函数。

查询和更新操作

在使用 LinkedUp 示例应用程序时,您可能会注意到某些操作(例如查看配置文件或执行搜索)几乎立即返回结果。其他操作(例如创建配置文件或添加连接)需要更长的时间。

这些性能差异说明了在linkedup容器中使用查询和更新调用之间的差异。

例如,在src/linkedup/main.mo文件中,createupdate函数是更改容器状态的更新调用,但程序使用getsearch函数的查询调用来查看或搜索配置文件:

// Profiles

  public shared(msg) func create(profile: NewProfile): async () {
    directory.createOne(msg.caller, profile);
  };

  public shared(msg) func update(profile: Profile): async () {
    if(Utils.hasAccess(msg.caller, profile)) {
      directory.updateOne(profile.id, profile);
    };
  };

  public query func get(userId: UserId): async Profile {
    Utils.getProfile(directory, userId)
  };

  public query func search(term: Text): async [Profile] {
    directory.findBy(term)
  };

容器之间的相互作用

在此示例应用程序中,linkedup容器利用容器中定义的函数connectd。这种分离简化了每个容器中的代码,并说明了如何通过从一个或多个其他容器调用一个容器中定义的通用函数来扩展项目。

要使一个容器中定义的公共函数在另一个容器中可用:

  1. import在调用容器中添加一条语句。

    在此示例中,公共函数在connectd容器中定义并由linkedup容器调用。

    因此,src/linkedup/main.mo包括以下代码:

    // Make the Connectd app's public methods available locally
    import Connectd "canister:connectd";
    

    跑步

  2. 使用canister.function语法在导入的容器中调用公共方法。

    在此示例中,linkedup容器调用导入容器中的connectandgetConnections函数connectd

您可以在源文件中看到启用linkedup容器和connectd容器之间交互的main.mo代码。

例如,src/connectd/main.mo定义了以下函数:

actor Connectd {
  flexible var graph: Digraph.Digraph = Digraph.Digraph();

  public func healthcheck(): async Bool { true };

  public func connect(userA: Vertex, userB: Vertex): async () {
    graph.addEdge(userA, userB);
  };

  public func getConnections(user: Vertex): async [Vertex] {
    graph.getAdjacent(user)
  };

};

由于该Import语句,这些connectd函数可用于linkedup容器,并且src/linkedup/main.mo包含以下代码:

  // Connections

  public shared(msg) func connect(userId: UserId): async () {
    // Call Connectd's public methods without an API
    await Connectd.connect(msg.caller, userId);
  };

  public func getConnections(userId: UserId): async [Profile] {
    let userIds = await Connectd.getConnections(userId);
    directory.findMany(userIds)
  };

  public shared(msg) func isConnected(userId: UserId): async Bool {
    let userIds = await Connectd.getConnections(msg.caller);
    Utils.includes(userId, userIds)
  };

  // User Auth

  public shared query(msg) func getOwnId(): async UserId { msg.caller }

};

停止本地网络

在您完成对linkedup程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。

要停止本地网络:

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

  2. 通过运行以下命令停止 Internet 计算机网络:

    dfx stop
    

添加带有身份的访问控制

应用程序通常需要基于角色的权限来控制不同用户可以执行的操作。

为了说明如何创建和切换用户身份,本教程创建了一个简单的程序,为分配到不同角色的用户显示不同的问候语。

在这个例子中,有三个命名角色owner—— admin、 和authorized

  • 分配了admin角色的用户会看到显示 的问候语You have a role with administrative privileges
  • 分配了authorized角色的用户会看到显示 的问候语Would you like to play a game?
  • 未分配这些角色之一的用户会看到显示 的问候语Nice to meet you!

此外,只有初始化容器的用户身份才会被分配owner角色,只有owneradmin角色才能将角色分配给其他用户。

概括地说,每个用户都有一个公钥/私钥对。公钥与用户访问的容器标识符结合形成一个安全主体,然后可以用作消息调用者来验证对运行在 Internet 计算机上的容器的函数调用。下图提供了用户身份如何验证消息调用者的简化视图。

在这里插入图片描述

在你开始之前

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

  • 您已经下载并安装了 DFINITY Canister SDK 包
  • 您至少运行了一个命令来default创建您的用户身份。对于$HOME/.config/dfx/identity/目录中的所有项目,您的默认用户身份将被全局存储。
  • 如果您使用 Visual Studio Code 作为 IDE,您已经为 Motoko 安装了 Visual Studio Code 插件。
  • 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。

创建一个新项目

创建一个用于测试访问控制和切换用户身份的新项目目录:

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

  2. 如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。

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

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

    cd access_hello
    

修改默认程序

在本教程中,您将使用具有分配和检索角色功能的程序替换模板源代码文件。

修改默认程序:

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

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

    // Import base modules
    import AssocList "mo:base/AssocList";
    import Error "mo:base/Error";
    import List "mo:base/List";
    
    shared({ caller = initializer }) actor class() {
    
        // Establish role-based greetings to display
        public shared({ caller }) func greet(name : Text) : async Text {
            if (has_permission(caller, #assign_role)) {
                return "Hello, " # name # ". You have a role with administrative privileges."
            } else if (has_permission(caller, #lowest)) {
                return "Welcome, " # name # ". You have an authorized account. Would you like to play a game?";
            } else {
                return "Greetings, " # name # ". Nice to meet you!";
            }
        };
    
        // Define custom types
        public type Role = {
            #owner;
            #admin;
            #authorized;
        };
    
        public type Permission = {
            #assign_role;
            #lowest;
        };
    
        private stable var roles: AssocList.AssocList<Principal, Role> = List.nil();
        private stable var role_requests: AssocList.AssocList<Principal, Role> = List.nil();
    
        func principal_eq(a: Principal, b: Principal): Bool {
            return a == b;
        };
    
        func get_role(pal: Principal) : ?Role {
            if (pal == initializer) {
                ?#owner;
            } else {
                AssocList.find<Principal, Role>(roles, pal, principal_eq);
            }
        };
    
        // Determine if a principal has a role with permissions
        func has_permission(pal: Principal, perm : Permission) : Bool {
            let role = get_role(pal);
            switch (role, perm) {
                case (?#owner or ?#admin, _) true;
                case (?#authorized, #lowest) true;
                case (_, _) false;
            }
        };
    
        // Reject unauthorized user identities
        func require_permission(pal: Principal, perm: Permission) : async () {
            if ( has_permission(pal, perm) == false ) {
                throw Error.reject( "unauthorized" );
            }
        };
    
        // Assign a new role to a principal
        public shared({ caller }) func assign_role( assignee: Principal, new_role: ?Role ) : async () {
            await require_permission( caller, #assign_role );
    
            switch new_role {
                case (?#owner) {
                    throw Error.reject( "Cannot assign anyone to be the owner" );
                };
                case (_) {};
            };
            if (assignee == initializer) {
                throw Error.reject( "Cannot assign a role to the canister owner" );
            };
            roles := AssocList.replace<Principal, Role>(roles, assignee, principal_eq, new_role).0;
            role_requests := AssocList.replace<Principal, Role>(role_requests, assignee, principal_eq, null).0;
        };
    
        public shared({ caller }) func request_role( role: Role ) : async Principal {
            role_requests := AssocList.replace<Principal, Role>(role_requests, caller, principal_eq, ?role).0;
            return caller;
        };
    
        // Return the principal of the message caller/user identity
        public shared({ caller }) func callerPrincipal() : async Principal {
            return caller;
        };
    
        // Return the role of the message caller/user identity
        public shared({ caller }) func my_role() : async ?Role {
            return get_role(caller);
        };
    
        public shared({ caller }) func my_role_request() : async ?Role {
            AssocList.find<Principal, Role>(role_requests, caller, principal_eq);
        };
    
        public shared({ caller }) func get_role_requests() : async List.List<(Principal,Role)> {
            await require_permission( caller, #assign_role );
            return role_requests;
        };
    
        public shared({ caller }) func get_roles() : async List.List<(Principal,Role)> {
            await require_permission( caller, #assign_role );
            return roles;
        };
    };
    

    让我们来看看这个程序的几个关键元素:

    • 您可能会注意到该greet函数是greet您在之前教程中看到的函数的变体。

      但是,在此程序中,该greet函数使用消息调用者来确定应应用的权限,并根据与调用者关联的权限,确定要显示的问候语。

    • 该程序定义了两种自定义类型——一种是 for Roles,一种是 for Permissions

    • assign_roles函数使消息调用者能够为与身份关联的主体分配角色。

    • callerPrincipal函数使您能够返回与身份关联的主体。

    • my_role函数使您能够返回与身份关联的角色。

  3. 保存更改并关闭main.mo文件以继续。

启动本地网络

在构建access_hello项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。

在本地启动网络:

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

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

  3. 通过运行以下命令在本地计算机上启动 Internet 计算机网络:

    dfx start --background
    

    在本地 Internet 计算机网络完成其启动操作后,您可以继续下一步。

注册、构建和部署应用程序

在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以通过运行该dfx deploy命令一步注册、构建和部署您的应用程序。也可以独立地使用单独执行的每个步骤

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

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

    dfx deploy access_hello
    
    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: access_hello
    Creating canisters...
    Creating canister "access_hello"...
    "access_hello" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai"
    Building canisters...
    Installing canisters...
    Installing code for canister access_hello, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai
    Deployed canisters.
    

检查当前身份上下文

在我们创建任何其他身份之前,让我们查看与您的default身份相关联的主要标识符以及您身份的周期钱包default。在 Internet 计算机上,主体是用户、容器、节点或子网的内部代表。主体的文本表示是您在使用主体数据类型时看到的外部标识符。

查看您当前的身份和原则:

  1. 通过运行以下命令验证当前活动的身份:

    dfx identity whoami
    

    该命令显示类似于以下内容的输出:

    default
    
  2. default通过运行以下命令检查用户身份的主体:

    dfx identity get-principal
    

    该命令显示类似于以下内容的输出:

    zen7w-sjxmx-jcslx-ey4hf-rfxdq-l4soz-7ie3o-hti3o-nyoma-nrkwa-cqe
    
  3. default通过运行以下命令检查与用户身份关联的角色:

    dfx canister --wallet=$(dfx identity get-wallet) call access_hello my_role
    

    该命令显示类似于以下内容的输出:

    (opt variant { owner })
    

创建新的用户身份

为了开始测试我们程序中的访问控制,让我们创建一些新的用户身份并将这些用户分配到不同的角色。

要创建新的用户身份:

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

  2. 通过运行以下命令创建新的管理用户身份:

    dfx identity new ic_admin
    

    该命令显示类似于以下内容的输出:

    Creating identity: "ic_admin".
    Created identity: "ic_admin".
    
  3. 调用该my_role函数以查看您的新用户身份尚未分配给任何角色。

    dfx --identity ic_admin canister call access_hello my_role
    

    该命令显示类似于以下内容的输出:

    Creating a wallet canister on the local network.
    The wallet canister on the "local" network for user "ic_admin" is "ryjl3-tyaaa-aaaaa-aaaba-cai"
    (null)
    
  4. 通过运行以下命令,切换当前活动的身份上下文以使用新的ic_admin用户身份并显示与ic_admin用户关联的主体:

    dfx identity use ic_admin && dfx identity get-principal
    

    该命令显示类似于以下内容的输出:

    Using identity: "ic_admin".
    c5wa6-3irl7-tuxuo-4vtyw-xsnhw-rv2a6-vcmdz-bzkca-vejmd-327zo-wae
    
  5. access_hello通过运行以下命令检查用于调用容器的主体:

    dfx canister call access_hello callerPrincipal
    

    该命令显示类似于以下内容的输出:

    (principal "ryjl3-tyaaa-aaaaa-aaaba-cai")
    

    默认情况下,cycles 钱包标识符是用于调用access_hello容器中方法的主体。然而,为了说明访问控制,我们想使用与用户上下文相关联的主体,而不是周期钱包。不过,在我们进入这一步之前,让我们为ic_admin用户分配一个角色。为此,我们需要切换到default具有该owner角色的用户身份。

为身份分配角色

将管理员角色分配给 ic_admin 身份:

  1. default通过运行以下命令,将当前活动的身份上下文切换为使用用户身份:

    dfx identity use default
    
  2. 通过使用 Candid 语法运行类似于以下的命令,为ic_admin主体分配admin角色:

    dfx canister --wallet=$(dfx identity get-wallet) call access_hello assign_role '((principal "c5wa6-3irl7-tuxuo-4vtyw-xsnhw-rv2a6-vcmdz-bzkca-vejmd-327zo-wae"),opt variant{admin})'
    

+ 或者,您可以重新运行命令以调用my_role函数来验证角色分配。

dfx --identity ic_admin canister call access_hello my_role

+ 该命令显示类似于以下内容的输出:

(opt variant { admin })
  1. 通过运行以下命令,使用您刚刚分配角色greetic_admin用户身份调用该函数admin

    dfx --identity ic_admin canister call access_hello greet "Internet Computer Admin"
    

    该命令显示类似于以下内容的输出:

    (
      "Hello, Internet Computer Admin. You have a role with administrative privileges.",
    )
    

添加授权用户身份

此时,您有一个default带有owner角色的ic_admin用户身份和一个带有角色的用户身份admin。让我们添加另一个用户身份并将其分配给authorized角色。但是,对于此示例,我们将使用环境变量来存储用户的主体。

添加新的授权用户身份:

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

  2. 通过运行以下命令创建新的授权用户身份:

    dfx identity new alice_auth
    

    该命令显示类似于以下内容的输出:

    Creating identity: "alice_auth".
    Created identity: "alice_auth".
    
  3. alice_auth通过运行以下命令,将当前活动的身份上下文切换为使用新的用户身份:

    dfx identity use alice_auth
    
  4. alice_auth通过运行以下命令将用户的主体存储在环境变量中:

    ALICE_ID=$(dfx identity get-principal)
    

    您可以通过运行以下命令来验证存储的主体:

    echo $ALICE_ID
    

    该命令显示类似于以下内容的输出:

    b5quc-npdph-l6qp4-kur4u-oxljq-7uddl-vfdo6-x2uo5-6y4a6-4pt6v-7qe
    
  5. 通过运行以下命令,使用ic_admin身份将authorized角色分配给alice_auth

    dfx --identity ic_admin canister call access_hello assign_role "(principal \"$ALICE_ID\", opt variant{authorized})"
    
  6. 调用该my_role函数以验证角色分配。

    dfx --identity alice_auth canister call access_hello my_role
    

    该命令显示类似于以下内容的输出:

    (opt variant { authorized })
    
  7. 通过运行以下命令,使用您刚刚分配角色greetalice_auth用户身份调用该函数authorized

    dfx canister call access_hello greet "Alice"
    

    该命令显示类似于以下内容的输出:

    (
      "Welcome, Alice. You have an authorized account. Would you like to play a game?",
    )
    

添加未经授权的用户身份

您现在已经看到了创建具有特定角色和权限的用户的简单示例。下一步是创建未分配给角色或授予任何特殊权限的用户身份。

添加未经授权的用户身份:

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

  2. 如果需要,通过运行以下命令检查您当前活动的身份:

    dfx identity whoami
    
  3. 通过运行以下命令创建新的用户身份:

    dfx identity new bob_standard
    

    该命令显示类似于以下内容的输出:

    Creating identity: "bob_standard".
    Created identity: "bob_standard".
    
  4. bob_standard通过运行以下命令将用户的主体存储在环境变量中:

    BOB_ID=$(dfx --identity bob_standard identity get-principal)
    
  5. 尝试使用bob_standard身份分配角色。

    dfx --identity bob_standard canister call access_hello assign_role "(principal \"$BOB_ID\", opt variant{authorized})"
    

    此命令返回unauthorized错误。

  6. 尝试使用default用户身份来分配bob_standardowner运行以下命令的作用:

    dfx --identity default canister --wallet=$(dfx --identity default identity get-wallet) call access_hello assign_role "(principal \"$BOB_ID\", opt variant{owner})"
    

    此命令失败,因为无法为用户分配owner角色。

  7. 通过运行以下命令,greet使用bob_standard用户身份调用该函数:

    dfx --identity bob_standard canister --no-wallet call access_hello greet "Bob"
    

    该命令显示类似于以下内容的输出:

    ("Greetings, Bob. Nice to meet you!")
    

为多个命令设置用户身份

到目前为止,您已经了解了如何为单个命令创建和切换用户身份。您还可以指定要使用的用户身份,然后在该用户身份的上下文中运行多个命令。

要在一个用户身份下运行多个命令:

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

  2. 通过运行以下命令列出当前可用的用户身份:

    dfx identity list
    

    该命令会显示类似于以下内容的输出,并带有指示当前活动用户身份的星号。

    alice_auth
    bob_standard
    default *
    ic_admin
    

    在此示例中,default除非您明确选择不同的身份,否则将使用用户身份。

  3. 从列表中选择一个新用户身份,并通过运行类似于以下内容的命令使其成为活动用户上下文:

    dfx identity use ic_admin
    

    + 该命令显示类似于以下内容的输出:

    Using identity: "ic_admin".
    

    如果您重新运行该dfx identity list命令,ic_admin用户身份将显示一个星号,以指示它是当前活动的用户上下文。

    您现在可以使用选定的用户身份运行命令,而无需--identity在命令行上指定。

停止本地网络

在您完成对程序的试验和使用身份后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。

要停止本地网络:

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

  2. 通过运行以下命令停止 Internet 计算机网络:

    dfx stop
    

接受来自钱包的周期

在做本地开发的时候,可以使用项目中的默认钱包发送周期,查看自己的周期余额。但是那些需要接收和销毁这些周期以执行其功能并向用户提供服务的程序呢?本教程提供了一个简单的示例来说明如何将接收周期和检查周期余额的函数添加到默认模板程序中。

此示例包含以下内容:

  • wallet_balance功能使您能够检查容器的当前循环平衡。
  • wallet_receive函数使程序能够接受发送到容器的循环。
  • greet函数接受文本参数并在终端中显示问候语。
  • owner函数返回消息调用者使用的主体。

在你开始之前

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

  • 您已经下载并安装了 DFINITY Canister SDK 包
  • 如果您使用 Visual Studio Code 作为 IDE,您已经为 Motoko 安装了 Visual Studio Code 插件。
  • 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。

创建一个新项目

创建一个用于测试访问控制和切换用户身份的新项目目录:

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

  2. 如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。

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

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

    cd cycles_hello
    

修改默认程序

对于本教程,您将修改模板源代码以包含用于接受周期和检查周期平衡的新函数。

修改默认程序:

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

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

    import Nat64 "mo:base/Nat64";
    import Cycles "mo:base/ExperimentalCycles";
    
    shared(msg) actor class HelloCycles (
       capacity: Nat
      ) {
    
      var balance = 0;
    
      // Return the current cycle balance
      public shared(msg) func wallet_balance() : async Nat {
        return balance;
      };
    
      // Return the cycles received up to the capacity allowed
      public func wallet_receive() : async { accepted: Nat64 } {
        let amount = Cycles.available();
        let limit : Nat = capacity - balance;
        let accepted =
          if (amount <= limit) amount
          else limit;
        let deposit = Cycles.accept(accepted);
        assert (deposit == accepted);
        balance += accepted;
        { accepted = Nat64.fromNat(accepted) };
      };
    
      // Return the greeting
      public func greet(name : Text) : async Text {
        return "Hello, " # name # "!";
      };
    
      // Return the principal of the caller/user identity
      public shared(msg) func owner() : async Principal {
        let currentOwner = msg.caller;
        return currentOwner;
      };
    
    };
    

    让我们来看看这个程序的几个关键元素:

    • 该程序导入了一个 Motoko 基础库ExperimentalCycles——它提供了使用循环的基本功能。
    • 该程序使用一个actor class而不是单个actor,因此它可以有多个actor实例来接受capacity所有实例的不同循环量,最多为a 。
    • capacity作为参数传递给actor 类。
    • msg.caller识别与呼叫相关联的主体。
  3. 保存更改并关闭main.mo文件以继续。

启动本地网络

在构建access_hello项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。

在本地启动网络:

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

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

  3. 通过运行以下命令在本地计算机上启动 Internet 计算机网络:

    dfx start --clean --background
    

    在本地 Internet 计算机网络完成其启动操作后,您可以继续下一步。

注册、构建和部署应用程序

在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署您的应用程序。

在本地部署应用程序:

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

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

    dfx deploy --argument '(360000000000)'
    

    此示例将capacity容器的 设置为 360,000,000,000 次循环。的dfx deploy命令输出然后显示有关的操作它执行,包括与此本地项目和钱包容器标识符创建的钱夹容器相关联的身份信息。

    例如:

    Deploying all canisters.
    Creating canisters...
    Creating canister "cycles_hello"...
    Creating the canister using the wallet canister...
    Creating a wallet canister on the local network.
    The wallet canister on the "local" network for user "default" is "rwlgt-iiaaa-aaaaa-aaaaa-cai"
    "cycles_hello" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai"
    Creating canister "cycles_hello_assets"...
    Creating the canister using the wallet canister...
    "cycles_hello_assets" canister created with canister id: "ryjl3-tyaaa-aaaaa-aaaba-cai"
    Building canisters...
    Building frontend...
    Installing canisters...
    Installing code for canister cycles_hello, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai
    Installing code for canister cycles_hello_assets, with canister_id ryjl3-tyaaa-aaaaa-aaaba-cai
    Authorizing our identity (default) to the asset canister...
    Uploading assets to asset canister...
    Deployed canisters.
    

测试应用程序

在本地 Internet 计算机网络上部署应用程序后,您可以试验钱包功能并使用dfx canister call命令测试您的程序。

要测试应用程序:

  1. default通过运行以下命令检查用户身份的主体:

    dfx canister call cycles_hello owner
    

    该命令针对当前身份显示类似于以下内容的输出:

    (principal "g3jww-sbmtm-gxsag-4mecu-72yc4-kef5v-euixq-og2kd-sav2v-p2sb3-pae")
    

    如果您尚未更改用于运行该dfx deploy命令的身份,则应通过运行该dfx identity get-principal命令获得相同的主体。这很重要,因为您必须是钱包容器的所有者才能执行某些任务,例如发送周期或授予其他保管人身份发送周期的权限。

  2. 通过运行以下命令检查初始钱包周期余额:

    dfx canister call cycles_hello wallet_balance
    

    您尚未向容器发送任何循环,因此该命令显示以下余额:

    (0)
    
  3. cycles_hello通过运行类似于以下的命令,使用容器主体将一些周期从您的默认钱包容器发送到容器:

    dfx canister call rwlgt-iiaaa-aaaaa-aaaaa-cai wallet_send '(record { canister = principal "rrkah-fqaaa-aaaaa-aaaaq-cai"; amount = (256000000000:nat64); } )'
    
  4. 如果您指定的数量低于允许的容量,或者您在运行命令时指定的数量,则调用该wallet_balance函数以查看cycles_hello容器具有您传输的周期数。capacity``dfx deploy

    dfx canister call cycles_hello wallet_balance
    

    该命令显示类似于以下内容的输出:

    (256_000_000_000)
    
  5. wallet_balance通过运行类似于以下的命令来调用该函数以查看默认钱包中的周期数:

    dfx canister call rwlgt-iiaaa-aaaaa-aaaaa-cai wallet_balance
    

    该命令使用 Candid 格式返回您指定为记录的钱包容器标识符的余额。例如,该命令可能会显示一个包含一个amount字段(由 hash 表示3_573_748_184)和 97,738,624,621,042 个周期的余额的记录,如下所示:

    (record { 3_573_748_184 = 97_738_624_621_042 })
    

    对于这个简单的教程,周期仅从默认钱包容器中的余额中消耗,而不是从cycles_hello容器中消耗。

  6. greet通过运行类似于以下的命令来调用该函数:

    dfx canister call cycles_hello greet '("from DFINITY")'
    
  7. 重新调用该wallet_balance函数以查看从您的默认钱包中扣除的周期数:

    dfx canister call rwlgt-iiaaa-aaaaa-aaaaa-cai wallet_balance
    

    例如,您可能会得到与此类似的结果:

    (记录{ 3_573_748_184 = 97_638_622_179_500 })
    

停止本地网络

在您完成对程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。

要停止本地网络:

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

  2. 通过运行以下命令停止 Internet 计算机网络:

    dfx stop
    

eturn currentOwner;
};

};


让我们来看看这个程序的几个关键元素:

- 该程序导入了一个 Motoko 基础库`ExperimentalCycles`——它提供了使用循环的基本功能。
- 该程序使用一个`actor class`而不是单个actor,因此它可以有多个actor实例来接受`capacity`所有实例的不同循环量,最多为a 。
- 将`capacity`作为参数传递给actor 类。
- 在`msg.caller`识别与呼叫相关联的主体。

3. 保存更改并关闭`main.mo`文件以继续。

### 启动本地网络

在构建`access_hello`项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。

在本地启动网络:

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

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

3. 通过运行以下命令在本地计算机上启动 Internet 计算机网络:

```bash
dfx start --clean --background

在本地 Internet 计算机网络完成其启动操作后,您可以继续下一步。

注册、构建和部署应用程序

在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署您的应用程序。

在本地部署应用程序:

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

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

    dfx deploy --argument '(360000000000)'
    

    此示例将capacity容器的 设置为 360,000,000,000 次循环。的dfx deploy命令输出然后显示有关的操作它执行,包括与此本地项目和钱包容器标识符创建的钱夹容器相关联的身份信息。

    例如:

    Deploying all canisters.
    Creating canisters...
    Creating canister "cycles_hello"...
    Creating the canister using the wallet canister...
    Creating a wallet canister on the local network.
    The wallet canister on the "local" network for user "default" is "rwlgt-iiaaa-aaaaa-aaaaa-cai"
    "cycles_hello" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai"
    Creating canister "cycles_hello_assets"...
    Creating the canister using the wallet canister...
    "cycles_hello_assets" canister created with canister id: "ryjl3-tyaaa-aaaaa-aaaba-cai"
    Building canisters...
    Building frontend...
    Installing canisters...
    Installing code for canister cycles_hello, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai
    Installing code for canister cycles_hello_assets, with canister_id ryjl3-tyaaa-aaaaa-aaaba-cai
    Authorizing our identity (default) to the asset canister...
    Uploading assets to asset canister...
    Deployed canisters.
    

测试应用程序

在本地 Internet 计算机网络上部署应用程序后,您可以试验钱包功能并使用dfx canister call命令测试您的程序。

要测试应用程序:

  1. default通过运行以下命令检查用户身份的主体:

    dfx canister call cycles_hello owner
    

    该命令针对当前身份显示类似于以下内容的输出:

    (principal "g3jww-sbmtm-gxsag-4mecu-72yc4-kef5v-euixq-og2kd-sav2v-p2sb3-pae")
    

    如果您尚未更改用于运行该dfx deploy命令的身份,则应通过运行该dfx identity get-principal命令获得相同的主体。这很重要,因为您必须是钱包容器的所有者才能执行某些任务,例如发送周期或授予其他保管人身份发送周期的权限。

  2. 通过运行以下命令检查初始钱包周期余额:

    dfx canister call cycles_hello wallet_balance
    

    您尚未向容器发送任何循环,因此该命令显示以下余额:

    (0)
    
  3. cycles_hello通过运行类似于以下的命令,使用容器主体将一些周期从您的默认钱包容器发送到容器:

    dfx canister call rwlgt-iiaaa-aaaaa-aaaaa-cai wallet_send '(record { canister = principal "rrkah-fqaaa-aaaaa-aaaaq-cai"; amount = (256000000000:nat64); } )'
    
  4. 如果您指定的数量低于允许的容量,或者您在运行命令时指定的数量,则调用该wallet_balance函数以查看cycles_hello容器具有您传输的周期数。capacity``dfx deploy

    dfx canister call cycles_hello wallet_balance
    

    该命令显示类似于以下内容的输出:

    (256_000_000_000)
    
  5. wallet_balance通过运行类似于以下的命令来调用该函数以查看默认钱包中的周期数:

    dfx canister call rwlgt-iiaaa-aaaaa-aaaaa-cai wallet_balance
    

    该命令使用 Candid 格式返回您指定为记录的钱包容器标识符的余额。例如,该命令可能会显示一个包含一个amount字段(由 hash 表示3_573_748_184)和 97,738,624,621,042 个周期的余额的记录,如下所示:

    (record { 3_573_748_184 = 97_738_624_621_042 })
    

    对于这个简单的教程,周期仅从默认钱包容器中的余额中消耗,而不是从cycles_hello容器中消耗。

  6. greet通过运行类似于以下的命令来调用该函数:

    dfx canister call cycles_hello greet '("from DFINITY")'
    
  7. 重新调用该wallet_balance函数以查看从您的默认钱包中扣除的周期数:

    dfx canister call rwlgt-iiaaa-aaaaa-aaaaa-cai wallet_balance
    

    例如,您可能会得到与此类似的结果:

    (记录{ 3_573_748_184 = 97_638_622_179_500 })
    

停止本地网络

在您完成对程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。

要停止本地网络:

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

  2. 通过运行以下命令停止 Internet 计算机网络:

    dfx stop
    

猜你喜欢

转载自blog.csdn.net/lk2684753/article/details/118224366
ICP