ZK L2三剑客之一的StarkNet,其原生编程语言Cairo的表现力从何而来?

StarkNet 是用于扩展以太坊的 L2 网络,也是使用 STARK 证明系统构建的通用的 ZK Rollup,它的基础设施和智能合约均使用的是Cairo 编程语言。

随着以太坊合并的完成,PoW 从生态系统中退出,以太坊也正式转为 PoS 机制。与此同时,以太坊 Layer 2 扩展解决方案的格局也正在迅速演变。

从广义上来讲,Layer 2 解决方案包括两种主要类型:optimistic Rollup 和 ZK(ZeroKnowledge) Rollup。

而在 ZK Rollup 中,两个主要的 ZK 证明系统是:

  • ZK-SNARK(Zero-Knowledge Succinct Non-Interactive Argument of Knowledge)——简洁非交互零知识证明
  • ZK-STARK(Zero-Knowledge Scalable Transparent Argument of Knowledge)——可扩展、透明零知识证明

StarkNet 是用于扩展以太坊的 L2 网络,也是使用 STARK 证明系统构建的通用的 ZK Rollup,它的基础设施和智能合约均使用的是Cairo 编程语言。

在本文中,我们将对 Cairo 语言及其独特功能进行概述。

请注意,Cairo 是一种相对较新的且正在发展的语言,其功能在未来可能会发生变化,本文内容主要是基于当前版本的 Cairo(0.10.0)。

Cairo 编程语言简介

Starknet 架构

在深入了解 Cairo 语言之前,我们可以先了解一下 Starknet 的架构。

图①显示了 Starknet 的主要组成部分、它与以太坊的关系,以及它的一般交易顺序。

在这一部分中,我们将重点讨论Sequencer、Prover 和 Verifier——这是理解 Starknet 工作原理必不可少的一步。

ZK L2三剑客之一的StarkNet,其原生编程语言Cairo的表现力从何而来?
图①:Starknet 架构

Sequencer负责对交易进行排序、验证并将交易打包到块中。目前,Sequencer 由 Starkware 以中心化的方式运行,但该公司计划在未来将 Sequencer 去中心化。

Prover则负责生成关于 Sequence 执行轨迹有效性的加密证明。目前,这项工作是由单一的 Prover,即「Share Prover」或「SHARP」执行的。

Verifier是以太坊 L1 上,用于验证 Starknet Prover 产生证明的智能合约,如果运行成功,其将更新以太坊 L1 上的状态用于记录保存。

因此,其他用户就可以在以太坊 L1 上查询Starknet Core 智能合约的状态,并验证Starknet 上的某项交易是否已经成功执行。

值得注意的是,只能被 Prover 看到但Verifier 看不到的某些 Core 指令被称为 「提示」,它是一段嵌入在 Cairo 程序中的 Python 代码(我们下文会提到)。

这种Prover和 Verifier 之间的不对称性使得一些零知识应用成为可能——例如,用户可以证明他有一个加密问题的解决方案,但不需要从以太坊 L1 的 Verifier 角度来公开这个解决方案。

Cairo 概述

这一部分主要分享 Cairo 语言的概述,包括其数据类型和内存模型等构建模块,以及一些独特的功能,如内置、隐式参数、提示和嵌入式测试函数。

Cairo 程序和 Cairo 合约

在 Starknet 中,Cairo 程序和 Cairo 合约之间有着明显的区别。

Cairo 程序是无状态(stateless)的。举个例子,不需要任何状态变量在 Starknet 上持续存在,一个 Cairo 程序即可对一个给定的输入执行哈希运算,并证明输出与特定的目标输出相匹配。

而 Cairo 合约是有状态(stateful)的。通过 Cairo 合约,状态变量会持续保留在区块链上,从而在 Starknet L2 上启动诸如 ERC20 token、自动做市商等应用程序。

数据类型

Cairo 的原始数据类型是「felt」,它代表了「field element」。felt 是一个整数,范围是 -P/2 < x<P/2,其中 P 是一个非常大的素数(目前是 252 位数)。

Cairo 对使用「felt」的算术运算没有内置的溢出保护。当出现溢出时,就会将其和 P 的适当倍数相加或相减,使结果回到这个范围内或对 P 进行有效的模数运算。

通过使用 felt 作为构建块,Cairo 支持包括元组、结构和数组在内的其他数据类型。

