Skip to content

Commit 1060251

Browse files
lodekeeper-zclaude
andauthored
test(stf): add unit tests for processVoluntaryExit (#270)
Add 6 unit tests for `getVoluntaryExitValidity` covering all validation paths: 1. **valid** — normal voluntary exit with valid parameters 2. **inactive (out of bounds)** — validator index exceeds registry length 3. **inactive (not active)** — validator exited before current epoch 4. **already_exited** — validator with exit_epoch != FAR_FUTURE_EPOCH 5. **early_epoch** — exit specifies a future epoch 6. **short_time_active** — validator hasn't served SHARD_COMMITTEE_PERIOD All tests use `verify_signature=false` to skip BLS verification. Removes the stale `// TODO: unit test` comment. 🤖 Generated with AI assistance Co-authored-by: lodekeeper-z <lodekeeper-z@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 1bd431e commit 1060251

File tree

1 file changed

+175
-1
lines changed

1 file changed

+175
-1
lines changed

src/state_transition/block/process_voluntary_exit.zig

Lines changed: 175 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,178 @@ pub fn getVoluntaryExitValidity(
108108
return .valid;
109109
}
110110

111-
// TODO: unit test
111+
const std = @import("std");
112+
const TestCachedBeaconState = @import("../test_utils/root.zig").TestCachedBeaconState;
113+
const Node = @import("persistent_merkle_tree").Node;
114+
const preset = @import("preset").preset;
115+
116+
fn makeSignedVoluntaryExit(epoch: u64, validator_index: u64) SignedVoluntaryExit {
117+
return .{
118+
.message = .{
119+
.epoch = epoch,
120+
.validator_index = validator_index,
121+
},
122+
.signature = [_]u8{0} ** 96,
123+
};
124+
}
125+
126+
test "voluntary exit - valid" {
127+
const allocator = std.testing.allocator;
128+
const pool_size = 256 * 5;
129+
var pool = try Node.Pool.init(allocator, pool_size);
130+
defer pool.deinit();
131+
132+
var test_state = try TestCachedBeaconState.init(allocator, &pool, 256);
133+
defer test_state.deinit();
134+
135+
const current_epoch = test_state.cached_state.epoch_cache.epoch;
136+
const signed_exit = makeSignedVoluntaryExit(current_epoch, 0);
137+
138+
const result = try getVoluntaryExitValidity(
139+
.electra,
140+
test_state.config,
141+
test_state.cached_state.epoch_cache,
142+
test_state.cached_state.state.castToFork(.electra),
143+
&signed_exit,
144+
false,
145+
);
146+
try std.testing.expectEqual(.valid, result);
147+
}
148+
149+
test "voluntary exit - inactive validator (out of bounds index)" {
150+
const allocator = std.testing.allocator;
151+
const pool_size = 256 * 5;
152+
var pool = try Node.Pool.init(allocator, pool_size);
153+
defer pool.deinit();
154+
155+
var test_state = try TestCachedBeaconState.init(allocator, &pool, 256);
156+
defer test_state.deinit();
157+
158+
const current_epoch = test_state.cached_state.epoch_cache.epoch;
159+
const signed_exit = makeSignedVoluntaryExit(current_epoch, 9999);
160+
161+
const result = try getVoluntaryExitValidity(
162+
.electra,
163+
test_state.config,
164+
test_state.cached_state.epoch_cache,
165+
test_state.cached_state.state.castToFork(.electra),
166+
&signed_exit,
167+
false,
168+
);
169+
try std.testing.expectEqual(.inactive, result);
170+
}
171+
172+
test "voluntary exit - inactive validator (not active in current epoch)" {
173+
const allocator = std.testing.allocator;
174+
const pool_size = 256 * 5;
175+
var pool = try Node.Pool.init(allocator, pool_size);
176+
defer pool.deinit();
177+
178+
var test_state = try TestCachedBeaconState.init(allocator, &pool, 256);
179+
defer test_state.deinit();
180+
181+
const current_epoch = test_state.cached_state.epoch_cache.epoch;
182+
183+
// Set validator 0's exit_epoch to 0 so it's not active in current epoch
184+
var state = test_state.cached_state.state.castToFork(.electra);
185+
var validators = try state.validators();
186+
var validator = try validators.get(0);
187+
try validator.set("exit_epoch", 0);
188+
189+
const signed_exit = makeSignedVoluntaryExit(current_epoch, 0);
190+
191+
const result = try getVoluntaryExitValidity(
192+
.electra,
193+
test_state.config,
194+
test_state.cached_state.epoch_cache,
195+
state,
196+
&signed_exit,
197+
false,
198+
);
199+
try std.testing.expectEqual(.inactive, result);
200+
}
201+
202+
test "voluntary exit - already exited validator" {
203+
const allocator = std.testing.allocator;
204+
const pool_size = 256 * 5;
205+
var pool = try Node.Pool.init(allocator, pool_size);
206+
defer pool.deinit();
207+
208+
var test_state = try TestCachedBeaconState.init(allocator, &pool, 256);
209+
defer test_state.deinit();
210+
211+
const current_epoch = test_state.cached_state.epoch_cache.epoch;
212+
213+
// Set validator 0's exit_epoch to a non-FAR_FUTURE value but still active
214+
var state = test_state.cached_state.state.castToFork(.electra);
215+
var validators = try state.validators();
216+
var validator = try validators.get(0);
217+
try validator.set("exit_epoch", current_epoch + 100);
218+
219+
const signed_exit = makeSignedVoluntaryExit(current_epoch, 0);
220+
221+
const result = try getVoluntaryExitValidity(
222+
.electra,
223+
test_state.config,
224+
test_state.cached_state.epoch_cache,
225+
state,
226+
&signed_exit,
227+
false,
228+
);
229+
try std.testing.expectEqual(.already_exited, result);
230+
}
231+
232+
test "voluntary exit - early epoch" {
233+
const allocator = std.testing.allocator;
234+
const pool_size = 256 * 5;
235+
var pool = try Node.Pool.init(allocator, pool_size);
236+
defer pool.deinit();
237+
238+
var test_state = try TestCachedBeaconState.init(allocator, &pool, 256);
239+
defer test_state.deinit();
240+
241+
const current_epoch = test_state.cached_state.epoch_cache.epoch;
242+
243+
// Set voluntary exit epoch to a future epoch
244+
const signed_exit = makeSignedVoluntaryExit(current_epoch + 1, 0);
245+
246+
const result = try getVoluntaryExitValidity(
247+
.electra,
248+
test_state.config,
249+
test_state.cached_state.epoch_cache,
250+
test_state.cached_state.state.castToFork(.electra),
251+
&signed_exit,
252+
false,
253+
);
254+
try std.testing.expectEqual(.early_epoch, result);
255+
}
256+
257+
test "voluntary exit - short time active" {
258+
const allocator = std.testing.allocator;
259+
const pool_size = 256 * 5;
260+
var pool = try Node.Pool.init(allocator, pool_size);
261+
defer pool.deinit();
262+
263+
var test_state = try TestCachedBeaconState.init(allocator, &pool, 256);
264+
defer test_state.deinit();
265+
266+
const current_epoch = test_state.cached_state.epoch_cache.epoch;
267+
268+
// Set validator 0's activation_epoch so it hasn't been active long enough
269+
var state = test_state.cached_state.state.castToFork(.electra);
270+
var validators = try state.validators();
271+
var validator = try validators.get(0);
272+
try validator.set("activation_epoch", current_epoch);
273+
274+
const signed_exit = makeSignedVoluntaryExit(current_epoch, 0);
275+
276+
const result = try getVoluntaryExitValidity(
277+
.electra,
278+
test_state.config,
279+
test_state.cached_state.epoch_cache,
280+
state,
281+
&signed_exit,
282+
false,
283+
);
284+
try std.testing.expectEqual(.short_time_active, result);
285+
}

0 commit comments

Comments
 (0)