From d0acd81157adb01dfd0b5ebf417d8c43758769f1 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Sun, 30 Jun 2019 10:02:18 +0100 Subject: [PATCH 01/12] 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) From 369c457d76efecd92a6771fefb95792625995107 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Sun, 30 Jun 2019 10:11:23 +0100 Subject: [PATCH 02/12] WIP2 --- specs/core/0_beacon-chain.md | 144 +++++++++--------- specs/core/1_custody-game.md | 6 +- test_generators/shuffling/main.py | 2 +- .../pyspec/eth2spec/test/helpers/deposits.py | 2 +- 4 files changed, 80 insertions(+), 74 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index c82524eb5..698f026c9 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -49,17 +49,23 @@ - [Beacon state](#beacon-state) - [`BeaconState`](#beaconstate) - [Helper functions](#helper-functions) - - [Math and crypto](#math-and-crypto) + - [Math](#math) - [`int_to_bytes`](#int_to_bytes) - [`bytes_to_int`](#bytes_to_int) - [`integer_squareroot`](#integer_squareroot) - [`xor`](#xor) + - [Crypto](#crypto) - [`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) + - [Predicates](#predicates) + - [`is_active_validator`](#is_active_validator) + - [`is_slashable_validator`](#is_slashable_validator) + - [`is_valid_merkle_branch`](#is_valid_merkle_branch) + - [`is_slashable_attestation_data`](#is_slashable_attestation_data) - [Beacon state getters](#beacon-state-getters) - [`get_current_epoch`](#get_current_epoch) - [`get_previous_epoch`](#get_previous_epoch) @@ -71,8 +77,7 @@ - [`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) @@ -83,8 +88,8 @@ - [`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) + + - [`shuffle_index`](#shuffle_index) - [`compute_committee`](#compute_committee) - [`get_crosslink_committee`](#get_crosslink_committee) - [`get_attesting_indices`](#get_attesting_indices) @@ -92,8 +97,7 @@ - [`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) + - [`delayed_activation_exit_epoch`](#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) @@ -619,6 +623,59 @@ The `hash` function is SHA256. `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). +### Predicates + +#### `is_valid_merkle_branch` + +```python +def is_valid_merkle_branch(leaf: Hash, branch: Sequence[Hash], depth: int, index: int, root: Hash) -> bool: + """ + Check if ``leaf`` at ``index`` verifies against the Merkle ``root`` and ``branch``. + """ + value = leaf + for i in range(depth): + if index // (2**i) % 2: + value = hash(branch[i] + value) + else: + value = hash(value + branch[i]) + return value == root +``` + +#### `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) +``` + +#### `is_slashable_attestation_data` + +```python +def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationData) -> bool: + """ + Check if ``data_1`` and ``data_2`` are slashable according to Casper FFG rules. + """ + return ( + # Double vote + (data_1 != data_2 and data_1.target.epoch == data_2.target.epoch) or + # Surround vote + (data_1.source.epoch < data_2.source.epoch and data_2.target.epoch < data_1.target.epoch) + ) +``` + ### Beacon state getters #### `get_current_epoch` @@ -889,22 +946,6 @@ def get_attesting_indices(state: BeaconState, -#### `verify_merkle_branch` - -```python -def verify_merkle_branch(leaf: Hash, branch: Sequence[Hash], depth: int, index: int, root: Hash) -> bool: - """ - 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(branch[i] + value) - else: - value = hash(value + branch[i]) - return value == root -``` - #### `bls_domain` @@ -936,24 +977,14 @@ def epoch_start_slot(epoch: Epoch) -> Slot: return Slot(epoch * SLOTS_PER_EPOCH) ``` -#### `is_active_validator` +#### `delayed_activation_exit_epoch` ```python -def is_active_validator(validator: Validator, epoch: Epoch) -> bool: +def delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: """ - Check if ``validator`` is active. + Return the epoch at which an activation or exit triggered in ``epoch`` takes effect. """ - 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) + return Epoch(epoch + 1 + ACTIVATION_EXIT_DELAY) ``` #### `increase_balance` @@ -976,10 +1007,10 @@ def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> state.balances[index] = 0 if delta > state.balances[index] else state.balances[index] - delta ``` -#### `get_shuffled_index` +#### `shuffle_index` ```python -def get_shuffled_index(index: ValidatorIndex, index_count: int, seed: Hash) -> ValidatorIndex: +def shuffle_index(index: ValidatorIndex, index_count: int, seed: Hash) -> ValidatorIndex: """ Return the shuffled validator index corresponding to ``seed`` (and ``index_count``). """ @@ -1010,7 +1041,7 @@ def compute_committee(indices: Sequence[ValidatorIndex], seed: Hash, index: int, count: int) -> Sequence[ValidatorIndex]: start = (len(indices) * index) // count end = (len(indices) * (index + 1)) // count - return [indices[get_shuffled_index(ValidatorIndex(i), len(indices), seed)] for i in range(start, end)] + return [indices[shuffle_index(ValidatorIndex(i), len(indices), seed)] for i in range(start, end)] ``` #### `validate_indexed_attestation` @@ -1046,31 +1077,6 @@ def validate_indexed_attestation(state: BeaconState, indexed_attestation: Indexe ) ``` -#### `is_slashable_attestation_data` - -```python -def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationData) -> bool: - """ - Check if ``data_1`` and ``data_2`` are slashable according to Casper FFG rules. - """ - return ( - # Double vote - (data_1 != data_2 and data_1.target.epoch == data_2.target.epoch) or - # Surround vote - (data_1.source.epoch < data_2.source.epoch and data_2.target.epoch < data_1.target.epoch) - ) -``` - -#### `get_delayed_activation_exit_epoch` - -```python -def get_delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: - """ - Return the epoch at which an activation or exit triggered in ``epoch`` takes effect. - """ - return Epoch(epoch + 1 + ACTIVATION_EXIT_DELAY) -``` - ### Routines for updating validator status *Note*: All functions in this section mutate `state`. @@ -1089,7 +1095,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: # Compute exit queue epoch exit_epochs = [v.exit_epoch for v in state.validators if v.exit_epoch != FAR_FUTURE_EPOCH] - exit_queue_epoch = max(exit_epochs + [get_delayed_activation_exit_epoch(get_current_epoch(state))]) + exit_queue_epoch = max(exit_epochs + [delayed_activation_exit_epoch(get_current_epoch(state))]) exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch]) if exit_queue_churn >= get_churn_limit(state): exit_queue_epoch += Epoch(1) @@ -1477,13 +1483,13 @@ def process_registry_updates(state: BeaconState) -> None: activation_queue = sorted([ index for index, validator in enumerate(state.validators) if validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and - validator.activation_epoch >= get_delayed_activation_exit_epoch(state.finalized_checkpoint.epoch) + validator.activation_epoch >= delayed_activation_exit_epoch(state.finalized_checkpoint.epoch) ], key=lambda index: state.validators[index].activation_eligibility_epoch) # Dequeued validators for activation up to churn limit (without resetting activation epoch) for index in activation_queue[:get_churn_limit(state)]: validator = state.validators[index] if validator.activation_epoch == FAR_FUTURE_EPOCH: - validator.activation_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) + validator.activation_epoch = delayed_activation_exit_epoch(get_current_epoch(state)) ``` #### Slashings @@ -1703,7 +1709,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: Process an Eth1 deposit, registering a validator or increasing its balance. """ # Verify the Merkle branch - assert verify_merkle_branch( + assert is_valid_merkle_branch( leaf=hash_tree_root(deposit.data), branch=deposit.proof, depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # add 1 for the SSZ length mix-in diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index 1fb5b4d3e..e588e9fdf 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -599,7 +599,7 @@ def process_chunk_challenge_response(state: BeaconState, # Verify minimum delay assert get_current_epoch(state) >= challenge.inclusion_epoch + ACTIVATION_EXIT_DELAY # Verify the chunk matches the crosslink data root - assert verify_merkle_branch( + assert is_valid_merkle_branch( leaf=hash_tree_root(response.chunk), branch=response.data_branch, depth=challenge.depth, @@ -624,7 +624,7 @@ def process_bit_challenge_response(state: BeaconState, responder = state.validators[challenge.responder_index] assert not responder.slashed # Verify the chunk matches the crosslink data root - assert verify_merkle_branch( + assert is_valid_merkle_branch( leaf=hash_tree_root(response.chunk), branch=response.data_branch, depth=ceillog2(challenge.chunk_count), @@ -632,7 +632,7 @@ def process_bit_challenge_response(state: BeaconState, root=challenge.data_root, ) # Verify the chunk bit leaf matches the challenge data - assert verify_merkle_branch( + assert is_valid_merkle_branch( leaf=response.chunk_bits_leaf, branch=response.chunk_bits_branch, depth=ceillog2(challenge.chunk_count) >> 8, diff --git a/test_generators/shuffling/main.py b/test_generators/shuffling/main.py index 291aa2c47..e40dda520 100644 --- a/test_generators/shuffling/main.py +++ b/test_generators/shuffling/main.py @@ -10,7 +10,7 @@ from preset_loader import loader def shuffling_case(seed, count): yield 'seed', '0x' + seed.hex() yield 'count', count - yield 'shuffled', [spec.get_shuffled_index(i, count, seed) for i in range(count)] + yield 'shuffled', [spec.shuffle_index(i, count, seed) for i in range(count)] @to_tuple diff --git a/test_libs/pyspec/eth2spec/test/helpers/deposits.py b/test_libs/pyspec/eth2spec/test/helpers/deposits.py index b46363e62..4f099be9d 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/deposits.py +++ b/test_libs/pyspec/eth2spec/test/helpers/deposits.py @@ -49,7 +49,7 @@ def build_deposit(spec, tree = calc_merkle_tree_from_leaves(tuple([d.hash_tree_root() for d in deposit_data_list])) proof = list(get_merkle_proof(tree, item_index=index)) + [(index + 1).to_bytes(32, 'little')] leaf = deposit_data.hash_tree_root() - assert spec.verify_merkle_branch(leaf, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH + 1, index, root) + assert spec.is_valid_merkle_branch(leaf, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH + 1, index, root) deposit = spec.Deposit(proof=proof, data=deposit_data) return deposit, root, deposit_data_list From e8532ced79877847a40a46e597a5e64d3f292d1d Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Sun, 30 Jun 2019 10:56:14 +0100 Subject: [PATCH 03/12] WIP --- specs/core/0_beacon-chain.md | 427 ++++++++---------- specs/core/0_fork-choice.md | 8 +- specs/core/1_custody-game.md | 8 +- specs/validator/0_beacon-chain-validator.md | 6 +- .../eth2spec/test/helpers/attestations.py | 8 +- .../pyspec/eth2spec/test/helpers/block.py | 4 +- .../test_process_attester_slashing.py | 2 +- .../test_process_block_header.py | 2 +- .../block_processing/test_process_transfer.py | 2 +- ..._process_justification_and_finalization.py | 4 +- .../eth2spec/test/sanity/test_blocks.py | 2 +- .../pyspec/eth2spec/test/test_finality.py | 2 +- 12 files changed, 224 insertions(+), 251 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 698f026c9..1d4a486a1 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -50,10 +50,10 @@ - [`BeaconState`](#beaconstate) - [Helper functions](#helper-functions) - [Math](#math) - - [`int_to_bytes`](#int_to_bytes) - - [`bytes_to_int`](#bytes_to_int) - [`integer_squareroot`](#integer_squareroot) - [`xor`](#xor) + - [`int_to_bytes`](#int_to_bytes) + - [`bytes_to_int`](#bytes_to_int) - [Crypto](#crypto) - [`hash`](#hash) - [`hash_tree_root`](#hash_tree_root) @@ -64,42 +64,39 @@ - [Predicates](#predicates) - [`is_active_validator`](#is_active_validator) - [`is_slashable_validator`](#is_slashable_validator) - - [`is_valid_merkle_branch`](#is_valid_merkle_branch) - [`is_slashable_attestation_data`](#is_slashable_attestation_data) + - [`is_valid_merkle_branch`](#is_valid_merkle_branch) + - [Misc](#misc) + - [`shuffle_index`](#shuffle_index) + - [`compute_committee`](#compute_committee) + - [`validate_indexed_attestation`](#validate_indexed_attestation) + - [`slot_to_epoch`](#slot_to_epoch) + - [`epoch_first_slot`](#epoch_first_slot) + - [`delayed_activation_exit_epoch`](#delayed_activation_exit_epoch) + - [`bls_domain`](#bls_domain) - [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) - - - [`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_block_root_at_slot`](#get_block_root_at_slot) - [`get_randao_mix`](#get_randao_mix) - - [`get_compact_committees_root`](#get_compact_committees_root) + - [`get_active_validator_indices`](#get_active_validator_indices) + - [`get_churn_limit`](#get_churn_limit) + - [`get_committee_count`](#get_committee_count) - [`get_seed`](#get_seed) - - [`get_beacon_proposer_index`](#get_beacon_proposer_index) - - - [`shuffle_index`](#shuffle_index) - - [`compute_committee`](#compute_committee) - [`get_crosslink_committee`](#get_crosslink_committee) - - [`get_attesting_indices`](#get_attesting_indices) + - [`get_start_shard`](#get_start_shard) + - [`get_shard_delta`](#get_shard_delta) + - [`get_proposer_index`](#get_proposer_index) + - [`get_attestation_data_slot`](#get_attestation_data_slot) + - [`get_compact_committees_root`](#get_compact_committees_root) - [`get_total_balance`](#get_total_balance) - [`get_domain`](#get_domain) - [`get_indexed_attestation`](#get_indexed_attestation) - - [`validate_indexed_attestation`](#validate_indexed_attestation) - - [`delayed_activation_exit_epoch`](#delayed_activation_exit_epoch) - - [`get_churn_limit`](#get_churn_limit) - - [Routines for updating validator status](#routines-for-updating-validator-status) + - [`get_attesting_indices`](#get_attesting_indices) + - [State mutators](#state-mutators) + - [`increase_balance`](#increase_balance) + - [`decrease_balance`](#decrease_balance) - [`initiate_validator_exit`](#initiate_validator_exit) - [`slash_validator`](#slash_validator) - [Genesis](#genesis) @@ -201,6 +198,7 @@ The following values are (non-configurable) constants used throughout the specif | `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` | `2**16` (= 65,536) | | `MIN_GENESIS_TIME` | `1578009600` (Jan 3, 2020) | | `JUSTIFICATION_BITS_LENGTH` | `4` | +| `ENDIANNESS` | `'little'` | * For the safety of crosslinks, `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) @@ -559,18 +557,6 @@ class BeaconState(Container): #### `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 @@ -597,11 +583,29 @@ def xor(bytes1: Bytes32, bytes2: Bytes32) -> Bytes32: return Bytes32(a ^ b for a, b in zip(bytes1, bytes2)) ``` +```python +def int_to_bytes(integer: int, length: int) -> bytes: + """ + Return the ``length``-byte serialization of ``integer``. + """ + return integer.to_bytes(length, ENDIANNESS) +``` + +#### `bytes_to_int` + +```python +def bytes_to_int(data: bytes) -> int: + """ + Return the integer deserialization of ``data``. + """ + return int.from_bytes(data, ENDIANNESS) +``` + ### Crypto #### `hash` -The `hash` function is SHA256. +`def hash(data: bytes) -> Hash` is SHA256. #### `hash_tree_root` @@ -625,22 +629,6 @@ The `hash` function is SHA256. ### Predicates -#### `is_valid_merkle_branch` - -```python -def is_valid_merkle_branch(leaf: Hash, branch: Sequence[Hash], depth: int, index: int, root: Hash) -> bool: - """ - Check if ``leaf`` at ``index`` verifies against the Merkle ``root`` and ``branch``. - """ - value = leaf - for i in range(depth): - if index // (2**i) % 2: - value = hash(branch[i] + value) - else: - value = hash(value + branch[i]) - return value == root -``` - #### `is_active_validator` ```python @@ -676,6 +664,137 @@ def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationDa ) ``` +#### `is_valid_merkle_branch` + +```python +def is_valid_merkle_branch(leaf: Hash, branch: Sequence[Hash], depth: int, index: int, root: Hash) -> bool: + """ + Check if ``leaf`` at ``index`` verifies against the Merkle ``root`` and ``branch``. + """ + value = leaf + for i in range(depth): + if index // (2**i) % 2: + value = hash(branch[i] + value) + else: + value = hash(value + branch[i]) + return value == root +``` + +### Misc + +#### `shuffle_index` + +```python +def shuffle_index(index: ValidatorIndex, index_count: int, seed: Hash) -> ValidatorIndex: + """ + Return the shuffled validator index corresponding to ``seed`` (and ``index_count``). + """ + assert index < index_count + assert index_count <= 2**40 + + # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) + # See the 'generalized domain' algorithm on page 3 + for current_round in range(SHUFFLE_ROUND_COUNT): + pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count + flip = ValidatorIndex((pivot + index_count - index) % index_count) + position = max(index, flip) + source = hash( + seed + int_to_bytes(current_round, length=1) + + int_to_bytes(position // 256, length=4) + ) + byte = source[(position % 256) // 8] + bit = (byte >> (position % 8)) % 2 + index = flip if bit else index + + return ValidatorIndex(index) +``` + +#### `compute_committee` + +```python +def compute_committee(indices: Sequence[ValidatorIndex], + seed: Hash, index: int, count: int) -> Sequence[ValidatorIndex]: + """ + Return the committee corresponding to ``indices``, ``seed``, ``index`` and committee ``count``. + """ + start = (len(indices) * index) // count + end = (len(indices) * (index + 1)) // count + return [indices[shuffle_index(ValidatorIndex(i), len(indices), seed)] for i in range(start, end)] +``` + +#### `validate_indexed_attestation` + +```python +def validate_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> None: + """ + Verify validity of ``indexed_attestation``. + """ + bit_0_indices = indexed_attestation.custody_bit_0_indices + bit_1_indices = indexed_attestation.custody_bit_1_indices + + # Verify no index has custody bit equal to 1 [to be removed in phase 1] + assert len(bit_1_indices) == 0 + # Verify max number of indices + assert len(bit_0_indices) + len(bit_1_indices) <= MAX_VALIDATORS_PER_COMMITTEE + # Verify index sets are disjoint + assert len(set(bit_0_indices).intersection(bit_1_indices)) == 0 + # Verify indices are sorted + assert bit_0_indices == sorted(bit_0_indices) and bit_1_indices == sorted(bit_1_indices) + # Verify aggregate signature + assert bls_verify_multiple( + pubkeys=[ + bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_0_indices]), + bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_1_indices]), + ], + message_hashes=[ + hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)), + hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)), + ], + signature=indexed_attestation.signature, + domain=get_domain(state, DOMAIN_ATTESTATION, indexed_attestation.data.target.epoch), + ) +``` + +#### `slot_to_epoch` + +```python +def slot_to_epoch(slot: Slot) -> Epoch: + """ + Return the epoch number of ``slot``. + """ + return Epoch(slot // SLOTS_PER_EPOCH) +``` + +#### `epoch_first_slot` + +```python +def epoch_first_slot(epoch: Epoch) -> Slot: + """ + Return the first slot of ``epoch``. + """ + return Slot(epoch * SLOTS_PER_EPOCH) +``` + +#### `delayed_activation_exit_epoch` + +```python +def delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: + """ + Return the epoch at which an activation or exit triggered in ``epoch`` takes effect. + """ + return Epoch(epoch + 1 + ACTIVATION_EXIT_DELAY) +``` + +#### `bls_domain` + +```python +def bls_domain(domain_type: int, fork_version: bytes=b'\x00' * 4) -> int: + """ + Return the BLS domain for the ``domain_type`` and ``fork_version``. + """ + return bytes_to_int(int_to_bytes(domain_type, length=4) + fork_version) +``` + ### Beacon state getters #### `get_current_epoch` @@ -706,7 +825,7 @@ def get_block_root(state: BeaconState, epoch: Epoch) -> Hash: """ Return the block root at a recent ``epoch``. """ - return get_block_root_at_slot(state, epoch_start_slot(epoch)) + return get_block_root_at_slot(state, epoch_first_slot(epoch)) ``` #### `get_block_root_at_slot` @@ -824,10 +943,10 @@ def get_shard_delta(state: BeaconState, epoch: Epoch) -> Shard: return Shard(min(get_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH)) ``` -#### `get_beacon_proposer_index` +#### `get_proposer_index` ```python -def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: +def get_proposer_index(state: BeaconState) -> ValidatorIndex: """ Return the beacon proposer index at the current epoch. """ @@ -857,7 +976,7 @@ def get_attestation_data_slot(state: BeaconState, data: AttestationData) -> Slot """ 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)) + return Slot(epoch_first_slot(data.target.epoch) + offset // (committee_count // SLOTS_PER_EPOCH)) ``` #### `get_compact_committees_root` @@ -936,63 +1055,14 @@ def get_attesting_indices(state: BeaconState, return set(index for i, index in enumerate(committee) if bits[i]) ``` - - - - - - - - - - - -#### `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) -``` - -#### `delayed_activation_exit_epoch` - -```python -def delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: - """ - Return the epoch at which an activation or exit triggered in ``epoch`` takes effect. - """ - return Epoch(epoch + 1 + ACTIVATION_EXIT_DELAY) -``` +### State mutators #### `increase_balance` ```python def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: """ - Increase validator balance by ``delta``. + Increase the validator balance at index ``index`` by ``delta``. """ state.balances[index] += delta ``` @@ -1002,91 +1072,17 @@ def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> ```python def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: """ - Decrease validator balance by ``delta`` with underflow protection. + Decrease the validator balance at index ``index`` by ``delta``, with underflow protection. """ state.balances[index] = 0 if delta > state.balances[index] else state.balances[index] - delta ``` -#### `shuffle_index` - -```python -def shuffle_index(index: ValidatorIndex, index_count: int, seed: Hash) -> ValidatorIndex: - """ - Return the shuffled validator index corresponding to ``seed`` (and ``index_count``). - """ - assert index < index_count - assert index_count <= 2**40 - - # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) - # See the 'generalized domain' algorithm on page 3 - for current_round in range(SHUFFLE_ROUND_COUNT): - pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count - flip = ValidatorIndex((pivot + index_count - index) % index_count) - position = max(index, flip) - source = hash( - seed + int_to_bytes(current_round, length=1) + - int_to_bytes(position // 256, length=4) - ) - byte = source[(position % 256) // 8] - bit = (byte >> (position % 8)) % 2 - index = flip if bit else index - - return ValidatorIndex(index) -``` - -#### `compute_committee` - -```python -def compute_committee(indices: Sequence[ValidatorIndex], - seed: Hash, index: int, count: int) -> Sequence[ValidatorIndex]: - start = (len(indices) * index) // count - end = (len(indices) * (index + 1)) // count - return [indices[shuffle_index(ValidatorIndex(i), len(indices), seed)] for i in range(start, end)] -``` - -#### `validate_indexed_attestation` - -```python -def validate_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> None: - """ - Verify validity of ``indexed_attestation``. - """ - bit_0_indices = indexed_attestation.custody_bit_0_indices - bit_1_indices = indexed_attestation.custody_bit_1_indices - - # Verify no index has custody bit equal to 1 [to be removed in phase 1] - assert len(bit_1_indices) == 0 - # Verify max number of indices - assert len(bit_0_indices) + len(bit_1_indices) <= MAX_VALIDATORS_PER_COMMITTEE - # Verify index sets are disjoint - assert len(set(bit_0_indices).intersection(bit_1_indices)) == 0 - # Verify indices are sorted - assert bit_0_indices == sorted(bit_0_indices) and bit_1_indices == sorted(bit_1_indices) - # Verify aggregate signature - assert bls_verify_multiple( - pubkeys=[ - bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_0_indices]), - bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_1_indices]), - ], - message_hashes=[ - hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)), - hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)), - ], - signature=indexed_attestation.signature, - domain=get_domain(state, DOMAIN_ATTESTATION, indexed_attestation.data.target.epoch), - ) -``` - -### Routines for updating validator status - -*Note*: All functions in this section mutate `state`. - #### `initiate_validator_exit` ```python def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: """ - Initiate the exit of the validator of the given ``index``. + Initiate the exit of the validator with index ``index``. """ # Return if validator already initiated exit validator = state.validators[index] @@ -1123,7 +1119,7 @@ def slash_validator(state: BeaconState, decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT) # Apply proposer and whistleblower rewards - proposer_index = get_beacon_proposer_index(state) + proposer_index = get_proposer_index(state) if whistleblower_index is None: whistleblower_index = proposer_index whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT) @@ -1134,22 +1130,12 @@ def slash_validator(state: BeaconState, ## Genesis -### Genesis state - -Before the Ethereum 2.0 genesis has been triggered, and for every Ethereum 1.0 block, call `initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)` where: +Before the Ethereum 2.0 genesis has been triggered, and for every Ethereum 1.0 block, let `candidate_state = initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)` where: * `eth1_block_hash` is the hash of the Ethereum 1.0 block * `eth1_timestamp` is the Unix timestamp corresponding to `eth1_block_hash` * `deposits` is the sequence of all deposits, ordered chronologically, up to the block with hash `eth1_block_hash` -The genesis state `genesis_state` is the return value of calling `initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)` only if `is_valid_genesis_state(genesis_state) is True`. - -Implementations can choose to support different (more optimized) variations of the below initialization approach: - - Build the `genesis_state` from a stream of deposits by incrementally updating the `state.eth1_data.deposit_root`. - - Compute deposit proofs for the final `state.eth1_data.deposit_root`, and process as a pre-determined collection. - -*Note*: The two constants `MIN_GENESIS_TIME` and `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` have yet to be agreed upon by the community, and can be updated as necessary. - ```python def initialize_beacon_state_from_eth1(eth1_block_hash: Hash, eth1_timestamp: uint64, @@ -1175,22 +1161,27 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash, validator.activation_epoch = GENESIS_EPOCH # Populate compact_committees_roots - genesis_committee_root = get_compact_committees_root(state, GENESIS_EPOCH) + committee_root = get_compact_committees_root(state, GENESIS_EPOCH) for index in range(EPOCHS_PER_HISTORICAL_VECTOR): - state.compact_committees_roots[index] = genesis_committee_root + state.compact_committees_roots[index] = committee_root return state ``` +### Genesis state + +Let `genesis_state = candidate_state` whenever `is_valid_genesis_state(candidate_state) is True` for the first time. + ```python def is_valid_genesis_state(state: BeaconState) -> bool: if state.genesis_time < MIN_GENESIS_TIME: return False - elif len(get_active_validator_indices(state, GENESIS_EPOCH)) < MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: + if len(get_active_validator_indices(state, GENESIS_EPOCH)) < MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: return False - else: - return True + return True ``` +*Note*: The `is_valid_genesis_state` function (including `MIN_GENESIS_TIME` and `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT`) is a placeholder for testing. It has yet to be finalized by the community, and can be updated as necessary. + ### Genesis block Let `genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))`. @@ -1287,7 +1278,7 @@ def get_matching_head_attestations(state: BeaconState, epoch: Epoch) -> Sequence ```python def get_unslashed_attesting_indices(state: BeaconState, attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]: - output = set() # type: Set[ValidatorIndex] + output = set() # Type: Set[ValidatorIndex] for a in attestations: output = output.union(get_attesting_indices(state, a.data, a.aggregation_bits)) return set(filter(lambda index: not state.validators[index].slashed, list(output))) @@ -1566,7 +1557,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: body_root=hash_tree_root(block.body), ) # Verify proposer is not slashed - proposer = state.validators[get_beacon_proposer_index(state)] + proposer = state.validators[get_proposer_index(state)] assert not proposer.slashed # Verify proposer signature assert bls_verify(proposer.pubkey, signing_root(block), block.signature, get_domain(state, DOMAIN_BEACON_PROPOSER)) @@ -1578,7 +1569,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: def process_randao(state: BeaconState, body: BeaconBlockBody) -> None: epoch = get_current_epoch(state) # Verify RANDAO reveal - proposer = state.validators[get_beacon_proposer_index(state)] + proposer = state.validators[get_proposer_index(state)] assert bls_verify(proposer.pubkey, hash_tree_root(epoch), body.randao_reveal, get_domain(state, DOMAIN_RANDAO)) # Mix in RANDAO reveal mix = xor(get_randao_mix(state, epoch), hash(body.randao_reveal)) @@ -1620,9 +1611,6 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: ```python def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None: - """ - Process ``ProposerSlashing`` operation. - """ proposer = state.validators[proposer_slashing.proposer_index] # Verify that the epoch is the same assert slot_to_epoch(proposer_slashing.header_1.slot) == slot_to_epoch(proposer_slashing.header_2.slot) @@ -1642,9 +1630,6 @@ def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSla ```python def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None: - """ - Process ``AttesterSlashing`` operation. - """ attestation_1 = attester_slashing.attestation_1 attestation_2 = attester_slashing.attestation_2 assert is_slashable_attestation_data(attestation_1.data, attestation_2.data) @@ -1665,9 +1650,6 @@ def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSla ```python def process_attestation(state: BeaconState, attestation: Attestation) -> None: - """ - Process ``Attestation`` operation. - """ data = attestation.data assert data.crosslink.shard < SHARD_COUNT assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state)) @@ -1679,7 +1661,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: data=data, aggregation_bits=attestation.aggregation_bits, inclusion_delay=state.slot - attestation_slot, - proposer_index=get_beacon_proposer_index(state), + proposer_index=get_proposer_index(state), ) if data.target.epoch == get_current_epoch(state): @@ -1705,9 +1687,6 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: ```python def process_deposit(state: BeaconState, deposit: Deposit) -> None: - """ - Process an Eth1 deposit, registering a validator or increasing its balance. - """ # Verify the Merkle branch assert is_valid_merkle_branch( leaf=hash_tree_root(deposit.data), @@ -1754,9 +1733,6 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: ```python def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None: - """ - Process ``VoluntaryExit`` operation. - """ validator = state.validators[exit.validator_index] # Verify the validator is active assert is_active_validator(validator, get_current_epoch(state)) @@ -1777,9 +1753,6 @@ def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None: ```python def process_transfer(state: BeaconState, transfer: Transfer) -> None: - """ - Process ``Transfer`` operation. - """ # Verify the balance the covers amount and fee (with overflow protection) assert state.balances[transfer.sender] >= max(transfer.amount + transfer.fee, transfer.amount, transfer.fee) # A transfer is valid in only one slot @@ -1803,7 +1776,7 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: # Process the transfer decrease_balance(state, transfer.sender, transfer.amount + transfer.fee) increase_balance(state, transfer.recipient, transfer.amount) - increase_balance(state, get_beacon_proposer_index(state), transfer.fee) + increase_balance(state, get_proposer_index(state), transfer.fee) # Verify balances are not dust assert not (0 < state.balances[transfer.sender] < MIN_DEPOSIT_AMOUNT) assert not (0 < state.balances[transfer.recipient] < MIN_DEPOSIT_AMOUNT) diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index 64afb3f6b..b2ae89e9f 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 = epoch_start_slot(store.justified_checkpoint.epoch) + justified_slot = epoch_first_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 > epoch_start_slot(store.finalized_checkpoint.epoch) + assert block.slot > epoch_first_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 + epoch_start_slot(target.epoch) * SECONDS_PER_SLOT + assert store.time >= base_state.genesis_time + epoch_first_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, epoch_start_slot(target.epoch)) + process_slots(base_state, epoch_first_slot(target.epoch)) store.checkpoint_states[target] = base_state target_state = store.checkpoint_states[target] diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index e588e9fdf..993a45b0f 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -382,7 +382,7 @@ def process_custody_key_reveal(state: BeaconState, reveal: CustodyKeyReveal) -> revealer.next_custody_reveal_period += 1 # Reward Block Preposer - proposer_index = get_beacon_proposer_index(state) + proposer_index = get_proposer_index(state) increase_balance( state, proposer_index, @@ -452,7 +452,7 @@ def process_early_derived_secret_reveal(state: BeaconState, reveal: EarlyDerived ) # Apply penalty - proposer_index = get_beacon_proposer_index(state) + proposer_index = get_proposer_index(state) whistleblower_index = reveal.masker_index whistleblowing_reward = Gwei(penalty // WHISTLEBLOWER_REWARD_QUOTIENT) proposer_reward = Gwei(whistleblowing_reward // PROPOSER_REWARD_QUOTIENT) @@ -493,7 +493,7 @@ def process_chunk_challenge(state: BeaconState, challenge: CustodyChunkChallenge # Add new chunk challenge record new_record = CustodyChunkChallengeRecord( challenge_index=state.custody_challenge_index, - challenger_index=get_beacon_proposer_index(state), + challenger_index=get_proposer_index(state), responder_index=challenge.responder_index, inclusion_epoch=get_current_epoch(state), data_root=challenge.attestation.data.crosslink.data_root, @@ -610,7 +610,7 @@ def process_chunk_challenge_response(state: BeaconState, records = state.custody_chunk_challenge_records records[records.index(challenge)] = CustodyChunkChallengeRecord() # Reward the proposer - proposer_index = get_beacon_proposer_index(state) + proposer_index = get_proposer_index(state) increase_balance(state, proposer_index, Gwei(get_base_reward(state, proposer_index) // MINOR_REWARD_QUOTIENT)) ``` diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index f4a83b833..9921bf034 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -146,7 +146,7 @@ def get_committee_assignment( assert epoch <= next_epoch committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH - start_slot = epoch_start_slot(epoch) + start_slot = epoch_first_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_start_shard(state, epoch) + offset) % SHARD_COUNT @@ -162,7 +162,7 @@ A validator can use the following function to see if they are supposed to propos ```python def is_proposer(state: BeaconState, validator_index: ValidatorIndex) -> bool: - return get_beacon_proposer_index(state) == validator_index + return get_proposer_index(state) == validator_index ``` *Note*: To see if a validator is assigned to propose during the slot, the beacon state must be in the epoch in question. At the epoch boundaries, the validator must run an epoch transition into the epoch to successfully check the proposal assignment of the first slot. @@ -307,7 +307,7 @@ 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 `start_slot = epoch_start_slot(get_current_epoch(head_state))`. +* Let `start_slot = epoch_first_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/helpers/attestations.py b/test_libs/pyspec/eth2spec/test/helpers/attestations.py index 38635bafe..053446a2d 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/attestations.py +++ b/test_libs/pyspec/eth2spec/test/helpers/attestations.py @@ -15,15 +15,15 @@ def build_attestation_data(spec, state, slot, shard): else: block_root = spec.get_block_root_at_slot(state, slot) - current_epoch_start_slot = spec.epoch_start_slot(spec.get_current_epoch(state)) - if slot < current_epoch_start_slot: + current_epoch_first_slot = spec.epoch_first_slot(spec.get_current_epoch(state)) + if slot < current_epoch_first_slot: epoch_boundary_root = spec.get_block_root(state, spec.get_previous_epoch(state)) - elif slot == current_epoch_start_slot: + elif slot == current_epoch_first_slot: epoch_boundary_root = block_root else: epoch_boundary_root = spec.get_block_root(state, spec.get_current_epoch(state)) - if slot < current_epoch_start_slot: + if slot < current_epoch_first_slot: source_epoch = state.previous_justified_checkpoint.epoch source_root = state.previous_justified_checkpoint.root else: diff --git a/test_libs/pyspec/eth2spec/test/helpers/block.py b/test_libs/pyspec/eth2spec/test/helpers/block.py index 16249fe93..324c5bbe7 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/block.py +++ b/test_libs/pyspec/eth2spec/test/helpers/block.py @@ -12,7 +12,7 @@ def sign_block(spec, state, block, proposer_index=None): if proposer_index is None: if block.slot == state.slot: - proposer_index = spec.get_beacon_proposer_index(state) + proposer_index = spec.get_proposer_index(state) else: if spec.slot_to_epoch(state.slot) + 1 > spec.slot_to_epoch(block.slot): print("warning: block slot far away, and no proposer index manually given." @@ -20,7 +20,7 @@ def sign_block(spec, state, block, proposer_index=None): # use stub state to get proposer index of future slot stub_state = deepcopy(state) spec.process_slots(stub_state, block.slot) - proposer_index = spec.get_beacon_proposer_index(stub_state) + proposer_index = spec.get_proposer_index(stub_state) privkey = privkeys[proposer_index] diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py index 7a6030157..7f539e3b9 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py @@ -30,7 +30,7 @@ def run_attester_slashing_processing(spec, state, attester_slashing, valid=True) + attester_slashing.attestation_1.custody_bit_1_indices ) - proposer_index = spec.get_beacon_proposer_index(state) + proposer_index = spec.get_proposer_index(state) pre_proposer_balance = get_balance(state, proposer_index) pre_slashings = {slashed_index: get_balance(state, slashed_index) for slashed_index in slashed_indices} pre_withdrawalable_epochs = { diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py index a2306ef4d..c348e57e1 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py @@ -75,7 +75,7 @@ def test_proposer_slashed(spec, state): # use stub state to get proposer index of next slot stub_state = deepcopy(state) next_slot(spec, stub_state) - proposer_index = spec.get_beacon_proposer_index(stub_state) + proposer_index = spec.get_proposer_index(stub_state) # set proposer to slashed state.validators[proposer_index].slashed = True diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py index 6903f0666..5ea8cbacf 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py @@ -21,7 +21,7 @@ def run_transfer_processing(spec, state, transfer, valid=True): yield 'post', None return - proposer_index = spec.get_beacon_proposer_index(state) + proposer_index = spec.get_proposer_index(state) pre_transfer_sender_balance = state.balances[transfer.sender] pre_transfer_recipient_balance = state.balances[transfer.recipient] pre_transfer_proposer_balance = state.balances[proposer_index] 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 f59da1fc5..a308fe3d2 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 @@ -33,7 +33,7 @@ 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 - start_slot = spec.epoch_start_slot(epoch) + start_slot = spec.epoch_first_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). @@ -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.epoch_start_slot(c.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c.root + state.block_roots[spec.epoch_first_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/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index b2eb19244..086d141a5 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -214,7 +214,7 @@ def test_attester_slashing(spec, state): # lost whistleblower reward assert get_balance(state, validator_index) < get_balance(pre_state, validator_index) - proposer_index = spec.get_beacon_proposer_index(state) + proposer_index = spec.get_proposer_index(state) # gained whistleblower reward assert ( get_balance(state, proposer_index) > diff --git a/test_libs/pyspec/eth2spec/test/test_finality.py b/test_libs/pyspec/eth2spec/test/test_finality.py index 5ff0ed1ef..d572b05fb 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.epoch_start_slot(spec.get_current_epoch(post_state)): + if slot_to_attest >= spec.epoch_first_slot(spec.get_current_epoch(post_state)): cur_attestation = get_valid_attestation(spec, post_state, slot_to_attest) block.body.attestations.append(cur_attestation) From 58c864ddf4a88b0ba85e2f709917cca3ba5b834e Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Sun, 30 Jun 2019 11:01:54 +0100 Subject: [PATCH 04/12] Push draft --- specs/core/0_beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 1d4a486a1..971ea5480 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -94,7 +94,7 @@ - [`get_domain`](#get_domain) - [`get_indexed_attestation`](#get_indexed_attestation) - [`get_attesting_indices`](#get_attesting_indices) - - [State mutators](#state-mutators) + - [Beacon state mutators](#beacon-state-mutators) - [`increase_balance`](#increase_balance) - [`decrease_balance`](#decrease_balance) - [`initiate_validator_exit`](#initiate_validator_exit) @@ -1055,7 +1055,7 @@ def get_attesting_indices(state: BeaconState, return set(index for i, index in enumerate(committee) if bits[i]) ``` -### State mutators +### Beacon state mutators #### `increase_balance` @@ -1500,7 +1500,7 @@ def process_slashings(state: BeaconState) -> None: ```python def process_final_updates(state: BeaconState) -> None: current_epoch = get_current_epoch(state) - next_epoch = Shard(current_epoch + 1) + next_epoch = Epoch(current_epoch + 1) # Reset eth1 data votes if (state.slot + 1) % SLOTS_PER_ETH1_VOTING_PERIOD == 0: state.eth1_data_votes = [] @@ -1691,7 +1691,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: assert is_valid_merkle_branch( leaf=hash_tree_root(deposit.data), branch=deposit.proof, - depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # add 1 for the SSZ length mix-in + depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the `List` length mix-in index=state.eth1_deposit_index, root=state.eth1_data.deposit_root, ) From 94404a58562adf9693007aa7d7860b2d49093f50 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Sun, 30 Jun 2019 14:11:46 +0100 Subject: [PATCH 05/12] A few more cleanups --- specs/core/0_beacon-chain.md | 102 +++++++++--------- .../test_process_voluntary_exit.py | 2 +- .../test_process_registry_updates.py | 2 +- 3 files changed, 52 insertions(+), 54 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 971ea5480..99de62887 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -81,9 +81,9 @@ - [`get_block_root_at_slot`](#get_block_root_at_slot) - [`get_randao_mix`](#get_randao_mix) - [`get_active_validator_indices`](#get_active_validator_indices) - - [`get_churn_limit`](#get_churn_limit) - - [`get_committee_count`](#get_committee_count) + - [`get_validator_churn_limit`](#get_validator_churn_limit) - [`get_seed`](#get_seed) + - [`get_committee_count`](#get_committee_count) - [`get_crosslink_committee`](#get_crosslink_committee) - [`get_start_shard`](#get_start_shard) - [`get_shard_delta`](#get_shard_delta) @@ -713,9 +713,11 @@ def shuffle_index(index: ValidatorIndex, index_count: int, seed: Hash) -> Valida ```python def compute_committee(indices: Sequence[ValidatorIndex], - seed: Hash, index: int, count: int) -> Sequence[ValidatorIndex]: + seed: Hash, + index: int, + count: int) -> Sequence[ValidatorIndex]: """ - Return the committee corresponding to ``indices``, ``seed``, ``index`` and committee ``count``. + Return the committee corresponding to ``indices``, ``seed``, ``index``, and committee ``count``. """ start = (len(indices) * index) // count end = (len(indices) * (index + 1)) // count @@ -780,7 +782,7 @@ def epoch_first_slot(epoch: Epoch) -> Slot: ```python def delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: """ - Return the epoch at which an activation or exit triggered in ``epoch`` takes effect. + Return the epoch where validator activations and exits initiated in ``epoch`` take effect. """ return Epoch(epoch + 1 + ACTIVATION_EXIT_DELAY) ``` @@ -812,7 +814,7 @@ def get_current_epoch(state: BeaconState) -> Epoch: ```python def get_previous_epoch(state: BeaconState) -> Epoch: """` - Return the previous epoch (current epoch if at ``GENESIS_EPOCH``). + Return the previous epoch (unless the current epoch is ``GENESIS_EPOCH``). """ current_epoch = get_current_epoch(state) return GENESIS_EPOCH if current_epoch == GENESIS_EPOCH else Epoch(current_epoch - 1) @@ -859,10 +861,10 @@ def get_active_validator_indices(state: BeaconState, epoch: Epoch) -> Sequence[V return [ValidatorIndex(i) for i, v in enumerate(state.validators) if is_active_validator(v, epoch)] ``` -#### `get_churn_limit` +#### `get_validator_churn_limit` ```python -def get_churn_limit(state: BeaconState) -> int: +def get_validator_churn_limit(state: BeaconState) -> int: """ Return the validator churn limit for the current epoch. """ @@ -870,24 +872,6 @@ def get_churn_limit(state: BeaconState) -> int: return max(MIN_PER_EPOCH_CHURN_LIMIT, len(active_validator_indices) // CHURN_LIMIT_QUOTIENT) ``` - -#### `get_committee_count` - -```python -def get_committee_count(state: BeaconState, epoch: Epoch) -> int: - """ - Return the number of committees at ``epoch``. - """ - active_validator_indices = get_active_validator_indices(state, epoch) - return max( - 1, - min( - SHARD_COUNT // SLOTS_PER_EPOCH, - len(active_validator_indices) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE, - ) - ) * SLOTS_PER_EPOCH -``` - #### `get_seed` ```python @@ -895,11 +879,25 @@ def get_seed(state: BeaconState, epoch: Epoch) -> Hash: """ Return the seed at ``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) - ) + randao_mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD)) # Avoid underflow + active_indices_root = hash_tree_root(List[ValidatorIndex, VALIDATOR_REGISTRY_LIMIT](get_active_validator_indices(state, epoch))) + return hash(randao_mix + active_indices_root + int_to_bytes(epoch, length=32)) +``` + +#### `get_committee_count` + +```python +def get_committee_count(state: BeaconState, epoch: Epoch) -> int: + """ + Return the number of committees at ``epoch``. + """ + return max( + 1, + min( + SHARD_COUNT // SLOTS_PER_EPOCH, + len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE, + ) + ) * SLOTS_PER_EPOCH ``` #### `get_crosslink_committee` @@ -1010,6 +1008,16 @@ def get_total_balance(state: BeaconState, indices: Set[ValidatorIndex]) -> Gwei: return Gwei(max(sum([state.validators[index].effective_balance for index in indices]), 1)) ``` +#### `get_total_active_balance` + +```python +def get_total_active_balance(state: BeaconState) -> Gwei: + """ + Return the combined effective balance of the active validators. + """ + return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state)))) +``` + #### `get_domain` ```python @@ -1093,7 +1101,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: exit_epochs = [v.exit_epoch for v in state.validators if v.exit_epoch != FAR_FUTURE_EPOCH] exit_queue_epoch = max(exit_epochs + [delayed_activation_exit_epoch(get_current_epoch(state))]) exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch]) - if exit_queue_churn >= get_churn_limit(state): + if exit_queue_churn >= get_validator_churn_limit(state): exit_queue_epoch += Epoch(1) # Set validator exit epoch and withdrawable epoch @@ -1219,11 +1227,9 @@ def process_slot(state: BeaconState) -> None: # Cache state root previous_state_root = hash_tree_root(state) state.state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_state_root - # Cache latest block header state root if state.latest_block_header.state_root == ZERO_HASH: state.latest_block_header.state_root = previous_state_root - # Cache block root previous_block_root = signing_root(state.latest_block_header) state.block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root @@ -1248,11 +1254,6 @@ def process_epoch(state: BeaconState) -> None: #### Helper functions -```python -def get_total_active_balance(state: BeaconState) -> Gwei: - return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state)))) -``` - ```python def get_matching_source_attestations(state: BeaconState, epoch: Epoch) -> Sequence[PendingAttestation]: assert epoch in (get_previous_epoch(state), get_current_epoch(state)) @@ -1278,7 +1279,7 @@ def get_matching_head_attestations(state: BeaconState, epoch: Epoch) -> Sequence ```python def get_unslashed_attesting_indices(state: BeaconState, attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]: - output = set() # Type: Set[ValidatorIndex] + output = set() # type: Set[ValidatorIndex] for a in attestations: output = output.union(get_attesting_indices(state, a.data, a.aggregation_bits)) return set(filter(lambda index: not state.validators[index].slashed, list(output))) @@ -1477,7 +1478,7 @@ def process_registry_updates(state: BeaconState) -> None: validator.activation_epoch >= delayed_activation_exit_epoch(state.finalized_checkpoint.epoch) ], key=lambda index: state.validators[index].activation_eligibility_epoch) # Dequeued validators for activation up to churn limit (without resetting activation epoch) - for index in activation_queue[:get_churn_limit(state)]: + for index in activation_queue[:get_validator_churn_limit(state)]: validator = state.validators[index] if validator.activation_epoch == FAR_FUTURE_EPOCH: validator.activation_epoch = delayed_activation_exit_epoch(get_current_epoch(state)) @@ -1703,13 +1704,10 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: amount = deposit.data.amount validator_pubkeys = [v.pubkey for v in state.validators] if pubkey not in validator_pubkeys: - # Verify the deposit signature (proof of possession). - # Invalid signatures are allowed by the deposit contract, - # and hence included on-chain, but must not be processed. + # Verify the deposit signature (proof of possession) for new validators. + # Note: The deposit contract does not check signatures. # Note: Deposits are valid across forks, hence the deposit domain is retrieved directly from `bls_domain` - if not bls_verify( - pubkey, signing_root(deposit.data), deposit.data.signature, bls_domain(DOMAIN_DEPOSIT) - ): + if not bls_verify(pubkey, signing_root(deposit.data), deposit.data.signature, bls_domain(DOMAIN_DEPOSIT)): return # Add validator and balance entries @@ -1757,14 +1755,14 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: assert state.balances[transfer.sender] >= max(transfer.amount + transfer.fee, transfer.amount, transfer.fee) # A transfer is valid in only one slot assert state.slot == transfer.slot - # Sender must satisfy at least one of the following conditions in the parenthesis: + # Sender must satisfy at least one of the following: assert ( - # * Has not been activated + # 1) Never have been eligible for activation state.validators[transfer.sender].activation_eligibility_epoch == FAR_FUTURE_EPOCH or - # * Is withdrawable + # 2) Be withdrawable get_current_epoch(state) >= state.validators[transfer.sender].withdrawable_epoch or - # * Balance after transfer is more than the effective balance threshold - transfer.amount + transfer.fee + MAX_EFFECTIVE_BALANCE <= state.balances[transfer.sender] + # 3) Have a balance of at least MAX_EFFECTIVE_BALANCE after the transfer + state.balances[transfer.sender] >= transfer.amount + transfer.fee + MAX_EFFECTIVE_BALANCE ) # Verify that the pubkey is valid assert ( diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py index 33cacc4e2..6c9298ecc 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py @@ -71,7 +71,7 @@ def test_success_exit_queue(spec, state): current_epoch = spec.get_current_epoch(state) # exit `MAX_EXITS_PER_EPOCH` - initial_indices = spec.get_active_validator_indices(state, current_epoch)[:spec.get_churn_limit(state)] + initial_indices = spec.get_active_validator_indices(state, current_epoch)[:spec.get_validator_churn_limit(state)] # Prepare a bunch of exits, based on the current state exit_queue = [] diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py index 19500d4ab..ab6a74a70 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py @@ -45,7 +45,7 @@ def test_activation_queue_sorting(spec, state): state.validators[mock_activations - 1].activation_eligibility_epoch = epoch # make sure we are hitting the churn - churn_limit = spec.get_churn_limit(state) + churn_limit = spec.get_validator_churn_limit(state) assert mock_activations > churn_limit yield from run_process_registry_updates(spec, state) From e873bb6e591b9b98a599fa304c502859ada2063a Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Sun, 30 Jun 2019 14:32:50 +0100 Subject: [PATCH 06/12] ready for review --- specs/core/0_beacon-chain.md | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 99de62887..96d827bbf 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -698,10 +698,7 @@ def shuffle_index(index: ValidatorIndex, index_count: int, seed: Hash) -> Valida pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count flip = ValidatorIndex((pivot + index_count - index) % index_count) position = max(index, flip) - source = hash( - seed + int_to_bytes(current_round, length=1) + - int_to_bytes(position // 256, length=4) - ) + source = hash(seed + int_to_bytes(current_round, length=1) + int_to_bytes(position // 256, length=4)) byte = source[(position % 256) // 8] bit = (byte >> (position % 8)) % 2 index = flip if bit else index @@ -879,9 +876,10 @@ def get_seed(state: BeaconState, epoch: Epoch) -> Hash: """ Return the seed at ``epoch``. """ - randao_mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD)) # Avoid underflow - active_indices_root = hash_tree_root(List[ValidatorIndex, VALIDATOR_REGISTRY_LIMIT](get_active_validator_indices(state, epoch))) - return hash(randao_mix + active_indices_root + int_to_bytes(epoch, length=32)) + mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD)) # Avoid underflow + active_indices = get_active_validator_indices(state, epoch) + active_indices_root = hash_tree_root(List[ValidatorIndex, VALIDATOR_REGISTRY_LIMIT](active_indices)) + return hash(mix + active_indices_root + int_to_bytes(epoch, length=32)) ``` #### `get_committee_count` @@ -891,13 +889,10 @@ def get_committee_count(state: BeaconState, epoch: Epoch) -> int: """ Return the number of committees at ``epoch``. """ - return max( - 1, - min( - SHARD_COUNT // SLOTS_PER_EPOCH, - len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE, - ) - ) * SLOTS_PER_EPOCH + return max(1, min( + SHARD_COUNT // SLOTS_PER_EPOCH, + len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE, + )) * SLOTS_PER_EPOCH ``` #### `get_crosslink_committee` @@ -1157,9 +1152,8 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash, # Process deposits leaves = list(map(lambda deposit: deposit.data, deposits)) for index, deposit in enumerate(deposits): - state.eth1_data.deposit_root = hash_tree_root( - List[DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH](*leaves[:index + 1]) - ) + deposit_data_list = List[DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH](*leaves[:index + 1]) + state.eth1_data.deposit_root = hash_tree_root(deposit_data_list) process_deposit(state, deposit) # Process activations @@ -1522,10 +1516,7 @@ def process_final_updates(state: BeaconState) -> None: state.randao_mixes[next_epoch % EPOCHS_PER_HISTORICAL_VECTOR] = get_randao_mix(state, current_epoch) # Set historical root accumulator if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0: - historical_batch = HistoricalBatch( - block_roots=state.block_roots, - state_roots=state.state_roots, - ) + historical_batch = HistoricalBatch(state.block_roots, state.state_roots) state.historical_roots.append(hash_tree_root(historical_batch)) # Rotate current/previous epoch attestations state.previous_epoch_attestations = state.current_epoch_attestations @@ -1554,7 +1545,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: state.latest_block_header = BeaconBlockHeader( slot=block.slot, parent_root=block.parent_root, - state_root=ZERO_HASH, # Overwritten in next `process_slot` call + state_root=ZERO_HASH, # Overwritten in the next `process_slot` call body_root=hash_tree_root(block.body), ) # Verify proposer is not slashed From 423d6dcb29af2fc2025363445e242dba0aff0362 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Sun, 30 Jun 2019 14:45:36 +0100 Subject: [PATCH 07/12] Fix bug --- specs/core/0_beacon-chain.md | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 96d827bbf..a5aa11367 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -9,7 +9,6 @@ - [Table of contents](#table-of-contents) - [Introduction](#introduction) - [Notation](#notation) - - [Terminology](#terminology) - [Custom types](#custom-types) - [Constants](#constants) - [Configuration](#configuration) @@ -129,29 +128,12 @@ This document represents the specification for Phase 0 of Ethereum 2.0 -- The Beacon Chain. -At the core of Ethereum 2.0 is a system chain called the "beacon chain". The beacon chain stores and manages the registry of [validators](#dfn-validator). In the initial deployment phases of Ethereum 2.0, the only mechanism to become a [validator](#dfn-validator) is to make a one-way ETH transaction to a deposit contract on Ethereum 1.0. Activation as a [validator](#dfn-validator) happens when Ethereum 1.0 deposit receipts are processed by the beacon chain, the activation balance is reached, and a queuing process is completed. Exit is either voluntary or done forcibly as a penalty for misbehavior. +At the core of Ethereum 2.0 is a system chain called the "beacon chain". The beacon chain stores and manages the registry of validators. In the initial deployment phases of Ethereum 2.0, the only mechanism to become a validator is to make a one-way ETH transaction to a deposit contract on Ethereum 1.0. Activation as a validator happens when Ethereum 1.0 deposit receipts are processed by the beacon chain, the activation balance is reached, and a queuing process is completed. Exit is either voluntary or done forcibly as a penalty for misbehavior. The primary source of load on the beacon chain is "attestations". Attestations are simultaneously availability votes for a shard block and proof-of-stake votes for a beacon block. A sufficient number of attestations for the same shard block create a "crosslink", confirming the shard segment up to that shard block into the beacon chain. Crosslinks also serve as infrastructure for asynchronous cross-shard communication. ## Notation -Code snippets appearing in `this style` are to be interpreted as Python code. - -## Terminology - -* **Validator**—a registered participant in the beacon chain. You can become one by sending ether into the Ethereum 1.0 deposit contract. -* **Active validator**—an active participant in the Ethereum 2.0 consensus invited to, among other things, propose and attest to blocks and vote for crosslinks. -* **Committee**—a (pseudo-) randomly sampled subset of [active validators](#dfn-active-validator). When a committee is referred to collectively, as in "this committee attests to X", this is assumed to mean "some subset of that committee that contains enough [validators](#dfn-validator) that the protocol recognizes it as representing the committee". -* **Proposer**—the [validator](#dfn-validator) that creates a beacon chain block. -* **Attester**—a [validator](#dfn-validator) that is part of a committee that needs to sign off on a beacon chain block while simultaneously creating a link (crosslink) to a recent shard block on a particular shard chain. -* **Beacon chain**—the central proof-of-stake chain that is the base of the sharding system. -* **Shard chain**—one of the chains on which user transactions take place and account data is stored. -* **Block root**—a 32-byte Merkle root of a beacon chain block or shard chain block. Previously called "block hash". -* **Crosslink**—a set of signatures from a committee attesting to a block in a shard chain that can be included into the beacon chain. Crosslinks are the main means by which the beacon chain "learns about" the updated state of shard chains. -* **Slot**—a period during which one proposer has the ability to create a beacon chain block and some attesters have the ability to make attestations. -* **Epoch**—an aligned span of slots during which all [validators](#dfn-validator) get exactly one chance to make an attestation. -* **Finalized**, **justified**—see the [Casper FFG paper](https://arxiv.org/abs/1710.09437). -* **Withdrawal period**—the number of slots between a [validator](#dfn-validator) exit and the [validator](#dfn-validator) balance being withdrawable. -* **Genesis time**—the Unix time of the genesis beacon chain block at slot 0. +Code snippets appearing in `this style` are to be interpreted as Python 3 code. ## Custom types @@ -255,7 +237,7 @@ The following values are (non-configurable) constants used throughout the specif | `INACTIVITY_PENALTY_QUOTIENT` | `2**25` (= 33,554,432) | | `MIN_SLASHING_PENALTY_QUOTIENT` | `2**5` (= 32) | -* The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12 epochs` (about 18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating [validators](#dfn-validator) to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline [validators](#dfn-validator) after `n` epochs is about `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)`; so after `INVERSE_SQRT_E_DROP_TIME` epochs, it is roughly `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`. +* The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12 epochs` (about 18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating validators to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline validators after `n` epochs is about `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)`; so after `INVERSE_SQRT_E_DROP_TIME` epochs, it is roughly `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`. ### Max operations per block @@ -1516,7 +1498,7 @@ def process_final_updates(state: BeaconState) -> None: state.randao_mixes[next_epoch % EPOCHS_PER_HISTORICAL_VECTOR] = get_randao_mix(state, current_epoch) # Set historical root accumulator if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0: - historical_batch = HistoricalBatch(state.block_roots, state.state_roots) + historical_batch = HistoricalBatch(block_roots=state.block_roots, state_roots=state.state_roots) state.historical_roots.append(hash_tree_root(historical_batch)) # Rotate current/previous epoch attestations state.previous_epoch_attestations = state.current_epoch_attestations From 0be2b8e6205f6f0312bb2dcddb7eb74ce57c2c1b Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Sun, 30 Jun 2019 16:10:22 +0100 Subject: [PATCH 08/12] Cleanup --- scripts/build_spec.py | 4 ++-- specs/core/0_beacon-chain.md | 17 +++++++---------- specs/core/1_custody-game.md | 2 +- specs/core/1_shard-data-chains.md | 6 +++--- .../eth2spec/test/helpers/attestations.py | 2 +- test_libs/pyspec/eth2spec/test/helpers/block.py | 2 +- .../pyspec/eth2spec/test/helpers/genesis.py | 2 +- .../block_processing/test_process_deposit.py | 2 +- .../block_processing/test_process_transfer.py | 2 +- .../pyspec/eth2spec/test/sanity/test_blocks.py | 4 ++-- 10 files changed, 20 insertions(+), 23 deletions(-) diff --git a/scripts/build_spec.py b/scripts/build_spec.py index 1f5fe1ee6..fe66a72d5 100644 --- a/scripts/build_spec.py +++ b/scripts/build_spec.py @@ -12,7 +12,7 @@ from typing import ( PHASE0_IMPORTS = '''from typing import ( - Any, Callable, Dict, Set, Sequence, Tuple, + Any, Dict, Set, Sequence, Tuple, ) from dataclasses import ( @@ -37,7 +37,7 @@ from eth2spec.utils.bls import ( from eth2spec.utils.hash_function import hash ''' PHASE1_IMPORTS = '''from typing import ( - Any, Callable, Dict, Optional, Set, Sequence, MutableSequence, Tuple, + Any, Dict, Optional, Set, Sequence, MutableSequence, Tuple, ) from dataclasses import ( diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a5aa11367..cd943187f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -147,7 +147,7 @@ We define the following Python custom types for type hinting and readability: | `ValidatorIndex` | `uint64` | a validator registry index | | `Gwei` | `uint64` | an amount in Gwei | | `Version` | `Bytes4` | a fork version number | -| `Hash` | `Bytes32` | a hashed result | +| `Hash` | `Bytes32` | a hash | | `BLSPubkey` | `Bytes48` | a BLS12-381 public key | | `BLSSignature` | `Bytes96` | a BLS12-381 signature | @@ -158,7 +158,6 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | | - | - | | `FAR_FUTURE_EPOCH` | `Epoch(2**64 - 1)` | -| `ZERO_HASH` | `Hash(b'\x00' * 32)` | | `BASE_REWARDS_PER_EPOCH` | `5` | | `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) | | `SECONDS_PER_DAY` | `86400` | @@ -542,11 +541,10 @@ class BeaconState(Container): #### `integer_squareroot` ```python -def integer_squareroot(n: int) -> int: +def integer_squareroot(n: uint64) -> uint64: """ Return the largest integer ``x`` such that ``x**2 <= n``. """ - assert n >= 0 x = n y = (x + 1) // 2 while y < x: @@ -1204,7 +1202,7 @@ def process_slot(state: BeaconState) -> None: previous_state_root = hash_tree_root(state) state.state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_state_root # Cache latest block header state root - if state.latest_block_header.state_root == ZERO_HASH: + if state.latest_block_header.state_root == Hash(): state.latest_block_header.state_root = previous_state_root # Cache block root previous_block_root = signing_root(state.latest_block_header) @@ -1527,7 +1525,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: state.latest_block_header = BeaconBlockHeader( slot=block.slot, parent_root=block.parent_root, - state_root=ZERO_HASH, # Overwritten in the next `process_slot` call + state_root=Hash(), # Overwritten in the next `process_slot` call body_root=hash_tree_root(block.body), ) # Verify proposer is not slashed @@ -1568,15 +1566,14 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: # Verify that there are no duplicate transfers assert len(body.transfers) == len(set(body.transfers)) - all_operations = ( + for operations, function in ( (body.proposer_slashings, process_proposer_slashing), (body.attester_slashings, process_attester_slashing), (body.attestations, process_attestation), (body.deposits, process_deposit), (body.voluntary_exits, process_voluntary_exit), (body.transfers, process_transfer), - ) # type: Sequence[Tuple[List, Callable]] - for operations, function in all_operations: + ): for operation in operations: function(state, operation) ``` @@ -1651,7 +1648,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: assert data.crosslink.parent_root == hash_tree_root(parent_crosslink) assert data.crosslink.start_epoch == parent_crosslink.end_epoch assert data.crosslink.end_epoch == min(data.target.epoch, parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK) - assert data.crosslink.data_root == ZERO_HASH # [to be removed in phase 1] + assert data.crosslink.data_root == Hash() # [to be removed in phase 1] # Check signature validate_indexed_attestation(state, get_indexed_attestation(state, attestation)) diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index 993a45b0f..bf221ee3e 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -595,7 +595,7 @@ def process_chunk_challenge_response(state: BeaconState, # Verify chunk index assert response.chunk_index == challenge.chunk_index # Verify bit challenge data is null - assert response.chunk_bits_branch == [] and response.chunk_bits_leaf == ZERO_HASH + assert response.chunk_bits_branch == [] and response.chunk_bits_leaf == Hash() # Verify minimum delay assert get_current_epoch(state) >= challenge.inclusion_epoch + ACTIVATION_EXIT_DELAY # Verify the chunk matches the crosslink data root diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index eb20abe7f..82843b7a6 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -309,11 +309,11 @@ def is_valid_shard_block(beacon_blocks: Sequence[BeaconBlock], assert beacon_block.slot <= candidate.slot # Check state root - assert candidate.state_root == ZERO_HASH # [to be removed in phase 2] + assert candidate.state_root == Hash() # [to be removed in phase 2] # Check parent block if candidate.slot == PHASE_1_FORK_SLOT: - assert candidate.parent_root == ZERO_HASH + assert candidate.parent_root == Hash() else: parent_block = next( (block for block in valid_shard_blocks if signing_root(block) == candidate.parent_root), @@ -395,7 +395,7 @@ def is_valid_beacon_attestation(shard: Shard, # Check previous attestation if candidate.data.previous_crosslink.epoch <= PHASE_1_FORK_EPOCH: - assert candidate.data.previous_crosslink.data_root == ZERO_HASH + assert candidate.data.previous_crosslink.data_root == Hash() else: previous_attestation = next( (attestation for attestation in valid_attestations if diff --git a/test_libs/pyspec/eth2spec/test/helpers/attestations.py b/test_libs/pyspec/eth2spec/test/helpers/attestations.py index 053446a2d..c47245bb9 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/attestations.py +++ b/test_libs/pyspec/eth2spec/test/helpers/attestations.py @@ -43,7 +43,7 @@ def build_attestation_data(spec, state, slot, shard): shard=shard, start_epoch=parent_crosslink.end_epoch, end_epoch=min(spec.slot_to_epoch(slot), parent_crosslink.end_epoch + spec.MAX_EPOCHS_PER_CROSSLINK), - data_root=spec.ZERO_HASH, + data_root=spec.Hash(), parent_root=hash_tree_root(parent_crosslink), ), ) diff --git a/test_libs/pyspec/eth2spec/test/helpers/block.py b/test_libs/pyspec/eth2spec/test/helpers/block.py index 324c5bbe7..befeec7d3 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/block.py +++ b/test_libs/pyspec/eth2spec/test/helpers/block.py @@ -59,7 +59,7 @@ def build_empty_block(spec, state, slot=None, signed=False): empty_block.slot = slot empty_block.body.eth1_data.deposit_count = state.eth1_deposit_index previous_block_header = deepcopy(state.latest_block_header) - if previous_block_header.state_root == spec.ZERO_HASH: + if previous_block_header.state_root == spec.Hash(): previous_block_header.state_root = state.hash_tree_root() empty_block.parent_root = signing_root(previous_block_header) diff --git a/test_libs/pyspec/eth2spec/test/helpers/genesis.py b/test_libs/pyspec/eth2spec/test/helpers/genesis.py index 7ffacd7d0..a5a816977 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/genesis.py +++ b/test_libs/pyspec/eth2spec/test/helpers/genesis.py @@ -27,7 +27,7 @@ def create_genesis_state(spec, num_validators): eth1_data=spec.Eth1Data( deposit_root=deposit_root, deposit_count=num_validators, - block_hash=spec.ZERO_HASH, + block_hash=spec.Hash(), ), latest_block_header=spec.BeaconBlockHeader(body_root=spec.hash_tree_root(spec.BeaconBlockBody())), ) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py index 685eb6ebb..54a284a6e 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py @@ -195,7 +195,7 @@ def test_bad_merkle_proof(spec, state): deposit = prepare_state_and_deposit(spec, state, validator_index, amount) # mess up merkle branch - deposit.proof[5] = spec.ZERO_HASH + deposit.proof[5] = spec.Hash() sign_deposit_data(spec, deposit.data, privkeys[validator_index], state=state) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py index 5ea8cbacf..436b36e5b 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py @@ -360,7 +360,7 @@ def test_non_existent_recipient(spec, state): @spec_state_test def test_invalid_pubkey(spec, state): transfer = get_valid_transfer(spec, state, signed=True) - state.validators[transfer.sender].withdrawal_credentials = spec.ZERO_HASH + state.validators[transfer.sender].withdrawal_credentials = spec.Hash() # un-activate so validator can transfer state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index 086d141a5..fa7da930d 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -63,7 +63,7 @@ def test_empty_block_transition(spec, state): assert len(state.eth1_data_votes) == pre_eth1_votes + 1 assert spec.get_block_root_at_slot(state, pre_slot) == block.parent_root - assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.ZERO_HASH + assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.Hash() @with_all_phases @@ -98,7 +98,7 @@ def test_skipped_slots(spec, state): yield 'post', state assert state.slot == block.slot - assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.ZERO_HASH + assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.Hash() for slot in range(pre_slot, state.slot): assert spec.get_block_root_at_slot(state, slot) == block.parent_root From 6d5e4da3e0d57afdf1c5b9a09250a1fe76f53b11 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 30 Jun 2019 12:42:24 -0500 Subject: [PATCH 09/12] PR feedback --- specs/core/0_beacon-chain.md | 49 ++++++++++--------- specs/core/0_fork-choice.md | 8 +-- specs/core/1_custody-game.md | 8 +-- specs/validator/0_beacon-chain-validator.md | 6 +-- .../eth2spec/test/helpers/attestations.py | 8 +-- .../pyspec/eth2spec/test/helpers/block.py | 4 +- .../test_process_attester_slashing.py | 2 +- .../test_process_block_header.py | 2 +- .../block_processing/test_process_transfer.py | 2 +- ..._process_justification_and_finalization.py | 4 +- .../eth2spec/test/sanity/test_blocks.py | 2 +- .../pyspec/eth2spec/test/test_finality.py | 2 +- 12 files changed, 49 insertions(+), 48 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index cd943187f..4753510c2 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -70,7 +70,7 @@ - [`compute_committee`](#compute_committee) - [`validate_indexed_attestation`](#validate_indexed_attestation) - [`slot_to_epoch`](#slot_to_epoch) - - [`epoch_first_slot`](#epoch_first_slot) + - [`epoch_start_slot`](#epoch_start_slot) - [`delayed_activation_exit_epoch`](#delayed_activation_exit_epoch) - [`bls_domain`](#bls_domain) - [Beacon state getters](#beacon-state-getters) @@ -86,7 +86,7 @@ - [`get_crosslink_committee`](#get_crosslink_committee) - [`get_start_shard`](#get_start_shard) - [`get_shard_delta`](#get_shard_delta) - - [`get_proposer_index`](#get_proposer_index) + - [`get_beacon_proposer_index`](#get_beacon_proposer_index) - [`get_attestation_data_slot`](#get_attestation_data_slot) - [`get_compact_committees_root`](#get_compact_committees_root) - [`get_total_balance`](#get_total_balance) @@ -541,7 +541,7 @@ class BeaconState(Container): #### `integer_squareroot` ```python -def integer_squareroot(n: uint64) -> uint64: +def integer_squareroot(n: int) -> int: """ Return the largest integer ``x`` such that ``x**2 <= n``. """ @@ -558,7 +558,7 @@ def integer_squareroot(n: uint64) -> uint64: ```python def xor(bytes1: Bytes32, bytes2: Bytes32) -> Bytes32: """ - Return the exclusive or of two 32-byte strings. + Return the exclusive-or of two 32-byte strings. """ return Bytes32(a ^ b for a, b in zip(bytes1, bytes2)) ``` @@ -744,12 +744,12 @@ def slot_to_epoch(slot: Slot) -> Epoch: return Epoch(slot // SLOTS_PER_EPOCH) ``` -#### `epoch_first_slot` +#### `epoch_start_slot` ```python -def epoch_first_slot(epoch: Epoch) -> Slot: +def epoch_start_slot(epoch: Epoch) -> Slot: """ - Return the first slot of ``epoch``. + Return the start slot of ``epoch``. """ return Slot(epoch * SLOTS_PER_EPOCH) ``` @@ -759,7 +759,7 @@ def epoch_first_slot(epoch: Epoch) -> Slot: ```python def delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: """ - Return the epoch where validator activations and exits initiated in ``epoch`` take effect. + Return the epoch during which validator activations and exits initiated in ``epoch`` take effect. """ return Epoch(epoch + 1 + ACTIVATION_EXIT_DELAY) ``` @@ -802,9 +802,9 @@ def get_previous_epoch(state: BeaconState) -> Epoch: ```python def get_block_root(state: BeaconState, epoch: Epoch) -> Hash: """ - Return the block root at a recent ``epoch``. + Return the block root at the start of a recent ``epoch``. """ - return get_block_root_at_slot(state, epoch_first_slot(epoch)) + return get_block_root_at_slot(state, epoch_start_slot(epoch)) ``` #### `get_block_root_at_slot` @@ -869,10 +869,11 @@ def get_committee_count(state: BeaconState, epoch: Epoch) -> int: """ Return the number of committees at ``epoch``. """ - return max(1, min( + committees_per_slot = max(1, min( SHARD_COUNT // SLOTS_PER_EPOCH, len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE, - )) * SLOTS_PER_EPOCH + )) + return committees_per_slot * SLOTS_PER_EPOCH ``` #### `get_crosslink_committee` @@ -909,19 +910,19 @@ def get_start_shard(state: BeaconState, epoch: Epoch) -> Shard: #### `get_shard_delta` ```python -def get_shard_delta(state: BeaconState, epoch: Epoch) -> Shard: +def get_shard_delta(state: BeaconState, epoch: Epoch) -> int: """ 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)) + return min(get_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH) ``` -#### `get_proposer_index` +#### `get_beacon_proposer_index` ```python -def get_proposer_index(state: BeaconState) -> ValidatorIndex: +def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: """ - Return the beacon proposer index at the current epoch. + Return the beacon proposer index at the current slot. """ epoch = get_current_epoch(state) committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH @@ -949,7 +950,7 @@ def get_attestation_data_slot(state: BeaconState, data: AttestationData) -> Slot """ 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_first_slot(data.target.epoch) + offset // (committee_count // SLOTS_PER_EPOCH)) + return Slot(epoch_start_slot(data.target.epoch) + offset // (committee_count // SLOTS_PER_EPOCH)) ``` #### `get_compact_committees_root` @@ -1102,7 +1103,7 @@ def slash_validator(state: BeaconState, decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT) # Apply proposer and whistleblower rewards - proposer_index = get_proposer_index(state) + proposer_index = get_beacon_proposer_index(state) if whistleblower_index is None: whistleblower_index = proposer_index whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT) @@ -1190,7 +1191,7 @@ def process_slots(state: BeaconState, slot: Slot) -> None: assert state.slot <= slot while state.slot < slot: process_slot(state) - # Process epoch on the first slot of the next epoch + # Process epoch on the start slot of the next epoch if (state.slot + 1) % SLOTS_PER_EPOCH == 0: process_epoch(state) state.slot += Slot(1) @@ -1529,7 +1530,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: body_root=hash_tree_root(block.body), ) # Verify proposer is not slashed - proposer = state.validators[get_proposer_index(state)] + proposer = state.validators[get_beacon_proposer_index(state)] assert not proposer.slashed # Verify proposer signature assert bls_verify(proposer.pubkey, signing_root(block), block.signature, get_domain(state, DOMAIN_BEACON_PROPOSER)) @@ -1541,7 +1542,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: def process_randao(state: BeaconState, body: BeaconBlockBody) -> None: epoch = get_current_epoch(state) # Verify RANDAO reveal - proposer = state.validators[get_proposer_index(state)] + proposer = state.validators[get_beacon_proposer_index(state)] assert bls_verify(proposer.pubkey, hash_tree_root(epoch), body.randao_reveal, get_domain(state, DOMAIN_RANDAO)) # Mix in RANDAO reveal mix = xor(get_randao_mix(state, epoch), hash(body.randao_reveal)) @@ -1632,7 +1633,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: data=data, aggregation_bits=attestation.aggregation_bits, inclusion_delay=state.slot - attestation_slot, - proposer_index=get_proposer_index(state), + proposer_index=get_beacon_proposer_index(state), ) if data.target.epoch == get_current_epoch(state): @@ -1744,7 +1745,7 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: # Process the transfer decrease_balance(state, transfer.sender, transfer.amount + transfer.fee) increase_balance(state, transfer.recipient, transfer.amount) - increase_balance(state, get_proposer_index(state), transfer.fee) + increase_balance(state, get_beacon_proposer_index(state), transfer.fee) # Verify balances are not dust assert not (0 < state.balances[transfer.sender] < MIN_DEPOSIT_AMOUNT) assert not (0 < state.balances[transfer.recipient] < MIN_DEPOSIT_AMOUNT) diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index b2ae89e9f..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 = epoch_first_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 > epoch_first_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 + epoch_first_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, epoch_first_slot(target.epoch)) + process_slots(base_state, epoch_start_slot(target.epoch)) store.checkpoint_states[target] = base_state target_state = store.checkpoint_states[target] diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index bf221ee3e..5e8be7b39 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -382,7 +382,7 @@ def process_custody_key_reveal(state: BeaconState, reveal: CustodyKeyReveal) -> revealer.next_custody_reveal_period += 1 # Reward Block Preposer - proposer_index = get_proposer_index(state) + proposer_index = get_beacon_proposer_index(state) increase_balance( state, proposer_index, @@ -452,7 +452,7 @@ def process_early_derived_secret_reveal(state: BeaconState, reveal: EarlyDerived ) # Apply penalty - proposer_index = get_proposer_index(state) + proposer_index = get_beacon_proposer_index(state) whistleblower_index = reveal.masker_index whistleblowing_reward = Gwei(penalty // WHISTLEBLOWER_REWARD_QUOTIENT) proposer_reward = Gwei(whistleblowing_reward // PROPOSER_REWARD_QUOTIENT) @@ -493,7 +493,7 @@ def process_chunk_challenge(state: BeaconState, challenge: CustodyChunkChallenge # Add new chunk challenge record new_record = CustodyChunkChallengeRecord( challenge_index=state.custody_challenge_index, - challenger_index=get_proposer_index(state), + challenger_index=get_beacon_proposer_index(state), responder_index=challenge.responder_index, inclusion_epoch=get_current_epoch(state), data_root=challenge.attestation.data.crosslink.data_root, @@ -610,7 +610,7 @@ def process_chunk_challenge_response(state: BeaconState, records = state.custody_chunk_challenge_records records[records.index(challenge)] = CustodyChunkChallengeRecord() # Reward the proposer - proposer_index = get_proposer_index(state) + proposer_index = get_beacon_proposer_index(state) increase_balance(state, proposer_index, Gwei(get_base_reward(state, proposer_index) // MINOR_REWARD_QUOTIENT)) ``` diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 9921bf034..f4a83b833 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -146,7 +146,7 @@ def get_committee_assignment( assert epoch <= next_epoch committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH - start_slot = epoch_first_slot(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_start_shard(state, epoch) + offset) % SHARD_COUNT @@ -162,7 +162,7 @@ A validator can use the following function to see if they are supposed to propos ```python def is_proposer(state: BeaconState, validator_index: ValidatorIndex) -> bool: - return get_proposer_index(state) == validator_index + return get_beacon_proposer_index(state) == validator_index ``` *Note*: To see if a validator is assigned to propose during the slot, the beacon state must be in the epoch in question. At the epoch boundaries, the validator must run an epoch transition into the epoch to successfully check the proposal assignment of the first slot. @@ -307,7 +307,7 @@ 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 `start_slot = epoch_first_slot(get_current_epoch(head_state))`. +* 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/helpers/attestations.py b/test_libs/pyspec/eth2spec/test/helpers/attestations.py index c47245bb9..92c4b843d 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/attestations.py +++ b/test_libs/pyspec/eth2spec/test/helpers/attestations.py @@ -15,15 +15,15 @@ def build_attestation_data(spec, state, slot, shard): else: block_root = spec.get_block_root_at_slot(state, slot) - current_epoch_first_slot = spec.epoch_first_slot(spec.get_current_epoch(state)) - if slot < current_epoch_first_slot: + 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_first_slot: + elif slot == current_epoch_start_slot: epoch_boundary_root = block_root else: epoch_boundary_root = spec.get_block_root(state, spec.get_current_epoch(state)) - if slot < current_epoch_first_slot: + if slot < current_epoch_start_slot: source_epoch = state.previous_justified_checkpoint.epoch source_root = state.previous_justified_checkpoint.root else: diff --git a/test_libs/pyspec/eth2spec/test/helpers/block.py b/test_libs/pyspec/eth2spec/test/helpers/block.py index befeec7d3..fdbc5ef55 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/block.py +++ b/test_libs/pyspec/eth2spec/test/helpers/block.py @@ -12,7 +12,7 @@ def sign_block(spec, state, block, proposer_index=None): if proposer_index is None: if block.slot == state.slot: - proposer_index = spec.get_proposer_index(state) + proposer_index = spec.get_beacon_proposer_index(state) else: if spec.slot_to_epoch(state.slot) + 1 > spec.slot_to_epoch(block.slot): print("warning: block slot far away, and no proposer index manually given." @@ -20,7 +20,7 @@ def sign_block(spec, state, block, proposer_index=None): # use stub state to get proposer index of future slot stub_state = deepcopy(state) spec.process_slots(stub_state, block.slot) - proposer_index = spec.get_proposer_index(stub_state) + proposer_index = spec.get_beacon_proposer_index(stub_state) privkey = privkeys[proposer_index] diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py index 7f539e3b9..7a6030157 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py @@ -30,7 +30,7 @@ def run_attester_slashing_processing(spec, state, attester_slashing, valid=True) + attester_slashing.attestation_1.custody_bit_1_indices ) - proposer_index = spec.get_proposer_index(state) + proposer_index = spec.get_beacon_proposer_index(state) pre_proposer_balance = get_balance(state, proposer_index) pre_slashings = {slashed_index: get_balance(state, slashed_index) for slashed_index in slashed_indices} pre_withdrawalable_epochs = { diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py index c348e57e1..a2306ef4d 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py @@ -75,7 +75,7 @@ def test_proposer_slashed(spec, state): # use stub state to get proposer index of next slot stub_state = deepcopy(state) next_slot(spec, stub_state) - proposer_index = spec.get_proposer_index(stub_state) + proposer_index = spec.get_beacon_proposer_index(stub_state) # set proposer to slashed state.validators[proposer_index].slashed = True diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py index 436b36e5b..f079ff578 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py @@ -21,7 +21,7 @@ def run_transfer_processing(spec, state, transfer, valid=True): yield 'post', None return - proposer_index = spec.get_proposer_index(state) + proposer_index = spec.get_beacon_proposer_index(state) pre_transfer_sender_balance = state.balances[transfer.sender] pre_transfer_recipient_balance = state.balances[transfer.recipient] pre_transfer_proposer_balance = state.balances[proposer_index] 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 a308fe3d2..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 @@ -33,7 +33,7 @@ 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 - start_slot = spec.epoch_first_slot(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). @@ -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.epoch_first_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/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index fa7da930d..b6f0ecba2 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -214,7 +214,7 @@ def test_attester_slashing(spec, state): # lost whistleblower reward assert get_balance(state, validator_index) < get_balance(pre_state, validator_index) - proposer_index = spec.get_proposer_index(state) + proposer_index = spec.get_beacon_proposer_index(state) # gained whistleblower reward assert ( get_balance(state, proposer_index) > diff --git a/test_libs/pyspec/eth2spec/test/test_finality.py b/test_libs/pyspec/eth2spec/test/test_finality.py index d572b05fb..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.epoch_first_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) From 3ae077a09b3e374f1e45461f1be535671c5b0476 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 30 Jun 2019 12:51:13 -0500 Subject: [PATCH 10/12] 'beacon state getters' -> ' beacon state accessors' --- specs/core/0_beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 4753510c2..7bc0c5377 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -73,7 +73,7 @@ - [`epoch_start_slot`](#epoch_start_slot) - [`delayed_activation_exit_epoch`](#delayed_activation_exit_epoch) - [`bls_domain`](#bls_domain) - - [Beacon state getters](#beacon-state-getters) + - [Beacon state accessors](#beacon-state-accessors) - [`get_current_epoch`](#get_current_epoch) - [`get_previous_epoch`](#get_previous_epoch) - [`get_block_root`](#get_block_root) @@ -774,7 +774,7 @@ def bls_domain(domain_type: int, fork_version: bytes=b'\x00' * 4) -> int: return bytes_to_int(int_to_bytes(domain_type, length=4) + fork_version) ``` -### Beacon state getters +### Beacon state accessors #### `get_current_epoch` From d1bc2f03acc9cff67dcecdd5945aa9c15e14f582 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 30 Jun 2019 13:00:22 -0500 Subject: [PATCH 11/12] remove 'int' types from spec typehints. replace with uint64 --- specs/core/0_beacon-chain.md | 16 ++++++++-------- specs/core/0_fork-choice.md | 4 ++-- specs/core/1_custody-game.md | 6 +++--- specs/core/1_shard-data-chains.md | 8 ++++---- specs/validator/0_beacon-chain-validator.md | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7bc0c5377..fb388e438 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -541,7 +541,7 @@ class BeaconState(Container): #### `integer_squareroot` ```python -def integer_squareroot(n: int) -> int: +def integer_squareroot(n: uint64) -> int: """ Return the largest integer ``x`` such that ``x**2 <= n``. """ @@ -564,7 +564,7 @@ def xor(bytes1: Bytes32, bytes2: Bytes32) -> Bytes32: ``` ```python -def int_to_bytes(integer: int, length: int) -> bytes: +def int_to_bytes(integer: uint64, length: uint64) -> bytes: """ Return the ``length``-byte serialization of ``integer``. """ @@ -647,7 +647,7 @@ def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationDa #### `is_valid_merkle_branch` ```python -def is_valid_merkle_branch(leaf: Hash, branch: Sequence[Hash], depth: int, index: int, root: Hash) -> bool: +def is_valid_merkle_branch(leaf: Hash, branch: Sequence[Hash], depth: uint64, index: uint64, root: Hash) -> bool: """ Check if ``leaf`` at ``index`` verifies against the Merkle ``root`` and ``branch``. """ @@ -665,7 +665,7 @@ def is_valid_merkle_branch(leaf: Hash, branch: Sequence[Hash], depth: int, index #### `shuffle_index` ```python -def shuffle_index(index: ValidatorIndex, index_count: int, seed: Hash) -> ValidatorIndex: +def shuffle_index(index: ValidatorIndex, index_count: uint64, seed: Hash) -> ValidatorIndex: """ Return the shuffled validator index corresponding to ``seed`` (and ``index_count``). """ @@ -691,8 +691,8 @@ def shuffle_index(index: ValidatorIndex, index_count: int, seed: Hash) -> Valida ```python def compute_committee(indices: Sequence[ValidatorIndex], seed: Hash, - index: int, - count: int) -> Sequence[ValidatorIndex]: + index: uint64, + count: uint64) -> Sequence[ValidatorIndex]: """ Return the committee corresponding to ``indices``, ``seed``, ``index``, and committee ``count``. """ @@ -767,7 +767,7 @@ def delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: #### `bls_domain` ```python -def bls_domain(domain_type: int, fork_version: bytes=b'\x00' * 4) -> int: +def bls_domain(domain_type: uint64, fork_version: bytes=b'\x00' * 4) -> int: """ Return the BLS domain for the ``domain_type`` and ``fork_version``. """ @@ -997,7 +997,7 @@ def get_total_active_balance(state: BeaconState) -> Gwei: #### `get_domain` ```python -def get_domain(state: BeaconState, domain_type: int, message_epoch: Epoch=None) -> int: +def get_domain(state: BeaconState, domain_type: uint64, message_epoch: Epoch=None) -> int: """ Return the signature domain (fork version concatenated with domain type) of a message. """ diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index 64afb3f6b..a45d79fbe 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -69,7 +69,7 @@ class LatestMessage(object): ```python @dataclass class Store(object): - time: int + time: uint64 justified_checkpoint: Checkpoint finalized_checkpoint: Checkpoint blocks: Dict[Hash, BeaconBlock] = field(default_factory=dict) @@ -141,7 +141,7 @@ def get_head(store: Store) -> Hash: #### `on_tick` ```python -def on_tick(store: Store, time: int) -> None: +def on_tick(store: Store, time: uint64) -> None: store.time = time ``` diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index 5e8be7b39..54a1ca34b 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -259,7 +259,7 @@ class BeaconBlockBody(Container): ### `ceillog2` ```python -def ceillog2(x: int) -> int: +def ceillog2(x: uint64) -> int: return x.bit_length() ``` @@ -275,7 +275,7 @@ def get_custody_chunk_count(crosslink: Crosslink) -> int: ### `get_bit` ```python -def get_bit(serialization: bytes, i: int) -> int: +def get_bit(serialization: bytes, i: uint64) -> int: """ Extract the bit in ``serialization`` at position ``i``. """ @@ -304,7 +304,7 @@ def get_chunk_bits_root(chunk_bits: bytes) -> Bytes32: ### `get_randao_epoch_for_custody_period` ```python -def get_randao_epoch_for_custody_period(period: int, validator_index: ValidatorIndex) -> Epoch: +def get_randao_epoch_for_custody_period(period: uint64, validator_index: ValidatorIndex) -> Epoch: next_period_start = (period + 1) * EPOCHS_PER_CUSTODY_PERIOD - validator_index % EPOCHS_PER_CUSTODY_PERIOD return Epoch(next_period_start + CUSTODY_PERIOD_TO_RANDAO_PADDING) ``` diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 82843b7a6..fd0e41b4f 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -132,8 +132,8 @@ class ShardBlockHeader(Container): def get_period_committee(state: BeaconState, epoch: Epoch, shard: Shard, - index: int, - count: int) -> Sequence[ValidatorIndex]: + index: uint64, + count: uint64) -> Sequence[ValidatorIndex]: """ Return committee for a period. Used to construct persistent committees. """ @@ -248,7 +248,7 @@ def verify_shard_attestation_signature(state: BeaconState, ```python def compute_crosslink_data_root(blocks: Sequence[ShardBlock]) -> Bytes32: - def is_power_of_two(value: int) -> bool: + def is_power_of_two(value: uint64) -> bool: return (value > 0) and (value & (value - 1) == 0) def pad_to_power_of_2(values: MutableSequence[bytes]) -> Sequence[bytes]: @@ -259,7 +259,7 @@ def compute_crosslink_data_root(blocks: Sequence[ShardBlock]) -> Bytes32: def hash_tree_root_of_bytes(data: bytes) -> bytes: return hash_tree_root([data[i:i + 32] for i in range(0, len(data), 32)]) - def zpad(data: bytes, length: int) -> bytes: + def zpad(data: bytes, length: uint64) -> bytes: return data + b'\x00' * (length - len(data)) return hash( diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index f4a83b833..0b6857a72 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -223,7 +223,7 @@ epoch_signature = bls_sign( The `block.eth1_data` field is for block proposers to vote on recent Eth 1.0 data. This recent data contains an Eth 1.0 block hash as well as the associated deposit root (as calculated by the `get_hash_tree_root()` method of the deposit contract) and deposit count after execution of the corresponding Eth 1.0 block. If over half of the block proposers in the current Eth 1.0 voting period vote for the same `eth1_data` then `state.eth1_data` updates at the end of the voting period. Each deposit in `block.body.deposits` must verify against `state.eth1_data.eth1_deposit_root`. -Let `get_eth1_data(distance: int) -> Eth1Data` be the (subjective) function that returns the Eth 1.0 data at distance `distance` relative to the Eth 1.0 head at the start of the current Eth 1.0 voting period. Let `previous_eth1_distance` be the distance relative to the Eth 1.0 block corresponding to `state.eth1_data.block_hash` at the start of the current Eth 1.0 voting period. An honest block proposer sets `block.eth1_data = get_eth1_vote(state, previous_eth1_distance)` where: +Let `get_eth1_data(distance: uint64) -> Eth1Data` be the (subjective) function that returns the Eth 1.0 data at distance `distance` relative to the Eth 1.0 head at the start of the current Eth 1.0 voting period. Let `previous_eth1_distance` be the distance relative to the Eth 1.0 block corresponding to `state.eth1_data.block_hash` at the start of the current Eth 1.0 voting period. An honest block proposer sets `block.eth1_data = get_eth1_vote(state, previous_eth1_distance)` where: ```python def get_eth1_vote(state: BeaconState, previous_eth1_distance: uint64) -> Eth1Data: From 6be1404b66c1238ed515e5718c146f757dca7d68 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 30 Jun 2019 13:16:56 -0500 Subject: [PATCH 12/12] delayed_activation_exit_epoch -> compute_activation_exit_epoch --- specs/core/0_beacon-chain.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index fb388e438..68915177c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -71,7 +71,7 @@ - [`validate_indexed_attestation`](#validate_indexed_attestation) - [`slot_to_epoch`](#slot_to_epoch) - [`epoch_start_slot`](#epoch_start_slot) - - [`delayed_activation_exit_epoch`](#delayed_activation_exit_epoch) + - [`compute_activation_exit_epoch`](#compute_activation_exit_epoch) - [`bls_domain`](#bls_domain) - [Beacon state accessors](#beacon-state-accessors) - [`get_current_epoch`](#get_current_epoch) @@ -754,10 +754,10 @@ def epoch_start_slot(epoch: Epoch) -> Slot: return Slot(epoch * SLOTS_PER_EPOCH) ``` -#### `delayed_activation_exit_epoch` +#### `compute_activation_exit_epoch` ```python -def delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: +def compute_activation_exit_epoch(epoch: Epoch) -> Epoch: """ Return the epoch during which validator activations and exits initiated in ``epoch`` take effect. """ @@ -1075,7 +1075,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: # Compute exit queue epoch exit_epochs = [v.exit_epoch for v in state.validators if v.exit_epoch != FAR_FUTURE_EPOCH] - exit_queue_epoch = max(exit_epochs + [delayed_activation_exit_epoch(get_current_epoch(state))]) + exit_queue_epoch = max(exit_epochs + [compute_activation_exit_epoch(get_current_epoch(state))]) exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch]) if exit_queue_churn >= get_validator_churn_limit(state): exit_queue_epoch += Epoch(1) @@ -1450,13 +1450,13 @@ def process_registry_updates(state: BeaconState) -> None: activation_queue = sorted([ index for index, validator in enumerate(state.validators) if validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and - validator.activation_epoch >= delayed_activation_exit_epoch(state.finalized_checkpoint.epoch) + validator.activation_epoch >= compute_activation_exit_epoch(state.finalized_checkpoint.epoch) ], key=lambda index: state.validators[index].activation_eligibility_epoch) # Dequeued validators for activation up to churn limit (without resetting activation epoch) for index in activation_queue[:get_validator_churn_limit(state)]: validator = state.validators[index] if validator.activation_epoch == FAR_FUTURE_EPOCH: - validator.activation_epoch = delayed_activation_exit_epoch(get_current_epoch(state)) + validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state)) ``` #### Slashings