diff --git a/build.zig.zon b/build.zig.zon index db74dd41..7f6085d4 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -14,8 +14,8 @@ .hash = "1220ab73fb7cc11b2308edc3364988e05efcddbcac31b707f55e6216d1b9c0da13f1", }, .starknet = .{ - .url = "https://github.com/StringNick/starknet-zig/archive/57810b7a64364f1bf12725ba823385c2a213bfa5.zip", - .hash = "1220d848be799ff21a80c6751c088ea619891ec450f20017cc7aa5cbbeb5904ae8b8", + .url = "https://github.com/StringNick/starknet-zig/archive/7d51aed59982146df3581d3d3320d509b0b10f54.zip", + .hash = "1220b00c055ce40da237598e0f1c8758e3434470be648816fa4701a09f683146d4cd", }, }, } diff --git a/src/poseidon_consts_gen.zig b/src/poseidon_consts_gen.zig deleted file mode 100644 index 2986653a..00000000 --- a/src/poseidon_consts_gen.zig +++ /dev/null @@ -1,141 +0,0 @@ -/// Generating constants for poseidon hashing function -/// All memory allocation is on arena allocator -/// -const std = @import("std"); -const Felt252 = @import("./math/fields/starknet.zig").Felt252; -const Allocator = std.mem.Allocator; - -const round_constants_block = "/// based on https://github.com/starkware-industries/poseidon/blob/5403dff9ff4eadb07deb5c0a43e88bedb011deb8/poseidon3.txt\n" ++ - "///\n///\n/// This file is autogenerated by poseidon_consts_gen.zig\n///\n///\n" ++ - "const Felt252 = @import(\"../../../fields/starknet.zig\").Felt252;\n\npub const POSEIDON_COMPRESSED_ROUND_CONSTS: [{d}]Felt252 = .{{\n {s}\n }};" ++ - "\n\npub const POSEIDON_FULL_ROUNDS: usize = {d};\n\npub const POSEIDON_PARTIAL_ROUNDS: usize = {d};\n\n"; - -const round_constants_block_item = ".{{ .fe = [4]u64{{ {}, {}, {}, {}, }} }},\n"; - -const ConfigJSON = struct { - full_rounds: usize, - partial_rounds: usize, - round_keys: [][3]u256, -}; - -// generateRoundConstantBlock - injecting compressed round constants and config into template -// result slice owner is caller, so it should be deinit by caller -fn generateRoundConstantBlock(allocator: Allocator, config: ConfigJSON, round_keys: []Felt252) ![]const u8 { - var array_tpl = std.ArrayList(u8).init(allocator); - defer array_tpl.deinit(); - - for (round_keys) |round_key| { - const value = round_key.fe; - // writing array felt item - try std.fmt.format(array_tpl.writer(), round_constants_block_item, .{ - value[0], - value[1], - value[2], - value[3], - }); - } - - var result = std.ArrayList(u8).init(allocator); - - try std.fmt.format(result.writer(), round_constants_block, .{ - round_keys.len, - try array_tpl.toOwnedSlice(), - config.full_rounds, - config.partial_rounds, - }); - - return try result.toOwnedSlice(); -} - -// parseConfig - parsing config from json, allocator should be arena allocator -fn parseConfig(allocator: Allocator, json_spec: []const u8) !ConfigJSON { - return try std.json.parseFromSliceLeaky( - ConfigJSON, - allocator, - json_spec, - .{ .allocate = std.json.AllocWhen.alloc_always }, - ); -} - -// compressRoundConstants - compressing round constants -// caller is owner of result slice and should deinit it -fn compressRoundConstants(allocator: Allocator, config: ConfigJSON, round_constants: [][3]Felt252) ![]Felt252 { - var result = std.ArrayList(Felt252).init(allocator); - - for (round_constants[0 .. config.full_rounds / 2]) |rk| { - inline for (0..3) |i| { - try result.append(rk[i]); - } - } - - var idx = config.full_rounds / 2; - - var state = [_]Felt252{Felt252.zero()} ** 3; - - // Add keys for partial rounds - for (0..config.partial_rounds) |_| { - inline for (0..3) |i| { - state[i] = Felt252.add(state[i], round_constants[idx][i]); - } - - // Add last state - try result.append(state[2]); - - // Reset last state - state[2] = Felt252.zero(); - - const st = Felt252.add(Felt252.add(state[0], state[1]), state[2]); - - // MixLayer - state[0] = Felt252.add(st, Felt252.mul(Felt252.two(), state[0])); - state[1] = Felt252.sub(st, Felt252.mul(Felt252.two(), state[1])); - state[2] = Felt252.sub(st, Felt252.mul(Felt252.two(), state[2])); - - idx += 1; - } - - // Add keys for first of the last full rounds - inline for (0..3) |i| { - state[i] = Felt252.add(state[i], round_constants[idx][i]); - try result.append(state[i]); - } - - for (round_constants[config.full_rounds / 2 + config.partial_rounds + 1 ..]) |rk| { - try result.appendSlice(&rk); - } - - return try result.toOwnedSlice(); -} - -// parseNumbersToFieldElement - parsing numbers to field element -// caller is owner of result slice and should deinit it -fn parseNumbersToFieldElement(allocator: Allocator, keys: [][3]u256) ![][3]Felt252 { - var result = try allocator.alloc([3]Felt252, keys.len); - - for (keys, 0..) |key, idx| { - for (key, 0..) |k, idy| { - result[idx][idy] = Felt252.fromInt(u256, k); - } - } - - return result; -} - -pub fn main() !void { - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - // writing constants for poseidon - const config = try parseConfig(allocator, @embedFile("./math/crypto/poseidon/gen/config.json")); - - const round_consts = try parseNumbersToFieldElement(allocator, config.round_keys); - - const compressed_round_consts = try compressRoundConstants(allocator, config, round_consts); - - const result = try generateRoundConstantBlock(allocator, config, compressed_round_consts); - - var file = try std.fs.cwd().openFile("./src/math/crypto/poseidon/gen/constants.zig", .{ .mode = .write_only }); - - try file.writer().writeAll(result); -} diff --git a/src/vm/builtins/builtin_runner/builtin_runner.zig b/src/vm/builtins/builtin_runner/builtin_runner.zig index c604d445..2654cd87 100644 --- a/src/vm/builtins/builtin_runner/builtin_runner.zig +++ b/src/vm/builtins/builtin_runner/builtin_runner.zig @@ -182,6 +182,7 @@ pub const BuiltinRunner = union(BuiltinName) { pub fn cellsPerInstance(self: *const BuiltinRunner) u32 { return switch (self.*) { .Output => 0, + .Poseidon => 6, inline else => |*builtin| builtin.cells_per_instance, }; } @@ -607,6 +608,7 @@ pub const BuiltinRunner = union(BuiltinName) { return switch (self.*) { .Output => 0, .SegmentArena => |*segment_arena| segment_arena.n_input_cells_per_instance, + .Poseidon => PoseidonBuiltinRunner.INPUT_CELLS_PER_POSEIDON, inline else => |*builtin| builtin.n_input_cells, }; } diff --git a/src/vm/builtins/builtin_runner/poseidon.zig b/src/vm/builtins/builtin_runner/poseidon.zig index 0e54628f..c7cb97f7 100644 --- a/src/vm/builtins/builtin_runner/poseidon.zig +++ b/src/vm/builtins/builtin_runner/poseidon.zig @@ -31,14 +31,12 @@ const expectEqualSlices = std.testing.expectEqualSlices; pub const PoseidonBuiltinRunner = struct { const Self = @This(); + pub const INPUT_CELLS_PER_POSEIDON = poseidon_instance_def.INPUT_CELLS_PER_POSEIDON; + /// Base base: usize = 0, /// Ratio ratio: ?u32, - /// Number of cells per instance - cells_per_instance: u32 = poseidon_instance_def.CELLS_PER_POSEIDON, - /// Number of input cells - n_input_cells: u32 = poseidon_instance_def.INPUT_CELLS_PER_POSEIDON, /// Stop pointer stop_ptr: ?usize = null, /// Included boolean flag @@ -157,7 +155,7 @@ pub const PoseidonBuiltinRunner = struct { // Calculate the expected stop pointer value based on the number of used instances. const stop_ptr = stop_pointer.offset; - if (stop_ptr != try self.getUsedInstances(segments) * self.cells_per_instance) + if (stop_ptr != try self.getUsedInstances(segments) * poseidon_instance_def.CELLS_PER_POSEIDON) return RunnerError.InvalidStopPointer; // Set the stop pointer and return the address of the stop pointer. @@ -201,7 +199,7 @@ pub const PoseidonBuiltinRunner = struct { return std.math.divCeil( usize, try self.getUsedCells(segments), - self.cells_per_instance, + 6, ); } @@ -226,32 +224,27 @@ pub const PoseidonBuiltinRunner = struct { address: Relocatable, memory: *Memory, ) !?MaybeRelocatable { + _ = allocator; // autofix // Calculate the index of the memory cell. - const index = @mod( + const index: usize = @mod( @as(usize, @intCast(address.offset)), - @as(usize, @intCast(self.cells_per_instance)), + poseidon_instance_def.CELLS_PER_POSEIDON, ); // Check if the index corresponds to an input cell, if so, return null. - if (index < self.n_input_cells) return null; + if (index < poseidon_instance_def.INPUT_CELLS_PER_POSEIDON) return null; // Check if the cell value is already cached, if so, return it. if (self.cache.get(address)) |felt| return .{ .felt = felt }; // Calculate the addresses for the first input cell and first output cell. const first_input_addr = try address.subUint(index); - const first_output_addr = try first_input_addr.addUint(self.n_input_cells); + const first_output_addr = try first_input_addr.addUint(poseidon_instance_def.INPUT_CELLS_PER_POSEIDON); // Initialize an array list to store input cell values. - var input_felts = try ArrayList(Felt252).initCapacity(allocator, self.n_input_cells); - defer input_felts.deinit(); - // Iterate over input cells, retrieve their values, and append them to the array list. - for (0..self.n_input_cells) |i| { - const val = memory.get(try first_input_addr.addUint(i)) orelse return null; - try input_felts.append(val.intoFelt() catch - return RunnerError.BuiltinExpectedInteger); - } + var input_felts = memory.getFeltRange(first_input_addr, poseidon_instance_def.INPUT_CELLS_PER_POSEIDON) catch return RunnerError.BuiltinExpectedInteger; + defer input_felts.deinit(); // Perform Poseidon permutation computation on the input cells. // TODO: optimize to use pointer on state @@ -261,11 +254,12 @@ pub const PoseidonBuiltinRunner = struct { PoseidonHasher.permuteComp(); - @memcpy(input_felts.items[0..3], PoseidonHasher.state[0..3]); - // Iterate over input cells and cache their computed values. - for (0..self.n_input_cells, input_felts.items) |i, elem| { - try self.cache.put(try first_output_addr.addUint(i), elem); + inline for (0..3) |i| { + try self.cache.put( + try first_output_addr.addUint(i), + PoseidonHasher.state[i], + ); } // Return the cached value for the specified memory cell address. diff --git a/src/vm/memory/memory.zig b/src/vm/memory/memory.zig index 03d38d2f..728f562f 100644 --- a/src/vm/memory/memory.zig +++ b/src/vm/memory/memory.zig @@ -869,8 +869,16 @@ pub const Memory = struct { ); errdefer values.deinit(); - for (0..size) |i| { - try values.append(try self.getFelt(try address.addUint(i))); + var segment = self.getSegmentAtIndex(address.segment_index) orelse return MemoryError.UnknownMemoryCell; + if (segment.len < address.offset + size) return MemoryError.UnknownMemoryCell; + + for (segment[address.offset .. address.offset + size]) |cell| { + if (cell.getValue()) |mr| { + switch (mr) { + inline .felt => |f| values.appendAssumeCapacity(f), + inline else => return MemoryError.ExpectedInteger, + } + } else return MemoryError.UnknownMemoryCell; } return values; diff --git a/src/vm/run_context.zig b/src/vm/run_context.zig index 2e3421bc..47fd2336 100644 --- a/src/vm/run_context.zig +++ b/src/vm/run_context.zig @@ -67,7 +67,7 @@ pub const RunContext = struct { try base_addr.subUint(@abs(instruction.off_0)) else // Convert i16 to u64 safely - try base_addr.addUint(@bitCast(instruction.off_0)); + try base_addr.addUint(@intCast(instruction.off_0)); } /// Compute OP 0 address for a given instruction.