Skip to content

Commit

Permalink
Add garaga hint (keep-starknet-strange#433)
Browse files Browse the repository at this point in the history
* Add garaga hint

* rename hint

---------

Co-authored-by: lanaivina <31368580+lana-shanghai@users.noreply.github.com>
  • Loading branch information
tcoratger and lana-shanghai authored Feb 29, 2024
1 parent 2c38f8f commit 174d6bf
Show file tree
Hide file tree
Showing 6 changed files with 428 additions and 17 deletions.
5 changes: 5 additions & 0 deletions src/hint_processor/builtin_hint_codes.zig
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
pub const GET_FELT_BIT_LENGTH =
\\x = ids.x
\\ids.bit_length = x.bit_length()
;

pub const ASSERT_NN = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.a)\nassert 0 <= ids.a % PRIME < range_check_builtin.bound, f'a = {ids.a} is out of range.'";
pub const VERIFY_ECDSA_SIGNATURE = "ecdsa_builtin.add_signature(ids.ecdsa_ptr.address_, (ids.signature_r, ids.signature_s))";
pub const IS_POSITIVE = "from starkware.cairo.common.math_utils import is_positive\nids.is_positive = 1 if is_positive(\n value=ids.value, prime=PRIME, rc_bound=range_check_builtin.bound) else 0";
Expand Down
194 changes: 194 additions & 0 deletions src/hint_processor/felt_bit_length.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const expectEqual = std.testing.expectEqual;
const expect = std.testing.expect;

const CairoVM = @import("../vm/core.zig").CairoVM;
const HintReference = @import("./hint_processor_def.zig").HintReference;
const hint_utils = @import("./hint_utils.zig");
const ApTracking = @import("../vm/types/programjson.zig").ApTracking;
const HintProcessor = @import("hint_processor_def.zig").CairoVMHintProcessor;
const HintData = @import("hint_processor_def.zig").HintData;
const Relocatable = @import("../vm/memory/relocatable.zig").Relocatable;
const MaybeRelocatable = @import("../vm/memory/relocatable.zig").MaybeRelocatable;
const Felt252 = @import("../math/fields/starknet.zig").Felt252;
const hint_codes = @import("builtin_hint_codes.zig");

/// Implements a hint that calculates the bit length of a variable and assigns it to another variable.
///
/// This function retrieves the value of variable `x` from the provided `ids_datas`, calculates its bit length,
/// and then inserts the bit length value into the `ids_datas` under the variable name `bit_length`.
///
/// # Arguments
///
/// - `allocator`: An allocator to manage memory allocation.
/// - `vm`: A pointer to the CairoVM instance.
/// - `ids_datas`: A hashmap containing variable names and their associated references.
/// - `ap_tracking`: An ApTracking instance providing access path tracking information.
///
/// # Errors
///
/// This function returns an error if there is any issue with retrieving or inserting values into the `ids_datas`.
///
/// # Implements hint:
/// ```python
/// x = ids.x,
/// ids.bit_length = x.bit_length()
/// ```
pub fn getFeltBitLength(
allocator: Allocator,
vm: *CairoVM,
ids_datas: std.StringHashMap(HintReference),
ap_tracking: ApTracking,
) !void {
// Retrieve the value of variable `x` from `ids_datas`.
const x = try hint_utils.getIntegerFromVarName("x", vm, ids_datas, ap_tracking);

// Calculate the bit length of `x`.
// Insert the bit length value into `ids_datas` under the variable name `bit_length`.
try hint_utils.insertValueFromVarName(
allocator,
"bit_length",
MaybeRelocatable.fromInt(usize, x.numBits()),
vm,
ids_datas,
ap_tracking,
);
}

test "FeltBitLength: simple test" {
// Initialize a hashmap to store variable references.
var ids_data = std.StringHashMap(HintReference).init(std.testing.allocator);
defer ids_data.deinit();

// Store references for variables "x" and "bit_length".
// Variable "x" is located at `fp + 0`, and "bit_length" at `fp + 1`.
try ids_data.put("x", HintReference.initSimple(0));
try ids_data.put("bit_length", HintReference.initSimple(1));

// Initialize the Cairo virtual machine.
var vm = try CairoVM.init(std.testing.allocator, .{});
defer vm.deinit();

// Set the frame pointer to point to the beginning of the stack.
vm.run_context.*.fp.* = .{};

// Allocate memory space for variables `ids.x` and `ids.bit_length`.
inline for (0..2) |_| _ = try vm.addMemorySegment();

// Set up memory segments in the virtual machine.
// The memory layout is: [0, 0, 0, 0, 0, 0, 0] (total 7 words).
try vm.segments.memory.setUpMemory(
std.testing.allocator,
.{.{ .{ 0, 0 }, .{7} }},
);
defer vm.segments.memory.deinitData(std.testing.allocator);

// Initialize a HintProcessor instance.
const hint_processor: HintProcessor = .{};

// Initialize HintData with the GET_FELT_BIT_LENGTH hint code and the `ids_data`.
var hint_data = HintData.init(hint_codes.GET_FELT_BIT_LENGTH, ids_data, .{});

// Execute the hint processor with the provided data.
try hint_processor.executeHint(std.testing.allocator, &vm, &hint_data, undefined, undefined);

// Retrieve the result from the memory location of `ids.bit_length`.
const res = try vm.getFelt(Relocatable.init(0, 1));

// Ensure that the result matches the expected value.
try expectEqual(Felt252.fromInt(u8, 3), res);
}

