Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(hints): add NewHint#47 #1030

Merged
merged 34 commits into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ee6d7e3
Refactor some cairo_programs as library
MegaRedHand Apr 20, 2023
59d9f35
Refactor pack and split, macro hygiene
MegaRedHand Apr 21, 2023
35d7275
Add NewHint#47
MegaRedHand Apr 21, 2023
2e6cd35
Update changelog
MegaRedHand Apr 21, 2023
70c22fa
Add stdlib prelude import
MegaRedHand Apr 21, 2023
a01dae9
Import HashMap from stdlib
MegaRedHand Apr 21, 2023
1de8d7d
Fix contracts
MegaRedHand Apr 21, 2023
f16eb18
Fix wasm-tests
MegaRedHand Apr 21, 2023
4a55fd4
Merge branch 'main' into newhint47-u512_udiv_rem
MegaRedHand Apr 21, 2023
7a1bea7
Change bitmask to `u128::MAX`
MegaRedHand Apr 21, 2023
a941a8e
Revert "Change bitmask to `u128::MAX`"
MegaRedHand Apr 21, 2023
14bedfe
Compare memory_holes with correct amount
MegaRedHand Apr 21, 2023
a3cb6aa
Rename uint512 -> fq, fq -> fq_test; Add comment
MegaRedHand Apr 21, 2023
726a3ef
Fix contract compilation errors
MegaRedHand Apr 21, 2023
425b0d0
Merge branch 'main' into newhint47-u512_udiv_rem
MegaRedHand Apr 21, 2023
af3d056
Fix compilation error
MegaRedHand Apr 21, 2023
b06e426
Fix cairo_run_fq test
MegaRedHand Apr 21, 2023
89ac8d2
Merge branch 'main' into newhint47-u512_udiv_rem
MegaRedHand Apr 21, 2023
976220c
Move changelog entry
MegaRedHand Apr 21, 2023
e233696
Fix failing tests
MegaRedHand Apr 24, 2023
44d2ddc
Merge branch 'main' into newhint47-u512_udiv_rem
MegaRedHand Apr 24, 2023
a20f4f3
Fix compilation errors
MegaRedHand Apr 24, 2023
e1c8a3f
Fix test error
MegaRedHand Apr 24, 2023
f07c7bf
Define empty mains on libraries
MegaRedHand Apr 24, 2023
fbfd226
Use bare array instead of vec
MegaRedHand Apr 24, 2023
99181d5
Make pack and split methods instead of functions
MegaRedHand Apr 24, 2023
2244805
Merge branch 'main' into newhint47-u512_udiv_rem
MegaRedHand Apr 24, 2023
f895c4f
Fix merge errors
MegaRedHand Apr 24, 2023
32e7e95
Fix errors and add BigInt3::split86
MegaRedHand Apr 24, 2023
c141e93
Merge branch 'main' into newhint47-u512_udiv_rem
MegaRedHand Apr 24, 2023
3cf7081
Make u512_pack and split functions into methods
MegaRedHand Apr 24, 2023
e1a5ae1
Re-add disappeared newline inside hint
MegaRedHand Apr 24, 2023
6888ed8
Fix compilation errors
MegaRedHand Apr 24, 2023
679121e
Merge branch 'main' into newhint47-u512_udiv_rem
MegaRedHand Apr 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,43 @@

#### Upcoming Changes

