builtin是cairo的一种预先写好的常用函数,包括 range-checks, Pedersen hash, ECDSA等。这种一般都是连续占用一段内存。比如像z=pedersen_hash(x,y)就要占用x,y,z三块,需要如下代码
// Write the value of x to [p + 0].
x = [p];
// Write the value of y to [p + 1].
y = [p + 1];
// The builtin enforces that [p + 2] == hash([p + 0], [p + 1]).
z = [p + 2];
因为cairo中,所有内存都是只读的,因而都是要在当前指针后面添加builtin,然后更新指针,所以就需要每次调用传入指针和要用的builtin,指针根据builtin的size更新。
from starkware.cairo.common.cairo_builtins import HashBuiltin
func hash2(hash_ptr: HashBuiltin*, x, y) -> (
hash_ptr: HashBuiltin*, z: felt
) {
let hash = hash_ptr;
// Invoke the hash function.
hash.x = x;
hash.y = y;
// Return the updated pointer (increased by 3 memory cells)
// and the result of the hash.
return (hash_ptr=hash_ptr + HashBuiltin.SIZE, z=hash.result);
}
但是在implicit用的时候,更新后的指针可能会被扔掉,导致错误,所以需要明确更新,或者用alloc_locals
from starkware.cairo.common.cairo_builtins import HashBuiltin
from starkware.cairo.common.hash import hash2
func foo(n) {
if (n == 0) {
return ();
}
foo(n=n - 1);
return ();
}
func bar{hash_ptr: HashBuiltin*}() {
alloc_locals;
hash2(1, 2);
// You can skip the line below, and the compiler will add
// it automatically, because of the alloc_locals keyword.
local hash_ptr: HashBuiltin* = hash_ptr; // 明确更新
foo(3);
hash2(3, 4);
return ();
}
另外就是if-else的时候,容易漏掉一个分支的更新,也会导致错误
if (x == 0) {
hash2(1, 2);
tempvar hash_ptr = hash_ptr;
} else {
tempvar hash_ptr = hash_ptr; //else 也有更新
}
cairo的layout,假如采用默认的plain layout,那么有可能不够某些builtin所要求的行数,这时候需要加入padding来凑够。
官方还给了个例子,用range check做除法,因为要保证x=q*y+r中的x,y都在[0, 2^64]
func div{range_check_ptr}(x, y) -> (q: felt, r: felt) {
alloc_locals;
local q;
local r;
%{ ids.q, ids.r = ids.x // ids.y, ids.x % ids.y %}
// Check that 0 <= x < 2**64.
[range_check_ptr] = x;
assert [range_check_ptr + 1] = 2 ** 64 - 1 - x; //x的check,调用了range_check_ptr
// Check that 0 <= y < 2**64.
[range_check_ptr + 2] = y;
assert [range_check_ptr + 3] = 2 ** 64 - 1 - y;
// Check that 0 <= q < 2**64.
[range_check_ptr + 4] = q;
assert [range_check_ptr + 5] = 2 ** 64 - 1 - q;
// Check that 0 <= r < y.
[range_check_ptr + 6] = r;
assert [range_check_ptr + 7] = y - 1 - r;
// Verify that x = q * y + r.
assert x = q * y + r;
let range_check_ptr = range_check_ptr + 8;
return (q=q, r=r);
}