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 中可以找到。

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

Subscribe to Starknet 中文
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.