From a5911016e56f9c904e65f0ae6483c8e348e3107f Mon Sep 17 00:00:00 2001 From: Nikita Orlov Date: Sun, 25 Feb 2024 16:31:29 +0100 Subject: [PATCH] initMainEntrypoint refact and unit tests (#405) * initMainEntrypoint refact and unit tests * short unwrap of var * improve style code --- src/vm/core.zig | 26 ++----- src/vm/error.zig | 4 + src/vm/memory/segments.zig | 18 ++--- src/vm/runners/cairo_runner.zig | 126 ++++++++++++++++++++++++-------- 4 files changed, 117 insertions(+), 57 deletions(-) diff --git a/src/vm/core.zig b/src/vm/core.zig index 060d9735..0e8c2669 100644 --- a/src/vm/core.zig +++ b/src/vm/core.zig @@ -1089,7 +1089,7 @@ pub const CairoVM = struct { return self.segments.loadData( self.allocator, ptr, - data, + data.items, ); } @@ -1644,24 +1644,12 @@ test "Core: test step for preset memory alloc hint extensive" { } const expected_trace = [_][3][2]u64{ - .{ - .{ 0, 3 }, .{ 1, 2 }, .{ 1, 2 } - }, - .{ - .{ 0, 0 }, .{ 1, 4 }, .{ 1, 4 } - }, - .{ - .{ 0, 2 }, .{ 1, 5 }, .{ 1, 4 } - }, - .{ - .{ 0, 5 }, .{ 1, 5 }, .{ 1, 2 } - }, - .{ - .{ 0, 7 }, .{ 1, 6 }, .{ 1, 2 } - }, - .{ - .{ 0, 8 }, .{ 1, 6 }, .{ 1, 2 } - }, + .{ .{ 0, 3 }, .{ 1, 2 }, .{ 1, 2 } }, + .{ .{ 0, 0 }, .{ 1, 4 }, .{ 1, 4 } }, + .{ .{ 0, 2 }, .{ 1, 5 }, .{ 1, 4 } }, + .{ .{ 0, 5 }, .{ 1, 5 }, .{ 1, 2 } }, + .{ .{ 0, 7 }, .{ 1, 6 }, .{ 1, 2 } }, + .{ .{ 0, 8 }, .{ 1, 6 }, .{ 1, 2 } }, }; try std.testing.expectEqual(expected_trace.len, vm.trace_context.state.enabled.entries.items.len); diff --git a/src/vm/error.zig b/src/vm/error.zig index d76a692a..e05394dd 100644 --- a/src/vm/error.zig +++ b/src/vm/error.zig @@ -139,6 +139,10 @@ pub const CairoRunnerError = error{ /// Represents different error conditions that occur in the built-in runners. pub const RunnerError = error{ + //Initialization failure: No execution base + NoExecBase, + // Initialization failure: No program base + NoProgBase, /// Errors associated with computing the address of a stop pointer of RangeCheckBuiltinRunner /// Raised when underflow occurs (i.e., subtracting 1 from 0), /// or when it fails to get a value for the computed address. diff --git a/src/vm/memory/segments.zig b/src/vm/memory/segments.zig index 0a178052..32e7ff09 100644 --- a/src/vm/memory/segments.zig +++ b/src/vm/memory/segments.zig @@ -295,18 +295,18 @@ pub const MemorySegmentManager = struct { self: *Self, allocator: Allocator, ptr: Relocatable, - data: *std.ArrayList(MaybeRelocatable), + data: []const MaybeRelocatable, ) !Relocatable { - var idx = data.items.len; + var idx = data.len; while (idx > 0) : (idx -= 1) { const i = idx - 1; try self.memory.set( allocator, try ptr.addUint(@intCast(i)), - data.items[i], + data[i], ); } - return ptr.addUint(data.items.len) catch MemoryError.Math; + return ptr.addUint(data.len) catch MemoryError.Math; } /// Records details for a specified segment, facilitating relocation: @@ -411,7 +411,7 @@ pub const MemorySegmentManager = struct { try self.loadData( self.allocator, ptr, - arg, + arg.items, ), ), std.ArrayList(Relocatable) => { @@ -427,7 +427,7 @@ pub const MemorySegmentManager = struct { try self.loadData( self.allocator, ptr, - &tmp, + tmp.items, ), ); }, @@ -1099,7 +1099,7 @@ test "MemorySegmentManager: loadData with empty data" { try memory_segment_manager.loadData( allocator, Relocatable.init(0, 3), - &data, + data.items, ), ); } @@ -1119,7 +1119,7 @@ test "MemorySegmentManager: loadData with one element" { const actual = try memory_segment_manager.loadData( allocator, Relocatable.init(0, 0), - &data, + data.items, ); defer memory_segment_manager.memory.deinitData(std.testing.allocator); @@ -1147,7 +1147,7 @@ test "MemorySegmentManager: loadData with three elements" { const actual = try memory_segment_manager.loadData( allocator, Relocatable.init(0, 0), - &data, + data.items, ); defer memory_segment_manager.memory.deinitData(std.testing.allocator); diff --git a/src/vm/runners/cairo_runner.zig b/src/vm/runners/cairo_runner.zig index fa458884..41a07e6a 100644 --- a/src/vm/runners/cairo_runner.zig +++ b/src/vm/runners/cairo_runner.zig @@ -120,15 +120,15 @@ pub const CairoRunner = struct { program: Program, allocator: Allocator, vm: CairoVM, - program_base: Relocatable = undefined, - execution_base: Relocatable = undefined, + program_base: ?Relocatable = null, + execution_base: ?Relocatable = null, initial_pc: ?Relocatable = null, initial_ap: ?Relocatable = null, initial_fp: ?Relocatable = null, final_pc: *Relocatable = undefined, instructions: std.ArrayList(MaybeRelocatable), // function_call_stack: std.ArrayList(MaybeRelocatable), - entrypoint_name: []const u8 = "main", + entrypoint: ?usize, layout: CairoLayout, runner_mode: RunnerMode, run_ended: bool = false, @@ -161,6 +161,7 @@ pub const CairoRunner = struct { .runner_mode = if (proof_mode) .proof_mode_canonical else .execution_mode, .relocated_memory = ArrayList(?Felt252).init(allocator), .execution_scopes = try ExecutionScopes.init(allocator), + .entrypoint = program.shared_program_data.main, }; } @@ -217,20 +218,25 @@ pub const CairoRunner = struct { /// # Arguments /// - `entrypoint:` The address, relative to the program segment, where execution begins. pub fn initState(self: *Self, entrypoint: usize, stack: *std.ArrayList(MaybeRelocatable)) !void { - self.initial_pc = self.program_base; - self.initial_pc.?.addUintInPlace(entrypoint); + if (self.program_base) |prog_base| { + self.initial_pc = try prog_base.addUint(entrypoint); - _ = try self.vm.segments.loadData( - self.allocator, - self.program_base, - &self.instructions, - ); + _ = try self.vm.segments.loadData( + self.allocator, + prog_base, + self.program.shared_program_data.data.items, + ); - _ = try self.vm.segments.loadData( - self.allocator, - self.execution_base, - stack, - ); + for (0..self.program.shared_program_data.data.items.len) |i| + self.vm.segments.memory.markAsAccessed(try prog_base.addUint(i)); + } + if (self.execution_base) |exec_base| { + _ = try self.vm.segments.loadData( + self.allocator, + exec_base, + stack.items, + ); + } else return RunnerError.NoProgBase; } pub fn initFunctionEntrypoint(self: *Self, entrypoint: usize, return_fp: Relocatable, stack: *std.ArrayList(MaybeRelocatable)) !Relocatable { @@ -262,9 +268,8 @@ pub const CairoRunner = struct { for (self.vm.builtin_runners.items) |*builtin_runner| { const builtin_stack = try builtin_runner.initialStack(self.allocator); defer builtin_stack.deinit(); - for (builtin_stack.items) |item| { - try stack.append(item); - } + + try stack.appendSlice(builtin_stack.items); } if (self.isProofMode()) { @@ -274,13 +279,16 @@ pub const CairoRunner = struct { var stack_prefix = try std.ArrayList(MaybeRelocatable).initCapacity(self.allocator, 2 + stack.items.len); defer stack_prefix.deinit(); - try stack_prefix.append(MaybeRelocatable.fromRelocatable(try self.execution_base.addUint(target_offset))); + try stack_prefix.append(MaybeRelocatable.fromRelocatable(try (self.execution_base orelse return RunnerError.NoExecBase).addUint(target_offset))); + try stack_prefix.append(MaybeRelocatable.fromFelt(Felt252.zero())); try stack_prefix.appendSlice(stack.items); var execution_public_memory = try std.ArrayList(usize).initCapacity(self.allocator, stack_prefix.items.len); + for (0..stack_prefix.items.len) |v| { try execution_public_memory.append(v); } + self.execution_public_memory = execution_public_memory; try self.initState(try (self.program.shared_program_data.start orelse @@ -297,23 +305,16 @@ pub const CairoRunner = struct { RunnerError.NoProgramStart), &stack); } - self.initial_fp = try self.execution_base.addUint(target_offset); + self.initial_fp = try (self.execution_base orelse return RunnerError.NoExecBase).addUint(target_offset); self.initial_ap = self.initial_fp; - return self.program_base.addUint(try (self.program.shared_program_data.end orelse + return (self.program_base orelse return RunnerError.NoExecBase).addUint(try (self.program.shared_program_data.end orelse RunnerError.NoProgramEnd)); } const return_fp = try self.vm.segments.addSegment(); - // Buffer for concatenation - var buffer: [100]u8 = undefined; - - // Concatenate strings - const full_entrypoint_name = try std.fmt.bufPrint(&buffer, "__main__.{s}", .{self.entrypoint_name}); - if (self.program.shared_program_data.identifiers.get(full_entrypoint_name)) |identifier| - if (identifier.pc) |pc| - return self.initFunctionEntrypoint(pc, return_fp, &stack); + if (self.entrypoint) |main| return self.initFunctionEntrypoint(main, return_fp, &stack); return RunnerError.MissingMain; } @@ -622,6 +623,73 @@ test "CairoRunner: initMainEntrypoint no main" { } else |_| {} } +test "CairoRunner: initMainEntrypoint" { + var program = try Program.initDefault(std.testing.allocator, true); + + program.shared_program_data.main = 1; + + var cairo_runner = try CairoRunner.init( + std.testing.allocator, + program, + "all_cairo", + ArrayList(MaybeRelocatable).init(std.testing.allocator), + try CairoVM.init( + std.testing.allocator, + .{}, + ), + false, + ); + + defer cairo_runner.deinit(std.testing.allocator); + // why this deinit is separated? + defer cairo_runner.vm.segments.memory.deinitData(std.testing.allocator); + + cairo_runner.program_base = Relocatable.init(0, 0); + cairo_runner.execution_base = Relocatable.init(0, 0); + + // Add an OutputBuiltinRunner to the CairoRunner without setting the stop pointer. + // try cairo_runner.vm.builtin_runners.append(.{ .Output = OutputBuiltinRunner.initDefault(std.testing.allocator) }); + try expectEqual( + Relocatable.init(1, 0), + cairo_runner.initMainEntrypoint(), + ); +} + +test "CairoRunner: initMainEntrypoint proof_mode empty program" { + var program = try Program.initDefault(std.testing.allocator, true); + + program.shared_program_data.main = 8; + program.shared_program_data.start = 0; + program.shared_program_data.end = 0; + + var runner = try CairoRunner.init( + std.testing.allocator, + program, + "all_cairo", + ArrayList(MaybeRelocatable).init(std.testing.allocator), + try CairoVM.init( + std.testing.allocator, + .{}, + ), + true, + ); + + runner.runner_mode = .proof_mode_canonical; + + defer runner.deinit(std.testing.allocator); + // why this deinit is separated? + defer runner.vm.segments.memory.deinitData(std.testing.allocator); + + try runner.initSegments(null); + + try expectEqual(Relocatable.init(1, 0), runner.execution_base); + try expectEqual(Relocatable.init(0, 0), runner.program_base); + try expectEqual(Relocatable.init(0, 0), runner.initMainEntrypoint()); + try expectEqual(Relocatable.init(1, 2), runner.initial_ap); + try expectEqual(runner.initial_fp, runner.initial_ap); + try expectEqual([2]usize{ 0, 1 }, runner.execution_public_memory.?.items[0..2].*); +} + test "CairoRunner: initVM should initialize the VM properly with no builtins" { // Initialize a CairoRunner with an empty program, "plain" layout, and empty instructions. var cairo_runner = try CairoRunner.init(