From e1a1f936feb7ed253d77a034ae8404c3a74630d2 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 30 Jan 2019 18:22:25 -0800 Subject: [PATCH 01/70] penalized -> slashed, SEED_LOOKAHEAD -> MIN_SEED_LOOKAHED --- specs/core/0_beacon-chain.md | 42 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7baca73e6..02e76fa58 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -216,7 +216,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `SLOT_DURATION` | `6` | seconds | 6 seconds | | `MIN_ATTESTATION_INCLUSION_DELAY` | `2**2` (= 4) | slots | 24 seconds | | `EPOCH_LENGTH` | `2**6` (= 64) | slots | 6.4 minutes | -| `SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | +| `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | | `ENTRY_EXIT_DELAY` | `2**2` (= 4) | epochs | 25.6 minutes | | `ETH1_DATA_VOTING_PERIOD` | `2**4` (= 16) | epochs | ~1.7 hours | | `MIN_VALIDATOR_WITHDRAWAL_EPOCHS` | `2**8` (= 256) | epochs | ~27 hours | @@ -228,7 +228,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `LATEST_BLOCK_ROOTS_LENGTH` | `2**13` (= 8,192) | slots | ~13 hours | | `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | | `LATEST_INDEX_ROOTS_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | -| `LATEST_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | +| `LATEST_SLASHED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | ### Reward and penalty quotients @@ -508,7 +508,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'latest_crosslinks': [Crosslink], 'latest_block_roots': ['bytes32'], 'latest_index_roots': ['bytes32'], - 'latest_penalized_balances': ['uint64'], # Balances penalized at every withdrawal period + 'latest_slashed_balances': ['uint64'], # Balances slashed at every withdrawal period 'latest_attestations': [PendingAttestation], 'batched_block_roots': ['bytes32'], @@ -532,8 +532,8 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'exit_epoch': 'uint64', # Epoch when validator withdrew 'withdrawal_epoch': 'uint64', - # Epoch when validator was penalized - 'penalized_epoch': 'uint64', + # Epoch when validator was slashed + 'slashed_epoch': 'uint64', # Status flags 'status_flags': 'uint64', } @@ -942,7 +942,7 @@ def generate_seed(state: BeaconState, """ return hash( - get_randao_mix(state, epoch - SEED_LOOKAHEAD) + + get_randao_mix(state, epoch - MIN_SEED_LOOKAHEAD) + get_active_index_root(state, epoch) ) ``` @@ -1256,7 +1256,7 @@ def process_deposit(state: BeaconState, activation_epoch=FAR_FUTURE_EPOCH, exit_epoch=FAR_FUTURE_EPOCH, withdrawal_epoch=FAR_FUTURE_EPOCH, - penalized_epoch=FAR_FUTURE_EPOCH, + slashed_epoch=FAR_FUTURE_EPOCH, status_flags=0, ) @@ -1327,13 +1327,13 @@ def penalize_validator(state: BeaconState, index: ValidatorIndex) -> None: """ exit_validator(state, index) validator = state.validator_registry[index] - state.latest_penalized_balances[get_current_epoch(state) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index) + state.latest_slashed_balances[get_current_epoch(state) % LATEST_SLASHED_EXIT_LENGTH] += get_effective_balance(state, index) whistleblower_index = get_beacon_proposer_index(state, state.slot) whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT state.validator_balances[whistleblower_index] += whistleblower_reward state.validator_balances[index] -= whistleblower_reward - validator.penalized_epoch = get_current_epoch(state) + validator.slashed_epoch = get_current_epoch(state) ``` #### `prepare_validator_for_withdrawal` @@ -1528,7 +1528,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], latest_crosslinks=[Crosslink(epoch=GENESIS_EPOCH, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], latest_block_roots=[ZERO_HASH for _ in range(LATEST_BLOCK_ROOTS_LENGTH)], latest_index_roots=[ZERO_HASH for _ in range(LATEST_INDEX_ROOTS_LENGTH)], - latest_penalized_balances=[0 for _ in range(LATEST_PENALIZED_EXIT_LENGTH)], + latest_slashed_balances=[0 for _ in range(LATEST_SLASHED_EXIT_LENGTH)], latest_attestations=[], batched_block_roots=[], @@ -1701,7 +1701,7 @@ For each `proposer_slashing` in `block.body.proposer_slashings`: * Verify that `proposer_slashing.proposal_data_1.slot == proposer_slashing.proposal_data_2.slot`. * Verify that `proposer_slashing.proposal_data_1.shard == proposer_slashing.proposal_data_2.shard`. * Verify that `proposer_slashing.proposal_data_1.block_root != proposer_slashing.proposal_data_2.block_root`. -* Verify that `proposer.penalized_epoch > get_current_epoch(state)`. +* Verify that `proposer.slashed_epoch > get_current_epoch(state)`. * Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_1), signature=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_data_1.slot), DOMAIN_PROPOSAL))`. * Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_2), signature=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_data_2.slot), DOMAIN_PROPOSAL))`. * Run `penalize_validator(state, proposer_slashing.proposer_index)`. @@ -1718,7 +1718,7 @@ For each `attester_slashing` in `block.body.attester_slashings`: * Verify that `is_double_vote(slashable_attestation_1.data, slashable_attestation_2.data)` or `is_surround_vote(slashable_attestation_1.data, slashable_attestation_2.data)`. * Verify that `verify_slashable_attestation(state, slashable_attestation_1)`. * Verify that `verify_slashable_attestation(state, slashable_attestation_2)`. -* Let `slashable_indices = [index for index in slashable_attestation_1.validator_indices if index in slashable_attestation_2.validator_indices and state.validator_registry[index].penalized_epoch > get_current_epoch(state)]`. +* Let `slashable_indices = [index for index in slashable_attestation_1.validator_indices if index in slashable_attestation_2.validator_indices and state.validator_registry[index].slashed_epoch > get_current_epoch(state)]`. * Verify that `len(slashable_indices) >= 1`. * Run `penalize_validator(state, index)` for each `index` in `slashable_indices`. @@ -1942,7 +1942,7 @@ Case 2: `epochs_since_finality > 4`: * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_justified_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_boundary_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_head_attester_indices`, loses `base_reward(state, index)`. -* Any [active_validator](#dfn-active-validator) `index` with `validator.penalized_epoch <= current_epoch`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. +* Any [active_validator](#dfn-active-validator) `index` with `validator.slashed_epoch <= current_epoch`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. * Any [validator](#dfn-validator) `index` in `previous_epoch_attester_indices` loses `base_reward(state, index) - base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_distance(state, index)` ##### Attestation inclusion @@ -2064,19 +2064,19 @@ def process_penalties_and_exits(state: BeaconState) -> None: total_balance = sum(get_effective_balance(state, i) for i in active_validator_indices) for index, validator in enumerate(state.validator_registry): - if current_epoch == validator.penalized_epoch + LATEST_PENALIZED_EXIT_LENGTH // 2: - epoch_index = current_epoch % LATEST_PENALIZED_EXIT_LENGTH - total_at_start = state.latest_penalized_balances[(epoch_index + 1) % LATEST_PENALIZED_EXIT_LENGTH] - total_at_end = state.latest_penalized_balances[epoch_index] + if current_epoch == validator.slashed_epoch + LATEST_SLASHED_EXIT_LENGTH // 2: + epoch_index = current_epoch % LATEST_SLASHED_EXIT_LENGTH + total_at_start = state.latest_slashed_balances[(epoch_index + 1) % LATEST_SLASHED_EXIT_LENGTH] + total_at_end = state.latest_slashed_balances[epoch_index] total_penalties = total_at_end - total_at_start penalty = get_effective_balance(state, index) * min(total_penalties * 3, total_balance) // total_balance state.validator_balances[index] -= penalty def eligible(index): validator = state.validator_registry[index] - if validator.penalized_epoch <= current_epoch: - penalized_withdrawal_epochs = LATEST_PENALIZED_EXIT_LENGTH // 2 - return current_epoch >= validator.penalized_epoch + penalized_withdrawal_epochs + if validator.slashed_epoch <= current_epoch: + slashed_withdrawal_epochs = LATEST_SLASHED_EXIT_LENGTH // 2 + return current_epoch >= validator.slashed_epoch + slashed_withdrawal_epochs else: return current_epoch >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWAL_EPOCHS @@ -2095,7 +2095,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: #### Final updates * Set `state.latest_index_roots[(next_epoch + ENTRY_EXIT_DELAY) % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, next_epoch + ENTRY_EXIT_DELAY))`. -* Set `state.latest_penalized_balances[(next_epoch) % LATEST_PENALIZED_EXIT_LENGTH] = state.latest_penalized_balances[current_epoch % LATEST_PENALIZED_EXIT_LENGTH]`. +* Set `state.latest_slashed_balances[(next_epoch) % LATEST_SLASHED_EXIT_LENGTH] = state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH]`. * Set `state.latest_randao_mixes[next_epoch % LATEST_RANDAO_MIXES_LENGTH] = get_randao_mix(state, current_epoch)`. * Remove any `attestation` in `state.latest_attestations` such that `slot_to_epoch(attestation.data.slot) < current_epoch`. From e2e2ce0a0478688d5f4422e09682ac6c899072f7 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 31 Jan 2019 07:56:48 -0800 Subject: [PATCH 02/70] name changes: active_exit_delay, latest_active_index_roots --- specs/core/0_beacon-chain.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 02e76fa58..fee1c7521 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -217,7 +217,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `MIN_ATTESTATION_INCLUSION_DELAY` | `2**2` (= 4) | slots | 24 seconds | | `EPOCH_LENGTH` | `2**6` (= 64) | slots | 6.4 minutes | | `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | -| `ENTRY_EXIT_DELAY` | `2**2` (= 4) | epochs | 25.6 minutes | +| `ACTIVATION_EXIT_DELAY` | `2**2` (= 4) | epochs | 25.6 minutes | | `ETH1_DATA_VOTING_PERIOD` | `2**4` (= 16) | epochs | ~1.7 hours | | `MIN_VALIDATOR_WITHDRAWAL_EPOCHS` | `2**8` (= 256) | epochs | ~27 hours | @@ -227,7 +227,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | - | - | :-: | :-: | | `LATEST_BLOCK_ROOTS_LENGTH` | `2**13` (= 8,192) | slots | ~13 hours | | `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | -| `LATEST_INDEX_ROOTS_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | +| `LATEST_ACTIVE_INDEX_ROOTS_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | | `LATEST_SLASHED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | ### Reward and penalty quotients @@ -507,7 +507,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git # Recent state 'latest_crosslinks': [Crosslink], 'latest_block_roots': ['bytes32'], - 'latest_index_roots': ['bytes32'], + 'latest_active_index_roots': ['bytes32'], 'latest_slashed_balances': ['uint64'], # Balances slashed at every withdrawal period 'latest_attestations': [PendingAttestation], 'batched_block_roots': ['bytes32'], @@ -785,7 +785,7 @@ def get_shuffling(seed: Bytes32, return split(shuffled_active_validator_indices, committees_per_epoch) ``` -**Invariant**: if `get_shuffling(seed, validators, epoch)` returns some value `x` for some `epoch <= get_current_epoch(state) + ENTRY_EXIT_DELAY`, it should return the same value `x` for the same `seed` and `epoch` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. +**Invariant**: if `get_shuffling(seed, validators, epoch)` returns some value `x` for some `epoch <= get_current_epoch(state) + ACTIVATION_EXIT_DELAY`, it should return the same value `x` for the same `seed` and `epoch` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. **Note**: this definition and the next few definitions make heavy use of repetitive computing. Production implementations are expected to appropriately use caching/memoization to avoid redoing work. @@ -928,8 +928,8 @@ def get_active_index_root(state: BeaconState, """ Return the index root at a recent ``epoch``. """ - assert get_current_epoch(state) - LATEST_INDEX_ROOTS_LENGTH + ENTRY_EXIT_DELAY < epoch <= get_current_epoch(state) + ENTRY_EXIT_DELAY - return state.latest_index_roots[epoch % LATEST_INDEX_ROOTS_LENGTH] + assert get_current_epoch(state) - LATEST_ACTIVE_INDEX_ROOTS_LENGTH + ACTIVATION_EXIT_DELAY < epoch <= get_current_epoch(state) + ACTIVATION_EXIT_DELAY + return state.latest_active_index_roots[epoch % LATEST_ACTIVE_INDEX_ROOTS_LENGTH] ``` ### `generate_seed` @@ -1181,7 +1181,7 @@ def get_entry_exit_effect_epoch(epoch: EpochNumber) -> EpochNumber: An entry or exit triggered in the ``epoch`` given by the input takes effect at the epoch given by the output. """ - return epoch + 1 + ENTRY_EXIT_DELAY + return epoch + 1 + ACTIVATION_EXIT_DELAY ``` ### `bls_verify` @@ -1527,7 +1527,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], # Recent state latest_crosslinks=[Crosslink(epoch=GENESIS_EPOCH, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], latest_block_roots=[ZERO_HASH for _ in range(LATEST_BLOCK_ROOTS_LENGTH)], - latest_index_roots=[ZERO_HASH for _ in range(LATEST_INDEX_ROOTS_LENGTH)], + latest_active_index_roots=[ZERO_HASH for _ in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH)], latest_slashed_balances=[0 for _ in range(LATEST_SLASHED_EXIT_LENGTH)], latest_attestations=[], batched_block_roots=[], @@ -1553,8 +1553,8 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], activate_validator(state, validator_index, is_genesis=True) genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, GENESIS_EPOCH)) - for index in range(LATEST_INDEX_ROOTS_LENGTH): - state.latest_index_roots[index] = genesis_active_index_root + for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH): + state.latest_active_index_roots[index] = genesis_active_index_root state.current_epoch_seed = generate_seed(state, GENESIS_EPOCH) return state @@ -2094,7 +2094,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: #### Final updates -* Set `state.latest_index_roots[(next_epoch + ENTRY_EXIT_DELAY) % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, next_epoch + ENTRY_EXIT_DELAY))`. +* Set `state.latest_active_index_roots[(next_epoch + ACTIVATION_EXIT_DELAY) % LATEST_ACTIVE_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, next_epoch + ACTIVATION_EXIT_DELAY))`. * Set `state.latest_slashed_balances[(next_epoch) % LATEST_SLASHED_EXIT_LENGTH] = state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH]`. * Set `state.latest_randao_mixes[next_epoch % LATEST_RANDAO_MIXES_LENGTH] = get_randao_mix(state, current_epoch)`. * Remove any `attestation` in `state.latest_attestations` such that `slot_to_epoch(attestation.data.slot) < current_epoch`. From 9ea095efe91027feceee61845fc0abd7b0ee01e2 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 31 Jan 2019 07:58:31 -0800 Subject: [PATCH 03/70] remove Number from custom types --- specs/core/0_beacon-chain.md | 42 ++++++++++++++++++------------------ tmp.md | 4 ++++ 2 files changed, 25 insertions(+), 21 deletions(-) create mode 100644 tmp.md diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index fee1c7521..83f377ae5 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -606,9 +606,9 @@ We define the following Python custom types for type hinting and readability: | Name | SSZ equivalent | Description | | - | - | - | -| `SlotNumber` | `uint64` | a slot number | -| `EpochNumber` | `uint64` | an epoch number | -| `ShardNumber` | `uint64` | a shard number | +| `Slot` | `uint64` | a slot number | +| `Epoch` | `uint64` | an epoch number | +| `Shard` | `uint64` | a shard number | | `ValidatorIndex` | `uint64` | an index in the validator registry | | `Gwei` | `uint64` | an amount in Gwei | | `Bytes32` | `bytes32` | 32 bytes of binary data | @@ -632,7 +632,7 @@ Note: We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethere ### `slot_to_epoch` ```python -def slot_to_epoch(slot: SlotNumber) -> EpochNumber: +def slot_to_epoch(slot: Slot) -> Epoch: """ Return the epoch number of the given ``slot``. """ @@ -642,7 +642,7 @@ def slot_to_epoch(slot: SlotNumber) -> EpochNumber: ### `get_current_epoch` ```python -def get_current_epoch(state: BeaconState) -> EpochNumber: +def get_current_epoch(state: BeaconState) -> Epoch: """ Return the current epoch of the given ``state``. """ @@ -652,7 +652,7 @@ def get_current_epoch(state: BeaconState) -> EpochNumber: ### `get_epoch_start_slot` ```python -def get_epoch_start_slot(epoch: EpochNumber) -> SlotNumber: +def get_epoch_start_slot(epoch: Epoch) -> Slot: """ Return the starting slot of the given ``epoch``. """ @@ -661,7 +661,7 @@ def get_epoch_start_slot(epoch: EpochNumber) -> SlotNumber: ### `is_active_validator` ```python -def is_active_validator(validator: Validator, epoch: EpochNumber) -> bool: +def is_active_validator(validator: Validator, epoch: Epoch) -> bool: """ Check if ``validator`` is active. """ @@ -671,7 +671,7 @@ def is_active_validator(validator: Validator, epoch: EpochNumber) -> bool: ### `get_active_validator_indices` ```python -def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber) -> List[ValidatorIndex]: +def get_active_validator_indices(validators: List[Validator], epoch: Epoch) -> List[ValidatorIndex]: """ Get indices of active validators from ``validators``. """ @@ -766,7 +766,7 @@ def get_epoch_committee_count(active_validator_count: int) -> int: ```python def get_shuffling(seed: Bytes32, validators: List[Validator], - epoch: EpochNumber) -> List[List[ValidatorIndex]] + epoch: Epoch) -> List[List[ValidatorIndex]] """ Shuffle ``validators`` into crosslink committees seeded by ``seed`` and ``epoch``. Return a list of ``committees_per_epoch`` committees where each @@ -832,8 +832,8 @@ def get_next_epoch_committee_count(state: BeaconState) -> int: ```python def get_crosslink_committees_at_slot(state: BeaconState, - slot: SlotNumber, - registry_change=False: bool) -> List[Tuple[List[ValidatorIndex], ShardNumber]]: + slot: Slot, + registry_change=False: bool) -> List[Tuple[List[ValidatorIndex], Shard]]: """ Return the list of ``(committee, shard)`` tuples for the ``slot``. @@ -897,7 +897,7 @@ def get_crosslink_committees_at_slot(state: BeaconState, ```python def get_block_root(state: BeaconState, - slot: SlotNumber) -> Bytes32: + slot: Slot) -> Bytes32: """ Return the block root at a recent ``slot``. """ @@ -912,7 +912,7 @@ def get_block_root(state: BeaconState, ```python def get_randao_mix(state: BeaconState, - epoch: EpochNumber) -> Bytes32: + epoch: Epoch) -> Bytes32: """ Return the randao mix at a recent ``epoch``. """ @@ -924,7 +924,7 @@ def get_randao_mix(state: BeaconState, ```python def get_active_index_root(state: BeaconState, - epoch: EpochNumber) -> Bytes32: + epoch: Epoch) -> Bytes32: """ Return the index root at a recent ``epoch``. """ @@ -936,7 +936,7 @@ def get_active_index_root(state: BeaconState, ```python def generate_seed(state: BeaconState, - epoch: EpochNumber) -> Bytes32: + epoch: Epoch) -> Bytes32: """ Generate a seed for the given ``epoch``. """ @@ -951,7 +951,7 @@ def generate_seed(state: BeaconState, ```python def get_beacon_proposer_index(state: BeaconState, - slot: SlotNumber) -> ValidatorIndex: + slot: Slot) -> ValidatorIndex: """ Return the beacon proposer index for the ``slot``. """ @@ -1029,7 +1029,7 @@ def get_effective_balance(state: State, index: ValidatorIndex) -> Gwei: ```python def get_fork_version(fork: Fork, - epoch: EpochNumber) -> int: + epoch: Epoch) -> int: """ Return the fork version of the given ``epoch``. """ @@ -1043,7 +1043,7 @@ def get_fork_version(fork: Fork, ```python def get_domain(fork: Fork, - epoch: EpochNumber, + epoch: Epoch, domain_type: int) -> int: """ Get the domain number that represents the fork meta and signature domain. @@ -1176,7 +1176,7 @@ def integer_squareroot(n: int) -> int: ### `get_entry_exit_effect_epoch` ```python -def get_entry_exit_effect_epoch(epoch: EpochNumber) -> EpochNumber: +def get_entry_exit_effect_epoch(epoch: Epoch) -> Epoch: """ An entry or exit triggered in the ``epoch`` given by the input takes effect at the epoch given by the output. @@ -1587,10 +1587,10 @@ The beacon chain fork choice rule is a hybrid that combines justification and fi * Abstractly define `Store` as the type of storage object for the chain data and `store` be the set of attestations and blocks that the [validator](#dfn-validator) `v` has observed and verified (in particular, block ancestors must be recursively verified). Attestations not yet included in any chain are still included in `store`. * Let `finalized_head` be the finalized block with the highest epoch. (A block `B` is finalized if there is a descendant of `B` in `store` the processing of which sets `B` as finalized.) * Let `justified_head` be the descendant of `finalized_head` with the highest epoch that has been justified for at least 1 epoch. (A block `B` is justified if there is a descendant of `B` in `store` the processing of which sets `B` as justified.) If no such descendant exists set `justified_head` to `finalized_head`. -* Let `get_ancestor(store: Store, block: BeaconBlock, slot: SlotNumber) -> BeaconBlock` be the ancestor of `block` with slot number `slot`. The `get_ancestor` function can be defined recursively as: +* Let `get_ancestor(store: Store, block: BeaconBlock, slot: Slot) -> BeaconBlock` be the ancestor of `block` with slot number `slot`. The `get_ancestor` function can be defined recursively as: ```python -def get_ancestor(store: Store, block: BeaconBlock, slot: SlotNumber) -> BeaconBlock: +def get_ancestor(store: Store, block: BeaconBlock, slot: Slot) -> BeaconBlock: """ Get the ancestor of ``block`` with slot number ``slot``; return ``None`` if not found. """ diff --git a/tmp.md b/tmp.md new file mode 100644 index 000000000..e9b31b31e --- /dev/null +++ b/tmp.md @@ -0,0 +1,4 @@ +Should we strength the registry change condition to all cro + +Need to set `active_index_root` for `ENTRY_EXIT_DELAY` in the future at the end +of the current registry change. \ No newline at end of file From 1593b926cce9f93338a1c584f3f858a6b5dafcc3 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 31 Jan 2019 08:02:54 -0800 Subject: [PATCH 04/70] rename current and previous vars for shuffling to use 'shuffling' --- specs/core/0_beacon-chain.md | 70 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 83f377ae5..ebad5c59f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -491,12 +491,12 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git # Randomness and committees 'latest_randao_mixes': ['bytes32'], - 'previous_epoch_start_shard': 'uint64', - 'current_epoch_start_shard': 'uint64', - 'previous_calculation_epoch': 'uint64', - 'current_calculation_epoch': 'uint64', - 'previous_epoch_seed': 'bytes32', - 'current_epoch_seed': 'bytes32', + 'previous_shuffling_start_shard': 'uint64', + 'current_shuffling_start_shard': 'uint64', + 'previous_shuffling_epoch': 'uint64', + 'current_shuffling_epoch': 'uint64', + 'previous_shuffling_seed': 'bytes32', + 'current_shuffling_seed': 'bytes32', # Finality 'previous_justified_epoch': 'uint64', @@ -798,7 +798,7 @@ def get_previous_epoch_committee_count(state: BeaconState) -> int: """ previous_active_validators = get_active_validator_indices( state.validator_registry, - state.previous_calculation_epoch, + state.previous_shuffling_epoch, ) return get_epoch_committee_count(len(previous_active_validators)) ``` @@ -812,7 +812,7 @@ def get_current_epoch_committee_count(state: BeaconState) -> int: """ current_active_validators = get_active_validator_indices( state.validator_registry, - state.current_calculation_epoch, + state.current_shuffling_epoch, ) return get_epoch_committee_count(len(current_active_validators)) ``` @@ -849,14 +849,14 @@ def get_crosslink_committees_at_slot(state: BeaconState, if epoch == previous_epoch: committees_per_epoch = get_previous_epoch_committee_count(state) - seed = state.previous_epoch_seed - shuffling_epoch = state.previous_calculation_epoch - shuffling_start_shard = state.previous_epoch_start_shard + seed = state.previous_shuffling_seed + shuffling_epoch = state.previous_shuffling_epoch + shuffling_start_shard = state.previous_shuffling_start_shard elif epoch == current_epoch: committees_per_epoch = get_current_epoch_committee_count(state) - seed = state.current_epoch_seed - shuffling_epoch = state.current_calculation_epoch - shuffling_start_shard = state.current_epoch_start_shard + seed = state.current_shuffling_seed + shuffling_epoch = state.current_shuffling_epoch + shuffling_start_shard = state.current_shuffling_start_shard elif epoch == next_epoch: current_committees_per_epoch = get_current_epoch_committee_count(state) committees_per_epoch = get_next_epoch_committee_count(state) @@ -865,13 +865,13 @@ def get_crosslink_committees_at_slot(state: BeaconState, epochs_since_last_registry_update = current_epoch - state.validator_registry_update_epoch if registry_change: seed = generate_seed(state, next_epoch) - shuffling_start_shard = (state.current_epoch_start_shard + current_committees_per_epoch) % SHARD_COUNT + shuffling_start_shard = (state.current_shuffling_start_shard + current_committees_per_epoch) % SHARD_COUNT elif epochs_since_last_registry_update > 1 and is_power_of_two(epochs_since_last_registry_update): seed = generate_seed(state, next_epoch) - shuffling_start_shard = state.current_epoch_start_shard + shuffling_start_shard = state.current_shuffling_start_shard else: - seed = state.current_epoch_seed - shuffling_start_shard = state.current_epoch_start_shard + seed = state.current_shuffling_seed + shuffling_start_shard = state.current_shuffling_start_shard shuffling = get_shuffling( seed, @@ -1511,12 +1511,12 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], # Randomness and committees latest_randao_mixes=[ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)], - previous_epoch_start_shard=GENESIS_START_SHARD, - current_epoch_start_shard=GENESIS_START_SHARD, - previous_calculation_epoch=GENESIS_EPOCH, - current_calculation_epoch=GENESIS_EPOCH, - previous_epoch_seed=ZERO_HASH, - current_epoch_seed=ZERO_HASH, + previous_shuffling_start_shard=GENESIS_START_SHARD, + current_shuffling_start_shard=GENESIS_START_SHARD, + previous_shuffling_epoch=GENESIS_EPOCH, + current_shuffling_epoch=GENESIS_EPOCH, + previous_shuffling_seed=ZERO_HASH, + current_shuffling_seed=ZERO_HASH, # Finality previous_justified_epoch=GENESIS_EPOCH, @@ -1555,7 +1555,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, GENESIS_EPOCH)) for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH): state.latest_active_index_roots[index] = genesis_active_index_root - state.current_epoch_seed = generate_seed(state, GENESIS_EPOCH) + state.current_shuffling_seed = generate_seed(state, GENESIS_EPOCH) return state ``` @@ -1977,14 +1977,14 @@ def process_ejections(state: BeaconState) -> None: First, update the following: -* Set `state.previous_calculation_epoch = state.current_calculation_epoch`. -* Set `state.previous_epoch_start_shard = state.current_epoch_start_shard`. -* Set `state.previous_epoch_seed = state.current_epoch_seed`. +* Set `state.previous_shuffling_epoch = state.current_shuffling_epoch`. +* Set `state.previous_shuffling_start_shard = state.current_shuffling_start_shard`. +* Set `state.previous_shuffling_seed = state.current_shuffling_seed`. If the following are satisfied: * `state.finalized_epoch > state.validator_registry_update_epoch` -* `state.latest_crosslinks[shard].epoch > state.validator_registry_update_epoch` for every shard number `shard` in `[(state.current_epoch_start_shard + i) % SHARD_COUNT for i in range(get_current_epoch_committee_count(state))]` (that is, for every shard in the current committees) +* `state.latest_crosslinks[shard].epoch > state.validator_registry_update_epoch` for every shard number `shard` in `[(state.current_shuffling_start_shard + i) % SHARD_COUNT for i in range(get_current_epoch_committee_count(state))]` (that is, for every shard in the current committees) update the validator registry and associated fields by running @@ -2035,17 +2035,17 @@ def update_validator_registry(state: BeaconState) -> None: and perform the following updates: -* Set `state.current_calculation_epoch = next_epoch` -* Set `state.current_epoch_start_shard = (state.current_epoch_start_shard + get_current_epoch_committee_count(state)) % SHARD_COUNT` -* Set `state.current_epoch_seed = generate_seed(state, state.current_calculation_epoch)` +* Set `state.current_shuffling_epoch = next_epoch` +* Set `state.current_shuffling_start_shard = (state.current_shuffling_start_shard + get_current_epoch_committee_count(state)) % SHARD_COUNT` +* Set `state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch)` If a validator registry update does _not_ happen do the following: * Let `epochs_since_last_registry_update = current_epoch - state.validator_registry_update_epoch`. * If `epochs_since_last_registry_update > 1` and `is_power_of_two(epochs_since_last_registry_update)`: - * Set `state.current_calculation_epoch = next_epoch`. - * Set `state.current_epoch_seed = generate_seed(state, state.current_calculation_epoch)` - * _Note_ that `state.current_epoch_start_shard` is left unchanged. + * Set `state.current_shuffling_epoch = next_epoch`. + * Set `state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch)` + * _Note_ that `state.current_shuffling_start_shard` is left unchanged. **Invariant**: the active index root that is hashed into the shuffling seed actually is the `hash_tree_root` of the validator set that is used for that epoch. From 6716105f1915d92c88aa4b94e488ef3171a9508c Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 31 Jan 2019 08:23:41 -0800 Subject: [PATCH 05/70] ChainStart -> Eth2Genesis --- specs/core/0_beacon-chain.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ebad5c59f..e8deb821d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -100,7 +100,7 @@ - [Deposit arguments](#deposit-arguments) - [Withdrawal credentials](#withdrawal-credentials) - [`Deposit` logs](#deposit-logs) - - [`ChainStart` log](#chainstart-log) + - [`Eth2Genesis` log](#chainstart-log) - [Vyper code](#vyper-code) - [On startup](#on-startup) - [Beacon chain processing](#beacon-chain-processing) @@ -1369,13 +1369,13 @@ The private key corresponding to `withdrawal_pubkey` will be required to initiat Every Ethereum 1.0 deposit, of size between `MIN_DEPOSIT_AMOUNT` and `MAX_DEPOSIT_AMOUNT`, emits a `Deposit` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12 signature) is not verified by the deposit contract. -### `ChainStart` log +### `Eth2Genesis` log -When sufficiently many full deposits have been made the deposit contract emits the `ChainStart` log. The beacon chain state may then be initialized by calling the `get_initial_beacon_state` function (defined below) where: +When sufficiently many full deposits have been made the deposit contract emits the `Eth2Genesis` log. The beacon chain state may then be initialized by calling the `get_initial_beacon_state` function (defined below) where: -* `genesis_time` equals `time` in the `ChainStart` log -* `latest_eth1_data.deposit_root` equals `deposit_root` in the `ChainStart` log, and `latest_eth1_data.block_hash` equals the hash of the block that included the log -* `initial_validator_deposits` is a list of `Deposit` objects built according to the `Deposit` logs up to the deposit that triggered the `ChainStart` log, processed in the order in which they were emitted (oldest to newest) +* `genesis_time` equals `time` in the `Eth2Genesis` log +* `latest_eth1_data.deposit_root` equals `deposit_root` in the `Eth2Genesis` log, and `latest_eth1_data.block_hash` equals the hash of the block that included the log +* `initial_validator_deposits` is a list of `Deposit` objects built according to the `Deposit` logs up to the deposit that triggered the `Eth2Genesis` log, processed in the order in which they were emitted (oldest to newest) ### Vyper code @@ -1391,7 +1391,7 @@ TWO_TO_POWER_OF_TREE_DEPTH: constant(uint256) = 4294967296 # 2**32 SECONDS_PER_DAY: constant(uint256) = 86400 Deposit: event({deposit_root: bytes32, data: bytes[528], merkle_tree_index: bytes[8], branch: bytes32[32]}) -ChainStart: event({deposit_root: bytes32, time: bytes[8]}) +Eth2Genesis: event({deposit_root: bytes32, time: bytes[8]}) zerohashes: bytes32[32] branch: bytes32[32] @@ -1454,7 +1454,7 @@ def deposit(deposit_input: bytes[512]): if self.full_deposit_count == CHAIN_START_FULL_DEPOSIT_THRESHOLD: timestamp_day_boundary: uint256 = as_unitless_number(block.timestamp) - as_unitless_number(block.timestamp) % SECONDS_PER_DAY + SECONDS_PER_DAY chainstart_time: bytes[8] = slice(concat("", convert(timestamp_day_boundary, bytes32)), start=24, len=8) - log.ChainStart(new_deposit_root, chainstart_time) + log.Eth2Genesis(new_deposit_root, chainstart_time) self.chainStarted = True ``` @@ -1485,7 +1485,7 @@ A valid block with slot `GENESIS_SLOT` (a "genesis block") has the following val } ``` -`STARTUP_STATE_ROOT` (in the above "genesis block") is generated from the `get_initial_beacon_state` function below. When enough full deposits have been made to the deposit contract and the `ChainStart` log has been emitted, `get_initial_beacon_state` will execute to compute the `hash_tree_root` of `BeaconState`. +`STARTUP_STATE_ROOT` (in the above "genesis block") is generated from the `get_initial_beacon_state` function below. When enough full deposits have been made to the deposit contract and the `Eth2Genesis` log has been emitted, `get_initial_beacon_state` will execute to compute the `hash_tree_root` of `BeaconState`. ```python def get_initial_beacon_state(initial_validator_deposits: List[Deposit], From 7ff345404e02c81d687642128838ecedd0ab90de Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 31 Jan 2019 08:26:46 -0800 Subject: [PATCH 06/70] simplify bls types --- 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 e8deb821d..cb44d7943 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -612,8 +612,8 @@ We define the following Python custom types for type hinting and readability: | `ValidatorIndex` | `uint64` | an index in the validator registry | | `Gwei` | `uint64` | an amount in Gwei | | `Bytes32` | `bytes32` | 32 bytes of binary data | -| `BLSPubkey` | `bytes48` | a BLS public key | -| `BLSSignature` | `bytes96` | a BLS signature | +| `Pubkey` | `bytes48` | a BLS12-381 public key | +| `Signature` | `bytes96` | a BLS12-381 signature | ## Helper functions @@ -1200,8 +1200,8 @@ def get_entry_exit_effect_epoch(epoch: Epoch) -> Epoch: ```python def validate_proof_of_possession(state: BeaconState, - pubkey: BLSPubkey, - proof_of_possession: BLSSignature, + pubkey: Pubkey, + proof_of_possession: Signature, withdrawal_credentials: Bytes32) -> bool: """ Verify the given ``proof_of_possession``. @@ -1230,9 +1230,9 @@ Used to add a [validator](#dfn-validator) or top up an existing [validator](#dfn ```python def process_deposit(state: BeaconState, - pubkey: BLSPubkey, + pubkey: Pubkey, amount: Gwei, - proof_of_possession: BLSSignature, + proof_of_possession: Signature, withdrawal_credentials: Bytes32) -> None: """ Process a deposit from Ethereum 1.0. From 11ae40fc4974f8b39cf9e079d62df2f5ac3ce584 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 1 Feb 2019 20:42:11 -0800 Subject: [PATCH 07/70] revery bls types to include BLS --- specs/core/0_beacon-chain.md | 12 ++++++------ tmp.md | 4 ---- 2 files changed, 6 insertions(+), 10 deletions(-) delete mode 100644 tmp.md diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index cb44d7943..372542a0a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -612,8 +612,8 @@ We define the following Python custom types for type hinting and readability: | `ValidatorIndex` | `uint64` | an index in the validator registry | | `Gwei` | `uint64` | an amount in Gwei | | `Bytes32` | `bytes32` | 32 bytes of binary data | -| `Pubkey` | `bytes48` | a BLS12-381 public key | -| `Signature` | `bytes96` | a BLS12-381 signature | +| `BLSPubkey` | `bytes48` | a BLS12-381 public key | +| `BLSSignature` | `bytes96` | a BLS12-381 signature | ## Helper functions @@ -1200,8 +1200,8 @@ def get_entry_exit_effect_epoch(epoch: Epoch) -> Epoch: ```python def validate_proof_of_possession(state: BeaconState, - pubkey: Pubkey, - proof_of_possession: Signature, + pubkey: BLSPubkey, + proof_of_possession: BLSSignature, withdrawal_credentials: Bytes32) -> bool: """ Verify the given ``proof_of_possession``. @@ -1230,9 +1230,9 @@ Used to add a [validator](#dfn-validator) or top up an existing [validator](#dfn ```python def process_deposit(state: BeaconState, - pubkey: Pubkey, + pubkey: BLSPubkey, amount: Gwei, - proof_of_possession: Signature, + proof_of_possession: BLSSignature, withdrawal_credentials: Bytes32) -> None: """ Process a deposit from Ethereum 1.0. diff --git a/tmp.md b/tmp.md deleted file mode 100644 index e9b31b31e..000000000 --- a/tmp.md +++ /dev/null @@ -1,4 +0,0 @@ -Should we strength the registry change condition to all cro - -Need to set `active_index_root` for `ENTRY_EXIT_DELAY` in the future at the end -of the current registry change. \ No newline at end of file From 8597cbef1fb28f6f54eedfded5b5f633b4928694 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 6 Feb 2019 20:32:56 -0600 Subject: [PATCH 08/70] Updated phase 1: shard block structure --- specs/core/1_shard-data-chains.md | 58 +++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index cabe2934e..f3324bbbd 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -16,10 +16,11 @@ Ethereum 2.0 consists of a central beacon chain along with `SHARD_COUNT` shard c Phase 1 depends upon all of the constants defined in [Phase 0](0_beacon-chain.md#constants) in addition to the following: -| Constant | Value | Unit | Approximation | -|------------------------|-----------------|-------|---------------| -| `SHARD_CHUNK_SIZE` | 2**5 (= 32) | bytes | | -| `SHARD_BLOCK_SIZE` | 2**14 (= 16384) | bytes | | +| Constant | Value | Unit | Approximation | +|-----------------------------|-----------------|--------|---------------| +| `SHARD_CHUNK_SIZE` | 2**5 (= 32) | bytes | | +| `SHARD_BLOCK_SIZE` | 2**14 (= 16384) | bytes | | +| `PROPOSAL_RESHUFFLE_PERIOD` | 2**11 (= 2048) | epochs | 9 days | ### Flags, domains, etc. @@ -40,12 +41,10 @@ A `ShardBlock` object has the following fields: 'slot': 'uint64', # What shard is it on 'shard_id': 'uint64', - # Parent block's hash of root + # Parent block's root 'parent_root': 'hash32', # Beacon chain block 'beacon_chain_ref': 'hash32', - # Depth of the Merkle tree - 'data_tree_depth': 'uint8', # Merkle root of data 'data_root': 'hash32' # State root (placeholder for now) @@ -67,22 +66,43 @@ For a block on a shard to be processed by a node, the following conditions must To validate a block header on shard `shard_id`, compute as follows: -* Verify that `beacon_chain_ref` is the hash of a block in the beacon chain with slot less than or equal to `slot`. Verify that `beacon_chain_ref` is equal to or a descendant of the `beacon_chain_ref` specified in the `ShardBlock` pointed to by `parent_root`. -* Let `state` be the state of the beacon chain block referred to by `beacon_chain_ref`. Let `validators` be `[validators[i] for i in state.current_persistent_committees[shard_id]]`. -* Assert `len(participation_bitfield) == ceil_div8(len(validators))` +* Verify that `beacon_chain_ref` is the hash of a block in the (canonical) beacon chain with slot less than or equal to `slot`. +* Verify that `beacon_chain_ref` is equal to or a descendant of the `beacon_chain_ref` specified in the `ShardBlock` pointed to by `parent_root`. +* Let `state` be the state of the beacon chain block referred to by `beacon_chain_ref`. Let `persistent_committee` be `[persistent_committee[i] for i in get_persistent_committee(state, slot, shard_id)`. +* Assert `verify_bitfield(participation_bitfield, len(persistent_committee))` * Let `proposer_index = hash(state.randao_mix + int_to_bytes8(shard_id) + int_to_bytes8(slot)) % len(validators)`. Let `msg` be the block but with the `block.signature` set to `[0, 0]`. Verify that `BLSVerify(pub=validators[proposer_index].pubkey, msg=hash(msg), sig=block.signature, domain=get_domain(state, slot, SHARD_PROPOSER_DOMAIN))` passes. -* Generate the `group_public_key` by adding the public keys of all the validators for whom the corresponding position in the bitfield is set to 1. Verify that `BLSVerify(pub=group_public_key, msg=parent_root, sig=block.aggregate_signature, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. +* Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, msg=parent_root, sig=block.aggregate_signature, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. -### Block Merklization helper +We define the helper `get_proposal_committee` as follows: ```python -def merkle_root(block_body): - assert len(block_body) == SHARD_BLOCK_SIZE - chunks = SHARD_BLOCK_SIZE // SHARD_CHUNK_SIZE - o = [0] * chunks + [block_body[i * SHARD_CHUNK_SIZE: (i+1) * SHARD_CHUNK_SIZE] for i in range(chunks)] - for i in range(chunks-1, 0, -1): - o[i] = hash(o[i*2] + o[i*2+1]) - return o[1] +def get_proposal_committee(seed: Bytes32, + validators: List[Validator], + shard: int, + epoch: EpochNumber) -> List[ValidatorIndex]: + + earlier_committee_start = epoch - (epoch % PROPOSAL_RESHUFFLE_PERIOD) - PROPOSAL_RESHUFFLE_PERIOD * 2 + earlier_committee = split(shuffle( + get_active_validator_indices(validators, earlier_committee_start), + generate_seed(state, earlier_committee_start) + ), SHARD_COUNT)[shard] + + later_committee_start = epoch - (epoch % PROPOSAL_RESHUFFLE_PERIOD) - PROPOSAL_RESHUFFLE_PERIOD + later_committee = split(shuffle( + get_active_validator_indices(validators, earlier_committee_start), + generate_seed(state, earlier_committee_start) + ), SHARD_COUNT)[shard] + + def get_switchover_epoch(index): + return ( + int.from_bytes(hash(generate_seed(state, earlier_committee_start) + bytes3(index)), 'little') % + PROPOSAL_RESHUFFLE_PERIOD + ) + + return ( + [i for i in earlier_committee if epoch % PROPOSAL_RESHUFFLE_PERIOD < get_switchover_epoch(i)] + + [i for i in later_committee if epoch % PROPOSAL_RESHUFFLE_PERIOD >= get_switchover_epoch(i)] + ) ``` ### Verifying shard block data From fb90fd7ac3b1accd9dc680439260dd25d59414c8 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 8 Feb 2019 01:07:31 -0600 Subject: [PATCH 09/70] Update specs/core/1_shard-data-chains.md Co-Authored-By: vbuterin --- specs/core/1_shard-data-chains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index f3324bbbd..f25844ecb 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -71,7 +71,7 @@ To validate a block header on shard `shard_id`, compute as follows: * Let `state` be the state of the beacon chain block referred to by `beacon_chain_ref`. Let `persistent_committee` be `[persistent_committee[i] for i in get_persistent_committee(state, slot, shard_id)`. * Assert `verify_bitfield(participation_bitfield, len(persistent_committee))` * Let `proposer_index = hash(state.randao_mix + int_to_bytes8(shard_id) + int_to_bytes8(slot)) % len(validators)`. Let `msg` be the block but with the `block.signature` set to `[0, 0]`. Verify that `BLSVerify(pub=validators[proposer_index].pubkey, msg=hash(msg), sig=block.signature, domain=get_domain(state, slot, SHARD_PROPOSER_DOMAIN))` passes. -* Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, msg=parent_root, sig=block.aggregate_signature, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. +* Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, message_hash=parent_root, sig=block.aggregate_signature, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. We define the helper `get_proposal_committee` as follows: From a1f542b6743bba384763cb0b76a67d2e919ed4e6 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 8 Feb 2019 01:07:46 -0600 Subject: [PATCH 10/70] Update specs/core/1_shard-data-chains.md Co-Authored-By: vbuterin --- specs/core/1_shard-data-chains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index f25844ecb..a401c87c3 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -78,7 +78,7 @@ We define the helper `get_proposal_committee` as follows: ```python def get_proposal_committee(seed: Bytes32, validators: List[Validator], - shard: int, + shard: ShardNumber, epoch: EpochNumber) -> List[ValidatorIndex]: earlier_committee_start = epoch - (epoch % PROPOSAL_RESHUFFLE_PERIOD) - PROPOSAL_RESHUFFLE_PERIOD * 2 From bdd7dc106af7e541da4f257d5233495f074aaff6 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 8 Feb 2019 01:08:01 -0600 Subject: [PATCH 11/70] Update specs/core/1_shard-data-chains.md Co-Authored-By: vbuterin --- specs/core/1_shard-data-chains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index a401c87c3..cb262a1ee 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -90,7 +90,7 @@ def get_proposal_committee(seed: Bytes32, later_committee_start = epoch - (epoch % PROPOSAL_RESHUFFLE_PERIOD) - PROPOSAL_RESHUFFLE_PERIOD later_committee = split(shuffle( get_active_validator_indices(validators, earlier_committee_start), - generate_seed(state, earlier_committee_start) + generate_seed(state, later_committee_start) ), SHARD_COUNT)[shard] def get_switchover_epoch(index): From 5e84d7e3ffd411e31f284989393085b20941a1b4 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 8 Feb 2019 01:08:15 -0600 Subject: [PATCH 12/70] Update specs/core/1_shard-data-chains.md Co-Authored-By: vbuterin --- specs/core/1_shard-data-chains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index cb262a1ee..2984b3fb7 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -95,7 +95,7 @@ def get_proposal_committee(seed: Bytes32, def get_switchover_epoch(index): return ( - int.from_bytes(hash(generate_seed(state, earlier_committee_start) + bytes3(index)), 'little') % + bytes_to_int(hash(generate_seed(state, earlier_committee_start) + bytes3(index))[0:8]) % PROPOSAL_RESHUFFLE_PERIOD ) From 5a1b2c29f262460c10bee8f2658294ddc2015326 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 8 Feb 2019 01:08:30 -0600 Subject: [PATCH 13/70] Update specs/core/1_shard-data-chains.md Co-Authored-By: vbuterin --- specs/core/1_shard-data-chains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 2984b3fb7..af49825bd 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -89,7 +89,7 @@ def get_proposal_committee(seed: Bytes32, later_committee_start = epoch - (epoch % PROPOSAL_RESHUFFLE_PERIOD) - PROPOSAL_RESHUFFLE_PERIOD later_committee = split(shuffle( - get_active_validator_indices(validators, earlier_committee_start), + get_active_validator_indices(validators, later_committee_start), generate_seed(state, later_committee_start) ), SHARD_COUNT)[shard] From 2cff0e6b7a3f3437bc040f1fd023385a6c1a743c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 8 Feb 2019 16:46:50 +0800 Subject: [PATCH 14/70] Use the new `bytesN` SSZ object `hash32` -> `bytes32` ['uint384'] -> `bytes96` --- specs/core/1_shard-data-chains.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index af49825bd..0af79235e 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -16,11 +16,11 @@ Ethereum 2.0 consists of a central beacon chain along with `SHARD_COUNT` shard c Phase 1 depends upon all of the constants defined in [Phase 0](0_beacon-chain.md#constants) in addition to the following: -| Constant | Value | Unit | Approximation | -|-----------------------------|-----------------|--------|---------------| -| `SHARD_CHUNK_SIZE` | 2**5 (= 32) | bytes | | -| `SHARD_BLOCK_SIZE` | 2**14 (= 16384) | bytes | | -| `PROPOSAL_RESHUFFLE_PERIOD` | 2**11 (= 2048) | epochs | 9 days | +| Constant | Value | Unit | Approximation | +|-----------------------------|------------------|--------|---------------| +| `SHARD_CHUNK_SIZE` | 2**5 (= 32) | bytes | | +| `SHARD_BLOCK_SIZE` | 2**14 (= 16,384) | bytes | | +| `PROPOSAL_RESHUFFLE_PERIOD` | 2**11 (= 2,048) | epochs | 9 days | ### Flags, domains, etc. @@ -42,18 +42,18 @@ A `ShardBlock` object has the following fields: # What shard is it on 'shard_id': 'uint64', # Parent block's root - 'parent_root': 'hash32', + 'parent_root': 'bytes32', # Beacon chain block - 'beacon_chain_ref': 'hash32', + 'beacon_chain_ref': 'bytes32', # Merkle root of data - 'data_root': 'hash32' + 'data_root': 'bytes32' # State root (placeholder for now) - 'state_root': 'hash32', + 'state_root': 'bytes32', # Block signature - 'signature': ['uint384'], + 'signature': 'bytes96', # Attestation 'participation_bitfield': 'bytes', - 'aggregate_signature': ['uint384'], + 'aggregate_signature': 'bytes96', } ``` From 410372ff0ed909f7b5a1063a74f2eee70343cea4 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 8 Feb 2019 03:48:58 -0600 Subject: [PATCH 15/70] Proposal committee -> persistent committee --- specs/core/1_shard-data-chains.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 0af79235e..1224449e3 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -16,11 +16,11 @@ Ethereum 2.0 consists of a central beacon chain along with `SHARD_COUNT` shard c Phase 1 depends upon all of the constants defined in [Phase 0](0_beacon-chain.md#constants) in addition to the following: -| Constant | Value | Unit | Approximation | -|-----------------------------|------------------|--------|---------------| -| `SHARD_CHUNK_SIZE` | 2**5 (= 32) | bytes | | -| `SHARD_BLOCK_SIZE` | 2**14 (= 16,384) | bytes | | -| `PROPOSAL_RESHUFFLE_PERIOD` | 2**11 (= 2,048) | epochs | 9 days | +| Constant | Value | Unit | Approximation | +|-------------------------------|------------------|--------|---------------| +| `SHARD_CHUNK_SIZE` | 2**5 (= 32) | bytes | | +| `SHARD_BLOCK_SIZE` | 2**14 (= 16,384) | bytes | | +| `PERSISTENT_COMMITTEE_PERIOD` | 2**11 (= 2,048) | epochs | 9 days | ### Flags, domains, etc. @@ -73,21 +73,21 @@ To validate a block header on shard `shard_id`, compute as follows: * Let `proposer_index = hash(state.randao_mix + int_to_bytes8(shard_id) + int_to_bytes8(slot)) % len(validators)`. Let `msg` be the block but with the `block.signature` set to `[0, 0]`. Verify that `BLSVerify(pub=validators[proposer_index].pubkey, msg=hash(msg), sig=block.signature, domain=get_domain(state, slot, SHARD_PROPOSER_DOMAIN))` passes. * Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, message_hash=parent_root, sig=block.aggregate_signature, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. -We define the helper `get_proposal_committee` as follows: +We define the helper `get_persistent_committee` as follows: ```python -def get_proposal_committee(seed: Bytes32, - validators: List[Validator], - shard: ShardNumber, - epoch: EpochNumber) -> List[ValidatorIndex]: +def get_persistent_commmitee(seed: Bytes32, + validators: List[Validator], + shard: ShardNumber, + epoch: EpochNumber) -> List[ValidatorIndex]: - earlier_committee_start = epoch - (epoch % PROPOSAL_RESHUFFLE_PERIOD) - PROPOSAL_RESHUFFLE_PERIOD * 2 + earlier_committee_start = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD * 2 earlier_committee = split(shuffle( get_active_validator_indices(validators, earlier_committee_start), generate_seed(state, earlier_committee_start) ), SHARD_COUNT)[shard] - later_committee_start = epoch - (epoch % PROPOSAL_RESHUFFLE_PERIOD) - PROPOSAL_RESHUFFLE_PERIOD + later_committee_start = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD later_committee = split(shuffle( get_active_validator_indices(validators, later_committee_start), generate_seed(state, later_committee_start) @@ -96,12 +96,12 @@ def get_proposal_committee(seed: Bytes32, def get_switchover_epoch(index): return ( bytes_to_int(hash(generate_seed(state, earlier_committee_start) + bytes3(index))[0:8]) % - PROPOSAL_RESHUFFLE_PERIOD + PERSISTENT_COMMITTEE_PERIOD ) return ( - [i for i in earlier_committee if epoch % PROPOSAL_RESHUFFLE_PERIOD < get_switchover_epoch(i)] + - [i for i in later_committee if epoch % PROPOSAL_RESHUFFLE_PERIOD >= get_switchover_epoch(i)] + [i for i in earlier_committee if epoch % PERSISTENT_COMMITTEE_PERIOD < get_switchover_epoch(i)] + + [i for i in later_committee if epoch % PERSISTENT_COMMITTEE_PERIOD >= get_switchover_epoch(i)] ) ``` From 47c396f3f8839644db5337922c1d31468d3cba6c Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 8 Feb 2019 03:50:00 -0600 Subject: [PATCH 16/70] Break lets into separate lines --- specs/core/1_shard-data-chains.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 1224449e3..3fa49697c 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -68,7 +68,8 @@ To validate a block header on shard `shard_id`, compute as follows: * Verify that `beacon_chain_ref` is the hash of a block in the (canonical) beacon chain with slot less than or equal to `slot`. * Verify that `beacon_chain_ref` is equal to or a descendant of the `beacon_chain_ref` specified in the `ShardBlock` pointed to by `parent_root`. -* Let `state` be the state of the beacon chain block referred to by `beacon_chain_ref`. Let `persistent_committee` be `[persistent_committee[i] for i in get_persistent_committee(state, slot, shard_id)`. +* Let `state` be the state of the beacon chain block referred to by `beacon_chain_ref`. +* Let `persistent_committee` be `[persistent_committee[i] for i in get_persistent_committee(state, slot, shard_id)`. * Assert `verify_bitfield(participation_bitfield, len(persistent_committee))` * Let `proposer_index = hash(state.randao_mix + int_to_bytes8(shard_id) + int_to_bytes8(slot)) % len(validators)`. Let `msg` be the block but with the `block.signature` set to `[0, 0]`. Verify that `BLSVerify(pub=validators[proposer_index].pubkey, msg=hash(msg), sig=block.signature, domain=get_domain(state, slot, SHARD_PROPOSER_DOMAIN))` passes. * Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, message_hash=parent_root, sig=block.aggregate_signature, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. From 65ec418213c025d64d0d498dab964950dedf4fba Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 8 Feb 2019 03:54:02 -0600 Subject: [PATCH 17/70] A few more cleanups --- specs/core/1_shard-data-chains.md | 93 ++++++++++++++++--------------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 3fa49697c..8b6d4ad5f 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -29,52 +29,9 @@ Phase 1 depends upon all of the constants defined in [Phase 0](0_beacon-chain.md | `SHARD_PROPOSER_DOMAIN`| 129 | | `SHARD_ATTESTER_DOMAIN`| 130 | -## Data Structures +## Helper functions -### Shard chain blocks - -A `ShardBlock` object has the following fields: - -```python -{ - # Slot number - 'slot': 'uint64', - # What shard is it on - 'shard_id': 'uint64', - # Parent block's root - 'parent_root': 'bytes32', - # Beacon chain block - 'beacon_chain_ref': 'bytes32', - # Merkle root of data - 'data_root': 'bytes32' - # State root (placeholder for now) - 'state_root': 'bytes32', - # Block signature - 'signature': 'bytes96', - # Attestation - 'participation_bitfield': 'bytes', - 'aggregate_signature': 'bytes96', -} -``` - -## Shard block processing - -For a block on a shard to be processed by a node, the following conditions must be met: - -* The `ShardBlock` pointed to by `parent_root` has already been processed and accepted -* The signature for the block from the _proposer_ (see below for definition) of that block is included along with the block in the network message object - -To validate a block header on shard `shard_id`, compute as follows: - -* Verify that `beacon_chain_ref` is the hash of a block in the (canonical) beacon chain with slot less than or equal to `slot`. -* Verify that `beacon_chain_ref` is equal to or a descendant of the `beacon_chain_ref` specified in the `ShardBlock` pointed to by `parent_root`. -* Let `state` be the state of the beacon chain block referred to by `beacon_chain_ref`. -* Let `persistent_committee` be `[persistent_committee[i] for i in get_persistent_committee(state, slot, shard_id)`. -* Assert `verify_bitfield(participation_bitfield, len(persistent_committee))` -* Let `proposer_index = hash(state.randao_mix + int_to_bytes8(shard_id) + int_to_bytes8(slot)) % len(validators)`. Let `msg` be the block but with the `block.signature` set to `[0, 0]`. Verify that `BLSVerify(pub=validators[proposer_index].pubkey, msg=hash(msg), sig=block.signature, domain=get_domain(state, slot, SHARD_PROPOSER_DOMAIN))` passes. -* Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, message_hash=parent_root, sig=block.aggregate_signature, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. - -We define the helper `get_persistent_committee` as follows: +#### get_persistent_committee ```python def get_persistent_commmitee(seed: Bytes32, @@ -106,6 +63,52 @@ def get_persistent_commmitee(seed: Bytes32, ) ``` +## Data Structures + +### Shard chain blocks + +A `ShardBlock` object has the following fields: + +```python +{ + # Slot number + 'slot': 'uint64', + # What shard is it on + 'shard_id': 'uint64', + # Parent block's root + 'parent_root': 'bytes32', + # Beacon chain block + 'beacon_chain_ref': 'bytes32', + # Merkle root of data + 'data_root': 'bytes32' + # State root (placeholder for now) + 'state_root': 'bytes32', + # Block signature + 'signature': 'bytes96', + # Attestation + 'participation_bitfield': 'bytes', + 'aggregate_signature': 'bytes96', +} +``` + +## Shard block processing + +For a `shard_block` on a shard to be processed by a node, the following conditions must be met: + +* The `ShardBlock` pointed to by `shard_block.parent_root` has already been processed and accepted +* The signature for the block from the _proposer_ (see below for definition) of that block is included along with the block in the network message object + +To validate a block header on shard `shard_block.shard_id`, compute as follows: + +* Verify that `shard_block.beacon_chain_ref` is the hash of a block in the (canonical) beacon chain with slot less than or equal to `slot`. +* Verify that `shard_block.beacon_chain_ref` is equal to or a descendant of the `shard_block.beacon_chain_ref` specified in the `ShardBlock` pointed to by `shard_block.parent_root`. +* Let `state` be the state of the beacon chain block referred to by `shard_block.beacon_chain_ref`. +* Let `persistent_committee` be `[persistent_committee[i] for i in get_persistent_committee(state, shard_block.slot, shard_block.shard_id)`. +* Assert `verify_bitfield(shard_block.participation_bitfield, len(persistent_committee))` +* Let `proposer_index = hash(state.randao_mix + int_to_bytes8(shard_block.shard_id) + int_to_bytes8(shard_block.slot)) % len(validators)`. Let `msg` be the `shard_block` but with `shard_block.signature` set to `[0, 0]`. Verify that `bls_verify(pubkey=validators[proposer_index].pubkey, message_hash=hash(msg), signature=shard_block.signature, domain=get_domain(state, shard_block.slot, SHARD_PROPOSER_DOMAIN))` passes. +* Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(shard_block.participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, message_hash=shard_block.parent_root, sig=shard_block.aggregate_signature, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. + + ### Verifying shard block data At network layer, we expect a shard block header to be broadcast along with its `block_body`. From e5bd78e5e659208f89bce16c9a0aa4f82cd03fae Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 8 Feb 2019 04:08:52 -0600 Subject: [PATCH 18/70] Persistent committee refactors --- specs/core/1_shard-data-chains.md | 38 +++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 8b6d4ad5f..b4bbf3a7e 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -31,6 +31,17 @@ Phase 1 depends upon all of the constants defined in [Phase 0](0_beacon-chain.md ## Helper functions +#### get_split_offset + +````python +def get_split_offset(list_size: int, chunks: int, index: int) -> int: + """ + Returns a value such that for a list L, chunk count k and index i, + split(L, k)[i] == L[get_split_offset(len(L), k, i): get_split_offset(len(L), k+1, i)] + """ + return (len(list_size) * index) // chunks +```` + #### get_persistent_committee ```python @@ -40,20 +51,28 @@ def get_persistent_commmitee(seed: Bytes32, epoch: EpochNumber) -> List[ValidatorIndex]: earlier_committee_start = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD * 2 - earlier_committee = split(shuffle( - get_active_validator_indices(validators, earlier_committee_start), - generate_seed(state, earlier_committee_start) - ), SHARD_COUNT)[shard] + earlier_validator_set = get_active_validator_indices(validators, earlier_committee_start) + earlier_seed = generate_seed(state, earlier_committee_start) + earlier_start_offset = get_split_offset(len(earlier_validator_set), SHARD_COUNT, shard) + earlier_end_offset = get_split_offset(len(earlier_validator_set), SHARD_COUNT, shard+1) + earlier_committee = [ + earlier_validator_set[get_permuted_index(i, len(earlier_validator_set), earlier_seed)] + for i in range(earlier_start_offset, earlier_end_offset) + ] later_committee_start = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD - later_committee = split(shuffle( - get_active_validator_indices(validators, later_committee_start), - generate_seed(state, later_committee_start) - ), SHARD_COUNT)[shard] + later_validator_set = get_active_validator_indices(validators, later_committee_start) + later_seed = generate_seed(state, later_committee_start) + later_start_offset = get_split_offset(len(later_validator_set), SHARD_COUNT, shard) + later_end_offset = get_split_offset(len(later_validator_set), SHARD_COUNT, shard+1) + later_committee = [ + later_validator_set[get_permuted_index(i, len(later_validator_set), later_seed)] + for i in range(later_start_offset, later_end_offset) + ] def get_switchover_epoch(index): return ( - bytes_to_int(hash(generate_seed(state, earlier_committee_start) + bytes3(index))[0:8]) % + bytes_to_int(hash(earlier_seed + bytes3(index))[0:8]) % PERSISTENT_COMMITTEE_PERIOD ) @@ -108,7 +127,6 @@ To validate a block header on shard `shard_block.shard_id`, compute as follows: * Let `proposer_index = hash(state.randao_mix + int_to_bytes8(shard_block.shard_id) + int_to_bytes8(shard_block.slot)) % len(validators)`. Let `msg` be the `shard_block` but with `shard_block.signature` set to `[0, 0]`. Verify that `bls_verify(pubkey=validators[proposer_index].pubkey, message_hash=hash(msg), signature=shard_block.signature, domain=get_domain(state, shard_block.slot, SHARD_PROPOSER_DOMAIN))` passes. * Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(shard_block.participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, message_hash=shard_block.parent_root, sig=shard_block.aggregate_signature, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. - ### Verifying shard block data At network layer, we expect a shard block header to be broadcast along with its `block_body`. From f9eaab1d044e2da9854217099b4bb8325388740f Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 8 Feb 2019 18:04:32 +0300 Subject: [PATCH 19/70] Silently skip deposits with invalid proof in process_deposit --- specs/core/0_beacon-chain.md | 49 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 21a991c42..c08343ef9 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1252,36 +1252,37 @@ def process_deposit(state: BeaconState, Note that this function mutates ``state``. """ # Validate the given `proof_of_possession` - assert validate_proof_of_possession( + valid_proof = validate_proof_of_possession( state, pubkey, proof_of_possession, withdrawal_credentials, ) - validator_pubkeys = [v.pubkey for v in state.validator_registry] - - if pubkey not in validator_pubkeys: - # Add new validator - validator = Validator( - pubkey=pubkey, - withdrawal_credentials=withdrawal_credentials, - activation_epoch=FAR_FUTURE_EPOCH, - exit_epoch=FAR_FUTURE_EPOCH, - withdrawal_epoch=FAR_FUTURE_EPOCH, - penalized_epoch=FAR_FUTURE_EPOCH, - status_flags=0, - ) - - # Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled. - state.validator_registry.append(validator) - state.validator_balances.append(amount) - else: - # Increase balance by deposit amount - index = validator_pubkeys.index(pubkey) - assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials - - state.validator_balances[index] += amount + if valid_proof: + validator_pubkeys = [v.pubkey for v in state.validator_registry] + + if pubkey not in validator_pubkeys: + # Add new validator + validator = Validator( + pubkey=pubkey, + withdrawal_credentials=withdrawal_credentials, + activation_epoch=FAR_FUTURE_EPOCH, + exit_epoch=FAR_FUTURE_EPOCH, + withdrawal_epoch=FAR_FUTURE_EPOCH, + penalized_epoch=FAR_FUTURE_EPOCH, + status_flags=0, + ) + + # Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled. + state.validator_registry.append(validator) + state.validator_balances.append(amount) + else: + # Increase balance by deposit amount + index = validator_pubkeys.index(pubkey) + assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials + + state.validator_balances[index] += amount ``` ### Routines for updating validator status From 057364a7d4552334060256679b89f43dd38dec10 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 8 Feb 2019 19:37:34 -0600 Subject: [PATCH 20/70] Add mandatory deposit index ordering Co-requisite with #589 --- specs/core/0_beacon-chain.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ebc12324c..78388cad2 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -526,6 +526,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git # Ethereum 1.0 chain data 'latest_eth1_data': Eth1Data, 'eth1_data_votes': [Eth1DataVote], + 'deposit_count': 'uint64' } ``` @@ -1717,6 +1718,7 @@ Verify that `len(block.body.deposits) <= MAX_DEPOSITS`. For each `deposit` in `block.body.deposits`: * Let `serialized_deposit_data` be the serialized form of `deposit.deposit_data`. It should be 8 bytes for `deposit_data.amount` followed by 8 bytes for `deposit_data.timestamp` and then the `DepositInput` bytes. That is, it should match `deposit_data` in the [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) of which the hash was placed into the Merkle tree. +* Verify that `deposit.index == state.deposit_index`. * Verify that `verify_merkle_branch(hash(serialized_deposit_data), deposit.branch, DEPOSIT_CONTRACT_TREE_DEPTH, deposit.index, state.latest_eth1_data.deposit_root)` is `True`. ```python @@ -1745,6 +1747,8 @@ process_deposit( ) ``` +* Set `state.deposit_index += 1`. + ##### Exits Verify that `len(block.body.exits) <= MAX_EXITS`. From 36d5120deb20d24546c8877aea212b6e961df0e3 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 8 Feb 2019 19:38:09 -0600 Subject: [PATCH 21/70] Initialize deposit index --- specs/core/0_beacon-chain.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 78388cad2..a7e2b6286 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1479,6 +1479,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], # Ethereum 1.0 chain data latest_eth1_data=latest_eth1_data, eth1_data_votes=[], + deposit_index=0 ) # Process initial deposits From 019fe8953e6aabd2176f93ab00b96904966882ca Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 8 Feb 2019 22:02:55 -0600 Subject: [PATCH 22/70] Fixed as per @djrtwo's comments --- 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 a7e2b6286..da2cce788 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -526,7 +526,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git # Ethereum 1.0 chain data 'latest_eth1_data': Eth1Data, 'eth1_data_votes': [Eth1DataVote], - 'deposit_count': 'uint64' + 'deposit_index': 'uint64' } ``` @@ -1479,7 +1479,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], # Ethereum 1.0 chain data latest_eth1_data=latest_eth1_data, eth1_data_votes=[], - deposit_index=0 + deposit_index=len(initial_validator_deposits) ) # Process initial deposits From ad0ff80be2b0f3682abf4134bc1660a566790c46 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 8 Feb 2019 22:06:03 -0600 Subject: [PATCH 23/70] Update specs/core/1_shard-data-chains.md Co-Authored-By: vbuterin --- specs/core/1_shard-data-chains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index b4bbf3a7e..e910e24ac 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -122,7 +122,7 @@ To validate a block header on shard `shard_block.shard_id`, compute as follows: * Verify that `shard_block.beacon_chain_ref` is the hash of a block in the (canonical) beacon chain with slot less than or equal to `slot`. * Verify that `shard_block.beacon_chain_ref` is equal to or a descendant of the `shard_block.beacon_chain_ref` specified in the `ShardBlock` pointed to by `shard_block.parent_root`. * Let `state` be the state of the beacon chain block referred to by `shard_block.beacon_chain_ref`. -* Let `persistent_committee` be `[persistent_committee[i] for i in get_persistent_committee(state, shard_block.slot, shard_block.shard_id)`. +* Let `persistent_committee = get_persistent_committee(state, shard_block.slot, shard_block.shard_id)`. * Assert `verify_bitfield(shard_block.participation_bitfield, len(persistent_committee))` * Let `proposer_index = hash(state.randao_mix + int_to_bytes8(shard_block.shard_id) + int_to_bytes8(shard_block.slot)) % len(validators)`. Let `msg` be the `shard_block` but with `shard_block.signature` set to `[0, 0]`. Verify that `bls_verify(pubkey=validators[proposer_index].pubkey, message_hash=hash(msg), signature=shard_block.signature, domain=get_domain(state, shard_block.slot, SHARD_PROPOSER_DOMAIN))` passes. * Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(shard_block.participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, message_hash=shard_block.parent_root, sig=shard_block.aggregate_signature, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. From 8dd111b7e62ba768c54d2af875dadb442303b103 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 8 Feb 2019 22:06:46 -0600 Subject: [PATCH 24/70] Update specs/core/1_shard-data-chains.md Co-Authored-By: vbuterin --- specs/core/1_shard-data-chains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index e910e24ac..9362d537e 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -124,7 +124,7 @@ To validate a block header on shard `shard_block.shard_id`, compute as follows: * Let `state` be the state of the beacon chain block referred to by `shard_block.beacon_chain_ref`. * Let `persistent_committee = get_persistent_committee(state, shard_block.slot, shard_block.shard_id)`. * Assert `verify_bitfield(shard_block.participation_bitfield, len(persistent_committee))` -* Let `proposer_index = hash(state.randao_mix + int_to_bytes8(shard_block.shard_id) + int_to_bytes8(shard_block.slot)) % len(validators)`. Let `msg` be the `shard_block` but with `shard_block.signature` set to `[0, 0]`. Verify that `bls_verify(pubkey=validators[proposer_index].pubkey, message_hash=hash(msg), signature=shard_block.signature, domain=get_domain(state, shard_block.slot, SHARD_PROPOSER_DOMAIN))` passes. +* Let `proposer_index = bytes_to_int(hash(state.current_epoch_seed + int_to_bytes8(shard_block.shard_id) + int_to_bytes8(shard_block.slot))[0:8]) % len(validators)`. Let `msg` be the `shard_block` but with `shard_block.signature` set to `[0, 0]`. Verify that `bls_verify(pubkey=validators[proposer_index].pubkey, message_hash=hash(msg), signature=shard_block.signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_PROPOSER_DOMAIN))` passes. * Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(shard_block.participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, message_hash=shard_block.parent_root, sig=shard_block.aggregate_signature, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. ### Verifying shard block data From e0d8cf42688b1011121cbc5f3b586e0513ab507c Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 8 Feb 2019 22:07:14 -0600 Subject: [PATCH 25/70] Update specs/core/1_shard-data-chains.md Co-Authored-By: vbuterin --- specs/core/1_shard-data-chains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 9362d537e..2752765c5 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -125,7 +125,7 @@ To validate a block header on shard `shard_block.shard_id`, compute as follows: * Let `persistent_committee = get_persistent_committee(state, shard_block.slot, shard_block.shard_id)`. * Assert `verify_bitfield(shard_block.participation_bitfield, len(persistent_committee))` * Let `proposer_index = bytes_to_int(hash(state.current_epoch_seed + int_to_bytes8(shard_block.shard_id) + int_to_bytes8(shard_block.slot))[0:8]) % len(validators)`. Let `msg` be the `shard_block` but with `shard_block.signature` set to `[0, 0]`. Verify that `bls_verify(pubkey=validators[proposer_index].pubkey, message_hash=hash(msg), signature=shard_block.signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_PROPOSER_DOMAIN))` passes. -* Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(shard_block.participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, message_hash=shard_block.parent_root, sig=shard_block.aggregate_signature, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. +* Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(shard_block.participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, message_hash=shard_block.parent_root, sig=shard_block.aggregate_signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_ATTESTER_DOMAIN))` passes. ### Verifying shard block data From 65c5a1a1b76d112e0d6ec2f7ac6fe1bf61ec6e27 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 8 Feb 2019 22:10:54 -0600 Subject: [PATCH 26/70] Fixed get_persistent_committee --- specs/core/1_shard-data-chains.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 2752765c5..132a3b7bc 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -45,13 +45,15 @@ def get_split_offset(list_size: int, chunks: int, index: int) -> int: #### get_persistent_committee ```python -def get_persistent_commmitee(seed: Bytes32, - validators: List[Validator], - shard: ShardNumber, - epoch: EpochNumber) -> List[ValidatorIndex]: +def get_persistent_commmitee(state: BeaconState, + shard: ShardNumber, + epoch: EpochNumber) -> List[ValidatorIndex]: + """ + Returns the persistent committee for the given shard at the given epoch + """ earlier_committee_start = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD * 2 - earlier_validator_set = get_active_validator_indices(validators, earlier_committee_start) + earlier_validator_set = get_active_validator_indices(state.validators, earlier_committee_start) earlier_seed = generate_seed(state, earlier_committee_start) earlier_start_offset = get_split_offset(len(earlier_validator_set), SHARD_COUNT, shard) earlier_end_offset = get_split_offset(len(earlier_validator_set), SHARD_COUNT, shard+1) @@ -61,7 +63,7 @@ def get_persistent_commmitee(seed: Bytes32, ] later_committee_start = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD - later_validator_set = get_active_validator_indices(validators, later_committee_start) + later_validator_set = get_active_validator_indices(state.validators, later_committee_start) later_seed = generate_seed(state, later_committee_start) later_start_offset = get_split_offset(len(later_validator_set), SHARD_COUNT, shard) later_end_offset = get_split_offset(len(later_validator_set), SHARD_COUNT, shard+1) From 0157aa039cbef094facb32d35aa5c5feb1297325 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 9 Feb 2019 11:10:03 +0300 Subject: [PATCH 27/70] Change var name to avoid confusing the reader into thinking the `valid_proof` variable contains a valid proof as opposed to just being a bool. Co-Authored-By: Nashatyrev --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index c08343ef9..604bab0f0 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1252,7 +1252,7 @@ def process_deposit(state: BeaconState, Note that this function mutates ``state``. """ # Validate the given `proof_of_possession` - valid_proof = validate_proof_of_possession( + proof_is_valid = validate_proof_of_possession( state, pubkey, proof_of_possession, From e48010b77b9604104500e44e2100909d4575e0be Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 9 Feb 2019 11:10:54 +0300 Subject: [PATCH 28/70] Change var name to avoid confusing the reader into thinking the `valid_proof` variable contains a valid proof as opposed to just being a bool. Co-Authored-By: Nashatyrev --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 604bab0f0..03fa7c801 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1259,7 +1259,7 @@ def process_deposit(state: BeaconState, withdrawal_credentials, ) - if valid_proof: + if proof_is_valid: validator_pubkeys = [v.pubkey for v in state.validator_registry] if pubkey not in validator_pubkeys: From 7886d9618606cfad88c5c8f9df2b99feecb1b79a Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 9 Feb 2019 07:21:38 -0800 Subject: [PATCH 29/70] change proof_is_valid to exit condition --- specs/core/0_beacon-chain.md | 50 +++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 03fa7c801..d53ebc901 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1259,30 +1259,32 @@ def process_deposit(state: BeaconState, withdrawal_credentials, ) - if proof_is_valid: - validator_pubkeys = [v.pubkey for v in state.validator_registry] - - if pubkey not in validator_pubkeys: - # Add new validator - validator = Validator( - pubkey=pubkey, - withdrawal_credentials=withdrawal_credentials, - activation_epoch=FAR_FUTURE_EPOCH, - exit_epoch=FAR_FUTURE_EPOCH, - withdrawal_epoch=FAR_FUTURE_EPOCH, - penalized_epoch=FAR_FUTURE_EPOCH, - status_flags=0, - ) - - # Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled. - state.validator_registry.append(validator) - state.validator_balances.append(amount) - else: - # Increase balance by deposit amount - index = validator_pubkeys.index(pubkey) - assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials - - state.validator_balances[index] += amount + if not proof_is_valid: + return + + validator_pubkeys = [v.pubkey for v in state.validator_registry] + + if pubkey not in validator_pubkeys: + # Add new validator + validator = Validator( + pubkey=pubkey, + withdrawal_credentials=withdrawal_credentials, + activation_epoch=FAR_FUTURE_EPOCH, + exit_epoch=FAR_FUTURE_EPOCH, + withdrawal_epoch=FAR_FUTURE_EPOCH, + penalized_epoch=FAR_FUTURE_EPOCH, + status_flags=0, + ) + + # Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled. + state.validator_registry.append(validator) + state.validator_balances.append(amount) + else: + # Increase balance by deposit amount + index = validator_pubkeys.index(pubkey) + assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials + + state.validator_balances[index] += amount ``` ### Routines for updating validator status From 8097b2373b1d0462ddc02309ceff4a9db958d23d Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 10 Feb 2019 00:09:34 -0600 Subject: [PATCH 30/70] Added deduplication --- specs/core/1_shard-data-chains.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 132a3b7bc..5213f7da7 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -77,11 +77,13 @@ def get_persistent_commmitee(state: BeaconState, bytes_to_int(hash(earlier_seed + bytes3(index))[0:8]) % PERSISTENT_COMMITTEE_PERIOD ) - - return ( + + # Take not-yet-cycled-out validators from earlier committee and already-cycled-in validators from + # later committee; return a sorted list of the union of the two, deduplicated + return sorted(list(set( [i for i in earlier_committee if epoch % PERSISTENT_COMMITTEE_PERIOD < get_switchover_epoch(i)] + [i for i in later_committee if epoch % PERSISTENT_COMMITTEE_PERIOD >= get_switchover_epoch(i)] - ) + ))) ``` ## Data Structures From abed5ffdaef901c49dcc51eeb4b1df6d6d7d8b38 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 10 Feb 2019 10:16:22 -0600 Subject: [PATCH 31/70] Updated phase 1: fork choice rule (#586) Adds the crosslink committee to the fork choice rule. This is useful because it means that even if a proposal committee is byzantine and attempts to prevent a crosslink via a "balance attack" (alternating between chain A and chain B being the canonical chain), the crosslink committee can force the equilibrium to flip to one side or the other. --- specs/core/1_shard-data-chains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index cabe2934e..a8d19ddd3 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -121,4 +121,4 @@ Verify that the `shard_block_combined_data_root` is the output of these function ### Shard block fork choice rule -The fork choice rule for any shard is LMD GHOST using the validators currently assigned to that shard, but instead of being rooted in the genesis it is rooted in the block referenced in the most recent accepted crosslink (ie. `state.crosslinks[shard].shard_block_root`). Only blocks whose `beacon_chain_ref` is the block in the main beacon chain at the specified `slot` should be considered (if the beacon chain skips a slot, then the block at that slot is considered to be the block in the beacon chain at the highest slot lower than a slot). +The fork choice rule for any shard is LMD GHOST using the shard chain attestations of the persistent committee and the beacon chain attestations of the crosslink committee currently assigned to that shard, but instead of being rooted in the genesis it is rooted in the block referenced in the most recent accepted crosslink (ie. `state.crosslinks[shard].shard_block_root`). Only blocks whose `beacon_chain_ref` is the block in the main beacon chain at the specified `slot` should be considered (if the beacon chain skips a slot, then the block at that slot is considered to be the block in the beacon chain at the highest slot lower than a slot). From 1f97206dcf843613b43f6b99a32dc29cb9cb7bcf Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 10 Feb 2019 10:17:21 -0600 Subject: [PATCH 32/70] Updated phase 1: commitments (#579) See #338 and #529 for discussion. --- specs/core/1_shard-data-chains.md | 40 +++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index a8d19ddd3..352b89b40 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -20,6 +20,7 @@ Phase 1 depends upon all of the constants defined in [Phase 0](0_beacon-chain.md |------------------------|-----------------|-------|---------------| | `SHARD_CHUNK_SIZE` | 2**5 (= 32) | bytes | | | `SHARD_BLOCK_SIZE` | 2**14 (= 16384) | bytes | | +| `CROSSLINK_LOOKBACK` | 2**5 (= 32) | slots | | ### Flags, domains, etc. @@ -98,26 +99,39 @@ A node should sign a crosslink only if the following conditions hold. **If a nod First, the conditions must recursively apply to the crosslink referenced in `last_crosslink_root` for the same shard (unless `last_crosslink_root` equals zero, in which case we are at the genesis). -Second, we verify the `shard_block_combined_data_root`. Let `h` be the slot _immediately after_ the slot of the shard block included by the last crosslink, and `h+n-1` be the slot number of the block directly referenced by the current `shard_block_root`. Let `B[i]` be the block at slot `h+i` in the shard chain. Let `bodies[0] .... bodies[n-1]` be the bodies of these blocks and `roots[0] ... roots[n-1]` the data roots. If there is a missing slot in the shard chain at position `h+i`, then `bodies[i] == b'\x00' * shard_block_maxbytes(state[i])` and `roots[i]` be the Merkle root of the empty data. Define `compute_merkle_root` be a simple Merkle root calculating function that takes as input a list of objects, where the list's length must be an exact power of two. We define the function for computing the combined data root as follows: +Second, we verify the `shard_chain_commitment`. +* Let `start_slot = state.latest_crosslinks[shard].epoch * EPOCH_LENGTH + EPOCH_LENGTH - CROSSLINK_LOOKBACK`. +* Let `end_slot = attestation.data.slot - attestation.data.slot % EPOCH_LENGTH - CROSSLINK_LOOKBACK`. +* Let `length = end_slot - start_slot`, `headers[0] .... headers[length-1]` be the serialized block headers in the canonical shard chain from the verifer's point of view (note that this implies that `headers` and `bodies` have been checked for validity). +* Let `bodies[0] ... bodies[length-1]` be the bodies of the blocks. +* Note: If there is a missing slot, then the header and body are the same as that of the block at the most recent slot that has a block. + +We define two helpers: ```python -ZERO_ROOT = merkle_root(bytes([0] * SHARD_BLOCK_SIZE)) - -def mk_combined_data_root(roots): - data = roots + [ZERO_ROOT for _ in range(len(roots), next_power_of_2(len(roots)))] - return compute_merkle_root(data) +def pad_to_power_of_2(values: List[bytes]) -> List[bytes]: + while not is_power_of_two(len(values)): + values = values + [SHARD_BLOCK_SIZE] + return values ``` -This outputs the root of a tree of the data roots, with the data roots all adjusted to have the same height if needed. The tree can also be viewed as a tree of all of the underlying data concatenated together, appropriately padded. Here is an equivalent definition that uses bodies instead of roots [TODO: check equivalence]: - ```python -def mk_combined_data_root(depths, bodies): - data = b''.join(bodies) - data += bytes([0] * (next_power_of_2(len(data)) - len(data)) - return compute_merkle_root([data[pos:pos+SHARD_CHUNK_SIZE] for pos in range(0, len(data), SHARD_CHUNK_SIZE)]) +def merkle_root_of_bytes(data: bytes) -> bytes: + return merkle_root([data[i:i+32] for i in range(0, len(data), 32)]) ``` -Verify that the `shard_block_combined_data_root` is the output of these functions. +We define the function for computing the commitment as follows: + +```python +def compute_commitment(headers: List[ShardBlock], bodies: List[bytes]) -> Bytes32: + return hash( + merkle_root(pad_to_power_of_2([merkle_root_of_bytes(zpad(serialize(h), SHARD_BLOCK_SIZE)) for h in headers])), + merkle_root(pad_to_power_of_2([merkle_root_of_bytes(h) for h in bodies])) + ) +``` + +The `shard_chain_commitment` is only valid if it equals `compute_commitment(headers, bodies)`. + ### Shard block fork choice rule From 2944a7ddfc67ededa4c3ad6f6a4f28b5a18b35ac Mon Sep 17 00:00:00 2001 From: Dean Eigenmann Date: Sun, 10 Feb 2019 17:19:12 +0100 Subject: [PATCH 33/70] Renamed `Helpers` -> `Variables`, seems to make more sense (#560) --- 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 78d24cde3..3c89cf1a9 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -125,7 +125,7 @@ - [Deposits](#deposits-1) - [Exits](#exits-1) - [Per-epoch processing](#per-epoch-processing) - - [Helpers](#helpers) + - [Helper variables](#helper-variables) - [Eth1 data](#eth1-data-1) - [Justification](#justification) - [Crosslinks](#crosslinks) @@ -1770,7 +1770,7 @@ For each `exit` in `block.body.exits`: The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. -#### Helpers +#### Helper variables * Let `current_epoch = get_current_epoch(state)`. * Let `previous_epoch = get_previous_epoch(state)`. From 046119fb2eb4252b4353edd95c70181fb40299e3 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 10 Feb 2019 15:44:58 -0600 Subject: [PATCH 34/70] Update specs/core/1_shard-data-chains.md --- specs/core/1_shard-data-chains.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index a4a67cd8b..c470395df 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -86,7 +86,27 @@ def get_persistent_commmitee(state: BeaconState, [i for i in later_committee if epoch % PERSISTENT_COMMITTEE_PERIOD >= get_switchover_epoch(i)] ))) ``` +#### get_shard_proposer_index +```python +def get_shard_proposer_index(state: BeaconState, + shard: ShardNumber, + slot: SlotNumber) -> ValidatorIndex: + seed = hash( + state.current_epoch_seed + + int_to_bytes8(shard) + + int_to_bytes8(slot) + ) + persistent_committee = get_persistent_committee(state, shard, slot_to_epoch(slot)) + # Default proposer + index = bytes_to_int(seed[0:8]) % len(persistent_committee) + # If default proposer exits, try the other proposers in order; if all are exited + # return None (ie. no block can be proposed) + validators_to_try = persistent_committee[index:] + persistent_committee[:index] + for index in validators_to_try: + if is_active_validator(state.validators[index], get_current_epoch(state)): + return index + return None ## Data Structures ### Shard chain blocks From ad3f43a4a6dfe472cc65fa9a2c5c934e18a45e24 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 10 Feb 2019 15:45:06 -0600 Subject: [PATCH 35/70] Update specs/core/1_shard-data-chains.md Co-Authored-By: vbuterin --- specs/core/1_shard-data-chains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index c470395df..cbbd98048 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -147,7 +147,7 @@ To validate a block header on shard `shard_block.shard_id`, compute as follows: * Verify that `shard_block.beacon_chain_ref` is the hash of a block in the (canonical) beacon chain with slot less than or equal to `slot`. * Verify that `shard_block.beacon_chain_ref` is equal to or a descendant of the `shard_block.beacon_chain_ref` specified in the `ShardBlock` pointed to by `shard_block.parent_root`. * Let `state` be the state of the beacon chain block referred to by `shard_block.beacon_chain_ref`. -* Let `persistent_committee = get_persistent_committee(state, shard_block.slot, shard_block.shard_id)`. +* Let `persistent_committee = get_persistent_committee(state, shard_block.shard_id, slot_to_epoch(shard_block.slot))`. * Assert `verify_bitfield(shard_block.participation_bitfield, len(persistent_committee))` * Let `proposer_index = bytes_to_int(hash(state.current_epoch_seed + int_to_bytes8(shard_block.shard_id) + int_to_bytes8(shard_block.slot))[0:8]) % len(validators)`. Let `msg` be the `shard_block` but with `shard_block.signature` set to `[0, 0]`. Verify that `bls_verify(pubkey=validators[proposer_index].pubkey, message_hash=hash(msg), signature=shard_block.signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_PROPOSER_DOMAIN))` passes. * Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(shard_block.participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, message_hash=shard_block.parent_root, sig=shard_block.aggregate_signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_ATTESTER_DOMAIN))` passes. From 9e66b069b223d714ca3e1da4c2f412f86d895367 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 10 Feb 2019 15:45:16 -0600 Subject: [PATCH 36/70] Update specs/core/1_shard-data-chains.md --- specs/core/1_shard-data-chains.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index cbbd98048..d07c6b1ed 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -149,6 +149,7 @@ To validate a block header on shard `shard_block.shard_id`, compute as follows: * Let `state` be the state of the beacon chain block referred to by `shard_block.beacon_chain_ref`. * Let `persistent_committee = get_persistent_committee(state, shard_block.shard_id, slot_to_epoch(shard_block.slot))`. * Assert `verify_bitfield(shard_block.participation_bitfield, len(persistent_committee))` +* For every `i in range(len(persistent_committee))` where `is_active_validator(state.validators[persistent_committee[i]], get_current_epoch(state))` returns `False`, verify that `get_bitfield_bit(shard_block.participation_bitfield, i) == 0` * Let `proposer_index = bytes_to_int(hash(state.current_epoch_seed + int_to_bytes8(shard_block.shard_id) + int_to_bytes8(shard_block.slot))[0:8]) % len(validators)`. Let `msg` be the `shard_block` but with `shard_block.signature` set to `[0, 0]`. Verify that `bls_verify(pubkey=validators[proposer_index].pubkey, message_hash=hash(msg), signature=shard_block.signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_PROPOSER_DOMAIN))` passes. * Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(shard_block.participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, message_hash=shard_block.parent_root, sig=shard_block.aggregate_signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_ATTESTER_DOMAIN))` passes. From 9bba3362eb18c9bcaff8353c34e254df7c628007 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 10 Feb 2019 15:45:47 -0600 Subject: [PATCH 37/70] Update specs/core/1_shard-data-chains.md --- specs/core/1_shard-data-chains.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index d07c6b1ed..29d47dfed 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -150,7 +150,10 @@ To validate a block header on shard `shard_block.shard_id`, compute as follows: * Let `persistent_committee = get_persistent_committee(state, shard_block.shard_id, slot_to_epoch(shard_block.slot))`. * Assert `verify_bitfield(shard_block.participation_bitfield, len(persistent_committee))` * For every `i in range(len(persistent_committee))` where `is_active_validator(state.validators[persistent_committee[i]], get_current_epoch(state))` returns `False`, verify that `get_bitfield_bit(shard_block.participation_bitfield, i) == 0` -* Let `proposer_index = bytes_to_int(hash(state.current_epoch_seed + int_to_bytes8(shard_block.shard_id) + int_to_bytes8(shard_block.slot))[0:8]) % len(validators)`. Let `msg` be the `shard_block` but with `shard_block.signature` set to `[0, 0]`. Verify that `bls_verify(pubkey=validators[proposer_index].pubkey, message_hash=hash(msg), signature=shard_block.signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_PROPOSER_DOMAIN))` passes. +* Let `proposer_index = get_shard_proposer_index(state, shard_block.shard_id, shard_block.slot)`. +* Verify that `proposer_index` is not `None`. +* Let `msg` be the `shard_block` but with `shard_block.signature` set to `[0, 0]`. +* Verify that `bls_verify(pubkey=validators[proposer_index].pubkey, message_hash=hash(msg), signature=shard_block.signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_PROPOSER_DOMAIN))` passes. * Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(shard_block.participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, message_hash=shard_block.parent_root, sig=shard_block.aggregate_signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_ATTESTER_DOMAIN))` passes. ### Verifying shard block data From ab44cbe380b57afaeb2121307cc4a83ae2f7b77c Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 10 Feb 2019 15:45:57 -0600 Subject: [PATCH 38/70] Update specs/core/1_shard-data-chains.md Co-Authored-By: vbuterin --- specs/core/1_shard-data-chains.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 29d47dfed..0c90e0028 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -154,7 +154,8 @@ To validate a block header on shard `shard_block.shard_id`, compute as follows: * Verify that `proposer_index` is not `None`. * Let `msg` be the `shard_block` but with `shard_block.signature` set to `[0, 0]`. * Verify that `bls_verify(pubkey=validators[proposer_index].pubkey, message_hash=hash(msg), signature=shard_block.signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_PROPOSER_DOMAIN))` passes. -* Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(shard_block.participation_bitfield, i) is True])`. Verify that `bls_verify(pubkey=group_public_key, message_hash=shard_block.parent_root, sig=shard_block.aggregate_signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_ATTESTER_DOMAIN))` passes. +* Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(shard_block.participation_bitfield, i) is True])`. +* Verify that `bls_verify(pubkey=group_public_key, message_hash=shard_block.parent_root, sig=shard_block.aggregate_signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_ATTESTER_DOMAIN))` passes. ### Verifying shard block data From db4f99d8992ab05236ba8ec35c23f5800ad677cf Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 10 Feb 2019 15:47:26 -0600 Subject: [PATCH 39/70] Fixed end of code block --- specs/core/1_shard-data-chains.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 0c90e0028..a4cf36134 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -107,6 +107,8 @@ def get_shard_proposer_index(state: BeaconState, if is_active_validator(state.validators[index], get_current_epoch(state)): return index return None +``` + ## Data Structures ### Shard chain blocks From e8f9dce1d3a11ee7adc99ce0f3cedd8eaf29c196 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 10 Feb 2019 18:38:42 -0700 Subject: [PATCH 40/70] simply reward logic for justified slot from previous epoch --- specs/core/0_beacon-chain.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 3c89cf1a9..d32716295 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1781,22 +1781,19 @@ The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. * Let `current_total_balance = get_total_balance(state, get_active_validator_indices(state.validator_registry, current_epoch))`. * Let `current_epoch_attestations = [a for a in state.latest_attestations if current_epoch == slot_to_epoch(a.data.slot)]`. (Note: this is the set of attestations of slots in the epoch `current_epoch`, _not_ attestations that got included in the chain during the epoch `current_epoch`.) * Validators justifying the epoch boundary block at the start of the current epoch: - * Let `current_epoch_boundary_attestations = [a for a in current_epoch_attestations if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(current_epoch)) and a.data.justified_epoch == state.justified_epoch]`. + * Let `current_epoch_boundary_attestations = [a for a in current_epoch_attestations if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(current_epoch))]`. * Let `current_epoch_boundary_attester_indices` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in current_epoch_boundary_attestations]`. * Let `current_epoch_boundary_attesting_balance = get_total_balance(state, current_epoch_boundary_attester_indices)`. [Validators](#dfn-Validator) attesting during the previous epoch: * Let `previous_total_balance = get_total_balance(state, get_active_validator_indices(state.validator_registry, previous_epoch))`. -* Validators that made an attestation during the previous epoch: +* Validators that made an attestation during the previous epoch, targeting the previous justified slot: * Let `previous_epoch_attestations = [a for a in state.latest_attestations if previous_epoch == slot_to_epoch(a.data.slot)]`. * Let `previous_epoch_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_attestations]`. -* Validators targeting the previous justified slot: - * Let `previous_epoch_justified_attestations = [a for a in current_epoch_attestations + previous_epoch_attestations if a.data.justified_epoch == state.previous_justified_epoch]`. - * Let `previous_epoch_justified_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_justified_attestations]`. - * Let `previous_epoch_justified_attesting_balance = get_total_balance(state, previous_epoch_justified_attester_indices)`. + * Let `previous_epoch_attesting_balance = get_total_balance(state, previous_epoch_attester_indices)`. * Validators justifying the epoch boundary block at the start of the previous epoch: - * Let `previous_epoch_boundary_attestations = [a for a in previous_epoch_justified_attestations if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(previous_epoch))]`. + * Let `previous_epoch_boundary_attestations = [a for a in previous_epoch_attestations if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(previous_epoch))]`. * Let `previous_epoch_boundary_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_boundary_attestations]`. * Let `previous_epoch_boundary_attesting_balance = get_total_balance(state, previous_epoch_boundary_attester_indices)`. * Validators attesting to the expected beacon chain head during the previous epoch: @@ -1870,8 +1867,8 @@ Note: When applying penalties in the following balance recalculations implemente Case 1: `epochs_since_finality <= 4`: * Expected FFG source: - * Any [validator](#dfn-validator) `index` in `previous_epoch_justified_attester_indices` gains `base_reward(state, index) * previous_epoch_justified_attesting_balance // previous_total_balance`. - * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_justified_attester_indices` loses `base_reward(state, index)`. + * Any [validator](#dfn-validator) `index` in `previous_epoch_attester_indices` gains `base_reward(state, index) * previous_epoch_attesting_balance // previous_total_balance`. + * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_attester_indices` loses `base_reward(state, index)`. * Expected FFG target: * Any [validator](#dfn-validator) `index` in `previous_epoch_boundary_attester_indices` gains `base_reward(state, index) * previous_epoch_boundary_attesting_balance // previous_total_balance`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_boundary_attester_indices` loses `base_reward(state, index)`. @@ -1883,7 +1880,7 @@ Case 1: `epochs_since_finality <= 4`: Case 2: `epochs_since_finality > 4`: -* Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_justified_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. +* Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_boundary_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_head_attester_indices`, loses `base_reward(state, index)`. * Any [active_validator](#dfn-active-validator) `index` with `validator.penalized_epoch <= current_epoch`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. From 1d77c57374f7af6cf83097ea753d4e00e08c325f Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 10 Feb 2019 18:43:56 -0700 Subject: [PATCH 41/70] clarify active validator meaning for rewards/penalties --- specs/core/0_beacon-chain.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d32716295..cf9f719ac 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1858,9 +1858,11 @@ First, we define some additional helpers: * Let `base_reward(state, index) = get_effective_balance(state, index) // base_reward_quotient // 5` for any validator with the given `index`. * Let `inactivity_penalty(state, index, epochs_since_finality) = base_reward(state, index) + get_effective_balance(state, index) * epochs_since_finality // INACTIVITY_PENALTY_QUOTIENT // 2` for any validator with the given `index`. +Note: When applying penalties in the following balance recalculations implementers should make sure the `uint64` does not underflow. + ##### Justification and finalization -Note: When applying penalties in the following balance recalculations implementers should make sure the `uint64` does not underflow. +Note: Rewards and penalties are for participation in the previous epoch, so the "active validator" set is drawn from `get_active_validator_indices(state.validator_registry, previous_epoch)`. * Let `epochs_since_finality = next_epoch - state.finalized_epoch`. @@ -1883,7 +1885,7 @@ Case 2: `epochs_since_finality > 4`: * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_boundary_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_head_attester_indices`, loses `base_reward(state, index)`. -* Any [active_validator](#dfn-active-validator) `index` with `validator.penalized_epoch <= current_epoch`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. +* Any [active validator](#dfn-active-validator) `index` with `validator.penalized_epoch <= current_epoch`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. * Any [validator](#dfn-validator) `index` in `previous_epoch_attester_indices` loses `base_reward(state, index) - base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_distance(state, index)` ##### Attestation inclusion From afca1e21278260aba51766ed380abcca42efddea Mon Sep 17 00:00:00 2001 From: Jordan Andrews Date: Mon, 11 Feb 2019 22:01:05 +1100 Subject: [PATCH 42/70] Add C# implementation --- specs/simple-serialize.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index b888ca76f..690974dde 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -452,6 +452,7 @@ return hash(b''.join([hash_tree_root_internal(getattr(x, field)) for field in va | Java | [ https://www.github.com/ConsenSys/cava/tree/master/ssz ](https://www.github.com/ConsenSys/cava/tree/master/ssz) | SSZ Java library part of the Cava suite | | Go | [ https://github.com/prysmaticlabs/prysm/tree/master/shared/ssz ](https://github.com/prysmaticlabs/prysm/tree/master/shared/ssz) | Go implementation of SSZ mantained by Prysmatic Labs | | Swift | [ https://github.com/yeeth/SimpleSerialize.swift ](https://github.com/yeeth/SimpleSerialize.swift) | Swift implementation maintained SSZ | +| C# | [ https://github.com/codingupastorm/csharp-ssz ](https://github.com/codingupastorm/csharp-ssz) | C# implementation maintained SSZ | ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 22755871491913ec9c4ddcf14a7115d520d76c6c Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 11 Feb 2019 13:29:54 +0000 Subject: [PATCH 43/70] Add note on leap seconds Clarify the local time is Unix time and add note on leap seconds. --- 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 3c89cf1a9..dea56cc08 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -167,7 +167,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be * **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, which 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 of `SLOT_DURATION` seconds, during which one proposer has the ability to create a beacon chain block and some attesters have the ability to make attestations +* **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 Casper FFG finalization [[casper-ffg]](#ref-casper-ffg) * **Withdrawal period** - the number of slots between a [validator](#dfn-validator) exit and the [validator](#dfn-validator) balance being withdrawable @@ -1522,7 +1522,7 @@ For a beacon chain block, `block`, to be processed by a node, the following cond * The parent block with root `block.parent_root` has been processed and accepted. * An Ethereum 1.0 block pointed to by the `state.latest_eth1_data.block_hash` has been processed and accepted. -* The node's local clock time is greater than or equal to `state.genesis_time + block.slot * SLOT_DURATION`. +* The node's Unix time is greater than or equal to `state.genesis_time + block.slot * SLOT_DURATION`. (Note that leap seconds mean that slots will occasionally last `SLOT_DURATION + 1` or `SLOT_DURATION - 1` seconds, possibly several times a year.) If these conditions are not met, the client should delay processing the beacon block until the conditions are all satisfied. From 0069241b9c20ddbb30262a23b6bf1a548841eaf2 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 11 Feb 2019 14:49:11 +0000 Subject: [PATCH 44/70] Alias `bytes` to `List[bytes1]` --- specs/simple-serialize.md | 91 ++++++++++----------------------------- 1 file changed, 22 insertions(+), 69 deletions(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 690974dde..65f1a7372 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -9,28 +9,24 @@ deserializing objects and data types. ## ToC * [About](#about) -* [Terminology](#terminology) +* [Variables and Functions](#variables-and-functions) * [Constants](#constants) * [Overview](#overview) + [Serialize/Encode](#serializeencode) - - [uint](#uint) - - [Bool](#bool) - - [Bytes](#bytes) - - [bytesN](#bytesn) - - [bytes](#bytes-1) + - [uintN](#uintn) + - [bool](#bool) + - [bytesN](#bytesn) - [List/Vectors](#listvectors) - [Container](#container) + [Deserialize/Decode](#deserializedecode) - - [uint](#uint-1) - - [Bool](#bool-1) - - [Bytes](#bytes-2) - - [bytesN](#bytesn-1) - - [bytes](#bytes-1) + - [uintN](#uintn-1) + - [bool](#bool-1) + - [bytesN](#bytesn-1) - [List/Vectors](#listvectors-1) - [Container](#container-1) + [Tree Hash](#tree-hash) - [`uint8`..`uint256`, `bool`, `bytes1`..`bytes32`](#uint8uint256-bool-bytes1bytes32) - - [`uint264`..`uintN`, `bytes`, `bytes33`..`bytesN`](#uint264uintn-bytes-bytes33bytesn) + - [`uint264`..`uintN`, `bytes33`..`bytesN`](#uint264uintn-bytes33bytesn) - [List/Vectors](#listvectors-2) - [Container](#container-2) * [Implementations](#implementations) @@ -68,11 +64,11 @@ overhead. ### Serialize/Encode -#### uint +#### uintN | uint Type | Usage | |:---------:|:-----------------------------------------------------------| -| `uintN` | Type of `N` bits unsigned integer, where ``N % 8 == 0``. | +| `uintN` | Type of `N` bits unsigned integer, where ``N % 8 == 0``. | Convert directly to bytes the size of the int. (e.g. ``uint16 = 2 bytes``) @@ -88,7 +84,7 @@ buffer_size = int_size / 8 return value.to_bytes(buffer_size, 'little') ``` -#### Bool +#### bool Convert directly to a single 0x00 or 0x01 byte. @@ -101,18 +97,13 @@ assert(value in (True, False)) return b'\x01' if value is True else b'\x00' ``` -#### Bytes +#### bytesN -| Bytes Type | Usage | -|:---------:|:------------------------------------| -| `bytesN` | Explicit length `N` bytes data. | -| `bytes` | Bytes data with arbitrary length. | - -##### bytesN +A fixed-size byte array. | Checks to perform | Code | |:---------------------------------------|:---------------------| -| Length in bytes is correct for `bytesN` | ``len(value) == N`` | +| Length in bytes is correct for `bytesN` | ``len(value) == N`` | ```python assert(len(value) == N) @@ -120,21 +111,6 @@ assert(len(value) == N) return value ``` -##### bytes -For general `bytes` type: -1. Get the length/number of bytes; Encode into a `4-byte` integer. -2. Append the value to the length and return: ``[ length_bytes ] + [ value_bytes ]`` - -| Check to perform | Code | -|:-------------------------------------|:-----------------------| -| Length of bytes can fit into 4 bytes | ``len(value) < 2**32`` | - -```python -assert(len(value) < 2**32) -byte_length = (len(value)).to_bytes(LENGTH_BYTES, 'little') -return byte_length + value -``` - #### List/Vectors Lists are a collection of elements of the same homogeneous type. @@ -146,6 +122,8 @@ Lists are a collection of elements of the same homogeneous type. 1. Serialize all list elements individually and concatenate them. 2. Prefix the concatenation with its length encoded as a `4-byte` **little-endian** unsigned integer. +We define `bytes` to be a synonym of `List[bytes1]`. + **Example in Python** ```python @@ -168,8 +146,8 @@ A container represents a heterogenous, associative collection of key-value pairs To serialize a container, obtain the list of its field's names in the specified order. For each field name in this list, obtain the corresponding value and serialize it. Tightly pack the complete set of serialized values in the same order as the field names into a buffer. Calculate the size of this buffer of serialized bytes and encode as a `4-byte` **little endian** `uint32`. Prepend the encoded length to the buffer. The result of this concatenation is the final serialized value of the container. -| Check to perform | Code | -|:--------------------------------------------|:----------------------------| +| Check to perform | Code | +|:----------------------------------------------|:----------------------------| | Length of serialized fields fits into 4 bytes | ``len(serialized) < 2**32`` | To serialize: @@ -231,7 +209,7 @@ At the final step, the following checks should be made: |:-------------------------|:-------------------------------------| | Ensure no extra length | `new_index == len(rawbytes)` | -#### uint +#### uintN Convert directly from bytes into integer utilising the number of bytes the same size as the integer length. (e.g. ``uint16 == 2 bytes``) @@ -245,7 +223,7 @@ assert(len(rawbytes) >= new_index) return int.from_bytes(rawbytes[current_index:current_index+byte_length], 'little'), new_index ``` -#### Bool +#### bool Return True if 0x01, False if 0x00. @@ -254,9 +232,7 @@ assert rawbytes in (b'\x00', b'\x01') return True if rawbytes == b'\x01' else False ``` -#### Bytes - -##### bytesN +#### bytesN Return the `N` bytes. @@ -266,28 +242,6 @@ new_index = current_index + N return rawbytes[current_index:current_index+N], new_index ``` -##### bytes - -Get the length of the bytes, return the bytes. - -| Check to perform | code | -|:--------------------------------------------------|:-------------------------------------------------| -| rawbytes has enough left for length | ``len(rawbytes) > current_index + LENGTH_BYTES`` | -| bytes to return not greater than serialized bytes | ``len(rawbytes) > bytes_end `` | - -```python -assert(len(rawbytes) > current_index + LENGTH_BYTES) -bytes_length = int.from_bytes(rawbytes[current_index:current_index + LENGTH_BYTES], 'little') - -bytes_start = current_index + LENGTH_BYTES -bytes_end = bytes_start + bytes_length -new_index = bytes_end - -assert(len(rawbytes) >= bytes_end) - -return rawbytes[bytes_start:bytes_end], new_index -``` - #### List/Vectors Deserialize each element in the list. @@ -295,7 +249,6 @@ Deserialize each element in the list. 2. Loop through deserializing each item in the list until you reach the entire length of the list. - | Check to perform | code | |:------------------------------------------|:----------------------------------------------------------------| | ``rawbytes`` has enough left for length | ``len(rawbytes) > current_index + LENGTH_BYTES`` | @@ -384,7 +337,7 @@ Refer to [the helper function `hash`](https://github.com/ethereum/eth2.0-specs/b Return the serialization of the value. -#### `uint264`..`uintN`, `bytes`, `bytes33`..`bytesN` +#### `uint264`..`uintN`, `bytes33`..`bytesN` Return the hash of the serialization of the value. From 98902d12e33873f4e3aec7dd5e100379ba72934f Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 11 Feb 2019 14:30:36 -0700 Subject: [PATCH 45/70] assert/unhandled exception note to state transition (#606) --- specs/core/0_beacon-chain.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index dea56cc08..0f020be53 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -154,7 +154,7 @@ The primary source of load on the beacon chain is "attestations". Attestations a ## Notation -Code snippets appearing in `this style` are to be interpreted as Python code. Beacon blocks that trigger unhandled Python exceptions (e.g. out-of-range list accesses) and failed asserts are considered invalid. +Code snippets appearing in `this style` are to be interpreted as Python code. ## Terminology @@ -1593,6 +1593,8 @@ We now define the state transition function. At a high level the state transitio The per-slot transitions focus on the slot counter and block roots records updates; the per-block transitions generally focus on verifying aggregate signatures and saving temporary records relating to the per-block activity in the `BeaconState`; the per-epoch transitions focus on the [validator](#dfn-validator) registry, including adjusting balances and activating and exiting [validators](#dfn-validator), as well as processing crosslinks and managing block justification/finalization. +Beacon blocks that trigger unhandled Python exceptions (e.g. out-of-range list accesses) and failed `assert`s during the state transition are considered invalid. + _Note_: If there are skipped slots between a block and its parent block, run the steps in the [per-slot](#per-slot-processing) and [per-epoch](#per-epoch-processing) sections once for each skipped slot and then once for the slot containing the new block. ### Per-slot processing From 3ea0c27be01650d2269e1b7bfb64c8cfc1242de0 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 22:06:26 +1100 Subject: [PATCH 46/70] Minor modification to reduce lines of code (#607) --- specs/core/0_beacon-chain.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0f020be53..11f5007ee 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2030,12 +2030,10 @@ def process_penalties_and_exits(state: BeaconState) -> None: eligible_indices = filter(eligible, all_indices) # Sort in order of exit epoch, and validators that exit within the same epoch exit in order of validator index sorted_indices = sorted(eligible_indices, key=lambda index: state.validator_registry[index].exit_epoch) - withdrawn_so_far = 0 - for index in sorted_indices: - prepare_validator_for_withdrawal(state, index) - withdrawn_so_far += 1 + for withdrawn_so_far, index in enumerate(sorted_indices): if withdrawn_so_far >= MAX_WITHDRAWALS_PER_EPOCH: break + prepare_validator_for_withdrawal(state, index) ``` #### Final updates From de90d4475e30db83669e72b787c408f37b4f9d2a Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Feb 2019 22:09:27 +1100 Subject: [PATCH 47/70] Provide definition for undefined `index` (#608) The `index` variable is not defined and, IMO, it's difficult to discern. --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 11f5007ee..0f0ad8f99 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1900,7 +1900,7 @@ For each `index` in `previous_epoch_attester_indices`, we determine the proposer For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(current_epoch))`: * Let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. -* For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`: +* For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot` and every `index` in `crosslink_committee`: * If `index in attesting_validators(crosslink_committee)`, `state.validator_balances[index] += base_reward(state, index) * total_attesting_balance(crosslink_committee) // get_total_balance(state, crosslink_committee))`. * If `index not in attesting_validators(crosslink_committee)`, `state.validator_balances[index] -= base_reward(state, index)`. From 41e95cf9eaa2d7a75882913093b3ffa60a0fb95e Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 12 Feb 2019 19:11:45 +0800 Subject: [PATCH 48/70] Refactor `get_persistent_committee` (#604) --- specs/core/1_shard-data-chains.md | 52 +++++++++++++++++-------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index a4cf36134..2503c8dad 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -43,42 +43,46 @@ def get_split_offset(list_size: int, chunks: int, index: int) -> int: return (len(list_size) * index) // chunks ```` +#### get_shuffled_committee + +```python +def get_shuffled_committee(state: BeaconState, + shard: ShardNumber, + committee_start_epoch: EpochNumber) -> List[ValidatorIndex]: + """ + Return shuffled committee. + """ + validator_indices = get_active_validator_indices(state.validators, committee_start_epoch) + seed = generate_seed(state, committee_start_epoch) + start_offset = get_split_offset(len(validator_indices), SHARD_COUNT, shard) + end_offset = get_split_offset(len(validator_indices), SHARD_COUNT, shard + 1) + return [ + validator_indices[get_permuted_index(i, len(validator_indices), seed)] + for i in range(start_offset, end_offset) + ] +``` + #### get_persistent_committee ```python -def get_persistent_commmitee(state: BeaconState, +def get_persistent_committee(state: BeaconState, shard: ShardNumber, epoch: EpochNumber) -> List[ValidatorIndex]: """ - Returns the persistent committee for the given shard at the given epoch + Return the persistent committee for the given ``shard`` at the given ``epoch``. """ - - earlier_committee_start = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD * 2 - earlier_validator_set = get_active_validator_indices(state.validators, earlier_committee_start) - earlier_seed = generate_seed(state, earlier_committee_start) - earlier_start_offset = get_split_offset(len(earlier_validator_set), SHARD_COUNT, shard) - earlier_end_offset = get_split_offset(len(earlier_validator_set), SHARD_COUNT, shard+1) - earlier_committee = [ - earlier_validator_set[get_permuted_index(i, len(earlier_validator_set), earlier_seed)] - for i in range(earlier_start_offset, earlier_end_offset) - ] - - later_committee_start = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD - later_validator_set = get_active_validator_indices(state.validators, later_committee_start) - later_seed = generate_seed(state, later_committee_start) - later_start_offset = get_split_offset(len(later_validator_set), SHARD_COUNT, shard) - later_end_offset = get_split_offset(len(later_validator_set), SHARD_COUNT, shard+1) - later_committee = [ - later_validator_set[get_permuted_index(i, len(later_validator_set), later_seed)] - for i in range(later_start_offset, later_end_offset) - ] - + earlier_committee_start_epoch = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD * 2 + earlier_committee = get_shuffled_committee(state, shard, earlier_committee_start_epoch) + + later_committee_start_epoch = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD + later_committee = get_shuffled_committee(state, shard, later_committee_start_epoch) + def get_switchover_epoch(index): return ( bytes_to_int(hash(earlier_seed + bytes3(index))[0:8]) % PERSISTENT_COMMITTEE_PERIOD ) - + # Take not-yet-cycled-out validators from earlier committee and already-cycled-in validators from # later committee; return a sorted list of the union of the two, deduplicated return sorted(list(set( From 9a4f4d53447de9d7998c8fd9d1504b126cccf7e3 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Feb 2019 11:12:29 +0000 Subject: [PATCH 49/70] Fix SSZ merkleisation bug (#602) --- specs/simple-serialize.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 65f1a7372..d4bd9e78d 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -367,13 +367,16 @@ def merkle_hash(lst): # Leave large items alone chunkz = lst - # Tree-hash - while len(chunkz) > 1: - if len(chunkz) % 2 == 1: - chunkz.append(b'\x00' * SSZ_CHUNK_SIZE) + # Merkleise + def next_power_of_2(x): + return 1 if x == 0 else 2**(x - 1).bit_length() + + for i in range(len(chunkz), next_power_of_2(len(chunkz))): + chunkz.append(b'\x00' * SSZ_CHUNK_SIZE) + while len(chunkz) > 1: chunkz = [hash(chunkz[i] + chunkz[i+1]) for i in range(0, len(chunkz), 2)] - # Return hash of root and length data + # Return hash of root and data length return hash(chunkz[0] + datalen) ``` From f871b9a0d1cda472d620a368d8c3fb2cead33c12 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Tue, 12 Feb 2019 12:52:07 +0100 Subject: [PATCH 50/70] Use little endian bit order (#575) Slightly more simple and common to find bit n at position `1 << n` --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0f0ad8f99..03be5ee17 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1077,7 +1077,7 @@ def get_bitfield_bit(bitfield: bytes, i: int) -> int: """ Extract the bit in ``bitfield`` at position ``i``. """ - return (bitfield[i // 8] >> (7 - (i % 8))) % 2 + return (bitfield[i // 8] >> (i % 8)) % 2 ``` ### `verify_bitfield` From de7263c838d947ac188c20abe7720be40220756c Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Feb 2019 12:24:19 +0000 Subject: [PATCH 51/70] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 98 +++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 053177f46..691c58bda 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -19,10 +19,10 @@ - [State list lengths](#state-list-lengths) - [Reward and penalty quotients](#reward-and-penalty-quotients) - [Status flags](#status-flags) - - [Max operations per block](#max-operations-per-block) + - [Max transactions per block](#max-transactions-per-block) - [Signature domains](#signature-domains) - [Data structures](#data-structures) - - [Beacon chain operations](#beacon-chain-operations) + - [Beacon chain transactions](#beacon-chain-transactions) - [Proposer slashings](#proposer-slashings) - [`ProposerSlashing`](#proposerslashing) - [Attester slashings](#attester-slashings) @@ -36,8 +36,8 @@ - [`Deposit`](#deposit) - [`DepositData`](#depositdata) - [`DepositInput`](#depositinput) - - [Exits](#exits) - - [`Exit`](#exit) + - [Voluntary exits](#voluntary-exits) + - [`VoluntaryExit`](#voluntaryexit) - [Beacon chain blocks](#beacon-chain-blocks) - [`BeaconBlock`](#beaconblock) - [`BeaconBlockBody`](#beaconblockbody) @@ -98,15 +98,15 @@ - [`activate_validator`](#activate_validator) - [`initiate_validator_exit`](#initiate_validator_exit) - [`exit_validator`](#exit_validator) - - [`penalize_validator`](#penalize_validator) + - [`slash_validator`](#slash_validator) - [`prepare_validator_for_withdrawal`](#prepare_validator_for_withdrawal) - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) - [Deposit arguments](#deposit-arguments) - [Withdrawal credentials](#withdrawal-credentials) - [`Deposit` logs](#deposit-logs) - - [`Eth2Genesis` log](#chainstart-log) + - [`Eth2Genesis` log](#eth2genesis-log) - [Vyper code](#vyper-code) - - [On startup](#on-startup) + - [On genesis](#on-genesis) - [Beacon chain processing](#beacon-chain-processing) - [Beacon chain fork choice rule](#beacon-chain-fork-choice-rule) - [Beacon chain state transition function](#beacon-chain-state-transition-function) @@ -118,12 +118,12 @@ - [Proposer signature](#proposer-signature) - [RANDAO](#randao) - [Eth1 data](#eth1-data) - - [Operations](#operations) + - [Transactions](#transactions) - [Proposer slashings](#proposer-slashings-1) - [Attester slashings](#attester-slashings-1) - [Attestations](#attestations-1) - [Deposits](#deposits-1) - - [Exits](#exits-1) + - [Voluntary exits](#voluntary-exits-1) - [Per-epoch processing](#per-epoch-processing) - [Helper variables](#helper-variables) - [Eth1 data](#eth1-data-1) @@ -247,7 +247,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | - | - | | `BASE_REWARD_QUOTIENT` | `2**5` (= 32) | | `WHISTLEBLOWER_REWARD_QUOTIENT` | `2**9` (= 512) | -| `INCLUDER_REWARD_QUOTIENT` | `2**3` (= 8) | +| `ATTESTATION_INCLUSION_REWARD_QUOTIENT` | `2**3` (= 8) | | `INACTIVITY_PENALTY_QUOTIENT` | `2**24` (= 16,777,216) | * The `BASE_REWARD_QUOTIENT` parameter dictates the per-epoch reward. It corresponds to ~2.54% annual interest assuming 10 million participating ETH in every epoch. @@ -260,7 +260,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | `INITIATED_EXIT` | `2**0` (= 1) | | `WITHDRAWABLE` | `2**1` (= 2) | -### Max operations per block +### Max transactions per block | Name | Value | | - | - | @@ -268,7 +268,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | `MAX_ATTESTER_SLASHINGS` | `2**0` (= 1) | | `MAX_ATTESTATIONS` | `2**7` (= 128) | | `MAX_DEPOSITS` | `2**4` (= 16) | -| `MAX_EXITS` | `2**4` (= 16) | +| `MAX_VOLUNTARY_EXITS` | `2**4` (= 16) | ### Signature domains @@ -284,7 +284,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. The following data structures are defined as [SimpleSerialize (SSZ)](https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md) objects. -### Beacon chain operations +### Beacon chain transactions #### Proposer slashings @@ -425,9 +425,9 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git } ``` -#### Exits +#### Voluntary exits -##### `Exit` +##### `VoluntaryExit` ```python { @@ -467,7 +467,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'attester_slashings': [AttesterSlashing], 'attestations': [Attestation], 'deposits': [Deposit], - 'exits': [Exit], + 'voluntary_exits': [VoluntaryExit], } ``` @@ -621,7 +621,7 @@ We define the following Python custom types for type hinting and readability: | `Slot` | `uint64` | a slot number | | `Epoch` | `uint64` | an epoch number | | `Shard` | `uint64` | a shard number | -| `ValidatorIndex` | `uint64` | an index in the validator registry | +| `ValidatorIndex` | `uint64` | a validator registry index | | `Gwei` | `uint64` | an amount in Gwei | | `Bytes32` | `bytes32` | 32 bytes of binary data | | `BLSPubkey` | `bytes48` | a BLS12-381 public key | @@ -654,7 +654,7 @@ def slot_to_epoch(slot: Slot) -> Epoch: ### `get_previous_epoch` ```python -def get_previous_epoch(state: BeaconState) -> EpochNumber: +def get_previous_epoch(state: BeaconState) -> Epoch: """` Return the previous epoch of the given ``state``. If the current epoch is ``GENESIS_EPOCH``, return ``GENESIS_EPOCH``. @@ -1335,12 +1335,12 @@ def exit_validator(state: BeaconState, index: ValidatorIndex) -> None: validator.exit_epoch = get_entry_exit_effect_epoch(get_current_epoch(state)) ``` -#### `penalize_validator` +#### `slash_validator` ```python -def penalize_validator(state: BeaconState, index: ValidatorIndex) -> None: +def slash_validator(state: BeaconState, index: ValidatorIndex) -> None: """ - Penalize the validator of the given ``index``. + Slash the validator with index ``index``. Note that this function mutates ``state``. """ exit_validator(state, index) @@ -1389,11 +1389,12 @@ Every Ethereum 1.0 deposit, of size between `MIN_DEPOSIT_AMOUNT` and `MAX_DEPOSI ### `Eth2Genesis` log -When sufficiently many full deposits have been made the deposit contract emits the `Eth2Genesis` log. The beacon chain state may then be initialized by calling the `get_initial_beacon_state` function (defined below) where: +When sufficiently many full deposits have been made the deposit contract emits the `Eth2Genesis` log. The beacon chain state may then be initialized by calling the `get_genesis_beacon_state` function (defined below) where: * `genesis_time` equals `time` in the `Eth2Genesis` log -* `latest_eth1_data.deposit_root` equals `deposit_root` in the `Eth2Genesis` log, and `latest_eth1_data.block_hash` equals the hash of the block that included the log -* `initial_validator_deposits` is a list of `Deposit` objects built according to the `Deposit` logs up to the deposit that triggered the `Eth2Genesis` log, processed in the order in which they were emitted (oldest to newest) +* `latest_eth1_data.deposit_root` equals `deposit_root` in the `Eth2Genesis` log +* `latest_eth1_data.block_hash` equals the hash of the block that included the log +* `genesis_validator_deposits` is a list of `Deposit` objects built according to the `Deposit` logs up to the deposit that triggered the `Eth2Genesis` log, processed in the order in which they were emitted (oldest to newest) ### Vyper code @@ -1407,7 +1408,7 @@ For convenience, we provide the interface to the contract here: * `get_deposit_root() -> bytes32`: returns the current root of the deposit tree * `deposit(bytes[512])`: adds a deposit instance to the deposit tree, incorporating the input argument and the value transferred in the given call. Note: the amount of value transferred *must* be within `MIN_DEPOSIT_AMOUNT` and `MAX_DEPOSIT_AMOUNT`, inclusive. Each of these constants are specified in units of Gwei. -## On startup +## On genesis A valid block with slot `GENESIS_SLOT` (a "genesis block") has the following values. Other validity rules (e.g. requiring a signature) do not apply. @@ -1415,7 +1416,7 @@ A valid block with slot `GENESIS_SLOT` (a "genesis block") has the following val { slot=GENESIS_SLOT, parent_root=ZERO_HASH, - state_root=STARTUP_STATE_ROOT, + state_root=GENESIS_STATE_ROOT, randao_reveal=EMPTY_SIGNATURE, eth1_data=Eth1Data( deposit_root=ZERO_HASH, @@ -1432,14 +1433,14 @@ A valid block with slot `GENESIS_SLOT` (a "genesis block") has the following val } ``` -`STARTUP_STATE_ROOT` (in the above "genesis block") is generated from the `get_initial_beacon_state` function below. When enough full deposits have been made to the deposit contract and the `Eth2Genesis` log has been emitted, `get_initial_beacon_state` will execute to compute the `hash_tree_root` of `BeaconState`. +`GENESIS_STATE_ROOT` (in the above "genesis block") is generated from the `get_genesis_beacon_state` function below. When enough full deposits have been made to the deposit contract and the `Eth2Genesis` log has been emitted, `get_genesis_beacon_state` will execute to compute the `hash_tree_root` of `BeaconState`. ```python -def get_initial_beacon_state(initial_validator_deposits: List[Deposit], +def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], genesis_time: int, latest_eth1_data: Eth1Data) -> BeaconState: """ - Get the initial ``BeaconState``. + Get the genesis ``BeaconState``. """ state = BeaconState( # Misc @@ -1482,11 +1483,11 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], # Ethereum 1.0 chain data latest_eth1_data=latest_eth1_data, eth1_data_votes=[], - deposit_index=len(initial_validator_deposits) + deposit_index=len(genesis_validator_deposits) ) - # Process initial deposits - for deposit in initial_validator_deposits: + # Process genesis deposits + for deposit in genesis_validator_deposits: process_deposit( state=state, pubkey=deposit.deposit_data.deposit_input.pubkey, @@ -1495,7 +1496,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], withdrawal_credentials=deposit.deposit_data.deposit_input.withdrawal_credentials, ) - # Process initial activations + # Process genesis activations for validator_index, _ in enumerate(state.validator_registry): if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT: activate_validator(state, validator_index, is_genesis=True) @@ -1636,7 +1637,7 @@ Below are the processing steps that happen at every `block`. * If there exists an `eth1_data_vote` in `states.eth1_data_votes` for which `eth1_data_vote.eth1_data == block.eth1_data` (there will be at most one), set `eth1_data_vote.vote_count += 1`. * Otherwise, append to `state.eth1_data_votes` a new `Eth1DataVote(eth1_data=block.eth1_data, vote_count=1)`. -#### Operations +#### Transactions ##### Proposer slashings @@ -1651,7 +1652,7 @@ For each `proposer_slashing` in `block.body.proposer_slashings`: * Verify that `proposer.slashed_epoch > get_current_epoch(state)`. * Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=hash_tree_root(proposer_slashing.proposal_data_1), signature=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_data_1.slot), DOMAIN_PROPOSAL))`. * Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=hash_tree_root(proposer_slashing.proposal_data_2), signature=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_data_2.slot), DOMAIN_PROPOSAL))`. -* Run `penalize_validator(state, proposer_slashing.proposer_index)`. +* Run `slash_validator(state, proposer_slashing.proposer_index)`. ##### Attester slashings @@ -1667,7 +1668,7 @@ For each `attester_slashing` in `block.body.attester_slashings`: * Verify that `verify_slashable_attestation(state, slashable_attestation_2)`. * Let `slashable_indices = [index for index in slashable_attestation_1.validator_indices if index in slashable_attestation_2.validator_indices and state.validator_registry[index].slashed_epoch > get_current_epoch(state)]`. * Verify that `len(slashable_indices) >= 1`. -* Run `penalize_validator(state, index)` for each `index` in `slashable_indices`. +* Run `slash_validator(state, index)` for each `index` in `slashable_indices`. ##### Attestations @@ -1755,11 +1756,11 @@ process_deposit( * Set `state.deposit_index += 1`. -##### Exits +##### Voluntary exits -Verify that `len(block.body.exits) <= MAX_EXITS`. +Verify that `len(block.body.voluntary_exits) <= MAX_VOLUNTARY_EXITS`. -For each `exit` in `block.body.exits`: +For each `exit` in `block.body.voluntary_exits`: * Let `validator = state.validator_registry[exit.validator_index]`. * Verify that `validator.exit_epoch > get_entry_exit_effect_epoch(get_current_epoch(state))`. @@ -1893,7 +1894,7 @@ Case 2: `epochs_since_finality > 4`: ##### Attestation inclusion -For each `index` in `previous_epoch_attester_indices`, we determine the proposer `proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index))` and set `state.validator_balances[proposer_index] += base_reward(state, index) // INCLUDER_REWARD_QUOTIENT`. +For each `index` in `previous_epoch_attester_indices`, we determine the proposer `proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index))` and set `state.validator_balances[proposer_index] += base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT`. ##### Crosslinks @@ -1995,18 +1996,16 @@ If a validator registry update does _not_ happen do the following: **Invariant**: the active index root that is hashed into the shuffling seed actually is the `hash_tree_root` of the validator set that is used for that epoch. -Regardless of whether or not a validator set change happens, run the following: +Regardless of whether or not a validator set change happens run `process_slashings(state)` and `process_withdrawals(state): ```python -def process_penalties_and_exits(state: BeaconState) -> None: +def process_slashings(state: BeaconState) -> None: """ - Process the penalties and prepare the validators who are eligible to withdrawal. + Process the slashings. Note that this function mutates ``state``. """ current_epoch = get_current_epoch(state) - # The active validators active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch) - # The total effective balance of active validators total_balance = sum(get_effective_balance(state, i) for i in active_validator_indices) for index, validator in enumerate(state.validator_registry): @@ -2017,6 +2016,15 @@ def process_penalties_and_exits(state: BeaconState) -> None: total_penalties = total_at_end - total_at_start penalty = get_effective_balance(state, index) * min(total_penalties * 3, total_balance) // total_balance state.validator_balances[index] -= penalty +``` + +```python +def process_withdrawals(state: BeaconState) -> None: + """ + Process the withdrawals. + Note that this function mutates ``state``. + """ + current_epoch = get_current_epoch(state) def eligible(index): validator = state.validator_registry[index] From d4f79c8878375a9e146f3491e2b4d3043a35a30a Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Feb 2019 12:28:13 +0000 Subject: [PATCH 52/70] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 691c58bda..a9dd7fc24 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1996,7 +1996,7 @@ If a validator registry update does _not_ happen do the following: **Invariant**: the active index root that is hashed into the shuffling seed actually is the `hash_tree_root` of the validator set that is used for that epoch. -Regardless of whether or not a validator set change happens run `process_slashings(state)` and `process_withdrawals(state): +Regardless of whether or not a validator set change happens run `process_slashings(state)` and `process_withdrawals(state)`: ```python def process_slashings(state: BeaconState) -> None: From bd9baeca0ccf436805464b57b62955a0b20c962a Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Feb 2019 13:37:30 +0000 Subject: [PATCH 53/70] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a9dd7fc24..2e5670706 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -177,15 +177,15 @@ Code snippets appearing in `this style` are to be interpreted as Python code. ### Misc -| Name | Value | Unit | +| Name | Value | | - | - | :-: | -| `SHARD_COUNT` | `2**10` (= 1,024) | shards | -| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | [validators](#dfn-validator) | -| `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | - | -| `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | - | -| `MAX_INDICES_PER_SLASHABLE_VOTE` | `2**12` (= 4,096) | votes | -| `MAX_WITHDRAWALS_PER_EPOCH` | `2**2` (= 4) | withdrawals | -| `SHUFFLE_ROUND_COUNT` | 90 | - | +| `SHARD_COUNT` | `2**10` (= 1,024) | +| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | +| `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | +| `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | +| `MAX_INDICES_PER_SLASHABLE_VOTE` | `2**12` (= 4,096) | +| `MAX_EXIT_DEQUEUES_PER_EPOCH` | `2**2` (= 4) | +| `SHUFFLE_ROUND_COUNT` | 90 | * 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 `EPOCH_LENGTH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) @@ -1996,7 +1996,7 @@ If a validator registry update does _not_ happen do the following: **Invariant**: the active index root that is hashed into the shuffling seed actually is the `hash_tree_root` of the validator set that is used for that epoch. -Regardless of whether or not a validator set change happens run `process_slashings(state)` and `process_withdrawals(state)`: +Regardless of whether or not a validator set change happens run `process_slashings(state)` and `process_exit_queue(state)`: ```python def process_slashings(state: BeaconState) -> None: @@ -2019,9 +2019,9 @@ def process_slashings(state: BeaconState) -> None: ``` ```python -def process_withdrawals(state: BeaconState) -> None: +def process_exit_queue(state: BeaconState) -> None: """ - Process the withdrawals. + Process the exit queue. Note that this function mutates ``state``. """ current_epoch = get_current_epoch(state) @@ -2034,12 +2034,11 @@ def process_withdrawals(state: BeaconState) -> None: else: return current_epoch >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWAL_EPOCHS - all_indices = list(range(len(state.validator_registry))) - eligible_indices = filter(eligible, all_indices) + eligible_indices = filter(eligible, list(range(len(state.validator_registry)))) # Sort in order of exit epoch, and validators that exit within the same epoch exit in order of validator index sorted_indices = sorted(eligible_indices, key=lambda index: state.validator_registry[index].exit_epoch) - for withdrawn_so_far, index in enumerate(sorted_indices): - if withdrawn_so_far >= MAX_WITHDRAWALS_PER_EPOCH: + for dequeues, index in enumerate(sorted_indices): + if dequeues >= MAX_EXIT_DEQUEUES_PER_EPOCH: break prepare_validator_for_withdrawal(state, index) ``` From 8213ad64e2505a0d9b84e11bc3a3a8ea28d9c5a6 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 12 Feb 2019 13:08:17 -0800 Subject: [PATCH 54/70] add note about attestations on chain voting for justified --- 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 cf9f719ac..3f50d103a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1779,7 +1779,7 @@ The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. [Validators](#dfn-Validator) attesting during the current epoch: * Let `current_total_balance = get_total_balance(state, get_active_validator_indices(state.validator_registry, current_epoch))`. -* Let `current_epoch_attestations = [a for a in state.latest_attestations if current_epoch == slot_to_epoch(a.data.slot)]`. (Note: this is the set of attestations of slots in the epoch `current_epoch`, _not_ attestations that got included in the chain during the epoch `current_epoch`.) +* Let `current_epoch_attestations = [a for a in state.latest_attestations if current_epoch == slot_to_epoch(a.data.slot)]`. (Note: Each of these attestations votes for the current justified epoch/block root because of the [attestation block validity rules](#attestations-1).) * Validators justifying the epoch boundary block at the start of the current epoch: * Let `current_epoch_boundary_attestations = [a for a in current_epoch_attestations if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(current_epoch))]`. * Let `current_epoch_boundary_attester_indices` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in current_epoch_boundary_attestations]`. @@ -1789,7 +1789,7 @@ The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. * Let `previous_total_balance = get_total_balance(state, get_active_validator_indices(state.validator_registry, previous_epoch))`. * Validators that made an attestation during the previous epoch, targeting the previous justified slot: - * Let `previous_epoch_attestations = [a for a in state.latest_attestations if previous_epoch == slot_to_epoch(a.data.slot)]`. + * Let `previous_epoch_attestations = [a for a in state.latest_attestations if previous_epoch == slot_to_epoch(a.data.slot)]`. (Note: Each of these attestations votes for the previous justified epoch/block root because of the [attestation block validity rules](#attestations-1).) * Let `previous_epoch_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_attestations]`. * Let `previous_epoch_attesting_balance = get_total_balance(state, previous_epoch_attester_indices)`. * Validators justifying the epoch boundary block at the start of the previous epoch: From 3459515c2c96f3da06594e04e82920a47fa68b06 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Feb 2019 21:57:54 +0000 Subject: [PATCH 55/70] Merkleise SSZ container elements (#595) Reasons to use `merkle_hash` instead of `hash` for containers: 1) **Smaller witnesses**: `BeaconState` is a somewhat wide container (26 fields as of now, likely 30+ in phase 2). With concatenation and plain concatenation the size of the Merkle witnesses for the top level are ~32 bytes per field element. 2) **Faster incremental hashing** 3) **Consistency**: Consistent with `merkle_hash` for lists/vectors. --- specs/simple-serialize.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index d4bd9e78d..c71654b67 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -390,10 +390,10 @@ Where the inner `hash_tree_root_internal` is a recursive application of the tree #### Container -Recursively tree hash the values in the container in the same order as the fields, and return the hash of the concatenation of the results. +Recursively tree hash the values in the container in the same order as the fields, and Merkle hash the results. ```python -return hash(b''.join([hash_tree_root_internal(getattr(x, field)) for field in value.fields])) +return merkle_hash([hash_tree_root_internal(getattr(x, field)) for field in value.fields]) ``` ## Implementations From b6d27edd6ea86385dde382675c1374616eef2ec6 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Feb 2019 22:38:29 +0000 Subject: [PATCH 56/70] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 42 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2e5670706..62cde4826 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -187,7 +187,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | `MAX_EXIT_DEQUEUES_PER_EPOCH` | `2**2` (= 4) | | `SHUFFLE_ROUND_COUNT` | 90 | -* 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 `EPOCH_LENGTH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) +* 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 at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) ### Deposit contract @@ -218,19 +218,19 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | `EMPTY_SIGNATURE` | `int_to_bytes96(0)` | | `BLS_WITHDRAWAL_PREFIX_BYTE` | `int_to_bytes1(0)` | -* `GENESIS_SLOT` should be at least as large in terms of time as the largest of the time parameters or state list lengths below (ie. it should be at least as large as any value measured in slots, and at least `EPOCH_LENGTH` times as large as any value measured in epochs). +* `GENESIS_SLOT` should be at least as large in terms of time as the largest of the time parameters or state list lengths below (ie. it should be at least as large as any value measured in slots, and at least `SLOTS_PER_EPOCH` times as large as any value measured in epochs). ### Time parameters | Name | Value | Unit | Duration | | - | - | :-: | :-: | -| `SLOT_DURATION` | `6` | seconds | 6 seconds | +| `SECONDS_PER_SLOT` | `6` | seconds | 6 seconds | | `MIN_ATTESTATION_INCLUSION_DELAY` | `2**2` (= 4) | slots | 24 seconds | -| `EPOCH_LENGTH` | `2**6` (= 64) | slots | 6.4 minutes | +| `SLOTS_PER_EPOCH` | `2**6` (= 64) | slots | 6.4 minutes | | `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | | `ACTIVATION_EXIT_DELAY` | `2**2` (= 4) | epochs | 25.6 minutes | -| `ETH1_DATA_VOTING_PERIOD` | `2**4` (= 16) | epochs | ~1.7 hours | -| `MIN_VALIDATOR_WITHDRAWAL_EPOCHS` | `2**8` (= 256) | epochs | ~27 hours | +| `EPOCHS_PER_ETH1_VOTING_PERIOD` | `2**4` (= 16) | epochs | ~1.7 hours | +| `MIN_VALIDATOR_WITHDRAWAL_DELAY` | `2**8` (= 256) | epochs | ~27 hours | ### State list lengths @@ -648,7 +648,7 @@ def slot_to_epoch(slot: Slot) -> Epoch: """ Return the epoch number of the given ``slot``. """ - return slot // EPOCH_LENGTH + return slot // SLOTS_PER_EPOCH ``` ### `get_previous_epoch` @@ -682,7 +682,7 @@ def get_epoch_start_slot(epoch: Epoch) -> Slot: """ Return the starting slot of the given ``epoch``. """ - return epoch * EPOCH_LENGTH + return epoch * SLOTS_PER_EPOCH ``` ### `is_active_validator` @@ -751,10 +751,10 @@ def get_epoch_committee_count(active_validator_count: int) -> int: return max( 1, min( - SHARD_COUNT // EPOCH_LENGTH, - active_validator_count // EPOCH_LENGTH // TARGET_COMMITTEE_SIZE, + SHARD_COUNT // SLOTS_PER_EPOCH, + active_validator_count // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE, ) - ) * EPOCH_LENGTH + ) * SLOTS_PER_EPOCH ``` ### `get_shuffling` @@ -879,8 +879,8 @@ def get_crosslink_committees_at_slot(state: BeaconState, state.validator_registry, shuffling_epoch, ) - offset = slot % EPOCH_LENGTH - committees_per_slot = committees_per_epoch // EPOCH_LENGTH + offset = slot % SLOTS_PER_EPOCH + committees_per_slot = committees_per_epoch // SLOTS_PER_EPOCH slot_start_shard = (shuffling_start_shard + committees_per_slot * offset) % SHARD_COUNT return [ @@ -1523,11 +1523,11 @@ For a beacon chain block, `block`, to be processed by a node, the following cond * The parent block with root `block.parent_root` has been processed and accepted. * An Ethereum 1.0 block pointed to by the `state.latest_eth1_data.block_hash` has been processed and accepted. -* The node's Unix time is greater than or equal to `state.genesis_time + block.slot * SLOT_DURATION`. (Note that leap seconds mean that slots will occasionally last `SLOT_DURATION + 1` or `SLOT_DURATION - 1` seconds, possibly several times a year.) +* The node's Unix time is greater than or equal to `state.genesis_time + block.slot * SECONDS_PER_SLOT`. (Note that leap seconds mean that slots will occasionally last `SECONDS_PER_SLOT + 1` or `SECONDS_PER_SLOT - 1` seconds, possibly several times a year.) If these conditions are not met, the client should delay processing the beacon block until the conditions are all satisfied. -Beacon block production is significantly different because of the proof of stake mechanism. A client simply checks what it thinks is the canonical chain when it should create a block, and looks up what its slot number is; when the slot arrives, it either proposes or attests to a block as required. Note that this requires each node to have a clock that is roughly (i.e. within `SLOT_DURATION` seconds) synchronized with the other nodes. +Beacon block production is significantly different because of the proof of stake mechanism. A client simply checks what it thinks is the canonical chain when it should create a block, and looks up what its slot number is; when the slot arrives, it either proposes or attests to a block as required. Note that this requires each node to have a clock that is roughly (i.e. within `SECONDS_PER_SLOT` seconds) synchronized with the other nodes. ### Beacon chain fork choice rule @@ -1590,7 +1590,7 @@ We now define the state transition function. At a high level the state transitio 1. The per-slot transitions, which happens at the start of every slot. 2. The per-block transitions, which happens at every block. -3. The per-epoch transitions, which happens at the end of the last slot of every epoch (i.e. `(state.slot + 1) % EPOCH_LENGTH == 0`). +3. The per-epoch transitions, which happens at the end of the last slot of every epoch (i.e. `(state.slot + 1) % SLOTS_PER_EPOCH == 0`). The per-slot transitions focus on the slot counter and block roots records updates; the per-block transitions generally focus on verifying aggregate signatures and saving temporary records relating to the per-block activity in the `BeaconState`; the per-epoch transitions focus on the [validator](#dfn-validator) registry, including adjusting balances and activating and exiting [validators](#dfn-validator), as well as processing crosslinks and managing block justification/finalization. @@ -1676,7 +1676,7 @@ Verify that `len(block.body.attestations) <= MAX_ATTESTATIONS`. For each `attestation` in `block.body.attestations`: -* Verify that `attestation.data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY < attestation.data.slot + EPOCH_LENGTH`. +* Verify that `attestation.data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY < attestation.data.slot + SLOTS_PER_EPOCH`. * Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if attestation.data.slot >= get_epoch_start_slot(get_current_epoch(state)) else state.previous_justified_epoch`. * Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, get_epoch_start_slot(attestation.data.justified_epoch))`. * Verify that either (i) `state.latest_crosslinks[attestation.data.shard] == attestation.data.latest_crosslink` or (ii) `state.latest_crosslinks[attestation.data.shard] == Crosslink(shard_block_root=attestation.data.shard_block_root, epoch=slot_to_epoch(attestation.data.slot))`. @@ -1771,7 +1771,7 @@ For each `exit` in `block.body.voluntary_exits`: ### Per-epoch processing -The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. +The steps below happen when `(state.slot + 1) % SLOTS_PER_EPOCH == 0`. #### Helper variables @@ -1824,9 +1824,9 @@ Define the following helpers to process attestation inclusion rewards and inclus #### Eth1 data -If `next_epoch % ETH1_DATA_VOTING_PERIOD == 0`: +If `next_epoch % EPOCHS_PER_ETH1_VOTING_PERIOD == 0`: -* If `eth1_data_vote.vote_count * 2 > ETH1_DATA_VOTING_PERIOD * EPOCH_LENGTH` for some `eth1_data_vote` in `state.eth1_data_votes` (ie. more than half the votes in this voting period were for that value), set `state.latest_eth1_data = eth1_data_vote.eth1_data`. +* If `eth1_data_vote.vote_count * 2 > EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH` for some `eth1_data_vote` in `state.eth1_data_votes` (ie. more than half the votes in this voting period were for that value), set `state.latest_eth1_data = eth1_data_vote.eth1_data`. * Set `state.eth1_data_votes = []`. #### Justification @@ -2032,7 +2032,7 @@ def process_exit_queue(state: BeaconState) -> None: slashed_withdrawal_epochs = LATEST_SLASHED_EXIT_LENGTH // 2 return current_epoch >= validator.slashed_epoch + slashed_withdrawal_epochs else: - return current_epoch >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWAL_EPOCHS + return current_epoch >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWAL_DELAY eligible_indices = filter(eligible, list(range(len(state.validator_registry)))) # Sort in order of exit epoch, and validators that exit within the same epoch exit in order of validator index From cf371c4973478da4f4cc0ee44efbde70495615fd Mon Sep 17 00:00:00 2001 From: vbuterin Date: Tue, 12 Feb 2019 16:41:38 -0600 Subject: [PATCH 57/70] Add support for transfers between withdrawn accounts (#601) --- specs/core/0_beacon-chain.md | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 03be5ee17..3c743207f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -38,6 +38,8 @@ - [`DepositInput`](#depositinput) - [Exits](#exits) - [`Exit`](#exit) + - [Transfers](#transfers) + - [`Transfer`](#transfer) - [Beacon chain blocks](#beacon-chain-blocks) - [`BeaconBlock`](#beaconblock) - [`BeaconBlockBody`](#beaconblockbody) @@ -231,6 +233,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | `ENTRY_EXIT_DELAY` | `2**2` (= 4) | epochs | 25.6 minutes | | `ETH1_DATA_VOTING_PERIOD` | `2**4` (= 16) | epochs | ~1.7 hours | | `MIN_VALIDATOR_WITHDRAWAL_EPOCHS` | `2**8` (= 256) | epochs | ~27 hours | +| `MIN_EXIT_EPOCHS_BEFORE_TRANSFER` | `2**13` (= 8,192) | epochs | ~36 days | ### State list lengths @@ -269,6 +272,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | `MAX_ATTESTATIONS` | `2**7` (= 128) | | `MAX_DEPOSITS` | `2**4` (= 16) | | `MAX_EXITS` | `2**4` (= 16) | +| `MAX_TRANSFERS` | `2**4` (= 16) | ### Signature domains @@ -279,6 +283,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | `DOMAIN_PROPOSAL` | `2` | | `DOMAIN_EXIT` | `3` | | `DOMAIN_RANDAO` | `4` | +| `DOMAIN_TRANSFER` | `5` | ## Data structures @@ -440,6 +445,27 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git } ``` +##### `Transfer` + +```python +{ + # Sender index + 'from': 'uint64', + # Recipient index + 'to': 'uint64', + # Amount in Gwei + 'amount': 'uint64', + # Fee in Gwei for block proposer + 'fee': 'uint64', + # Inclusion slot + 'slot': 'uint64', + # Sender withdrawal pubkey + 'pubkey': 'bytes48', + # Sender signature + 'signature': 'bytes96', +} +``` + ### Beacon chain blocks #### `BeaconBlock` @@ -468,6 +494,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'attestations': [Attestation], 'deposits': [Deposit], 'exits': [Exit], + 'transfers': [Transfer], } ``` @@ -1768,6 +1795,26 @@ For each `exit` in `block.body.exits`: * Verify that `bls_verify(pubkey=validator.pubkey, message_hash=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT))`. * Run `initiate_validator_exit(state, exit.validator_index)`. +##### Transfers + +Note: Transfers are a temporary functionality for phases 0 and 1, to be removed in phase 2. + +Verify that `len(block.body.transfers) <= MAX_TRANSFERS` and that all transfers are distinct. + +For each `transfer` in `block.body.transfers`: + +* Verify that `state.validator_balances[transfer.from] >= transfer.amount`. +* Verify that `state.validator_balances[transfer.from] >= transfer.fee`. +* Verify that `state.validator_balances[transfer.from] == transfer.amount + transfer.fee` or `state.validator_balances[transfer.from] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT`. +* Verify that `transfer.slot == state.slot`. +* Verify that `get_current_epoch(state) >= state.validator_registry[transfer.from].exit_epoch + MIN_EXIT_EPOCHS_BEFORE_TRANSFER`. +* Verify that `state.validator_registry[transfer.from].withdrawal_credentials == BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer.pubkey)[1:]`. +* Let `transfer_message = hash_tree_root(Transfer(from=transfer.from, to=transfer.to, amount=transfer.amount, fee=transfer.fee, slot=transfer.slot, signature=EMPTY_SIGNATURE))`. +* Verify that `bls_verify(pubkey=transfer.pubkey, message_hash=transfer_message, signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER))`. +* Set `state.validator_balances[transfer.from] -= transfer.amount + transfer.fee`. +* Set `state.validator_balances[transfer.to] += transfer.amount`. +* Set `state.validator_balances[get_beacon_proposer_index(state, state.slot)] += transfer.fee`. + ### Per-epoch processing The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. From a730295982e4a8af7292a9d2d6868fb30119409b Mon Sep 17 00:00:00 2001 From: terence tsao Date: Wed, 13 Feb 2019 01:40:24 +0000 Subject: [PATCH 58/70] Update 0_beacon-chain.md (#614) --- specs/core/0_beacon-chain.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 3c743207f..07fead3a3 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -919,8 +919,6 @@ def get_crosslink_committees_at_slot(state: BeaconState, ] ``` -**Note**: we plan to replace the shuffling algorithm with a pointwise-evaluable shuffle (see https://github.com/ethereum/eth2.0-specs/issues/323), which will allow calculation of the committees for each slot individually. - ### `get_block_root` ```python From f7320ec25be03cbc08a69ac23a6d058f7fd89db5 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 14 Feb 2019 00:30:00 +0800 Subject: [PATCH 59/70] Fix `compute_commitment` 1. Use `+` to concatenate the merkle roots in `hash` function. 2. Fix `pad_to_power_of_2`: padding with `[b'\x00' * SHARD_BLOCK_SIZE]`, not `[SHARD_BLOCK_SIZE]`. --- specs/core/1_shard-data-chains.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 2503c8dad..365fb640d 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -187,14 +187,15 @@ We define two helpers: ```python def pad_to_power_of_2(values: List[bytes]) -> List[bytes]: + zero_shard_block = b'\x00' * SHARD_BLOCK_SIZE while not is_power_of_two(len(values)): - values = values + [SHARD_BLOCK_SIZE] + values = values + [zero_shard_block] return values ``` ```python def merkle_root_of_bytes(data: bytes) -> bytes: - return merkle_root([data[i:i+32] for i in range(0, len(data), 32)]) + return merkle_root([data[i:i + 32] for i in range(0, len(data), 32)]) ``` We define the function for computing the commitment as follows: @@ -202,8 +203,16 @@ We define the function for computing the commitment as follows: ```python def compute_commitment(headers: List[ShardBlock], bodies: List[bytes]) -> Bytes32: return hash( - merkle_root(pad_to_power_of_2([merkle_root_of_bytes(zpad(serialize(h), SHARD_BLOCK_SIZE)) for h in headers])), - merkle_root(pad_to_power_of_2([merkle_root_of_bytes(h) for h in bodies])) + merkle_root( + pad_to_power_of_2([ + merkle_root_of_bytes(zpad(serialize(h), SHARD_BLOCK_SIZE)) for h in headers + ]) + ) + + merkle_root( + pad_to_power_of_2([ + merkle_root_of_bytes(h) for h in bodies + ]) + ) ) ``` From fd5c3ee8b5c11c1b65d37c95ccc98027a333ae6f Mon Sep 17 00:00:00 2001 From: terence tsao Date: Wed, 13 Feb 2019 08:38:03 -0800 Subject: [PATCH 60/70] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d00a2817b..0852f53f7 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1587,7 +1587,7 @@ def lmd_ghost(store: Store, start_state: BeaconState, start_block: BeaconBlock) Execute the LMD-GHOST algorithm to find the head ``BeaconBlock``. """ validators = start_state.validator_registry - active_validator_indices = get_active_validator_indices(validators, start_state.slot) + active_validator_indices = get_active_validator_indices(validators, slot_to_epoch(start_state.slot)) attestation_targets = [ (validator_index, get_latest_attestation_target(store, validator_index)) for validator_index in active_validator_indices From eadfa20b99b857ae9de7d764ee03ea18043026c2 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 14 Feb 2019 04:20:53 -0600 Subject: [PATCH 61/70] Bring forward changes to withdrawability from phase 1 (#615) * Bring forward changes to withdrawability from phase 1 * The `WITHDRAWABLE` flag is removed; instead, a validator's withdrawability is determined through the `withdrawable_epoch` field (renamed and re-purposed from `withdrawal_epoch` which was not used) * When a validator passes through the withdrawal queue, the `prepare_validator_for_withdrawal` function does not let them withdraw immediately; instead, they have to wait `MIN_VALIDATOR_WITHDRAWAL_EPOCHS`. This extra minimum delay serves no value in phase 0, but is crucial for phase 1 as the period between a validator passing through the queue and the validator being eligible to withdraw is where proof of custody challenges can come in; adding it in phase 0 is only half a line of code so easier to add it now. * If a validator is penalized, they are no longer subject to the exit queue; instead, their `withdrawable_epoch` is set `LATEST_PENALIZED_EXIT_LENGTH` into the future and this is used to determine when the validator can withdraw * Changes the eligibility condition for a transfer to use the `withdrawable_epoch` --- specs/core/0_beacon-chain.md | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0852f53f7..45e707585 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -126,6 +126,7 @@ - [Attestations](#attestations-1) - [Deposits](#deposits-1) - [Exits](#exits-1) + - [Transfers](#transfers-1) - [Per-epoch processing](#per-epoch-processing) - [Helper variables](#helper-variables) - [Eth1 data](#eth1-data-1) @@ -232,8 +233,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | `SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | | `ENTRY_EXIT_DELAY` | `2**2` (= 4) | epochs | 25.6 minutes | | `ETH1_DATA_VOTING_PERIOD` | `2**4` (= 16) | epochs | ~1.7 hours | -| `MIN_VALIDATOR_WITHDRAWAL_EPOCHS` | `2**8` (= 256) | epochs | ~27 hours | -| `MIN_EXIT_EPOCHS_BEFORE_TRANSFER` | `2**13` (= 8,192) | epochs | ~36 days | +| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours | ### State list lengths @@ -261,7 +261,6 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | Name | Value | | - | - | | `INITIATED_EXIT` | `2**0` (= 1) | -| `WITHDRAWABLE` | `2**1` (= 2) | ### Max operations per block @@ -445,6 +444,8 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git } ``` +#### Transfers + ##### `Transfer` ```python @@ -569,8 +570,8 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'activation_epoch': 'uint64', # Epoch when validator exited 'exit_epoch': 'uint64', - # Epoch when validator withdrew - 'withdrawal_epoch': 'uint64', + # Epoch when validator is eligible to withdraw + 'withdrawable_epoch': 'uint64', # Epoch when validator was penalized 'penalized_epoch': 'uint64', # Status flags @@ -1298,7 +1299,7 @@ def process_deposit(state: BeaconState, withdrawal_credentials=withdrawal_credentials, activation_epoch=FAR_FUTURE_EPOCH, exit_epoch=FAR_FUTURE_EPOCH, - withdrawal_epoch=FAR_FUTURE_EPOCH, + withdrawable_epoch=FAR_FUTURE_EPOCH, penalized_epoch=FAR_FUTURE_EPOCH, status_flags=0, ) @@ -1368,6 +1369,7 @@ def penalize_validator(state: BeaconState, index: ValidatorIndex) -> None: Penalize the validator of the given ``index``. Note that this function mutates ``state``. """ + assert state.slot < validator.withdrawable_epoch # [TO BE REMOVED IN PHASE 2] exit_validator(state, index) validator = state.validator_registry[index] state.latest_penalized_balances[get_current_epoch(state) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index) @@ -1377,6 +1379,7 @@ def penalize_validator(state: BeaconState, index: ValidatorIndex) -> None: state.validator_balances[whistleblower_index] += whistleblower_reward state.validator_balances[index] -= whistleblower_reward validator.penalized_epoch = get_current_epoch(state) + validator.withdrawable_epoch = get_current_epoch(state) + LATEST_PENALIZED_EXIT_LENGTH ``` #### `prepare_validator_for_withdrawal` @@ -1384,11 +1387,12 @@ def penalize_validator(state: BeaconState, index: ValidatorIndex) -> None: ```python def prepare_validator_for_withdrawal(state: BeaconState, index: ValidatorIndex) -> None: """ - Set the validator with the given ``index`` with ``WITHDRAWABLE`` flag. + Set the validator with the given ``index`` as withdrawable + ``MIN_VALIDATOR_WITHDRAWABILITY_DELAY`` after the current epoch. Note that this function mutates ``state``. """ validator = state.validator_registry[index] - validator.status_flags |= WITHDRAWABLE + validator.withdrawable_epoch = get_current_epoch(state) + MIN_VALIDATOR_WITHDRAWABILITY_DELAY ``` ## Ethereum 1.0 deposit contract @@ -1804,8 +1808,8 @@ For each `transfer` in `block.body.transfers`: * Verify that `state.validator_balances[transfer.from] >= transfer.amount`. * Verify that `state.validator_balances[transfer.from] >= transfer.fee`. * Verify that `state.validator_balances[transfer.from] == transfer.amount + transfer.fee` or `state.validator_balances[transfer.from] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT`. -* Verify that `transfer.slot == state.slot`. -* Verify that `get_current_epoch(state) >= state.validator_registry[transfer.from].exit_epoch + MIN_EXIT_EPOCHS_BEFORE_TRANSFER`. +* Verify that `state.slot == transfer.slot`. +* Verify that `get_current_epoch(state) >= state.validator_registry[transfer.from].withdrawable_epoch`. * Verify that `state.validator_registry[transfer.from].withdrawal_credentials == BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer.pubkey)[1:]`. * Let `transfer_message = hash_tree_root(Transfer(from=transfer.from, to=transfer.to, amount=transfer.amount, fee=transfer.fee, slot=transfer.slot, signature=EMPTY_SIGNATURE))`. * Verify that `bls_verify(pubkey=transfer.pubkey, message_hash=transfer_message, signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER))`. @@ -2064,11 +2068,12 @@ def process_penalties_and_exits(state: BeaconState) -> None: def eligible(index): validator = state.validator_registry[index] - if validator.penalized_epoch <= current_epoch: - penalized_withdrawal_epochs = LATEST_PENALIZED_EXIT_LENGTH // 2 - return current_epoch >= validator.penalized_epoch + penalized_withdrawal_epochs + # Cannot exit if you already have + if validator.withdrawable_epoch < FAR_FUTURE_EPOCH: + return False + # Can exit if at least the minimum amount of time has passed else: - return current_epoch >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWAL_EPOCHS + return current_epoch >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY all_indices = list(range(len(state.validator_registry))) eligible_indices = filter(eligible, all_indices) From 6c49270664eb5a77cc6a99992fe1f99c15810ad0 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 14 Feb 2019 08:00:12 -0600 Subject: [PATCH 62/70] Added minimum slashing penalty of ~1 ETH (#624) --- specs/core/0_beacon-chain.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 45e707585..32e705d35 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -252,6 +252,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | `WHISTLEBLOWER_REWARD_QUOTIENT` | `2**9` (= 512) | | `INCLUDER_REWARD_QUOTIENT` | `2**3` (= 8) | | `INACTIVITY_PENALTY_QUOTIENT` | `2**24` (= 16,777,216) | +| `MIN_PENALTY_QUOTIENT` | `2**5` (= 32) | * The `BASE_REWARD_QUOTIENT` parameter dictates the per-epoch reward. It corresponds to ~2.54% annual interest assuming 10 million participating ETH in every epoch. * The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12 epochs` (~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)`. @@ -2063,7 +2064,10 @@ def process_penalties_and_exits(state: BeaconState) -> None: total_at_start = state.latest_penalized_balances[(epoch_index + 1) % LATEST_PENALIZED_EXIT_LENGTH] total_at_end = state.latest_penalized_balances[epoch_index] total_penalties = total_at_end - total_at_start - penalty = get_effective_balance(state, index) * min(total_penalties * 3, total_balance) // total_balance + penalty = max( + get_effective_balance(state, index) * min(total_penalties * 3, total_balance) // total_balance, + get_effective_balance(state, index) // MIN_PENALTY_QUOTIENT + ) state.validator_balances[index] -= penalty def eligible(index): From d7fb7729e6c5f670359f962024ff99c94bb37745 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Fri, 15 Feb 2019 01:14:59 +1100 Subject: [PATCH 63/70] Add cautionary assert in shuffling function (#622) --- specs/core/0_beacon-chain.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 32e705d35..f77602465 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -744,6 +744,8 @@ def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int: https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf See the 'generalized domain' algorithm on page 3. """ + assert index < list_size + for round in range(SHUFFLE_ROUND_COUNT): pivot = bytes_to_int(hash(seed + int_to_bytes1(round))[0:8]) % list_size flip = (pivot - index) % list_size From 677efe584067f9b91fa233703e600aef69b3b967 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 14 Feb 2019 13:16:19 -0700 Subject: [PATCH 64/70] fix off by one attestaton issue due to attesting to post state of block (#627) --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index f77602465..6b2442f03 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1708,7 +1708,7 @@ Verify that `len(block.body.attestations) <= MAX_ATTESTATIONS`. For each `attestation` in `block.body.attestations`: * Verify that `attestation.data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY < attestation.data.slot + EPOCH_LENGTH`. -* Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if attestation.data.slot >= get_epoch_start_slot(get_current_epoch(state)) else state.previous_justified_epoch`. +* Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if slot_to_epoch(attestation.data.slot + 1) >= get_current_epoch(state) else state.previous_justified_epoch`. * Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, get_epoch_start_slot(attestation.data.justified_epoch))`. * Verify that either (i) `state.latest_crosslinks[attestation.data.shard] == attestation.data.latest_crosslink` or (ii) `state.latest_crosslinks[attestation.data.shard] == Crosslink(shard_block_root=attestation.data.shard_block_root, epoch=slot_to_epoch(attestation.data.slot))`. * Verify bitfields and aggregate signature: From 380f5bc86bfbf0191d7881e6debafb432ea90139 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Thu, 14 Feb 2019 14:00:43 -0800 Subject: [PATCH 65/70] Update 0_beacon-chain-validator.md --- specs/validator/0_beacon-chain-validator.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 93c3fb408..b893941ec 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -260,9 +260,9 @@ Set `attestation_data.beacon_block_root = hash_tree_root(head)` where `head` is ##### Epoch boundary root -Set `attestation_data.epoch_boundary_root = hash_tree_root(epoch_boundary)` where `epoch_boundary` is the block at the most recent epoch boundary in the chain defined by `head` -- i.e. the `BeaconBlock` where `block.slot == get_epoch_start_slot(head.slot)`. +Set `attestation_data.epoch_boundary_root = hash_tree_root(epoch_boundary)` where `epoch_boundary` is the block at the most recent epoch boundary in the chain defined by `head` -- i.e. the `BeaconBlock` where `block.slot == get_epoch_start_slot(slot_to_epoch(head.slot))`. -_Note:_ This can be looked up in the state using `get_block_root(state, get_epoch_start_slot(head.slot))`. +_Note:_ This can be looked up in the state using `get_block_root(state, get_epoch_start_slot(slot_to_epoch(head.slot)))`. ##### Shard block root From 375659dc6c92ddcbf1682a024f9afc4fb24335c6 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 14 Feb 2019 16:02:01 -0700 Subject: [PATCH 66/70] name changes in validator doc and phase 1 doc --- specs/core/1_shard-data-chains.md | 18 +++++++++--------- specs/validator/0_beacon-chain-validator.md | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 365fb640d..cbb84aa0e 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -47,8 +47,8 @@ def get_split_offset(list_size: int, chunks: int, index: int) -> int: ```python def get_shuffled_committee(state: BeaconState, - shard: ShardNumber, - committee_start_epoch: EpochNumber) -> List[ValidatorIndex]: + shard: Shard, + committee_start_epoch: Epoch) -> List[ValidatorIndex]: """ Return shuffled committee. """ @@ -66,8 +66,8 @@ def get_shuffled_committee(state: BeaconState, ```python def get_persistent_committee(state: BeaconState, - shard: ShardNumber, - epoch: EpochNumber) -> List[ValidatorIndex]: + shard: Shard, + epoch: Epoch) -> List[ValidatorIndex]: """ Return the persistent committee for the given ``shard`` at the given ``epoch``. """ @@ -94,10 +94,10 @@ def get_persistent_committee(state: BeaconState, ```python def get_shard_proposer_index(state: BeaconState, - shard: ShardNumber, - slot: SlotNumber) -> ValidatorIndex: + shard: Shard, + slot: Slot) -> ValidatorIndex: seed = hash( - state.current_epoch_seed + + state.current_shuffling_seed + int_to_bytes8(shard) + int_to_bytes8(slot) ) @@ -177,8 +177,8 @@ A node should sign a crosslink only if the following conditions hold. **If a nod First, the conditions must recursively apply to the crosslink referenced in `last_crosslink_root` for the same shard (unless `last_crosslink_root` equals zero, in which case we are at the genesis). Second, we verify the `shard_chain_commitment`. -* Let `start_slot = state.latest_crosslinks[shard].epoch * EPOCH_LENGTH + EPOCH_LENGTH - CROSSLINK_LOOKBACK`. -* Let `end_slot = attestation.data.slot - attestation.data.slot % EPOCH_LENGTH - CROSSLINK_LOOKBACK`. +* Let `start_slot = state.latest_crosslinks[shard].epoch * SLOTS_PER_EPOCH + SLOTS_PER_EPOCH - CROSSLINK_LOOKBACK`. +* Let `end_slot = attestation.data.slot - attestation.data.slot % SLOTS_PER_EPOCH - CROSSLINK_LOOKBACK`. * Let `length = end_slot - start_slot`, `headers[0] .... headers[length-1]` be the serialized block headers in the canonical shard chain from the verifer's point of view (note that this implies that `headers` and `bodies` have been checked for validity). * Let `bodies[0] ... bodies[length-1]` be the bodies of the blocks. * Note: If there is a missing slot, then the header and body are the same as that of the block at the most recent slot that has a block. diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index b893941ec..2211e2e41 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -34,7 +34,7 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers - [Attester slashings](#attester-slashings) - [Attestations](#attestations) - [Deposits](#deposits) - - [Exits](#exits) + - [Voluntary exits](#voluntary-exits) - [Attestations](#attestations-1) - [Attestation data](#attestation-data) - [Slot](#slot-1) @@ -118,7 +118,7 @@ Once a validator has been processed and added to the beacon state's `validator_r ### Activation -In normal operation, the validator is quickly activated at which point the validator is added to the shuffling and begins validation after an additional `ENTRY_EXIT_DELAY` epochs (25.6 minutes). +In normal operation, the validator is quickly activated at which point the validator is added to the shuffling and begins validation after an additional `ACTIVATION_EXIT_DELAY` epochs (25.6 minutes). The function [`is_active_validator`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_active_validator) can be used to check if a validator is active during a given epoch. Usage is as follows: @@ -232,15 +232,15 @@ Up to `MAX_ATTESTATIONS` aggregate attestations can be included in the `block`. Up to `MAX_DEPOSITS` [`Deposit`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposit) objects can be included in the `block`. These deposits are constructed from the `Deposit` logs from the [Eth1.0 deposit contract](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#ethereum-10-deposit-contract) and must be processed in sequential order. The deposits included in the `block` must satisfy the verification conditions found in [deposits processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposits-1). -##### Exits +##### Voluntary exits -Up to `MAX_EXITS` [`Exit`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#exit) objects can be included in the `block`. The exits must satisfy the verification conditions found in [exits processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#exits-1). +Up to `MAX_VOLUNTARY_EXITS` [`VoluntaryExit`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#voluntaryexit) objects can be included in the `block`. The exits must satisfy the verification conditions found in [exits processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#exits-1). ### Attestations A validator is expected to create, sign, and broadcast an attestation during each epoch. The slot during which the validator performs this role is any slot at which `get_crosslink_committees_at_slot(state, slot)` contains a committee that contains `validator_index`. -A validator should create and broadcast the attestation halfway through the `slot` during which the validator is assigned -- that is `SLOT_DURATION * 0.5` seconds after the start of `slot`. +A validator should create and broadcast the attestation halfway through the `slot` during which the validator is assigned -- that is `SECONDS_PER_SLOT * 0.5` seconds after the start of `slot`. #### Attestation data @@ -347,7 +347,7 @@ Either (2) or (3) occurs if (1) fails. The choice between (2) and (3) is determi def get_next_epoch_committee_assignment( state: BeaconState, validator_index: ValidatorIndex, - registry_change: bool) -> Tuple[List[ValidatorIndex], ShardNumber, SlotNumber, bool]: + registry_change: bool) -> Tuple[List[ValidatorIndex], Shard, Slot, bool]: """ Return the committee assignment in the next epoch for ``validator_index`` and ``registry_change``. ``assignment`` returned is a tuple of the following form: @@ -360,14 +360,14 @@ def get_next_epoch_committee_assignment( current_epoch = get_current_epoch(state) next_epoch = current_epoch + 1 next_epoch_start_slot = get_epoch_start_slot(next_epoch) - for slot in range(next_epoch_start_slot, next_epoch_start_slot + EPOCH_LENGTH): + for slot in range(next_epoch_start_slot, next_epoch_start_slot + SLOTS_PER_EPOCH): crosslink_committees = get_crosslink_committees_at_slot( state, slot, registry_change=registry_change, ) selected_committees = [ - committee # Tuple[List[ValidatorIndex], ShardNumber] + committee # Tuple[List[ValidatorIndex], Shard] for committee in crosslink_committees if validator_index in committee[0] ] From e0c28a3cd06309caa61b72c83feafefd983df226 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 15 Feb 2019 18:25:30 +0800 Subject: [PATCH 67/70] Fix `slash_validator` Define `validator` before using it. --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a48d881fa..e181fc28c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1372,9 +1372,9 @@ def slash_validator(state: BeaconState, index: ValidatorIndex) -> None: Slash the validator with index ``index``. Note that this function mutates ``state``. """ + validator = state.validator_registry[index] assert state.slot < validator.withdrawable_epoch # [TO BE REMOVED IN PHASE 2] exit_validator(state, index) - validator = state.validator_registry[index] state.latest_slashed_balances[get_current_epoch(state) % LATEST_SLASHED_EXIT_LENGTH] += get_effective_balance(state, index) whistleblower_index = get_beacon_proposer_index(state, state.slot) From 6f856ba00913fefba28d217ec20acd302bf05b15 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 15 Feb 2019 18:33:22 +0800 Subject: [PATCH 68/70] Comparing in slot --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index e181fc28c..673c2eeaf 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1373,7 +1373,7 @@ def slash_validator(state: BeaconState, index: ValidatorIndex) -> None: Note that this function mutates ``state``. """ validator = state.validator_registry[index] - assert state.slot < validator.withdrawable_epoch # [TO BE REMOVED IN PHASE 2] + assert state.slot < get_epoch_start_slot(validator.withdrawable_epoch) # [TO BE REMOVED IN PHASE 2] exit_validator(state, index) state.latest_slashed_balances[get_current_epoch(state) % LATEST_SLASHED_EXIT_LENGTH] += get_effective_balance(state, index) From 984eb79f68ca8533fccf95155219504b422655a0 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 15 Feb 2019 21:46:47 +0800 Subject: [PATCH 69/70] Fix unix time condition of an acceptable block (#636) --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a48d881fa..023315356 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1555,7 +1555,7 @@ For a beacon chain block, `block`, to be processed by a node, the following cond * The parent block with root `block.parent_root` has been processed and accepted. * An Ethereum 1.0 block pointed to by the `state.latest_eth1_data.block_hash` has been processed and accepted. -* The node's Unix time is greater than or equal to `state.genesis_time + block.slot * SECONDS_PER_SLOT`. (Note that leap seconds mean that slots will occasionally last `SECONDS_PER_SLOT + 1` or `SECONDS_PER_SLOT - 1` seconds, possibly several times a year.) +* The node's Unix time is greater than or equal to `state.genesis_time + (block.slot - GENESIS_SLOT) * SECONDS_PER_SLOT`. (Note that leap seconds mean that slots will occasionally last `SECONDS_PER_SLOT + 1` or `SECONDS_PER_SLOT - 1` seconds, possibly several times a year.) If these conditions are not met, the client should delay processing the beacon block until the conditions are all satisfied. From e074bc2c344b0154e1d993141def13cc48389ec1 Mon Sep 17 00:00:00 2001 From: Jonny Rhea Date: Fri, 15 Feb 2019 08:48:09 -0600 Subject: [PATCH 70/70] Update 0_beacon-chain.md (#635) replace vote_data with slashable_attestation in verify_slashable_attestation() --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 023315356..490a306b4 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1168,7 +1168,7 @@ def verify_slashable_attestation(state: BeaconState, slashable_attestation: Slas hash_tree_root(AttestationDataAndCustodyBit(data=slashable_attestation.data, custody_bit=0b1)), ], signature=slashable_attestation.aggregate_signature, - domain=get_domain(state.fork, slot_to_epoch(vote_data.data.slot), DOMAIN_ATTESTATION), + domain=get_domain(state.fork, slot_to_epoch(slashable_attestation.data.slot), DOMAIN_ATTESTATION), ) ```