Skip to content

Commit d281f9b

Browse files
authored
test: add spec tests (#55)
* test: add spec tests * chore: remove spec_tests.zig from tree * chore: replace deprecated filter field
1 parent 4e64cb5 commit d281f9b

File tree

10 files changed

+820
-5
lines changed

10 files changed

+820
-5
lines changed

.github/workflows/ci.yml

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,38 @@ jobs:
3838
if: matrix.os == 'ubuntu-latest'
3939
run: zig fmt --check .
4040

41+
spec-tests:
42+
name: spec tests
43+
runs-on: ubuntu-latest
44+
steps:
45+
- uses: actions/checkout@v4
46+
with:
47+
submodules: recursive
48+
fetch-depth: 0
49+
50+
- uses: mlugg/setup-zig@v2
51+
with:
52+
version: ${{ env.ZIG_VERSION }}
53+
cache-key: ubuntu-latest-${{ env.ZIG_VERSION }}
54+
4155
- name: zig build
4256
run: zig build
4357

44-
- name: Test
45-
run: zig build test
58+
- name: Cache spec tests
59+
id: spec-tests
60+
uses: actions/cache@v4
61+
with:
62+
path: test/spec/spec_tests
63+
key: ${{ hashFiles('test/spec/version.txt') }}
64+
65+
- name: Download spec tests
66+
run: |
67+
zig build download_spec_tests
68+
69+
- name: Write spec tests
70+
run: |
71+
zig build write_spec_tests
4672
73+
- name: Run spec tests
74+
run: |
75+
zig build run_spec_tests

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
zig-out/
22
.zig-cache/
3-
bun/node_modules/
4-
bun/benchmark_data/
3+
test/spec/spec_tests
4+
test/spec/spec_tests.zig

build.zig

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,64 @@ pub fn build(b: *std.Build) !void {
3535
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
3636
const test_step = b.step("test", "Run unit tests");
3737
test_step.dependOn(&run_lib_unit_tests.step);
38+
39+
// download spec tests
40+
41+
const options_spec_test_options = b.addOptions();
42+
const option_spec_test_url = b.option([]const u8, "spec_test_url", "") orelse "https://github.com/ethereum/consensus-spec-tests";
43+
options_spec_test_options.addOption([]const u8, "spec_test_url", option_spec_test_url);
44+
const option_spec_test_version = b.option([]const u8, "spec_test_version", "") orelse "v1.5.0";
45+
options_spec_test_options.addOption([]const u8, "spec_test_version", option_spec_test_version);
46+
const option_spec_test_out_dir = b.option([]const u8, "spec_test_out_dir", "") orelse "test/spec/spec_tests";
47+
options_spec_test_options.addOption([]const u8, "spec_test_out_dir", option_spec_test_out_dir);
48+
const options_module_spec_test_options = options_spec_test_options.createModule();
49+
50+
const exe_download_spec_tests = b.addExecutable(.{
51+
.name = "download_spec_tests",
52+
.root_module = b.createModule(.{
53+
.root_source_file = b.path("test/spec/download_spec_tests.zig"),
54+
.target = target,
55+
.optimize = optimize,
56+
}),
57+
});
58+
exe_download_spec_tests.root_module.addImport("spec_test_options", options_module_spec_test_options);
59+
60+
const run_exe_download_spec_tests = b.addRunArtifact(exe_download_spec_tests);
61+
if (b.args) |args| run_exe_download_spec_tests.addArgs(args);
62+
const tls_run_exe_download_spec_tests = b.step("download_spec_tests", "Run the download_spec_tests executable");
63+
tls_run_exe_download_spec_tests.dependOn(&run_exe_download_spec_tests.step);
64+
65+
// write spec tests
66+
67+
const exe_write_spec_tests = b.addExecutable(.{
68+
.name = "write_spec_tests",
69+
.root_module = b.createModule(.{
70+
.root_source_file = b.path("test/spec/write_spec_tests.zig"),
71+
.target = target,
72+
.optimize = optimize,
73+
}),
74+
});
75+
exe_write_spec_tests.root_module.addImport("spec_test_options", options_module_spec_test_options);
76+
77+
const run_exe_write_spec_tests = b.addRunArtifact(exe_write_spec_tests);
78+
if (b.args) |args| run_exe_write_spec_tests.addArgs(args);
79+
const tls_run_exe_write_spec_tests = b.step("write_spec_tests", "Run the write_spec_tests executable");
80+
tls_run_exe_write_spec_tests.dependOn(&run_exe_write_spec_tests.step);
81+
82+
// run spec tests
83+
84+
const spec_tests = b.addTest(.{
85+
.root_source_file = b.path("test/spec/spec_tests.zig"),
86+
.target = target,
87+
.optimize = optimize,
88+
.filters = b.option([]const []const u8, "spec_test_filters", "Spec test filters") orelse &[_][]const u8{},
89+
});
90+
spec_tests.root_module.addImport("spec_test_options", options_module_spec_test_options);
91+
spec_tests.root_module.addImport("blst", blst_mod);
92+
spec_tests.root_module.addImport("yaml", b.dependency("yaml", .{}).module("yaml"));
93+
94+
const run_spec_tests = b.addRunArtifact(spec_tests);
95+
if (b.args) |args| run_spec_tests.addArgs(args);
96+
const tls_run_spec_tests = b.step("run_spec_tests", "Run the spec tests");
97+
tls_run_spec_tests.dependOn(&run_spec_tests.step);
3898
}

build.zig.zon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
.url = "git+https://github.com/ChainSafe/blst.git#4d5bbcc0dd06f80b76fb124420c899570a4dd1e4",
1313
.hash = "blst_zig-0.0.0-cnAxzogIAAB8nH6TSAGbUtZ6scv2F64aVlcuJabg5HWi",
1414
},
15+
.yaml = .{
16+
.url = "git+https://github.com/chainsafe/zig-yaml#e0e5962579e990a66c21424416c7ac092b20b772",
17+
.hash = "zig_yaml-0.1.0-C1161m2NAgDhth5OvjG1o1UNKcdo-XNfO82m10J1g4Cl",
18+
},
1519
},
1620
.paths = .{
1721
"build.zig",

src/Signature.zig

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,9 @@ pub fn fastAggregateVerify(
109109
msg: *const [32]u8,
110110
dst: []const u8,
111111
pks: []const PublicKey,
112+
pks_validate: bool,
112113
) BlstError!bool {
113-
const agg_pk = try AggregatePublicKey.aggregate(pks, false);
114+
const agg_pk = try AggregatePublicKey.aggregate(pks, pks_validate);
114115
const pk = agg_pk.toPublicKey();
115116

116117
return try self.aggregateVerify(
@@ -199,6 +200,11 @@ pub fn subgroupCheck(self: *const Self) bool {
199200
return c.blst_p2_affine_in_g2(&self.point);
200201
}
201202

203+
/// Check if the `Signature` is the point at infinity.
204+
pub fn isInfinity(self: *const Self) bool {
205+
return c.blst_p2_affine_is_inf(&self.point);
206+
}
207+
202208
/// Check if two signatures are equal.
203209
pub fn isEqual(self: *const Self, other: *const Self) bool {
204210
return c.blst_p2_affine_is_equal(&self.point, &other.point);

src/root.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub const PublicKey = @import("PublicKey.zig");
1313
pub const Signature = @import("Signature.zig");
1414
pub const AggregatePublicKey = @import("AggregatePublicKey.zig");
1515
pub const AggregateSignature = @import("AggregateSignature.zig");
16+
pub const BlstError = @import("error.zig").BlstError;
1617

1718
pub const verifyMultipleAggregateSignatures = @import("fast_verify.zig").verifyMultipleAggregateSignatures;
1819

test/spec/download_spec_tests.zig

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
const std = @import("std");
2+
const Allocator = std.mem.Allocator;
3+
4+
const spec_test_options = @import("spec_test_options");
5+
6+
pub fn main() !void {
7+
const allocator = std.heap.smp_allocator;
8+
const tests = &[_][]const u8{
9+
"general",
10+
};
11+
try write_version();
12+
for (tests) |test_name| {
13+
try download_and_extract_spec_test(
14+
allocator,
15+
spec_test_options.spec_test_url,
16+
spec_test_options.spec_test_version,
17+
test_name,
18+
spec_test_options.spec_test_out_dir,
19+
);
20+
}
21+
}
22+
23+
fn write_version() !void {
24+
const version_file = try std.fs.cwd().createFile("test/spec/version.txt", .{});
25+
defer version_file.close();
26+
27+
var writer = version_file.writer().any();
28+
try writer.print(
29+
\\// This file is generated by download_spec_tests.zig.
30+
\\// This file exists as a cache key for the spec tests in CI.
31+
\\// Do not commit changes by hand.
32+
\\
33+
\\{s}
34+
\\
35+
, .{spec_test_options.spec_test_version});
36+
}
37+
38+
fn download_and_extract_spec_test(
39+
allocator: Allocator,
40+
spec_test_url: []const u8,
41+
spec_test_version: []const u8,
42+
test_name: []const u8,
43+
out_dir: []const u8,
44+
) !void {
45+
try download_spec_test_archive(allocator, spec_test_url, spec_test_version, test_name, out_dir);
46+
try extract_spec_test_archive(allocator, spec_test_version, test_name, out_dir);
47+
}
48+
49+
fn download_spec_test_archive(
50+
allocator: Allocator,
51+
spec_test_url: []const u8,
52+
spec_test_version: []const u8,
53+
test_name: []const u8,
54+
out_dir: []const u8,
55+
) !void {
56+
const filename = try std.fmt.allocPrint(allocator, "{s}.tar.gz", .{test_name});
57+
defer allocator.free(filename);
58+
59+
const archive_dirname = try std.fs.path.join(allocator, &[_][]const u8{ out_dir, spec_test_version });
60+
defer allocator.free(archive_dirname);
61+
62+
std.fs.cwd().makePath(archive_dirname) catch {};
63+
const archive_dir = try std.fs.cwd().openDir(archive_dirname, .{});
64+
65+
const already_downloaded = archive_dir.openFile(filename, .{}) catch null;
66+
if (already_downloaded != null) {
67+
std.log.info("already downloaded {s}", .{filename});
68+
already_downloaded.?.close();
69+
return;
70+
}
71+
72+
var file = try archive_dir.createFile(filename, .{});
73+
defer file.close();
74+
75+
std.log.info("downloading spec test {s} {s}", .{ spec_test_version, test_name });
76+
77+
var client: std.http.Client = .{ .allocator = allocator };
78+
defer client.deinit();
79+
80+
const url = try std.fmt.allocPrint(
81+
allocator,
82+
"{s}/releases/download/{s}/{s}.tar.gz",
83+
.{
84+
spec_test_url,
85+
spec_test_version,
86+
test_name,
87+
},
88+
);
89+
defer allocator.free(url);
90+
91+
std.log.info("spec test url: {s}", .{url});
92+
93+
const uri = try std.Uri.parse(url);
94+
95+
var server_header_buffer: [16 * 1024]u8 = undefined;
96+
97+
var req = try client.open(.GET, uri, .{
98+
.server_header_buffer = &server_header_buffer,
99+
});
100+
defer req.deinit();
101+
102+
std.log.info("sending request", .{});
103+
try req.send();
104+
105+
try req.wait();
106+
107+
// write to disk
108+
109+
std.log.info("writing response to disk - {s}", .{filename});
110+
111+
var buf = try allocator.alloc(u8, 16 * 1024000);
112+
defer allocator.free(buf);
113+
var reader = req.reader();
114+
var bytes_count: usize = 0;
115+
while (true) {
116+
const read_bytes = try reader.readAll(buf);
117+
try file.writeAll(buf[0..read_bytes]);
118+
bytes_count += read_bytes;
119+
if (read_bytes != buf.len) {
120+
break;
121+
}
122+
}
123+
std.log.info("response written to disk - {d} bytes", .{bytes_count});
124+
}
125+
126+
fn extract_spec_test_archive(
127+
allocator: Allocator,
128+
spec_test_version: []const u8,
129+
test_name: []const u8,
130+
out_dir: []const u8,
131+
) !void {
132+
const archive_dirname = try std.fs.path.join(allocator, &[_][]const u8{ out_dir, spec_test_version });
133+
defer allocator.free(archive_dirname);
134+
135+
const filename = try std.fmt.allocPrint(allocator, "{s}.tar.gz", .{test_name});
136+
defer allocator.free(filename);
137+
138+
const archive_dir = try std.fs.cwd().openDir(archive_dirname, .{});
139+
140+
const already_extracted = archive_dir.openDir(test_name, .{}) catch null;
141+
if (already_extracted != null) {
142+
std.log.info("already extracted {s}", .{filename});
143+
return;
144+
}
145+
146+
const archive_file = try archive_dir.openFile(filename, .{});
147+
defer archive_file.close();
148+
149+
archive_dir.makeDir(test_name) catch {};
150+
const extracted_out = try archive_dir.openDir(test_name, .{});
151+
152+
const file_reader = archive_file.reader();
153+
154+
var decompressor = std.compress.gzip.decompressor(file_reader);
155+
156+
const gzip_reader = decompressor.reader();
157+
158+
std.log.info("extracting {s}", .{filename});
159+
160+
try std.tar.pipeToFileSystem(extracted_out, gzip_reader, .{});
161+
std.log.info("extract {s} complete", .{filename});
162+
}

0 commit comments

Comments
 (0)