* Add missing hint on vrf whitelist [#1030](https://github.com/lambdaclass/cairo-rs/pull/1030):

`BuiltinHintProcessor` now supports the following hint:

```python
def split(num: int, num_bits_shift: int, length: int):
a = []
for _ in range(length):
a.append( num & ((1 << num_bits_shift) - 1) )
num = num >> num_bits_shift
return tuple(a)

def pack(z, num_bits_shift: int) -> int:
limbs = (z.low, z.high)
return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))

def pack_extended(z, num_bits_shift: int) -> int:
limbs = (z.d0, z.d1, z.d2, z.d3)
return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))

x = pack_extended(ids.x, num_bits_shift = 128)
div = pack(ids.div, num_bits_shift = 128)

quotient, remainder = divmod(x, div)

quotient_split = split(quotient, num_bits_shift=128, length=4)

ids.quotient.d0 = quotient_split[0]
ids.quotient.d1 = quotient_split[1]
ids.quotient.d2 = quotient_split[2]
ids.quotient.d3 = quotient_split[3]

remainder_split = split(remainder, num_bits_shift=128, length=2)
ids.remainder.low = remainder_split[0]
ids.remainder.high = remainder_split[1]
```

* BREAKING CHANGE: Fix `CairoRunner::get_memory_holes` [#1027](https://github.com/lambdaclass/cairo-rs/pull/1027):

* Skip builtin segements when counting memory holes
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
from starkware.cairo.common.cairo_builtins import BitwiseBuiltin
from starkware.cairo.common.bool import TRUE
from cairo_programs.uint384 import uint384_lib, Uint384, Uint384_expand
from cairo_programs.uint384_extension import uint384_extension_lib
from cairo_programs.uint384 import u384, Uint384, Uint384_expand
from cairo_programs.uint384_extension import u384_ext
from cairo_programs.field_arithmetic import field_arithmetic


func run_get_square{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}(prime: Uint384, generator: Uint384, num: Uint384, iterations: felt) {
func run_get_square{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}(
prime: Uint384, generator: Uint384, num: Uint384, iterations: felt
) {
alloc_locals;
if (iterations == 0) {
return ();
}

let (square) = field_arithmetic.mul(num, num, prime);
let (square) = field_arithmetic.mul(num, num, prime);

let (success, root_1) = field_arithmetic.get_square_root(square, prime, generator);
assert success = 1;

// We calculate this before in order to prevent revoked range_check_ptr reference due to branching
let (root_2) = uint384_lib.sub(prime, root_1);
let (is_first_root) = uint384_lib.eq(root_1, num);
let (root_2) = u384.sub(prime, root_1);
let (is_first_root) = u384.eq(root_1, num);

if ( is_first_root != TRUE) {
if (is_first_root != TRUE) {
assert root_2 = num;
}

return run_get_square(prime, generator, square, iterations -1);
return run_get_square(prime, generator, square, iterations - 1);
}

func main{range_check_ptr: felt, bitwise_ptr: BitwiseBuiltin*}() {
let p = Uint384(18446744069414584321, 0, 0); // Goldilocks Prime
let p = Uint384(18446744069414584321, 0, 0); // Goldilocks Prime
let x = Uint384(5, 0, 0);
let g = Uint384(7, 0, 0);
run_get_square(p, g, x, 100);
Expand Down
45 changes: 23 additions & 22 deletions cairo_programs/field_arithmetic.cairo
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
%builtins range_check bitwise

// Code taken from https://github.com/NethermindEth/research-basic-Cairo-operations-big-integers/blob/fbf532651959f27037d70cd70ec6dbaf987f535c/lib/field_arithmetic.cairo
from starkware.cairo.common.bitwise import bitwise_and, bitwise_or, bitwise_xor
from starkware.cairo.common.cairo_builtins import BitwiseBuiltin
from starkware.cairo.common.math import assert_in_range, assert_le, assert_nn_le, assert_not_zero
from starkware.cairo.common.math_cmp import is_le
from starkware.cairo.common.pow import pow
from starkware.cairo.common.registers import get_ap, get_fp_and_pc
from cairo_programs.uint384 import uint384_lib, Uint384, Uint384_expand, SHIFT, HALF_SHIFT
from cairo_programs.uint384_extension import uint384_extension_lib, Uint768
from cairo_programs.uint384 import u384, Uint384, Uint384_expand, SHIFT, HALF_SHIFT
from cairo_programs.uint384_extension import u384_ext, Uint768

// Functions for operating elements in a finite field F_p (i.e. modulo a prime p), with p of at most 384 bits
namespace field_arithmetic {
// Computes a * b modulo p
func mul{range_check_ptr}(a: Uint384, b: Uint384, p: Uint384) -> (res: Uint384) {
let (low: Uint384, high: Uint384) = uint384_lib.mul_d(a, b);
let (low: Uint384, high: Uint384) = u384.mul_d(a, b);
let full_mul_result: Uint768 = Uint768(low.d0, low.d1, low.d2, high.d0, high.d1, high.d2);
let (
quotient: Uint768, remainder: Uint384
) = uint384_extension_lib.unsigned_div_rem_uint768_by_uint384(full_mul_result, p);
let (quotient: Uint768, remainder: Uint384) = u384_ext.unsigned_div_rem_uint768_by_uint384(
full_mul_result, p
);
return (remainder,);
}

// Computes a**2 modulo p
func square{range_check_ptr}(a: Uint384, p: Uint384) -> (res: Uint384) {
let (low: Uint384, high: Uint384) = uint384_lib.square_e(a);
let (low: Uint384, high: Uint384) = u384.square_e(a);
let full_mul_result: Uint768 = Uint768(low.d0, low.d1, low.d2, high.d0, high.d1, high.d2);
let (
quotient: Uint768, remainder: Uint384
) = uint384_extension_lib.unsigned_div_rem_uint768_by_uint384(full_mul_result, p);
let (quotient: Uint768, remainder: Uint384) = u384_ext.unsigned_div_rem_uint768_by_uint384(
full_mul_result, p
);
return (remainder,);
}

Expand All @@ -43,7 +45,7 @@ namespace field_arithmetic {
alloc_locals;

// TODO: Create an equality function within field_arithmetic to avoid overflow bugs
let (is_zero) = uint384_lib.eq(x, Uint384(0, 0, 0));
let (is_zero) = u384.eq(x, Uint384(0, 0, 0));
if (is_zero == 1) {
return (1, Uint384(0, 0, 0));
}
Expand Down Expand Up @@ -101,35 +103,34 @@ namespace field_arithmetic {
// Verify that the values computed in the hint are what they are supposed to be
let (gx: Uint384) = mul(generator, x, p);
if (success_x == 1) {
uint384_lib.check(sqrt_x);
let (is_valid) = uint384_lib.lt(sqrt_x, p);
assert is_valid = 1;
u384.check(sqrt_x);
let (is_valid) = u384.lt(sqrt_x, p);
assert is_valid = 1;
let (sqrt_x_squared: Uint384) = mul(sqrt_x, sqrt_x, p);
// Note these checks may fail if the input x does not satisfy 0<= x < p
// TODO: Create a equality function within field_arithmetic to avoid overflow bugs
let (check_x) = uint384_lib.eq(x, sqrt_x_squared);
let (check_x) = u384.eq(x, sqrt_x_squared);
assert check_x = 1;
return (1, sqrt_x);
} else {
// In this case success_gx = 1
uint384_lib.check(sqrt_gx);
let (is_valid) = uint384_lib.lt(sqrt_gx, p);
assert is_valid = 1;
u384.check(sqrt_gx);
let (is_valid) = u384.lt(sqrt_gx, p);
assert is_valid = 1;
let (sqrt_gx_squared: Uint384) = mul(sqrt_gx, sqrt_gx, p);
let (check_gx) = uint384_lib.eq(gx, sqrt_gx_squared);
let (check_gx) = u384.eq(gx, sqrt_gx_squared);
assert check_gx = 1;
// No square roots were found
// Note that Uint384(0, 0, 0) is not a square root here, but something needs to be returned
return (0, Uint384(0, 0, 0));
}
}

}

func test_field_arithmetics_extension_operations{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() {
// Test get_square

//Small prime
// Small prime
let p_a = Uint384(7, 0, 0);
let x_a = Uint384(2, 0, 0);
let generator_a = Uint384(3, 0, 0);
Expand All @@ -141,7 +142,7 @@ func test_field_arithmetics_extension_operations{range_check_ptr, bitwise_ptr: B
assert r_a.d2 = 0;

// Goldilocks Prime
let p_b = Uint384(18446744069414584321, 0, 0); // Goldilocks Prime
let p_b = Uint384(18446744069414584321, 0, 0); // Goldilocks Prime
let x_b = Uint384(25, 0, 0);
let generator_b = Uint384(7, 0, 0);
let (s_b, r_b) = field_arithmetic.get_square_root(x_b, p_b, generator_b);
Expand Down
42 changes: 42 additions & 0 deletions cairo_programs/fq.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
%builtins range_check

from cairo_programs.uint512 import u512_unsigned_div_rem, Uint256, Uint512

func test_u512_unsigned_div_rem{range_check_ptr}() {
let x = Uint512(26362362, 32523523, 135525, 15521);
let div = Uint256(1, 0);

let (q, r) = u512_unsigned_div_rem(x, div);

// x / 1 = x
assert q = Uint512(26362362, 32523523, 135525, 15521);
// x % 1 = 0
assert r = Uint256(0, 0);

let x = Uint512(
154693353187447763037373048681595478410,
49972746532502551770198072697358847685,
274245096733591597256384467752461786671,
218971140682455857220416392230637548564,
);
let div = Uint256(
103510830969771876705678198448587782120, 321696934602460025966614305804515599536
);

let (q, r) = u512_unsigned_div_rem(x, div);

assert q = Uint512(
203702859112426540420143348051200561496, 231621784431619772183895351989849416356, 0, 0
);
assert r = Uint256(
294644766503248848032677663267093316042, 283333580363207111408148984050656446476
);

return ();
}

func main{range_check_ptr}() {
test_u512_unsigned_div_rem();

return ();
}
74 changes: 1 addition & 73 deletions cairo_programs/uint384.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
%builtins range_check bitwise
// Code taken from https://github.com/NethermindEth/research-basic-Cairo-operations-big-integers/blob/main/lib/uint384.cairo
from starkware.cairo.common.bitwise import bitwise_and, bitwise_or, bitwise_xor
from starkware.cairo.common.cairo_builtins import BitwiseBuiltin
Expand Down Expand Up @@ -37,7 +36,7 @@ const SHIFT = 2 ** 128;
const ALL_ONES = 2 ** 128 - 1;
const HALF_SHIFT = 2 ** 64;

namespace uint384_lib {
namespace u384 {
// Verifies that the given integer is valid.
func check{range_check_ptr}(a: Uint384) {
[range_check_ptr] = a.d0;
Expand Down Expand Up @@ -497,74 +496,3 @@ namespace uint384_lib {
return (res=root);
}
}

func test_uint384_operations{range_check_ptr}() {
// Test unsigned_div_rem
let a = Uint384(83434123481193248, 82349321849739284, 839243219401320423);
let div = Uint384(9283430921839492319493, 313248123482483248, 3790328402913840);
let (quotient: Uint384, remainder: Uint384) = uint384_lib.unsigned_div_rem{
range_check_ptr=range_check_ptr
}(a, div);
assert quotient.d0 = 221;
assert quotient.d1 = 0;
assert quotient.d2 = 0;

assert remainder.d0 = 340282366920936411825224315027446796751;
assert remainder.d1 = 340282366920938463394229121463989152931;
assert remainder.d2 = 1580642357361782;

// Test split_128
let b = 6805647338418769269267492148635364229100;
let (low, high) = uint384_lib.split_128{range_check_ptr=range_check_ptr}(b);
assert high = 19;
assert low = 340282366920938463463374607431768211436;

// Test _add_no_uint384_test

let c = Uint384(3789423292314891293, 21894, 340282366920938463463374607431768211455);
let d = Uint384(32838232, 17, 8);
let (sum_res, carry) = uint384_lib._add_no_uint384_check(c, d);

assert sum_res.d0 = 3789423292347729525;
assert sum_res.d1 = 21911;
assert sum_res.d2 = 7;
assert carry = 1;

// Test unsigned_div_rem_expanded
let e = Uint384(83434123481193248, 82349321849739284, 839243219401320423);
let div_expand = Uint384_expand(
9283430921839492319493, 313248123482483248, 3790328402913840, 13, 78990, 109, 7
);
let (quotient: Uint384, remainder: Uint384) = uint384_lib.unsigned_div_rem_expanded{
range_check_ptr=range_check_ptr
}(a, div_expand);
assert quotient.d0 = 7699479077076334;
assert quotient.d1 = 0;
assert quotient.d2 = 0;

assert remainder.d0 = 340279955073565776659831804641277151872;
assert remainder.d1 = 340282366920938463463356863525615958397;
assert remainder.d2 = 16;

// Test sqrt
let f = Uint384(83434123481193248, 82349321849739284, 839243219401320423);
let (root) = uint384_lib.sqrt(f);
assert root.d0 = 100835122758113432298839930225328621183;
assert root.d1 = 916102188;
assert root.d2 = 0;

let g = Uint384(1, 1, 1);
let (sign_g) = uint384_lib.signed_nn(g);
assert sign_g = 1;

let h = Uint384(0, 0, 170141183460469231731687303715884105729);
let (sign_h) = uint384_lib.signed_nn(h);
assert sign_h = 0;

return ();
}

func main{range_check_ptr: felt, bitwise_ptr: BitwiseBuiltin*}() {
test_uint384_operations();
return ();
}
Loading