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

By [Starknet 中文](https://paragraph.com/@starknet-zh) · 2022-10-31

---

> 作者：[Darington Nnam](https://twitter.com/0xdarlington) 原文：[Journey Through Cairo VII — Builtins, Hints and Revoked References](https://medium.com/@darlingtonnnam/journey-through-cairo-vii-builtins-hints-and-revoked-references-afc6da487972) 翻译：[Louis Wang](https://twitter.com/lviswang) 校对：[「StarkNet 中文社区」](https://twitter.com/StarkNet_ZH)

欢迎来到我们的系列文章 「Cairo之旅 」第七讲。[上一讲](https://mirror.xyz/starknet-zh.eth/uZSoeFud7D2qNH-Iit8zyXK6aenB2OsmG4h__sRRIwU)中介绍隐式参数以及递归。今天，我们将讨论内置程序、提示和撤销引用。

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

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

内置程序
----

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

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

在 [CairoLang 代码库](https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/cairo/common/cairo_builtins.cairo)中有一些内置的结构帮助器，能轻松导入到 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 合约的区别，可以回看[第二课](https://mirror.xyz/starknet-zh.eth/MkxaRxBgs5BSW0jS7Plos4jIM2ZmRhUc5OEAzppBWKI)内容。

这是官方文档中[内置程序](https://www.cairo-lang.org/docs/how_cairo_works/builtins.html?highlight=builtins)的文档，如 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 合约中，因为提示不会被添加到字节码中，因此不计入执行步骤的总数。换句话说，从验证者的角度无法看到提示存在。可查看[官方文档](https://www.cairo-lang.org/docs/hello_cairo/program_input.html#hints)深入了解提示的作用。

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

    %{ 
        # Python hint goes here 
    %}
    

撤销引用
----

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

撤销引用，主要发生在对另一个函数的调用指令中，在定义一个依赖于 `ap`（临时变量）的引用变量和使用它之间。因为编译器可能无法计算 `ap` 的变化（因为人们可能从程序的另一个地方跳到标签，或者可能调用一个未知方式改变 `ap` 的函数）。Cairo [文档](https://www.cairo-lang.org/docs/how_cairo_works/consts.html#references)中对此有深入阐述。

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

### 局部变量

与基于 **ap** 寄存器的临时变量（使用 **tempvar** 和 **let** 关键字创建的变量）不同，本地变量是基于 **fp** 寄存器的，难以被撤销。

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

    let y = 20
    local y= y
    

通过测试可以更好理解：

### revoked\_references01.cairo

![](https://storage.googleapis.com/papyrus_images/69425319d4d5b04f9ffbcb3bd98f0bf8e8f0d4f84ebb488c05ed0e2ab85d8207.png)

如果我们试图运行这段代码，将得到**撤销引用**错误。所以需要编辑 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
    

检查能否通过测试：

![](https://storage.googleapis.com/papyrus_images/89d022d58a5475064ef9d350afc709fe0d877d874434b897b95d2954aeb532a2.png)

成功！

最后
--

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

我们简略地讲解了与内置函数和提示相关的测试题以控制文章篇幅，完整的答案在我的 [GitHub repo](https://github.com/Darlington02/starklings-article-solutions) 中可以找到。

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

---

*Originally published on [Starknet 中文](https://paragraph.com/@starknet-zh/cairo-vii)*
