Skip to content

Commit

Permalink
Implement checkDilutedCheckUsage for Cairo runner (#475)
Browse files Browse the repository at this point in the history
* Implement checkDilutedCheckUsage for Cairo runner

* solve conflicts

---------

Co-authored-by: Nikita Orlov <[email protected]>
  • Loading branch information
tcoratger and StringNick authored Mar 22, 2024
1 parent e7c44eb commit ffc5a82
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 26 deletions.
65 changes: 40 additions & 25 deletions src/vm/core_test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ const build_options = @import("../build_options.zig");
const BuiltinRunner = @import("./builtins/builtin_runner/builtin_runner.zig").BuiltinRunner;
const BitwiseBuiltinRunner = @import("./builtins/builtin_runner/bitwise.zig").BitwiseBuiltinRunner;
const KeccakBuiltinRunner = @import("./builtins/builtin_runner/keccak.zig").KeccakBuiltinRunner;
const BitwiseInstanceDef = @import("./types/bitwise_instance_def.zig").BitwiseInstanceDef;
const KeccakInstanceDef = @import("./types/keccak_instance_def.zig").KeccakInstanceDef;
const Felt252 = @import("../math/fields/starknet.zig").Felt252;
const HashBuiltinRunner = @import("./builtins/builtin_runner/hash.zig").HashBuiltinRunner;
Expand Down Expand Up @@ -131,11 +130,14 @@ test "CairoVM: deduceMemoryCell builtin valid" {
.{},
);
defer vm.deinit();
var instance_def: BitwiseInstanceDef = .{};
try vm.builtin_runners.append(BuiltinRunner{ .Bitwise = BitwiseBuiltinRunner.init(
&instance_def,
true,
) });
try vm.builtin_runners.append(
BuiltinRunner{
.Bitwise = BitwiseBuiltinRunner.init(
&.{},
true,
),
},
);

try vm.segments.memory.setUpMemory(
std.testing.allocator,
Expand Down Expand Up @@ -1896,11 +1898,14 @@ test "CairoVM: computeOp0Deductions with a valid built in and non null deduceMem
.{},
);
defer vm.deinit();
var instance_def: BitwiseInstanceDef = .{};
try vm.builtin_runners.append(BuiltinRunner{ .Bitwise = BitwiseBuiltinRunner.init(
&instance_def,
true,
) });
try vm.builtin_runners.append(
BuiltinRunner{
.Bitwise = BitwiseBuiltinRunner.init(
&.{},
true,
),
},
);
try vm.segments.memory.setUpMemory(
std.testing.allocator,
.{
Expand Down Expand Up @@ -2150,21 +2155,28 @@ test "CairoVM: getBuiltinRunners should return a reference to the builtin runner
.{},
);
defer vm.deinit();
var instance_def: BitwiseInstanceDef = .{ .ratio = null, .total_n_bits = 2 };
try vm.builtin_runners.append(BuiltinRunner{ .Bitwise = BitwiseBuiltinRunner.init(
&instance_def,
true,
) });
try vm.builtin_runners.append(
BuiltinRunner{
.Bitwise = BitwiseBuiltinRunner.init(
&.{ .ratio = null, .total_n_bits = 2 },
true,
),
},
);

// Test check
try expectEqual(&vm.builtin_runners, vm.getBuiltinRunners());

var expected = ArrayList(BuiltinRunner).init(std.testing.allocator);
defer expected.deinit();
try expected.append(BuiltinRunner{ .Bitwise = BitwiseBuiltinRunner.init(
&instance_def,
true,
) });
try expected.append(
BuiltinRunner{
.Bitwise = BitwiseBuiltinRunner.init(
&.{ .ratio = null, .total_n_bits = 2 },
true,
),
},
);
try expectEqualSlices(
BuiltinRunner,
expected.items,
Expand Down Expand Up @@ -2274,11 +2286,14 @@ test "CairoVM: computeOp1Deductions should return op1 from deduceMemoryCell if n
var vm = try CairoVM.init(std.testing.allocator, .{});
defer vm.deinit();

var instance_def: BitwiseInstanceDef = .{};
try vm.builtin_runners.append(BuiltinRunner{ .Bitwise = BitwiseBuiltinRunner.init(
&instance_def,
true,
) });
try vm.builtin_runners.append(
BuiltinRunner{
.Bitwise = BitwiseBuiltinRunner.init(
&.{},
true,
),
},
);
try vm.segments.memory.setUpMemory(
std.testing.allocator,
.{
Expand Down
1 change: 1 addition & 0 deletions src/vm/error.zig
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ pub const MemoryError = error{
SignatureNotFound,
// Invalid signature
InvalidSignature,
InsufficientAllocatedCells,
};

/// Represents the error conditions that are related to the `CairoRunner`.
Expand Down
163 changes: 163 additions & 0 deletions src/vm/runners/cairo_runner.zig
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,68 @@ pub const CairoRunner = struct {
return res;
}

/// Checks the diluted check usage to ensure sufficient allocation of memory units.
///
/// This method calculates the amount of memory units used by the built-in runners
/// for diluted checks and verifies if it meets the required upper bound.
///
/// # Arguments
///
/// - `self`: A mutable reference to the CairoRunner instance.
/// - `allocator`: The allocator to be used for memory allocation.
///
/// # Returns
///
/// This method does not return a value, but it may panic if insufficient memory units are allocated.
///
/// # Errors
///
/// - Panics with a `MemoryError` if the allocated memory units for diluted checks are insufficient.
pub fn checkDilutedCheckUsage(self: *Self, allocator: Allocator) !void {
// Check if diluted pool instance is defined
if (self.layout.diluted_pool_instance_def) |diluted_pool_instance| {
var used_units_by_builtins: usize = 0;

// Iterate through each built-in runner
for (self.vm.builtin_runners.items) |*builtin_runner| {
// Calculate the used units by the built-in runner for diluted checks
const used_units = try builtin_runner.getUsedDilutedCheckUnits(
allocator,
diluted_pool_instance.spacing,
diluted_pool_instance.n_bits,
);

// Calculate the multiplier based on the current step and the runner's ratio
const multiplier = try std.math.divExact(
usize,
self.vm.current_step,
builtin_runner.ratio() orelse 1,
);

// Update the total used units by built-ins
used_units_by_builtins += used_units * multiplier;
}

// Calculate the total diluted units based on the diluted pool instance and the current step
const diluted_units = @as(
usize,
@intCast(diluted_pool_instance.units_per_step),
) * self.vm.current_step;

// Calculate the unused diluted units
const unused_diluted_units = diluted_units -| used_units_by_builtins;

// Calculate the upper bound for diluted check usage
const diluted_usage_upper_bound = @as(usize, @intCast(1)) <<
@intCast(diluted_pool_instance.n_bits);

// Check if unused diluted units are less than the upper bound
if (unused_diluted_units < diluted_usage_upper_bound) {
return MemoryError.InsufficientAllocatedCells;
}
}
}

/// Retrieves a pointer to the list of built-in functions defined in the program associated with the CairoRunner.
///
/// This function returns a pointer to an ArrayList containing the names of the built-in functions defined in the program.
Expand Down Expand Up @@ -4179,3 +4241,104 @@ test "CairoRunner: run until next power of 2" {
// Assert that the current step count is 10.
try expectEqual(@as(usize, 10), cairo_runner.vm.current_step);
}

test "CairoRunner: checkDilutedCheckUsage without pool instance" {
// Create a CairoRunner instance for testing.
var cairo_runner = try CairoRunner.init(
std.testing.allocator,
try Program.initDefault(std.testing.allocator, true),
"all_cairo",
ArrayList(MaybeRelocatable).init(std.testing.allocator),
try CairoVM.init(
std.testing.allocator,
.{},
),
false,
);
defer cairo_runner.deinit(std.testing.allocator);

// Set diluted pool instance to null for testing without pool instance.
cairo_runner.layout.diluted_pool_instance_def = null;

// Call checkDilutedCheckUsage method and expect no error.
try cairo_runner.checkDilutedCheckUsage(std.testing.allocator);
}

test "CairoRunner: checkDilutedCheckUsage without builtins runners" {
// Create a CairoRunner instance for testing.
var cairo_runner = try CairoRunner.init(
std.testing.allocator,
try Program.initDefault(std.testing.allocator, true),
"all_cairo",
ArrayList(MaybeRelocatable).init(std.testing.allocator),
try CairoVM.init(
std.testing.allocator,
.{},
),
false,
);
defer cairo_runner.deinit(std.testing.allocator);

// Set current step to simulate no built-in runners.
cairo_runner.vm.current_step = 10000;

// Call checkDilutedCheckUsage method and expect no error.
try cairo_runner.checkDilutedCheckUsage(std.testing.allocator);
}

test "CairoRunner: checkDilutedCheckUsage with insufficient allocated cells" {
// Create a CairoRunner instance for testing.
var cairo_runner = try CairoRunner.init(
std.testing.allocator,
try Program.initDefault(std.testing.allocator, true),
"all_cairo",
ArrayList(MaybeRelocatable).init(std.testing.allocator),
try CairoVM.init(
std.testing.allocator,
.{},
),
false,
);
defer cairo_runner.deinit(std.testing.allocator);

// Set current step to simulate insufficient allocated cells.
cairo_runner.vm.current_step = 100;

// Call checkDilutedCheckUsage method and expect MemoryError.InsufficientAllocatedCells.
try expectError(
MemoryError.InsufficientAllocatedCells,
cairo_runner.checkDilutedCheckUsage(std.testing.allocator),
);
}

test "CairoRunner: checkDilutedCheckUsage with bitwise builtin" {
// Create a CairoRunner instance for testing.
var cairo_runner = try CairoRunner.init(
std.testing.allocator,
try Program.initDefault(std.testing.allocator, true),
"all_cairo",
ArrayList(MaybeRelocatable).init(std.testing.allocator),
try CairoVM.init(
std.testing.allocator,
.{},
),
false,
);
defer cairo_runner.deinit(std.testing.allocator);

// Set current step to simulate usage with bitwise builtin.
cairo_runner.vm.current_step = 8192;

// Append bitwise builtin runner.
try cairo_runner.vm.builtin_runners.append(
.{
.Bitwise = BitwiseBuiltinRunner.init(
&.{},
true,
),
},
);

// Call checkDilutedCheckUsage method and expect no error.
try cairo_runner.checkDilutedCheckUsage(std.testing.allocator);
}
2 changes: 1 addition & 1 deletion src/vm/types/diluted_pool_instance_def.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub const DilutedPoolInstanceDef = struct {
/// Logarithm of the ratio between diluted cells in the pool and CPU steps.
///
/// Can be negative for scenarios with few builtins requiring diluted units (e.g., bitwise and Keccak).
units_per_step: ?i32 = 16,
units_per_step: i32 = 16,

/// Represents the spacing between consecutive information-carrying bits in diluted form.
spacing: u32 = 4,
Expand Down

0 comments on commit ffc5a82

Please sign in to comment.