Skip to content

Commit 2810c20

Browse files
tcoratgerclaude
andauthored
docs(testing): align aggregation-bits vectors with doc-writer conventions (#919)
* docs(testing): align aggregation-bits vectors with doc-writer conventions Follow-up to #899. Brings the aggregation-bits bounds vectors in line with the consensus test-vector documentation standard: - Drop all inline body comments from the test vectors; the Given/When/Then docstring is the single source of truth. - Strip the "a client that..." narration from every Then section so it states only the asserted outcome (rejection reason or post-state). - Move the bitfield layout and SSZ-encoding rationale into Given as atomic facts, and note the bitfield-limit vs registry distinction. - Tighten the aggregation_bits field and resolver docstrings to terse one-sentence-per-line phrasing. - Note in process_attestations that signature verification normally rejects an out-of-range bit first, so this guards the unsigned path. - Make is_final_block keyword-only on the block builder. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(testing): use bullets for the aggregation_bits field docstring Per the doc-writer rules, render the field's three behaviors as bullet points instead of prose, and fix the summary line so it describes the default (derived) case rather than only the verbatim override. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3e90d00 commit 2810c20

4 files changed

Lines changed: 18 additions & 34 deletions

File tree

packages/testing/src/consensus_testing/test_fixtures/state_transition.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ def _build_block_from_spec(
199199
block_spec: BlockSpec,
200200
state: State,
201201
block_registry: dict[str, Block],
202+
*,
202203
is_final_block: bool,
203204
) -> tuple[Block, State | None]:
204205
"""

packages/testing/src/consensus_testing/test_types/attestation_specs.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -161,21 +161,15 @@ class AggregatedAttestationSpec(AttestationSpec):
161161

162162
aggregation_bits: AggregationBits | None = None
163163
"""
164-
Raw aggregation bits placed into the block body verbatim.
164+
Raw aggregation bits for the block body, overriding index derivation.
165165
166-
When None (default), the bits are derived from the validator indices,
167-
producing the tightest bitfield that covers the highest set index.
168-
Set this to author bit patterns the derivation cannot express:
169-
a zero-length bitfield, all-false bits, or trailing padding past the
170-
validator registry.
171-
172-
Only honored by the state transition format's forced-attestation
173-
path, which bypasses signing. Signed paths derive their bits from
174-
the validator indices so proofs match the claimed participants.
166+
- When unset, bits are derived from the validator indices.
167+
- When set, the bits are used verbatim, even when zero-length or padded.
168+
- Only the unsigned forced-attestation path honors the override.
175169
"""
176170

177171
def resolve_aggregation_bits(self) -> AggregationBits:
178-
"""Return the explicit bits override, or bits derived from the validator indices."""
172+
"""Return the bit override when present, else bits derived from the validator indices."""
179173
if self.aggregation_bits is not None:
180174
return self.aggregation_bits
181175
return AggregationBits.from_indices(self.validator_indices)

src/lean_spec/spec/forks/lstar/state_transition.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,9 @@ def process_attestations(
456456
# A vote from a nonexistent validator has no flag to set,
457457
# so the whole block is invalid.
458458
# Trailing unset bits beyond the registry are harmless padding.
459+
#
460+
# Signature verification normally rejects such a bit first.
461+
# This guards the unsigned path, which has no signature stage.
459462
for validator_index in voting_validator_indices:
460463
if not validator_index.is_within_registry(Uint64(len(state.validators))):
461464
raise SpecRejectionError(

tests/consensus/lstar/state_transition/test_aggregation_bits.py

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ def test_aggregation_bit_beyond_validator_registry_rejects_block(
2727
- 4 validators (indices 0-3); the vote tally has one flag per validator.
2828
- the chain:
2929
genesis -> block_1(1) -> block(2)
30-
- block(2) carries an attestation for block_1 whose aggregation bits
31-
name V0 and V1 plus nonexistent validator 4.
30+
- block(2) carries an attestation for block_1 naming V0, V1, and V4.
31+
- index 4 is within the bitfield limit but one past the registry.
3232
3333
When
3434
----
@@ -37,9 +37,6 @@ def test_aggregation_bit_beyond_validator_registry_rejects_block(
3737
Then
3838
----
3939
- the block is rejected with VALIDATOR_INDEX_OUT_OF_RANGE.
40-
- a client that silently skips the attestation instead would accept a
41-
block the rest of the network refuses: a consensus split on a single
42-
crafted block.
4340
"""
4441
state_transition_test(
4542
blocks=[
@@ -48,8 +45,6 @@ def test_aggregation_bit_beyond_validator_registry_rejects_block(
4845
slot=Slot(2),
4946
parent_label="block_1",
5047
forced_attestations=[
51-
# Bits 0, 1, and 4 are set.
52-
# Bit 4 points one past the 4-validator registry.
5348
AggregatedAttestationSpec(
5449
validator_indices=[
5550
ValidatorIndex(0),
@@ -89,9 +84,6 @@ def test_all_false_aggregation_bits_rejects_block(
8984
Then
9085
----
9186
- the block is rejected with EMPTY_AGGREGATION_BITS.
92-
- a client that processes the attestation as a no-op instead leaves an
93-
all-false tally entry in its post-state, diverging from clients that
94-
reject the block.
9587
"""
9688
state_transition_test(
9789
blocks=[
@@ -128,6 +120,7 @@ def test_zero_length_aggregation_bits_rejects_block(
128120
genesis -> block_1(1) -> block(2)
129121
- block(2) carries an attestation for block_1 whose aggregation bits
130122
hold no bits at all.
123+
- a zero-length bitfield is a distinct SSZ encoding from an all-false one.
131124
132125
When
133126
----
@@ -136,9 +129,6 @@ def test_zero_length_aggregation_bits_rejects_block(
136129
Then
137130
----
138131
- the block is rejected with EMPTY_AGGREGATION_BITS.
139-
- the zero-length bitfield is a distinct SSZ encoding from an all-false
140-
bitfield of registry size; both name no voter and both must reject
141-
the block identically across clients.
142132
"""
143133
state_transition_test(
144134
blocks=[
@@ -173,21 +163,19 @@ def test_oversized_aggregation_bits_with_in_range_votes_processes_normally(
173163
- 4 validators; a slot needs 3 votes (2/3) to be justified.
174164
- the chain:
175165
genesis -> block_1(1) -> block(2)
176-
- block(2) carries an attestation for block_1 whose bitfield is 6 bits
177-
long (two bits past the registry) with only V0, V1, and V2 set.
166+
- block(2) carries an attestation for block_1.
167+
- the bitfield is 6 bits long with only V0, V1, V2 set.
168+
- bits 4 and 5 are unset padding past the registry.
178169
179170
When
180171
----
181172
- the chain processes both blocks.
182173
183174
Then
184175
----
185-
- the attestation is processed normally: every set bit addresses a real
186-
validator, and the trailing unset padding is harmless.
187-
- block_1's slot is justified and its pending tally is cleared.
188-
- a client that skips the attestation because of the bitfield length
189-
computes a different post-state for a valid block; the pinned
190-
post-state root catches that divergence directly.
176+
- block_1's slot is justified.
177+
- the pending tally for block_1 is cleared.
178+
- finalization stays at genesis.
191179
"""
192180
state_transition_test(
193181
blocks=[
@@ -196,8 +184,6 @@ def test_oversized_aggregation_bits_with_in_range_votes_processes_normally(
196184
slot=Slot(2),
197185
parent_label="block_1",
198186
forced_attestations=[
199-
# Bits 0-2 are set; bits 3-5 are unset.
200-
# Bits 4 and 5 pad past the 4-validator registry.
201187
AggregatedAttestationSpec(
202188
validator_indices=[],
203189
aggregation_bits=AggregationBits(

0 commit comments

Comments
 (0)