Cairo 1 实战入门:编写测试部署ERC-20代币智能合约

概述

读者可以前往 我的博客 获得更好的阅读体验。

Cairo 是 ZK Rollup 的领域专用语言,目前仅用于 StarkNet 项目。随着 Rollup 叙事的发展,我们认为 cairo 在未来一定会成为智能合约开发的核心语言。

本文类似我之前编写的 Foundry教程:编写测试部署ERC-20代币智能合约 ,介绍了使用 cairo 1 进行编程、测试和部署的全流程。由于缺乏易用工具,本文放弃了本地测试网部署。

本文仅使用 Rust 的部分基础语法,并进行了详细说明,所以读者可以没有 rust 开发基础,但如果读者熟悉 rust 基础语法,那么阅读代码会更加容易。

本文的重点在于介绍 cairo 1 的合约语法部分,理论上,rust 开发者阅读完本文后,就可以熟练编写基础 cairo 1 合约。

本文的部分内容为 solidity 与 cairo 的对比,如果读者不熟悉 solidity 可以直接跳过。由于笔者对 rust 了解不多,所以本文没有给出 cairo 与 rust 的对比。

值得注意的是,笔者没有详细介绍 ERC20 各函数的功能,读者可以参考 eip 文档 或者 SNIP 文档

安装

在开始进行 Cairo 编程前,我们需要安装准备相关环境。笔者使用的是 WSL Ubuntu 22.04 系统。但事实上,使用 macOS 也可达到相同的开发体验。

本文使用了 Cairo 1 语言,相比于大量依赖于 Python 的 Cairo 0 语言,Cairo 1 语言相关的开发工具基本都使用了 Rust 。这意味着我们可以通过直接下载编译后的二进制安装包进行安装。

我们主要依赖于以下工具:

  1. Cairo 开发工具链
  2. Cairo 包管理器 scarb

读者需要 nodejsnpm 工具,考虑到大部分读者在系统内应包含此工具,我们不再详细介绍。事实上,如果读者没有此工具,也可以继续阅读。

如果读者没有 nodejs 工具链,可以考虑使用 nvm 工具进行安装

我们主要介绍 Cairo 开发工具链的安装,使用以下命令下载 release 中编译好的二进制压缩包:

curl -L -o cairo.zip https://github.com/starkware-libs/cairo/releases/download/v1.0.0-alpha.7/release-x86_64-unknown-linux-musl.tar.gz

上述命令中的 v1.0.0-alpha.7 是笔者编写时的最新版本,请读者根据 releases 中的最新版本自行替换。

下载完成后,我们使用以下命令解压缩文件:

tar -xvf cairo.zip

最终,读者会获得一个 cairo/ 文件夹,该文件内结构如下:

.
├── bin
│   ├── cairo-compile
│   ├── cairo-format
│   ├── cairo-language-server
│   ├── cairo-run
│   ├── cairo-test
│   ├── sierra-compile
│   ├── starknet-compile
│   └── starknet-sierra-compile
└── corelib
    ├── Scarb.toml
    ├── cairo_project.toml
    └── src

请读者将 cairo/bin 部分加入系统变量 PATH 中,即完成安装工作。

我一般直接修改 .bashrc 来永久性增加系统变量,可以在 .bashrc 内增加类似 export PATH="$PATH:/root/.cairo/bin" 的命令来添加系统变量。

使用以下命令测试安装是否成功:

cairo-compile -V

Scarb的安装方法与 cairo 基本一致,读者可以参考 文档。使用以下命令测试安装是否成功:

scarb -V

最后,我们安装 vscode 中的开发插件,值得注意的是,目前开发插件需要自行编译安装,使用 download-directory 工具下载 vscode-cairo 文件夹,并在其中运行以下命令:

sudo npm install --global @vscode/vsce
npm install
vsce package
code --install-extension cairo1*.vsix

如果读者遇到错误,请参考 文档,或者直接使用下文我编译好的插件。

如果读者不想自己编译 cairo1*.vsix 文件,我提供了一个编译后的插件,点击此处 进行下载。此插件版本对应 v1.0.0-alpha.7,请读者注意时效性。当然,我相信未来我们可以直接在拓展市场下载此插件。

下载后仅需要运行 code --install-extension cairo1*.vsix,使用以下方法导入安装:

visx install

安装完成后,进入插件的设置页面,如下图:

Cairo1 Setting

在插件设置页面内,在 Language Server Path 内填入 cairo-language-server 二进制文件地址,可以使用 which cairo-language-server 命令获得。在 Scarb Path 内填入 scarb 二进制文件地址,可以使用 which scarb 命令获得。完成上述设置后,请重启 VSCode 软件。

一个示例配置如下(请勿直接抄写文件地址):

Cairo Setting Example

Cairo vs. Solidity

考虑到本文大部分读者具有 solidity 编程背景,本文将梳理 EVMcairoVM 的区别。本节内容对于 Cairo 0 的开发者而言有阅读必要,但对于 Cairo 1 的开发者而言,理论上可以跳过本文。

在数据类型方面,事实上,EVM 的原生数据类型仅有 uint256 ,其他类型都是由 solidity 编程语言在编译过程中实现的。

而在 cairo 中,原生数据类型仅有 felt 类型,读者可简单认为该类型为 uint252。需要注意的是,该类型定义在 有限域 上,更加准确的定义为 0 ≤ x < P 0 \leq x < P 0x<P ,而 P = 2 251 + 17 ⋅ 2 192 + 1 P = 2^{251}+17 \cdot 2^{192} + 1 P=2251+172192+1 。其他数据类型都是由 corelib 标准库和编译器实现的。

