Six new tests and repairs to two that were confounded. The suite goes
from 12 cases to 16.
Two existing tests would have stayed green with the checks they target
deleted, which is the worst kind of passing test:
- The "helper id out of range" case also passed new_id = 5 > n = 4,
which params_are_valid rejects several lines earlier. It now uses a
valid enrollment target (new_id = 4) and an id of 5, so the id range
check is the sole failing condition.
- Every call in the empty-set test used (n = 1, t = 2), which fails on
threshold > n_participants regardless of n_ids. It now uses
(n = 2, t = 2, new_id = 2) -- a valid tuple in every respect except
n_ids = 0.
New coverage, in the order the review ranked it:
- run_frost_enrollment_api_test: NULL for every ARG_NONNULL pointer on
all five entry points, plus an unusable pubkey object on the four that
take one (previously only params_hash was covered). Also asserts that
a shares_gen call rejected at an ARG_CHECK does NOT consume the seed,
since it never reaches the body -- the complement of the "a failed
call always consumes it" property the previous commit pinned.
- run_frost_enrollment_infinity_test: pubshare_derive's infinity
rejection, which nothing reached before. With u = 2 and new_id = 2 the
Lagrange coefficients are exactly -1 and 2, so P_0 = 2*P_1 makes the
interpolation vanish; the same two points at a different target
succeed, which is what distinguishes the infinity check from a
parameter rejection.
- run_frost_enrollment_max_size_test: full protocol runs at the largest
sizes the API admits -- enrollment at n = 127 with u = 127, and repair
in a full n = 128 group with u = 127. u cannot reach 128 in either
mode (enrollment needs n < 128, repair excludes the target from the
helper set), so these reach one entry below the fixed-size arrays'
bound, which is as far as a valid tuple goes. Everything before this
capped at n <= 7. Runs in 51 ms.
- run_frost_enrollment_no_side_effects_test: the C analogue of the
reference implementation's test_participant_not_in_dkg, which plan
§1.2 listed and the Phase 3 list dropped. Every existing
participant's secret share, public share and the group key are
byte-identical before and after an enrollment, and the new share
differs from all of them.
- The pubshare_derive test now also pins the OTHER end of the
polynomial. The public API cannot ask for x-coordinate 0 -- that is
identifier -1, and new_id is a uint32_t bounded by n_participants --
so the convention there is checked by running frost's own
derive_thresh_pubkey over the same loaded points and requiring it to
reproduce the group key. With the existing check at x = new_id, both
ends of the interpolation this module depends on are now fixed.
Test-structure repairs the review called out:
- The mismatch test's disagreement now enters where it would in reality,
at helper 0's round 1.1 call (which is made with new_id = 3 while
helper 1 uses 4), rather than by running a clean round and
overwriting the outputs afterwards.
- The oversized-helper-set test re-points a single dealt run at {0,1}
and then {0,1,2} through a named helper, instead of struct-copying a
~540 KB run and hand-editing u and ids[2] -- which would have broken
silently if deal()'s helper-selection rule changed.
- frost_enrollment_test_run instances in the mismatch test are now
static. That test needs four live at once, which was over 2 MB of
stack.
- The randomized test's `if (sub_ids[t-2] >= new_id) continue;` was
unreachable: the loop bound gives sub_ids[t-2] <= n-1 and new_id == n
in that branch. It is now the CHECK that states the invariant.
- The pubshare_derive test's `k` was reset and reused as both helper
counter and aligned-array index inside the same loop body.
Verification: 16/16 pass at -i=16, -i=200 and -i=1000; ./tests,
./noverify_tests and ./exhaustive_tests exit 0; the module is clean
under valgrind (0 errors from 0 contexts).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>