diff --git a/src/spec_tests/ssz_static/root.zig b/src/spec_tests/ssz_static/root.zig index b74f5ef..ec57b0b 100644 --- a/src/spec_tests/ssz_static/root.zig +++ b/src/spec_tests/ssz_static/root.zig @@ -9,10 +9,6 @@ const Yaml = @import("../../yaml/yaml.zig").Yaml; const gpa = testing.allocator; /// Loads and parses a YAML file into a Yaml object -/// Parameters: -/// file_path: Path to the YAML file to load -/// Returns: -/// Parsed Yaml object or error fn loadFromFile(file_path: []const u8) !Yaml { const file = try std.fs.cwd().openFile(file_path, .{}); defer file.close(); @@ -26,11 +22,69 @@ fn loadFromFile(file_path: []const u8) !Yaml { const Roots = struct { root: [32]u8, }; + +const StructMultiPhase = union { + Attestation: types.Attestation, + BeaconBlockBody: types.BeaconBlockBody, + BeaconState: types.BeaconState, + BlobIdentifier: types.BlobIdentifier, + BlobSidecar: types.BlobSidecar, + BLSToExecutionChange: types.BLSToExecutionChange, + ConsolidationRequest: types.ConsolidationRequest, + ContributionAndProof: types.ContributionAndProof, + DepositRequest: types.DepositRequest, + ExecutionPayload: types.ExecutionPayload, + ExecutionPayloadHeader: types.ExecutionPayloadHeader, + HistoricalSummary: types.HistoricalSummary, + LightClientBootstrap: types.LightClientBootstrap, + LightClientFinalityUpdate: types.LightClientFinalityUpdate, + LightClientHeader: types.LightClientHeader, + LightClientOptimisticUpdate: types.LightClientOptimisticUpdate, + LightClientUpdate: types.LightClientUpdate, + PendingBalanceDeposit: types.PendingBalanceDeposit, + PendingConsolidation: types.PendingConsolidation, + PendingPartialWithdrawal: types.PendingPartialWithdrawal, + PowBlock: types.PowBlock, + SignedBLSToExecutionChange: types.SignedBLSToExecutionChange, + SignedContributionAndProof: types.SignedContributionAndProof, + SignedVoluntaryExit: types.SignedVoluntaryExit, + SyncAggregate: types.SyncAggregate, + SyncCommittee: types.SyncCommittee, + SyncCommitteeContribution: types.SyncCommitteeContribution, + SyncCommitteeMessage: types.SyncCommitteeMessage, + Withdrawal: types.Withdrawal, + WithdrawalRequest: types.WithdrawalRequest, +}; + // test cases for all phases const CommonUnion = union { + // AggregateAndProof: types.AggregateAndProof, + // AttestationData: types.AttestationData, + // AttesterSlashing: types.AttesterSlashing, + // BeaconBlock: types.BeaconBlock, + // BeaconBlockHeader: types.BeaconBlockHeader, + Checkpoint: types.Checkpoint, + // Deposit: types.Deposit, + // DepositData: types.DepositData, + // DepositMessage: types.DepositMessage, + // Eth1Block: types.Eth1Block, + // Eth1Data: types.Eth1Data, Fork: types.Fork, + ForkData: types.ForkData, + // HistoricalBatch: types.HistoricalBatch, + // IndexedAttestation: types.IndexedAttestation, + // PendingAttestation: types.PendingAttestation, + // ProposerSlashing: types.ProposerSlashing, + // SignedBeaconBlock: types.SignedBeaconBlock, + // SignedBeaconBlockHeader: types.SignedBeaconBlockHeader, + // SigningData: types.SigningData, + SyncAggregatorSelectionData: types.SyncAggregatorSelectionData, // only exist in altair or later + // Validator: types.Validator, + VoluntaryExit: types.VoluntaryExit, }; +const forkDirs = [_][]const u8{ "phase0", "altair", "bellatrix", "capella", "deneb", "electra" }; + test "ssz static" { const testPath = "consensus-spec-tests/tests/mainnet"; const gpa1 = testing.allocator; @@ -38,30 +92,31 @@ test "ssz static" { inline for (fields) |field| { const fieldType = field.type; const fieldName = field.name; - const ssz_type_path = try std.fmt.allocPrint(gpa1, "{s}/phase0/ssz_static/{s}", .{ testPath, fieldName }); - - var dirs = try getLeafDirs(gpa1, ssz_type_path); - - // deinit the dirs array - defer { - for (dirs.items) |item| { - gpa1.free(item); + for (forkDirs) |fork| { + const ssz_type_path = try std.fmt.allocPrint(gpa1, "{s}/{s}/ssz_static/{s}", .{ testPath, fork, fieldName }); + + var dirs = getLeafDirs(gpa1, ssz_type_path) catch |err| { + if (err == error.FileNotFound) { + continue; + } + return err; + }; + // deinit the dirs array + defer { + for (dirs.items) |item| { + gpa1.free(item); + } + dirs.deinit(); } - dirs.deinit(); - } - for (dirs.items) |dir| { - try testSSZStatic(dir, fieldType); + for (dirs.items) |dir| { + try testSSZStatic(dir, fieldType); + } } } } /// Recursively finds all leaf directories (directories with no subdirectories) starting from the given path -/// Parameters: -/// allocator: Memory allocator for dynamic allocations -/// path: Starting directory path to search from -/// Returns: -/// ArrayList containing paths to all leaf directories fn getLeafDirs(allocator: std.mem.Allocator, path: []const u8) !std.ArrayList([]const u8) { var leafDirs = std.ArrayList([]const u8).init(allocator); // defer leafDirs.deinit(); @@ -101,12 +156,6 @@ fn getLeafDirs(allocator: std.mem.Allocator, path: []const u8) !std.ArrayList([] } /// Tests SSZ (Simple Serialize) static functionality by performing: -/// 1. YAML parsing -/// 2. Hash tree root verification -/// 3. SSZ encoding/decoding with snappy compression -/// Parameters: -/// path: Directory path containing test files -/// t: Type to test SSZ operations against fn testSSZStatic(path: []const u8, t: type) !void { // parse from yaml const valueFile = try std.fmt.allocPrint(testing.allocator, "{s}/value.yaml", .{path}); diff --git a/src/yaml/yaml.zig b/src/yaml/yaml.zig index ea2934c..ffcfa77 100644 --- a/src/yaml/yaml.zig +++ b/src/yaml/yaml.zig @@ -1,5 +1,3 @@ -//! The code bellow is essentially a port of https://github.com/kubkon/zig-yaml - const std = @import("std"); const assert = std.debug.assert; const math = std.math; @@ -29,6 +27,7 @@ pub const Map = std.StringArrayHashMap(Value); pub const Value = union(enum) { empty, int: i64, + uint: u64, float: f64, string: []const u8, list: List, @@ -39,6 +38,11 @@ pub const Value = union(enum) { return self.int; } + pub fn asUint(self: Value) !u64 { + if (self != .uint) return error.TypeMismatch; + return self.uint; + } + pub fn asFloat(self: Value) !f64 { if (self != .float) return error.TypeMismatch; return self.float; @@ -90,6 +94,7 @@ pub const Value = union(enum) { switch (self) { .empty => return, .int => |int| return writer.print("{}", .{int}), + .uint => |uint| return writer.print("{}", .{uint}), .float => |float| return writer.print("{d}", .{float}), .string => |string| return writer.print("{s}", .{string}), .list => |list| { @@ -205,8 +210,15 @@ pub const Value = union(enum) { const raw = tree.getRaw(node.start, node.end); try_int: { - const int = std.fmt.parseInt(i64, raw, 0) catch break :try_int; - return Value{ .int = int }; + if (std.fmt.parseUnsigned(u64, raw, 0)) |uint| { + if (uint > math.maxInt(i64)) { + return Value{ .uint = uint }; + } + return Value{ .int = @intCast(uint) }; + } else |_| { + const int = std.fmt.parseInt(i64, raw, 0) catch break :try_int; + return Value{ .int = int }; + } } try_float: { @@ -393,7 +405,13 @@ pub const Yaml = struct { fn parseValue(self: *Yaml, comptime T: type, value: Value) Error!T { return switch (@typeInfo(T)) { - .int => math.cast(T, try value.asInt()) orelse return error.Overflow, + .int => { + return switch (value) { + .int => math.cast(T, try value.asInt()) orelse return error.Overflow, + .uint => math.cast(T, try value.asUint()) orelse return error.Overflow, + else => return error.TypeMismatch, + }; + }, .float => if (value.asFloat()) |float| { return math.lossyCast(T, float); } else |_| {