与 solidity 提供的常规计算机代数不同,cairo 的所有计算都定义在域上,简单来说,就是所有计算完成后都需要与 P P P 进行模除。当然,这似乎与常规的计算机代数相同。但 felt 类型的除法是令人惊奇的。在 solidity 中,我们认为 x / y 的结果为 ⌊ x / y ⌋ \lfloor x / y \rfloor x/y ,设 x = 7 x = 7 x=7 y = 3 y=3 y=3 ,那么在 solidity 中计算结果为 2 ,但在 cairo 中,计算结果为 1,206,167,596,222,043,737,899,107,594,365,023,368,541,035,738,443,865,566,657,697,352,045,290,673,496

这是因为 cairo 对 felt 的除法做出了以下要求,设 z = x / y z = x / y z=x/y ,那么 z ∗ y = x z * y = x zy=x 是恒成立的。该保证使上述离谱结果的出现。更加详细的解释,请参考 Field elements。请读者在进行 felt 数据类型除法时注意。

在研究完基础数据类型后,我们需要考虑运行环境,众所周知,EVM 是一个基于栈的虚拟机,所有运算都发生在栈上,但 carioVM 则是直接在内存上进行计算。当然,cario 虚拟机也存在寄存器,但功能都较为底层,在正常开发时较少使用。如果读者对此感兴趣请参考 Cairo 0 的 文档

在内存模型上,solidity 使用了可变稀疏内存,我们可以使用 mstore 等操作符在内存的任意位置写入数据,且可以在同一地址内进行覆写操作,但 cairo 的内存模型为不可变连续内存,这意味着我们不能在内存地址内任意写入数据且不能改变已写入的内存数据。该特性使编写循环结构变得几乎不可行,我们只能使用递归的方式编写循环。当然,Cairo 1 引入了 loop 结构也可以更好的实现循环效果,但对于 for 循环语句,目前仍未见到相关语法结构,可能暂未实现。

如果读者熟悉 elixir 等函数式编程语言,应该对于递归代替循环的编程逻辑较为熟悉。如果读者有空闲时间,可以考虑学习一下。

Cairo 0 vs Cairo 1

本文在编写时,cairo 1 已经发布,笔者认为 Cairo 1 应该是进一步发展的重点,所以本文主要介绍 Cairo 1 。考虑到读者不一定了解 Cairo 语言的发展历程且了解 Cairo 0 是有意义的,所以本节主要介绍 Cairo 0 和 Cairo 1 之间的区别。

在语法方面,cairo 1.0 与 Rust 语法几乎完全一致,但可能有部分语法由于 CairoVM 的限制无法实现。而 cairo 0 则与 golang 等语言类似。另一方面,Cairo 0 支持一些底层编程方法,允许开发者直接调整寄存器和内存。同时,cairo 0 要求开发者手动维护内存,没有自动的内存分配系统。一个并不恰当的类比是 Cairo 0 类似 huff 语言,而 cairo 1 类似 solidity 语言。

在本文编写时,cairo 0 并没有语法文档,只有官方提供的两个教程:

  1. Hello, Cairo
  2. How Cairo Works

前者属于实战入门,而后者则是自底向上的分析。读者可根据自身爱好选择教程。我推荐读者阅读后者,因为后者涉及大量对 CairoVM 的底层分析,这些内容是不会随语言特性改变而改变的。

很遗憾,cairo 1 似乎没有完整文档,语法特性几乎完全借鉴于 Rust ,但至于那些特性不被支持,暂且没有完整的语法文档。如果读者希望了解 cairo 1 ,可以参考以下文档:

  1. Starknet Cairo 101 Automated Workshop
  2. A First Look at Cairo 1.0: A Safer, Stronger & Simpler Provable Programming Language
  3. The Starknet Book
  4. Awesome Cairo 该仓库给出了很多 cairo 1 的资源,建议参考

上述资料都处于快速变化中,读者应随时参考官方最新动态。

此处的临时文档来自官方的 discord 群组,可能随时会下架或并入官方网站

在编译上,Cairo 1 引入了中间编译层,该表示层被称为 Sierra ,而最终的编译结果被称为 casm ,更多信息可以参考 Under the hood of Cairo 1.0: Exploring Sierra 。如下图:

cairo 1 complie

Hello World 与测试

本节所有代码都可以在 helloERC20 github 仓库内找到。

在了解基本的 CairoVM 的基础知识后,我们进入真正的编程阶段。首先,我们需要初始化项目,使用以下命令初始化包:

scarb new helloERC20

使用 cd helloERC20 进入项目目录,我们可以看到以下目录结构:

.
├── Scarb.toml
└── src
    └── lib.cairo

此目录结构内仍缺少一些项目配置,请读者增加 cairo_project.toml 文件,并在内部输入以下内容:

[crate_roots]
helloERC20 = "src"

该配置将为 cairo 编译器等工具指明项目入口和顶层包的名称。更多关于 cairo_project.toml 作用的详细内容,我们会在后文介绍 use 关键词时给出。

请读者在 src 文件夹下创建 tests.cairo 文件,此文件为测试入口,所有在此文件中给出的模块都会被测试。此文件暂时为空,但我们马上会向其内部输入内容。

src 文件夹下创建 tests 文件夹,该文件夹内放置编写后的单元测试。最终,我们可以获得以下项目结构:

.
├── Scarb.toml
├── cairo_project.toml
└── src
    ├── lib.cairo
    └── tests.cairo

这是目前最标准的项目结构。

我们接下来介绍每个文件和文件夹的作用,如下:

  1. lib.cairo 类似 Rust 项目中的 lib.rs 文件,主要用于项目作为库被其他开发者调用时使用。作为库时入口,编译器将 lib.cairo 内标识的文件进行
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WongSSH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值