diff --git a/src/vm/core_test.zig b/src/vm/core_test.zig index 6fd95989..78608898 100644 --- a/src/vm/core_test.zig +++ b/src/vm/core_test.zig @@ -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; @@ -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, @@ -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, .{ @@ -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, @@ -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, .{ diff --git a/src/vm/error.zig b/src/vm/error.zig index dc578751..60dbae0e 100644 --- a/src/vm/error.zig +++ b/src/vm/error.zig @@ -135,6 +135,7 @@ pub const MemoryError = error{ SignatureNotFound, // Invalid signature InvalidSignature, + InsufficientAllocatedCells, }; /// Represents the error conditions that are related to the `CairoRunner`. diff --git a/src/vm/runners/cairo_runner.zig b/src/vm/runners/cairo_runner.zig index 4c4e01c0..a25f8591 100644 --- a/src/vm/runners/cairo_runner.zig +++ b/src/vm/runners/cairo_runner.zig @@ -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. @@ -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); +} diff --git a/src/vm/types/diluted_pool_instance_def.zig b/src/vm/types/diluted_pool_instance_def.zig index 9de508e5..f826fc9e 100644 --- a/src/vm/types/diluted_pool_instance_def.zig +++ b/src/vm/types/diluted_pool_instance_def.zig @@ -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,