diff --git a/src/math/fields/fields.zig b/src/math/fields/fields.zig index ac46c2f9..77b16622 100644 --- a/src/math/fields/fields.zig +++ b/src/math/fields/fields.zig @@ -146,32 +146,6 @@ pub fn Field(comptime F: type, comptime mod: u256) type { return .{ .fe = mont }; } - /// Create a field element from a signed integer in Montgomery representation. - /// - /// This function converts a signed integer to a field element in Montgomery form. - pub fn fromSignedInteger(num: i256) Self { - var lbe: [BytesSize]u8 = [_]u8{0} ** BytesSize; - std.mem.writeInt( - i256, - lbe[0..], - @mod(num, Modulo), - .little, - ); - - var nonMont: F.NonMontgomeryDomainFieldElement = undefined; - F.fromBytes( - &nonMont, - lbe, - ); - var mont: F.MontgomeryDomainFieldElement = undefined; - F.toMontgomery( - &mont, - nonMont, - ); - - return .{ .fe = mont }; - } - /// Get the field element representing zero. /// /// Returns a field element with a value of zero. diff --git a/src/math/fields/starknet.zig b/src/math/fields/starknet.zig index f6ec11db..6272f5f1 100644 --- a/src/math/fields/starknet.zig +++ b/src/math/fields/starknet.zig @@ -134,19 +134,6 @@ test "Felt252 fromInteger" { ); } -test "Felt252 fromSignedInteger" { - try expectEqual( - Felt252{ .fe = .{ - 0xffffffeb9bf00041, 0x9987fff, 0xfffffffffffb7c00, 0x7fffea55af00670, - } }, - Felt252.fromSignedInteger(-106710729501573572985208420194530329073740042555888586719234), - ); - try expectEqual( - Felt252.fromInt(u8, 10), - Felt252.fromSignedInteger(10), - ); -} - test "Felt252 toInteger" { try expectEqual( @as( diff --git a/src/vm/types/program.zig b/src/vm/types/program.zig index b4612f97..f25ab87c 100644 --- a/src/vm/types/program.zig +++ b/src/vm/types/program.zig @@ -19,6 +19,7 @@ const ProgramError = @import("../error.zig").ProgramError; const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const expectError = std.testing.expectError; +const expectEqualDeep = std.testing.expectEqualDeep; const expectEqualStrings = std.testing.expectEqualStrings; const expectEqualSlices = std.testing.expectEqualSlices; @@ -47,11 +48,11 @@ pub const HintsCollection = struct { std.hash_map.default_max_load_percentage, ), - /// Initializes a new HintsCollection. + /// Initializes a new HintsCollection with default values. /// /// # Params: /// - `allocator`: The allocator used to initialize the collection. - pub fn init(allocator: Allocator) Self { + pub fn initDefault(allocator: Allocator) Self { return .{ .hints = std.ArrayList(HintParams).init(allocator), .hints_ranges = std.AutoHashMap( @@ -114,11 +115,11 @@ pub const SharedProgramData = struct { /// Collection of hints. hints_collection: HintsCollection, /// Program's main entry point (optional, defaults to `null`). - main: ?usize, + main: ?usize = null, /// Start of the program (optional, defaults to `null`). - start: ?usize, + start: ?usize = null, /// End of the program (optional, defaults to `null`). - end: ?usize, + end: ?usize = null, /// List of error message attributes. error_message_attributes: std.ArrayList(Attribute), /// Map of `usize` to `InstructionLocation`. @@ -135,10 +136,7 @@ pub const SharedProgramData = struct { pub fn init(allocator: Allocator) Self { return .{ .data = std.ArrayList(MaybeRelocatable).init(allocator), - .hints_collection = HintsCollection.init(allocator), - .main = null, - .start = null, - .end = null, + .hints_collection = HintsCollection.initDefault(allocator), .error_message_attributes = std.ArrayList(Attribute).init(allocator), .instruction_locations = std.AutoHashMap( usize, @@ -203,6 +201,49 @@ pub const Program = struct { /// Stores the list of built-in names. builtins: std.ArrayList(BuiltinName), + /// Initializes a new `Program` instance with provided parameters. + /// + /// # Parameters + /// - `allocator`: The allocator used to initialize the program. + /// - `builtins`: List of built-in names. + /// - `data`: List of `MaybeRelocatable` items. + /// - `main`: The main entry point for the program (optional, defaults to `null`). + /// - `hints`: Map of offsets to `HintParams` lists (unused, included for autofix compatibility). + /// - `reference_manager`: The `ReferenceManager` instance. + /// - `identifiers`: Map of identifiers to `Identifier` instances. + /// - `error_message_attributes`: List of `Attribute` items for error messages. + /// - `instruction_locations`: Map of `usize` to `InstructionLocation` (optional, defaults to `null`). + /// + /// # Returns + /// A new `Program` instance initialized with the provided parameters. + pub fn init( + allocator: Allocator, + builtins: std.ArrayList(BuiltinName), + data: std.ArrayList(MaybeRelocatable), + main: ?usize, + hints: std.AutoHashMap(usize, std.ArrayList(HintParams)), + reference_manager: ReferenceManager, + identifiers: std.StringHashMap(Identifier), + error_message_attributes: std.ArrayList(Attribute), + instruction_locations: ?std.StringHashMap(InstructionLocation), + ) !Self { + _ = hints; // autofix + + return .{ + .shared_program_data = .{ + .data = data, + .hints_collection = HintsCollection.initDefault(allocator), + .main = main, + .error_message_attributes = error_message_attributes, + .instruction_locations = instruction_locations, + .identifiers = identifiers, + .reference_manager = try reference_manager.getReferenceList(allocator), + }, + .constants = try Self.extractConstants(identifiers, allocator), + .builtins = builtins, + }; + } + /// Initializes a new `Program` instance. /// /// # Params: @@ -249,7 +290,7 @@ pub const Program = struct { // Try to insert the constant into the result map. try constants.put( kv.key_ptr.*, - Felt252.fromSignedInteger(kv.value_ptr.*.value orelse return ProgramError.ConstWithoutValue), + kv.value_ptr.*.valueFelt orelse return ProgramError.ConstWithoutValue, ); } } @@ -304,6 +345,55 @@ pub const Program = struct { return self.shared_program_data.instruction_locations.?.get(key); } + /// Retrieves an identifier from the program's shared data based on the provided key. + /// + /// This function looks up the identifier map within the shared program data using the provided key. + /// If the key is found, the corresponding `Identifier` instance is returned; otherwise, `null` is returned. + /// + /// # Params: + /// - `key`: A byte slice representing the key to retrieve the identifier. + /// + /// # Returns: + /// - An optional `Identifier` corresponding to the provided key, if found; otherwise, `null`. + pub fn getIdentifier(self: *Self, key: []const u8) ?Identifier { + return self.shared_program_data.identifiers.get(key); + } + + /// Retrieves an iterator for the identifiers stored in the program's shared data. + /// + /// This method returns an iterator for the `std.StringHashMap(Identifier)` containing + /// identifiers within the shared program data. + /// + /// # Returns: + /// - An iterator for the identifiers stored in the program's shared data. + pub fn iteratorIdentifier(self: *Self) std.StringHashMap(Identifier).Iterator { + return self.shared_program_data.identifiers.iterator(); + } + + /// Retrieves the length of the list of `MaybeRelocatable` items within the shared program data. + /// + /// This function returns the number of elements in the list of `MaybeRelocatable` items, providing + /// information about the amount of shared program data present in the program instance. + /// + /// # Params: + /// - `self`: A pointer to the `Program` instance. + /// + /// # Returns: + /// - The number of elements in the list of `MaybeRelocatable` items. + pub fn dataLen(self: *Self) usize { + return self.shared_program_data.data.items.len; + } + + /// Retrieves the number of built-ins in the program. + /// + /// This method returns the length of the list of built-in names stored in the program instance. + /// + /// # Returns: + /// - The number of built-ins in the program. + pub fn builtinsLen(self: *Self) usize { + return self.builtins.items.len; + } + /// Deinitializes the `Program` instance, freeing allocated memory. /// /// # Params: @@ -335,7 +425,7 @@ test "Program: extractConstants should extract the constants from identifiers" { "__main__.main.SIZEOF_LOCALS", .{ .type = "const", - .value = 0, + .valueFelt = Felt252.zero(), }, ); @@ -362,7 +452,7 @@ test "Program: extractConstants should extract the constants from identifiers us "starkware.cairo.common.alloc.alloc.SIZEOF_LOCALS", .{ .type = "const", - .value = 0, + .valueFelt = Felt252.zero(), }, ); @@ -371,7 +461,7 @@ test "Program: extractConstants should extract the constants from identifiers us "starkware.cairo.common.bitwise.ALL_ONES", .{ .type = "const", - .value = -106710729501573572985208420194530329073740042555888586719234, + .valueFelt = Felt252.fromInt(u256, 106710729501573572985208420194530329073740042555888586719234).neg(), }, ); @@ -380,7 +470,7 @@ test "Program: extractConstants should extract the constants from identifiers us "starkware.cairo.common.cairo_keccak.keccak.KECCAK_CAPACITY_IN_WORDS", .{ .type = "const", - .value = 8, + .valueFelt = Felt252.fromInt(u8, 8), }, ); @@ -389,7 +479,7 @@ test "Program: extractConstants should extract the constants from identifiers us "starkware.cairo.common.cairo_keccak.keccak.KECCAK_FULL_RATE_IN_BYTES", .{ .type = "const", - .value = 136, + .valueFelt = Felt252.fromInt(u8, 136), }, ); @@ -398,7 +488,7 @@ test "Program: extractConstants should extract the constants from identifiers us "starkware.cairo.common.cairo_keccak.keccak.KECCAK_FULL_RATE_IN_WORDS", .{ .type = "const", - .value = 17, + .valueFelt = Felt252.fromInt(u8, 17), }, ); @@ -407,7 +497,7 @@ test "Program: extractConstants should extract the constants from identifiers us "starkware.cairo.common.cairo_keccak.keccak.KECCAK_STATE_SIZE_FELTS", .{ .type = "const", - .value = 25, + .valueFelt = Felt252.fromInt(u8, 25), }, ); @@ -430,32 +520,447 @@ test "Program: extractConstants should extract the constants from identifiers us // Check if the extracted constant values match the expected values. try expectEqual( - Felt252.zero(), - constants.get("starkware.cairo.common.alloc.alloc.SIZEOF_LOCALS").?, + @as(u256, 0), + constants.get("starkware.cairo.common.alloc.alloc.SIZEOF_LOCALS").?.toInteger(), ); try expectEqual( - Felt252.fromSignedInteger(-106710729501573572985208420194530329073740042555888586719234), + Felt252.fromInt(u256, 106710729501573572985208420194530329073740042555888586719234).neg(), constants.get("starkware.cairo.common.bitwise.ALL_ONES").?, ); try expectEqual( - Felt252.fromInt(u8, 8), - constants.get("starkware.cairo.common.cairo_keccak.keccak.KECCAK_CAPACITY_IN_WORDS").?, + @as(u256, 8), + constants.get("starkware.cairo.common.cairo_keccak.keccak.KECCAK_CAPACITY_IN_WORDS").?.toInteger(), + ); + + try expectEqual( + @as(u256, 136), + constants.get("starkware.cairo.common.cairo_keccak.keccak.KECCAK_FULL_RATE_IN_BYTES").?.toInteger(), + ); + + try expectEqual( + @as(u256, 17), + constants.get("starkware.cairo.common.cairo_keccak.keccak.KECCAK_FULL_RATE_IN_WORDS").?.toInteger(), + ); + + try expectEqual( + @as(u256, 25), + constants.get("starkware.cairo.common.cairo_keccak.keccak.KECCAK_STATE_SIZE_FELTS").?.toInteger(), + ); +} + +test "Program: init function should init a basic program" { + // Initialize the reference manager, builtins, hints, identifiers, and error message attributes. + const reference_manager = ReferenceManager.init(std.testing.allocator); + const builtins = std.ArrayList(BuiltinName).init(std.testing.allocator); + const hints = std.AutoHashMap(usize, std.ArrayList(HintParams)).init(std.testing.allocator); + const identifiers = std.StringHashMap(Identifier).init(std.testing.allocator); + const error_message_attributes = std.ArrayList(Attribute).init(std.testing.allocator); + + // Initialize a list of MaybeRelocatable items. + 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)); + + // Initialize a Program instance using the init function. + var program = try Program.init( + std.testing.allocator, + builtins, + data, + null, + hints, + reference_manager, + identifiers, + error_message_attributes, + null, + ); + + // Defer the deinitialization of the program to free allocated memory after the test case. + defer program.deinit(std.testing.allocator); + + // Assertions to validate the initialized program state. + try expectEqual(@as(usize, 0), program.builtins.items.len); + try expectEqual(@as(usize, 0), program.constants.count()); + try expectEqualSlices(MaybeRelocatable, data.items, program.shared_program_data.data.items); + try expectEqual(@as(?usize, null), program.shared_program_data.main); + try expectEqualDeep(identifiers, program.shared_program_data.identifiers); + try expectEqual( + @as(usize, 0), + program.shared_program_data.hints_collection.hints.items.len, + ); + try expectEqual( + @as(usize, 0), + program.shared_program_data.hints_collection.hints_ranges.count(), + ); +} + +test "Program: init function should init a basic program (data length function)" { + // Initialize the reference manager, builtins, hints, identifiers, and error message attributes. + const reference_manager = ReferenceManager.init(std.testing.allocator); + const builtins = std.ArrayList(BuiltinName).init(std.testing.allocator); + const hints = std.AutoHashMap(usize, std.ArrayList(HintParams)).init(std.testing.allocator); + const identifiers = std.StringHashMap(Identifier).init(std.testing.allocator); + const error_message_attributes = std.ArrayList(Attribute).init(std.testing.allocator); + + // Initialize a list of MaybeRelocatable items. + 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)); + + // Initialize a Program instance using the init function. + var program = try Program.init( + std.testing.allocator, + builtins, + data, + null, // Main entry point (null for this test case). + hints, + reference_manager, + identifiers, + error_message_attributes, + null, // Instruction locations (null for this test case). + ); + + // Defer the deinitialization of the program to free allocated memory after the test case. + defer program.deinit(std.testing.allocator); + + // Ensure that the `dataLen` function returns the expected length of the shared program data. + try expectEqual(@as(usize, 6), program.dataLen()); +} + +test "Program: init function should init a program with identifiers" { + // Initialize the reference manager, builtins, hints, and error message attributes. + const reference_manager = ReferenceManager.init(std.testing.allocator); + const builtins = std.ArrayList(BuiltinName).init(std.testing.allocator); + const hints = std.AutoHashMap(usize, std.ArrayList(HintParams)).init(std.testing.allocator); + const error_message_attributes = std.ArrayList(Attribute).init(std.testing.allocator); + + // Initialize a list of MaybeRelocatable items. + 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)); + + // Initialize a StringHashMap for identifiers. + var identifiers = std.StringHashMap(Identifier).init(std.testing.allocator); + + // Add identifiers to the StringHashMap. + try identifiers.put( + "__main__.main", + .{ + .pc = 0, + .type = "function", + }, + ); + + try identifiers.put( + "__main__.main.SIZEOF_LOCALS", + .{ + .type = "const", + .valueFelt = Felt252.zero(), + }, + ); + + // Initialize a Program instance using the init function. + var program = try Program.init( + std.testing.allocator, + builtins, + data, + null, // Main entry point (null for this test case). + hints, + reference_manager, + identifiers, + error_message_attributes, + null, // Instruction locations (null for this test case). + ); + + // Defer the deinitialization of the program to free allocated memory after the test case. + defer program.deinit(std.testing.allocator); + + // Assertions to validate the initialized program state. + try expectEqual(@as(usize, 0), program.builtins.items.len); + try expectEqualSlices(MaybeRelocatable, data.items, program.shared_program_data.data.items); + try expectEqual(@as(?usize, null), program.shared_program_data.main); + try expectEqualDeep(identifiers, program.shared_program_data.identifiers); + try expectEqual( + @as(usize, 0), + program.shared_program_data.hints_collection.hints.items.len, + ); + try expectEqual( + @as(usize, 0), + program.shared_program_data.hints_collection.hints_ranges.count(), + ); + + // Additional assertions for programs with identifiers. + try expectEqual(@as(usize, 1), program.constants.count()); + try expectEqual(Felt252.zero(), program.constants.get("__main__.main.SIZEOF_LOCALS").?); +} + +test "Program: init function should init a program with identifiers (get identifiers)" { + // Initialize the reference manager, builtins, hints, and error message attributes. + const reference_manager = ReferenceManager.init(std.testing.allocator); + const builtins = std.ArrayList(BuiltinName).init(std.testing.allocator); + const hints = std.AutoHashMap(usize, std.ArrayList(HintParams)).init(std.testing.allocator); + const error_message_attributes = std.ArrayList(Attribute).init(std.testing.allocator); + + // Initialize a list of MaybeRelocatable items. + 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)); + + // Initialize a StringHashMap for identifiers. + var identifiers = std.StringHashMap(Identifier).init(std.testing.allocator); + + // Add identifiers to the StringHashMap. + try identifiers.put( + "__main__.main", + .{ + .pc = 0, + .type = "function", + }, + ); + + try identifiers.put( + "__main__.main.SIZEOF_LOCALS", + .{ + .type = "const", + .valueFelt = Felt252.zero(), + }, + ); + + // Initialize a Program instance using the init function. + var program = try Program.init( + std.testing.allocator, + builtins, + data, + null, // Main entry point (null for this test case). + hints, + reference_manager, + identifiers, + error_message_attributes, + null, // Instruction locations (null for this test case). + ); + + // Defer the deinitialization of the program to free allocated memory after the test case. + defer program.deinit(std.testing.allocator); + + // Test: Verify that identifiers added to the program match those in the initial StringHashMap. + + // Expect the identifier "__main__.main" to match between the program and the initial StringHashMap. + try expectEqual( + identifiers.get("__main__.main"), + program.getIdentifier("__main__.main"), ); + // Expect the identifier "__main__.main.SIZEOF_LOCALS" to match between the program and the initial StringHashMap. try expectEqual( - Felt252.fromInt(u8, 136), - constants.get("starkware.cairo.common.cairo_keccak.keccak.KECCAK_FULL_RATE_IN_BYTES").?, + identifiers.get("__main__.main.SIZEOF_LOCALS"), + program.getIdentifier("__main__.main.SIZEOF_LOCALS"), ); + // Expect the identifier "missing" to be null in both the program and the initial StringHashMap. try expectEqual( - Felt252.fromInt(u8, 17), - constants.get("starkware.cairo.common.cairo_keccak.keccak.KECCAK_FULL_RATE_IN_WORDS").?, + identifiers.get("missing"), + program.getIdentifier("missing"), ); +} +test "Program: iteratorIdentifier should return an iterator over identifiers" { + // Initialize the reference manager, builtins, hints, and error message attributes. + const reference_manager = ReferenceManager.init(std.testing.allocator); + const builtins = std.ArrayList(BuiltinName).init(std.testing.allocator); + const hints = std.AutoHashMap(usize, std.ArrayList(HintParams)).init(std.testing.allocator); + const error_message_attributes = std.ArrayList(Attribute).init(std.testing.allocator); + + // Initialize a list of MaybeRelocatable items. + 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)); + + // Initialize a StringHashMap for identifiers. + var identifiers = std.StringHashMap(Identifier).init(std.testing.allocator); + + // Add identifiers to the StringHashMap. + try identifiers.put( + "__main__.main", + .{ + .pc = 0, + .type = "function", + }, + ); + + try identifiers.put( + "__main__.main.SIZEOF_LOCALS", + .{ + .type = "const", + .valueFelt = Felt252.zero(), + }, + ); + + // Initialize a Program instance using the init function. + var program = try Program.init( + std.testing.allocator, + builtins, + data, + null, // Main entry point (null for this test case). + hints, + reference_manager, + identifiers, + error_message_attributes, + null, // Instruction locations (null for this test case). + ); + + // Defer the deinitialization of the program to free allocated memory after the test case. + defer program.deinit(std.testing.allocator); + + // Ensure that the iterator returned by program.iteratorIdentifier() is equal to identifiers.iterator(). + try expectEqualDeep(identifiers.iterator(), program.iteratorIdentifier()); + + // Initialize iterators for both the StringHashMap and the Program. + var program_it = program.iteratorIdentifier(); + var it = identifiers.iterator(); + + // Ensure that the size of the iterators match. + try expectEqual(@as(usize, 2), program_it.hm.size); + + // Iterate through both iterators simultaneously and ensure key-value pairs match. + while (it.next()) |kv| { + const program_kv = program_it.next().?; + try expectEqual(kv.key_ptr.*, program_kv.key_ptr.*); + try expectEqual(kv.value_ptr.*, program_kv.value_ptr.*); + } +} + +test "Program: init function should init a program with builtins" { + // Initialize the reference manager, hints, error message attributes, and identifiers. + const reference_manager = ReferenceManager.init(std.testing.allocator); + const hints = std.AutoHashMap(usize, std.ArrayList(HintParams)).init(std.testing.allocator); + const error_message_attributes = std.ArrayList(Attribute).init(std.testing.allocator); + const identifiers = std.StringHashMap(Identifier).init(std.testing.allocator); + + // Initialize a list of MaybeRelocatable items. + 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)); + + // Initialize a list of builtins. + var builtins = std.ArrayList(BuiltinName).init(std.testing.allocator); + try builtins.append(BuiltinName.range_check); + try builtins.append(BuiltinName.bitwise); + + // Initialize a Program instance using the init function. + var program = try Program.init( + std.testing.allocator, + builtins, + data, + null, // Main entry point (null for this test case). + hints, + reference_manager, + identifiers, + error_message_attributes, + null, // Instruction locations (null for this test case). + ); + + // Defer the deinitialization of the program to free allocated memory after the test case. + defer program.deinit(std.testing.allocator); + + // Assertions to validate the initialized program state. + try expectEqualSlices(BuiltinName, builtins.items, program.builtins.items); + try expectEqualSlices(MaybeRelocatable, data.items, program.shared_program_data.data.items); + try expectEqual(@as(?usize, null), program.shared_program_data.main); + try expectEqualDeep(identifiers, program.shared_program_data.identifiers); try expectEqual( - Felt252.fromInt(u8, 25), - constants.get("starkware.cairo.common.cairo_keccak.keccak.KECCAK_STATE_SIZE_FELTS").?, + @as(usize, 0), + program.shared_program_data.hints_collection.hints.items.len, + ); + try expectEqual( + @as(usize, 0), + program.shared_program_data.hints_collection.hints_ranges.count(), + ); + // Validate that the number of built-ins in the program matches the expected count. + try expectEqual( + @as(usize, 2), + program.builtinsLen(), + ); +} + +test "Program: init a new program with invalid identifiers should return an error" { + // Initialize the reference manager, builtins, hints, and error message attributes. + var reference_manager = ReferenceManager.init(std.testing.allocator); + defer reference_manager.deinit(); + var builtins = std.ArrayList(BuiltinName).init(std.testing.allocator); + defer builtins.deinit(); + var hints = std.AutoHashMap(usize, std.ArrayList(HintParams)).init(std.testing.allocator); + defer hints.deinit(); + var error_message_attributes = std.ArrayList(Attribute).init(std.testing.allocator); + defer error_message_attributes.deinit(); + + // Initialize a list of MaybeRelocatable items. + var data = std.ArrayList(MaybeRelocatable).init(std.testing.allocator); + defer data.deinit(); + + 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)); + + // Initialize a StringHashMap for identifiers. + var identifiers = std.StringHashMap(Identifier).init(std.testing.allocator); + defer identifiers.deinit(); + + // Add identifiers to the StringHashMap. + try identifiers.put( + "__main__.main", + .{ + .pc = 0, + .type = "function", + }, + ); + + try identifiers.put( + "__main__.main.SIZEOF_LOCALS", + .{ + .type = "const", + }, + ); + + try expectError( + ProgramError.ConstWithoutValue, + Program.init( + std.testing.allocator, + builtins, + data, + null, // Main entry point (null for this test case). + hints, + reference_manager, + identifiers, + error_message_attributes, + null, // Instruction locations (null for this test case). + ), ); } diff --git a/src/vm/types/programjson.zig b/src/vm/types/programjson.zig index d059afed..6bec90fe 100644 --- a/src/vm/types/programjson.zig +++ b/src/vm/types/programjson.zig @@ -10,6 +10,7 @@ const Register = @import("../instructions.zig").Register; const Program = @import("./program.zig").Program; const HintsCollection = @import("./program.zig").HintsCollection; const SharedProgramData = @import("./program.zig").SharedProgramData; +const HintReference = @import("../../hint_processor/hint_processor_def.zig").HintReference; const PRIME_STR = @import("../../math/fields/starknet.zig").PRIME_STR; /// Represents a singly linked list structure specialized for storing Instructions. @@ -179,6 +180,11 @@ pub const InstructionLocation = struct { /// /// This instruction translates to '[ap] = [ap - 1] * 2, ap++', /// setting 'ap' to the calculated value of 10. +/// +/// ## Warning: +/// +/// This type is valid for a program in Json format uniquely +/// (before deserialization of the value which is stored as a string here) pub const Reference = struct { /// Tracking data for the register associated with the reference. ap_tracking_data: ApTracking, @@ -188,6 +194,50 @@ pub const Reference = struct { value: []const u8, }; +/// Represents the structure defining a memory address. +/// +/// This structure includes offset values, a dereference flag, and the value type. +pub const ValueAddress = struct { + /// First offset value associated with the memory address. + offset1: OffsetValue, + /// Second offset value associated with the memory address. + offset2: OffsetValue, + /// Indicates whether the memory address is dereferenced. + dereference: bool, + /// Type of the value stored at the memory address. + value_type: []const u8, +}; + +/// Represents a reference to a memory address defined for a specific program location (pc). +/// +/// This structure defines a reference tied to a program counter (pc), holding a value +/// and tracking data for register (ap_tracking_data). +/// +/// It may have multiple definition sites (locations) and is associated with a code element responsible for its creation. +/// +/// For example, +/// +/// Defines a reference 'x' to the Ap register tied to the current instruction. +/// +/// [ap] = 5, ap++; +/// +/// As 'ap' incremented, the reference evaluates to (ap - 1) instead of 'ap'. +/// +/// [ap] = [x] * 2, ap++; +/// +/// This instruction translates to '[ap] = [ap - 1] * 2, ap++', +/// setting 'ap' to the calculated value of 10. +/// +/// Represents a reference after deserialization where `value` is a `ValueAddress` structure +pub const ReferenceProgram = struct { + /// Tracking data for the register associated with the reference. + ap_tracking_data: ApTracking, + /// Program counter (pc) tied to the reference (optional, defaults to null). + pc: ?usize, + /// Value/address of the reference. + value_address: ValueAddress, +}; + /// Represents a manager for references to memory addresses defined for specific program locations (pcs). /// /// This structure maintains a list of references (`references`) @@ -195,14 +245,40 @@ pub const ReferenceManager = struct { const Self = @This(); /// List of references managed by the `ReferenceManager`. - references: std.ArrayList(Reference), + references: std.ArrayList(ReferenceProgram), /// Initializes a new `ReferenceManager` instance. /// /// # Params: /// - `allocator`: The allocator used to initialize the instance. pub fn init(allocator: Allocator) Self { - return .{ .references = std.ArrayList(Reference).init(allocator) }; + return .{ .references = std.ArrayList(ReferenceProgram).init(allocator) }; + } + + pub fn getReferenceList(self: *const Self, allocator: Allocator) !std.ArrayList(HintReference) { + var result = std.ArrayList(HintReference).init(allocator); + errdefer result.deinit(); + + for (self.references.items) |ref| { + const ap_tracking_data = switch (ref.value_address.offset1) { + .reference => |r| if (r[0] == .AP) ref.ap_tracking_data else null, + else => switch (ref.value_address.offset2) { + .reference => |r| if (r[0] == .AP) ref.ap_tracking_data else null, + else => null, + }, + }; + try result.append( + .{ + .offset1 = ref.value_address.offset1, + .offset2 = ref.value_address.offset2, + .dereference = ref.value_address.dereference, + .ap_tracking_data = ap_tracking_data, + .cairo_type = ref.value_address.value_type, + }, + ); + } + + return result; } /// Deinitializes the `ReferenceManager`, freeing allocated memory. @@ -495,7 +571,13 @@ pub const ProgramJson = struct { try constants.put( key, // Convert the value to Felt252 and add it to the hashmap. - if (value.value) |v| Felt252.fromSignedInteger(v) else return ProgramError.ConstWithoutValue, + if (value.value) |v| + if (v < 0) + Felt252.fromInt(u256, @intCast(-v)).neg() + else + Felt252.fromInt(u256, @intCast(v)) + else + return ProgramError.ConstWithoutValue, ); } } @@ -556,7 +638,10 @@ pub const ProgramJson = struct { var val = value; // If the identifier has a numeric value, convert it to Felt252 and update the valueFelt field. if (val.value) |v| { - val.valueFelt = Felt252.fromSignedInteger(v); + val.valueFelt = if (v < 0) + Felt252.fromInt(u256, @intCast(-v)).neg() + else + Felt252.fromInt(u256, @intCast(v)); } // Put the identifier and its metadata into the hashmap. try identifiers.put(key, val); @@ -763,7 +848,7 @@ pub const ProgramJson = struct { if (max_hint_pc >= self.data.?.len) return ProgramError.InvalidHintPc; // Initialize a new HintsCollection. - var hints_collection = HintsCollection.init(allocator); + var hints_collection = HintsCollection.initDefault(allocator); errdefer hints_collection.deinit(); // Iterate over the hints map to populate the HintsCollection. @@ -793,7 +878,7 @@ pub const ProgramJson = struct { } // Return an empty HintsCollection if there are no valid hints. - return HintsCollection.init(allocator); + return HintsCollection.initDefault(allocator); } }; @@ -1551,7 +1636,10 @@ test "ProgramJson: parseProgramJson with constant deserialization" { .pc = null, .type = "const", .value = -3618502788666131213697322783095070105623107215331596699973092056135872020481, - .valueFelt = Felt252.fromSignedInteger(-3618502788666131213697322783095070105623107215331596699973092056135872020481), + .valueFelt = Felt252.fromInt( + u256, + 3618502788666131213697322783095070105623107215331596699973092056135872020481, + ).neg(), .full_name = null, .members = null, .cairo_type = null, @@ -1571,7 +1659,10 @@ test "ProgramJson: parseProgramJson with constant deserialization" { .pc = null, .type = "const", .value = -106710729501573572985208420194530329073740042555888586719234, - .valueFelt = Felt252.fromSignedInteger(-106710729501573572985208420194530329073740042555888586719234), + .valueFelt = Felt252.fromInt( + u256, + 106710729501573572985208420194530329073740042555888586719234, + ).neg(), .full_name = null, .members = null, .cairo_type = null,