作为一种低级语言,Cairo 中指针的使用率也极高。

大括号 [x] 用于返回内存位置 x 中的值,而 &x 则用于返回变量 x 的内存地址。图②所示的例子声明了一个带有内存分配的数组,返回的指针与偏移量一起被用来表示数组中不同元素的内存位置。

ZK L2三剑客之一的StarkNet,其原生编程语言Cairo的表现力从何而来?
图②:Cairo 中的数组

更复杂的数据类型(如 hashmap),可以用 storage_var 装饰器实现为函数,从而允许进行读写操作。

ZK L2三剑客之一的StarkNet,其原生编程语言Cairo的表现力从何而来?
图③: Cairo 中的hashmap

内存模型

Cairo 拥有只读的非确定性内存,这意味着每个内存单元中的值只能被写入一次,而且在 Cairo 程序执行过程中不能被改变。

因此,根据一个数值是否被写入内存位置,断言的指令[x]==7 可能意味着以下两种情况:

① 从内存位置读取值 x 并验证该值是 7

② 如果内存单元 x 还没有被写入,则将值 7 写入内存单元 x

在 Cairo 中有三个「register」用于低级别的内存访问,即「ap」、「p」和「pc」。

  • ap:分配指针,用于显示未使用的内存的起始位置
  • fp:帧指针,指向当前的函数
  • pc:程序计数器,指向当前指令

使用以上定义,诸如 [ap]=[ap-1]*[fp] 这样的表达式将取前一个分配指针的值,乘以帧指针的值,并将结果写入当前分配指针的内存位置。

由于 Cairo 的只读内存特性,它通常使用递归而不是 for 循环。例如,图④所示的函数使用递归来计算第 n 个斐波那契数。

ZK L2三剑客之一的StarkNet,其原生编程语言Cairo的表现力从何而来?
图④:一个递归的斐波那契函数

内置参数和隐式参数

与 EVM 中的预编译合约类似,Cairo 包含内置函数——这些内置函数是经过优化的低级执行单元,可执行预定义的计算,如哈希函数、系统调用和范围检查。

所有使用内置程序的函数都需要获得内置程序的指针作为参数,并返回一个更新的指针到下一个未使用的实例。

由于这种模式非常普遍,Cairo 为它创造了一个语法糖(Syntactic sugar),叫做「隐式参数」。如图⑤所示,大括号声明 hash_ptr0 是一个「隐式参数」,允许该函数调用预定义的 hash2() 函数。这就自动给函数添加了一个参数和一个返回值,所以工程师不必再手动添加每个利用低级执行单元的函数。

ZK L2三剑客之一的StarkNet,其原生编程语言Cairo的表现力从何而来?
图⑤:内置参数和隐式参数

提示

「提示」是 Cairo 语言的一个独特功能。

它是一个 Python 代码块,由 Starknet Prover 执行于下一条指令之前。「提示」可以与 Cairo 程序的变量或内存进行交互,允许工程师在 Cairo 程序中运用 Python 的功能。为了使用提示,Python 代码需要通过 %{ 和 %} 作为代码首尾(如图⑥所示)。

请注意,Starknet Verifier在 Cairo 程序执行中看不到「提示」,这允许用户在不向以太坊 L1 Verifier 提供任何秘密信息的情况下生成一个证明。此外,「提示」只能在 Cairo 程序中使用,而不用于 Cairo合约,除非该合约被 Starknet 列入白名单。

ZK L2三剑客之一的StarkNet,其原生编程语言Cairo的表现力从何而来?
图⑥:Cairo中的提示

测试函数

名称以 test_开头的外部函数在 Cairo 中被解释为单元测试。这使得工程师可以直接在 Cairo 中实现单元测试,而无需编写单独的测试文件。图⑦显示了一个验证简单算术运算结果的测试函数。

ZK L2三剑客之一的StarkNet,其原生编程语言Cairo的表现力从何而来?
图⑦: Cairo 中的测试函数

在 Cairo 中编写智能合约

在 Cairo 中编写智能合约需要熟悉它的一些基本设计模式。本文的这一部分将介绍一些简单且常见的模式,并在适用的地方将它们与 Solidity 进行比较。

库导入,而非合约继承

Cairo 语言并不像 Solidity 那样支持合约继承。

