-
Notifications
You must be signed in to change notification settings - Fork 35
pre-generated test keys for faster CI #587
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
Changes from all commits
01939c1
b049f8c
b9f87d0
bfa13c0
9c66581
45a719d
4494bdb
1fedb8a
50b2aea
0f3b10a
4a8cf72
dbbdb84
1b93bff
be94c00
df98fab
4513158
892443e
628a6b6
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 |
|---|---|---|
|
|
@@ -18,6 +18,8 @@ jobs: | |
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| submodules: recursive | ||
|
|
||
| - name: Set up Zig | ||
| uses: mlugg/[email protected] | ||
|
|
@@ -82,6 +84,8 @@ jobs: | |
| os: [ubuntu-latest, macos-latest] | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| submodules: recursive | ||
|
|
||
| - name: Free disk space (Ubuntu) | ||
| if: runner.os == 'Linux' | ||
|
|
@@ -142,6 +146,8 @@ jobs: | |
| os: [ubuntu-latest, macos-latest] | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| submodules: recursive | ||
|
|
||
| - name: Free disk space (Ubuntu) | ||
| if: runner.os == 'Linux' | ||
|
|
@@ -282,6 +288,8 @@ jobs: | |
| os: [ubuntu-latest, macos-latest] | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| submodules: recursive | ||
|
|
||
| - name: Free disk space (Ubuntu) | ||
| if: runner.os == 'Linux' | ||
|
|
@@ -327,6 +335,7 @@ jobs: | |
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| submodules: recursive | ||
| fetch-depth: 0 # Fetch full history to get git commit info | ||
|
|
||
| - name: Free disk space (Ubuntu) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -55,30 +55,39 @@ fn getOrCreateCachedKeyPair( | |||||
| pub const KeyManager = struct { | ||||||
| keys: std.AutoHashMap(usize, xmss.KeyPair), | ||||||
| allocator: Allocator, | ||||||
| owns_keypairs: bool, | ||||||
| /// Tracks which keypairs are owned (allocated by us) vs borrowed (cached). | ||||||
| owned_keys: std.AutoHashMap(usize, void), | ||||||
|
|
||||||
| const Self = @This(); | ||||||
|
|
||||||
| pub fn init(allocator: Allocator) Self { | ||||||
| return Self{ | ||||||
| .keys = std.AutoHashMap(usize, xmss.KeyPair).init(allocator), | ||||||
| .allocator = allocator, | ||||||
| .owns_keypairs = true, | ||||||
| .owned_keys = std.AutoHashMap(usize, void).init(allocator), | ||||||
| }; | ||||||
| } | ||||||
|
|
||||||
| pub fn deinit(self: *Self) void { | ||||||
| if (self.owns_keypairs) { | ||||||
| var it = self.keys.iterator(); | ||||||
| while (it.next()) |entry| { | ||||||
| var it = self.keys.iterator(); | ||||||
| while (it.next()) |entry| { | ||||||
| if (self.owned_keys.contains(entry.key_ptr.*)) { | ||||||
| entry.value_ptr.deinit(); | ||||||
| } | ||||||
| } | ||||||
| self.keys.deinit(); | ||||||
| self.owned_keys.deinit(); | ||||||
| } | ||||||
|
|
||||||
| /// Add an owned keypair that will be freed on deinit. | ||||||
| pub fn addKeypair(self: *Self, validator_id: usize, keypair: xmss.KeyPair) !void { | ||||||
| try self.keys.put(validator_id, keypair); | ||||||
| try self.owned_keys.put(validator_id, {}); | ||||||
| } | ||||||
|
|
||||||
| /// Add a cached/borrowed keypair that will NOT be freed on deinit. | ||||||
| pub fn addCachedKeypair(self: *Self, validator_id: usize, keypair: xmss.KeyPair) !void { | ||||||
| try self.keys.put(validator_id, keypair); | ||||||
| } | ||||||
|
|
||||||
| pub fn loadFromKeypairDir(_: *Self, _: []const u8) !void { | ||||||
|
|
@@ -162,24 +171,105 @@ pub const KeyManager = struct { | |||||
| } | ||||||
| }; | ||||||
|
|
||||||
| /// Maximum size of a serialized XMSS private key (20MB). | ||||||
| const MAX_SK_SIZE = 1024 * 1024 * 20; | ||||||
|
|
||||||
| /// Maximum size of a serialized XMSS public key (256 bytes). | ||||||
| const MAX_PK_SIZE = 256; | ||||||
|
|
||||||
| /// Number of pre-generated test keys available in the test-keys submodule. | ||||||
| const NUM_PREGENERATED_KEYS: usize = 32; | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are PROD_KEYS so will be a little misleading |
||||||
|
|
||||||
| const build_options = @import("build_options"); | ||||||
|
|
||||||
| /// Find the test-keys directory using the repo root path injected by build.zig. | ||||||
| fn findTestKeysDir() ?[]const u8 { | ||||||
| const keys_path = build_options.test_keys_path; | ||||||
| if (keys_path.len == 0) return null; | ||||||
|
|
||||||
| // Verify it actually exists at runtime | ||||||
| if (std.fs.cwd().openDir(keys_path, .{})) |dir| { | ||||||
| var d = dir; | ||||||
| d.close(); | ||||||
| return keys_path; | ||||||
| } else |_| {} | ||||||
|
|
||||||
| return null; | ||||||
| } | ||||||
|
|
||||||
| /// Load a single pre-generated key pair from SSZ files on disk. | ||||||
| fn loadPreGeneratedKey( | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is just test key or for a normal key as well? if normal key can this also be used in cli/node.zig so as to have DRY for loading keys
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. merging this and creating a separate issue for this that zclawz can tackle |
||||||
| allocator: Allocator, | ||||||
| keys_dir: []const u8, | ||||||
| index: usize, | ||||||
| ) !xmss.KeyPair { | ||||||
| // Build file paths | ||||||
| var sk_path_buf: [512]u8 = undefined; | ||||||
| const sk_path = std.fmt.bufPrint(&sk_path_buf, "{s}/validator_{d}_sk.ssz", .{ keys_dir, index }) catch unreachable; | ||||||
|
|
||||||
| var pk_path_buf: [512]u8 = undefined; | ||||||
| const pk_path = std.fmt.bufPrint(&pk_path_buf, "{s}/validator_{d}_pk.ssz", .{ keys_dir, index }) catch unreachable; | ||||||
|
|
||||||
| // Read private key | ||||||
| var sk_file = try std.fs.cwd().openFile(sk_path, .{}); | ||||||
| defer sk_file.close(); | ||||||
| const sk_data = try sk_file.readToEndAlloc(allocator, MAX_SK_SIZE); | ||||||
| defer allocator.free(sk_data); | ||||||
|
|
||||||
| // Read public key | ||||||
| var pk_file = try std.fs.cwd().openFile(pk_path, .{}); | ||||||
| defer pk_file.close(); | ||||||
| const pk_data = try pk_file.readToEndAlloc(allocator, MAX_PK_SIZE); | ||||||
| defer allocator.free(pk_data); | ||||||
|
|
||||||
| // Reconstruct keypair from SSZ | ||||||
| return xmss.KeyPair.fromSsz(allocator, sk_data, pk_data); | ||||||
| } | ||||||
|
|
||||||
| pub fn getTestKeyManager( | ||||||
| allocator: Allocator, | ||||||
| num_validators: usize, | ||||||
| max_slot: usize, | ||||||
| ) !KeyManager { | ||||||
| var key_manager = KeyManager.init(allocator); | ||||||
| key_manager.owns_keypairs = false; | ||||||
| errdefer key_manager.deinit(); | ||||||
|
|
||||||
| var num_active_epochs = max_slot + 1; | ||||||
| // to reuse cached keypairs, gen for 10 since most tests ask for < 10 max slot including | ||||||
| // building mock chain for tests. otherwise getOrCreateCachedKeyPair might cleanup previous | ||||||
| // key generated for smaller life time | ||||||
| if (num_active_epochs < 10) num_active_epochs = 10; | ||||||
| // Determine how many keys we can load from pre-generated files | ||||||
| const keys_dir = findTestKeysDir(); | ||||||
| const num_preloaded = if (keys_dir != null) | ||||||
| @min(num_validators, NUM_PREGENERATED_KEYS) | ||||||
| else | ||||||
| 0; | ||||||
|
|
||||||
| // Load pre-generated keys (fast path: near-instant from SSZ files) | ||||||
| var actually_loaded: usize = 0; | ||||||
| if (keys_dir) |dir| { | ||||||
| for (0..num_preloaded) |i| { | ||||||
| const keypair = loadPreGeneratedKey(allocator, dir, i) catch |err| { | ||||||
| std.debug.print("Failed to load pre-generated key {d}: {}\n", .{ i, err }); | ||||||
| break; | ||||||
| }; | ||||||
| key_manager.addKeypair(i, keypair) catch |err| { | ||||||
| std.debug.print("Failed to add pre-generated key {d}: {}\n", .{ i, err }); | ||||||
| break; | ||||||
| }; | ||||||
| actually_loaded += 1; | ||||||
| } | ||||||
| std.debug.print("Loaded {d} pre-generated test keys from {s}\n", .{ actually_loaded, dir }); | ||||||
| } else { | ||||||
| std.debug.print("Pre-generated keys not found, generating all keys at runtime\n", .{}); | ||||||
| } | ||||||
|
|
||||||
| // Generate remaining keys at runtime (for validators beyond the loaded set) | ||||||
| if (num_validators > actually_loaded) { | ||||||
| var num_active_epochs = max_slot + 1; | ||||||
| if (num_active_epochs < 10) num_active_epochs = 10; | ||||||
|
|
||||||
| for (0..num_validators) |i| { | ||||||
| const keypair = try getOrCreateCachedKeyPair(i, num_active_epochs); | ||||||
| try key_manager.addKeypair(i, keypair); | ||||||
| for (actually_loaded..num_validators) |i| { | ||||||
| const keypair = try getOrCreateCachedKeyPair(i, num_active_epochs); | ||||||
| try key_manager.addCachedKeypair(i, keypair); | ||||||
| } | ||||||
| std.debug.print("Generated {d} additional keys at runtime\n", .{num_validators - actually_loaded}); | ||||||
| } | ||||||
|
|
||||||
| return key_manager; | ||||||
|
|
||||||
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.
are these values for the testkey config or real keys with 2^32 lifttime?
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.
We still don't support TEST_KEYS, #508 is still pending