test "FeltBitLength: range test" {
// Iterate over a range of values from 0 to 251 (inclusive).
for (0..252) |i| {
// Initialize a hashmap to store variable references.
var ids_data = std.StringHashMap(HintReference).init(std.testing.allocator);
defer ids_data.deinit();

// Store references for variables "x" and "bit_length".
// Variable "x" is located at `fp + 0`, and "bit_length" at `fp + 1`.
try ids_data.put("x", HintReference.initSimple(0));
try ids_data.put("bit_length", HintReference.initSimple(1));

// Initialize the Cairo virtual machine.
var vm = try CairoVM.init(std.testing.allocator, .{});
defer vm.deinit();

// Set the frame pointer to point to the beginning of the stack.
vm.run_context.*.fp.* = .{};

// Allocate memory space for variables `ids.x` and `ids.bit_length`.
inline for (0..2) |_| _ = try vm.addMemorySegment();

// Set the value of `ids.x` to 2^i.
try vm.segments.memory.set(
std.testing.allocator,
.{},
MaybeRelocatable.fromFelt(Felt252.two().pow(i)),
);
defer vm.segments.memory.deinitData(std.testing.allocator);

// Initialize a HintProcessor instance.
const hint_processor: HintProcessor = .{};

// Initialize HintData with the GET_FELT_BIT_LENGTH hint code and the `ids_data`.
var hint_data = HintData.init(hint_codes.GET_FELT_BIT_LENGTH, ids_data, .{});

// Execute the hint processor with the provided data.
try hint_processor.executeHint(std.testing.allocator, &vm, &hint_data, undefined, undefined);

// Retrieve the result from the memory location of `ids.bit_length`.
const res = try vm.getFelt(Relocatable.init(0, 1));

// Ensure that the result matches the expected value.
try expectEqual(Felt252.fromInt(u256, i + 1), res);
}
}

test "FeltBitLength: wrap around" {
// Initialize a hashmap to store variable references.
var ids_data = std.StringHashMap(HintReference).init(std.testing.allocator);
defer ids_data.deinit();

// Store references for variables "x" and "bit_length".
// Variable "x" is located at `fp + 0`, and "bit_length" at `fp + 1`.
try ids_data.put("x", HintReference.initSimple(0));
try ids_data.put("bit_length", HintReference.initSimple(1));

// Initialize the Cairo virtual machine.
var vm = try CairoVM.init(std.testing.allocator, .{});
defer vm.deinit();

// Set the frame pointer to point to the beginning of the stack.
vm.run_context.*.fp.* = .{};

// Allocate memory space for variables `ids.x` and `ids.bit_length`.
inline for (0..2) |_| _ = try vm.addMemorySegment();

// Set the value of `ids.x` to (Felt252.Modulo - 1) + 1, causing wrap around.
try vm.segments.memory.set(
std.testing.allocator,
.{},
MaybeRelocatable.fromFelt(
Felt252.fromInt(u256, Felt252.Modulo - 1).add(Felt252.one()),
),
);
defer vm.segments.memory.deinitData(std.testing.allocator);

// Initialize a HintProcessor instance.
const hint_processor: HintProcessor = .{};

// Initialize HintData with the GET_FELT_BIT_LENGTH hint code and the `ids_data`.
var hint_data = HintData.init(hint_codes.GET_FELT_BIT_LENGTH, ids_data, .{});

// Execute the hint processor with the provided data.
try hint_processor.executeHint(std.testing.allocator, &vm, &hint_data, undefined, undefined);

// Retrieve the result from the memory location of `ids.bit_length`.
const res = try vm.getFelt(Relocatable.init(0, 1));

// Ensure that the result matches the expected value (0).
try expectEqual(Felt252.zero(), res);
}
3 changes: 3 additions & 0 deletions src/hint_processor/hint_processor_def.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const Relocatable = @import("../vm/memory/relocatable.zig").Relocatable;
const hint_codes = @import("builtin_hint_codes.zig");
const math_hints = @import("math_hints.zig");
const memcpy_hint_utils = @import("memcpy_hint_utils.zig");
const felt_bit_length = @import("felt_bit_length.zig");

const deserialize_utils = @import("../parser/deserialize_utils.zig");

Expand Down Expand Up @@ -197,6 +198,8 @@ pub const CairoVMHintProcessor = struct {
try memcpy_hint_utils.exitScope(exec_scopes);
} else if (std.mem.eql(u8, hint_codes.MEMCPY_ENTER_SCOPE, hint_data.code)) {
try memcpy_hint_utils.memcpyEnterScope(allocator, vm, exec_scopes, hint_data.ids_data, hint_data.ap_tracking);
} else if (std.mem.eql(u8, hint_codes.GET_FELT_BIT_LENGTH, hint_data.code)) {
try felt_bit_length.getFeltBitLength(allocator, vm, hint_data.ids_data, hint_data.ap_tracking);
} else {}
}

Expand Down
Loading

0 comments on commit 174d6bf

Please sign in to comment.