From b6d63a04c58dbeeed59be28d9fd3850eed5eb45c Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Fri, 29 May 2026 19:08:20 +0200 Subject: [PATCH 1/2] refactor: expand all abbreviated identifiers to full words A reference specification should read as explicitly as possible, so every abbreviated identifier is spelled out in full across src, tests, and the packages/ testing framework (~280 distinct renames). Highlights: - validator_id -> validator_index (the domain-correct term) - att -> attestation, msg -> message, sig -> signature, sk -> secret_key, pk/pubkey -> public_key, idx -> index, prev -> previous, agg -> aggregate, prop -> proposal, conn -> connection, privkey -> private_key, len -> length - function/method/class names too (split_by_msg -> split_by_message, Pubkey -> PublicKey, get_attestation_pubkey -> get_attestation_public_key) - MSG_LEN_FE -> MESSAGE_LENGTH_FIELD_ELEMENTS Kept verbatim: canonical protocol IDs (peer_id, node_id, protocol_id, subnet_id, stream_id), ENR fields (attnets, seq), num_* prefixes, the reqresp protocol name, library APIs (argparse dest, model_config, tmp_path), and external symbols (e.g. lean_multisig_py functions like split_type_2_by_msg). Internal config/fixture string keys were updated to match the renamed fields (genesis YAML keys, validator registry manifest keys, one metrics coverage-section label). Test assertions/fixtures referencing identifiers via strings (parametrize names) were aligned. Adds a NO ABBREVIATIONS IN IDENTIFIERS rule to CLAUDE.md documenting the convention and its canonical exceptions. just check passes (ruff, format, ty, codespell, mdformat). Co-Authored-By: Claude Opus 4.8 (1M context) --- CLAUDE.md | 27 ++ .../testing/src/consensus_testing/genesis.py | 10 +- .../testing/src/consensus_testing/keys.py | 161 ++++--- .../test_fixtures/api_endpoint.py | 4 +- .../test_fixtures/fork_choice.py | 18 +- .../test_fixtures/gossipsub_handler.py | 6 +- .../test_fixtures/networking_codec.py | 16 +- .../test_fixtures/state_transition.py | 8 +- .../test_types/aggregated_attestation_spec.py | 8 +- .../test_types/block_spec.py | 75 +-- .../gossip_aggregated_attestation_spec.py | 12 +- .../test_types/gossip_attestation_spec.py | 6 +- .../test_types/step_types.py | 2 +- .../test_types/store_checks.py | 49 +- packages/testing/src/framework/cli/fill.py | 6 +- .../src/framework/pytest_plugins/filler.py | 56 +-- src/lean_spec/cli/args.py | 6 +- src/lean_spec/cli/bootstrap.py | 8 +- src/lean_spec/cli/run.py | 4 +- src/lean_spec/node/anchor.py | 6 +- .../node/api/endpoints/checkpoints.py | 2 +- src/lean_spec/node/chain/service.py | 4 +- src/lean_spec/node/genesis/config.py | 22 +- src/lean_spec/node/metrics/registry.py | 4 +- .../networking/client/event_source/gossip.py | 14 +- .../networking/client/event_source/live.py | 80 ++-- .../node/networking/client/reqresp_client.py | 68 +-- src/lean_spec/node/networking/enr/enr.py | 10 +- src/lean_spec/node/networking/enr/rlp.py | 8 +- .../node/networking/gossipsub/behavior.py | 94 ++-- .../node/networking/gossipsub/mcache.py | 62 +-- .../node/networking/gossipsub/parameters.py | 6 +- .../node/networking/gossipsub/rpc.py | 34 +- .../node/networking/service/service.py | 4 +- .../node/networking/transport/__init__.py | 4 +- .../networking/transport/identity/keypair.py | 6 +- .../node/networking/transport/peer_id.py | 18 +- .../networking/transport/quic/connection.py | 60 +-- .../node/networking/transport/quic/stream.py | 4 +- .../node/networking/transport/quic/tls.py | 16 +- src/lean_spec/node/networking/types.py | 2 +- src/lean_spec/node/node.py | 20 +- src/lean_spec/node/snappy/compress.py | 12 +- src/lean_spec/node/snappy/encoding.py | 22 +- src/lean_spec/node/storage/sqlite.py | 52 +- src/lean_spec/node/sync/head_sync.py | 4 +- src/lean_spec/node/sync/service.py | 42 +- src/lean_spec/node/validator/registry.py | 32 +- src/lean_spec/node/validator/service.py | 27 +- src/lean_spec/spec/crypto/merkleization.py | 18 +- src/lean_spec/spec/crypto/poseidon.py | 14 +- src/lean_spec/spec/crypto/xmss/constants.py | 40 +- src/lean_spec/spec/crypto/xmss/containers.py | 2 +- src/lean_spec/spec/crypto/xmss/encoding.py | 12 +- src/lean_spec/spec/crypto/xmss/field.py | 4 +- src/lean_spec/spec/crypto/xmss/interface.py | 88 ++-- src/lean_spec/spec/crypto/xmss/merkle.py | 12 +- src/lean_spec/spec/crypto/xmss/poseidon.py | 56 +-- src/lean_spec/spec/crypto/xmss/prf.py | 10 +- src/lean_spec/spec/crypto/xmss/types.py | 6 +- src/lean_spec/spec/forks/lstar/containers.py | 70 +-- src/lean_spec/spec/forks/lstar/spec.py | 139 +++--- src/lean_spec/spec/forks/lstar/store.py | 4 +- src/lean_spec/spec/forks/protocol.py | 8 +- src/lean_spec/spec/ssz/collections.py | 12 +- tests/api/conftest.py | 2 +- tests/api/endpoints/test_checkpoints.py | 2 +- .../fc/test_attestation_source_divergence.py | 4 +- .../fc/test_attestation_target_selection.py | 30 +- .../lstar/fc/test_block_attestation_limits.py | 6 +- .../lstar/fc/test_block_production.py | 20 +- .../lstar/fc/test_checkpoint_sync.py | 2 +- .../fc/test_duplicate_attestation_data.py | 2 +- tests/consensus/lstar/fc/test_equivocation.py | 22 +- .../fc/test_finalization_mid_processing.py | 6 +- .../lstar/fc/test_fork_choice_head.py | 30 +- .../lstar/fc/test_fork_choice_reorgs.py | 40 +- ...ossip_aggregated_attestation_validation.py | 18 +- .../fc/test_gossip_attestation_validation.py | 42 +- tests/consensus/lstar/fc/test_safe_target.py | 20 +- .../lstar/fc/test_signature_aggregation.py | 14 +- .../consensus/lstar/fc/test_store_pruning.py | 48 +- tests/consensus/lstar/fc/test_tick_system.py | 8 +- .../networking/test_gossipsub_handlers.py | 22 +- .../lstar/networking/test_gossipsub_rpc.py | 28 +- .../lstar/ssz/test_consensus_containers.py | 46 +- .../lstar/ssz/test_xmss_containers.py | 20 +- .../state_transition/test_finalization.py | 64 +-- .../lstar/state_transition/test_genesis.py | 22 +- .../state_transition/test_justification.py | 74 +-- .../test_empty_aggregation_bits.py | 2 +- .../test_index_out_of_range.py | 2 +- .../test_invalid_signatures.py | 4 +- .../test_valid_signatures.py | 16 +- tests/interop/helpers/assertions.py | 26 +- tests/interop/helpers/node_runner.py | 77 +-- tests/interop/test_consensus_lifecycle.py | 6 +- tests/lean_spec/cli/test_args.py | 10 +- tests/lean_spec/cli/test_bootstrap.py | 42 +- tests/lean_spec/cli/test_run.py | 2 +- tests/lean_spec/conftest.py | 14 +- tests/lean_spec/helpers/__init__.py | 4 +- tests/lean_spec/helpers/builders.py | 65 +-- tests/lean_spec/helpers/mocks.py | 2 +- tests/lean_spec/node/chain/test_service.py | 4 +- tests/lean_spec/node/genesis/test_config.py | 98 ++-- tests/lean_spec/node/genesis/test_state.py | 28 +- .../networking/client/test_event_source.py | 32 +- .../client/test_gossip_reception.py | 8 +- .../networking/client/test_reqresp_client.py | 108 ++--- .../client/test_reqresp_client_range.py | 62 +-- .../lean_spec/node/networking/enr/test_enr.py | 60 +-- .../lean_spec/node/networking/enr/test_rlp.py | 48 +- .../gossipsub/integration/conftest.py | 2 +- .../networking/gossipsub/integration/node.py | 6 +- .../integration/test_connectivity.py | 24 +- .../integration/test_heartbeat_integ.py | 18 +- .../gossipsub/integration/test_idontwant.py | 16 +- .../gossipsub/integration/test_propagation.py | 33 +- .../gossipsub/integration/test_stress.py | 25 +- .../gossipsub/test_cache_edge_cases.py | 116 ++--- .../networking/gossipsub/test_gossipsub.py | 12 +- .../networking/gossipsub/test_handlers.py | 72 +-- .../networking/gossipsub/test_heartbeat.py | 30 +- .../node/networking/gossipsub/test_publish.py | 6 +- .../gossipsub/test_rpc_edge_cases.py | 6 +- .../node/networking/service/test_service.py | 18 +- .../node/networking/test_network_service.py | 6 +- .../lean_spec/node/networking/test_reqresp.py | 14 +- .../transport/quic/test_connection.py | 132 ++--- .../transport/quic/test_negotiation.py | 24 +- .../networking/transport/quic/test_tls.py | 76 +-- .../node/networking/transport/test_peer_id.py | 52 +- .../node/observability/test_observer.py | 28 +- tests/lean_spec/node/snappy/test_framing.py | 4 +- tests/lean_spec/node/snappy/test_snappy.py | 14 +- tests/lean_spec/node/storage/test_sqlite.py | 12 +- .../node/sync/test_reaggregate_from_block.py | 6 +- tests/lean_spec/node/sync/test_service.py | 42 +- tests/lean_spec/node/test_anchor.py | 8 +- tests/lean_spec/node/test_node.py | 26 +- .../lean_spec/node/validator/test_registry.py | 114 ++--- .../lean_spec/node/validator/test_service.py | 228 +++++---- tests/lean_spec/spec/crypto/test_hash.py | 10 +- .../spec/crypto/xmss/test_aggregation.py | 452 ++++++++++-------- .../spec/crypto/xmss/test_constants.py | 60 +-- .../spec/crypto/xmss/test_containers.py | 6 +- .../spec/crypto/xmss/test_encoding.py | 34 +- .../lean_spec/spec/crypto/xmss/test_field.py | 6 +- .../spec/crypto/xmss/test_interface.py | 69 +-- .../lean_spec/spec/crypto/xmss/test_merkle.py | 10 +- .../spec/crypto/xmss/test_poseidon.py | 8 +- tests/lean_spec/spec/crypto/xmss/test_prf.py | 2 +- .../lean_spec/spec/crypto/xmss/test_types.py | 10 +- .../spec/forks/lstar/forkchoice/conftest.py | 4 +- .../forkchoice/test_attestation_target.py | 80 ++-- ...test_block_production_justification_gap.py | 12 +- .../forkchoice/test_compute_block_weights.py | 8 +- .../forkchoice/test_store_attestations.py | 164 ++++--- .../lstar/forkchoice/test_store_pruning.py | 20 +- .../lstar/forkchoice/test_time_management.py | 4 +- .../forkchoice/test_validate_attestation.py | 10 +- .../forks/lstar/forkchoice/test_validator.py | 88 ++-- .../lstar/state/test_state_aggregation.py | 103 ++-- .../lstar/state/test_state_justified_slots.py | 16 +- .../state/test_state_process_attestations.py | 8 +- .../lstar/test_attestation_aggregation.py | 22 +- .../spec/forks/lstar/test_validator.py | 4 +- tests/lean_spec/spec/ssz/test_byte_arrays.py | 38 +- tests/lean_spec/spec/ssz/test_collections.py | 6 +- tests/lean_spec/spec/ssz/test_ssz_base.py | 10 +- tests/lean_spec/spec/ssz/test_uint.py | 52 +- 172 files changed, 2897 insertions(+), 2617 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d57f03d3c..a7b7a2c1d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,3 +28,30 @@ subspecifications that the Lean Ethereum protocol relies on. - When refactoring from functions to classes, DELETE the old functions entirely - Update ALL call sites to use the new API directly - Old patterns must be REMOVED, not preserved alongside new ones +- **CRITICAL - NO ABBREVIATIONS IN IDENTIFIERS**: This is a STRICT requirement. Every + identifier — variables, parameters, function and method names, class names, attributes, + and constants — must spell words out in full. A reference spec must be as explicit as + possible; abbreviations make it ambiguous. This applies to source, tests, and the + `packages/` testing framework. + - Expand truncated words. Examples: + - `att` / `att_data` → `attestation` / `attestation_data` + - `msg` → `message`, `sig` → `signature`, `sk` → `secret_key`, `pk` / `pubkey` → `public_key` + - `idx` → `index`, `prev` → `previous`, `curr` → `current`, `agg` → `aggregate` + - `prop` → `proposal`, `conn` → `connection`, `addr` → `address`, `cert` → `certificate` + - `privkey` → `private_key`, `elem` → `element`, `buf` → `buffer`, `dir` → `directory` + - `len` → `length` (inside a name, never the `len()` builtin), `fe` → `field_elements` + - Use the correct domain term, not just any expansion: a validator is referenced by its + INDEX, so `validator_id` → `validator_index` (never `validator_id`). + - KEEP canonical protocol identifiers that genuinely use "ID": `peer_id`, `node_id`, + `protocol_id`, `subnet_id`, `stream_id`. The trailing `_id` is fine on these; only expand + the word part (`msg_id` → `message_id`, but `peer_id` stays). + - KEEP canonical short field/format names and accepted prefixes: `attnets` and `seq` + (ENR fields), `pem`, `num`/`num_*` (eth2 number-of style, e.g. `num_validators`), and the + `reqresp` libp2p protocol name. + - KEEP universal Python idioms and library/stdlib API names verbatim: `args`, `kwargs`, + `config`, `model_config`, `tmp_path`, `dest` (argparse), `exc_info`, `__init__`, `__repr__`. + - NEVER rename external/wire identifiers: third-party library symbols (e.g. functions + imported from `lean_multisig_py`), protobuf field names, JSON/YAML keys, pydantic aliases, + or any on-the-wire string. Rename the Python identifier, never the serialized contract. + - When a fully-expanded name becomes unwieldy, prefer a shorter but still complete phrasing + (drop redundant words) rather than re-introducing an abbreviation. diff --git a/packages/testing/src/consensus_testing/genesis.py b/packages/testing/src/consensus_testing/genesis.py index 17b277c98..930143711 100644 --- a/packages/testing/src/consensus_testing/genesis.py +++ b/packages/testing/src/consensus_testing/genesis.py @@ -30,13 +30,13 @@ def _build_validators(num_validators: int) -> Validators: validators = [] for i in range(num_validators): - idx = ValidatorIndex(i) - attestation_pubkey, proposal_pubkey = key_manager.get_public_keys(idx) + index = ValidatorIndex(i) + attestation_public_key, proposal_public_key = key_manager.get_public_keys(index) validators.append( Validator( - attestation_pubkey=Bytes52(attestation_pubkey.encode_bytes()), - proposal_pubkey=Bytes52(proposal_pubkey.encode_bytes()), - index=idx, + attestation_public_key=Bytes52(attestation_public_key.encode_bytes()), + proposal_public_key=Bytes52(proposal_public_key.encode_bytes()), + index=index, ) ) diff --git a/packages/testing/src/consensus_testing/keys.py b/packages/testing/src/consensus_testing/keys.py index d71327e91..c0e2f2bbb 100755 --- a/packages/testing/src/consensus_testing/keys.py +++ b/packages/testing/src/consensus_testing/keys.py @@ -110,7 +110,7 @@ def create_dummy_signature() -> Signature: A zero-valued signature with correct field sizes. """ # Build a single zero-filled hash digest with the scheme's hash length. - zero_digest = HashDigestVector(data=[Fp(0)] * TARGET_CONFIG.HASH_LEN_FE) + zero_digest = HashDigestVector(data=[Fp(0)] * TARGET_CONFIG.HASH_LENGTH_FIELD_ELEMENTS) # The Merkle authentication path needs one sibling per tree level. # @@ -123,7 +123,7 @@ def create_dummy_signature() -> Signature: # Assemble a complete signature with all components zeroed out. return Signature( path=HashTreeOpening(siblings=siblings), - rho=Randomness(data=[Fp(0)] * TARGET_CONFIG.RAND_LEN_FE), + rho=Randomness(data=[Fp(0)] * TARGET_CONFIG.RAND_LENGTH_FIELD_ELEMENTS), hashes=hashes, ) @@ -151,7 +151,7 @@ def create_dummy_signature() -> Signature: """ -def get_keys_dir(scheme_name: str) -> Path: +def get_keys_directory(scheme_name: str) -> Path: """ Resolve the on-disk directory that holds key files for a scheme. @@ -253,7 +253,7 @@ def __init__( self.max_slot = max_slot self.scheme_name = scheme_name self.scheme = LEAN_ENV_TO_SCHEMES[scheme_name] - self._keys_dir = get_keys_dir(scheme_name) + self._keys_directory = get_keys_directory(scheme_name) # Raw JSON cache: nested dict of hex-encoded SSZ strings, very lightweight. self._json_cache: dict[ValidatorIndex, dict[str, dict[str, str]]] = {} @@ -281,26 +281,26 @@ def _scan_indices(self) -> set[ValidatorIndex]: """ if self._available_indices is None: # Verify the key directory exists before scanning. - if not self._keys_dir.exists(): + if not self._keys_directory.exists(): raise FileNotFoundError( - f"Keys directory not found: {self._keys_dir} - " + f"Keys directory not found: {self._keys_directory} - " f"Run: python -m consensus_testing.keys --scheme {self.scheme_name}" ) # Each JSON file is named by its validator index (e.g. "0.json"). self._available_indices = { - ValidatorIndex(int(f.stem)) for f in self._keys_dir.glob("*.json") + ValidatorIndex(int(f.stem)) for f in self._keys_directory.glob("*.json") } # An empty directory is as bad as a missing one. if not self._available_indices: raise FileNotFoundError( - f"No key files found in: {self._keys_dir} - " + f"No key files found in: {self._keys_directory} - " f"Run: python -m consensus_testing.keys --scheme {self.scheme_name}" ) return self._available_indices - def _load_json(self, idx: ValidatorIndex) -> dict[str, dict[str, str]]: + def _load_json(self, index: ValidatorIndex) -> dict[str, dict[str, str]]: """ Load raw JSON for a single validator, caching the result. @@ -308,7 +308,7 @@ def _load_json(self, idx: ValidatorIndex) -> dict[str, dict[str, str]]: Keeping them as strings avoids the cost of deserializing secret keys. Args: - idx: Validator index to load. + index: Validator index to load. Returns: Nested dictionary of hex-encoded SSZ strings keyed by role then field. @@ -316,17 +316,17 @@ def _load_json(self, idx: ValidatorIndex) -> dict[str, dict[str, str]]: Raises: KeyError: If no key file exists for the index. """ - if idx not in self._json_cache: + if index not in self._json_cache: # Resolve the per-validator JSON file path. - key_file = self._keys_dir / f"{idx}.json" + key_file = self._keys_directory / f"{index}.json" try: with key_file.open() as f: - self._json_cache[idx] = json.load(f) + self._json_cache[index] = json.load(f) except FileNotFoundError: raise KeyError(f"Key file not found: {key_file}") from None - return self._json_cache[idx] + return self._json_cache[index] - def _get_secret_key(self, idx: ValidatorIndex, role: KeyRole) -> SecretKey: + def _get_secret_key(self, index: ValidatorIndex, role: KeyRole) -> SecretKey: """ Deserialize a single secret key from disk. @@ -334,17 +334,17 @@ def _get_secret_key(self, idx: ValidatorIndex, role: KeyRole) -> SecretKey: The other three fields remain as lightweight hex strings in the cache. Args: - idx: Validator index to look up. + index: Validator index to look up. role: Which signing role's secret to decode (attestation or proposal). Returns: The deserialized secret key. """ # Load the raw JSON (cached), then decode only the requested field. - data = self._load_json(idx) + data = self._load_json(index) return SecretKey.decode_bytes(bytes.fromhex(data[f"{role}_keypair"]["secret_key"])) - def __getitem__(self, idx: ValidatorIndex) -> ValidatorKeyPair: + def __getitem__(self, index: ValidatorIndex) -> ValidatorKeyPair: """ Fully deserialize a key pair including secrets. @@ -352,15 +352,15 @@ def __getitem__(self, idx: ValidatorIndex) -> ValidatorKeyPair: heavy secret key objects unnecessarily. """ try: - return ValidatorKeyPair.model_validate(self._load_json(idx)) + return ValidatorKeyPair.model_validate(self._load_json(index)) except KeyError: - raise KeyError(f"Validator {idx} not found (available: {len(self)})") from None + raise KeyError(f"Validator {index} not found (available: {len(self)})") from None - def __contains__(self, idx: object) -> bool: + def __contains__(self, index: object) -> bool: """Check whether a validator index has keys on disk.""" - if not isinstance(idx, ValidatorIndex): + if not isinstance(index, ValidatorIndex): return False - return idx in self._scan_indices() + return index in self._scan_indices() def __len__(self) -> int: """Return the number of available validator key pairs.""" @@ -370,7 +370,7 @@ def __iter__(self) -> Iterator[ValidatorIndex]: """Iterate over validator indices in ascending order.""" return iter(sorted(self._scan_indices())) - def get_public_keys(self, idx: ValidatorIndex) -> tuple[PublicKey, PublicKey]: + def get_public_keys(self, index: ValidatorIndex) -> tuple[PublicKey, PublicKey]: """ Return attestation and proposal public keys without touching secrets. @@ -378,23 +378,23 @@ def get_public_keys(self, idx: ValidatorIndex) -> tuple[PublicKey, PublicKey]: Secret keys (~2.7 KB raw, ~370 MB as Python objects) are not touched. Args: - idx: Validator index to look up. + index: Validator index to look up. Returns: Tuple of (attestation public key, proposal public key). """ - if idx not in self._public_cache: + if index not in self._public_cache: # Decode only the two public key fields from the raw JSON. - data = self._load_json(idx) - self._public_cache[idx] = ( + data = self._load_json(index) + self._public_cache[index] = ( PublicKey.decode_bytes(bytes.fromhex(data["attestation_keypair"]["public_key"])), PublicKey.decode_bytes(bytes.fromhex(data["proposal_keypair"]["public_key"])), ) - return self._public_cache[idx] + return self._public_cache[index] def _sign_with_secret( self, - validator_id: ValidatorIndex, + validator_index: ValidatorIndex, slot: Slot, message: Bytes32, role: KeyRole, @@ -413,7 +413,7 @@ def _sign_with_secret( 3. Keep the advanced object for the next sign Args: - validator_id: Which validator's key to use. + validator_index: Which validator's key to use. slot: Target slot to sign for. message: The 32-byte message digest to sign. role: Which signing role's key (attestation or proposal) to advance. @@ -421,42 +421,42 @@ def _sign_with_secret( Raises: ValueError: If the slot exceeds the key's total lifetime. """ - cache_key = (validator_id, role) + cache_key = (validator_index, role) # Reuse the cached object directly when present, else decode from disk. # Holding the object avoids the bytes-to-object round-trip on every sign. # That round-trip dominated prod-scheme runtime under the compact-bytes cache. if cache_key in self._secret_state: - sk = self._secret_state[cache_key] + secret_key = self._secret_state[cache_key] else: - sk = self._get_secret_key(validator_id, role) + secret_key = self._get_secret_key(validator_index, role) # Advance the key state until the target slot falls within the prepared interval. # # Each advancement step extends the interval by consuming the next one-time signing leaf. - prepared = self.scheme.get_prepared_interval(sk) + prepared = self.scheme.get_prepared_interval(secret_key) while int(slot) not in prepared: - activation = self.scheme.get_activation_interval(sk) + activation = self.scheme.get_activation_interval(secret_key) # If the prepared interval already reaches the activation boundary, # no further advancement is possible, the key is exhausted. if prepared.stop >= activation.stop: raise ValueError(f"Slot {slot} exceeds key lifetime {activation.stop}") - sk = self.scheme.advance_preparation(sk) - prepared = self.scheme.get_prepared_interval(sk) + secret_key = self.scheme.advance_preparation(secret_key) + prepared = self.scheme.get_prepared_interval(secret_key) # Produce the signature for the target slot. - signature = self.scheme.sign(sk, slot, message) + signature = self.scheme.sign(secret_key, slot, message) # Park the advanced object back in the cache for the next sign. - self._secret_state[cache_key] = sk + self._secret_state[cache_key] = secret_key return signature def sign_attestation_data( self, - validator_id: ValidatorIndex, + validator_index: ValidatorIndex, attestation_data: AttestationData, ) -> Signature: """ @@ -466,7 +466,7 @@ def sign_attestation_data( The proposal key remains untouched. Args: - validator_id: Which validator signs. + validator_index: Which validator signs. attestation_data: The attestation to sign. Returns: @@ -478,7 +478,7 @@ def sign_attestation_data( # Derive the message digest from the attestation data and delegate # to the shared signing logic with the attestation secret. return self._sign_with_secret( - validator_id, + validator_index, attestation_data.slot, hash_tree_root(attestation_data), "attestation", @@ -486,7 +486,7 @@ def sign_attestation_data( def sign_block_root( self, - validator_id: ValidatorIndex, + validator_index: ValidatorIndex, slot: Slot, block_root: Bytes32, ) -> Signature: @@ -497,7 +497,7 @@ def sign_block_root( The attestation key remains untouched. Args: - validator_id: Which validator signs. + validator_index: Which validator signs. slot: Slot of the block being proposed. block_root: The hash tree root of the block. @@ -507,11 +507,11 @@ def sign_block_root( Raises: ValueError: If the slot exceeds key lifetime. """ - return self._sign_with_secret(validator_id, slot, block_root, "proposal") + return self._sign_with_secret(validator_index, slot, block_root, "proposal") def sign_and_aggregate( self, - validator_ids: list[ValidatorIndex], + validator_indices: list[ValidatorIndex], attestation_data: AttestationData, ) -> SingleMessageAggregate: """ @@ -526,19 +526,19 @@ def sign_and_aggregate( binding all participants to (data, slot). Args: - validator_ids: Validators to sign with. + validator_indices: Validators to sign with. attestation_data: The attestation data to sign. Returns: - Cryptographically valid single-message aggregate proof covering validator_ids. + Cryptographically valid single-message aggregate proof covering validator_indices. """ raw_xmss = [ ( - vid, - self.get_public_keys(vid)[0], - self.sign_attestation_data(vid, attestation_data), + validator_index, + self.get_public_keys(validator_index)[0], + self.sign_attestation_data(validator_index, attestation_data), ) - for vid in validator_ids + for validator_index in validator_indices ] return SingleMessageAggregate.aggregate( children=[], @@ -577,21 +577,24 @@ def build_attestation_proofs( lookup = signature_lookup or {} proofs: list[SingleMessageAggregate] = [] - for agg in aggregated_attestations: + for aggregate in aggregated_attestations: # Decode which validators participated from the bitfield. - validator_ids = agg.aggregation_bits.to_validator_indices() + validator_indices = aggregate.aggregation_bits.to_validator_indices() # Try the lookup first for pre-computed signatures. # Fall back to signing on the fly for any missing entries. - sigs_for_data = lookup.get(agg.data, {}) + signatures_for_data = lookup.get(aggregate.data, {}) # Collect the attestation public keys for each participant. - public_keys = [self.get_public_keys(vid)[0] for vid in validator_ids] + public_keys = [ + self.get_public_keys(validator_index)[0] for validator_index in validator_indices + ] # Gather individual signatures, computing any that are missing. signatures = [ - sigs_for_data.get(vid) or self.sign_attestation_data(vid, agg.data) - for vid in validator_ids + signatures_for_data.get(validator_index) + or self.sign_attestation_data(validator_index, aggregate.data) + for validator_index in validator_indices ] # Produce a single aggregated proof that the leanVM can verify @@ -599,9 +602,9 @@ def build_attestation_proofs( proofs.append( SingleMessageAggregate.aggregate( children=[], - raw_xmss=list(zip(validator_ids, public_keys, signatures, strict=True)), - message=hash_tree_root(agg.data), - slot=agg.data.slot, + raw_xmss=list(zip(validator_indices, public_keys, signatures, strict=True)), + message=hash_tree_root(aggregate.data), + slot=aggregate.data.slot, ) ) @@ -663,7 +666,7 @@ def _generate_keys(lean_env: str, count: int, max_slot: int) -> None: max_slot: Maximum signable slot (key lifetime = max_slot + 1 slots). """ scheme = LEAN_ENV_TO_SCHEMES[lean_env] - keys_dir = get_keys_dir(lean_env) + keys_directory = get_keys_directory(lean_env) num_slots = max_slot + 1 num_workers = os.cpu_count() or 1 @@ -673,11 +676,11 @@ def _generate_keys(lean_env: str, count: int, max_slot: int) -> None: ) # Ensure the output directory exists. - keys_dir.mkdir(parents=True, exist_ok=True) + keys_directory.mkdir(parents=True, exist_ok=True) # Remove stale key files from previous runs that may have generated # a different number of keys. - for old_file in keys_dir.glob("*.json"): + for old_file in keys_directory.glob("*.json"): old_file.unlink() # Generate key pairs in parallel across all CPU cores. @@ -687,15 +690,15 @@ def _generate_keys(lean_env: str, count: int, max_slot: int) -> None: gen_start = time.monotonic() with ProcessPoolExecutor(max_workers=num_workers) as executor: worker_func = partial(_generate_single_keypair, scheme, num_slots) - for idx, key_pair in enumerate(executor.map(worker_func, range(count))): + for index, key_pair in enumerate(executor.map(worker_func, range(count))): elapsed = time.monotonic() - gen_start - print(f"[{idx + 1}/{count}] saved key #{idx} ({elapsed:.0f}s elapsed)") + print(f"[{index + 1}/{count}] saved key #{index} ({elapsed:.0f}s elapsed)") - key_file = keys_dir / f"{idx}.json" + key_file = keys_directory / f"{index}.json" key_file.write_text(key_pair.model_dump_json(indent=2)) total = time.monotonic() - gen_start - print(f"Saved {count} key pairs to {keys_dir}/ ({total:.0f}s total)") + print(f"Saved {count} key pairs to {keys_directory}/ ({total:.0f}s total)") def download_keys(scheme: str) -> None: @@ -708,16 +711,16 @@ def download_keys(scheme: str) -> None: Args: scheme: Scheme name ("test" or "prod"). """ - base_dir = Path(__file__).parent / "test_keys" + base_directory = Path(__file__).parent / "test_keys" url = KEY_DOWNLOAD_URLS[scheme] print(f"Downloading {scheme} keys from {url}...") # Reserve a temp path; we open it explicitly below so the writer can close # before the reader opens. - tmp_fd, tmp_name = tempfile.mkstemp(suffix=".tar.gz") - os.close(tmp_fd) - tmp_path = Path(tmp_name) + temporary_fd, temporary_name = tempfile.mkstemp(suffix=".tar.gz") + os.close(temporary_fd) + tmp_path = Path(temporary_name) try: # Close the writer before opening the reader. @@ -727,17 +730,17 @@ def download_keys(scheme: str) -> None: shutil.copyfileobj(response, out) # Remove any existing keys for this scheme before extracting. - target_dir = base_dir / f"{scheme}_scheme" - if target_dir.exists(): - shutil.rmtree(target_dir) - base_dir.mkdir(parents=True, exist_ok=True) + target_directory = base_directory / f"{scheme}_scheme" + if target_directory.exists(): + shutil.rmtree(target_directory) + base_directory.mkdir(parents=True, exist_ok=True) # Extract the archive into the base directory. # The archive root is the scheme directory itself. with tarfile.open(tmp_path, "r:gz") as tar: - tar.extractall(path=base_dir, filter="data") + tar.extractall(path=base_directory, filter="data") - print(f"Extracted {scheme} keys to {target_dir}/") + print(f"Extracted {scheme} keys to {target_directory}/") finally: # Always clean up the temporary download file. tmp_path.unlink(missing_ok=True) diff --git a/packages/testing/src/consensus_testing/test_fixtures/api_endpoint.py b/packages/testing/src/consensus_testing/test_fixtures/api_endpoint.py index 3f6296d86..8477a1db3 100644 --- a/packages/testing/src/consensus_testing/test_fixtures/api_endpoint.py +++ b/packages/testing/src/consensus_testing/test_fixtures/api_endpoint.py @@ -51,7 +51,7 @@ def _build_store(num_validators: int, genesis_time: int, anchor_slot: int = 0) - ) block = _make_genesis_block(state) # No validator identity — fixture only reads store data, never signs. - return fork.create_store(state, block, validator_id=None) + return fork.create_store(state, block, validator_index=None) # Walk the chain from genesis through anchor_slot using empty blocks. # The returned pair (state, block) is internally consistent with the @@ -62,7 +62,7 @@ def _build_store(num_validators: int, genesis_time: int, anchor_slot: int = 0) - anchor_slot=Slot(anchor_slot), genesis_time=Uint64(genesis_time), ) - return fork.create_store(state, block, validator_id=None) + return fork.create_store(state, block, validator_index=None) def _health_response(_store: Store, _fixture: "ApiEndpointTest") -> dict[str, Any]: 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 13631c767..fa277a429 100644 --- a/packages/testing/src/consensus_testing/test_fixtures/fork_choice.py +++ b/packages/testing/src/consensus_testing/test_fixtures/fork_choice.py @@ -190,7 +190,7 @@ def make_fixture(self) -> Self: # Expected anchor-init failure path. # # When anchor_valid is False, the test asserts that Store.from_anchor - # rejects the given (state, block) pair. The pubkey sync that normally + # rejects the given (state, block) pair. The public_key sync that normally # fixes up the anchor block's state root is skipped, since that would # mask the very inconsistency under test. if not self.anchor_valid: @@ -202,7 +202,7 @@ def make_fixture(self) -> Self: spec.create_store( self.anchor_state, self.anchor_block, - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), ) except AssertionError as e: if self.expected_anchor_error is not None and self.expected_anchor_error not in str( @@ -224,17 +224,17 @@ def make_fixture(self) -> Self: # Tests requiring higher max slot trigger key expansion. key_manager = XmssKeyManager.shared(max_slot=self.max_slot) - # Validator pubkey synchronization + # Validator public_key synchronization # - # Test states use placeholder pubkeys. + # Test states use placeholder public_keys. # We must replace them with the key manager's actual keys. # Otherwise signature verification will fail. updated_validators = [] for i, validator in enumerate(self.anchor_state.validators): - idx = ValidatorIndex(i) - attestation_pubkey, proposal_pubkey = key_manager.get_public_keys(idx) - validator.attestation_pubkey = attestation_pubkey.encode_bytes() - validator.proposal_pubkey = proposal_pubkey.encode_bytes() + index = ValidatorIndex(i) + attestation_public_key, proposal_public_key = key_manager.get_public_keys(index) + validator.attestation_public_key = attestation_public_key.encode_bytes() + validator.proposal_public_key = proposal_public_key.encode_bytes() updated_validators.append(validator) # Updating validators changes the state root. @@ -249,7 +249,7 @@ def make_fixture(self) -> Self: store = spec.create_store( self.anchor_state, self.anchor_block, - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), ) # Block registry for fork creation diff --git a/packages/testing/src/consensus_testing/test_fixtures/gossipsub_handler.py b/packages/testing/src/consensus_testing/test_fixtures/gossipsub_handler.py index 58c85191c..cd409bb02 100644 --- a/packages/testing/src/consensus_testing/test_fixtures/gossipsub_handler.py +++ b/packages/testing/src/consensus_testing/test_fixtures/gossipsub_handler.py @@ -233,12 +233,12 @@ async def _execute(self) -> dict[str, Any]: # When a peer requests a message via IWANT, the handler looks it up # here and sends the payload back. for entry in self.initial_state.get("cachedMessages", []): - msg = GossipsubMessage( + message = GossipsubMessage( topic=entry["topic"].encode("utf-8"), raw_data=_unhex(entry["data"]), ) - msg._cached_id = MessageId(_unhex(entry["messageId"])) - behavior.message_cache.put(TopicId(entry["topic"]), msg) + message._cached_id = MessageId(_unhex(entry["messageId"])) + behavior.message_cache.put(TopicId(entry["topic"]), message) # Build the incoming RPC from the event. from_peer = _peer_id(self.event["fromPeer"]) diff --git a/packages/testing/src/consensus_testing/test_fixtures/networking_codec.py b/packages/testing/src/consensus_testing/test_fixtures/networking_codec.py index c4d2d470e..bd34e01c9 100644 --- a/packages/testing/src/consensus_testing/test_fixtures/networking_codec.py +++ b/packages/testing/src/consensus_testing/test_fixtures/networking_codec.py @@ -22,7 +22,7 @@ decode_request, encode_request, ) -from lean_spec.node.networking.transport.peer_id import KeyType, PeerId, PublicKeyProto +from lean_spec.node.networking.transport.peer_id import KeyType, PeerId, PublicKeyProtobuf from lean_spec.node.networking.varint import decode_varint, encode_varint from lean_spec.node.snappy import compress, decompress, frame_compress, frame_decompress from lean_spec.spec.forks import SubnetId @@ -103,7 +103,7 @@ def _make_decode_failure(self) -> dict[str, Any]: """Assert that decoding `input.bytes` with `input.decoder` raises. Dispatches to one of the wire-format decoders and confirms that the - expected exception (on :attr:`expect_exception`) is raised. Used to + expected exception (on :attribute:`expect_exception`) is raised. Used to generate negative-path test vectors for client decoders. The input record carries two fields: @@ -370,9 +370,9 @@ def _make_peer_id(self) -> dict[str, Any]: key_type = key_type_map[self.input["keyType"]] key_data = _from_hex(self.input["publicKey"]) - proto = PublicKeyProto(key_type=key_type, key_data=key_data) - encoded_proto = proto.encode() - peer_id = PeerId.from_public_key(proto) + protobuf = PublicKeyProtobuf(key_type=key_type, key_data=key_data) + encoded_protobuf = protobuf.encode() + peer_id = PeerId.from_public_key(protobuf) peer_id_str = str(peer_id) # Roundtrip: Base58 decode → re-encode must match. @@ -380,7 +380,7 @@ def _make_peer_id(self) -> dict[str, Any]: assert roundtrip == peer_id, "PeerId Base58 roundtrip failed" return { - "protobufEncoded": _to_hex(encoded_proto), + "protobufEncoded": _to_hex(encoded_protobuf), "peerId": peer_id_str, } @@ -388,9 +388,9 @@ def _make_peer_id(self) -> dict[str, Any]: def _build_rpc(d: dict[str, Any]) -> RPC: """Build an RPC from a JSON-friendly dict.""" subs = [_build_sub_opts(s) for s in d.get("subscriptions", [])] - msgs = [_build_message(m) for m in d.get("publish", [])] + messages = [_build_message(m) for m in d.get("publish", [])] ctrl = _build_control(d["control"]) if d.get("control") else None - return RPC(subscriptions=subs, publish=msgs, control=ctrl) + return RPC(subscriptions=subs, publish=messages, control=ctrl) def _build_sub_opts(d: dict[str, Any]) -> SubOpts: 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 9666b82b8..07c95874f 100644 --- a/packages/testing/src/consensus_testing/test_fixtures/state_transition.py +++ b/packages/testing/src/consensus_testing/test_fixtures/state_transition.py @@ -63,7 +63,7 @@ class StateTransitionTest(BaseConsensusFixture): make_fixture() and stores them in the private _filled_blocks attribute. """ - # TODO: We should figure out a configuration to raise if a private attr is + # TODO: We should figure out a configuration to raise if a private attribute is # attempted to be set during model initialization. _filled_blocks: list[Block] = PrivateAttr(default_factory=list) """ @@ -278,7 +278,9 @@ def _build_block_from_spec( if spec.forced_attestations: forced = [ AggregatedAttestation( - aggregation_bits=ValidatorIndices(data=fa.validator_ids).to_aggregation_bits(), + aggregation_bits=ValidatorIndices( + data=fa.validator_indices + ).to_aggregation_bits(), data=fa.build_attestation_data(block_registry, state), ) for fa in spec.forced_attestations @@ -328,7 +330,7 @@ def _build_aggregated_payloads_from_spec( raise NotImplementedError("signer_ids not yet supported in StateTransitionTest") attestation_data = spec.build_attestation_data(block_registry, state) - proof = key_manager.sign_and_aggregate(spec.validator_ids, attestation_data) + proof = key_manager.sign_and_aggregate(spec.validator_indices, attestation_data) payloads.setdefault(attestation_data, set()).add(proof) return payloads diff --git a/packages/testing/src/consensus_testing/test_types/aggregated_attestation_spec.py b/packages/testing/src/consensus_testing/test_types/aggregated_attestation_spec.py index ff9731c1c..da9ac6d60 100644 --- a/packages/testing/src/consensus_testing/test_types/aggregated_attestation_spec.py +++ b/packages/testing/src/consensus_testing/test_types/aggregated_attestation_spec.py @@ -27,7 +27,7 @@ class AggregatedAttestationSpec(CamelModel): Source defaults to the latest justified checkpoint unless overridden. """ - validator_ids: list[ValidatorIndex] + validator_indices: list[ValidatorIndex] """The indices of validators making the attestation (required).""" slot: Slot @@ -79,14 +79,14 @@ class AggregatedAttestationSpec(CamelModel): """ Override which validators actually sign the attestation. - When None (default), signatures are generated using the validators in validator_ids. + When None (default), signatures are generated using the validators in validator_indices. When specified, signatures are generated using these validator indices instead. This creates a mismatch between claimed participants and actual signers. Useful for testing that verification rejects attestations where valid signatures don't correspond to the claimed validators. - Must have same length as validator_ids when specified. + Must have same length as validator_indices when specified. """ def build_attestation_data( @@ -181,7 +181,7 @@ def build_invalid_proof( """ attestation_data = self.build_attestation_data(block_registry, state) - aggregation_bits = ValidatorIndices(data=self.validator_ids).to_aggregation_bits() + aggregation_bits = ValidatorIndices(data=self.validator_indices).to_aggregation_bits() invalid_aggregated = AggregatedAttestation( aggregation_bits=aggregation_bits, data=attestation_data, diff --git a/packages/testing/src/consensus_testing/test_types/block_spec.py b/packages/testing/src/consensus_testing/test_types/block_spec.py index 269694e42..ce857f7bd 100644 --- a/packages/testing/src/consensus_testing/test_types/block_spec.py +++ b/packages/testing/src/consensus_testing/test_types/block_spec.py @@ -193,7 +193,7 @@ def build_attestations( Returns: Tuple of: - All built attestations (one per validator per spec) - - Signature lookup keyed by (attestation_data, validator_id) + - Signature lookup keyed by (attestation_data, validator_index) - Subset of attestations that have valid (non-dummy) signatures """ if self.attestations is None: @@ -210,9 +210,9 @@ def build_attestations( # Create one attestation per validator. # Each validator signs independently; signatures aggregate later. - for validator_id in aggregated_spec.validator_ids: + for validator_index in aggregated_spec.validator_indices: attestation = Attestation( - validator_id=validator_id, + validator_index=validator_index, data=attestation_data, ) attestations.append(attestation) @@ -221,7 +221,7 @@ def build_attestations( # Invalid signatures test rejection paths. if aggregated_spec.valid_signature: signature = key_manager.sign_attestation_data( - validator_id, + validator_index, attestation_data, ) valid_attestations.add(attestation) @@ -230,7 +230,7 @@ def build_attestations( # Index signature by attestation data and validator ID. signature_lookup.setdefault(attestation_data, {}).setdefault( - validator_id, + validator_index, signature, ) @@ -267,13 +267,13 @@ def _sign_block( proposer_index: Which validator proposes this block. key_manager: XMSS key manager for signing. state: State providing the validator registry used to resolve - participant pubkeys for the merge. + participant public_keys for the merge. Returns: Complete signed block. """ block_root = hash_tree_root(final_block) - proposer_pubkey = key_manager.get_public_keys(proposer_index)[1] + proposer_public_key = key_manager.get_public_keys(proposer_index)[1] # The binding rejects placeholder bytes; if anything in the merged # input is a dummy (invalid proposer sig or a build_invalid_proof @@ -291,19 +291,19 @@ def _sign_block( ) proposer_single_message_aggregate = SingleMessageAggregate.aggregate( children=[], - raw_xmss=[(proposer_index, proposer_pubkey, proposer_signature)], + raw_xmss=[(proposer_index, proposer_public_key, proposer_signature)], message=block_root, slot=self.slot, ) public_keys_per_part: list[list] = [ [ - state.validators[vid].get_attestation_pubkey() - for vid in proof.participants.to_validator_indices() + state.validators[validator_index].get_attestation_public_key() + for validator_index in proof.participants.to_validator_indices() ] for proof in attestation_proofs ] - public_keys_per_part.append([proposer_pubkey]) + public_keys_per_part.append([proposer_public_key]) proof = MultiMessageAggregate.aggregate( [*attestation_proofs, proposer_single_message_aggregate], @@ -358,15 +358,20 @@ def build_signed_block( # Separate valid and invalid attestation specs. # Valid specs go through normal aggregation; invalid specs get special proofs. invalid_specs = [ - att_spec - for att_spec in (self.attestations or []) - if not att_spec.valid_signature - or (att_spec.signer_ids is not None and att_spec.signer_ids != att_spec.validator_ids) + attestation_spec + for attestation_spec in (self.attestations or []) + if not attestation_spec.valid_signature + or ( + attestation_spec.signer_ids is not None + and attestation_spec.signer_ids != attestation_spec.validator_indices + ) ] # Build a valid-only copy for normal attestation construction. self.attestations = [ - att_spec for att_spec in (self.attestations or []) if att_spec not in invalid_specs + attestation_spec + for attestation_spec in (self.attestations or []) + if attestation_spec not in invalid_specs ] valid_only = self @@ -378,26 +383,28 @@ def build_signed_block( # Group attestations that share the same AttestationData. # Validators seeing the same head/source/target produce identical data, # so they can be merged into a single aggregated attestation. - data_to_validator_ids: dict[AttestationData, list[ValidatorIndex]] = defaultdict(list) + data_to_validator_indices: dict[AttestationData, list[ValidatorIndex]] = defaultdict(list) for attestation in valid_attestations: - data_to_validator_ids[attestation.data].append(attestation.validator_id) + data_to_validator_indices[attestation.data].append(attestation.validator_index) # Build one AggregatedAttestation per unique data. # Each carries a bitfield marking which validators participated. aggregated_attestations = [ AggregatedAttestation( - aggregation_bits=ValidatorIndices(data=validator_ids).to_aggregation_bits(), + aggregation_bits=ValidatorIndices(data=validator_indices).to_aggregation_bits(), data=data, ) - for data, validator_ids in data_to_validator_ids.items() + for data, validator_indices in data_to_validator_indices.items() ] - attestation_sigs = key_manager.build_attestation_proofs( + attestation_signatures = key_manager.build_attestation_proofs( AggregatedAttestations(data=aggregated_attestations), signature_lookup=signature_lookup, ) aggregated_payloads = { - agg_att.data: {proof} - for agg_att, proof in zip(aggregated_attestations, attestation_sigs, strict=True) + aggregate_attestation.data: {proof} + for aggregate_attestation, proof in zip( + aggregated_attestations, attestation_signatures, strict=True + ) } final_block, _, _, aggregated_signatures = spec.build_block( @@ -491,16 +498,16 @@ def build_signed_block_with_store( # Gossip valid attestation signatures into the Store. # This runs signature verification through the spec's validation path. for attestation in valid_attestations: - sigs_for_data = attestation_signatures.get(attestation.data) + signatures_for_data = attestation_signatures.get(attestation.data) if ( - sigs_for_data is None - or (signature := sigs_for_data.get(attestation.validator_id)) is None + signatures_for_data is None + or (signature := signatures_for_data.get(attestation.validator_index)) is None ): continue store = spec.on_gossip_attestation( store, SignedAttestation( - validator_id=attestation.validator_id, + validator_index=attestation.validator_index, data=attestation.data, signature=signature, ), @@ -535,18 +542,22 @@ def build_signed_block_with_store( # Append forced attestations that bypass the builder's MAX cap. # Each entry is signed and aggregated so the block carries valid proofs. if self.forced_attestations: - for att_spec in self.forced_attestations: - att_data = att_spec.build_attestation_data(block_registry, parent_state) - proof = key_manager.sign_and_aggregate(att_spec.validator_ids, att_data) + for attestation_spec in self.forced_attestations: + attestation_data = attestation_spec.build_attestation_data( + block_registry, parent_state + ) + proof = key_manager.sign_and_aggregate( + attestation_spec.validator_indices, attestation_data + ) block_proofs.append(proof) final_block.body.attestations = AggregatedAttestations( data=[ *final_block.body.attestations.data, AggregatedAttestation( aggregation_bits=ValidatorIndices( - data=att_spec.validator_ids, + data=attestation_spec.validator_indices, ).to_aggregation_bits(), - data=att_data, + data=attestation_data, ), ] ) diff --git a/packages/testing/src/consensus_testing/test_types/gossip_aggregated_attestation_spec.py b/packages/testing/src/consensus_testing/test_types/gossip_aggregated_attestation_spec.py index 5046c4c87..dcff401e9 100644 --- a/packages/testing/src/consensus_testing/test_types/gossip_aggregated_attestation_spec.py +++ b/packages/testing/src/consensus_testing/test_types/gossip_aggregated_attestation_spec.py @@ -24,7 +24,7 @@ class GossipAggregatedAttestationSpec(CamelModel): The spec allows overriding head/source checkpoints to exercise validation logic. """ - validator_ids: list[ValidatorIndex] + validator_indices: list[ValidatorIndex] """Claimed validators participating in the aggregation.""" slot: Slot @@ -176,8 +176,8 @@ def build_signed( # - Claimed validators appear in the proof's participant bitfield. # - Actual signers produce the cryptographic material. # They default to the same set for honest attestations. - validator_ids = self.validator_ids - signer_ids = self.signer_ids or self.validator_ids + validator_indices = self.validator_indices + signer_ids = self.signer_ids or self.validator_indices # Path 1: Invalid signature. # @@ -186,7 +186,7 @@ def build_signed( if not self.valid_signature: placeholder = ByteList512KiB(data=b"\x00" * 32) proof = SingleMessageAggregate( - participants=ValidatorIndices(data=validator_ids).to_aggregation_bits(), + participants=ValidatorIndices(data=validator_indices).to_aggregation_bits(), proof=placeholder, ) return SignedAggregatedAttestation(data=attestation_data, proof=proof) @@ -201,9 +201,9 @@ def build_signed( # The proof is cryptographically valid for the actual signers, # but the claimed participants no longer match. # The store must detect and reject this inconsistency. - if self.signer_ids and self.signer_ids != self.validator_ids: + if self.signer_ids and self.signer_ids != self.validator_indices: proof = SingleMessageAggregate( - participants=ValidatorIndices(data=validator_ids).to_aggregation_bits(), + participants=ValidatorIndices(data=validator_indices).to_aggregation_bits(), proof=proof.proof, ) diff --git a/packages/testing/src/consensus_testing/test_types/gossip_attestation_spec.py b/packages/testing/src/consensus_testing/test_types/gossip_attestation_spec.py index bf80be5b9..2506e38e9 100644 --- a/packages/testing/src/consensus_testing/test_types/gossip_attestation_spec.py +++ b/packages/testing/src/consensus_testing/test_types/gossip_attestation_spec.py @@ -22,7 +22,7 @@ class GossipAttestationSpec(CamelModel): Similar to AggregatedAttestationSpec but for individual gossip attestations. """ - validator_id: ValidatorIndex + validator_index: ValidatorIndex """The index of the validator making the attestation (required).""" slot: Slot @@ -205,13 +205,13 @@ def build_signed( attestation_data = LstarSpec().produce_attestation_data(store, self.slot) signature = ( - key_manager.sign_attestation_data(self.validator_id, attestation_data) + key_manager.sign_attestation_data(self.validator_index, attestation_data) if self.valid_signature else create_dummy_signature() ) return SignedAttestation( - validator_id=self.validator_id, + validator_index=self.validator_index, data=attestation_data, signature=signature, ) diff --git a/packages/testing/src/consensus_testing/test_types/step_types.py b/packages/testing/src/consensus_testing/test_types/step_types.py index 9cead89a9..ec9768f05 100644 --- a/packages/testing/src/consensus_testing/test_types/step_types.py +++ b/packages/testing/src/consensus_testing/test_types/step_types.py @@ -102,7 +102,7 @@ class BlockStep(BaseForkChoiceStep): in the private _filled_block attribute for serialization. """ - # TODO: We should figure out a configuration to raise if a private attr is + # TODO: We should figure out a configuration to raise if a private attribute is # attempted to be set during model initialization. _filled_block: Block | None = PrivateAttr(default=None) """The filled Block, processed through the spec.""" diff --git a/packages/testing/src/consensus_testing/test_types/store_checks.py b/packages/testing/src/consensus_testing/test_types/store_checks.py index 7d3abbd51..c222da311 100644 --- a/packages/testing/src/consensus_testing/test_types/store_checks.py +++ b/packages/testing/src/consensus_testing/test_types/store_checks.py @@ -435,14 +435,15 @@ def _resolve(label: str) -> Bytes32: ) actual_count = len(filled_block.body.attestations.data) if actual_count != self.block_attestation_count: - att_info = [ - f" - participants={list(att.aggregation_bits.to_validator_indices())}" - for att in filled_block.body.attestations.data + attestation_info = [ + f" - participants={list(attestation.aggregation_bits.to_validator_indices())}" + for attestation in filled_block.body.attestations.data ] raise AssertionError( f"Step {step_index}: block body has {actual_count} aggregated " f"attestations, expected {self.block_attestation_count}\n" - f"Attestations in block:\n" + ("\n".join(att_info) if att_info else " (empty)") + f"Attestations in block:\n" + + ("\n".join(attestation_info) if attestation_info else " (empty)") ) # Detailed block body attestation structure @@ -499,21 +500,23 @@ def _validate_block_attestations( """Validate detailed attestation structure in the block body.""" actual_attestations = filled_block.body.attestations.data actual_participants_list = [ - {int(v) for v in att.aggregation_bits.to_validator_indices()} - for att in actual_attestations + {int(v) for v in attestation.aggregation_bits.to_validator_indices()} + for attestation in actual_attestations ] for check in expected_checks: - matching_att = None - matching_idx = None - for idx, att in enumerate(actual_attestations): - actual_participants = {int(v) for v in att.aggregation_bits.to_validator_indices()} + matching_attestation = None + matching_index = None + for index, attestation in enumerate(actual_attestations): + actual_participants = { + int(v) for v in attestation.aggregation_bits.to_validator_indices() + } if actual_participants == check.participants: - matching_att = att - matching_idx = idx + matching_attestation = attestation + matching_index = index break - if matching_att is None: + if matching_attestation is None: raise AssertionError( f"Step {step_index}: no aggregated attestation found with " f"participants={check.participants}\n" @@ -521,19 +524,19 @@ def _validate_block_attestations( ) if check.attestation_slot is not None: - if matching_att.data.slot != check.attestation_slot: + if matching_attestation.data.slot != check.attestation_slot: raise AssertionError( - f"Step {step_index}: attestation[{matching_idx}] with " + f"Step {step_index}: attestation[{matching_index}] with " f"participants={check.participants} has " - f"slot={matching_att.data.slot}, expected {check.attestation_slot}" + f"slot={matching_attestation.data.slot}, expected {check.attestation_slot}" ) if check.target_slot is not None: - if matching_att.data.target.slot != check.target_slot: + if matching_attestation.data.target.slot != check.target_slot: raise AssertionError( - f"Step {step_index}: attestation[{matching_idx}] with " + f"Step {step_index}: attestation[{matching_index}] with " f"participants={check.participants} has " - f"target_slot={matching_att.data.target.slot}, " + f"target_slot={matching_attestation.data.target.slot}, " f"expected {check.target_slot}" ) @@ -570,11 +573,11 @@ def _validate_lexicographic_head( ) weight = 0 for attestation in known_attestations.values(): - att_head_root = attestation.head.root - if att_head_root == root: + attestation_head_root = attestation.head.root + if attestation_head_root == root: weight += 1 - elif att_head_root in store.blocks: - current = att_head_root + elif attestation_head_root in store.blocks: + current = attestation_head_root while current in store.blocks and store.blocks[current].slot > slot: parent = store.blocks[current].parent_root if parent == root: diff --git a/packages/testing/src/framework/cli/fill.py b/packages/testing/src/framework/cli/fill.py index cf7656585..a3f2f8758 100644 --- a/packages/testing/src/framework/cli/fill.py +++ b/packages/testing/src/framework/cli/fill.py @@ -78,12 +78,12 @@ def fill( # Check and download keys if needed (only for consensus layer) if layer.lower() == "consensus": # Import here to avoid loading leanSpec modules before LEAN_ENV is set - from consensus_testing.keys import download_keys, get_keys_dir + from consensus_testing.keys import download_keys, get_keys_directory - keys_dir = get_keys_dir(scheme.lower()) + keys_directory = get_keys_directory(scheme.lower()) # Check if keys already exist, if not, download them - if not (keys_dir.exists() and any(keys_dir.glob("*.json"))): + if not (keys_directory.exists() and any(keys_directory.glob("*.json"))): click.echo(f"Test keys for '{scheme}' scheme not found. Downloading...") download_keys(scheme.lower()) diff --git a/packages/testing/src/framework/pytest_plugins/filler.py b/packages/testing/src/framework/pytest_plugins/filler.py index af5eec73a..905b45b2b 100644 --- a/packages/testing/src/framework/pytest_plugins/filler.py +++ b/packages/testing/src/framework/pytest_plugins/filler.py @@ -15,16 +15,16 @@ class FixtureCollector: """Collects generated fixtures and writes them to disk.""" - def __init__(self, output_dir: Path, fork: str, layer: str): + def __init__(self, output_directory: Path, fork: str, layer: str): """ Initialize the fixture collector. Args: - output_dir: Root directory for generated fixtures. + output_directory: Root directory for generated fixtures. fork: The fork name (e.g., "Lstar"). layer: The Ethereum layer (e.g., "consensus", "execution"). """ - self.output_dir = output_dir + self.output_directory = output_directory self.fork = fork self.layer = layer self.fixtures: list[tuple[str, str, Any, str]] = [] @@ -69,13 +69,13 @@ def add_fixture( test_path = relative_path.with_suffix("") # Build output path: fixtures/{layer}/{format}/{test_path} - format_dir = fixture_format.replace("_test", "") - fixture_dir = self.output_dir / layer / format_dir / test_path - fixture_path = fixture_dir / f"{base_func_name}.json" + format_directory = fixture_format.replace("_test", "") + fixture_directory = self.output_directory / layer / format_directory / test_path + fixture_path = fixture_directory / f"{base_func_name}.json" - config.fixture_path_absolute = str(fixture_path.absolute()) # type: ignore[attr-defined] - config.fixture_path_relative = str(fixture_path.relative_to(self.output_dir)) # type: ignore[attr-defined] - config.fixture_format = fixture_format # type: ignore[attr-defined] + config.fixture_path_absolute = str(fixture_path.absolute()) # type: ignore[attribute-defined] + config.fixture_path_relative = str(fixture_path.relative_to(self.output_directory)) # type: ignore[attribute-defined] + config.fixture_format = fixture_format # type: ignore[attribute-defined] def write_fixtures(self) -> None: """Write all collected fixtures to disk, grouped by test function.""" @@ -104,11 +104,11 @@ def write_fixtures(self) -> None: test_path = relative_path.with_suffix("") # Build output path: fixtures/{layer}/{format}/{test_path} - format_dir = fixture_format.replace("_test", "") - fixture_dir = self.output_dir / self.layer / format_dir / test_path - fixture_dir.mkdir(parents=True, exist_ok=True) + format_directory = fixture_format.replace("_test", "") + fixture_directory = self.output_directory / self.layer / format_directory / test_path + fixture_directory.mkdir(parents=True, exist_ok=True) - output_file = fixture_dir / f"{base_func_name}.json" + output_file = fixture_directory / f"{base_func_name}.json" all_tests = {} for _, fixture, test_nodeid in fixtures_list: @@ -198,12 +198,12 @@ def pytest_configure(config: pytest.Config) -> None: ) # Store layer for later use (needed by pytest_ignore_collect hook) - config.test_layer = layer # type: ignore[attr-defined] + config.test_layer = layer # type: ignore[attribute-defined] # Dynamically import layer-specific package try: layer_module = importlib.import_module(f"{layer}_testing") - config.layer_module = layer_module # type: ignore[attr-defined] + config.layer_module = layer_module # type: ignore[attribute-defined] except ImportError as e: pytest.exit( f"Failed to import {layer}_testing module: {e}", @@ -228,7 +228,7 @@ def pytest_configure(config: pytest.Config) -> None: ) # Get options - output_dir = Path(config.getoption("--output")) + output_directory = Path(config.getoption("--output")) fork_name = config.getoption("--fork") clean = config.getoption("--clean") @@ -258,25 +258,25 @@ def pytest_configure(config: pytest.Config) -> None: pytest.exit("Invalid fork specified.", returncode=pytest.ExitCode.USAGE_ERROR) # Check output directory - if output_dir.exists() and any(output_dir.iterdir()): + if output_directory.exists() and any(output_directory.iterdir()): if not clean: - contents = list(output_dir.iterdir()) + contents = list(output_directory.iterdir()) summary = ", ".join(item.name for item in contents[:5]) if len(contents) > 5: summary += ", ..." pytest.exit( - f"Output directory '{output_dir}' is not empty. " + f"Output directory '{output_directory}' is not empty. " f"Contains: {summary}. Use --clean to remove all existing files " "or specify a different output directory.", returncode=pytest.ExitCode.USAGE_ERROR, ) - shutil.rmtree(output_dir) + shutil.rmtree(output_directory) - output_dir.mkdir(parents=True, exist_ok=True) + output_directory.mkdir(parents=True, exist_ok=True) # Create collector with layer info - config.fixture_collector = FixtureCollector(output_dir, fork_name, layer) # type: ignore[attr-defined] - config.test_fork_class = fork_class # type: ignore[attr-defined] + config.fixture_collector = FixtureCollector(output_directory, fork_name, layer) # type: ignore[attribute-defined] + config.test_fork_class = fork_class # type: ignore[attribute-defined] def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]) -> None: @@ -285,7 +285,7 @@ def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item return fork_class = config.test_fork_class - layer_module = config.layer_module # type: ignore[attr-defined] + layer_module = config.layer_module # type: ignore[attribute-defined] registry = layer_module.forks.registry verbose = config.getoption("verbose") deselected = [] @@ -429,7 +429,7 @@ def pre(request: pytest.FixtureRequest, fork: Any) -> Any: Tests can request this fixture to customize the initial state, or omit it to use the default (auto-injected by framework). """ - layer = request.config.test_layer # type: ignore[attr-defined] + layer = request.config.test_layer # type: ignore[attribute-defined] if layer == "execution": pytest.exit( @@ -437,7 +437,7 @@ def pre(request: pytest.FixtureRequest, fork: Any) -> Any: returncode=pytest.ExitCode.USAGE_ERROR, ) - layer_module = request.config.layer_module # type: ignore[attr-defined] + layer_module = request.config.layer_module # type: ignore[attribute-defined] spec = fork.spec_class()() if hasattr(request, "param"): @@ -510,8 +510,8 @@ def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: if "fork" not in metafunc.fixturenames: return - fork_class = metafunc.config.test_fork_class # type: ignore[attr-defined] - layer_module = metafunc.config.layer_module # type: ignore[attr-defined] + fork_class = metafunc.config.test_fork_class # type: ignore[attribute-defined] + layer_module = metafunc.config.layer_module # type: ignore[attribute-defined] registry = layer_module.forks.registry if not _is_test_valid_for_fork(metafunc, fork_class, registry.get_fork_by_name): diff --git a/src/lean_spec/cli/args.py b/src/lean_spec/cli/args.py index 2e9ccd523..7ad9e91f4 100644 --- a/src/lean_spec/cli/args.py +++ b/src/lean_spec/cli/args.py @@ -24,7 +24,7 @@ class CliArgs(StrictBaseModel): bootnodes: tuple[str, ...] """Raw bootnode strings, each either a multiaddr or an ENR.""" - listen_addr: str + listen_address: str """Multiaddr the node binds for inbound QUIC connections.""" checkpoint_sync_url: str | None @@ -86,7 +86,7 @@ def parse_args(argv: list[str] | None = None) -> CliArgs: parser.add_argument( "--listen", default="/ip4/0.0.0.0/udp/9001/quic-v1", - dest="listen_addr", + dest="listen_address", help="Address to listen on (default: /ip4/0.0.0.0/udp/9001/quic-v1)", ) parser.add_argument( @@ -151,7 +151,7 @@ def parse_args(argv: list[str] | None = None) -> CliArgs: return CliArgs( genesis_path=namespace.genesis_path, bootnodes=tuple(namespace.bootnodes), - listen_addr=namespace.listen_addr, + listen_address=namespace.listen_address, checkpoint_sync_url=namespace.checkpoint_sync_url, validator_keys_path=namespace.validator_keys_path, node_id=namespace.node_id, diff --git a/src/lean_spec/cli/bootstrap.py b/src/lean_spec/cli/bootstrap.py index 51398a93c..b02abc0ca 100644 --- a/src/lean_spec/cli/bootstrap.py +++ b/src/lean_spec/cli/bootstrap.py @@ -54,7 +54,7 @@ class NodeBootstrap: Any ENR inputs are already resolved to plain multiaddrs.""" - listen_addr: str | None + listen_address: str | None """Inbound listen address. A value of None means a dial-only configuration.""" @@ -142,7 +142,7 @@ def from_cli_args(cls, args: CliArgs) -> NodeBootstrap: ValidatorRegistry() if args.validator_keys_path is None else ValidatorRegistry.from_keys_directory( - node_id=args.node_id, base_dir=args.validator_keys_path + node_id=args.node_id, base_directory=args.validator_keys_path ) ) if len(registry) > 0: @@ -196,7 +196,7 @@ def from_cli_args(cls, args: CliArgs) -> NodeBootstrap: registry=registry, fork=DEFAULT_REGISTRY.current, bootnode_multiaddrs=tuple(bootnode_multiaddrs), - listen_addr=args.listen_addr or None, + listen_address=args.listen_address or None, checkpoint_sync_url=args.checkpoint_sync_url, node_id=args.node_id, is_aggregator=args.is_aggregator, @@ -220,5 +220,5 @@ async def build_anchor(self) -> Anchor: url=self.checkpoint_sync_url, genesis=self.genesis, fork=self.fork, - validator_id=self.registry.primary_index(), + validator_index=self.registry.primary_index(), ) diff --git a/src/lean_spec/cli/run.py b/src/lean_spec/cli/run.py index 641ecc325..01b260c04 100644 --- a/src/lean_spec/cli/run.py +++ b/src/lean_spec/cli/run.py @@ -46,7 +46,7 @@ async def _build_event_source(boot: NodeBootstrap) -> LiveNetworkEventSource: # Validator-owned subnets carry the attestations the mesh must propagate. # Missing one of them breaks the attestation flow for that validator. subnets: set[SubnetId] = { - idx.compute_subnet_id(ATTESTATION_COMMITTEE_COUNT) for idx in boot.registry.indices() + index.compute_subnet_id(ATTESTATION_COMMITTEE_COUNT) for index in boot.registry.indices() } # Aggregator extras are advisory subnets the operator wants this node to see. @@ -110,7 +110,7 @@ async def run_node(boot: NodeBootstrap) -> None: await event_source.start_serving( status=anchor.initial_status, current_slot_lookup=node.clock.current_slot, - listen_addr=boot.listen_addr, + listen_address=boot.listen_address, bootnode_multiaddrs=boot.bootnode_multiaddrs, ) diff --git a/src/lean_spec/node/anchor.py b/src/lean_spec/node/anchor.py index b1dc3fc41..da58644ef 100644 --- a/src/lean_spec/node/anchor.py +++ b/src/lean_spec/node/anchor.py @@ -78,7 +78,7 @@ async def from_checkpoint( url: str, genesis: GenesisConfig, fork: ForkProtocol, - validator_id: ValidatorIndex | None, + validator_index: ValidatorIndex | None, ) -> Anchor: """ Build an anchor by fetching a finalized state from a peer. @@ -90,7 +90,7 @@ async def from_checkpoint( url: HTTP endpoint of the node serving the checkpoint state. genesis: Local genesis used as a chain-identity guard via genesis time. fork: Fork specification driving state and store construction. - validator_id: Local validator index used as a forkchoice tiebreaker hint. + validator_index: Local validator index used as a forkchoice tiebreaker hint. Raises: CheckpointSyncError: For every failure mode covering transport, @@ -127,7 +127,7 @@ async def from_checkpoint( ) # The protocol return type is structural, but only one concrete store ships. - store = cast(Store, fork.create_store(state, anchor_block, validator_id)) + store = cast(Store, fork.create_store(state, anchor_block, validator_index)) head_slot = store.blocks[store.head].slot return cls( diff --git a/src/lean_spec/node/api/endpoints/checkpoints.py b/src/lean_spec/node/api/endpoints/checkpoints.py index 232925de9..371c86c8a 100644 --- a/src/lean_spec/node/api/endpoints/checkpoints.py +++ b/src/lean_spec/node/api/endpoints/checkpoints.py @@ -15,7 +15,7 @@ async def handle_justified(request: web.Request) -> web.Response: Response: JSON object with fields: - slot (integer): The slot number of the justified checkpoint. - - root (string): The block root as 0x-prefixed hex string (66 chars total). + - root (string): The block root as 0x-prefixed hex string (66 characters total). Status Codes: 200 OK: Checkpoint returned successfully. diff --git a/src/lean_spec/node/chain/service.py b/src/lean_spec/node/chain/service.py index 407e9a2a3..f53e9f0d4 100644 --- a/src/lean_spec/node/chain/service.py +++ b/src/lean_spec/node/chain/service.py @@ -130,8 +130,8 @@ async def run(self) -> None: # No publisher is wired in tests and offline runs. publish = self.sync_service.publish_aggregated_attestation if new_aggregated_attestations and publish is not None: - for agg in new_aggregated_attestations: - await publish(agg) + for aggregate in new_aggregated_attestations: + await publish(aggregate) logger.info( "Tick: slot=%d interval=%d head=%s finalized=slot%d", diff --git a/src/lean_spec/node/genesis/config.py b/src/lean_spec/node/genesis/config.py index 6c8165725..328921460 100644 --- a/src/lean_spec/node/genesis/config.py +++ b/src/lean_spec/node/genesis/config.py @@ -6,10 +6,10 @@ GENESIS_TIME: 1704085200 GENESIS_VALIDATORS: - - attestation_pubkey: 0xe2a03c16122c7e0f... - proposal_pubkey: 0x0767e65924063f79... - - attestation_pubkey: 0xabcdef0123456789... - proposal_pubkey: 0x9876543210fedcba... + - attestation_public_key: 0xe2a03c16122c7e0f... + proposal_public_key: 0x0767e65924063f79... + - attestation_public_key: 0xabcdef0123456789... + proposal_public_key: 0x9876543210fedcba... """ from __future__ import annotations @@ -28,13 +28,13 @@ class GenesisValidatorEntry(StrictBaseModel): """A single validator's public keys in the genesis configuration.""" - attestation_pubkey: Bytes52 + attestation_public_key: Bytes52 """XMSS public key for signing attestations.""" - proposal_pubkey: Bytes52 + proposal_public_key: Bytes52 """XMSS public key for signing proposer attestations in blocks.""" - @field_validator("attestation_pubkey", "proposal_pubkey", mode="before") + @field_validator("attestation_public_key", "proposal_public_key", mode="before") @classmethod def _yaml_int_to_hex(cls, v: Any) -> Any: """ @@ -87,8 +87,8 @@ class GenesisConfig(StrictBaseModel): Each entry contains two XMSS public keys: - - attestation_pubkey: for signing attestations - - proposal_pubkey: for signing proposer attestations in blocks + - attestation_public_key: for signing attestations + - proposal_public_key: for signing proposer attestations in blocks Security note: 2/3+ collusion controls the chain until new validators join. """ @@ -103,8 +103,8 @@ def to_validators(self) -> Validators: return Validators( data=[ Validator( - attestation_pubkey=entry.attestation_pubkey, - proposal_pubkey=entry.proposal_pubkey, + attestation_public_key=entry.attestation_public_key, + proposal_public_key=entry.proposal_public_key, index=ValidatorIndex(i), ) for i, entry in enumerate(self.genesis_validators) diff --git a/src/lean_spec/node/metrics/registry.py b/src/lean_spec/node/metrics/registry.py index 29bb5ba07..23d4ba0e2 100644 --- a/src/lean_spec/node/metrics/registry.py +++ b/src/lean_spec/node/metrics/registry.py @@ -47,14 +47,14 @@ # Section labels for attestation aggregate coverage gauges. These match the # names printed in slot/report logs: timely, late, block, combined, -# agg_start_new, proposal_payloads, proposal_gossip, and proposal_combined. +# aggregate_start_new, proposal_payloads, proposal_gossip, and proposal_combined. # Slot is the X-axis (time series progression), not a label dimension. ATTESTATION_AGGREGATE_COVERAGE_SECTIONS = ( "timely", "late", "block", "combined", - "agg_start_new", + "aggregate_start_new", "proposal_payloads", "proposal_gossip", "proposal_combined", diff --git a/src/lean_spec/node/networking/client/event_source/gossip.py b/src/lean_spec/node/networking/client/event_source/gossip.py index ed4bb069c..ffc002c2b 100644 --- a/src/lean_spec/node/networking/client/event_source/gossip.py +++ b/src/lean_spec/node/networking/client/event_source/gossip.py @@ -208,7 +208,7 @@ async def read_gossip_message(stream: InboundStreamProtocol) -> tuple[str, bytes Gossip message wire format:: - [topic_len: varint][topic: UTF-8][data_len: varint][data: bytes] + [topic_length: varint][topic: UTF-8][data_length: varint][data: bytes] Args: stream: QUIC stream to read from. @@ -282,24 +282,24 @@ async def read_gossip_message(stream: InboundStreamProtocol) -> tuple[str, bytes # # The varint tells us how many bytes the topic string occupies. # Most topics are ~50 bytes, so this is typically a 1-byte varint. - topic_len, topic_len_bytes = decode_varint(bytes(buffer), 0) - topic_end = topic_len_bytes + topic_len + topic_length, topic_length_bytes = decode_varint(bytes(buffer), 0) + topic_end = topic_length_bytes + topic_length if len(buffer) >= topic_end: # We have the complete topic string. # # Topics are UTF-8 encoded. Invalid encoding indicates # a protocol violation or corrupted data. - topic_str = buffer[topic_len_bytes:topic_end].decode("utf-8") + topic_str = buffer[topic_length_bytes:topic_end].decode("utf-8") if len(buffer) > topic_end: # Parse data length varint. # # This tells us how many bytes of compressed data follow. # Block messages can be several hundred KB compressed. - data_len, data_len_bytes = decode_varint(bytes(buffer), topic_end) - data_start = topic_end + data_len_bytes - data_end = data_start + data_len + data_length, data_length_bytes = decode_varint(bytes(buffer), topic_end) + data_start = topic_end + data_length_bytes + data_end = data_start + data_length if len(buffer) >= data_end: # We have the complete message. diff --git a/src/lean_spec/node/networking/client/event_source/live.py b/src/lean_spec/node/networking/client/event_source/live.py index da3e5c37a..b3b13b6b6 100644 --- a/src/lean_spec/node/networking/client/event_source/live.py +++ b/src/lean_spec/node/networking/client/event_source/live.py @@ -388,7 +388,7 @@ async def start_serving( *, status: Status, current_slot_lookup: CurrentSlotLookup, - listen_addr: str | None, + listen_address: str | None, bootnode_multiaddrs: Sequence[str], ) -> None: """ @@ -405,7 +405,7 @@ async def start_serving( Args: status: Initial finalized and head checkpoints the responder serves. current_slot_lookup: Wall-clock-to-slot callback for range bounds. - listen_addr: Multiaddr to bind for inbound connections, or None for dial-only. + listen_address: Multiaddr to bind for inbound connections, or None for dial-only. bootnode_multiaddrs: Pre-resolved outbound peers. Raises: @@ -432,9 +432,9 @@ async def start_serving( else: logger.warning("Failed to connect to bootnode %s", multiaddr) - if listen_addr: - logger.info("Starting listener on %s", listen_addr) - listener_task = asyncio.create_task(self.listen(listen_addr)) + if listen_address: + logger.info("Starting listener on %s", listen_address) + listener_task = asyncio.create_task(self.listen(listen_address)) # Surface bind failures synchronously instead of as silent crashes. await asyncio.sleep(0.1) @@ -488,11 +488,11 @@ async def _handle_gossipsub_message(self, event: GossipsubMessageEvent) -> None: block = SignedBlock.decode_bytes(event.data) await self._emit_gossip_block(block, event.peer_id) case TopicKind.ATTESTATION_SUBNET: - att = SignedAttestation.decode_bytes(event.data) - await self._emit_gossip_attestation(att, event.peer_id) + attestation = SignedAttestation.decode_bytes(event.data) + await self._emit_gossip_attestation(attestation, event.peer_id) case TopicKind.AGGREGATED_ATTESTATION: - agg = SignedAggregatedAttestation.decode_bytes(event.data) - await self._emit_gossip_aggregated_attestation(agg, event.peer_id) + aggregate = SignedAggregatedAttestation.decode_bytes(event.data) + await self._emit_gossip_aggregated_attestation(aggregate, event.peer_id) except SSZSerializationError as e: raise GossipMessageError(f"SSZ decode failed: {e}") from e @@ -557,15 +557,15 @@ async def dial(self, multiaddr: str) -> PeerId | None: try: # Detect transport type and connect accordingly. if is_quic_multiaddr(multiaddr): - conn = await self._dial_quic(multiaddr) + connection = await self._dial_quic(multiaddr) else: - conn = await self.connection_manager.connect(multiaddr) + connection = await self.connection_manager.connect(multiaddr) - peer_id = conn.peer_id + peer_id = connection.peer_id # Register connection. - self._connections[peer_id] = conn - self.reqresp_client.register_connection(peer_id, conn) + self._connections[peer_id] = connection + self.reqresp_client.register_connection(peer_id, connection) # Emit connected event. await self._events.put(PeerConnectedEvent(peer_id=peer_id)) @@ -575,15 +575,15 @@ async def dial(self, multiaddr: str) -> PeerId | None: # The peer (listener) will try to open an outbound gossipsub stream to us. # If we don't start accepting streams before our own operations, there's # a deadlock: we wait for them to accept our stream, they wait for us. - task = asyncio.create_task(self._accept_streams(peer_id, conn)) + task = asyncio.create_task(self._accept_streams(peer_id, connection)) self._gossip_tasks.add(task) task.add_done_callback(self._gossip_tasks.discard) # Exchange status. - await self._exchange_status(peer_id, conn) + await self._exchange_status(peer_id, connection) # Set up gossipsub stream for full protocol support. - await self._setup_gossipsub_stream(peer_id, conn) + await self._setup_gossipsub_stream(peer_id, connection) logger.info("Connected to peer %s at %s", peer_id, multiaddr) return peer_id @@ -660,7 +660,7 @@ async def _listen_quic(self, multiaddr: str) -> None: on_connection=self._handle_inbound_connection, ) - async def _handle_inbound_connection(self, conn: QuicConnection) -> None: + async def _handle_inbound_connection(self, connection: QuicConnection) -> None: """ Handle a new inbound connection. @@ -669,19 +669,19 @@ async def _handle_inbound_connection(self, conn: QuicConnection) -> None: gossipsub setup are deferred to avoid race conditions. Args: - conn: Established connection. + connection: Established connection. """ - peer_id = conn.peer_id + peer_id = connection.peer_id # Register connection. - self._connections[peer_id] = conn - self.reqresp_client.register_connection(peer_id, conn) + self._connections[peer_id] = connection + self.reqresp_client.register_connection(peer_id, connection) # Emit connected event. await self._events.put(PeerConnectedEvent(peer_id=peer_id)) # Start accepting streams to handle peer's requests. - task = asyncio.create_task(self._accept_streams(peer_id, conn)) + task = asyncio.create_task(self._accept_streams(peer_id, connection)) self._gossip_tasks.add(task) task.add_done_callback(self._gossip_tasks.discard) @@ -699,14 +699,14 @@ async def _handle_inbound_connection(self, conn: QuicConnection) -> None: async def _exchange_status( self, peer_id: PeerId, - conn: QuicConnection, + connection: QuicConnection, ) -> None: """ Exchange Status messages with a peer. Args: peer_id: Peer identifier. - conn: QuicConnection to use. + connection: QuicConnection to use. """ if self._our_status is None: logger.debug("No status set, skipping status exchange") @@ -729,7 +729,7 @@ async def _exchange_status( async def _setup_gossipsub_stream( self, peer_id: PeerId, - conn: QuicConnection, + connection: QuicConnection, ) -> None: """ Set up the GossipSub stream for a peer. @@ -742,7 +742,7 @@ async def _setup_gossipsub_stream( Args: peer_id: Peer identifier. - conn: QuicConnection to use. + connection: QuicConnection to use. """ # Why: # The dialing path and the inbound-stream handler can both reach this @@ -758,7 +758,7 @@ async def _setup_gossipsub_stream( return try: # Open the gossipsub stream. - stream = await conn.open_stream(GOSSIPSUB_DEFAULT_PROTOCOL_ID) + stream = await connection.open_stream(GOSSIPSUB_DEFAULT_PROTOCOL_ID) logger.info( "Opened outbound gossipsub stream_id=%d to %s (expect odd=server-initiated)", stream.stream_id, @@ -787,11 +787,11 @@ async def disconnect(self, peer_id: PeerId) -> None: Args: peer_id: Peer to disconnect. """ - conn = self._connections.pop(peer_id, None) + connection = self._connections.pop(peer_id, None) self._outbound_setup_locks.pop(peer_id, None) - if conn is not None: + if connection is not None: self.reqresp_client.unregister_connection(peer_id) - await conn.close() + await connection.close() await self._events.put(PeerDisconnectedEvent(peer_id=peer_id)) logger.info("Disconnected from peer %s", peer_id) @@ -870,7 +870,7 @@ async def _emit_gossip_aggregated_attestation( ) ) - async def _accept_streams(self, peer_id: PeerId, conn: QuicConnection) -> None: + async def _accept_streams(self, peer_id: PeerId, connection: QuicConnection) -> None: """ Accept incoming streams from a connection. @@ -879,7 +879,7 @@ async def _accept_streams(self, peer_id: PeerId, conn: QuicConnection) -> None: Args: peer_id: Peer that owns the connection. - conn: QUIC connection to accept streams from. + connection: QUIC connection to accept streams from. WHY BACKGROUND STREAM ACCEPTANCE? @@ -921,7 +921,7 @@ async def _accept_streams(self, peer_id: PeerId, conn: QuicConnection) -> None: # # This blocks until a peer opens a stream or the connection closes. # QUIC handles the low-level multiplexing. - stream = await conn.accept_stream() + stream = await connection.accept_stream() except Exception as e: # Connection closed or other transport error. # @@ -937,7 +937,9 @@ async def _accept_streams(self, peer_id: PeerId, conn: QuicConnection) -> None: protocol_id, wrapper = negotiated if protocol_id in (GOSSIPSUB_DEFAULT_PROTOCOL_ID, GOSSIPSUB_PROTOCOL_ID_V12): - await self._handle_gossipsub_inbound_stream(peer_id, conn, protocol_id, wrapper) + await self._handle_gossipsub_inbound_stream( + peer_id, connection, protocol_id, wrapper + ) elif protocol_id in REQRESP_PROTOCOL_IDS: self._handle_reqresp_inbound_stream(peer_id, protocol_id, wrapper) else: @@ -980,7 +982,7 @@ async def _negotiate_inbound_stream( Args: peer_id: Peer that owns the connection (for log context). - stream: Raw stream returned by ``conn.accept_stream``. + stream: Raw stream returned by ``connection.accept_stream``. Returns: Tuple of (protocol_id, wrapper) on success, None on failure. @@ -1050,7 +1052,7 @@ async def _negotiate_inbound_stream( async def _handle_gossipsub_inbound_stream( self, peer_id: PeerId, - conn: QuicConnection, + connection: QuicConnection, protocol_id: ProtocolId, wrapper: QuicStreamAdapter, ) -> None: @@ -1059,7 +1061,7 @@ async def _handle_gossipsub_inbound_stream( Args: peer_id: Peer that opened the stream. - conn: Connection the stream belongs to (used to open our + connection: Connection the stream belongs to (used to open our outbound stream when needed). protocol_id: Negotiated gossipsub protocol id (v1.2). wrapper: Adapter holding any bytes already buffered during @@ -1096,7 +1098,7 @@ async def _handle_gossipsub_inbound_stream( # # Idempotent and serialised internally: a concurrent dialing-path # setup for the same peer will not race with this one. - gossip_task = asyncio.create_task(self._setup_gossipsub_stream(peer_id, conn)) + gossip_task = asyncio.create_task(self._setup_gossipsub_stream(peer_id, connection)) self._gossip_tasks.add(gossip_task) gossip_task.add_done_callback(self._gossip_tasks.discard) diff --git a/src/lean_spec/node/networking/client/reqresp_client.py b/src/lean_spec/node/networking/client/reqresp_client.py index ff7cfd388..89090d10e 100644 --- a/src/lean_spec/node/networking/client/reqresp_client.py +++ b/src/lean_spec/node/networking/client/reqresp_client.py @@ -85,15 +85,15 @@ class ReqRespClient: timeout: float = REQUEST_TIMEOUT_SECONDS """Request timeout in seconds.""" - def register_connection(self, peer_id: PeerId, conn: QuicConnection) -> None: + def register_connection(self, peer_id: PeerId, connection: QuicConnection) -> None: """ Register a connection for req/resp use. Args: peer_id: Peer identifier. - conn: Established QUIC connection. + connection: Established QUIC connection. """ - self._connections[peer_id] = conn + self._connections[peer_id] = connection def unregister_connection(self, peer_id: PeerId) -> None: """ @@ -125,14 +125,14 @@ async def request_blocks_by_root( if not roots: return [] - conn = self._connections.get(peer_id) - if conn is None: + connection = self._connections.get(peer_id) + if connection is None: logger.debug("No connection to peer %s for blocks_by_root", peer_id) return [] try: return await asyncio.wait_for( - self._do_blocks_by_root_request(conn, roots), + self._do_blocks_by_root_request(connection, roots), timeout=self.timeout, ) except asyncio.TimeoutError: @@ -144,7 +144,7 @@ async def request_blocks_by_root( async def _do_blocks_by_root_request( self, - conn: QuicConnection, + connection: QuicConnection, roots: list[Bytes32], ) -> list[SignedBlock]: """ @@ -154,14 +154,14 @@ async def _do_blocks_by_root_request( and reads all response chunks. Args: - conn: QuicConnection to use. + connection: QuicConnection to use. roots: Block roots to request. Returns: List of blocks received. """ # Open a new stream and negotiate the protocol. - stream = await conn.open_stream(BLOCKS_BY_ROOT_PROTOCOL_V1) + stream = await connection.open_stream(BLOCKS_BY_ROOT_PROTOCOL_V1) try: # Build and send the request. @@ -244,14 +244,14 @@ async def request_blocks_by_range( # Range would overflow Uint64; cannot be satisfied. return [] - conn = self._connections.get(peer_id) - if conn is None: + connection = self._connections.get(peer_id) + if connection is None: logger.debug("No connection to peer %s for blocks_by_range", peer_id) return [] try: return await asyncio.wait_for( - self._do_blocks_by_range_request(conn, start_slot, count), + self._do_blocks_by_range_request(connection, start_slot, count), timeout=self.timeout, ) except CodecError: @@ -266,7 +266,7 @@ async def request_blocks_by_range( async def _do_blocks_by_range_request( self, - conn: QuicConnection, + connection: QuicConnection, start_slot: Slot, count: Uint64, ) -> list[SignedBlock]: @@ -277,7 +277,7 @@ async def _do_blocks_by_range_request( and reads all response chunks. Args: - conn: QuicConnection to use. + connection: QuicConnection to use. start_slot: Start slot of the range. count: Number of blocks to request. @@ -285,7 +285,7 @@ async def _do_blocks_by_range_request( List of blocks received. """ # Open a new stream and negotiate the protocol. - stream = await conn.open_stream(BLOCKS_BY_RANGE_PROTOCOL_V1) + stream = await connection.open_stream(BLOCKS_BY_RANGE_PROTOCOL_V1) end_slot_exclusive = int(start_slot) + int(count) try: @@ -302,8 +302,8 @@ async def _do_blocks_by_range_request( # Each block is sent as a separate response chunk. # We read until the stream closes or we get all blocks. blocks: list[SignedBlock] = [] - prev_slot: Slot | None = None - prev_root: Bytes32 | None = None + previous_slot: Slot | None = None + previous_root: Bytes32 | None = None stream_ended = False for _ in range(int(count)): @@ -321,8 +321,8 @@ async def _do_blocks_by_range_request( block_slot = inner.slot # Slots MUST be strictly increasing across the stream. - if prev_slot is not None and block_slot <= prev_slot: - raise CodecError(f"Non-monotonic slot: {block_slot} <= {prev_slot}") + if previous_slot is not None and block_slot <= previous_slot: + raise CodecError(f"Non-monotonic slot: {block_slot} <= {previous_slot}") # Block MUST fall inside the requested half-open range. # Use int math so an end_slot near 2**64 cannot wrap. @@ -337,16 +337,16 @@ async def _do_blocks_by_range_request( # empty slots. The next non-empty block's parent_root # therefore equals the last received block's root, # regardless of how many empty slots lie between. - if prev_root is not None and inner.parent_root != prev_root: + if previous_root is not None and inner.parent_root != previous_root: raise CodecError( f"Parent root mismatch at slot {block_slot}: " - f"expected {prev_root.hex()}, " + f"expected {previous_root.hex()}, " f"got {inner.parent_root.hex()}" ) blocks.append(SignedBlock.decode_bytes(ssz_bytes)) - prev_slot = block_slot - prev_root = hash_tree_root(inner) + previous_slot = block_slot + previous_root = hash_tree_root(inner) elif code == ResponseCode.RESOURCE_UNAVAILABLE: # Peer doesn't have this block, continue. @@ -359,7 +359,7 @@ async def _do_blocks_by_range_request( except CodecError as e: # Protocol violation: log with peer id and re-raise. - logger.warning("Protocol violation from %s: %s", conn.peer_id, e) + logger.warning("Protocol violation from %s: %s", connection.peer_id, e) raise # No-more-than-count enforcement. @@ -373,9 +373,9 @@ async def _do_blocks_by_range_request( except Exception: extra = b"" if extra: - msg = "Peer sent more than count BlocksByRange chunks" - logger.warning("Protocol violation from %s: %s", conn.peer_id, msg) - raise CodecError(msg) + message = "Peer sent more than count BlocksByRange chunks" + logger.warning("Protocol violation from %s: %s", connection.peer_id, message) + raise CodecError(message) return blocks @@ -401,14 +401,14 @@ async def send_status( Returns: Peer's status, or None on error. """ - conn = self._connections.get(peer_id) - if conn is None: + connection = self._connections.get(peer_id) + if connection is None: logger.debug("No connection to peer %s for status", peer_id) return None try: return await asyncio.wait_for( - self._do_status_request(conn, status), + self._do_status_request(connection, status), timeout=self.timeout, ) except asyncio.TimeoutError: @@ -420,7 +420,7 @@ async def send_status( async def _do_status_request( self, - conn: QuicConnection, + connection: QuicConnection, status: Status, retry_count: int = 0, ) -> Status | None: @@ -428,7 +428,7 @@ async def _do_status_request( Execute a Status request. Args: - conn: QuicConnection to use. + connection: QuicConnection to use. status: Our status to send. retry_count: Number of retries attempted (internal). @@ -442,7 +442,7 @@ async def _do_status_request( # This may fail if the QUIC stream is in a bad state right after # the handshake. The error "cannot call write() after FIN" can # happen during multistream negotiation writes. - stream = await conn.open_stream(STATUS_PROTOCOL_V1) + stream = await connection.open_stream(STATUS_PROTOCOL_V1) # Yield to allow aioquic to complete any pending state transitions. # @@ -489,7 +489,7 @@ async def _do_status_request( except Exception: pass await asyncio.sleep(0.01) # Small delay before retry - return await self._do_status_request(conn, status, retry_count + 1) + return await self._do_status_request(connection, status, retry_count + 1) raise finally: diff --git a/src/lean_spec/node/networking/enr/enr.py b/src/lean_spec/node/networking/enr/enr.py index 30e3be16b..e3ae5818d 100644 --- a/src/lean_spec/node/networking/enr/enr.py +++ b/src/lean_spec/node/networking/enr/enr.py @@ -249,7 +249,7 @@ def compute_node_id(self) -> NodeId | None: """ Compute the node ID from the public key. - Per EIP-778 "v4" identity scheme: keccak256(uncompressed_pubkey). + Per EIP-778 "v4" identity scheme: keccak256(uncompressed_public_key). The hash is computed over the 64-byte x||y coordinates. """ if self.public_key is None: @@ -352,17 +352,17 @@ def from_rlp(cls, rlp_data: bytes) -> Self: # Keys are strings, values are arbitrary bytes. # EIP-778 requires keys to be lexicographically sorted. pairs: dict[EnrKey, bytes] = {} - prev_key: EnrKey | None = None + previous_key: EnrKey | None = None for i in range(2, len(items), 2): key = EnrKey(items[i].decode("utf-8")) - if prev_key is not None and key <= prev_key: + if previous_key is not None and key <= previous_key: raise ValueError( f"ENR keys must be lexicographically sorted per EIP-778: " - f"'{key}' follows '{prev_key}'" + f"'{key}' follows '{previous_key}'" ) value = items[i + 1] pairs[key] = value - prev_key = key + previous_key = key enr = cls( signature=signature, diff --git a/src/lean_spec/node/networking/enr/rlp.py b/src/lean_spec/node/networking/enr/rlp.py index bc79e20f1..2a1561fc2 100644 --- a/src/lean_spec/node/networking/enr/rlp.py +++ b/src/lean_spec/node/networking/enr/rlp.py @@ -344,12 +344,12 @@ def _decode_length(data: bytes, offset: int, base: int) -> tuple[int, int]: # # prefix = 0xb9 # short_length = 0xb9 - 0x80 = 57 - # len_of_len = 57 - 55 = 2 + # length_of_length = 57 - 55 = 2 # length = int(04 00, big) = 1024 # payload range = [3, 1027) - len_of_len = short_length - SHORT_FORM_MAX + length_of_length = short_length - SHORT_FORM_MAX length_start = offset + 1 - length_end = length_start + len_of_len + length_end = length_start + length_of_length if length_end > len(data): raise RLPDecodingError(f"Data too short: need {length_end}, have {len(data)}") @@ -360,7 +360,7 @@ def _decode_length(data: bytes, offset: int, base: int) -> tuple[int, int]: # # Example: input b9 00 38 [56 bytes] is rejected. # The shorter equivalent b8 38 [56 bytes] is the canonical form. - if len_of_len > 1 and data[length_start] == 0: + if length_of_length > 1 and data[length_start] == 0: raise RLPDecodingError("Non-canonical: leading zeros in length encoding") length = int.from_bytes(data[length_start:length_end], "big") diff --git a/src/lean_spec/node/networking/gossipsub/behavior.py b/src/lean_spec/node/networking/gossipsub/behavior.py index fd2455a76..fba229000 100644 --- a/src/lean_spec/node/networking/gossipsub/behavior.py +++ b/src/lean_spec/node/networking/gossipsub/behavior.py @@ -251,7 +251,7 @@ def __post_init__(self) -> None: self._short_id = Uint16(self._instance_id % 0x10000) self.mesh = MeshState(params=self.params) self.message_cache = MessageCache( - mcache_len=self.params.mcache_len, + mcache_length=self.params.mcache_length, mcache_gossip=self.params.mcache_gossip, ) self.seen_cache = SeenCache(ttl_seconds=self.params.seen_ttl_secs) @@ -456,7 +456,7 @@ async def publish(self, topic: TopicId, data: bytes) -> None: topic: Topic to publish to. data: Message payload. """ - msg = Message(topic=topic, data=data) + message = Message(topic=topic, data=data) topic_bytes = topic.encode("utf-8") # Decompress once for message ID computation. @@ -466,18 +466,18 @@ async def publish(self, topic: TopicId, data: bytes) -> None: # - decompression succeeded (VALID_SNAPPY), # - failed (INVALID_SNAPPY). decompressed, domain = _try_decompress(data) - msg_id = GossipsubMessage.compute_id(topic_bytes, decompressed, domain=domain) + message_id = GossipsubMessage.compute_id(topic_bytes, decompressed, domain=domain) - if self.seen_cache.has(msg_id): - logger.debug("Skipping duplicate message %s", msg_id.hex()[:8]) + if self.seen_cache.has(message_id): + logger.debug("Skipping duplicate message %s", message_id.hex()[:8]) return - self.seen_cache.add(msg_id, Timestamp(time.time())) + self.seen_cache.add(message_id, Timestamp(time.time())) # Cache stores compressed data for IWANT responses. - cache_msg = GossipsubMessage(topic=topic_bytes, raw_data=data) - cache_msg._cached_id = msg_id - self.message_cache.put(topic, cache_msg) + cache_message = GossipsubMessage(topic=topic_bytes, raw_data=data) + cache_message._cached_id = message_id + self.message_cache.put(topic, cache_message) if topic in self.mesh.subscriptions: peers = self.mesh.get_mesh_peers(topic) @@ -502,7 +502,7 @@ async def publish(self, topic: TopicId, data: bytes) -> None: len(outbound_peers), ) - rpc = RPC(publish=[msg]) + rpc = RPC(publish=[message]) for peer_id in peers: await self._send_rpc(peer_id, rpc) @@ -543,8 +543,8 @@ async def _handle_rpc(self, peer_id: PeerId, rpc: RPC) -> None: for sub in rpc.subscriptions: await self._handle_subscription(peer_id, sub) - for msg in rpc.publish: - await self._handle_message(peer_id, msg) + for message in rpc.publish: + await self._handle_message(peer_id, message) if rpc.control: await self._handle_control(peer_id, rpc.control) @@ -568,51 +568,51 @@ async def _handle_subscription(self, peer_id: PeerId, sub: SubOpts) -> None: GossipsubPeerEvent(peer_id=peer_id, topic=sub.topic_id, subscribed=sub.subscribe) ) - async def _handle_message(self, peer_id: PeerId, msg: Message) -> None: + async def _handle_message(self, peer_id: PeerId, message: Message) -> None: """Handle a published message from a peer.""" - if not msg.topic: + if not message.topic: return - topic_bytes = msg.topic.encode("utf-8") + topic_bytes = message.topic.encode("utf-8") # Decompress once for message ID computation and event delivery. # # Data is decompressed before the message ID function runs. # The decompressed data is also passed to the event consumer, # eliminating a second decompression there. - decompressed, domain = _try_decompress(msg.data) - msg_id = GossipsubMessage.compute_id(topic_bytes, decompressed, domain=domain) + decompressed, domain = _try_decompress(message.data) + message_id = GossipsubMessage.compute_id(topic_bytes, decompressed, domain=domain) # Deduplicate: each message is processed at most once. - if self.seen_cache.has(msg_id): + if self.seen_cache.has(message_id): return - self.seen_cache.add(msg_id, Timestamp(time.time())) + self.seen_cache.add(message_id, Timestamp(time.time())) # Cache stores compressed data for IWANT responses. - cache_msg = GossipsubMessage(topic=topic_bytes, raw_data=msg.data) - cache_msg._cached_id = msg_id - self.message_cache.put(TopicId(msg.topic), cache_msg) + cache_message = GossipsubMessage(topic=topic_bytes, raw_data=message.data) + cache_message._cached_id = message_id + self.message_cache.put(TopicId(message.topic), cache_message) # Only forward on topics we participate in (have a mesh for). - if msg.topic in self.mesh.subscriptions: - mesh_peers = self.mesh.get_mesh_peers(TopicId(msg.topic)) - forward_rpc = RPC(publish=[msg]) + if message.topic in self.mesh.subscriptions: + mesh_peers = self.mesh.get_mesh_peers(TopicId(message.topic)) + forward_rpc = RPC(publish=[message]) for mesh_peer in mesh_peers: if mesh_peer == peer_id: continue # Skip peers that told us they already have this message. peer_state = self._peers.get(mesh_peer) - if peer_state is not None and msg_id in peer_state.dont_want_ids: + if peer_state is not None and message_id in peer_state.dont_want_ids: continue await self._send_rpc(mesh_peer, forward_rpc) # Large messages warrant IDONTWANT to prevent redundant # forwards from other mesh peers who also received this. - if len(msg.data) >= IDONTWANT_SIZE_THRESHOLD: + if len(message.data) >= IDONTWANT_SIZE_THRESHOLD: idontwant_rpc = RPC( control=ControlMessage( - idontwant=[ControlIDontWant(message_ids=[bytes(msg_id)])] + idontwant=[ControlIDontWant(message_ids=[bytes(message_id)])] ) ) for mesh_peer in mesh_peers: @@ -622,12 +622,12 @@ async def _handle_message(self, peer_id: PeerId, msg: Message) -> None: # Event carries decompressed data for the consumer. # This eliminates the need for a second decompression in the event handler. event = GossipsubMessageEvent( - peer_id=peer_id, topic=msg.topic, data=decompressed, message_id=msg_id + peer_id=peer_id, topic=message.topic, data=decompressed, message_id=message_id ) await self._event_queue.put(event) logger.debug( - "Received message %s from %s on topic %s", msg_id.hex()[:8], peer_id, msg.topic + "Received message %s from %s on topic %s", message_id.hex()[:8], peer_id, message.topic ) async def _handle_control(self, peer_id: PeerId, control: ControlMessage) -> None: @@ -704,13 +704,15 @@ async def _handle_prune(self, peer_id: PeerId, prune: ControlPrune) -> None: async def _handle_ihave(self, peer_id: PeerId, ihave: ControlIHave) -> None: """Handle an IHAVE advertisement from a peer.""" wanted = [] - for msg_id in ihave.message_ids: + for message_id in ihave.message_ids: # Message IDs must be exactly 20 bytes (SHA256 truncated to 160 bits). - if len(msg_id) != 20: + if len(message_id) != 20: continue - msg_id_typed = MessageId(msg_id) - if not self.seen_cache.has(msg_id_typed) and not self.message_cache.has(msg_id_typed): - wanted.append(msg_id) + message_id_typed = MessageId(message_id) + if not self.seen_cache.has(message_id_typed) and not self.message_cache.has( + message_id_typed + ): + wanted.append(message_id) if not wanted: return @@ -724,11 +726,11 @@ async def _handle_iwant(self, peer_id: PeerId, iwant: ControlIWant) -> None: """Handle an IWANT request from a peer.""" messages = [] - for msg_id in iwant.message_ids: - if len(msg_id) != 20: + for message_id in iwant.message_ids: + if len(message_id) != 20: continue - msg_id_typed = MessageId(msg_id) - cached = self.message_cache.get(msg_id_typed) + message_id_typed = MessageId(message_id) + cached = self.message_cache.get(message_id_typed) if cached: topic_id = TopicId(cached.topic.decode("utf-8")) messages.append(Message(topic=topic_id, data=cached.raw_data)) @@ -748,10 +750,10 @@ def _handle_idontwant(self, peer_id: PeerId, idontwant: ControlIDontWant) -> Non if state is None: return - for msg_id in idontwant.message_ids: - if len(msg_id) != 20: + for message_id in idontwant.message_ids: + if len(message_id) != 20: continue - state.dont_want_ids.add(MessageId(msg_id)) + state.dont_want_ids.add(MessageId(message_id)) async def _heartbeat_loop(self) -> None: """Background heartbeat for mesh maintenance.""" @@ -855,8 +857,8 @@ async def _maintain_mesh(self, topic: TopicId, now: float) -> None: async def _emit_gossip(self, topic: TopicId) -> None: """Send IHAVE gossip to non-mesh peers.""" - msg_ids = self.message_cache.get_gossip_ids(topic) - if not msg_ids: + message_ids = self.message_cache.get_gossip_ids(topic) + if not message_ids: return # Only peers with outbound streams can receive gossip. @@ -870,14 +872,14 @@ async def _emit_gossip(self, topic: TopicId) -> None: if not gossip_peers: return - ihave = ControlIHave(topic_id=topic, message_ids=cast(list[bytes], msg_ids)) + ihave = ControlIHave(topic_id=topic, message_ids=cast(list[bytes], message_ids)) rpc = RPC(control=ControlMessage(ihave=[ihave])) for peer_id in gossip_peers: await self._send_rpc(peer_id, rpc) logger.debug( - "IHAVE %d messages to %d peers for topic %s", len(msg_ids), len(gossip_peers), topic + "IHAVE %d messages to %d peers for topic %s", len(message_ids), len(gossip_peers), topic ) async def _send_rpc(self, peer_id: PeerId, rpc: RPC) -> None: diff --git a/src/lean_spec/node/networking/gossipsub/mcache.py b/src/lean_spec/node/networking/gossipsub/mcache.py index c8d67978e..23a224d99 100644 --- a/src/lean_spec/node/networking/gossipsub/mcache.py +++ b/src/lean_spec/node/networking/gossipsub/mcache.py @@ -42,7 +42,7 @@ Key Parameters -------------- -- **mcache_len** (6): Total windows retained +- **mcache_length** (6): Total windows retained - **mcache_gossip** (3): Recent windows included in IHAVE Only the first `mcache_gossip` windows are advertised via IHAVE. @@ -98,7 +98,7 @@ class MessageCache: - **IHAVE gossip**: Get message IDs for advertisement """ - mcache_len: int = 6 + mcache_length: int = 6 """Number of history windows to retain. Messages are evicted after this many heartbeat intervals. @@ -111,7 +111,7 @@ class MessageCache: """Number of recent windows to include in IHAVE gossip. Only messages from the most recent windows are advertised. - Should be less than or equal to mcache_len. + Should be less than or equal to mcache_length. """ _windows: deque[set[MessageId]] = field(init=False, repr=False) @@ -129,7 +129,7 @@ class MessageCache: def __post_init__(self) -> None: """Initialize the sliding window structure.""" - self._windows = deque(maxlen=self.mcache_len) + self._windows = deque(maxlen=self.mcache_length) self._windows.append(set()) def put(self, topic: TopicId, message: GossipsubMessage) -> bool: @@ -145,39 +145,39 @@ def put(self, topic: TopicId, message: GossipsubMessage) -> bool: Returns: True if added (not a duplicate). """ - msg_id = message.id + message_id = message.id - if msg_id in self._by_id: + if message_id in self._by_id: return False - self._windows[0].add(msg_id) - self._by_id[msg_id] = CacheEntry(message=message, topic=topic) + self._windows[0].add(message_id) + self._by_id[message_id] = CacheEntry(message=message, topic=topic) return True - def get(self, msg_id: MessageId) -> GossipsubMessage | None: + def get(self, message_id: MessageId) -> GossipsubMessage | None: """Retrieve a message by ID. Used to respond to IWANT requests from peers. Args: - msg_id: Message ID to look up. + message_id: Message ID to look up. Returns: The cached message, or None if not found/evicted. """ - entry = self._by_id.get(msg_id) + entry = self._by_id.get(message_id) return entry.message if entry else None - def has(self, msg_id: MessageId) -> bool: + def has(self, message_id: MessageId) -> bool: """Check if a message is cached. Args: - msg_id: Message ID to check. + message_id: Message ID to check. Returns: True if the message is in the cache. """ - return msg_id in self._by_id + return message_id in self._by_id def get_gossip_ids(self, topic: TopicId) -> list[MessageId]: """Get message IDs for IHAVE gossip. @@ -196,10 +196,10 @@ def get_gossip_ids(self, topic: TopicId) -> list[MessageId]: windows_to_check = min(self.mcache_gossip, len(self._windows)) for window in islice(self._windows, windows_to_check): - for msg_id in window: - entry = self._by_id.get(msg_id) + for message_id in window: + entry = self._by_id.get(message_id) if entry and entry.topic == topic: - result.append(msg_id) + result.append(message_id) return result @@ -216,11 +216,11 @@ def shift(self) -> int: """ evicted = 0 - if len(self._windows) >= self.mcache_len: + if len(self._windows) >= self.mcache_length: oldest = self._windows.pop() - for msg_id in oldest: - if msg_id in self._by_id: - del self._by_id[msg_id] + for message_id in oldest: + if message_id in self._by_id: + del self._by_id[message_id] evicted += 1 self._windows.appendleft(set()) @@ -261,32 +261,32 @@ class SeenCache: expiry during cleanup. """ - def add(self, msg_id: MessageId, timestamp: Timestamp) -> bool: + def add(self, message_id: MessageId, timestamp: Timestamp) -> bool: """Mark a message as seen. Args: - msg_id: Message ID to mark as seen. + message_id: Message ID to mark as seen. timestamp: Current Unix timestamp. Returns: True if newly seen (not a duplicate). """ - if msg_id in self._timestamps: + if message_id in self._timestamps: return False - self._timestamps[msg_id] = timestamp + self._timestamps[message_id] = timestamp return True - def has(self, msg_id: MessageId) -> bool: + def has(self, message_id: MessageId) -> bool: """Check if a message has been seen. Args: - msg_id: Message ID to check. + message_id: Message ID to check. Returns: True if the message has been seen. """ - return msg_id in self._timestamps + return message_id in self._timestamps def cleanup(self, current_time: float) -> int: """Remove expired entries. @@ -301,9 +301,9 @@ def cleanup(self, current_time: float) -> int: Number of entries removed. """ cutoff = current_time - self.ttl_seconds - expired = [msg_id for msg_id, ts in self._timestamps.items() if ts < cutoff] + expired = [message_id for message_id, ts in self._timestamps.items() if ts < cutoff] - for msg_id in expired: - del self._timestamps[msg_id] + for message_id in expired: + del self._timestamps[message_id] return len(expired) diff --git a/src/lean_spec/node/networking/gossipsub/parameters.py b/src/lean_spec/node/networking/gossipsub/parameters.py index 209b740df..5feb9ea76 100644 --- a/src/lean_spec/node/networking/gossipsub/parameters.py +++ b/src/lean_spec/node/networking/gossipsub/parameters.py @@ -37,7 +37,7 @@ :: - mcache_len Total history windows kept (6) + mcache_length Total history windows kept (6) mcache_gossip Windows included in IHAVE gossip (3) seen_ttl Duplicate detection window @@ -132,11 +132,11 @@ class GossipsubParameters(StrictBaseModel): # Message Cache Parameters - mcache_len: int = 6 + mcache_length: int = 6 """Total number of history windows in the message cache. - Messages are stored for this many heartbeat intervals. - - After mcache_len heartbeats, messages are evicted. + - After mcache_length heartbeats, messages are evicted. """ mcache_gossip: int = 3 diff --git a/src/lean_spec/node/networking/gossipsub/rpc.py b/src/lean_spec/node/networking/gossipsub/rpc.py index 52bf3ca3f..42ae4aca8 100644 --- a/src/lean_spec/node/networking/gossipsub/rpc.py +++ b/src/lean_spec/node/networking/gossipsub/rpc.py @@ -35,7 +35,7 @@ References: ----------- - Protobuf encoding: https://protobuf.dev/programming-guides/encoding/ -- go-libp2p-pubsub rpc.proto: https://github.com/libp2p/go-libp2p-pubsub/blob/master/pb/rpc.proto +- go-libp2p-pubsub rpc.protobuf: https://github.com/libp2p/go-libp2p-pubsub/blob/master/pb/rpc.protobuf """ from __future__ import annotations @@ -205,7 +205,7 @@ def encode(self) -> bytes: @classmethod def decode(cls, data: bytes) -> Message: """Decode from protobuf.""" - msg = cls() + message = cls() pos = 0 while pos < len(data): @@ -217,21 +217,21 @@ def decode(cls, data: bytes) -> Message: pos += length if field_num == 1: - msg.from_peer = field_data + message.from_peer = field_data elif field_num == 2: - msg.data = field_data + message.data = field_data elif field_num == 3: - msg.seqno = field_data + message.seqno = field_data elif field_num == 4: - msg.topic = TopicId(field_data.decode("utf-8")) + message.topic = TopicId(field_data.decode("utf-8")) elif field_num == 5: - msg.signature = field_data + message.signature = field_data elif field_num == 6: - msg.key = field_data + message.key = field_data else: pos = _skip_field(data, pos, wire_type) - return msg + return message @dataclass(slots=True) @@ -249,8 +249,8 @@ def encode(self) -> bytes: result = bytearray() if self.topic_id: result.extend(encode_string(1, self.topic_id)) - for msg_id in self.message_ids: - result.extend(encode_bytes(2, msg_id)) + for message_id in self.message_ids: + result.extend(encode_bytes(2, message_id)) return bytes(result) @classmethod @@ -288,8 +288,8 @@ class ControlIWant: def encode(self) -> bytes: """Encode as protobuf.""" result = bytearray() - for msg_id in self.message_ids: - result.extend(encode_bytes(1, msg_id)) + for message_id in self.message_ids: + result.extend(encode_bytes(1, message_id)) return bytes(result) @classmethod @@ -396,8 +396,8 @@ class ControlIDontWant: def encode(self) -> bytes: """Encode as protobuf.""" result = bytearray() - for msg_id in self.message_ids: - result.extend(encode_bytes(1, msg_id)) + for message_id in self.message_ids: + result.extend(encode_bytes(1, message_id)) return bytes(result) @classmethod @@ -520,8 +520,8 @@ def encode(self) -> bytes: for sub in self.subscriptions: result.extend(encode_length_delimited(1, sub.encode())) - for msg in self.publish: - result.extend(encode_length_delimited(2, msg.encode())) + for message in self.publish: + result.extend(encode_length_delimited(2, message.encode())) if self.control and not self.control.is_empty(): result.extend(encode_length_delimited(3, self.control.encode())) diff --git a/src/lean_spec/node/networking/service/service.py b/src/lean_spec/node/networking/service/service.py index 6414696b6..7cf553ac0 100644 --- a/src/lean_spec/node/networking/service/service.py +++ b/src/lean_spec/node/networking/service/service.py @@ -149,12 +149,12 @@ async def _handle_event(self, event: NetworkEvent) -> None: # Logs attestation source and validation result. await self.sync_service.on_gossip_attestation(attestation, peer_id) - case GossipAggregatedAttestationEvent(signed_attestation=att, peer_id=peer_id): + case GossipAggregatedAttestationEvent(signed_attestation=attestation, peer_id=peer_id): # Route aggregated attestations to sync service. # # Aggregates contain multiple validator votes and are used # to advance justification and finalization. - await self.sync_service.on_gossip_aggregated_attestation(att, peer_id) + await self.sync_service.on_gossip_aggregated_attestation(attestation, peer_id) case PeerStatusEvent(peer_id=peer_id, status=status): # Route peer status updates to sync service. diff --git a/src/lean_spec/node/networking/transport/__init__.py b/src/lean_spec/node/networking/transport/__init__.py index e51712d3d..4533713b0 100644 --- a/src/lean_spec/node/networking/transport/__init__.py +++ b/src/lean_spec/node/networking/transport/__init__.py @@ -23,7 +23,7 @@ """ from .identity import IdentityKeypair, Secp256k1PublicKey -from .peer_id import Base58, KeyType, Multihash, MultihashCode, PeerId, PublicKeyProto +from .peer_id import Base58, KeyType, Multihash, MultihashCode, PeerId, PublicKeyProtobuf from .quic import ( NegotiationError, QuicConnection, @@ -44,7 +44,7 @@ "Secp256k1PublicKey", # PeerId (peer_id module) "PeerId", - "PublicKeyProto", + "PublicKeyProtobuf", "Multihash", "KeyType", "MultihashCode", diff --git a/src/lean_spec/node/networking/transport/identity/keypair.py b/src/lean_spec/node/networking/transport/identity/keypair.py index b1ed9d007..fe748bd56 100644 --- a/src/lean_spec/node/networking/transport/identity/keypair.py +++ b/src/lean_spec/node/networking/transport/identity/keypair.py @@ -18,7 +18,7 @@ from lean_spec.spec.ssz import Bytes33 -from ..peer_id import KeyType, PeerId, PublicKeyProto +from ..peer_id import KeyType, PeerId, PublicKeyProtobuf __all__ = [ "IdentityKeypair", @@ -117,8 +117,8 @@ def to_peer_id(self) -> PeerId: Returns: PeerId derived from this keypair. """ - proto = PublicKeyProto( + protobuf = PublicKeyProtobuf( key_type=KeyType.SECP256K1, key_data=self.public_key.to_bytes(), ) - return PeerId.from_public_key(proto) + return PeerId.from_public_key(protobuf) diff --git a/src/lean_spec/node/networking/transport/peer_id.py b/src/lean_spec/node/networking/transport/peer_id.py index 6df3834e6..8b1887a2c 100644 --- a/src/lean_spec/node/networking/transport/peer_id.py +++ b/src/lean_spec/node/networking/transport/peer_id.py @@ -6,7 +6,7 @@ 2. If encoded <= 42 bytes: PeerId = multihash(identity, encoded) 3. If encoded > 42 bytes: PeerId = multihash(sha256, sha256(encoded)) -Protobuf wire format (from crypto.proto): +Protobuf wire format (from crypto.protobuf): message PublicKey { required KeyType Type = 1; // Field 1, varint required bytes Data = 2; // Field 2, length-delimited @@ -26,7 +26,7 @@ References: - https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md - - https://github.com/libp2p/go-libp2p/blob/master/core/crypto/pb/crypto.proto + - https://github.com/libp2p/go-libp2p/blob/master/core/crypto/pb/crypto.protobuf - https://github.com/multiformats/multihash """ @@ -42,7 +42,7 @@ __all__ = [ # Main types "PeerId", - "PublicKeyProto", + "PublicKeyProtobuf", "Multihash", # Enums "KeyType", @@ -54,7 +54,7 @@ class KeyType(IntEnum): """ - libp2p-crypto key type codes (from crypto.proto KeyType enum). + libp2p-crypto key type codes (from crypto.protobuf KeyType enum). These identify the cryptographic algorithm used for the public key. """ @@ -167,10 +167,10 @@ def decode(cls, s: str) -> bytes: # Convert from base58 to integer num = 0 - for char in s: - index = cls.ALPHABET.find(char) + for character in s: + index = cls.ALPHABET.find(character) if index < 0: - raise ValueError(f"Invalid Base58 character: {char!r}") + raise ValueError(f"Invalid Base58 character: {character!r}") num = num * 58 + index # Convert to bytes @@ -249,7 +249,7 @@ def from_data(cls, data: bytes) -> Multihash: @dataclass(frozen=True, slots=True) -class PublicKeyProto: +class PublicKeyProtobuf: """ A public key in libp2p-crypto protobuf format. @@ -349,7 +349,7 @@ def from_base58(cls, s: str) -> PeerId: return cls(multihash=Base58.decode(s)) @classmethod - def from_public_key(cls, public_key: PublicKeyProto) -> PeerId: + def from_public_key(cls, public_key: PublicKeyProtobuf) -> PeerId: """ Derive PeerId from a public key. diff --git a/src/lean_spec/node/networking/transport/quic/connection.py b/src/lean_spec/node/networking/transport/quic/connection.py index 503d4e017..9083cc800 100644 --- a/src/lean_spec/node/networking/transport/quic/connection.py +++ b/src/lean_spec/node/networking/transport/quic/connection.py @@ -62,7 +62,7 @@ class QuicConnection: _protocol: QuicConnectionProtocol _peer_id: PeerId - _remote_addr: str + _remote_address: str _streams: dict[int, QuicStream] = field(default_factory=dict) _incoming_streams: asyncio.Queue[QuicStream] = field(default_factory=lambda: asyncio.Queue()) _closed: bool = False @@ -73,9 +73,9 @@ def peer_id(self) -> PeerId: return self._peer_id @property - def remote_addr(self) -> str: + def remote_address(self) -> str: """Remote address in multiaddr format.""" - return self._remote_addr + return self._remote_address async def open_stream(self, protocol: ProtocolId) -> QuicStream: """ @@ -224,7 +224,7 @@ def quic_event_received(self, event: QuicEvent) -> None: # aioquic may not expose this directly, need to check. # For now, mark handshake complete. # TODO: Extract and verify peer certificate - self.peer_identity = None # Will be set if we can extract cert + self.peer_identity = None # Will be set if we can extract certificate except Exception: self.peer_identity = None @@ -324,15 +324,15 @@ class QuicConnectionManager: Usage: manager = await QuicConnectionManager.create(identity_key) - conn = await manager.connect("/ip4/127.0.0.1/udp/9000/quic-v1") - stream = await conn.open_stream("/leanconsensus/req/status/1/ssz_snappy") + connection = await manager.connect("/ip4/127.0.0.1/udp/9000/quic-v1") + stream = await connection.open_stream("/leanconsensus/req/status/1/ssz_snappy") """ _identity_key: IdentityKeypair _peer_id: PeerId _config: QuicConfiguration _connections: dict[PeerId, QuicConnection] = field(default_factory=dict) - _temp_dir: Path | None = None + _temp_directory: Path | None = None _context_managers: list[contextlib.AbstractAsyncContextManager[object]] = field( default_factory=list ) @@ -354,13 +354,13 @@ async def create( peer_id = identity_key.to_peer_id() # Generate libp2p certificate. - private_pem, cert_pem, _ = generate_libp2p_certificate(identity_key) + private_pem, certificate_pem, _ = generate_libp2p_certificate(identity_key) - # Write cert/key to temp files (aioquic requires file paths). - temp_dir = Path(tempfile.mkdtemp()) - cert_path = temp_dir / "cert.pem" - key_path = temp_dir / "key.pem" - cert_path.write_bytes(cert_pem) + # Write certificate/key to temp files (aioquic requires file paths). + temp_directory = Path(tempfile.mkdtemp()) + certificate_path = temp_directory / "cert.pem" + key_path = temp_directory / "key.pem" + certificate_path.write_bytes(certificate_pem) key_path.write_bytes(private_pem) # Configure QUIC. @@ -369,13 +369,13 @@ async def create( is_client=True, verify_mode=ssl.CERT_NONE, # We verify via libp2p extension, not CA ) - config.load_cert_chain(str(cert_path), str(key_path)) + config.load_cert_chain(str(certificate_path), str(key_path)) return cls( _identity_key=identity_key, _peer_id=peer_id, _config=config, - _temp_dir=temp_dir, + _temp_directory=temp_directory, ) @property @@ -441,16 +441,16 @@ async def connect(self, multiaddr: str) -> QuicConnection: temp_key = IdentityKeypair.generate() peer_id = temp_key.to_peer_id() - conn = QuicConnection( + connection = QuicConnection( _protocol=protocol, _peer_id=peer_id, - _remote_addr=multiaddr, + _remote_address=multiaddr, ) - protocol.connection = conn + protocol.connection = connection protocol._replay_buffered_events() - self._connections[peer_id] = conn - return conn + self._connections[peer_id] = connection + return connection except Exception as e: raise QuicTransportError(f"Failed to connect: {e}") from e @@ -491,10 +491,10 @@ async def listen( ) # Reuse the same certificate as client config. - if self._temp_dir: - cert_path = self._temp_dir / "cert.pem" - key_path = self._temp_dir / "key.pem" - server_config.load_cert_chain(str(cert_path), str(key_path)) + if self._temp_directory: + certificate_path = self._temp_directory / "cert.pem" + key_path = self._temp_directory / "key.pem" + server_config.load_cert_chain(str(certificate_path), str(key_path)) # Callback to set up connection when handshake completes. # Captures this manager's state (self, on_connection, host, port). @@ -502,18 +502,18 @@ def handle_handshake(protocol_instance: LibP2PQuicProtocol) -> None: temp_key = IdentityKeypair.generate() remote_peer_id = temp_key.to_peer_id() - remote_addr = f"/ip4/{host}/udp/{port}/quic-v1/p2p/{remote_peer_id}" - conn = QuicConnection( + remote_address = f"/ip4/{host}/udp/{port}/quic-v1/p2p/{remote_peer_id}" + connection = QuicConnection( _protocol=protocol_instance, _peer_id=remote_peer_id, - _remote_addr=remote_addr, + _remote_address=remote_address, ) - protocol_instance.connection = conn + protocol_instance.connection = connection protocol_instance._replay_buffered_events() - self._connections[remote_peer_id] = conn + self._connections[remote_peer_id] = connection # Invoke callback asynchronously so it doesn't block event processing. - asyncio.ensure_future(on_connection(conn)) + asyncio.ensure_future(on_connection(connection)) # Protocol factory that attaches our callback to each new instance. def create_protocol(*args, **kwargs) -> LibP2PQuicProtocol: diff --git a/src/lean_spec/node/networking/transport/quic/stream.py b/src/lean_spec/node/networking/transport/quic/stream.py index 5b0af2887..83510cbf8 100644 --- a/src/lean_spec/node/networking/transport/quic/stream.py +++ b/src/lean_spec/node/networking/transport/quic/stream.py @@ -121,8 +121,8 @@ async def write(self, data: bytes) -> None: except Exception as e: # If aioquic raises an exception, mark our stream as write-closed # to prevent further attempts. - error_msg = str(e) - if "FIN" in error_msg or "closed" in error_msg.lower(): + error_message = str(e) + if "FIN" in error_message or "closed" in error_message.lower(): self._write_closed = True raise QuicTransportError(f"Write failed on stream {self._stream_id}: {e}") from e diff --git a/src/lean_spec/node/networking/transport/quic/tls.py b/src/lean_spec/node/networking/transport/quic/tls.py index 767834065..c71240646 100644 --- a/src/lean_spec/node/networking/transport/quic/tls.py +++ b/src/lean_spec/node/networking/transport/quic/tls.py @@ -101,7 +101,7 @@ def generate_libp2p_certificate( # This matches webpki's expected format for self-signed certificates. ski_digest = hashlib.sha256(tls_public_bytes).digest()[:20] - cert = ( + certificate = ( x509.CertificateBuilder() .subject_name(subject) .issuer_name(issuer) @@ -131,9 +131,9 @@ def generate_libp2p_certificate( format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption(), ) - cert_pem = cert.public_bytes(serialization.Encoding.PEM) + certificate_pem = certificate.public_bytes(serialization.Encoding.PEM) - return private_pem, cert_pem, cert + return private_pem, certificate_pem, certificate def _create_extension_payload( @@ -170,7 +170,7 @@ def _create_extension_payload( # Encode PublicKey as protobuf. # Field 1 (Type): tag=0x08, value=2 (secp256k1) # Field 2 (Data): tag=0x12, length, bytes - public_key_proto = ( + public_key_protobuf = ( bytes([0x08, KeyType.SECP256K1, 0x12, len(public_key_compressed)]) + public_key_compressed ) @@ -180,21 +180,21 @@ def _create_extension_payload( # publicKey OCTET STRING, # signature OCTET STRING # } - return _encode_asn1_signed_key(public_key_proto, signature) + return _encode_asn1_signed_key(public_key_protobuf, signature) -def _encode_asn1_signed_key(public_key_proto: bytes, signature: bytes) -> bytes: +def _encode_asn1_signed_key(public_key_protobuf: bytes, signature: bytes) -> bytes: """ Encode SignedKey as ASN.1 DER. ASN.1 structure: SEQUENCE { - OCTET STRING (public_key_proto), + OCTET STRING (public_key_protobuf), OCTET STRING (signature) } """ # Encode the two OCTET STRINGs. - octet1 = _encode_asn1_octet_string(public_key_proto) + octet1 = _encode_asn1_octet_string(public_key_protobuf) octet2 = _encode_asn1_octet_string(signature) # Encode the SEQUENCE. diff --git a/src/lean_spec/node/networking/types.py b/src/lean_spec/node/networking/types.py index 32249a7b6..67ea7ff0a 100644 --- a/src/lean_spec/node/networking/types.py +++ b/src/lean_spec/node/networking/types.py @@ -19,7 +19,7 @@ class DomainType(Bytes4): class NodeId(Bytes32): - """32-byte ENR node identifier, derived from `keccak256(pubkey)`.""" + """32-byte ENR node identifier, derived from `keccak256(public_key)`.""" class ForkDigest(Bytes4): diff --git a/src/lean_spec/node/node.py b/src/lean_spec/node/node.py index ee0827e9c..d940af661 100644 --- a/src/lean_spec/node/node.py +++ b/src/lean_spec/node/node.py @@ -214,7 +214,7 @@ def from_genesis(cls, config: NodeConfig) -> Node: # # If database contains valid state, resume from there. # Otherwise, fall through to genesis initialization. - validator_id = ( + validator_index = ( config.validator_registry.primary_index() if config.validator_registry else None ) # The composition root narrows the protocol to its concrete fork. @@ -226,7 +226,7 @@ def from_genesis(cls, config: NodeConfig) -> Node: ) fork = config.fork store = cls._try_load_store_from_database( - database, validator_id, config.genesis_time, config.time_fn, fork + database, validator_index, config.genesis_time, config.time_fn, fork ) # An explicit anchor wins over genesis synthesis but loses to the database. @@ -255,7 +255,7 @@ def from_genesis(cls, config: NodeConfig) -> Node: # Initialize forkchoice store. # # Genesis block is both justified and finalized. - store = fork.create_store(state, block, validator_id) + store = fork.create_store(state, block, validator_index) # Persist genesis to database if available. # @@ -341,7 +341,9 @@ def from_genesis(cls, config: NodeConfig) -> Node: # Without local processing, the node would not see its own produced # blocks/attestations in forkchoice until they arrived back via gossip. async def publish_attestation_wrapper(attestation: SignedAttestation) -> None: - subnet_id = attestation.validator_id.compute_subnet_id(ATTESTATION_COMMITTEE_COUNT) + subnet_id = attestation.validator_index.compute_subnet_id( + ATTESTATION_COMMITTEE_COUNT + ) await network_service.publish_attestation(attestation, subnet_id) await sync_service.on_gossip_attestation(attestation) @@ -372,7 +374,7 @@ async def publish_block_wrapper(block: SignedBlock) -> None: @staticmethod def _try_load_store_from_database( database: Database | None, - validator_id: ValidatorIndex | None, + validator_index: ValidatorIndex | None, genesis_time: Uint64 | None = None, time_fn: Callable[[], float] = time.time, fork: ForkProtocol | None = None, @@ -390,7 +392,7 @@ def _try_load_store_from_database( Args: database: Database to load from. - validator_id: Validator index for the store instance. + validator_index: Validator index for the store instance. genesis_time: Unix timestamp of genesis (slot 0). time_fn: Wall-clock time source. fork: Fork specification for store construction. @@ -451,7 +453,7 @@ def _try_load_store_from_database( latest_finalized=finalized, blocks={head_root: head_block}, states={head_root: head_state}, - validator_id=validator_id, + validator_index=validator_index, ) async def run(self, *, install_signal_handlers: bool = True) -> None: @@ -502,8 +504,8 @@ def _install_signal_handlers(self) -> None: """ try: loop = asyncio.get_running_loop() - for sig in (signal.SIGINT, signal.SIGTERM): - loop.add_signal_handler(sig, self._shutdown.set) + for signature in (signal.SIGINT, signal.SIGTERM): + loop.add_signal_handler(signature, self._shutdown.set) except (ValueError, RuntimeError): # Cannot add handlers outside main thread. pass diff --git a/src/lean_spec/node/snappy/compress.py b/src/lean_spec/node/snappy/compress.py index d77324bfc..ba7f3357f 100644 --- a/src/lean_spec/node/snappy/compress.py +++ b/src/lean_spec/node/snappy/compress.py @@ -194,15 +194,15 @@ def _compress_block(block: bytes) -> bytes: # in the inner loop. The remaining bytes are emitted as literals. while ip < len(block) - INPUT_MARGIN_BYTES: # Step 1: Hash the 4 bytes at current position. - hash_val = _hash_4_bytes(block, ip, table_bits) + hash_value = _hash_4_bytes(block, ip, table_bits) # Step 2: Look up the hash to find a potential match. - match_pos = table[hash_val] + match_pos = table[hash_value] # Step 3: Update hash table with current position. # # Must happen AFTER lookup to avoid matching ourselves. - table[hash_val] = ip + table[hash_value] = ip # Step 4: Check if we found a real match. # @@ -303,7 +303,7 @@ def _hash_4_bytes(data: bytes, pos: int, table_bits: int) -> int: = 1145258561 Step 2: Multiply by magic constant - hash_val = (1145258561 * 0x1e35a7bd) & 0xFFFFFFFF + hash_value = (1145258561 * 0x1e35a7bd) & 0xFFFFFFFF = 0x9E3779B9 (example result) Step 3: Take top 10 bits @@ -326,7 +326,7 @@ def _hash_4_bytes(data: bytes, pos: int, table_bits: int) -> int: # The magic constant (0x1e35a7bd) is chosen to spread input bits # across the output. This reduces collisions for similar inputs. # We mask to 32 bits because Python integers are arbitrary precision. - hash_val = (value * HASH_MULTIPLIER) & 0xFFFFFFFF + hash_value = (value * HASH_MULTIPLIER) & 0xFFFFFFFF # Take top bits. # @@ -334,7 +334,7 @@ def _hash_4_bytes(data: bytes, pos: int, table_bits: int) -> int: # The multiplication spreads information to the high bits. # Bottom bits are more affected by the low bits of the input, # which tend to be similar for nearby positions. - return hash_val >> (32 - table_bits) + return hash_value >> (32 - table_bits) def _matches_at(data: bytes, pos1: int, pos2: int) -> bool: diff --git a/src/lean_spec/node/snappy/encoding.py b/src/lean_spec/node/snappy/encoding.py index 917292d9a..6e222dccc 100644 --- a/src/lean_spec/node/snappy/encoding.py +++ b/src/lean_spec/node/snappy/encoding.py @@ -112,20 +112,20 @@ def encode_literal_tag(length: int) -> bytes: # Large literal: tag indicates 2-byte length follows. # Format: [61 (6 bits)][LITERAL (2 bits)] + [length-1 (16 bits LE)] tag = (LITERAL_LENGTH_2_BYTES << 2) | LITERAL - len_minus_1 = length - 1 - return bytes([tag, len_minus_1 & 0xFF, (len_minus_1 >> 8) & 0xFF]) + length_minus_1 = length - 1 + return bytes([tag, length_minus_1 & 0xFF, (length_minus_1 >> 8) & 0xFF]) elif length <= 16777216: # Very large literal: tag indicates 3-byte length follows. # Format: [62 (6 bits)][LITERAL (2 bits)] + [length-1 (24 bits LE)] tag = (LITERAL_LENGTH_3_BYTES << 2) | LITERAL - len_minus_1 = length - 1 + length_minus_1 = length - 1 return bytes( [ tag, - len_minus_1 & 0xFF, - (len_minus_1 >> 8) & 0xFF, - (len_minus_1 >> 16) & 0xFF, + length_minus_1 & 0xFF, + (length_minus_1 >> 8) & 0xFF, + (length_minus_1 >> 16) & 0xFF, ] ) @@ -133,14 +133,14 @@ def encode_literal_tag(length: int) -> bytes: # Maximum literal: tag indicates 4-byte length follows. # Format: [63 (6 bits)][LITERAL (2 bits)] + [length-1 (32 bits LE)] tag = (LITERAL_LENGTH_4_BYTES << 2) | LITERAL - len_minus_1 = length - 1 + length_minus_1 = length - 1 return bytes( [ tag, - len_minus_1 & 0xFF, - (len_minus_1 >> 8) & 0xFF, - (len_minus_1 >> 16) & 0xFF, - (len_minus_1 >> 24) & 0xFF, + length_minus_1 & 0xFF, + (length_minus_1 >> 8) & 0xFF, + (length_minus_1 >> 16) & 0xFF, + (length_minus_1 >> 24) & 0xFF, ] ) diff --git a/src/lean_spec/node/storage/sqlite.py b/src/lean_spec/node/storage/sqlite.py index 0c6dccb12..49eb12b7e 100644 --- a/src/lean_spec/node/storage/sqlite.py +++ b/src/lean_spec/node/storage/sqlite.py @@ -89,7 +89,7 @@ def __init__( # # The check_same_thread=False flag allows multiple threads to share # this connection. SQLite serializes writes internally. - self._conn = sqlite3.connect( + self._connection = sqlite3.connect( str(self._path), check_same_thread=False, ) @@ -97,13 +97,13 @@ def __init__( # Row factory enables dict-like access: row["column_name"]. # # This makes the code more readable than tuple indexing. - self._conn.row_factory = sqlite3.Row + self._connection.row_factory = sqlite3.Row self._init_schema() def _init_schema(self) -> None: """Create tables if they don't exist.""" - cursor = self._conn.cursor() + cursor = self._connection.cursor() # Block and state tables use root hash as primary key. # @@ -129,14 +129,14 @@ def _init_schema(self) -> None: # Needed for checkpoint sync and API endpoints that query by state root. cursor.execute(STATE_ROOT_INDEX_CREATE_TABLE) - self._conn.commit() + self._connection.commit() # Block Operations def get_block(self, root: Bytes32) -> SpecBlockType | None: """Retrieve a block by its root hash.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() # Query by root hash, the canonical identifier in consensus. # @@ -161,7 +161,7 @@ def get_block(self, root: Bytes32) -> SpecBlockType | None: def put_block(self, block: SpecBlockType, root: Bytes32) -> None: """Store a block with its root hash.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() # INSERT OR REPLACE handles both new blocks and updates. # @@ -188,7 +188,7 @@ def put_block(self, block: SpecBlockType, root: Bytes32) -> None: def get_state(self, root: Bytes32) -> SpecStateType | None: """Retrieve a state by its associated block root.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() cursor.execute( f"SELECT data FROM {STATES_TABLE_NAME} WHERE root = ?", (bytes(root),), @@ -210,7 +210,7 @@ def get_state(self, root: Bytes32) -> SpecStateType | None: def put_state(self, state: SpecStateType, root: Bytes32) -> None: """Store a state indexed by its associated block root.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() # States should be stored at epoch boundaries for efficient access. # @@ -236,7 +236,7 @@ def put_state(self, state: SpecStateType, root: Bytes32) -> None: def get_justified_checkpoint(self) -> Checkpoint | None: """Retrieve the latest justified checkpoint.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() # Justified checkpoint: has received 2/3 attestation weight. # @@ -261,7 +261,7 @@ def get_justified_checkpoint(self) -> Checkpoint | None: def put_justified_checkpoint(self, checkpoint: Checkpoint) -> None: """Store the latest justified checkpoint.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() cursor.execute( f""" INSERT OR REPLACE INTO {CHECKPOINTS_TABLE_NAME} (key, data) @@ -275,7 +275,7 @@ def put_justified_checkpoint(self, checkpoint: Checkpoint) -> None: def get_finalized_checkpoint(self) -> Checkpoint | None: """Retrieve the latest finalized checkpoint.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() # Finalized checkpoint: irreversible under normal operation. # @@ -300,7 +300,7 @@ def get_finalized_checkpoint(self) -> Checkpoint | None: def put_finalized_checkpoint(self, checkpoint: Checkpoint) -> None: """Store the latest finalized checkpoint.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() cursor.execute( f""" INSERT OR REPLACE INTO {CHECKPOINTS_TABLE_NAME} (key, data) @@ -320,7 +320,7 @@ def put_finalized_checkpoint(self, checkpoint: Checkpoint) -> None: def get_head_root(self) -> Bytes32 | None: """Retrieve the current head block root.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() # The head root identifies the current best block. # @@ -343,7 +343,7 @@ def get_head_root(self) -> Bytes32 | None: def put_head_root(self, root: Bytes32) -> None: """Store the current head block root.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() cursor.execute( f""" INSERT OR REPLACE INTO {CHECKPOINTS_TABLE_NAME} (key, data) @@ -364,7 +364,7 @@ def put_head_root(self, root: Bytes32) -> None: def get_block_root_by_slot(self, slot: Slot) -> Bytes32 | None: """Retrieve block root for a specific slot.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() # Returns the canonical block at this slot. # @@ -385,7 +385,7 @@ def get_block_root_by_slot(self, slot: Slot) -> Bytes32 | None: def put_block_root_by_slot(self, slot: Slot, root: Bytes32) -> None: """Index a block root by its slot.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() # Updates as the canonical chain changes. # @@ -411,7 +411,7 @@ def put_block_root_by_slot(self, slot: Slot, root: Bytes32) -> None: def get_block_root_by_state_root(self, state_root: Bytes32) -> Bytes32 | None: """Look up the block root associated with a state root.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() cursor.execute( f"SELECT block_root FROM {STATE_ROOT_INDEX_TABLE_NAME} WHERE state_root = ?", (bytes(state_root),), @@ -429,7 +429,7 @@ def get_block_root_by_state_root(self, state_root: Bytes32) -> Bytes32 | None: def put_block_root_by_state_root(self, state_root: Bytes32, block_root: Bytes32) -> None: """Index a block root by the state root it produced.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() cursor.execute( f""" INSERT OR REPLACE INTO {STATE_ROOT_INDEX_TABLE_NAME} (state_root, block_root) @@ -451,7 +451,7 @@ def put_block_root_by_state_root(self, state_root: Bytes32, block_root: Bytes32) def get_genesis_time(self) -> Uint64 | None: """Retrieve the stored genesis time.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() cursor.execute( f"SELECT data FROM {CHECKPOINTS_TABLE_NAME} WHERE key = ?", (CHECKPOINTS_KEY_GENESIS_TIME,), @@ -469,7 +469,7 @@ def get_genesis_time(self) -> Uint64 | None: def put_genesis_time(self, genesis_time: Uint64) -> None: """Store genesis time for future restarts.""" try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() cursor.execute( f""" INSERT OR REPLACE INTO {CHECKPOINTS_TABLE_NAME} (key, data) @@ -492,15 +492,15 @@ def batch_write(self) -> Generator[None]: """ try: yield - self._conn.commit() + self._connection.commit() except (StorageWriteError, StorageCorruptionError): - self._conn.rollback() + self._connection.rollback() raise except sqlite3.Error as e: - self._conn.rollback() + self._connection.rollback() raise StorageWriteError(f"Batch write failed: {e}") from e except BaseException: - self._conn.rollback() + self._connection.rollback() raise # Pruning @@ -524,7 +524,7 @@ def prune_before_slot(self, slot: Slot, keep_roots: frozenset[Bytes32]) -> int: Total number of entries pruned across all tables. """ try: - cursor = self._conn.cursor() + cursor = self._connection.cursor() total_pruned = 0 # Build the exclusion set for parameterized queries. @@ -578,7 +578,7 @@ def prune_before_slot(self, slot: Slot, keep_roots: frozenset[Bytes32]) -> int: def close(self) -> None: """Close database connection.""" - self._conn.close() + self._connection.close() def __enter__(self) -> SQLiteDatabase: """Context manager entry.""" diff --git a/src/lean_spec/node/sync/head_sync.py b/src/lean_spec/node/sync/head_sync.py index c6782c069..18326dcc1 100644 --- a/src/lean_spec/node/sync/head_sync.py +++ b/src/lean_spec/node/sync/head_sync.py @@ -304,11 +304,11 @@ async def _process_cached_descendants( self.block_cache.remove(child_root) # Recursively process this child's descendants. - desc_count, store = await self._process_cached_descendants( + description_count, store = await self._process_cached_descendants( parent_root=child_root, store=store, ) - processed_count += desc_count + processed_count += description_count except Exception as exc: # Processing failed. Leave in cache for retry or discard. diff --git a/src/lean_spec/node/sync/service.py b/src/lean_spec/node/sync/service.py index 5c9058b24..cad53f8e1 100644 --- a/src/lean_spec/node/sync/service.py +++ b/src/lean_spec/node/sync/service.py @@ -385,17 +385,17 @@ async def on_gossip_attestation( return slot = attestation.data.slot - validator_id = attestation.validator_id + validator_index = attestation.validator_index peer_str = str(peer_id) if peer_id is not None else "local" logger.info( "Attestation received from peer %s slot=%s validator=%s", peer_str, slot, - validator_id, + validator_index, ) # Aggregator role requires both an active validator and operator opt-in. - is_aggregator_role = self.store.validator_id is not None and self.is_aggregator + is_aggregator_role = self.store.validator_index is not None and self.is_aggregator # The store validates the signature and updates branch weights. # @@ -411,7 +411,7 @@ async def on_gossip_attestation( "Attestation from peer %s slot=%s validator=%s: validation and signature ok", peer_str, slot, - validator_id, + validator_index, ) except (AssertionError, KeyError) as e: metrics.lean_attestations_invalid_total.labels(source="gossip").inc() @@ -419,7 +419,7 @@ async def on_gossip_attestation( "Attestation from peer %s slot=%s validator=%s: validation or signature failed: %s", peer_str, slot, - validator_id, + validator_index, e, ) # Target block has not arrived yet; buffer for post-block replay. @@ -472,7 +472,7 @@ async def on_gossip_aggregated_attestation( def _replay_pending_attestations(self) -> None: """Retry buffered attestations after a block is processed.""" # Aggregator role for this replay matches the live gossip path. - is_aggregator_role = self.store.validator_id is not None and self.is_aggregator + is_aggregator_role = self.store.validator_index is not None and self.is_aggregator # Drain the queue into a local and iterate it. # Successful retries disappear into the store. @@ -496,9 +496,9 @@ def _replay_pending_attestations(self) -> None: self._pending_attestations.append(attestation) # Same mechanism for aggregated attestations. - pending_agg = self._pending_aggregated_attestations + pending_aggregate = self._pending_aggregated_attestations self._pending_aggregated_attestations = deque(maxlen=MAX_PENDING_ATTESTATIONS) - for signed_attestation in pending_agg: + for signed_attestation in pending_aggregate: try: self.store = self.spec.on_gossip_aggregated_attestation( self.store, signed_attestation @@ -543,26 +543,26 @@ def _deconstruct_block_into_store( return store, [] # The multi-message aggregate proof was built against the parent state's validator set. - # Without it we cannot resolve the pubkey layout the proof was bound to. + # Without it we cannot resolve the public_key layout the proof was bound to. parent_state = store.states.get(block.block.parent_root) if parent_state is None: return store, [] validators = parent_state.validators - # Build the per-message pubkey layout once. + # Build the per-message public_key layout once. # The layout is invariant per block: one entry per body attestation # in order, then the proposer entry. Hoisted out of the per-att loop # to avoid quadratic work when many block attestations need splitting. public_keys_per_message: list[list[PublicKey]] = [] - for att in block_attestations: + for attestation in block_attestations: public_keys_per_message.append( [ - validators[vid].get_attestation_pubkey() - for vid in att.aggregation_bits.to_validator_indices() + validators[validator_index].get_attestation_public_key() + for validator_index in attestation.aggregation_bits.to_validator_indices() ] ) public_keys_per_message.append( - [validators[block.block.proposer_index].get_proposal_pubkey()] + [validators[block.block.proposer_index].get_proposal_public_key()] ) # Index local partial single-message aggregate proofs by AttestationData root. Equivalent @@ -581,8 +581,8 @@ def _deconstruct_block_into_store( } aggregates: list[SignedAggregatedAttestation] = [] - for att in block_attestations: - data = att.data + for attestation in block_attestations: + data = attestation.data # Only spend a split on attestations that can still move # justification forward. A target at or behind the store's @@ -591,7 +591,7 @@ def _deconstruct_block_into_store( continue data_root = hash_tree_root(data) - block_participants = set(att.aggregation_bits.to_validator_indices()) + block_participants = set(attestation.aggregation_bits.to_validator_indices()) local_proofs = local_proofs_by_root.get(data_root, []) local_union: set = set() @@ -606,10 +606,10 @@ def _deconstruct_block_into_store( try: # The split takes the bits from the block attestation this # component binds, since the Rust binding does not return them. - block_single_message_aggregate = block.proof.split_by_msg( + block_single_message_aggregate = block.proof.split_by_message( message=data_root, public_keys_per_message=public_keys_per_message, - participants=att.aggregation_bits, + participants=attestation.aggregation_bits, ) if local_proofs: @@ -618,8 +618,8 @@ def _deconstruct_block_into_store( ( child, [ - validators[vid].get_attestation_pubkey() - for vid in child.participants.to_validator_indices() + validators[validator_index].get_attestation_public_key() + for validator_index in child.participants.to_validator_indices() ], ) for child in (block_single_message_aggregate, *local_proofs) diff --git a/src/lean_spec/node/validator/registry.py b/src/lean_spec/node/validator/registry.py index 4e39cc1b1..9a7698f1b 100644 --- a/src/lean_spec/node/validator/registry.py +++ b/src/lean_spec/node/validator/registry.py @@ -19,8 +19,8 @@ num_validators: 3 validators: - index: 0 - pubkey_hex: 0xe2a03c... - privkey_file: validator_0_sk.ssz + public_key_hex: 0xe2a03c... + private_key_file: validator_0_secret_key.ssz """ from __future__ import annotations @@ -48,16 +48,16 @@ class ValidatorManifestEntry(BaseModel): index: ValidatorIndex """Validator index in the registry.""" - attestation_pubkey_hex: Bytes52 + attestation_public_key_hex: Bytes52 """XMSS public key for signing attestations.""" - proposal_pubkey_hex: Bytes52 + proposal_public_key_hex: Bytes52 """XMSS public key for signing block proposals.""" - attestation_privkey_file: str + attestation_private_key_file: str """Filename of the attestation private key file.""" - proposal_privkey_file: str + proposal_private_key_file: str """Filename of the proposal private key file.""" @@ -216,7 +216,7 @@ def __len__(self) -> int: return len(self._validators) @classmethod - def from_keys_directory(cls, node_id: str, base_dir: Path | str) -> ValidatorRegistry: + def from_keys_directory(cls, node_id: str, base_directory: Path | str) -> ValidatorRegistry: """Load a validator registry from the ream/zeam keystore layout. Two files relative to the base directory: @@ -227,7 +227,7 @@ def from_keys_directory(cls, node_id: str, base_dir: Path | str) -> ValidatorReg Args: node_id: Identifier looked up in the node-to-validator mapping. - base_dir: Directory containing the two layout files. + base_directory: Directory containing the two layout files. Returns: Registry populated with the keys assigned to the node. @@ -236,7 +236,7 @@ def from_keys_directory(cls, node_id: str, base_dir: Path | str) -> ValidatorReg FileNotFoundError: If the manifest file is missing. A missing validators mapping is allowed and yields an empty registry. """ - base = Path(base_dir) + base = Path(base_directory) manifest_path = base / "hash-sig-keys" / "validator-keys-manifest.yaml" if not manifest_path.exists(): raise FileNotFoundError(f"Validator keys manifest not found: {manifest_path}") @@ -289,7 +289,7 @@ def from_yaml( # Load keys for assigned validators. registry = cls() - manifest_dir = manifest_path.parent + manifest_directory = manifest_path.parent for index in assigned_indices: entry = manifest_by_index.get(ValidatorIndex(index)) @@ -304,22 +304,22 @@ def from_yaml( continue # Load attestation secret key from SSZ file. - att_key_path = manifest_dir / entry.attestation_privkey_file + attestation_key_path = manifest_directory / entry.attestation_private_key_file try: - attestation_secret_key = SecretKey.decode_bytes(att_key_path.read_bytes()) + attestation_secret_key = SecretKey.decode_bytes(attestation_key_path.read_bytes()) except FileNotFoundError as e: - raise ValueError(f"Attestation key file not found: {att_key_path}") from e + raise ValueError(f"Attestation key file not found: {attestation_key_path}") from e except Exception as e: raise ValueError( f"Failed to load attestation key for validator {index}: {e}" ) from e # Load proposal secret key from SSZ file. - prop_key_path = manifest_dir / entry.proposal_privkey_file + proposal_key_path = manifest_directory / entry.proposal_private_key_file try: - proposal_secret_key = SecretKey.decode_bytes(prop_key_path.read_bytes()) + proposal_secret_key = SecretKey.decode_bytes(proposal_key_path.read_bytes()) except FileNotFoundError as e: - raise ValueError(f"Proposal key file not found: {prop_key_path}") from e + raise ValueError(f"Proposal key file not found: {proposal_key_path}") from e except Exception as e: raise ValueError(f"Failed to load proposal key for validator {index}: {e}") from e diff --git a/src/lean_spec/node/validator/service.py b/src/lean_spec/node/validator/service.py index a2c26015b..09432c930 100644 --- a/src/lean_spec/node/validator/service.py +++ b/src/lean_spec/node/validator/service.py @@ -377,7 +377,8 @@ async def _produce_attestations(self, slot: Slot) -> None: # validator's attestation in attestation_signatures, reducing the # aggregation count below the 2/3 safe-target threshold. is_aggregator_role = ( - self.sync_service.store.validator_id is not None and self.sync_service.is_aggregator + self.sync_service.store.validator_index is not None + and self.sync_service.is_aggregator ) try: self.sync_service.store = self.spec.on_gossip_attestation( @@ -435,7 +436,7 @@ def _sign_block( "proposal_secret_key", ) - # Resolve validator pubkeys from state using validator indices. + # Resolve validator public_keys from state using validator indices. key_state = self.sync_service.store.states.get(block_root) if key_state is None: key_state = self.sync_service.store.states.get(self.sync_service.store.head) @@ -447,13 +448,13 @@ def _sign_block( validators = key_state.validators if not validator_index.is_valid(Uint64(len(validators))): raise ValueError(f"Validator {validator_index} not found in state validators") - proposer_pubkey = validators[validator_index].get_proposal_pubkey() + proposer_public_key = validators[validator_index].get_proposal_public_key() # Wrap the proposer's raw XMSS signature into a singleton single-message aggregate. # The single fresh entry carries the proposer index alongside its key and signature. proposer_single_message_aggregate = SingleMessageAggregate.aggregate( children=[], - raw_xmss=[(validator_index, proposer_pubkey, proposer_signature)], + raw_xmss=[(validator_index, proposer_public_key, proposer_signature)], message=block_root, slot=block.slot, ) @@ -461,23 +462,23 @@ def _sign_block( # Merge the per-attestation proofs and the proposer single-message aggregate into one # multi-message aggregate proof. Order matters: verify_signatures expects the proposer # entry to be last, parallel to block.body.attestations + 1. - # The pubkey lookup below indexes the active validator set, so each + # The public_key lookup below indexes the active validator set, so each # participant must fall within it. # A stale partial aggregate would otherwise blow up deep inside # the aggregator with an opaque KeyError. num_validators = Uint64(len(validators)) public_keys_per_part: list[list[PublicKey]] = [] for proof in attestation_proofs: - part_pubkeys: list[PublicKey] = [] - for vid in proof.participants.to_validator_indices(): - if not vid.is_valid(num_validators): + part_public_keys: list[PublicKey] = [] + for validator_index in proof.participants.to_validator_indices(): + if not validator_index.is_valid(num_validators): raise ValueError( - f"Attestation proof references validator {vid}; " + f"Attestation proof references validator {validator_index}; " f"active set has {num_validators} validators" ) - part_pubkeys.append(validators[vid].get_attestation_pubkey()) - public_keys_per_part.append(part_pubkeys) - public_keys_per_part.append([proposer_pubkey]) + part_public_keys.append(validators[validator_index].get_attestation_public_key()) + public_keys_per_part.append(part_public_keys) + public_keys_per_part.append([proposer_public_key]) merged = MultiMessageAggregate.aggregate( [*attestation_proofs, proposer_single_message_aggregate], @@ -520,7 +521,7 @@ def _sign_attestation( ) return SignedAttestation( - validator_id=validator_index, + validator_index=validator_index, data=attestation_data, signature=signature, ) diff --git a/src/lean_spec/spec/crypto/merkleization.py b/src/lean_spec/spec/crypto/merkleization.py index 741421710..884079aef 100644 --- a/src/lean_spec/spec/crypto/merkleization.py +++ b/src/lean_spec/spec/crypto/merkleization.py @@ -39,7 +39,7 @@ def _next_pow2(x: int) -> int: _ZERO_HASHES: Final[tuple[Bytes32, ...]] = tuple( accumulate( repeat(None, 64), - lambda prev, _: Bytes32(sha256(prev + prev).digest()), + lambda previous, _: Bytes32(sha256(previous + previous).digest()), initial=ZERO_HASH, ) ) @@ -265,11 +265,11 @@ def _htr_bitlist_base(value: BaseBitlist) -> Bytes32: @hash_tree_root.register def _htr_vector(value: SSZVector) -> Bytes32: cls = type(value) - elem_t, length = cls.ELEMENT_TYPE, cls.LENGTH - if issubclass(elem_t, (BaseUint, Boolean, Fp)): + element_t, length = cls.ELEMENT_TYPE, cls.LENGTH + if issubclass(element_t, (BaseUint, Boolean, Fp)): # Basic elements pack their serialized bytes into a single byte stream before chunking. - elem_size = elem_t.get_byte_length() - limit_chunks = ceil(length * elem_size / BYTES_PER_CHUNK) + element_size = element_t.get_byte_length() + limit_chunks = ceil(length * element_size / BYTES_PER_CHUNK) return merkleize( _pack_bytes(b"".join(e.encode_bytes() for e in value)), limit=limit_chunks, @@ -281,10 +281,10 @@ def _htr_vector(value: SSZVector) -> Bytes32: @hash_tree_root.register def _htr_list(value: SSZList) -> Bytes32: cls = type(value) - elem_t, limit = cls.ELEMENT_TYPE, cls.LIMIT - if issubclass(elem_t, (BaseUint, Boolean, Fp)): - elem_size = elem_t.get_byte_length() - limit_chunks = ceil(limit * elem_size / BYTES_PER_CHUNK) + element_t, limit = cls.ELEMENT_TYPE, cls.LIMIT + if issubclass(element_t, (BaseUint, Boolean, Fp)): + element_size = element_t.get_byte_length() + limit_chunks = ceil(limit * element_size / BYTES_PER_CHUNK) root = merkleize( _pack_bytes(b"".join(e.encode_bytes() for e in value)), limit=limit_chunks, diff --git a/src/lean_spec/spec/crypto/poseidon.py b/src/lean_spec/spec/crypto/poseidon.py index 67f1ad34e..212eeaa97 100644 --- a/src/lean_spec/spec/crypto/poseidon.py +++ b/src/lean_spec/spec/crypto/poseidon.py @@ -1286,7 +1286,7 @@ def _permute_jit( - No initial linear layer is applied before the round structure begins. - This matches the original Poseidon design. """ - const_idx = 0 + const_index = 0 # Phase 1: opening full rounds. # @@ -1294,8 +1294,8 @@ def _permute_jit( # attacker control over inputs is highest. for _ in range(half_rounds_f): # Add round constants to entire state. - state[:] = (state + round_constants[const_idx : const_idx + width]) % p - const_idx += width + state[:] = (state + round_constants[const_index : const_index + width]) % p + const_index += width # Apply S-box (x -> x^d) to full state. # @@ -1314,8 +1314,8 @@ def _permute_jit( # It still saturates algebraic degree while cutting SNARK constraint cost. for _ in range(rounds_p): # Add round constants to entire state. - state[:] = (state + round_constants[const_idx : const_idx + width]) % p - const_idx += width + state[:] = (state + round_constants[const_index : const_index + width]) % p + const_index += width # Apply S-box to first element only. state[0] = (state[0] * state[0] % p) * state[0] % p @@ -1329,8 +1329,8 @@ def _permute_jit( # unwind the partial-round middle. for _ in range(half_rounds_f): # Add round constants to entire state. - state[:] = (state + round_constants[const_idx : const_idx + width]) % p - const_idx += width + state[:] = (state + round_constants[const_index : const_index + width]) % p + const_index += width # Apply S-box to full state. state[:] = (state * state % p) * state % p diff --git a/src/lean_spec/spec/crypto/xmss/constants.py b/src/lean_spec/spec/crypto/xmss/constants.py index ea7660251..1632aea19 100644 --- a/src/lean_spec/spec/crypto/xmss/constants.py +++ b/src/lean_spec/spec/crypto/xmss/constants.py @@ -40,21 +40,21 @@ class XmssConfig(StrictBaseModel): MAX_TRIES: int """How often one should try at most to resample a random value.""" - PARAMETER_LEN: int + PARAMETER_LENGTH: int """The length of the public parameter P. It is used to specialize the hash function.""" - TWEAK_LEN_FE: int + TWEAK_LENGTH_FIELD_ELEMENTS: int """The length of a domain-separating tweak.""" - MSG_LEN_FE: int + MESSAGE_LENGTH_FIELD_ELEMENTS: int """The length of a message after being encoded into field elements.""" - RAND_LEN_FE: int + RAND_LENGTH_FIELD_ELEMENTS: int """The length of the randomness rho used during message encoding.""" - HASH_LEN_FE: int + HASH_LENGTH_FIELD_ELEMENTS: int """The output length of the main tweakable hash function.""" CAPACITY: int @@ -81,12 +81,12 @@ def LEAVES_PER_BOTTOM_TREE(self) -> int: return 1 << (self.LOG_LIFETIME // 2) @property - def MH_HASH_LEN_FE(self) -> int: + def MH_HASH_LENGTH_FIELD_ELEMENTS(self) -> int: """Number of Poseidon output field elements needed for the aborting decode.""" return math.ceil(self.DIMENSION / self.Z) @property - def SIGNATURE_LEN_BYTES(self) -> int: + def SIGNATURE_LENGTH_BYTES(self) -> int: """The SSZ-encoded size of a signature in bytes. # Layout @@ -96,10 +96,10 @@ def SIGNATURE_LEN_BYTES(self) -> int: released chain ends : one digest per hash chain (variable) """ # One sibling digest per level climbed from leaf to root. - path_siblings_size = self.LOG_LIFETIME * self.HASH_LEN_FE * P_BYTES - rho_size = self.RAND_LEN_FE * P_BYTES + path_siblings_size = self.LOG_LIFETIME * self.HASH_LENGTH_FIELD_ELEMENTS * P_BYTES + rho_size = self.RAND_LENGTH_FIELD_ELEMENTS * P_BYTES # One released chain end per chain, so the count is the scheme dimension. - hashes_size = self.DIMENSION * self.HASH_LEN_FE * P_BYTES + hashes_size = self.DIMENSION * self.HASH_LENGTH_FIELD_ELEMENTS * P_BYTES # SSZ writes a four-byte offset ahead of each variable-length field. # @@ -122,11 +122,11 @@ def SIGNATURE_LEN_BYTES(self) -> int: Q=127, TARGET_SUM=200, MAX_TRIES=100_000, - PARAMETER_LEN=5, - TWEAK_LEN_FE=2, - MSG_LEN_FE=9, - RAND_LEN_FE=7, - HASH_LEN_FE=8, + PARAMETER_LENGTH=5, + TWEAK_LENGTH_FIELD_ELEMENTS=2, + MESSAGE_LENGTH_FIELD_ELEMENTS=9, + RAND_LENGTH_FIELD_ELEMENTS=7, + HASH_LENGTH_FIELD_ELEMENTS=8, CAPACITY=9, ) """Production XMSS configuration matching the canonical Rust instantiation.""" @@ -141,11 +141,11 @@ def SIGNATURE_LEN_BYTES(self) -> int: Q=127, TARGET_SUM=6, MAX_TRIES=100_000, - PARAMETER_LEN=5, - TWEAK_LEN_FE=2, - MSG_LEN_FE=9, - RAND_LEN_FE=7, - HASH_LEN_FE=8, + PARAMETER_LENGTH=5, + TWEAK_LENGTH_FIELD_ELEMENTS=2, + MESSAGE_LENGTH_FIELD_ELEMENTS=9, + RAND_LENGTH_FIELD_ELEMENTS=7, + HASH_LENGTH_FIELD_ELEMENTS=8, CAPACITY=9, ) """Lightweight XMSS configuration for fast test execution.""" diff --git a/src/lean_spec/spec/crypto/xmss/containers.py b/src/lean_spec/spec/crypto/xmss/containers.py index 94f61e9ed..539714325 100644 --- a/src/lean_spec/spec/crypto/xmss/containers.py +++ b/src/lean_spec/spec/crypto/xmss/containers.py @@ -55,7 +55,7 @@ def is_fixed_size(cls) -> bool: @override def get_byte_length(cls) -> int: """Fixed byte length of an SSZ-encoded signature.""" - return TARGET_CONFIG.SIGNATURE_LEN_BYTES + return TARGET_CONFIG.SIGNATURE_LENGTH_BYTES @model_serializer(mode="plain", when_used="json") def _serialize_as_bytes(self) -> str: diff --git a/src/lean_spec/spec/crypto/xmss/encoding.py b/src/lean_spec/spec/crypto/xmss/encoding.py index 07976e13d..cf6b5205b 100644 --- a/src/lean_spec/spec/crypto/xmss/encoding.py +++ b/src/lean_spec/spec/crypto/xmss/encoding.py @@ -62,7 +62,7 @@ def encode_message(config: XmssConfig, message: Bytes32) -> list[Fp]: The bytes are read little-endian as a single integer. """ acc = int.from_bytes(message, "little") - return int_to_base_p(acc, config.MSG_LEN_FE) + return int_to_base_p(acc, config.MESSAGE_LENGTH_FIELD_ELEMENTS) def encode_epoch(config: XmssConfig, epoch: Uint64) -> list[Fp]: @@ -74,7 +74,7 @@ def encode_epoch(config: XmssConfig, epoch: Uint64) -> list[Fp]: # # (epoch << 8) | MESSAGE_PREFIX acc = (int(epoch) << 8) | TWEAK_PREFIX_MESSAGE - return int_to_base_p(acc, config.TWEAK_LEN_FE) + return int_to_base_p(acc, config.TWEAK_LENGTH_FIELD_ELEMENTS) def aborting_decode(config: XmssConfig, field_elements: list[Fp]) -> list[int] | None: @@ -130,12 +130,12 @@ def message_hash( Codeword of DIMENSION digits in [0, BASE-1], or None on rejection. """ # Encode the message and epoch as field elements before hashing. - message_fe = encode_message(config, message) - epoch_fe = encode_epoch(config, epoch) + message_field_elements = encode_message(config, message) + epoch_field_elements = encode_epoch(config, epoch) # One Poseidon call produces enough output for the aborting decode. - base_input = message_fe + parameter.elements + epoch_fe + rho.elements - poseidon_output = poseidon.compress(base_input, 24, config.MH_HASH_LEN_FE) + base_input = message_field_elements + parameter.elements + epoch_field_elements + rho.elements + poseidon_output = poseidon.compress(base_input, 24, config.MH_HASH_LENGTH_FIELD_ELEMENTS) return aborting_decode(config, poseidon_output) diff --git a/src/lean_spec/spec/crypto/xmss/field.py b/src/lean_spec/spec/crypto/xmss/field.py index 053b0b8bc..0c3fa0779 100644 --- a/src/lean_spec/spec/crypto/xmss/field.py +++ b/src/lean_spec/spec/crypto/xmss/field.py @@ -24,9 +24,9 @@ def random_field_elements(length: int) -> list[Fp]: def random_parameter(config: XmssConfig) -> Parameter: """Sample a fresh public parameter for one XMSS key pair.""" - return Parameter(data=random_field_elements(config.PARAMETER_LEN)) + return Parameter(data=random_field_elements(config.PARAMETER_LENGTH)) def random_domain(config: XmssConfig) -> HashDigestVector: """Sample a fresh hash-digest-sized vector of field elements.""" - return HashDigestVector(data=random_field_elements(config.HASH_LEN_FE)) + return HashDigestVector(data=random_field_elements(config.HASH_LENGTH_FIELD_ELEMENTS)) diff --git a/src/lean_spec/spec/crypto/xmss/interface.py b/src/lean_spec/spec/crypto/xmss/interface.py index b89436e19..4e189fa22 100644 --- a/src/lean_spec/spec/crypto/xmss/interface.py +++ b/src/lean_spec/spec/crypto/xmss/interface.py @@ -159,8 +159,8 @@ def key_gen(self, activation_slot: Slot, num_active_slots: Uint64) -> KeyPair: ) # Pack the public root and the resident signer state into the key pair. - pk = PublicKey(root=top_tree.root(), parameter=parameter) - sk = SecretKey( + public_key = PublicKey(root=top_tree.root(), parameter=parameter) + secret_key = SecretKey( prf_key=prf_key, parameter=parameter, activation_slot=Slot(actual_activation_slot), @@ -170,9 +170,9 @@ def key_gen(self, activation_slot: Slot, num_active_slots: Uint64) -> KeyPair: left_bottom_tree=left_bottom_tree, right_bottom_tree=right_bottom_tree, ) - return KeyPair(public_key=pk, secret_key=sk) + return KeyPair(public_key=public_key, secret_key=secret_key) - def sign(self, sk: SecretKey, slot: Slot, message: Bytes32) -> Signature: + def sign(self, secret_key: SecretKey, slot: Slot, message: Bytes32) -> Signature: """Produce a signature for a message at a specific slot. Phase 1: enforce that the slot is inside the activation and prepared windows. @@ -180,11 +180,11 @@ def sign(self, sk: SecretKey, slot: Slot, message: Bytes32) -> Signature: Phase 3: walk each Winternitz chain to the released hash dictated by the codeword. Phase 4: build the combined Merkle path through the bottom and top trees. - Signing is deterministic in (sk, slot, message). + Signing is deterministic in (secret_key, slot, message). A secret key must never sign two different messages for the same slot. Args: - sk: Secret key. + secret_key: Secret key. slot: Signing slot. message: Message to sign. @@ -197,13 +197,13 @@ def sign(self, sk: SecretKey, slot: Slot, message: Bytes32) -> Signature: """ config = self.config slot_int = int(slot) - activation_int = int(sk.activation_slot) + activation_int = int(secret_key.activation_slot) # Phase 1a: the slot must lie in the key's activation range. # # This is a synchronized one-time scheme: each slot consumes a distinct one-time key. # The key only holds material for the contiguous range fixed at generation. - if not (activation_int <= slot_int < activation_int + int(sk.num_active_slots)): + if not (activation_int <= slot_int < activation_int + int(secret_key.num_active_slots)): raise ValueError("Key is not active for the specified slot.") # Phase 1b: the slot must lie in the prepared window. @@ -211,7 +211,7 @@ def sign(self, sk: SecretKey, slot: Slot, message: Bytes32) -> Signature: # The signature opens this slot's leaf through the bottom tree that holds it. # Only the two adjacent resident bottom trees are available. # A slot outside them would force rebuilding a tree from the PRF, so we refuse. - prepared = self.get_prepared_interval(sk) + prepared = self.get_prepared_interval(secret_key) if slot_int not in prepared: raise ValueError( f"Slot {slot} is outside the prepared interval " @@ -228,8 +228,10 @@ def sign(self, sk: SecretKey, slot: Slot, message: Bytes32) -> Signature: # The randomness is derived from the PRF, keyed by the message and an attempt counter. # Signing the same message twice is therefore reproducible. for attempts in range(config.MAX_TRIES): - rho = sk.prf_key.derive_randomness(config, slot, message, Uint64(attempts)) - codeword = target_sum_encode(self.poseidon, config, sk.parameter, message, rho, slot) + rho = secret_key.prf_key.derive_randomness(config, slot, message, Uint64(attempts)) + codeword = target_sum_encode( + self.poseidon, config, secret_key.parameter, message, rho, slot + ) if codeword is not None: break else: @@ -248,10 +250,10 @@ def sign(self, sk: SecretKey, slot: Slot, message: Bytes32) -> Signature: # The verifier later finishes the remaining steps to reach the chain end. ots_hashes: list[HashDigestVector] = [] for chain_index, steps in enumerate(codeword): - start_digest = sk.prf_key.derive_chain_start(config, slot, Uint64(chain_index)) + start_digest = secret_key.prf_key.derive_chain_start(config, slot, Uint64(chain_index)) ots_digest = self.poseidon.hash_chain( config=config, - parameter=sk.parameter, + parameter=secret_key.parameter, epoch=slot, chain_index=chain_index, start_step=0, @@ -265,14 +267,18 @@ def sign(self, sk: SecretKey, slot: Slot, message: Bytes32) -> Signature: # The opening climbs the bottom tree that holds the slot, then the top tree. # The slot's side of the prepared window selects which resident bottom tree to climb. boundary = prepared.start + config.LEAVES_PER_BOTTOM_TREE - bottom_tree = sk.left_bottom_tree if slot_int < boundary else sk.right_bottom_tree - path = combined_path(sk.top_tree, bottom_tree, Uint64(slot)) + bottom_tree = ( + secret_key.left_bottom_tree if slot_int < boundary else secret_key.right_bottom_tree + ) + path = combined_path(secret_key.top_tree, bottom_tree, Uint64(slot)) # The signature carries the opening, the randomness, and the released chain values. # The randomness lets the verifier recompute the same codeword. return Signature(path=path, rho=rho, hashes=HashDigestList(data=ots_hashes)) - def verify(self, pk: PublicKey, slot: Slot, message: Bytes32, sig: Signature) -> bool: + def verify( + self, public_key: PublicKey, slot: Slot, message: Bytes32, signature: Signature + ) -> bool: """Verify a signature against a public key, message, and slot. Phase 1: bound-check the slot. @@ -282,10 +288,10 @@ def verify(self, pk: PublicKey, slot: Slot, message: Bytes32, sig: Signature) -> Phase 4: rebuild the Merkle root from the chain endpoints and the opening. Args: - pk: Public key. + public_key: Public key. slot: Signing slot claimed by the signature. message: Message claimed by the signature. - sig: Signature to verify. + signature: Signature to verify. Returns: True when the signature is valid against the public key, false otherwise. @@ -300,7 +306,9 @@ def verify(self, pk: PublicKey, slot: Slot, message: Bytes32, sig: Signature) -> # Phase 2: rederive the codeword from the signature's randomness. # A failing aborting decode means the signature cannot be valid. - codeword = target_sum_encode(self.poseidon, config, pk.parameter, message, sig.rho, slot) + codeword = target_sum_encode( + self.poseidon, config, public_key.parameter, message, signature.rho, slot + ) if codeword is None: return False @@ -309,10 +317,10 @@ def verify(self, pk: PublicKey, slot: Slot, message: Bytes32, sig: Signature) -> for chain_index, xi in enumerate(codeword): # The signature provides the digest after xi steps along the chain. # We hash the remaining BASE - 1 - xi times to reach the endpoint. - start_digest = sig.hashes[chain_index] + start_digest = signature.hashes[chain_index] end_digest = self.poseidon.hash_chain( config=config, - parameter=pk.parameter, + parameter=public_key.parameter, epoch=slot, chain_index=chain_index, start_step=xi, @@ -325,22 +333,22 @@ def verify(self, pk: PublicKey, slot: Slot, message: Bytes32, sig: Signature) -> return verify_path( poseidon=self.poseidon, config=config, - parameter=pk.parameter, - root=pk.root, + parameter=public_key.parameter, + root=public_key.root, position=slot, leaf_parts=chain_ends, - opening=sig.path, + opening=signature.path, ) - def get_activation_interval(self, sk: SecretKey) -> range: + def get_activation_interval(self, secret_key: SecretKey) -> range: """Return the activation interval as a Python range. A signature is only valid for a slot inside this range. """ - start = int(sk.activation_slot) - return range(start, start + int(sk.num_active_slots)) + start = int(secret_key.activation_slot) + return range(start, start + int(secret_key.num_active_slots)) - def get_prepared_interval(self, sk: SecretKey) -> range: + def get_prepared_interval(self, secret_key: SecretKey) -> range: """Return the prepared interval as a Python range. The prepared interval is the slot window covered by the two resident bottom trees. @@ -348,10 +356,10 @@ def get_prepared_interval(self, sk: SecretKey) -> range: rebuilding a bottom tree from the PRF. """ leaves_per_bottom_tree = self.config.LEAVES_PER_BOTTOM_TREE - start = int(sk.left_bottom_tree_index) * leaves_per_bottom_tree + start = int(secret_key.left_bottom_tree_index) * leaves_per_bottom_tree return range(start, start + 2 * leaves_per_bottom_tree) - def advance_preparation(self, sk: SecretKey) -> SecretKey: + def advance_preparation(self, secret_key: SecretKey) -> SecretKey: """Slide the prepared window one bottom tree forward. Phase 1: bail out when the next window would exceed the activation interval. @@ -361,33 +369,33 @@ def advance_preparation(self, sk: SecretKey) -> SecretKey: Returning the same key when no advancement is possible keeps callers simple. Args: - sk: Secret key whose prepared window should advance. + secret_key: Secret key whose prepared window should advance. Returns: A secret key with the window shifted by one bottom tree. """ - left_index = int(sk.left_bottom_tree_index) + left_index = int(secret_key.left_bottom_tree_index) # Phase 1: no advancement once the activation interval is fully consumed. next_prepared_end_slot = (left_index + 3) * self.config.LEAVES_PER_BOTTOM_TREE - activation_end = int(sk.activation_slot) + int(sk.num_active_slots) + activation_end = int(secret_key.activation_slot) + int(secret_key.num_active_slots) if next_prepared_end_slot > activation_end: - return sk + return secret_key # Phase 2: rebuild the next bottom tree from the master PRF key. new_right_bottom_tree = HashSubTree.from_prf_key( poseidon=self.poseidon, config=self.config, - prf_key=sk.prf_key, + prf_key=secret_key.prf_key, bottom_tree_index=Uint64(left_index + 2), - parameter=sk.parameter, + parameter=secret_key.parameter, ) # Phase 3: rotate the right tree into the left slot, advance the index. - sk.left_bottom_tree = sk.right_bottom_tree - sk.right_bottom_tree = new_right_bottom_tree - sk.left_bottom_tree_index = Uint64(left_index + 1) - return sk + secret_key.left_bottom_tree = secret_key.right_bottom_tree + secret_key.right_bottom_tree = new_right_bottom_tree + secret_key.left_bottom_tree_index = Uint64(left_index + 1) + return secret_key PROD_SIGNATURE_SCHEME = GeneralizedXmssScheme(config=PROD_CONFIG, poseidon=PROD_POSEIDON) diff --git a/src/lean_spec/spec/crypto/xmss/merkle.py b/src/lean_spec/spec/crypto/xmss/merkle.py index 44057ef16..26dd9ed1d 100644 --- a/src/lean_spec/spec/crypto/xmss/merkle.py +++ b/src/lean_spec/spec/crypto/xmss/merkle.py @@ -340,10 +340,10 @@ def new_bottom_tree( # The real root is the node at this tree's index, not always position zero. # An odd index leaves a random pad at position zero, so select by absolute index. top = subtree.layers[-1] - root_idx = int(bottom_tree_index - top.start_index) + root_index = int(bottom_tree_index - top.start_index) root_layer = HashTreeLayer( start_index=bottom_tree_index, - nodes=HashDigestList(data=[top.nodes[root_idx]]), + nodes=HashDigestList(data=[top.nodes[root_index]]), ) return cls( depth=Uint64(depth), @@ -479,10 +479,10 @@ def path(self, position: Uint64) -> HashTreeOpening: for layer in self.layers[:-1]: # The sibling sits at the position with the last bit flipped, then we # rebase by the layer's start_index because the layer is sparse. - sibling_idx = (pos ^ 1) - int(layer.start_index) - if not (0 <= sibling_idx < len(layer.nodes)): - raise ValueError(f"Sibling index {sibling_idx} out of bounds.") - siblings.append(layer.nodes[sibling_idx]) + sibling_index = (pos ^ 1) - int(layer.start_index) + if not (0 <= sibling_index < len(layer.nodes)): + raise ValueError(f"Sibling index {sibling_index} out of bounds.") + siblings.append(layer.nodes[sibling_index]) pos //= 2 return HashTreeOpening(siblings=HashDigestList(data=siblings)) diff --git a/src/lean_spec/spec/crypto/xmss/poseidon.py b/src/lean_spec/spec/crypto/xmss/poseidon.py index 2e357bdf2..2a2e59ce0 100644 --- a/src/lean_spec/spec/crypto/xmss/poseidon.py +++ b/src/lean_spec/spec/crypto/xmss/poseidon.py @@ -47,7 +47,7 @@ def _get_engine(self, width: int) -> Poseidon: self._engines[width] = Poseidon(params) return self._engines[width] - def compress(self, input_vec: list[Fp], width: int, output_len: int) -> list[Fp]: + def compress(self, input_vec: list[Fp], width: int, output_length: int) -> list[Fp]: """Poseidon in compression mode. Computes Truncate(Permute(padded_input) + padded_input). @@ -59,13 +59,13 @@ def compress(self, input_vec: list[Fp], width: int, output_len: int) -> list[Fp] Args: input_vec: Field elements to hash. width: Permutation state width, either 16 or 24. - output_len: Number of output field elements to return. + output_length: Number of output field elements to return. Returns: - Truncated digest of output_len field elements. + Truncated digest of output_length field elements. """ # The output cannot be longer than the input vector after padding. - if len(input_vec) < output_len: + if len(input_vec) < output_length: raise ValueError("Input vector is too short for requested output length.") # Select the cached engine matching the requested permutation width. @@ -78,9 +78,9 @@ def compress(self, input_vec: list[Fp], width: int, output_len: int) -> list[Fp] permuted_state = engine.permute(padded_input) final_state = [p + i for p, i in zip(permuted_state, padded_input, strict=True)] - return final_state[:output_len] + return final_state[:output_length] - def safe_domain_separator(self, lengths: list[int], capacity_len: int) -> list[Fp]: + def safe_domain_separator(self, lengths: list[int], capacity_length: int) -> list[Fp]: """Build a capacity initialization vector for the sponge construction. Hashes the packed length parameters into a fixed-size capacity value. @@ -88,10 +88,10 @@ def safe_domain_separator(self, lengths: list[int], capacity_len: int) -> list[F Args: lengths: Integer parameters that define the hash context. - capacity_len: Number of field elements in the returned capacity value. + capacity_length: Number of field elements in the returned capacity value. Returns: - A capacity vector of length capacity_len. + A capacity vector of length capacity_length. """ # Pack all lengths into a single unambiguous integer using 32-bit slots. acc = 0 @@ -101,29 +101,29 @@ def safe_domain_separator(self, lengths: list[int], capacity_len: int) -> list[F # Compress the decomposed vector through the width-24 engine. # Width 24 is the only mode used for sponge domain separation. input_vec = int_to_base_p(acc, 24) - return self.compress(input_vec, 24, capacity_len) + return self.compress(input_vec, 24, capacity_length) def sponge( self, input_vec: list[Fp], capacity_value: list[Fp], - output_len: int, + output_length: int, width: int, ) -> list[Fp]: """Poseidon in sponge mode. Phase 1: load capacity, zero-extend input to a multiple of the rate. Phase 2: absorb each rate-sized chunk by replacement, then permute. - Phase 3: squeeze the rate slots until output_len elements are produced. + Phase 3: squeeze the rate slots until output_length elements are produced. Args: input_vec: Variable-length input. capacity_value: Domain-separating capacity initialization. - output_len: Desired output length in field elements. + output_length: Desired output length in field elements. width: Permutation state width. Returns: - A digest of output_len field elements. + A digest of output_length field elements. """ # The capacity must leave at least one rate slot for absorbing input. if len(capacity_value) >= width: @@ -137,23 +137,23 @@ def sponge( padded_input = input_vec + [Fp(value=0)] * num_extra # Layout: capacity slots first, then rate slots. - cap_len = len(capacity_value) + cap_length = len(capacity_value) state = [Fp(value=0)] * width - state[:cap_len] = capacity_value + state[:cap_length] = capacity_value # Phase 2: absorb each chunk by overwriting the rate slots. for chunk in batched(padded_input, rate): for j, value in enumerate(chunk): - state[cap_len + j] = value + state[cap_length + j] = value state = engine.permute(state) # Phase 3: squeeze rate slots, permuting until enough output is available. output: list[Fp] = [] - while len(output) < output_len: - output.extend(state[cap_len : cap_len + rate]) + while len(output) < output_length: + output.extend(state[cap_length : cap_length + rate]) state = engine.permute(state) - return output[:output_len] + return output[:output_length] def tweak_hash( self, @@ -177,7 +177,7 @@ def tweak_hash( message_parts: Digests to hash together. Returns: - A digest of HASH_LEN_FE field elements. + A digest of HASH_LENGTH_FIELD_ELEMENTS field elements. """ # Pack the tweak fields into one integer, then split it into base-P field elements. # @@ -191,12 +191,12 @@ def tweak_hash( acc = (level << 40) | (int(index) << 8) | TWEAK_PREFIX_TREE case ChainTweak(epoch=epoch, chain_index=chain_index, step=step): acc = (int(epoch) << 24) | (chain_index << 16) | (step << 8) | TWEAK_PREFIX_CHAIN - encoded_tweak = int_to_base_p(acc, config.TWEAK_LEN_FE) + encoded_tweak = int_to_base_p(acc, config.TWEAK_LENGTH_FIELD_ELEMENTS) if len(message_parts) == 1: # Hash chain step: width-16 compression of (digest || parameter || tweak). input_vec = message_parts[0].elements + parameter.elements + encoded_tweak - result = self.compress(input_vec, 16, config.HASH_LEN_FE) + result = self.compress(input_vec, 16, config.HASH_LENGTH_FIELD_ELEMENTS) elif len(message_parts) == 2: # Merkle node: width-24 compression of (parameter || tweak || left || right). @@ -206,22 +206,22 @@ def tweak_hash( + message_parts[0].elements + message_parts[1].elements ) - result = self.compress(input_vec, 24, config.HASH_LEN_FE) + result = self.compress(input_vec, 24, config.HASH_LENGTH_FIELD_ELEMENTS) else: # Merkle leaf: sponge mode over many concatenated digests. - flattened_message = [elem for part in message_parts for elem in part.elements] + flattened_message = [element for part in message_parts for element in part.elements] input_vec = parameter.elements + encoded_tweak + flattened_message # The domain separator binds the sponge to this hashing task shape. lengths = [ - config.PARAMETER_LEN, - config.TWEAK_LEN_FE, + config.PARAMETER_LENGTH, + config.TWEAK_LENGTH_FIELD_ELEMENTS, config.DIMENSION, - config.HASH_LEN_FE, + config.HASH_LENGTH_FIELD_ELEMENTS, ] capacity_value = self.safe_domain_separator(lengths, config.CAPACITY) - result = self.sponge(input_vec, capacity_value, config.HASH_LEN_FE, 24) + result = self.sponge(input_vec, capacity_value, config.HASH_LENGTH_FIELD_ELEMENTS, 24) return HashDigestVector(data=result) diff --git a/src/lean_spec/spec/crypto/xmss/prf.py b/src/lean_spec/spec/crypto/xmss/prf.py index 74ca075e4..3f375a32b 100644 --- a/src/lean_spec/spec/crypto/xmss/prf.py +++ b/src/lean_spec/spec/crypto/xmss/prf.py @@ -25,7 +25,7 @@ PRF_DOMAIN_SEP_RANDOMNESS: Final[bytes] = b"\x01" """Subdomain tag for signing-randomness derivation.""" -PRF_BYTES_PER_FE: Final[int] = 16 +PRF_BYTES_PER_FIELD_ELEMENT: Final[int] = 16 """ SHAKE128 bytes consumed per output field element. @@ -84,12 +84,12 @@ def derive_chain_start( ) # Pull enough SHAKE128 bytes to fill one digest of field elements. - num_bytes_to_read = PRF_BYTES_PER_FE * config.HASH_LEN_FE + num_bytes_to_read = PRF_BYTES_PER_FIELD_ELEMENT * config.HASH_LENGTH_FIELD_ELEMENTS prf_output_bytes = hashlib.shake_128(input_data).digest(num_bytes_to_read) return HashDigestVector( data=[ Fp(value=int.from_bytes(bytes(chunk), "big")) - for chunk in batched(prf_output_bytes, PRF_BYTES_PER_FE) + for chunk in batched(prf_output_bytes, PRF_BYTES_PER_FIELD_ELEMENT) ] ) @@ -138,11 +138,11 @@ def derive_randomness( + counter.to_bytes(8, "big") ) - num_bytes_to_read = PRF_BYTES_PER_FE * config.RAND_LEN_FE + num_bytes_to_read = PRF_BYTES_PER_FIELD_ELEMENT * config.RAND_LENGTH_FIELD_ELEMENTS prf_output_bytes = hashlib.shake_128(input_data).digest(num_bytes_to_read) return Randomness( data=[ Fp(value=int.from_bytes(bytes(chunk), "big")) - for chunk in batched(prf_output_bytes, PRF_BYTES_PER_FE) + for chunk in batched(prf_output_bytes, PRF_BYTES_PER_FIELD_ELEMENT) ] ) diff --git a/src/lean_spec/spec/crypto/xmss/types.py b/src/lean_spec/spec/crypto/xmss/types.py index 2f67e59bc..575c63343 100644 --- a/src/lean_spec/spec/crypto/xmss/types.py +++ b/src/lean_spec/spec/crypto/xmss/types.py @@ -57,7 +57,7 @@ class HashDigestVector(SSZVector[Fp]): The fixed size lets SSZ pack these back-to-back without per-element offsets. """ - LENGTH = TARGET_CONFIG.HASH_LEN_FE + LENGTH = TARGET_CONFIG.HASH_LENGTH_FIELD_ELEMENTS """One Poseidon digest, measured in field elements.""" @@ -75,7 +75,7 @@ class Parameter(SSZVector[Fp]): - Public knowledge. """ - LENGTH = TARGET_CONFIG.PARAMETER_LEN + LENGTH = TARGET_CONFIG.PARAMETER_LENGTH class Randomness(SSZVector[Fp]): @@ -87,7 +87,7 @@ class Randomness(SSZVector[Fp]): - The chosen randomness travels in the signature so the verifier recomputes the same codeword. """ - LENGTH = TARGET_CONFIG.RAND_LEN_FE + LENGTH = TARGET_CONFIG.RAND_LENGTH_FIELD_ELEMENTS class HashTreeOpening(Container): diff --git a/src/lean_spec/spec/forks/lstar/containers.py b/src/lean_spec/spec/forks/lstar/containers.py index 3854a65fd..1dc2e80b6 100644 --- a/src/lean_spec/spec/forks/lstar/containers.py +++ b/src/lean_spec/spec/forks/lstar/containers.py @@ -150,7 +150,7 @@ class SingleMessageAggregate(Container): """Bitfield indicating which validators contributed signatures.""" proof: ByteList512KiB - """Aggregated proof bytes in compact no-pubkeys representation.""" + """Aggregated proof bytes in compact public-key-free representation.""" @classmethod def aggregate( @@ -194,24 +194,24 @@ def aggregate( # # Fresh signers bring their own index. # Child proofs expose theirs through the participant bitfield. - all_indices = {vid for vid, _, _ in raw_xmss}.union( + all_indices = {validator_index for validator_index, _, _ in raw_xmss}.union( *(child.participants.to_validator_indices() for child, _ in children) ) participants = ValidatorIndices(data=sorted(all_indices)).to_aggregation_bits() # Phase 2: serialize inputs to the prover's wire format. - raw_pubkeys_ssz = [pk.encode_bytes() for _, pk, _ in raw_xmss] - raw_signatures_ssz = [sig.encode_bytes() for _, _, sig in raw_xmss] + raw_public_keys_ssz = [public_key.encode_bytes() for _, public_key, _ in raw_xmss] + raw_signatures_ssz = [signature.encode_bytes() for _, _, signature in raw_xmss] children_bytes = [ - ([pk.encode_bytes() for pk in pubkeys], bytes(child.proof.data)) - for child, pubkeys in children + ([public_key.encode_bytes() for public_key in public_keys], bytes(child.proof.data)) + for child, public_keys in children ] # Phase 3: hand off to the Rust prover. # The mode argument routes the call to the matching backend bytecode. try: _, single_message_aggregate_wire = aggregate_type_1( - raw_pubkeys_ssz, + raw_public_keys_ssz, raw_signatures_ssz, bytes(message), int(slot), @@ -233,15 +233,15 @@ def verify( message: Bytes32, slot: Slot, ) -> None: - """Verify this single-message single-message aggregate proof against a pubkey set. + """Verify this single-message single-message aggregate proof against a public_key set. Args: - public_keys: Pubkeys for the validators named by participants. + public_keys: PublicKeys for the validators named by participants. message: Message bound by the proof. slot: Slot bound by the proof. Raises: - AggregationError: When the pubkey count does not match the bitfield + AggregationError: When the public_key count does not match the bitfield or the Rust verifier rejects the proof. """ # The bitfield names one validator per set bit. @@ -258,7 +258,7 @@ def verify( # The mode argument selects the matching backend bytecode. try: verify_type_1( - [pk.encode_bytes() for pk in public_keys], + [public_key.encode_bytes() for public_key in public_keys], bytes(message), int(slot), bytes(self.proof.data), @@ -282,7 +282,7 @@ class MultiMessageAggregate(Container): model_config = Container.model_config | {"frozen": True} proof: ByteList512KiB - """Compact no-pubkeys serialized multi-message aggregate proof bytes.""" + """Compact public-key-free serialized multi-message aggregate proof bytes.""" @classmethod def aggregate( @@ -306,7 +306,7 @@ def aggregate( A merged proof binding every component to its own message. Raises: - AggregationError: When no proofs are given, a pubkey list disagrees + AggregationError: When no proofs are given, a public_key list disagrees with its participant count, or the prover rejects the inputs. """ if not parts: @@ -318,15 +318,15 @@ def aggregate( # # A miscount would otherwise fail deep in the prover with an opaque error. single_message_aggregate_entries: list[tuple[list[bytes], bytes]] = [] - for idx, (part, pubkeys) in enumerate(zip(parts, public_keys_per_part, strict=True)): + for index, (part, public_keys) in enumerate(zip(parts, public_keys_per_part, strict=True)): expected = len(part.participants.to_validator_indices()) - if len(pubkeys) != expected: + if len(public_keys) != expected: raise AggregationError( - f"multi-message aggregate entry {idx} " - f"expected {expected} pubkeys, got {len(pubkeys)}" + f"multi-message aggregate entry {index} " + f"expected {expected} pubkeys, got {len(public_keys)}" ) single_message_aggregate_entries.append( - ([pk.encode_bytes() for pk in pubkeys], bytes(part.proof.data)) + ([public_key.encode_bytes() for public_key in public_keys], bytes(part.proof.data)) ) # Hand the per-component keys and proof bytes to the Rust prover. @@ -343,7 +343,7 @@ def aggregate( return cls(proof=ByteList512KiB(data=multi_message_aggregate_wire)) - def split_by_msg( + def split_by_message( self, message: Bytes32, public_keys_per_message: list[list[PublicKey]], @@ -362,7 +362,7 @@ def split_by_msg( Args: message: Message that selects the single-message aggregate component. - public_keys_per_message: Pubkey layout this multi-message aggregate was built with. + public_keys_per_message: PublicKey layout this multi-message aggregate was built with. participants: Bitfield naming the validators of the recovered component. Returns: @@ -372,8 +372,9 @@ def split_by_msg( AggregationError: When the Rust binding rejects the split. """ # Each component carries the public keys named by its bitfield, in the same order. - pub_keys_per_component_ssz: list[list[bytes]] = [ - [pk.encode_bytes() for pk in pks] for pks in public_keys_per_message + public_keys_per_component_ssz: list[list[bytes]] = [ + [public_key.encode_bytes() for public_key in public_keys] + for public_keys in public_keys_per_message ] # Hand the key layout, merged proof, and selector message to the Rust prover. @@ -381,7 +382,7 @@ def split_by_msg( # The mode argument selects the matching backend bytecode. try: _, single_message_aggregate_wire = split_type_2_by_msg( - pub_keys_per_component_ssz, + public_keys_per_component_ssz, bytes(self.proof.data), bytes(message), LOG_INV_RATE, @@ -426,17 +427,18 @@ def verify( ) # Serialize the key layout and the per-component message bindings. - pub_keys_per_component_ssz: list[list[bytes]] = [ - [pk.encode_bytes() for pk in pks] for pks in public_keys_per_message + public_keys_per_component_ssz: list[list[bytes]] = [ + [public_key.encode_bytes() for public_key in public_keys] + for public_keys in public_keys_per_message ] - expected_messages = [(bytes(msg), int(slot)) for msg, slot in messages] + expected_messages = [(bytes(message), int(slot)) for message, slot in messages] # Hand the layout, bindings, and merged proof to the Rust verifier. # # The mode argument selects the matching backend bytecode. try: verify_type_2_with_messages( - pub_keys_per_component_ssz, + public_keys_per_component_ssz, expected_messages, bytes(self.proof.data), mode=LEAN_ENV, @@ -464,22 +466,22 @@ class Config(Container): class Validator(Container): """Represents a validator's static metadata and operational interface.""" - attestation_pubkey: Bytes52 + attestation_public_key: Bytes52 """XMSS public key for signing attestations.""" - proposal_pubkey: Bytes52 + proposal_public_key: Bytes52 """XMSS public key for signing proposer attestations in blocks.""" index: ValidatorIndex = ValidatorIndex(0) """Validator index in the registry.""" - def get_attestation_pubkey(self) -> PublicKey: + def get_attestation_public_key(self) -> PublicKey: """Get the XMSS public key used for attestation verification.""" - return PublicKey.decode_bytes(bytes(self.attestation_pubkey)) + return PublicKey.decode_bytes(bytes(self.attestation_public_key)) - def get_proposal_pubkey(self) -> PublicKey: + def get_proposal_public_key(self) -> PublicKey: """Get the XMSS public key used for proposer attestation verification.""" - return PublicKey.decode_bytes(bytes(self.proposal_pubkey)) + return PublicKey.decode_bytes(bytes(self.proposal_public_key)) class Validators(SSZList[Validator]): @@ -541,7 +543,7 @@ class Attestation(Container): model_config = Container.model_config | {"frozen": True} - validator_id: ValidatorIndex + validator_index: ValidatorIndex """The index of the validator making the attestation.""" data: AttestationData diff --git a/src/lean_spec/spec/forks/lstar/spec.py b/src/lean_spec/spec/forks/lstar/spec.py index 9631efe33..3890e87e3 100644 --- a/src/lean_spec/spec/forks/lstar/spec.py +++ b/src/lean_spec/spec/forks/lstar/spec.py @@ -483,9 +483,9 @@ def process_attestations( # # A vote is represented as a boolean flag. # If it was previously absent, flip it to True. - for validator_id in attestation.aggregation_bits.to_validator_indices(): - if not justifications[target.root][validator_id]: - justifications[target.root][validator_id] = Boolean(True) + for validator_index in attestation.aggregation_bits.to_validator_indices(): + if not justifications[target.root][validator_index]: + justifications[target.root][validator_index] = Boolean(True) # Check whether the vote count crosses the supermajority threshold. # @@ -668,21 +668,21 @@ def build_block( list(state.historical_block_hashes) + [parent_root] + [ZERO_HASH] * num_empty_slots ) - processed_att_data: set[AttestationData] = set() + processed_attestation_data: set[AttestationData] = set() while True: found_entries = False - for att_data, proofs in sorted( + for attestation_data, proofs in sorted( aggregated_payloads.items(), key=lambda item: item[0].target.slot ): - if att_data in processed_att_data: + if attestation_data in processed_attestation_data: continue - if Uint8(len(processed_att_data)) >= MAX_ATTESTATIONS_DATA: + if Uint8(len(processed_attestation_data)) >= MAX_ATTESTATIONS_DATA: break - if att_data.head.root not in known_block_roots: + if attestation_data.head.root not in known_block_roots: continue # Chain-match runs first. @@ -690,13 +690,13 @@ def build_block( # It rejects checkpoints whose slot is past the chain view. # That prevents the bounded queries below from indexing out of range. if not self._attestation_data_matches_chain( - att_data, extended_historical_block_hashes + attestation_data, extended_historical_block_hashes ): continue # The source slot must already be justified on this chain. if not current_justified_slots.is_slot_justified( - current_finalized_slot, att_data.source.slot + current_finalized_slot, attestation_data.source.slot ): continue @@ -707,8 +707,8 @@ def build_block( # Including them in the body propagates them into peers' payload pool. # The bypass below keeps them past the target-already-justified check, # since slot 0 is implicitly justified and would otherwise filter them. - is_genesis_self_vote = att_data.source.slot == Slot(0) and ( - att_data.target.slot == Slot(0) + is_genesis_self_vote = attestation_data.source.slot == Slot(0) and ( + attestation_data.target.slot == Slot(0) ) # Skip attestations whose target slot is already justified. @@ -717,11 +717,11 @@ def build_block( # Entries the state transition will later drop are still kept here. # They carry head-vote weight for fork choice. if not is_genesis_self_vote and current_justified_slots.is_slot_justified( - current_finalized_slot, att_data.target.slot + current_finalized_slot, attestation_data.target.slot ): continue - processed_att_data.add(att_data) + processed_attestation_data.add(attestation_data) found_entries = True @@ -731,7 +731,7 @@ def build_block( aggregated_attestations.append( self.aggregated_attestation_class( aggregation_bits=proof.participants, - data=att_data, + data=attestation_data, ) ) @@ -775,14 +775,16 @@ def build_block( # selected for the same AttestationData across iterations. Group them # and merge each group into a single recursive proof. proof_groups: dict[AttestationData, list[SingleMessageAggregate]] = {} - for att, sig in zip(aggregated_attestations, aggregated_signatures, strict=True): - proof_groups.setdefault(att.data, []).append(sig) + for attestation, signature in zip( + aggregated_attestations, aggregated_signatures, strict=True + ): + proof_groups.setdefault(attestation.data, []).append(signature) aggregated_attestations = [] aggregated_signatures = [] - for att_data, proofs in proof_groups.items(): + for attestation_data, proofs in proof_groups.items(): if len(proofs) == 1: - sig = proofs[0] + signature = proofs[0] else: # Multiple proofs for the same data were aggregated separately. # Merge them into one recursive proof using children-only @@ -791,22 +793,22 @@ def build_block( ( proof, [ - state.validators[vid].get_attestation_pubkey() - for vid in proof.participants.to_validator_indices() + state.validators[validator_index].get_attestation_public_key() + for validator_index in proof.participants.to_validator_indices() ], ) for proof in proofs ] - sig = SingleMessageAggregate.aggregate( + signature = SingleMessageAggregate.aggregate( children=children, raw_xmss=[], - message=hash_tree_root(att_data), - slot=att_data.slot, + message=hash_tree_root(attestation_data), + slot=attestation_data.slot, ) - aggregated_signatures.append(sig) + aggregated_signatures.append(signature) aggregated_attestations.append( self.aggregated_attestation_class( - aggregation_bits=sig.participants, data=att_data + aggregation_bits=signature.participants, data=attestation_data ) ) @@ -859,21 +861,24 @@ def verify_signatures( # # Without this binding a proposer could pair honest signatures # with attacker-chosen attestation data that resolves to the same - # pubkeys, crediting validators for votes they never cast. + # public_keys, crediting validators for votes they never cast. message_bindings: list[tuple[Bytes32, Slot]] = [] - # One pubkey set per attestation, in body order. + # One public_key set per attestation, in body order. # # The attestation list and the proof component list are parallel. # Each attestation names the validators that voted for its data. # Its matching proof component proves those validators signed. for aggregated_attestation in aggregated_attestations: - validator_ids = aggregated_attestation.aggregation_bits.to_validator_indices() - for validator_id in validator_ids: - assert validator_id.is_valid(num_validators), "Validator index out of range" + validator_indices = aggregated_attestation.aggregation_bits.to_validator_indices() + for validator_index in validator_indices: + assert validator_index.is_valid(num_validators), "Validator index out of range" public_keys_per_message.append( - [validators[vid].get_attestation_pubkey() for vid in validator_ids] + [ + validators[validator_index].get_attestation_public_key() + for validator_index in validator_indices + ] ) message_bindings.append( ( @@ -890,7 +895,7 @@ def verify_signatures( proposer_index = block.proposer_index assert proposer_index.is_valid(num_validators), "Proposer index out of range" - public_keys_per_message.append([validators[proposer_index].get_proposal_pubkey()]) + public_keys_per_message.append([validators[proposer_index].get_proposal_public_key()]) message_bindings.append((hash_tree_root(block), block.slot)) try: @@ -909,7 +914,7 @@ def create_store( # type: ignore[override] # ty: ignore[invalid-method-overrid self, state: SpecStateType, anchor_block: SpecBlockType, - validator_id: ValidatorIndex | None, + validator_index: ValidatorIndex | None, ) -> LstarStore: """Initialize a forkchoice store from an anchor state and block. @@ -964,7 +969,7 @@ def create_store( # type: ignore[override] # ty: ignore[invalid-method-overrid latest_finalized=anchor_checkpoint, blocks={anchor_root: anchor_block}, states={anchor_root: state}, - validator_id=validator_id, + validator_index=validator_index, ) def prune_stale_attestation_data(self, store: LstarStore) -> LstarStore: @@ -985,8 +990,8 @@ def prune_stale_attestation_data(self, store: LstarStore) -> LstarStore: # Each mapping is keyed by attestation data, so we check membership by slot # against the finalized slot. store.attestation_signatures = { - attestation_data: sigs - for attestation_data, sigs in store.attestation_signatures.items() + attestation_data: signatures + for attestation_data, signatures in store.attestation_signatures.items() if attestation_data.target.slot > store.latest_finalized.slot } store.latest_new_aggregated_payloads = { @@ -1085,7 +1090,7 @@ def on_gossip_attestation( AssertionError: If signature verification fails. """ with observe_on_attestation(): - validator_id = signed_attestation.validator_id + validator_index = signed_attestation.validator_index attestation_data = signed_attestation.data signature = signed_attestation.signature @@ -1098,10 +1103,11 @@ def on_gossip_attestation( f"No state available to verify attestation signature for target block " f"{attestation_data.target.root.hex()}" ) - assert validator_id.is_valid(Uint64(len(key_state.validators))), ( - f"Validator {validator_id} not found in state {attestation_data.target.root.hex()}" + assert validator_index.is_valid(Uint64(len(key_state.validators))), ( + f"Validator {validator_index} not found in state " + f"{attestation_data.target.root.hex()}" ) - public_key = key_state.validators[validator_id].get_attestation_pubkey() + public_key = key_state.validators[validator_index].get_attestation_public_key() assert TARGET_SIGNATURE_SCHEME.verify( public_key, attestation_data.slot, hash_tree_root(attestation_data), signature @@ -1113,7 +1119,7 @@ def on_gossip_attestation( # Non-aggregator nodes validate and drop — they never store gossip signatures. if is_aggregator: store.attestation_signatures.setdefault(attestation_data, set()).add( - AttestationSignatureEntry(validator_id, signature) + AttestationSignatureEntry(validator_index, signature) ) return store @@ -1139,7 +1145,7 @@ def on_gossip_aggregated_attestation( self.validate_attestation(store, data) # Get validator IDs who participated in this aggregation - validator_ids = proof.participants.to_validator_indices() + validator_indices = proof.participants.to_validator_indices() # Retrieve the relevant state to look up public keys for verification. key_state = store.states.get(data.target.root) @@ -1150,13 +1156,16 @@ def on_gossip_aggregated_attestation( # Ensure all participants exist in the active set validators = key_state.validators - for validator_id in validator_ids: - assert validator_id.is_valid(Uint64(len(validators))), ( - f"Validator {validator_id} not found in state {data.target.root.hex()}" + for validator_index in validator_indices: + assert validator_index.is_valid(Uint64(len(validators))), ( + f"Validator {validator_index} not found in state {data.target.root.hex()}" ) # Prepare public keys for verification - public_keys = [validators[vid].get_attestation_pubkey() for vid in validator_ids] + public_keys = [ + validators[validator_index].get_attestation_public_key() + for validator_index in validator_indices + ] # Verify the single-message aggregate single-message aggregated proof. try: @@ -1216,13 +1225,13 @@ def on_block( # The block body constrains how many distinct AttestationData # entries it may carry. aggregated_attestations = block.body.attestations - att_data_set = {att.data for att in aggregated_attestations} - assert len(att_data_set) == len(aggregated_attestations), ( + attestation_data_set = {attestation.data for attestation in aggregated_attestations} + assert len(attestation_data_set) == len(aggregated_attestations), ( "Block contains duplicate AttestationData entries; " "each AttestationData must appear at most once" ) - assert len(att_data_set) <= int(MAX_ATTESTATIONS_DATA), ( - f"Block contains {len(att_data_set)} distinct AttestationData entries; " + assert len(attestation_data_set) <= int(MAX_ATTESTATIONS_DATA), ( + f"Block contains {len(attestation_data_set)} distinct AttestationData entries; " f"maximum is {MAX_ATTESTATIONS_DATA}" ) @@ -1290,10 +1299,10 @@ def extract_attestations_from_aggregated_payloads( for attestation_data, proofs in aggregated_payloads.items(): for proof in proofs: - for validator_id in proof.participants.to_validator_indices(): - existing = attestations.get(validator_id) + for validator_index in proof.participants.to_validator_indices(): + existing = attestations.get(validator_index) if existing is None or existing.slot < attestation_data.slot: - attestations[validator_id] = attestation_data + attestations[validator_index] = attestation_data return attestations def compute_block_weights(self, store: LstarStore) -> dict[Bytes32, int]: @@ -1549,7 +1558,7 @@ def aggregate(self, store: LstarStore) -> tuple[LstarStore, list[SignedAggregate - Newly produced proofs are recorded for future reuse. """ validators = store.states[store.head].validators - gossip_sigs = store.attestation_signatures + gossip_signatures = store.attestation_signatures new = store.latest_new_aggregated_payloads known = store.latest_known_aggregated_payloads @@ -1558,7 +1567,7 @@ def aggregate(self, store: LstarStore) -> tuple[LstarStore, list[SignedAggregate # Only attestation data with a new payload or a raw gossip signature # can trigger aggregation. Known payloads alone cannot — they exist # only to help extend coverage when combined with fresh evidence. - for data in new.keys() | gossip_sigs.keys(): + for data in new.keys() | gossip_signatures.keys(): # Phase 1: Select # # Start with the cheapest option: reuse proofs that already @@ -1582,12 +1591,12 @@ def aggregate(self, store: LstarStore) -> tuple[LstarStore, list[SignedAggregate # construction regardless of network arrival order. raw_entries = [ ( - e.validator_id, - validators[e.validator_id].get_attestation_pubkey(), + e.validator_index, + validators[e.validator_index].get_attestation_public_key(), e.signature, ) - for e in sorted(gossip_sigs.get(data, set()), key=lambda e: e.validator_id) - if e.validator_id not in covered + for e in sorted(gossip_signatures.get(data, set()), key=lambda e: e.validator_index) + if e.validator_index not in covered ] # The aggregation layer enforces a minimum: either at least one @@ -1608,8 +1617,8 @@ def aggregate(self, store: LstarStore) -> tuple[LstarStore, list[SignedAggregate ( child, [ - validators[vid].get_attestation_pubkey() - for vid in child.participants.to_validator_indices() + validators[validator_index].get_attestation_public_key() + for validator_index in child.participants.to_validator_indices() ], ) for child in child_proofs @@ -1631,9 +1640,9 @@ def aggregate(self, store: LstarStore) -> tuple[LstarStore, list[SignedAggregate # Record freshly produced proofs so future rounds can reuse them. # Remove gossip signatures that were consumed by this aggregation. store.latest_new_aggregated_payloads = {} - for signed_att in new_aggregates: - store.latest_new_aggregated_payloads.setdefault(signed_att.data, set()).add( - signed_att.proof + for signed_attestation in new_aggregates: + store.latest_new_aggregated_payloads.setdefault(signed_attestation.data, set()).add( + signed_attestation.proof ) for data in store.latest_new_aggregated_payloads: diff --git a/src/lean_spec/spec/forks/lstar/store.py b/src/lean_spec/spec/forks/lstar/store.py index 45778d1f4..812ed3458 100644 --- a/src/lean_spec/spec/forks/lstar/store.py +++ b/src/lean_spec/spec/forks/lstar/store.py @@ -32,7 +32,7 @@ class AttestationSignatureEntry(NamedTuple): that attested to the same AttestationData. """ - validator_id: ValidatorIndex + validator_index: ValidatorIndex signature: Signature @@ -112,7 +112,7 @@ class Store[StateT: Container, BlockT: Container](StrictBaseModel): `Store`'s latest justified and latest finalized checkpoints. """ - validator_id: ValidatorIndex | None + validator_index: ValidatorIndex | None """Index of the validator running this store instance.""" attestation_signatures: dict[AttestationData, set[AttestationSignatureEntry]] = Field( diff --git a/src/lean_spec/spec/forks/protocol.py b/src/lean_spec/spec/forks/protocol.py index d6f40fa1d..6b8c8fbe1 100644 --- a/src/lean_spec/spec/forks/protocol.py +++ b/src/lean_spec/spec/forks/protocol.py @@ -146,7 +146,7 @@ def data(self) -> SpecAttestationDataType: ... @property - def validator_id(self) -> ValidatorIndex: + def validator_index(self) -> ValidatorIndex: """Index of the validator that produced this attestation.""" ... @@ -205,7 +205,7 @@ def latest_finalized(self) -> Checkpoint: ... @property - def validator_id(self) -> ValidatorIndex | None: + def validator_index(self) -> ValidatorIndex | None: """Index of the local validator owning this store, if any.""" ... @@ -224,7 +224,7 @@ def from_anchor( cls, state: SpecStateType, anchor_block: SpecBlockType, - validator_id: ValidatorIndex | None, + validator_index: ValidatorIndex | None, ) -> Self: """Construct a forkchoice store anchored at the given state/block.""" ... @@ -310,7 +310,7 @@ def create_store( self, state: SpecStateType, anchor_block: SpecBlockType, - validator_id: ValidatorIndex | None, + validator_index: ValidatorIndex | None, ) -> SpecStoreType: """Construct a forkchoice store anchored at the given state and block.""" diff --git a/src/lean_spec/spec/ssz/collections.py b/src/lean_spec/spec/ssz/collections.py index 337a76157..650721f73 100644 --- a/src/lean_spec/spec/ssz/collections.py +++ b/src/lean_spec/spec/ssz/collections.py @@ -70,10 +70,10 @@ def _validate_offsets(offsets: list[int], scope: int, type_name: str) -> None: return # Pairwise comparison catches any decreasing step in the table. - for prev, curr in pairwise(offsets): - if curr < prev: + for previous, current in pairwise(offsets): + if current < previous: raise SSZSerializationError( - f"{type_name}: offsets not monotonically increasing: {prev} -> {curr}" + f"{type_name}: offsets not monotonically increasing: {previous} -> {current}" ) # The final boundary is the scope appended by the decoder. @@ -377,14 +377,14 @@ def deserialize(cls, stream: IO[bytes], scope: int) -> Self: # Fixed-size case: elements pack back-to-back at a known stride. # The byte budget must match LENGTH times the element width exactly. if cls.is_fixed_size(): - elem_byte_length = cls.ELEMENT_TYPE.get_byte_length() - expected_total = elem_byte_length * cls.LENGTH + element_byte_length = cls.ELEMENT_TYPE.get_byte_length() + expected_total = element_byte_length * cls.LENGTH if scope != expected_total: raise SSZSerializationError( f"{cls.__name__}: expected {expected_total} bytes, got {scope}" ) elements = [ - cls.ELEMENT_TYPE.deserialize(stream, elem_byte_length) for _ in range(cls.LENGTH) + cls.ELEMENT_TYPE.deserialize(stream, element_byte_length) for _ in range(cls.LENGTH) ] return cls(data=elements) diff --git a/tests/api/conftest.py b/tests/api/conftest.py index fdc3d7e52..dddb28fcf 100644 --- a/tests/api/conftest.py +++ b/tests/api/conftest.py @@ -66,7 +66,7 @@ def run(self) -> None: def _create_server(self) -> ApiServer: """Create the API server with a test store and aggregator controller.""" genesis = make_genesis_data( - num_validators=3, validator_id=None, genesis_time=int(time.time()) + num_validators=3, validator_index=None, genesis_time=int(time.time()) ) store = genesis.store diff --git a/tests/api/endpoints/test_checkpoints.py b/tests/api/endpoints/test_checkpoints.py index 5e33b2933..5d6724641 100644 --- a/tests/api/endpoints/test_checkpoints.py +++ b/tests/api/endpoints/test_checkpoints.py @@ -42,7 +42,7 @@ def test_has_root(self, server_url: str) -> None: assert "root" in data root = data["root"] - # Root should be a 0x-prefixed hex string (32 bytes = 66 chars with prefix) + # Root should be a 0x-prefixed hex string (32 bytes = 66 characters with prefix) assert isinstance(root, str) assert root.startswith("0x"), "Root must have 0x prefix" assert len(root) == 66 diff --git a/tests/consensus/lstar/fc/test_attestation_source_divergence.py b/tests/consensus/lstar/fc/test_attestation_source_divergence.py index f0b865c28..cf48382f6 100644 --- a/tests/consensus/lstar/fc/test_attestation_source_divergence.py +++ b/tests/consensus/lstar/fc/test_attestation_source_divergence.py @@ -74,7 +74,7 @@ def test_justified_divergence_self_heals_in_next_block( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -105,7 +105,7 @@ def test_justified_divergence_self_heals_in_next_block( label="fork_4", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(1), ValidatorIndex(2), ValidatorIndex(3), diff --git a/tests/consensus/lstar/fc/test_attestation_target_selection.py b/tests/consensus/lstar/fc/test_attestation_target_selection.py index 7de9d6070..2f33baf8e 100644 --- a/tests/consensus/lstar/fc/test_attestation_target_selection.py +++ b/tests/consensus/lstar/fc/test_attestation_target_selection.py @@ -98,7 +98,7 @@ def test_attestation_target_advances_with_attestations( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(1), target_slot=Slot(1), target_root_label="block_1", @@ -116,7 +116,7 @@ def test_attestation_target_advances_with_attestations( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(2)], + validator_indices=[ValidatorIndex(2)], slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -134,7 +134,7 @@ def test_attestation_target_advances_with_attestations( label="block_4", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(3)], + validator_indices=[ValidatorIndex(3)], slot=Slot(3), target_slot=Slot(3), target_root_label="block_3", @@ -151,7 +151,7 @@ def test_attestation_target_advances_with_attestations( slot=Slot(5), attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(4), target_slot=Slot(4), target_root_label="block_4", @@ -255,7 +255,7 @@ def test_attestation_target_with_extended_chain( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(1), target_slot=Slot(1), target_root_label="block_1", @@ -273,7 +273,7 @@ def test_attestation_target_with_extended_chain( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(2)], + validator_indices=[ValidatorIndex(2)], slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -291,7 +291,7 @@ def test_attestation_target_with_extended_chain( label="block_4", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(3)], + validator_indices=[ValidatorIndex(3)], slot=Slot(3), target_slot=Slot(3), target_root_label="block_3", @@ -309,7 +309,7 @@ def test_attestation_target_with_extended_chain( label="block_5", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(4), target_slot=Slot(4), target_root_label="block_4", @@ -327,7 +327,7 @@ def test_attestation_target_with_extended_chain( label="block_6", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(5), target_slot=Slot(5), target_root_label="block_5", @@ -345,7 +345,7 @@ def test_attestation_target_with_extended_chain( label="block_7", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(2)], + validator_indices=[ValidatorIndex(2)], slot=Slot(6), target_slot=Slot(6), target_root_label="block_6", @@ -362,7 +362,7 @@ def test_attestation_target_with_extended_chain( slot=Slot(8), attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(3)], + validator_indices=[ValidatorIndex(3)], slot=Slot(7), target_slot=Slot(7), target_root_label="block_7", @@ -438,7 +438,7 @@ def test_attestation_target_justifiable_constraint( attestations=( [ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex((i - 1) % num_validators)], + validator_indices=[ValidatorIndex((i - 1) % num_validators)], slot=Slot(i - 1), target_slot=Slot(i - 1), target_root_label=f"block_{i - 1}", @@ -548,7 +548,7 @@ def test_attestation_target_selection_after_finality_has_moved( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -588,7 +588,7 @@ def test_attestation_target_selection_after_finality_has_moved( label="block_8", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -598,7 +598,7 @@ def test_attestation_target_selection_after_finality_has_moved( target_root_label="block_2", ), AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), diff --git a/tests/consensus/lstar/fc/test_block_attestation_limits.py b/tests/consensus/lstar/fc/test_block_attestation_limits.py index 09ed1655b..8b6d27dec 100644 --- a/tests/consensus/lstar/fc/test_block_attestation_limits.py +++ b/tests/consensus/lstar/fc/test_block_attestation_limits.py @@ -85,7 +85,7 @@ def test_block_with_maximum_attestations( parent_label=f"b_{targets[-1]}", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i % 4)], + validator_indices=[ValidatorIndex(i % 4)], slot=proposal_slot, target_slot=s, target_root_label=f"b_{s}", @@ -149,7 +149,7 @@ def test_block_exceeding_maximum_attestations_is_rejected( parent_label=f"b_{targets[-1]}", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i % 4)], + validator_indices=[ValidatorIndex(i % 4)], slot=proposal_slot, target_slot=s, target_root_label=f"b_{s}", @@ -158,7 +158,7 @@ def test_block_exceeding_maximum_attestations_is_rejected( ], forced_attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=proposal_slot, target_slot=forced_target, target_root_label=f"b_{forced_target}", diff --git a/tests/consensus/lstar/fc/test_block_production.py b/tests/consensus/lstar/fc/test_block_production.py index ef2310ec3..0b7981248 100644 --- a/tests/consensus/lstar/fc/test_block_production.py +++ b/tests/consensus/lstar/fc/test_block_production.py @@ -85,7 +85,7 @@ def test_block_builder_fixed_point_advances_justification( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -143,7 +143,7 @@ def test_block_builder_fixed_point_advances_justification( # 3/4 validators. Matches justified=1 on the first pass. GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -160,7 +160,7 @@ def test_block_builder_fixed_point_advances_justification( # Only unlocked after A justifies slot 2 on the first pass. GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(1), ValidatorIndex(2), ValidatorIndex(3), @@ -309,7 +309,7 @@ def test_produce_block_enforces_max_attestations_data_limit( attestation_steps: list[GossipAggregatedAttestationStep] = [ GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=validators, + validator_indices=validators, slot=Slot(num_target_blocks), target_slot=Slot(n), target_root_label=f"block_{n}", @@ -390,7 +390,7 @@ def test_produce_block_includes_pending_attestations( # data.slot=2 matches the current slot. GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1), ValidatorIndex(2)], + validator_indices=[ValidatorIndex(1), ValidatorIndex(2)], slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -469,7 +469,7 @@ def test_block_builder_recovers_finality_after_non_zero_boundary_stall( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -509,7 +509,7 @@ def test_block_builder_recovers_finality_after_non_zero_boundary_stall( label="block_8", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -519,7 +519,7 @@ def test_block_builder_recovers_finality_after_non_zero_boundary_stall( target_root_label="block_2", ), AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -552,7 +552,7 @@ def test_block_builder_recovers_finality_after_non_zero_boundary_stall( TickStep(time=aggregate_time), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -566,7 +566,7 @@ def test_block_builder_recovers_finality_after_non_zero_boundary_stall( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(1), ValidatorIndex(2), ValidatorIndex(3), diff --git a/tests/consensus/lstar/fc/test_checkpoint_sync.py b/tests/consensus/lstar/fc/test_checkpoint_sync.py index d9e96b9a1..dea034a0a 100644 --- a/tests/consensus/lstar/fc/test_checkpoint_sync.py +++ b/tests/consensus/lstar/fc/test_checkpoint_sync.py @@ -258,7 +258,7 @@ def test_fork_off_non_genesis_anchor( label="fork_b_12", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), diff --git a/tests/consensus/lstar/fc/test_duplicate_attestation_data.py b/tests/consensus/lstar/fc/test_duplicate_attestation_data.py index 0c82ae14f..0ec070103 100644 --- a/tests/consensus/lstar/fc/test_duplicate_attestation_data.py +++ b/tests/consensus/lstar/fc/test_duplicate_attestation_data.py @@ -47,7 +47,7 @@ def test_block_with_duplicate_aggregated_attestation_data_rejected( validator set just by repeating the entry. """ duplicated_spec = AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(1), target_slot=Slot(1), target_root_label="block_1", diff --git a/tests/consensus/lstar/fc/test_equivocation.py b/tests/consensus/lstar/fc/test_equivocation.py index 33fb26adc..66936730c 100644 --- a/tests/consensus/lstar/fc/test_equivocation.py +++ b/tests/consensus/lstar/fc/test_equivocation.py @@ -63,7 +63,7 @@ def test_equivocating_proposer_two_blocks_at_same_slot( label="equivocation_a", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(1), target_slot=Slot(1), target_root_label="block_1", @@ -84,7 +84,7 @@ def test_equivocating_proposer_two_blocks_at_same_slot( label="equivocation_b", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(1), target_slot=Slot(1), target_root_label="block_1", @@ -154,7 +154,7 @@ def test_equivocating_proposer_with_split_attestations( label="fork_a", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(1), target_slot=Slot(1), target_root_label="block_1", @@ -176,7 +176,7 @@ def test_equivocating_proposer_with_split_attestations( label="fork_b", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(1), target_slot=Slot(1), target_root_label="block_1", @@ -192,7 +192,7 @@ def test_equivocating_proposer_with_split_attestations( # Phase 1: V0, V1 gossip-attest to fork_a (2 votes) AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), slot=Slot(2), target_slot=Slot(2), target_root_label="fork_a", @@ -200,7 +200,7 @@ def test_equivocating_proposer_with_split_attestations( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(2), target_slot=Slot(2), target_root_label="fork_a", @@ -209,7 +209,7 @@ def test_equivocating_proposer_with_split_attestations( # Phase 1: V2, V3 gossip-attest to fork_b (2 votes) AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(2), + validator_index=ValidatorIndex(2), slot=Slot(2), target_slot=Slot(2), target_root_label="fork_b", @@ -217,7 +217,7 @@ def test_equivocating_proposer_with_split_attestations( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(3), + validator_index=ValidatorIndex(3), slot=Slot(2), target_slot=Slot(2), target_root_label="fork_b", @@ -235,7 +235,7 @@ def test_equivocating_proposer_with_split_attestations( # Phase 2: V4 gossip-attests to fork_b (now 3 vs 2) AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(4), + validator_index=ValidatorIndex(4), slot=Slot(2), target_slot=Slot(2), target_root_label="fork_b", @@ -301,7 +301,7 @@ def test_same_slot_equivocating_attesters_count_once( # V0 and V1's first votes stick on fork_a. GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -313,7 +313,7 @@ def test_same_slot_equivocating_attesters_count_once( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(3), diff --git a/tests/consensus/lstar/fc/test_finalization_mid_processing.py b/tests/consensus/lstar/fc/test_finalization_mid_processing.py index 6648ea213..dbce65d63 100644 --- a/tests/consensus/lstar/fc/test_finalization_mid_processing.py +++ b/tests/consensus/lstar/fc/test_finalization_mid_processing.py @@ -71,7 +71,7 @@ def test_finalization_advances_mid_attestation_processing( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -116,7 +116,7 @@ def test_finalization_advances_mid_attestation_processing( # Finalization: range(1+1, 2) = empty -> finalizes slot 1 # Need 3/4 validators for supermajority AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -133,7 +133,7 @@ def test_finalization_advances_mid_attestation_processing( # If skipped, latest_justified stays at 2 # This is how we detect the bug! AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), diff --git a/tests/consensus/lstar/fc/test_fork_choice_head.py b/tests/consensus/lstar/fc/test_fork_choice_head.py index 62ca7eb51..d59e9392a 100644 --- a/tests/consensus/lstar/fc/test_fork_choice_head.py +++ b/tests/consensus/lstar/fc/test_fork_choice_head.py @@ -214,7 +214,7 @@ def test_duplicate_block_processed_idempotently( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -249,7 +249,7 @@ def test_duplicate_block_processed_idempotently( label="block_2_dup", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -397,7 +397,7 @@ def test_head_switches_to_heavier_fork( label="fork_b_4", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(2)], + validator_indices=[ValidatorIndex(2)], slot=Slot(3), target_slot=Slot(3), target_root_label="fork_b", @@ -457,7 +457,7 @@ def test_head_with_deep_fork_split( label="fork_a_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(2)], + validator_indices=[ValidatorIndex(2)], slot=Slot(2), target_slot=Slot(2), target_root_label="fork_a_2", @@ -473,7 +473,7 @@ def test_head_with_deep_fork_split( label="fork_a_4", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(3)], + validator_indices=[ValidatorIndex(3)], slot=Slot(3), target_slot=Slot(3), target_root_label="fork_a_3", @@ -494,7 +494,7 @@ def test_head_with_deep_fork_split( label="fork_b_6", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(5), target_slot=Slot(5), target_root_label="fork_b_5", @@ -510,7 +510,7 @@ def test_head_with_deep_fork_split( label="fork_b_7", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(6), target_slot=Slot(6), target_root_label="fork_b_6", @@ -525,7 +525,7 @@ def test_head_with_deep_fork_split( label="fork_b_8", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(2)], + validator_indices=[ValidatorIndex(2)], slot=Slot(7), target_slot=Slot(7), target_root_label="fork_b_7", @@ -586,7 +586,7 @@ def test_head_selection_by_weight_not_depth( label="a_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(2), target_slot=Slot(2), target_root_label="a_2", @@ -630,7 +630,7 @@ def test_head_selection_by_weight_not_depth( label="b_12", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(1), ValidatorIndex(2), ValidatorIndex(3), @@ -717,7 +717,7 @@ def test_fork_from_before_finalization_not_considered( parent_label="block_1", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i) for i in range(6)], + validator_indices=[ValidatorIndex(i) for i in range(6)], slot=Slot(2), target_slot=Slot(1), target_root_label="block_1", @@ -739,7 +739,7 @@ def test_fork_from_before_finalization_not_considered( parent_label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i) for i in range(6)], + validator_indices=[ValidatorIndex(i) for i in range(6)], slot=Slot(3), target_slot=Slot(2), target_root_label="block_2", @@ -760,7 +760,7 @@ def test_fork_from_before_finalization_not_considered( parent_label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i) for i in range(6)], + validator_indices=[ValidatorIndex(i) for i in range(6)], slot=Slot(4), target_slot=Slot(3), target_root_label="block_3", @@ -781,7 +781,7 @@ def test_fork_from_before_finalization_not_considered( parent_label="block_4", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i) for i in range(6)], + validator_indices=[ValidatorIndex(i) for i in range(6)], slot=Slot(5), target_slot=Slot(4), target_root_label="block_4", @@ -851,7 +851,7 @@ def test_fork_from_before_finalization_not_considered( label="dead_7", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i) for i in range(3, 8)], + validator_indices=[ValidatorIndex(i) for i in range(3, 8)], slot=Slot(7), target_slot=Slot(6), target_root_label="dead_6", diff --git a/tests/consensus/lstar/fc/test_fork_choice_reorgs.py b/tests/consensus/lstar/fc/test_fork_choice_reorgs.py index 004e410ba..6852d6426 100644 --- a/tests/consensus/lstar/fc/test_fork_choice_reorgs.py +++ b/tests/consensus/lstar/fc/test_fork_choice_reorgs.py @@ -90,7 +90,7 @@ def test_simple_one_block_reorg( label="fork_b_4", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(2)], + validator_indices=[ValidatorIndex(2)], slot=Slot(3), target_slot=Slot(3), target_root_label="fork_b_3", @@ -174,7 +174,7 @@ def test_two_block_reorg_progressive_building( label="fork_a_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(2), target_slot=Slot(2), target_root_label="fork_a_2", @@ -210,7 +210,7 @@ def test_two_block_reorg_progressive_building( label="fork_b_6", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1), ValidatorIndex(3)], + validator_indices=[ValidatorIndex(1), ValidatorIndex(3)], slot=Slot(5), target_slot=Slot(5), target_root_label="fork_b_5", @@ -291,7 +291,7 @@ def test_three_block_deep_reorg( label="fork_a_4", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(2), ValidatorIndex(3)], + validator_indices=[ValidatorIndex(2), ValidatorIndex(3)], slot=Slot(3), target_slot=Slot(3), target_root_label="fork_a_3", @@ -321,7 +321,7 @@ def test_three_block_deep_reorg( label="fork_b_6", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(2), ValidatorIndex(5), @@ -430,7 +430,7 @@ def test_reorg_with_slot_gaps( label="fork_a_7", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(3)], + validator_indices=[ValidatorIndex(3)], slot=Slot(3), target_slot=Slot(3), target_root_label="fork_a_3", @@ -453,7 +453,7 @@ def test_reorg_with_slot_gaps( label="fork_b_8", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(2), ValidatorIndex(5), @@ -578,7 +578,7 @@ def test_three_way_fork_competition( label="fork_c_5", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(4), target_slot=Slot(4), target_root_label="fork_c_4", @@ -606,7 +606,7 @@ def test_three_way_fork_competition( label="fork_b_7", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1), ValidatorIndex(2)], + validator_indices=[ValidatorIndex(1), ValidatorIndex(2)], slot=Slot(6), target_slot=Slot(6), target_root_label="fork_b_6", @@ -691,7 +691,7 @@ def test_reorg_prevention_heavy_fork_resists_light_competition( label="fork_a_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(2)], + validator_indices=[ValidatorIndex(2)], slot=Slot(2), target_slot=Slot(2), target_root_label="fork_a_2", @@ -710,7 +710,7 @@ def test_reorg_prevention_heavy_fork_resists_light_competition( label="fork_a_4", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(3)], + validator_indices=[ValidatorIndex(3)], slot=Slot(3), target_slot=Slot(3), target_root_label="fork_a_3", @@ -729,7 +729,7 @@ def test_reorg_prevention_heavy_fork_resists_light_competition( label="fork_a_5", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(4)], + validator_indices=[ValidatorIndex(4)], slot=Slot(4), target_slot=Slot(4), target_root_label="fork_a_4", @@ -748,7 +748,7 @@ def test_reorg_prevention_heavy_fork_resists_light_competition( label="fork_a_6", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(5)], + validator_indices=[ValidatorIndex(5)], slot=Slot(5), target_slot=Slot(5), target_root_label="fork_a_5", @@ -867,7 +867,7 @@ def test_back_and_forth_reorg_oscillation( label="fork_b_4", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(3), target_slot=Slot(3), target_root_label="fork_b_3", @@ -895,7 +895,7 @@ def test_back_and_forth_reorg_oscillation( label="fork_a_6", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(7), ValidatorIndex(6)], + validator_indices=[ValidatorIndex(7), ValidatorIndex(6)], slot=Slot(5), target_slot=Slot(5), target_root_label="fork_a_5", @@ -923,7 +923,7 @@ def test_back_and_forth_reorg_oscillation( label="fork_b_8", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1), ValidatorIndex(7)], + validator_indices=[ValidatorIndex(1), ValidatorIndex(7)], slot=Slot(7), target_slot=Slot(7), target_root_label="fork_b_7", @@ -1006,7 +1006,7 @@ def test_reorg_depth_across_deep_chain_split( label="a_11", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(7)], + validator_indices=[ValidatorIndex(7)], slot=Slot(1), target_slot=Slot(1), target_root_label="common", @@ -1040,7 +1040,7 @@ def test_reorg_depth_across_deep_chain_split( label="b_12", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -1145,7 +1145,7 @@ def test_reorg_on_newly_justified_slot( label="fork_a_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(2)], + validator_indices=[ValidatorIndex(2)], slot=Slot(2), target_slot=Slot(2), target_root_label="fork_a_1", @@ -1193,7 +1193,7 @@ def test_reorg_on_newly_justified_slot( label="fork_b_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(3), diff --git a/tests/consensus/lstar/fc/test_gossip_aggregated_attestation_validation.py b/tests/consensus/lstar/fc/test_gossip_aggregated_attestation_validation.py index 43c80e67c..7b3ed18c0 100644 --- a/tests/consensus/lstar/fc/test_gossip_aggregated_attestation_validation.py +++ b/tests/consensus/lstar/fc/test_gossip_aggregated_attestation_validation.py @@ -42,7 +42,7 @@ def test_valid_gossip_aggregated_attestation( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -69,7 +69,7 @@ def test_aggregated_attestation_unknown_source_rejected( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -99,7 +99,7 @@ def test_aggregated_attestation_target_slot_mismatch_rejected( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(2), target_slot=Slot(3), target_root_label="block_2", @@ -127,7 +127,7 @@ def test_aggregated_attestation_head_slot_mismatch_rejected( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -161,7 +161,7 @@ def test_aggregated_attestation_source_after_target_rejected( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(3), target_slot=Slot(2), target_root_label="block_2", @@ -191,7 +191,7 @@ def test_aggregated_attestation_too_far_in_future_rejected( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(4), target_slot=Slot(2), target_root_label="block_2", @@ -232,7 +232,7 @@ def test_aggregated_attestation_at_disparity_boundary_allowed( TickStep(interval=SLOT_3_BOUNDARY_INTERVAL), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(3), target_slot=Slot(2), target_root_label="block_2", @@ -271,7 +271,7 @@ def test_aggregated_attestation_just_beyond_disparity_boundary_rejected( TickStep(interval=SLOT_3_JUST_BEYOND_BOUNDARY_INTERVAL), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(3), target_slot=Slot(2), target_root_label="block_2", @@ -314,7 +314,7 @@ def test_aggregated_attestation_one_full_slot_in_future_rejected( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(1)], + validator_indices=[ValidatorIndex(1)], slot=Slot(3), target_slot=Slot(2), target_root_label="block_2", diff --git a/tests/consensus/lstar/fc/test_gossip_attestation_validation.py b/tests/consensus/lstar/fc/test_gossip_attestation_validation.py index 2e964f078..0fd2c6032 100644 --- a/tests/consensus/lstar/fc/test_gossip_attestation_validation.py +++ b/tests/consensus/lstar/fc/test_gossip_attestation_validation.py @@ -53,7 +53,7 @@ def test_valid_gossip_attestation( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -90,7 +90,7 @@ def test_attestation_target_slot_mismatch_rejected( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(2), target_slot=Slot(3), target_root_label="block_2", @@ -128,7 +128,7 @@ def test_attestation_too_far_in_future_rejected( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(4), target_slot=Slot(2), target_root_label="block_2", @@ -169,7 +169,7 @@ def test_attestation_at_disparity_boundary_allowed( TickStep(interval=SLOT_3_BOUNDARY_INTERVAL), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(3), target_slot=Slot(2), target_root_label="block_2", @@ -208,7 +208,7 @@ def test_attestation_just_beyond_disparity_boundary_rejected( TickStep(interval=SLOT_3_JUST_BEYOND_BOUNDARY_INTERVAL), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(3), target_slot=Slot(2), target_root_label="block_2", @@ -251,7 +251,7 @@ def test_attestation_one_full_slot_in_future_rejected( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(3), target_slot=Slot(2), target_root_label="block_2", @@ -302,7 +302,7 @@ def test_multiple_gossip_attestations_from_different_validators( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), slot=Slot(5), target_slot=Slot(5), target_root_label="block_5", @@ -310,7 +310,7 @@ def test_multiple_gossip_attestations_from_different_validators( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(5), target_slot=Slot(5), target_root_label="block_5", @@ -318,7 +318,7 @@ def test_multiple_gossip_attestations_from_different_validators( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(2), + validator_index=ValidatorIndex(2), slot=Slot(5), target_slot=Slot(5), target_root_label="block_5", @@ -355,7 +355,7 @@ def test_gossip_attestation_with_invalid_signature( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -395,7 +395,7 @@ def test_gossip_attestation_with_unknown_validator( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(999), + validator_index=ValidatorIndex(999), slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -438,7 +438,7 @@ def test_attestation_source_slot_exceeds_target_rejected( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(3), target_slot=Slot(2), target_root_label="block_2", @@ -482,7 +482,7 @@ def test_attestation_head_older_than_target_rejected( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(3), target_slot=Slot(3), target_root_label="block_3", @@ -521,7 +521,7 @@ def test_attestation_source_slot_override_exceeds_target_rejected( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -562,7 +562,7 @@ def test_attestation_source_slot_mismatch_rejected( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -602,7 +602,7 @@ def test_attestation_head_slot_mismatch_rejected( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(5), target_slot=Slot(2), target_root_label="block_2", @@ -643,7 +643,7 @@ def test_gossip_attestation_chain_extended_after_gossip( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -651,7 +651,7 @@ def test_gossip_attestation_chain_extended_after_gossip( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -699,7 +699,7 @@ def test_attestation_unknown_target_block_rejected( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -739,7 +739,7 @@ def test_attestation_unknown_head_block_rejected( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -779,7 +779,7 @@ def test_attestation_unknown_source_block_rejected( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", diff --git a/tests/consensus/lstar/fc/test_safe_target.py b/tests/consensus/lstar/fc/test_safe_target.py index a09da194c..e77da4bd4 100644 --- a/tests/consensus/lstar/fc/test_safe_target.py +++ b/tests/consensus/lstar/fc/test_safe_target.py @@ -98,7 +98,7 @@ def test_safe_target_does_not_advance_below_supermajority( # computation at interval 3 can read them. GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i) for i in range(num_attesters)], + validator_indices=[ValidatorIndex(i) for i in range(num_attesters)], slot=Slot(3), target_slot=Slot(2), target_root_label="block_2", @@ -183,7 +183,7 @@ def test_safe_target_advances_incrementally_along_the_chain( TickStep(time=14), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -207,7 +207,7 @@ def test_safe_target_advances_incrementally_along_the_chain( TickStep(time=18), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -231,7 +231,7 @@ def test_safe_target_advances_incrementally_along_the_chain( TickStep(time=22), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -291,7 +291,7 @@ def test_safe_target_follows_heavier_fork_on_split( # Supermajority (4/6) attests to block_b. GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -305,7 +305,7 @@ def test_safe_target_follows_heavier_fork_on_split( # Minority (2/6) attests to block_a. GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(4), ValidatorIndex(5), ], @@ -375,7 +375,7 @@ def test_safe_target_is_conservative_relative_to_lmd_ghost_head( # 6/8 vote for block_2. Weight: block_1 += 6, block_2 += 6. GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -391,7 +391,7 @@ def test_safe_target_is_conservative_relative_to_lmd_ghost_head( # 2/8 vote for block_3. Weight: block_1 += 2, block_2 += 2, block_3 += 2. GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(6), ValidatorIndex(7), ], @@ -459,7 +459,7 @@ def test_safe_target_ignores_known_pool_at_interval_3( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ], @@ -493,7 +493,7 @@ def test_safe_target_ignores_known_pool_at_interval_3( # Combined with "known": total weight = 4 = threshold. GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(2), ValidatorIndex(3), ], diff --git a/tests/consensus/lstar/fc/test_signature_aggregation.py b/tests/consensus/lstar/fc/test_signature_aggregation.py index 70404e9f7..0a9fc2fb7 100644 --- a/tests/consensus/lstar/fc/test_signature_aggregation.py +++ b/tests/consensus/lstar/fc/test_signature_aggregation.py @@ -46,13 +46,13 @@ def test_multiple_specs_same_target_merge_into_one( slot=Slot(2), attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0), ValidatorIndex(1)], + validator_indices=[ValidatorIndex(0), ValidatorIndex(1)], slot=Slot(1), target_slot=Slot(1), target_root_label="block_1", ), AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(2), ValidatorIndex(3)], + validator_indices=[ValidatorIndex(2), ValidatorIndex(3)], slot=Slot(1), target_slot=Slot(1), target_root_label="block_1", @@ -107,13 +107,13 @@ def test_different_targets_create_separate_aggregations( slot=Slot(3), attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0), ValidatorIndex(1)], + validator_indices=[ValidatorIndex(0), ValidatorIndex(1)], slot=Slot(1), target_slot=Slot(1), target_root_label="block_1", ), AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(2), ValidatorIndex(3)], + validator_indices=[ValidatorIndex(2), ValidatorIndex(3)], slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", @@ -179,14 +179,14 @@ def test_mixed_attestations_multiple_targets_and_validators( attestations=[ # Attestations for older block AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0), ValidatorIndex(1)], + validator_indices=[ValidatorIndex(0), ValidatorIndex(1)], slot=Slot(2), target_slot=Slot(2), target_root_label="block_2", ), # Attestations for newer block AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(2), ValidatorIndex(3)], + validator_indices=[ValidatorIndex(2), ValidatorIndex(3)], slot=Slot(3), target_slot=Slot(3), target_root_label="block_3", @@ -244,7 +244,7 @@ def test_all_validators_attest_in_single_aggregation( slot=Slot(2), attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), diff --git a/tests/consensus/lstar/fc/test_store_pruning.py b/tests/consensus/lstar/fc/test_store_pruning.py index c128c4e69..00c021aa3 100644 --- a/tests/consensus/lstar/fc/test_store_pruning.py +++ b/tests/consensus/lstar/fc/test_store_pruning.py @@ -77,7 +77,7 @@ def test_finalization_prunes_stale_aggregated_payloads( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -106,7 +106,7 @@ def test_finalization_prunes_stale_aggregated_payloads( label="block_5", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -139,7 +139,7 @@ def test_finalization_prunes_stale_aggregated_payloads( # Stale gossip: target=1 (at finalized slot), should be pruned later GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -155,7 +155,7 @@ def test_finalization_prunes_stale_aggregated_payloads( # V3 is unique to this attestation (not in stale) GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(1), ValidatorIndex(2), ValidatorIndex(3), @@ -201,7 +201,7 @@ def test_finalization_prunes_stale_aggregated_payloads( label="block_6", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -279,7 +279,7 @@ def test_finalization_prunes_stale_attestation_signatures( parent_label="block_1", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i) for i in range(6)], + validator_indices=[ValidatorIndex(i) for i in range(6)], slot=Slot(2), target_slot=Slot(1), target_root_label="block_1", @@ -301,7 +301,7 @@ def test_finalization_prunes_stale_attestation_signatures( parent_label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i) for i in range(6)], + validator_indices=[ValidatorIndex(i) for i in range(6)], slot=Slot(3), target_slot=Slot(2), target_root_label="block_2", @@ -323,7 +323,7 @@ def test_finalization_prunes_stale_attestation_signatures( parent_label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i) for i in range(6)], + validator_indices=[ValidatorIndex(i) for i in range(6)], slot=Slot(4), target_slot=Slot(3), target_root_label="block_3", @@ -360,7 +360,7 @@ def test_finalization_prunes_stale_attestation_signatures( # their respective slots. GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2)], + validator_indices=[ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2)], slot=Slot(5), target_slot=Slot(1), target_root_label="block_1", @@ -370,7 +370,7 @@ def test_finalization_prunes_stale_attestation_signatures( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2)], + validator_indices=[ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2)], slot=Slot(5), target_slot=Slot(2), target_root_label="block_2", @@ -380,7 +380,7 @@ def test_finalization_prunes_stale_attestation_signatures( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2)], + validator_indices=[ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2)], slot=Slot(5), target_slot=Slot(3), target_root_label="block_3", @@ -390,7 +390,7 @@ def test_finalization_prunes_stale_attestation_signatures( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2)], + validator_indices=[ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2)], slot=Slot(5), target_slot=Slot(4), target_root_label="block_4", @@ -400,7 +400,7 @@ def test_finalization_prunes_stale_attestation_signatures( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2)], + validator_indices=[ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2)], slot=Slot(5), target_slot=Slot(5), target_root_label="block_5", @@ -424,7 +424,7 @@ def test_finalization_prunes_stale_attestation_signatures( # These land in latest_new_aggregated_payloads (first batch already migrated). GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(3), ValidatorIndex(4), ValidatorIndex(5)], + validator_indices=[ValidatorIndex(3), ValidatorIndex(4), ValidatorIndex(5)], slot=Slot(5), target_slot=Slot(1), target_root_label="block_1", @@ -434,7 +434,7 @@ def test_finalization_prunes_stale_attestation_signatures( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(3), ValidatorIndex(4), ValidatorIndex(5)], + validator_indices=[ValidatorIndex(3), ValidatorIndex(4), ValidatorIndex(5)], slot=Slot(5), target_slot=Slot(2), target_root_label="block_2", @@ -444,7 +444,7 @@ def test_finalization_prunes_stale_attestation_signatures( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(3), ValidatorIndex(4), ValidatorIndex(5)], + validator_indices=[ValidatorIndex(3), ValidatorIndex(4), ValidatorIndex(5)], slot=Slot(5), target_slot=Slot(3), target_root_label="block_3", @@ -454,7 +454,7 @@ def test_finalization_prunes_stale_attestation_signatures( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(3), ValidatorIndex(4), ValidatorIndex(5)], + validator_indices=[ValidatorIndex(3), ValidatorIndex(4), ValidatorIndex(5)], slot=Slot(5), target_slot=Slot(4), target_root_label="block_4", @@ -464,7 +464,7 @@ def test_finalization_prunes_stale_attestation_signatures( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ValidatorIndex(3), ValidatorIndex(4), ValidatorIndex(5)], + validator_indices=[ValidatorIndex(3), ValidatorIndex(4), ValidatorIndex(5)], slot=Slot(5), target_slot=Slot(5), target_root_label="block_5", @@ -476,7 +476,7 @@ def test_finalization_prunes_stale_attestation_signatures( # These populate attestation_signatures (the raw signature pool). AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(6), + validator_index=ValidatorIndex(6), slot=Slot(5), target_slot=Slot(1), target_root_label="block_1", @@ -487,7 +487,7 @@ def test_finalization_prunes_stale_attestation_signatures( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(6), + validator_index=ValidatorIndex(6), slot=Slot(5), target_slot=Slot(2), target_root_label="block_2", @@ -498,7 +498,7 @@ def test_finalization_prunes_stale_attestation_signatures( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(6), + validator_index=ValidatorIndex(6), slot=Slot(5), target_slot=Slot(3), target_root_label="block_3", @@ -509,7 +509,7 @@ def test_finalization_prunes_stale_attestation_signatures( ), AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(6), + validator_index=ValidatorIndex(6), slot=Slot(5), target_slot=Slot(4), target_root_label="block_4", @@ -521,7 +521,7 @@ def test_finalization_prunes_stale_attestation_signatures( # Pre-finalization check: all three pools contain targets 1-5 AttestationStep( attestation=GossipAttestationSpec( - validator_id=ValidatorIndex(6), + validator_index=ValidatorIndex(6), slot=Slot(5), target_slot=Slot(5), target_root_label="block_5", @@ -557,7 +557,7 @@ def test_finalization_prunes_stale_attestation_signatures( parent_label="block_5", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i) for i in range(6)], + validator_indices=[ValidatorIndex(i) for i in range(6)], slot=Slot(6), target_slot=Slot(4), target_root_label="block_4", diff --git a/tests/consensus/lstar/fc/test_tick_system.py b/tests/consensus/lstar/fc/test_tick_system.py index 69a0e4c22..97c6efb8c 100644 --- a/tests/consensus/lstar/fc/test_tick_system.py +++ b/tests/consensus/lstar/fc/test_tick_system.py @@ -88,7 +88,7 @@ def test_tick_interval_progression_through_full_slot( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -249,7 +249,7 @@ def test_tick_interval_0_skips_acceptance_when_not_proposer( # Start with a pending aggregated attestation for slot 3. GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -304,7 +304,7 @@ def test_tick_interval_0_skips_acceptance_when_not_proposer( # is within the allowed +1 future-slot margin. GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -357,7 +357,7 @@ def test_tick_interval_0_skips_acceptance_when_not_proposer( ), GossipAggregatedAttestationStep( attestation=GossipAggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(1), ValidatorIndex(2), ValidatorIndex(3), diff --git a/tests/consensus/lstar/networking/test_gossipsub_handlers.py b/tests/consensus/lstar/networking/test_gossipsub_handlers.py index 3d61c8448..bb0228012 100644 --- a/tests/consensus/lstar/networking/test_gossipsub_handlers.py +++ b/tests/consensus/lstar/networking/test_gossipsub_handlers.py @@ -7,10 +7,10 @@ TOPIC = "test_topic" PARAMS = {"d": 4, "dLow": 3, "dHigh": 6, "dLazy": 3} -MSG_ID = "0x8dc6bba09a9550cdccb1b1b432bb04919901ce1e" +MESSAGE_ID = "0x8dc6bba09a9550cdccb1b1b432bb04919901ce1e" """Message ID for topic=b'test_topic', data=0xdeadbeef, invalid-snappy domain.""" -MSG_ID_2 = "0xb51451075fcc3e8f0baa9041d8256c647ceaeaac" +MESSAGE_ID_2 = "0xb51451075fcc3e8f0baa9041d8256c647ceaeaac" """Message ID for topic=b'test_topic', data=0xcafebabe, invalid-snappy domain.""" @@ -156,7 +156,7 @@ def test_ihave_unseen_triggers_iwant(gossipsub_handler: GossipsubHandlerTestFill }, event={ "fromPeer": "peerAx", - "ihave": [{"topicId": TOPIC, "messageIds": [MSG_ID]}], + "ihave": [{"topicId": TOPIC, "messageIds": [MESSAGE_ID]}], }, ) @@ -170,12 +170,12 @@ def test_ihave_seen_no_iwant(gossipsub_handler: GossipsubHandlerTestFiller) -> N "subscriptions": [TOPIC], "meshes": {}, "peers": {"peerAx": {"subscriptions": [TOPIC]}}, - "seenMessageIds": [MSG_ID], + "seenMessageIds": [MESSAGE_ID], "cachedMessages": [], }, event={ "fromPeer": "peerAx", - "ihave": [{"topicId": TOPIC, "messageIds": [MSG_ID]}], + "ihave": [{"topicId": TOPIC, "messageIds": [MESSAGE_ID]}], }, ) @@ -189,12 +189,12 @@ def test_ihave_mixed(gossipsub_handler: GossipsubHandlerTestFiller) -> None: "subscriptions": [TOPIC], "meshes": {}, "peers": {"peerAx": {"subscriptions": [TOPIC]}}, - "seenMessageIds": [MSG_ID], + "seenMessageIds": [MESSAGE_ID], "cachedMessages": [], }, event={ "fromPeer": "peerAx", - "ihave": [{"topicId": TOPIC, "messageIds": [MSG_ID, MSG_ID_2]}], + "ihave": [{"topicId": TOPIC, "messageIds": [MESSAGE_ID, MESSAGE_ID_2]}], }, ) @@ -213,12 +213,12 @@ def test_iwant_cached_responds(gossipsub_handler: GossipsubHandlerTestFiller) -> "peers": {"peerAx": {"subscriptions": [TOPIC]}}, "seenMessageIds": [], "cachedMessages": [ - {"topic": TOPIC, "data": "0xdeadbeef", "messageId": MSG_ID}, + {"topic": TOPIC, "data": "0xdeadbeef", "messageId": MESSAGE_ID}, ], }, event={ "fromPeer": "peerAx", - "iwant": [{"messageIds": [MSG_ID]}], + "iwant": [{"messageIds": [MESSAGE_ID]}], }, ) @@ -261,7 +261,7 @@ def test_message_duplicate_not_forwarded( "senderX": {"subscriptions": [TOPIC]}, "peerAx": {"subscriptions": [TOPIC]}, }, - "seenMessageIds": [MSG_ID], + "seenMessageIds": [MESSAGE_ID], }, event={ "fromPeer": "senderX", @@ -282,7 +282,7 @@ def test_message_idontwant_skips_peer( "meshes": {TOPIC: ["senderX", "peerAx", "peerBx"]}, "peers": { "senderX": {"subscriptions": [TOPIC]}, - "peerAx": {"subscriptions": [TOPIC], "dontWantIds": [MSG_ID]}, + "peerAx": {"subscriptions": [TOPIC], "dontWantIds": [MESSAGE_ID]}, "peerBx": {"subscriptions": [TOPIC]}, }, }, diff --git a/tests/consensus/lstar/networking/test_gossipsub_rpc.py b/tests/consensus/lstar/networking/test_gossipsub_rpc.py index ae0f684e8..ba042a154 100644 --- a/tests/consensus/lstar/networking/test_gossipsub_rpc.py +++ b/tests/consensus/lstar/networking/test_gossipsub_rpc.py @@ -7,9 +7,9 @@ TOPIC_A = "/leanconsensus/0x12345678/block/ssz_snappy" TOPIC_B = "/leanconsensus/0x12345678/aggregation/ssz_snappy" -MSG_ID_1 = "0x" + "aa" * 20 -MSG_ID_2 = "0x" + "bb" * 20 -MSG_ID_3 = "0x" + "cc" * 20 +MESSAGE_ID_1 = "0x" + "aa" * 20 +MESSAGE_ID_2 = "0x" + "bb" * 20 +MESSAGE_ID_3 = "0x" + "cc" * 20 PEER_ID = "0x" + "dd" * 32 @@ -132,7 +132,7 @@ def test_ihave_single_id(networking_codec: NetworkingCodecTestFiller) -> None: "subscriptions": [], "publish": [], "control": { - "ihave": [{"topicId": TOPIC_A, "messageIds": [MSG_ID_1]}], + "ihave": [{"topicId": TOPIC_A, "messageIds": [MESSAGE_ID_1]}], }, }, ) @@ -146,7 +146,9 @@ def test_ihave_multiple_ids(networking_codec: NetworkingCodecTestFiller) -> None "subscriptions": [], "publish": [], "control": { - "ihave": [{"topicId": TOPIC_A, "messageIds": [MSG_ID_1, MSG_ID_2, MSG_ID_3]}], + "ihave": [ + {"topicId": TOPIC_A, "messageIds": [MESSAGE_ID_1, MESSAGE_ID_2, MESSAGE_ID_3]} + ], }, }, ) @@ -177,7 +179,7 @@ def test_iwant_single_id(networking_codec: NetworkingCodecTestFiller) -> None: "subscriptions": [], "publish": [], "control": { - "iwant": [{"messageIds": [MSG_ID_1]}], + "iwant": [{"messageIds": [MESSAGE_ID_1]}], }, }, ) @@ -191,7 +193,7 @@ def test_iwant_multiple_ids(networking_codec: NetworkingCodecTestFiller) -> None "subscriptions": [], "publish": [], "control": { - "iwant": [{"messageIds": [MSG_ID_1, MSG_ID_2]}], + "iwant": [{"messageIds": [MESSAGE_ID_1, MESSAGE_ID_2]}], }, }, ) @@ -261,7 +263,7 @@ def test_idontwant_single_id(networking_codec: NetworkingCodecTestFiller) -> Non "subscriptions": [], "publish": [], "control": { - "idontwant": [{"messageIds": [MSG_ID_1]}], + "idontwant": [{"messageIds": [MESSAGE_ID_1]}], }, }, ) @@ -275,7 +277,7 @@ def test_idontwant_multiple_ids(networking_codec: NetworkingCodecTestFiller) -> "subscriptions": [], "publish": [], "control": { - "idontwant": [{"messageIds": [MSG_ID_1, MSG_ID_2]}], + "idontwant": [{"messageIds": [MESSAGE_ID_1, MESSAGE_ID_2]}], }, }, ) @@ -304,11 +306,11 @@ def test_control_all_types(networking_codec: NetworkingCodecTestFiller) -> None: "subscriptions": [], "publish": [], "control": { - "ihave": [{"topicId": TOPIC_A, "messageIds": [MSG_ID_1]}], - "iwant": [{"messageIds": [MSG_ID_2]}], + "ihave": [{"topicId": TOPIC_A, "messageIds": [MESSAGE_ID_1]}], + "iwant": [{"messageIds": [MESSAGE_ID_2]}], "graft": [{"topicId": TOPIC_A}], "prune": [{"topicId": TOPIC_B, "backoff": 60}], - "idontwant": [{"messageIds": [MSG_ID_3]}], + "idontwant": [{"messageIds": [MESSAGE_ID_3]}], }, }, ) @@ -381,7 +383,7 @@ def test_rpc_full(networking_codec: NetworkingCodecTestFiller) -> None: "publish": [{"topic": TOPIC_A, "data": "0xdeadbeef"}], "control": { "graft": [{"topicId": TOPIC_B}], - "idontwant": [{"messageIds": [MSG_ID_1]}], + "idontwant": [{"messageIds": [MESSAGE_ID_1]}], }, }, ) diff --git a/tests/consensus/lstar/ssz/test_consensus_containers.py b/tests/consensus/lstar/ssz/test_consensus_containers.py index d7c5700f9..901172930 100644 --- a/tests/consensus/lstar/ssz/test_consensus_containers.py +++ b/tests/consensus/lstar/ssz/test_consensus_containers.py @@ -90,7 +90,7 @@ def test_attestation_zero(ssz: SSZTestFiller) -> None: """SSZ roundtrip for Attestation with zero values.""" ssz( type_name="Attestation", - value=Attestation(validator_id=ValidatorIndex(0), data=_zero_attestation_data()), + value=Attestation(validator_index=ValidatorIndex(0), data=_zero_attestation_data()), ) @@ -98,7 +98,7 @@ def test_attestation_typical(ssz: SSZTestFiller) -> None: """SSZ roundtrip for Attestation with typical values.""" ssz( type_name="Attestation", - value=Attestation(validator_id=ValidatorIndex(42), data=_typical_attestation_data()), + value=Attestation(validator_index=ValidatorIndex(42), data=_typical_attestation_data()), ) @@ -110,7 +110,7 @@ def test_signed_attestation_minimal(ssz: SSZTestFiller) -> None: ssz( type_name="SignedAttestation", value=SignedAttestation( - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), data=_zero_attestation_data(), signature=create_dummy_signature(), ), @@ -303,8 +303,8 @@ def test_validator_zero(ssz: SSZTestFiller) -> None: ssz( type_name="Validator", value=Validator( - attestation_pubkey=Bytes52.zero(), - proposal_pubkey=Bytes52.zero(), + attestation_public_key=Bytes52.zero(), + proposal_public_key=Bytes52.zero(), index=ValidatorIndex(0), ), ) @@ -315,8 +315,8 @@ def test_validator_typical(ssz: SSZTestFiller) -> None: ssz( type_name="Validator", value=Validator( - attestation_pubkey=Bytes52(b"\xab" * 52), - proposal_pubkey=Bytes52(b"\xab" * 52), + attestation_public_key=Bytes52(b"\xab" * 52), + proposal_public_key=Bytes52(b"\xab" * 52), index=ValidatorIndex(42), ), ) @@ -348,8 +348,8 @@ def test_state_minimal(ssz: SSZTestFiller) -> None: validators=Validators( data=[ Validator( - attestation_pubkey=Bytes52.zero(), - proposal_pubkey=Bytes52.zero(), + attestation_public_key=Bytes52.zero(), + proposal_public_key=Bytes52.zero(), index=ValidatorIndex(0), ) ] @@ -383,23 +383,23 @@ def test_state_with_validators(ssz: SSZTestFiller) -> None: validators=Validators( data=[ Validator( - attestation_pubkey=Bytes52(b"\x01" * 52), - proposal_pubkey=Bytes52(b"\x01" * 52), + attestation_public_key=Bytes52(b"\x01" * 52), + proposal_public_key=Bytes52(b"\x01" * 52), index=ValidatorIndex(0), ), Validator( - attestation_pubkey=Bytes52(b"\x02" * 52), - proposal_pubkey=Bytes52(b"\x02" * 52), + attestation_public_key=Bytes52(b"\x02" * 52), + proposal_public_key=Bytes52(b"\x02" * 52), index=ValidatorIndex(1), ), Validator( - attestation_pubkey=Bytes52(b"\x03" * 52), - proposal_pubkey=Bytes52(b"\x03" * 52), + attestation_public_key=Bytes52(b"\x03" * 52), + proposal_public_key=Bytes52(b"\x03" * 52), index=ValidatorIndex(2), ), Validator( - attestation_pubkey=Bytes52(b"\x04" * 52), - proposal_pubkey=Bytes52(b"\x04" * 52), + attestation_public_key=Bytes52(b"\x04" * 52), + proposal_public_key=Bytes52(b"\x04" * 52), index=ValidatorIndex(3), ), ] @@ -502,8 +502,8 @@ def test_validator_max_index(ssz: SSZTestFiller) -> None: ssz( type_name="Validator", value=Validator( - attestation_pubkey=Bytes52(b"\xff" * 52), - proposal_pubkey=Bytes52(b"\xff" * 52), + attestation_public_key=Bytes52(b"\xff" * 52), + proposal_public_key=Bytes52(b"\xff" * 52), index=ValidatorIndex(2**64 - 1), ), ) @@ -549,13 +549,13 @@ def test_state_with_full_history(ssz: SSZTestFiller) -> None: validators=Validators( data=[ Validator( - attestation_pubkey=Bytes52(b"\x01" * 52), - proposal_pubkey=Bytes52(b"\x01" * 52), + attestation_public_key=Bytes52(b"\x01" * 52), + proposal_public_key=Bytes52(b"\x01" * 52), index=ValidatorIndex(0), ), Validator( - attestation_pubkey=Bytes52(b"\x02" * 52), - proposal_pubkey=Bytes52(b"\x02" * 52), + attestation_public_key=Bytes52(b"\x02" * 52), + proposal_public_key=Bytes52(b"\x02" * 52), index=ValidatorIndex(1), ), ] diff --git a/tests/consensus/lstar/ssz/test_xmss_containers.py b/tests/consensus/lstar/ssz/test_xmss_containers.py index 22d0107fc..6389766b5 100644 --- a/tests/consensus/lstar/ssz/test_xmss_containers.py +++ b/tests/consensus/lstar/ssz/test_xmss_containers.py @@ -29,7 +29,7 @@ def _zero_hash_digest_vector() -> HashDigestVector: """Build a hash digest vector with all field elements set to zero.""" - return HashDigestVector(data=[Fp(0) for _ in range(TARGET_CONFIG.HASH_LEN_FE)]) + return HashDigestVector(data=[Fp(0) for _ in range(TARGET_CONFIG.HASH_LENGTH_FIELD_ELEMENTS)]) def _zero_parameter() -> Parameter: @@ -60,8 +60,8 @@ def test_signature_actual(ssz: SSZTestFiller) -> None: """SSZ roundtrip for a real Signature produced by the XMSS signing algorithm.""" key_manager = XmssKeyManager.shared() scheme = key_manager.scheme - sk = key_manager[ValidatorIndex(0)].attestation_keypair.secret_key - signature = scheme.sign(sk, Slot(0), Bytes32(b"\x42" * 32)) + secret_key = key_manager[ValidatorIndex(0)].attestation_keypair.secret_key + signature = scheme.sign(secret_key, Slot(0), Bytes32(b"\x42" * 32)) ssz(type_name="Signature", value=signature) @@ -113,7 +113,9 @@ def test_public_key_typical(ssz: SSZTestFiller) -> None: ssz( type_name="PublicKey", value=PublicKey( - root=HashDigestVector(data=[Fp(i + 1) for i in range(TARGET_CONFIG.HASH_LEN_FE)]), + root=HashDigestVector( + data=[Fp(i + 1) for i in range(TARGET_CONFIG.HASH_LENGTH_FIELD_ELEMENTS)] + ), parameter=Parameter(data=[Fp(100 + i) for i in range(Parameter.LENGTH)]), ), ) @@ -138,7 +140,9 @@ def test_hash_tree_opening_typical(ssz: SSZTestFiller) -> None: siblings=HashDigestList( data=[ HashDigestVector( - data=[Fp(i + j * 10) for i in range(TARGET_CONFIG.HASH_LEN_FE)] + data=[ + Fp(i + j * 10) for i in range(TARGET_CONFIG.HASH_LENGTH_FIELD_ELEMENTS) + ] ) for j in range(3) ] @@ -169,7 +173,11 @@ def test_hash_tree_layer_typical(ssz: SSZTestFiller) -> None: start_index=Uint64(42), nodes=HashDigestList( data=[ - HashDigestVector(data=[Fp(i + j * 7) for i in range(TARGET_CONFIG.HASH_LEN_FE)]) + HashDigestVector( + data=[ + Fp(i + j * 7) for i in range(TARGET_CONFIG.HASH_LENGTH_FIELD_ELEMENTS) + ] + ) for j in range(2) ] ), diff --git a/tests/consensus/lstar/state_transition/test_finalization.py b/tests/consensus/lstar/state_transition/test_finalization.py index 4952b7d35..438fa460f 100644 --- a/tests/consensus/lstar/state_transition/test_finalization.py +++ b/tests/consensus/lstar/state_transition/test_finalization.py @@ -54,7 +54,7 @@ def test_finalization_on_next_justifiable_step( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -70,7 +70,7 @@ def test_finalization_on_next_justifiable_step( parent_label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -129,7 +129,7 @@ def test_pending_justification_survives_finalization_rebase( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -155,7 +155,7 @@ def test_pending_justification_survives_finalization_rebase( parent_label="block_4", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ], slot=Slot(4), @@ -163,7 +163,7 @@ def test_pending_justification_survives_finalization_rebase( target_root_label="block_3", ), AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -231,7 +231,7 @@ def test_no_finalization_when_intermediate_justifiable_slot_exists( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -257,7 +257,7 @@ def test_no_finalization_when_intermediate_justifiable_slot_exists( parent_label="block_4", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -317,7 +317,7 @@ def test_mid_block_finalized_slot_visibility( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -337,7 +337,7 @@ def test_mid_block_finalized_slot_visibility( parent_label="block_7", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -347,7 +347,7 @@ def test_mid_block_finalized_slot_visibility( target_root_label="block_2", ), AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -414,7 +414,7 @@ def test_finalization_prunes_stale_pending_votes_and_rebases_window( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -431,7 +431,7 @@ def test_finalization_prunes_stale_pending_votes_and_rebases_window( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ], @@ -452,7 +452,7 @@ def test_finalization_prunes_stale_pending_votes_and_rebases_window( label="block_5", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -468,7 +468,7 @@ def test_finalization_prunes_stale_pending_votes_and_rebases_window( parent_label="block_5", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -531,7 +531,7 @@ def test_non_adjacent_justification_finalizes_across_non_justifiable_gap( label="block_7", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -549,7 +549,7 @@ def test_non_adjacent_justification_finalizes_across_non_justifiable_gap( parent_label="block_9", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -608,7 +608,7 @@ def test_no_finalization_when_rebased_boundary_exposes_intermediate_justifiable_ label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -629,7 +629,7 @@ def test_no_finalization_when_rebased_boundary_exposes_intermediate_justifiable_ label="block_8", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -639,7 +639,7 @@ def test_no_finalization_when_rebased_boundary_exposes_intermediate_justifiable_ target_root_label="block_2", ), AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -660,7 +660,7 @@ def test_no_finalization_when_rebased_boundary_exposes_intermediate_justifiable_ parent_label="block_13", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -732,7 +732,7 @@ def test_mid_block_finalized_slot_rejects_target_that_loses_justifiability( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -755,7 +755,7 @@ def test_mid_block_finalized_slot_rejects_target_that_loses_justifiability( parent_label="block_9", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -765,7 +765,7 @@ def test_mid_block_finalized_slot_rejects_target_that_loses_justifiability( target_root_label="block_2", ), AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -838,7 +838,7 @@ def test_merged_attestations_for_same_target_justify_and_finalize_cleanly( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -854,7 +854,7 @@ def test_merged_attestations_for_same_target_justify_and_finalize_cleanly( parent_label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -864,7 +864,7 @@ def test_merged_attestations_for_same_target_justify_and_finalize_cleanly( target_root_label="block_2", ), AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(3), ], slot=Slot(3), @@ -924,7 +924,7 @@ def test_rebased_finalization_prunes_stale_votes_and_preserves_future_votes( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -942,7 +942,7 @@ def test_rebased_finalization_prunes_stale_votes_and_preserves_future_votes( label="block_5", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ], @@ -960,7 +960,7 @@ def test_rebased_finalization_prunes_stale_votes_and_preserves_future_votes( label="block_8", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -970,7 +970,7 @@ def test_rebased_finalization_prunes_stale_votes_and_preserves_future_votes( target_root_label="block_2", ), AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -991,7 +991,7 @@ def test_rebased_finalization_prunes_stale_votes_and_preserves_future_votes( parent_label="block_13", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ], slot=Slot(14), @@ -999,7 +999,7 @@ def test_rebased_finalization_prunes_stale_votes_and_preserves_future_votes( target_root_label="block_13", ), AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), diff --git a/tests/consensus/lstar/state_transition/test_genesis.py b/tests/consensus/lstar/state_transition/test_genesis.py index 5de6f2c85..1750f8aac 100644 --- a/tests/consensus/lstar/state_transition/test_genesis.py +++ b/tests/consensus/lstar/state_transition/test_genesis.py @@ -36,19 +36,19 @@ def _generate_max_registry_pre_state() -> State: - """Build genesis with the registry filled to capacity using placeholder pubkeys. + """Build genesis with the registry filled to capacity using placeholder public_keys. XMSS key generation is intentionally skipped: every validator carries a - zero pubkey. This is sound because the state-transition path used here + zero public_key. This is sound because the state-transition path used here counts attestation votes without verifying signatures, so the keys are never read. """ - zero_pubkey = Bytes52.zero() + zero_public_key = Bytes52.zero() validators = Validators( data=[ Validator( - attestation_pubkey=zero_pubkey, - proposal_pubkey=zero_pubkey, + attestation_public_key=zero_public_key, + proposal_public_key=zero_public_key, index=ValidatorIndex(i), ) for i in range(int(VALIDATOR_REGISTRY_LIMIT)) @@ -67,7 +67,7 @@ def test_genesis_default_configuration( -------- Generate a genesis state with default parameters: - genesis_time = 0 - - 4 validators with zero pubkeys + - 4 validators with zero public_keys """ state_transition_test( pre=generate_pre_state(), @@ -150,7 +150,7 @@ def test_genesis_custom_validator_set( -------- Generate a genesis state with: - 8 validators instead of default 4 - - Custom validator pubkeys + - Custom validator public_keys Expected Behavior ----------------- @@ -232,7 +232,9 @@ def test_genesis_maximum_validators_with_forced_threshold_attestation( slot=Slot(2), forced_attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i) for i in range(supermajority_threshold)], + validator_indices=[ + ValidatorIndex(i) for i in range(supermajority_threshold) + ], slot=Slot(2), target_slot=Slot(1), target_root_label="block_1", @@ -288,7 +290,7 @@ def test_genesis_single_validator( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(2), target_slot=Slot(1), target_root_label="block_1", @@ -303,7 +305,7 @@ def test_genesis_single_validator( slot=Slot(3), attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(3), target_slot=Slot(2), target_root_label="block_2", diff --git a/tests/consensus/lstar/state_transition/test_justification.py b/tests/consensus/lstar/state_transition/test_justification.py index 497d777bd..07ec8ee7d 100644 --- a/tests/consensus/lstar/state_transition/test_justification.py +++ b/tests/consensus/lstar/state_transition/test_justification.py @@ -49,7 +49,7 @@ def test_supermajority_attestations_justify_block( parent_label="block_1", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -97,7 +97,7 @@ def test_even_validator_threshold_boundary( parent_label="block_1", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -146,7 +146,7 @@ def test_below_threshold_support_does_not_justify( parent_label="block_1", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -198,7 +198,7 @@ def test_votes_accumulate_across_blocks( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -214,7 +214,7 @@ def test_votes_accumulate_across_blocks( parent_label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(3), ], slot=Slot(3), @@ -265,7 +265,7 @@ def test_repeated_validators_do_not_double_count_across_blocks( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ], @@ -281,7 +281,7 @@ def test_repeated_validators_do_not_double_count_across_blocks( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ], @@ -328,7 +328,7 @@ def test_repeated_validator_does_not_double_count_within_same_block( parent_label="block_1", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -338,7 +338,7 @@ def test_repeated_validator_does_not_double_count_within_same_block( target_root_label="block_1", ), AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ], slot=Slot(2), @@ -401,7 +401,7 @@ def test_pronic_boundary_acceptance( parent_label="block_6", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -456,7 +456,7 @@ def test_non_justifiable_boundary_rejection( parent_label="block_7", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -513,7 +513,7 @@ def test_square_boundary_acceptance( parent_label="block_9", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -563,7 +563,7 @@ def test_split_supermajority_aggregations_in_same_block_justify( parent_label="block_1", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ], @@ -572,7 +572,7 @@ def test_split_supermajority_aggregations_in_same_block_justify( target_root_label="block_1", ), AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(2), ValidatorIndex(3), ], @@ -622,7 +622,7 @@ def test_odd_validator_threshold_boundary_justifies( parent_label="block_1", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -674,7 +674,7 @@ def test_odd_validator_threshold_boundary_does_not_justify( parent_label="block_1", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -733,7 +733,7 @@ def test_supermajority_with_mismatched_target_root_is_ignored( parent_label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -784,7 +784,7 @@ def test_attestation_with_target_root_not_in_historical_hashes_is_skipped( parent_label="block_1", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -838,7 +838,7 @@ def test_justification_clears_only_the_resolved_target_votes( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -855,7 +855,7 @@ def test_justification_clears_only_the_resolved_target_votes( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ], @@ -870,7 +870,7 @@ def test_justification_clears_only_the_resolved_target_votes( parent_label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(3), ], slot=Slot(4), @@ -930,7 +930,7 @@ def test_target_at_or_before_source_is_ignored( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -947,7 +947,7 @@ def test_target_at_or_before_source_is_ignored( label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ], @@ -968,7 +968,7 @@ def test_target_at_or_before_source_is_ignored( label="block_5", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -984,7 +984,7 @@ def test_target_at_or_before_source_is_ignored( parent_label="block_5", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(2), ], slot=Slot(6), @@ -1052,7 +1052,7 @@ def test_attestation_with_already_justified_target_is_silently_skipped( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -1071,7 +1071,7 @@ def test_attestation_with_already_justified_target_is_silently_skipped( parent_label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(3), ], slot=Slot(3), @@ -1138,7 +1138,7 @@ def test_attestation_with_zero_hash_source_root_is_skipped( # Malformed: source root = ZERO_HASH. # A real source root would be the genesis block hash. AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -1151,7 +1151,7 @@ def test_attestation_with_zero_hash_source_root_is_skipped( # Valid: correct source derived from genesis justified. # Threshold: 3*3=9 >= 2*4=8 -> justifies slot 1. AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -1216,7 +1216,7 @@ def test_attestation_with_zero_hash_target_root_is_skipped( # Malformed: target root = ZERO_HASH. # A real target root would be block_1's hash. AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -1228,7 +1228,7 @@ def test_attestation_with_zero_hash_target_root_is_skipped( # Valid: correct target resolved from block_1 label. # Threshold: 3*3=9 >= 2*4=8 -> justifies slot 1. AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -1299,7 +1299,7 @@ def test_attestation_with_unjustified_source_is_silently_skipped( label="block_2", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -1330,7 +1330,7 @@ def test_attestation_with_unjustified_source_is_silently_skipped( parent_label="block_3", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ], @@ -1341,7 +1341,7 @@ def test_attestation_with_unjustified_source_is_silently_skipped( ], forced_attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(2), ValidatorIndex(3), ], @@ -1429,7 +1429,7 @@ def test_same_block_multi_target_attestations_advance_to_highest_slot( parent_label="block_9", attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -1439,7 +1439,7 @@ def test_same_block_multi_target_attestations_advance_to_highest_slot( target_root_label="block_4", ), AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), @@ -1451,7 +1451,7 @@ def test_same_block_multi_target_attestations_advance_to_highest_slot( ], forced_attestations=[ AggregatedAttestationSpec( - validator_ids=[ + validator_indices=[ ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), diff --git a/tests/consensus/lstar/verify_signatures/test_empty_aggregation_bits.py b/tests/consensus/lstar/verify_signatures/test_empty_aggregation_bits.py index 7b92b5efb..716dad0cf 100644 --- a/tests/consensus/lstar/verify_signatures/test_empty_aggregation_bits.py +++ b/tests/consensus/lstar/verify_signatures/test_empty_aggregation_bits.py @@ -44,7 +44,7 @@ def test_empty_aggregation_bits_rejected( slot=Slot(1), attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(1), target_slot=Slot(0), target_root_label="genesis", diff --git a/tests/consensus/lstar/verify_signatures/test_index_out_of_range.py b/tests/consensus/lstar/verify_signatures/test_index_out_of_range.py index 6334dd8d9..96496a58f 100644 --- a/tests/consensus/lstar/verify_signatures/test_index_out_of_range.py +++ b/tests/consensus/lstar/verify_signatures/test_index_out_of_range.py @@ -47,7 +47,7 @@ def test_attestation_validator_index_out_of_range_rejected( slot=Slot(2), attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(99)], + validator_indices=[ValidatorIndex(99)], slot=Slot(1), target_slot=Slot(0), target_root_label="genesis", diff --git a/tests/consensus/lstar/verify_signatures/test_invalid_signatures.py b/tests/consensus/lstar/verify_signatures/test_invalid_signatures.py index 3631d060e..e03c70814 100644 --- a/tests/consensus/lstar/verify_signatures/test_invalid_signatures.py +++ b/tests/consensus/lstar/verify_signatures/test_invalid_signatures.py @@ -79,7 +79,7 @@ def test_invalid_aggregated_attestation_signature( attestations=[ # Valid aggregated attestation AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(2), target_slot=Slot(1), target_root_label="genesis", @@ -87,7 +87,7 @@ def test_invalid_aggregated_attestation_signature( ), # Invalid aggregated attestation (different target to force separate aggregation) AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(2)], + validator_indices=[ValidatorIndex(2)], slot=Slot(1), target_slot=Slot(0), target_root_label="genesis", diff --git a/tests/consensus/lstar/verify_signatures/test_valid_signatures.py b/tests/consensus/lstar/verify_signatures/test_valid_signatures.py index 773941cad..c5f6c35f9 100644 --- a/tests/consensus/lstar/verify_signatures/test_valid_signatures.py +++ b/tests/consensus/lstar/verify_signatures/test_valid_signatures.py @@ -27,7 +27,7 @@ def test_proposer_signature( Expected Behavior ----------------- 1. Proposer's signature in SignedBlock can be verified against - the validator's pubkey in the state + the validator's public_key in the state Why This Matters ---------------- @@ -60,9 +60,9 @@ def test_proposer_and_attester_signatures( Expected Behavior ----------------- 1. Proposer's signature in SignedBlock can be verified against - the validator's pubkey in the state + the validator's public_key in the state 2. Aggregated attestation signatures can be verified against the validators' - pubkeys in the state + public_keys in the state Why This Matters ---------------- @@ -77,7 +77,7 @@ def test_proposer_and_attester_signatures( slot=Slot(1), attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0), ValidatorIndex(2)], + validator_indices=[ValidatorIndex(0), ValidatorIndex(2)], slot=Slot(1), target_slot=Slot(0), target_root_label="genesis", @@ -114,7 +114,7 @@ def test_all_four_validators_attesting( slot=Slot(1), attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0), ValidatorIndex(2), ValidatorIndex(3)], + validator_indices=[ValidatorIndex(0), ValidatorIndex(2), ValidatorIndex(3)], slot=Slot(1), target_slot=Slot(0), target_root_label="genesis", @@ -151,7 +151,7 @@ def test_single_validator_attestation( slot=Slot(1), attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0)], + validator_indices=[ValidatorIndex(0)], slot=Slot(1), target_slot=Slot(0), target_root_label="genesis", @@ -190,13 +190,13 @@ def test_multiple_attestation_groups_same_data( slot=Slot(1), attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(0), ValidatorIndex(2)], + validator_indices=[ValidatorIndex(0), ValidatorIndex(2)], slot=Slot(1), target_slot=Slot(0), target_root_label="genesis", ), AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(3)], + validator_indices=[ValidatorIndex(3)], slot=Slot(1), target_slot=Slot(0), target_root_label="genesis", diff --git a/tests/interop/helpers/assertions.py b/tests/interop/helpers/assertions.py index f09a6dc56..cd7a6d23f 100644 --- a/tests/interop/helpers/assertions.py +++ b/tests/interop/helpers/assertions.py @@ -119,18 +119,18 @@ def assert_checkpoint_monotonicity( num_nodes = len(checkpoint_history[0]) - for node_idx in range(num_nodes): - prev_justified = 0 - prev_finalized = 0 - for phase_idx, phase_diags in enumerate(checkpoint_history): - d = phase_diags[node_idx] - assert d.justified_slot >= prev_justified, ( - f"Node {node_idx} justified_slot regressed: " - f"{prev_justified} -> {d.justified_slot} at phase {phase_idx}" + for node_index in range(num_nodes): + previous_justified = 0 + previous_finalized = 0 + for phase_index, phase_diags in enumerate(checkpoint_history): + d = phase_diags[node_index] + assert d.justified_slot >= previous_justified, ( + f"Node {node_index} justified_slot regressed: " + f"{previous_justified} -> {d.justified_slot} at phase {phase_index}" ) - assert d.finalized_slot >= prev_finalized, ( - f"Node {node_idx} finalized_slot regressed: " - f"{prev_finalized} -> {d.finalized_slot} at phase {phase_idx}" + assert d.finalized_slot >= previous_finalized, ( + f"Node {node_index} finalized_slot regressed: " + f"{previous_finalized} -> {d.finalized_slot} at phase {phase_index}" ) - prev_justified = d.justified_slot - prev_finalized = d.finalized_slot + previous_justified = d.justified_slot + previous_finalized = d.finalized_slot diff --git a/tests/interop/helpers/node_runner.py b/tests/interop/helpers/node_runner.py index 676b116fb..41f68afdd 100644 --- a/tests/interop/helpers/node_runner.py +++ b/tests/interop/helpers/node_runner.py @@ -49,7 +49,7 @@ class TestNode: event_source: LiveNetworkEventSource """Network event source for connection management.""" - listen_addr: str + listen_address: str """P2P listen address (e.g., '/ip4/127.0.0.1/udp/20600/quic-v1').""" index: int @@ -152,22 +152,22 @@ async def stop(self) -> None: except (asyncio.CancelledError, asyncio.TimeoutError, Exception): pass - async def dial(self, addr: str, timeout: float = 10.0) -> bool: + async def dial(self, address: str, timeout: float = 10.0) -> bool: """ Connect to a peer. Args: - addr: Multiaddr of the peer. + address: Multiaddr of the peer. timeout: Dial timeout in seconds. Returns: True if connection succeeded. """ try: - peer_id = await asyncio.wait_for(self.event_source.dial(addr), timeout=timeout) + peer_id = await asyncio.wait_for(self.event_source.dial(address), timeout=timeout) return peer_id is not None except asyncio.TimeoutError: - logger.warning("Dial to %s timed out after %.1fs", addr, timeout) + logger.warning("Dial to %s timed out after %.1fs", address, timeout) return False @@ -221,20 +221,23 @@ def _generate_validators(self) -> None: num_active_slots = int(scheme.config.LIFETIME) for i in range(self.num_validators): - att_keypair = scheme.key_gen(Slot(0), Uint64(num_active_slots)) - prop_keypair = scheme.key_gen(Slot(0), Uint64(num_active_slots)) - self._secret_keys[ValidatorIndex(i)] = (att_keypair.secret_key, prop_keypair.secret_key) + attestation_keypair = scheme.key_gen(Slot(0), Uint64(num_active_slots)) + proposal_keypair = scheme.key_gen(Slot(0), Uint64(num_active_slots)) + self._secret_keys[ValidatorIndex(i)] = ( + attestation_keypair.secret_key, + proposal_keypair.secret_key, + ) - att_pubkey_bytes = att_keypair.public_key.encode_bytes()[:52] - att_pubkey = Bytes52(att_pubkey_bytes.ljust(52, b"\x00")) + attestation_public_key_bytes = attestation_keypair.public_key.encode_bytes()[:52] + attestation_public_key = Bytes52(attestation_public_key_bytes.ljust(52, b"\x00")) - prop_pubkey_bytes = prop_keypair.public_key.encode_bytes()[:52] - prop_pubkey = Bytes52(prop_pubkey_bytes.ljust(52, b"\x00")) + proposal_public_key_bytes = proposal_keypair.public_key.encode_bytes()[:52] + proposal_public_key = Bytes52(proposal_public_key_bytes.ljust(52, b"\x00")) validators.append( Validator( - attestation_pubkey=att_pubkey, - proposal_pubkey=prop_pubkey, + attestation_public_key=attestation_public_key, + proposal_public_key=proposal_public_key, index=ValidatorIndex(i), ) ) @@ -267,7 +270,7 @@ async def start_node( p2p_port = self.port_allocator.allocate_port() # QUIC over UDP is the only supported transport. # QUIC provides native multiplexing, flow control, and TLS 1.3 encryption. - listen_addr = f"/ip4/127.0.0.1/udp/{p2p_port}/quic-v1" + listen_address = f"/ip4/127.0.0.1/udp/{p2p_port}/quic-v1" event_source = await LiveNetworkEventSource.create() event_source.set_network_name(self.network_name) @@ -275,14 +278,14 @@ async def start_node( validator_registry: ValidatorRegistry | None = None if validator_indices: registry = ValidatorRegistry() - for idx in validator_indices: - if idx in self._secret_keys: - att_sk, prop_sk = self._secret_keys[idx] + for index in validator_indices: + if index in self._secret_keys: + attestation_secret_key, proposal_secret_key = self._secret_keys[index] registry.add( ValidatorEntry( - index=ValidatorIndex(idx), - attestation_secret_key=att_sk, - proposal_secret_key=prop_sk, + index=ValidatorIndex(index), + attestation_secret_key=attestation_secret_key, + proposal_secret_key=proposal_secret_key, ) ) if len(registry) > 0: @@ -335,7 +338,7 @@ async def start_node( test_node = TestNode( node=node, event_source=event_source, - listen_addr=listen_addr, + listen_address=listen_address, index=node_index, ) @@ -347,7 +350,7 @@ async def start_node( event_source._stop_event.clear() listener_task = asyncio.create_task( - event_source.listen(listen_addr), + event_source.listen(listen_address), name=f"listener-{node_index}", ) @@ -359,7 +362,7 @@ async def start_node( try: listener_task.result() except OSError as e: - raise RuntimeError(f"Failed to start listener on {listen_addr}: {e}") from e + raise RuntimeError(f"Failed to start listener on {listen_address}: {e}") from e test_node._listener_task = listener_task @@ -375,8 +378,8 @@ async def start_node( # Validators only subscribe to the subnets they are assigned to. # This matches the Ethereum gossip specification. if validator_indices: - for idx in validator_indices: - subnet_id = int(idx) % int(ATTESTATION_COMMITTEE_COUNT) + for index in validator_indices: + subnet_id = int(index) % int(ATTESTATION_COMMITTEE_COUNT) topic = TopicId( f"/leanconsensus/{self.network_name}/attestation_{subnet_id}/ssz_snappy" ) @@ -391,8 +394,8 @@ async def start_node( await test_node.start() if bootnodes: - for addr in bootnodes: - await test_node.dial(addr) + for address in bootnodes: + await test_node.dial(address) self.nodes.append(test_node) # Log node startup with gossipsub instance ID for debugging. @@ -400,7 +403,7 @@ async def start_node( logger.info( "Started node %d on %s (validators: %s, services=%s, GS=%x)", node_index, - listen_addr, + listen_address, validator_indices, "running" if start_services else "pending", gs_id, @@ -449,7 +452,9 @@ async def start_all( # A node is an aggregator if it controls any of the first # ATTESTATION_COMMITTEE_COUNT validators. - is_node_aggregator = any(vid in aggregator_indices for vid in validator_indices) + is_node_aggregator = any( + validator_index in aggregator_indices for validator_index in validator_indices + ) await self.start_node( i, @@ -469,14 +474,14 @@ async def start_all( await asyncio.sleep(0.5) # Phase 2: Establish peer connections. - for dialer_idx, listener_idx in topology: - dialer = self.nodes[dialer_idx] - listener = self.nodes[listener_idx] - success = await dialer.dial(listener.listen_addr) + for dialer_index, listener_index in topology: + dialer = self.nodes[dialer_index] + listener = self.nodes[listener_index] + success = await dialer.dial(listener.listen_address) if success: - logger.info("Connected node %d -> node %d", dialer_idx, listener_idx) + logger.info("Connected node %d -> node %d", dialer_index, listener_index) else: - logger.warning("Failed to connect node %d -> node %d", dialer_idx, listener_idx) + logger.warning("Failed to connect node %d -> node %d", dialer_index, listener_index) # Phase 3: Wait for gossipsub mesh to stabilize. # diff --git a/tests/interop/test_consensus_lifecycle.py b/tests/interop/test_consensus_lifecycle.py index 348433b27..2a7a26049 100644 --- a/tests/interop/test_consensus_lifecycle.py +++ b/tests/interop/test_consensus_lifecycle.py @@ -248,10 +248,10 @@ async def test_consensus_lifecycle(node_cluster: NodeCluster) -> None: if slot <= 1: continue checked_blocks += 1 - att_count = len(block.body.attestations) - if att_count > 0: + attestation_count = len(block.body.attestations) + if attestation_count > 0: blocks_with_attestations += 1 - logger.info("Slot %d: %d attestations in block body", slot, att_count) + logger.info("Slot %d: %d attestations in block body", slot, attestation_count) # At least one block after slot 1 must exist and contain attestations. assert checked_blocks >= 1, "No blocks after slot 1 found in store" diff --git a/tests/lean_spec/cli/test_args.py b/tests/lean_spec/cli/test_args.py index 5bc89435c..7df2d5842 100644 --- a/tests/lean_spec/cli/test_args.py +++ b/tests/lean_spec/cli/test_args.py @@ -17,7 +17,7 @@ def test_only_genesis_populates_defaults(self) -> None: assert parse_args(["--genesis", "config.yaml"]) == CliArgs( genesis_path=Path("config.yaml"), bootnodes=(), - listen_addr="/ip4/0.0.0.0/udp/9001/quic-v1", + listen_address="/ip4/0.0.0.0/udp/9001/quic-v1", checkpoint_sync_url=None, validator_keys_path=None, node_id="lean_spec_0", @@ -34,7 +34,7 @@ class TestParseArgsFullFlagSet: def test_full_flag_set_round_trip(self, tmp_path: Path) -> None: """Every flag set on the command line shows up in the parsed value.""" - keys_dir = tmp_path / "keys" + keys_directory = tmp_path / "keys" assert parse_args( [ "--genesis", @@ -46,7 +46,7 @@ def test_full_flag_set_round_trip(self, tmp_path: Path) -> None: "--checkpoint-sync-url", "http://localhost:5052", "--validator-keys", - str(keys_dir), + str(keys_directory), "--node-id", "node_7", "--verbose", @@ -60,9 +60,9 @@ def test_full_flag_set_round_trip(self, tmp_path: Path) -> None: ) == CliArgs( genesis_path=Path("g.yaml"), bootnodes=("/ip4/1.1.1.1/udp/9000/quic-v1",), - listen_addr="/ip4/0.0.0.0/udp/9100/quic-v1", + listen_address="/ip4/0.0.0.0/udp/9100/quic-v1", checkpoint_sync_url="http://localhost:5052", - validator_keys_path=keys_dir, + validator_keys_path=keys_directory, node_id="node_7", verbose=True, no_color=True, diff --git a/tests/lean_spec/cli/test_bootstrap.py b/tests/lean_spec/cli/test_bootstrap.py index 2836bb8d4..c630c0da8 100644 --- a/tests/lean_spec/cli/test_bootstrap.py +++ b/tests/lean_spec/cli/test_bootstrap.py @@ -27,11 +27,11 @@ def enr_keypair() -> tuple[ec.EllipticCurvePrivateKey, bytes]: """One secp256k1 keypair shared across the whole session.""" private_key = ec.generate_private_key(ec.SECP256K1()) - compressed_pubkey = private_key.public_key().public_bytes( + compressed_public_key = private_key.public_key().public_bytes( encoding=serialization.Encoding.X962, format=serialization.PublicFormat.CompressedPoint, ) - return private_key, compressed_pubkey + return private_key, compressed_public_key @pytest.fixture(scope="session") @@ -39,7 +39,7 @@ def make_enr( enr_keypair: tuple[ec.EllipticCurvePrivateKey, bytes], ) -> Callable[..., str]: """Build a signed ENR string for the given IP and optional UDP port.""" - private_key, compressed_pubkey = enr_keypair + private_key, compressed_public_key = enr_keypair def _sign(content_items: list[RLPItem]) -> bytes: # Hash the RLP-encoded content with keccak-256 and sign the digest. @@ -60,7 +60,7 @@ def _build(ip_bytes: bytes, udp_port: int | None = None) -> str: b"ip", ip_bytes, b"secp256k1", - compressed_pubkey, + compressed_public_key, ] # UDP port is optional; absent means the record has no dialable endpoint. @@ -97,24 +97,28 @@ def genesis_yaml(tmp_path: Path) -> Path: @pytest.fixture -def validator_keys_dir(tmp_path: Path) -> Path: +def validator_keys_directory(tmp_path: Path) -> Path: """Materialise a one-validator key directory the registry loader accepts.""" # The registry loader expects the ream/zeam two-file layout. keys_root = tmp_path / "keys" - hash_sig_dir = keys_root / "hash-sig-keys" - hash_sig_dir.mkdir(parents=True) + hash_signature_directory = keys_root / "hash-sig-keys" + hash_signature_directory.mkdir(parents=True) # Borrow a real precomputed XMSS keypair from the shared manager. km = XmssKeyManager.shared(max_slot=Slot(10)) kp = km[ValidatorIndex(0)] # Drop both secret keys to disk under the names the manifest references. - (hash_sig_dir / "att_key_0.ssz").write_bytes(kp.attestation_keypair.secret_key.encode_bytes()) - (hash_sig_dir / "prop_key_0.ssz").write_bytes(kp.proposal_keypair.secret_key.encode_bytes()) + (hash_signature_directory / "att_key_0.ssz").write_bytes( + kp.attestation_keypair.secret_key.encode_bytes() + ) + (hash_signature_directory / "prop_key_0.ssz").write_bytes( + kp.proposal_keypair.secret_key.encode_bytes() + ) # Manifest carries one validator with placeholder public keys. - # The loader does not verify the pubkeys against the secret keys here. - manifest = hash_sig_dir / "validator-keys-manifest.yaml" + # The loader does not verify the public_keys against the secret keys here. + manifest = hash_signature_directory / "validator-keys-manifest.yaml" manifest.write_text( "key_scheme: SIGTopLevelTargetSumLifetime32Dim64Base8\n" "hash_function: Poseidon2\n" @@ -125,10 +129,10 @@ def validator_keys_dir(tmp_path: Path) -> Path: "num_validators: 1\n" "validators:\n" " - index: 0\n" - f" attestation_pubkey_hex: '0x{'00' * 52}'\n" - f" proposal_pubkey_hex: '0x{'00' * 52}'\n" - " attestation_privkey_file: att_key_0.ssz\n" - " proposal_privkey_file: prop_key_0.ssz\n" + f" attestation_public_key_hex: '0x{'00' * 52}'\n" + f" proposal_public_key_hex: '0x{'00' * 52}'\n" + " attestation_private_key_file: att_key_0.ssz\n" + " proposal_private_key_file: prop_key_0.ssz\n" ) # Node-to-validator mapping assigns the only validator to the default node id. @@ -220,12 +224,12 @@ def test_empty_string_parses_to_empty_tuple( assert boot.aggregate_subnet_ids == () def test_valid_extras_parse_into_tuple( - self, make_boot: Callable[..., NodeBootstrap], validator_keys_dir: Path + self, make_boot: Callable[..., NodeBootstrap], validator_keys_directory: Path ) -> None: """A comma list with aggregator mode and a populated registry resolves in order.""" boot = make_boot( "--validator-keys", - str(validator_keys_dir), + str(validator_keys_directory), "--is-aggregator", "--aggregate-subnet-ids", "1,2,3", @@ -277,7 +281,7 @@ def test_empty_listen_address_becomes_none( ) -> None: """An empty listen string resolves to no listener.""" boot = make_boot("--listen", "") - assert boot.listen_addr is None + assert boot.listen_address is None class TestBuildAnchor: @@ -318,5 +322,5 @@ async def test_checkpoint_url_calls_from_checkpoint( "url": "http://localhost:5052", "genesis": boot.genesis, "fork": boot.fork, - "validator_id": boot.registry.primary_index(), + "validator_index": boot.registry.primary_index(), } diff --git a/tests/lean_spec/cli/test_run.py b/tests/lean_spec/cli/test_run.py index 4db6e432d..a6b78b106 100644 --- a/tests/lean_spec/cli/test_run.py +++ b/tests/lean_spec/cli/test_run.py @@ -46,7 +46,7 @@ def _build(**overrides: Any) -> NodeBootstrap: "registry": ValidatorRegistry(), "fork": DEFAULT_REGISTRY.current, "bootnode_multiaddrs": (), - "listen_addr": None, + "listen_address": None, "checkpoint_sync_url": None, "node_id": "lean_spec_0", "is_aggregator": False, diff --git a/tests/lean_spec/conftest.py b/tests/lean_spec/conftest.py index 4bf531dbd..9ea7c1bce 100644 --- a/tests/lean_spec/conftest.py +++ b/tests/lean_spec/conftest.py @@ -37,7 +37,7 @@ def key_manager() -> XmssKeyManager: return XmssKeyManager.shared(max_slot=Slot(20)) -_DEFAULT_VALIDATOR_ID = ValidatorIndex(0) +_DEFAULT_VALIDATOR_INDEX = ValidatorIndex(0) @pytest.fixture @@ -46,12 +46,12 @@ def store_factory(key_manager: XmssKeyManager) -> Callable[..., Store]: def _create( num_validators: int = 12, - validator_id: ValidatorIndex | None = _DEFAULT_VALIDATOR_ID, + validator_index: ValidatorIndex | None = _DEFAULT_VALIDATOR_INDEX, genesis_time: int = 0, ) -> Store: return make_store( num_validators=num_validators, - validator_id=validator_id, + validator_index=validator_index, genesis_time=genesis_time, key_manager=key_manager, ) @@ -80,7 +80,7 @@ def base_store(spec: LstarSpec, genesis_state: State, genesis_block: Block) -> S return spec.create_store( genesis_state, genesis_block, - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), ) @@ -107,7 +107,7 @@ def keyed_genesis_block(keyed_genesis: GenesisData) -> Block: @pytest.fixture def keyed_store(keyed_genesis: GenesisData) -> Store: - """Fork choice store with real XMSS keys, validator_id=0.""" + """Fork choice store with real XMSS keys, validator_index=0.""" return keyed_genesis.store @@ -115,5 +115,5 @@ def keyed_store(keyed_genesis: GenesisData) -> Store: def observer_store( spec: LstarSpec, keyed_genesis_state: State, keyed_genesis_block: Block ) -> Store: - """Fork choice store with validator_id=None (non-validator observer).""" - return spec.create_store(keyed_genesis_state, keyed_genesis_block, validator_id=None) + """Fork choice store with validator_index=None (non-validator observer).""" + return spec.create_store(keyed_genesis_state, keyed_genesis_block, validator_index=None) diff --git a/tests/lean_spec/helpers/__init__.py b/tests/lean_spec/helpers/__init__.py index 808cdff6f..d4821b7a8 100644 --- a/tests/lean_spec/helpers/__init__.py +++ b/tests/lean_spec/helpers/__init__.py @@ -40,7 +40,7 @@ RecordingSyncDatabase, ) -TEST_VALIDATOR_ID = ValidatorIndex(0) +TEST_VALIDATOR_INDEX = ValidatorIndex(0) __all__ = [ @@ -78,5 +78,5 @@ "RecordedCall", "RecordingSyncDatabase", # Constants - "TEST_VALIDATOR_ID", + "TEST_VALIDATOR_INDEX", ] diff --git a/tests/lean_spec/helpers/builders.py b/tests/lean_spec/helpers/builders.py index d99d16140..d7e76846c 100644 --- a/tests/lean_spec/helpers/builders.py +++ b/tests/lean_spec/helpers/builders.py @@ -59,7 +59,7 @@ def make_bytes32(seed: int) -> Bytes32: def _zero_digest() -> HashDigestVector: """Create a zero-filled hash digest vector.""" - return HashDigestVector(data=[Fp(0)] * TARGET_CONFIG.HASH_LEN_FE) + return HashDigestVector(data=[Fp(0)] * TARGET_CONFIG.HASH_LENGTH_FIELD_ELEMENTS) def make_mock_signature() -> Signature: @@ -68,7 +68,7 @@ def make_mock_signature() -> Signature: Fills path, rho, and hashes with zeros at the exact dimensions required by the active scheme. This ensures the signature serializes - to exactly SIGNATURE_LEN_BYTES, matching the fixed-size SSZ encoding. + to exactly SIGNATURE_LENGTH_BYTES, matching the fixed-size SSZ encoding. """ return Signature( path=HashTreeOpening( @@ -76,7 +76,7 @@ def make_mock_signature() -> Signature: data=[_zero_digest() for _ in range(TARGET_CONFIG.LOG_LIFETIME)] ) ), - rho=Randomness(data=[Fp(0)] * TARGET_CONFIG.RAND_LEN_FE), + rho=Randomness(data=[Fp(0)] * TARGET_CONFIG.RAND_LENGTH_FIELD_ELEMENTS), hashes=HashDigestList(data=[_zero_digest() for _ in range(TARGET_CONFIG.DIMENSION)]), ) @@ -89,8 +89,8 @@ def make_validators(count: int) -> Validators: """ validators = [ Validator( - attestation_pubkey=Bytes52(b"\x00" * 52), - proposal_pubkey=Bytes52(b"\x00" * 52), + attestation_public_key=Bytes52(b"\x00" * 52), + proposal_public_key=Bytes52(b"\x00" * 52), index=ValidatorIndex(i), ) for i in range(count) @@ -102,13 +102,13 @@ def make_validators_from_key_manager(key_manager: XmssKeyManager, count: int) -> """Build a validator registry with real XMSS keys from a key manager.""" validators = [] for i in range(count): - idx = ValidatorIndex(i) - att_pk, prop_pk = key_manager.get_public_keys(idx) + index = ValidatorIndex(i) + attestation_public_key, proposal_public_key = key_manager.get_public_keys(index) validators.append( Validator( - attestation_pubkey=Bytes52(att_pk.encode_bytes()), - proposal_pubkey=Bytes52(prop_pk.encode_bytes()), - index=idx, + attestation_public_key=Bytes52(attestation_public_key.encode_bytes()), + proposal_public_key=Bytes52(proposal_public_key.encode_bytes()), + index=index, ) ) return Validators(data=validators) @@ -255,7 +255,7 @@ def make_signed_attestation( source=source_checkpoint, ) return SignedAttestation( - validator_id=validator, + validator_index=validator, data=attestation_data, signature=make_mock_signature(), ) @@ -279,7 +279,7 @@ def make_test_block(slot: int = 1, seed: int = 0) -> SignedBlock: ) -_DEFAULT_VALIDATOR_ID = ValidatorIndex(0) +_DEFAULT_VALIDATOR_INDEX = ValidatorIndex(0) _DEFAULT_ATTESTATION_SLOT = Slot(1) @@ -295,7 +295,7 @@ def make_genesis_data( num_validators: int = 3, genesis_time: int = 0, key_manager: XmssKeyManager | None = None, - validator_id: ValidatorIndex | None = _DEFAULT_VALIDATOR_ID, + validator_index: ValidatorIndex | None = _DEFAULT_VALIDATOR_INDEX, ) -> GenesisData: """Create a forkchoice store with genesis state and block, returning all three.""" if key_manager is not None: @@ -304,13 +304,13 @@ def make_genesis_data( validators = make_validators(num_validators) genesis_state = make_genesis_state(validators=validators, genesis_time=genesis_time) genesis_block = make_genesis_block(genesis_state) - store = LstarSpec().create_store(genesis_state, genesis_block, validator_id=validator_id) + store = LstarSpec().create_store(genesis_state, genesis_block, validator_index=validator_index) return GenesisData(store, genesis_state, genesis_block) def make_store( num_validators: int = 3, - validator_id: ValidatorIndex | None = _DEFAULT_VALIDATOR_ID, + validator_index: ValidatorIndex | None = _DEFAULT_VALIDATOR_INDEX, genesis_time: int = 0, key_manager: XmssKeyManager | None = None, ) -> Store: @@ -319,20 +319,20 @@ def make_store( num_validators=num_validators, genesis_time=genesis_time, key_manager=key_manager, - validator_id=validator_id, + validator_index=validator_index, ).store def make_store_with_attestation_data( key_manager: XmssKeyManager, num_validators: int, - validator_id: ValidatorIndex, + validator_index: ValidatorIndex, attestation_slot: Slot = _DEFAULT_ATTESTATION_SLOT, ) -> tuple[Store, AttestationData]: """Create a store with validators and produce attestation data for testing.""" store = make_store( num_validators=num_validators, - validator_id=validator_id, + validator_index=validator_index, key_manager=key_manager, ) store.time = Interval.from_slot(attestation_slot) @@ -343,7 +343,7 @@ def make_store_with_attestation_data( def make_store_with_attestation_signatures( key_manager: XmssKeyManager, num_validators: int, - validator_id: ValidatorIndex, + validator_index: ValidatorIndex, attesting_validators: list[ValidatorIndex], attestation_slot: Slot = _DEFAULT_ATTESTATION_SLOT, ) -> tuple[Store, AttestationData]: @@ -351,13 +351,16 @@ def make_store_with_attestation_signatures( store, attestation_data = make_store_with_attestation_data( key_manager, num_validators, - validator_id, + validator_index, attestation_slot, ) store.attestation_signatures = { attestation_data: { - AttestationSignatureEntry(vid, key_manager.sign_attestation_data(vid, attestation_data)) - for vid in attesting_validators + AttestationSignatureEntry( + validator_index, + key_manager.sign_attestation_data(validator_index, attestation_data), + ) + for validator_index in attesting_validators }, } return store, attestation_data @@ -398,11 +401,11 @@ def make_aggregated_proof( data_root = hash_tree_root(attestation_data) raw_xmss = [ ( - vid, - key_manager.get_public_keys(vid)[0], - key_manager.sign_attestation_data(vid, attestation_data), + validator_index, + key_manager.get_public_keys(validator_index)[0], + key_manager.sign_attestation_data(validator_index, attestation_data), ) - for vid in participants + for validator_index in participants ] return SingleMessageAggregate.aggregate( children=[], @@ -454,18 +457,18 @@ def make_signed_block_from_store( head_state = new_store.states[new_store.head] public_keys_per_part: list[list] = [ [ - head_state.validators[vid].get_attestation_pubkey() - for vid in proof.participants.to_validator_indices() + head_state.validators[validator_index].get_attestation_public_key() + for validator_index in proof.participants.to_validator_indices() ] for proof in attestation_proofs ] - proposer_pubkey = head_state.validators[proposer_index].get_proposal_pubkey() - public_keys_per_part.append([proposer_pubkey]) + proposer_public_key = head_state.validators[proposer_index].get_proposal_public_key() + public_keys_per_part.append([proposer_public_key]) proposer_signature = key_manager.sign_block_root(proposer_index, slot, block_root) proposer_single_message_aggregate = SingleMessageAggregate.aggregate( children=[], - raw_xmss=[(proposer_index, proposer_pubkey, proposer_signature)], + raw_xmss=[(proposer_index, proposer_public_key, proposer_signature)], message=block_root, slot=slot, ) diff --git a/tests/lean_spec/helpers/mocks.py b/tests/lean_spec/helpers/mocks.py index 007cef984..5fc4d589b 100644 --- a/tests/lean_spec/helpers/mocks.py +++ b/tests/lean_spec/helpers/mocks.py @@ -168,7 +168,7 @@ def __init__(self, head_slot: int = 0) -> None: self.head_slot: Slot = Slot(head_slot) self._attestations_received: list[SignedAttestation] = [] self._aggregated_attestations_received: list[SignedAggregatedAttestation] = [] - self.validator_id = None + self.validator_index = None self.latest_justified = Checkpoint(root=Bytes32.zero(), slot=Slot(0)) self.latest_finalized = Checkpoint(root=Bytes32.zero(), slot=Slot(0)) self.states: dict[Bytes32, object] = {} diff --git a/tests/lean_spec/node/chain/test_service.py b/tests/lean_spec/node/chain/test_service.py index d3a2d6804..5655b1e89 100644 --- a/tests/lean_spec/node/chain/test_service.py +++ b/tests/lean_spec/node/chain/test_service.py @@ -53,9 +53,9 @@ class MockSyncService: is_aggregator: bool = False published_aggregations: list = field(default_factory=list) - async def publish_aggregated_attestation(self, agg: SignedAggregatedAttestation) -> None: + async def publish_aggregated_attestation(self, aggregate: SignedAggregatedAttestation) -> None: """Record published aggregations.""" - self.published_aggregations.append(agg) + self.published_aggregations.append(aggregate) class TestChainServiceLifecycle: diff --git a/tests/lean_spec/node/genesis/test_config.py b/tests/lean_spec/node/genesis/test_config.py index 3e094550a..659d2f475 100644 --- a/tests/lean_spec/node/genesis/test_config.py +++ b/tests/lean_spec/node/genesis/test_config.py @@ -18,32 +18,41 @@ def _load(content: str) -> GenesisConfig: return GenesisConfig.model_validate(yaml.safe_load(content)) -# Sample pubkeys (52 bytes each, hex-encoded) -SAMPLE_PUBKEY_1 = "0x" + "00" * 52 -SAMPLE_PUBKEY_2 = "0x" + "01" * 52 -SAMPLE_PUBKEY_3 = "0x" + "02" * 52 +# Sample public_keys (52 bytes each, hex-encoded) +SAMPLE_PUBLIC_KEY_1 = "0x" + "00" * 52 +SAMPLE_PUBLIC_KEY_2 = "0x" + "01" * 52 +SAMPLE_PUBLIC_KEY_3 = "0x" + "02" * 52 SAMPLE_YAML = yaml.dump( { "GENESIS_TIME": 1704085200, "GENESIS_VALIDATORS": [ - {"attestation_pubkey": SAMPLE_PUBKEY_1, "proposal_pubkey": SAMPLE_PUBKEY_1}, - {"attestation_pubkey": SAMPLE_PUBKEY_2, "proposal_pubkey": SAMPLE_PUBKEY_2}, - {"attestation_pubkey": SAMPLE_PUBKEY_3, "proposal_pubkey": SAMPLE_PUBKEY_3}, + { + "attestation_public_key": SAMPLE_PUBLIC_KEY_1, + "proposal_public_key": SAMPLE_PUBLIC_KEY_1, + }, + { + "attestation_public_key": SAMPLE_PUBLIC_KEY_2, + "proposal_public_key": SAMPLE_PUBLIC_KEY_2, + }, + { + "attestation_public_key": SAMPLE_PUBLIC_KEY_3, + "proposal_public_key": SAMPLE_PUBLIC_KEY_3, + }, ], } ) -# Real pubkeys -PUBKEY_1 = ( +# Real public_keys +PUBLIC_KEY_1 = ( "0xe2a03c16122c7e0f940e2301aa460c54a2e1e8343968bb2782f26636f051e65e" "c589c858b9c7980b276ebe550056b23f0bdc3b5a" ) -PUBKEY_2 = ( +PUBLIC_KEY_2 = ( "0x0767e65924063f79ae92ee1953685f06718b1756cc665a299bd61b4b82055e37" "7237595d9a27887421b5233d09a50832db2f303d" ) -PUBKEY_3 = ( +PUBLIC_KEY_3 = ( "0xd4355005bc37f76f390dcd2bcc51677d8c6ab44e0cc64913fb84ad459789a311" "05bd9a69afd2690ffd737d22ec6e3b31d47a642f" ) @@ -70,31 +79,31 @@ def test_load_from_yaml_file(self) -> None: assert config.genesis_time == Uint64(1704085200) assert len(config.genesis_validators) == 3 - def test_pubkeys_parsed_correctly(self) -> None: - """Pubkeys are converted to Bytes52 instances.""" + def test_public_keys_parsed_correctly(self) -> None: + """PublicKeys are converted to Bytes52 instances.""" config = _load(SAMPLE_YAML) for entry in config.genesis_validators: - assert isinstance(entry.attestation_pubkey, Bytes52) - assert len(entry.attestation_pubkey) == 52 - assert isinstance(entry.proposal_pubkey, Bytes52) - assert len(entry.proposal_pubkey) == 52 + assert isinstance(entry.attestation_public_key, Bytes52) + assert len(entry.attestation_public_key) == 52 + assert isinstance(entry.proposal_public_key, Bytes52) + assert len(entry.proposal_public_key) == 52 - def test_pubkey_without_0x_prefix(self) -> None: - """Handles pubkeys without 0x prefix (zeam format).""" + def test_public_key_without_0x_prefix(self) -> None: + """Handles public_keys without 0x prefix (zeam format).""" yaml_content = yaml.dump( { "GENESIS_TIME": 1704085200, "GENESIS_VALIDATORS": [ - {"attestation_pubkey": "00" * 52, "proposal_pubkey": "00" * 52}, - {"attestation_pubkey": "01" * 52, "proposal_pubkey": "01" * 52}, + {"attestation_public_key": "00" * 52, "proposal_public_key": "00" * 52}, + {"attestation_public_key": "01" * 52, "proposal_public_key": "01" * 52}, ], } ) config = _load(yaml_content) assert len(config.genesis_validators) == 2 - assert config.genesis_validators[0].attestation_pubkey == Bytes52(b"\x00" * 52) + assert config.genesis_validators[0].attestation_public_key == Bytes52(b"\x00" * 52) class TestGenesisConfigValidators: @@ -109,8 +118,11 @@ def test_to_validators_creates_indexed_list(self) -> None: for i, validator in enumerate(validators.data): assert validator.index == ValidatorIndex(i) - assert validator.attestation_pubkey == config.genesis_validators[i].attestation_pubkey - assert validator.proposal_pubkey == config.genesis_validators[i].proposal_pubkey + assert ( + validator.attestation_public_key + == config.genesis_validators[i].attestation_public_key + ) + assert validator.proposal_public_key == config.genesis_validators[i].proposal_public_key def test_empty_validators_list(self) -> None: """Handles empty validator list.""" @@ -129,26 +141,32 @@ def test_empty_validators_list(self) -> None: class TestGenesisConfigValidation: """Tests for validation errors.""" - def test_invalid_pubkey_raises_validation_error(self) -> None: + def test_invalid_public_key_raises_validation_error(self) -> None: """Rejects malformed hex.""" yaml_content = yaml.dump( { "GENESIS_TIME": 1704085200, "GENESIS_VALIDATORS": [ - {"attestation_pubkey": "not_valid_hex", "proposal_pubkey": "not_valid_hex"}, + { + "attestation_public_key": "not_valid_hex", + "proposal_public_key": "not_valid_hex", + }, ], } ) with pytest.raises(ValidationError): _load(yaml_content) - def test_wrong_length_pubkey_raises_error(self) -> None: - """Rejects pubkeys with wrong length.""" + def test_wrong_length_public_key_raises_error(self) -> None: + """Rejects public_keys with wrong length.""" yaml_content = yaml.dump( { "GENESIS_TIME": 1704085200, "GENESIS_VALIDATORS": [ - {"attestation_pubkey": "0x0011223344", "proposal_pubkey": "0x0011223344"}, + { + "attestation_public_key": "0x0011223344", + "proposal_public_key": "0x0011223344", + }, ], } ) @@ -161,8 +179,8 @@ def test_missing_genesis_time_raises_error(self) -> None: { "GENESIS_VALIDATORS": [ { - "attestation_pubkey": SAMPLE_PUBKEY_1, - "proposal_pubkey": SAMPLE_PUBKEY_1, + "attestation_public_key": SAMPLE_PUBLIC_KEY_1, + "proposal_public_key": SAMPLE_PUBLIC_KEY_1, }, ], } @@ -195,15 +213,15 @@ def test_validators_not_a_list_raises_error(self) -> None: class TestCrossClientFormat: """Tests for cross-client YAML config format compatibility.""" - def test_hex_prefixed_pubkeys(self) -> None: - """Loads config with 0x-prefixed hex pubkeys (cross-client convention).""" + def test_hex_prefixed_public_keys(self) -> None: + """Loads config with 0x-prefixed hex public_keys (cross-client convention).""" yaml_content = yaml.dump( { "GENESIS_TIME": 1704085200, "GENESIS_VALIDATORS": [ - {"attestation_pubkey": PUBKEY_1, "proposal_pubkey": PUBKEY_1}, - {"attestation_pubkey": PUBKEY_2, "proposal_pubkey": PUBKEY_2}, - {"attestation_pubkey": PUBKEY_3, "proposal_pubkey": PUBKEY_3}, + {"attestation_public_key": PUBLIC_KEY_1, "proposal_public_key": PUBLIC_KEY_1}, + {"attestation_public_key": PUBLIC_KEY_2, "proposal_public_key": PUBLIC_KEY_2}, + {"attestation_public_key": PUBLIC_KEY_3, "proposal_public_key": PUBLIC_KEY_3}, ], } ) @@ -212,8 +230,8 @@ def test_hex_prefixed_pubkeys(self) -> None: assert config.genesis_time == Uint64(1704085200) assert len(config.genesis_validators) == 3 - # Verify all pubkeys match expected values. - expected_pubkeys = [ + # Verify all public_keys match expected values. + expected_public_keys = [ Bytes52( bytes.fromhex( "e2a03c16122c7e0f940e2301aa460c54a2e1e8343968bb2782f26636f051e65e" @@ -233,5 +251,5 @@ def test_hex_prefixed_pubkeys(self) -> None: ) ), ] - for entry, expected in zip(config.genesis_validators, expected_pubkeys, strict=True): - assert entry.attestation_pubkey == expected + for entry, expected in zip(config.genesis_validators, expected_public_keys, strict=True): + assert entry.attestation_public_key == expected diff --git a/tests/lean_spec/node/genesis/test_state.py b/tests/lean_spec/node/genesis/test_state.py index abbfdc485..ee44e37ef 100644 --- a/tests/lean_spec/node/genesis/test_state.py +++ b/tests/lean_spec/node/genesis/test_state.py @@ -16,12 +16,16 @@ def test_genesis_block_hash_comparison(spec: LstarSpec) -> None: """Test that genesis block hashes are deterministic and differ with different inputs.""" # Create first genesis state with 3 validators - # Fill pubkeys with different values (1, 2, 3) + # Fill public_keys with different values (1, 2, 3) pubkeys1 = [Bytes52(bytes([i + 1] * 52)) for i in range(3)] validators1 = Validators( data=[ - Validator(attestation_pubkey=pubkey, proposal_pubkey=pubkey, index=ValidatorIndex(i)) - for i, pubkey in enumerate(pubkeys1) + Validator( + attestation_public_key=public_key, + proposal_public_key=public_key, + index=ValidatorIndex(i), + ) + for i, public_key in enumerate(pubkeys1) ] ) @@ -62,12 +66,16 @@ def test_genesis_block_hash_comparison(spec: LstarSpec) -> None: assert genesis_block_hash1 == genesis_block_hash1_copy # Create second genesis state with different validators - # Fill pubkeys with different values (10, 11, 12) + # Fill public_keys with different values (10, 11, 12) pubkeys2 = [Bytes52(bytes([i + 10] * 52)) for i in range(3)] validators2 = Validators( data=[ - Validator(attestation_pubkey=pubkey, proposal_pubkey=pubkey, index=ValidatorIndex(i)) - for i, pubkey in enumerate(pubkeys2) + Validator( + attestation_public_key=public_key, + proposal_public_key=public_key, + index=ValidatorIndex(i), + ) + for i, public_key in enumerate(pubkeys2) ] ) @@ -94,8 +102,12 @@ def test_genesis_block_hash_comparison(spec: LstarSpec) -> None: pubkeys3 = [Bytes52(bytes([i + 1] * 52)) for i in range(3)] validators3 = Validators( data=[ - Validator(attestation_pubkey=pubkey, proposal_pubkey=pubkey, index=ValidatorIndex(i)) - for i, pubkey in enumerate(pubkeys3) + Validator( + attestation_public_key=public_key, + proposal_public_key=public_key, + index=ValidatorIndex(i), + ) + for i, public_key in enumerate(pubkeys3) ] ) diff --git a/tests/lean_spec/node/networking/client/test_event_source.py b/tests/lean_spec/node/networking/client/test_event_source.py index 11c2305d6..e02a10d06 100644 --- a/tests/lean_spec/node/networking/client/test_event_source.py +++ b/tests/lean_spec/node/networking/client/test_event_source.py @@ -83,12 +83,12 @@ def _build_gossip_wire(topic: str, ssz_data: bytes) -> bytes: """ topic_bytes = topic.encode("utf-8") compressed = compress(ssz_data) - buf = bytearray() - buf.extend(encode_varint(len(topic_bytes))) - buf.extend(topic_bytes) - buf.extend(encode_varint(len(compressed))) - buf.extend(compressed) - return bytes(buf) + buffer = bytearray() + buffer.extend(encode_varint(len(topic_bytes))) + buffer.extend(topic_bytes) + buffer.extend(encode_varint(len(compressed))) + buffer.extend(compressed) + return bytes(buffer) class MockStream: @@ -273,10 +273,10 @@ async def test_message_split_between_topic_and_data(self) -> None: topic_str = _block_topic() wire = _build_gossip_wire(topic_str, block.encode_bytes()) - # Find the boundary: after topic_len varint + topic bytes + # Find the boundary: after topic_length varint + topic bytes topic_bytes = topic_str.encode("utf-8") - varint_len = len(encode_varint(len(topic_bytes))) - boundary = varint_len + len(topic_bytes) + varint_length = len(encode_varint(len(topic_bytes))) + boundary = varint_length + len(topic_bytes) chunks = [wire[:boundary], wire[boundary:]] stream = MockStream(chunks) @@ -287,8 +287,8 @@ async def test_message_split_between_topic_and_data(self) -> None: async def test_three_chunk_delivery(self) -> None: """Handles message delivered in three arbitrary pieces.""" - att = _make_attestation() - wire = _build_gossip_wire(_attestation_topic(), att.encode_bytes()) + attestation = _make_attestation() + wire = _build_gossip_wire(_attestation_topic(), attestation.encode_bytes()) third = len(wire) // 3 chunks = [wire[:third], wire[third : 2 * third], wire[2 * third :]] @@ -467,13 +467,13 @@ async def test_disconnect_known_peer_emits_event(self) -> None: es = _make_event_source() peer_id = PeerId.from_base58("peerA") - mock_conn = AsyncMock(spec=QuicConnection) - es._connections[peer_id] = mock_conn + mock_connection = AsyncMock(spec=QuicConnection) + es._connections[peer_id] = mock_connection await es.disconnect(peer_id) assert peer_id not in es._connections - mock_conn.close.assert_awaited_once() + mock_connection.close.assert_awaited_once() from lean_spec.node.networking.service.events import PeerDisconnectedEvent @@ -658,8 +658,8 @@ def test_block_roundtrip_preserves_ssz(self) -> None: def test_attestation_roundtrip_preserves_ssz(self) -> None: """Attestation SSZ bytes are identical after decode.""" handler = GossipHandler(network_name=FORK_DIGEST) - att = _make_attestation() - original_bytes = att.encode_bytes() + attestation = _make_attestation() + original_bytes = attestation.encode_bytes() result = handler.decode_message( _attestation_topic(), diff --git a/tests/lean_spec/node/networking/client/test_gossip_reception.py b/tests/lean_spec/node/networking/client/test_gossip_reception.py index e2fda1f13..44f98aef4 100644 --- a/tests/lean_spec/node/networking/client/test_gossip_reception.py +++ b/tests/lean_spec/node/networking/client/test_gossip_reception.py @@ -120,7 +120,7 @@ def build_gossip_message(topic: str, ssz_data: bytes) -> bytes: """ Build a complete gossip message from topic and SSZ data. - Format: [topic_len varint][topic][data_len varint][compressed_data] + Format: [topic_length varint][topic][data_length varint][compressed_data] Uses raw Snappy compression as required by Ethereum gossip protocol. """ @@ -145,9 +145,9 @@ def test_is_exception_subclass(self) -> None: def test_message_preserved(self) -> None: """Error message is preserved.""" - msg = "Test error message" - error = GossipMessageError(msg) - assert str(error) == msg + message = "Test error message" + error = GossipMessageError(message) + assert str(error) == message def test_can_be_raised_and_caught(self) -> None: """Can be raised and caught properly.""" diff --git a/tests/lean_spec/node/networking/client/test_reqresp_client.py b/tests/lean_spec/node/networking/client/test_reqresp_client.py index a61a6e5ae..b3f5692b4 100644 --- a/tests/lean_spec/node/networking/client/test_reqresp_client.py +++ b/tests/lean_spec/node/networking/client/test_reqresp_client.py @@ -123,20 +123,20 @@ def test_register_connection(self) -> None: """Connections can be registered for a peer.""" client = make_client() peer_id = PeerId.from_base58("16Uiu2HAmTestPeer123") - conn = MockConnection() + connection = MockConnection() - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] assert peer_id in client._connections - assert client._connections[peer_id] is conn + assert client._connections[peer_id] is connection def test_unregister_connection(self) -> None: """Connections can be unregistered.""" client = make_client() peer_id = PeerId.from_base58("16Uiu2HAmTestPeer123") - conn = MockConnection() + connection = MockConnection() - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] client.unregister_connection(peer_id) assert peer_id not in client._connections @@ -178,9 +178,9 @@ async def test_send_status_success(self) -> None: response_bytes = ResponseCode.SUCCESS.encode(peer_status.encode_bytes()) stream = MockStream(response_chunks=[response_bytes]) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] our_status = make_test_status() result = await client.send_status(peer_id, our_status) @@ -207,9 +207,9 @@ async def test_send_status_server_error_response(self) -> None: response_bytes = ResponseCode.SERVER_ERROR.encode(b"Internal error") stream = MockStream(response_chunks=[response_bytes]) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] result = await client.send_status(peer_id, make_test_status()) @@ -222,9 +222,9 @@ async def test_send_status_stream_closed(self) -> None: # Empty response simulates closed stream stream = MockStream(response_chunks=[]) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] result = await client.send_status(peer_id, make_test_status()) @@ -239,9 +239,9 @@ async def test_send_status_writes_request(self) -> None: response_bytes = ResponseCode.SUCCESS.encode(peer_status.encode_bytes()) stream = MockStream(response_chunks=[response_bytes]) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] our_status = make_test_status() await client.send_status(peer_id, our_status) @@ -260,9 +260,9 @@ async def test_send_status_closes_stream(self) -> None: response_bytes = ResponseCode.SUCCESS.encode(peer_status.encode_bytes()) stream = MockStream(response_chunks=[response_bytes]) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] await client.send_status(peer_id, make_test_status()) @@ -282,9 +282,9 @@ async def test_request_single_block_success(self) -> None: response_bytes = ResponseCode.SUCCESS.encode(block.encode_bytes()) stream = MockStream(response_chunks=[response_bytes]) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] roots = [Bytes32(b"\x11" * 32)] blocks = await client.request_blocks_by_root(peer_id, roots) @@ -303,9 +303,9 @@ async def test_request_multiple_blocks_success(self) -> None: response2 = ResponseCode.SUCCESS.encode(block2.encode_bytes()) stream = MockStream(response_chunks=[response1, response2]) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] roots = [Bytes32(b"\x11" * 32), Bytes32(b"\x22" * 32)] blocks = await client.request_blocks_by_root(peer_id, roots) @@ -325,9 +325,9 @@ async def test_request_blocks_partial_response(self) -> None: unavailable_response = ResponseCode.RESOURCE_UNAVAILABLE.encode(b"Not found") stream = MockStream(response_chunks=[success_response, unavailable_response]) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] roots = [Bytes32(b"\x11" * 32), Bytes32(b"\x22" * 32)] blocks = await client.request_blocks_by_root(peer_id, roots) @@ -341,14 +341,14 @@ async def test_request_blocks_empty_request(self) -> None: client = make_client() peer_id = PeerId.from_base58("16Uiu2HAmTestPeer123") - conn = MockConnection() - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection = MockConnection() + client.register_connection(peer_id, connection) # type: ignore[arg-type] blocks = await client.request_blocks_by_root(peer_id, []) assert len(blocks) == 0 # No stream should be opened for empty request - assert len(conn.opened_protocols) == 0 + assert len(connection.opened_protocols) == 0 async def test_request_blocks_no_connection(self) -> None: """Request to unconnected peer returns empty.""" @@ -371,9 +371,9 @@ async def test_request_blocks_stream_closed_early(self) -> None: # Only one response, but we request two blocks stream = MockStream(response_chunks=[response]) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] roots = [Bytes32(b"\x11" * 32), Bytes32(b"\x22" * 32)] blocks = await client.request_blocks_by_root(peer_id, roots) @@ -392,9 +392,9 @@ async def test_request_blocks_server_error_stops_reading(self) -> None: # Error response should stop reading (not continue like RESOURCE_UNAVAILABLE) stream = MockStream(response_chunks=[success_response, error_response]) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] roots = [Bytes32(b"\x11" * 32), Bytes32(b"\x22" * 32), Bytes32(b"\x33" * 32)] blocks = await client.request_blocks_by_root(peer_id, roots) @@ -418,9 +418,9 @@ async def slow_read() -> bytes: stream = MockStream() stream.read = slow_read # type: ignore[method-assign] - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] result = await client.send_status(peer_id, make_test_status()) @@ -438,9 +438,9 @@ async def slow_read() -> bytes: stream = MockStream() stream.read = slow_read # type: ignore[method-assign] - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] roots = [Bytes32(b"\x11" * 32)] blocks = await client.request_blocks_by_root(peer_id, roots) @@ -464,9 +464,9 @@ async def test_write_failure_returns_gracefully(self) -> None: peer_id = PeerId.from_base58("16Uiu2HAmTestPeer123") stream = MockStream(_fail_on_write=True) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] result = await client.send_status(peer_id, make_test_status()) @@ -478,9 +478,9 @@ async def test_read_failure_returns_gracefully(self) -> None: peer_id = PeerId.from_base58("16Uiu2HAmTestPeer123") stream = MockStream(_fail_on_read=True) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] result = await client.send_status(peer_id, make_test_status()) @@ -491,8 +491,8 @@ async def test_open_stream_failure_returns_gracefully(self) -> None: client = make_client() peer_id = PeerId.from_base58("16Uiu2HAmTestPeer123") - conn = MockConnection(_fail_on_open=True) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection = MockConnection(_fail_on_open=True) + client.register_connection(peer_id, connection) # type: ignore[arg-type] result = await client.send_status(peer_id, make_test_status()) @@ -506,9 +506,9 @@ async def test_malformed_response_handled(self) -> None: # Invalid response: wrong response code format malformed = b"\xff\xff\xff\xff" stream = MockStream(response_chunks=[malformed]) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] result = await client.send_status(peer_id, make_test_status()) @@ -525,9 +525,9 @@ async def test_blocks_codec_error_stops_reading(self) -> None: malformed = b"\x00\xff\xff\xff" stream = MockStream(response_chunks=[valid_response, malformed]) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] roots = [Bytes32(b"\x11" * 32), Bytes32(b"\x22" * 32)] blocks = await client.request_blocks_by_root(peer_id, roots) @@ -629,9 +629,9 @@ async def test_stream_closed_on_success(self) -> None: response = ResponseCode.SUCCESS.encode(peer_status.encode_bytes()) stream = MockStream(response_chunks=[response]) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] await client.send_status(peer_id, make_test_status()) @@ -645,9 +645,9 @@ async def test_stream_closed_on_error(self) -> None: # Error response response = ResponseCode.SERVER_ERROR.encode(b"Error") stream = MockStream(response_chunks=[response]) - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] await client.send_status(peer_id, make_test_status()) @@ -671,9 +671,9 @@ async def track_close() -> None: stream = MockStream() stream.read = slow_read # type: ignore[method-assign] stream.close = track_close # type: ignore[method-assign] - conn = MockConnection(streams=[stream]) + connection = MockConnection(streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + client.register_connection(peer_id, connection) # type: ignore[arg-type] await client.send_status(peer_id, make_test_status()) @@ -694,14 +694,14 @@ async def test_correct_protocol_negotiated(self) -> None: status_stream = MockStream(response_chunks=[status_response]) block_stream = MockStream(response_chunks=[block_response]) - status_conn = MockConnection(streams=[status_stream]) - block_conn = MockConnection(streams=[block_stream]) + status_connection = MockConnection(streams=[status_stream]) + block_connection = MockConnection(streams=[block_stream]) - client.register_connection(peer1, status_conn) # type: ignore[arg-type] - client.register_connection(peer2, block_conn) # type: ignore[arg-type] + client.register_connection(peer1, status_connection) # type: ignore[arg-type] + client.register_connection(peer2, block_connection) # type: ignore[arg-type] await client.send_status(peer1, make_test_status()) await client.request_blocks_by_root(peer2, [Bytes32(b"\x11" * 32)]) - assert status_conn.opened_protocols == [STATUS_PROTOCOL_V1] - assert block_conn.opened_protocols == [BLOCKS_BY_ROOT_PROTOCOL_V1] + assert status_connection.opened_protocols == [STATUS_PROTOCOL_V1] + assert block_connection.opened_protocols == [BLOCKS_BY_ROOT_PROTOCOL_V1] diff --git a/tests/lean_spec/node/networking/client/test_reqresp_client_range.py b/tests/lean_spec/node/networking/client/test_reqresp_client_range.py index 0b2f856a1..565ca15ce 100644 --- a/tests/lean_spec/node/networking/client/test_reqresp_client_range.py +++ b/tests/lean_spec/node/networking/client/test_reqresp_client_range.py @@ -149,42 +149,42 @@ class TestReqRespClientBlocksByRange: async def test_zero_count_returns_empty_without_opening_stream(self, peer_id: PeerId) -> None: """A count of zero short-circuits without opening a stream.""" client = make_client() - conn = MockRangeConnection(peer_id=peer_id) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection = MockRangeConnection(peer_id=peer_id) + client.register_connection(peer_id, connection) # type: ignore[arg-type] blocks = await client.request_blocks_by_range(peer_id, Slot(0), Uint64(0)) assert blocks == [] - assert conn.opened_protocols == [] + assert connection.opened_protocols == [] async def test_count_above_max_returns_empty_without_opening_stream( self, peer_id: PeerId ) -> None: """A count strictly larger than MAX_REQUEST_BLOCKS is rejected locally.""" client = make_client() - conn = MockRangeConnection(peer_id=peer_id) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection = MockRangeConnection(peer_id=peer_id) + client.register_connection(peer_id, connection) # type: ignore[arg-type] blocks = await client.request_blocks_by_range( peer_id, Slot(0), Uint64(MAX_REQUEST_BLOCKS + 1) ) assert blocks == [] - assert conn.opened_protocols == [] + assert connection.opened_protocols == [] async def test_overflow_range_returns_empty_without_opening_stream( self, peer_id: PeerId ) -> None: """A start_slot+count overflow above 2**64-1 is rejected locally.""" client = make_client() - conn = MockRangeConnection(peer_id=peer_id) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection = MockRangeConnection(peer_id=peer_id) + client.register_connection(peer_id, connection) # type: ignore[arg-type] max_slot = int(Uint64.max_value()) blocks = await client.request_blocks_by_range(peer_id, Slot(max_slot - 4), Uint64(10)) assert blocks == [] - assert conn.opened_protocols == [] + assert connection.opened_protocols == [] async def test_no_connection_returns_empty(self, peer_id: PeerId) -> None: """A request with no registered connection returns an empty list.""" @@ -200,13 +200,13 @@ async def test_full_range_success(self, peer_id: PeerId) -> None: chain = build_chain(start_slot=10, count=4) stream = MockRangeStream(response_chunks=[encode_success(b) for b in chain]) - conn = MockRangeConnection(peer_id=peer_id, streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection = MockRangeConnection(peer_id=peer_id, streams=[stream]) + client.register_connection(peer_id, connection) # type: ignore[arg-type] blocks = await client.request_blocks_by_range(peer_id, Slot(10), Uint64(4)) assert blocks == chain - assert conn.opened_protocols == [BLOCKS_BY_RANGE_PROTOCOL_V1] + assert connection.opened_protocols == [BLOCKS_BY_RANGE_PROTOCOL_V1] assert stream.closed is True assert stream.finish_write_called is True @@ -216,8 +216,8 @@ async def test_partial_response_when_stream_closes_early(self, peer_id: PeerId) chain = build_chain(start_slot=20, count=2) stream = MockRangeStream(response_chunks=[encode_success(b) for b in chain]) - conn = MockRangeConnection(peer_id=peer_id, streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection = MockRangeConnection(peer_id=peer_id, streams=[stream]) + client.register_connection(peer_id, connection) # type: ignore[arg-type] blocks = await client.request_blocks_by_range(peer_id, Slot(20), Uint64(5)) @@ -237,8 +237,8 @@ async def test_resource_unavailable_chunks_are_skipped(self, peer_id: PeerId) -> encode_success(chain[1]), ], ) - conn = MockRangeConnection(peer_id=peer_id, streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection = MockRangeConnection(peer_id=peer_id, streams=[stream]) + client.register_connection(peer_id, connection) # type: ignore[arg-type] blocks = await client.request_blocks_by_range(peer_id, Slot(30), Uint64(4)) @@ -253,8 +253,8 @@ async def test_non_monotonic_slots_raise_codec_error(self, peer_id: PeerId) -> N duplicate = empty_signed_block(Slot(40), hash_tree_root(first.block), state_seed=2) stream = MockRangeStream(response_chunks=[encode_success(first), encode_success(duplicate)]) - conn = MockRangeConnection(peer_id=peer_id, streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection = MockRangeConnection(peer_id=peer_id, streams=[stream]) + client.register_connection(peer_id, connection) # type: ignore[arg-type] with pytest.raises(CodecError, match=r"Non-monotonic slot"): await client.request_blocks_by_range(peer_id, Slot(40), Uint64(2)) @@ -266,8 +266,8 @@ async def test_out_of_range_slot_raises_codec_error(self, peer_id: PeerId) -> No out_of_range = empty_signed_block(Slot(60), Bytes32(b"\xaa" * 32), state_seed=1) stream = MockRangeStream(response_chunks=[encode_success(out_of_range)]) - conn = MockRangeConnection(peer_id=peer_id, streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection = MockRangeConnection(peer_id=peer_id, streams=[stream]) + client.register_connection(peer_id, connection) # type: ignore[arg-type] with pytest.raises(CodecError, match=r"outside requested range"): await client.request_blocks_by_range(peer_id, Slot(50), Uint64(3)) @@ -289,8 +289,8 @@ async def test_parent_root_continuity_violation_across_skipped_slot( bad = empty_signed_block(Slot(73), Bytes32.zero(), state_seed=2) stream = MockRangeStream(response_chunks=[encode_success(first), encode_success(bad)]) - conn = MockRangeConnection(peer_id=peer_id, streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection = MockRangeConnection(peer_id=peer_id, streams=[stream]) + client.register_connection(peer_id, connection) # type: ignore[arg-type] with pytest.raises(CodecError, match=r"Parent root mismatch"): await client.request_blocks_by_range(peer_id, Slot(70), Uint64(5)) @@ -304,8 +304,8 @@ async def test_parent_root_continuity_holds_across_skipped_slots(self, peer_id: second = empty_signed_block(Slot(85), parent_root=hash_tree_root(first.block), state_seed=2) stream = MockRangeStream(response_chunks=[encode_success(first), encode_success(second)]) - conn = MockRangeConnection(peer_id=peer_id, streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection = MockRangeConnection(peer_id=peer_id, streams=[stream]) + client.register_connection(peer_id, connection) # type: ignore[arg-type] blocks = await client.request_blocks_by_range(peer_id, Slot(80), Uint64(10)) @@ -327,8 +327,8 @@ async def test_more_than_count_chunks_raises_codec_error(self, peer_id: PeerId) encode_success(extra), ], ) - conn = MockRangeConnection(peer_id=peer_id, streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection = MockRangeConnection(peer_id=peer_id, streams=[stream]) + client.register_connection(peer_id, connection) # type: ignore[arg-type] with pytest.raises(CodecError, match=r"more than count"): await client.request_blocks_by_range(peer_id, Slot(100), Uint64(2)) @@ -337,14 +337,14 @@ async def test_timeout_returns_empty_list(self, peer_id: PeerId) -> None: """A request that times out returns an empty list rather than raising.""" client = make_client() client.timeout = 0.01 - conn = MockRangeConnection(peer_id=peer_id, streams=[MockRangeStream()]) + connection = MockRangeConnection(peer_id=peer_id, streams=[MockRangeStream()]) async def slow_read() -> bytes: await asyncio.sleep(1.0) return b"" - conn.streams[0].read = slow_read # type: ignore[method-assign] - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection.streams[0].read = slow_read # type: ignore[method-assign] + client.register_connection(peer_id, connection) # type: ignore[arg-type] blocks = await client.request_blocks_by_range(peer_id, Slot(0), Uint64(3)) @@ -357,8 +357,8 @@ async def test_server_error_stops_reading_and_returns_partial(self, peer_id: Pee error_chunk = ResponseCode.SERVER_ERROR.encode(b"db boom") stream = MockRangeStream(response_chunks=[encode_success(chain[0]), error_chunk]) - conn = MockRangeConnection(peer_id=peer_id, streams=[stream]) - client.register_connection(peer_id, conn) # type: ignore[arg-type] + connection = MockRangeConnection(peer_id=peer_id, streams=[stream]) + client.register_connection(peer_id, connection) # type: ignore[arg-type] blocks = await client.request_blocks_by_range(peer_id, Slot(200), Uint64(3)) diff --git a/tests/lean_spec/node/networking/enr/test_enr.py b/tests/lean_spec/node/networking/enr/test_enr.py index 1140fb23c..548f7566e 100644 --- a/tests/lean_spec/node/networking/enr/test_enr.py +++ b/tests/lean_spec/node/networking/enr/test_enr.py @@ -46,7 +46,7 @@ OFFICIAL_IPV4 = "127.0.0.1" OFFICIAL_UDP_PORT = Port(30303) OFFICIAL_IDENTITY_SCHEME = "v4" -OFFICIAL_SECP256K1_PUBKEY = bytes.fromhex( +OFFICIAL_SECP256K1_PUBLIC_KEY = bytes.fromhex( "03ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138" ) OFFICIAL_SIGNATURE = bytes.fromhex( @@ -88,7 +88,7 @@ def test_official_enr_public_key(self) -> None: enr = ENR.from_string(OFFICIAL_ENR_STRING) assert enr.public_key is not None assert len(enr.public_key) == 33 - assert bytes(enr.public_key) == OFFICIAL_SECP256K1_PUBKEY + assert bytes(enr.public_key) == OFFICIAL_SECP256K1_PUBLIC_KEY def test_official_enr_signature_length(self) -> None: """Official ENR has 64-byte signature.""" @@ -113,7 +113,7 @@ def test_official_enr_has_quic_multiaddr(self) -> None: assert multiaddr == f"/ip4/{OFFICIAL_IPV4}/udp/{OFFICIAL_UDP_PORT}/quic-v1" def test_official_enr_node_id(self) -> None: - """Official ENR node ID matches keccak256(uncompressed_pubkey). + """Official ENR node ID matches keccak256(uncompressed_public_key). Per EIP-778 "v4" identity scheme: "To derive a node address, take the keccak256 hash of the @@ -125,12 +125,14 @@ def test_official_enr_node_id(self) -> None: enr = ENR.from_string(OFFICIAL_ENR_STRING) # Get the compressed 33-byte secp256k1 public key - compressed_pubkey = enr.public_key - assert compressed_pubkey is not None - assert bytes(compressed_pubkey) == OFFICIAL_SECP256K1_PUBKEY + compressed_public_key = enr.public_key + assert compressed_public_key is not None + assert bytes(compressed_public_key) == OFFICIAL_SECP256K1_PUBLIC_KEY # Uncompress to 65 bytes (0x04 || x || y) - public_key = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256K1(), compressed_pubkey) + public_key = ec.EllipticCurvePublicKey.from_encoded_point( + ec.SECP256K1(), compressed_public_key + ) uncompressed = public_key.public_bytes( encoding=serialization.Encoding.X962, format=serialization.PublicFormat.UncompressedPoint, @@ -231,7 +233,7 @@ def test_malformed_rlp_rejected(self) -> None: def test_valid_minimal_enr(self) -> None: """Minimal valid ENR with only required fields parses.""" - # [signature(64), seq(1), "id", "v4", "secp256k1", pubkey(33)] + # [signature(64), seq(1), "id", "v4", "secp256k1", public_key(33)] rlp_data = encode_rlp( [ b"\x00" * 64, # signature @@ -239,7 +241,7 @@ def test_valid_minimal_enr(self) -> None: b"id", b"v4", b"secp256k1", - b"\x02" + b"\x00" * 32, # compressed pubkey + b"\x02" + b"\x00" * 32, # compressed public_key ] ) b64_content = base64.urlsafe_b64encode(rlp_data).decode("utf-8").rstrip("=") @@ -413,7 +415,7 @@ def test_is_valid_returns_false_for_missing_identity_scheme(self) -> None: ) assert not enr.is_valid() - def test_is_valid_returns_false_for_wrong_pubkey_length(self) -> None: + def test_is_valid_returns_false_for_wrong_public_key_length(self) -> None: """is_valid() returns False for public key != 33 bytes.""" # 32 bytes (uncompressed prefix missing) enr = ENR( @@ -704,7 +706,7 @@ def test_enr_exactly_300_bytes_succeeds(self) -> None: # The key "z" + value needs to fit in remaining space # Account for RLP overhead (key length byte + value length bytes) if padding_needed > 3: - value_len = padding_needed - 3 # Approximate, may need adjustment + value_length = padding_needed - 3 # Approximate, may need adjustment padded = encode_rlp( [ signature, @@ -714,12 +716,12 @@ def test_enr_exactly_300_bytes_succeeds(self) -> None: b"secp256k1", b"\x02" + b"\x00" * 32, b"zz", - b"\x00" * value_len, + b"\x00" * value_length, ] ) # Adjust if needed while len(padded) < 300: - value_len += 1 + value_length += 1 padded = encode_rlp( [ signature, @@ -729,11 +731,11 @@ def test_enr_exactly_300_bytes_succeeds(self) -> None: b"secp256k1", b"\x02" + b"\x00" * 32, b"zz", - b"\x00" * value_len, + b"\x00" * value_length, ] ) while len(padded) > 300: - value_len -= 1 + value_length -= 1 padded = encode_rlp( [ signature, @@ -743,7 +745,7 @@ def test_enr_exactly_300_bytes_succeeds(self) -> None: b"secp256k1", b"\x02" + b"\x00" * 32, b"zz", - b"\x00" * value_len, + b"\x00" * value_length, ] ) @@ -762,7 +764,7 @@ def test_enr_301_bytes_rejected(self) -> None: padding_needed = 301 - len(basic) if padding_needed > 3: - value_len = padding_needed - 3 + value_length = padding_needed - 3 padded = encode_rlp( [ signature, @@ -772,11 +774,11 @@ def test_enr_301_bytes_rejected(self) -> None: b"secp256k1", b"\x02" + b"\x00" * 32, b"zz", - b"\x00" * value_len, + b"\x00" * value_length, ] ) while len(padded) < 301: - value_len += 1 + value_length += 1 padded = encode_rlp( [ signature, @@ -786,11 +788,11 @@ def test_enr_301_bytes_rejected(self) -> None: b"secp256k1", b"\x02" + b"\x00" * 32, b"zz", - b"\x00" * value_len, + b"\x00" * value_length, ] ) while len(padded) > 301: - value_len -= 1 + value_length -= 1 padded = encode_rlp( [ signature, @@ -800,7 +802,7 @@ def test_enr_301_bytes_rejected(self) -> None: b"secp256k1", b"\x02" + b"\x00" * 32, b"zz", - b"\x00" * value_len, + b"\x00" * value_length, ] ) @@ -938,7 +940,7 @@ def test_self_signed_enr_verifies(self) -> None: # Generate a test keypair using cryptography library. private_key = ec.generate_private_key(ec.SECP256K1()) public_key = private_key.public_key() - compressed_pubkey = public_key.public_bytes( + compressed_public_key = public_key.public_bytes( encoding=serialization.Encoding.X962, format=serialization.PublicFormat.CompressedPoint, ) @@ -949,7 +951,7 @@ def test_self_signed_enr_verifies(self) -> None: b"id", b"v4", b"secp256k1", - compressed_pubkey, + compressed_public_key, ] content_rlp = encode_rlp(content_items) @@ -963,13 +965,13 @@ def test_self_signed_enr_verifies(self) -> None: # Convert DER signature to r || s format. r, s = decode_dss_signature(signature_der) - sig_64 = r.to_bytes(32, "big") + s.to_bytes(32, "big") + signature_64 = r.to_bytes(32, "big") + s.to_bytes(32, "big") # Create ENR. enr = ENR( - signature=Bytes64(sig_64), + signature=Bytes64(signature_64), seq=SeqNumber(1), - pairs={keys.ID: b"v4", keys.SECP256K1: compressed_pubkey}, + pairs={keys.ID: b"v4", keys.SECP256K1: compressed_public_key}, ) assert enr.verify_signature() @@ -979,9 +981,9 @@ def test_tampered_signature_fails_verification(self) -> None: enr = ENR.from_string(OFFICIAL_ENR_STRING) # Tamper with signature - tampered_sig = bytes([enr.signature[0] ^ 0xFF]) + bytes(enr.signature[1:]) + tampered_signature = bytes([enr.signature[0] ^ 0xFF]) + bytes(enr.signature[1:]) tampered_enr = ENR( - signature=Bytes64(tampered_sig), + signature=Bytes64(tampered_signature), seq=enr.seq, pairs=enr.pairs, ) diff --git a/tests/lean_spec/node/networking/enr/test_rlp.py b/tests/lean_spec/node/networking/enr/test_rlp.py index 15a10033a..2d0a0f413 100644 --- a/tests/lean_spec/node/networking/enr/test_rlp.py +++ b/tests/lean_spec/node/networking/enr/test_rlp.py @@ -27,7 +27,7 @@ A short string's prefix is this base plus its length, so the range runs 0x80 through 0xB7. """ -SHORT_STRING_MAX_LEN = 55 +SHORT_STRING_MAX_LENGTH = 55 """Largest payload length that fits the short string range. Anything larger switches to the long form, where the length itself follows the prefix. @@ -45,7 +45,7 @@ A short list's prefix is this base plus its payload length, so the range runs 0xC0 through 0xF7. """ -SHORT_LIST_MAX_LEN = 55 +SHORT_LIST_MAX_LENGTH = 55 """Largest payload length that fits the short list range. Anything larger switches to the long form, where the length itself follows the prefix. @@ -85,10 +85,10 @@ def test_encode_byte_0x7f(self) -> None: result = encode_rlp(b"\x7f") assert result == bytes.fromhex("7f") - @pytest.mark.parametrize("byte_val", range(0x00, SINGLE_BYTE_MAX + 1)) - def test_encode_all_single_byte_values(self, byte_val: int) -> None: + @pytest.mark.parametrize("byte_value", range(0x00, SINGLE_BYTE_MAX + 1)) + def test_encode_all_single_byte_values(self, byte_value: int) -> None: """All single-byte values 0x00-0x7f encode as themselves.""" - data = bytes([byte_val]) + data = bytes([byte_value]) result = encode_rlp(data) assert result == data @@ -104,7 +104,7 @@ def test_encode_short_string_dog(self) -> None: def test_encode_short_string_55_bytes(self) -> None: """55-byte string uses short string encoding (max for this category).""" data = b"Lorem ipsum dolor sit amet, consectetur adipisicing eli" - assert len(data) == SHORT_STRING_MAX_LEN + assert len(data) == SHORT_STRING_MAX_LENGTH result = encode_rlp(data) expected = bytes.fromhex( "b74c6f72656d20697073756d20646f6c6f722073697420616d65742c20" @@ -117,7 +117,7 @@ def test_encode_single_byte_above_0x7f(self) -> None: result = encode_rlp(b"\x80") assert result == bytes([SHORT_STRING_PREFIX + 1, 0x80]) - @pytest.mark.parametrize("length", [1, 10, 20, 30, 40, 50, SHORT_STRING_MAX_LEN]) + @pytest.mark.parametrize("length", [1, 10, 20, 30, 40, 50, SHORT_STRING_MAX_LENGTH]) def test_encode_short_string_various_lengths(self, length: int) -> None: """Short strings of various lengths are prefixed with 0x80 + length.""" # Use bytes above 0x7f to ensure short string encoding is used @@ -133,7 +133,7 @@ class TestEncodeLongString: def test_encode_long_string_56_bytes(self) -> None: """56-byte string uses long string encoding.""" data = b"Lorem ipsum dolor sit amet, consectetur adipisicing elit" - assert len(data) == SHORT_STRING_MAX_LEN + 1 + assert len(data) == SHORT_STRING_MAX_LENGTH + 1 result = encode_rlp(data) expected = bytes.fromhex( "b8384c6f72656d20697073756d20646f6c6f722073697420616d65742c20" @@ -155,7 +155,7 @@ def test_encode_long_string_1024_bytes(self) -> None: def test_encode_long_string_boundary(self) -> None: """String at exact boundary (56 bytes) uses long encoding.""" - data = b"a" * (SHORT_STRING_MAX_LEN + 1) + data = b"a" * (SHORT_STRING_MAX_LENGTH + 1) result = encode_rlp(data) # Prefix 0xb8 = 0xb7 + 1 (1 byte for length) assert result[0] == LONG_STRING_PREFIX @@ -191,9 +191,9 @@ def test_encode_short_list_max_payload(self) -> None: """Short list with 55 bytes of payload uses short list encoding.""" # Create a list that has exactly 55 bytes of payload # Each "a" encodes as 0x61 (single byte), so 55 "a"s = 55 bytes payload - items: list[RLPItem] = [b"a" for _ in range(SHORT_LIST_MAX_LEN)] + items: list[RLPItem] = [b"a" for _ in range(SHORT_LIST_MAX_LENGTH)] result = encode_rlp(items) - assert result[0] == SHORT_LIST_PREFIX + SHORT_LIST_MAX_LEN # 0xf7 + assert result[0] == SHORT_LIST_PREFIX + SHORT_LIST_MAX_LENGTH # 0xf7 class TestEncodeLongList: @@ -342,10 +342,10 @@ def test_decode_byte_0x7f(self) -> None: result = decode_rlp(bytes.fromhex("7f")) assert result == b"\x7f" - @pytest.mark.parametrize("byte_val", range(0x00, SINGLE_BYTE_MAX + 1)) - def test_decode_all_single_byte_values(self, byte_val: int) -> None: + @pytest.mark.parametrize("byte_value", range(0x00, SINGLE_BYTE_MAX + 1)) + def test_decode_all_single_byte_values(self, byte_value: int) -> None: """All single-byte values 0x00-0x7f decode correctly.""" - data = bytes([byte_val]) + data = bytes([byte_value]) result = decode_rlp(data) assert result == data @@ -558,8 +558,8 @@ class TestEncodeDecodeRoundtrip: b"\x7f", b"\x80", b"dog", - b"a" * SHORT_STRING_MAX_LEN, - b"a" * (SHORT_STRING_MAX_LEN + 1), + b"a" * SHORT_STRING_MAX_LENGTH, + b"a" * (SHORT_STRING_MAX_LENGTH + 1), b"a" * 256, [], [b""], @@ -710,24 +710,24 @@ def test_single_byte_max_boundary(self) -> None: def test_short_string_max_boundary(self) -> None: """Verify short-string boundary (55 vs 56 bytes).""" # 55 bytes = short string encoding (prefix 0xb7) - data_55 = b"a" * SHORT_STRING_MAX_LEN + data_55 = b"a" * SHORT_STRING_MAX_LENGTH encoded_55 = encode_rlp(data_55) - assert encoded_55[0] == SHORT_STRING_PREFIX + SHORT_STRING_MAX_LEN # 0xb7 + assert encoded_55[0] == SHORT_STRING_PREFIX + SHORT_STRING_MAX_LENGTH # 0xb7 # 56 bytes = long string encoding (prefix 0xb8) - data_56 = b"a" * (SHORT_STRING_MAX_LEN + 1) + data_56 = b"a" * (SHORT_STRING_MAX_LENGTH + 1) encoded_56 = encode_rlp(data_56) assert encoded_56[0] == LONG_STRING_PREFIX # 0xb8 def test_short_list_max_boundary(self) -> None: """Verify short-list boundary (55 vs 56 bytes payload).""" # 55 bytes payload = short list encoding (prefix 0xf7) - items_55: list[RLPItem] = [b"a" for _ in range(SHORT_LIST_MAX_LEN)] + items_55: list[RLPItem] = [b"a" for _ in range(SHORT_LIST_MAX_LENGTH)] encoded_55 = encode_rlp(items_55) - assert encoded_55[0] == SHORT_LIST_PREFIX + SHORT_LIST_MAX_LEN # 0xf7 + assert encoded_55[0] == SHORT_LIST_PREFIX + SHORT_LIST_MAX_LENGTH # 0xf7 # 56 bytes payload = long list encoding (prefix 0xf8) - items_56: list[RLPItem] = [b"a" for _ in range(SHORT_LIST_MAX_LEN + 1)] + items_56: list[RLPItem] = [b"a" for _ in range(SHORT_LIST_MAX_LENGTH + 1)] encoded_56 = encode_rlp(items_56) assert encoded_56[0] == LONG_LIST_PREFIX # 0xf8 @@ -740,7 +740,7 @@ def test_prefix_boundaries(self) -> None: assert LONG_LIST_PREFIX == 0xF8 # Short string prefix range: 0x80-0xb7 (length 0-55) - assert SHORT_STRING_PREFIX + SHORT_STRING_MAX_LEN == 0xB7 + assert SHORT_STRING_PREFIX + SHORT_STRING_MAX_LENGTH == 0xB7 # Short list prefix range: 0xc0-0xf7 (length 0-55) - assert SHORT_LIST_PREFIX + SHORT_LIST_MAX_LEN == 0xF7 + assert SHORT_LIST_PREFIX + SHORT_LIST_MAX_LENGTH == 0xF7 diff --git a/tests/lean_spec/node/networking/gossipsub/integration/conftest.py b/tests/lean_spec/node/networking/gossipsub/integration/conftest.py index 55a0f3f31..cc01d6944 100644 --- a/tests/lean_spec/node/networking/gossipsub/integration/conftest.py +++ b/tests/lean_spec/node/networking/gossipsub/integration/conftest.py @@ -28,7 +28,7 @@ def fast_params(**overrides: int | float | str) -> GossipsubParameters: "d_lazy": 2, "heartbeat_interval_secs": 0.05, "fanout_ttl_secs": 5, - "mcache_len": 6, + "mcache_length": 6, "mcache_gossip": 3, "seen_ttl_secs": 120, } diff --git a/tests/lean_spec/node/networking/gossipsub/integration/node.py b/tests/lean_spec/node/networking/gossipsub/integration/node.py index d143efdc4..1f93593be 100644 --- a/tests/lean_spec/node/networking/gossipsub/integration/node.py +++ b/tests/lean_spec/node/networking/gossipsub/integration/node.py @@ -115,9 +115,9 @@ async def wait_for_message( while True: # Check already-collected messages before waiting. - for msg in self.received_messages: - if topic is None or msg.topic == topic: - return msg + for message in self.received_messages: + if topic is None or message.topic == topic: + return message remaining = deadline - asyncio.get_event_loop().time() if remaining <= 0: diff --git a/tests/lean_spec/node/networking/gossipsub/integration/test_connectivity.py b/tests/lean_spec/node/networking/gossipsub/integration/test_connectivity.py index ce51595d1..3e2c8e348 100644 --- a/tests/lean_spec/node/networking/gossipsub/integration/test_connectivity.py +++ b/tests/lean_spec/node/networking/gossipsub/integration/test_connectivity.py @@ -60,12 +60,12 @@ async def test_publish_delivers_to_subscriber( await asyncio.sleep(0.1) data = b"hello" - msg_id = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), data) + message_id = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), data) await a.publish(TOPIC, data) - msg = await b.wait_for_message(TOPIC, timeout=3.0) + message = await b.wait_for_message(TOPIC, timeout=3.0) - assert msg == GossipsubMessageEvent( - peer_id=msg.peer_id, topic=TOPIC, data=data, message_id=msg_id + assert message == GossipsubMessageEvent( + peer_id=message.peer_id, topic=TOPIC, data=data, message_id=message_id ) @@ -109,20 +109,20 @@ async def test_bidirectional_message_exchange( data_a = b"from-a" data_b = b"from-b" - msg_id_a = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), data_a) - msg_id_b = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), data_b) + message_id_a = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), data_a) + message_id_b = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), data_b) await a.publish(TOPIC, data_a) await b.publish(TOPIC, data_b) - msg_b = await b.wait_for_message(TOPIC, timeout=3.0) - msg_a = await a.wait_for_message(TOPIC, timeout=3.0) + message_b = await b.wait_for_message(TOPIC, timeout=3.0) + message_a = await a.wait_for_message(TOPIC, timeout=3.0) - assert msg_b == GossipsubMessageEvent( - peer_id=msg_b.peer_id, topic=TOPIC, data=data_a, message_id=msg_id_a + assert message_b == GossipsubMessageEvent( + peer_id=message_b.peer_id, topic=TOPIC, data=data_a, message_id=message_id_a ) - assert msg_a == GossipsubMessageEvent( - peer_id=msg_a.peer_id, topic=TOPIC, data=data_b, message_id=msg_id_b + assert message_a == GossipsubMessageEvent( + peer_id=message_a.peer_id, topic=TOPIC, data=data_b, message_id=message_id_b ) diff --git a/tests/lean_spec/node/networking/gossipsub/integration/test_heartbeat_integ.py b/tests/lean_spec/node/networking/gossipsub/integration/test_heartbeat_integ.py index b7ea374c8..33ad9b5bf 100644 --- a/tests/lean_spec/node/networking/gossipsub/integration/test_heartbeat_integ.py +++ b/tests/lean_spec/node/networking/gossipsub/integration/test_heartbeat_integ.py @@ -49,12 +49,12 @@ async def test_multiple_heartbeats_stabilize( async def test_cache_aging_evicts_messages( network: GossipsubTestNetwork, ) -> None: - """After mcache_len heartbeat shifts, cached messages are evicted.""" + """After mcache_length heartbeat shifts, cached messages are evicted.""" - # The message cache is a sliding window of mcache_len slots. + # The message cache is a sliding window of mcache_length slots. # Each heartbeat shifts the window forward by one slot. - # After mcache_len shifts, the oldest slot falls off the window. - params = fast_params(mcache_len=3) + # After mcache_length shifts, the oldest slot falls off the window. + params = fast_params(mcache_length=3) nodes = await network.create_nodes(2, params) await network.start_all() await nodes[0].connect_to(nodes[1]) @@ -65,15 +65,15 @@ async def test_cache_aging_evicts_messages( await publisher.publish(TOPIC, b"will-be-evicted") # Compute the message ID for lookup. - msg_id = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), b"will-be-evicted") - assert publisher.behavior.message_cache.has(msg_id) + message_id = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), b"will-be-evicted") + assert publisher.behavior.message_cache.has(message_id) - # Shift the cache mcache_len times. The message was in slot 0. + # Shift the cache mcache_length times. The message was in slot 0. # After 3 shifts, slot 0 falls off the window and the message is gone. - for _ in range(params.mcache_len): + for _ in range(params.mcache_length): publisher.behavior.message_cache.shift() - assert not publisher.behavior.message_cache.has(msg_id) + assert not publisher.behavior.message_cache.has(message_id) @pytest.mark.asyncio diff --git a/tests/lean_spec/node/networking/gossipsub/integration/test_idontwant.py b/tests/lean_spec/node/networking/gossipsub/integration/test_idontwant.py index 8b8c1aa1a..e87256011 100644 --- a/tests/lean_spec/node/networking/gossipsub/integration/test_idontwant.py +++ b/tests/lean_spec/node/networking/gossipsub/integration/test_idontwant.py @@ -50,13 +50,13 @@ async def test_large_message_triggers_idontwant( # Exceed the threshold so the receiver triggers IDONTWANT. large_data = b"x" * (IDONTWANT_SIZE_THRESHOLD + 1024) - msg_id = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), large_data) + message_id = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), large_data) await nodes[0].publish(TOPIC, large_data) for node in nodes[1:]: - msg = await node.wait_for_message(TOPIC, timeout=5.0) - assert msg == GossipsubMessageEvent( - peer_id=msg.peer_id, topic=TOPIC, data=large_data, message_id=msg_id + message = await node.wait_for_message(TOPIC, timeout=5.0) + assert message == GossipsubMessageEvent( + peer_id=message.peer_id, topic=TOPIC, data=large_data, message_id=message_id ) # Brief pause for IDONTWANT RPCs to propagate. @@ -127,13 +127,13 @@ async def test_idontwant_prevents_redundant_forward( await network.stabilize_mesh(TOPIC, rounds=5) large_data = b"y" * (IDONTWANT_SIZE_THRESHOLD + 512) - msg_id = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), large_data) + message_id = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), large_data) await nodes[0].publish(TOPIC, large_data) for node in nodes[1:]: - msg = await node.wait_for_message(TOPIC, timeout=5.0) - assert msg == GossipsubMessageEvent( - peer_id=msg.peer_id, topic=TOPIC, data=large_data, message_id=msg_id + message = await node.wait_for_message(TOPIC, timeout=5.0) + assert message == GossipsubMessageEvent( + peer_id=message.peer_id, topic=TOPIC, data=large_data, message_id=message_id ) # Verify each node received exactly once, not duplicated by redundant forwards. diff --git a/tests/lean_spec/node/networking/gossipsub/integration/test_propagation.py b/tests/lean_spec/node/networking/gossipsub/integration/test_propagation.py index cd31f39ee..0dff5835c 100644 --- a/tests/lean_spec/node/networking/gossipsub/integration/test_propagation.py +++ b/tests/lean_spec/node/networking/gossipsub/integration/test_propagation.py @@ -33,14 +33,14 @@ async def test_chain_propagation( await network.stabilize_mesh(TOPIC, rounds=5) data = b"chain-msg" - msg_id = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), data) + message_id = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), data) await network.nodes[0].publish(TOPIC, data) # All other nodes should receive the message. for node in network.nodes[1:]: - msg = await node.wait_for_message(TOPIC, timeout=5.0) - assert msg == GossipsubMessageEvent( - peer_id=msg.peer_id, topic=TOPIC, data=data, message_id=msg_id + message = await node.wait_for_message(TOPIC, timeout=5.0) + assert message == GossipsubMessageEvent( + peer_id=message.peer_id, topic=TOPIC, data=data, message_id=message_id ) @@ -58,13 +58,13 @@ async def test_full_mesh_all_receive( publisher = network.nodes[0] data = b"broadcast" - msg_id = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), data) + message_id = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), data) await publisher.publish(TOPIC, data) for node in network.nodes[1:]: - msg = await node.wait_for_message(TOPIC, timeout=5.0) - assert msg == GossipsubMessageEvent( - peer_id=msg.peer_id, topic=TOPIC, data=data, message_id=msg_id + message = await node.wait_for_message(TOPIC, timeout=5.0) + assert message == GossipsubMessageEvent( + peer_id=message.peer_id, topic=TOPIC, data=data, message_id=message_id ) # In a full mesh, multiple paths exist to each node. @@ -118,10 +118,10 @@ async def test_many_messages_all_delivered( await network.stabilize_mesh(TOPIC, rounds=3) publisher = network.nodes[0] - msg_count = 20 + message_count = 20 - payloads = [f"msg-{i}".encode() for i in range(msg_count)] - msg_ids = [GossipsubMessage.compute_id(TOPIC.encode("utf-8"), p) for p in payloads] + payloads = [f"msg-{i}".encode() for i in range(message_count)] + message_ids = [GossipsubMessage.compute_id(TOPIC.encode("utf-8"), p) for p in payloads] for payload in payloads: await publisher.publish(TOPIC, payload) @@ -131,10 +131,13 @@ async def test_many_messages_all_delivered( await asyncio.sleep(0.01) for node in network.nodes[1:]: - msgs = await node.wait_for_messages(msg_count, TOPIC, timeout=10.0) - assert msgs == [ + messages = await node.wait_for_messages(message_count, TOPIC, timeout=10.0) + assert messages == [ GossipsubMessageEvent( - peer_id=msgs[i].peer_id, topic=TOPIC, data=payloads[i], message_id=msg_ids[i] + peer_id=messages[i].peer_id, + topic=TOPIC, + data=payloads[i], + message_id=message_ids[i], ) - for i in range(msg_count) + for i in range(message_count) ] diff --git a/tests/lean_spec/node/networking/gossipsub/integration/test_stress.py b/tests/lean_spec/node/networking/gossipsub/integration/test_stress.py index 7bb7110f9..53773e3d6 100644 --- a/tests/lean_spec/node/networking/gossipsub/integration/test_stress.py +++ b/tests/lean_spec/node/networking/gossipsub/integration/test_stress.py @@ -63,21 +63,24 @@ async def test_concurrent_publish( # All 5 nodes publish at the same time. # This tests concurrent access to shared mesh state and message caches. payloads = [f"concurrent-{i}".encode() for i in range(5)] - all_msg_ids = {p: GossipsubMessage.compute_id(TOPIC.encode("utf-8"), p) for p in payloads} + all_message_ids = {p: GossipsubMessage.compute_id(TOPIC.encode("utf-8"), p) for p in payloads} await asyncio.gather(*(node.publish(TOPIC, payloads[i]) for i, node in enumerate(nodes))) # Each node publishes one message but does not deliver its own. # So each expects exactly the 4 messages from the other publishers. for i, node in enumerate(nodes): - msgs = await node.wait_for_messages(4, TOPIC, timeout=10.0) + messages = await node.wait_for_messages(4, TOPIC, timeout=10.0) expected_data = {payloads[j] for j in range(5) if j != i} - assert {msg.data for msg in msgs} == expected_data, ( - f"{node.peer_id}: expected {expected_data}, got {[m.data for m in msgs]}" + assert {message.data for message in messages} == expected_data, ( + f"{node.peer_id}: expected {expected_data}, got {[m.data for m in messages]}" ) - for msg in msgs: - assert msg == GossipsubMessageEvent( - peer_id=msg.peer_id, topic=TOPIC, data=msg.data, message_id=all_msg_ids[msg.data] + for message in messages: + assert message == GossipsubMessageEvent( + peer_id=message.peer_id, + topic=TOPIC, + data=message.data, + message_id=all_message_ids[message.data], ) @@ -99,12 +102,12 @@ async def test_large_network_ring( await network.stabilize_mesh(TOPIC, rounds=5) data = b"ring-message" - msg_id = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), data) + message_id = GossipsubMessage.compute_id(TOPIC.encode("utf-8"), data) await network.nodes[0].publish(TOPIC, data) # All other nodes should receive the message. for node in network.nodes[1:]: - msg = await node.wait_for_message(TOPIC, timeout=10.0) - assert msg == GossipsubMessageEvent( - peer_id=msg.peer_id, topic=TOPIC, data=data, message_id=msg_id + message = await node.wait_for_message(TOPIC, timeout=10.0) + assert message == GossipsubMessageEvent( + peer_id=message.peer_id, topic=TOPIC, data=data, message_id=message_id ) diff --git a/tests/lean_spec/node/networking/gossipsub/test_cache_edge_cases.py b/tests/lean_spec/node/networking/gossipsub/test_cache_edge_cases.py index e638ca1e4..c38914f05 100644 --- a/tests/lean_spec/node/networking/gossipsub/test_cache_edge_cases.py +++ b/tests/lean_spec/node/networking/gossipsub/test_cache_edge_cases.py @@ -13,22 +13,22 @@ class TestMessageCacheShift: """Tests for MessageCache.shift() edge cases.""" def test_shift_when_not_full(self) -> None: - """shift() does not evict when fewer windows than mcache_len.""" - cache = MessageCache(mcache_len=6, mcache_gossip=3) - msg = GossipsubMessage(topic=b"t", raw_data=b"data1") - cache.put(TopicId("t"), msg) + """shift() does not evict when fewer windows than mcache_length.""" + cache = MessageCache(mcache_length=6, mcache_gossip=3) + message = GossipsubMessage(topic=b"t", raw_data=b"data1") + cache.put(TopicId("t"), message) # Only 1 window used out of 6; shift should evict nothing. evicted = cache.shift() assert evicted == 0 - assert cache.has(msg.id) + assert cache.has(message.id) def test_shift_evicts_oldest_window(self) -> None: """shift() evicts messages from the oldest window at capacity.""" - cache = MessageCache(mcache_len=3, mcache_gossip=2) + cache = MessageCache(mcache_length=3, mcache_gossip=2) - msg = GossipsubMessage(topic=b"t", raw_data=b"old") - cache.put(TopicId("t"), msg) + message = GossipsubMessage(topic=b"t", raw_data=b"old") + cache.put(TopicId("t"), message) # Fill to capacity (3 windows total: initial + 2 shifts). cache.shift() @@ -37,26 +37,26 @@ def test_shift_evicts_oldest_window(self) -> None: # Next shift should evict the original window. evicted = cache.shift() assert evicted == 1 - assert not cache.has(msg.id) + assert not cache.has(message.id) def test_shift_returns_correct_eviction_count(self) -> None: """shift() returns the number of messages evicted.""" - cache = MessageCache(mcache_len=2, mcache_gossip=1) + cache = MessageCache(mcache_length=2, mcache_gossip=1) # Put 3 messages in the first window. - msgs = [GossipsubMessage(topic=b"t", raw_data=f"d{i}".encode()) for i in range(3)] - for m in msgs: + messages = [GossipsubMessage(topic=b"t", raw_data=f"d{i}".encode()) for i in range(3)] + for m in messages: cache.put(TopicId("t"), m) # One shift: still within capacity (2 windows). evicted = cache.shift() assert evicted == 0 - assert all(cache.has(m.id) for m in msgs) + assert all(cache.has(m.id) for m in messages) - # Second shift: oldest window (with 3 msgs) is evicted. + # Second shift: oldest window (with 3 messages) is evicted. evicted = cache.shift() assert evicted == 3 - assert not any(cache.has(m.id) for m in msgs) + assert not any(cache.has(m.id) for m in messages) class TestMessageCacheGetGossipIds: @@ -64,27 +64,27 @@ class TestMessageCacheGetGossipIds: def test_returns_only_recent_windows(self) -> None: """get_gossip_ids() only returns IDs from mcache_gossip windows.""" - cache = MessageCache(mcache_len=4, mcache_gossip=2) + cache = MessageCache(mcache_length=4, mcache_gossip=2) - # Window 0: put msg_old. - msg_old = GossipsubMessage(topic=b"t", raw_data=b"old") - cache.put(TopicId("t"), msg_old) + # Window 0: put message_old. + message_old = GossipsubMessage(topic=b"t", raw_data=b"old") + cache.put(TopicId("t"), message_old) - # Shift twice: msg_old is now in window 2 (outside gossip=2). + # Shift twice: message_old is now in window 2 (outside gossip=2). cache.shift() cache.shift() - # Window 0 (current): put msg_new. - msg_new = GossipsubMessage(topic=b"t", raw_data=b"new") - cache.put(TopicId("t"), msg_new) + # Window 0 (current): put message_new. + message_new = GossipsubMessage(topic=b"t", raw_data=b"new") + cache.put(TopicId("t"), message_new) ids = cache.get_gossip_ids(TopicId("t")) - assert msg_new.id in ids - assert msg_old.id not in ids + assert message_new.id in ids + assert message_old.id not in ids def test_filters_by_topic(self) -> None: """get_gossip_ids() only returns IDs for the requested topic.""" - cache = MessageCache(mcache_len=6, mcache_gossip=3) + cache = MessageCache(mcache_length=6, mcache_gossip=3) msg1 = GossipsubMessage(topic=b"topic1", raw_data=b"data1") msg2 = GossipsubMessage(topic=b"topic2", raw_data=b"data2") @@ -103,19 +103,19 @@ def test_filters_by_topic(self) -> None: def test_iwant_after_gossip_window(self) -> None: """Messages outside gossip window are still retrievable via get().""" - cache = MessageCache(mcache_len=4, mcache_gossip=1) + cache = MessageCache(mcache_length=4, mcache_gossip=1) - msg = GossipsubMessage(topic=b"t", raw_data=b"data") - cache.put(TopicId("t"), msg) + message = GossipsubMessage(topic=b"t", raw_data=b"data") + cache.put(TopicId("t"), message) # Shift past the gossip window but still within cache. cache.shift() cache.shift() # Not in gossip IDs anymore. - assert msg.id not in cache.get_gossip_ids(TopicId("t")) + assert message.id not in cache.get_gossip_ids(TopicId("t")) # But still retrievable via IWANT. - assert cache.get(msg.id) is not None + assert cache.get(message.id) is not None class TestMessageCachePutAndGet: @@ -124,9 +124,9 @@ class TestMessageCachePutAndGet: def test_get_retrieves_cached_message(self) -> None: """get() retrieves a message by ID after put().""" cache = MessageCache() - msg = GossipsubMessage(topic=b"t", raw_data=b"data") - cache.put(TopicId("t"), msg) - assert cache.get(msg.id) == msg + message = GossipsubMessage(topic=b"t", raw_data=b"data") + cache.put(TopicId("t"), message) + assert cache.get(message.id) == message def test_get_returns_none_for_unknown(self) -> None: """get() returns None for an unknown message ID.""" @@ -136,19 +136,19 @@ def test_get_returns_none_for_unknown(self) -> None: def test_put_duplicate_returns_false(self) -> None: """Putting the same message twice returns False on second call.""" cache = MessageCache() - msg = GossipsubMessage(topic=b"t", raw_data=b"data") + message = GossipsubMessage(topic=b"t", raw_data=b"data") - assert cache.put(TopicId("t"), msg) is True - assert cache.put(TopicId("t"), msg) is False - assert cache.has(msg.id) + assert cache.put(TopicId("t"), message) is True + assert cache.put(TopicId("t"), message) is False + assert cache.has(message.id) def test_has_method(self) -> None: """The has() method works for message IDs.""" cache = MessageCache() - msg = GossipsubMessage(topic=b"t", raw_data=b"data") - cache.put(TopicId("t"), msg) + message = GossipsubMessage(topic=b"t", raw_data=b"data") + cache.put(TopicId("t"), message) - assert cache.has(msg.id) + assert cache.has(message.id) assert not cache.has(MessageId(b"\x00" * 20)) @@ -158,15 +158,15 @@ class TestSeenCache: def test_add_returns_true_for_new(self) -> None: """add() returns True for a new message ID.""" seen = SeenCache(ttl_seconds=120) - msg_id = MessageId(b"12345678901234567890") - assert seen.add(msg_id, Timestamp(time.time())) is True + message_id = MessageId(b"12345678901234567890") + assert seen.add(message_id, Timestamp(time.time())) is True def test_add_returns_false_for_duplicate(self) -> None: """add() returns False for an already-seen message ID.""" seen = SeenCache(ttl_seconds=120) - msg_id = MessageId(b"12345678901234567890") - seen.add(msg_id, Timestamp(time.time())) - assert seen.add(msg_id, Timestamp(time.time())) is False + message_id = MessageId(b"12345678901234567890") + seen.add(message_id, Timestamp(time.time())) + assert seen.add(message_id, Timestamp(time.time())) is False def test_cleanup_removes_expired(self) -> None: """cleanup() removes entries past TTL.""" @@ -187,20 +187,20 @@ def test_cleanup_no_expired(self) -> None: """cleanup() with no expired entries removes nothing.""" seen = SeenCache(ttl_seconds=120) now = time.time() - msg_id = MessageId(b"12345678901234567890") - seen.add(msg_id, Timestamp(now)) + message_id = MessageId(b"12345678901234567890") + seen.add(message_id, Timestamp(now)) removed = seen.cleanup(now) assert removed == 0 - assert seen.has(msg_id) + assert seen.has(message_id) def test_has_method(self) -> None: """The has() method works for seen message IDs.""" seen = SeenCache() - msg_id = MessageId(b"12345678901234567890") - seen.add(msg_id, Timestamp(time.time())) + message_id = MessageId(b"12345678901234567890") + seen.add(message_id, Timestamp(time.time())) - assert seen.has(msg_id) + assert seen.has(message_id) assert not seen.has(MessageId(b"\x00" * 20)) @@ -227,15 +227,15 @@ def test_id_differs_with_different_topic(self) -> None: def test_id_is_20_bytes(self) -> None: """Message ID is exactly 20 bytes.""" - msg = GossipsubMessage(topic=b"t", raw_data=b"d") - assert len(msg.id) == 20 - assert isinstance(msg.id, MessageId) + message = GossipsubMessage(topic=b"t", raw_data=b"d") + assert len(message.id) == 20 + assert isinstance(message.id, MessageId) def test_id_is_cached(self) -> None: """The ID is computed once and the same object is returned on subsequent accesses.""" - msg = GossipsubMessage(topic=b"t", raw_data=b"d") - first_id = msg.id - second_id = msg.id + message = GossipsubMessage(topic=b"t", raw_data=b"d") + first_id = message.id + second_id = message.id assert first_id is second_id diff --git a/tests/lean_spec/node/networking/gossipsub/test_gossipsub.py b/tests/lean_spec/node/networking/gossipsub/test_gossipsub.py index ba66b8a3e..025d518af 100644 --- a/tests/lean_spec/node/networking/gossipsub/test_gossipsub.py +++ b/tests/lean_spec/node/networking/gossipsub/test_gossipsub.py @@ -46,13 +46,13 @@ def test_default_parameters(self) -> None: assert params.d_lazy == 6 assert params.heartbeat_interval_secs == 0.7 assert params.fanout_ttl_secs == 60 - assert params.mcache_len == 6 + assert params.mcache_length == 6 assert params.mcache_gossip == 3 # Test relationships assert params.d_low < params.d < params.d_high assert params.d_lazy <= params.d - assert params.mcache_gossip <= params.mcache_len + assert params.mcache_gossip <= params.mcache_length class TestControlMessages: @@ -380,7 +380,7 @@ def test_subopts_encode_decode(self) -> None: def test_message_encode_decode(self) -> None: """Test Message encoding/decoding.""" - msg = Message( + message = Message( from_peer=b"peer123", data=b"hello world", seqno=b"\x00\x01\x02\x03\x04\x05\x06\x07", @@ -388,12 +388,12 @@ def test_message_encode_decode(self) -> None: signature=b"sig" * 16, key=b"pubkey", ) - assert Message.decode(msg.encode()) == msg + assert Message.decode(message.encode()) == message def test_message_minimal(self) -> None: """Test Message with only required fields.""" - msg = Message(topic=TopicId("/test/topic"), data=b"payload") - assert Message.decode(msg.encode()) == msg + message = Message(topic=TopicId("/test/topic"), data=b"payload") + assert Message.decode(message.encode()) == message def test_control_graft_encode_decode(self) -> None: """Test ControlGraft encoding/decoding.""" diff --git a/tests/lean_spec/node/networking/gossipsub/test_handlers.py b/tests/lean_spec/node/networking/gossipsub/test_handlers.py index cd992c051..4dfc81143 100644 --- a/tests/lean_spec/node/networking/gossipsub/test_handlers.py +++ b/tests/lean_spec/node/networking/gossipsub/test_handlers.py @@ -203,13 +203,13 @@ async def test_ihave_sends_iwant_for_unseen(self) -> None: """IHAVE for unseen messages triggers IWANT.""" behavior, capture = make_behavior() peer_id = add_peer(behavior, "peer1") - msg_id = b"12345678901234567890" + message_id = b"12345678901234567890" - ihave = ControlIHave(topic_id=TopicId("topic"), message_ids=[msg_id]) + ihave = ControlIHave(topic_id=TopicId("topic"), message_ids=[message_id]) await behavior._handle_ihave(peer_id, ihave) assert capture.sent == [ - (peer_id, RPC(control=ControlMessage(iwant=[ControlIWant(message_ids=[msg_id])]))) + (peer_id, RPC(control=ControlMessage(iwant=[ControlIWant(message_ids=[message_id])]))) ] @pytest.mark.asyncio @@ -217,12 +217,12 @@ async def test_ihave_ignores_seen(self) -> None: """IHAVE for already-seen messages does not trigger IWANT.""" behavior, capture = make_behavior() peer_id = add_peer(behavior, "peer1") - msg_id = MessageId(b"12345678901234567890") + message_id = MessageId(b"12345678901234567890") # Mark as seen - behavior.seen_cache.add(msg_id, Timestamp(time.time())) + behavior.seen_cache.add(message_id, Timestamp(time.time())) - ihave = ControlIHave(topic_id=TopicId("topic"), message_ids=[bytes(msg_id)]) + ihave = ControlIHave(topic_id=TopicId("topic"), message_ids=[bytes(message_id)]) await behavior._handle_ihave(peer_id, ihave) assert capture.sent == [] @@ -272,10 +272,10 @@ async def test_iwant_responds_with_cached(self) -> None: peer_id = add_peer(behavior, "peer1") # Put a message in cache - msg = GossipsubMessage(topic=b"topic", raw_data=b"payload") - behavior.message_cache.put(TopicId("topic"), msg) + message = GossipsubMessage(topic=b"topic", raw_data=b"payload") + behavior.message_cache.put(TopicId("topic"), message) - iwant = ControlIWant(message_ids=[bytes(msg.id)]) + iwant = ControlIWant(message_ids=[bytes(message.id)]) await behavior._handle_iwant(peer_id, iwant) assert capture.sent == [ @@ -364,10 +364,10 @@ async def test_new_message_forwarded_excluding_sender(self) -> None: behavior.mesh.add_to_mesh(topic, sender) behavior.mesh.add_to_mesh(topic, mesh_rx) - msg = Message(topic=topic, data=b"hello") - await behavior._handle_message(sender, msg) + message = Message(topic=topic, data=b"hello") + await behavior._handle_message(sender, message) - assert capture.sent == [(mesh_rx, RPC(publish=[msg]))] + assert capture.sent == [(mesh_rx, RPC(publish=[message]))] @pytest.mark.asyncio async def test_duplicate_message_ignored(self) -> None: @@ -377,13 +377,13 @@ async def test_duplicate_message_ignored(self) -> None: behavior.subscribe(topic) peer_id = add_peer(behavior, "peer1", {topic}) - msg = Message(topic=topic, data=b"hello") + message = Message(topic=topic, data=b"hello") - await behavior._handle_message(peer_id, msg) + await behavior._handle_message(peer_id, message) assert capture.sent == [] # Second time should be ignored - await behavior._handle_message(peer_id, msg) + await behavior._handle_message(peer_id, message) assert capture.sent == [] @pytest.mark.asyncio @@ -392,8 +392,8 @@ async def test_message_event_emitted(self) -> None: behavior, _ = make_behavior() peer_id = add_peer(behavior, "peer1") - msg = Message(topic=TopicId("topic"), data=b"payload") - await behavior._handle_message(peer_id, msg) + message = Message(topic=TopicId("topic"), data=b"payload") + await behavior._handle_message(peer_id, message) assert behavior._event_queue.get_nowait() == GossipsubMessageEvent( peer_id=peer_id, @@ -408,8 +408,8 @@ async def test_empty_topic_ignored(self) -> None: behavior, capture = make_behavior() peer_id = add_peer(behavior, "peer1") - msg = Message(topic=TopicId(""), data=b"data") - await behavior._handle_message(peer_id, msg) + message = Message(topic=TopicId(""), data=b"data") + await behavior._handle_message(peer_id, message) assert capture.sent == [] assert behavior._event_queue.empty() @@ -421,8 +421,8 @@ async def test_not_forwarded_when_not_subscribed(self) -> None: peer_id = add_peer(behavior, "peer1") # Not subscribed to "topic" - msg = Message(topic=TopicId("topic"), data=b"data") - await behavior._handle_message(peer_id, msg) + message = Message(topic=TopicId("topic"), data=b"data") + await behavior._handle_message(peer_id, message) assert capture.sent == [] assert behavior._event_queue.get_nowait() == GossipsubMessageEvent( @@ -445,17 +445,17 @@ async def test_idontwant_sent_for_large_messages(self) -> None: behavior.mesh.add_to_mesh(topic, other) large_data = b"x" * IDONTWANT_SIZE_THRESHOLD - msg = Message(topic=topic, data=large_data) - msg_id = GossipsubMessage.compute_id(topic.encode("utf-8"), large_data) - await behavior._handle_message(sender, msg) + message = Message(topic=topic, data=large_data) + message_id = GossipsubMessage.compute_id(topic.encode("utf-8"), large_data) + await behavior._handle_message(sender, message) assert capture.sent == [ - (other, RPC(publish=[msg])), + (other, RPC(publish=[message])), ( other, RPC( control=ControlMessage( - idontwant=[ControlIDontWant(message_ids=[bytes(msg_id)])] + idontwant=[ControlIDontWant(message_ids=[bytes(message_id)])] ) ), ), @@ -474,10 +474,10 @@ async def test_idontwant_not_sent_for_small_messages(self) -> None: behavior.mesh.add_to_mesh(topic, other) small_data = b"x" * (IDONTWANT_SIZE_THRESHOLD - 1) - msg = Message(topic=topic, data=small_data) - await behavior._handle_message(sender, msg) + message = Message(topic=topic, data=small_data) + await behavior._handle_message(sender, message) - assert capture.sent == [(other, RPC(publish=[msg]))] + assert capture.sent == [(other, RPC(publish=[message]))] @pytest.mark.asyncio async def test_message_not_forwarded_to_idontwant_peer(self) -> None: @@ -491,13 +491,13 @@ async def test_message_not_forwarded_to_idontwant_peer(self) -> None: behavior.mesh.add_to_mesh(topic, sender) behavior.mesh.add_to_mesh(topic, peer_ax) - msg_id = GossipsubMessage.compute_id(topic.encode("utf-8"), b"hello") + message_id = GossipsubMessage.compute_id(topic.encode("utf-8"), b"hello") # peer_ax says it doesn't want this message - behavior._peers[peer_ax].dont_want_ids.add(msg_id) + behavior._peers[peer_ax].dont_want_ids.add(message_id) - msg = Message(topic=topic, data=b"hello") - await behavior._handle_message(sender, msg) + message = Message(topic=topic, data=b"hello") + await behavior._handle_message(sender, message) assert capture.sent == [] @@ -510,12 +510,12 @@ def test_idontwant_populates_peer_set(self) -> None: behavior, _ = make_behavior() peer_id = add_peer(behavior, "peer1") - msg_ids = [b"12345678901234567890", b"abcdefghijklmnopqrst"] - idontwant = ControlIDontWant(message_ids=msg_ids) + message_ids = [b"12345678901234567890", b"abcdefghijklmnopqrst"] + idontwant = ControlIDontWant(message_ids=message_ids) behavior._handle_idontwant(peer_id, idontwant) state = behavior._peers[peer_id] - for mid in msg_ids: + for mid in message_ids: assert MessageId(mid) in state.dont_want_ids def test_idontwant_unknown_peer(self) -> None: diff --git a/tests/lean_spec/node/networking/gossipsub/test_heartbeat.py b/tests/lean_spec/node/networking/gossipsub/test_heartbeat.py index 5523d2ab4..989623e16 100644 --- a/tests/lean_spec/node/networking/gossipsub/test_heartbeat.py +++ b/tests/lean_spec/node/networking/gossipsub/test_heartbeat.py @@ -176,8 +176,8 @@ async def test_sends_ihave_to_non_mesh_peers(self) -> None: behavior.subscribe(topic) # Add message to cache - msg = GossipsubMessage(topic=topic.encode("utf-8"), raw_data=b"data") - behavior.message_cache.put(topic, msg) + message = GossipsubMessage(topic=topic.encode("utf-8"), raw_data=b"data") + behavior.message_cache.put(topic, message) # Add mesh peer and non-mesh peer mesh_pid = add_peer(behavior, "meshPx", {topic}) @@ -191,7 +191,7 @@ async def test_sends_ihave_to_non_mesh_peers(self) -> None: non_mesh_pid, RPC( control=ControlMessage( - ihave=[ControlIHave(topic_id=topic, message_ids=[msg.id])] + ihave=[ControlIHave(topic_id=topic, message_ids=[message.id])] ) ), ) @@ -217,8 +217,8 @@ async def test_skips_peers_without_outbound_stream(self) -> None: topic = TopicId("test_topic") behavior.subscribe(topic) - msg = GossipsubMessage(topic=topic.encode("utf-8"), raw_data=b"data") - behavior.message_cache.put(topic, msg) + message = GossipsubMessage(topic=topic.encode("utf-8"), raw_data=b"data") + behavior.message_cache.put(topic, message) # Only add peer without stream (no mesh peers either) add_peer(behavior, "noStrm", {topic}, with_stream=False) @@ -236,17 +236,17 @@ async def test_shifts_message_cache(self) -> None: """Heartbeat shifts the message cache window.""" behavior, _ = make_behavior() - msg = GossipsubMessage(topic=b"topic", raw_data=b"data") - behavior.message_cache.put(TopicId("topic"), msg) + message = GossipsubMessage(topic=b"topic", raw_data=b"data") + behavior.message_cache.put(TopicId("topic"), message) - assert behavior.message_cache.has(msg.id) + assert behavior.message_cache.has(message.id) # Run heartbeat several times to shift through all windows for _ in range(7): await behavior._heartbeat() # After enough shifts, old messages should be evicted - assert not behavior.message_cache.has(msg.id) + assert not behavior.message_cache.has(message.id) @pytest.mark.asyncio async def test_cleans_seen_cache(self) -> None: @@ -254,12 +254,12 @@ async def test_cleans_seen_cache(self) -> None: behavior, _ = make_behavior() behavior.seen_cache = SeenCache(ttl_seconds=1) - msg_id = MessageId(b"12345678901234567890") - behavior.seen_cache.add(msg_id, Timestamp(time.time() - 10)) # Already expired + message_id = MessageId(b"12345678901234567890") + behavior.seen_cache.add(message_id, Timestamp(time.time() - 10)) # Already expired await behavior._heartbeat() - assert not behavior.seen_cache.has(msg_id) + assert not behavior.seen_cache.has(message_id) @pytest.mark.asyncio async def test_iterates_all_subscribed_topics(self) -> None: @@ -332,8 +332,8 @@ async def test_gossip_includes_fanout_topics(self) -> None: behavior.mesh.update_fanout(fan_topic, {fan_peer}) # Add a message to cache for the fanout topic - msg = GossipsubMessage(topic=fan_topic.encode("utf-8"), raw_data=b"data") - behavior.message_cache.put(fan_topic, msg) + message = GossipsubMessage(topic=fan_topic.encode("utf-8"), raw_data=b"data") + behavior.message_cache.put(fan_topic, message) await behavior._heartbeat() @@ -349,7 +349,7 @@ async def test_gossip_includes_fanout_topics(self) -> None: fan_peer, RPC( control=ControlMessage( - ihave=[ControlIHave(topic_id=fan_topic, message_ids=[msg.id])] + ihave=[ControlIHave(topic_id=fan_topic, message_ids=[message.id])] ) ), ) diff --git a/tests/lean_spec/node/networking/gossipsub/test_publish.py b/tests/lean_spec/node/networking/gossipsub/test_publish.py index 0120f3ef8..177a45777 100644 --- a/tests/lean_spec/node/networking/gossipsub/test_publish.py +++ b/tests/lean_spec/node/networking/gossipsub/test_publish.py @@ -84,9 +84,9 @@ async def test_publish_caches_message(self) -> None: await behavior.publish(topic, b"cacheMe") - msg_id = GossipsubMessage.compute_id(topic.encode("utf-8"), b"cacheMe") - assert behavior.seen_cache.has(msg_id) - assert behavior.message_cache.has(msg_id) + message_id = GossipsubMessage.compute_id(topic.encode("utf-8"), b"cacheMe") + assert behavior.seen_cache.has(message_id) + assert behavior.message_cache.has(message_id) @pytest.mark.asyncio async def test_publish_empty_mesh_no_crash(self) -> None: diff --git a/tests/lean_spec/node/networking/gossipsub/test_rpc_edge_cases.py b/tests/lean_spec/node/networking/gossipsub/test_rpc_edge_cases.py index 0b7b067a8..216e26e51 100644 --- a/tests/lean_spec/node/networking/gossipsub/test_rpc_edge_cases.py +++ b/tests/lean_spec/node/networking/gossipsub/test_rpc_edge_cases.py @@ -103,13 +103,13 @@ def test_rpc_with_unknown_varint_field(self) -> None: def test_message_with_unknown_field(self) -> None: """Message ignores unknown length-delimited fields.""" - msg = Message(topic=TopicId("t"), data=b"d") - data = bytearray(msg.encode()) + message = Message(topic=TopicId("t"), data=b"d") + data = bytearray(message.encode()) # Append unknown field 99. data.extend(encode_bytes(99, b"unknown_data")) - assert Message.decode(bytes(data)) == msg + assert Message.decode(bytes(data)) == message class TestLengthValidation: diff --git a/tests/lean_spec/node/networking/service/test_service.py b/tests/lean_spec/node/networking/service/test_service.py index 11db41ab2..270fdf67f 100644 --- a/tests/lean_spec/node/networking/service/test_service.py +++ b/tests/lean_spec/node/networking/service/test_service.py @@ -169,11 +169,11 @@ async def test_gossip_aggregated_attestation_routed(self, peer_id: PeerId) -> No sync_service = create_mock_sync_service(peer_id) - signed_agg = make_signed_aggregated_attestation() + signed_aggregate = make_signed_aggregated_attestation() topic = GossipTopic.committee_aggregation(FORK_DIGEST) events: list[NetworkEvent] = [ GossipAggregatedAttestationEvent( - signed_attestation=signed_agg, + signed_attestation=signed_aggregate, peer_id=peer_id, topic=topic, ), @@ -185,7 +185,7 @@ async def test_gossip_aggregated_attestation_routed(self, peer_id: PeerId) -> No svc, _ = _make_network_service(events, sync_service=sync_service) await svc.run() - mock_handler.assert_awaited_once_with(signed_agg, peer_id) + mock_handler.assert_awaited_once_with(signed_aggregate, peer_id) # --------------------------------------------------------------------------- @@ -223,7 +223,7 @@ async def test_gossip_attestation_routed(self, peer_id: PeerId) -> None: sync_service = create_mock_sync_service(peer_id) attestation = SignedAttestation( - validator_id=ValidatorIndex(1), + validator_index=ValidatorIndex(1), data=AttestationData( slot=Slot(1), head=Checkpoint(root=Bytes32.zero(), slot=Slot(1)), @@ -350,7 +350,7 @@ async def test_publish_attestation_happy_path(self, peer_id: PeerId) -> None: """Attestation is SSZ-encoded, compressed, and published to subnet topic.""" svc, source = _make_network_service([], peer_id=peer_id) attestation = SignedAttestation( - validator_id=ValidatorIndex(7), + validator_index=ValidatorIndex(7), data=AttestationData( slot=Slot(3), head=Checkpoint(root=Bytes32.zero(), slot=Slot(3)), @@ -374,7 +374,7 @@ async def test_publish_attestation_different_subnets(self, peer_id: PeerId) -> N """Different SubnetId values produce different topic strings.""" svc, source = _make_network_service([], peer_id=peer_id) attestation = SignedAttestation( - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), data=AttestationData( slot=Slot(1), head=Checkpoint(root=Bytes32.zero(), slot=Slot(1)), @@ -400,16 +400,16 @@ async def test_publish_aggregated_attestation_happy_path(self, peer_id: PeerId) """Aggregated attestation is encoded, compressed, and published.""" svc, source = _make_network_service([], peer_id=peer_id) - signed_agg = make_signed_aggregated_attestation() + signed_aggregate = make_signed_aggregated_attestation() - await svc.publish_aggregated_attestation(signed_agg) + await svc.publish_aggregated_attestation(signed_aggregate) assert len(source._published) == 1 topic_id, data = source._published[0] expected_topic = GossipTopic.committee_aggregation(FORK_DIGEST).to_topic_id() assert topic_id == expected_topic - assert data == compress(signed_agg.encode_bytes()) + assert data == compress(signed_aggregate.encode_bytes()) # --------------------------------------------------------------------------- diff --git a/tests/lean_spec/node/networking/test_network_service.py b/tests/lean_spec/node/networking/test_network_service.py index 794ec30b2..1f351ef8b 100644 --- a/tests/lean_spec/node/networking/test_network_service.py +++ b/tests/lean_spec/node/networking/test_network_service.py @@ -165,7 +165,7 @@ async def test_attestation_processed_by_store( sync_service.state = SyncState.SYNCING attestation = SignedAttestation( - validator_id=ValidatorIndex(42), + validator_index=ValidatorIndex(42), data=AttestationData( slot=Slot(1), head=Checkpoint(root=Bytes32.zero(), slot=Slot(1)), @@ -197,7 +197,7 @@ async def test_attestation_processed_by_store( # Verify attestation was passed to store updated_store = cast(MockForkchoiceStore, sync_service.store) assert len(updated_store._attestations_received) == initial_count + 1 - assert updated_store._attestations_received[-1].validator_id == ValidatorIndex(42) + assert updated_store._attestations_received[-1].validator_index == ValidatorIndex(42) async def test_attestation_ignored_in_idle_state( self, @@ -212,7 +212,7 @@ async def test_attestation_ignored_in_idle_state( initial_count = len(mock_store._attestations_received) attestation = SignedAttestation( - validator_id=ValidatorIndex(99), + validator_index=ValidatorIndex(99), data=AttestationData( slot=Slot(1), head=Checkpoint(root=Bytes32.zero(), slot=Slot(1)), diff --git a/tests/lean_spec/node/networking/test_reqresp.py b/tests/lean_spec/node/networking/test_reqresp.py index f40edbf80..aee8b10f0 100644 --- a/tests/lean_spec/node/networking/test_reqresp.py +++ b/tests/lean_spec/node/networking/test_reqresp.py @@ -91,11 +91,11 @@ def test_success_response(self) -> None: def test_error_response(self) -> None: """Error response encodes and decodes correctly.""" - error_msg = b"Block not found" - encoded = ResponseCode.RESOURCE_UNAVAILABLE.encode(error_msg) + error_message = b"Block not found" + encoded = ResponseCode.RESOURCE_UNAVAILABLE.encode(error_message) code, decoded = ResponseCode.decode(encoded) assert code == ResponseCode.RESOURCE_UNAVAILABLE - assert decoded == error_msg + assert decoded == error_message def test_all_response_codes(self) -> None: """All standard response codes work correctly.""" @@ -146,7 +146,7 @@ def test_varint_format_matches_protobuf(self) -> None: assert encode_varint(16384) == b"\x80\x80\x01" def test_request_wire_format(self) -> None: - """Request wire format matches spec: [varint_len][snappy_payload].""" + """Request wire format matches spec: [varint_length][snappy_payload].""" ssz_data = b"test" encoded = encode_request(ssz_data) @@ -159,7 +159,7 @@ def test_request_wire_format(self) -> None: assert snappy_data.startswith(b"\xff\x06\x00\x00sNaPpY") def test_response_wire_format(self) -> None: - """Response wire format matches spec: [code][varint_len][snappy_payload].""" + """Response wire format matches spec: [code][varint_length][snappy_payload].""" ssz_data = b"test" encoded = ResponseCode.SUCCESS.encode(ssz_data) @@ -421,8 +421,8 @@ def test_minimum_valid_request(self) -> None: def test_single_byte_payload(self) -> None: """Single-byte payload roundtrips correctly.""" - for byte_val in [0x00, 0x7F, 0x80, 0xFF]: - ssz_data = bytes([byte_val]) + for byte_value in [0x00, 0x7F, 0x80, 0xFF]: + ssz_data = bytes([byte_value]) encoded = encode_request(ssz_data) decoded = decode_request(encoded) assert decoded == ssz_data diff --git a/tests/lean_spec/node/networking/transport/quic/test_connection.py b/tests/lean_spec/node/networking/transport/quic/test_connection.py index df9049769..5e906763f 100644 --- a/tests/lean_spec/node/networking/transport/quic/test_connection.py +++ b/tests/lean_spec/node/networking/transport/quic/test_connection.py @@ -66,7 +66,7 @@ def quic_connection(mock_protocol: MagicMock, peer_a: PeerId) -> QuicConnection: return QuicConnection( _protocol=mock_protocol, _peer_id=peer_a, - _remote_addr="/ip4/127.0.0.1/udp/9000/quic-v1", + _remote_address="/ip4/127.0.0.1/udp/9000/quic-v1", ) @@ -546,16 +546,16 @@ class TestLibP2PQuicProtocol: @pytest.fixture def protocol(self) -> LibP2PQuicProtocol: """A protocol with mocked parent internals (bypasses aioquic constructor).""" - proto = LibP2PQuicProtocol.__new__(LibP2PQuicProtocol) - proto.connection = None - proto.peer_identity = None - proto.handshake_complete = asyncio.Event() - proto._buffered_events = [] - proto._on_handshake = None - proto._quic = MagicMock() - proto._quic._streams = {} - proto.transmit = MagicMock() - return proto + protobuf = LibP2PQuicProtocol.__new__(LibP2PQuicProtocol) + protobuf.connection = None + protobuf.peer_identity = None + protobuf.handshake_complete = asyncio.Event() + protobuf._buffered_events = [] + protobuf._on_handshake = None + protobuf._quic = MagicMock() + protobuf._quic._streams = {} + protobuf.transmit = MagicMock() + return protobuf def test_handshake_completed_sets_event(self, protocol: LibP2PQuicProtocol) -> None: """Handshake completion is signaled so the connection wrapper can proceed.""" @@ -585,7 +585,7 @@ def test_callback_skipped_when_connection_already_exists( protocol.connection = QuicConnection( _protocol=mock_protocol, _peer_id=peer_a, - _remote_addr="/ip4/127.0.0.1/udp/9000/quic-v1", + _remote_address="/ip4/127.0.0.1/udp/9000/quic-v1", ) event = HandshakeCompleted( @@ -598,18 +598,18 @@ def test_events_forwarded_to_connection( self, protocol: LibP2PQuicProtocol, mock_protocol: MagicMock, peer_a: PeerId ) -> None: """Events are forwarded to the connection when it is assigned.""" - conn = QuicConnection( + connection = QuicConnection( _protocol=mock_protocol, _peer_id=peer_a, - _remote_addr="/ip4/127.0.0.1/udp/9000/quic-v1", + _remote_address="/ip4/127.0.0.1/udp/9000/quic-v1", ) - protocol.connection = conn + protocol.connection = connection event = StreamDataReceived(data=b"hello", end_stream=False, stream_id=0) protocol.quic_event_received(event) - assert 0 in conn._streams - assert conn._streams[0]._read_buffer.get_nowait() == b"hello" + assert 0 in connection._streams + assert connection._streams[0]._read_buffer.get_nowait() == b"hello" def test_events_buffered_between_handshake_and_connection( self, protocol: LibP2PQuicProtocol @@ -651,17 +651,17 @@ def test_replay_forwards_and_clears_buffered_events( e2 = StreamDataReceived(data=b"second", end_stream=False, stream_id=0) protocol.quic_event_received(e2) - conn = QuicConnection( + connection = QuicConnection( _protocol=mock_protocol, _peer_id=peer_a, - _remote_addr="/ip4/127.0.0.1/udp/9000/quic-v1", + _remote_address="/ip4/127.0.0.1/udp/9000/quic-v1", ) - protocol.connection = conn + protocol.connection = connection protocol._replay_buffered_events() assert protocol._buffered_events == [] - assert 0 in conn._streams - assert conn._streams[0]._read_buffer.qsize() == 2 + assert 0 in connection._streams + assert connection._streams[0]._read_buffer.qsize() == 2 def test_replay_noop_without_connection(self, protocol: LibP2PQuicProtocol) -> None: """Replay is a no-op when connection is not yet assigned.""" @@ -676,7 +676,7 @@ def test_replay_noop_when_empty( protocol.connection = QuicConnection( _protocol=mock_protocol, _peer_id=peer_a, - _remote_addr="/ip4/127.0.0.1/udp/9000/quic-v1", + _remote_address="/ip4/127.0.0.1/udp/9000/quic-v1", ) protocol._replay_buffered_events() assert protocol._buffered_events == [] @@ -864,9 +864,9 @@ def test_peer_id_returns_set_value( """The connection exposes the peer ID set during construction.""" assert quic_connection.peer_id == peer_a - def test_remote_addr_returns_set_value(self, quic_connection: QuicConnection) -> None: + def test_remote_address_returns_set_value(self, quic_connection: QuicConnection) -> None: """The connection exposes the remote multiaddr set during construction.""" - assert quic_connection.remote_addr == "/ip4/127.0.0.1/udp/9000/quic-v1" + assert quic_connection.remote_address == "/ip4/127.0.0.1/udp/9000/quic-v1" class TestQuicConnectionOpenStreamHappyPath: @@ -949,17 +949,17 @@ class TestQuicConnectionManagerCreate: """ @patch("lean_spec.node.networking.transport.quic.connection.generate_libp2p_certificate") - async def test_create_generates_cert_and_configures_quic( - self, mock_gen_cert: MagicMock + async def test_create_generates_certificate_and_configures_quic( + self, mock_gen_certificate: MagicMock ) -> None: - """Full creation flow: generate cert, write to disk, configure QUIC. + """Full creation flow: generate certificate, write to disk, configure QUIC. The certificate is written to temp files because aioquic only accepts file paths for TLS configuration. """ # Simulate certificate generation returning PEM + DER bytes. - mock_gen_cert.return_value = (b"PRIVATE-KEY", b"CERTIFICATE", b"DER-CERT") + mock_gen_certificate.return_value = (b"PRIVATE-KEY", b"CERTIFICATE", b"DER-CERT") # Simulate an identity keypair that produces a known peer ID. mock_identity = MagicMock() @@ -977,7 +977,7 @@ async def test_create_generates_cert_and_configures_quic( # Verify the manager was configured correctly. assert manager.peer_id == mock_peer_id - mock_gen_cert.assert_called_once_with(mock_identity) + mock_gen_certificate.assert_called_once_with(mock_identity) mock_config_cls.assert_called_once_with( alpn_protocols=[LIBP2P_ALPN_PROTOCOL], is_client=True, @@ -986,9 +986,9 @@ async def test_create_generates_cert_and_configures_quic( mock_config.load_cert_chain.assert_called_once() # Verify temp files were written with the certificate data. - assert manager._temp_dir is not None - assert (manager._temp_dir / "cert.pem").read_bytes() == b"CERTIFICATE" - assert (manager._temp_dir / "key.pem").read_bytes() == b"PRIVATE-KEY" + assert manager._temp_directory is not None + assert (manager._temp_directory / "cert.pem").read_bytes() == b"CERTIFICATE" + assert (manager._temp_directory / "key.pem").read_bytes() == b"PRIVATE-KEY" class TestQuicConnectionManagerConnect: @@ -1011,7 +1011,7 @@ def manager(self) -> QuicConnectionManager: _identity_key=mock_identity, _peer_id=mock_peer_id, _config=mock_config, - _temp_dir=Path("/tmp/test"), + _temp_directory=Path("/tmp/test"), ) async def test_connect_non_quic_raises(self, manager: QuicConnectionManager) -> None: @@ -1043,11 +1043,11 @@ async def test_connect_happy_path_with_peer_id( # Connect to a multiaddr that includes a peer ID. multiaddr = "/ip4/127.0.0.1/udp/9000/quic-v1/p2p/peerB" - conn = await manager.connect(multiaddr) + connection = await manager.connect(multiaddr) # The peer ID from the multiaddr is used, not a generated one. - assert conn.peer_id == PeerId.from_base58("peerB") - assert conn.remote_addr == multiaddr + assert connection.peer_id == PeerId.from_base58("peerB") + assert connection.remote_address == multiaddr # Buffered events from the handshake window are replayed. mock_protocol._replay_buffered_events.assert_called_once() @@ -1086,10 +1086,10 @@ async def test_connect_happy_path_without_peer_id( mock_identity_cls.generate.return_value = mock_temp_key # Connect without a peer ID in the multiaddr. - conn = await manager.connect("/ip4/127.0.0.1/udp/9000/quic-v1") + connection = await manager.connect("/ip4/127.0.0.1/udp/9000/quic-v1") # A temporary peer ID was generated and used. - assert conn.peer_id == temp_peer + assert connection.peer_id == temp_peer @patch("lean_spec.node.networking.transport.quic.connection.quic_connect") async def test_connect_wraps_exception( @@ -1118,14 +1118,14 @@ class TestQuicConnectionManagerListen: """ @pytest.fixture - def manager_with_temp_dir(self, tmp_path: Path) -> QuicConnectionManager: - """A manager with a real temp dir containing cert files. + def manager_with_temp_directory(self, tmp_path: Path) -> QuicConnectionManager: + """A manager with a real temp dir containing certificate files. - Uses tmp_path so cert files exist on disk for load_cert_chain. + Uses tmp_path so certificate files exist on disk for load_cert_chain. """ - cert_path = tmp_path / "cert.pem" + certificate_path = tmp_path / "cert.pem" key_path = tmp_path / "key.pem" - cert_path.write_bytes(b"CERTIFICATE") + certificate_path.write_bytes(b"CERTIFICATE") key_path.write_bytes(b"PRIVATE-KEY") mock_identity = MagicMock() @@ -1135,16 +1135,16 @@ def manager_with_temp_dir(self, tmp_path: Path) -> QuicConnectionManager: _identity_key=mock_identity, _peer_id=mock_peer_id, _config=mock_config, - _temp_dir=tmp_path, + _temp_directory=tmp_path, ) async def test_listen_non_quic_raises( - self, manager_with_temp_dir: QuicConnectionManager + self, manager_with_temp_directory: QuicConnectionManager ) -> None: """Listening on a non-QUIC multiaddr is rejected immediately.""" callback = AsyncMock() with pytest.raises(QuicTransportError, match=r"Not a QUIC multiaddr"): - await manager_with_temp_dir.listen("/ip4/0.0.0.0/udp/9000", callback) + await manager_with_temp_directory.listen("/ip4/0.0.0.0/udp/9000", callback) @patch("lean_spec.node.networking.transport.quic.connection.QuicConfiguration") @patch("lean_spec.node.networking.transport.quic.connection.quic_serve") @@ -1152,7 +1152,7 @@ async def test_listen_configures_server_and_serves( self, mock_quic_serve: MagicMock, mock_config_cls: MagicMock, - manager_with_temp_dir: QuicConnectionManager, + manager_with_temp_directory: QuicConnectionManager, ) -> None: """Server uses is_client=False, CERT_NONE, and the libp2p ALPN. @@ -1178,7 +1178,7 @@ async def test_listen_configures_server_and_serves( pytest.raises(asyncio.CancelledError), ): mock_event_cls.return_value = mock_event - await manager_with_temp_dir.listen("/ip4/0.0.0.0/udp/9000/quic-v1", callback) + await manager_with_temp_directory.listen("/ip4/0.0.0.0/udp/9000/quic-v1", callback) # Verify the server was configured as a non-client with libp2p ALPN. mock_config_cls.assert_called_once_with( @@ -1197,7 +1197,7 @@ async def test_listen_handle_handshake_creates_connection( mock_identity_cls: MagicMock, mock_quic_serve: MagicMock, mock_config_cls: MagicMock, - manager_with_temp_dir: QuicConnectionManager, + manager_with_temp_directory: QuicConnectionManager, ) -> None: """The handshake callback creates and registers a QuicConnection. @@ -1236,7 +1236,7 @@ async def capture_serve(*args: object, **kwargs: object) -> MagicMock: pytest.raises(asyncio.CancelledError), ): mock_event_cls.return_value = mock_event - await manager_with_temp_dir.listen("/ip4/0.0.0.0/udp/9000/quic-v1", callback) + await manager_with_temp_directory.listen("/ip4/0.0.0.0/udp/9000/quic-v1", callback) # The factory must have been captured from quic_serve kwargs. assert captured_create_protocol is not None @@ -1245,32 +1245,32 @@ async def capture_serve(*args: object, **kwargs: object) -> MagicMock: # # The parent constructor is patched to avoid aioquic dependencies. with patch.object(LibP2PQuicProtocol.__bases__[0], "__init__", return_value=None): - proto_instance = captured_create_protocol() + protobuf_instance = captured_create_protocol() # The factory must have attached a handshake callback. - assert proto_instance._on_handshake is not None + assert protobuf_instance._on_handshake is not None # Simulate the handshake callback being invoked by aioquic. - proto_instance._quic = MagicMock() - proto_instance.transmit = MagicMock() - proto_instance.connection = None - proto_instance._buffered_events = [] + protobuf_instance._quic = MagicMock() + protobuf_instance.transmit = MagicMock() + protobuf_instance.connection = None + protobuf_instance._buffered_events = [] - proto_instance._on_handshake(proto_instance) + protobuf_instance._on_handshake(protobuf_instance) # The callback creates a connection and registers it in the manager. - assert proto_instance.connection is not None - assert proto_instance.connection.peer_id == temp_peer - assert temp_peer in manager_with_temp_dir._connections + assert protobuf_instance.connection is not None + assert protobuf_instance.connection.peer_id == temp_peer + assert temp_peer in manager_with_temp_directory._connections @patch("lean_spec.node.networking.transport.quic.connection.QuicConfiguration") @patch("lean_spec.node.networking.transport.quic.connection.quic_serve") - async def test_listen_without_temp_dir_skips_cert_loading( + async def test_listen_without_temp_directory_skips_certificate_loading( self, mock_quic_serve: MagicMock, mock_config_cls: MagicMock, ) -> None: - """Without a temp directory, cert loading is skipped gracefully. + """Without a temp directory, certificate loading is skipped gracefully. This guards against a crash if the manager was not constructed via the normal create() factory. @@ -1281,12 +1281,12 @@ async def test_listen_without_temp_dir_skips_cert_loading( mock_config_cls.return_value = mock_server_config mock_quic_serve.return_value = MagicMock() - # Create a manager with no temp directory (no cert files on disk). + # Create a manager with no temp directory (no certificate files on disk). manager = QuicConnectionManager( _identity_key=MagicMock(), _peer_id=PeerId.from_base58("peerA"), _config=MagicMock(), - _temp_dir=None, + _temp_directory=None, ) callback = AsyncMock() @@ -1303,5 +1303,5 @@ async def test_listen_without_temp_dir_skips_cert_loading( mock_event_cls.return_value = mock_event await manager.listen("/ip4/0.0.0.0/udp/9000/quic-v1", callback) - # Cert loading was skipped because no temp dir exists. + # Certificate loading was skipped because no temp dir exists. mock_server_config.load_cert_chain.assert_not_called() diff --git a/tests/lean_spec/node/networking/transport/quic/test_negotiation.py b/tests/lean_spec/node/networking/transport/quic/test_negotiation.py index bcd03dc5f..3aca0dfd8 100644 --- a/tests/lean_spec/node/networking/transport/quic/test_negotiation.py +++ b/tests/lean_spec/node/networking/transport/quic/test_negotiation.py @@ -209,8 +209,8 @@ async def test_message_format(self) -> None: raw = await stream.read(100) expected_payload = STATUS_ID.encode("utf-8") + b"\n" - expected_len = len(expected_payload) - assert raw[0] == expected_len + expected_length = len(expected_payload) + assert raw[0] == expected_length assert raw[1:] == expected_payload async def test_message_roundtrip(self) -> None: @@ -363,8 +363,8 @@ async def test_read_negotiation_message_varint_too_long(self) -> None: async def test_read_negotiation_message_message_too_long(self) -> None: """Message too long""" stream, peer = _create_stream_pair() - too_big_len = MAX_MESSAGE_SIZE + 1 - payload = b"A" * too_big_len + too_big_length = MAX_MESSAGE_SIZE + 1 + payload = b"A" * too_big_length length_prefix = encode_varint(len(payload)) peer.write(length_prefix) await peer.drain() @@ -431,7 +431,7 @@ async def test_read_n_none_returns_buffer(self) -> None: async def test_read_n_none_empty_buffer(self) -> None: """read(n=None) with empty buffer reads from stream.""" stream, peer = _create_stream_pair() - stream._stream._read_queue.put_nowait(b"from stream") # type: ignore[attr-defined] + stream._stream._read_queue.put_nowait(b"from stream") # type: ignore[attribute-defined] result = await stream.read() assert result == b"from stream" @@ -455,15 +455,15 @@ async def test_read_empty_stream(self) -> None: """read(n) returns empty bytes when stream is closed.""" stream, _ = _create_stream_pair() stream._buffer = b"" - stream._stream._read_queue.put_nowait(b"") # type: ignore[attr-defined] + stream._stream._read_queue.put_nowait(b"") # type: ignore[attribute-defined] result = await stream.read(10) assert result == b"" async def test_readexactly_accumulates_chunks(self) -> None: """readexactly accumulates chunks until n bytes.""" stream, peer = _create_stream_pair() - stream._stream._read_queue.put_nowait(b"ab") # type: ignore[attr-defined] - stream._stream._read_queue.put_nowait(b"cd") # type: ignore[attr-defined] + stream._stream._read_queue.put_nowait(b"ab") # type: ignore[attribute-defined] + stream._stream._read_queue.put_nowait(b"cd") # type: ignore[attribute-defined] result = await stream.readexactly(4) assert result == b"abcd" assert stream._buffer == b"" @@ -471,8 +471,8 @@ async def test_readexactly_accumulates_chunks(self) -> None: async def test_readexactly_eof_error(self) -> None: """readexactly raises EOFError when stream closes early.""" stream, peer = _create_stream_pair() - stream._stream._read_queue.put_nowait(b"partial") # type: ignore[attr-defined] - stream._stream._read_queue.put_nowait(b"") # type: ignore[attr-defined] + stream._stream._read_queue.put_nowait(b"partial") # type: ignore[attribute-defined] + stream._stream._read_queue.put_nowait(b"") # type: ignore[attribute-defined] with pytest.raises(EOFError, match="Stream closed"): await stream.readexactly(100) @@ -527,14 +527,14 @@ class TestReadNegotiationMessage: async def test_message_connection_closed(self) -> None: """Raises error when connection closes while reading length.""" stream, peer = _create_stream_pair() - stream._stream._read_queue.put_nowait(b"") # type: ignore[attr-defined] + stream._stream._read_queue.put_nowait(b"") # type: ignore[attribute-defined] with pytest.raises(NegotiationError, match="Connection closed"): await stream._read_negotiation_message() async def test_message_varint_too_long(self) -> None: """Raises error when varint has more than 5 continuation bytes.""" stream, peer = _create_stream_pair() - stream._stream._read_queue.put_nowait(bytes([0x80, 0x80, 0x80, 0x80, 0x80, 0x80])) # type: ignore[attr-defined] + stream._stream._read_queue.put_nowait(bytes([0x80, 0x80, 0x80, 0x80, 0x80, 0x80])) # type: ignore[attribute-defined] with pytest.raises(NegotiationError, match="Varint too long"): await stream._read_negotiation_message() diff --git a/tests/lean_spec/node/networking/transport/quic/test_tls.py b/tests/lean_spec/node/networking/transport/quic/test_tls.py index 7d015a6c6..f912b207f 100644 --- a/tests/lean_spec/node/networking/transport/quic/test_tls.py +++ b/tests/lean_spec/node/networking/transport/quic/test_tls.py @@ -172,9 +172,9 @@ class TestEncodeAsn1SignedKey: def test_structure_is_sequence_of_two_octet_strings(self) -> None: """Output is a SEQUENCE containing two OCTET STRINGs.""" - proto = b"\x08\x02\x12\x21" + bytes(33) - sig = bytes(64) - result = _encode_asn1_signed_key(proto, sig) + protobuf = b"\x08\x02\x12\x21" + bytes(33) + signature = bytes(64) + result = _encode_asn1_signed_key(protobuf, signature) assert result[0] == 0x30 # SEQUENCE tag @@ -183,11 +183,11 @@ def test_structure_is_sequence_of_two_octet_strings(self) -> None: # First OCTET STRING tag1, val1, rest = _parse_der_tlv_with_rest(inner) assert tag1 == 0x04 - assert val1 == proto + assert val1 == protobuf # Second OCTET STRING tag2, val2, rest2 = _parse_der_tlv_with_rest(rest) assert tag2 == 0x04 - assert val2 == sig + assert val2 == signature assert rest2 == b"" @@ -225,16 +225,16 @@ def test_protobuf_encoding(self, identity_key: IdentityKeypair) -> None: payload = _create_extension_payload(identity_key, tls_public_bytes) _, inner = _parse_der_tlv(payload) - _, public_key_proto, _ = _parse_der_tlv_with_rest(inner) + _, public_key_protobuf, _ = _parse_der_tlv_with_rest(inner) # Protobuf field 1 (Type): varint tag=0x08, value=2 (secp256k1) - assert public_key_proto[0] == 0x08 - assert public_key_proto[1] == KeyType.SECP256K1 + assert public_key_protobuf[0] == 0x08 + assert public_key_protobuf[1] == KeyType.SECP256K1 # Protobuf field 2 (Data): length-delimited tag=0x12 - assert public_key_proto[2] == 0x12 - key_len = public_key_proto[3] - assert key_len == 33 # compressed secp256k1 - key_data = public_key_proto[4 : 4 + key_len] + assert public_key_protobuf[2] == 0x12 + key_length = public_key_protobuf[3] + assert key_length == 33 # compressed secp256k1 + key_data = public_key_protobuf[4 : 4 + key_length] assert key_data == bytes(identity_key.public_key.to_bytes()) def test_signature_verifies(self, identity_key: IdentityKeypair) -> None: @@ -264,13 +264,13 @@ class TestGenerateLibp2pCertificate: def test_returns_parseable_pem(self, identity_key: IdentityKeypair) -> None: """Both PEM outputs are parseable by the cryptography library.""" - private_pem, cert_pem, cert = generate_libp2p_certificate(identity_key) + private_pem, certificate_pem, certificate = generate_libp2p_certificate(identity_key) loaded_key = serialization.load_pem_private_key(private_pem, password=None) assert isinstance(loaded_key, ec.EllipticCurvePrivateKey) - loaded_cert = x509.load_pem_x509_certificate(cert_pem) - assert loaded_cert.serial_number == cert.serial_number + loaded_certificate = x509.load_pem_x509_certificate(certificate_pem) + assert loaded_certificate.serial_number == certificate.serial_number def test_tls_key_is_p256(self, identity_key: IdentityKeypair) -> None: """The ephemeral TLS key uses P-256 (SECP256R1), not secp256k1.""" @@ -281,25 +281,25 @@ def test_tls_key_is_p256(self, identity_key: IdentityKeypair) -> None: def test_empty_subject_and_issuer(self, identity_key: IdentityKeypair) -> None: """Subject and issuer are empty to match rust-libp2p's format.""" - _, _, cert = generate_libp2p_certificate(identity_key) - assert cert.subject == x509.Name([]) - assert cert.issuer == x509.Name([]) + _, _, certificate = generate_libp2p_certificate(identity_key) + assert certificate.subject == x509.Name([]) + assert certificate.issuer == x509.Name([]) def test_certificate_signed_with_sha256(self, identity_key: IdentityKeypair) -> None: """Certificate uses SHA-256 for the outer TLS signature.""" - _, _, cert = generate_libp2p_certificate(identity_key) - assert cert.signature_hash_algorithm is not None - assert cert.signature_hash_algorithm.name == "sha256" + _, _, certificate = generate_libp2p_certificate(identity_key) + assert certificate.signature_hash_algorithm is not None + assert certificate.signature_hash_algorithm.name == "sha256" def test_validity_window(self, identity_key: IdentityKeypair) -> None: """not_valid_before is ~1 day before now, not_valid_after is ~365 days after now.""" from datetime import datetime, timedelta, timezone now = datetime.now(timezone.utc) - _, _, cert = generate_libp2p_certificate(identity_key) + _, _, certificate = generate_libp2p_certificate(identity_key) - not_before_delta = now - cert.not_valid_before_utc - not_after_delta = cert.not_valid_after_utc - now + not_before_delta = now - certificate.not_valid_before_utc + not_after_delta = certificate.not_valid_after_utc - now tolerance = timedelta(minutes=5) assert abs(not_before_delta - timedelta(days=1)) < tolerance @@ -307,12 +307,12 @@ def test_validity_window(self, identity_key: IdentityKeypair) -> None: def test_subject_key_identifier(self, identity_key: IdentityKeypair) -> None: """SKI extension is sha256(tls_public_key_der)[:20].""" - _, _, cert = generate_libp2p_certificate(identity_key) + _, _, certificate = generate_libp2p_certificate(identity_key) - ski_ext = cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier) + ski_ext = certificate.extensions.get_extension_for_class(x509.SubjectKeyIdentifier) assert ski_ext.critical is False - tls_public_bytes = cert.public_key().public_bytes( + tls_public_bytes = certificate.public_key().public_bytes( encoding=serialization.Encoding.DER, format=serialization.PublicFormat.SubjectPublicKeyInfo, ) @@ -321,17 +321,17 @@ def test_subject_key_identifier(self, identity_key: IdentityKeypair) -> None: def test_libp2p_extension_present_and_non_critical(self, identity_key: IdentityKeypair) -> None: """The libp2p extension is present with the correct OID and is non-critical.""" - _, _, cert = generate_libp2p_certificate(identity_key) + _, _, certificate = generate_libp2p_certificate(identity_key) - ext = cert.extensions.get_extension_for_oid(LIBP2P_EXTENSION_OID) + ext = certificate.extensions.get_extension_for_oid(LIBP2P_EXTENSION_OID) assert ext.critical is False assert isinstance(ext.value, x509.UnrecognizedExtension) def test_libp2p_extension_signature_verifies(self, identity_key: IdentityKeypair) -> None: """The identity proof signature in the extension verifies end-to-end.""" - _, _, cert = generate_libp2p_certificate(identity_key) + _, _, certificate = generate_libp2p_certificate(identity_key) - ext = cert.extensions.get_extension_for_oid(LIBP2P_EXTENSION_OID) + ext = certificate.extensions.get_extension_for_oid(LIBP2P_EXTENSION_OID) assert isinstance(ext.value, x509.UnrecognizedExtension) payload = ext.value.value @@ -340,7 +340,7 @@ def test_libp2p_extension_signature_verifies(self, identity_key: IdentityKeypair _, _, rest = _parse_der_tlv_with_rest(inner) _, signature, _ = _parse_der_tlv_with_rest(rest) - tls_public_bytes = cert.public_key().public_bytes( + tls_public_bytes = certificate.public_key().public_bytes( encoding=serialization.Encoding.DER, format=serialization.PublicFormat.SubjectPublicKeyInfo, ) @@ -349,8 +349,8 @@ def test_libp2p_extension_signature_verifies(self, identity_key: IdentityKeypair def test_self_signed(self, identity_key: IdentityKeypair) -> None: """The certificate is self-signed: its TLS public key verifies the outer signature.""" - _, _, cert = generate_libp2p_certificate(identity_key) - cert.verify_directly_issued_by(cert) + _, _, certificate = generate_libp2p_certificate(identity_key) + certificate.verify_directly_issued_by(certificate) def test_ephemeral_key_uniqueness(self, identity_key: IdentityKeypair) -> None: """Two calls with the same identity key produce different TLS keys.""" @@ -360,11 +360,11 @@ def test_ephemeral_key_uniqueness(self, identity_key: IdentityKeypair) -> None: assert pem1 != pem2 assert cert1.serial_number != cert2.serial_number - def test_returned_cert_matches_pem(self, identity_key: IdentityKeypair) -> None: + def test_returned_certificate_matches_pem(self, identity_key: IdentityKeypair) -> None: """The returned certificate object matches the PEM encoding.""" - _, cert_pem, cert = generate_libp2p_certificate(identity_key) - reparsed = x509.load_pem_x509_certificate(cert_pem) - assert reparsed == cert + _, certificate_pem, certificate = generate_libp2p_certificate(identity_key) + reparsed = x509.load_pem_x509_certificate(certificate_pem) + assert reparsed == certificate # --------------------------------------------------------------------------- diff --git a/tests/lean_spec/node/networking/transport/test_peer_id.py b/tests/lean_spec/node/networking/transport/test_peer_id.py index c54dad21a..b02a96030 100644 --- a/tests/lean_spec/node/networking/transport/test_peer_id.py +++ b/tests/lean_spec/node/networking/transport/test_peer_id.py @@ -21,7 +21,7 @@ Multihash, MultihashCode, PeerId, - PublicKeyProto, + PublicKeyProtobuf, ) # Protobuf tag constants for test assertions @@ -78,7 +78,7 @@ def test_base58_roundtrip(self) -> None: decoded = Base58.decode(encoded) assert decoded == data, f"Roundtrip failed for {data.hex()}" - def test_base58_decode_invalid_char(self) -> None: + def test_base58_decode_invalid_character(self) -> None: """Decoding invalid characters raises ValueError.""" with pytest.raises(ValueError, match="Invalid Base58 character"): Base58.decode("0") # '0' not in Base58 @@ -142,8 +142,8 @@ def test_encode_format(self) -> None: key_type = KeyType.SECP256K1 key_data = bytes([0x02] + [0] * 32) # 33 bytes compressed secp256k1 - proto = PublicKeyProto(key_type=key_type, key_data=key_data) - encoded = proto.encode() + protobuf = PublicKeyProtobuf(key_type=key_type, key_data=key_data) + encoded = protobuf.encode() # Field 1 tag (0x08 = field 1, varint) assert encoded[0] == _PROTOBUF_TAG_TYPE @@ -160,8 +160,8 @@ def test_encode_different_types(self) -> None: """Different key types produce different encodings.""" key_data = bytes(33) # secp256k1 compressed is 33 bytes - ed25519 = PublicKeyProto(key_type=KeyType.ED25519, key_data=key_data).encode() - secp256k1 = PublicKeyProto(key_type=KeyType.SECP256K1, key_data=key_data).encode() + ed25519 = PublicKeyProtobuf(key_type=KeyType.ED25519, key_data=key_data).encode() + secp256k1 = PublicKeyProtobuf(key_type=KeyType.SECP256K1, key_data=key_data).encode() assert ed25519 != secp256k1 # Both start with 0x08 (type field tag) @@ -184,8 +184,8 @@ def test_ed25519_encoding_matches_spec(self) -> None: ) key_data = full_encoded[4:] # Skip 08 01 12 20 - proto = PublicKeyProto(key_type=KeyType.ED25519, key_data=key_data) - encoded = proto.encode() + protobuf = PublicKeyProtobuf(key_type=KeyType.ED25519, key_data=key_data) + encoded = protobuf.encode() assert encoded == full_encoded @@ -201,8 +201,8 @@ def test_secp256k1_encoding_matches_spec(self) -> None: ) key_data = full_encoded[4:] # Skip 08 02 12 21 - proto = PublicKeyProto(key_type=KeyType.SECP256K1, key_data=key_data) - encoded = proto.encode() + protobuf = PublicKeyProtobuf(key_type=KeyType.SECP256K1, key_data=key_data) + encoded = protobuf.encode() assert encoded == full_encoded @@ -221,8 +221,8 @@ def test_ecdsa_encoding_matches_spec(self) -> None: ) key_data = full_encoded[4:] # Skip 08 03 12 5b - proto = PublicKeyProto(key_type=KeyType.ECDSA, key_data=key_data) - encoded = proto.encode() + protobuf = PublicKeyProtobuf(key_type=KeyType.ECDSA, key_data=key_data) + encoded = protobuf.encode() assert encoded == full_encoded @@ -282,7 +282,9 @@ def test_peer_id_uses_sha256_for_large_keys(self) -> None: # Create a key type that produces > 42 bytes encoded # A 128-byte key should exceed the limit large_key = bytes(128) - peer_id = PeerId.from_public_key(PublicKeyProto(key_type=KeyType.RSA, key_data=large_key)) + peer_id = PeerId.from_public_key( + PublicKeyProtobuf(key_type=KeyType.RSA, key_data=large_key) + ) decoded = Base58.decode(str(peer_id)) @@ -332,8 +334,8 @@ def test_ed25519_from_spec_test_vector(self) -> None: # Extract key data and verify re-encoding matches key_data = spec_encoded[4:] - proto = PublicKeyProto(key_type=KeyType.ED25519, key_data=key_data) - our_encoded = proto.encode() + protobuf = PublicKeyProtobuf(key_type=KeyType.ED25519, key_data=key_data) + our_encoded = protobuf.encode() assert our_encoded == spec_encoded, "Our encoding must match spec" # Compute PeerId (36 bytes <= 42, uses identity multihash) @@ -370,8 +372,8 @@ def test_secp256k1_from_spec_test_vector(self) -> None: # Extract key data and verify re-encoding matches key_data = spec_encoded[4:] - proto = PublicKeyProto(key_type=KeyType.SECP256K1, key_data=key_data) - our_encoded = proto.encode() + protobuf = PublicKeyProtobuf(key_type=KeyType.SECP256K1, key_data=key_data) + our_encoded = protobuf.encode() assert our_encoded == spec_encoded, "Our encoding must match spec" # Compute PeerId (37 bytes <= 42, uses identity multihash) @@ -414,8 +416,8 @@ def test_ecdsa_from_spec_test_vector(self) -> None: # Extract key data and verify re-encoding matches key_data = spec_encoded[4:] - proto = PublicKeyProto(key_type=KeyType.ECDSA, key_data=key_data) - our_encoded = proto.encode() + protobuf = PublicKeyProtobuf(key_type=KeyType.ECDSA, key_data=key_data) + our_encoded = protobuf.encode() assert our_encoded == spec_encoded, "Our encoding must match spec" # Compute PeerId (95 bytes > 42, uses SHA256 multihash) @@ -435,8 +437,8 @@ def test_ed25519_peer_id_prefix(self) -> None: # Any 32-byte Ed25519 key should produce a PeerId starting with 12D3KooW # because the first bytes are: 00 24 08 01 (identity, 36, tag, Ed25519) key_data = bytes(32) # All zeros - proto = PublicKeyProto(key_type=KeyType.ED25519, key_data=key_data) - encoded = proto.encode() + protobuf = PublicKeyProtobuf(key_type=KeyType.ED25519, key_data=key_data) + encoded = protobuf.encode() multihash = Multihash.from_data(encoded).encode() peer_id = Base58.encode(multihash) @@ -448,8 +450,8 @@ def test_secp256k1_peer_id_prefix(self) -> None: # because the first bytes are: 00 25 08 02 (identity, 37, tag, secp256k1) # The exact prefix after "16Uiu2" varies based on key data key_data = bytes([0x02] + [0] * 32) # Compressed format - proto = PublicKeyProto(key_type=KeyType.SECP256K1, key_data=key_data) - encoded = proto.encode() + protobuf = PublicKeyProtobuf(key_type=KeyType.SECP256K1, key_data=key_data) + encoded = protobuf.encode() multihash = Multihash.from_data(encoded).encode() peer_id = Base58.encode(multihash) @@ -465,8 +467,8 @@ def test_known_secp256k1_peer_id(self) -> None: key_data = bytes.fromhex( "037777e994e452c21604f91de093ce415f5432f701dd8cd1a7a6fea0e630bfca99" ) - proto = PublicKeyProto(key_type=KeyType.SECP256K1, key_data=key_data) - peer_id = PeerId.from_public_key(proto) + protobuf = PublicKeyProtobuf(key_type=KeyType.SECP256K1, key_data=key_data) + peer_id = PeerId.from_public_key(protobuf) # Expected PeerId from spec test vector expected = "16Uiu2HAmLhLvBoYaoZfaMUKuibM6ac163GwKY74c5kiSLg5KvLpY" diff --git a/tests/lean_spec/node/observability/test_observer.py b/tests/lean_spec/node/observability/test_observer.py index 919954ce6..6cc481f25 100644 --- a/tests/lean_spec/node/observability/test_observer.py +++ b/tests/lean_spec/node/observability/test_observer.py @@ -82,11 +82,11 @@ class TestNullObserverDefault: def test_default_observer_is_null(self) -> None: assert isinstance(observer_module._observer, _NullObserver) - @pytest.mark.parametrize(("method_name", "_metric_attr", "_cm"), SPEC_EVENTS) + @pytest.mark.parametrize(("method_name", "_metric_attribute", "_cm"), SPEC_EVENTS) def test_null_observer_discards_events( self, method_name: str, - _metric_attr: str, + _metric_attribute: str, _cm: Callable[[], AbstractContextManager[None]], ) -> None: getattr(_NullObserver(), method_name)(0.5) @@ -104,11 +104,11 @@ def test_replaces_singleton(self) -> None: class TestPrometheusObserverUninitialized: """PrometheusObserver is a no-op when metrics have not been initialized.""" - @pytest.mark.parametrize(("method_name", "_metric_attr", "_cm"), SPEC_EVENTS) + @pytest.mark.parametrize(("method_name", "_metric_attribute", "_cm"), SPEC_EVENTS) def test_no_error_when_metrics_not_initialized( self, method_name: str, - _metric_attr: str, + _metric_attribute: str, _cm: Callable[[], AbstractContextManager[None]], ) -> None: getattr(PrometheusObserver(), method_name)(0.1) @@ -117,26 +117,26 @@ def test_no_error_when_metrics_not_initialized( class TestPrometheusObserverWithRegistry: """Each hook forwards into its paired Prometheus histogram.""" - @pytest.mark.parametrize(("method_name", "metric_attr", "_cm"), SPEC_EVENTS) + @pytest.mark.parametrize(("method_name", "metric_attribute", "_cm"), SPEC_EVENTS) def test_observes_single_value( self, fresh_registry: CollectorRegistry, method_name: str, - metric_attr: str, + metric_attribute: str, _cm: Callable[[], AbstractContextManager[None]], ) -> None: _init_metrics(fresh_registry) getattr(PrometheusObserver(), method_name)(0.5) - assert _get_histogram_sum(getattr(metrics, metric_attr)) == 0.5 + assert _get_histogram_sum(getattr(metrics, metric_attribute)) == 0.5 - @pytest.mark.parametrize(("method_name", "metric_attr", "_cm"), SPEC_EVENTS) + @pytest.mark.parametrize(("method_name", "metric_attribute", "_cm"), SPEC_EVENTS) def test_accumulates_multiple_values( self, fresh_registry: CollectorRegistry, method_name: str, - metric_attr: str, + metric_attribute: str, _cm: Callable[[], AbstractContextManager[None]], ) -> None: _init_metrics(fresh_registry) @@ -145,7 +145,7 @@ def test_accumulates_multiple_values( getattr(observer, method_name)(0.5) getattr(observer, method_name)(0.75) - assert _get_histogram_sum(getattr(metrics, metric_attr)) == 1.25 + assert _get_histogram_sum(getattr(metrics, metric_attribute)) == 1.25 class _RecordingObserver: @@ -171,11 +171,11 @@ def on_attestation_timed(self, seconds: float) -> None: class TestObserveContextManagers: """Each observe_* context manager publishes on clean exit, not on raise.""" - @pytest.mark.parametrize(("method_name", "_metric_attr", "cm"), SPEC_EVENTS) + @pytest.mark.parametrize(("method_name", "_metric_attribute", "cm"), SPEC_EVENTS) def test_publishes_on_clean_exit( self, method_name: str, - _metric_attr: str, + _metric_attribute: str, cm: Callable[[], AbstractContextManager[None]], ) -> None: observer = _RecordingObserver() @@ -187,11 +187,11 @@ def test_publishes_on_clean_exit( assert len(observer.samples[method_name]) == 1 assert observer.samples[method_name][0] >= 0.0 - @pytest.mark.parametrize(("method_name", "_metric_attr", "cm"), SPEC_EVENTS) + @pytest.mark.parametrize(("method_name", "_metric_attribute", "cm"), SPEC_EVENTS) def test_does_not_publish_when_body_raises( self, method_name: str, - _metric_attr: str, + _metric_attribute: str, cm: Callable[[], AbstractContextManager[None]], ) -> None: observer = _RecordingObserver() diff --git a/tests/lean_spec/node/snappy/test_framing.py b/tests/lean_spec/node/snappy/test_framing.py index bdc9fd7a8..8d460afde 100644 --- a/tests/lean_spec/node/snappy/test_framing.py +++ b/tests/lean_spec/node/snappy/test_framing.py @@ -360,8 +360,8 @@ def test_wire_format_structure(self) -> None: chunk_type = compressed[pos] assert chunk_type in (0x00, 0x01) # Compressed or uncompressed - chunk_len = int.from_bytes(compressed[pos + 1 : pos + 4], "little") - assert chunk_len >= 4 # At least CRC + chunk_length = int.from_bytes(compressed[pos + 1 : pos + 4], "little") + assert chunk_length >= 4 # At least CRC # CRC is first 4 bytes of chunk data crc_bytes = compressed[pos + 4 : pos + 8] diff --git a/tests/lean_spec/node/snappy/test_snappy.py b/tests/lean_spec/node/snappy/test_snappy.py index e3a2620b8..2df5a4052 100644 --- a/tests/lean_spec/node/snappy/test_snappy.py +++ b/tests/lean_spec/node/snappy/test_snappy.py @@ -23,12 +23,12 @@ ) # Path to test data files -TESTDATA_DIR = Path(__file__).parent / "testdata" +TESTDATA_DIRECTORY = Path(__file__).parent / "testdata" def load_test_file(filename: str, size_limit: int = 0) -> bytes: """Load a test data file, optionally truncated to size_limit.""" - path = TESTDATA_DIR / filename + path = TESTDATA_DIRECTORY / filename data = path.read_bytes() if size_limit > 0: data = data[:size_limit] @@ -206,9 +206,9 @@ def test_empty_input(self) -> None: def test_single_byte(self) -> None: """Single byte roundtrips correctly.""" - for char in [b"a", b"X", b"\x00", b"\xff"]: - compressed = compress(char) - assert decompress(compressed) == char + for character in [b"a", b"X", b"\x00", b"\xff"]: + compressed = compress(character) + assert decompress(compressed) == character def test_short_strings(self) -> None: """Short strings roundtrip correctly.""" @@ -430,11 +430,11 @@ class TestUtilities: def test_max_compressed_length(self) -> None: """max_compressed_length returns a valid upper bound.""" for size in [0, 100, 1000, 65536, 100000]: - max_len = max_compressed_length(size) + max_length = max_compressed_length(size) data = bytes(range(256)) * (size // 256 + 1) data = data[:size] compressed = compress(data) - assert len(compressed) <= max_len + assert len(compressed) <= max_length class TestSpecificPatterns: diff --git a/tests/lean_spec/node/storage/test_sqlite.py b/tests/lean_spec/node/storage/test_sqlite.py index ec17f4e32..e213f0c27 100644 --- a/tests/lean_spec/node/storage/test_sqlite.py +++ b/tests/lean_spec/node/storage/test_sqlite.py @@ -322,12 +322,12 @@ def test_corrupt_block_data_raises_corruption_error(self, db: SQLiteDatabase) -> root = Bytes32(b"\x20" * 32) # Write garbage bytes directly into the blocks table. - cursor = db._conn.cursor() + cursor = db._connection.cursor() cursor.execute( "INSERT INTO blocks (root, slot, data) VALUES (?, ?, ?)", (bytes(root), 0, b"not valid ssz"), ) - db._conn.commit() + db._connection.commit() with pytest.raises(StorageCorruptionError, match="Corrupt block"): db.get_block(root) @@ -336,24 +336,24 @@ def test_corrupt_state_data_raises_corruption_error(self, db: SQLiteDatabase) -> """Reading corrupt state data raises StorageCorruptionError.""" root = Bytes32(b"\x21" * 32) - cursor = db._conn.cursor() + cursor = db._connection.cursor() cursor.execute( "INSERT INTO states (root, slot, data) VALUES (?, ?, ?)", (bytes(root), 0, b"not valid ssz"), ) - db._conn.commit() + db._connection.commit() with pytest.raises(StorageCorruptionError, match="Corrupt state"): db.get_state(root) def test_corrupt_checkpoint_data_raises_corruption_error(self, db: SQLiteDatabase) -> None: """Reading corrupt checkpoint data raises StorageCorruptionError.""" - cursor = db._conn.cursor() + cursor = db._connection.cursor() cursor.execute( "INSERT OR REPLACE INTO checkpoints (key, data) VALUES (?, ?)", ("justified", b"not valid ssz"), ) - db._conn.commit() + db._connection.commit() with pytest.raises(StorageCorruptionError, match="Corrupt justified"): db.get_justified_checkpoint() diff --git a/tests/lean_spec/node/sync/test_reaggregate_from_block.py b/tests/lean_spec/node/sync/test_reaggregate_from_block.py index e1edd3a3b..4d7b0691e 100644 --- a/tests/lean_spec/node/sync/test_reaggregate_from_block.py +++ b/tests/lean_spec/node/sync/test_reaggregate_from_block.py @@ -54,12 +54,12 @@ def _setup( The chain block sits at slot 1. The returned signed block sits at slot 2 and carries one attestation whose target is the slot-1 block, ahead of the still-genesis justified checkpoint. The returned store holds the - slot-1 block and its state (the parent state the multi-message aggregate pubkey layout + slot-1 block and its state (the parent state the multi-message aggregate public_key layout is resolved against) with the justified checkpoint still at genesis. """ spec = LstarSpec() base_store = make_store( - num_validators=NUM_VALIDATORS, validator_id=ValidatorIndex(0), key_manager=key_manager + num_validators=NUM_VALIDATORS, validator_index=ValidatorIndex(0), key_manager=key_manager ) consumer_store, chain_block = make_signed_block_from_store( @@ -142,7 +142,7 @@ def test_skips_when_block_adds_no_new_validators( def test_noop_when_parent_state_missing(peer_id: PeerId, key_manager: XmssKeyManager) -> None: - """Without the parent state the pubkey layout cannot be resolved -> no-op.""" + """Without the parent state the public_key layout cannot be resolved -> no-op.""" chain_store, signed_block, _ = _setup( key_manager, block_participants=[ValidatorIndex(1), ValidatorIndex(2)] ) diff --git a/tests/lean_spec/node/sync/test_service.py b/tests/lean_spec/node/sync/test_service.py index 2c5565688..b5dc3fdaa 100644 --- a/tests/lean_spec/node/sync/test_service.py +++ b/tests/lean_spec/node/sync/test_service.py @@ -37,7 +37,7 @@ def _signed_aggregated_attestation(key_manager: XmssKeyManager) -> SignedAggrega _, attestation_data = make_store_with_attestation_data( key_manager, num_validators=4, - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), ) proof = make_aggregated_proof(key_manager, [ValidatorIndex(1)], attestation_data) return SignedAggregatedAttestation(data=attestation_data, proof=proof) @@ -303,7 +303,9 @@ async def test_attestation_buffered_when_block_unknown( ) mock_store = cast(MockForkchoiceStore, sync_service.store) - mock_store.reject_attestation = lambda att: att.data.target.root == unknown_root + mock_store.reject_attestation = ( + lambda attestation: attestation.data.target.root == unknown_root + ) await sync_service.on_gossip_attestation(attestation) @@ -559,8 +561,8 @@ async def test_publisher_field_is_invoked( service = create_mock_sync_service(peer_id) published: list[SignedAggregatedAttestation] = [] - async def capture(agg: SignedAggregatedAttestation) -> None: - published.append(agg) + async def capture(aggregate: SignedAggregatedAttestation) -> None: + published.append(aggregate) service.publish_aggregated_attestation = capture signed = _signed_aggregated_attestation(key_manager) @@ -621,12 +623,12 @@ async def test_pending_attestations_trimmed_to_max( unknown = Bytes32(b"\xcd" * 32) mock_store = cast(MockForkchoiceStore, sync_service.store) - mock_store.reject_attestation = lambda _att: True + mock_store.reject_attestation = lambda _attestation: True for i in range(MAX_PENDING_ATTESTATIONS + 50): target = Checkpoint(root=unknown, slot=Slot(i)) - att = make_signed_attestation(ValidatorIndex(0), target=target) - await sync_service.on_gossip_attestation(att) + attestation = make_signed_attestation(ValidatorIndex(0), target=target) + await sync_service.on_gossip_attestation(attestation) assert len(sync_service._pending_attestations) == MAX_PENDING_ATTESTATIONS last_slot = MAX_PENDING_ATTESTATIONS + 49 @@ -641,7 +643,7 @@ async def test_pending_aggregated_trimmed_to_max( sync_service.state = SyncState.SYNCING mock_store = cast(MockForkchoiceStore, sync_service.store) - mock_store.reject_aggregated_attestation = lambda _att: True + mock_store.reject_aggregated_attestation = lambda _attestation: True signed = _signed_aggregated_attestation(key_manager) for _ in range(MAX_PENDING_ATTESTATIONS + 10): @@ -673,7 +675,7 @@ async def test_aggregated_buffered_on_key_error( signed = _signed_aggregated_attestation(key_manager) mock_store = cast(MockForkchoiceStore, sync_service.store) - mock_store.reject_aggregated_attestation = lambda _att: True + mock_store.reject_aggregated_attestation = lambda _attestation: True await sync_service.on_gossip_aggregated_attestation(signed) assert list(sync_service._pending_aggregated_attestations) == [signed] @@ -686,17 +688,17 @@ async def test_replay_pending_mixed_success_and_failure( sync_service.state = SyncState.SYNCING head = sync_service.store.head ok_target = Checkpoint(root=head, slot=Slot(0)) - ok_att = make_signed_attestation(ValidatorIndex(0), target=ok_target) + ok_attestation = make_signed_attestation(ValidatorIndex(0), target=ok_target) bad_signed = _signed_aggregated_attestation(key_manager) mock_store = cast(MockForkchoiceStore, sync_service.store) - mock_store.reject_aggregated_attestation = lambda att: att is bad_signed + mock_store.reject_aggregated_attestation = lambda attestation: attestation is bad_signed - sync_service._pending_attestations.append(ok_att) + sync_service._pending_attestations.append(ok_attestation) sync_service._pending_aggregated_attestations.append(bad_signed) sync_service._replay_pending_attestations() - assert ok_att in mock_store._attestations_received + assert ok_attestation in mock_store._attestations_received assert list(sync_service._pending_aggregated_attestations) == [bad_signed] @@ -710,20 +712,20 @@ def test_replay_plain_mixed_success_and_failure(self, sync_service: SyncService) head = mock_store.head unknown = Bytes32(b"\xee" * 32) - ok_att = make_signed_attestation( + ok_attestation = make_signed_attestation( ValidatorIndex(0), target=Checkpoint(root=head, slot=Slot(0)), ) - bad_att = make_signed_attestation( + bad_attestation = make_signed_attestation( ValidatorIndex(1), target=Checkpoint(root=unknown, slot=Slot(5)), ) - mock_store.reject_attestation = lambda att: att.data.target.root == unknown + mock_store.reject_attestation = lambda attestation: attestation.data.target.root == unknown - sync_service._pending_attestations.append(ok_att) - sync_service._pending_attestations.append(bad_att) + sync_service._pending_attestations.append(ok_attestation) + sync_service._pending_attestations.append(bad_attestation) sync_service._replay_pending_attestations() - assert ok_att in mock_store._attestations_received - assert list(sync_service._pending_attestations) == [bad_att] + assert ok_attestation in mock_store._attestations_received + assert list(sync_service._pending_attestations) == [bad_attestation] diff --git a/tests/lean_spec/node/test_anchor.py b/tests/lean_spec/node/test_anchor.py index a6a514fb9..fbfe9af7b 100644 --- a/tests/lean_spec/node/test_anchor.py +++ b/tests/lean_spec/node/test_anchor.py @@ -54,7 +54,7 @@ async def test_genesis_time_mismatch_raises(self) -> None: url="http://localhost:5052", genesis=local_genesis, fork=LstarSpec(), - validator_id=None, + validator_index=None, ) async def test_verification_failure_raises(self) -> None: @@ -80,7 +80,7 @@ async def test_verification_failure_raises(self) -> None: url="http://localhost:5052", genesis=local_genesis, fork=LstarSpec(), - validator_id=None, + validator_index=None, ) async def test_network_error_propagates(self) -> None: @@ -101,7 +101,7 @@ async def test_network_error_propagates(self) -> None: url="http://localhost:5052", genesis=local_genesis, fork=LstarSpec(), - validator_id=None, + validator_index=None, ) async def test_success_builds_store_and_status(self) -> None: @@ -121,7 +121,7 @@ async def test_success_builds_store_and_status(self) -> None: url="http://localhost:5052", genesis=local_genesis, fork=LstarSpec(), - validator_id=None, + validator_index=None, ) assert anchor.store is not None diff --git a/tests/lean_spec/node/test_node.py b/tests/lean_spec/node/test_node.py index d6f2ef7b2..f3714693f 100644 --- a/tests/lean_spec/node/test_node.py +++ b/tests/lean_spec/node/test_node.py @@ -184,14 +184,14 @@ class TestDatabaseLoading: def test_returns_none_when_no_database(self) -> None: """No database returns None.""" - assert Node._try_load_store_from_database(None, validator_id=None) is None + assert Node._try_load_store_from_database(None, validator_index=None) is None def test_returns_none_when_no_head_root(self) -> None: """Empty database returns None.""" mock_db = MagicMock() mock_db.get_head_root.return_value = None - assert Node._try_load_store_from_database(mock_db, validator_id=None) is None + assert Node._try_load_store_from_database(mock_db, validator_index=None) is None def test_returns_none_when_block_missing(self) -> None: """Missing block returns None.""" @@ -200,7 +200,7 @@ def test_returns_none_when_block_missing(self) -> None: mock_db.get_block.return_value = None mock_db.get_state.return_value = MagicMock() - assert Node._try_load_store_from_database(mock_db, validator_id=None) is None + assert Node._try_load_store_from_database(mock_db, validator_index=None) is None def test_returns_none_when_state_missing(self) -> None: """Missing state returns None.""" @@ -209,21 +209,21 @@ def test_returns_none_when_state_missing(self) -> None: mock_db.get_block.return_value = MagicMock() mock_db.get_state.return_value = None - assert Node._try_load_store_from_database(mock_db, validator_id=None) is None + assert Node._try_load_store_from_database(mock_db, validator_index=None) is None def test_returns_none_when_justified_missing(self) -> None: """Missing justified checkpoint returns None.""" mock_db = _make_mock_db_with_partial_data() mock_db.get_justified_checkpoint.return_value = None - assert Node._try_load_store_from_database(mock_db, validator_id=None) is None + assert Node._try_load_store_from_database(mock_db, validator_index=None) is None def test_returns_none_when_finalized_missing(self) -> None: """Missing finalized checkpoint returns None.""" mock_db = _make_mock_db_with_partial_data() mock_db.get_finalized_checkpoint.return_value = None - assert Node._try_load_store_from_database(mock_db, validator_id=None) is None + assert Node._try_load_store_from_database(mock_db, validator_index=None) is None def test_successful_load_uses_wall_clock_time(self) -> None: """Store time uses wall clock when it exceeds block-based time.""" @@ -234,7 +234,7 @@ def test_successful_load_uses_wall_clock_time(self) -> None: wall_time = float(GENESIS_TIME) + 100.0 store = Node._try_load_store_from_database( db, - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), genesis_time=GENESIS_TIME, time_fn=lambda: wall_time, ) @@ -257,7 +257,7 @@ def test_load_uses_block_time_when_wall_clock_behind(self) -> None: wall_time = float(GENESIS_TIME) + 10.0 store = Node._try_load_store_from_database( db, - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), genesis_time=GENESIS_TIME, time_fn=lambda: wall_time, ) @@ -400,7 +400,7 @@ def test_falls_back_to_database_genesis_time(self) -> None: wall_time = float(GENESIS_TIME) + 100.0 store = Node._try_load_store_from_database( db, - validator_id=None, + validator_index=None, genesis_time=None, time_fn=lambda: wall_time, ) @@ -420,7 +420,7 @@ def test_zero_genesis_time_when_database_returns_none(self) -> None: wall_time = 200.0 store = Node._try_load_store_from_database( db, - validator_id=None, + validator_index=None, genesis_time=None, time_fn=lambda: wall_time, ) @@ -496,9 +496,9 @@ async def test_attestation_publish_wrapper_calls_both_services( assert node_with_validator.validator_service.on_attestation is not None mock_attestation = MagicMock() - # The wrapper calls validator_id.compute_subnet_id(ATTESTATION_COMMITTEE_COUNT). + # The wrapper calls validator_index.compute_subnet_id(ATTESTATION_COMMITTEE_COUNT). expected_subnet = 42 - mock_attestation.validator_id.compute_subnet_id.return_value = expected_subnet + mock_attestation.validator_index.compute_subnet_id.return_value = expected_subnet publish_attestation = AsyncMock() on_gossip_attestation = AsyncMock() @@ -518,7 +518,7 @@ async def test_attestation_publish_wrapper_calls_both_services( await node_with_validator.validator_service.on_attestation(mock_attestation) # Verify subnet_id computation used the correct committee count. - mock_attestation.validator_id.compute_subnet_id.assert_called_once_with( + mock_attestation.validator_index.compute_subnet_id.assert_called_once_with( ATTESTATION_COMMITTEE_COUNT ) # Verify the computed subnet_id was forwarded to the network layer. diff --git a/tests/lean_spec/node/validator/test_registry.py b/tests/lean_spec/node/validator/test_registry.py index 60a7a5304..696dcf934 100644 --- a/tests/lean_spec/node/validator/test_registry.py +++ b/tests/lean_spec/node/validator/test_registry.py @@ -22,12 +22,12 @@ def registry_state(registry: ValidatorRegistry) -> dict[ValidatorIndex, tuple[object, object]]: - """Extract full registry state as index → (att_sk, prop_sk) mapping.""" + """Map each registry index to its (attestation_secret_key, proposal_secret_key) pair.""" result: dict[ValidatorIndex, tuple[object, object]] = {} - for idx in registry.indices(): - entry = registry.get(idx) - assert entry is not None, f"Registry contains index {idx} but get() returned None" - result[idx] = (entry.attestation_secret_key, entry.proposal_secret_key) + for index in registry.indices(): + entry = registry.get(index) + assert entry is not None, f"Registry contains index {index} but get() returned None" + result[index] = (entry.attestation_secret_key, entry.proposal_secret_key) return result @@ -59,10 +59,10 @@ def _manifest_entry_dict(index: int, suffix: str = "") -> dict[str, object]: """Return a manifest entry dict for a validator at the given index.""" return { "index": index, - "attestation_pubkey_hex": "0x" + f"{index:02d}" * 52, - "proposal_pubkey_hex": "0x" + f"{index:02d}" * 52, - "attestation_privkey_file": f"att_key_{index}{suffix}.ssz", - "proposal_privkey_file": f"prop_key_{index}{suffix}.ssz", + "attestation_public_key_hex": "0x" + f"{index:02d}" * 52, + "proposal_public_key_hex": "0x" + f"{index:02d}" * 52, + "attestation_private_key_file": f"att_key_{index}{suffix}.ssz", + "proposal_private_key_file": f"prop_key_{index}{suffix}.ssz", } @@ -92,40 +92,40 @@ def test_construction_stores_all_fields(self) -> None: """All fields are stored and accessible after construction.""" entry = ValidatorManifestEntry( index=ValidatorIndex(3), - attestation_pubkey_hex=Bytes52("0x" + "aa" * 52), - proposal_pubkey_hex=Bytes52("0x" + "bb" * 52), - attestation_privkey_file="att.ssz", - proposal_privkey_file="prop.ssz", + attestation_public_key_hex=Bytes52("0x" + "aa" * 52), + proposal_public_key_hex=Bytes52("0x" + "bb" * 52), + attestation_private_key_file="att.ssz", + proposal_private_key_file="prop.ssz", ) assert entry == ValidatorManifestEntry( index=ValidatorIndex(3), - attestation_pubkey_hex=Bytes52("0x" + "aa" * 52), - proposal_pubkey_hex=Bytes52("0x" + "bb" * 52), - attestation_privkey_file="att.ssz", - proposal_privkey_file="prop.ssz", + attestation_public_key_hex=Bytes52("0x" + "aa" * 52), + proposal_public_key_hex=Bytes52("0x" + "bb" * 52), + attestation_private_key_file="att.ssz", + proposal_private_key_file="prop.ssz", ) - def test_integer_pubkey_rejected(self) -> None: - """Integer pubkeys are rejected — only valid 52-byte hex strings accepted.""" + def test_integer_public_key_rejected(self) -> None: + """Integer public_keys are rejected — only valid 52-byte hex strings accepted.""" with pytest.raises((TypeError, ValidationError)): ValidatorManifestEntry( index=ValidatorIndex(0), - attestation_pubkey_hex=0x123, # type: ignore[arg-type] - proposal_pubkey_hex=Bytes52("0x" + "aa" * 52), - attestation_privkey_file="att.ssz", - proposal_privkey_file="prop.ssz", + attestation_public_key_hex=0x123, # type: ignore[arg-type] + proposal_public_key_hex=Bytes52("0x" + "aa" * 52), + attestation_private_key_file="att.ssz", + proposal_private_key_file="prop.ssz", ) - def test_wrong_length_pubkey_rejected(self) -> None: + def test_wrong_length_public_key_rejected(self) -> None: """Hex strings that don't decode to exactly 52 bytes are rejected.""" with pytest.raises((SSZValueError, ValidationError)): ValidatorManifestEntry( index=ValidatorIndex(0), - attestation_pubkey_hex=Bytes52("0x" + "aa" * 10), - proposal_pubkey_hex=Bytes52("0x" + "bb" * 52), - attestation_privkey_file="att.ssz", - proposal_privkey_file="prop.ssz", + attestation_public_key_hex=Bytes52("0x" + "aa" * 10), + proposal_public_key_hex=Bytes52("0x" + "bb" * 52), + attestation_private_key_file="att.ssz", + proposal_private_key_file="prop.ssz", ) @@ -163,17 +163,17 @@ def test_from_yaml_file_parses_validators_list(self, tmp_path: Path) -> None: assert manifest.validators == [ ValidatorManifestEntry( index=ValidatorIndex(0), - attestation_pubkey_hex=Bytes52("0x" + "00" * 52), - proposal_pubkey_hex=Bytes52("0x" + "00" * 52), - attestation_privkey_file="att_key_0.ssz", - proposal_privkey_file="prop_key_0.ssz", + attestation_public_key_hex=Bytes52("0x" + "00" * 52), + proposal_public_key_hex=Bytes52("0x" + "00" * 52), + attestation_private_key_file="att_key_0.ssz", + proposal_private_key_file="prop_key_0.ssz", ), ValidatorManifestEntry( index=ValidatorIndex(1), - attestation_pubkey_hex=Bytes52("0x" + "01" * 52), - proposal_pubkey_hex=Bytes52("0x" + "01" * 52), - attestation_privkey_file="att_key_1.ssz", - proposal_privkey_file="prop_key_1.ssz", + attestation_public_key_hex=Bytes52("0x" + "01" * 52), + proposal_public_key_hex=Bytes52("0x" + "01" * 52), + attestation_private_key_file="att_key_1.ssz", + proposal_private_key_file="prop_key_1.ssz", ), ] @@ -203,12 +203,12 @@ def test_empty_file_returns_empty_dict(self, tmp_path: Path) -> None: class TestValidatorRegistry: """Tests for ValidatorRegistry dataclass.""" - def _entry(self, km: XmssKeyManager, idx: int) -> ValidatorEntry: + def _entry(self, km: XmssKeyManager, index: int) -> ValidatorEntry: """Create a ValidatorEntry with real keys for the given index.""" - vid = ValidatorIndex(idx) - kp = km[vid] + validator_index = ValidatorIndex(index) + kp = km[validator_index] return ValidatorEntry( - index=vid, + index=validator_index, attestation_secret_key=kp.attestation_keypair.secret_key, proposal_secret_key=kp.proposal_keypair.secret_key, ) @@ -238,7 +238,7 @@ def test_get_miss_returns_none(self, km: XmssKeyManager) -> None: def test_add_multiple_entries(self, km: XmssKeyManager) -> None: """Multiple entries are stored with correct index-to-key mapping.""" registry = ValidatorRegistry() - entries = {idx: self._entry(km, idx) for idx in [3, 1, 4]} + entries = {index: self._entry(km, index) for index in [3, 1, 4]} for entry in entries.values(): registry.add(entry) @@ -270,7 +270,7 @@ def test_contains_unknown_index(self) -> None: assert ValidatorIndex(99) not in registry - def test_len_after_adds(self, km: XmssKeyManager) -> None: + def test_length_after_adds(self, km: XmssKeyManager) -> None: """__len__ reflects the number of entries added.""" registry = ValidatorRegistry() assert len(registry) == 0 @@ -314,12 +314,12 @@ def test_add_overwrites_existing_entry(self, km: XmssKeyManager) -> None: registry = ValidatorRegistry() old_entry = self._entry(km, 5) # Create a different entry for the same index using different key pairs. - kp_att = km[ValidatorIndex(6)] - kp_prop = km[ValidatorIndex(7)] + kp_attestation = km[ValidatorIndex(6)] + kp_proposal = km[ValidatorIndex(7)] new_entry = ValidatorEntry( index=ValidatorIndex(5), - attestation_secret_key=kp_att.attestation_keypair.secret_key, - proposal_secret_key=kp_prop.proposal_keypair.secret_key, + attestation_secret_key=kp_attestation.attestation_keypair.secret_key, + proposal_secret_key=kp_proposal.proposal_keypair.secret_key, ) registry.add(old_entry) @@ -328,8 +328,8 @@ def test_add_overwrites_existing_entry(self, km: XmssKeyManager) -> None: assert len(registry) == 1 assert registry_state(registry) == { ValidatorIndex(5): ( - kp_att.attestation_keypair.secret_key, - kp_prop.proposal_keypair.secret_key, + kp_attestation.attestation_keypair.secret_key, + kp_proposal.proposal_keypair.secret_key, ) } @@ -356,8 +356,8 @@ def _write_real_key_files( ) -> None: """Write real SSZ-encoded XMSS key files for the given validator indices.""" for i in indices: - vid = ValidatorIndex(i) - kp = km[vid] + validator_index = ValidatorIndex(i) + kp = km[validator_index] (directory / f"att_key_{i}.ssz").write_bytes( kp.attestation_keypair.secret_key.encode_bytes() ) @@ -388,13 +388,13 @@ def test_happy_path_loads_assigned_validators(self, tmp_path: Path, km: XmssKeyM # Loaded keys should match the originals from the key manager. for i in [0, 1]: - vid = ValidatorIndex(i) - entry = registry.get(vid) + validator_index = ValidatorIndex(i) + entry = registry.get(validator_index) assert entry is not None - expected_att = km[vid].attestation_keypair.secret_key.encode_bytes() - expected_prop = km[vid].proposal_keypair.secret_key.encode_bytes() - assert entry.attestation_secret_key.encode_bytes() == expected_att - assert entry.proposal_secret_key.encode_bytes() == expected_prop + expected_attestation = km[validator_index].attestation_keypair.secret_key.encode_bytes() + expected_proposal = km[validator_index].proposal_keypair.secret_key.encode_bytes() + assert entry.attestation_secret_key.encode_bytes() == expected_attestation + assert entry.proposal_secret_key.encode_bytes() == expected_proposal def test_unknown_node_returns_empty_registry(self, tmp_path: Path) -> None: """An unrecognised node ID produces an empty registry without error.""" @@ -548,4 +548,4 @@ class TestValidatorRegistryFromKeysDirectory: def test_missing_manifest_raises(self, tmp_path: Path) -> None: """The loader raises when the manifest file is absent.""" with pytest.raises(FileNotFoundError, match="Validator keys manifest not found"): - ValidatorRegistry.from_keys_directory(node_id="lean_spec_0", base_dir=tmp_path) + ValidatorRegistry.from_keys_directory(node_id="lean_spec_0", base_directory=tmp_path) diff --git a/tests/lean_spec/node/validator/test_service.py b/tests/lean_spec/node/validator/test_service.py index c7581b678..083ef7ff8 100644 --- a/tests/lean_spec/node/validator/test_service.py +++ b/tests/lean_spec/node/validator/test_service.py @@ -29,7 +29,7 @@ from lean_spec.spec.forks.lstar.spec import LstarSpec from lean_spec.spec.ssz import Bytes32, Uint64 from tests.lean_spec.helpers import ( - TEST_VALIDATOR_ID, + TEST_VALIDATOR_INDEX, MockNetworkRequester, make_aggregated_proof, make_signed_block, @@ -52,11 +52,11 @@ def _make_registry(key_manager: XmssKeyManager, *indices: int) -> ValidatorRegis """Build a ValidatorRegistry with real XMSS keys for the given indices.""" registry = ValidatorRegistry() for i in indices: - vid = ValidatorIndex(i) - kp = key_manager[vid] + validator_index = ValidatorIndex(i) + kp = key_manager[validator_index] registry.add( ValidatorEntry( - index=vid, + index=validator_index, attestation_secret_key=kp.attestation_keypair.secret_key, proposal_secret_key=kp.proposal_keypair.secret_key, ) @@ -66,10 +66,10 @@ def _make_registry(key_manager: XmssKeyManager, *indices: int) -> ValidatorRegis def _make_entry(key_manager: XmssKeyManager, index: int = 0) -> ValidatorEntry: """Return a ValidatorEntry with real XMSS keys.""" - vid = ValidatorIndex(index) - kp = key_manager[vid] + validator_index = ValidatorIndex(index) + kp = key_manager[validator_index] return ValidatorEntry( - index=vid, + index=validator_index, attestation_secret_key=kp.attestation_keypair.secret_key, proposal_secret_key=kp.proposal_keypair.secret_key, ) @@ -134,9 +134,9 @@ def test_attestation_proofs_merge_into_envelope( """Aggregated attestation proofs are merged into the block envelope.""" service, block = self._setup(sync_service, key_manager) attestation_data = spec.produce_attestation_data(sync_service.store, Slot(1)) - agg_proof = make_aggregated_proof(key_manager, [ValidatorIndex(0)], attestation_data) + aggregate_proof = make_aggregated_proof(key_manager, [ValidatorIndex(0)], attestation_data) - result = service._sign_block(block, ValidatorIndex(0), [agg_proof]) + result = service._sign_block(block, ValidatorIndex(0), [aggregate_proof]) assert result.block.proposer_index == ValidatorIndex(0) @@ -177,42 +177,42 @@ def _setup( clock=SlotClock(genesis_time=Uint64(0)), registry=registry, ) - att_data = spec.produce_attestation_data(sync_service.store, Slot(1)) - return service, att_data + attestation_data = spec.produce_attestation_data(sync_service.store, Slot(1)) + return service, attestation_data def test_fields_populated_correctly( self, sync_service: SyncService, key_manager: XmssKeyManager, spec: LstarSpec ) -> None: """The signed attestation contains the correct validator ID, data, and a valid signature.""" - service, att_data = self._setup(sync_service, key_manager, spec, index=3) + service, attestation_data = self._setup(sync_service, key_manager, spec, index=3) - result = service._sign_attestation(att_data, ValidatorIndex(3)) + result = service._sign_attestation(attestation_data, ValidatorIndex(3)) - assert result.validator_id == ValidatorIndex(3) - assert result.data is att_data + assert result.validator_index == ValidatorIndex(3) + assert result.data is attestation_data public_key = key_manager[ValidatorIndex(3)].attestation_keypair.public_key assert TARGET_SIGNATURE_SCHEME.verify( - pk=public_key, - slot=att_data.slot, - message=hash_tree_root(att_data), - sig=result.signature, + public_key=public_key, + slot=attestation_data.slot, + message=hash_tree_root(attestation_data), + signature=result.signature, ) def test_uses_attestation_key_not_proposal_key( self, sync_service: SyncService, key_manager: XmssKeyManager, spec: LstarSpec ) -> None: """Attestation signature verifies with the attestation public key, not the proposal key.""" - service, att_data = self._setup(sync_service, key_manager, spec) + service, attestation_data = self._setup(sync_service, key_manager, spec) - result = service._sign_attestation(att_data, ValidatorIndex(0)) + result = service._sign_attestation(attestation_data, ValidatorIndex(0)) - attestation_pk = key_manager[ValidatorIndex(0)].attestation_keypair.public_key + attestation_public_key = key_manager[ValidatorIndex(0)].attestation_keypair.public_key assert TARGET_SIGNATURE_SCHEME.verify( - pk=attestation_pk, - slot=att_data.slot, - message=hash_tree_root(att_data), - sig=result.signature, + public_key=attestation_public_key, + slot=attestation_data.slot, + message=hash_tree_root(attestation_data), + signature=result.signature, ) def test_missing_validator_raises_value_error( @@ -224,10 +224,10 @@ def test_missing_validator_raises_value_error( clock=SlotClock(genesis_time=Uint64(0)), registry=ValidatorRegistry(), ) - att_data = spec.produce_attestation_data(sync_service.store, Slot(1)) + attestation_data = spec.produce_attestation_data(sync_service.store, Slot(1)) with pytest.raises(ValueError, match="No secret key for validator 99"): - service._sign_attestation(att_data, ValidatorIndex(99)) + service._sign_attestation(attestation_data, ValidatorIndex(99)) class TestSignWithKey: @@ -245,8 +245,8 @@ def _setup( index: int = 0, ) -> tuple[ValidatorService, ValidatorRegistry, ValidatorEntry, object, object]: entry = _make_entry(key_manager, index) - att_key = entry.attestation_secret_key - prop_key = entry.proposal_secret_key + attestation_key = entry.attestation_secret_key + proposal_key = entry.proposal_secret_key registry = ValidatorRegistry() registry.add(entry) service = ValidatorService( @@ -254,56 +254,58 @@ def _setup( clock=SlotClock(genesis_time=Uint64(0)), registry=registry, ) - return service, registry, entry, att_key, prop_key + return service, registry, entry, attestation_key, proposal_key def test_no_advancement_when_slot_already_prepared( self, sync_service: SyncService, key_manager: XmssKeyManager ) -> None: """No key advancement when the slot is already within the prepared interval.""" - service, _, entry, att_key, _ = self._setup(sync_service, key_manager) - mock_sig = MagicMock(name="sig") + service, _, entry, attestation_key, _ = self._setup(sync_service, key_manager) + mock_signature = MagicMock(name="sig") with patch(_SCHEME) as scheme: scheme.get_prepared_interval.return_value = [3] - scheme.sign.return_value = mock_sig + scheme.sign.return_value = mock_signature service._sign_with_key(entry, Slot(3), MagicMock(), "attestation_secret_key") scheme.advance_preparation.assert_not_called() - scheme.sign.assert_called_once_with(att_key, Slot(3), scheme.sign.call_args[0][2]) + scheme.sign.assert_called_once_with(attestation_key, Slot(3), scheme.sign.call_args[0][2]) def test_key_advanced_once_until_slot_in_interval( self, sync_service: SyncService, key_manager: XmssKeyManager ) -> None: """Key advances exactly once when one step covers the target slot.""" - service, _, entry, att_key, _ = self._setup(sync_service, key_manager) + service, _, entry, attestation_key, _ = self._setup(sync_service, key_manager) advanced = MagicMock(name="advanced_key") - mock_sig = MagicMock(name="sig") + mock_signature = MagicMock(name="sig") with patch(_SCHEME) as scheme: - scheme.get_prepared_interval.side_effect = lambda key: [] if key is att_key else [5] + scheme.get_prepared_interval.side_effect = ( + lambda key: [] if key is attestation_key else [5] + ) scheme.advance_preparation.return_value = advanced - scheme.sign.return_value = mock_sig + scheme.sign.return_value = mock_signature service._sign_with_key(entry, Slot(5), MagicMock(), "attestation_secret_key") - scheme.advance_preparation.assert_called_once_with(att_key) + scheme.advance_preparation.assert_called_once_with(attestation_key) scheme.sign.assert_called_once_with(advanced, Slot(5), scheme.sign.call_args[0][2]) def test_key_advanced_multiple_times_until_prepared( self, sync_service: SyncService, key_manager: XmssKeyManager ) -> None: """Key advancement loops until the target slot falls within the interval.""" - service, _, entry, att_key, _ = self._setup(sync_service, key_manager) + service, _, entry, attestation_key, _ = self._setup(sync_service, key_manager) key_v1 = MagicMock(name="key_v1") key_v2 = MagicMock(name="key_v2") key_v3 = MagicMock(name="key_v3") - mock_sig = MagicMock(name="sig") + mock_signature = MagicMock(name="sig") with patch(_SCHEME) as scheme: scheme.get_prepared_interval.side_effect = lambda key: [7] if key is key_v3 else [] scheme.advance_preparation.side_effect = [key_v1, key_v2, key_v3] - scheme.sign.return_value = mock_sig + scheme.sign.return_value = mock_signature service._sign_with_key(entry, Slot(7), MagicMock(), "attestation_secret_key") @@ -314,11 +316,13 @@ def test_updated_entry_persisted_in_registry( self, sync_service: SyncService, key_manager: XmssKeyManager ) -> None: """After signing, the registry holds an entry with the advanced key.""" - service, registry, entry, att_key, _ = self._setup(sync_service, key_manager) + service, registry, entry, attestation_key, _ = self._setup(sync_service, key_manager) advanced = MagicMock(name="advanced_att") with patch(_SCHEME) as scheme: - scheme.get_prepared_interval.side_effect = lambda key: [] if key is att_key else [4] + scheme.get_prepared_interval.side_effect = ( + lambda key: [] if key is attestation_key else [4] + ) scheme.advance_preparation.return_value = advanced scheme.sign.return_value = MagicMock() @@ -332,59 +336,69 @@ def test_attestation_key_updated_proposal_key_unchanged( self, sync_service: SyncService, key_manager: XmssKeyManager ) -> None: """Signing with the attestation key updates only that key; proposal key is untouched.""" - service, registry, entry, att_key, prop_key = self._setup(sync_service, key_manager) - advanced_att = MagicMock(name="new_att") + service, registry, entry, attestation_key, proposal_key = self._setup( + sync_service, key_manager + ) + advanced_attestation = MagicMock(name="new_att") with patch(_SCHEME) as scheme: - scheme.get_prepared_interval.side_effect = lambda key: [] if key is att_key else [1] - scheme.advance_preparation.return_value = advanced_att + scheme.get_prepared_interval.side_effect = ( + lambda key: [] if key is attestation_key else [1] + ) + scheme.advance_preparation.return_value = advanced_attestation scheme.sign.return_value = MagicMock() service._sign_with_key(entry, Slot(1), MagicMock(), "attestation_secret_key") stored = registry.get(ValidatorIndex(0)) assert stored is not None - assert stored.attestation_secret_key is advanced_att - assert stored.proposal_secret_key is prop_key + assert stored.attestation_secret_key is advanced_attestation + assert stored.proposal_secret_key is proposal_key def test_proposal_key_updated_attestation_key_unchanged( self, sync_service: SyncService, key_manager: XmssKeyManager ) -> None: """Signing with the proposal key updates only that key; attestation key is untouched.""" - service, registry, entry, att_key, prop_key = self._setup(sync_service, key_manager) - advanced_prop = MagicMock(name="new_prop") + service, registry, entry, attestation_key, proposal_key = self._setup( + sync_service, key_manager + ) + advanced_proposal = MagicMock(name="new_prop") with patch(_SCHEME) as scheme: - scheme.get_prepared_interval.side_effect = lambda key: [] if key is prop_key else [1] - scheme.advance_preparation.return_value = advanced_prop + scheme.get_prepared_interval.side_effect = ( + lambda key: [] if key is proposal_key else [1] + ) + scheme.advance_preparation.return_value = advanced_proposal scheme.sign.return_value = MagicMock() service._sign_with_key(entry, Slot(1), MagicMock(), "proposal_secret_key") stored = registry.get(ValidatorIndex(0)) assert stored is not None - assert stored.proposal_secret_key is advanced_prop - assert stored.attestation_secret_key is att_key + assert stored.proposal_secret_key is advanced_proposal + assert stored.attestation_secret_key is attestation_key def test_returns_updated_entry_and_signature( self, sync_service: SyncService, key_manager: XmssKeyManager ) -> None: """Return value is (updated ValidatorEntry, Signature) — both fields correct.""" - service, _, entry, att_key, _ = self._setup(sync_service, key_manager) + service, _, entry, attestation_key, _ = self._setup(sync_service, key_manager) advanced = MagicMock(name="adv") - mock_sig = MagicMock(name="ret_sig") + mock_signature = MagicMock(name="ret_sig") with patch(_SCHEME) as scheme: - scheme.get_prepared_interval.side_effect = lambda key: [] if key is att_key else [2] + scheme.get_prepared_interval.side_effect = ( + lambda key: [] if key is attestation_key else [2] + ) scheme.advance_preparation.return_value = advanced - scheme.sign.return_value = mock_sig + scheme.sign.return_value = mock_signature - ret_entry, ret_sig = service._sign_with_key( + returned_entry, returned_signature = service._sign_with_key( entry, Slot(2), MagicMock(), "attestation_secret_key" ) - assert ret_sig is mock_sig - assert ret_entry.attestation_secret_key is advanced + assert returned_signature is mock_signature + assert returned_entry.attestation_secret_key is advanced class TestValidatorServiceBasic: @@ -598,22 +612,22 @@ async def test_attestation_processed_locally( target_slot = Slot(1) published: list[SignedAttestation] = [] - async def capture_att(att: SignedAttestation) -> None: - published.append(att) + async def capture_attestation(attestation: SignedAttestation) -> None: + published.append(attestation) registry = _make_registry(key_manager, 0) service = ValidatorService( sync_service=sync_service, clock=SlotClock(genesis_time=Uint64(0)), registry=registry, - on_attestation=capture_att, + on_attestation=capture_attestation, ) await service._produce_attestations(target_slot) # Attestation was published. assert len(published) == 1 - assert published[0].validator_id == ValidatorIndex(0) + assert published[0].validator_index == ValidatorIndex(0) async def test_gossip_handler_exception_logged_and_attestation_still_published( self, @@ -628,15 +642,15 @@ async def test_gossip_handler_exception_logged_and_attestation_still_published( target_slot = Slot(1) published: list[SignedAttestation] = [] - async def capture_att(att: SignedAttestation) -> None: - published.append(att) + async def capture_attestation(attestation: SignedAttestation) -> None: + published.append(attestation) registry = _make_registry(key_manager, 0) service = ValidatorService( sync_service=sync_service, clock=SlotClock(genesis_time=Uint64(0)), registry=registry, - on_attestation=capture_att, + on_attestation=capture_attestation, ) with ( @@ -841,10 +855,10 @@ async def test_all_validators_attest_including_proposer( key and gossips a separate attestation with the attestation key. """ registry = _make_registry(key_manager, *range(num_validators)) - attestation_validator_ids: list[ValidatorIndex] = [] + attestation_validator_indices: list[ValidatorIndex] = [] - async def capture_attestation(att: SignedAttestation) -> None: - attestation_validator_ids.append(att.validator_id) + async def capture_attestation(attestation: SignedAttestation) -> None: + attestation_validator_indices.append(attestation.validator_index) service = ValidatorService( sync_service=sync_service, @@ -856,7 +870,7 @@ async def capture_attestation(att: SignedAttestation) -> None: await service._produce_attestations(slot) expected = {ValidatorIndex(i) for i in range(num_validators)} - assert set(attestation_validator_ids) == expected + assert set(attestation_validator_indices) == expected assert service.attestations_produced == num_validators @@ -876,7 +890,9 @@ def key_manager(self) -> XmssKeyManager: @pytest.fixture def real_store(self, key_manager: XmssKeyManager) -> Store: """Forkchoice store with validators using real public keys.""" - return make_store(num_validators=6, key_manager=key_manager, validator_id=TEST_VALIDATOR_ID) + return make_store( + num_validators=6, key_manager=key_manager, validator_index=TEST_VALIDATOR_INDEX + ) @pytest.fixture def real_sync_service(self, real_store: Store) -> SyncService: @@ -964,18 +980,18 @@ async def capture_attestation(attestation: SignedAttestation) -> None: assert len(attestations_produced) == 6 - for signed_att in attestations_produced: - validator_id = signed_att.validator_id - public_key = key_manager[validator_id].attestation_keypair.public_key - message_bytes = hash_tree_root(signed_att.data) + for signed_attestation in attestations_produced: + validator_index = signed_attestation.validator_index + public_key = key_manager[validator_index].attestation_keypair.public_key + message_bytes = hash_tree_root(signed_attestation.data) is_valid = TARGET_SIGNATURE_SCHEME.verify( - pk=public_key, - slot=signed_att.data.slot, + public_key=public_key, + slot=signed_attestation.data.slot, message=message_bytes, - sig=signed_att.signature, + signature=signed_attestation.signature, ) - assert is_valid, f"Attestation signature for validator {validator_id} failed" + assert is_valid, f"Attestation signature for validator {validator_index} failed" async def test_attestation_data_references_correct_checkpoints( self, @@ -1008,8 +1024,8 @@ async def capture_attestation(attestation: SignedAttestation) -> None: expected_head_root = store.head expected_source = store.latest_justified - for signed_att in attestations_produced: - data = signed_att.data + for signed_attestation in attestations_produced: + data = signed_attestation.data assert data.head.root == expected_head_root assert data.source == expected_source assert data.slot == Slot(1) @@ -1058,10 +1074,10 @@ async def test_block_includes_pending_attestations( public_keys = [] signatures = [] - for vid in participants: - sig = key_manager.sign_attestation_data(vid, attestation_data) - signatures.append(sig) - public_keys.append(key_manager[vid].attestation_keypair.public_key) + for validator_index in participants: + signature = key_manager.sign_attestation_data(validator_index, attestation_data) + signatures.append(signature) + public_keys.append(key_manager[validator_index].attestation_keypair.public_key) proof = SingleMessageAggregate.aggregate( children=[], @@ -1121,10 +1137,10 @@ async def capture_attestation(attestation: SignedAttestation) -> None: assert len(attestations_by_slot[Slot(1)]) > 0 assert len(attestations_by_slot[Slot(2)]) > 0 - for att in attestations_by_slot[Slot(1)]: - assert att.data.slot == Slot(1) - for att in attestations_by_slot[Slot(2)]: - assert att.data.slot == Slot(2) + for attestation in attestations_by_slot[Slot(1)]: + assert attestation.data.slot == Slot(1) + for attestation in attestations_by_slot[Slot(2)]: + assert attestation.data.slot == Slot(2) async def test_proposer_also_gossips_attestation( self, @@ -1162,9 +1178,11 @@ async def capture_attestation(attestation: SignedAttestation) -> None: assert len(blocks_produced) == 1 assert blocks_produced[0].block.proposer_index == proposer_index - attestation_validator_ids = {att.validator_id for att in attestations_produced} + attestation_validator_indices = { + attestation.validator_index for attestation in attestations_produced + } expected_attesters = {ValidatorIndex(i) for i in range(6)} - assert attestation_validator_ids == expected_attesters + assert attestation_validator_indices == expected_attesters async def test_block_state_root_is_valid( self, @@ -1225,25 +1243,25 @@ async def capture_attestation(attestation: SignedAttestation) -> None: await service._produce_attestations(test_slot) - for signed_att in attestations_produced: - validator_id = signed_att.validator_id - public_key = key_manager[validator_id].attestation_keypair.public_key - message_bytes = hash_tree_root(signed_att.data) + for signed_attestation in attestations_produced: + validator_index = signed_attestation.validator_index + public_key = key_manager[validator_index].attestation_keypair.public_key + message_bytes = hash_tree_root(signed_attestation.data) is_valid = TARGET_SIGNATURE_SCHEME.verify( - pk=public_key, + public_key=public_key, slot=test_slot, message=message_bytes, - sig=signed_att.signature, + signature=signed_attestation.signature, ) - assert is_valid, f"Slot {test_slot} signature failed for validator {validator_id}" + assert is_valid, f"Slot {test_slot} signature failed for validator {validator_index}" wrong_slot = test_slot + Slot(1) is_invalid = TARGET_SIGNATURE_SCHEME.verify( - pk=public_key, + public_key=public_key, slot=wrong_slot, message=message_bytes, - sig=signed_att.signature, + signature=signed_attestation.signature, ) assert not is_invalid, "Signature should not verify with the wrong slot" diff --git a/tests/lean_spec/spec/crypto/test_hash.py b/tests/lean_spec/spec/crypto/test_hash.py index 163cb488b..a71e959e2 100644 --- a/tests/lean_spec/spec/crypto/test_hash.py +++ b/tests/lean_spec/spec/crypto/test_hash.py @@ -241,13 +241,13 @@ class EmptyContainer(Container): """Container with zero fields.""" -def le_padded(value: int, byte_len: int) -> Bytes32: +def le_padded(value: int, byte_length: int) -> Bytes32: """Encode an integer little-endian and right-pad to one chunk.""" - return pad(value.to_bytes(byte_len, "little")) + return pad(value.to_bytes(byte_length, "little")) @pytest.mark.parametrize( - "uint_type, byte_len, value", + "uint_type, byte_length, value", [ (Uint8, 1, 0x00), (Uint8, 1, 0x01), @@ -264,9 +264,9 @@ def le_padded(value: int, byte_len: int) -> Bytes32: (Uint64, 8, 0xFFFFFFFFFFFFFFFF), ], ) -def test_hash_tree_root_uints(uint_type: type, byte_len: int, value: int) -> None: +def test_hash_tree_root_uints(uint_type: type, byte_length: int, value: int) -> None: """Unsigned integers hash as their little-endian bytes padded to one chunk.""" - assert hash_tree_root(uint_type(value)) == le_padded(value, byte_len) + assert hash_tree_root(uint_type(value)) == le_padded(value, byte_length) @pytest.mark.parametrize( diff --git a/tests/lean_spec/spec/crypto/xmss/test_aggregation.py b/tests/lean_spec/spec/crypto/xmss/test_aggregation.py index acac1e6d1..834f97994 100644 --- a/tests/lean_spec/spec/crypto/xmss/test_aggregation.py +++ b/tests/lean_spec/spec/crypto/xmss/test_aggregation.py @@ -18,78 +18,94 @@ def _sign_and_aggregate( key_manager: XmssKeyManager, - validator_ids: list[ValidatorIndex], - att_data_args: tuple[Slot, int, int, Checkpoint], + validator_indices: list[ValidatorIndex], + attestation_data_args: tuple[Slot, int, int, Checkpoint], ) -> SingleMessageAggregate: """Sign attestation data with the given validators and aggregate.""" - slot, head, target, source = att_data_args - att_data = make_attestation_data_simple(slot, make_bytes32(head), make_bytes32(target), source) - data_root = hash_tree_root(att_data) + slot, head, target, source = attestation_data_args + attestation_data = make_attestation_data_simple( + slot, make_bytes32(head), make_bytes32(target), source + ) + data_root = hash_tree_root(attestation_data) raw_xmss = [ ( - vid, - key_manager[vid].attestation_keypair.public_key, - key_manager.sign_attestation_data(vid, att_data), + validator_index, + key_manager[validator_index].attestation_keypair.public_key, + key_manager.sign_attestation_data(validator_index, attestation_data), ) - for vid in validator_ids + for validator_index in validator_indices ] return SingleMessageAggregate.aggregate( children=[], raw_xmss=raw_xmss, message=data_root, - slot=att_data.slot, + slot=attestation_data.slot, ) def test_aggregate_multiple_signatures(key_manager: XmssKeyManager) -> None: """Multiple validators' signatures aggregate into one single-message aggregate proof.""" source = Checkpoint(root=make_bytes32(10), slot=Slot(0)) - att_data = make_attestation_data_simple(Slot(2), make_bytes32(11), make_bytes32(12), source) - vids = [ValidatorIndex(i) for i in range(4)] + attestation_data = make_attestation_data_simple( + Slot(2), make_bytes32(11), make_bytes32(12), source + ) + validator_indices = [ValidatorIndex(i) for i in range(4)] raw_xmss = [ ( - vid, - key_manager[vid].attestation_keypair.public_key, - key_manager.sign_attestation_data(vid, att_data), + validator_index, + key_manager[validator_index].attestation_keypair.public_key, + key_manager.sign_attestation_data(validator_index, attestation_data), ) - for vid in vids + for validator_index in validator_indices ] proof = SingleMessageAggregate.aggregate( children=[], raw_xmss=raw_xmss, - message=hash_tree_root(att_data), - slot=att_data.slot, + message=hash_tree_root(attestation_data), + slot=attestation_data.slot, ) - assert set(proof.participants.to_validator_indices()) == set(vids) + assert set(proof.participants.to_validator_indices()) == set(validator_indices) - public_keys = [key_manager[vid].attestation_keypair.public_key for vid in vids] - proof.verify(public_keys=public_keys, message=hash_tree_root(att_data), slot=att_data.slot) + public_keys = [ + key_manager[validator_index].attestation_keypair.public_key + for validator_index in validator_indices + ] + proof.verify( + public_keys=public_keys, + message=hash_tree_root(attestation_data), + slot=attestation_data.slot, + ) def test_aggregate_children_with_raw_signatures(key_manager: XmssKeyManager) -> None: """A child proof can be combined with additional raw signatures.""" source = Checkpoint(root=make_bytes32(30), slot=Slot(0)) - att_args = (Slot(4), 31, 32, source) - att_data = make_attestation_data_simple( - att_args[0], make_bytes32(att_args[1]), make_bytes32(att_args[2]), att_args[3] + attestation_args = (Slot(4), 31, 32, source) + attestation_data = make_attestation_data_simple( + attestation_args[0], + make_bytes32(attestation_args[1]), + make_bytes32(attestation_args[2]), + attestation_args[3], ) # Child: validators 0, 1 - child = _sign_and_aggregate(key_manager, [ValidatorIndex(0), ValidatorIndex(1)], att_args) + child = _sign_and_aggregate( + key_manager, [ValidatorIndex(0), ValidatorIndex(1)], attestation_args + ) # Additional raw signatures: validators 2, 3 extra_vids = [ValidatorIndex(2), ValidatorIndex(3)] raw_xmss = [ ( - vid, - key_manager[vid].attestation_keypair.public_key, - key_manager.sign_attestation_data(vid, att_data), + validator_index, + key_manager[validator_index].attestation_keypair.public_key, + key_manager.sign_attestation_data(validator_index, attestation_data), ) - for vid in extra_vids + for validator_index in extra_vids ] parent = SingleMessageAggregate.aggregate( @@ -101,87 +117,108 @@ def test_aggregate_children_with_raw_signatures(key_manager: XmssKeyManager) -> for child in [child, child] ], raw_xmss=raw_xmss, - message=hash_tree_root(att_data), - slot=att_data.slot, + message=hash_tree_root(attestation_data), + slot=attestation_data.slot, ) expected_vids = {ValidatorIndex(i) for i in range(4)} assert set(parent.participants.to_validator_indices()) == expected_vids public_keys = [key_manager[ValidatorIndex(i)].attestation_keypair.public_key for i in range(4)] - parent.verify(public_keys=public_keys, message=hash_tree_root(att_data), slot=att_data.slot) + parent.verify( + public_keys=public_keys, + message=hash_tree_root(attestation_data), + slot=attestation_data.slot, + ) def test_aggregate_three_children(key_manager: XmssKeyManager) -> None: """Three child proofs can be aggregated together.""" source = Checkpoint(root=make_bytes32(40), slot=Slot(0)) - att_args = (Slot(5), 41, 42, source) - att_data = make_attestation_data_simple( - att_args[0], make_bytes32(att_args[1]), make_bytes32(att_args[2]), att_args[3] + attestation_args = (Slot(5), 41, 42, source) + attestation_data = make_attestation_data_simple( + attestation_args[0], + make_bytes32(attestation_args[1]), + make_bytes32(attestation_args[2]), + attestation_args[3], ) - child_a = _sign_and_aggregate(key_manager, [ValidatorIndex(0)], att_args) - child_b = _sign_and_aggregate(key_manager, [ValidatorIndex(1)], att_args) - child_c = _sign_and_aggregate(key_manager, [ValidatorIndex(2)], att_args) + child_a = _sign_and_aggregate(key_manager, [ValidatorIndex(0)], attestation_args) + child_b = _sign_and_aggregate(key_manager, [ValidatorIndex(1)], attestation_args) + child_c = _sign_and_aggregate(key_manager, [ValidatorIndex(2)], attestation_args) - child_a_pks = [key_manager[ValidatorIndex(0)].attestation_keypair.public_key] - child_b_pks = [key_manager[ValidatorIndex(1)].attestation_keypair.public_key] - child_c_pks = [key_manager[ValidatorIndex(2)].attestation_keypair.public_key] + child_a_public_keys = [key_manager[ValidatorIndex(0)].attestation_keypair.public_key] + child_b_public_keys = [key_manager[ValidatorIndex(1)].attestation_keypair.public_key] + child_c_public_keys = [key_manager[ValidatorIndex(2)].attestation_keypair.public_key] parent = SingleMessageAggregate.aggregate( - children=[(child_a, child_a_pks), (child_b, child_b_pks), (child_c, child_c_pks)], + children=[ + (child_a, child_a_public_keys), + (child_b, child_b_public_keys), + (child_c, child_c_public_keys), + ], raw_xmss=[], - message=hash_tree_root(att_data), - slot=att_data.slot, + message=hash_tree_root(attestation_data), + slot=attestation_data.slot, ) expected_vids = {ValidatorIndex(i) for i in range(3)} assert set(parent.participants.to_validator_indices()) == expected_vids public_keys = [key_manager[ValidatorIndex(i)].attestation_keypair.public_key for i in range(3)] - parent.verify(public_keys=public_keys, message=hash_tree_root(att_data), slot=att_data.slot) + parent.verify( + public_keys=public_keys, + message=hash_tree_root(attestation_data), + slot=attestation_data.slot, + ) def test_aggregate_children_of_children(key_manager: XmssKeyManager) -> None: """Two-level recursive aggregation: aggregate proofs that are themselves aggregated.""" source = Checkpoint(root=make_bytes32(90), slot=Slot(0)) - att_args = (Slot(6), 91, 92, source) - att_data = make_attestation_data_simple( - att_args[0], make_bytes32(att_args[1]), make_bytes32(att_args[2]), att_args[3] + attestation_args = (Slot(6), 91, 92, source) + attestation_data = make_attestation_data_simple( + attestation_args[0], + make_bytes32(attestation_args[1]), + make_bytes32(attestation_args[2]), + attestation_args[3], ) - msg = hash_tree_root(att_data) + message = hash_tree_root(attestation_data) # Level 0: four individual leaf proofs. - leaf_a = _sign_and_aggregate(key_manager, [ValidatorIndex(0)], att_args) - leaf_b = _sign_and_aggregate(key_manager, [ValidatorIndex(1)], att_args) - leaf_c = _sign_and_aggregate(key_manager, [ValidatorIndex(2)], att_args) - leaf_d = _sign_and_aggregate(key_manager, [ValidatorIndex(3)], att_args) + leaf_a = _sign_and_aggregate(key_manager, [ValidatorIndex(0)], attestation_args) + leaf_b = _sign_and_aggregate(key_manager, [ValidatorIndex(1)], attestation_args) + leaf_c = _sign_and_aggregate(key_manager, [ValidatorIndex(2)], attestation_args) + leaf_d = _sign_and_aggregate(key_manager, [ValidatorIndex(3)], attestation_args) - leaf_a_pks = [key_manager[ValidatorIndex(0)].attestation_keypair.public_key] - leaf_b_pks = [key_manager[ValidatorIndex(1)].attestation_keypair.public_key] - leaf_c_pks = [key_manager[ValidatorIndex(2)].attestation_keypair.public_key] - leaf_d_pks = [key_manager[ValidatorIndex(3)].attestation_keypair.public_key] + leaf_a_public_keys = [key_manager[ValidatorIndex(0)].attestation_keypair.public_key] + leaf_b_public_keys = [key_manager[ValidatorIndex(1)].attestation_keypair.public_key] + leaf_c_public_keys = [key_manager[ValidatorIndex(2)].attestation_keypair.public_key] + leaf_d_public_keys = [key_manager[ValidatorIndex(3)].attestation_keypair.public_key] # Level 1: two intermediate proofs. mid_ab = SingleMessageAggregate.aggregate( - children=[(leaf_a, leaf_a_pks), (leaf_b, leaf_b_pks)], + children=[(leaf_a, leaf_a_public_keys), (leaf_b, leaf_b_public_keys)], raw_xmss=[], - message=msg, - slot=att_data.slot, + message=message, + slot=attestation_data.slot, ) mid_cd = SingleMessageAggregate.aggregate( - children=[(leaf_c, leaf_c_pks), (leaf_d, leaf_d_pks)], + children=[(leaf_c, leaf_c_public_keys), (leaf_d, leaf_d_public_keys)], raw_xmss=[], - message=msg, - slot=att_data.slot, + message=message, + slot=attestation_data.slot, ) # Level 2: final root proof. root = SingleMessageAggregate.aggregate( - children=[(mid_ab, leaf_a_pks + leaf_b_pks), (mid_cd, leaf_c_pks + leaf_d_pks)], + children=[ + (mid_ab, leaf_a_public_keys + leaf_b_public_keys), + (mid_cd, leaf_c_public_keys + leaf_d_public_keys), + ], raw_xmss=[], - message=msg, - slot=att_data.slot, + message=message, + slot=attestation_data.slot, ) assert set(root.participants.to_validator_indices()) == {ValidatorIndex(i) for i in range(4)} @@ -189,43 +226,46 @@ def test_aggregate_children_of_children(key_manager: XmssKeyManager) -> None: public_keys=[ key_manager[ValidatorIndex(i)].attestation_keypair.public_key for i in range(4) ], - message=msg, - slot=att_data.slot, + message=message, + slot=attestation_data.slot, ) def test_aggregate_mixed_children_and_raw_multiple(key_manager: XmssKeyManager) -> None: """Two child proofs combined with additional raw signatures.""" source = Checkpoint(root=make_bytes32(100), slot=Slot(0)) - att_args = (Slot(7), 101, 102, source) - att_data = make_attestation_data_simple( - att_args[0], make_bytes32(att_args[1]), make_bytes32(att_args[2]), att_args[3] + attestation_args = (Slot(7), 101, 102, source) + attestation_data = make_attestation_data_simple( + attestation_args[0], + make_bytes32(attestation_args[1]), + make_bytes32(attestation_args[2]), + attestation_args[3], ) - msg = hash_tree_root(att_data) + message = hash_tree_root(attestation_data) # Two child proofs. - child_a = _sign_and_aggregate(key_manager, [ValidatorIndex(0)], att_args) - child_b = _sign_and_aggregate(key_manager, [ValidatorIndex(1)], att_args) + child_a = _sign_and_aggregate(key_manager, [ValidatorIndex(0)], attestation_args) + child_b = _sign_and_aggregate(key_manager, [ValidatorIndex(1)], attestation_args) - child_a_pks = [key_manager[ValidatorIndex(0)].attestation_keypair.public_key] - child_b_pks = [key_manager[ValidatorIndex(1)].attestation_keypair.public_key] + child_a_public_keys = [key_manager[ValidatorIndex(0)].attestation_keypair.public_key] + child_b_public_keys = [key_manager[ValidatorIndex(1)].attestation_keypair.public_key] # Additional raw signatures from validators 2 and 3. extra_vids = [ValidatorIndex(2), ValidatorIndex(3)] raw_xmss = [ ( - vid, - key_manager[vid].attestation_keypair.public_key, - key_manager.sign_attestation_data(vid, att_data), + validator_index, + key_manager[validator_index].attestation_keypair.public_key, + key_manager.sign_attestation_data(validator_index, attestation_data), ) - for vid in extra_vids + for validator_index in extra_vids ] proof = SingleMessageAggregate.aggregate( - children=[(child_a, child_a_pks), (child_b, child_b_pks)], + children=[(child_a, child_a_public_keys), (child_b, child_b_public_keys)], raw_xmss=raw_xmss, - message=msg, - slot=att_data.slot, + message=message, + slot=attestation_data.slot, ) assert set(proof.participants.to_validator_indices()) == {ValidatorIndex(i) for i in range(4)} @@ -233,20 +273,20 @@ def test_aggregate_mixed_children_and_raw_multiple(key_manager: XmssKeyManager) public_keys=[ key_manager[ValidatorIndex(i)].attestation_keypair.public_key for i in range(4) ], - message=msg, - slot=att_data.slot, + message=message, + slot=attestation_data.slot, ) -def test_single_message_aggregate_verify_rejects_pubkey_count_mismatch( +def test_single_message_aggregate_verify_rejects_public_key_count_mismatch( key_manager: XmssKeyManager, ) -> None: - """Verification refuses a pubkey set whose size does not match the bitfield.""" + """Verification refuses a public_key set whose size does not match the bitfield.""" source = Checkpoint(root=make_bytes32(160), slot=Slot(0)) - att_args = (Slot(2), 161, 162, source) - vids = [ValidatorIndex(0), ValidatorIndex(1)] + attestation_args = (Slot(2), 161, 162, source) + validator_indices = [ValidatorIndex(0), ValidatorIndex(1)] - proof = _sign_and_aggregate(key_manager, vids, att_args) + proof = _sign_and_aggregate(key_manager, validator_indices, attestation_args) # The bitfield names two validators but only one key is supplied. only_one = [key_manager[ValidatorIndex(0)].attestation_keypair.public_key] @@ -254,10 +294,10 @@ def test_single_message_aggregate_verify_rejects_pubkey_count_mismatch( AggregationError, match="single-message aggregate verify expected 2 pubkeys for participants, got 1", ): - proof.verify(public_keys=only_one, message=make_bytes32(161), slot=att_args[0]) + proof.verify(public_keys=only_one, message=make_bytes32(161), slot=attestation_args[0]) -def test_multi_message_aggregate_split_by_msg_rejected_under_test_prover( +def test_multi_message_aggregate_split_by_message_rejected_under_test_prover( key_manager: XmssKeyManager, ) -> None: """Splitting a merged proof aborts under the reduced test-config prover. @@ -267,29 +307,36 @@ def test_multi_message_aggregate_split_by_msg_rejected_under_test_prover( Exercising it here drives the serialization and error-translation path. """ source = Checkpoint(root=make_bytes32(600), slot=Slot(0)) - att_args_a = (Slot(11), 601, 602, source) - att_args_b = (Slot(11), 603, 604, source) - att_data_a = make_attestation_data_simple( - att_args_a[0], make_bytes32(att_args_a[1]), make_bytes32(att_args_a[2]), att_args_a[3] + attestation_args_a = (Slot(11), 601, 602, source) + attestation_args_b = (Slot(11), 603, 604, source) + attestation_data_a = make_attestation_data_simple( + attestation_args_a[0], + make_bytes32(attestation_args_a[1]), + make_bytes32(attestation_args_a[2]), + attestation_args_a[3], ) vids_a = [ValidatorIndex(0), ValidatorIndex(1)] vids_b = [ValidatorIndex(2), ValidatorIndex(3)] - part_a = _sign_and_aggregate(key_manager, vids_a, att_args_a) - part_b = _sign_and_aggregate(key_manager, vids_b, att_args_b) + part_a = _sign_and_aggregate(key_manager, vids_a, attestation_args_a) + part_b = _sign_and_aggregate(key_manager, vids_b, attestation_args_b) - pubkeys_a = [key_manager[vid].attestation_keypair.public_key for vid in vids_a] - pubkeys_b = [key_manager[vid].attestation_keypair.public_key for vid in vids_b] + public_keys_a = [ + key_manager[validator_index].attestation_keypair.public_key for validator_index in vids_a + ] + public_keys_b = [ + key_manager[validator_index].attestation_keypair.public_key for validator_index in vids_b + ] merged = MultiMessageAggregate.aggregate( parts=[part_a, part_b], - public_keys_per_part=[pubkeys_a, pubkeys_b], + public_keys_per_part=[public_keys_a, public_keys_b], ) with pytest.raises(AggregationError, match="multi-message aggregate split failed"): - merged.split_by_msg( - message=hash_tree_root(att_data_a), - public_keys_per_message=[pubkeys_a, pubkeys_b], + merged.split_by_message( + message=hash_tree_root(attestation_data_a), + public_keys_per_message=[public_keys_a, public_keys_b], participants=part_a.participants, ) @@ -297,31 +344,39 @@ def test_multi_message_aggregate_split_by_msg_rejected_under_test_prover( def test_aggregate_wrong_message_fails_verification(key_manager: XmssKeyManager) -> None: """Verification fails when the caller passes a message that does not match the proof.""" source = Checkpoint(root=make_bytes32(120), slot=Slot(0)) - att_data = make_attestation_data_simple(Slot(1), make_bytes32(121), make_bytes32(122), source) - vid = ValidatorIndex(0) + attestation_data = make_attestation_data_simple( + Slot(1), make_bytes32(121), make_bytes32(122), source + ) + validator_index = ValidatorIndex(0) - proof = _sign_and_aggregate(key_manager, [vid], (att_data.slot, 121, 122, source)) + proof = _sign_and_aggregate( + key_manager, [validator_index], (attestation_data.slot, 121, 122, source) + ) with pytest.raises(AggregationError, match="verification failed"): proof.verify( - public_keys=[key_manager[vid].attestation_keypair.public_key], + public_keys=[key_manager[validator_index].attestation_keypair.public_key], message=make_bytes32(123), - slot=att_data.slot, + slot=attestation_data.slot, ) def test_aggregate_wrong_slot_fails_verification(key_manager: XmssKeyManager) -> None: """Verification fails when the caller passes a slot that does not match the proof.""" source = Checkpoint(root=make_bytes32(130), slot=Slot(0)) - att_data = make_attestation_data_simple(Slot(2), make_bytes32(131), make_bytes32(132), source) - vid = ValidatorIndex(1) + attestation_data = make_attestation_data_simple( + Slot(2), make_bytes32(131), make_bytes32(132), source + ) + validator_index = ValidatorIndex(1) - proof = _sign_and_aggregate(key_manager, [vid], (att_data.slot, 131, 132, source)) + proof = _sign_and_aggregate( + key_manager, [validator_index], (attestation_data.slot, 131, 132, source) + ) with pytest.raises(AggregationError, match="verification failed"): proof.verify( - public_keys=[key_manager[vid].attestation_keypair.public_key], - message=hash_tree_root(att_data), + public_keys=[key_manager[validator_index].attestation_keypair.public_key], + message=hash_tree_root(attestation_data), slot=Slot(99), ) @@ -329,10 +384,14 @@ def test_aggregate_wrong_slot_fails_verification(key_manager: XmssKeyManager) -> def test_aggregate_corrupted_proof_fails_verification(key_manager: XmssKeyManager) -> None: """Verification fails when proof bytes are corrupted.""" source = Checkpoint(root=make_bytes32(140), slot=Slot(0)) - att_data = make_attestation_data_simple(Slot(3), make_bytes32(141), make_bytes32(142), source) - vid = ValidatorIndex(2) + attestation_data = make_attestation_data_simple( + Slot(3), make_bytes32(141), make_bytes32(142), source + ) + validator_index = ValidatorIndex(2) - proof = _sign_and_aggregate(key_manager, [vid], (att_data.slot, 141, 142, source)) + proof = _sign_and_aggregate( + key_manager, [validator_index], (attestation_data.slot, 141, 142, source) + ) corrupted_bytes = bytearray(proof.proof.data) corrupted_bytes[10] ^= 0xFF @@ -344,34 +403,37 @@ def test_aggregate_corrupted_proof_fails_verification(key_manager: XmssKeyManage with pytest.raises(AggregationError, match="verification failed"): proof.verify( - public_keys=[key_manager[vid].attestation_keypair.public_key], - message=hash_tree_root(att_data), - slot=att_data.slot, + public_keys=[key_manager[validator_index].attestation_keypair.public_key], + message=hash_tree_root(attestation_data), + slot=attestation_data.slot, ) def test_aggregate_child_signed_different_message_fails(key_manager: XmssKeyManager) -> None: """Aggregating children that signed different messages fails inside the binding.""" source = Checkpoint(root=make_bytes32(150), slot=Slot(0)) - att_args_a = (Slot(4), 151, 152, source) - att_args_b = (Slot(4), 161, 162, source) - att_data_b = make_attestation_data_simple( - att_args_b[0], make_bytes32(att_args_b[1]), make_bytes32(att_args_b[2]), att_args_b[3] + attestation_args_a = (Slot(4), 151, 152, source) + attestation_args_b = (Slot(4), 161, 162, source) + attestation_data_b = make_attestation_data_simple( + attestation_args_b[0], + make_bytes32(attestation_args_b[1]), + make_bytes32(attestation_args_b[2]), + attestation_args_b[3], ) - child_a = _sign_and_aggregate(key_manager, [ValidatorIndex(0)], att_args_a) - child_b = _sign_and_aggregate(key_manager, [ValidatorIndex(1)], att_args_b) + child_a = _sign_and_aggregate(key_manager, [ValidatorIndex(0)], attestation_args_a) + child_b = _sign_and_aggregate(key_manager, [ValidatorIndex(1)], attestation_args_b) - child_a_pks = [key_manager[ValidatorIndex(0)].attestation_keypair.public_key] - child_b_pks = [key_manager[ValidatorIndex(1)].attestation_keypair.public_key] + child_a_public_keys = [key_manager[ValidatorIndex(0)].attestation_keypair.public_key] + child_b_public_keys = [key_manager[ValidatorIndex(1)].attestation_keypair.public_key] # The binding rejects mismatching messages during recursive aggregation. with pytest.raises(AggregationError): SingleMessageAggregate.aggregate( - children=[(child_a, child_a_pks), (child_b, child_b_pks)], + children=[(child_a, child_a_public_keys), (child_b, child_b_public_keys)], raw_xmss=[], - message=hash_tree_root(att_data_b), - slot=att_data_b.slot, + message=hash_tree_root(attestation_data_b), + slot=attestation_data_b.slot, ) @@ -381,19 +443,19 @@ def test_multi_message_aggregate_rejects_empty_parts() -> None: MultiMessageAggregate.aggregate(parts=[], public_keys_per_part=[]) -def test_multi_message_aggregate_rejects_mismatched_pubkey_layout( +def test_multi_message_aggregate_rejects_mismatched_public_key_layout( key_manager: XmssKeyManager, ) -> None: - """The per-part pubkey layout must match the participant count of each part.""" + """The per-part public_key layout must match the participant count of each part.""" source = Checkpoint(root=make_bytes32(200), slot=Slot(0)) - att_args = (Slot(7), 201, 202, source) + attestation_args = (Slot(7), 201, 202, source) part = _sign_and_aggregate( key_manager, [ValidatorIndex(0), ValidatorIndex(1)], - att_args, + attestation_args, ) - # Layout claims one pubkey for a part that binds two participants. + # Layout claims one public_key for a part that binds two participants. wrong_layout = [[key_manager[ValidatorIndex(0)].attestation_keypair.public_key]] with pytest.raises(AggregationError, match="expected 2 pubkeys, got 1"): @@ -406,11 +468,14 @@ def test_multi_message_aggregate_rejects_mismatched_pubkey_layout( def test_multi_message_aggregate_propagates_prover_error(key_manager: XmssKeyManager) -> None: """A corrupted component proof makes the merge prover reject the inputs.""" source = Checkpoint(root=make_bytes32(210), slot=Slot(0)) - att_args = (Slot(8), 211, 212, source) - vids = [ValidatorIndex(0), ValidatorIndex(1)] + attestation_args = (Slot(8), 211, 212, source) + validator_indices = [ValidatorIndex(0), ValidatorIndex(1)] - part = _sign_and_aggregate(key_manager, vids, att_args) - pubkeys = [key_manager[vid].attestation_keypair.public_key for vid in vids] + part = _sign_and_aggregate(key_manager, validator_indices, attestation_args) + public_keys = [ + key_manager[validator_index].attestation_keypair.public_key + for validator_index in validator_indices + ] corrupted_bytes = bytearray(part.proof.data) corrupted_bytes[10] ^= 0xFF @@ -421,7 +486,7 @@ def test_multi_message_aggregate_propagates_prover_error(key_manager: XmssKeyMan ) with pytest.raises(AggregationError, match="merge_many_type_1 failed"): - MultiMessageAggregate.aggregate(parts=[part], public_keys_per_part=[pubkeys]) + MultiMessageAggregate.aggregate(parts=[part], public_keys_per_part=[public_keys]) def test_multi_message_aggregate_verify_round_trip(key_manager: XmssKeyManager) -> None: @@ -429,39 +494,43 @@ def test_multi_message_aggregate_verify_round_trip(key_manager: XmssKeyManager) source = Checkpoint(root=make_bytes32(300), slot=Slot(0)) # Two distinct messages signed by disjoint validator sets. - att_args_a = (Slot(8), 301, 302, source) - att_args_b = (Slot(8), 303, 304, source) - att_data_a = make_attestation_data_simple( - att_args_a[0], - make_bytes32(att_args_a[1]), - make_bytes32(att_args_a[2]), - att_args_a[3], + attestation_args_a = (Slot(8), 301, 302, source) + attestation_args_b = (Slot(8), 303, 304, source) + attestation_data_a = make_attestation_data_simple( + attestation_args_a[0], + make_bytes32(attestation_args_a[1]), + make_bytes32(attestation_args_a[2]), + attestation_args_a[3], ) - att_data_b = make_attestation_data_simple( - att_args_b[0], - make_bytes32(att_args_b[1]), - make_bytes32(att_args_b[2]), - att_args_b[3], + attestation_data_b = make_attestation_data_simple( + attestation_args_b[0], + make_bytes32(attestation_args_b[1]), + make_bytes32(attestation_args_b[2]), + attestation_args_b[3], ) vids_a = [ValidatorIndex(0), ValidatorIndex(1)] vids_b = [ValidatorIndex(2), ValidatorIndex(3)] - part_a = _sign_and_aggregate(key_manager, vids_a, att_args_a) - part_b = _sign_and_aggregate(key_manager, vids_b, att_args_b) + part_a = _sign_and_aggregate(key_manager, vids_a, attestation_args_a) + part_b = _sign_and_aggregate(key_manager, vids_b, attestation_args_b) - pubkeys_a = [key_manager[vid].attestation_keypair.public_key for vid in vids_a] - pubkeys_b = [key_manager[vid].attestation_keypair.public_key for vid in vids_b] + public_keys_a = [ + key_manager[validator_index].attestation_keypair.public_key for validator_index in vids_a + ] + public_keys_b = [ + key_manager[validator_index].attestation_keypair.public_key for validator_index in vids_b + ] merged = MultiMessageAggregate.aggregate( parts=[part_a, part_b], - public_keys_per_part=[pubkeys_a, pubkeys_b], + public_keys_per_part=[public_keys_a, public_keys_b], ) merged.verify( - public_keys_per_message=[pubkeys_a, pubkeys_b], + public_keys_per_message=[public_keys_a, public_keys_b], messages=[ - (hash_tree_root(att_data_a), att_data_a.slot), - (hash_tree_root(att_data_b), att_data_b.slot), + (hash_tree_root(attestation_data_a), attestation_data_a.slot), + (hash_tree_root(attestation_data_b), attestation_data_b.slot), ], ) @@ -474,42 +543,46 @@ def test_multi_message_aggregate_verify_rejects_message_swap(key_manager: XmssKe """ source = Checkpoint(root=make_bytes32(400), slot=Slot(0)) - att_args_a = (Slot(9), 401, 402, source) - att_args_b = (Slot(9), 403, 404, source) - att_data_a = make_attestation_data_simple( - att_args_a[0], - make_bytes32(att_args_a[1]), - make_bytes32(att_args_a[2]), - att_args_a[3], + attestation_args_a = (Slot(9), 401, 402, source) + attestation_args_b = (Slot(9), 403, 404, source) + attestation_data_a = make_attestation_data_simple( + attestation_args_a[0], + make_bytes32(attestation_args_a[1]), + make_bytes32(attestation_args_a[2]), + attestation_args_a[3], ) - att_data_b = make_attestation_data_simple( - att_args_b[0], - make_bytes32(att_args_b[1]), - make_bytes32(att_args_b[2]), - att_args_b[3], + attestation_data_b = make_attestation_data_simple( + attestation_args_b[0], + make_bytes32(attestation_args_b[1]), + make_bytes32(attestation_args_b[2]), + attestation_args_b[3], ) vids_a = [ValidatorIndex(0), ValidatorIndex(1)] vids_b = [ValidatorIndex(2), ValidatorIndex(3)] - part_a = _sign_and_aggregate(key_manager, vids_a, att_args_a) - part_b = _sign_and_aggregate(key_manager, vids_b, att_args_b) + part_a = _sign_and_aggregate(key_manager, vids_a, attestation_args_a) + part_b = _sign_and_aggregate(key_manager, vids_b, attestation_args_b) - pubkeys_a = [key_manager[vid].attestation_keypair.public_key for vid in vids_a] - pubkeys_b = [key_manager[vid].attestation_keypair.public_key for vid in vids_b] + public_keys_a = [ + key_manager[validator_index].attestation_keypair.public_key for validator_index in vids_a + ] + public_keys_b = [ + key_manager[validator_index].attestation_keypair.public_key for validator_index in vids_b + ] merged = MultiMessageAggregate.aggregate( parts=[part_a, part_b], - public_keys_per_part=[pubkeys_a, pubkeys_b], + public_keys_per_part=[public_keys_a, public_keys_b], ) - # Swap the parallel messages: part_a's pubkeys are now paired with part_b's + # Swap the parallel messages: part_a's public_keys are now paired with part_b's # message and vice versa. with pytest.raises(AggregationError, match="verification failed"): merged.verify( - public_keys_per_message=[pubkeys_a, pubkeys_b], + public_keys_per_message=[public_keys_a, public_keys_b], messages=[ - (hash_tree_root(att_data_b), att_data_b.slot), - (hash_tree_root(att_data_a), att_data_a.slot), + (hash_tree_root(attestation_data_b), attestation_data_b.slot), + (hash_tree_root(attestation_data_a), attestation_data_a.slot), ], ) @@ -519,19 +592,22 @@ def test_multi_message_aggregate_verify_rejects_mismatched_messages_length( ) -> None: """messages must have the same length as public_keys_per_message.""" source = Checkpoint(root=make_bytes32(500), slot=Slot(0)) - att_args = (Slot(10), 501, 502, source) + attestation_args = (Slot(10), 501, 502, source) - vids = [ValidatorIndex(0), ValidatorIndex(1)] - part = _sign_and_aggregate(key_manager, vids, att_args) - pubkeys = [key_manager[vid].attestation_keypair.public_key for vid in vids] + validator_indices = [ValidatorIndex(0), ValidatorIndex(1)] + part = _sign_and_aggregate(key_manager, validator_indices, attestation_args) + public_keys = [ + key_manager[validator_index].attestation_keypair.public_key + for validator_index in validator_indices + ] merged = MultiMessageAggregate.aggregate( parts=[part], - public_keys_per_part=[pubkeys], + public_keys_per_part=[public_keys], ) with pytest.raises(AggregationError, match="expected 1 message bindings, got 0"): merged.verify( - public_keys_per_message=[pubkeys], + public_keys_per_message=[public_keys], messages=[], ) diff --git a/tests/lean_spec/spec/crypto/xmss/test_constants.py b/tests/lean_spec/spec/crypto/xmss/test_constants.py index 4208c52fb..265b8b79c 100644 --- a/tests/lean_spec/spec/crypto/xmss/test_constants.py +++ b/tests/lean_spec/spec/crypto/xmss/test_constants.py @@ -71,22 +71,22 @@ def test_leaves_per_bottom_tree_is_square_root_of_lifetime() -> None: pytest.param(16, 8, 2, id="two full field elements exactly cover sixteen digits"), ], ) -def test_mh_hash_len_rounds_up(dimension: int, z: int, expected: int) -> None: +def test_mh_hash_length_rounds_up(dimension: int, z: int, expected: int) -> None: """The aborting-decode output length is the dimension divided by digits, rounded up.""" kwargs = _valid_config_kwargs() kwargs["DIMENSION"] = dimension kwargs["Z"] = z - assert XmssConfig(**kwargs).MH_HASH_LEN_FE == expected + assert XmssConfig(**kwargs).MH_HASH_LENGTH_FIELD_ELEMENTS == expected -def test_signature_len_bytes_matches_layout() -> None: +def test_signature_length_bytes_matches_layout() -> None: """The advertised signature length equals the sum of its SSZ-encoded fields.""" config = TEST_CONFIG - path = config.LOG_LIFETIME * config.HASH_LEN_FE * P_BYTES - rho = config.RAND_LEN_FE * P_BYTES - hashes = config.DIMENSION * config.HASH_LEN_FE * P_BYTES + path = config.LOG_LIFETIME * config.HASH_LENGTH_FIELD_ELEMENTS * P_BYTES + rho = config.RAND_LENGTH_FIELD_ELEMENTS * P_BYTES + hashes = config.DIMENSION * config.HASH_LENGTH_FIELD_ELEMENTS * P_BYTES expected = path + rho + hashes + 3 * BYTES_PER_LENGTH_OFFSET - assert config.SIGNATURE_LEN_BYTES == expected + assert config.SIGNATURE_LENGTH_BYTES == expected @pytest.mark.parametrize( @@ -98,11 +98,11 @@ def test_signature_len_bytes_matches_layout() -> None: ("Q", 127), ("TARGET_SUM", 200), ("LOG_LIFETIME", 32), - ("PARAMETER_LEN", 5), - ("TWEAK_LEN_FE", 2), - ("MSG_LEN_FE", 9), - ("RAND_LEN_FE", 7), - ("HASH_LEN_FE", 8), + ("PARAMETER_LENGTH", 5), + ("TWEAK_LENGTH_FIELD_ELEMENTS", 2), + ("MESSAGE_LENGTH_FIELD_ELEMENTS", 9), + ("RAND_LENGTH_FIELD_ELEMENTS", 7), + ("HASH_LENGTH_FIELD_ELEMENTS", 8), ("CAPACITY", 9), ], ) @@ -139,13 +139,13 @@ def _compute_security_levels(config: XmssConfig) -> dict[str, float]: base = config.BASE # Each field element contributes floor(log2(P)) = 31 bits. - fe_bits = 31 - bits_digest = config.HASH_LEN_FE * fe_bits - bits_param = config.PARAMETER_LEN * fe_bits - bits_rand = config.RAND_LEN_FE * fe_bits + field_element_bits = 31 + bits_digest = config.HASH_LENGTH_FIELD_ELEMENTS * field_element_bits + bits_param = config.PARAMETER_LENGTH * field_element_bits + bits_rand = config.RAND_LENGTH_FIELD_ELEMENTS * field_element_bits # Raw message hash output is v chunks of w bits each. - bits_msg = v * w_bits + bits_message = v * w_bits # Abort correction from [HKKTW26] Corollary 1, Remark 14. # @@ -159,7 +159,7 @@ def _compute_security_levels(config: XmssConfig) -> dict[str, float]: non_abort_total = ((q * wz) / P) ** ell abort_correction_bits = -math.log2(non_abort_total) - bits_msg_eff = bits_msg + abort_correction_bits + bits_message_eff = bits_message + abort_correction_bits log5 = math.log2(5) log12 = math.log2(12) @@ -173,7 +173,7 @@ def _compute_security_levels(config: XmssConfig) -> dict[str, float]: k_classical = min( bits_digest - log5 - 2 * w_bits - log_lifetime - logv, bits_param - log5 - 3, - bits_msg_eff - log5 - 1, + bits_message_eff - log5 - 1, bits_rand - log5 - logqs - log_max_tries - 1, ) @@ -181,7 +181,7 @@ def _compute_security_levels(config: XmssConfig) -> dict[str, float]: k_quantum = min( bits_digest / 2 - log5 - 2 * w_bits - log_lifetime - logv - log12, (bits_param - 5) / 2 - log5 - 2, - (bits_msg_eff - 3) / 2 - log5 - 1, + (bits_message_eff - 3) / 2 - log5 - 1, (bits_rand - logqs) / 2 - log5 - log3 - log_max_tries, ) @@ -230,8 +230,8 @@ def test_prod_abort_probability_is_negligible() -> None: """The aborting decode rejection probability is below two to the minus 28.""" config = PROD_CONFIG ell = math.ceil(config.DIMENSION / config.Z) - non_abort_per_fe = (config.Q * config.BASE**config.Z) / P - total_non_abort = non_abort_per_fe**ell + non_abort_per_field_element = (config.Q * config.BASE**config.Z) / P + total_non_abort = non_abort_per_field_element**ell assert 1 - total_non_abort < 2**-28 @@ -253,10 +253,10 @@ def test_prod_z_equals_twenty_four_over_digit_width() -> None: assert PROD_CONFIG.Z == 24 // w_bits -def test_prod_mh_hash_len_covers_dimension() -> None: +def test_prod_mh_hash_length_covers_dimension() -> None: """The aborting-decode output produces at least one digit per hash chain.""" config = PROD_CONFIG - assert config.MH_HASH_LEN_FE * config.Z >= config.DIMENSION + assert config.MH_HASH_LENGTH_FIELD_ELEMENTS * config.Z >= config.DIMENSION def test_prod_binding_constraint_is_message_hash() -> None: @@ -264,12 +264,12 @@ def test_prod_binding_constraint_is_message_hash() -> None: config = PROD_CONFIG v = config.DIMENSION w_bits = int(math.log2(config.BASE)) - fe_bits = 31 + field_element_bits = 31 - bits_digest = config.HASH_LEN_FE * fe_bits - bits_param = config.PARAMETER_LEN * fe_bits - bits_rand = config.RAND_LEN_FE * fe_bits - bits_msg = v * w_bits + bits_digest = config.HASH_LENGTH_FIELD_ELEMENTS * field_element_bits + bits_param = config.PARAMETER_LENGTH * field_element_bits + bits_rand = config.RAND_LENGTH_FIELD_ELEMENTS * field_element_bits + bits_message = v * w_bits log5 = math.log2(5) log_lifetime = math.log2(config.LIFETIME) @@ -279,7 +279,7 @@ def test_prod_binding_constraint_is_message_hash() -> None: classical_bounds = [ bits_digest - log5 - 2 * w_bits - log_lifetime - logv, bits_param - log5 - 3, - bits_msg - log5 - 1, + bits_message - log5 - 1, bits_rand - log5 - log_lifetime - log_max_tries - 1, ] assert classical_bounds.index(min(classical_bounds)) == 2 diff --git a/tests/lean_spec/spec/crypto/xmss/test_containers.py b/tests/lean_spec/spec/crypto/xmss/test_containers.py index feb1aa99d..fd404d2a4 100644 --- a/tests/lean_spec/spec/crypto/xmss/test_containers.py +++ b/tests/lean_spec/spec/crypto/xmss/test_containers.py @@ -281,7 +281,7 @@ def test_public_key_ssz_roundtrip(signed_key_pair: KeyPair) -> None: def test_public_key_encoded_size_matches_layout(signed_key_pair: KeyPair) -> None: """The encoded public key is the digest plus parameter packed into field bytes.""" encoded = signed_key_pair.public_key.encode_bytes() - expected = (TEST_CONFIG.HASH_LEN_FE + TEST_CONFIG.PARAMETER_LEN) * P_BYTES + expected = (TEST_CONFIG.HASH_LENGTH_FIELD_ELEMENTS + TEST_CONFIG.PARAMETER_LENGTH) * P_BYTES assert len(encoded) == expected @@ -298,7 +298,7 @@ def test_signature_is_fixed_size() -> None: def test_signature_byte_length_matches_config() -> None: """The signature byte length matches the configured fixed length.""" - assert Signature.get_byte_length() == TEST_CONFIG.SIGNATURE_LEN_BYTES + assert Signature.get_byte_length() == TEST_CONFIG.SIGNATURE_LENGTH_BYTES def test_signature_ssz_roundtrip(sample_signature: Signature) -> None: @@ -308,7 +308,7 @@ def test_signature_ssz_roundtrip(sample_signature: Signature) -> None: def test_signature_encoded_size_matches_config(sample_signature: Signature) -> None: """The encoded signature length matches the advertised fixed length.""" - assert len(sample_signature.encode_bytes()) == TEST_CONFIG.SIGNATURE_LEN_BYTES + assert len(sample_signature.encode_bytes()) == TEST_CONFIG.SIGNATURE_LENGTH_BYTES def test_signature_json_is_prefixed_hex(sample_signature: Signature) -> None: diff --git a/tests/lean_spec/spec/crypto/xmss/test_encoding.py b/tests/lean_spec/spec/crypto/xmss/test_encoding.py index cc7c2a247..b492bef2c 100644 --- a/tests/lean_spec/spec/crypto/xmss/test_encoding.py +++ b/tests/lean_spec/spec/crypto/xmss/test_encoding.py @@ -20,27 +20,29 @@ def _parameter() -> Parameter: """Return a fixed public parameter for encoding tests.""" - return Parameter(data=[Fp(value=1)] * TEST_CONFIG.PARAMETER_LEN) + return Parameter(data=[Fp(value=1)] * TEST_CONFIG.PARAMETER_LENGTH) def test_encode_message_zero_is_all_zero_limbs() -> None: """An all-zero message encodes to all-zero field elements.""" encoded = encode_message(TEST_CONFIG, Bytes32(b"\x00" * 32)) - assert encoded == [Fp(value=0)] * TEST_CONFIG.MSG_LEN_FE + assert encoded == [Fp(value=0)] * TEST_CONFIG.MESSAGE_LENGTH_FIELD_ELEMENTS def test_encode_message_reads_little_endian() -> None: """A maximal message encodes to its little-endian base-P decomposition.""" message = Bytes32(b"\xff" * 32) acc = int.from_bytes(message, "little") - assert encode_message(TEST_CONFIG, message) == int_to_base_p(acc, TEST_CONFIG.MSG_LEN_FE) + assert encode_message(TEST_CONFIG, message) == int_to_base_p( + acc, TEST_CONFIG.MESSAGE_LENGTH_FIELD_ELEMENTS + ) @pytest.mark.parametrize("epoch", [0, 42, 2**32 - 1]) def test_encode_epoch_matches_prefixed_decomposition(epoch: int) -> None: """An epoch encodes to its value shifted above the message prefix.""" acc = (epoch << 8) | TWEAK_PREFIX_MESSAGE - expected = int_to_base_p(acc, TEST_CONFIG.TWEAK_LEN_FE) + expected = int_to_base_p(acc, TEST_CONFIG.TWEAK_LENGTH_FIELD_ELEMENTS) assert encode_epoch(TEST_CONFIG, Uint64(epoch)) == expected @@ -54,22 +56,24 @@ def test_aborting_decode_known_decomposition() -> None: """A hand-built quotient decodes to its base-BASE digits, truncated to the dimension.""" config = TEST_CONFIG d_value = 5 - fe_list = [Fp(value=config.Q * d_value)] * config.MH_HASH_LEN_FE + field_element_list = [Fp(value=config.Q * d_value)] * config.MH_HASH_LENGTH_FIELD_ELEMENTS - expected_per_fe = [] + expected_per_field_element = [] remaining = d_value for _ in range(config.Z): - expected_per_fe.append(remaining % config.BASE) + expected_per_field_element.append(remaining % config.BASE) remaining //= config.BASE - expected = (expected_per_fe * config.MH_HASH_LEN_FE)[: config.DIMENSION] + expected = (expected_per_field_element * config.MH_HASH_LENGTH_FIELD_ELEMENTS)[ + : config.DIMENSION + ] - assert aborting_decode(config, fe_list) == expected + assert aborting_decode(config, field_element_list) == expected def test_aborting_decode_accepts_largest_valid_element() -> None: """The element just below the abort threshold decodes successfully.""" config = TEST_CONFIG - result = aborting_decode(config, [Fp(value=P - 2)] * config.MH_HASH_LEN_FE) + result = aborting_decode(config, [Fp(value=P - 2)] * config.MH_HASH_LENGTH_FIELD_ELEMENTS) assert result is not None assert len(result) == config.DIMENSION assert all(0 <= d < config.BASE for d in result) @@ -83,8 +87,8 @@ def test_aborting_decode_rejects_threshold_element() -> None: def test_message_hash_yields_valid_codeword() -> None: """The message hash decodes to a codeword of dimension digits in range.""" config = TEST_CONFIG - parameter = Parameter(data=random_field_elements(config.PARAMETER_LEN)) - randomness = Randomness(data=random_field_elements(config.RAND_LEN_FE)) + parameter = Parameter(data=random_field_elements(config.PARAMETER_LENGTH)) + randomness = Randomness(data=random_field_elements(config.RAND_LENGTH_FIELD_ELEMENTS)) result = message_hash( TEST_POSEIDON, config, parameter, Uint64(313), randomness, Bytes32(b"\xaa" * 32) @@ -100,7 +104,7 @@ def test_target_sum_encode_accepts_codeword_on_target_layer() -> None: config = TEST_CONFIG parameter = _parameter() # Attempt counter three lands the all-zero message on the target-sum layer. - rho = Randomness(data=int_to_base_p(3, config.RAND_LEN_FE)) + rho = Randomness(data=int_to_base_p(3, config.RAND_LENGTH_FIELD_ELEMENTS)) codeword = target_sum_encode( TEST_POSEIDON, config, parameter, Bytes32(b"\x00" * 32), rho, Uint64(0) @@ -115,7 +119,7 @@ def test_target_sum_encode_rejects_codeword_off_target_layer() -> None: config = TEST_CONFIG parameter = _parameter() # Attempt counter zero produces a codeword whose digits do not sum to the target. - rho = Randomness(data=int_to_base_p(0, config.RAND_LEN_FE)) + rho = Randomness(data=int_to_base_p(0, config.RAND_LENGTH_FIELD_ELEMENTS)) assert ( target_sum_encode(TEST_POSEIDON, config, parameter, Bytes32(b"\x00" * 32), rho, Uint64(0)) @@ -140,7 +144,7 @@ def test_target_sum_encode_propagates_aborting_decode_failure( TEST_CONFIG, _parameter(), Bytes32(b"\x00" * 32), - Randomness(data=int_to_base_p(0, TEST_CONFIG.RAND_LEN_FE)), + Randomness(data=int_to_base_p(0, TEST_CONFIG.RAND_LENGTH_FIELD_ELEMENTS)), Uint64(0), ) is None diff --git a/tests/lean_spec/spec/crypto/xmss/test_field.py b/tests/lean_spec/spec/crypto/xmss/test_field.py index 2b188fbf2..fa34d693a 100644 --- a/tests/lean_spec/spec/crypto/xmss/test_field.py +++ b/tests/lean_spec/spec/crypto/xmss/test_field.py @@ -47,7 +47,7 @@ def test_int_to_base_p_roundtrip_is_reversible() -> None: """Decomposing then recomposing recovers the original integer.""" num_limbs = 5 original_limbs = [secrets.randbelow(P) for _ in range(num_limbs)] - original_value = sum(val * (P**i) for i, val in enumerate(original_limbs)) + original_value = sum(value * (P**i) for i, value in enumerate(original_limbs)) decomposed = [int(fp) for fp in int_to_base_p(original_value, num_limbs)] @@ -78,11 +78,11 @@ def test_random_parameter_has_parameter_length() -> None: """A sampled parameter has the configured parameter length.""" parameter = random_parameter(TEST_CONFIG) assert isinstance(parameter, Parameter) - assert len(parameter.data) == TEST_CONFIG.PARAMETER_LEN + assert len(parameter.data) == TEST_CONFIG.PARAMETER_LENGTH def test_random_domain_has_hash_length() -> None: """A sampled domain vector has the configured digest length.""" domain = random_domain(TEST_CONFIG) assert isinstance(domain, HashDigestVector) - assert len(domain.data) == TEST_CONFIG.HASH_LEN_FE + assert len(domain.data) == TEST_CONFIG.HASH_LENGTH_FIELD_ELEMENTS diff --git a/tests/lean_spec/spec/crypto/xmss/test_interface.py b/tests/lean_spec/spec/crypto/xmss/test_interface.py index c51b5b521..def181a62 100644 --- a/tests/lean_spec/spec/crypto/xmss/test_interface.py +++ b/tests/lean_spec/spec/crypto/xmss/test_interface.py @@ -29,7 +29,7 @@ def _test_correctness_roundtrip( # # Generate a new key pair for the specified active range. kp = scheme.key_gen(Slot(activation_slot), Uint64(num_active_slots)) - pk, sk = kp.public_key, kp.secret_key + public_key, secret_key = kp.public_key, kp.secret_key # SIGN & VERIFY # @@ -40,10 +40,10 @@ def _test_correctness_roundtrip( # Sign the message at the chosen slot. # # This might take a moment as it may try multiple rho values. - signature = scheme.sign(sk, test_slot, message) + signature = scheme.sign(secret_key, test_slot, message) # Verification of the valid signature must succeed. - is_valid = scheme.verify(pk, test_slot, message, signature) + is_valid = scheme.verify(public_key, test_slot, message, signature) assert is_valid, "Verification of a valid signature failed" # TEST INVALID CASES @@ -59,25 +59,30 @@ def _test_correctness_roundtrip( # # We detect this by checking if both messages encode to the same codeword. original_codeword = target_sum_encode( - scheme.poseidon, scheme.config, pk.parameter, message, signature.rho, test_slot + scheme.poseidon, scheme.config, public_key.parameter, message, signature.rho, test_slot ) tampered_codeword = target_sum_encode( - scheme.poseidon, scheme.config, pk.parameter, tampered_message, signature.rho, test_slot + scheme.poseidon, + scheme.config, + public_key.parameter, + tampered_message, + signature.rho, + test_slot, ) if tampered_codeword != original_codeword: # Different codewords: verification must fail - is_invalid_msg = scheme.verify(pk, test_slot, tampered_message, signature) - assert not is_invalid_msg, "Verification succeeded for a tampered message" + is_invalid_message = scheme.verify(public_key, test_slot, tampered_message, signature) + assert not is_invalid_message, "Verification succeeded for a tampered message" else: # Codeword collision: verification succeeds (expected with small test parameters) - is_collision_valid = scheme.verify(pk, test_slot, tampered_message, signature) + is_collision_valid = scheme.verify(public_key, test_slot, tampered_message, signature) assert is_collision_valid, "Verification failed despite identical codewords" # Verification must fail if the slot is incorrect. if num_active_slots > 1: wrong_slot = Slot(int(test_slot) + 1) - is_invalid_slot = scheme.verify(pk, wrong_slot, message, signature) + is_invalid_slot = scheme.verify(public_key, wrong_slot, message, signature) assert not is_invalid_slot, "Verification succeeded for an incorrect slot" @@ -105,9 +110,9 @@ def test_get_activation_interval() -> None: """Tests that get_activation_interval returns the correct range.""" scheme = TEST_SIGNATURE_SCHEME # Use 8 slots (half of LIFETIME=16) - sk = scheme.key_gen(Slot(4), Uint64(8)).secret_key + secret_key = scheme.key_gen(Slot(4), Uint64(8)).secret_key - interval = scheme.get_activation_interval(sk) + interval = scheme.get_activation_interval(secret_key) # Verify it's a range assert isinstance(interval, range) @@ -121,9 +126,9 @@ def test_get_prepared_interval() -> None: """Tests that get_prepared_interval returns the correct range.""" scheme = TEST_SIGNATURE_SCHEME # Use full lifetime - sk = scheme.key_gen(Slot(0), Uint64(16)).secret_key + secret_key = scheme.key_gen(Slot(0), Uint64(16)).secret_key - interval = scheme.get_prepared_interval(sk) + interval = scheme.get_prepared_interval(secret_key) # Verify it's a range assert isinstance(interval, range) @@ -139,18 +144,18 @@ def test_advance_preparation() -> None: scheme = TEST_SIGNATURE_SCHEME # Request 3 bottom trees' worth of slots to ensure room to advance leafs_per_bottom_tree = 1 << (scheme.config.LOG_LIFETIME // 2) - sk = scheme.key_gen(Slot(0), Uint64(3 * leafs_per_bottom_tree)).secret_key + secret_key = scheme.key_gen(Slot(0), Uint64(3 * leafs_per_bottom_tree)).secret_key # Get initial prepared interval - initial_interval = scheme.get_prepared_interval(sk) - initial_left_index = sk.left_bottom_tree_index + initial_interval = scheme.get_prepared_interval(secret_key) + initial_left_index = secret_key.left_bottom_tree_index # Advance preparation (returns new SecretKey since models are immutable) - sk = scheme.advance_preparation(sk) + secret_key = scheme.advance_preparation(secret_key) # Get new prepared interval - new_interval = scheme.get_prepared_interval(sk) - new_left_index = sk.left_bottom_tree_index + new_interval = scheme.get_prepared_interval(secret_key) + new_left_index = secret_key.left_bottom_tree_index # Verify the left index incremented assert new_left_index == initial_left_index + Uint64(1) @@ -166,13 +171,13 @@ def test_sign_requires_prepared_interval() -> None: scheme = TEST_SIGNATURE_SCHEME # Request 3 bottom trees' worth of slots to have room for testing leafs_per_bottom_tree = 1 << (scheme.config.LOG_LIFETIME // 2) - sk = scheme.key_gen(Slot(0), Uint64(3 * leafs_per_bottom_tree)).secret_key + secret_key = scheme.key_gen(Slot(0), Uint64(3 * leafs_per_bottom_tree)).secret_key # Get the prepared interval - prepared_interval = scheme.get_prepared_interval(sk) + prepared_interval = scheme.get_prepared_interval(secret_key) # Try to sign outside the prepared interval (but inside activation interval) - activation_interval = scheme.get_activation_interval(sk) + activation_interval = scheme.get_activation_interval(secret_key) # Pick an epoch just beyond the prepared interval outside_epoch = Slot(prepared_interval.stop) @@ -183,22 +188,22 @@ def test_sign_requires_prepared_interval() -> None: # Signing should fail message = Bytes32(b"\x42" * 32) with pytest.raises(ValueError, match="outside the prepared interval"): - scheme.sign(sk, outside_epoch, message) + scheme.sign(secret_key, outside_epoch, message) def test_deterministic_signing() -> None: """Tests that signing the same message with the same key produces the same signature.""" scheme = TEST_SIGNATURE_SCHEME # Use full lifetime - sk = scheme.key_gen(Slot(0), Uint64(16)).secret_key + secret_key = scheme.key_gen(Slot(0), Uint64(16)).secret_key # Use epoch within prepared interval epoch = Slot(4) message = Bytes32(b"\x42" * 32) # Sign twice - sig1 = scheme.sign(sk, epoch, message) - sig2 = scheme.sign(sk, epoch, message) + sig1 = scheme.sign(secret_key, epoch, message) + sig2 = scheme.sign(secret_key, epoch, message) # Signatures should be identical (deterministic) assert sig1.rho == sig2.rho @@ -293,33 +298,33 @@ def test_rejects_slot_beyond_lifetime(self) -> None: # Generate valid keys. kp = scheme.key_gen(Slot(0), Uint64(int(scheme.config.LIFETIME))) - pk, sk = kp.public_key, kp.secret_key + public_key, secret_key = kp.public_key, kp.secret_key # Sign a valid message at a valid epoch. valid_epoch = Slot(4) message = Bytes32(b"\x42" * 32) - signature = scheme.sign(sk, valid_epoch, message) + signature = scheme.sign(secret_key, valid_epoch, message) # Verify with an epoch beyond LIFETIME. invalid_epoch = Slot(int(scheme.config.LIFETIME) + 1) # Must return False, not raise. - result = scheme.verify(pk, invalid_epoch, message, signature) + result = scheme.verify(public_key, invalid_epoch, message, signature) assert result is False def test_rejects_very_large_slot(self) -> None: """verify returns False for absurdly large slot values.""" scheme = TEST_SIGNATURE_SCHEME kp = scheme.key_gen(Slot(0), Uint64(int(scheme.config.LIFETIME))) - pk, sk = kp.public_key, kp.secret_key + public_key, secret_key = kp.public_key, kp.secret_key valid_epoch = Slot(4) message = Bytes32(b"\x42" * 32) - signature = scheme.sign(sk, valid_epoch, message) + signature = scheme.sign(secret_key, valid_epoch, message) # Try to verify with a huge epoch. huge_epoch = Slot(2**32) # Must return False, not raise. - result = scheme.verify(pk, huge_epoch, message, signature) + result = scheme.verify(public_key, huge_epoch, message, signature) assert result is False diff --git a/tests/lean_spec/spec/crypto/xmss/test_merkle.py b/tests/lean_spec/spec/crypto/xmss/test_merkle.py index d9042ed68..62a9b454d 100644 --- a/tests/lean_spec/spec/crypto/xmss/test_merkle.py +++ b/tests/lean_spec/spec/crypto/xmss/test_merkle.py @@ -30,12 +30,12 @@ def _run_commit_open_verify_roundtrip( num_leaves: int, depth: int, start_index: int, - leaf_parts_len: int, + leaf_parts_length: int, ) -> None: """Build a tree, then open and verify every active leaf against its root.""" parameter = random_parameter(config) leaves: list[list[HashDigestVector]] = [ - [random_domain(config) for _ in range(leaf_parts_len)] for _ in range(num_leaves) + [random_domain(config) for _ in range(leaf_parts_length)] for _ in range(num_leaves) ] leaf_hashes: list[HashDigestVector] = [ @@ -74,7 +74,7 @@ def _run_commit_open_verify_roundtrip( @pytest.mark.parametrize( - "num_leaves, depth, start_index, leaf_parts_len", + "num_leaves, depth, start_index, leaf_parts_length", [ pytest.param(16, 4, 0, 3, id="Full tree (depth 4)", marks=pytest.mark.slow), pytest.param(12, 5, 0, 5, id="Half tree, left-aligned (depth 5)", marks=pytest.mark.slow), @@ -90,12 +90,12 @@ def test_commit_open_verify_roundtrip( num_leaves: int, depth: int, start_index: int, - leaf_parts_len: int, + leaf_parts_length: int, ) -> None: """A built tree opens and verifies every leaf for various shapes.""" assert start_index + num_leaves <= (1 << depth) _run_commit_open_verify_roundtrip( - PROD_POSEIDON, PROD_CONFIG, num_leaves, depth, start_index, leaf_parts_len + PROD_POSEIDON, PROD_CONFIG, num_leaves, depth, start_index, leaf_parts_length ) diff --git a/tests/lean_spec/spec/crypto/xmss/test_poseidon.py b/tests/lean_spec/spec/crypto/xmss/test_poseidon.py index 92d2cef09..ea310f7dd 100644 --- a/tests/lean_spec/spec/crypto/xmss/test_poseidon.py +++ b/tests/lean_spec/spec/crypto/xmss/test_poseidon.py @@ -17,7 +17,7 @@ def _parameter() -> Parameter: """Return a fixed public parameter for hashing tests.""" - return Parameter(data=[Fp(value=1)] * TEST_CONFIG.PARAMETER_LEN) + return Parameter(data=[Fp(value=1)] * TEST_CONFIG.PARAMETER_LENGTH) @pytest.mark.parametrize("width", [16, 24]) @@ -99,7 +99,7 @@ def test_tweak_hash_chain_uses_width_sixteen_compression() -> None: [random_domain(TEST_CONFIG)], ) assert isinstance(result, HashDigestVector) - assert len(result.data) == TEST_CONFIG.HASH_LEN_FE + assert len(result.data) == TEST_CONFIG.HASH_LENGTH_FIELD_ELEMENTS def test_tweak_hash_node_uses_width_twenty_four_compression() -> None: @@ -110,7 +110,7 @@ def test_tweak_hash_node_uses_width_twenty_four_compression() -> None: TreeTweak(level=1, index=Uint64(0)), [random_domain(TEST_CONFIG), random_domain(TEST_CONFIG)], ) - assert len(result.data) == TEST_CONFIG.HASH_LEN_FE + assert len(result.data) == TEST_CONFIG.HASH_LENGTH_FIELD_ELEMENTS def test_tweak_hash_leaf_uses_sponge_mode() -> None: @@ -119,7 +119,7 @@ def test_tweak_hash_leaf_uses_sponge_mode() -> None: result = TEST_POSEIDON.tweak_hash( TEST_CONFIG, _parameter(), TreeTweak(level=0, index=Uint64(0)), parts ) - assert len(result.data) == TEST_CONFIG.HASH_LEN_FE + assert len(result.data) == TEST_CONFIG.HASH_LENGTH_FIELD_ELEMENTS def test_tweak_hash_chain_and_tree_tweaks_are_domain_separated() -> None: diff --git a/tests/lean_spec/spec/crypto/xmss/test_prf.py b/tests/lean_spec/spec/crypto/xmss/test_prf.py index 571e291cb..e4e1b0dc6 100644 --- a/tests/lean_spec/spec/crypto/xmss/test_prf.py +++ b/tests/lean_spec/spec/crypto/xmss/test_prf.py @@ -53,7 +53,7 @@ def test_apply_is_sensitive_to_inputs() -> None: epoch1 = Uint64(10) chain_index1 = Uint64(20) baseline_output = key1.derive_chain_start(config, epoch1, chain_index1) - assert len(baseline_output) == config.HASH_LEN_FE + assert len(baseline_output) == config.HASH_LENGTH_FIELD_ELEMENTS # Test sensitivity to the key. key2 = PRFKey(b"\x22" * PRF_KEY_LENGTH) diff --git a/tests/lean_spec/spec/crypto/xmss/test_types.py b/tests/lean_spec/spec/crypto/xmss/test_types.py index 38332a5e6..5fabecf5f 100644 --- a/tests/lean_spec/spec/crypto/xmss/test_types.py +++ b/tests/lean_spec/spec/crypto/xmss/test_types.py @@ -36,29 +36,29 @@ def test_node_list_limit_is_twice_the_leaf_row() -> None: def test_hash_digest_vector_length_is_digest_length() -> None: """A digest vector holds exactly one Poseidon output worth of elements.""" - assert HashDigestVector.LENGTH == TEST_CONFIG.HASH_LEN_FE + assert HashDigestVector.LENGTH == TEST_CONFIG.HASH_LENGTH_FIELD_ELEMENTS def test_hash_digest_vector_accepts_exact_length() -> None: """A digest vector of the configured length validates.""" - data = [Fp(value=i) for i in range(TEST_CONFIG.HASH_LEN_FE)] + data = [Fp(value=i) for i in range(TEST_CONFIG.HASH_LENGTH_FIELD_ELEMENTS)] assert HashDigestVector(data=data).data == tuple(data) def test_hash_digest_vector_rejects_wrong_length() -> None: """A digest vector of the wrong length fails validation.""" with pytest.raises(SSZValueError): - HashDigestVector(data=[Fp(value=0)] * (TEST_CONFIG.HASH_LEN_FE + 1)) + HashDigestVector(data=[Fp(value=0)] * (TEST_CONFIG.HASH_LENGTH_FIELD_ELEMENTS + 1)) def test_parameter_length_is_parameter_length() -> None: """A parameter holds the configured number of personalization elements.""" - assert Parameter.LENGTH == TEST_CONFIG.PARAMETER_LEN + assert Parameter.LENGTH == TEST_CONFIG.PARAMETER_LENGTH def test_randomness_length_is_randomness_length() -> None: """The signing randomness holds the configured number of elements.""" - assert Randomness.LENGTH == TEST_CONFIG.RAND_LEN_FE + assert Randomness.LENGTH == TEST_CONFIG.RAND_LENGTH_FIELD_ELEMENTS def test_hash_digest_list_limit_is_node_list_limit() -> None: diff --git a/tests/lean_spec/spec/forks/lstar/forkchoice/conftest.py b/tests/lean_spec/spec/forks/lstar/forkchoice/conftest.py index 0622b0754..7f6800ddf 100644 --- a/tests/lean_spec/spec/forks/lstar/forkchoice/conftest.py +++ b/tests/lean_spec/spec/forks/lstar/forkchoice/conftest.py @@ -10,13 +10,13 @@ from lean_spec.node.chain.clock import Interval from lean_spec.spec.forks.lstar import Store -from tests.lean_spec.helpers import TEST_VALIDATOR_ID, make_store +from tests.lean_spec.helpers import TEST_VALIDATOR_INDEX, make_store @pytest.fixture def pruning_store() -> Store: """Store with 3 validators for pruning tests.""" - return make_store(num_validators=3, validator_id=TEST_VALIDATOR_ID) + return make_store(num_validators=3, validator_index=TEST_VALIDATOR_INDEX) @pytest.fixture diff --git a/tests/lean_spec/spec/forks/lstar/forkchoice/test_attestation_target.py b/tests/lean_spec/spec/forks/lstar/forkchoice/test_attestation_target.py index 38f382fab..ef3dac020 100644 --- a/tests/lean_spec/spec/forks/lstar/forkchoice/test_attestation_target.py +++ b/tests/lean_spec/spec/forks/lstar/forkchoice/test_attestation_target.py @@ -155,12 +155,12 @@ def test_safe_target_requires_supermajority( # Create signed attestations and process them for i in range(threshold - 1): - vid = ValidatorIndex(i) - sig = key_manager.sign_attestation_data(vid, attestation_data) + validator_index = ValidatorIndex(i) + signature = key_manager.sign_attestation_data(validator_index, attestation_data) signed_attestation = SignedAttestation( - validator_id=vid, + validator_index=validator_index, data=attestation_data, - signature=sig, + signature=signature, ) # Process as gossip (requires aggregator flag) store = spec.on_gossip_attestation(store, signed_attestation, is_aggregator=True) @@ -201,12 +201,12 @@ def test_safe_target_advances_with_supermajority( # Create signed attestations and process them for i in range(threshold + 1): - vid = ValidatorIndex(i) - sig = key_manager.sign_attestation_data(vid, attestation_data) + validator_index = ValidatorIndex(i) + signature = key_manager.sign_attestation_data(validator_index, attestation_data) signed_attestation = SignedAttestation( - validator_id=vid, + validator_index=validator_index, data=attestation_data, - signature=sig, + signature=signature, ) store = spec.on_gossip_attestation(store, signed_attestation, is_aggregator=True) @@ -240,12 +240,12 @@ def test_update_safe_target_uses_new_attestations( # Create signed attestations and process them for i in range(num_validators): - vid = ValidatorIndex(i) - sig = key_manager.sign_attestation_data(vid, attestation_data) + validator_index = ValidatorIndex(i) + signature = key_manager.sign_attestation_data(validator_index, attestation_data) signed_attestation = SignedAttestation( - validator_id=vid, + validator_index=validator_index, data=attestation_data, - signature=sig, + signature=signature, ) store = spec.on_gossip_attestation(store, signed_attestation, is_aggregator=True) @@ -294,12 +294,12 @@ def test_justification_with_supermajority_attestations( # Add attestations from threshold validators using the new workflow for i in range(threshold + 1): - vid = ValidatorIndex(i) - sig = key_manager.sign_attestation_data(vid, attestation_data) + validator_index = ValidatorIndex(i) + signature = key_manager.sign_attestation_data(validator_index, attestation_data) signed_attestation = SignedAttestation( - validator_id=vid, + validator_index=validator_index, data=attestation_data, - signature=sig, + signature=signature, ) store = spec.on_gossip_attestation(store, signed_attestation, is_aggregator=True) @@ -341,7 +341,7 @@ def test_justification_requires_valid_source( ) attestation = Attestation( - validator_id=ValidatorIndex(5), + validator_index=ValidatorIndex(5), data=AttestationData( slot=slot, head=Checkpoint(root=block_root, slot=slot), @@ -377,12 +377,12 @@ def test_justification_tracking_with_multiple_targets( attestation_data_head = spec.produce_attestation_data(store, head_block.slot) for i in range(num_validators // 2): - vid = ValidatorIndex(i) - sig = key_manager.sign_attestation_data(vid, attestation_data_head) + validator_index = ValidatorIndex(i) + signature = key_manager.sign_attestation_data(validator_index, attestation_data_head) signed_attestation = SignedAttestation( - validator_id=vid, + validator_index=validator_index, data=attestation_data_head, - signature=sig, + signature=signature, ) store = spec.on_gossip_attestation(store, signed_attestation, is_aggregator=True) @@ -416,22 +416,22 @@ def test_finalization_after_consecutive_justification( # Create attestations from all validators for the previous block if slot_num > 1: - prev_head = store.head - prev_block = store.blocks[prev_head] + previous_head = store.head + previous_block = store.blocks[previous_head] attestation_data = AttestationData( - slot=prev_block.slot, - head=Checkpoint(root=prev_head, slot=prev_block.slot), - target=Checkpoint(root=prev_head, slot=prev_block.slot), + slot=previous_block.slot, + head=Checkpoint(root=previous_head, slot=previous_block.slot), + target=Checkpoint(root=previous_head, slot=previous_block.slot), source=store.latest_justified, ) for i in range(threshold + 1): - vid = ValidatorIndex(i) - sig = key_manager.sign_attestation_data(vid, attestation_data) + validator_index = ValidatorIndex(i) + signature = key_manager.sign_attestation_data(validator_index, attestation_data) signed_attestation = SignedAttestation( - validator_id=vid, + validator_index=validator_index, data=attestation_data, - signature=sig, + signature=signature, ) store = spec.on_gossip_attestation( store, signed_attestation, is_aggregator=True @@ -475,7 +475,7 @@ def test_attestation_target_single_validator( key_manager: XmssKeyManager, ) -> None: """Attestation target computation should work with single validator.""" - store = make_store(num_validators=1, key_manager=key_manager, validator_id=None) + store = make_store(num_validators=1, key_manager=key_manager, validator_index=None) # Should be able to get attestation target target = spec.get_attestation_target(store) @@ -526,12 +526,12 @@ def test_full_attestation_cycle( num_validators = len(store.states[block_1_root].validators) for i in range(num_validators): - vid = ValidatorIndex(i) - sig = key_manager.sign_attestation_data(vid, attestation_data) + validator_index = ValidatorIndex(i) + signature = key_manager.sign_attestation_data(validator_index, attestation_data) signed_attestation = SignedAttestation( - validator_id=vid, + validator_index=validator_index, data=attestation_data, - signature=sig, + signature=signature, ) # Process as gossip store = spec.on_gossip_attestation(store, signed_attestation, is_aggregator=True) @@ -574,10 +574,10 @@ def test_attestation_target_after_on_block( # Merge it with the per-attestation single-message aggregates # into the block-level multi-message aggregate. proposer_signature = key_manager.sign_block_root(proposer_1, slot_1, block_root) - proposer_pubkey = key_manager.get_public_keys(proposer_1)[1] + proposer_public_key = key_manager.get_public_keys(proposer_1)[1] proposer_single_message_aggregate = SingleMessageAggregate.aggregate( children=[], - raw_xmss=[(proposer_1, proposer_pubkey, proposer_signature)], + raw_xmss=[(proposer_1, proposer_public_key, proposer_signature)], message=block_root, slot=slot_1, ) @@ -585,12 +585,12 @@ def test_attestation_target_after_on_block( head_state = store.states[store.head] public_keys_per_part: list[list] = [ [ - head_state.validators[vid].get_attestation_pubkey() - for vid in proof.participants.to_validator_indices() + head_state.validators[validator_index].get_attestation_public_key() + for validator_index in proof.participants.to_validator_indices() ] for proof in signatures ] - public_keys_per_part.append([proposer_pubkey]) + public_keys_per_part.append([proposer_public_key]) merged = MultiMessageAggregate.aggregate( [*signatures, proposer_single_message_aggregate], diff --git a/tests/lean_spec/spec/forks/lstar/forkchoice/test_block_production_justification_gap.py b/tests/lean_spec/spec/forks/lstar/forkchoice/test_block_production_justification_gap.py index a4fb715cb..ec1581741 100644 --- a/tests/lean_spec/spec/forks/lstar/forkchoice/test_block_production_justification_gap.py +++ b/tests/lean_spec/spec/forks/lstar/forkchoice/test_block_production_justification_gap.py @@ -66,7 +66,7 @@ def add_block(block_spec: BlockSpec) -> None: label="block_4", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i) for i in range(6)], + validator_indices=[ValidatorIndex(i) for i in range(6)], slot=Slot(4), target_slot=Slot(1), target_root_label="block_1", @@ -87,7 +87,7 @@ def add_block(block_spec: BlockSpec) -> None: label="block_5", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(6), ValidatorIndex(7)], + validator_indices=[ValidatorIndex(6), ValidatorIndex(7)], slot=Slot(5), target_slot=Slot(4), target_root_label="block_4", @@ -107,7 +107,7 @@ def add_block(block_spec: BlockSpec) -> None: label="block_6", attestations=[ AggregatedAttestationSpec( - validator_ids=[ValidatorIndex(i) for i in range(6)], + validator_indices=[ValidatorIndex(i) for i in range(6)], slot=Slot(6), target_slot=Slot(2), target_root_label="block_2", @@ -129,7 +129,9 @@ def add_block(block_spec: BlockSpec) -> None: # Its source is NOT block_5's latest_justified (which is block_1). # The filter must accept it on source-slot-justified, not full-Checkpoint equality. block_6_target_atts = [ - att for att in store.latest_known_aggregated_payloads if att.target == block_2_checkpoint + attestation + for attestation in store.latest_known_aggregated_payloads + if attestation.target == block_2_checkpoint ] assert len(block_6_target_atts) == 1 assert block_6_target_atts[0].source == Checkpoint(root=genesis_root, slot=Slot(0)) @@ -142,7 +144,7 @@ def add_block(block_spec: BlockSpec) -> None: # The produced block's post-state caught up to the store's justified checkpoint. # Its body carries the attestation that closed the gap. new_block_root = hash_tree_root(new_block) - body_targets = [att.data.target for att in new_block.body.attestations] + body_targets = [attestation.data.target for attestation in new_block.body.attestations] assert new_store.latest_justified == block_2_checkpoint assert new_block.parent_root == hash_tree_root(block_registry["block_5"]) assert new_store.states[new_block_root].latest_justified == block_2_checkpoint diff --git a/tests/lean_spec/spec/forks/lstar/forkchoice/test_compute_block_weights.py b/tests/lean_spec/spec/forks/lstar/forkchoice/test_compute_block_weights.py index 9cb9bf2ac..352fc472e 100644 --- a/tests/lean_spec/spec/forks/lstar/forkchoice/test_compute_block_weights.py +++ b/tests/lean_spec/spec/forks/lstar/forkchoice/test_compute_block_weights.py @@ -56,7 +56,7 @@ def test_linear_chain_weight_accumulates_upward(spec: LstarSpec, base_store: Sto new_states[block1_root] = genesis_state new_states[block2_root] = genesis_state - att_data = AttestationData( + attestation_data = AttestationData( slot=Slot(2), head=Checkpoint(root=block2_root, slot=Slot(2)), target=Checkpoint(root=block2_root, slot=Slot(2)), @@ -64,7 +64,7 @@ def test_linear_chain_weight_accumulates_upward(spec: LstarSpec, base_store: Sto ) proof = _make_empty_proof([ValidatorIndex(0)]) aggregated_payloads = { - att_data: {proof}, + attestation_data: {proof}, } base_store.blocks = new_blocks @@ -99,7 +99,7 @@ def test_multiple_attestations_accumulate(spec: LstarSpec, base_store: Store) -> new_states = dict(base_store.states) new_states[block1_root] = base_store.states[genesis_root] - att_data = AttestationData( + attestation_data = AttestationData( slot=Slot(1), head=Checkpoint(root=block1_root, slot=Slot(1)), target=Checkpoint(root=block1_root, slot=Slot(1)), @@ -108,7 +108,7 @@ def test_multiple_attestations_accumulate(spec: LstarSpec, base_store: Store) -> proof = _make_empty_proof([ValidatorIndex(0), ValidatorIndex(1)]) aggregated_payloads = { - att_data: {proof}, + attestation_data: {proof}, } base_store.blocks = new_blocks diff --git a/tests/lean_spec/spec/forks/lstar/forkchoice/test_store_attestations.py b/tests/lean_spec/spec/forks/lstar/forkchoice/test_store_attestations.py index 686e872b7..aa4212c00 100644 --- a/tests/lean_spec/spec/forks/lstar/forkchoice/test_store_attestations.py +++ b/tests/lean_spec/spec/forks/lstar/forkchoice/test_store_attestations.py @@ -19,7 +19,7 @@ from lean_spec.spec.forks.lstar.spec import LstarSpec from lean_spec.spec.ssz import ByteList512KiB, Bytes32 from tests.lean_spec.helpers import ( - TEST_VALIDATOR_ID, + TEST_VALIDATOR_INDEX, make_aggregated_proof, make_signed_block_from_store, make_store, @@ -70,23 +70,24 @@ def test_on_block_preserves_immutability_of_aggregated_payloads( ) -> None: """Verify that Store.on_block doesn't mutate previous store's latest_new_aggregated_payloads.""" base_store = make_store( - num_validators=3, key_manager=key_manager, validator_id=TEST_VALIDATOR_ID + num_validators=3, key_manager=key_manager, validator_index=TEST_VALIDATOR_INDEX ) # First block with attestations from validators 1 and 2 attestation_slot_1 = Slot(1) attestation_data_1 = spec.produce_attestation_data(base_store, attestation_slot_1) - gossip_sigs_1 = { + gossip_signatures_1 = { attestation_data_1: { AttestationSignatureEntry( - validator_id, key_manager.sign_attestation_data(validator_id, attestation_data_1) + validator_index, + key_manager.sign_attestation_data(validator_index, attestation_data_1), ) - for validator_id in (ValidatorIndex(1), ValidatorIndex(2)) + for validator_index in (ValidatorIndex(1), ValidatorIndex(2)) }, } - base_store.attestation_signatures = gossip_sigs_1 + base_store.attestation_signatures = gossip_signatures_1 producer_store_1 = base_store consumer_store_1, signed_block_1 = make_signed_block_from_store( @@ -98,16 +99,17 @@ def test_on_block_preserves_immutability_of_aggregated_payloads( attestation_slot_2 = Slot(2) attestation_data_2 = spec.produce_attestation_data(store_after_block_1, attestation_slot_2) - gossip_sigs_2 = { + gossip_signatures_2 = { attestation_data_2: { AttestationSignatureEntry( - validator_id, key_manager.sign_attestation_data(validator_id, attestation_data_2) + validator_index, + key_manager.sign_attestation_data(validator_index, attestation_data_2), ) - for validator_id in (ValidatorIndex(1), ValidatorIndex(2)) + for validator_index in (ValidatorIndex(1), ValidatorIndex(2)) }, } - store_after_block_1.attestation_signatures = gossip_sigs_2 + store_after_block_1.attestation_signatures = gossip_signatures_2 producer_store_2 = store_after_block_1 store_before_block_2, signed_block_2 = make_signed_block_from_store( @@ -115,7 +117,7 @@ def test_on_block_preserves_immutability_of_aggregated_payloads( ) # Capture the original list lengths for keys that already exist - original_sig_lengths = { + original_signature_lengths = { k: len(v) for k, v in store_before_block_2.latest_new_aggregated_payloads.items() } @@ -123,7 +125,7 @@ def test_on_block_preserves_immutability_of_aggregated_payloads( store_after_block_2 = spec.on_block(store_before_block_2, signed_block_2) # Verify immutability: the list lengths in store_before_block_2 should not have changed - for key, original_length in original_sig_lengths.items(): + for key, original_length in original_signature_lengths.items(): current_length = len(store_before_block_2.latest_new_aggregated_payloads[key]) assert current_length == original_length, ( f"Immutability violated: list for key {key} grew from {original_length} to " @@ -155,11 +157,11 @@ def test_aggregator_stores_received_attestation( attester_validator = ValidatorIndex(1) store, attestation_data = make_store_with_attestation_data( - key_manager, num_validators=8, validator_id=current_validator + key_manager, num_validators=8, validator_index=current_validator ) signed_attestation = SignedAttestation( - validator_id=attester_validator, + validator_index=attester_validator, data=attestation_data, signature=key_manager.sign_attestation_data(attester_validator, attestation_data), ) @@ -170,8 +172,8 @@ def test_aggregator_stores_received_attestation( updated_store = spec.on_gossip_attestation(store, signed_attestation, is_aggregator=True) - sigs = updated_store.attestation_signatures.get(attestation_data, set()) - assert attester_validator in {entry.validator_id for entry in sigs}, ( + signatures = updated_store.attestation_signatures.get(attestation_data, set()) + assert attester_validator in {entry.validator_index for entry in signatures}, ( "Aggregator should store any attestation it receives" ) @@ -183,12 +185,12 @@ def test_aggregator_stores_multiple_attestations( attesters = [ValidatorIndex(1), ValidatorIndex(2), ValidatorIndex(3)] store, attestation_data = make_store_with_attestation_data( - key_manager, num_validators=8, validator_id=current_validator + key_manager, num_validators=8, validator_index=current_validator ) def make_signed(v: ValidatorIndex) -> SignedAttestation: return SignedAttestation( - validator_id=v, + validator_index=v, data=attestation_data, signature=key_manager.sign_attestation_data(v, attestation_data), ) @@ -198,7 +200,7 @@ def make_signed(v: ValidatorIndex) -> SignedAttestation: updated = spec.on_gossip_attestation(updated, make_signed(v), is_aggregator=True) stored_ids = { - entry.validator_id + entry.validator_index for entry in updated.attestation_signatures.get(attestation_data, set()) } assert stored_ids == set(attesters), ( @@ -213,19 +215,19 @@ def test_non_aggregator_never_stores_signature( attester_validator = ValidatorIndex(1) store, attestation_data = make_store_with_attestation_data( - key_manager, num_validators=8, validator_id=current_validator + key_manager, num_validators=8, validator_index=current_validator ) signed_attestation = SignedAttestation( - validator_id=attester_validator, + validator_index=attester_validator, data=attestation_data, signature=key_manager.sign_attestation_data(attester_validator, attestation_data), ) updated_store = spec.on_gossip_attestation(store, signed_attestation, is_aggregator=False) - sigs = updated_store.attestation_signatures.get(attestation_data, set()) - assert attester_validator not in {entry.validator_id for entry in sigs}, ( + signatures = updated_store.attestation_signatures.get(attestation_data, set()) + assert attester_validator not in {entry.validator_index for entry in signatures}, ( "Non-aggregator should never store gossip signatures" ) @@ -237,11 +239,11 @@ def test_non_aggregator_does_not_create_signatures_entry( attester_validator = ValidatorIndex(1) store, attestation_data = make_store_with_attestation_data( - key_manager, num_validators=8, validator_id=current_validator + key_manager, num_validators=8, validator_index=current_validator ) signed_attestation = SignedAttestation( - validator_id=attester_validator, + validator_index=attester_validator, data=attestation_data, signature=key_manager.sign_attestation_data(attester_validator, attestation_data), ) @@ -272,7 +274,7 @@ def test_valid_proof_stored_correctly( participants = [ValidatorIndex(1), ValidatorIndex(2)] store, attestation_data = make_store_with_attestation_data( - key_manager, num_validators=4, validator_id=ValidatorIndex(0) + key_manager, num_validators=4, validator_index=ValidatorIndex(0) ) data_root = hash_tree_root(attestation_data) @@ -280,11 +282,11 @@ def test_valid_proof_stored_correctly( # Create valid aggregated proof raw_xmss = [ ( - vid, - key_manager[vid].attestation_keypair.public_key, - key_manager.sign_attestation_data(vid, attestation_data), + validator_index, + key_manager[validator_index].attestation_keypair.public_key, + key_manager.sign_attestation_data(validator_index, attestation_data), ) - for vid in participants + for validator_index in participants ] proof = SingleMessageAggregate.aggregate( children=[], @@ -319,18 +321,18 @@ def test_attestation_data_used_as_key( participants = [ValidatorIndex(1)] store, attestation_data = make_store_with_attestation_data( - key_manager, num_validators=4, validator_id=ValidatorIndex(0) + key_manager, num_validators=4, validator_index=ValidatorIndex(0) ) data_root = hash_tree_root(attestation_data) raw_xmss = [ ( - vid, - key_manager[vid].attestation_keypair.public_key, - key_manager.sign_attestation_data(vid, attestation_data), + validator_index, + key_manager[validator_index].attestation_keypair.public_key, + key_manager.sign_attestation_data(validator_index, attestation_data), ) - for vid in participants + for validator_index in participants ] proof = SingleMessageAggregate.aggregate( children=[], @@ -358,18 +360,18 @@ def test_invalid_proof_rejected(self, key_manager: XmssKeyManager, spec: LstarSp signers = [ValidatorIndex(1), ValidatorIndex(2)] store, attestation_data = make_store_with_attestation_data( - key_manager, num_validators=4, validator_id=ValidatorIndex(0) + key_manager, num_validators=4, validator_index=ValidatorIndex(0) ) data_root = hash_tree_root(attestation_data) raw_xmss = [ ( - vid, - key_manager[vid].attestation_keypair.public_key, - key_manager.sign_attestation_data(vid, attestation_data), + validator_index, + key_manager[validator_index].attestation_keypair.public_key, + key_manager.sign_attestation_data(validator_index, attestation_data), ) - for vid in signers + for validator_index in signers ] proof = SingleMessageAggregate.aggregate( children=[], @@ -403,7 +405,7 @@ def test_multiple_proofs_accumulate(self, key_manager: XmssKeyManager, spec: Lst all proofs should be stored in the list. """ store, attestation_data = make_store_with_attestation_data( - key_manager, num_validators=4, validator_id=ValidatorIndex(0) + key_manager, num_validators=4, validator_index=ValidatorIndex(0) ) data_root = hash_tree_root(attestation_data) @@ -412,11 +414,11 @@ def test_multiple_proofs_accumulate(self, key_manager: XmssKeyManager, spec: Lst participants_1 = [ValidatorIndex(1), ValidatorIndex(2)] raw_xmss_1 = [ ( - vid, - key_manager[vid].attestation_keypair.public_key, - key_manager.sign_attestation_data(vid, attestation_data), + validator_index, + key_manager[validator_index].attestation_keypair.public_key, + key_manager.sign_attestation_data(validator_index, attestation_data), ) - for vid in participants_1 + for validator_index in participants_1 ] proof_1 = SingleMessageAggregate.aggregate( children=[], @@ -429,11 +431,11 @@ def test_multiple_proofs_accumulate(self, key_manager: XmssKeyManager, spec: Lst participants_2 = [ValidatorIndex(1), ValidatorIndex(3)] raw_xmss_2 = [ ( - vid, - key_manager[vid].attestation_keypair.public_key, - key_manager.sign_attestation_data(vid, attestation_data), + validator_index, + key_manager[validator_index].attestation_keypair.public_key, + key_manager.sign_attestation_data(validator_index, attestation_data), ) - for vid in participants_2 + for validator_index in participants_2 ] proof_2 = SingleMessageAggregate.aggregate( children=[], @@ -480,7 +482,7 @@ def test_aggregates_attestation_signatures_into_proof( store, attestation_data = make_store_with_attestation_signatures( key_manager, num_validators=4, - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), attesting_validators=attesting_validators, ) @@ -506,7 +508,7 @@ def test_aggregated_proof_is_valid(self, key_manager: XmssKeyManager, spec: Lsta store, attestation_data = make_store_with_attestation_signatures( key_manager, num_validators=4, - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), attesting_validators=attesting_validators, ) @@ -517,7 +519,10 @@ def test_aggregated_proof_is_valid(self, key_manager: XmssKeyManager, spec: Lsta # Extract participants from the proof participants = proof.participants.to_validator_indices() - public_keys = [key_manager[vid].attestation_keypair.public_key for vid in participants] + public_keys = [ + key_manager[validator_index].attestation_keypair.public_key + for validator_index in participants + ] # Verify proof is valid proof.verify( @@ -537,7 +542,7 @@ def test_empty_attestation_signatures_produces_no_proofs( store, _ = make_store_with_attestation_signatures( key_manager, num_validators=4, - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), attesting_validators=[], # No attesters ) @@ -555,25 +560,25 @@ def test_multiple_attestation_data_grouped_separately( Each unique AttestationData should produce its own aggregated proof. """ base_store = make_store( - num_validators=4, key_manager=key_manager, validator_id=ValidatorIndex(0) + num_validators=4, key_manager=key_manager, validator_index=ValidatorIndex(0) ) # Create two different attestation data (different slots) - att_data_1 = spec.produce_attestation_data(base_store, Slot(1)) + attestation_data_1 = spec.produce_attestation_data(base_store, Slot(1)) # Create a second attestation data with different head - att_data_2 = AttestationData( + attestation_data_2 = AttestationData( slot=Slot(1), head=Checkpoint(root=Bytes32(b"\x01" * 32), slot=Slot(1)), - target=att_data_1.target, - source=att_data_1.source, + target=attestation_data_1.target, + source=attestation_data_1.source, ) # Validators 1 attests to data_1, validator 2 attests to data_2 - sig_1 = key_manager.sign_attestation_data(ValidatorIndex(1), att_data_1) - sig_2 = key_manager.sign_attestation_data(ValidatorIndex(2), att_data_2) + signature_1 = key_manager.sign_attestation_data(ValidatorIndex(1), attestation_data_1) + signature_2 = key_manager.sign_attestation_data(ValidatorIndex(2), attestation_data_2) attestation_signatures = { - att_data_1: {AttestationSignatureEntry(ValidatorIndex(1), sig_1)}, - att_data_2: {AttestationSignatureEntry(ValidatorIndex(2), sig_2)}, + attestation_data_1: {AttestationSignatureEntry(ValidatorIndex(1), signature_1)}, + attestation_data_2: {AttestationSignatureEntry(ValidatorIndex(2), signature_2)}, } base_store.attestation_signatures = attestation_signatures @@ -582,8 +587,8 @@ def test_multiple_attestation_data_grouped_separately( updated_store, _ = spec.aggregate(store) # Verify both attestation data have separate proofs - assert att_data_1 in updated_store.latest_new_aggregated_payloads - assert att_data_2 in updated_store.latest_new_aggregated_payloads + assert attestation_data_1 in updated_store.latest_new_aggregated_payloads + assert attestation_data_2 in updated_store.latest_new_aggregated_payloads class TestTickIntervalAggregation: @@ -608,7 +613,7 @@ def test_interval_2_triggers_aggregation_for_aggregator( store, attestation_data = make_store_with_attestation_signatures( key_manager, num_validators=4, - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), attesting_validators=attesting_validators, ) @@ -638,7 +643,7 @@ def test_interval_2_skips_aggregation_for_non_aggregator( store, attestation_data = make_store_with_attestation_signatures( key_manager, num_validators=4, - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), attesting_validators=attesting_validators, ) @@ -666,7 +671,7 @@ def test_other_intervals_do_not_trigger_aggregation( store, attestation_data = make_store_with_attestation_signatures( key_manager, num_validators=4, - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), attesting_validators=attesting_validators, ) @@ -700,7 +705,7 @@ def test_interval_0_accepts_attestations_with_proposal( rather than aggregation. """ store = make_store( - num_validators=4, key_manager=key_manager, validator_id=ValidatorIndex(0) + num_validators=4, key_manager=key_manager, validator_index=ValidatorIndex(0) ) # Set time to interval 4 (so next tick wraps to interval 0) @@ -739,7 +744,7 @@ def test_gossip_to_aggregation_to_storage( aggregator_id = ValidatorIndex(0) store = make_store( - num_validators=num_validators, key_manager=key_manager, validator_id=aggregator_id + num_validators=num_validators, key_manager=key_manager, validator_index=aggregator_id ) # Advance the clock to slot 1 so the attestation's slot has begun. store.time = Interval.from_slot(Slot(1)) @@ -750,11 +755,11 @@ def test_gossip_to_aggregation_to_storage( # (all in same subnet since ATTESTATION_COMMITTEE_COUNT=1 by default) attesting_validators = [ValidatorIndex(1), ValidatorIndex(2)] - for vid in attesting_validators: + for validator_index in attesting_validators: signed_attestation = SignedAttestation( - validator_id=vid, + validator_index=validator_index, data=attestation_data, - signature=key_manager.sign_attestation_data(vid, attestation_data), + signature=key_manager.sign_attestation_data(validator_index, attestation_data), ) store = spec.on_gossip_attestation( store, @@ -763,10 +768,12 @@ def test_gossip_to_aggregation_to_storage( ) # Verify signatures were stored - sigs = store.attestation_signatures.get(attestation_data, set()) - stored_validators = {entry.validator_id for entry in sigs} - for vid in attesting_validators: - assert vid in stored_validators, f"Signature for {vid} should be stored" + signatures = store.attestation_signatures.get(attestation_data, set()) + stored_validators = {entry.validator_index for entry in signatures} + for validator_index in attesting_validators: + assert validator_index in stored_validators, ( + f"Signature for {validator_index} should be stored" + ) # Step 2: Advance to interval 2 (aggregation interval) store.time = Interval(1) @@ -780,7 +787,10 @@ def test_gossip_to_aggregation_to_storage( # Step 4: Verify the proof is valid proof = next(iter(store.latest_new_aggregated_payloads[attestation_data])) participants = proof.participants.to_validator_indices() - public_keys = [key_manager[vid].attestation_keypair.public_key for vid in participants] + public_keys = [ + key_manager[validator_index].attestation_keypair.public_key + for validator_index in participants + ] proof.verify( public_keys=public_keys, diff --git a/tests/lean_spec/spec/forks/lstar/forkchoice/test_store_pruning.py b/tests/lean_spec/spec/forks/lstar/forkchoice/test_store_pruning.py index 740582d16..b7a441ae1 100644 --- a/tests/lean_spec/spec/forks/lstar/forkchoice/test_store_pruning.py +++ b/tests/lean_spec/spec/forks/lstar/forkchoice/test_store_pruning.py @@ -225,25 +225,25 @@ def test_mixed_stale_and_fresh_entries(spec: LstarSpec, pruning_store: Store) -> for i in range(1, 11) # Slots 1-10 ] - gossip_sigs = { - att: {AttestationSignatureEntry(ValidatorIndex(i), make_mock_signature())} - for i, att in enumerate(attestations, start=1) + gossip_signatures = { + attestation: {AttestationSignatureEntry(ValidatorIndex(i), make_mock_signature())} + for i, attestation in enumerate(attestations, start=1) } # Finalized at slot 5 means slots 1-5 are stale, 6-10 are fresh - store.attestation_signatures = gossip_sigs + store.attestation_signatures = gossip_signatures store.latest_finalized = make_checkpoint(root_seed=255, slot=5) # Verify all data exists before pruning - for att in attestations: - assert att in store.attestation_signatures + for attestation in attestations: + assert attestation in store.attestation_signatures pruned_store = spec.prune_stale_attestation_data(store) # Entries with target.slot <= 5 should be pruned (slots 1-5) - for att in attestations[:5]: - assert att not in pruned_store.attestation_signatures + for attestation in attestations[:5]: + assert attestation not in pruned_store.attestation_signatures # Entries with target.slot > 5 should be kept (slots 6-10) - for att in attestations[5:]: - assert att in pruned_store.attestation_signatures + for attestation in attestations[5:]: + assert attestation in pruned_store.attestation_signatures diff --git a/tests/lean_spec/spec/forks/lstar/forkchoice/test_time_management.py b/tests/lean_spec/spec/forks/lstar/forkchoice/test_time_management.py index 5a84085e9..52b461ed7 100644 --- a/tests/lean_spec/spec/forks/lstar/forkchoice/test_time_management.py +++ b/tests/lean_spec/spec/forks/lstar/forkchoice/test_time_management.py @@ -15,7 +15,7 @@ from lean_spec.spec.forks.lstar.containers import Block, Validators from lean_spec.spec.forks.lstar.spec import LstarSpec from lean_spec.spec.ssz import Bytes32, Uint64 -from tests.lean_spec.helpers import TEST_VALIDATOR_ID, make_empty_block_body +from tests.lean_spec.helpers import TEST_VALIDATOR_INDEX, make_empty_block_body class TestGetForkchoiceStore: @@ -45,7 +45,7 @@ def test_store_time_from_anchor_slot(self, anchor_slot: int) -> None: store = spec.create_store( state, anchor_block, - validator_id=TEST_VALIDATOR_ID, + validator_index=TEST_VALIDATOR_INDEX, ) assert store.time == Interval(int(INTERVALS_PER_SLOT) * anchor_slot) diff --git a/tests/lean_spec/spec/forks/lstar/forkchoice/test_validate_attestation.py b/tests/lean_spec/spec/forks/lstar/forkchoice/test_validate_attestation.py index 189af32dd..62431a8a4 100644 --- a/tests/lean_spec/spec/forks/lstar/forkchoice/test_validate_attestation.py +++ b/tests/lean_spec/spec/forks/lstar/forkchoice/test_validate_attestation.py @@ -56,7 +56,7 @@ def test_head_checkpoint_slot_mismatch_rejected( # The block actually lives at slot 1. # This violates the consistency check: checkpoint slot must match block slot. attestation = Attestation( - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), data=AttestationData( slot=slot_1, head=Checkpoint(root=block_root, slot=Slot(999)), @@ -93,7 +93,7 @@ def test_head_slot_less_than_source_rejected( # Since source <= target is enforced first, head < source also means head < target. # The topology check catches this via the head >= target assertion. attestation = Attestation( - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), data=AttestationData( slot=slot_2, head=Checkpoint(root=genesis_root, slot=Slot(0)), @@ -130,7 +130,7 @@ def test_head_slot_less_than_target_rejected( # It must be at least as recent as the target checkpoint. # Slot 1 < slot 2 violates this ordering. attestation = Attestation( - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), data=AttestationData( slot=slot_2, head=Checkpoint(root=block_1_root, slot=slot_1), @@ -167,7 +167,7 @@ def test_valid_attestation_with_correct_head_passes( # Source <= target <= head, and all slots match their blocks. # This should pass every validation stage. attestation = Attestation( - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), data=AttestationData( slot=slot_1, head=Checkpoint(root=block_root, slot=slot_1), @@ -194,7 +194,7 @@ def test_head_equal_to_source_and_target_passes( genesis_checkpoint = Checkpoint(root=genesis_root, slot=Slot(0)) attestation = Attestation( - validator_id=ValidatorIndex(0), + validator_index=ValidatorIndex(0), data=AttestationData( slot=Slot(0), head=genesis_checkpoint, diff --git a/tests/lean_spec/spec/forks/lstar/forkchoice/test_validator.py b/tests/lean_spec/spec/forks/lstar/forkchoice/test_validator.py index 05313e080..8a6ac7714 100644 --- a/tests/lean_spec/spec/forks/lstar/forkchoice/test_validator.py +++ b/tests/lean_spec/spec/forks/lstar/forkchoice/test_validator.py @@ -18,7 +18,7 @@ ) from lean_spec.spec.forks.lstar.spec import LstarSpec from lean_spec.spec.ssz import Bytes32, Uint64 -from tests.lean_spec.helpers import TEST_VALIDATOR_ID, make_aggregated_proof, make_store +from tests.lean_spec.helpers import TEST_VALIDATOR_INDEX, make_aggregated_proof, make_store class TestBlockProduction: @@ -27,14 +27,14 @@ class TestBlockProduction: def test_produce_block_basic(self, sample_store: Store, spec: LstarSpec) -> None: """Test basic block production by authorized proposer.""" slot = Slot(1) - validator_idx = ValidatorIndex(1) # Proposer for slot 1 + validator_index = ValidatorIndex(1) # Proposer for slot 1 store, block, _signatures = spec.produce_block_with_signatures( - sample_store, slot, validator_idx + sample_store, slot, validator_index ) # Verify block structure assert block.slot == slot - assert block.proposer_index == validator_idx + assert block.proposer_index == validator_index assert block.parent_root == sample_store.head assert isinstance(block.body, BlockBody) assert block.state_root != Bytes32.zero() # Should have computed state root @@ -69,7 +69,7 @@ def test_produce_block_with_attestations( source=sample_store.latest_justified, ) signed_5 = SignedAttestation( - validator_id=ValidatorIndex(5), + validator_index=ValidatorIndex(5), data=data_5, signature=key_manager.sign_attestation_data(ValidatorIndex(5), data_5), ) @@ -80,7 +80,7 @@ def test_produce_block_with_attestations( source=sample_store.latest_justified, ) signed_6 = SignedAttestation( - validator_id=ValidatorIndex(6), + validator_index=ValidatorIndex(6), data=data_6, signature=key_manager.sign_attestation_data(ValidatorIndex(6), data_6), ) @@ -94,24 +94,24 @@ def test_produce_block_with_attestations( known_payloads.setdefault(signed_5.data, set()).add(proof_5) known_payloads.setdefault(signed_6.data, set()).add(proof_6) - gossip_sigs = {} - gossip_sigs.setdefault(signed_5.data, set()).add( + gossip_signatures = {} + gossip_signatures.setdefault(signed_5.data, set()).add( AttestationSignatureEntry(ValidatorIndex(5), signed_5.signature) ) - gossip_sigs.setdefault(signed_6.data, set()).add( + gossip_signatures.setdefault(signed_6.data, set()).add( AttestationSignatureEntry(ValidatorIndex(6), signed_6.signature) ) sample_store.latest_known_aggregated_payloads = known_payloads - sample_store.attestation_signatures = gossip_sigs + sample_store.attestation_signatures = gossip_signatures slot = Slot(2) - validator_idx = ValidatorIndex(2) # Proposer for slot 2 + validator_index = ValidatorIndex(2) # Proposer for slot 2 store, block, signatures = spec.produce_block_with_signatures( sample_store, slot, - validator_idx, + validator_index, ) # Block should include the attestations we added. @@ -120,17 +120,22 @@ def test_produce_block_with_attestations( # Verify block structure is correct assert block.slot == slot - assert block.proposer_index == validator_idx + assert block.proposer_index == validator_index assert block.state_root != Bytes32.zero() # Verify each aggregated signature proof - for agg_att, proof in zip(block.body.attestations.data, signatures, strict=True): + for aggregate_attestation, proof in zip( + block.body.attestations.data, signatures, strict=True + ): participants = proof.participants.to_validator_indices() - public_keys = [key_manager[vid].attestation_keypair.public_key for vid in participants] + public_keys = [ + key_manager[validator_index].attestation_keypair.public_key + for validator_index in participants + ] proof.verify( public_keys=public_keys, - message=hash_tree_root(agg_att.data), - slot=agg_att.data.slot, + message=hash_tree_root(aggregate_attestation.data), + slot=aggregate_attestation.data.slot, ) def test_produce_block_sequential_slots(self, sample_store: Store, spec: LstarSpec) -> None: @@ -177,7 +182,7 @@ def test_produce_block_sequential_slots(self, sample_store: Store, spec: LstarSp def test_produce_block_empty_attestations(self, sample_store: Store, spec: LstarSpec) -> None: """Test block production with no available attestations.""" slot = Slot(3) - validator_idx = ValidatorIndex(3) + validator_index = ValidatorIndex(3) # Ensure no attestations in store (clear aggregated payloads) sample_store.latest_known_aggregated_payloads = {} @@ -185,13 +190,13 @@ def test_produce_block_empty_attestations(self, sample_store: Store, spec: Lstar store, block, _signatures = spec.produce_block_with_signatures( sample_store, slot, - validator_idx, + validator_index, ) # Should produce valid block with empty attestations assert len(block.body.attestations) == 0 assert block.slot == slot - assert block.proposer_index == validator_idx + assert block.proposer_index == validator_index assert block.state_root != Bytes32.zero() def test_produce_block_state_consistency( @@ -199,7 +204,7 @@ def test_produce_block_state_consistency( ) -> None: """Test that produced block's state is consistent with block content.""" slot = Slot(4) - validator_idx = ValidatorIndex(4) + validator_index = ValidatorIndex(4) # Add some attestations to test state computation head_block = sample_store.blocks[sample_store.head] @@ -211,7 +216,7 @@ def test_produce_block_state_consistency( source=sample_store.latest_justified, ) signed_7 = SignedAttestation( - validator_id=ValidatorIndex(7), + validator_index=ValidatorIndex(7), data=data_7, signature=key_manager.sign_attestation_data(ValidatorIndex(7), data_7), ) @@ -226,7 +231,7 @@ def test_produce_block_state_consistency( store, block, signatures = spec.produce_block_with_signatures( sample_store, slot, - validator_idx, + validator_index, ) block_hash = hash_tree_root(block) @@ -235,13 +240,18 @@ def test_produce_block_state_consistency( assert hash_tree_root(stored_state) == block.state_root # Verify each aggregated proof binds to its attestation in the block. - for agg_att, proof in zip(block.body.attestations.data, signatures, strict=True): + for aggregate_attestation, proof in zip( + block.body.attestations.data, signatures, strict=True + ): participants = proof.participants.to_validator_indices() - public_keys = [key_manager[vid].attestation_keypair.public_key for vid in participants] + public_keys = [ + key_manager[validator_index].attestation_keypair.public_key + for validator_index in participants + ] proof.verify( public_keys=public_keys, - message=hash_tree_root(agg_att.data), - slot=agg_att.data.slot, + message=hash_tree_root(aggregate_attestation.data), + slot=aggregate_attestation.data.slot, ) @@ -252,20 +262,20 @@ def test_block_production_then_attestation(self, sample_store: Store, spec: Lsta """Test producing a block then creating attestation for it.""" # Proposer produces block for slot 1 proposer_slot = Slot(1) - proposer_idx = ValidatorIndex(1) - spec.produce_block_with_signatures(sample_store, proposer_slot, proposer_idx) + proposer_index = ValidatorIndex(1) + spec.produce_block_with_signatures(sample_store, proposer_slot, proposer_index) # Update store state after block production sample_store = spec.update_head(sample_store) # Other validator creates attestation for slot 2 attestor_slot = Slot(2) - attestor_idx = ValidatorIndex(7) + attestor_index = ValidatorIndex(7) attestation_data = spec.produce_attestation_data(sample_store, attestor_slot) - attestation = Attestation(validator_id=attestor_idx, data=attestation_data) + attestation = Attestation(validator_index=attestor_index, data=attestation_data) # Attestation should reference the new block as head (if it became head) - assert attestation.validator_id == attestor_idx + assert attestation.validator_index == attestor_index assert attestation.data.slot == attestor_slot # The attestation should be consistent with current forkchoice state @@ -286,7 +296,7 @@ def test_multiple_validators_coordination(self, sample_store: Store, spec: Lstar attestations = [] for i in range(2, 6): attestation_data = spec.produce_attestation_data(sample_store, Slot(2)) - attestation = Attestation(validator_id=ValidatorIndex(i), data=attestation_data) + attestation = Attestation(validator_index=ValidatorIndex(i), data=attestation_data) attestations.append(attestation) # All attestations should be consistent @@ -343,8 +353,8 @@ def test_validator_edge_cases(self, sample_store: Store, spec: LstarSpec) -> Non # Should be able to produce attestation attestation_data = spec.produce_attestation_data(sample_store, Slot(10)) - attestation = Attestation(validator_id=max_validator, data=attestation_data) - assert attestation.validator_id == max_validator + attestation = Attestation(validator_index=max_validator, data=attestation_data) + assert attestation.validator_index == max_validator def test_validator_operations_empty_store(self, spec: LstarSpec) -> None: """Test validator operations with minimal store state.""" @@ -357,7 +367,7 @@ def test_validator_operations_empty_store(self, spec: LstarSpec) -> None: ValidatorIndex(1), ) attestation_data = spec.produce_attestation_data(store, Slot(1)) - attestation = Attestation(validator_id=ValidatorIndex(2), data=attestation_data) + attestation = Attestation(validator_index=ValidatorIndex(2), data=attestation_data) assert isinstance(block, Block) assert isinstance(attestation, Attestation) @@ -391,7 +401,7 @@ def test_produce_block_missing_parent_state(self, spec: LstarSpec) -> None: latest_finalized=checkpoint, blocks={}, # No blocks states={}, # No states - validator_id=TEST_VALIDATOR_ID, + validator_index=TEST_VALIDATOR_INDEX, ) # The forkchoice head walk asserts that the justified root is known. @@ -419,5 +429,5 @@ def test_validator_operations_invalid_parameters( # Attestation can be created for any validator attestation_data = spec.produce_attestation_data(sample_store, Slot(1)) - attestation = Attestation(validator_id=large_validator, data=attestation_data) - assert attestation.validator_id == large_validator + attestation = Attestation(validator_index=large_validator, data=attestation_data) + assert attestation.validator_index == large_validator diff --git a/tests/lean_spec/spec/forks/lstar/state/test_state_aggregation.py b/tests/lean_spec/spec/forks/lstar/state/test_state_aggregation.py index f9c09b939..7b07b196b 100644 --- a/tests/lean_spec/spec/forks/lstar/state/test_state_aggregation.py +++ b/tests/lean_spec/spec/forks/lstar/state/test_state_aggregation.py @@ -31,14 +31,14 @@ def test_aggregated_signatures_prefers_full_gossip_payload( store = make_store(num_validators=2, key_manager=container_key_manager) head_state = store.states[store.head] source = Checkpoint(root=make_bytes32(1), slot=Slot(0)) - att_data = make_attestation_data_simple( + attestation_data = make_attestation_data_simple( Slot(2), make_bytes32(3), make_bytes32(4), source=source ) attestation_signatures = { - att_data: { + attestation_data: { AttestationSignatureEntry( ValidatorIndex(i), - container_key_manager.sign_attestation_data(ValidatorIndex(i), att_data), + container_key_manager.sign_attestation_data(ValidatorIndex(i), attestation_data), ) for i in range(2) } @@ -54,10 +54,12 @@ def test_aggregated_signatures_prefers_full_gossip_payload( } public_keys = [ - head_state.validators[ValidatorIndex(i)].get_attestation_pubkey() for i in range(2) + head_state.validators[ValidatorIndex(i)].get_attestation_public_key() for i in range(2) ] results[0].proof.verify( - public_keys=public_keys, message=hash_tree_root(att_data), slot=att_data.slot + public_keys=public_keys, + message=hash_tree_root(attestation_data), + slot=attestation_data.slot, ) @@ -70,16 +72,16 @@ def test_build_block_collects_valid_available_attestations( parent_root = hash_tree_root(state.latest_block_header) source = Checkpoint(root=parent_root, slot=Slot(0)) target = Checkpoint(root=parent_root, slot=Slot(0)) - att_data = AttestationData( + attestation_data = AttestationData( slot=Slot(1), head=Checkpoint(root=parent_root, slot=Slot(0)), target=target, source=source, ) - data_root = hash_tree_root(att_data) + data_root = hash_tree_root(attestation_data) - proof = make_aggregated_proof(container_key_manager, [ValidatorIndex(0)], att_data) - aggregated_payloads = {att_data: {proof}} + proof = make_aggregated_proof(container_key_manager, [ValidatorIndex(0)], attestation_data) + aggregated_payloads = {attestation_data: {proof}} block, post_state, aggregated_atts, aggregated_proofs = spec.build_block( state, @@ -103,7 +105,7 @@ def test_build_block_collects_valid_available_attestations( aggregated_proofs[0].verify( public_keys=[container_key_manager[ValidatorIndex(0)].attestation_keypair.public_key], message=data_root, - slot=att_data.slot, + slot=attestation_data.slot, ) @@ -148,32 +150,32 @@ def test_aggregated_signatures_with_multiple_data_groups( """Multiple attestation data groups should be processed independently.""" store = make_store(num_validators=4, key_manager=container_key_manager) source = Checkpoint(root=make_bytes32(22), slot=Slot(0)) - att_data1 = make_attestation_data_simple( + attestation_data1 = make_attestation_data_simple( Slot(9), make_bytes32(23), make_bytes32(24), source=source ) - att_data2 = make_attestation_data_simple( + attestation_data2 = make_attestation_data_simple( Slot(10), make_bytes32(25), make_bytes32(26), source=source ) attestation_signatures = { - att_data1: { + attestation_data1: { AttestationSignatureEntry( ValidatorIndex(0), - container_key_manager.sign_attestation_data(ValidatorIndex(0), att_data1), + container_key_manager.sign_attestation_data(ValidatorIndex(0), attestation_data1), ), AttestationSignatureEntry( ValidatorIndex(1), - container_key_manager.sign_attestation_data(ValidatorIndex(1), att_data1), + container_key_manager.sign_attestation_data(ValidatorIndex(1), attestation_data1), ), }, - att_data2: { + attestation_data2: { AttestationSignatureEntry( ValidatorIndex(2), - container_key_manager.sign_attestation_data(ValidatorIndex(2), att_data2), + container_key_manager.sign_attestation_data(ValidatorIndex(2), attestation_data2), ), AttestationSignatureEntry( ValidatorIndex(3), - container_key_manager.sign_attestation_data(ValidatorIndex(3), att_data2), + container_key_manager.sign_attestation_data(ValidatorIndex(3), attestation_data2), ), }, } @@ -183,15 +185,16 @@ def test_aggregated_signatures_with_multiple_data_groups( assert len(results) == 2 - for signed_att in results: - participants = signed_att.proof.participants.to_validator_indices() + for signed_attestation in results: + participants = signed_attestation.proof.participants.to_validator_indices() public_keys = [ - container_key_manager[vid].attestation_keypair.public_key for vid in participants + container_key_manager[validator_index].attestation_keypair.public_key + for validator_index in participants ] - signed_att.proof.verify( + signed_attestation.proof.verify( public_keys=public_keys, - message=hash_tree_root(signed_att.data), - slot=signed_att.data.slot, + message=hash_tree_root(signed_attestation.data), + slot=signed_attestation.data.slot, ) @@ -231,19 +234,19 @@ def test_build_block_state_root_valid_when_signatures_split( source = Checkpoint(root=parent_root, slot=Slot(0)) target = Checkpoint(root=parent_root, slot=Slot(0)) - att_data = AttestationData( + attestation_data = AttestationData( slot=Slot(1), head=Checkpoint(root=parent_root, slot=Slot(0)), target=target, source=source, ) - proof_0 = make_aggregated_proof(container_key_manager, [ValidatorIndex(0)], att_data) + proof_0 = make_aggregated_proof(container_key_manager, [ValidatorIndex(0)], attestation_data) fallback_proof = make_aggregated_proof( - container_key_manager, [ValidatorIndex(1), ValidatorIndex(2)], att_data + container_key_manager, [ValidatorIndex(1), ValidatorIndex(2)], attestation_data ) - aggregated_payloads = {att_data: {proof_0, fallback_proof}} + aggregated_payloads = {attestation_data: {proof_0, fallback_proof}} block, _, aggregated_atts, _ = spec.build_block( pre_state, @@ -283,21 +286,25 @@ def test_build_block_skips_other_chain_source( correct_source = Checkpoint(root=parent_root, slot=Slot(0)) wrong_source = Checkpoint(root=make_bytes32(99), slot=Slot(0)) - att_data_good = AttestationData( + attestation_data_good = AttestationData( slot=Slot(1), head=Checkpoint(root=parent_root, slot=Slot(0)), target=Checkpoint(root=parent_root, slot=Slot(0)), source=correct_source, ) - att_data_bad = AttestationData( + attestation_data_bad = AttestationData( slot=Slot(1), head=Checkpoint(root=parent_root, slot=Slot(0)), target=Checkpoint(root=parent_root, slot=Slot(0)), source=wrong_source, ) - proof_good = make_aggregated_proof(container_key_manager, [ValidatorIndex(0)], att_data_good) - proof_bad = make_aggregated_proof(container_key_manager, [ValidatorIndex(1)], att_data_bad) + proof_good = make_aggregated_proof( + container_key_manager, [ValidatorIndex(0)], attestation_data_good + ) + proof_bad = make_aggregated_proof( + container_key_manager, [ValidatorIndex(1)], attestation_data_bad + ) _, _, aggregated_atts, _ = spec.build_block( state, @@ -305,11 +312,14 @@ def test_build_block_skips_other_chain_source( proposer_index=ValidatorIndex(1), parent_root=parent_root, known_block_roots={parent_root}, - aggregated_payloads={att_data_good: {proof_good}, att_data_bad: {proof_bad}}, + aggregated_payloads={ + attestation_data_good: {proof_good}, + attestation_data_bad: {proof_bad}, + }, ) assert len(aggregated_atts) == 1 - assert aggregated_atts[0].data == att_data_good + assert aggregated_atts[0].data == attestation_data_good def test_build_block_skips_unknown_head_root( @@ -323,22 +333,24 @@ def test_build_block_skips_unknown_head_root( source = Checkpoint(root=parent_root, slot=Slot(0)) unknown_root = make_bytes32(200) - att_data_known = AttestationData( + attestation_data_known = AttestationData( slot=Slot(1), head=Checkpoint(root=parent_root, slot=Slot(0)), target=Checkpoint(root=parent_root, slot=Slot(0)), source=source, ) - att_data_unknown = AttestationData( + attestation_data_unknown = AttestationData( slot=Slot(1), head=Checkpoint(root=unknown_root, slot=Slot(0)), target=Checkpoint(root=parent_root, slot=Slot(0)), source=source, ) - proof_known = make_aggregated_proof(container_key_manager, [ValidatorIndex(0)], att_data_known) + proof_known = make_aggregated_proof( + container_key_manager, [ValidatorIndex(0)], attestation_data_known + ) proof_unknown = make_aggregated_proof( - container_key_manager, [ValidatorIndex(1)], att_data_unknown + container_key_manager, [ValidatorIndex(1)], attestation_data_unknown ) _, _, aggregated_atts, _ = spec.build_block( @@ -347,11 +359,14 @@ def test_build_block_skips_unknown_head_root( proposer_index=ValidatorIndex(1), parent_root=parent_root, known_block_roots={parent_root}, - aggregated_payloads={att_data_known: {proof_known}, att_data_unknown: {proof_unknown}}, + aggregated_payloads={ + attestation_data_known: {proof_known}, + attestation_data_unknown: {proof_unknown}, + }, ) assert len(aggregated_atts) == 1 - assert aggregated_atts[0].data == att_data_known + assert aggregated_atts[0].data == attestation_data_known def test_aggregate_with_no_signatures( @@ -440,7 +455,7 @@ def test_build_block_fixed_point_closes_justified_divergence( source = Checkpoint(root=genesis_root, slot=Slot(0)) target = Checkpoint(root=block_1_root, slot=Slot(1)) - att_data = AttestationData( + attestation_data = AttestationData( slot=Slot(3), head=target, target=target, @@ -450,7 +465,7 @@ def test_build_block_fixed_point_closes_justified_divergence( proof = make_aggregated_proof( container_key_manager, [ValidatorIndex(1), ValidatorIndex(2), ValidatorIndex(3)], - att_data, + attestation_data, ) # Call build_block with the divergent attestations in the pool. @@ -465,12 +480,12 @@ def test_build_block_fixed_point_closes_justified_divergence( proposer_index=ValidatorIndex(3), parent_root=block_2_root, known_block_roots={genesis_root, block_1_root, block_2_root}, - aggregated_payloads={att_data: {proof}}, + aggregated_payloads={attestation_data: {proof}}, ) # The block must include the justifying attestations. assert len(aggregated_atts) == 1 - assert aggregated_atts[0].data == att_data + assert aggregated_atts[0].data == attestation_data # Justification must have advanced: the fixed-point loop closed the gap. assert post_state.latest_justified.slot >= Slot(1) diff --git a/tests/lean_spec/spec/forks/lstar/state/test_state_justified_slots.py b/tests/lean_spec/spec/forks/lstar/state/test_state_justified_slots.py index 160963c46..775c932a6 100644 --- a/tests/lean_spec/spec/forks/lstar/state/test_state_justified_slots.py +++ b/tests/lean_spec/spec/forks/lstar/state/test_state_justified_slots.py @@ -55,14 +55,14 @@ def test_justified_slots_rebases_when_finalization_advances(spec: LstarSpec) -> source_0 = Checkpoint(root=block_1.parent_root, slot=Slot(0)) target_1 = Checkpoint(root=block_2.parent_root, slot=Slot(1)) - att_0_to_1 = make_aggregated_attestation( + attestation_0_to_1 = make_aggregated_attestation( participant_ids=[ValidatorIndex(0), ValidatorIndex(1)], attestation_slot=Slot(2), source=source_0, target=target_1, ) - block_2 = make_block(state, Slot(2), attestations=[att_0_to_1]) + block_2 = make_block(state, Slot(2), attestations=[attestation_0_to_1]) state = spec.process_block(state, block_2) # Block 3 (slot 3): justify slot 2 with source=1 -> target=2, which finalizes slot 1. @@ -71,14 +71,14 @@ def test_justified_slots_rebases_when_finalization_advances(spec: LstarSpec) -> source_1 = Checkpoint(root=block_2.parent_root, slot=Slot(1)) target_2 = Checkpoint(root=block_3.parent_root, slot=Slot(2)) - att_1_to_2 = make_aggregated_attestation( + attestation_1_to_2 = make_aggregated_attestation( participant_ids=[ValidatorIndex(0), ValidatorIndex(1)], attestation_slot=Slot(3), source=source_1, target=target_2, ) - block_3 = make_block(state, Slot(3), attestations=[att_1_to_2]) + block_3 = make_block(state, Slot(3), attestations=[attestation_1_to_2]) state = spec.process_block(state, block_3) assert state.latest_finalized.slot == Slot(1) @@ -126,13 +126,13 @@ def test_pruning_keeps_pending_justifications(spec: LstarSpec) -> None: block_2 = make_block(state, Slot(2), attestations=[]) source_0 = Checkpoint(root=block_1.parent_root, slot=Slot(0)) target_1 = Checkpoint(root=block_2.parent_root, slot=Slot(1)) - att_0_to_1 = make_aggregated_attestation( + attestation_0_to_1 = make_aggregated_attestation( participant_ids=[ValidatorIndex(0), ValidatorIndex(1)], attestation_slot=Slot(2), source=source_0, target=target_1, ) - block_2 = make_block(state, Slot(2), attestations=[att_0_to_1]) + block_2 = make_block(state, Slot(2), attestations=[attestation_0_to_1]) state = spec.process_block(state, block_2) assert state.latest_finalized.slot == Slot(0) @@ -173,7 +173,7 @@ def test_pruning_keeps_pending_justifications(spec: LstarSpec) -> None: source_1 = Checkpoint(root=state.historical_block_hashes[1], slot=Slot(1)) target_2 = Checkpoint(root=state.historical_block_hashes[2], slot=Slot(2)) - att_1_to_2 = make_aggregated_attestation( + attestation_1_to_2 = make_aggregated_attestation( participant_ids=[ValidatorIndex(0), ValidatorIndex(1)], attestation_slot=Slot(5), source=source_1, @@ -184,7 +184,7 @@ def test_pruning_keeps_pending_justifications(spec: LstarSpec) -> None: # # Pruning iterates over all slots for each root in history. # Duplicate roots must map to multiple slots, not just one. - state = spec.process_attestations(state, [att_1_to_2]) + state = spec.process_attestations(state, [attestation_1_to_2]) # Verify finalization succeeded. assert state.latest_finalized.slot == Slot(1) diff --git a/tests/lean_spec/spec/forks/lstar/state/test_state_process_attestations.py b/tests/lean_spec/spec/forks/lstar/state/test_state_process_attestations.py index b95b18d2c..670b61654 100644 --- a/tests/lean_spec/spec/forks/lstar/state/test_state_process_attestations.py +++ b/tests/lean_spec/spec/forks/lstar/state/test_state_process_attestations.py @@ -110,7 +110,7 @@ def test_attestation_with_target_beyond_history_is_silently_rejected( target_slot = Slot(10) target_root = make_bytes32(99) - att_data = AttestationData( + attestation_data = AttestationData( slot=target_slot, head=Checkpoint(root=target_root, slot=target_slot), target=Checkpoint(root=target_root, slot=target_slot), @@ -123,7 +123,7 @@ def test_attestation_with_target_beyond_history_is_silently_rejected( aggregation_bits=ValidatorIndices( data=[ValidatorIndex(0), ValidatorIndex(1)] ).to_aggregation_bits(), - data=att_data, + data=attestation_data, ) # Process the attestation. @@ -192,7 +192,7 @@ def test_attestation_with_source_beyond_history_is_silently_rejected( target_slot = Slot(10) some_root = make_bytes32(42) - att_data = AttestationData( + attestation_data = AttestationData( slot=target_slot, head=Checkpoint(root=some_root, slot=target_slot), target=Checkpoint(root=some_root, slot=target_slot), @@ -203,7 +203,7 @@ def test_attestation_with_source_beyond_history_is_silently_rejected( aggregation_bits=ValidatorIndices( data=[ValidatorIndex(0), ValidatorIndex(1)] ).to_aggregation_bits(), - data=att_data, + data=attestation_data, ) # Process the attestation. diff --git a/tests/lean_spec/spec/forks/lstar/test_attestation_aggregation.py b/tests/lean_spec/spec/forks/lstar/test_attestation_aggregation.py index e31ef31f1..c4f88d2a6 100644 --- a/tests/lean_spec/spec/forks/lstar/test_attestation_aggregation.py +++ b/tests/lean_spec/spec/forks/lstar/test_attestation_aggregation.py @@ -13,7 +13,7 @@ class TestAggregatedAttestation: def test_aggregated_attestation_structure(self) -> None: """Test that aggregated attestation properly stores bits and data.""" - att_data = AttestationData( + attestation_data = AttestationData( slot=Slot(5), head=Checkpoint(root=Bytes32.zero(), slot=Slot(4)), target=Checkpoint(root=Bytes32.zero(), slot=Slot(3)), @@ -21,25 +21,27 @@ def test_aggregated_attestation_structure(self) -> None: ) bits = ValidatorIndices(data=[ValidatorIndex(2), ValidatorIndex(7)]).to_aggregation_bits() - agg = AggregatedAttestation(aggregation_bits=bits, data=att_data) + aggregate = AggregatedAttestation(aggregation_bits=bits, data=attestation_data) # Verify we can extract validator indices - indices = agg.aggregation_bits.to_validator_indices() + indices = aggregate.aggregation_bits.to_validator_indices() assert set(indices) == {ValidatorIndex(2), ValidatorIndex(7)} - assert agg.data == att_data + assert aggregate.data == attestation_data def test_aggregated_attestation_with_many_validators(self) -> None: """Test aggregated attestation with many validators.""" - att_data = AttestationData( + attestation_data = AttestationData( slot=Slot(10), head=Checkpoint(root=Bytes32.zero(), slot=Slot(9)), target=Checkpoint(root=Bytes32.zero(), slot=Slot(8)), source=Checkpoint(root=Bytes32.zero(), slot=Slot(7)), ) - validator_ids = ValidatorIndices(data=[ValidatorIndex(i) for i in [0, 5, 10, 15, 20, 25]]) - bits = validator_ids.to_aggregation_bits() - agg = AggregatedAttestation(aggregation_bits=bits, data=att_data) + validator_indices = ValidatorIndices( + data=[ValidatorIndex(i) for i in [0, 5, 10, 15, 20, 25]] + ) + bits = validator_indices.to_aggregation_bits() + aggregate = AggregatedAttestation(aggregation_bits=bits, data=attestation_data) - recovered = agg.aggregation_bits.to_validator_indices() - assert recovered == validator_ids + recovered = aggregate.aggregation_bits.to_validator_indices() + assert recovered == validator_indices diff --git a/tests/lean_spec/spec/forks/lstar/test_validator.py b/tests/lean_spec/spec/forks/lstar/test_validator.py index b78f9e7fa..6deb0aa30 100644 --- a/tests/lean_spec/spec/forks/lstar/test_validator.py +++ b/tests/lean_spec/spec/forks/lstar/test_validator.py @@ -82,8 +82,8 @@ def test_predicate_matches_classmethod(self, num_validators: int) -> None: for slot_num in slot_window: slot = Slot(slot_num) chosen = ValidatorIndex.proposer_for_slot(slot, registry_size) - for validator_idx in range(num_validators): - candidate = ValidatorIndex(validator_idx) + for validator_index in range(num_validators): + candidate = ValidatorIndex(validator_index) assert candidate.is_proposer_for(slot, registry_size) == (candidate == chosen) diff --git a/tests/lean_spec/spec/ssz/test_byte_arrays.py b/tests/lean_spec/spec/ssz/test_byte_arrays.py index 4f2dd08af..f0fa25fd3 100644 --- a/tests/lean_spec/spec/ssz/test_byte_arrays.py +++ b/tests/lean_spec/spec/ssz/test_byte_arrays.py @@ -167,7 +167,7 @@ def test_hex(self) -> None: """The hex method returns the lowercase hex string.""" assert Bytes4(b"\x00\x01\x02\x03").hex() == "00010203" - def test_len_iter_getitem(self) -> None: + def test_length_iter_getitem(self) -> None: """The instance supports len, iteration, and integer indexing.""" v = Bytes4(b"\x00\x01\x02\x03") assert len(v) == 4 @@ -228,31 +228,31 @@ def test_encode_decode_roundtrip(self, cls: type[BaseBytes], payload: bytes) -> assert v.encode_bytes() == payload assert cls.decode_bytes(payload) == v - buf = io.BytesIO() - n = v.serialize(buf) + buffer = io.BytesIO() + n = v.serialize(buffer) assert n == len(payload) - buf.seek(0) - v2 = cls.deserialize(buf, len(payload)) + buffer.seek(0) + v2 = cls.deserialize(buffer, len(payload)) assert v == v2 def test_deserialize_scope_mismatch_raises(self) -> None: """deserialize rejects a scope that doesn't match LENGTH.""" - buf = io.BytesIO(b"\x00\x01\x02\x03") + buffer = io.BytesIO(b"\x00\x01\x02\x03") with pytest.raises( SSZSerializationError, match=re.escape("Bytes4: expected 4 bytes, got 3"), ): - Bytes4.deserialize(buf, 3) + Bytes4.deserialize(buffer, 3) def test_deserialize_stream_truncation_raises(self) -> None: """deserialize detects when the stream ends before delivering scope bytes.""" - buf = io.BytesIO(b"\x00\x01") + buffer = io.BytesIO(b"\x00\x01") with pytest.raises( SSZSerializationError, match=re.escape("Bytes4: expected 4 bytes, got 2"), ): - Bytes4.deserialize(buf, 4) + Bytes4.deserialize(buffer, 4) class TestBaseBytesPydantic: @@ -432,40 +432,40 @@ class TestByteList(BaseByteList): assert x.encode_bytes() == data assert TestByteList.decode_bytes(data) == x - buf = io.BytesIO() - n = x.serialize(buf) + buffer = io.BytesIO() + n = x.serialize(buffer) assert n == len(data) - buf.seek(0) - y = TestByteList.deserialize(buf, len(data)) + buffer.seek(0) + y = TestByteList.deserialize(buffer, len(data)) assert y == x def test_deserialize_negative_scope_raises(self) -> None: """deserialize rejects a negative scope.""" - buf = io.BytesIO(b"") + buffer = io.BytesIO(b"") with pytest.raises( SSZSerializationError, match=re.escape("ByteList16: negative scope"), ): - ByteList16.deserialize(buf, -1) + ByteList16.deserialize(buffer, -1) def test_deserialize_over_limit_raises(self) -> None: """deserialize rejects a scope exceeding LIMIT.""" - buf = io.BytesIO(b"\x00" * 6) + buffer = io.BytesIO(b"\x00" * 6) with pytest.raises( SSZValueError, match=re.escape("ByteList5 exceeds limit of 5, got 6"), ): - ByteList5.deserialize(buf, 6) + ByteList5.deserialize(buffer, 6) def test_deserialize_stream_truncation_raises(self) -> None: """deserialize detects when the stream ends before delivering scope bytes.""" - buf = io.BytesIO(b"\x00\x01") + buffer = io.BytesIO(b"\x00\x01") with pytest.raises( SSZSerializationError, match=re.escape("ByteList16: expected 3 bytes, got 2"), ): - ByteList16.deserialize(buf, 3) + ByteList16.deserialize(buffer, 3) class TestBaseByteListPydantic: diff --git a/tests/lean_spec/spec/ssz/test_collections.py b/tests/lean_spec/spec/ssz/test_collections.py index ff8196209..f9ae892ca 100644 --- a/tests/lean_spec/spec/ssz/test_collections.py +++ b/tests/lean_spec/spec/ssz/test_collections.py @@ -924,10 +924,10 @@ def test_variable_size_list_rejects_final_offset_overflow(self) -> None: def test_variable_size_list_single_element_decodes(self) -> None: """A single-element list reads no further offsets after the first.""" - val = VariableContainer(a=Uint8(1), b=Uint16List4(data=[Uint16(10)])) - encoded = VariableContainerList2(data=[val]).encode_bytes() + value = VariableContainer(a=Uint8(1), b=Uint16List4(data=[Uint16(10)])) + encoded = VariableContainerList2(data=[value]).encode_bytes() - assert VariableContainerList2.decode_bytes(encoded) == VariableContainerList2(data=[val]) + assert VariableContainerList2.decode_bytes(encoded) == VariableContainerList2(data=[value]) class TestJsonSerialization: diff --git a/tests/lean_spec/spec/ssz/test_ssz_base.py b/tests/lean_spec/spec/ssz/test_ssz_base.py index d4a91850f..5bd376be5 100644 --- a/tests/lean_spec/spec/ssz/test_ssz_base.py +++ b/tests/lean_spec/spec/ssz/test_ssz_base.py @@ -34,27 +34,27 @@ class SmallBitlist(BaseBitlist): LIMIT = 8 -class TestSSZModelLen: +class TestSSZModelLength: """Tests for SSZModel.__len__() on both collection and container models. Uses BaseBitlist (not SSZList) for the data-path because SSZList overrides __len__ with its own implementation. BaseBitlist inherits SSZModel's version. """ - def test_len_data_path_via_bitlist(self) -> None: + def test_length_data_path_via_bitlist(self) -> None: """BaseBitlist delegates to SSZModel.__len__ which returns len(data).""" bl = SmallBitlist(data=(Boolean(True), Boolean(False), Boolean(True))) assert len(bl) == 3 - def test_len_empty_data_path_via_bitlist(self) -> None: + def test_length_empty_data_path_via_bitlist(self) -> None: bl = SmallBitlist(data=()) assert len(bl) == 0 - def test_len_container_returns_field_count(self) -> None: + def test_length_container_returns_field_count(self) -> None: container = TwoFieldContainer(x=Uint8(1), y=Uint16(2)) assert len(container) == 2 - def test_len_three_field_container(self) -> None: + def test_length_three_field_container(self) -> None: container = ThreeFieldContainer(a=Uint8(5), b=Uint64(42), c=Uint16List4(data=[Uint16(1)])) assert len(container) == 3 diff --git a/tests/lean_spec/spec/ssz/test_uint.py b/tests/lean_spec/spec/ssz/test_uint.py index 9be1e617d..1ca055421 100644 --- a/tests/lean_spec/spec/ssz/test_uint.py +++ b/tests/lean_spec/spec/ssz/test_uint.py @@ -51,7 +51,7 @@ def test_pydantic_validation_accepts_valid_int(uint_class: Type[BaseUint]) -> No """Tests that Pydantic validation correctly accepts a valid integer.""" model = UINT_MODELS[uint_class] instance = model(value=10) - value = instance.value # type: ignore[attr-defined] + value = instance.value # type: ignore[attribute-defined] assert isinstance(value, uint_class) assert value == uint_class(10) @@ -125,41 +125,41 @@ def test_max_method_returns_correct_value(uint_class: Type[BaseUint]) -> None: def test_arithmetic_operators(uint_class: Type[BaseUint]) -> None: """Tests all standard arithmetic operators.""" # Use smaller values for high-bit integers to avoid massive numbers - a_val, b_val = (100, 3) if uint_class.BITS > 8 else (20, 3) - a = uint_class(a_val) - b = uint_class(b_val) + a_value, b_value = (100, 3) if uint_class.BITS > 8 else (20, 3) + a = uint_class(a_value) + b = uint_class(b_value) max_int = (2**uint_class.BITS) - 1 - max_val = uint_class(max_int) + max_value = uint_class(max_int) name = uint_class.__name__ # Addition - assert a + b == uint_class(a_val + b_val) - expected = f"{max_int + b_val} out of range for {name} [0, {max_int}]" + assert a + b == uint_class(a_value + b_value) + expected = f"{max_int + b_value} out of range for {name} [0, {max_int}]" with pytest.raises(SSZValueError, match=rf"^{re.escape(expected)}$"): - _ = max_val + b + _ = max_value + b # Subtraction - assert a - b == uint_class(a_val - b_val) - expected = f"{b_val - a_val} out of range for {name} [0, {max_int}]" + assert a - b == uint_class(a_value - b_value) + expected = f"{b_value - a_value} out of range for {name} [0, {max_int}]" with pytest.raises(SSZValueError, match=rf"^{re.escape(expected)}$"): _ = b - a # Multiplication - assert a * b == uint_class(a_val * b_val) - expected = f"{max_int * b_val} out of range for {name} [0, {max_int}]" + assert a * b == uint_class(a_value * b_value) + expected = f"{max_int * b_value} out of range for {name} [0, {max_int}]" with pytest.raises(SSZValueError, match=rf"^{re.escape(expected)}$"): - _ = max_val * b + _ = max_value * b # Floor Division - assert a // b == uint_class(a_val // b_val) + assert a // b == uint_class(a_value // b_value) # Modulo - assert a % b == uint_class(a_val % b_val) + assert a % b == uint_class(a_value % b_value) # Exponentiation - assert uint_class(b_val) ** uint_class(4) == uint_class(b_val**4) + assert uint_class(b_value) ** uint_class(4) == uint_class(b_value**4) if uint_class.BITS <= 16: # Pow gets too big quickly - expected = f"{a_val**b_val} out of range for {name} [0, {max_int}]" + expected = f"{a_value**b_value} out of range for {name} [0, {max_int}]" with pytest.raises(SSZValueError, match=rf"^{re.escape(expected)}$"): _ = a**b @@ -316,8 +316,8 @@ def test_hash(uint_class: Type[BaseUint]) -> None: def test_index_list_access(uint_class: Type[BaseUint]) -> None: """Tests that Uint types can be used directly for list indexing.""" data = ["a", "b", "c", "d", "e"] - idx = uint_class(2) - assert data[idx] == "c" + index = uint_class(2) + assert data[index] == "c" assert data[uint_class(0)] == "a" assert data[uint_class(4)] == "e" @@ -353,18 +353,18 @@ def test_index_range(uint_class: Type[BaseUint]) -> None: @pytest.mark.parametrize("uint_class", ALL_UINT_TYPES) def test_index_hex_bin_oct(uint_class: Type[BaseUint]) -> None: """Tests that Uint types work with hex(), bin(), oct().""" - val = uint_class(42) - assert hex(val) == "0x2a" - assert bin(val) == "0b101010" - assert oct(val) == "0o52" + value = uint_class(42) + assert hex(value) == "0x2a" + assert bin(value) == "0b101010" + assert oct(value) == "0o52" @pytest.mark.parametrize("uint_class", ALL_UINT_TYPES) def test_index_operator_index(uint_class: Type[BaseUint]) -> None: """Tests that operator.index() works with Uint types.""" - val = uint_class(42) - assert operator.index(val) == 42 - assert isinstance(operator.index(val), int) + value = uint_class(42) + assert operator.index(value) == 42 + assert isinstance(operator.index(value), int) class TestUintSSZ: From 8b4f53bb24ce93e70579b3982cad4aef2cb40073 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Fri, 29 May 2026 19:15:42 +0200 Subject: [PATCH 2/2] fix(tests): correct XmssKeyManager __slots__ entry after rename The attribute self._keys_dir was renamed to self._keys_directory, but the matching __slots__ string entry must be renamed too -- otherwise setting the attribute raises AttributeError (slotted classes have no __dict__). Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/testing/src/consensus_testing/keys.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/testing/src/consensus_testing/keys.py b/packages/testing/src/consensus_testing/keys.py index c0e2f2bbb..833241f4c 100755 --- a/packages/testing/src/consensus_testing/keys.py +++ b/packages/testing/src/consensus_testing/keys.py @@ -184,7 +184,7 @@ class XmssKeyManager: "max_slot", "scheme_name", "scheme", - "_keys_dir", + "_keys_directory", "_json_cache", "_public_cache", "_available_indices",