Skip to content

Commit 4aa8799

Browse files
committed
test(stf): add unit tests for initiateValidatorExit
Tests cover: - No-op when validator already has exit epoch set - Sets exit and withdrawable epochs correctly (electra) - Multiple sequential exits produce valid exit epochs - Second call on same validator is a no-op (idempotent) 🤖 Generated with AI assistance
1 parent 1060251 commit 4aa8799

1 file changed

Lines changed: 128 additions & 0 deletions

File tree

src/state_transition/block/initiate_validator_exit.zig

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,131 @@ pub fn initiateValidatorExit(
6868
try std.math.add(u64, try validator.get("exit_epoch"), config.chain.MIN_VALIDATOR_WITHDRAWABILITY_DELAY),
6969
);
7070
}
71+
72+
// ─── Tests ───────────────────────────────────────────────────────────
73+
74+
const std_testing = std.testing;
75+
const TestCachedBeaconState = @import("../test_utils/root.zig").TestCachedBeaconState;
76+
const Node = @import("persistent_merkle_tree").Node;
77+
const preset = @import("preset").preset;
78+
79+
test "initiateValidatorExit - no-op if already exited" {
80+
const allocator = std_testing.allocator;
81+
const num_validators = 256;
82+
const pool_size = num_validators * 5;
83+
var pool = try Node.Pool.init(allocator, pool_size);
84+
defer pool.deinit();
85+
86+
var test_state = try TestCachedBeaconState.init(allocator, &pool, num_validators);
87+
defer test_state.deinit();
88+
89+
const state = test_state.cached_state.state.castToFork(.electra);
90+
const epoch_cache = test_state.cached_state.epoch_cache;
91+
92+
// Set validator 0's exit_epoch to something other than FAR_FUTURE_EPOCH
93+
var validators = try state.validators();
94+
var validator = try validators.get(0);
95+
try validator.set("exit_epoch", 10);
96+
try validator.set("withdrawable_epoch", 10 + test_state.config.chain.MIN_VALIDATOR_WITHDRAWABILITY_DELAY);
97+
98+
// Call initiateValidatorExit — should be a no-op
99+
try initiateValidatorExit(.electra, test_state.config, epoch_cache, state, validator);
100+
101+
// exit_epoch should remain unchanged
102+
try std_testing.expectEqual(@as(u64, 10), try validator.get("exit_epoch"));
103+
}
104+
105+
test "initiateValidatorExit - sets exit and withdrawable epochs" {
106+
const allocator = std_testing.allocator;
107+
const num_validators = 256;
108+
const pool_size = num_validators * 5;
109+
var pool = try Node.Pool.init(allocator, pool_size);
110+
defer pool.deinit();
111+
112+
var test_state = try TestCachedBeaconState.init(allocator, &pool, num_validators);
113+
defer test_state.deinit();
114+
115+
const state = test_state.cached_state.state.castToFork(.electra);
116+
const epoch_cache = test_state.cached_state.epoch_cache;
117+
118+
var validators = try state.validators();
119+
var validator = try validators.get(0);
120+
121+
// Validator should start with FAR_FUTURE_EPOCH
122+
try std_testing.expectEqual(FAR_FUTURE_EPOCH, try validator.get("exit_epoch"));
123+
124+
try initiateValidatorExit(.electra, test_state.config, epoch_cache, state, validator);
125+
126+
const exit_epoch = try validator.get("exit_epoch");
127+
try std_testing.expect(exit_epoch != FAR_FUTURE_EPOCH);
128+
try std_testing.expectEqual(
129+
exit_epoch + test_state.config.chain.MIN_VALIDATOR_WITHDRAWABILITY_DELAY,
130+
try validator.get("withdrawable_epoch"),
131+
);
132+
}
133+
134+
test "initiateValidatorExit - multiple exits" {
135+
const allocator = std_testing.allocator;
136+
const num_validators = 256;
137+
const pool_size = num_validators * 5;
138+
var pool = try Node.Pool.init(allocator, pool_size);
139+
defer pool.deinit();
140+
141+
var test_state = try TestCachedBeaconState.init(allocator, &pool, num_validators);
142+
defer test_state.deinit();
143+
144+
const state = test_state.cached_state.state.castToFork(.electra);
145+
const epoch_cache = test_state.cached_state.epoch_cache;
146+
147+
var validators = try state.validators();
148+
149+
// Exit multiple validators and verify each gets a valid exit epoch
150+
var v0 = try validators.get(0);
151+
var v1 = try validators.get(1);
152+
var v2 = try validators.get(2);
153+
154+
try initiateValidatorExit(.electra, test_state.config, epoch_cache, state, v0);
155+
try initiateValidatorExit(.electra, test_state.config, epoch_cache, state, v1);
156+
try initiateValidatorExit(.electra, test_state.config, epoch_cache, state, v2);
157+
158+
const exit0 = try v0.get("exit_epoch");
159+
const exit1 = try v1.get("exit_epoch");
160+
const exit2 = try v2.get("exit_epoch");
161+
162+
// All should have valid exit epochs
163+
try std_testing.expect(exit0 != FAR_FUTURE_EPOCH);
164+
try std_testing.expect(exit1 != FAR_FUTURE_EPOCH);
165+
try std_testing.expect(exit2 != FAR_FUTURE_EPOCH);
166+
167+
// All should have valid withdrawable epochs
168+
const delay = test_state.config.chain.MIN_VALIDATOR_WITHDRAWABILITY_DELAY;
169+
try std_testing.expectEqual(exit0 + delay, try v0.get("withdrawable_epoch"));
170+
try std_testing.expectEqual(exit1 + delay, try v1.get("withdrawable_epoch"));
171+
try std_testing.expectEqual(exit2 + delay, try v2.get("withdrawable_epoch"));
172+
}
173+
174+
test "initiateValidatorExit - second call is no-op" {
175+
const allocator = std_testing.allocator;
176+
const num_validators = 256;
177+
const pool_size = num_validators * 5;
178+
var pool = try Node.Pool.init(allocator, pool_size);
179+
defer pool.deinit();
180+
181+
var test_state = try TestCachedBeaconState.init(allocator, &pool, num_validators);
182+
defer test_state.deinit();
183+
184+
const state = test_state.cached_state.state.castToFork(.electra);
185+
const epoch_cache = test_state.cached_state.epoch_cache;
186+
187+
var validators = try state.validators();
188+
var validator = try validators.get(0);
189+
190+
// First call — should set exit epoch
191+
try initiateValidatorExit(.electra, test_state.config, epoch_cache, state, validator);
192+
const exit_epoch = try validator.get("exit_epoch");
193+
try std_testing.expect(exit_epoch != FAR_FUTURE_EPOCH);
194+
195+
// Second call — should be a no-op
196+
try initiateValidatorExit(.electra, test_state.config, epoch_cache, state, validator);
197+
try std_testing.expectEqual(exit_epoch, try validator.get("exit_epoch"));
198+
}

0 commit comments

Comments
 (0)