-
Notifications
You must be signed in to change notification settings - Fork 35
feat(spectest): add SSZ roundtrip runner with comptime type dispatch #715
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
base: main
Are you sure you want to change the base?
Changes from all commits
273dad7
ee92ba4
ef8272f
0d4f970
ec3bb64
3acb9e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,23 @@ | ||
| pub const FixtureKind = enum { | ||
| state_transition, | ||
| fork_choice, | ||
| ssz, | ||
|
|
||
| pub fn runnerModule(self: FixtureKind) []const u8 { | ||
| return switch (self) { | ||
| .state_transition => "state_transition", | ||
| .fork_choice => "fork_choice", | ||
| .ssz => "ssz", | ||
| }; | ||
| } | ||
|
|
||
| pub fn handlerSubdir(self: FixtureKind) []const u8 { | ||
| return switch (self) { | ||
| .state_transition => "state_transition", | ||
| .fork_choice => "fc", | ||
| .ssz => "ssz", | ||
| }; | ||
| } | ||
| }; | ||
|
|
||
| pub const all = [_]FixtureKind{ .state_transition, .fork_choice }; | ||
| pub const all = [_]FixtureKind{ .state_transition, .fork_choice, .ssz }; |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -421,11 +421,9 @@ fn runStep( | |||||
| } else if (std.mem.eql(u8, step_type, "tick")) { | ||||||
| break :blk processTickStep(ctx, json_ctx.fixture_label, json_ctx.case_name, step_index, step_obj); | ||||||
| } else if (std.mem.eql(u8, step_type, "attestation")) { | ||||||
| std.debug.print( | ||||||
| "fixture {s} case {s}{f}: attestation steps unsupported\n", | ||||||
| .{ json_ctx.fixture_label, json_ctx.case_name, json_ctx.formatStep() }, | ||||||
| ); | ||||||
| return FixtureError.UnsupportedFixture; | ||||||
| break :blk processAttestationStep(ctx, json_ctx.fixture_label, json_ctx.case_name, step_index, step_obj); | ||||||
| } else if (std.mem.eql(u8, step_type, "gossipAggregatedAttestation")) { | ||||||
| break :blk processGossipAggregatedAttestationStep(ctx, json_ctx.fixture_label, json_ctx.case_name, step_index, step_obj); | ||||||
| } else { | ||||||
| std.debug.print( | ||||||
| "fixture {s} case {s}{f}: unknown stepType {s}\n", | ||||||
|
|
@@ -850,6 +848,311 @@ fn processTickStep( | |||||
| try advanceForkchoiceIntervals(ctx, target_intervals, false); | ||||||
| } | ||||||
|
|
||||||
| fn processAttestationStep( | ||||||
| ctx: *StepContext, | ||||||
| fixture_path: []const u8, | ||||||
| case_name: []const u8, | ||||||
| step_index: usize, | ||||||
| step_obj: std.json.ObjectMap, | ||||||
| ) !void { | ||||||
| const att_value = step_obj.get("attestation") orelse { | ||||||
| std.debug.print( | ||||||
| "fixture {s} case {s}{f}: attestation step missing attestation field\n", | ||||||
| .{ fixture_path, case_name, formatStep(step_index) }, | ||||||
| ); | ||||||
| return FixtureError.InvalidFixture; | ||||||
| }; | ||||||
| const att_obj = switch (att_value) { | ||||||
| .object => |map| map, | ||||||
| else => { | ||||||
| std.debug.print( | ||||||
| "fixture {s} case {s}{f}: attestation must be object\n", | ||||||
| .{ fixture_path, case_name, formatStep(step_index) }, | ||||||
| ); | ||||||
| return FixtureError.InvalidFixture; | ||||||
| }, | ||||||
| }; | ||||||
|
|
||||||
| const validator_id = try expectU64Field(att_obj, &.{ "validatorId", "validator_id" }, fixture_path, case_name, step_index, "attestation.validatorId"); | ||||||
| const data_obj = try expectObject(att_obj, "data", fixture_path, case_name, step_index); | ||||||
| const attestation_data = try parseAttestationData(data_obj, fixture_path, case_name, step_index, "attestation.data"); | ||||||
|
|
||||||
| // Validate validator exists in the anchor state. | ||||||
| const num_validators = ctx.fork_choice.anchorState.validators.constSlice().len; | ||||||
| if (validator_id >= num_validators) { | ||||||
| return error.UnknownValidator; | ||||||
| } | ||||||
|
|
||||||
| // Validate attestation data (block existence, slot relationships, future slot). | ||||||
| try validateAttestationDataForGossip(ctx, attestation_data); | ||||||
|
|
||||||
| // Signature verification is not supported in this runner; detect fixture cases | ||||||
| // that expect a signature failure and return an error to match the expected outcome. | ||||||
| if (step_obj.get("expectedError")) |err_value| { | ||||||
| switch (err_value) { | ||||||
| .string => |err_str| { | ||||||
| if (std.mem.indexOf(u8, err_str, "ignature") != null) { | ||||||
| return error.SignatureVerificationNotSupported; | ||||||
| } | ||||||
| }, | ||||||
| else => {}, | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| const attestation = types.Attestation{ | ||||||
| .validator_id = validator_id, | ||||||
| .data = attestation_data, | ||||||
| }; | ||||||
|
|
||||||
| ctx.fork_choice.onAttestation(attestation, false) catch |err| { | ||||||
| return err; | ||||||
| }; | ||||||
|
|
||||||
| _ = try ctx.fork_choice.updateHead(); | ||||||
| } | ||||||
|
|
||||||
| fn processGossipAggregatedAttestationStep( | ||||||
| ctx: *StepContext, | ||||||
| fixture_path: []const u8, | ||||||
| case_name: []const u8, | ||||||
| step_index: usize, | ||||||
| step_obj: std.json.ObjectMap, | ||||||
| ) !void { | ||||||
| const att_value = step_obj.get("attestation") orelse { | ||||||
| std.debug.print( | ||||||
| "fixture {s} case {s}{f}: gossipAggregatedAttestation step missing attestation field\n", | ||||||
| .{ fixture_path, case_name, formatStep(step_index) }, | ||||||
| ); | ||||||
| return FixtureError.InvalidFixture; | ||||||
| }; | ||||||
| const att_obj = switch (att_value) { | ||||||
| .object => |map| map, | ||||||
| else => { | ||||||
| std.debug.print( | ||||||
| "fixture {s} case {s}{f}: attestation must be object\n", | ||||||
| .{ fixture_path, case_name, formatStep(step_index) }, | ||||||
| ); | ||||||
| return FixtureError.InvalidFixture; | ||||||
| }, | ||||||
| }; | ||||||
|
|
||||||
| const data_obj = try expectObject(att_obj, "data", fixture_path, case_name, step_index); | ||||||
| const attestation_data = try parseAttestationData(data_obj, fixture_path, case_name, step_index, "attestation.data"); | ||||||
|
|
||||||
| // Validate attestation data (block existence, slot relationships, future slot). | ||||||
| try validateAttestationDataForGossip(ctx, attestation_data); | ||||||
|
|
||||||
| // Parse proof to extract participants. | ||||||
| const proof_obj = try expectObject(att_obj, "proof", fixture_path, case_name, step_index); | ||||||
| const participants_value = proof_obj.get("participants") orelse { | ||||||
| std.debug.print( | ||||||
| "fixture {s} case {s}{f}: proof missing participants\n", | ||||||
| .{ fixture_path, case_name, formatStep(step_index) }, | ||||||
| ); | ||||||
| return FixtureError.InvalidFixture; | ||||||
| }; | ||||||
|
|
||||||
| // Parse participants aggregation bits. | ||||||
| var aggregation_bits = try parseAggregationBitsValue(ctx.allocator, participants_value, fixture_path, case_name, step_index, "proof.participants"); | ||||||
| errdefer aggregation_bits.deinit(); | ||||||
|
||||||
| errdefer aggregation_bits.deinit(); | |
| defer aggregation_bits.deinit(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file already imports
constants.zigand definesMAX_FUTURE_SLOT_TOLERANCE = 1, but the future-slot check uses a literal+ 1. Useconstants.MAX_FUTURE_SLOT_TOLERANCE(and consider@addWithOverflowifSlotcan reach its max) to keep the tolerance consistent and avoid duplicating the value.