From d0acd81157adb01dfd0b5ebf417d8c43758769f1 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Sun, 30 Jun 2019 10:02:18 +0100 Subject: [PATCH] WIP --- specs/core/0_beacon-chain.md | 664 +++++++++--------- specs/core/0_fork-choice.md | 10 +- specs/core/1_custody-game.md | 10 +- specs/core/1_shard-data-chains.md | 4 +- specs/light_client/sync_protocol.md | 2 +- specs/validator/0_beacon-chain-validator.md | 12 +- .../test/fork_choice/test_on_attestation.py | 2 +- .../eth2spec/test/helpers/attestations.py | 6 +- .../test/helpers/attester_slashings.py | 4 +- .../test_process_crosslinks.py | 2 +- ..._process_justification_and_finalization.py | 10 +- .../pyspec/eth2spec/test/test_finality.py | 2 +- 12 files changed, 373 insertions(+), 355 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 3f834c19c..c82524eb5 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -49,48 +49,52 @@ - [Beacon state](#beacon-state) - [`BeaconState`](#beaconstate) - [Helper functions](#helper-functions) - - [`xor`](#xor) - - [`hash`](#hash) - - [`hash_tree_root`](#hash_tree_root) - - [`signing_root`](#signing_root) - - [`bls_domain`](#bls_domain) - - [`slot_to_epoch`](#slot_to_epoch) - - [`get_previous_epoch`](#get_previous_epoch) - - [`get_current_epoch`](#get_current_epoch) - - [`get_epoch_start_slot`](#get_epoch_start_slot) - - [`is_active_validator`](#is_active_validator) - - [`is_slashable_validator`](#is_slashable_validator) - - [`get_active_validator_indices`](#get_active_validator_indices) - - [`increase_balance`](#increase_balance) - - [`decrease_balance`](#decrease_balance) - - [`get_epoch_committee_count`](#get_epoch_committee_count) - - [`get_shard_delta`](#get_shard_delta) - - [`get_epoch_start_shard`](#get_epoch_start_shard) - - [`get_attestation_data_slot`](#get_attestation_data_slot) - - [`get_block_root_at_slot`](#get_block_root_at_slot) - - [`get_block_root`](#get_block_root) - - [`get_randao_mix`](#get_randao_mix) - - [`get_compact_committees_root`](#get_compact_committees_root) - - [`generate_seed`](#generate_seed) - - [`get_beacon_proposer_index`](#get_beacon_proposer_index) - - [`verify_merkle_branch`](#verify_merkle_branch) - - [`get_shuffled_index`](#get_shuffled_index) - - [`compute_committee`](#compute_committee) - - [`get_crosslink_committee`](#get_crosslink_committee) - - [`get_attesting_indices`](#get_attesting_indices) - - [`int_to_bytes`](#int_to_bytes) - - [`bytes_to_int`](#bytes_to_int) - - [`get_total_balance`](#get_total_balance) - - [`get_domain`](#get_domain) - - [`convert_to_indexed`](#convert_to_indexed) - - [`validate_indexed_attestation`](#validate_indexed_attestation) - - [`is_slashable_attestation_data`](#is_slashable_attestation_data) - - [`integer_squareroot`](#integer_squareroot) - - [`get_delayed_activation_exit_epoch`](#get_delayed_activation_exit_epoch) - - [`get_churn_limit`](#get_churn_limit) - - [`bls_verify`](#bls_verify) - - [`bls_verify_multiple`](#bls_verify_multiple) - - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) + - [Math and crypto](#math-and-crypto) + - [`int_to_bytes`](#int_to_bytes) + - [`bytes_to_int`](#bytes_to_int) + - [`integer_squareroot`](#integer_squareroot) + - [`xor`](#xor) + - [`hash`](#hash) + - [`hash_tree_root`](#hash_tree_root) + - [`signing_root`](#signing_root) + - [`bls_verify`](#bls_verify) + - [`bls_verify_multiple`](#bls_verify_multiple) + - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) + - [Beacon state getters](#beacon-state-getters) + - [`get_current_epoch`](#get_current_epoch) + - [`get_previous_epoch`](#get_previous_epoch) + - [`get_committee_count`](#get_committee_count) + - [`get_start_shard`](#get_start_shard) + - [`get_active_validator_indices`](#get_active_validator_indices) + + + - [`bls_domain`](#bls_domain) + - [`slot_to_epoch`](#slot_to_epoch) + - [`epoch_start_slot`](#epoch_start_slot) + - [`is_active_validator`](#is_active_validator) + - [`is_slashable_validator`](#is_slashable_validator) + - [`increase_balance`](#increase_balance) + - [`decrease_balance`](#decrease_balance) + - [`get_shard_delta`](#get_shard_delta) + - [`get_attestation_data_slot`](#get_attestation_data_slot) + - [`get_block_root_at_slot`](#get_block_root_at_slot) + - [`get_block_root`](#get_block_root) + - [`get_randao_mix`](#get_randao_mix) + - [`get_compact_committees_root`](#get_compact_committees_root) + - [`get_seed`](#get_seed) + - [`get_beacon_proposer_index`](#get_beacon_proposer_index) + - [`verify_merkle_branch`](#verify_merkle_branch) + - [`get_shuffled_index`](#get_shuffled_index) + - [`compute_committee`](#compute_committee) + - [`get_crosslink_committee`](#get_crosslink_committee) + - [`get_attesting_indices`](#get_attesting_indices) + - [`get_total_balance`](#get_total_balance) + - [`get_domain`](#get_domain) + - [`get_indexed_attestation`](#get_indexed_attestation) + - [`validate_indexed_attestation`](#validate_indexed_attestation) + - [`is_slashable_attestation_data`](#is_slashable_attestation_data) + - [`get_delayed_activation_exit_epoch`](#get_delayed_activation_exit_epoch) + - [`get_churn_limit`](#get_churn_limit) - [Routines for updating validator status](#routines-for-updating-validator-status) - [`initiate_validator_exit`](#initiate_validator_exit) - [`slash_validator`](#slash_validator) @@ -547,133 +551,154 @@ class BeaconState(Container): *Note*: The definitions below are for specification purposes and are not necessarily optimal implementations. -### `xor` +### Math + +#### `int_to_bytes` + +```python +def int_to_bytes(integer: int, length: int) -> bytes: + return integer.to_bytes(length, 'little') +``` + +#### `bytes_to_int` + +```python +def bytes_to_int(data: bytes) -> int: + return int.from_bytes(data, 'little') +``` + +#### `integer_squareroot` + +```python +def integer_squareroot(n: int) -> int: + """ + Return the largest integer ``x`` such that ``x**2 <= n``. + """ + assert n >= 0 + x = n + y = (x + 1) // 2 + while y < x: + x = y + y = (x + n // x) // 2 + return x +``` + +#### `xor` ```python def xor(bytes1: Bytes32, bytes2: Bytes32) -> Bytes32: + """ + Return the exclusive or of two 32-byte strings. + """ return Bytes32(a ^ b for a, b in zip(bytes1, bytes2)) ``` -### `hash` +### Crypto + +#### `hash` The `hash` function is SHA256. -*Note*: We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethereum 2.0 deployment phase. +#### `hash_tree_root` -### `hash_tree_root` +`def hash_tree_root(object: SSZSerializable) -> Hash` is a function for hashing objects into a single root by utilizing a hash tree structure, as defined in the [SSZ spec](../simple-serialize.md#merkleization). -`def hash_tree_root(object: SSZSerializable) -> Hash` is a function for hashing objects into a single root by utilizing a hash tree structure, as defined in the [SimpleSerialize spec](../simple-serialize.md#merkleization). +#### `signing_root` -### `signing_root` +`def signing_root(object: Container) -> Hash` is a function for computing signing messages, as defined in the [SSZ spec](../simple-serialize.md#self-signed-containers). -`def signing_root(object: Container) -> Hash` is a function for computing signing messages, as defined in the [SimpleSerialize spec](../simple-serialize.md#self-signed-containers). +#### `bls_verify` -### `bls_domain` +`bls_verify` is a function for verifying a BLS signature, as defined in the [BLS Signature spec](../bls_signature.md#bls_verify). + +#### `bls_verify_multiple` + +`bls_verify_multiple` is a function for verifying a BLS signature constructed from multiple messages, as defined in the [BLS Signature spec](../bls_signature.md#bls_verify_multiple). + +#### `bls_aggregate_pubkeys` + +`bls_aggregate_pubkeys` is a function for aggregating multiple BLS public keys into a single aggregate key, as defined in the [BLS Signature spec](../bls_signature.md#bls_aggregate_pubkeys). + +### Beacon state getters + +#### `get_current_epoch` ```python -def bls_domain(domain_type: int, fork_version: bytes=b'\x00\x00\x00\x00') -> int: +def get_current_epoch(state: BeaconState) -> Epoch: """ - Return the bls domain given by the ``domain_type`` and optional 4 byte ``fork_version`` (defaults to zero). + Return the current epoch. """ - return bytes_to_int(int_to_bytes(domain_type, length=4) + fork_version) + return slot_to_epoch(state.slot) ``` -### `slot_to_epoch` - -```python -def slot_to_epoch(slot: Slot) -> Epoch: - """ - Return the epoch number of the given ``slot``. - """ - return Epoch(slot // SLOTS_PER_EPOCH) -``` - -### `get_previous_epoch` +#### `get_previous_epoch` ```python def get_previous_epoch(state: BeaconState) -> Epoch: """` - Return the previous epoch of the given ``state``. - Return the current epoch if it's genesis epoch. + Return the previous epoch (current epoch if at ``GENESIS_EPOCH``). """ current_epoch = get_current_epoch(state) return GENESIS_EPOCH if current_epoch == GENESIS_EPOCH else Epoch(current_epoch - 1) ``` -### `get_current_epoch` +#### `get_block_root` ```python -def get_current_epoch(state: BeaconState) -> Epoch: +def get_block_root(state: BeaconState, epoch: Epoch) -> Hash: """ - Return the current epoch of the given ``state``. + Return the block root at a recent ``epoch``. """ - return slot_to_epoch(state.slot) + return get_block_root_at_slot(state, epoch_start_slot(epoch)) ``` -### `get_epoch_start_slot` +#### `get_block_root_at_slot` ```python -def get_epoch_start_slot(epoch: Epoch) -> Slot: +def get_block_root_at_slot(state: BeaconState, slot: Slot) -> Hash: """ - Return the starting slot of the given ``epoch``. + Return the block root at a recent ``slot``. """ - return Slot(epoch * SLOTS_PER_EPOCH) + assert slot < state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT + return state.block_roots[slot % SLOTS_PER_HISTORICAL_ROOT] ``` -### `is_active_validator` +#### `get_randao_mix` ```python -def is_active_validator(validator: Validator, epoch: Epoch) -> bool: +def get_randao_mix(state: BeaconState, epoch: Epoch) -> Hash: """ - Check if ``validator`` is active. + Return the randao mix at a recent ``epoch``. """ - return validator.activation_epoch <= epoch < validator.exit_epoch + return state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR] ``` -### `is_slashable_validator` - -```python -def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool: - """ - Check if ``validator`` is slashable. - """ - return (not validator.slashed) and (validator.activation_epoch <= epoch < validator.withdrawable_epoch) -``` - -### `get_active_validator_indices` +#### `get_active_validator_indices` ```python def get_active_validator_indices(state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]: """ - Get active validator indices at ``epoch``. + Return the sequence of active validator indices at ``epoch``. """ return [ValidatorIndex(i) for i, v in enumerate(state.validators) if is_active_validator(v, epoch)] ``` -### `increase_balance` +#### `get_churn_limit` ```python -def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: +def get_churn_limit(state: BeaconState) -> int: """ - Increase validator balance by ``delta``. + Return the validator churn limit for the current epoch. """ - state.balances[index] += delta + active_validator_indices = get_active_validator_indices(state, get_current_epoch(state)) + return max(MIN_PER_EPOCH_CHURN_LIMIT, len(active_validator_indices) // CHURN_LIMIT_QUOTIENT) ``` -### `decrease_balance` + +#### `get_committee_count` ```python -def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: - """ - Decrease validator balance by ``delta`` with underflow protection. - """ - state.balances[index] = 0 if delta > state.balances[index] else state.balances[index] - delta -``` - -### `get_epoch_committee_count` - -```python -def get_epoch_committee_count(state: BeaconState, epoch: Epoch) -> int: +def get_committee_count(state: BeaconState, epoch: Epoch) -> int: """ Return the number of committees at ``epoch``. """ @@ -687,22 +712,41 @@ def get_epoch_committee_count(state: BeaconState, epoch: Epoch) -> int: ) * SLOTS_PER_EPOCH ``` -### `get_shard_delta` +#### `get_seed` ```python -def get_shard_delta(state: BeaconState, epoch: Epoch) -> int: +def get_seed(state: BeaconState, epoch: Epoch) -> Hash: """ - Return the number of shards to increment ``state.start_shard`` during ``epoch``. + Return the seed at ``epoch``. """ - return min(get_epoch_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH) + return hash( + get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD)) + # Avoid underflow + hash_tree_root(List[ValidatorIndex, VALIDATOR_REGISTRY_LIMIT](get_active_validator_indices(state, epoch))) + + int_to_bytes(epoch, length=32) + ) ``` -### `get_epoch_start_shard` +#### `get_crosslink_committee` ```python -def get_epoch_start_shard(state: BeaconState, epoch: Epoch) -> Shard: +def get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard) -> Sequence[ValidatorIndex]: """ - Return the start shard of the 0th committee in an epoch. + Return the crosslink committee at ``epoch`` for ``shard``. + """ + return compute_committee( + indices=get_active_validator_indices(state, epoch), + seed=get_seed(state, epoch), + index=(shard + SHARD_COUNT - get_start_shard(state, epoch)) % SHARD_COUNT, + count=get_committee_count(state, epoch), + ) +``` + +#### `get_start_shard` + +```python +def get_start_shard(state: BeaconState, epoch: Epoch) -> Shard: + """ + Return the start shard of the 0th committee at ``epoch``. """ assert epoch <= get_current_epoch(state) + 1 check_epoch = Epoch(get_current_epoch(state) + 1) @@ -713,60 +757,62 @@ def get_epoch_start_shard(state: BeaconState, epoch: Epoch) -> Shard: return shard ``` -### `get_attestation_data_slot` +#### `get_shard_delta` + +```python +def get_shard_delta(state: BeaconState, epoch: Epoch) -> Shard: + """ + Return the number of shards to increment ``state.start_shard`` at ``epoch``. + """ + return Shard(min(get_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH)) +``` + +#### `get_beacon_proposer_index` + +```python +def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: + """ + Return the beacon proposer index at the current epoch. + """ + epoch = get_current_epoch(state) + committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH + offset = committees_per_slot * (state.slot % SLOTS_PER_EPOCH) + shard = Shard((get_start_shard(state, epoch) + offset) % SHARD_COUNT) + first_committee = get_crosslink_committee(state, epoch, shard) + MAX_RANDOM_BYTE = 2**8 - 1 + seed = get_seed(state, epoch) + i = 0 + while True: + candidate_index = first_committee[(epoch + i) % len(first_committee)] + random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32] + effective_balance = state.validators[candidate_index].effective_balance + if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: + return ValidatorIndex(candidate_index) + i += 1 +``` + +#### `get_attestation_data_slot` ```python def get_attestation_data_slot(state: BeaconState, data: AttestationData) -> Slot: - committee_count = get_epoch_committee_count(state, data.target.epoch) - offset = (data.crosslink.shard + SHARD_COUNT - get_epoch_start_shard(state, data.target.epoch)) % SHARD_COUNT - return Slot(get_epoch_start_slot(data.target.epoch) + offset // (committee_count // SLOTS_PER_EPOCH)) + """ + Return the slot corresponding to the attestation ``data``. + """ + committee_count = get_committee_count(state, data.target.epoch) + offset = (data.crosslink.shard + SHARD_COUNT - get_start_shard(state, data.target.epoch)) % SHARD_COUNT + return Slot(epoch_start_slot(data.target.epoch) + offset // (committee_count // SLOTS_PER_EPOCH)) ``` -### `get_block_root_at_slot` - -```python -def get_block_root_at_slot(state: BeaconState, - slot: Slot) -> Hash: - """ - Return the block root at a recent ``slot``. - """ - assert slot < state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT - return state.block_roots[slot % SLOTS_PER_HISTORICAL_ROOT] -``` - -### `get_block_root` - -```python -def get_block_root(state: BeaconState, - epoch: Epoch) -> Hash: - """ - Return the block root at a recent ``epoch``. - """ - return get_block_root_at_slot(state, get_epoch_start_slot(epoch)) -``` - -### `get_randao_mix` - -```python -def get_randao_mix(state: BeaconState, - epoch: Epoch) -> Hash: - """ - Return the randao mix at a recent ``epoch``. - ``epoch`` expected to be between (current_epoch - EPOCHS_PER_HISTORICAL_VECTOR, current_epoch]. - """ - return state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR] -``` - -### `get_compact_committees_root` +#### `get_compact_committees_root` ```python def get_compact_committees_root(state: BeaconState, epoch: Epoch) -> Hash: """ - Return the compact committee root for the current epoch. + Return the compact committee root at ``epoch``. """ committees = [CompactCommittee() for _ in range(SHARD_COUNT)] - start_shard = get_epoch_start_shard(state, epoch) - for committee_number in range(get_epoch_committee_count(state, epoch)): + start_shard = get_start_shard(state, epoch) + for committee_number in range(get_committee_count(state, epoch)): shard = Shard((start_shard + committee_number) % SHARD_COUNT) for index in get_crosslink_committee(state, epoch, shard): validator = state.validators[index] @@ -778,63 +824,159 @@ def get_compact_committees_root(state: BeaconState, epoch: Epoch) -> Hash: return hash_tree_root(Vector[CompactCommittee, SHARD_COUNT](committees)) ``` -### `generate_seed` +#### `get_total_balance` ```python -def generate_seed(state: BeaconState, - epoch: Epoch) -> Hash: +def get_total_balance(state: BeaconState, indices: Set[ValidatorIndex]) -> Gwei: """ - Generate a seed for the given ``epoch``. + Return the combined effective balance of the ``indices``. (1 Gwei minimum to avoid divisions by zero.) """ - return hash( - get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD)) + # Avoid underflow - hash_tree_root(List[ValidatorIndex, VALIDATOR_REGISTRY_LIMIT](get_active_validator_indices(state, epoch))) + - int_to_bytes(epoch, length=32) + return Gwei(max(sum([state.validators[index].effective_balance for index in indices]), 1)) +``` + +#### `get_domain` + +```python +def get_domain(state: BeaconState, domain_type: int, message_epoch: Epoch=None) -> int: + """ + Return the signature domain (fork version concatenated with domain type) of a message. + """ + epoch = get_current_epoch(state) if message_epoch is None else message_epoch + fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version + return bls_domain(domain_type, fork_version) +``` + +#### `get_indexed_attestation` + +```python +def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> IndexedAttestation: + """ + Return the indexed attestation corresponding to ``attestation``. + """ + attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) + custody_bit_1_indices = get_attesting_indices(state, attestation.data, attestation.custody_bits) + assert custody_bit_1_indices.issubset(attesting_indices) + custody_bit_0_indices = attesting_indices.difference(custody_bit_1_indices) + + return IndexedAttestation( + custody_bit_0_indices=sorted(custody_bit_0_indices), + custody_bit_1_indices=sorted(custody_bit_1_indices), + data=attestation.data, + signature=attestation.signature, ) ``` -### `get_beacon_proposer_index` +#### `get_attesting_indices` ```python -def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: +def get_attesting_indices(state: BeaconState, + data: AttestationData, + bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Set[ValidatorIndex]: """ - Return the current beacon proposer index. + Return the set of attesting indices corresponding to ``data`` and ``bits``. """ - epoch = get_current_epoch(state) - committees_per_slot = get_epoch_committee_count(state, epoch) // SLOTS_PER_EPOCH - offset = committees_per_slot * (state.slot % SLOTS_PER_EPOCH) - shard = Shard((get_epoch_start_shard(state, epoch) + offset) % SHARD_COUNT) - first_committee = get_crosslink_committee(state, epoch, shard) - MAX_RANDOM_BYTE = 2**8 - 1 - seed = generate_seed(state, epoch) - i = 0 - while True: - candidate_index = first_committee[(epoch + i) % len(first_committee)] - random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32] - effective_balance = state.validators[candidate_index].effective_balance - if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: - return ValidatorIndex(candidate_index) - i += 1 + committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard) + return set(index for i, index in enumerate(committee) if bits[i]) ``` -### `verify_merkle_branch` + + + + + + + + + + +#### `verify_merkle_branch` ```python -def verify_merkle_branch(leaf: Hash, proof: Sequence[Hash], depth: int, index: int, root: Hash) -> bool: +def verify_merkle_branch(leaf: Hash, branch: Sequence[Hash], depth: int, index: int, root: Hash) -> bool: """ - Verify that the given ``leaf`` is on the merkle branch ``proof`` - starting with the given ``root``. + Verify that the ``leaf`` at ``index`` verifies against ``root`` on the Merkle ``branch``. """ value = leaf for i in range(depth): if index // (2**i) % 2: - value = hash(proof[i] + value) + value = hash(branch[i] + value) else: - value = hash(value + proof[i]) + value = hash(value + branch[i]) return value == root ``` -### `get_shuffled_index` + +#### `bls_domain` + +```python +def bls_domain(domain_type: int, fork_version: bytes=b'\x00\x00\x00\x00') -> int: + """ + Return the bls domain given by the ``domain_type`` and optional 4 byte ``fork_version`` (defaults to zero). + """ + return bytes_to_int(int_to_bytes(domain_type, length=4) + fork_version) +``` + +#### `slot_to_epoch` + +```python +def slot_to_epoch(slot: Slot) -> Epoch: + """ + Return the epoch number of the given ``slot``. + """ + return Epoch(slot // SLOTS_PER_EPOCH) +``` + +#### `epoch_start_slot` + +```python +def epoch_start_slot(epoch: Epoch) -> Slot: + """ + Return the starting slot of the given ``epoch``. + """ + return Slot(epoch * SLOTS_PER_EPOCH) +``` + +#### `is_active_validator` + +```python +def is_active_validator(validator: Validator, epoch: Epoch) -> bool: + """ + Check if ``validator`` is active. + """ + return validator.activation_epoch <= epoch < validator.exit_epoch +``` + +#### `is_slashable_validator` + +```python +def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool: + """ + Check if ``validator`` is slashable. + """ + return (not validator.slashed) and (validator.activation_epoch <= epoch < validator.withdrawable_epoch) +``` + +#### `increase_balance` + +```python +def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: + """ + Increase validator balance by ``delta``. + """ + state.balances[index] += delta +``` + +#### `decrease_balance` + +```python +def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: + """ + Decrease validator balance by ``delta`` with underflow protection. + """ + state.balances[index] = 0 if delta > state.balances[index] else state.balances[index] - delta +``` + +#### `get_shuffled_index` ```python def get_shuffled_index(index: ValidatorIndex, index_count: int, seed: Hash) -> ValidatorIndex: @@ -861,7 +1003,7 @@ def get_shuffled_index(index: ValidatorIndex, index_count: int, seed: Hash) -> V return ValidatorIndex(index) ``` -### `compute_committee` +#### `compute_committee` ```python def compute_committee(indices: Sequence[ValidatorIndex], @@ -871,90 +1013,7 @@ def compute_committee(indices: Sequence[ValidatorIndex], return [indices[get_shuffled_index(ValidatorIndex(i), len(indices), seed)] for i in range(start, end)] ``` -### `get_crosslink_committee` - -```python -def get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard) -> Sequence[ValidatorIndex]: - return compute_committee( - indices=get_active_validator_indices(state, epoch), - seed=generate_seed(state, epoch), - index=(shard + SHARD_COUNT - get_epoch_start_shard(state, epoch)) % SHARD_COUNT, - count=get_epoch_committee_count(state, epoch), - ) -``` - -### `get_attesting_indices` - -```python -def get_attesting_indices(state: BeaconState, - data: AttestationData, - bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Set[ValidatorIndex]: - """ - Return the set of attesting indices corresponding to ``data`` and ``bitfield``. - """ - committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard) - return set(index for i, index in enumerate(committee) if bits[i]) -``` - -### `int_to_bytes` - -```python -def int_to_bytes(integer: int, length: int) -> bytes: - return integer.to_bytes(length, 'little') -``` - -### `bytes_to_int` - -```python -def bytes_to_int(data: bytes) -> int: - return int.from_bytes(data, 'little') -``` - -### `get_total_balance` - -```python -def get_total_balance(state: BeaconState, indices: Set[ValidatorIndex]) -> Gwei: - """ - Return the combined effective balance of the ``indices``. (1 Gwei minimum to avoid divisions by zero.) - """ - return Gwei(max(sum([state.validators[index].effective_balance for index in indices]), 1)) -``` - -### `get_domain` - -```python -def get_domain(state: BeaconState, - domain_type: int, - message_epoch: Epoch=None) -> int: - """ - Return the signature domain (fork version concatenated with domain type) of a message. - """ - epoch = get_current_epoch(state) if message_epoch is None else message_epoch - fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version - return bls_domain(domain_type, fork_version) -``` - -### `convert_to_indexed` - -```python -def convert_to_indexed(state: BeaconState, attestation: Attestation) -> IndexedAttestation: - """ - Convert ``attestation`` to (almost) indexed-verifiable form. - """ - attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) - custody_bit_1_indices = get_attesting_indices(state, attestation.data, attestation.custody_bits) - assert custody_bit_1_indices.issubset(attesting_indices) - custody_bit_0_indices = attesting_indices.difference(custody_bit_1_indices) - - return IndexedAttestation( - custody_bit_0_indices=sorted(custody_bit_0_indices), - custody_bit_1_indices=sorted(custody_bit_1_indices), - data=attestation.data, - signature=attestation.signature, - ) -``` - -### `validate_indexed_attestation` +#### `validate_indexed_attestation` ```python def validate_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> None: @@ -987,7 +1046,7 @@ def validate_indexed_attestation(state: BeaconState, indexed_attestation: Indexe ) ``` -### `is_slashable_attestation_data` +#### `is_slashable_attestation_data` ```python def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationData) -> bool: @@ -1002,23 +1061,7 @@ def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationDa ) ``` -### `integer_squareroot` - -```python -def integer_squareroot(n: int) -> int: - """ - The largest integer ``x`` such that ``x**2`` is less than or equal to ``n``. - """ - assert n >= 0 - x = n - y = (x + 1) // 2 - while y < x: - x = y - y = (x + n // x) // 2 - return x -``` - -### `get_delayed_activation_exit_epoch` +#### `get_delayed_activation_exit_epoch` ```python def get_delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: @@ -1028,31 +1071,6 @@ def get_delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: return Epoch(epoch + 1 + ACTIVATION_EXIT_DELAY) ``` -### `get_churn_limit` - -```python -def get_churn_limit(state: BeaconState) -> int: - """ - Return the churn limit based on the active validator count. - """ - return max( - MIN_PER_EPOCH_CHURN_LIMIT, - len(get_active_validator_indices(state, get_current_epoch(state))) // CHURN_LIMIT_QUOTIENT - ) -``` - -### `bls_verify` - -`bls_verify` is a function for verifying a BLS signature, as defined in the [BLS Signature spec](../bls_signature.md#bls_verify). - -### `bls_verify_multiple` - -`bls_verify_multiple` is a function for verifying a BLS signature constructed from multiple messages, as defined in the [BLS Signature spec](../bls_signature.md#bls_verify_multiple). - -### `bls_aggregate_pubkeys` - -`bls_aggregate_pubkeys` is a function for aggregating multiple BLS public keys into a single aggregate key, as defined in the [BLS Signature spec](../bls_signature.md#bls_aggregate_pubkeys). - ### Routines for updating validator status *Note*: All functions in this section mutate `state`. @@ -1340,8 +1358,8 @@ def process_justification_and_finalization(state: BeaconState) -> None: def process_crosslinks(state: BeaconState) -> None: state.previous_crosslinks = [c for c in state.current_crosslinks] for epoch in (get_previous_epoch(state), get_current_epoch(state)): - for offset in range(get_epoch_committee_count(state, epoch)): - shard = Shard((get_epoch_start_shard(state, epoch) + offset) % SHARD_COUNT) + for offset in range(get_committee_count(state, epoch)): + shard = Shard((get_start_shard(state, epoch) + offset) % SHARD_COUNT) crosslink_committee = set(get_crosslink_committee(state, epoch, shard)) winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, epoch, shard) if 3 * get_total_balance(state, attesting_indices) >= 2 * get_total_balance(state, crosslink_committee): @@ -1413,8 +1431,8 @@ def get_crosslink_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[G rewards = [Gwei(0) for _ in range(len(state.validators))] penalties = [Gwei(0) for _ in range(len(state.validators))] epoch = get_previous_epoch(state) - for offset in range(get_epoch_committee_count(state, epoch)): - shard = Shard((get_epoch_start_shard(state, epoch) + offset) % SHARD_COUNT) + for offset in range(get_committee_count(state, epoch)): + shard = Shard((get_start_shard(state, epoch) + offset) % SHARD_COUNT) crosslink_committee = set(get_crosslink_committee(state, epoch, shard)) winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, epoch, shard) attesting_balance = get_total_balance(state, attesting_indices) @@ -1674,7 +1692,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: assert data.crosslink.data_root == ZERO_HASH # [to be removed in phase 1] # Check signature - validate_indexed_attestation(state, convert_to_indexed(state, attestation)) + validate_indexed_attestation(state, get_indexed_attestation(state, attestation)) ``` ##### Deposits @@ -1687,7 +1705,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: # Verify the Merkle branch assert verify_merkle_branch( leaf=hash_tree_root(deposit.data), - proof=deposit.proof, + branch=deposit.proof, depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # add 1 for the SSZ length mix-in index=state.eth1_deposit_index, root=state.eth1_data.deposit_root, diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index c54824f3b..64afb3f6b 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -124,7 +124,7 @@ def get_latest_attesting_balance(store: Store, root: Hash) -> Gwei: def get_head(store: Store) -> Hash: # Execute the LMD-GHOST fork choice head = store.justified_checkpoint.root - justified_slot = get_epoch_start_slot(store.justified_checkpoint.epoch) + justified_slot = epoch_start_slot(store.justified_checkpoint.epoch) while True: children = [ root for root in store.blocks.keys() @@ -162,7 +162,7 @@ def on_block(store: Store, block: BeaconBlock) -> None: store.finalized_checkpoint.root ) # Check that block is later than the finalized epoch slot - assert block.slot > get_epoch_start_slot(store.finalized_checkpoint.epoch) + assert block.slot > epoch_start_slot(store.finalized_checkpoint.epoch) # Check the block is valid and compute the post-state state = state_transition(pre_state, block) # Add new state for this block to the store @@ -190,11 +190,11 @@ def on_attestation(store: Store, attestation: Attestation) -> None: # Attestations cannot be from future epochs. If they are, delay consideration until the epoch arrivesr base_state = store.block_states[target.root].copy() - assert store.time >= base_state.genesis_time + get_epoch_start_slot(target.epoch) * SECONDS_PER_SLOT + assert store.time >= base_state.genesis_time + epoch_start_slot(target.epoch) * SECONDS_PER_SLOT # Store target checkpoint state if not yet seen if target not in store.checkpoint_states: - process_slots(base_state, get_epoch_start_slot(target.epoch)) + process_slots(base_state, epoch_start_slot(target.epoch)) store.checkpoint_states[target] = base_state target_state = store.checkpoint_states[target] @@ -204,7 +204,7 @@ def on_attestation(store: Store, attestation: Attestation) -> None: assert store.time >= (attestation_slot + 1) * SECONDS_PER_SLOT # Get state at the `target` to validate attestation and calculate the committees - indexed_attestation = convert_to_indexed(target_state, attestation) + indexed_attestation = get_indexed_attestation(target_state, attestation) validate_indexed_attestation(target_state, indexed_attestation) # Update latest messages diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index 24076fef6..1fb5b4d3e 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -473,7 +473,7 @@ For each `challenge` in `block.body.custody_chunk_challenges`, run the following ```python def process_chunk_challenge(state: BeaconState, challenge: CustodyChunkChallenge) -> None: # Verify the attestation - validate_indexed_attestation(state, convert_to_indexed(state, challenge.attestation)) + validate_indexed_attestation(state, get_indexed_attestation(state, challenge.attestation)) # Verify it is not too late to challenge assert slot_to_epoch(challenge.attestation.data.slot) >= get_current_epoch(state) - MAX_CHUNK_CHALLENGE_DELAY responder = state.validators[challenge.responder_index] @@ -526,7 +526,7 @@ def process_bit_challenge(state: BeaconState, challenge: CustodyBitChallenge) -> # Verify challenger is slashable assert is_slashable_validator(challenger, get_current_epoch(state)) # Verify attestation - validate_indexed_attestation(state, convert_to_indexed(state, attestation)) + validate_indexed_attestation(state, get_indexed_attestation(state, attestation)) # Verify attestation is eligible for challenging responder = state.validators[challenge.responder_index] assert epoch + responder.max_reveal_lateness <= get_reveal_period(state, challenge.responder_index) @@ -601,7 +601,7 @@ def process_chunk_challenge_response(state: BeaconState, # Verify the chunk matches the crosslink data root assert verify_merkle_branch( leaf=hash_tree_root(response.chunk), - proof=response.data_branch, + branch=response.data_branch, depth=challenge.depth, index=response.chunk_index, root=challenge.data_root, @@ -626,7 +626,7 @@ def process_bit_challenge_response(state: BeaconState, # Verify the chunk matches the crosslink data root assert verify_merkle_branch( leaf=hash_tree_root(response.chunk), - proof=response.data_branch, + branch=response.data_branch, depth=ceillog2(challenge.chunk_count), index=response.chunk_index, root=challenge.data_root, @@ -634,7 +634,7 @@ def process_bit_challenge_response(state: BeaconState, # Verify the chunk bit leaf matches the challenge data assert verify_merkle_branch( leaf=response.chunk_bits_leaf, - proof=response.chunk_bits_branch, + branch=response.chunk_bits_branch, depth=ceillog2(challenge.chunk_count) >> 8, index=response.chunk_index // 256, root=challenge.chunk_bits_merkle_root diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 432820b7c..eb20abe7f 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -139,7 +139,7 @@ def get_period_committee(state: BeaconState, """ return compute_committee( indices=get_active_validator_indices(state, epoch), - seed=generate_seed(state, epoch), + seed=get_seed(state, epoch), index=shard * count + index, count=SHARD_COUNT * count, ) @@ -150,7 +150,7 @@ def get_period_committee(state: BeaconState, ```python def get_switchover_epoch(state: BeaconState, epoch: Epoch, index: ValidatorIndex) -> int: earlier_start_epoch = Epoch(epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD * 2) - return (bytes_to_int(hash(generate_seed(state, earlier_start_epoch) + int_to_bytes(index, length=3)[0:8])) + return (bytes_to_int(hash(get_seed(state, earlier_start_epoch) + int_to_bytes(index, length=3)[0:8])) % PERSISTENT_COMMITTEE_PERIOD) ``` diff --git a/specs/light_client/sync_protocol.md b/specs/light_client/sync_protocol.md index 9a089720b..2b1703f21 100644 --- a/specs/light_client/sync_protocol.md +++ b/specs/light_client/sync_protocol.md @@ -84,7 +84,7 @@ def get_period_data(block: ExtendedBeaconBlock, shard_id: Shard, later: bool) -> indices = get_period_committee(block.state, shard_id, period_start, 0, committee_count) return PeriodData( validator_count, - generate_seed(block.state, period_start), + get_seed(block.state, period_start), [block.state.validators[i] for i in indices], ) ``` diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 1b103b218..f4a83b833 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -145,11 +145,11 @@ def get_committee_assignment( next_epoch = get_current_epoch(state) + 1 assert epoch <= next_epoch - committees_per_slot = get_epoch_committee_count(state, epoch) // SLOTS_PER_EPOCH - epoch_start_slot = get_epoch_start_slot(epoch) - for slot in range(epoch_start_slot, epoch_start_slot + SLOTS_PER_EPOCH): + committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH + start_slot = epoch_start_slot(epoch) + for slot in range(start_slot, start_slot + SLOTS_PER_EPOCH): offset = committees_per_slot * (slot % SLOTS_PER_EPOCH) - slot_start_shard = (get_epoch_start_shard(state, epoch) + offset) % SHARD_COUNT + slot_start_shard = (get_start_shard(state, epoch) + offset) % SHARD_COUNT for i in range(committees_per_slot): shard = (slot_start_shard + i) % SHARD_COUNT committee = get_crosslink_committee(state, epoch, shard) @@ -307,8 +307,8 @@ Set `attestation_data.beacon_block_root = signing_root(head_block)`. * Set `attestation_data.target_root = epoch_boundary_block_root` where `epoch_boundary_block_root` is the root of block at the most recent epoch boundary. *Note*: `epoch_boundary_block_root` can be looked up in the state using: -* Let `epoch_start_slot = get_epoch_start_slot(get_current_epoch(head_state))`. -* Let `epoch_boundary_block_root = signing_root(head_block) if epoch_start_slot == head_state.slot else get_block_root(state, epoch_start_slot)`. +* Let `start_slot = epoch_start_slot(get_current_epoch(head_state))`. +* Let `epoch_boundary_block_root = signing_root(head_block) if start_slot == head_state.slot else get_block_root(state, start_slot)`. ##### Crosslink vote diff --git a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py index 5adb022a6..400675888 100644 --- a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py +++ b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py @@ -14,7 +14,7 @@ def run_on_attestation(spec, state, store, attestation, valid=True): else: assert False - indexed_attestation = spec.convert_to_indexed(state, attestation) + indexed_attestation = spec.get_indexed_attestation(state, attestation) spec.on_attestation(store, attestation) assert ( store.latest_messages[indexed_attestation.custody_bit_0_indices[0]] == diff --git a/test_libs/pyspec/eth2spec/test/helpers/attestations.py b/test_libs/pyspec/eth2spec/test/helpers/attestations.py index ffd8d1afa..38635bafe 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/attestations.py +++ b/test_libs/pyspec/eth2spec/test/helpers/attestations.py @@ -15,7 +15,7 @@ def build_attestation_data(spec, state, slot, shard): else: block_root = spec.get_block_root_at_slot(state, slot) - current_epoch_start_slot = spec.get_epoch_start_slot(spec.get_current_epoch(state)) + current_epoch_start_slot = spec.epoch_start_slot(spec.get_current_epoch(state)) if slot < current_epoch_start_slot: epoch_boundary_root = spec.get_block_root(state, spec.get_previous_epoch(state)) elif slot == current_epoch_start_slot: @@ -54,8 +54,8 @@ def get_valid_attestation(spec, state, slot=None, signed=False): slot = state.slot epoch = spec.slot_to_epoch(slot) - epoch_start_shard = spec.get_epoch_start_shard(state, epoch) - committees_per_slot = spec.get_epoch_committee_count(state, epoch) // spec.SLOTS_PER_EPOCH + epoch_start_shard = spec.get_start_shard(state, epoch) + committees_per_slot = spec.get_committee_count(state, epoch) // spec.SLOTS_PER_EPOCH shard = (epoch_start_shard + committees_per_slot * (slot % spec.SLOTS_PER_EPOCH)) % spec.SHARD_COUNT attestation_data = build_attestation_data(spec, state, slot, shard) diff --git a/test_libs/pyspec/eth2spec/test/helpers/attester_slashings.py b/test_libs/pyspec/eth2spec/test/helpers/attester_slashings.py index 9c68b7bbe..20abcacfb 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/attester_slashings.py +++ b/test_libs/pyspec/eth2spec/test/helpers/attester_slashings.py @@ -13,6 +13,6 @@ def get_valid_attester_slashing(spec, state, signed_1=False, signed_2=False): sign_attestation(spec, state, attestation_2) return spec.AttesterSlashing( - attestation_1=spec.convert_to_indexed(state, attestation_1), - attestation_2=spec.convert_to_indexed(state, attestation_2), + attestation_1=spec.get_indexed_attestation(state, attestation_1), + attestation_2=spec.get_indexed_attestation(state, attestation_2), ) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py index 59d47f0ad..41d784c50 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py @@ -83,7 +83,7 @@ def test_single_crosslink_update_from_previous_epoch(spec, state): @with_all_phases @spec_state_test def test_double_late_crosslink(spec, state): - if spec.get_epoch_committee_count(state, spec.get_current_epoch(state)) < spec.SHARD_COUNT: + if spec.get_committee_count(state, spec.get_current_epoch(state)) < spec.SHARD_COUNT: print("warning: ignoring test, test-assumptions are incompatible with configuration") return diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index 1744d388c..f59da1fc5 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -10,8 +10,8 @@ def run_process_just_and_fin(spec, state): def get_shards_for_slot(spec, state, slot): epoch = spec.slot_to_epoch(slot) - epoch_start_shard = spec.get_epoch_start_shard(state, epoch) - committees_per_slot = spec.get_epoch_committee_count(state, epoch) // spec.SLOTS_PER_EPOCH + epoch_start_shard = spec.get_start_shard(state, epoch) + committees_per_slot = spec.get_committee_count(state, epoch) // spec.SLOTS_PER_EPOCH shard = (epoch_start_shard + committees_per_slot * (slot % spec.SLOTS_PER_EPOCH)) % spec.SHARD_COUNT return [shard + i for i in range(committees_per_slot)] @@ -33,8 +33,8 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support total_balance = spec.get_total_active_balance(state) remaining_balance = total_balance * 2 // 3 - epoch_start_slot = spec.get_epoch_start_slot(epoch) - for slot in range(epoch_start_slot, epoch_start_slot + spec.SLOTS_PER_EPOCH): + start_slot = spec.epoch_start_slot(epoch) + for slot in range(start_slot, start_slot + spec.SLOTS_PER_EPOCH): for shard in get_shards_for_slot(spec, state, slot): # Check if we already have had sufficient balance. (and undone if we don't want it). # If so, do not create more attestations. (we do not have empty pending attestations normally anyway) @@ -80,7 +80,7 @@ def get_checkpoints(spec, epoch): def put_checkpoints_in_block_roots(spec, state, checkpoints): for c in checkpoints: - state.block_roots[spec.get_epoch_start_slot(c.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c.root + state.block_roots[spec.epoch_start_slot(c.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c.root def finalize_on_234(spec, state, epoch, sufficient_support): diff --git a/test_libs/pyspec/eth2spec/test/test_finality.py b/test_libs/pyspec/eth2spec/test/test_finality.py index 2d86ef523..5ff0ed1ef 100644 --- a/test_libs/pyspec/eth2spec/test/test_finality.py +++ b/test_libs/pyspec/eth2spec/test/test_finality.py @@ -43,7 +43,7 @@ def next_epoch_with_attestations(spec, block = build_empty_block_for_next_slot(spec, post_state) if fill_cur_epoch and post_state.slot >= spec.MIN_ATTESTATION_INCLUSION_DELAY: slot_to_attest = post_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY + 1 - if slot_to_attest >= spec.get_epoch_start_slot(spec.get_current_epoch(post_state)): + if slot_to_attest >= spec.epoch_start_slot(spec.get_current_epoch(post_state)): cur_attestation = get_valid_attestation(spec, post_state, slot_to_attest) block.body.attestations.append(cur_attestation)