diff --git a/packages/testing/src/consensus_testing/test_fixtures/fork_choice.py b/packages/testing/src/consensus_testing/test_fixtures/fork_choice.py index bef3295e..a1492a64 100644 --- a/packages/testing/src/consensus_testing/test_fixtures/fork_choice.py +++ b/packages/testing/src/consensus_testing/test_fixtures/fork_choice.py @@ -316,7 +316,7 @@ def _build_block_from_spec( """ # Determine proposer index proposer_index = spec.proposer_index or Uint64( - int(spec.slot) % store.states[store.head].validators.count + int(spec.slot) % len(store.states[store.head].validators) ) # Resolve parent root from label or default to head diff --git a/packages/testing/src/consensus_testing/test_fixtures/state_transition.py b/packages/testing/src/consensus_testing/test_fixtures/state_transition.py index ca4985d0..e24de23d 100644 --- a/packages/testing/src/consensus_testing/test_fixtures/state_transition.py +++ b/packages/testing/src/consensus_testing/test_fixtures/state_transition.py @@ -195,7 +195,7 @@ def _build_block_from_spec(self, spec: BlockSpec, state: State) -> tuple[Block, Block and cached post-state (None if not computed). """ # Use provided proposer index or compute it - proposer_index = spec.proposer_index or Uint64(int(spec.slot) % int(state.validators.count)) + proposer_index = spec.proposer_index or Uint64(int(spec.slot) % len(state.validators)) # Use provided parent root or compute it if spec.parent_root is not None: diff --git a/packages/testing/src/consensus_testing/test_fixtures/verify_signatures.py b/packages/testing/src/consensus_testing/test_fixtures/verify_signatures.py index dca4bbd3..fa628843 100644 --- a/packages/testing/src/consensus_testing/test_fixtures/verify_signatures.py +++ b/packages/testing/src/consensus_testing/test_fixtures/verify_signatures.py @@ -185,7 +185,7 @@ def _build_block_from_spec( A complete signed block with all attestations. """ # Determine proposer index - proposer_index = spec.proposer_index or Uint64(int(spec.slot) % int(state.validators.count)) + proposer_index = spec.proposer_index or Uint64(int(spec.slot) % len(state.validators)) # Resolve parent root parent_state = state.process_slots(spec.slot) diff --git a/packages/testing/src/consensus_testing/test_types/state_expectation.py b/packages/testing/src/consensus_testing/test_types/state_expectation.py index 98e076f3..8564cbb1 100644 --- a/packages/testing/src/consensus_testing/test_types/state_expectation.py +++ b/packages/testing/src/consensus_testing/test_types/state_expectation.py @@ -147,7 +147,7 @@ def validate_against_state(self, state: "State") -> None: ) elif field_name == "validator_count": - actual_count = state.validators.count + actual_count = len(state.validators) if actual_count != expected_value: raise AssertionError( f"State validation failed: validator_count = {actual_count}, " diff --git a/src/lean_spec/subspecs/containers/block/block.py b/src/lean_spec/subspecs/containers/block/block.py index c1eef772..091eacb5 100644 --- a/src/lean_spec/subspecs/containers/block/block.py +++ b/src/lean_spec/subspecs/containers/block/block.py @@ -9,7 +9,7 @@ can propose. """ -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING from lean_spec.subspecs.containers.slot import Slot from lean_spec.types import Bytes32, Uint64 @@ -17,7 +17,6 @@ from ...xmss.containers import Signature as XmssSignature from ..attestation import Attestation -from ..validator import Validator from .types import ( AggregatedAttestations, AttestationSignatures, @@ -184,7 +183,7 @@ def verify_signatures(self, parent_state: "State") -> bool: for validator_id, signature in zip(validator_ids, aggregated_signature, strict=True): # Ensure validator exists in the active set assert validator_id < Uint64(len(validators)), "Validator index out of range" - validator = cast(Validator, validators[validator_id]) + validator = validators[validator_id] assert signature.verify( validator.get_pubkey(), @@ -198,7 +197,7 @@ def verify_signatures(self, parent_state: "State") -> bool: assert proposer_attestation.validator_id < Uint64(len(validators)), ( "Proposer index out of range" ) - proposer = cast(Validator, validators[proposer_attestation.validator_id]) + proposer = validators[proposer_attestation.validator_id] assert proposer_signature.verify( proposer.get_pubkey(), diff --git a/src/lean_spec/subspecs/containers/state/state.py b/src/lean_spec/subspecs/containers/state/state.py index f6a9476f..b0cfc728 100644 --- a/src/lean_spec/subspecs/containers/state/state.py +++ b/src/lean_spec/subspecs/containers/state/state.py @@ -242,7 +242,7 @@ def process_block_header(self, block: Block) -> "State": assert is_proposer( validator_index=block.proposer_index, slot=self.slot, - num_validators=Uint64(self.validators.count), + num_validators=Uint64(len(self.validators)), ), "Incorrect block proposer" # Verify the chain link. @@ -426,7 +426,7 @@ def process_attestations( justifications = ( { root: self.justifications_validators[ - i * self.validators.count : (i + 1) * self.validators.count + i * len(self.validators) : (i + 1) * len(self.validators) ] for i, root in enumerate(self.justifications_roots) } @@ -505,7 +505,7 @@ def process_attestations( # If this is the first vote for the target block, create a fresh tally sheet: # - one boolean per validator, all initially False. if target.root not in justifications: - justifications[target.root] = [Boolean(False)] * self.validators.count + justifications[target.root] = [Boolean(False)] * len(self.validators) # Mark that this validator has voted for the target. # @@ -524,7 +524,7 @@ def process_attestations( # 3 * (number of votes) ≥ 2 * (total validators) count = sum(bool(justified) for justified in justifications[target.root]) - if 3 * count >= (2 * self.validators.count): + if 3 * count >= (2 * len(self.validators)): # The block becomes justified # # The chain now considers this block part of its safe head. diff --git a/src/lean_spec/subspecs/containers/state/types.py b/src/lean_spec/subspecs/containers/state/types.py index 4299872f..af2f28c0 100644 --- a/src/lean_spec/subspecs/containers/state/types.py +++ b/src/lean_spec/subspecs/containers/state/types.py @@ -39,7 +39,8 @@ class Validators(SSZList): ELEMENT_TYPE = Validator LIMIT = int(DEVNET_CONFIG.validator_registry_limit) - @property - def count(self) -> int: - """Return the number of validators in the registry.""" - return len(self.data) + def __getitem__(self, index: int) -> Validator: + """Access a validator by index with proper typing.""" + item = self.data[index] + assert isinstance(item, Validator) + return item diff --git a/src/lean_spec/subspecs/forkchoice/store.py b/src/lean_spec/subspecs/forkchoice/store.py index f6bba9db..51e754fb 100644 --- a/src/lean_spec/subspecs/forkchoice/store.py +++ b/src/lean_spec/subspecs/forkchoice/store.py @@ -690,7 +690,7 @@ def update_safe_target(self) -> "Store": """ # Get validator count from head state head_state = self.states[self.head] - num_validators = head_state.validators.count + num_validators = len(head_state.validators) # Calculate 2/3 majority threshold (ceiling division) min_target_score = -(-num_validators * 2 // 3) @@ -967,7 +967,7 @@ def produce_block_with_signatures( head_state = store.states[head_root] # Validate proposer authorization for this slot - num_validators = Uint64(head_state.validators.count) + num_validators = Uint64(len(head_state.validators)) assert is_proposer(validator_index, slot, num_validators), ( f"Validator {validator_index} is not the proposer for slot {slot}" ) diff --git a/tests/lean_spec/subspecs/forkchoice/test_validator.py b/tests/lean_spec/subspecs/forkchoice/test_validator.py index 400ca09f..70d4f64f 100644 --- a/tests/lean_spec/subspecs/forkchoice/test_validator.py +++ b/tests/lean_spec/subspecs/forkchoice/test_validator.py @@ -643,7 +643,7 @@ def test_validator_operations_invalid_parameters(self, sample_store: Store) -> N # Get the state to determine number of validators genesis_hash = sample_store.head state = sample_store.states[genesis_hash] - num_validators = Uint64(state.validators.count) + num_validators = Uint64(len(state.validators)) # is_proposer should work (though likely return False) result = is_proposer(large_validator, large_slot, num_validators)