diff --git a/docs/03-move/02-quick-start.md b/docs/03-move/02-quick-start.md index 8aad65673..c749c23c0 100644 --- a/docs/03-move/02-quick-start.md +++ b/docs/03-move/02-quick-start.md @@ -210,7 +210,7 @@ If it is forcibly copied, modified or discarded, the code compilation will go wr For more information, please refer to the [Understanding Ability](./04-understanding-ability.md) chapter. ::: -Generally speaking, we believe that **the structure with `key` ability is resources**. +Generally speaking, we believe that **the structure with `key` and `store` abilities is resources**. We modify the code and follow the prompts to add `key` ability. diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/01-starcoin-usage.md b/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/01-starcoin-usage.md index 4793ab0a1..802ee1b08 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/01-starcoin-usage.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/01-starcoin-usage.md @@ -1,7 +1,6 @@ # 如何使用 Starcoin CLI -`starcoin` 命令可以用来启动节点,运行本地网络,加入测试网络或者主网络,也可以直接连接到远程节点的接口上,纯粹作为命令行客户端工具使用。 -运行本地网络或者加入测试网络可以方便地测试用户的智能合约代码,`starcoin` 的子命令 `dev` 可以编译,执行和发布智能合约。 +`starcoin` 命令可以用来启动节点,运行本地网络,加入测试网络或者主网络,也可以直接连接到远程节点的接口上,纯粹作为命令行客户端工具使用。运行本地网络或者加入测试网络可以方便地测试用户的智能合约代码,`starcoin` 的子命令 `dev` 可以编译,执行和发布智能合约。 当你按照前面的教程安装好 `starcoin` 后,可以运行 `starcoin -h` 查看帮助。 @@ -34,7 +33,8 @@ $ starcoin -n dev Waiting SIGINT or SIGTERM ... ``` -此时在上面的控制台输出中可以看到节点的四种 RPC 访问方式 +此时在上面的控制台输出中可以看到节点的四种 RPC 访问方式: + * IPC 文件在 .../dev/starcoin.ipc * HTTP 服务默认运行在 0.0.0.0:9850 * TCP 服务默认运行在 0.0.0.0:9860 @@ -42,8 +42,7 @@ Waiting SIGINT or SIGTERM ... ### 查看链的信息 -本地的 dev 模式默认使用一个临时目录,每次重启都会重置数据。 -dev 节点启动后,会在日志中打印 IPC 文件路径,我们通过 IPC(进程间通信)文件可以连接到节点执行命令,比如: +本地的 dev 模式默认使用一个临时目录,每次重启都会重置数据。dev 节点启动后,会在日志中打印 IPC 文件路径,我们通过 IPC(进程间通信)文件可以连接到节点执行命令,比如: ```bash $ starcoin -c /var/folders/_4/1ghtd3z15qjcw8yj905dcql40000gn/T/.tmph3EJ8S/dev/starcoin.ipc chain info @@ -156,8 +155,7 @@ starcoin% chain info } ``` -像上面这样进入控制台,如果发现当前尚未启动 Starcoin 节点,则会自动在后台启动一个节点,控制台和节点在进程内通信。 -这样的节点生命周期和控制台的生命周期绑定,从控制台退出后,节点也会自动退出。 +像上面这样进入控制台,如果发现当前尚未启动 Starcoin 节点,则会自动在后台启动一个节点,控制台和节点在进程内通信。这样的节点生命周期和控制台的生命周期绑定,从控制台退出后,节点也会自动退出。 当然也可以通过 IPC 或者 WebSocket RPC 的方式连接,然后进入控制台。 @@ -185,9 +183,7 @@ starcoin -c ws://main.seed.starcoin.org:9870 chain info|jq '.ok.head.number' ### Node exit for an unexpected error: file limit the maximum number of open file descriptors is too small, got xxxx, expect greater or equal to 45056 -出现这个错误的原因是因为ubuntu系统默认的可打开的最大数量文件描述符不够。 -通过一下步骤,将系统的可打开的最大数量文件描述设置为65536即可。 -首先,将`DefaultLimitNOFILE=65536`添加到文件`/etc/systemd/system.conf` 和 `/etc/systemd/user.conf`的最末段,然后重启系统即可,最后可以通过`ulimit -n`命令来检查可打开的最大数量文件描述符限制 +出现这个错误的原因是因为ubuntu系统默认的可打开的最大数量文件描述符不够。通过一下步骤,将系统的可打开的最大数量文件描述设置为65536即可。首先,将`DefaultLimitNOFILE=65536`添加到文件`/etc/systemd/system.conf` 和 `/etc/systemd/user.conf`的最末段,然后重启系统即可,最后可以通过`ulimit -n`命令来检查可打开的最大数量文件描述符限制 ## 选项说明 diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/02-starcoin-console.md b/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/02-starcoin-console.md index a100a7191..b7feedb73 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/02-starcoin-console.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/02-starcoin-console.md @@ -6,8 +6,7 @@ 在节点启动的时候进入控制台。 -注意:如果不指定存放数据的目录,以这种方式直接进入交互式控制台,会在 `/tmp` 目录下随机生成一个用于存放当前进程的数据目录。 -退出交互式控制台,进程结束,存放数据的目录也会立即被删除。 +注意:如果不指定存放数据的目录,以这种方式直接进入交互式控制台,会在 `/tmp` 目录下随机生成一个用于存放当前进程的数据目录。退出交互式控制台,进程结束,存放数据的目录也会立即被删除。 ```shell starcoin -n dev console @@ -99,8 +98,7 @@ starcoin -c /path/to/starcoin.ipc console starcoin.exe -c \\.\pipe\starcoin\dev\starcoin.ipc console ``` -此时,`starcoin console` 连接到了 IPC 文件对应节点的网络。 -可以是 dev、主网、测试网等,取决于启动的节点加入的网络。 +此时,`starcoin console` 连接到了 IPC 文件对应节点的网络。可以是 dev、主网、测试网等,取决于启动的节点加入的网络。 ### 通过 WebSocket diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/03-dev-network.md b/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/03-dev-network.md index 664e79f37..f06dfb5fa 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/03-dev-network.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/03-dev-network.md @@ -54,8 +54,7 @@ starcoin -c starcoin.ipc dev get-coin -v 100STC ## 工作方式 -*dev* 网络的出块模式跟主网和测试网不一样,*dev* 网络是按需出块,没有交易就不出块。 -*dev* 网络上的时间不自动流逝,需要通过出块的方式让时间变化。如果想快速跳过时间,进行测试,可以用 `sleep` 命令。 +*dev* 网络的出块模式跟主网和测试网不一样,*dev* 网络是按需出块,没有交易就不出块。*dev* 网络上的时间不自动流逝,需要通过出块的方式让时间变化。如果想快速跳过时间,进行测试,可以用 `sleep` 命令。 ## 技巧 diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/05-fast-sync-data.md b/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/05-fast-sync-data.md index 31d6e9c95..f09f3878e 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/05-fast-sync-data.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/05-fast-sync-data.md @@ -24,8 +24,7 @@ cd starcoin-artifacts/ ## 同步区块数据 -如果启动一个新的全节点,通常是下载其他节点的主网区块数据到新节点,并以下载的区块数据启动节点。 -下载(导出)的区块数据存储在 CSV 文件上,方便数据交换和存储。 +如果启动一个新的全节点,通常是下载其他节点的主网区块数据到新节点,并以下载的区块数据启动节点。下载(导出)的区块数据存储在 CSV 文件上,方便数据交换和存储。 ### 导出块 @@ -62,18 +61,15 @@ mkdir ~/bak ### 使用脚本导入区块数据(推荐) -`starcoin` 的仓库中提供了 [`import_block.sh`](https://github.com/starcoinorg/starcoin/blob/master/scripts/import_block.sh) 脚本,预编译版本也打包有相应的脚本。 -使用脚本导入区块数据会更加便利,而不必手动导出和导入。 +`starcoin` 的仓库中提供了 [`import_block.sh`](https://github.com/starcoinorg/starcoin/blob/master/scripts/import_block.sh) 脚本,预编译版本也打包有相应的脚本。使用脚本导入区块数据会更加便利,而不必手动导出和导入。 ```shell ./import_block.sh main ~/.starcoin/main ``` -这个脚本接收两个参数,参数1指定网络名称,例如 `main`, `barnard`, `proxima` 或 `halley`,参数2指定数据存储的目录,例如 `~/.starcoin/main` 或任意自定义的路径。 -这个脚本会跳过已经有的区块,每获取一个区块都会自动更新进度,中断脚本后再执行会接着原来的高度继续导入。 +这个脚本接收两个参数,参数1指定网络名称,例如 `main`, `barnard`, `proxima` 或 `halley`,参数2指定数据存储的目录,例如 `~/.starcoin/main` 或任意自定义的路径。这个脚本会跳过已经有的区块,每获取一个区块都会自动更新进度,中断脚本后再执行会接着原来的高度继续导入。 -执行脚本后,就会自动下载主网区块数据到 `~/.starcoin/main` 目录并导入。 -等待下载并导入完成,就可以使用这些区块数据运行新的节点了。 +执行脚本后,就会自动下载主网区块数据到 `~/.starcoin/main` 目录并导入。等待下载并导入完成,就可以使用这些区块数据运行新的节点了。 注意:使用这个脚本必须保证 `starcoin_db_exporter` 和 `import_block.sh` 在同一路径下。 @@ -105,9 +101,7 @@ mkdir ~/snapshot ./starcoin_db_exporter export-snapshot -i ~/.starcoin/main -n main -o ~/snapshot -t true ``` -通过 `-t` 选项指定是否使用增量导出,`true` 启用,`false` 禁用。 -如果要使用增量导出,需要保证 `~/snapshot` 目录下有旧的快照,比如原来 `~/snapshot` 目录下的快照高度为 `1-400w`,现在需要导 `1-500w` 高度的快照。 -如果使用了增量导出,则先会把后面 `400w-500w` 高度的快照导出,再与原来的快照合并,以此来节省时间。 +通过 `-t` 选项指定是否使用增量导出,`true` 启用,`false` 禁用。如果要使用增量导出,需要保证 `~/snapshot` 目录下有旧的快照,比如原来 `~/snapshot` 目录下的快照高度为 `1-400w`,现在需要导 `1-500w` 高度的快照。如果使用了增量导出,则先会把后面 `400w-500w` 高度的快照导出,再与原来的快照合并,以此来节省时间。 ### 导入快照 diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/06-main-network.md b/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/06-main-network.md index b01ddf9dc..581438b3c 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/06-main-network.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/06-main-network.md @@ -2,8 +2,7 @@ ## 简介 -2021年5月18日,Starcoin 举行全球发布会,在聚光灯的照耀下,宣布启动主网。 -这是一个里程碑时刻,意味着 Starcoin 这艘船舶,在区块链的蓝海里,正式扬帆起航。 +2021年5月18日,Starcoin 举行全球发布会,在聚光灯的照耀下,宣布启动主网。这是一个里程碑时刻,意味着 Starcoin 这艘船舶,在区块链的蓝海里,正式扬帆起航。 ## Windows 加入主网络 diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/07-private-network.md b/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/07-private-network.md index ec5e5eacc..b4f65b486 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/07-private-network.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/02-getting-started/02-setup/07-private-network.md @@ -32,8 +32,7 @@ starcoin_generator -n : --genesis-config starcoin_generator -n my_chain:123 --genesis-config halley genesis_config ``` -该命令将内置的 `halley` 网络配置作为模板,生成一个名为 `genesis_config.json` 配置文件在 `~/.starcoin/my_chain` 目录下。 -可以用编辑器修改 `~/.starcoin/my_chain/genesis_config.json` 文件中的参数。 +该命令将内置的 `halley` 网络配置作为模板,生成一个名为 `genesis_config.json` 配置文件在 `~/.starcoin/my_chain` 目录下。可以用编辑器修改 `~/.starcoin/my_chain/genesis_config.json` 文件中的参数。 注:如果不想配置文件生成在默认的 `~/.starcoin/` 目录下,也可以通过 `-d` 选项指定目录。 @@ -47,8 +46,7 @@ starcoin_generator -n my_chain:123 genesis 该命令根据前面生成的创世配置文件来生成创世区块。 -上面例子中的创世配置文件是 `~/.starcoin/my_chain/genesis_config.json`。 -当然,也可以将 `genesis_config.json` 文件放置在其他位置,然后通过绝对路径指定,比如: +上面例子中的创世配置文件是 `~/.starcoin/my_chain/genesis_config.json`。当然,也可以将 `genesis_config.json` 文件放置在其他位置,然后通过绝对路径指定,比如: ```shell starcoin_generator -n my_chain:123 --genesis-config /data/conf/my_chain/genesis_config.json genesis diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/02-quick-start.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/02-quick-start.md index 1cc496847..b46147913 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/02-quick-start.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/02-quick-start.md @@ -1,8 +1,6 @@ # 快速开始 -在这个教程中,我们将实现一个简单的计数器,来展示 Move 是如何通过代码来管理资源的。 -这篇文档涉及的内容包括背景知识、写代码、如何编译、如何发布到链上、如何调用。 -完整的代码仓库在[这里](https://github.com/starcoinorg/starcoin-cookbook/tree/main/examples/my-counter)。 +在这个教程中,我们将实现一个简单的计数器,来展示 Move 是如何通过代码来管理资源的。这篇文档涉及的内容包括背景知识、写代码、如何编译、如何发布到链上、如何调用。完整的代码仓库在[这里](https://github.com/starcoinorg/starcoin-cookbook/tree/main/examples/my-counter)。 提前准备: @@ -42,8 +40,7 @@ my-counter :::tip Move 模块 模块是定义**结构体类型**和操作结构体的**函数**的库。*(可以借助其他编程语言“类(class)”的概念来帮助理解)*。 -模块会被发布到发布者的地址下,模块中的*入口方法(entry functions)*可以被大家调用执行。 -*(可以借助函数计算平台 —— 如 AWS Lambda —— 上发布函数、调用函数来帮助理解)* +模块会被发布到发布者的地址下,模块中的*入口方法(entry functions)*可以被大家调用执行。*(可以借助函数计算平台 —— 如 AWS Lambda —— 上发布函数、调用函数来帮助理解)* ::: 由 *Move.toml* 文件和 *sources* 目录组成的项目,会被认为是一个 [Move 包(Move Package)](./03-move-language/01-packages.md)。 @@ -64,8 +61,7 @@ module
:: { ### 第一版代码 -在 `MyCounter` 模块中,我们定义一个结构体 `Counter`,包含有一个字段 `value`,代表这个计数器触发的次数。 -`value` 的类型是 `u64`,也就是无符号64位整型。 +在 `MyCounter` 模块中,我们定义一个结构体 `Counter`,包含有一个字段 `value`,代表这个计数器触发的次数。`value` 的类型是 `u64`,也就是无符号64位整型。 下面是 `MyCounter` 模块的第一版代码: @@ -144,20 +140,15 @@ module MyCounterAddr::MyCounter { 这里的 `move_to(&signer, T)` 是一个内置方法,作用是将类型为 `T` 的资源添加到账户 `signer` 的地址下的存储空间(尖括号在这里表示泛型)。 :::info 更多信息 -这里的存储空间是 `GlobalState`,可以先简单理解为存放账户的*资源*和*模块代码*的地方。 -更多详细信息可以查阅[概念-状态](../99-concepts/04-state.md)。 +这里的存储空间是 `GlobalState`,可以先简单理解为存放账户的*资源*和*模块代码*的地方。更多详细信息可以查阅[概念-状态](../99-concepts/04-state.md)。 ::: -在第6行的 `init` 函数的参数 `account: &signer` 中的 `signer` 是 Move 的内置类型。 -想要将资源放到调用者的账户地址下,需要将 `&signer` 参数传递给函数。 -`&signer` 数据类型代表当前交易的发起者(函数的调用者,可以是任何账户)。 +在第6行的 `init` 函数的参数 `account: &signer` 中的 `signer` 是 Move 的内置类型。想要将资源放到调用者的账户地址下,需要将 `&signer` 参数传递给函数。`&signer` 数据类型代表当前交易的发起者(函数的调用者,可以是任何账户)。 :::info 帮你理解 —— signer -`signer` 就像是 Linux 下的 *uid* 一样的东西。登陆 Linux 系统后,你输入的所有命令,都被认为是“这个已登陆的**经过认证**的用户”操作的。 -关键来了,这个认证过程不是在运行的命令、程序中做的,而是开机后由操作系统完成的。 +`signer` 就像是 Linux 下的 *uid* 一样的东西。登陆 Linux 系统后,你输入的所有命令,都被认为是“这个已登陆的**经过认证**的用户”操作的。关键来了,这个认证过程不是在运行的命令、程序中做的,而是开机后由操作系统完成的。 -对应到 Move 中,这个认证过程就是和其他区块链系统类似的、我们熟知的“私钥签名,公钥验证”的过程。 -在带有 `&signer` 参数的函数执行时,发起者的身份已经被 Starcoin 区块链认证过了。 +对应到 Move 中,这个认证过程就是和其他区块链系统类似的、我们熟知的“私钥签名,公钥验证”的过程。在带有 `&signer` 参数的函数执行时,发起者的身份已经被 Starcoin 区块链认证过了。 ::: 我们试着编译一下: @@ -179,16 +170,12 @@ error[E05001]: ability constraint not satisfied │ Invalid call of 'move_to' ``` -出错了!提示我们 “ability constraint not satisfied”,下面还有一句 “The type 'MyCounterAddr::MyCounter::Counter' does not have the ability 'key'”。 -编译器告诉我们 `MyCounterAddr::MyCounter::Counter` 这个资源类型缺少 `key` *能力(ability)*,所以不能用 `move_to` 添加到账户地址下。 +出错了!提示我们 “ability constraint not satisfied”,下面还有一句 “The type 'MyCounterAddr::MyCounter::Counter' does not have the ability 'key'”。编译器告诉我们 `MyCounterAddr::MyCounter::Counter` 这个资源类型缺少 `key` *能力(ability)*,所以不能用 `move_to` 添加到账户地址下。 这里涉及到了 Move 的*能力*特性。 :::tip 概念 —— ability -Move语言是面向资源的语言,核心是资源的管理。 -针对资源拥有什么“**能力**”,Move 编程语言抽象了资源的四个属性 —— 可复制(copy)、可索引(key)、可丢弃(drop)、可储存(store)。 -通过这四个属性的不同组合,用户可以方便的定义出任何能力的资源。 -比如用户可以通过 `key + copy + drop + store` 的组合定义出一个普通的信息类型,通过 `key + store` 的组合定义出一个资产类型 —— 例如 NFT —— 没有 `copy` 属性可以保证 NFT 不能被随意的复制,提升了安全性。 +Move语言是面向资源的语言,核心是资源的管理。针对资源拥有什么“**能力**”,Move 编程语言抽象了资源的四个属性 —— 可复制(copy)、可索引(key)、可丢弃(drop)、可储存(store)。通过这四个属性的不同组合,用户可以方便的定义出任何能力的资源。比如用户可以通过 `key + copy + drop + store` 的组合定义出一个普通的信息类型,通过 `key + store` 的组合定义出一个资产类型 —— 例如 NFT —— 没有 `copy` 属性可以保证 NFT 不能被随意的复制,提升了安全性。 Move 提供的四种能力: @@ -197,13 +184,12 @@ Move 提供的四种能力: * key:表示该值是否可以作为全局状态的键进行访问 * store:表示该值是否可以被存储到全局状态 -通过给资源赋予不同的能力,Move 虚拟机可以从根本上保证「资源」只能*转移(move)*,至于能否拷贝、修改、丢弃,需要看资源的具体能力。 -如果强行拷贝、修改或者丢弃,代码编译会出错,根本没有机会运行。 +通过给资源赋予不同的能力,Move 虚拟机可以从根本上保证「资源」只能*转移(move)*,至于能否拷贝、修改、丢弃,需要看资源的具体能力。如果强行拷贝、修改或者丢弃,代码编译会出错,根本没有机会运行。 更多信息可以参考:[认识 Ability](./04-understanding-ability.md) 章节。 ::: -一般来说我们认为,**有 `key` 能力的结构体,就是资源**。 +一般来说我们认为,**有 `key` 和 `store` 能力的结构体,就是资源**。 我们修改代码,按照提示添加 `key` 能力。 @@ -245,8 +231,7 @@ module MyCounterAddr::MyCounter { } ``` -注意第3行我们引用了一个依赖 —— [StarcoinFramwork](https://github.com/starcoinorg/starcoin-framework) 可以认为是 Starcoin 的 Stdlib 标准库。 -我们需要使用库中的 `Signer::address_of(&signer)` 方法来提取 `signer` 的地址。 +注意第3行我们引用了一个依赖 —— [StarcoinFramwork](https://github.com/starcoinorg/starcoin-framework) 可以认为是 Starcoin 的 Stdlib 标准库。我们需要使用库中的 `Signer::address_of(&signer)` 方法来提取 `signer` 的地址。 为了添加依赖到项目中,修改 *Move.toml* 文件 @@ -272,9 +257,7 @@ StarcoinFramework = {git = "https://github.com/starcoinorg/starcoin-framework.gi 4. `borrow_global_mut(address): &mut T`:返回地址下类型为 `T` 的资源的*可变引用(mutable reference)*。 5. `exists(address): bool`:判断地址下是否有类型为 `T` 的资源。 -要使用这些方法,资源 `T` 必须定义在当前模块。 -**这确保了资源只能被定义资源的模块所提供的 API 方法来操作**。 -参数 `address` 和 `signer` 代表了类型为 `T` 的资源存储的地址。 +要使用这些方法,资源 `T` 必须定义在当前模块。**这确保了资源只能被定义资源的模块所提供的 API 方法来操作**。参数 `address` 和 `signer` 代表了类型为 `T` 的资源存储的地址。 ::: 然后我们试着编译一下: @@ -293,14 +276,12 @@ error[E04020]: missing acquires annotation │ Invalid call to borrow_global_mut. ``` -哦!又出错了。 -报错信息提示了我们第14行调用方法获取 `Counter` 结构时,类型(Counter 结构)必须出现在调用上下文的 `acquires` 列表中,而当前函数的 `acquires` 列表没有包含这个类型。 +哦!又出错了。报错信息提示了我们第14行调用方法获取 `Counter` 结构时,类型(Counter 结构)必须出现在调用上下文的 `acquires` 列表中,而当前函数的 `acquires` 列表没有包含这个类型。 这里我们引入 *acquire* 的概念。 :::tip 概念 -当一个函数用 `move_from()`、`borrow_global()`、`borrow_global_mut()` 访问资源时,函数必须要显示声明需要“**获取**”哪种资源。 -这会被 Move 的类型系统确保对资源的引用是安全的、不存在悬空引用。 +当一个函数用 `move_from()`、`borrow_global()`、`borrow_global_mut()` 访问资源时,函数必须要显示声明需要“**获取**”哪种资源。这会被 Move 的类型系统确保对资源的引用是安全的、不存在悬空引用。 ::: 修改后的代码如下: @@ -396,8 +377,7 @@ Release done: release/my-counter.v0.0.1.blob, package hash: 0x31b36a1cd0fd13e840 它将打包编译模块,获得二进制包。 -前文中我们准备了地址为 `0xcada49d6a37864931afb639203501695` 的账户,如果没有余额,可以通过 `dev get-coin` 命令获取一些测试币。 -现在将编译好的模块部署到这个账户地址下。 +前文中我们准备了地址为 `0xcada49d6a37864931afb639203501695` 的账户,如果没有余额,可以通过 `dev get-coin` 命令获取一些测试币。现在将编译好的模块部署到这个账户地址下。 ```starcoin title="starcoin控制台" {1,3,5} starcoin% account unlock 0xcada49d6a37864931afb639203501695 -p @@ -478,8 +458,7 @@ txn 0x032c0eda779157e0ef3949338c3b3e4e6528c7720776d02c2cb0ddd64804f1c2 submitted } ``` -`init_counter` 函数中,我们初始化了一个 *Counter 对象(资源)*,然后 `move_to` 到了调用者地址下。 -让我们看看这个资源是否存在。使用 `state list resource
` 命令查看给定地址下的资源列表。 +`init_counter` 函数中,我们初始化了一个 *Counter 对象(资源)*,然后 `move_to` 到了调用者地址下。让我们看看这个资源是否存在。使用 `state list resource
` 命令查看给定地址下的资源列表。 ```starcoin title="starcoin控制台" {1} starcoin% state list resource 0xcada49d6a37864931afb639203501695 @@ -505,8 +484,7 @@ starcoin% state list resource 0xcada49d6a37864931afb639203501695 1. Linux 的绝对路径名 `/path/to/file` 就是 *fully qualified file name*,相对的 `./to/file` 是一个相对路径地址。 2. 域名系统中,`google.com.` 是一个 *fully qualified domain name*,注意最后的 `.`。意味着这个域名不要继续被递归解析。 -那么对应到 Move 语言中,资源类型是发布到某个地址下的,属于这个地址。 -地址 `0x001` 可以创建一个 `Counter` 类型的资源,地址 `0x002` 也可以创建一个 `Counter` 类型的资源,要区分两个 `Counter`,就需要带上地址和模块名。 +那么对应到 Move 语言中,资源类型是发布到某个地址下的,属于这个地址。地址 `0x001` 可以创建一个 `Counter` 类型的资源,地址 `0x002` 也可以创建一个 `Counter` 类型的资源,要区分两个 `Counter`,就需要带上地址和模块名。 ```
:::: @@ -561,11 +539,7 @@ starcoin% state get resource 0x012ABC 0xcada49d6a37864931afb639203501695::MyCoun 我们用 *Python* 的 `pip` 或者 *Node.js* 的 `npm` 来辅助理解。 -在 `pip` 和 `npm` 这样的中心化包管理托管平台出现之前,我们想安装一个包,需要 `setup.py install /path/to/package`。 -这样子当然不便于包的分发传播与索引。 -后来有了 `pip` 我们是怎么做的呢,包作者先将自己的包打包上传到 `pip` 仓库,`pip` 会存储包并建立索引。 -普通用户只需要 `pip install package_name` 即可。 -`pip` 工具会根据你提供的 `package_name` 下载源码,然后执行安装。这两种安装包的方式其实是一样的。 +在 `pip` 和 `npm` 这样的中心化包管理托管平台出现之前,我们想安装一个包,需要 `setup.py install /path/to/package`。这样子当然不便于包的分发传播与索引。后来有了 `pip` 我们是怎么做的呢,包作者先将自己的包打包上传到 `pip` 仓库,`pip` 会存储包并建立索引。普通用户只需要 `pip install package_name` 即可。`pip` 工具会根据你提供的 `package_name` 下载源码,然后执行安装。这两种安装包的方式其实是一样的。 现在对应到 Move 中。在 `script function` 出现之前是只有 `script` 的,`script` 写在与 `sources` 目录平级的 `scripts` 目录下。 @@ -575,9 +549,7 @@ starcoin% state get resource 0x012ABC 0xcada49d6a37864931afb639203501695::MyCoun starcoin% account execute-script ``` -`script function` 作为 `script` 的替代,[被添加到了 Move 语言中](https://github.com/move-language/move/commit/e0a3acb7d5e3f5dbc07ee76b47dd05229584d4d0)。 -类比于保存在 `pip` 仓库中的软件包。`script function` 会在模块中一起发布到一个地址下(就像包作者把软件包发布在 `pip` 中一样)。 -此时,要调用 `script`,需要创建一个交易,`payload` 中指向已经发布的代码的地址即可。对应到 Starcoin 控制台中是: +`script function` 作为 `script` 的替代,[被添加到了 Move 语言中](https://github.com/move-language/move/commit/e0a3acb7d5e3f5dbc07ee76b47dd05229584d4d0)。类比于保存在 `pip` 仓库中的软件包。`script function` 会在模块中一起发布到一个地址下(就像包作者把软件包发布在 `pip` 中一样)。此时,要调用 `script`,需要创建一个交易,`payload` 中指向已经发布的代码的地址即可。对应到 Starcoin 控制台中是: ``` starcoin% account execute-function --function <0x地址>::<模块>::<函数> --arg xxx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/04-understanding-ability.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/04-understanding-ability.md index 710054abd..89a56d8ac 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/04-understanding-ability.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/04-understanding-ability.md @@ -1,7 +1,6 @@ # 认识 Ability -Move 具有独特的类型系统 —— 非常灵活和可定制,每种类型最多可以拥有4种能力(Ability)。 -这4种能力分别被4个限定符所修饰,它们定义了类型的值是否可以被复制、丢弃和存储。 +Move 具有独特的类型系统 —— 非常灵活和可定制,每种类型最多可以拥有4种能力(Ability)。这4种能力分别被4个限定符所修饰,它们定义了类型的值是否可以被复制、丢弃和存储。 > 这四种能力(Ability)的限制符分别是:`copy`,`drop`,`store` 和 `key`, @@ -81,8 +80,7 @@ error: │ ``` -方法 `Country::new_country()` 创建了一个值,这个值没有被传递到任何其它地方,所以它应该在函数结束时被丢弃。 -但是 Country 类型没有 Drop 能力,所以运行时报错了。现在让我们加上 Drop 限制符试试看。 +方法 `Country::new_country()` 创建了一个值,这个值没有被传递到任何其它地方,所以它应该在函数结束时被丢弃。但是 Country 类型没有 Drop 能力,所以运行时报错了。现在让我们加上 Drop 限制符试试看。 ## drop @@ -114,8 +112,7 @@ script { ## copy -我们学习了如何创建一个结构体 Country 并在函数结束时丢弃它。 -但是如果我们想要复制一个结构体呢?默认情况下结构体是按值传递的,制造一个结构体的副本需要借助关键字 copy (我们会在 下一章 更加深入的学习): +我们学习了如何创建一个结构体 Country 并在函数结束时丢弃它。但是如果我们想要复制一个结构体呢?默认情况下结构体是按值传递的,制造一个结构体的副本需要借助关键字 copy (我们会在 下一章 更加深入的学习): ``` script { @@ -152,7 +149,4 @@ module Country { ## 小结 -基本类型缺省具有 store,copy 和 drop 限制。 -缺省情况下结构体不带任何限制符。 -copy 和 drop 限制符定义了一个值是否可以被复制和丢弃。 -一个结构体有可能带有所有4种限制符。 +基本类型缺省具有 store,copy 和 drop 限制。缺省情况下结构体不带任何限制符。copy 和 drop 限制符定义了一个值是否可以被复制和丢弃。一个结构体有可能带有所有4种限制符。 diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/05-move-package-manager.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/05-move-package-manager.md index 14efae2fd..3bbb8519b 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/05-move-package-manager.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/05-move-package-manager.md @@ -2,9 +2,7 @@ Move Package Manager(mpm)是一个命令行工具,用于开发移动项目,例如用于 Rust 的 Cargo,或用于 NodeJS 的 NPM。 -它集成了 [move-language/move](https://github.com/move-language/move/tree/main/language/tools/move-package) 中引入的最新 move 包系统,并通过 diem 复用 [move-cli](https://github.com/move-language/move/tree/main/language/tools/move-cli) 的大部分功能。 -在深入学习本教程之前,请先阅读 move book 的 [pacakge 部分](https://github.com/move-language/move/blob/main/language/documentation/book/src/packages.md)。 -了解移动包的工作方式是一个先决条件。 +它集成了 [move-language/move](https://github.com/move-language/move/tree/main/language/tools/move-package) 中引入的最新 move 包系统,并通过 diem 复用 [move-cli](https://github.com/move-language/move/tree/main/language/tools/move-cli) 的大部分功能。在深入学习本教程之前,请先阅读 move book 的 [pacakge 部分](https://github.com/move-language/move/blob/main/language/documentation/book/src/packages.md)。了解移动包的工作方式是一个先决条件。 ## 安装 @@ -216,9 +214,7 @@ OPTIONS: --rpc use remote starcoin rpc as initial state ``` -指令 `init` 可以声明规范测试的初始状态。 -你可以通过提供参数 `-n test` 从新的区块链状态开始,或者从远程状态快照,如 `--rpc http://main.seed.starcoin.org:9850 --block-number 100000` 分叉。 -`--address ` 可用于声明其他命名地址,稍后将在规范测试中使用。 +指令 `init` 可以声明规范测试的初始状态。你可以通过提供参数 `-n test` 从新的区块链状态开始,或者从远程状态快照,如 `--rpc http://main.seed.starcoin.org:9850 --block-number 100000` 分叉。`--address ` 可用于声明其他命名地址,稍后将在规范测试中使用。 例子: @@ -253,11 +249,9 @@ OPTIONS: 指令 `block` 开始一个新的块。 -此块指令和下一个块指令之间的每个指令都在此块中运行。 -你可以通过自定义 `--author`、`--timestamp`、`--uncles` 来满足您的需要。 +此块指令和下一个块指令之间的每个指令都在此块中运行。你可以通过自定义 `--author`、`--timestamp`、`--uncles` 来满足您的需要。 -如果没有指定块指令,事务将在默认块上运行,其块号是初始状态的下一个块号。 -如果你从一个区块编号为 `h` 的远程状态分叉,那么下一个区块的编号是 `h+1`。 +如果没有指定块指令,事务将在默认块上运行,其块号是初始状态的下一个块号。如果你从一个区块编号为 `h` 的远程状态分叉,那么下一个区块的编号是 `h+1`。 例子: @@ -289,9 +283,7 @@ OPTIONS: --public-key ``` -指令 `faucet` 可以创建和点击一个地址(可以命名为 `alice`、`tom` 等地址或 `0x1`、`0x2` 等原始地址),其中包含一定数量的 STC。 -如果地址是命名地址,它将自动生成一个原始地址(和公钥)并将其分配给命名地址。 -如果您对 `public-key` 有一些特定要求,请使用 `--public-key` 来指定它。 +指令 `faucet` 可以创建和点击一个地址(可以命名为 `alice`、`tom` 等地址或 `0x1`、`0x2` 等原始地址),其中包含一定数量的 STC。如果地址是命名地址,它将自动生成一个原始地址(和公钥)并将其分配给命名地址。如果您对 `public-key` 有一些特定要求,请使用 `--public-key` 来指定它。 例子: @@ -323,11 +315,9 @@ OPTIONS: --syntax ``` -指令 `publish` 可以将模块发布到区块链。 -模块代码必须遵循指令。 +指令 `publish` 可以将模块发布到区块链。模块代码必须遵循指令。 -`--gas-budget` 指定交易的最大气体。 -`--syntax` 现在可以输入。 +`--gas-budget` 指定交易的最大气体。`--syntax` 现在可以输入。 示例: @@ -381,12 +371,9 @@ ARGS: ``` -指令 `run` 可以执行脚本函数的脚本。 -如果是脚本,则脚本代码必须遵循指令。 -如果是脚本函数,则应提供 ``。 +指令 `run` 可以执行脚本函数的脚本。如果是脚本,则脚本代码必须遵循指令。如果是脚本函数,则应提供 ``。 -`--signers` 声明交易发送者。 -`--type-args` 和 `--args` 声明类型参数和脚本函数脚本的参数。 +`--signers` 声明交易发送者。`--type-args` 和 `--args` 声明类型参数和脚本函数脚本的参数。 例子: @@ -455,11 +442,7 @@ OPTIONS: ### 集成测试期望 -每个集成测试都应该有一个对应的期望文件,其中包含集成测试中每个指令的期望输出。 -Move 包管理器会将集成测试的测试结果与期望文件进行比较。 -如果有不同的输出,则集成测试失败。 -您可以在第一次运行 `mpm integration-test` 时通过提供 `--ub` 参数来生成预期的文件。 -但是您必须检查生成的输出是否真的是您的集成测试的预期输出。 +每个集成测试都应该有一个对应的期望文件,其中包含集成测试中每个指令的期望输出。Move 包管理器会将集成测试的测试结果与期望文件进行比较。如果有不同的输出,则集成测试失败。您可以在第一次运行 `mpm integration-test` 时通过提供 `--ub` 参数来生成预期的文件。但是您必须检查生成的输出是否真的是您的集成测试的预期输出。 例子: diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/06-starcoin-framework/2-signer-delegated.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/06-starcoin-framework/2-signer-delegated.md index 9964e6835..a09a1bb7f 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/06-starcoin-framework/2-signer-delegated.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/06-starcoin-framework/2-signer-delegated.md @@ -2,10 +2,7 @@ ## 从以太坊的合约账户说起 -对比比特币,以太坊最大的创新是引入智能合约,智能合约让区块链拥有了强大的表达能力。 -而智能合约的背后,是全新的账户模型。跟比特币的 UTXO 模型不同,以太坊采用了 Account 模型。 -以太坊有两种类型的 Account,分别是外部账户(Externally Owned Account)和合约账户(Contract Account)。 -这两种账户之间主要有三个方面的差异: +对比比特币,以太坊最大的创新是引入智能合约,智能合约让区块链拥有了强大的表达能力。而智能合约的背后,是全新的账户模型。跟比特币的 UTXO 模型不同,以太坊采用了 Account 模型。以太坊有两种类型的 Account,分别是外部账户(Externally Owned Account)和合约账户(Contract Account)。这两种账户之间主要有三个方面的差异: ![eth_eoc_vs_contract](../../../../../../static/img/contract_account/eth_eoc_vs_contract.jpg) @@ -13,8 +10,7 @@ * 是否有合约代码 * 能否主动发起交易 -外部账户需要通过私钥创建,是面向普通用户的账户,只能存储当前用户的 Balance,不允许有代码。用户通过私钥控制账户,主动发起交易,是普通用户的入口。 -而合约账户由外部账户的 Address 等数据计算而来,可以存储代码。账户状态由合约控制,存储通过合约产生的所有数据。 +外部账户需要通过私钥创建,是面向普通用户的账户,只能存储当前用户的 Balance,不允许有代码。用户通过私钥控制账户,主动发起交易,是普通用户的入口。而合约账户由外部账户的 Address 等数据计算而来,可以存储代码。账户状态由合约控制,存储通过合约产生的所有数据。 ![eth_create_account](../../../../../../static/img/contract_account/eth_create_account.jpg) @@ -29,19 +25,15 @@ 5. 合约调用的 Context 理解起来比较复杂,容易引起安全问题; 6. 代码和状态没有分开存储; -以太坊在 Account 模型上做了很多创新,但是随着区块链的快速发展,很多设计上的缺陷也越来越明显。 -由于 Account 模型对智能合约的支持非常友好,Starcoin 也采用了 Account 模型。 -跟以太坊的 Account 模型对比,Starcoin 的 Account 模型有非常大的优势,在继承 Account 模型优势的基础上,很好的弥补了以太坊 Account 模型的不足。 +以太坊在 Account 模型上做了很多创新,但是随着区块链的快速发展,很多设计上的缺陷也越来越明显。由于 Account 模型对智能合约的支持非常友好,Starcoin 也采用了 Account 模型。跟以太坊的 Account 模型对比,Starcoin 的 Account 模型有非常大的优势,在继承 Account 模型优势的基础上,很好的弥补了以太坊 Account 模型的不足。 ## 更先进的 Account 模型 -为了更好地为广大用户提供服务,Starcoin 的 Account 模型设计了 Data 区和 Code 区。 -Data 区用于存储用户数据,Code 区用于存储合约代码,彻底将“用户数据”与“合约代码”分开存储。 +为了更好地为广大用户提供服务,Starcoin 的 Account 模型设计了 Data 区和 Code 区。Data 区用于存储用户数据,Code 区用于存储合约代码,彻底将“用户数据”与“合约代码”分开存储。 ![starcoin_account](../../../../../../static/img/contract_account/starcoin_account.jpg) -由于 Starcoin 的智能合约语言 Move 是面向资源编程的,合约生成的 Resource 在 Move 虚拟机的保障下,只能在账户之间转移,不能被修改(修改必须调用合约)。 -所以,在 Starcoin 的账户模型上运行 Move 合约有非常多的优势: +由于 Starcoin 的智能合约语言 Move 是面向资源编程的,合约生成的 Resource 在 Move 虚拟机的保障下,只能在账户之间转移,不能被修改(修改必须调用合约)。所以,在 Starcoin 的账户模型上运行 Move 合约有非常多的优势: * 账户区分 Data 区和 Code 区,合约存储在账户 Code 区 * 任何账户都可以同时包含 Data 和 Code @@ -56,10 +48,7 @@ Data 区用于存储用户数据,Code 区用于存储合约代码,彻底将 ## Starcoin的合约账户 -前面对比了 Starcoin 和以太坊的账户模型,可以说,Starcoin 有更先进的账户模型。 -任何一个 Starcoin 账户可以同时存储 Data 和 Code,能覆盖以太坊的所有场景。 -为了扩展更丰富的应用场景,Starcoin 也设计了合约账户,所以,Starcoin 跟以太坊一样也包含外部账户(Externally Owned Account)和合约账户(Contract Account)。 -但是,Starcoin 的合约账户跟以太坊的合约账户有非常大的区别。接下来我们从以下3个方面,深入介绍 Starcoin 的合约账户。 +前面对比了 Starcoin 和以太坊的账户模型,可以说,Starcoin 有更先进的账户模型。任何一个 Starcoin 账户可以同时存储 Data 和 Code,能覆盖以太坊的所有场景。为了扩展更丰富的应用场景,Starcoin 也设计了合约账户,所以,Starcoin 跟以太坊一样也包含外部账户(Externally Owned Account)和合约账户(Contract Account)。但是,Starcoin 的合约账户跟以太坊的合约账户有非常大的区别。接下来我们从以下3个方面,深入介绍 Starcoin 的合约账户。 ### 1. Starcoin合约账户 @@ -71,8 +60,7 @@ Data 区用于存储用户数据,Code 区用于存储合约代码,彻底将 ### 2. Starcoin如何实现合约账户 - Starcoin 的账户代码在 Stdlib 的 `Account.move` 合约中,实现了外部账户(Externally Owned Account)和合约账户(Contract Account)。 - 而跟合约账户相关的 Move 代码非常的简洁和巧妙,充分利用了 Move 的优势和特点。以下是 Starcoin 合约账户的主要实现: +Starcoin 的账户代码在 Stdlib 的 `Account.move` 合约中,实现了外部账户(Externally Owned Account)和合约账户(Contract Account)。而跟合约账户相关的 Move 代码非常的简洁和巧妙,充分利用了 Move 的优势和特点。以下是 Starcoin 合约账户的主要实现: ``` // SignerDelegated can only be stored under address, not in other structs. @@ -105,9 +93,7 @@ SignerDelegated 像是一把锁,SignerCapability 像是一把钥匙。代码 ### 3. 创建 Starcoin 合约账户 -前面我们说过,Starcoin 的账户实现是统一的,合约账户和外部账户之间可以相互转换。 -跟以太坊的合约账户创建流程不同,Starcoin 的合约账户创建流程只是在外部账户创建流程的基础上,多了一步“托管签名权”。 -下图是 Starcoin 创建账户的流程,最后紫色→和紫色+表示通过外部账户创建合约账户。 +前面我们说过,Starcoin 的账户实现是统一的,合约账户和外部账户之间可以相互转换。跟以太坊的合约账户创建流程不同,Starcoin 的合约账户创建流程只是在外部账户创建流程的基础上,多了一步“托管签名权”。下图是 Starcoin 创建账户的流程,最后紫色→和紫色+表示通过外部账户创建合约账户。 ![create_starcoin_account](../../../../../../static/img/contract_account/create_starcoin_account.jpg) @@ -123,19 +109,13 @@ Starcoin 将 addr 合约账户的签名权 SignerCapability 进行托管,实 ### 1. 使用合约账户的例子 -我们来想象一下这样的场景。假设某个平台有普通用户和VIP用户,并且针对不同类型的用户读写不同的 Pool 数据。 -在合约设计的阶段,考虑到 Common Pool 和 VIP Pool 的安全,不提供直接发起交易的能力,而将两个 Pool 合约的签名权 SignerCapability 托管给 Category Contract。 -在用户调用合约的时候,先确定用户类型,再根据类型使用对应的签名权切换身份,调用对应的合约。 -这样既能轻松统一用户的入口,保障业务逻辑的正确,又能更好地提供合约的安全性。 +我们来想象一下这样的场景。假设某个平台有普通用户和VIP用户,并且针对不同类型的用户读写不同的 Pool 数据。在合约设计的阶段,考虑到 Common Pool 和 VIP Pool 的安全,不提供直接发起交易的能力,而将两个 Pool 合约的签名权 SignerCapability 托管给 Category Contract。在用户调用合约的时候,先确定用户类型,再根据类型使用对应的签名权切换身份,调用对应的合约。这样既能轻松统一用户的入口,保障业务逻辑的正确,又能更好地提供合约的安全性。 ![starcoin_pool_contract](../../../../../../static/img/contract_account/starcoin_pool_contract.jpg) ### 2. Starcoin 的第一个合约账户 Genesis Account -在 Starcoin 的 Stdlib 中也能找到很多合约账户应用的例子,最典型的就是 GenesisSignerCapability。 -Genesis Account 是 Starcoin 的第一合约账户,没有私钥,不受任何人管理,是一个极其特殊的账户。 -同时,Genesis Account 又担负着存储一些全局数据的重任,是 NFT 和 Oracle 的注册中心,是如何做到的呢? -我们来看一下 GenesisSignerCapability 合约的核心代码: +在 Starcoin 的 Stdlib 中也能找到很多合约账户应用的例子,最典型的就是 GenesisSignerCapability。Genesis Account 是 Starcoin 的第一合约账户,没有私钥,不受任何人管理,是一个极其特殊的账户。同时,Genesis Account 又担负着存储一些全局数据的重任,是 NFT 和 Oracle 的注册中心,是如何做到的呢?我们来看一下 GenesisSignerCapability 合约的核心代码: ``` struct GenesisSignerCapability has key { @@ -158,8 +138,6 @@ Genesis Account 是 Starcoin 的第一合约账户,没有私钥,不受任何 ## 总结 -相比以太坊的账户模型,Starcoin 的账户模型有非常多的优点,能够覆盖以太坊的所有场景,同时,又能解决很多以太坊面临的问题。 -可以说,Starcoin 有更先进的账户模型。 +相比以太坊的账户模型,Starcoin 的账户模型有非常多的优点,能够覆盖以太坊的所有场景,同时,又能解决很多以太坊面临的问题。可以说,Starcoin 有更先进的账户模型。 -Starcoin 在统一的账户模型上,巧妙地实现了外部账户和合约账户。 -外部账户是普通用户的入口。合约账户则让合约轻松拥有「切换身份」的能力,为合约扩展了更多有意思的场景。 +Starcoin 在统一的账户模型上,巧妙地实现了外部账户和合约账户。外部账户是普通用户的入口。合约账户则让合约轻松拥有「切换身份」的能力,为合约扩展了更多有意思的场景。 diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/06-starcoin-framework/5-nft.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/06-starcoin-framework/5-nft.md index 46e0aed4f..84d629a9d 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/06-starcoin-framework/5-nft.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/06-starcoin-framework/5-nft.md @@ -2,18 +2,13 @@ NFT(non-fungible token)即非同质化代币,也称为不可替代性代币或契约代币。 -非同质化代币用于追踪一个特定事物的所有权,这个事物可以是一个数字资产,例如一个电子游戏道具、数字藏品; -也可以对应现实中的资产,比如一辆车、一栋房子、遗嘱、土地权等等。 -这些特定的事物可以与契约唯一绑定,通过 NFT 追踪契约的拥有者。 +非同质化代币用于追踪一个特定事物的所有权,这个事物可以是一个数字资产,例如一个电子游戏道具、数字藏品;也可以对应现实中的资产,比如一辆车、一栋房子、遗嘱、土地权等等。这些特定的事物可以与契约唯一绑定,通过 NFT 追踪契约的拥有者。 -Starcoin 使用 Move 作为智能合约语言,并且巧妙地运用 Move 语言的优点,定义了一套安全的、可扩展的标准 NFT 协议,开箱即用,简洁高效。 -跟以太坊的NFT协议对比,Starcoin 的标准NFT协议有更加丰富的特性。 +Starcoin 使用 Move 作为智能合约语言,并且巧妙地运用 Move 语言的优点,定义了一套安全的、可扩展的标准 NFT 协议,开箱即用,简洁高效。跟以太坊的NFT协议对比,Starcoin 的标准NFT协议有更加丰富的特性。 ## 作用 -在智能合约中,代币(token)用来表达可拆分的数字资源,而要表达不可拆分的资源,就需要 NFT。 -在 Move 中,任何一个不可 copy 和 drop 的类型实例,都可以认为是一个不可拆分的资源,是一个 NFT。 -但 NFT 需要一种统一展示的标准,以及 NFT 的收集和转移方法,所以设计本标准。 +在智能合约中,代币(token)用来表达可拆分的数字资源,而要表达不可拆分的资源,就需要 NFT。在 Move 中,任何一个不可 copy 和 drop 的类型实例,都可以认为是一个不可拆分的资源,是一个 NFT。但 NFT 需要一种统一展示的标准,以及 NFT 的收集和转移方法,所以设计本标准。 ## 目标 @@ -44,8 +39,7 @@ NFT 是 Move 中的一种类型,它支持 store ability,但不可 copy 以 4. `type_meta`:开发者自定义的 metadata,同时用来标记 NFT 的类型。Metadata 不是资源,它表达信息,所以支持 copy + store + drop。 5. `body`:NFT 包含的资源,可以用来嵌入其他的资源。 -如果把 NFT 视为一个箱子,NFT 本身定义了这个箱子的归属,唯一编号,以及展示方式,而 NFTBody 就是箱子中封装的珠宝。 -展示方式通过 Metadata 来定义。 +如果把 NFT 视为一个箱子,NFT 本身定义了这个箱子的归属,唯一编号,以及展示方式,而 NFTBody 就是箱子中封装的珠宝。展示方式通过 Metadata 来定义。 ```rust struct Metadata has copy, store, drop { @@ -60,8 +54,7 @@ struct Metadata has copy, store, drop { } ``` -Metadata 定义了 NFT 展示所需要的基本信息,名称,图片,描述。 -如果有其他需要扩展的信息,可以定义在 `type_meta` 中。图片有两个字段表达,`image` 表示图片地址,`image_data` 可以直接保存图片的二进制数据,客户端展示的时候,使用 `image` 和 `image_data` 中不为空的那个字段。 +Metadata 定义了 NFT 展示所需要的基本信息,名称,图片,描述。如果有其他需要扩展的信息,可以定义在 `type_meta` 中。图片有两个字段表达,`image` 表示图片地址,`image_data` 可以直接保存图片的二进制数据,客户端展示的时候,使用 `image` 和 `image_data` 中不为空的那个字段。 另外,有的 NFT 的所有实例会使用同一个图片,这种情况下,NFT metadata 中的 `image` 和 `image_data` 可以都为空,客户端展示的时候使用 NFTTypeInfoV2 中的 metadata。 @@ -75,8 +68,7 @@ struct NFTTypeInfoV2 has key, store { } ``` -NFTTypeInfoV2 用于维护 NFT id 的计数器,以及该 NFT 类型的全局 metata,每一种 NFT 类型需要先在注册中心注册。 -所有的 NFT 类型都注册在 `0x1` 这个账号下。 +NFTTypeInfoV2 用于维护 NFT id 的计数器,以及该 NFT 类型的全局 metata,每一种 NFT 类型需要先在注册中心注册。所有的 NFT 类型都注册在 `0x1` 这个账号下。 > 注: NFTTypeInfo 在 stdlibv7 中变为 NFTTypeInfoV2 @@ -174,9 +166,7 @@ struct VideoNFTBody has store{ ### 自定义转让逻辑 -有的 NFT 应用场景下,NFT 转让是受限的,比如作为会员凭证。 -这种情况下,需要自定义一种 NFT 的存储机制,从而实现自定义转让机制。 -储存在 NFTGallery 中的 NFT,完全受用户控制,NFT 的开发者不能限制它的使用和转让。 +有的 NFT 应用场景下,NFT 转让是受限的,比如作为会员凭证。这种情况下,需要自定义一种 NFT 的存储机制,从而实现自定义转让机制。储存在 NFTGallery 中的 NFT,完全受用户控制,NFT 的开发者不能限制它的使用和转让。 以 IdentifierNFT 为例, IdentifierNFT 是一种 NFT 容器,它保证每个用户只能拥有一个同一个类型的 NFT,NFT 开发者授予用户 NFT 后,用户无法转让,一般用在用户身份相关的 NFT 场景下,比如荣誉奖章等。 @@ -219,5 +209,4 @@ public fun revoke(_cap: &mut BurnC ## 总结 -Starcoin 的 NFT 协议是一套非常完善的工具,有良好的安全性和可扩展性,可以预见,未来会有非常大的发展空间。 -MerkleNFT 和 GenesisNFT 巧妙地将 MerkleTree 与 NFT 协议结合,轻松解决了大数组等疑难问题,相信在 NFT 空投等场景下会有非常大的作用。 +Starcoin 的 NFT 协议是一套非常完善的工具,有良好的安全性和可扩展性,可以预见,未来会有非常大的发展空间。MerkleNFT 和 GenesisNFT 巧妙地将 MerkleTree 与 NFT 协议结合,轻松解决了大数组等疑难问题,相信在 NFT 空投等场景下会有非常大的作用。 diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/06-starcoin-framework/7-oracle.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/06-starcoin-framework/7-oracle.md index e4c7318a2..092524d67 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/06-starcoin-framework/7-oracle.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/06-starcoin-framework/7-oracle.md @@ -4,14 +4,9 @@ Starcoin 标准Oracle协议介绍 ## 为什么需要Oracle? -区块链发展至今,遇到过很多困难,这些困难的背后往往催生出更多的创新。 -链上如何获取真实世界的数据?合约如何捕捉市场的价格波动?链游如何保证不可预测性? -类似的很多疑问困扰着我们,Oracle(预言机)应运而生。 -Oracle不仅打通了链上与链下,也在公链之间开了一扇门,极大的拓展了区块链的应用场景。 -所以Oracle就像一座桥梁,让不同地方的数据相互之间建立起联系。 +区块链发展至今,遇到过很多困难,这些困难的背后往往催生出更多的创新。链上如何获取真实世界的数据?合约如何捕捉市场的价格波动?链游如何保证不可预测性?类似的很多疑问困扰着我们,Oracle(预言机)应运而生。Oracle不仅打通了链上与链下,也在公链之间开了一扇门,极大的拓展了区块链的应用场景。所以Oracle就像一座桥梁,让不同地方的数据相互之间建立起联系。 -Oracle已成为公链生态中必备的一个基础组件。 -Starcoin也定义了一套标准的Oracle协议,它就像卫星一样,将星球上的数据收集起来,再通过Move合约分发到其他的地方,创造丰富的价值。 +Oracle已成为公链生态中必备的一个基础组件。Starcoin也定义了一套标准的Oracle协议,它就像卫星一样,将星球上的数据收集起来,再通过Move合约分发到其他的地方,创造丰富的价值。 ## Starcoin的标准Oracle协议 @@ -108,13 +103,11 @@ Starcoin的标准Oracle协议在结构上保持简洁高效,很好地应用了 ### 1. 实时价格 -用户可以从不同的数据源采集加密货币的时间和价格数据,通过Oracle以接近实时的方式提交到链上。 -其他合约可以通过筛选数据源,直接使用这些数据,或者聚合处理之后使用这些数据,非常的方便和灵活。 +用户可以从不同的数据源采集加密货币的时间和价格数据,通过Oracle以接近实时的方式提交到链上。其他合约可以通过筛选数据源,直接使用这些数据,或者聚合处理之后使用这些数据,非常的方便和灵活。 ### 2. 随机数和链游 -我们都知道在输入确定的情况下,智能合约必须保证所有人的执行结果是一致的,所以链上很难提供类似随机数的机制。 -而通过Oracle,用户可以将 VRF 随机数据提交到链上,这样既能验证随机数的正确性,又能让合约拥有随机的能力而增加更丰富的场景。 +我们都知道在输入确定的情况下,智能合约必须保证所有人的执行结果是一致的,所以链上很难提供类似随机数的机制。而通过Oracle,用户可以将 VRF 随机数据提交到链上,这样既能验证随机数的正确性,又能让合约拥有随机的能力而增加更丰富的场景。 以游戏为例,不可预测性是游戏具有吸引力的重要的因素之一。游戏通过Oracle获取随机数,可以保障游戏的不可预测性。 @@ -122,8 +115,7 @@ Starcoin的标准Oracle协议在结构上保持简洁高效,很好地应用了 ### 3. 链下复杂计算 -由于Gas、单线程等限制,智能合约中不能有复杂的计算逻辑,这会让智能合约的场景非常受限。 -如果通过某种类似Proof的方案,让复杂的计算在链下执行,然后把结果和Proof提交到链上的Oracle中,提供给其他合约使用,会大大降低智能合约的设计难度,同时,能够丰富智能合约的应用场景。 +由于Gas、单线程等限制,智能合约中不能有复杂的计算逻辑,这会让智能合约的场景非常受限。如果通过某种类似Proof的方案,让复杂的计算在链下执行,然后把结果和Proof提交到链上的Oracle中,提供给其他合约使用,会大大降低智能合约的设计难度,同时,能够丰富智能合约的应用场景。 ### 4. 保险理赔 @@ -131,25 +123,17 @@ Starcoin的标准Oracle协议在结构上保持简洁高效,很好地应用了 ### 5. 预测市场 -市场是瞬息万变的,任何链下的事件产生,都可以通过Oracle快速映射到链上。 -智能合约通过获取Oracle提供的数据,触发预测市场执行结算等操作。例如,体育竞技结果、黄金价格变化、电竞等等。 +市场是瞬息万变的,任何链下的事件产生,都可以通过Oracle快速映射到链上。智能合约通过获取Oracle提供的数据,触发预测市场执行结算等操作。例如,体育竞技结果、黄金价格变化、电竞等等。 ## Starcoin的PriceOracle实现 -我们深入了解了Starcoin标准Oracle协议的设计,然后探讨了其丰富的应用场景。 -接下来,我们以市场价格的场景为例,了解一下如何在标准的Oracle协议之上,将特定场景的业务逻辑添加进去,实现一个完整的Oracle应用。 +我们深入了解了Starcoin标准Oracle协议的设计,然后探讨了其丰富的应用场景。接下来,我们以市场价格的场景为例,了解一下如何在标准的Oracle协议之上,将特定场景的业务逻辑添加进去,实现一个完整的Oracle应用。 -Starcoin在标准的Oracle协议之上封装了一个PriceOracle模块,针对价格场景做了官方的实现。 -PriceOracle也是一个通用的合约,能够注册任意类型的数字资产。 -然后,Starcoin在PriceOracle之上,实现了一个STCUSDOracle合约,注册了一对货币组合STC和USDT。 -再通过PriceOracleScript合约,将STC对应USD的价格注册到链上的Oracle中。 -最后,任何人可以通过PriceOracleAggregator聚合器对价格进行筛选、过滤,将聚合得到的价格应用到自己的产品中。 -接下来,我们来深入的分析一下PriceOracle、STCUSDOracle以及PriceOracleAggregator的源码。 +Starcoin在标准的Oracle协议之上封装了一个PriceOracle模块,针对价格场景做了官方的实现。PriceOracle也是一个通用的合约,能够注册任意类型的数字资产。然后,Starcoin在PriceOracle之上,实现了一个STCUSDOracle合约,注册了一对货币组合STC和USDT。再通过PriceOracleScript合约,将STC对应USD的价格注册到链上的Oracle中。最后,任何人可以通过PriceOracleAggregator聚合器对价格进行筛选、过滤,将聚合得到的价格应用到自己的产品中。接下来,我们来深入的分析一下PriceOracle、STCUSDOracle以及PriceOracleAggregator的源码。 1. PriceOracle -PriceOracle建立在标准的Oracle协议之上,是针对价格场景实现的一个通用合约。 -也就是说,任何Price形态的数据,都可以通过PriceOracle上链。 +PriceOracle建立在标准的Oracle协议之上,是针对价格场景实现的一个通用合约。也就是说,任何Price形态的数据,都可以通过PriceOracle上链。 ``` struct PriceOracleInfo has copy,store,drop{ @@ -157,8 +141,7 @@ struct PriceOracleInfo has copy,store,drop{ } ``` -在标准的Oracle协议中,OracleInfo有一个泛型参数是Info: copy+store+drop。 -在PriceOracle中,Info对应的具体实现是PriceOracleInfo,里面只包含了计算因子scaling_factor,所以PriceOracleInfo必须有copy、store、drop这3种ability,这是Info这个泛型参数的要求。 +在标准的Oracle协议中,OracleInfo有一个泛型参数是Info: copy+store+drop。在PriceOracle中,Info对应的具体实现是PriceOracleInfo,里面只包含了计算因子scaling_factor,所以PriceOracleInfo必须有copy、store、drop这3种ability,这是Info这个泛型参数的要求。 在明确了PriceOracleInfo的数据定义之后,可以调用Oracle合约的register_oracle函数注册PriceOracleInfo,还可以调用Oracle的init_data_source和update将数据上链。 @@ -172,9 +155,7 @@ PriceOracle是标准Oracle协议上面的一个应用,所以继承了Oracle协 struct STCUSD has copy,store,drop {} ``` -我们看到,在标准的Oracle协议中,OracleInfo除了Info,还有另外一个泛型参数OracleT: copy+store+drop,OracleT表示一对货币组合,Info表示信息。 -PriceOracle只实现了PriceOracleInfo,OracleT在PriceOracle也是泛型参数,而STCUSDTOracle合约的最大作用就是确定OracleT的类型为STCUSD。 -当然,任何人可以实现属于自己的一对货币组合合约,定义自己的产品。 +我们看到,在标准的Oracle协议中,OracleInfo除了Info,还有另外一个泛型参数OracleT: copy+store+drop,OracleT表示一对货币组合,Info表示信息。PriceOracle只实现了PriceOracleInfo,OracleT在PriceOracle也是泛型参数,而STCUSDTOracle合约的最大作用就是确定OracleT的类型为STCUSD。当然,任何人可以实现属于自己的一对货币组合合约,定义自己的产品。 3. PriceOracleScript diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/07-deploy-first-move-contract.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/07-deploy-first-move-contract.md index ac402bf73..5e72d44cc 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/07-deploy-first-move-contract.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/07-deploy-first-move-contract.md @@ -6,20 +6,15 @@ ## 背景 -Move 语言是 Meta(原 Facebook)公司为 *Libra* 项目(后更名为 *Diem*)开发的*领域专用语言(DSL)*。 -Move 语言作为*面向资源编程*的智能合约编程语言,无疑给区块链技术注入新的活力。 -*Starcoin 区块链*是**第一条使用 Move 语言的的公链**,作为 Move 语言的贡献者以及受益者,Starcoin 一直努力构建 *Move 生态*。 +Move 语言是 Meta(原 Facebook)公司为 *Libra* 项目(后更名为 *Diem*)开发的*领域专用语言(DSL)*。Move 语言作为*面向资源编程*的智能合约编程语言,无疑给区块链技术注入新的活力。*Starcoin 区块链*是**第一条使用 Move 语言的的公链**,作为 Move 语言的贡献者以及受益者,Starcoin 一直努力构建 *Move 生态*。 -虽然 Diem 因为一些不可抗拒的因素而告终,但是这过程中诞生的一批先进技术,无疑给 Web3 世界注入了新的活力。 -不少热衷区块链技术、憧憬 Web3 美好的世界的技术者,纷纷奔赴新的航程,沿用 *Diem 区块链*留下的开源技术构建了两条基于 Move 语言来编写智能合约的新公链 *Aptos* 和 *Sui*(目前已处于测试网阶段)。 +虽然 Diem 因为一些不可抗拒的因素而告终,但是这过程中诞生的一批先进技术,无疑给 Web3 世界注入了新的活力。不少热衷区块链技术、憧憬 Web3 美好的世界的技术者,纷纷奔赴新的航程,沿用 *Diem 区块链*留下的开源技术构建了两条基于 Move 语言来编写智能合约的新公链 *Aptos* 和 *Sui*(目前已处于测试网阶段)。 Move 生态中的三大公链 *Starcoin*、*Aptos* 和 *Sui*,正以*星星之火可以燎原*之势,共同为 Move 生态发光发热。 ## 部署智能合约的重要性 -区块链技术的发展经历了两个阶段,比特币(BTC)开启了*区块链1.0*时代,以太坊(ETH)开启了*区块链2.0*时代。 -以太坊的出现为区块链带来了*智能合约*这一关键技术,让区块链不只停留在记账这一单的目的,而是带来更多的应用拓展性。 -遗憾的是,智能合约如同一把双刃剑,在带来众多丰富功能拓展的同时,也容易让智能合约开发者无意间引入不安全的代码,让链的资产受到威胁。 +区块链技术的发展经历了两个阶段,比特币(BTC)开启了*区块链1.0*时代,以太坊(ETH)开启了*区块链2.0*时代。以太坊的出现为区块链带来了*智能合约*这一关键技术,让区块链不只停留在记账这一单的目的,而是带来更多的应用拓展性。遗憾的是,智能合约如同一把双刃剑,在带来众多丰富功能拓展的同时,也容易让智能合约开发者无意间引入不安全的代码,让链的资产受到威胁。 我想,编写简单、安全、易部署的智能合约应该是*区块链3.0*时代应该关注的重点,*面向资源编程*的 Move 语言,无疑给这个问题提供了一个很好的解决方案。 @@ -33,9 +28,7 @@ Move 生态中的三大公链 *Starcoin*、*Aptos* 和 *Sui*,正以*星星之 - 如何打包(编译)一个 Move 模块; - 如何将编译好的二进制文件部署到 Starcoin 区块链上。 -首先,根据[如何设置本地开发网络](../02-getting-started/02-setup/03-dev-network.md)的描述启动一个 *dev* 网络节点。 -并且为账户获取一些 *dev* 网络的测试代币,并解锁账户。 -这里将地址 `0xb19b07b76f00a8df445368a91c0547cc` 作为部署智能合约的账户地址。 +首先,根据[如何设置本地开发网络](../02-getting-started/02-setup/03-dev-network.md)的描述启动一个 *dev* 网络节点。并且为账户获取一些 *dev* 网络的测试代币,并解锁账户。这里将地址 `0xb19b07b76f00a8df445368a91c0547cc` 作为部署智能合约的账户地址。 ```bash starcoin% dev get-coin 0xb19b07b76f00a8df445368a91c0547cc @@ -100,8 +93,7 @@ StarcoinFramework = {git = "https://github.com/starcoinorg/starcoin-framework.gi 1. 修改合约地址 -智能合约是部署在某个账户下的,因此需要修改 *Move.toml* 文件中 `MyCounterAddr` 的值为你实际的账户地址。 -这里以 `0xb19b07b76f00a8df445368a91c0547cc` 为例。 +智能合约是部署在某个账户下的,因此需要修改 *Move.toml* 文件中 `MyCounterAddr` 的值为你实际的账户地址。这里以 `0xb19b07b76f00a8df445368a91c0547cc` 为例。 2. 编译模块 @@ -131,8 +123,7 @@ starcoin% account unlock 0xb19b07b76f00a8df445368a91c0547cc -p - mpm deploy -部署模块需要账号进行签名,`mpm deploy` 命令支持三种账号模式: -1) 本地钱包; 2) 私钥文件; 3) 环境变量。 +部署模块需要账号进行签名,`mpm deploy` 命令支持三种账号模式:1) 本地钱包; 2) 私钥文件; 3) 环境变量。 三种账号模式对应的命令分别如下: diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/100-move-prover/01-move-spec-language.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/100-move-prover/01-move-spec-language.md index e202bf866..c71e82a2b 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/100-move-prover/01-move-spec-language.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/100-move-prover/01-move-spec-language.md @@ -1,10 +1,13 @@ # Move 规范语言 ## 表达式 (Expression) + MSL 中的表达式是 Move 程序表达式的子集以及一组附加结构,如以下所述。 ### 类型系统 + MSL 中的类型系统和 Move 的很相似,但有几点区别: + - Move 中的所有整数类型(`u8`、`u64` 和 `u128`)视为同一类型。 在 MSL 中,这种类型被称为 `num`,它是一种任意精度的*有符号*整数类型。 当 MSL 引用类型为 `u8` 这类整数的变量名时,它将自动扩展为 `num`. @@ -21,73 +24,82 @@ MSL 中的类型系统和 Move 的很相似,但有几点区别: - 还有一种附加类型叫 `range`,它表示整数区间,用 `n..m` 的方式表示一个区间。 ### 命名 -名称解析的工作方式类似于 Move 语言。 -`use` 声明可以为导入的名称引入别名。 -MSL 函数和变量名必须以小写字母开头。 -Schema 名称被视为类型,并且必须以大写字母开头(Schema 是一种新的命名构造,[后面](#TODO)会讨论到)。 -Move 函数、MSL 函数、Move 类型和 Schema 都在相同的命名空间,因此如果通过 Move 的 `use` 声明引入别名,它们必须不能有歧义。 -由于用的是同一命名空间,MSL 函数的名称不能与 Move 函数的名称相同。 -为了解决这个问题,约定俗成,我们给 MSL 函数添加前缀: -比如对于 Move 函数 `has_access`,对应的 MSL 函数叫 `spec_has_access`。 +名称解析的工作方式类似于 Move 语言。`use` 声明可以为导入的名称引入别名。MSL 函数和变量名必须以小写字母开头。Schema 名称被视为类型,并且必须以大写字母开头(Schema 是一种新的命名构造,[后面](#TODO)会讨论到)。 + +Move 函数、MSL 函数、Move 类型和 Schema 都在相同的命名空间,因此如果通过 Move 的 `use` 声明引入别名,它们必须不能有歧义。由于用的是同一命名空间,MSL 函数的名称不能与 Move 函数的名称相同。为了解决这个问题,约定俗成,我们给 MSL 函数添加前缀:比如对于 Move 函数 `has_access`,对应的 MSL 函数叫 `spec_has_access`。 ### 操作符 + 除了 `&`、`&mut` 和 `*` (解引用), MSL 支持所有 Move 中全部运算符。 -除了 Move 中已有的操作符,MSL 还支持向量下标 `v[i]`、 -切片 `v[i..j]` 和区间构造 `i..j`(整数区间是一种新的内置类型,称为 `range`)。 -此外,还支持布尔蕴涵 `p ==> q`,它比 `!p || q` 更直观。 +除了 Move 中已有的操作符,MSL 还支持向量下标 `v[i]`、切片 `v[i..j]` 和区间构造 `i..j`(整数区间是一种新的内置类型,称为 `range`)。此外,还支持布尔蕴涵 `p ==> q`,它比 `!p || q` 更直观。 ### 函数调用 + 像 Move 一样,MSL 表达式也可以调用函数。但是,被调用的必须是 [MSL 函数](#辅助函数) 或 Move 的 **纯**函数。 Move 中的纯函数是指,不修改全局状态、并且不使用 MSL 不支持的 Move 表达式([上面](#操作符)提到的引用相关)的函数。 一个扩展是,如果 Move 函数定义包含直接的 `assert`,则从 MSL 表达式调用它时将忽略断言,并且该函数将被视为纯函数。例如: + ```rust fun get(addr: address): &T { assert(exists(addr), ERROR_CODE); borrow_global(addr) } ``` + 此函数是纯函数,可以从 MSL 表达式中调用。断言将被忽略,函数将被解释为: + ```rust spec fun get(addr: address): T { global(addr) } ``` + 这是因为 MSL 具有 [偏语义](#偏语义)。 ### 语句 -支持有限制的顺序形式,形如 `{ let x = foo(); x + x }`,以及 if-then-else。 -不支持 Move 语言的其他语句形式。 + +支持有限制的顺序形式,形如 `{ let x = foo(); x + x }`,以及 if-then-else。不支持 Move 语言的其他语句形式。 ### 打包和解包 + 支持打包表达式。但目前*不支持*解包表达式。 ### 量词 + 支持*全称量词*和*存在量词*。一般形式是 + ```rust forall <绑定>, ..., <绑定> [ where <表达式> ] : <表达式> exists <绑定>, ..., <绑定> [ where <表达式> ] : <表达式> ``` + - 绑定允许两种形式:`名称: 类型` 或 `名称 in <表达式>`。第二种形式中的`<表达式>`目前必须是 `range` 或 `vector`。 - 可选约束 `where <表达式>` 允许限制量化范围。`forall x: T where p: q` 等价于 `forall x: T : p ==> q`; 而 `exists x: T where p: q` 等价于 `exists x: T : p && q`。 注意也可以对类型进行量化。例如: + ```rust forall t: type, addr: address where exists>(addr): exists>(addr) ``` ### 选择操作符 + 选择操作符允许选择满足谓词的值: + ```rust choose a: address where exists(a) && global(a).value > 0 ``` + 如果谓词不可满足,则选择的结果将是不确定的(参见[偏语义](#偏语义)的讨论)。 选择还可以以从一组整数中选择*最小值*的形式出现,如: + ```rust choose min i: num where in_range(v, i) && v[i] == 2 ``` ### 内置函数 + MSL 支持许多内置常量和函数。其中大部分在 Move 语言中不可用: - `MAX_U8: num`, `MAX_U64: num`, `MAX_U128: num` 返回对应类型的最大值。 @@ -107,28 +119,22 @@ MSL 支持许多内置常量和函数。其中大部分在 Move 语言中不可 - `old(T): T` 返回传入参数在函数入口处的原始值。允许在 `ensures` 后置条件、内联 spec 块(有附加限制)和某些形式的不变量中出现,后面会有讨论。 - `TRACE(T): T` 在语义上是恒等函数,能让 Prover 创建的错误消息中的参数值可视化。 -内置函数位于模块的未命名外部范围内。如果模块定义了一个函数 `len`,那么这个定义将隐藏相应的内置函数。 -要在这种情况下访问内置函数,可以用 `::len(v)` 这种写法。 +内置函数位于模块的未命名外部范围内。如果模块定义了一个函数 `len`,那么这个定义将隐藏相应的内置函数。要在这种情况下访问内置函数,可以用 `::len(v)` 这种写法。 ### 偏语义 -在 MSL 中,表达式具有偏语义(partial semantics)。 -对比之下,Move 程序表达式具有全语义(total semantics),因为它们要么有返回值,要么中止。 -形如 `e[X]` 的表达式依赖于某变量 `X`,它对于 `X` 的某些赋值可能有意义,但对于其他赋值则没有。 -如果整个表达式结果不需要子表达式的值,则子表达式无意义也没关系。 -因此,写 `y != 0 && x / y > 0` 还是 `x / y > 0 && y != 0` 并不重要:布尔运算符是可交换的。 +在 MSL 中,表达式具有偏语义(partial semantics)。对比之下,Move 程序表达式具有全语义(total semantics),因为它们要么有返回值,要么中止。 + +形如 `e[X]` 的表达式依赖于某变量 `X`,它对于 `X` 的某些赋值可能有意义,但对于其他赋值则没有。如果整个表达式结果不需要子表达式的值,则子表达式无意义也没关系。因此,写 `y != 0 && x / y > 0` 还是 `x / y > 0 && y != 0` 并不重要:布尔运算符是可交换的。 -这一基本原则在更高级的语言构造中也适用。 -例如,在 spec 中,条件以怎样的顺序书写无关紧要: -`aborts_if y != 0; ensures result == x / y;` 与 `ensures result == x / y; aborts_if y != 0;` 一样。 -此外,`aborts_if P; aborts_if Q;` 与 `aborts_if Q || P` 也相同。 +这一基本原则在更高级的语言构造中也适用。例如,在 spec 中,条件以怎样的顺序书写无关紧要:`aborts_if y != 0; ensures result == x / y;` 与 `ensures result == x / y; aborts_if y != 0;` 一样。此外,`aborts_if P; aborts_if Q;` 与 `aborts_if Q || P` 也相同。 -此外,偏语义的原则被继承到 [规范辅助函数](#辅助函数) 中,它们的行为是透明的。 -具体来说,内联这些函数和调用它们(按表达式调用的参数传递语义)等价。 +此外,偏语义的原则被继承到 [规范辅助函数](#辅助函数) 中,它们的行为是透明的。具体来说,内联这些函数和调用它们(按表达式调用的参数传递语义)等价。 ## 规范 (Specification) -规范包含在*规范块*(Specification block, 本文缩写为 spec 块)中,它可以作为模块成员出现在 Move 函数当中。 -下面列出了各种类型的 spec 块,后续会有更多说明。 + +规范包含在*规范块*(Specification block, 本文缩写为 spec 块)中,它可以作为模块成员出现在 Move 函数当中。下面列出了各种类型的 spec 块,后续会有更多说明。 + ```rust module M { resource struct Counter { @@ -170,10 +176,13 @@ module M { } } ``` + 除了 Move 函数中的 spec 块之外,spec 块的位置是无关紧要的。此外,结构、函数或模块的规范块可以重复多次,累积内容。 ### 分离规范 + 除了将规范放入与常规 Move 定义相同的模块中,还可以将它们放入单独的“规范”模块中,该模块可以存在于相同或不同的文件中: + ```rust module M { ... @@ -182,12 +191,13 @@ spec M { spec increment { .. } } ``` + 规范模块的语法与常规模块的语法相同,但是不允许定义 Move 函数和结构。 规范模块必须与它所对应的 Move 模块一起编译,而不能单独编译和验证。 -如果 Move 定义相距甚远(例如在不同的文件中),则可以使用此函数的签名来扩充 Move 函数的规范,以提供足够的上下文来理解规范。 -在常规模块和规范模块中可以选择启用此语法: +如果 Move 定义相距甚远(例如在不同的文件中),则可以使用此函数的签名来扩充 Move 函数的规范,以提供足够的上下文来理解规范。在常规模块和规范模块中可以选择启用此语法: + ```rust public fun increment(a: address) acquires Counter { .. } ... @@ -195,32 +205,35 @@ spec increment(a: address) { .. } // 注意这里加上了函数签名,而不 ``` ### 编译指示和属性 -编译指示(pragma)和属性(property)是影响规范解释的通用机制。它们也是在成为主流语法的一部分之前尝试新概念的扩展点。 -这里我们简单介绍它们的一般语法;个别情况将在后面讨论。 + +编译指示(pragma)和属性(property)是影响规范解释的通用机制。它们也是在成为主流语法的一部分之前尝试新概念的扩展点。这里我们简单介绍它们的一般语法;个别情况将在后面讨论。 编译指示的一般形式是: + ```rust spec .. { pragma <名称> = <字面值>; } ``` + 属性的一般形式是: + ```rust spec .. { <指令> [<名称> = <字面值>] <内容>; // ensures、aborts_if、include 等 } ``` -`<字面值>` 可以是 MSL(或 Move 语言)支持的任何值。也可以省略赋值,在这种情况下使用默认值。 -例如,通常用 `pragma option;`,它相当于 `pragma option = true;` 的简写。 + +`<字面值>` 可以是 MSL(或 Move 语言)支持的任何值。也可以省略赋值,在这种情况下使用默认值。例如,通常用 `pragma option;`,它相当于 `pragma option = true;` 的简写。 除了单个编译指示或属性,还可以提供一个列表,如 `invariant [global, isolated] P`. ### 编译指示的继承 -如果在模块层级的 spec 块中用 pragma 设置一个值,这个值默认会适用到模块中所有其他 spec 块当中。 -函数或结构的 spec 块中的具体 pragma 可以覆盖继承的默认值。 -此外,一些 pragma 的默认值可以通过配置 Prover 来定义。 + +如果在模块层级的 spec 块中用 pragma 设置一个值,这个值默认会适用到模块中所有其他 spec 块当中。函数或结构的 spec 块中的具体 pragma 可以覆盖继承的默认值。此外,一些 pragma 的默认值可以通过配置 Prover 来定义。 下面这个例子用到了 `verify` 指示,它用于打开或关闭验证。 + ```rust spec module { pragma verify = false; // 默认情况下,不要验证这个模块中的规范... @@ -233,32 +246,31 @@ spec increment { ``` ### 通用编译指示和属性 + 许多编译指示控制验证的通用行为。这些列在下表中。 -| 名称 | 说明 | -|---------------------------------|----| -| `verify` | 打开或关闭验证 -| `intrinsic` | 让函数跳过 Move 实现并使用 Prover 的 native 实现。这样就可以让函数表现得像 native 的一样,即使它实际上是用 Move 实现的 -| `timeout` | 为函数或模块设置超时(单位为秒)。覆盖命令行选项提供的超时。 -| `verify_duration_estimate` | 设置函数验证所需时间的估计值(单位为秒)。如果它比 `timeout` 时间更长,则跳过验证。 -| `seed` | 为函数或模块设置随机种子。覆盖命令行选项提供的种子。 +| 名称 | 说明 | +|----------------------------|------------------------------------------------------------------------------------------------------------------------| +| `verify` | 打开或关闭验证 | +| `intrinsic` | 让函数跳过 Move 实现并使用 Prover 的 native 实现。这样就可以让函数表现得像 native 的一样,即使它实际上是用 Move 实现的 | +| `timeout` | 为函数或模块设置超时(单位为秒)。覆盖命令行选项提供的超时。 | +| `verify_duration_estimate` | 设置函数验证所需时间的估计值(单位为秒)。如果它比 `timeout` 时间更长,则跳过验证。 | +| `seed` | 为函数或模块设置随机种子。覆盖命令行选项提供的种子。 | 以下属性控制验证的通用行为: -| 名称 | 说明 | -|------------|-------------- -| `[deactivated]` | 从验证中排除关联的条件。 +| 名称 | 说明 | +|-----------------|--------------------------| +| `[deactivated]` | 从验证中排除关联的条件。 | ### 前置/后置状态 -spec 块中的一些条件会同时用到*前置状态*和*后置状态*,将它们相互关联。 -函数规范就是一个例子:在 `ensures P` 条件中,前置状态(函数入口处)和后置状态(函数退出处)通过谓词 `P` 关联。 -不过这个概念其实更普遍,也适用于不变量(invariant),其中全局更新前后两个状态分别是前置状态和后置状态。 -在前后置状态都可用的环境下,表达式默认在后置状态中被求值。 -若要在前置状态中求值表达式,可以使用内置函数 `old(exp)`,该函数返回前置状态参数的求值。 -注意 `exp` 中的每个子表达式都是在前置状态中计算的,包括对辅助函数的调用。 +spec 块中的一些条件会同时用到*前置状态*和*后置状态*,将它们相互关联。函数规范就是一个例子:在 `ensures P` 条件中,前置状态(函数入口处)和后置状态(函数退出处)通过谓词 `P` 关联。不过这个概念其实更普遍,也适用于不变量(invariant),其中全局更新前后两个状态分别是前置状态和后置状态。 + +在前后置状态都可用的环境下,表达式默认在后置状态中被求值。若要在前置状态中求值表达式,可以使用内置函数 `old(exp)`,该函数返回前置状态参数的求值。注意 `exp` 中的每个子表达式都是在前置状态中计算的,包括对辅助函数的调用。 这里所讨论的「状态」包括对全局资源内存的赋值、和对函数的任何类型为 `&mut T` 的参数的赋值。例子: + ```rust fun increment(counter: &mut u64) { *counter = *counter + 1 } spec increment { @@ -276,9 +288,8 @@ spec increment_R { } ``` -值得注意的是,可以使用 `old()` 的表达式是有限制的。 -在没有仔细理解它的意义之前,我们很容易错误地认为所有“值随时间变化的表达式”都可以放在其中。 -但如果你试卷写下类似下面这样一个函数和 spec: +值得注意的是,可以使用 `old()` 的表达式是有限制的。在没有仔细理解它的意义之前,我们很容易错误地认为所有“值随时间变化的表达式”都可以放在其中。但如果你试卷写下类似下面这样一个函数和 spec: + ``` public fun sub1(n: u64): u64 { n = n - 1; @@ -287,45 +298,47 @@ public fun sub1(n: u64): u64 { spec sub1 { ensures n == old(n) - 1; } ``` + 运行后会得到错误信息:old(...) applied to expression which doesn't depend on state. -直觉上这里的变量 `n` 的值确实在执行前后产生了变化,但编译器却认为它是无状态的。 -检查编译器代码,可以看到在 -[`rewrite_exp`](https://github.com/move-language/move/blob/f3b6112d16b0811babfca1bab597095041c1a115/language/move-model/src/spec_translator.rs#L590) -当中,对 `old()` 的调用会作一个 `is_pure` 的检查,看表达式是不是纯的(即 pure,无副作用)。 -[`is_pure()`](https://github.com/move-language/move/blob/f3b6112d16b0811babfca1bab597095041c1a115/language/move-model/src/ast.rs#L1011) 要求表达式要么包含可变引用(mutable reference),要么含有全局状态,或者调用了用到内存的函数。 +直觉上这里的变量 `n` 的值确实在执行前后产生了变化,但编译器却认为它是无状态的。检查编译器代码,可以看到在[`rewrite_exp`](https://github.com/move-language/move/blob/f3b6112d16b0811babfca1bab597095041c1a115/language/move-model/src/spec_translator.rs#L590)当中,对 `old()` 的调用会作一个 `is_pure` 的检查,看表达式是不是纯的(即 pure,无副作用)。[`is_pure()`](https://github.com/move-language/move/blob/f3b6112d16b0811babfca1bab597095041c1a115/language/move-model/src/ast.rs#L1011) 要求表达式要么包含可变引用(mutable reference),要么含有全局状态,或者调用了用到内存的函数。 因此,把 spec 变成如下形式: + ``` spec sub1 { ensures result == n - 1; } ``` + 这样代码就能通过语法检查并通过验证了。 ### 辅助函数 + MSL 允许定义辅助函数。然后可以在表达式中使用这些函数。 辅助函数使用以下语法定义: + ```rust spec fun exists_balance(a: address): bool { exists>(a) } ``` 如例所示,辅助函数可以用泛型。此外,它们也可以访问全局状态。 -对于前置还是后置状态上,辅助函数是中立的。也就是说,它们在当前活动的状态下进行求值。 -例如,为了查看前置状态中是否存在余额,使用 `old(exists_balance(a))`. -因此,在辅助函数的定义中不允许使用 `old(..)` 表达式。 +对于前置还是后置状态上,辅助函数是中立的。也就是说,它们在当前活动的状态下进行求值。例如,为了查看前置状态中是否存在余额,使用 `old(exists_balance(a))`.因此,在辅助函数的定义中不允许使用 `old(..)` 表达式。 辅助函数是偏函数;参见 [偏语义](#偏语义) 的讨论。 #### 无解释函数 + 只要省略函数体,辅助函数就会被定义为**无解释**的: + ```rust spec fun something(x: num): num; ``` -无解释函数允许 Prover 赋予它任意含义,只要它在给定的验证上下文中是一致。 -无解释函数是规范抽象的有用工具(另见 [此处](#TODO))。 + +无解释函数允许 Prover 赋予它任意含义,只要它在给定的验证上下文中是一致。无解释函数是规范抽象的有用工具(另见 [此处](#TODO))。 #### 公理 + 辅助函数的含义可以通过使用**公理**来进一步约束。目前,公理必须包含在模块的 spec 块中: ```move @@ -333,10 +346,7 @@ spec module { axiom forall x: num: something(x) == x + 1; } ``` -应谨慎使用公理,因为它们可能通过相互矛盾的假设在规范逻辑中引入不可靠的推论。 -Move Prover 支持通过 `--check-inconsistency` 命令行选项通过 -[冒烟测试](https://zh.wikipedia.org/wiki/%E5%86%92%E7%83%9F%E6%B5%8B%E8%AF%95_(%E8%BD%AF%E4%BB%B6)) -检测不可靠的假设。 +应谨慎使用公理,因为它们可能通过相互矛盾的假设在规范逻辑中引入不可靠的推论。Move Prover 支持通过 `--check-inconsistency` 命令行选项通过[冒烟测试](https://zh.wikipedia.org/wiki/%E5%86%92%E7%83%9F%E6%B5%8B%E8%AF%95_(%E8%BD%AF%E4%BB%B6))检测不可靠的假设。 ### let 绑定 TODO @@ -346,6 +356,7 @@ TODO MSL 几乎可以完整表示的 Move 语言语义(不过目前还没有形式化的证明),但有一个例外:返回 `&mut T` 类型的函数。 考虑以下代码: + ```rust struct S { x: u64, y: u64 } @@ -358,12 +369,10 @@ spec x_or_y { } ``` -我们没法在 MSL 中指定 `x_or_y` 函数的完整语义,因为 MSL 中不能表达可变引用的语义。 -虽然可以表达「在函数结束时返回的引用指向的值」有什么性质,但后续效果,比如 `*x_or_y(b, &mut s) = 2` 无法被表达。 +我们没法在 MSL 中指定 `x_or_y` 函数的完整语义,因为 MSL 中不能表达可变引用的语义。虽然可以表达「在函数结束时返回的引用指向的值」有什么性质,但后续效果,比如 `*x_or_y(b, &mut s) = 2` 无法被表达。 + +但是,Move Prover 其实**可以理解**这样的函数的意义——只是我们可以用 MSL 表达的性质被限制了。实际上,这意味着我们不能把函数 `x_or_y` 变成不透明的,并且必须让 Prover 直接看函数实现来做验证。具体来说,我们可以验证以下函数,它可以是不透明的: -但是,Move Prover 其实**可以理解**这样的函数的意义——只是我们可以用 MSL 表达的性质被限制了。 -实际上,这意味着我们不能把函数 `x_or_y` 变成不透明的,并且必须让 Prover 直接看函数实现来做验证。 -具体来说,我们可以验证以下函数,它可以是不透明的: ```rust fun x_or_y_test(s: S): S { *x_or_y(true, &mut s) = 2; @@ -375,4 +384,4 @@ spec x_or_y_test { ensures result.y == s.y; }/in // TODO: Typo -``` \ No newline at end of file +``` diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/100-move-prover/02-mvp-tutorial.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/100-move-prover/02-mvp-tutorial.md index 386b4e954..9003c2ba3 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/100-move-prover/02-mvp-tutorial.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/100-move-prover/02-mvp-tutorial.md @@ -4,22 +4,18 @@ 形式化验证是一种使用严格的数学方法来描述行为和推理计算机系统的正确性的技术。现在已经在操作系统、编译器等对正确性要求高的领域有一定应用。 -部署在区块链上的智能合约操纵着各种数字资产,它们的正确性也十分关键。**Move Prover(MVP)** 就是为防止 Move 语言编写的智能合约中的错误而设计。 -用户可以使用 **Move 规范语言(MSL)** 指定智能合约的功能属性,然后使用 Move Prover 自动静态地检查它们。 +部署在区块链上的智能合约操纵着各种数字资产,它们的正确性也十分关键。**Move Prover(MVP)** 就是为防止 Move 语言编写的智能合约中的错误而设计。用户可以使用 **Move 规范语言(MSL)** 指定智能合约的功能属性,然后使用 Move Prover 自动静态地检查它们。 简单地说,Move 文件中可以有两种成分: - 一部分是程序代码,这是我们多数人最熟悉的部分。它用 Move 程序语言 (有时候也直接叫 Move 语言) 写成。我们用它定义数据类型、函数。 - 另一部分是形式规范(Formal specification)。它是可选的,用 Move 规范语言写成。我们用它说明程序代码应该满足怎样的性质。比如描述函数的行为。 -当我们写了形式规范的时候,调用 Move Prover 后,它会按照写的规范去验证 Move 程序有没有满足这些要求,帮助开发人员在开发阶段尽早发现潜在的问题, -并让其它用户对已经验证过的程序性质有信心。 +当我们写了形式规范的时候,调用 Move Prover 后,它会按照写的规范去验证 Move 程序有没有满足这些要求,帮助开发人员在开发阶段尽早发现潜在的问题,并让其它用户对已经验证过的程序性质有信心。 ## 安装 Prover 的依赖 -在使用 Move Prover 前,我们先安装它的一些外部依赖。 -假设你已经有了一份 [Starcoin](https://github.com/starcoinorg/starcoin) 的源代码,并且已经构建了项目。 -我们切换到 Starcoin 的根目录下,运行下面的命令: +在使用 Move Prover 前,我们先安装它的一些外部依赖。假设你已经有了一份 [Starcoin](https://github.com/starcoinorg/starcoin) 的源代码,并且已经构建了项目。我们切换到 Starcoin 的根目录下,运行下面的命令: ```bash cd $PATH_TO_STARCOIN @@ -29,8 +25,7 @@ source ~/.profile 当上面的命令执行完毕时,输入 `boogie /version`,如果输出类似 "Boogie program verifier version X.X.X",那么安装已经成功。 -注意,目前 Move Prover 只能在 UNIX 系操作系统下运行(例如 Linux、macOS)。 -Windows 用户可以通过安装 [WSL](https://docs.microsoft.com/en-us/windows/wsl/install) 来运行。 +注意,目前 Move Prover 只能在 UNIX 系操作系统下运行(例如 Linux、macOS)。Windows 用户可以通过安装 [WSL](https://docs.microsoft.com/en-us/windows/wsl/install) 来运行。 ## 准备要验证的示例 @@ -128,9 +123,7 @@ module NamedAddr::BasicCoin { ### TOML 配置 -BasicCoin 使用到了 Starcoin 标准库的一些设施,也要把 `StarcoinFramework` 添加到依赖当中。 -同时,BasicCoin 中用到了命名地址,我们也要指定它应该被何数值地址替换。 -因此,我们把 Move.toml 修改如下: +BasicCoin 使用到了 Starcoin 标准库的一些设施,也要把 `StarcoinFramework` 添加到依赖当中。同时,BasicCoin 中用到了命名地址,我们也要指定它应该被何数值地址替换。因此,我们把 Move.toml 修改如下: ```toml [package] @@ -155,8 +148,7 @@ spec balance_of { ``` 语法上,这段代码可以添加在 BasicCoin 这个模块内的任何地方,但为了让阅读代码的时候方便清晰地看到定义和规范的对应关系,推荐把它就放在 `balance_of` 函数的定义后面。 -简单地说,`spec balance_of {...}` 这个代码块将会包含我们对 `balance_of` 这个函数的**性质规范 (property specification)**。 -性质规范有很多种,常见的一些例子有: +简单地说,`spec balance_of {...}` 这个代码块将会包含我们对 `balance_of` 这个函数的**性质规范 (property specification)**。性质规范有很多种,常见的一些例子有: - 这个函数会异常中止 (abort) 吗?它在什么情况下会异常中止? - 调用这个函数的参数要满足什么条件? @@ -164,8 +156,7 @@ spec balance_of { - 函数执行后,会对虚拟机状态产生怎样的改变? - 这个函数会维持怎样的不变量(invariant)? -例如,当我们没有给出任何中止条件时,Move Prover 默认允许一切可能的异常中止。 -而上面这个简单的片段中,我们用指示 `aborts_if_is_strict` 告诉 Prover: +例如,当我们没有给出任何中止条件时,Move Prover 默认允许一切可能的异常中止。而上面这个简单的片段中,我们用指示 `aborts_if_is_strict` 告诉 Prover: > 我希望严格检查这个函数的异常中止的可能。如果出现了任何程序员没有列出的中止的情况,请报错。 @@ -197,9 +188,7 @@ error: abort not covered by any of the `aborts_if` clauses Error: exiting with verification errors ``` -Prover 的输出告诉我们,它找到了一种让 `balance_of` 函数异常中止的情形,但我们却没有明确指出这种异常中止的可能。 -接着看触发异常中止的代码,可以发现,异常是在 `owner` 不拥有 `Balance` 类型的资源时调用内置的 `borrow_global` 函数造成的。 -根据错误信息的指导,我们便可以添加如下的 `aborts_if` 条件: +Prover 的输出告诉我们,它找到了一种让 `balance_of` 函数异常中止的情形,但我们却没有明确指出这种异常中止的可能。接着看触发异常中止的代码,可以发现,异常是在 `owner` 不拥有 `Balance` 类型的资源时调用内置的 `borrow_global` 函数造成的。根据错误信息的指导,我们便可以添加如下的 `aborts_if` 条件: ```rust spec balance_of { @@ -208,8 +197,7 @@ spec balance_of { } ``` -添加这个条件后,尝试再调用 Prover,可以看到不再有验证错误。 -现在我们可以有信心确认:`balance_of` 函数有且仅有一种异常结束的可能,那就是参数 `owner` 不拥有 `Balance` 类型的资源。 +添加这个条件后,尝试再调用 Prover,可以看到不再有验证错误。现在我们可以有信心确认:`balance_of` 函数有且仅有一种异常结束的可能,那就是参数 `owner` 不拥有 `Balance` 类型的资源。 ## 验证 `withdraw` 函数 @@ -240,19 +228,12 @@ spec withdraw { 可以看到, -- 一个 spec 块可以包含 let 绑定,它可以给比较长的表达式绑定一个名称,并可以反复使用。 -`global(addr): T` 是一个内置函数,它返回地址 `addr` 处类型为 `T` 的资源。 -这里,我们通过 let 绑定将 `balance` 设置为 `addr` 所拥有的代币数量; +- 一个 spec 块可以包含 let 绑定,它可以给比较长的表达式绑定一个名称,并可以反复使用。`global(addr): T` 是一个内置函数,它返回地址 `addr` 处类型为 `T` 的资源。这里,我们通过 let 绑定将 `balance` 设置为 `addr` 所拥有的代币数量; - `exists(address): bool`是一个内置函数,如果资源 `T` 在地址 `addr` 处存在,则返回 true;否则返回 false. -这两行 `aborts_if` 语句对应于上面提到的两个条件。 -一般来说,如果某个函数有多个 `aborts_if` 条件,这些条件就会被或逻辑连接起来。 +这两行 `aborts_if` 语句对应于上面提到的两个条件。一般来说,如果某个函数有多个 `aborts_if` 条件,这些条件就会被或逻辑连接起来。 -像前面提到的那样,如果我们没指定任何异常中止的条件,Prover 就不会对异常中止作任何限制。 -但一旦我们给出了任何一种中止的条件,Prover 就默认我们想严格检查所有异常中止的可能,因此需要列出所有可能的条件, -相当于隐式加了 `pragma aborts_if_is_strict` 这条指示。 -如果只列出了部分异常退出的条件,Prover 会报验证错误。 -然而,如果在 spec 块中定义了 `pragma aborts_if_is_partial`, 就相当于告诉 Prover: +像前面提到的那样,如果我们没指定任何异常中止的条件,Prover 就不会对异常中止作任何限制。但一旦我们给出了任何一种中止的条件,Prover 就默认我们想严格检查所有异常中止的可能,因此需要列出所有可能的条件,相当于隐式加了 `pragma aborts_if_is_strict` 这条指示。如果只列出了部分异常退出的条件,Prover 会报验证错误。然而,如果在 spec 块中定义了 `pragma aborts_if_is_partial`, 就相当于告诉 Prover: > 我只想列出一部分会导致异常中止的条件,请仅仅验证在这些条件下是否会异常中止。 @@ -262,17 +243,13 @@ spec withdraw { - 当同时删除所有 `aborts_if` 条件时,Prover 反而不会报错; - 当加上 `pragma aborts_if_is_partial` 时,无论保留几条 `aborts_if` 条件,Prover 都不会报错(当然了,条件本身要是正确的)。 -有读者可能会对 spec 块中三个语句的顺序的排列产生好奇: -`balance` 的定义为什么可以写在 `aborts_if !exists>(addr)` 的后面。 -因为,如果后者成立的话,`balance` 实际上是不存在的。这个顺序不会导致 Prover 出错吗? -简单地说:不会,spec 块当中的语句是声明式的,顺序没有任何影响。 +有读者可能会对 spec 块中三个语句的顺序的排列产生好奇:`balance` 的定义为什么可以写在 `aborts_if !exists>(addr)` 的后面。因为,如果后者成立的话,`balance` 实际上是不存在的。这个顺序不会导致 Prover 出错吗?简单地说:不会,spec 块当中的语句是声明式的,顺序没有任何影响。 如果想作更细致的了解,可以参考 [MSL 文档](#TODO) 以获得更多信息。 ### 指定 `withdraw` 的功能性质 -接下来我们来定义功能性质。 -下面 spec 块当中的两个 `ensures` 语句给出了我们对 `widthdraw` 功能上的期待: +接下来我们来定义功能性质。下面 spec 块当中的两个 `ensures` 语句给出了我们对 `widthdraw` 功能上的期待: ```rust spec withdraw { @@ -286,8 +263,7 @@ spec withdraw { } ``` -这段代码中,首先通过使用 `let post` 绑定,把 `balance_post` 定义为函数执行后 `addr` 的余额,它应该等于 `balance - amount`。 -然后,`result` 是一个特殊的名字,表示返回值,它应该是金额为 `amount` 的代币。 +这段代码中,首先通过使用 `let post` 绑定,把 `balance_post` 定义为函数执行后 `addr` 的余额,它应该等于 `balance - amount`。然后,`result` 是一个特殊的名字,表示返回值,它应该是金额为 `amount` 的代币。 ## 验证 `deposit` 函数 @@ -319,16 +295,9 @@ spec deposit { `ensures` 语句用于让 Prover 确定在任何情况下,函数执行后 `addr` 中的余额都可以被正确地更新。 -前面提到过的语法此处不再赘述。 -敏锐的读者可能已经发现,有一点值得注意:表达式 `balance + check_value > MAX_U64` 在 Move 程序中是有问题的。 -因为左边的加法会可能引起溢出的异常。 -如果我们在 Move 程序中想写一个类似的检查,应该用类似 `balance > MAX_U64 - check_value` 的表达式来避开溢出的问题。 +前面提到过的语法此处不再赘述。敏锐的读者可能已经发现,有一点值得注意:表达式 `balance + check_value > MAX_U64` 在 Move 程序中是有问题的。因为左边的加法会可能引起溢出的异常。如果我们在 Move 程序中想写一个类似的检查,应该用类似 `balance > MAX_U64 - check_value` 的表达式来避开溢出的问题。 -但是,这个表达式在 Move 规范语言(MSL)中却完全没问题。 -由于 spec 块使用的是 MSL 语言,它的类型系统和 Move 不一样。 -MSL 中,所有的整数都是 `num` 类型,它是数学意义上的整数。也就是说,它是有符号数,而且没有大小限制。 -当在 MSL 中引用 Move 程序中的数据时,所有内置整数类型 (`u8`,`u64` 等)都会被自动转换成 `num` 类型。 -在 MSL 文档中可以找到更详细的关于类型系统的说明。 +但是,这个表达式在 Move 规范语言(MSL)中却完全没问题。由于 spec 块使用的是 MSL 语言,它的类型系统和 Move 不一样。MSL 中,所有的整数都是 `num` 类型,它是数学意义上的整数。也就是说,它是有符号数,而且没有大小限制。当在 MSL 中引用 Move 程序中的数据时,所有内置整数类型 (`u8`,`u64` 等)都会被自动转换成 `num` 类型。在 MSL 文档中可以找到更详细的关于类型系统的说明。 ## 验证 `transfer` 函数 @@ -356,15 +325,9 @@ spec transfer { } ``` -这里的 `from` 是 `signer` 类型,而并非一个直接的地址。 -虽然程序中我们有创建一个名为 `addr_from` 的局部变量,但在 spec 块中我们无法直接引用它。 -同时,这个地址的表达式要重复好几次,反复书写很累赘,我们再次把它绑定到 `addr_from` 上面。 -然后用 `let` 和 `let post` 定义几个变量,对应着函数执行前后 `addr_from` 和 `to` 两个地址内的余额。 -最后用 `ensures` 语句告诉 Prover `from` 内的余额应该减去 `amount`;`to` 内的余额以应该增加 `amount`。 +这里的 `from` 是 `signer` 类型,而并非一个直接的地址。虽然程序中我们有创建一个名为 `addr_from` 的局部变量,但在 spec 块中我们无法直接引用它。同时,这个地址的表达式要重复好几次,反复书写很累赘,我们再次把它绑定到 `addr_from` 上面。然后用 `let` 和 `let post` 定义几个变量,对应着函数执行前后 `addr_from` 和 `to` 两个地址内的余额。最后用 `ensures` 语句告诉 Prover `from` 内的余额应该减去 `amount`;`to` 内的余额以应该增加 `amount`。 -乍看之下,似乎完全没有问题。可是真的是这样吗? -我们来看看 Prover 是否认为这就是「对这个函数行为的正确描述」。 -在输入 `mpm package prove` 后可以看到: +乍看之下,似乎完全没有问题。可是真的是这样吗?我们来看看 Prover 是否认为这就是「对这个函数行为的正确描述」。在输入 `mpm package prove` 后可以看到: ``` error: post-condition does not hold @@ -384,9 +347,7 @@ error: post-condition does not hold = _witness = ``` -令人有些出乎意料,Prover 提示了后置条件不满足,说明前面的 spec 块中的所描述的行为和 `transfer` 函数并不完全一致。 -为什么会这样呢?我们再往下看:使得后置条件不满足的的参数是 `from = signer{0x0}` 和 `to = 0x0`. -看到这里我们应该清楚原因了:当账户向自己转账时,`to` 和 `from` 指向的地址都一样,所以余额不产生任何变化。 +令人有些出乎意料,Prover 提示了后置条件不满足,说明前面的 spec 块中的所描述的行为和 `transfer` 函数并不完全一致。为什么会这样呢?我们再往下看:使得后置条件不满足的的参数是 `from = signer{0x0}` 和 `to = 0x0`.看到这里我们应该清楚原因了:当账户向自己转账时,`to` 和 `from` 指向的地址都一样,所以余额不产生任何变化。 现在有两个解决方案: @@ -408,11 +369,10 @@ let post ne_post = balance_from_post == balance_from - amount ensures if (addr_from == to) eq_post else ne_post; ``` -注意这里的 `if (P) E1 else E2` 和程序逻辑中的条件执行不太相同—— -它实际上是个语法糖,等价于同时 `ensures` 了 `P ==> E1` 和 `!P ==> E2`。 -而 `p ==> q` 又实际上就是 `!p || q`. +注意这里的 `if (P) E1 else E2` 和程序逻辑中的条件执行不太相同——它实际上是个语法糖,等价于同时 `ensures` 了 `P ==> E1` 和 `!P ==> E2`。而 `p ==> q` 又实际上就是 `!p || q`. 也就是说,第二种写法的末尾实际上表示这样的逻辑: + ```rust ensures (addr_from == to ===> eq_post) && (addr_from != to ===> ne_post); ``` @@ -423,19 +383,15 @@ ensures (addr_from == to ===> eq_post) && (addr_from != to ===> ne_post); ensures (addr_from != to || eq_post) && (addr_from == to || ne_post); ``` -有兴趣的读者可以通过直值表或化简到范式的方式自行验证一下, -前面的 `(addr_from == to && eq_post) || (addr_from != to && ne_post)` 和后面的 `(addr_from != to || eq_post) && (addr_from == to || ne_post)` 实际上也是完全等价的表达式。 +有兴趣的读者可以通过直值表或化简到范式的方式自行验证一下,前面的 `(addr_from == to && eq_post) || (addr_from != to && ne_post)` 和后面的 `(addr_from != to || eq_post) && (addr_from == to || ne_post)` 实际上也是完全等价的表达式。 -**方案乙** 不修改 spec,直接在函数体内加上 `assert!(from_addr != to, EEQUAL_ADDR)`, -并在前面加上错误码 `EEQUAL_ADDR` 的定义,让自我转账交易无法完成。 +**方案乙** 不修改 spec,直接在函数体内加上 `assert!(from_addr != to, EEQUAL_ADDR)`,并在前面加上错误码 `EEQUAL_ADDR` 的定义,让自我转账交易无法完成。 -显然,自己给自己转账并没有实际意义,不如直接禁止这种交易。 -因此方案乙是更好的做法。它直接保证了成功执行时两者肯定不是同一地址,而且代码也更为简洁。 +显然,自己给自己转账并没有实际意义,不如直接禁止这种交易。因此方案乙是更好的做法。它直接保证了成功执行时两者肯定不是同一地址,而且代码也更为简洁。 ### 练习 -目前我们只完成了 `transfer` 函数的功能性验证。但没有说明它会在哪些情况下异常中止。 -作为练习,请给它加上合适的 `aborts_if` 条件。答案可以在后面章节中找到。TODO +目前我们只完成了 `transfer` 函数的功能性验证。但没有说明它会在哪些情况下异常中止。作为练习,请给它加上合适的 `aborts_if` 条件。答案可以在后面章节中找到。TODO ## 验证 `mint` 函数 @@ -445,9 +401,7 @@ ensures (addr_from != to || eq_post) && (addr_from == to || ne_post); public fun mint(mint_addr: address, amount: u64, _witness: CoinType) acquires Balance ``` -它负责铸造出金额为 `amount` 的代币,并存到地址 `mint_addr` 中。 -比较有趣的是 `_witness`,其类型为 `CoinType`。 -因为只有定义 `CoinType` 的模块才能构造出这个类型的值,这就保证了调用者身份。 +它负责铸造出金额为 `amount` 的代币,并存到地址 `mint_addr` 中。比较有趣的是 `_witness`,其类型为 `CoinType`。因为只有定义 `CoinType` 的模块才能构造出这个类型的值,这就保证了调用者身份。 `mint` 函数中实际上只有一句对 `deposit` 的调用。不难想到,它们俩的要满足的规范应该有很多的相似之处。 照猫画虎,不难写出: @@ -486,16 +440,13 @@ spec publish_balance { ## 使用 Schema 简化冗余规范 -恭喜!到目前为止,我们已经一步一步完成了 BasicCoin 的全部函数的验证。 -但是,如果仔细看代码的话,不少 spec 块看起来十分相似,如果能让它们精简一些的话,文件结构会更清晰。 +恭喜!到目前为止,我们已经一步一步完成了 BasicCoin 的全部函数的验证。但是,如果仔细看代码的话,不少 spec 块看起来十分相似,如果能让它们精简一些的话,文件结构会更清晰。 -Schema 是一种通过将属性分组来构建规范的手段。 -从语义上讲,它们也是语法糖,在 spec 块中使用它们等价于将它们包含的条件展开到函数、结构或模块。 +Schema 是一种通过将属性分组来构建规范的手段。从语义上讲,它们也是语法糖,在 spec 块中使用它们等价于将它们包含的条件展开到函数、结构或模块。 ### 消除简单重复 -作为一个最明显的例子,`mint` 和 `deposit` 的 spec 块除了变量名有点不一样(用术语来说,它们是[可 alpha 转换](https://en.wikipedia.org/wiki/Lambda_calculus#%CE%B1-conversion)的),整体结构可以说是完全一致。 -为了简化它们,我们来创建一个 Schema: +作为一个最明显的例子,`mint` 和 `deposit` 的 spec 块除了变量名有点不一样(用术语来说,它们是[可 alpha 转换](https://en.wikipedia.org/wiki/Lambda_calculus#%CE%B1-conversion)的),整体结构可以说是完全一致。为了简化它们,我们来创建一个 Schema: ```rust spec schema DepositSchema { @@ -512,10 +463,7 @@ spec schema DepositSchema { } ``` -这个 Schema 声明了两个有类型的变量,以及一些关于这些变量应该满足的条件。 -当其它地方想用这个 Schema 的时候,就要用 `include DepositSchema {addr: XX, amount: YY}` 来导入它。 -其中 `XX` 和 `YY` 分是用来替代 `addr` 和 `amount` 的表达式。 -如果表达式和对应的变量名正好一样,刚可以只写变量名,或者直接省略。 +这个 Schema 声明了两个有类型的变量,以及一些关于这些变量应该满足的条件。当其它地方想用这个 Schema 的时候,就要用 `include DepositSchema {addr: XX, amount: YY}` 来导入它。其中 `XX` 和 `YY` 分是用来替代 `addr` 和 `amount` 的表达式。如果表达式和对应的变量名正好一样,刚可以只写变量名,或者直接省略。 有了上面的 Schema 定义之后,我们现在可以简化之前的 spec 了: @@ -556,11 +504,9 @@ spec transfer { } ``` -它虽然相对复杂,但经过一点分析还是不难看出,实际上糅合了 `withdraw` 和 `deposit` 两种逻辑。 -这一点从 `transfer` 的调用关系也可以分析出来。 +它虽然相对复杂,但经过一点分析还是不难看出,实际上糅合了 `withdraw` 和 `deposit` 两种逻辑。这一点从 `transfer` 的调用关系也可以分析出来。 -在前面一步当中,我们已经有了 `DepositSchema`。 -现在,我们把 `withdraw` 的验证规范重构一下:把它提取成一个 Schema,以及一个用到此 Schema 的 spec 块: +在前面一步当中,我们已经有了 `DepositSchema`。现在,我们把 `withdraw` 的验证规范重构一下:把它提取成一个 Schema,以及一个用到此 Schema 的 spec 块: ```rust spec withdraw { @@ -599,6 +545,4 @@ spec transfer { ### 练习 -除了上面的示例以外,再找一个 spec 块(例如 `publish_balance`),将它也拆分成一个 Schema 声明和一个使用对应 Schema 的 spec 块。 -作为一个练习,你创建的 Schema 可能在这份代码中无法利用,所以感觉看不出什么好处。 -但如果在后面开发中,有别的函数调用 `publish_balance`,就会更方便了。 +除了上面的示例以外,再找一个 spec 块(例如 `publish_balance`),将它也拆分成一个 Schema 声明和一个使用对应 Schema 的 spec 块。作为一个练习,你创建的 Schema 可能在这份代码中无法利用,所以感觉看不出什么好处。但如果在后面开发中,有别的函数调用 `publish_balance`,就会更方便了。 diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/12-call-function.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/12-call-function.md index 60a8463f4..79f96a978 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/12-call-function.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/12-call-function.md @@ -1,7 +1,6 @@ # 如何调用函数 -在[快速开始](./02-quick-start.md)这一节教程中,我们尝试了如何调用脚本函数。 -那么普通函数改如何调用呢? +在[快速开始](./02-quick-start.md)这一节教程中,我们尝试了如何调用脚本函数。那么普通函数改如何调用呢? 接下来将通过一个小例子来展示如何调用普通函数。 diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/96-move-vm/01-summary.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/96-move-vm/01-summary.md index 542d5fc93..a07b52f83 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/96-move-vm/01-summary.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/96-move-vm/01-summary.md @@ -1,7 +1,7 @@ # 1. Move虚拟机简要介绍 ------- ## 0. 前言 + 下面的内容分为两部分,第一部分介绍了 Move 虚拟机的结构: 作为一个栈式虚拟机,它的操作数栈、调用栈。另外还介绍了 Move 虚拟机中,虚拟机提供了数据结构来支持函数调用和返回。 第二部分介绍了 Move 虚拟机中比较关键的字节码指令的作用。 diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/96-move-vm/02-create-resource.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/96-move-vm/02-create-resource.md index 7f2f21262..1d88890b5 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/96-move-vm/02-create-resource.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/96-move-vm/02-create-resource.md @@ -612,5 +612,3 @@ pub fn delete_resource(&self, addr: AccountAddress, tag: StructTag) -> Result<() ``` 到目前为止,在某个账户下创建资源的源码分析完毕。 - - diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/96-move-vm/03-modify-and-drop-resource.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/96-move-vm/03-modify-and-drop-resource.md index f65d228c9..1a17f99dd 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/96-move-vm/03-modify-and-drop-resource.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/96-move-vm/03-modify-and-drop-resource.md @@ -281,6 +281,3 @@ GlobalValueEffect::Deleted => { ``` 最后调用 `maybe_commit_effects()` 函数将状态变更集合的内存对象,写入并提交到磁盘。 - - - diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/01-move-unit-test.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/01-move-unit-test.md index b85b81704..ec5ad852c 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/01-move-unit-test.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/01-move-unit-test.md @@ -11,14 +11,14 @@ Move 的单元测试是通过源语言的三个新的注解来实现的。 - 将一个模块或模块成员(使用、函数或结构)标记为仅用于测试。 - 以及标记一个测试预计会失败。 -这些注解可以放在一个具有任何可见性的函数上。 -每当一个模块或模块成员被注解为 `#[test_only]` 或 `#[test]` ,它将不会被包含在编译的字节码中,除非它是在测试场景下编译的。 +这些注解可以放在一个具有任何可见性的函数上。每当一个模块或模块成员被注解为 `#[test_only]` 或 `#[test]` ,它将不会被包含在编译的字节码中,除非它是在测试场景下编译的。 ### 测试注解 - 意思和用法 `#[test]` 和 `#[expected_failure]` 注解可以选择与参数一起使用或不与参数一起使用。 如果没有参数,注解 `#[test]` 只能放在没有参数的函数上。该注解只是用来标记某个函数是测试函数。 + ``` #[test] // OK fun this_is_a_test() { ... } @@ -27,9 +27,7 @@ fun this_is_a_test() { ... } fun this_is_not_correct(arg: signer) { ... } ``` -测试函数还可以加上 `#[expected_failure]` 注解。这个注解标记了这个测试应该产生错误。 -想要指定某个具体错误代码,可以给 `#[expected_failure]` 加上参数:`#[expected_failure(abort_code = )]`。 -如果它产生不同的错误代码或者不抛错,测试将失败。 +测试函数还可以加上 `#[expected_failure]` 注解。这个注解标记了这个测试应该产生错误。想要指定某个具体错误代码,可以给 `#[expected_failure]` 加上参数:`#[expected_failure(abort_code = )]`。如果它产生不同的错误代码或者不抛错,测试将失败。 ``` #[test] @@ -50,10 +48,7 @@ public(script) fun this_other_test_will_abort_and_pass() { abort 1 } ``` -带参数的测试注解的形式是:`#[test( =
, ..., =
)]`. -如果一个函数以这种方式被注释,那么该函数的参数必须是参数 `, ..., ` 的排列组合. -也就是说,这些参数在函数中出现的顺序和它们在测试注解中的顺序不一定相同,但它们必须能够通过名称相互匹配。 -目前测试参数只支持 `signer` 类型。 +带参数的测试注解的形式是:`#[test( =
, ..., =
)]`.如果一个函数以这种方式被注释,那么该函数的参数必须是参数 `, ..., ` 的排列组合.也就是说,这些参数在函数中出现的顺序和它们在测试注解中的顺序不一定相同,但它们必须能够通过名称相互匹配。目前测试参数只支持 `signer` 类型。 ``` #[test(arg = @0xC0FFEE)] // OK @@ -75,8 +70,7 @@ fun this_is_correct_now(arg: signer) { ... } ``` -一个预期失败的注解也可以采取 `#[expected_failure(abort_code = )]` 的形式。 -如果一个测试函数以这样的方式被注释,测试必须以等于 `` 的错误代码中止。任何其他的失败或中止代码将导致测试失败。 +一个预期失败的注解也可以采取 `#[expected_failure(abort_code = )]` 的形式。如果一个测试函数以这样的方式被注释,测试必须以等于 `` 的错误代码中止。任何其他的失败或中止代码将导致测试失败。 ``` #[test, expected_failure(abort_code = 1)] // This test will fail @@ -87,9 +81,7 @@ fun this_test_should_abort_and_fail() { abort 0 } fun this_test_should_abort_and_pass_too() { abort 0 } ``` -一个模块和它的任何成员都可以被声明为 `#[test_only]`。 -这种情况下,被标记的模块或者成员只有在测试模式下编译时才会被包含在编译的Move字节码中。 -也只能在测试函数中调用。 +一个模块和它的任何成员都可以被声明为 `#[test_only]`。这种情况下,被标记的模块或者成员只有在测试模式下编译时才会被包含在编译的Move字节码中。也只能在测试函数中调用。 ``` #[test_only] // test only attributes can be attached to modules @@ -111,12 +103,7 @@ fun test_only_function(...) { ... } ### 执行单元测试 -Move 项目的单元测试可以通过 `mpm package test` 命令来运行。 -运行测试时,每个测试的结果有 PASS、FAIL 或 TIMEOUT 三种。 -如果一个测试案例失败了,失败的位置和导致失败的函数名称会报告出来(如果可能的话)。接下来有一个例子。 -如果一个测试超过了可以执行的最大指令数,它将被标记为超时。 -这个约束可以通过命令参数来改变,其默认值被设置为5000条指令。 -此外,虽然测试的结果是确定的,但测试默认是并行运行的,所以测试结果的排序是不确定的,除非只用一个线程运行(见下面的OPTIONS)。 +Move 项目的单元测试可以通过 `mpm package test` 命令来运行。运行测试时,每个测试的结果有 PASS、FAIL 或 TIMEOUT 三种。如果一个测试案例失败了,失败的位置和导致失败的函数名称会报告出来(如果可能的话)。接下来有一个例子。如果一个测试超过了可以执行的最大指令数,它将被标记为超时。这个约束可以通过命令参数来改变,其默认值被设置为5000条指令。此外,虽然测试的结果是确定的,但测试默认是并行运行的,所以测试结果的排序是不确定的,除非只用一个线程运行(见下面的OPTIONS)。 更多的命令参数可以参看help: `mpm pacakge -h` 。 @@ -254,6 +241,7 @@ Test result: FAILED. Total tests: 3; passed: 0; failed: 3 ``` #### `-s` or `--statistics` + 通过这个参数,你可以收集有关测试运行的统计数据,报告每个测试的运行时间和执行的指令。 例如,如果我们想看到上面例子中测试的统计数字。 @@ -336,4 +324,4 @@ Failures in 0x1::MyModule: └────────────────── Test result: FAILED. Total tests: 4; passed: 3; failed: 1 -``` \ No newline at end of file +``` diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/02-move-integration-test.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/02-move-integration-test.md index 5e15e60ae..b31c7bae9 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/02-move-integration-test.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/02-move-integration-test.md @@ -11,8 +11,7 @@ 所有的动作都被包装成事务。所有的集成测试文件应该在软件包根路径下的`integration-tests`目录中。 -集成测试是在源码文件添加注解来表示测试指令。注解以`//#`开头,用空行隔开。指令的工作方式类似于命令行,开发者提供命令名称和命令参数, -mpm 将会自动执行指令,就像系统执行命令行 CLI 命令一样。 +集成测试是在源码文件添加注解来表示测试指令。注解以`//#`开头,用空行隔开。指令的工作方式类似于命令行,开发者提供命令名称和命令参数,mpm 将会自动执行指令,就像系统执行命令行 CLI 命令一样。 ``` $ mpm integration-test --help @@ -179,9 +178,7 @@ OPTIONS: --amount [default: 100000000000] ``` -`faucet` 指令可以创建地址,给地址充钱。 -地址可以是命名地址(named address),比如 `alice`, `tom`, 或者是字面地址(raw address),比如 `0x1`, `0x2`。 -如果是命名地址,它会为该地址自动生成一个字面地址以及对应的公钥。 +`faucet` 指令可以创建地址,给地址充钱。地址可以是命名地址(named address),比如 `alice`, `tom`, 或者是字面地址(raw address),比如 `0x1`, `0x2`。如果是命名地址,它会为该地址自动生成一个字面地址以及对应的公钥。 例子: @@ -245,8 +242,7 @@ ARGS: api params, should be a json array string ``` -`call-api` 指令可以用来调用远程 RPC 的接口。返回值会被保存在 mpm 的运行环境中,可以后续的测试指令中用模板变量来获取返回结果并将其作为后续指令的参数输入。 -可以通过 [jsonrpc](https://starcoinorg.github.io/jsonrpcdoc/) 查看 Starcoin 的 API 接口和它们的输出格式。 +`call-api` 指令可以用来调用远程 RPC 的接口。返回值会被保存在 mpm 的运行环境中,可以后续的测试指令中用模板变量来获取返回结果并将其作为后续指令的参数输入。可以通过 [jsonrpc](https://starcoinorg.github.io/jsonrpcdoc/) 查看 Starcoin 的 API 接口和它们的输出格式。 示例: @@ -278,10 +274,10 @@ OPTIONS: --args ``` -`package` 指令可以将模块打包并返回打包后的 bytes,hash 和保存下来的临时文件。 -这常在两阶段提交的测试用到。 +`package` 指令可以将模块打包并返回打包后的 bytes,hash 和保存下来的临时文件。这常在两阶段提交的测试用到。 输出示例: + ``` { "file":"/tmp/.tmpnok0M6/0x3c6b00fadc6b4f37fa6e2c6c0949e97c89b00c07c0d1f1761671e6fe18792676.blob", @@ -323,10 +319,10 @@ OPTIONS: `deploy` 指令可以将打包后的模块部署到区块链上。 -`--signers` 一般在两阶段提交中用到,在第二阶段发布模型是,任何人都可以发起模型部署的交易。 -`--gas-budget` 指定交易的最大 gas 。 +- `--signers` 一般在两阶段提交中用到,在第二阶段发布模型是,任何人都可以发起模型部署的交易。 +- `--gas-budget` 指定交易的最大 gas 。 -示例: +示例: ``` //# deploy {{$.package[0].file}} @@ -354,8 +350,7 @@ OPTIONS: -`pulish` 指令可以将某个模块发布到链上。 -模块代码必须跟在该指令后面。 +`pulish` 指令可以将某个模块发布到链上。模块代码必须跟在该指令后面。 - `--gas-budget` 指定了该交易的最大gas。 - `--syntax` 暂时还没有用到,可以忽略。 @@ -415,9 +410,7 @@ ARGS: ``` -`run` 指令可以执行一个脚本或者脚本函数。 -如果是脚本,那么脚本代码必须跟在该指令后面。 -如果是脚本函数,那么函数名字 `` 必须提供。 +`run` 指令可以执行一个脚本或者脚本函数。如果是脚本,那么脚本代码必须跟在该指令后面。如果是脚本函数,那么函数名字 `` 必须提供。 - `--signers` 声明交易的发起者。 - `--type-args` 和 `--args` 声明交易要执行的函数的类型参数和参数。 @@ -461,7 +454,7 @@ OPTIONS: `view` 指令可以查询某个地址下的某个资源数据。 -例子: +例子: ``` //# view --address alice --resource 01::Account::Account @@ -490,12 +483,9 @@ OPTIONS: ### 集成测试的期望结果 -每个集成测试都应该有一个相应的期望文件,它包含了集成测试中每个指令的预期输出。 -mpm 将比较集成测试的测试结果和期望文件。 -如果两者不同,那么该集成测试就会失败。 +每个集成测试都应该有一个相应的期望文件,它包含了集成测试中每个指令的预期输出。mpm 将比较集成测试的测试结果和期望文件。如果两者不同,那么该集成测试就会失败。 -你可以在运行`mpm integration-test`时提供`--ub`参数来生成期望文件。 -但你必须检查生成的输出是否真的是你的集成测试的预期输出。 +你可以在运行`mpm integration-test`时提供`--ub`参数来生成期望文件。但你必须检查生成的输出是否真的是你的集成测试的预期输出。 例子: @@ -505,4 +495,4 @@ mpm pacakge build mpm integration-test test_coin_swap ``` -以上就是集成测试的用法,如有疑问,欢迎在 starcoin 的 discord 论坛提出。 \ No newline at end of file +以上就是集成测试的用法,如有疑问,欢迎在 starcoin 的 discord 论坛提出。 diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/03-how-to-debug.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/03-how-to-debug.md index dc4edf511..8eed28f15 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/03-how-to-debug.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/03-how-to-debug.md @@ -5,10 +5,10 @@ ### 简单示例 -我们从一个简单的例子开始。 -这是一个非常简单的 Token 模块,我将留下一些小错误来展示整个 debug 过程。 +我们从一个简单的例子开始。这是一个非常简单的 Token 模块,我将留下一些小错误来展示整个 debug 过程。 + +**你可以使用下面的地址来测试:** -**你可以使用下面的地址来测试** ``` address:0xf2aa2eae4ceaae88b308fc904975e4ae public_key:0x98826ab91a9a5d85dec536418090aa6342991bc8f947613721c8165e7102b132 @@ -17,17 +17,20 @@ private_key:0xa5ead1fb25114b335ad21a07ed5cee8cecba8763309ec78656e7c4ccaf5735e7 - 使用 mpm 命令创建项目 + ``` mpm package new MyCake ``` - 进入目录并编辑 mycake.move 文件 + ``` cd MyCake vi sources/mycake.move ``` 在文件中输入以下代码: + ``` address Chef{ module Cake{ @@ -70,6 +73,7 @@ address Chef{ ``` - 编辑 Move.toml 文件 + ``` vi Move.toml ``` @@ -87,12 +91,11 @@ StarcoinFramework = { git = "https://github.com/starcoinorg/starcoin-framework.g ### 单元测试 -代码编写完成后,应首先进行单元测试,保证代码的正确性。 -单元测试通常用来测试一些函数或者功能模块的正确性。 +代码编写完成后,应首先进行单元测试,保证代码的正确性。单元测试通常用来测试一些函数或者功能模块的正确性。 -**测试 add 函数的返回值** +**测试 add 函数的返回值** -- 添加单元测试代码 +- 添加单元测试代码 ``` address Chef{ @@ -140,11 +143,13 @@ address Chef{ ``` - 运行测试命令 + ``` mpm package test ``` - 获取测试结果 + ``` BUILDING UnitTest BUILDING StarcoinFramework @@ -171,10 +176,10 @@ Failures in 0xf2aa2eae4ceaae88b308fc904975e4ae::Cake: Test result: FAILED. Total tests: 1; passed: 0; failed: 1 ``` -**可以看到 add 函数的返回结果不是我们预期的** +**可以看到 add 函数的返回结果不是我们预期的** + +我们检查一下 add 函数,可以看到其内部实现是错误的。修复这个错误: -我们检查一下 add 函数,可以看到其内部实现是错误的。 -修复这个错误: ``` public fun add (x:u128, y:u128 ):u128{ x + y @@ -182,11 +187,13 @@ Test result: FAILED. Total tests: 1; passed: 0; failed: 1 ``` - 重新运行单元测试 + ``` mpm package test ``` - 获取测试结果 + ``` CACHED UnitTest CACHED StarcoinFramework @@ -195,24 +202,25 @@ Running Move unit tests [ PASS ] 0xf2aa2eae4ceaae88b308fc904975e4ae::Cake::add_test Test result: OK. Total tests: 1; passed: 1; failed: 0 ``` -恭喜,所有测试通过了! +恭喜,所有测试通过了! 通过这种方式可以发现函数或功能模块里的问题。你可以在单元测试中打印变量,也可以调用其他函数,但是一定记住,单元测试是非常局限的。如果你需要 signature ,那你可以使用集成测试。 ### 集成测试 -单元测试只能满足一小部分的测试需求。 -更多情况下,我们在测试阶段需要模拟代码在区块链上运行的情况,很多问题只有在区块链上运行时才能暴露出来。 +单元测试只能满足一小部分的测试需求。更多情况下,我们在测试阶段需要模拟代码在区块链上运行的情况,很多问题只有在区块链上运行时才能暴露出来。 集成测试最适合完成这项需求。 - 创建一个 integration-tests 的工作目录,并添加 mycake_test.move 文件 + ``` mkdir integration-tests vi integration-test/mycake.move ``` -在 mycake.move 文件中添加以下代码: +在 mycake.move 文件中添加以下代码: + ``` //# init -n test --public-keys Chef=0x98826ab91a9a5d85dec536418090aa6342991bc8f947613721c8165e7102b132 @@ -265,13 +273,15 @@ script { // check: EXECUTED ``` -- 运行集成测试 +- 运行集成测试 + ``` mpm integration-test ``` **命令行中将输出如下结果** 可以看到,大部分测试结果是符合预期的。但是最后一个测试的结果是错误的,我们需要仔细检查最后一项测试代码,理清其背后的逻辑。 + ``` BUILDING StarcoinFramework BUILDING MyCake @@ -327,9 +337,10 @@ failures: test result: FAILED. 0 passed; 1 failed; 0 filtered out ``` -- 问题分析 +- 问题分析 首先看一下代码,我们获取了一些 cake 代币,然后想发送给 guest 账户;但是执行 send_cake 后,guest 账户里并没有收到 cake 代币。 + ``` //# run --signers Chef script { @@ -343,27 +354,35 @@ script { } // check: EXECUTED ``` + 通过上面的单元测试,我们知道 make_cake 函数是正常的,**那么问题一定出在 send_cake 上面**。 我们就来检查一下吧。 + ``` public fun send_cake( _to :address , cake: Token::Token ){ Account::deposit(@Chef, cake); } ``` + 仔细查看 send_cake 函数,可以看到 Account::deposit(@Chef, cake) 的参数错误写成了管理员的地址,而实际上应该是接受代币的账户地址。 修复该问题: + ``` public fun send_cake( to :address , cake: Token::Token ){ Account::deposit(to, cake); } ``` + 好了,我们重新运行 integration-test + ``` mpm integration-test ``` + **这下测试项中没有出现错误了,但是我们的测试结果仍然失败了。这又是什么原因?** + ``` BUILDING StarcoinFramework BUILDING MyCake @@ -413,15 +432,17 @@ failures: test result: FAILED. 0 passed; 1 failed; 0 filtered out ``` - + - 更新测试基准 **这是因为我们需要更新测试基准** + ``` mpm integration-test --ub ``` 该命令会在 integration-tests 目录下生成和测试文件同名,后缀为 exp 的文件。命令运行结束后,所有测试通过! + ``` BUILDING StarcoinFramework BUILDING MyCake @@ -435,8 +456,10 @@ test result: ok. 1 passed; 0 failed; 0 filtered out **当修改测试项后,记得一定要在在测试命令中加上 "--ub" 选项:`mpm integration-test --ub`。** -### 正确代码 +### 正确代码 + #### Move.toml + ``` [package] name = "MyCake" @@ -447,9 +470,10 @@ Chef = "0xf2aa2eae4ceaae88b308fc904975e4ae" [dependencies] StarcoinFramework = { git = "https://github.com/starcoinorg/starcoin-framework.git", rev = "01c84198819310620f2417413c3c800df8292ae5" } - ``` + #### mycake.move + ``` address Chef{ module Cake{ @@ -492,9 +516,10 @@ address Chef{ } } } - ``` + #### mycake_test.move + ``` //# init -n test --public-keys Chef=0x98826ab91a9a5d85dec536418090aa6342991bc8f947613721c8165e7102b132 @@ -544,4 +569,4 @@ script { } } // check: EXECUTED -``` \ No newline at end of file +``` diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/04-disassembler.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/04-disassembler.md index cc45e71be..f11af2cd8 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/04-disassembler.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/97-move-test/04-disassembler.md @@ -1,16 +1,14 @@ # 移动虚拟机、字节码和反汇编程序 -当我们在编写一段 Move 代码、编译并在 Move 虚拟机中执行它时, -我们有时希望了解幕后实际发生的事情。 + +当我们在编写一段 Move 代码、编译并在 Move 虚拟机中执行它时,我们有时希望了解幕后实际发生的事情。 在这篇文章中,我们将看一下 Move 底层是如何工作的。 ## 移动虚拟机执行模型 + 移动解释器在字节码级别处理程序执行。 -就像其他基于堆栈的解释器一样,当看到一条指令时,移动解释器 -可能会消耗堆栈中的操作数并将结果推送给它。但与 x86 机器不同的是, -在 x86 机器上,操作数/变量与调用堆栈共享同一区域,但 Move VM 逻辑上将两者分离 -开来:Move 解释器有两部分——一个操作数栈和一个调用栈(其内部结构如下图)。 +就像其他基于堆栈的解释器一样,当看到一条指令时,移动解释器可能会消耗堆栈中的操作数并将结果推送给它。但与 x86 机器不同的是,在 x86 机器上,操作数/变量与调用堆栈共享同一区域,但 Move VM 逻辑上将两者分离开来:Move 解释器有两部分——一个操作数栈和一个调用栈(其内部结构如下图)。 ``` 解释器(Interpreter) @@ -30,21 +28,16 @@ └── frame[m] <-- 调用栈顶 ``` -在任何程序调用中,调用者准备参数(也称为实际参数)并把它们压 -到操作数栈,然后 `Call` 指令将在栈顶创建一个新帧(frame),分配给 -被调用给函数,其中形式参数是从堆栈中复制的。 +在任何程序调用中,调用者准备参数(也称为实际参数)并把它们压到操作数栈,然后 `Call` 指令将在栈顶创建一个新帧(frame),分配给被调用给函数,其中形式参数是从堆栈中复制的。 -我们可以从它们的名称中理解大多数指令的含义。每当你想知道任何指令的效果,参考 `execute_code_impl()` -[interpreter.rs](https://github.com/starcoinorg/move/blob/main/language/move-vm/runtime/src/move_vm.rs) -以了解详情。 +我们可以从它们的名称中理解大多数指令的含义。每当你想知道任何指令的效果,参考 `execute_code_impl()`[interpreter.rs](https://github.com/starcoinorg/move/blob/main/language/move-vm/runtime/src/move_vm.rs)以了解详情。 ## Move 反汇编器 (Disassembler) -大多数时候,Move 开发人员在源代码级别编写代码、测试和调试。 -然而,由于字节码实际上是 Move VM 执行的内容,在一些罕见的情况下, -我们可能需要检查特定模块或函数的相应字节码。 -例如,在 Starcoin Framework 的 `IdentiferNFT` 模块中有一个函数 `owns()`,在 -`NFT.move` 当中。它的原始实现是: +大多数时候,Move 开发人员在源代码级别编写代码、测试和调试。然而,由于字节码实际上是 Move VM 执行的内容,在一些罕见的情况下,我们可能需要检查特定模块或函数的相应字节码。 + +例如,在 Starcoin Framework 的 `IdentiferNFT` 模块中有一个函数 `owns()`,在`NFT.move` 当中。它的原始实现是: + ``` public fun owns(owner: address): bool acquires IdentifierNFT { if (!exists>(owner)) { @@ -55,10 +48,8 @@ public fun owns(owner: address): b } ``` +有些读者可能已经注意到,`if not X then false else Y`形式的逻辑是实际上相当于`X and Y`。因此,很容易将函数简化为这种形式(让我们称之为单行版本): -有些读者可能已经注意到,`if not X then false else Y`形式的逻辑是 -实际上相当于`X and Y`。因此,很容易将函数简化为这种形式 -(让我们称之为单行版本): ``` public fun owns(owner: address): bool acquires IdentifierNFT { exists>(owner)) && @@ -66,14 +57,12 @@ public fun owns(owner: address): b } ``` -但是如果我们尝试运行测试,我们可能会注意到单行版本消耗的 gas 费用与 -原始版本不一样。为什么? +但是如果我们尝试运行测试,我们可能会注意到单行版本消耗的 gas 费用与原始版本不一样。为什么? 现在让我们来检查一下字节码。 -首先,让我们检查原始实现的字节码。 -要反汇编模块 `IdentifierNFT`,请运行 `mpm package disassemble --name IdentifierNFT`。 -这是 `owns` 函数的结果: +首先,让我们检查原始实现的字节码。要反汇编模块 `IdentifierNFT`,请运行 `mpm package disassemble --name IdentifierNFT`。这是 `owns` 函数的结果: + ``` public owns(id_nft: address): bool { B0: @@ -97,20 +86,14 @@ B3: } ``` -让我们尝试使用我们之前学到的东西来理解它: -函数中有4个[基本块](https://en.wikipedia.org/wiki/Basic_block):B0、B1、B2、B3。 -在 B0 中,它把 `owner` 地址复制到栈中,然后用它来做存在性检查, -然后是一个否定,最后条件跳转到 B2。 +让我们尝试使用我们之前学到的东西来理解它:函数中有4个[基本块](https://en.wikipedia.org/wiki/Basic_block):B0、B1、B2、B3。在 B0 中,它把 `owner` 地址复制到栈中,然后用它来做存在性检查,然后是一个否定,最后条件跳转到 B2。 B2 只是简单地把值 false 压栈并返回。 -B1 是一个没有实际意义的基本块,它只将控制流带到 B3。B3 从指令 7-8 中对地址 owner 进行全局借用, -指令 9 将其借用结果存储在 `id_nft` 中。指令 10 再次将其加载到堆栈中(是的,你 -可能已经注意到一个简单的窥视孔指令优化可以消除指令 9-10。目前 Move -编译器并没有真正做太多优化)。最后三个指令 11-13 返回 -字段 0 上的谓词 `is_some()`。 +B1 是一个没有实际意义的基本块,它只将控制流带到 B3。B3 从指令 7-8 中对地址 owner 进行全局借用,指令 9 将其借用结果存储在 `id_nft` 中。指令 10 再次将其加载到堆栈中(是的,你可能已经注意到一个简单的窥视孔指令优化可以消除指令 9-10。目前 Move编译器并没有真正做太多优化)。最后三个指令 11-13 返回字段 0 上的谓词 `is_some()`。 现在让我们检查一下单行版本的字节码: + ``` public owns(%#1: address): bool { B0: @@ -135,8 +118,8 @@ B4: } ``` -此处不再赘述,因为大部分指令的意思都是一样的。 -这里提一下值得注意的一些不同之处: +此处不再赘述,因为大部分指令的意思都是一样的。这里提一下值得注意的一些不同之处: + 1. 单行版本的基础块更多。 2. 结果不再直接从堆栈返回。请注意,编译器生成了一个未命名的局部变量 `%#1`,任何要返回的值都存储在其中, 最后是 B4 返回它。 diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/98-move-examples/01-create-a-new-token.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/98-move-examples/01-create-a-new-token.md index fa8a0f48c..f6570e5ed 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/98-move-examples/01-create-a-new-token.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/98-move-examples/01-create-a-new-token.md @@ -6,8 +6,7 @@ 首先,按照[如何设置本地开发网络](../../02-getting-started/02-setup/03-dev-network.md)中的描述启动一个开发网络,并获得一些 STC 代币,比如 `1000000000`。 -在本文档中,我将使用我的开发网络的默认帐户地址 `0xb19b07b76f00a8df445368a91c0547cc` 来代表**发行**和**发送**新 Token 的人。 -我还创建了另一个帐户 `0x831d51f0087596e6aa4e7b3b9c85f945` 并将一些 STC 转移给它。该帐户将用于接收 Token。 +在本文档中,我将使用我的开发网络的默认帐户地址 `0xb19b07b76f00a8df445368a91c0547cc` 来代表**发行**和**发送**新 Token 的人。我还创建了另一个帐户 `0x831d51f0087596e6aa4e7b3b9c85f945` 并将一些 STC 转移给它。该帐户将用于接收 Token。 创建自定义代币的代码源文件位于 [my-token](https://github.com/starcoinorg/starcoin-cookbook/tree/main/examples/my-token)。 diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/README.md b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/README.md index fde6d05af..4de7e025e 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/03-move/README.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/03-move/README.md @@ -2,5 +2,4 @@ 这一章主要介绍 Move 开发的相关内容。 -首先介绍如何搭建 Move 语言的开发环境,接着就可以动手跟着快速开始这一章节的内容体验 Move 合约开发的基本流程。 -接着介绍 Move 语言一些重要概念、Starcoin 开发的 `mpm` 工具、如何跟合约交互,集成测试等内容。 +首先介绍如何搭建 Move 语言的开发环境,接着就可以动手跟着快速开始这一章节的内容体验 Move 合约开发的基本流程。接着介绍 Move 语言一些重要概念、Starcoin 开发的 `mpm` 工具、如何跟合约交互,集成测试等内容。