Skip to content

Commit

Permalink
chore: add HintCollection tests (keep-starknet-strange#389)
Browse files Browse the repository at this point in the history
* chore: add tests 1

* feat: add tests

* chore: review
  • Loading branch information
dhruvkelawala authored Feb 23, 2024
1 parent 50a72f2 commit 42ff5c1
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 29 deletions.
230 changes: 202 additions & 28 deletions src/vm/types/program.zig
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,34 @@ pub const HintsRanges = union(enum) {
std.hash_map.AutoContext(Relocatable),
std.hash_map.default_max_load_percentage,
),
NonExtensive: std.ArrayList(HintRange),
NonExtensive: std.ArrayList(?HintRange),

pub fn init(
allocator: Allocator,
max_hint_pc: usize,
extensive_hints: bool,
) Self {
) !Self {
return switch (extensive_hints) {
true => Self{ .Extensive = std.AutoHashMap(Relocatable, HintRange).init(allocator) },
false => Self{ .NonExtensive = std.ArrayList(HintRange).init(allocator) },
true => .{ .Extensive = std.AutoHashMap(Relocatable, HintRange).init(allocator) },
false => .{
.NonExtensive = blk: {
var res = std.ArrayList(?HintRange).init(allocator);
errdefer res.deinit();
const append_result = res.appendNTimes(null, max_hint_pc + 1);
if (append_result) {
break :blk res;
} else |err| {
return err;
}
},
},
};
}

pub fn add(self: *Self, offset: usize, range: HintRange) !void {
return switch (self.*) {
.Extensive => |*extensive| try extensive.put(Relocatable.init(0, offset), range),
.NonExtensive => |*non_extensive| try non_extensive.append(range),
.NonExtensive => |*non_extensive| non_extensive.items[offset] = range,
};
}

Expand Down Expand Up @@ -116,10 +128,10 @@ pub const HintsCollection = struct {
var hints_values = try std.ArrayList(HintParams).initCapacity(allocator, total_hints_len);
errdefer hints_values.deinit();

var hints_ranges = HintsRanges.init(allocator, extensive_hints);
var hints_ranges = try HintsRanges.init(allocator, max_hint_pc, extensive_hints);

it = hints.iterator();
while (it.next()) |kv| {
while (it.next()) |*kv| {
if (kv.value_ptr.*.len > 0) {
try hints_ranges.add(
kv.key_ptr.*,
Expand All @@ -129,7 +141,7 @@ pub const HintsCollection = struct {
}
}

return Self{
return .{
.hints = hints_values,
.hints_ranges = hints_ranges,
};
Expand All @@ -139,10 +151,10 @@ pub const HintsCollection = struct {
///
/// # Params:
/// - `allocator`: The allocator used to initialize the collection.
pub fn initDefault(allocator: Allocator) Self {
pub fn initDefault(allocator: Allocator) !Self {
return .{
.hints = std.ArrayList(HintParams).init(allocator),
.hints_ranges = HintsRanges.init(allocator, true),
.hints_ranges = try HintsRanges.init(allocator, 0, true),
};
}

Expand All @@ -164,24 +176,28 @@ pub const HintsCollection = struct {
var res = std.AutoHashMap(usize, []const HintParams).init(allocator);
errdefer res.deinit();

if (!self.hints_ranges.isExtensive()) {
return ProgramError.NonExtensiveHints;
}

// Iterate over hints_ranges to populate the AutoHashMap.
var it = self.hints_ranges.Extensive.iterator();
if (self.hints_ranges.isExtensive()) {
// Iterate over hints_ranges to populate the AutoHashMap.
var it = self.hints_ranges.Extensive.iterator();

while (it.next()) |kv| {
// Calculate the end index of the hint slice.
const end = kv.value_ptr.start + kv.value_ptr.length;

// Check if the end index is within bounds of hints.items.
if (end <= self.hints.items.len) {
// Put the offset and corresponding hint slice into the AutoHashMap.
try res.put(
kv.key_ptr.offset,
self.hints.items[kv.value_ptr.start..end],
);
while (it.next()) |kv| {
// Calculate the end index of the hint slice.
const end = kv.value_ptr.start + kv.value_ptr.length;

// Check if the end index is within bounds of hints.items.
if (end <= self.hints.items.len) {
// Put the offset and corresponding hint slice into the AutoHashMap.
try res.put(
kv.key_ptr.offset,
self.hints.items[kv.value_ptr.start..end],
);
}
}
} else {
for (self.hints_ranges.NonExtensive.items, 0..) |range, pc| {
if (range) |r| {
try res.put(pc, self.hints.items[r.start..(r.start + r.length)]);
}
}
}

Expand Down Expand Up @@ -316,11 +332,12 @@ pub const Program = struct {
identifiers: std.StringHashMap(Identifier),
error_message_attributes: std.ArrayList(Attribute),
instruction_locations: ?std.StringHashMap(InstructionLocation),
extensive_hints: bool,
) !Self {
return .{
.shared_program_data = .{
.data = data,
.hints_collection = try HintsCollection.init(allocator, hints, data.items.len, true),
.hints_collection = try HintsCollection.init(allocator, hints, data.items.len, extensive_hints),
.main = main,
.error_message_attributes = error_message_attributes,
.instruction_locations = instruction_locations,
Expand Down Expand Up @@ -667,6 +684,7 @@ test "Program: init function should init a basic program" {
identifiers,
error_message_attributes,
null,
true,
);

// Defer the deinitialization of the program to free allocated memory after the test case.
Expand Down Expand Up @@ -716,6 +734,7 @@ test "Program: init function should init a basic program (data length function)"
identifiers,
error_message_attributes,
null, // Instruction locations (null for this test case).
true,
);

// Defer the deinitialization of the program to free allocated memory after the test case.
Expand Down Expand Up @@ -772,6 +791,7 @@ test "Program: init function should init a program with identifiers" {
identifiers,
error_message_attributes,
null, // Instruction locations (null for this test case).
true,
);

// Defer the deinitialization of the program to free allocated memory after the test case.
Expand Down Expand Up @@ -843,6 +863,7 @@ test "Program: init function should init a program with identifiers (get identif
identifiers,
error_message_attributes,
null, // Instruction locations (null for this test case).
true,
);

// Defer the deinitialization of the program to free allocated memory after the test case.
Expand Down Expand Up @@ -916,6 +937,7 @@ test "Program: iteratorIdentifier should return an iterator over identifiers" {
identifiers,
error_message_attributes,
null, // Instruction locations (null for this test case).
true,
);

// Defer the deinitialization of the program to free allocated memory after the test case.
Expand Down Expand Up @@ -971,6 +993,7 @@ test "Program: init function should init a program with builtins" {
identifiers,
error_message_attributes,
null, // Instruction locations (null for this test case).
true,
);

// Defer the deinitialization of the program to free allocated memory after the test case.
Expand Down Expand Up @@ -1050,6 +1073,157 @@ test "Program: init a new program with invalid identifiers should return an erro
identifiers,
error_message_attributes,
null, // Instruction locations (null for this test case).
true,
),
);
}

test "Program: new program with extensive hints" {
const allocator = std.testing.allocator;
const reference_manager = ReferenceManager.init(allocator);
const builtins = std.ArrayList(BuiltinName).init(allocator);

var data = std.ArrayList(MaybeRelocatable).init(std.testing.allocator);

try data.append(MaybeRelocatable.fromInt(u256, 5189976364521848832));
try data.append(MaybeRelocatable.fromInt(u256, 1000));
try data.append(MaybeRelocatable.fromInt(u256, 5189976364521848832));
try data.append(MaybeRelocatable.fromInt(u256, 2000));
try data.append(MaybeRelocatable.fromInt(u256, 5201798304953696256));
try data.append(MaybeRelocatable.fromInt(u256, 2345108766317314046));

var hints = std.AutoHashMap(usize, []const HintParams).init(allocator);

const default_scopes = &[_][]const u8{};
const default_flow_tracking_data = .{ .ap_tracking = .{ .offset = 0, .group = 0 }, .reference_ids = null };
try hints.put(
5,
&[_]HintParams{
HintParams.init("c", default_scopes, default_flow_tracking_data),
HintParams.init("d", default_scopes, default_flow_tracking_data),
},
);
try hints.put(
1,
&[_]HintParams{
HintParams.init("a", default_scopes, default_flow_tracking_data),
},
);
try hints.put(
4,
&[_]HintParams{
HintParams.init("b", default_scopes, default_flow_tracking_data),
},
);

const identifiers = std.StringHashMap(Identifier).init(allocator);

var program = try Program.init(
allocator,
builtins,
data,
null,
hints,
reference_manager,
identifiers,
std.ArrayList(Attribute).init(allocator),
null,
true,
);

defer program.deinit(allocator);

try expectEqual(program.builtins, builtins);
try expectEqual(program.shared_program_data.data.items, data.items);
try expectEqual(program.shared_program_data.main, null);
try expectEqual(program.shared_program_data.identifiers, identifiers);

var program_hints = try program.shared_program_data.hints_collection.intoHashMap(allocator);
defer program_hints.deinit();
try expectEqual(@as(usize, 3), program_hints.count());
try expect(program_hints.get(5).?.len == 2);
try expectEqualDeep(hints.get(5).?[0], program_hints.get(5).?[0]);
try expectEqualDeep(hints.get(5).?[1], program_hints.get(5).?[1]);

try expect(program_hints.get(1).?.len == 1);
try expectEqualDeep(hints.get(1).?[0], program_hints.get(1).?[0]);

try expect(hints.get(4).?.len == 1);
try expectEqualDeep(hints.get(4).?[0], program_hints.get(4).?[0]);
}

test "Program: new program with non-extensive hints" {
const allocator = std.testing.allocator;

const reference_manager = ReferenceManager.init(allocator);

const builtins = std.ArrayList(BuiltinName).init(allocator);

var data = std.ArrayList(MaybeRelocatable).init(std.testing.allocator);

try data.append(MaybeRelocatable.fromInt(u256, 5189976364521848832));
try data.append(MaybeRelocatable.fromInt(u256, 1000));
try data.append(MaybeRelocatable.fromInt(u256, 5189976364521848832));
try data.append(MaybeRelocatable.fromInt(u256, 2000));
try data.append(MaybeRelocatable.fromInt(u256, 5201798304953696256));
try data.append(MaybeRelocatable.fromInt(u256, 2345108766317314046));

var hints = std.AutoHashMap(usize, []const HintParams).init(allocator);

const default_scopes = &[_][]const u8{};
const default_flow_tracking_data = .{ .ap_tracking = .{ .offset = 0, .group = 0 }, .reference_ids = null };
try hints.put(
5,
&[_]HintParams{
HintParams.init("c", default_scopes, default_flow_tracking_data),
HintParams.init("d", default_scopes, default_flow_tracking_data),
},
);
try hints.put(
1,
&[_]HintParams{
HintParams.init("a", default_scopes, default_flow_tracking_data),
},
);
try hints.put(
4,
&[_]HintParams{
HintParams.init("b", default_scopes, default_flow_tracking_data),
},
);

const identifiers = std.StringHashMap(Identifier).init(allocator);

var program = try Program.init(
allocator,
builtins,
data,
null,
hints,
reference_manager,
identifiers,
std.ArrayList(Attribute).init(allocator),
null,
false,
);

defer program.deinit(allocator);

try expectEqual(program.builtins, builtins);
try expectEqual(program.shared_program_data.data.items, data.items);
try expectEqual(program.shared_program_data.main, null);
try expectEqual(program.shared_program_data.identifiers, identifiers);

var program_hints = try program.shared_program_data.hints_collection.intoHashMap(allocator);
defer program_hints.deinit();
try expectEqual(@as(usize, 3), program_hints.count());
try expect(program_hints.get(5).?.len == 2);
try expectEqualDeep(hints.get(5).?[0], program_hints.get(5).?[0]);
try expectEqualDeep(hints.get(5).?[1], program_hints.get(5).?[1]);

try expect(program_hints.get(1).?.len == 1);
try expectEqualDeep(hints.get(1).?[0], program_hints.get(1).?[0]);

try expect(hints.get(4).?.len == 1);
try expectEqualDeep(hints.get(4).?[0], program_hints.get(4).?[0]);
}
12 changes: 11 additions & 1 deletion src/vm/types/programjson.zig
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,22 @@ pub const Attribute = struct {
/// This structure defines parameters related to a hint, comprising code details, accessible scopes,
/// and flow tracking data.
pub const HintParams = struct {
const Self = @This();

/// Code associated with the hint.
code: []const u8,
/// Accessible scopes for the hint.
accessible_scopes: []const []const u8,
/// Flow tracking data related to the hint.
flow_tracking_data: FlowTrackingData,

pub fn init(code: []const u8, accessible_scopes: []const []const u8, flow_tracking_data: FlowTrackingData) Self {
return .{
.code = code,
.accessible_scopes = accessible_scopes,
.flow_tracking_data = flow_tracking_data,
};
}
};

/// Represents an instruction with associated location details.
Expand Down Expand Up @@ -851,7 +861,7 @@ pub const ProgramJson = struct {
if (max_hint_pc >= self.data.?.len) return ProgramError.InvalidHintPc;

// Initialize a new HintsCollection.
var hints_collection = HintsCollection.initDefault(allocator);
var hints_collection = try HintsCollection.initDefault(allocator);
errdefer hints_collection.deinit();

// Iterate over the hints map to populate the HintsCollection.
Expand Down

0 comments on commit 42ff5c1

Please sign in to comment.