Diamond

Posted on Oct 30, 2022Read on Mirror.xyz

Cairo 之旅 VII:内置程序、提示和撤销引用

作者:Darington Nnam 原文:Journey Through Cairo VII — Builtins, Hints and Revoked References 翻译:Louis Wang 校对:「StarkNet 中文社区」

欢迎来到我们的系列文章 「Cairo之旅 」第七讲。上一讲中介绍隐式参数以及递归。今天,我们将讨论内置程序、提示和撤销引用。

像往常一样,如果你是中途加入,建议从头开始看我们的文章。

P.S:教程中的语法代码都是在 Cairo v0.9.0 版本下使用的

内置程序

内置程序 (Builtins) 是预定义的优化的低级执行单元,它被添加到 Cairo 的 CPU 上帮助执行预定义的计算,如 pedersen hashing、签名验证、位运算等在 Vanilla Cairo 中的执行很昂贵。

Cairo 中的每个内置程序都被分配了一个单独的内存位置,通过常规的 Cairo 内存调用,使用隐式参数进行访问。

CairoLang 代码库中有一些内置的结构帮助器,能轻松导入到 starknet 合约中使用。

Cairo 中可用的内置程序列表:

  1. output - output 内置程序,通过一个指向 felt 类型的指针访问,用于编写程序输出。

  2. pedersen - pedersen 内置程序,使用指向 HashBuiltin 类型的指针访问,用于 pedersen 哈希计算。

  3. range_check - 不同于其他内置程序,range_check 是使用访问型 felt,而不是指针访问的。这个内置程序主要用于整数比较,便于检查一个字段元素是否在 [0, 2^128] 范围内。

  4. ecdsa - ecdsa 内置程序,用一个指向 SignatureBuiltin 类型的指针访问,用于验证 ECDSA 签名。

  5. bitwise - bitwise 内置程序,通过指向 BitwiseBuiltin 类型的指针访问,用于对 felts 进行位操作。

使用 %builtins 指令来指定 Cairo 代码中任意有效内置程序,其顺序与上述列表惯例相同。在你的代码开端指定内置程序指令:

%builtins output pedersen range_check ecdsa bitwise

PS:请注意,只有编写 Cairo 程序时,才需要这样指定你的内置程序。如果编写 Starknet 合约就无需指定,因为在背后已经完成了。如果你还不知道 Cairo 程序和 Cairo 合约的区别,可以回看第二课内容。

这是官方文档中内置程序的文档,如 pedersen 内置程序:

from starkware.cairo.common.cairo_builtins import HashBuiltin

func hash2{hash_ptr: HashBuiltin*}(x, y) -> (z: felt):
    # Create a copy of the reference and advance hash_ptr.
    let hash = hash_ptr
    let hash_ptr = hash_ptr + HashBuiltin.SIZE
    # Invoke the hash function.
    hash.x = x
    hash.y = y
    # Return the result of the hash.
    # The updated pointer is returned automatically.
    return (z=hash.result);
}

提示

提示(Hint)是 Python 代码的片段,包含了仅验证者可见和执行指令。

尽管提示在编写 Cairo 代码时非常有用,但建议不要把它们算入 Starknet 合约中,因为提示不会被添加到字节码中,因此不计入执行步骤的总数。换句话说,从验证者的角度无法看到提示存在。可查看官方文档深入了解提示的作用。

把它封装在字符 % { … %} 之间即可在 Cairo 中指定一个提示:

%{ 
    # Python hint goes here 
%}

撤销引用

撤销引用 (Revoked Reference) 是我在 Cairo 中为数不多的难以理解概念之一,不过它将成为 Cairo 1.0 的过去。

撤销引用,主要发生在对另一个函数的调用指令中,在定义一个依赖于 ap(临时变量)的引用变量和使用它之间。因为编译器可能无法计算 ap 的变化(因为人们可能从程序的另一个地方跳到标签,或者可能调用一个未知方式改变 ap 的函数)。Cairo 文档中对此有深入阐述。

一般可以通过在函数作用域内添加关键字 alloc_locals 来解决这个问题,但在最复杂的情况下,可能需要创建一个局部变量来解决问题。

局部变量

与基于 ap 寄存器的临时变量(使用 tempvarlet 关键字创建的变量)不同,本地变量是基于 fp 寄存器的,难以被撤销。

本地变量是用 local 关键字创建:

let y = 20
local y= y

通过测试可以更好理解:

revoked_references01.cairo

如果我们试图运行这段代码,将得到撤销引用错误。所以需要编辑 bar 函数来通过测试。

首先,我们需要在函数范围内添加 alloc_locals 关键字,然后通过引用绑定,重新引用 hash_ptr 的隐式参数:

func bar{hash_ptr : HashBuiltin*}():
   alloc_locals
   hash2(1, 2)  # Do not change
   foo(3)  # Do not change
   # Insert something here to make the test pass
   local hash_ptr: HashBuiltin* = hash_ptr
   hash2(3, 4)  # Do not change
   return ()  # Do not change
end

检查能否通过测试:

成功!

最后

恭喜!现在我们已经能深入理解了内置程序、提示和撤销引用。

我们简略地讲解了与内置函数和提示相关的测试题以控制文章篇幅,完整的答案在我的 GitHub repo 中可以找到。

如果觉得本教程对你有帮助,转发分享给其他人吧~