Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ssz test for more types #14

Merged
merged 3 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 76 additions & 27 deletions src/spec_tests/ssz_static/root.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -26,42 +22,101 @@ 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;
const fields = @typeInfo(CommonUnion).@"union".fields;
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();
Expand Down Expand Up @@ -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});
Expand Down
28 changes: 23 additions & 5 deletions src/yaml/yaml.zig
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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| {
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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 |_| {
Expand Down
Loading