为了使用其它 Cairo 合约中的逻辑或存储变量,工程师需要导入另一个合约(通常称为「库」),并使用该合约的命名空间来代替相关函数或状态变量。

库定义了可重用的逻辑和存储变量,然后可以通过合约公开这些变量。

例如,图⑧中的 ERC20 库包含了传递函数的实现,但不能直接调用(没有任何装饰器的函数被默认为内部函数),而图⑨中的 ERC20 合约通过使用「ERC20」命名空间、函数名称以及外部装饰器来公开传递函数。

ZK L2三剑客之一的StarkNet,其原生编程语言Cairo的表现力从何而来?
图⑧:ERC20 库中的 transfer() 函数
ZK L2三剑客之一的StarkNet,其原生编程语言Cairo的表现力从何而来?
图⑨:ERC20 合约中的 transfer() 函数

访问控制

Cairo 语言并不像 Solidity 那样支持修饰符。

为了实现对特权函数的访问控制,工程师需要通过内置的 get_caller_address() 函数提取合约的调用者(相当于 Solidity 中的 msg.sender),并检查调用者是否拥有必要的权限——就像 Ownable 库中的所有权检查,如图⑩所示。

ZK L2三剑客之一的StarkNet,其原生编程语言Cairo的表现力从何而来?
图⑩:Cairo的访问控制

可升级的合约

在 Cairo 中,可通过代理合约与实现合约来实现合约的可升级(类似于 Solidity 中的代理模式)。

其中,代理合约包含一个default函数(类似于 Solidit 中 fallback() 函数),如图?所示。

实现合约导入「proxy」命名空间并初始化代理,并包含了一个具有适当访问控制的合约升级机制(类似于 Solidity 中的 UUPS 代理模式)。

如果要部署一个可升级的合约,首先需要声明一个实现合约类,并计算它的类哈希值。然后用实现合约的类哈希值,以及描述调用初始化代理合约的输入来部署代理合约。

ZK L2三剑客之一的StarkNet,其原生编程语言Cairo的表现力从何而来?
:代理合约默认函数

Cairo 发展路线图

Cairo 语言正在积极开发中,它的重大升级 Cairo1.0 计划在 2023 年初进行。目前,Starkware 已经开源了 Cairo 1.0 编译器的第一个版本。

Cairo1.0 引入了「Sierra」,这是一个介于 Cairo 1.0 和 Cairo 字节码之间的安全中间表示,可以证明每一次 Cairo 的运行。

此外,Cairo 1.0 将包含简化的语法和更容易使用的语言结构。例如在 Cairo 中,「for」循环将成为可能,且将支持布尔表达式。除此之外,还将引入本地 uint256 数据类型,以及常规的整数除法和相关类型的溢出保护。

Cairo1.0 还将加入改进的类型安全保证,以及更直观的库(如词典及数组)。Cairo1.0 将带来的功能十分值得期待,这些功能可以有助于极大地缓解当前版本部分开发者的痛点。

(声明:请读者严格遵守所在地法律法规,本文不代表任何投资建议)

(0)
上一篇 2022年12月16日 下午12:13
下一篇 2022年12月16日 下午1:44

相关推荐

  • 第一个Metaverse炒作周期结束了吗?

    2022 年被称为「元宇宙之年」。但最终,这是喜忧参半的一年,有意义的元宇宙成就必须与噪音和消极情绪作斗争。这反映在 Twitter 的推文中,其中的情绪、兴趣和参与涵盖了从巨大的热情到彻底的拒绝。

    2023年3月31日
    288
  • Crypto VC们最近有何动向?7个工具助你轻松追踪

    你可能经常会听到「VC 投了」之类的话,这些 VC 都是谁?哪些口碑最好?而且,最重要的是,他们投资了哪些项目?你如何跟上他们的步伐呢?不需要成为一名专家研究员,以下 7 种工具可以帮助你探索 VC 并找到下一个 50x。

    2023年5月16日
    615
  • 为什么Cosmos治理更严谨和健康?参与者快速入门手册

    Cosmos 区块链预先打包了一个治理系统——一个去中心化的决策过程。在协议层面上,这些决策需要表达为提案,可以无需权限创建,由验证者和委托人进行投票,并提交到区块链上,在那里内容可以被验证者执行。

    2023年6月7日
    325

发表回复

登录后才能评论
微信

联系我们
邮箱:whylweb3@163.com
微信:gaoshuang613