作者: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 中可用的内置程序列表:
output - output 内置程序,通过一个指向 felt 类型的指针访问,用于编写程序输出。
pedersen - pedersen 内置程序,使用指向 HashBuiltin 类型的指针访问,用于 pedersen 哈希计算。
range_check - 不同于其他内置程序,range_check 是使用访问型 felt,而不是指针访问的。这个内置程序主要用于整数比较,便于检查一个字段元素是否在 [0, 2^128]
范围内。
ecdsa - ecdsa 内置程序,用一个指向 SignatureBuiltin 类型的指针访问,用于验证 ECDSA 签名。
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 寄存器的临时变量(使用 tempvar 和 let 关键字创建的变量)不同,本地变量是基于 fp 寄存器的,难以被撤销。
本地变量是用 local 关键字创建:
let y = 20
local y= y
通过测试可以更好理解:
如果我们试图运行这段代码,将得到撤销引用错误。所以需要编辑 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 中可以找到。
如果觉得本教程对你有帮助,转发分享给其他人吧~