From 2bf020d49d4f3f7cbc052b2a88a98c2d15ce12e9 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 23 Apr 2020 23:46:27 +0800 Subject: [PATCH 01/43] Apply strict uint64 casting --- specs/phase0/beacon-chain.md | 112 ++++++++++++++++++----------------- specs/phase0/fork-choice.md | 8 +-- specs/phase0/validator.md | 2 +- 3 files changed, 63 insertions(+), 59 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 142cf3b02..d1f97a506 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -170,9 +170,9 @@ The following values are (non-configurable) constants used throughout the specif | `GENESIS_SLOT` | `Slot(0)` | | `GENESIS_EPOCH` | `Epoch(0)` | | `FAR_FUTURE_EPOCH` | `Epoch(2**64 - 1)` | -| `BASE_REWARDS_PER_EPOCH` | `4` | -| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) | -| `JUSTIFICATION_BITS_LENGTH` | `4` | +| `BASE_REWARDS_PER_EPOCH` | `uint64(4)` | +| `DEPOSIT_CONTRACT_TREE_DEPTH` | `uint64(2**5)` (= 32) | +| `JUSTIFICATION_BITS_LENGTH` | `uint64(4)` | | `ENDIANNESS` | `'little'` | ## Configuration @@ -183,17 +183,17 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | | - | - | -| `MAX_COMMITTEES_PER_SLOT` | `2**6` (= 64) | -| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | -| `MAX_VALIDATORS_PER_COMMITTEE` | `2**11` (= 2,048) | -| `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) | -| `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) | -| `SHUFFLE_ROUND_COUNT` | `90` | -| `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` | `2**14` (= 16,384) | -| `MIN_GENESIS_TIME` | `1578009600` (Jan 3, 2020) | -| `HYSTERESIS_QUOTIENT` | `4` | -| `HYSTERESIS_DOWNWARD_MULTIPLIER` | `1` | -| `HYSTERESIS_UPWARD_MULTIPLIER` | `5` | +| `MAX_COMMITTEES_PER_SLOT` | `uint64(2**6)` (= 64) | +| `TARGET_COMMITTEE_SIZE` | `uint64(2**7)` (= 128) | +| `MAX_VALIDATORS_PER_COMMITTEE` | `uint64(2**11)` (= 2,048) | +| `MIN_PER_EPOCH_CHURN_LIMIT` | `uint64(2**2)` (= 4) | +| `CHURN_LIMIT_QUOTIENT` | `uint64(2**16)` (= 65,536) | +| `SHUFFLE_ROUND_COUNT` | `uint64(90)` | +| `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` | `uint64(2**14)` (= 16,384) | +| `MIN_GENESIS_TIME` | `uint64(1578009600)` (Jan 3, 2020) | +| `HYSTERESIS_QUOTIENT` | `uint64(4)` | +| `HYSTERESIS_DOWNWARD_MULTIPLIER` | `uint64(1)` | +| `HYSTERESIS_UPWARD_MULTIPLIER` | `uint64(5)` | - For the safety of committees, `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](http://web.archive.org/web/20190504131341/https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) @@ -217,37 +217,37 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | Unit | Duration | | - | - | :-: | :-: | -| `MIN_GENESIS_DELAY` | `86400` | seconds | 1 day | -| `SECONDS_PER_SLOT` | `12` | seconds | 12 seconds | -| `MIN_ATTESTATION_INCLUSION_DELAY` | `2**0` (= 1) | slots | 12 seconds | -| `SLOTS_PER_EPOCH` | `2**5` (= 32) | slots | 6.4 minutes | -| `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | -| `MAX_SEED_LOOKAHEAD` | `2**2` (= 4) | epochs | 25.6 minutes | -| `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `2**2` (= 4) | epochs | 25.6 minutes | -| `EPOCHS_PER_ETH1_VOTING_PERIOD` | `2**5` (= 32) | epochs | ~3.4 hours | -| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~27 hours | -| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours | +<<<<<<< HEAD +| `MIN_GENESIS_DELAY` | `uint64(86400)` | seconds | 1 day | +| `SECONDS_PER_SLOT` | `uint64(12)` | seconds | 12 seconds | +| `MIN_ATTESTATION_INCLUSION_DELAY` | `uint64(2**0)` (= 1) | slots | 12 seconds | +| `SLOTS_PER_EPOCH` | `uint64(2**5)` (= 32) | slots | 6.4 minutes | +| `MIN_SEED_LOOKAHEAD` | `uint64(2**0)` (= 1) | epochs | 6.4 minutes | +| `MAX_SEED_LOOKAHEAD` | `uint64(2**2)` (= 4) | epochs | 25.6 minutes | +| `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `uint64(2**2)` (= 4) | epochs | 25.6 minutes | +| `EPOCHS_PER_ETH1_VOTING_PERIOD` | `uint64(2**5)` (= 32) | epochs | ~3.4 hours | +| `SLOTS_PER_HISTORICAL_ROOT` | `uint64(2**13)` (= 8,192) | slots | ~27 hours | +| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `uint64(2**8)` (= 256) | epochs | ~27 hours | | `SHARD_COMMITTEE_PERIOD` | `Epoch(2**8)` (= 256) | epochs | ~27 hours | - ### State list lengths | Name | Value | Unit | Duration | | - | - | :-: | :-: | -| `EPOCHS_PER_HISTORICAL_VECTOR` | `2**16` (= 65,536) | epochs | ~0.8 years | -| `EPOCHS_PER_SLASHINGS_VECTOR` | `2**13` (= 8,192) | epochs | ~36 days | -| `HISTORICAL_ROOTS_LIMIT` | `2**24` (= 16,777,216) | historical roots | ~52,262 years | -| `VALIDATOR_REGISTRY_LIMIT` | `2**40` (= 1,099,511,627,776) | validators | +| `EPOCHS_PER_HISTORICAL_VECTOR` | `uint64(2**16)` (= 65,536) | epochs | ~0.8 years | +| `EPOCHS_PER_SLASHINGS_VECTOR` | `uint64(2**13)` (= 8,192) | epochs | ~36 days | +| `HISTORICAL_ROOTS_LIMIT` | `uint64(2**24)` (= 16,777,216) | historical roots | ~52,262 years | +| `VALIDATOR_REGISTRY_LIMIT` | `uint64(2**40)` (= 1,099,511,627,776) | validators | ### Rewards and penalties | Name | Value | | - | - | -| `BASE_REWARD_FACTOR` | `2**6` (= 64) | -| `WHISTLEBLOWER_REWARD_QUOTIENT` | `2**9` (= 512) | -| `PROPOSER_REWARD_QUOTIENT` | `2**3` (= 8) | -| `INACTIVITY_PENALTY_QUOTIENT` | `2**24` (= 16,777,216) | -| `MIN_SLASHING_PENALTY_QUOTIENT` | `2**5` (= 32) | +| `BASE_REWARD_FACTOR` | `uint64(2**6)` (= 64) | +| `WHISTLEBLOWER_REWARD_QUOTIENT` | `uint64(2**9)` (= 512) | +| `PROPOSER_REWARD_QUOTIENT` | `uint64(2**3)` (= 8) | +| `INACTIVITY_PENALTY_QUOTIENT` | `uint64(2**24)` (= 16,777,216) | +| `MIN_SLASHING_PENALTY_QUOTIENT` | `uint64(2**5)` (= 32) | - The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12` epochs (about 18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating validators to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline validators after `n` epochs is about `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)`; so after `INVERSE_SQRT_E_DROP_TIME` epochs, it is roughly `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`. @@ -560,9 +560,9 @@ def integer_squareroot(n: uint64) -> uint64: x = n y = (x + 1) // 2 while y < x: - x = y + x = uint64(y) y = (x + n // x) // 2 - return x + return uint64(x) ``` #### `xor` @@ -592,7 +592,7 @@ def bytes_to_int(data: bytes) -> uint64: """ Return the integer deserialization of ``data`` interpreted as ``ENDIANNESS``-endian. """ - return int.from_bytes(data, ENDIANNESS) + return uint64(int.from_bytes(data, ENDIANNESS)) ``` ### Crypto @@ -731,14 +731,18 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) # See the 'generalized domain' algorithm on page 3 - for current_round in range(SHUFFLE_ROUND_COUNT): - pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count + for current_round in map(uint64, range(SHUFFLE_ROUND_COUNT)): + pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=uint64(1)))[0:8]) % index_count flip = (pivot + index_count - index) % index_count position = max(index, flip) - source = hash(seed + int_to_bytes(current_round, length=1) + int_to_bytes(position // 256, length=4)) + source = hash( + seed + + int_to_bytes(current_round, length=uint64(1)) + + int_to_bytes(uint64(position // 256), length=uint64(4)) + ) byte = source[(position % 256) // 8] bit = (byte >> (position % 8)) % 2 - index = flip if bit else index + index = uint64(flip) if bit else index return index ``` @@ -754,8 +758,8 @@ def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex] MAX_RANDOM_BYTE = 2**8 - 1 i = 0 while True: - candidate_index = indices[compute_shuffled_index(i % len(indices), len(indices), seed)] - random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32] + candidate_index = indices[compute_shuffled_index(uint64(i % len(indices)), uint64(len(indices)), seed)] + random_byte = hash(seed + int_to_bytes(uint64(i // 32), length=uint64(8)))[i % 32] effective_balance = state.validators[candidate_index].effective_balance if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: return candidate_index @@ -774,7 +778,7 @@ def compute_committee(indices: Sequence[ValidatorIndex], """ start = (len(indices) * index) // count end = (len(indices) * (index + 1)) // count - return [indices[compute_shuffled_index(i, len(indices), seed)] for i in range(start, end)] + return [indices[compute_shuffled_index(uint64(i), uint64(len(indices)), seed)] for i in range(start, end)] ``` #### `compute_epoch_at_slot` @@ -933,7 +937,7 @@ def get_validator_churn_limit(state: BeaconState) -> uint64: Return the validator churn limit for the current epoch. """ active_validator_indices = get_active_validator_indices(state, get_current_epoch(state)) - return max(MIN_PER_EPOCH_CHURN_LIMIT, len(active_validator_indices) // CHURN_LIMIT_QUOTIENT) + return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices) // CHURN_LIMIT_QUOTIENT)) ``` #### `get_seed` @@ -944,7 +948,7 @@ def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Bytes Return the seed at ``epoch``. """ mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) # Avoid underflow - return hash(domain_type + int_to_bytes(epoch, length=8) + mix) + return hash(domain_type + int_to_bytes(epoch, length=uint64(8)) + mix) ``` #### `get_committee_count_at_slot` @@ -955,9 +959,9 @@ def get_committee_count_at_slot(state: BeaconState, slot: Slot) -> uint64: Return the number of committees at ``slot``. """ epoch = compute_epoch_at_slot(slot) - return max(1, min( + return max(uint64(1), min( MAX_COMMITTEES_PER_SLOT, - len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE, + uint64(len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE), )) ``` @@ -973,8 +977,8 @@ def get_beacon_committee(state: BeaconState, slot: Slot, index: CommitteeIndex) return compute_committee( indices=get_active_validator_indices(state, epoch), seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER), - index=(slot % SLOTS_PER_EPOCH) * committees_per_slot + index, - count=committees_per_slot * SLOTS_PER_EPOCH, + index=uint64((slot % SLOTS_PER_EPOCH) * committees_per_slot + index), + count=uint64(committees_per_slot * SLOTS_PER_EPOCH), ) ``` @@ -986,7 +990,7 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: Return the beacon proposer index at the current slot. """ epoch = get_current_epoch(state) - seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=8)) + seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=uint64(8))) indices = get_active_validator_indices(state, epoch) return compute_proposer_index(state, indices, seed) ``` @@ -1094,7 +1098,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: exit_queue_epoch = max(exit_epochs + [compute_activation_exit_epoch(get_current_epoch(state))]) exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch]) if exit_queue_churn >= get_validator_churn_limit(state): - exit_queue_epoch += Epoch(1) + exit_queue_epoch = Epoch(exit_queue_epoch + 1) # Set validator exit epoch and withdrawable epoch validator.exit_epoch = exit_queue_epoch @@ -1728,13 +1732,13 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: assert is_valid_merkle_branch( leaf=hash_tree_root(deposit.data), branch=deposit.proof, - depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in + depth=uint64(DEPOSIT_CONTRACT_TREE_DEPTH + 1), # Add 1 for the List length mix-in index=state.eth1_deposit_index, root=state.eth1_data.deposit_root, ) # Deposits must be processed in order - state.eth1_deposit_index += 1 + state.eth1_deposit_index = uint64(state.eth1_deposit_index + 1) pubkey = deposit.data.pubkey amount = deposit.data.amount diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index 4ed2733e1..fb5740ae5 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -100,7 +100,7 @@ _The block for `anchor_root` is incorrectly initialized to the block header, rat ```python def get_forkchoice_store(anchor_state: BeaconState) -> Store: - anchor_block_header = anchor_state.latest_block_header.copy() + anchor_block_header: BeaconBlockHeader = anchor_state.latest_block_header.copy() if anchor_block_header.state_root == Bytes32(): anchor_block_header.state_root = hash_tree_root(anchor_state) anchor_root = hash_tree_root(anchor_block_header) @@ -108,7 +108,7 @@ def get_forkchoice_store(anchor_state: BeaconState) -> Store: justified_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root) finalized_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root) return Store( - time=anchor_state.genesis_time + SECONDS_PER_SLOT * anchor_state.slot, + time=uint64(anchor_state.genesis_time + SECONDS_PER_SLOT * anchor_state.slot), genesis_time=anchor_state.genesis_time, justified_checkpoint=justified_checkpoint, finalized_checkpoint=finalized_checkpoint, @@ -300,7 +300,7 @@ def validate_on_attestation(store: Store, attestation: Attestation) -> None: def store_target_checkpoint_state(store: Store, target: Checkpoint) -> None: # Store target checkpoint state if not yet seen if target not in store.checkpoint_states: - base_state = store.block_states[target.root].copy() + base_state: BeaconState = store.block_states[target.root].copy() process_slots(base_state, compute_start_slot_at_epoch(target.epoch)) store.checkpoint_states[target] = base_state ``` @@ -344,7 +344,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: block = signed_block.message # Make a copy of the state to avoid mutability issues assert block.parent_root in store.block_states - pre_state = store.block_states[block.parent_root].copy() + pre_state: BeaconState = store.block_states[block.parent_root].copy() # Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past. assert get_current_slot(store) >= block.slot # Add new block to the store diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index adf23c840..630d42950 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -270,7 +270,7 @@ An honest block proposer sets `block.body.eth1_data = get_eth1_vote(state)` wher ```python def compute_time_at_slot(state: BeaconState, slot: Slot) -> uint64: - return state.genesis_time + slot * SECONDS_PER_SLOT + return uint64(state.genesis_time + slot * SECONDS_PER_SLOT) ``` ```python From 88d7315739b475f3297151f27866f8bef39cc7f5 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 15 May 2020 21:49:17 +0800 Subject: [PATCH 02/43] Fix typing --- specs/phase0/beacon-chain.md | 4 ++-- specs/phase0/validator.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index d1f97a506..2678a379f 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -1476,12 +1476,12 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence _, inactivity_penalties = get_inactivity_penalty_deltas(state) rewards = [ - source_rewards[i] + target_rewards[i] + head_rewards[i] + inclusion_delay_rewards[i] + Gwei(source_rewards[i] + target_rewards[i] + head_rewards[i] + inclusion_delay_rewards[i]) for i in range(len(state.validators)) ] penalties = [ - source_penalties[i] + target_penalties[i] + head_penalties[i] + inactivity_penalties[i] + Gwei(source_penalties[i] + target_penalties[i] + head_penalties[i] + inactivity_penalties[i]) for i in range(len(state.validators)) ] diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index 630d42950..805498250 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -430,7 +430,7 @@ def compute_subnet_for_attestation(state: BeaconState, attestation: Attestation) slots_since_epoch_start = attestation.data.slot % SLOTS_PER_EPOCH committees_since_epoch_start = get_committee_count_at_slot(state, attestation.data.slot) * slots_since_epoch_start - return (committees_since_epoch_start + attestation.data.index) % ATTESTATION_SUBNET_COUNT + return uint64((committees_since_epoch_start + attestation.data.index) % ATTESTATION_SUBNET_COUNT) ``` ### Attestation aggregation From 9f340d5fd1b0b2e0b7c014e24a9c0787b433baea Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 15 May 2020 21:53:14 +0800 Subject: [PATCH 03/43] (i) Fix leftover (ii) `SHARD_COMMITTEE_PERIOD` should be uint64 --- specs/phase0/beacon-chain.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 2678a379f..60ed518f6 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -217,7 +217,6 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | Unit | Duration | | - | - | :-: | :-: | -<<<<<<< HEAD | `MIN_GENESIS_DELAY` | `uint64(86400)` | seconds | 1 day | | `SECONDS_PER_SLOT` | `uint64(12)` | seconds | 12 seconds | | `MIN_ATTESTATION_INCLUSION_DELAY` | `uint64(2**0)` (= 1) | slots | 12 seconds | @@ -228,7 +227,7 @@ The following values are (non-configurable) constants used throughout the specif | `EPOCHS_PER_ETH1_VOTING_PERIOD` | `uint64(2**5)` (= 32) | epochs | ~3.4 hours | | `SLOTS_PER_HISTORICAL_ROOT` | `uint64(2**13)` (= 8,192) | slots | ~27 hours | | `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `uint64(2**8)` (= 256) | epochs | ~27 hours | -| `SHARD_COMMITTEE_PERIOD` | `Epoch(2**8)` (= 256) | epochs | ~27 hours | +| `SHARD_COMMITTEE_PERIOD` | `uint64(2**8)` (= 256) | epochs | ~27 hours | ### State list lengths From 8f70453aeff73b37304abc77983af6b4d4dac1e1 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 15 May 2020 23:09:07 +0800 Subject: [PATCH 04/43] PR feedback from Proto --- specs/phase0/beacon-chain.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 60ed518f6..4f2f98458 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -732,7 +732,7 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> # See the 'generalized domain' algorithm on page 3 for current_round in map(uint64, range(SHUFFLE_ROUND_COUNT)): pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=uint64(1)))[0:8]) % index_count - flip = (pivot + index_count - index) % index_count + flip = uint64((pivot + index_count - index) % index_count) position = max(index, flip) source = hash( seed @@ -741,7 +741,7 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> ) byte = source[(position % 256) // 8] bit = (byte >> (position % 8)) % 2 - index = uint64(flip) if bit else index + index = flip if bit else index return index ``` @@ -1097,7 +1097,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: exit_queue_epoch = max(exit_epochs + [compute_activation_exit_epoch(get_current_epoch(state))]) exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch]) if exit_queue_churn >= get_validator_churn_limit(state): - exit_queue_epoch = Epoch(exit_queue_epoch + 1) + exit_queue_epoch += Epoch(1) # Set validator exit epoch and withdrawable epoch validator.exit_epoch = exit_queue_epoch From cd91380d8056cf86ef98690a5ee7192db5a4f824 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 18 May 2020 14:34:12 +0800 Subject: [PATCH 05/43] PR feedback from proto: set `length` back to `int` --- specs/phase0/beacon-chain.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 4f2f98458..8e212ba13 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -577,7 +577,7 @@ def xor(bytes_1: Bytes32, bytes_2: Bytes32) -> Bytes32: #### `int_to_bytes` ```python -def int_to_bytes(n: uint64, length: uint64) -> bytes: +def int_to_bytes(n: uint64, length: int) -> bytes: """ Return the ``length``-byte serialization of ``n`` in ``ENDIANNESS``-endian. """ @@ -731,13 +731,13 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) # See the 'generalized domain' algorithm on page 3 for current_round in map(uint64, range(SHUFFLE_ROUND_COUNT)): - pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=uint64(1)))[0:8]) % index_count + pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count flip = uint64((pivot + index_count - index) % index_count) position = max(index, flip) source = hash( seed - + int_to_bytes(current_round, length=uint64(1)) - + int_to_bytes(uint64(position // 256), length=uint64(4)) + + int_to_bytes(current_round, length=1) + + int_to_bytes(uint64(position // 256), length=4) ) byte = source[(position % 256) // 8] bit = (byte >> (position % 8)) % 2 @@ -758,7 +758,7 @@ def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex] i = 0 while True: candidate_index = indices[compute_shuffled_index(uint64(i % len(indices)), uint64(len(indices)), seed)] - random_byte = hash(seed + int_to_bytes(uint64(i // 32), length=uint64(8)))[i % 32] + random_byte = hash(seed + int_to_bytes(uint64(i // 32), length=8))[i % 32] effective_balance = state.validators[candidate_index].effective_balance if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: return candidate_index @@ -947,7 +947,7 @@ def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Bytes Return the seed at ``epoch``. """ mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) # Avoid underflow - return hash(domain_type + int_to_bytes(epoch, length=uint64(8)) + mix) + return hash(domain_type + int_to_bytes(epoch, length=8) + mix) ``` #### `get_committee_count_at_slot` @@ -989,7 +989,7 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: Return the beacon proposer index at the current slot. """ epoch = get_current_epoch(state) - seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=uint64(8))) + seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=8)) indices = get_active_validator_indices(state, epoch) return compute_proposer_index(state, indices, seed) ``` From 4428a6aedffc66162edec92431ce0b127b5874d5 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 18 Jun 2020 13:45:32 +0800 Subject: [PATCH 06/43] Fix table --- specs/phase0/beacon-chain.md | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 74c09a0c5..f8357be96 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -218,9 +218,9 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | Unit | Duration | | - | - | :-: | :-: | -<<<<<<< HEAD -| `MIN_GENESIS_DELAY` | `uint64(86400)` | seconds | 1 day | +| `GENESIS_DELAY` | `uint64(172800)` | seconds | 2 days | | `SECONDS_PER_SLOT` | `uint64(12)` | seconds | 12 seconds | +| `SECONDS_PER_ETH1_BLOCK` | `uint64(14)` | seconds | 14 seconds | | `MIN_ATTESTATION_INCLUSION_DELAY` | `uint64(2**0)` (= 1) | slots | 12 seconds | | `SLOTS_PER_EPOCH` | `uint64(2**5)` (= 32) | slots | 6.4 minutes | | `MIN_SEED_LOOKAHEAD` | `uint64(2**0)` (= 1) | epochs | 6.4 minutes | @@ -230,20 +230,6 @@ The following values are (non-configurable) constants used throughout the specif | `SLOTS_PER_HISTORICAL_ROOT` | `uint64(2**13)` (= 8,192) | slots | ~27 hours | | `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `uint64(2**8)` (= 256) | epochs | ~27 hours | | `SHARD_COMMITTEE_PERIOD` | `uint64(2**8)` (= 256) | epochs | ~27 hours | -======= -| `GENESIS_DELAY` | `172800` | seconds | 2 days | -| `SECONDS_PER_SLOT` | `12` | seconds | 12 seconds | -| `SECONDS_PER_ETH1_BLOCK` | `14` | seconds | 14 seconds | -| `MIN_ATTESTATION_INCLUSION_DELAY` | `2**0` (= 1) | slots | 12 seconds | -| `SLOTS_PER_EPOCH` | `2**5` (= 32) | slots | 6.4 minutes | -| `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | -| `MAX_SEED_LOOKAHEAD` | `2**2` (= 4) | epochs | 25.6 minutes | -| `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `2**2` (= 4) | epochs | 25.6 minutes | -| `EPOCHS_PER_ETH1_VOTING_PERIOD` | `2**5` (= 32) | epochs | ~3.4 hours | -| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~27 hours | -| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours | -| `SHARD_COMMITTEE_PERIOD` | `Epoch(2**8)` (= 256) | epochs | ~27 hours | ->>>>>>> dev ### State list lengths From 26c540fbf9fa0dceb7b09210f82af6e7a8b71c84 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 18 Jun 2020 14:07:59 +0800 Subject: [PATCH 07/43] Fix `get_min_new_period_epochs` helper --- .../eth2spec/test/validator/test_validator_unittest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/validator/test_validator_unittest.py b/tests/core/pyspec/eth2spec/test/validator/test_validator_unittest.py index e749b1e3d..f5346e4e1 100644 --- a/tests/core/pyspec/eth2spec/test/validator/test_validator_unittest.py +++ b/tests/core/pyspec/eth2spec/test/validator/test_validator_unittest.py @@ -40,9 +40,9 @@ def run_is_candidate_block(spec, eth1_block, period_start, success=True): def get_min_new_period_epochs(spec): - return int( - spec.SECONDS_PER_ETH1_BLOCK * spec.ETH1_FOLLOW_DISTANCE * 2 # to seconds - / spec.SECONDS_PER_SLOT / spec.SLOTS_PER_EPOCH + return ( + (spec.SECONDS_PER_ETH1_BLOCK * spec.ETH1_FOLLOW_DISTANCE * 2) # to seconds + // spec.SECONDS_PER_SLOT // spec.SLOTS_PER_EPOCH ) From a061758a664f74f992d5b7ec32913e8ef8bb768e Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 25 Jun 2020 17:52:05 +0800 Subject: [PATCH 08/43] Use `encode_bytes` to implement `int_to_bytes` Rename `bytes_to_int` to `bytes_to_uint64` Use `encode_bytes` to implement `int_to_bytes` Rename `int_to_bytes` to `uint_to_bytes` and move it to `ssz_impl.py` --- setup.py | 8 ++-- specs/phase0/beacon-chain.md | 40 +++++++++---------- specs/phase0/validator.md | 2 +- specs/phase1/beacon-chain.md | 4 +- specs/phase1/validator.md | 2 +- .../pyspec/eth2spec/utils/ssz/ssz_impl.py | 5 +++ 6 files changed, 32 insertions(+), 29 deletions(-) diff --git a/setup.py b/setup.py index cc3b00da9..86985b96d 100644 --- a/setup.py +++ b/setup.py @@ -94,9 +94,9 @@ from dataclasses import ( from lru import LRU -from eth2spec.utils.ssz.ssz_impl import hash_tree_root +from eth2spec.utils.ssz.ssz_impl import hash_tree_root, uint_to_bytes from eth2spec.utils.ssz.ssz_typing import ( - View, boolean, Container, List, Vector, uint64, + View, boolean, Container, List, Vector, uint8, uint32, uint64, Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector, ) from eth2spec.utils import bls @@ -118,9 +118,9 @@ from dataclasses import ( from lru import LRU -from eth2spec.utils.ssz.ssz_impl import hash_tree_root +from eth2spec.utils.ssz.ssz_impl import hash_tree_root, uint_to_bytes from eth2spec.utils.ssz.ssz_typing import ( - View, boolean, Container, List, Vector, uint64, uint8, bit, + View, boolean, Container, List, Vector, uint8, uint32, uint64, bit, ByteList, ByteVector, Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector, ) from eth2spec.utils import bls diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index cd52762d5..df3661810 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -55,8 +55,8 @@ - [Math](#math) - [`integer_squareroot`](#integer_squareroot) - [`xor`](#xor) - - [`int_to_bytes`](#int_to_bytes) - - [`bytes_to_int`](#bytes_to_int) + - [`uint_to_bytes`](#uint_to_bytes) + - [`bytes_to_uint64`](#bytes_to_uint64) - [Crypto](#crypto) - [`hash`](#hash) - [`hash_tree_root`](#hash_tree_root) @@ -576,20 +576,14 @@ def xor(bytes_1: Bytes32, bytes_2: Bytes32) -> Bytes32: return Bytes32(a ^ b for a, b in zip(bytes_1, bytes_2)) ``` -#### `int_to_bytes` +#### `uint_to_bytes` + +`def uint_to_bytes(n: uint8) -> bytes` is a function for serializing the `uint` type object to bytes in ``ENDIANNESS``-endian. The expected length of the output is the byte-length of the `uint` type. + +#### `bytes_to_uint64` ```python -def int_to_bytes(n: uint64, length: uint64) -> bytes: - """ - Return the ``length``-byte serialization of ``n`` in ``ENDIANNESS``-endian. - """ - return n.to_bytes(length, ENDIANNESS) -``` - -#### `bytes_to_int` - -```python -def bytes_to_int(data: bytes) -> uint64: +def bytes_to_uint64(data: bytes) -> uint64: """ Return the integer deserialization of ``data`` interpreted as ``ENDIANNESS``-endian. """ @@ -732,11 +726,15 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) # See the 'generalized domain' algorithm on page 3 - for current_round in range(SHUFFLE_ROUND_COUNT): - pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count - flip = (pivot + index_count - index) % index_count + for current_round in map(uint8, range(SHUFFLE_ROUND_COUNT)): + pivot = bytes_to_uint64(hash(seed + uint_to_bytes(current_round))[0:8]) % index_count + flip = uint64((pivot + index_count - index) % index_count) position = max(index, flip) - source = hash(seed + int_to_bytes(current_round, length=1) + int_to_bytes(position // 256, length=4)) + source = hash( + seed + + uint_to_bytes(current_round) + + uint_to_bytes(uint32(position // 256)) + ) byte = source[(position % 256) // 8] bit = (byte >> (position % 8)) % 2 index = flip if bit else index @@ -756,7 +754,7 @@ def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex] i = 0 while True: candidate_index = indices[compute_shuffled_index(i % len(indices), len(indices), seed)] - random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32] + random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32] effective_balance = state.validators[candidate_index].effective_balance if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: return candidate_index @@ -945,7 +943,7 @@ def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Bytes Return the seed at ``epoch``. """ mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) # Avoid underflow - return hash(domain_type + int_to_bytes(epoch, length=8) + mix) + return hash(domain_type + uint_to_bytes(epoch) + mix) ``` #### `get_committee_count_per_slot` @@ -986,7 +984,7 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: Return the beacon proposer index at the current slot. """ epoch = get_current_epoch(state) - seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=8)) + seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + uint_to_bytes(state.slot)) indices = get_active_validator_indices(state, epoch) return compute_proposer_index(state, indices, seed) ``` diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index e325f75f6..6a895e1fa 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -465,7 +465,7 @@ def get_slot_signature(state: BeaconState, slot: Slot, privkey: int) -> BLSSigna def is_aggregator(state: BeaconState, slot: Slot, index: CommitteeIndex, slot_signature: BLSSignature) -> bool: committee = get_beacon_committee(state, slot, index) modulo = max(1, len(committee) // TARGET_AGGREGATORS_PER_COMMITTEE) - return bytes_to_int(hash(slot_signature)[0:8]) % modulo == 0 + return bytes_to_uint64(hash(slot_signature)[0:8]) % modulo == 0 ``` #### Construct aggregate diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index e103d51f4..43cdcff78 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -581,8 +581,8 @@ def get_shard_proposer_index(beacon_state: BeaconState, slot: Slot, shard: Shard """ epoch = compute_epoch_at_slot(slot) committee = get_shard_committee(beacon_state, epoch, shard) - seed = hash(get_seed(beacon_state, epoch, DOMAIN_SHARD_COMMITTEE) + int_to_bytes(slot, length=8)) - r = bytes_to_int(seed[:8]) + seed = hash(get_seed(beacon_state, epoch, DOMAIN_SHARD_COMMITTEE) + uint_to_bytes(slot)) + r = bytes_to_uint64(seed[:8]) return committee[r % len(committee)] ``` diff --git a/specs/phase1/validator.md b/specs/phase1/validator.md index 8b7ade60b..26e769dff 100644 --- a/specs/phase1/validator.md +++ b/specs/phase1/validator.md @@ -463,7 +463,7 @@ def get_light_client_slot_signature(state: BeaconState, slot: Slot, privkey: int def is_light_client_aggregator(state: BeaconState, slot: Slot, slot_signature: BLSSignature) -> bool: committee = get_light_client_committee(state, compute_epoch_at_slot(slot)) modulo = max(1, len(committee) // TARGET_LIGHT_CLIENT_AGGREGATORS_PER_SLOT) - return bytes_to_int(hash(slot_signature)[0:8]) % modulo == 0 + return bytes_to_uint64(hash(slot_signature)[0:8]) % modulo == 0 ``` #### Construct aggregate diff --git a/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py b/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py index 34e6c4ee8..a47c77c33 100644 --- a/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py +++ b/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py @@ -1,3 +1,4 @@ +from remerkleable.basic import uint from remerkleable.core import View from remerkleable.byte_arrays import Bytes32 @@ -8,3 +9,7 @@ def serialize(obj: View) -> bytes: def hash_tree_root(obj: View) -> Bytes32: return Bytes32(obj.get_backing().merkle_root()) + + +def uint_to_bytes(n: uint) -> bytes: + return serialize(n) From 531184f42bbda5a5d2e8244b583014bbd2f2225b Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 26 Jun 2020 15:41:47 +0200 Subject: [PATCH 09/43] Infer types where possible, e.g. uint64+uint64=uint64 --- specs/phase0/beacon-chain.md | 37 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index ec4751fc3..df9390964 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -561,9 +561,9 @@ def integer_squareroot(n: uint64) -> uint64: x = n y = (x + 1) // 2 while y < x: - x = uint64(y) + x = y y = (x + n // x) // 2 - return uint64(x) + return x ``` #### `xor` @@ -732,15 +732,11 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) # See the 'generalized domain' algorithm on page 3 - for current_round in map(uint64, range(SHUFFLE_ROUND_COUNT)): + for current_round in range(SHUFFLE_ROUND_COUNT): pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count - flip = uint64((pivot + index_count - index) % index_count) + flip = (pivot + index_count - index) % index_count position = max(index, flip) - source = hash( - seed - + int_to_bytes(current_round, length=1) - + int_to_bytes(uint64(position // 256), length=4) - ) + source = hash(seed + int_to_bytes(current_round, length=1) + int_to_bytes(position // 256, length=4)) byte = source[(position % 256) // 8] bit = (byte >> (position % 8)) % 2 index = flip if bit else index @@ -757,10 +753,11 @@ def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex] """ assert len(indices) > 0 MAX_RANDOM_BYTE = 2**8 - 1 - i = 0 + i = uint64(0) + total = uint64(len(indices)) while True: - candidate_index = indices[compute_shuffled_index(uint64(i % len(indices)), uint64(len(indices)), seed)] - random_byte = hash(seed + int_to_bytes(uint64(i // 32), length=8))[i % 32] + candidate_index = indices[compute_shuffled_index(i % total, total, seed)] + random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32] effective_balance = state.validators[candidate_index].effective_balance if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: return candidate_index @@ -938,7 +935,7 @@ def get_validator_churn_limit(state: BeaconState) -> uint64: Return the validator churn limit for the current epoch. """ active_validator_indices = get_active_validator_indices(state, get_current_epoch(state)) - return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices) // CHURN_LIMIT_QUOTIENT)) + return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT) ``` #### `get_seed` @@ -961,7 +958,7 @@ def get_committee_count_per_slot(state: BeaconState, epoch: Epoch) -> uint64: """ return max(uint64(1), min( MAX_COMMITTEES_PER_SLOT, - uint64(len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE), + uint64(len(get_active_validator_indices(state, epoch))) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE, )) ``` @@ -977,8 +974,8 @@ def get_beacon_committee(state: BeaconState, slot: Slot, index: CommitteeIndex) return compute_committee( indices=get_active_validator_indices(state, epoch), seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER), - index=uint64((slot % SLOTS_PER_EPOCH) * committees_per_slot + index), - count=uint64(committees_per_slot * SLOTS_PER_EPOCH), + index=(slot % SLOTS_PER_EPOCH) * committees_per_slot + index, + count=committees_per_slot * SLOTS_PER_EPOCH, ) ``` @@ -1501,12 +1498,12 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence _, inactivity_penalties = get_inactivity_penalty_deltas(state) rewards = [ - Gwei(source_rewards[i] + target_rewards[i] + head_rewards[i] + inclusion_delay_rewards[i]) + source_rewards[i] + target_rewards[i] + head_rewards[i] + inclusion_delay_rewards[i] for i in range(len(state.validators)) ] penalties = [ - Gwei(source_penalties[i] + target_penalties[i] + head_penalties[i] + inactivity_penalties[i]) + source_penalties[i] + target_penalties[i] + head_penalties[i] + inactivity_penalties[i] for i in range(len(state.validators)) ] @@ -1773,13 +1770,13 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: assert is_valid_merkle_branch( leaf=hash_tree_root(deposit.data), branch=deposit.proof, - depth=uint64(DEPOSIT_CONTRACT_TREE_DEPTH + 1), # Add 1 for the List length mix-in + depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in index=state.eth1_deposit_index, root=state.eth1_data.deposit_root, ) # Deposits must be processed in order - state.eth1_deposit_index = uint64(state.eth1_deposit_index + 1) + state.eth1_deposit_index += 1 pubkey = deposit.data.pubkey amount = deposit.data.amount From 3fb0257cbb8cd95336d4b57fb42fa5e02a74baf4 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 26 Jun 2020 15:55:07 +0200 Subject: [PATCH 10/43] update remerkleable for uint and mypy improvements --- setup.py | 2 +- tests/core/pyspec/eth2spec/utils/ssz/ssz_typing.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 85d1a95ea..801f215d0 100644 --- a/setup.py +++ b/setup.py @@ -519,7 +519,7 @@ setup( "py_ecc==4.0.0", "milagro_bls_binding==1.3.0", "dataclasses==0.6", - "remerkleable==0.1.16", + "remerkleable==0.1.17", "ruamel.yaml==0.16.5", "lru-dict==1.1.6" ] diff --git a/tests/core/pyspec/eth2spec/utils/ssz/ssz_typing.py b/tests/core/pyspec/eth2spec/utils/ssz/ssz_typing.py index e6536c748..b3a0b9962 100644 --- a/tests/core/pyspec/eth2spec/utils/ssz/ssz_typing.py +++ b/tests/core/pyspec/eth2spec/utils/ssz/ssz_typing.py @@ -5,4 +5,4 @@ from remerkleable.complex import Container, Vector, List from remerkleable.basic import boolean, bit, uint, byte, uint8, uint16, uint32, uint64, uint128, uint256 from remerkleable.bitfields import Bitvector, Bitlist from remerkleable.byte_arrays import ByteVector, Bytes1, Bytes4, Bytes8, Bytes32, Bytes48, Bytes96, ByteList -from remerkleable.core import BasicView, View, TypeDef +from remerkleable.core import BasicView, View From 3b7617f51ace8161b5fc89ea1eaf2ee102f200c5 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 26 Jun 2020 16:13:38 +0200 Subject: [PATCH 11/43] make extracted byte uint8 for bitshift, do not use negative slice indexing, avoid negative comparison in test --- setup.py | 2 +- specs/phase0/beacon-chain.md | 4 ++-- .../epoch_processing/test_process_rewards_and_penalties.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 801f215d0..3a4fd1e18 100644 --- a/setup.py +++ b/setup.py @@ -96,7 +96,7 @@ from lru import LRU from eth2spec.utils.ssz.ssz_impl import hash_tree_root, copy from eth2spec.utils.ssz.ssz_typing import ( - View, boolean, Container, List, Vector, uint64, + View, boolean, Container, List, Vector, uint64, uint8, Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector, ) from eth2spec.utils import bls diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index df9390964..c7dd25d3a 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -737,7 +737,7 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> flip = (pivot + index_count - index) % index_count position = max(index, flip) source = hash(seed + int_to_bytes(current_round, length=1) + int_to_bytes(position // 256, length=4)) - byte = source[(position % 256) // 8] + byte = uint8(source[(position % 256) // 8]) bit = (byte >> (position % 8)) % 2 index = flip if bit else index @@ -1317,7 +1317,7 @@ def process_justification_and_finalization(state: BeaconState) -> None: # Process justifications state.previous_justified_checkpoint = state.current_justified_checkpoint - state.justification_bits[1:] = state.justification_bits[:-1] + state.justification_bits[1:] = state.justification_bits[:JUSTIFICATION_BITS_LENGTH - 1] state.justification_bits[0] = 0b0 matching_target_attestations = get_matching_target_attestations(state, previous_epoch) # Previous epoch if get_attesting_balance(state, matching_target_attestations) * 3 >= get_total_active_balance(state) * 2: diff --git a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py index d3744f897..72bdd3e92 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py +++ b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py @@ -48,7 +48,7 @@ def test_genesis_epoch_full_attestations_no_rewards(spec, state): attestation = get_valid_attestation(spec, state, signed=True) attestations.append(attestation) # fill each created slot in state after inclusion delay - if slot - spec.MIN_ATTESTATION_INCLUSION_DELAY >= 0: + if slot >= spec.MIN_ATTESTATION_INCLUSION_DELAY: include_att = attestations[slot - spec.MIN_ATTESTATION_INCLUSION_DELAY] add_attestations_to_state(spec, state, [include_att], state.slot) next_slot(spec, state) From 1d954ee9bd0c2a925b02666ec4dbde237e2857c7 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 29 Jun 2020 12:33:52 +0800 Subject: [PATCH 12/43] PR feedback from @ericsson49 Co-authored-by: Alex Vlasov --- specs/phase0/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index df3661810..ba701c2b1 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -578,7 +578,7 @@ def xor(bytes_1: Bytes32, bytes_2: Bytes32) -> Bytes32: #### `uint_to_bytes` -`def uint_to_bytes(n: uint8) -> bytes` is a function for serializing the `uint` type object to bytes in ``ENDIANNESS``-endian. The expected length of the output is the byte-length of the `uint` type. +`def uint_to_bytes(n: uint) -> bytes` is a function for serializing the `uint` type object to bytes in ``ENDIANNESS``-endian. The expected length of the output is the byte-length of the `uint` type. #### `bytes_to_uint64` From 4b239e94b742a89df26fff5a74feac8815f7463c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 29 Jun 2020 12:50:01 +0800 Subject: [PATCH 13/43] Mix PR feedback from Danny and Proto --- specs/phase0/beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index ba701c2b1..1994b8691 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -726,13 +726,13 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) # See the 'generalized domain' algorithm on page 3 - for current_round in map(uint8, range(SHUFFLE_ROUND_COUNT)): - pivot = bytes_to_uint64(hash(seed + uint_to_bytes(current_round))[0:8]) % index_count - flip = uint64((pivot + index_count - index) % index_count) + for current_round in range(SHUFFLE_ROUND_COUNT): + pivot = bytes_to_uint64(hash(seed + uint_to_bytes(uint8(current_round)))[0:8]) % index_count + flip = (pivot + index_count - index) % index_count position = max(index, flip) source = hash( seed - + uint_to_bytes(current_round) + + uint_to_bytes(uint8(current_round)) + uint_to_bytes(uint32(position // 256)) ) byte = source[(position % 256) // 8] From 07274736b978cf61b05739d1d86abbf29cc1b69d Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 8 Jul 2020 01:42:09 +0200 Subject: [PATCH 14/43] Match gossip v1.1 D_low, extend gossip_history param, add FAQ section --- specs/phase0/p2p-interface.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index f0c3c80b6..b26bfeeb2 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -79,6 +79,7 @@ It consists of four main sections: - [Why must all clients use the same gossip topic instead of one negotiated between each peer pair?](#why-must-all-clients-use-the-same-gossip-topic-instead-of-one-negotiated-between-each-peer-pair) - [Why are the topics strings and not hashes?](#why-are-the-topics-strings-and-not-hashes) - [Why are we overriding the default libp2p pubsub `message-id`?](#why-are-we-overriding-the-default-libp2p-pubsub-message-id) + - [Why are these specific gossip parameters chosen?](#why-are-these-specific-gossip-parameters-chosen) - [Why is there `MAXIMUM_GOSSIP_CLOCK_DISPARITY` when validating slot ranges of messages in gossip subnets?](#why-is-there-maximum_gossip_clock_disparity-when-validating-slot-ranges-of-messages-in-gossip-subnets) - [Why are there `ATTESTATION_SUBNET_COUNT` attestation subnets?](#why-are-there-attestation_subnet_count-attestation-subnets) - [Why are attestations limited to be broadcast on gossip channels within `SLOTS_PER_EPOCH` slots?](#why-are-attestations-limited-to-be-broadcast-on-gossip-channels-within-slots_per_epoch-slots) @@ -213,12 +214,12 @@ including the [gossipsub v1.1](https://github.com/libp2p/specs/blob/master/pubsu The following gossipsub [parameters](https://github.com/libp2p/specs/tree/master/pubsub/gossipsub#meshsub-an-overlay-mesh-router) will be used: - `D` (topic stable mesh target count): 6 -- `D_low` (topic stable mesh low watermark): 4 +- `D_low` (topic stable mesh low watermark): 5 - `D_high` (topic stable mesh high watermark): 12 - `D_lazy` (gossip target): 6 - `fanout_ttl` (ttl for fanout maps for topics we are not subscribed to but have published to, seconds): 60 - `gossip_advertise` (number of windows to gossip about): 3 -- `gossip_history` (number of heartbeat intervals to retain message IDs): 5 +- `gossip_history` (number of heartbeat intervals to retain message IDs): 385 - `heartbeat_interval` (frequency of heartbeat, seconds): 1 ### Topics and messages @@ -1146,6 +1147,17 @@ Some examples of where messages could be duplicated: Partial aggregates could be duplicated * Clients re-publishing seen messages +### Why are these specific gossip parameters chosen? + +- `D`, `D_low`, `D_high`, `D_lazy`: recommended defaults. +- `fanout_ttl`: 60 (TODO: increase to cover an epoch?) +- `gossip_advertise`: 3 (TODO: to increase to 6?) +- `gossip_history`: `SLOTS_PER_EPOCH * SECONDS_PER_SLOT / heartbeat_interval = approx. 385`. Attestation validity is bounded by an epoch, so this is the safe max bound. +- `heartbeat_interval`: 1 second, recommended default. + For Eth2 specifically, 0.6-0.7s was recommended in the [GossipSub evaluation report by Protocol Labs](https://gateway.ipfs.io/ipfs/QmRAFP5DBnvNjdYSbWhEhVRJJDFCLpPyvew5GwCCB4VxM4). + And this parameter will be experimented with in testnets. + + ### Why is there `MAXIMUM_GOSSIP_CLOCK_DISPARITY` when validating slot ranges of messages in gossip subnets? For some gossip channels (e.g. those for Attestations and BeaconBlocks), From cf42fd4828e36ad368fd31b88cda809bd83e91b9 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 16 Jul 2020 16:34:21 +0800 Subject: [PATCH 15/43] Rename `PHASE_1_GENESIS_SLOT` to `PHASE_1_FORK_SLOT` and set it to `Slot(0)` for testing. --- configs/README.md | 2 +- configs/mainnet/phase1.yaml | 2 +- configs/minimal/phase1.yaml | 4 +- specs/phase1/beacon-chain.md | 27 ++++++++------ specs/phase1/phase1-fork.md | 8 ++-- specs/phase1/shard-fork-choice.md | 2 +- specs/phase1/validator.md | 3 ++ tests/core/pyspec/eth2spec/test/context.py | 3 -- .../test/fork_choice/test_on_shard_head.py | 12 +++--- .../eth2spec/test/helpers/attestations.py | 28 ++++++++------ .../pyspec/eth2spec/test/helpers/state.py | 6 +-- .../test_process_chunk_challenge.py | 20 +++++++--- .../test_process_custody_slashing.py | 1 + .../test_process_shard_transition.py | 2 +- .../test_process_challenge_deadlines.py | 5 ++- .../test_process_custody_final_updates.py | 12 ++++-- .../test/phase1/sanity/test_blocks.py | 12 +++--- .../test/phase1/sanity/test_shard_blocks.py | 37 +++++++++++++------ 18 files changed, 112 insertions(+), 74 deletions(-) diff --git a/configs/README.md b/configs/README.md index be3c60e6f..353cd35db 100644 --- a/configs/README.md +++ b/configs/README.md @@ -15,7 +15,7 @@ Over time, the need to sync an older state may be deprecated. In this case, the prefix on the new constant may be removed, and the old constant will keep a special name before completely being removed. A previous iteration of forking made use of "timelines", but this collides with the definitions used in the spec (constants for special forking slots, etc.), and was not integrated sufficiently in any of the spec tools or implementations. -Instead, the config essentially doubles as fork definition now, e.g. changing the value for `PHASE_1_GENESIS_SLOT` changes the fork. +Instead, the config essentially doubles as fork definition now, e.g. changing the value for `PHASE_1_FORK_SLOT` changes the fork. Another reason to prefer forking through constants is the ability to program a forking moment based on context, instead of being limited to a static slot number. diff --git a/configs/mainnet/phase1.yaml b/configs/mainnet/phase1.yaml index 8c0800957..a9c7f89cc 100644 --- a/configs/mainnet/phase1.yaml +++ b/configs/mainnet/phase1.yaml @@ -5,7 +5,7 @@ # --------------------------------------------------------------- PHASE_1_FORK_VERSION: 0x01000000 # [STUB] -PHASE_1_GENESIS_SLOT: 32 +PHASE_1_FORK_SLOT: 0 INITIAL_ACTIVE_SHARDS: 64 diff --git a/configs/minimal/phase1.yaml b/configs/minimal/phase1.yaml index 7fbc7e5a3..09e288900 100644 --- a/configs/minimal/phase1.yaml +++ b/configs/minimal/phase1.yaml @@ -5,8 +5,8 @@ # --------------------------------------------------------------- # [customized] for testnet distinction PHASE_1_FORK_VERSION: 0x01000001 -# [customized] for testing -PHASE_1_GENESIS_SLOT: 8 +# [STUB] +PHASE_1_FORK_SLOT: 0 # [customized] reduced for testing INITIAL_ACTIVE_SHARDS: 2 diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index e7ad5913a..16a0ea4cb 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -760,20 +760,23 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None: committee = get_beacon_committee(state, data.slot, data.index) assert len(attestation.aggregation_bits) == len(committee) - if attestation.data.target.epoch == get_current_epoch(state): - assert attestation.data.source == state.current_justified_checkpoint + if data.target.epoch == get_current_epoch(state): + assert data.source == state.current_justified_checkpoint else: - assert attestation.data.source == state.previous_justified_checkpoint + assert data.source == state.previous_justified_checkpoint # Type 1: on-time attestations - if is_on_time_attestation(state, attestation.data): + if is_on_time_attestation(state, data): # Correct parent block root assert data.beacon_block_root == get_block_root_at_slot(state, compute_previous_slot(state.slot)) # Correct shard number - shard = compute_shard_from_committee_index(state, attestation.data.index, attestation.data.slot) - assert attestation.data.shard == shard - # On-time attestations should have a non-empty shard transition root - assert attestation.data.shard_transition_root != hash_tree_root(ShardTransition()) + shard = compute_shard_from_committee_index(state, data.index, data.slot) + assert data.shard == shard + if data.slot == PHASE_1_FORK_SLOT: + assert data.shard_transition_root == hash_tree_root(ShardTransition()) + else: + # On-time attestations should have a non-empty shard transition root + assert data.shard_transition_root != hash_tree_root(ShardTransition()) # Type 2: no shard transition else: # Ensure delayed attestation @@ -811,7 +814,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: ```python def apply_shard_transition(state: BeaconState, shard: Shard, transition: ShardTransition) -> None: # TODO: only need to check it once when phase 1 starts - assert state.slot > PHASE_1_GENESIS_SLOT + assert state.slot > PHASE_1_FORK_SLOT # Correct data root count offset_slots = get_offset_slots(state, shard) @@ -976,8 +979,10 @@ def verify_empty_shard_transition(state: BeaconState, shard_transitions: Sequenc def process_shard_transitions(state: BeaconState, shard_transitions: Sequence[ShardTransition], attestations: Sequence[Attestation]) -> None: - # Process crosslinks - process_crosslinks(state, shard_transitions, attestations) + if compute_previous_slot(state.slot) != PHASE_1_FORK_SLOT: + # Process crosslinks + process_crosslinks(state, shard_transitions, attestations) + # Verify the empty proposal shard states assert verify_empty_shard_transition(state, shard_transitions) ``` diff --git a/specs/phase1/phase1-fork.md b/specs/phase1/phase1-fork.md index e83e9ef4a..e3949aca8 100644 --- a/specs/phase1/phase1-fork.md +++ b/specs/phase1/phase1-fork.md @@ -35,18 +35,18 @@ Warning: this configuration is not definitive. | Name | Value | | - | - | | `PHASE_1_FORK_VERSION` | `Version('0x01000000')` | -| `PHASE_1_GENESIS_SLOT` | `2**5` **TBD** | +| `PHASE_1_FORK_SLOT` | `Slot(0)` **TBD** | | `INITIAL_ACTIVE_SHARDS` | `2**6` (= 64) | ## Fork to Phase 1 ### Fork trigger -TBD. Social consensus, along with state conditions such as epoch boundary, finality, deposits, active validator count, etc. may be part of the decision process to trigger the fork. For now we assume the condition will be triggered at slot `PHASE_1_GENESIS_SLOT`, where `PHASE_1_GENESIS_SLOT % SLOTS_PER_EPOCH == 0`. +TBD. Social consensus, along with state conditions such as epoch boundary, finality, deposits, active validator count, etc. may be part of the decision process to trigger the fork. For now we assume the condition will be triggered at slot `PHASE_1_FORK_SLOT`, where `PHASE_1_FORK_SLOT % SLOTS_PER_EPOCH == 0`. ### Upgrading the state -After `process_slots` of Phase 0 finishes, if `state.slot == PHASE_1_GENESIS_SLOT`, an irregular state change is made to upgrade to Phase 1. +After `process_slots` of Phase 0 finishes, if `state.slot == PHASE_1_FORK_SLOT`, an irregular state change is made to upgrade to Phase 1. ```python def upgrade_to_phase1(pre: phase0.BeaconState) -> BeaconState: @@ -102,7 +102,7 @@ def upgrade_to_phase1(pre: phase0.BeaconState) -> BeaconState: current_epoch_start_shard=Shard(0), shard_states=List[ShardState, MAX_SHARDS]( ShardState( - slot=pre.slot, + slot=compute_previous_slot(pre.slot), gasprice=MIN_GASPRICE, latest_block_root=Root(), ) for i in range(INITIAL_ACTIVE_SHARDS) diff --git a/specs/phase1/shard-fork-choice.md b/specs/phase1/shard-fork-choice.md index 8416009d7..bdc6ab8f4 100644 --- a/specs/phase1/shard-fork-choice.md +++ b/specs/phase1/shard-fork-choice.md @@ -48,7 +48,7 @@ def get_forkchoice_shard_store(anchor_state: BeaconState, shard: Shard) -> Shard shard=shard, signed_blocks={ anchor_state.shard_states[shard].latest_block_root: SignedShardBlock( - message=ShardBlock(slot=anchor_state.slot, shard=shard) + message=ShardBlock(slot=compute_previous_slot(anchor_state.slot), shard=shard) ) }, block_states={anchor_state.shard_states[shard].latest_block_root: anchor_state.copy().shard_states[shard]}, diff --git a/specs/phase1/validator.md b/specs/phase1/validator.md index b89b90c8a..7f5313554 100644 --- a/specs/phase1/validator.md +++ b/specs/phase1/validator.md @@ -310,6 +310,9 @@ def get_shard_transition_fields( def get_shard_transition(beacon_state: BeaconState, shard: Shard, shard_blocks: Sequence[SignedShardBlock]) -> ShardTransition: + if beacon_state.slot == PHASE_1_FORK_SLOT: + return ShardTransition() + offset_slots = compute_offset_slots( get_latest_slot_for_shard(beacon_state, shard), Slot(beacon_state.slot + 1), diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index 0d953cf24..68696ea0a 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -63,9 +63,6 @@ def _prepare_state(balances_fn: Callable[[Any], Sequence[int]], threshold_fn: Ca # TODO: instead of upgrading a test phase0 genesis state we can also write a phase1 state helper. # Decide based on performance/consistency results later. state = phases[PHASE1].upgrade_to_phase1(state) - # Shard state slot must lag behind BeaconState slot by at least 1 - # Will handle this more elegantly with fork mechanics - spec.process_slots(state, state.slot + 1) return state diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_shard_head.py b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_shard_head.py index a87a85917..f38f1aef1 100644 --- a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_shard_head.py +++ b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_shard_head.py @@ -8,7 +8,10 @@ from eth2spec.test.helpers.shard_block import ( get_committee_index_of_shard, ) from eth2spec.test.helpers.fork_choice import add_block_to_store, get_anchor_root -from eth2spec.test.helpers.state import state_transition_and_sign_block +from eth2spec.test.helpers.state import ( + state_transition_and_sign_block, + transition_to_valid_shard_slot, +) from eth2spec.test.helpers.block import build_empty_block @@ -102,21 +105,18 @@ def apply_shard_and_beacon(spec, state, store, shard_store, shard_blocks_buffer) @spec_state_test @never_bls # Set to never_bls for testing `check_pending_shard_blocks` def test_basic(spec, state): - spec.PHASE_1_GENESIS_SLOT = 0 # NOTE: mock genesis slot here - state = spec.upgrade_to_phase1(state) - shard = spec.Shard(1) + transition_to_valid_shard_slot(spec, state) # Initialization store = spec.get_forkchoice_store(state) anchor_root = get_anchor_root(spec, state) assert spec.get_head(store) == anchor_root + shard = spec.Shard(1) shard_store = spec.get_forkchoice_shard_store(state, shard) shard_head_root = spec.get_shard_head(store, shard_store) assert shard_head_root == state.shard_states[shard].latest_block_root - assert shard_store.block_states[shard_head_root].slot == 1 assert shard_store.block_states[shard_head_root] == state.shard_states[shard] - # For mainnet config, it's possible that only one committee of `shard` per epoch. # we set this counter to test more rounds. shard_committee_counter = 2 diff --git a/tests/core/pyspec/eth2spec/test/helpers/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/attestations.py index 3445f9ff6..fa6a90059 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attestations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attestations.py @@ -69,7 +69,7 @@ def build_attestation_data(spec, state, slot, index, shard=None, shard_transitio source_epoch = state.current_justified_checkpoint.epoch source_root = state.current_justified_checkpoint.root - attestation_data = spec.AttestationData( + data = spec.AttestationData( slot=slot, index=index, beacon_block_root=block_root, @@ -79,23 +79,27 @@ def build_attestation_data(spec, state, slot, index, shard=None, shard_transitio if spec.fork == PHASE1: if shard is None: - shard = spec.compute_shard_from_committee_index(state, attestation_data.index, attestation_data.slot) - attestation_data.shard = shard + shard = spec.compute_shard_from_committee_index(state, data.index, data.slot) + data.shard = shard if shard_transition is not None: last_offset_index = len(shard_transition.shard_data_roots) - 1 - attestation_data.shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root - attestation_data.shard_transition_root = shard_transition.hash_tree_root() + data.shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root + data.shard_transition_root = shard_transition.hash_tree_root() else: if on_time: - shard_transition = spec.get_shard_transition(state, shard, shard_blocks=[]) - last_offset_index = len(shard_transition.shard_data_roots) - 1 - attestation_data.shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root - attestation_data.shard_transition_root = shard_transition.hash_tree_root() + if data.slot == spec.PHASE_1_FORK_SLOT: + data.shard_head_root = spec.Root() + data.shard_transition_root = spec.ShardTransition().hash_tree_root() + else: + shard_transition = spec.get_shard_transition(state, shard, shard_blocks=[]) + last_offset_index = len(shard_transition.shard_data_roots) - 1 + data.shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root + data.shard_transition_root = shard_transition.hash_tree_root() else: - attestation_data.shard_head_root = state.shard_states[shard].latest_block_root - attestation_data.shard_transition_root = spec.Root() - return attestation_data + data.shard_head_root = state.shard_states[shard].latest_block_root + data.shard_transition_root = spec.Root() + return data def get_valid_on_time_attestation(spec, state, slot=None, index=None, shard_transition=None, signed=False): diff --git a/tests/core/pyspec/eth2spec/test/helpers/state.py b/tests/core/pyspec/eth2spec/test/helpers/state.py index c70a47742..f53d8f711 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/state.py +++ b/tests/core/pyspec/eth2spec/test/helpers/state.py @@ -42,12 +42,10 @@ def transition_to_slot_via_block(spec, state, slot): def transition_to_valid_shard_slot(spec, state): """ - Transition to slot `spec.PHASE_1_GENESIS_SLOT + 1` and fork at `spec.PHASE_1_GENESIS_SLOT`. + Transition to slot `spec.PHASE_1_FORK_SLOT + 1` and fork at `spec.PHASE_1_FORK_SLOT`. """ - transition_to(spec, state, spec.PHASE_1_GENESIS_SLOT) - state = spec.upgrade_to_phase1(state) # `upgrade_to_phase1` is a pure function + transition_to(spec, state, spec.PHASE_1_FORK_SLOT) next_slot(spec, state) - return state def next_epoch(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_chunk_challenge.py b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_chunk_challenge.py index 4ef1d667a..395bcc2fd 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_chunk_challenge.py +++ b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_chunk_challenge.py @@ -6,7 +6,7 @@ from eth2spec.test.helpers.custody import ( from eth2spec.test.helpers.attestations import ( get_valid_on_time_attestation, ) -from eth2spec.test.helpers.state import transition_to +from eth2spec.test.helpers.state import transition_to, transition_to_valid_shard_slot from eth2spec.test.context import ( PHASE0, with_all_phases_except, @@ -68,7 +68,8 @@ def run_custody_chunk_response_processing(spec, state, custody_response, valid=T @with_all_phases_except([PHASE0]) @spec_state_test def test_challenge_appended(spec, state): - transition_to(spec, state, state.slot + 1) + transition_to_valid_shard_slot(spec, state) + transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1 shard = 0 offset_slots = spec.get_offset_slots(state, shard) shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) @@ -89,7 +90,8 @@ def test_challenge_appended(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_challenge_empty_element_replaced(spec, state): - transition_to(spec, state, state.slot + 1) + transition_to_valid_shard_slot(spec, state) + transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1 shard = 0 offset_slots = spec.get_offset_slots(state, shard) shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) @@ -112,7 +114,8 @@ def test_challenge_empty_element_replaced(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_duplicate_challenge(spec, state): - transition_to(spec, state, state.slot + 1) + transition_to_valid_shard_slot(spec, state) + transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1 shard = 0 offset_slots = spec.get_offset_slots(state, shard) shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) @@ -135,7 +138,8 @@ def test_duplicate_challenge(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_second_challenge(spec, state): - transition_to(spec, state, state.slot + 1) + transition_to_valid_shard_slot(spec, state) + transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1 shard = 0 offset_slots = spec.get_offset_slots(state, shard) shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) @@ -160,6 +164,7 @@ def test_second_challenge(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_multiple_epochs_custody(spec, state): + transition_to_valid_shard_slot(spec, state) transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 3) shard = 0 @@ -182,6 +187,7 @@ def test_multiple_epochs_custody(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_many_epochs_custody(spec, state): + transition_to_valid_shard_slot(spec, state) transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 20) shard = 0 @@ -204,6 +210,7 @@ def test_many_epochs_custody(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_off_chain_attestation(spec, state): + transition_to_valid_shard_slot(spec, state) transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH) shard = 0 @@ -222,6 +229,7 @@ def test_off_chain_attestation(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_custody_response(spec, state): + transition_to_valid_shard_slot(spec, state) transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH) shard = 0 @@ -251,6 +259,7 @@ def test_custody_response(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_custody_response_multiple_epochs(spec, state): + transition_to_valid_shard_slot(spec, state) transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 3) shard = 0 @@ -280,6 +289,7 @@ def test_custody_response_multiple_epochs(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_custody_response_many_epochs(spec, state): + transition_to_valid_shard_slot(spec, state) transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 20) shard = 0 diff --git a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_custody_slashing.py b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_custody_slashing.py index 07cd76996..23a60cd22 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_custody_slashing.py +++ b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_custody_slashing.py @@ -63,6 +63,7 @@ def run_standard_custody_slashing_test(spec, slashing_message_data=None, correct=True, valid=True): + transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1 if shard_lateness is None: shard_lateness = spec.SLOTS_PER_EPOCH transition_to(spec, state, state.slot + shard_lateness) diff --git a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py index 866831686..2bb569eac 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py +++ b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py @@ -22,7 +22,7 @@ from eth2spec.test.helpers.state import transition_to, transition_to_valid_shard def get_initial_env(spec, state, target_len_offset_slot): - state = transition_to_valid_shard_slot(spec, state) + transition_to_valid_shard_slot(spec, state) committee_index = spec.CommitteeIndex(0) target_shard_slot = state.slot + target_len_offset_slot - 1 shard = spec.compute_shard_from_committee_index(state, committee_index, target_shard_slot) diff --git a/tests/core/pyspec/eth2spec/test/phase1/epoch_processing/test_process_challenge_deadlines.py b/tests/core/pyspec/eth2spec/test/phase1/epoch_processing/test_process_challenge_deadlines.py index 675b0d8da..828ace91c 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/epoch_processing/test_process_challenge_deadlines.py +++ b/tests/core/pyspec/eth2spec/test/phase1/epoch_processing/test_process_challenge_deadlines.py @@ -5,7 +5,7 @@ from eth2spec.test.helpers.custody import ( from eth2spec.test.helpers.attestations import ( get_valid_on_time_attestation, ) -from eth2spec.test.helpers.state import transition_to +from eth2spec.test.helpers.state import transition_to, transition_to_valid_shard_slot from eth2spec.test.context import ( PHASE0, with_all_phases_except, @@ -26,7 +26,8 @@ def run_process_challenge_deadlines(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_validator_slashed_after_chunk_challenge(spec, state): - transition_to(spec, state, state.slot + 1) + transition_to_valid_shard_slot(spec, state) + transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1 shard = 0 offset_slots = spec.get_offset_slots(state, shard) shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) diff --git a/tests/core/pyspec/eth2spec/test/phase1/epoch_processing/test_process_custody_final_updates.py b/tests/core/pyspec/eth2spec/test/phase1/epoch_processing/test_process_custody_final_updates.py index 93fea19cf..0541411da 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/epoch_processing/test_process_custody_final_updates.py +++ b/tests/core/pyspec/eth2spec/test/phase1/epoch_processing/test_process_custody_final_updates.py @@ -10,7 +10,7 @@ from eth2spec.test.helpers.custody import ( from eth2spec.test.helpers.attestations import ( get_valid_on_time_attestation, ) -from eth2spec.test.helpers.state import next_epoch_via_block, transition_to +from eth2spec.test.helpers.state import next_epoch_via_block, transition_to, transition_to_valid_shard_slot from eth2spec.test.context import ( with_all_phases_except, spec_state_test, @@ -32,6 +32,8 @@ def run_process_custody_final_updates(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_validator_withdrawal_delay(spec, state): + transition_to_valid_shard_slot(spec, state) + transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1 spec.initiate_validator_exit(state, 0) assert state.validators[0].withdrawable_epoch < spec.FAR_FUTURE_EPOCH @@ -43,6 +45,8 @@ def test_validator_withdrawal_delay(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_validator_withdrawal_reenable_after_custody_reveal(spec, state): + transition_to_valid_shard_slot(spec, state) + transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1 spec.initiate_validator_exit(state, 0) assert state.validators[0].withdrawable_epoch < spec.FAR_FUTURE_EPOCH @@ -66,7 +70,8 @@ def test_validator_withdrawal_reenable_after_custody_reveal(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_validator_withdrawal_suspend_after_chunk_challenge(spec, state): - transition_to(spec, state, state.slot + 1) + transition_to_valid_shard_slot(spec, state) + transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1 shard = 0 offset_slots = spec.get_offset_slots(state, shard) shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) @@ -114,7 +119,8 @@ def test_validator_withdrawal_suspend_after_chunk_challenge(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_validator_withdrawal_resume_after_chunk_challenge_response(spec, state): - transition_to(spec, state, state.slot + 1) + transition_to_valid_shard_slot(spec, state) + transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1 shard = 0 offset_slots = spec.get_offset_slots(state, shard) shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) diff --git a/tests/core/pyspec/eth2spec/test/phase1/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/phase1/sanity/test_blocks.py index f69cd4793..5fe3394db 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase1/sanity/test_blocks.py @@ -105,7 +105,7 @@ def test_process_beacon_block_with_normal_shard_transition(spec, state): # skip return - state = transition_to_valid_shard_slot(spec, state) + transition_to_valid_shard_slot(spec, state) target_len_offset_slot = 1 committee_index = spec.CommitteeIndex(0) @@ -123,7 +123,7 @@ def test_process_beacon_block_with_empty_proposal_transition(spec, state): # skip return - state = transition_to_valid_shard_slot(spec, state) + transition_to_valid_shard_slot(spec, state) target_len_offset_slot = 1 committee_index = spec.CommitteeIndex(0) @@ -146,7 +146,7 @@ def test_with_shard_transition_with_custody_challenge_and_response(spec, state): # skip return - state = transition_to_valid_shard_slot(spec, state) + transition_to_valid_shard_slot(spec, state) # build shard block shard = 0 @@ -179,7 +179,7 @@ def test_with_shard_transition_with_custody_challenge_and_response(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_custody_key_reveal(spec, state): - state = transition_to_valid_shard_slot(spec, state) + transition_to_valid_shard_slot(spec, state) transition_to(spec, state, state.slot + spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH) block = build_empty_block(spec, state, slot=state.slot + 1) @@ -192,7 +192,7 @@ def test_custody_key_reveal(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_early_derived_secret_reveal(spec, state): - state = transition_to_valid_shard_slot(spec, state) + transition_to_valid_shard_slot(spec, state) block = build_empty_block(spec, state, slot=state.slot + 1) early_derived_secret_reveal = get_valid_early_derived_secret_reveal(spec, state) block.body.early_derived_secret_reveals = [early_derived_secret_reveal] @@ -208,7 +208,7 @@ def test_custody_slashing(spec, state): # skip return - state = transition_to_valid_shard_slot(spec, state) + transition_to_valid_shard_slot(spec, state) # Build shard block shard = 0 diff --git a/tests/core/pyspec/eth2spec/test/phase1/sanity/test_shard_blocks.py b/tests/core/pyspec/eth2spec/test/phase1/sanity/test_shard_blocks.py index 041b882ab..440d0dd8d 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/sanity/test_shard_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase1/sanity/test_shard_blocks.py @@ -51,10 +51,12 @@ def test_valid_shard_block(spec, state): # skip return - beacon_state = transition_to_valid_shard_slot(spec, state) + beacon_state = state.copy() + transition_to_valid_shard_slot(spec, beacon_state) + shard = 0 shard_state = beacon_state.shard_states[shard] - signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True) + signed_shard_block = build_shard_block(spec, state, shard, slot=beacon_state.slot, signed=True) yield from run_shard_blocks(spec, shard_state, signed_shard_block, beacon_state) @@ -71,7 +73,9 @@ def test_invalid_shard_parent_root(spec, state): # skip return - beacon_state = transition_to_valid_shard_slot(spec, state) + beacon_state = state.copy() + transition_to_valid_shard_slot(spec, beacon_state) + shard = 0 shard_state = beacon_state.shard_states[shard] signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True) @@ -88,7 +92,8 @@ def test_invalid_beacon_parent_root(spec, state): # skip return - beacon_state = transition_to_valid_shard_slot(spec, state) + beacon_state = state.copy() + transition_to_valid_shard_slot(spec, beacon_state) shard = 0 shard_state = beacon_state.shard_states[shard] signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True) @@ -105,7 +110,8 @@ def test_invalid_slot(spec, state): # skip return - beacon_state = transition_to_valid_shard_slot(spec, state) + beacon_state = state.copy() + transition_to_valid_shard_slot(spec, beacon_state) shard = 0 shard_state = beacon_state.shard_states[shard] signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True) @@ -123,7 +129,8 @@ def test_invalid_proposer_index(spec, state): # skip return - beacon_state = transition_to_valid_shard_slot(spec, state) + beacon_state = state.copy() + transition_to_valid_shard_slot(spec, beacon_state) shard = 0 shard_state = beacon_state.shard_states[shard] signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True) @@ -147,7 +154,8 @@ def test_out_of_bound_offset(spec, state): # skip return - beacon_state = transition_to_valid_shard_slot(spec, state) + beacon_state = state.copy() + transition_to_valid_shard_slot(spec, beacon_state) shard = 0 slot = ( beacon_state.shard_states[shard].slot @@ -170,7 +178,8 @@ def test_invalid_offset(spec, state): # skip return - beacon_state = transition_to_valid_shard_slot(spec, state) + beacon_state = state.copy() + transition_to_valid_shard_slot(spec, beacon_state) # 4 is not in `SHARD_BLOCK_OFFSETS` shard = 0 slot = beacon_state.shard_states[shard].slot + 4 @@ -191,7 +200,8 @@ def test_empty_block_body(spec, state): # skip return - beacon_state = transition_to_valid_shard_slot(spec, state) + beacon_state = state.copy() + transition_to_valid_shard_slot(spec, beacon_state) shard = 0 shard_state = beacon_state.shard_states[shard] signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, body=b'', signed=True) @@ -212,7 +222,8 @@ def test_invalid_signature(spec, state): # skip return - beacon_state = transition_to_valid_shard_slot(spec, state) + beacon_state = state.copy() + transition_to_valid_shard_slot(spec, beacon_state) shard = 0 shard_state = beacon_state.shard_states[shard] signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=False) @@ -233,7 +244,8 @@ def test_max_offset(spec, state): # skip return - beacon_state = transition_to_valid_shard_slot(spec, state) + beacon_state = state.copy() + transition_to_valid_shard_slot(spec, beacon_state) shard = 0 slot = beacon_state.shard_states[shard].slot + spec.SHARD_BLOCK_OFFSETS[spec.MAX_SHARD_BLOCKS_PER_ATTESTATION - 1] transition_to(spec, beacon_state, slot) @@ -253,7 +265,8 @@ def test_pending_shard_parent_block(spec, state): return # Block N - beacon_state = transition_to_valid_shard_slot(spec, state) + beacon_state = state.copy() + transition_to_valid_shard_slot(spec, beacon_state) shard = 0 shard_state = beacon_state.shard_states[shard] signed_shard_block_1 = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True) From 8fb96f18608b4827a72495e7fba33b20d5ce92b7 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 17 Jul 2020 00:10:04 +0800 Subject: [PATCH 16/43] Enable more phase 0 tests to be compatible with phase 1 --- .../test_process_attester_slashing.py | 33 +++++++++---------- .../test_process_rewards_and_penalties.py | 7 ++-- .../test/phase0/sanity/test_blocks.py | 10 +++--- .../pyspec/eth2spec/test/test_finality.py | 4 +-- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attester_slashing.py b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attester_slashing.py index 192e0390d..5f26ba5f7 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attester_slashing.py +++ b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attester_slashing.py @@ -1,6 +1,5 @@ from eth2spec.test.context import ( - PHASE0, - spec_state_test, expect_assertion_error, always_bls, with_all_phases, with_phases + spec_state_test, expect_assertion_error, always_bls, with_all_phases ) from eth2spec.test.helpers.attestations import sign_indexed_attestation from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing, \ @@ -197,7 +196,7 @@ def test_participants_already_slashed(spec, state): # Some of the following tests are phase0 only: phase 1 lists participants with bitfields instead of index list. -@with_phases([PHASE0]) +@with_all_phases @spec_state_test @always_bls def test_att1_high_index(spec, state): @@ -210,7 +209,7 @@ def test_att1_high_index(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test @always_bls def test_att2_high_index(spec, state): @@ -223,7 +222,7 @@ def test_att2_high_index(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test @always_bls def test_att1_empty_indices(spec, state): @@ -235,7 +234,7 @@ def test_att1_empty_indices(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test @always_bls def test_att2_empty_indices(spec, state): @@ -247,7 +246,7 @@ def test_att2_empty_indices(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test @always_bls def test_all_empty_indices(spec, state): @@ -262,7 +261,7 @@ def test_all_empty_indices(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test @always_bls def test_att1_bad_extra_index(spec, state): @@ -278,7 +277,7 @@ def test_att1_bad_extra_index(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test @always_bls def test_att1_bad_replaced_index(spec, state): @@ -294,7 +293,7 @@ def test_att1_bad_replaced_index(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test @always_bls def test_att2_bad_extra_index(spec, state): @@ -310,7 +309,7 @@ def test_att2_bad_extra_index(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test @always_bls def test_att2_bad_replaced_index(spec, state): @@ -326,7 +325,7 @@ def test_att2_bad_replaced_index(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test @always_bls def test_att1_duplicate_index_normal_signed(spec, state): @@ -346,7 +345,7 @@ def test_att1_duplicate_index_normal_signed(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test @always_bls def test_att2_duplicate_index_normal_signed(spec, state): @@ -366,7 +365,7 @@ def test_att2_duplicate_index_normal_signed(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test @always_bls def test_att1_duplicate_index_double_signed(spec, state): @@ -381,7 +380,7 @@ def test_att1_duplicate_index_double_signed(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test @always_bls def test_att2_duplicate_index_double_signed(spec, state): @@ -396,7 +395,7 @@ def test_att2_duplicate_index_double_signed(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test def test_unsorted_att_1(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True) @@ -409,7 +408,7 @@ def test_unsorted_att_1(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test def test_unsorted_att_2(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False) diff --git a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py index 72bdd3e92..0137ddca4 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py +++ b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py @@ -1,7 +1,6 @@ from eth2spec.test.context import ( - PHASE0, spec_state_test, spec_test, - with_all_phases, with_phases, single_phase, + with_all_phases, single_phase, with_custom_state, zero_activation_threshold, misc_balances, low_single_balance, @@ -25,7 +24,7 @@ def run_process_rewards_and_penalties(spec, state): yield from run_epoch_processing_with(spec, state, 'process_rewards_and_penalties') -@with_phases([PHASE0]) +@with_all_phases @spec_state_test def test_genesis_epoch_no_attestations_no_penalties(spec, state): pre_state = state.copy() @@ -38,7 +37,7 @@ def test_genesis_epoch_no_attestations_no_penalties(spec, state): assert state.balances[index] == pre_state.balances[index] -@with_phases([PHASE0]) +@with_all_phases @spec_state_test def test_genesis_epoch_full_attestations_no_rewards(spec, state): attestations = [] diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py index c3b1dceed..6dffcdaba 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py @@ -484,7 +484,7 @@ def test_duplicate_attester_slashing(spec, state): # All AttesterSlashing tests should be adopted for Phase 1 but helper support is not yet there -@with_phases([PHASE0]) +@with_all_phases @spec_state_test def test_multiple_attester_slashings_no_overlap(spec, state): # Skip test if config cannot handle multiple AttesterSlashings per block @@ -525,7 +525,7 @@ def test_multiple_attester_slashings_no_overlap(spec, state): check_attester_slashing_effect(spec, pre_state, state, full_indices) -@with_phases([PHASE0]) +@with_all_phases @spec_state_test def test_multiple_attester_slashings_partial_overlap(spec, state): # Skip test if config cannot handle multiple AttesterSlashings per block @@ -740,7 +740,7 @@ def prepare_signed_exits(spec, state, indices): # exceeding the minimal-config randao mixes memory size. # Applies to all voluntary-exit sanity block tests. -@with_phases([PHASE0]) +@with_all_phases @spec_state_test def test_voluntary_exit(spec, state): validator_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1] @@ -768,7 +768,7 @@ def test_voluntary_exit(spec, state): assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH -@with_phases([PHASE0]) +@with_all_phases @spec_state_test def test_double_validator_exit_same_block(spec, state): validator_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1] @@ -789,7 +789,7 @@ def test_double_validator_exit_same_block(spec, state): yield 'post', None -@with_phases([PHASE0]) +@with_all_phases @spec_state_test def test_multiple_different_validator_exits_same_block(spec, state): validator_indices = [ diff --git a/tests/core/pyspec/eth2spec/test/test_finality.py b/tests/core/pyspec/eth2spec/test/test_finality.py index adbadcdf2..106c7ab02 100644 --- a/tests/core/pyspec/eth2spec/test/test_finality.py +++ b/tests/core/pyspec/eth2spec/test/test_finality.py @@ -1,4 +1,4 @@ -from eth2spec.test.context import PHASE0, spec_state_test, never_bls, with_all_phases, with_phases +from eth2spec.test.context import spec_state_test, never_bls, with_all_phases from eth2spec.test.helpers.state import next_epoch_via_block from eth2spec.test.helpers.attestations import next_epoch_with_attestations @@ -28,7 +28,7 @@ def check_finality(spec, assert state.finalized_checkpoint == prev_state.finalized_checkpoint -@with_phases([PHASE0]) +@with_all_phases @spec_state_test @never_bls def test_finality_no_updates_at_genesis(spec, state): From 0b81c967fa1bd0adedcb4f1e367eb4a6bc73165c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 17 Jul 2020 00:18:44 +0800 Subject: [PATCH 17/43] Add notes --- specs/phase1/beacon-chain.md | 2 ++ specs/phase1/validator.md | 1 + tests/core/pyspec/eth2spec/test/helpers/attestations.py | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index 16a0ea4cb..759687a2f 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -772,6 +772,7 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None: # Correct shard number shard = compute_shard_from_committee_index(state, data.index, data.slot) assert data.shard == shard + # NOTE: We currently set `PHASE_1_FORK_SLOT` to `GENESIS_SLOT` for test vectors. if data.slot == PHASE_1_FORK_SLOT: assert data.shard_transition_root == hash_tree_root(ShardTransition()) else: @@ -979,6 +980,7 @@ def verify_empty_shard_transition(state: BeaconState, shard_transitions: Sequenc def process_shard_transitions(state: BeaconState, shard_transitions: Sequence[ShardTransition], attestations: Sequence[Attestation]) -> None: + # NOTE: We currently set `PHASE_1_FORK_SLOT` to `GENESIS_SLOT` for test vectors. if compute_previous_slot(state.slot) != PHASE_1_FORK_SLOT: # Process crosslinks process_crosslinks(state, shard_transitions, attestations) diff --git a/specs/phase1/validator.md b/specs/phase1/validator.md index 7f5313554..7e9cddd06 100644 --- a/specs/phase1/validator.md +++ b/specs/phase1/validator.md @@ -310,6 +310,7 @@ def get_shard_transition_fields( def get_shard_transition(beacon_state: BeaconState, shard: Shard, shard_blocks: Sequence[SignedShardBlock]) -> ShardTransition: + # NOTE: We currently set `PHASE_1_FORK_SLOT` to `GENESIS_SLOT` for test vectors. if beacon_state.slot == PHASE_1_FORK_SLOT: return ShardTransition() diff --git a/tests/core/pyspec/eth2spec/test/helpers/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/attestations.py index fa6a90059..ae90fc808 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attestations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attestations.py @@ -88,7 +88,7 @@ def build_attestation_data(spec, state, slot, index, shard=None, shard_transitio data.shard_transition_root = shard_transition.hash_tree_root() else: if on_time: - if data.slot == spec.PHASE_1_FORK_SLOT: + if data.slot == spec.GENESIS_SLOT: data.shard_head_root = spec.Root() data.shard_transition_root = spec.ShardTransition().hash_tree_root() else: From 74aa027e38f4822274ff65a07e2588e1409356ad Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 20 Jul 2020 16:37:43 +0800 Subject: [PATCH 18/43] Apply suggestions from @mkalinin Co-authored-by: Mikhail Kalinin --- specs/phase1/beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index 759687a2f..4fe0316f3 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -773,11 +773,11 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None: shard = compute_shard_from_committee_index(state, data.index, data.slot) assert data.shard == shard # NOTE: We currently set `PHASE_1_FORK_SLOT` to `GENESIS_SLOT` for test vectors. - if data.slot == PHASE_1_FORK_SLOT: - assert data.shard_transition_root == hash_tree_root(ShardTransition()) - else: + if data.slot > PHASE_1_FORK_SLOT: # On-time attestations should have a non-empty shard transition root assert data.shard_transition_root != hash_tree_root(ShardTransition()) + else: + assert data.shard_transition_root == hash_tree_root(ShardTransition()) # Type 2: no shard transition else: # Ensure delayed attestation @@ -981,7 +981,7 @@ def process_shard_transitions(state: BeaconState, shard_transitions: Sequence[ShardTransition], attestations: Sequence[Attestation]) -> None: # NOTE: We currently set `PHASE_1_FORK_SLOT` to `GENESIS_SLOT` for test vectors. - if compute_previous_slot(state.slot) != PHASE_1_FORK_SLOT: + if compute_previous_slot(state.slot) > PHASE_1_FORK_SLOT: # Process crosslinks process_crosslinks(state, shard_transitions, attestations) From c981fc4411392de1bd31430fd937b21328a8d674 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 20 Jul 2020 16:47:21 +0800 Subject: [PATCH 19/43] Apply mkalinin's suggestion --- specs/phase1/beacon-chain.md | 4 ++-- specs/phase1/validator.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index 4fe0316f3..a98487fba 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -773,7 +773,7 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None: shard = compute_shard_from_committee_index(state, data.index, data.slot) assert data.shard == shard # NOTE: We currently set `PHASE_1_FORK_SLOT` to `GENESIS_SLOT` for test vectors. - if data.slot > PHASE_1_FORK_SLOT: + if data.slot > GENESIS_SLOT: # On-time attestations should have a non-empty shard transition root assert data.shard_transition_root != hash_tree_root(ShardTransition()) else: @@ -981,7 +981,7 @@ def process_shard_transitions(state: BeaconState, shard_transitions: Sequence[ShardTransition], attestations: Sequence[Attestation]) -> None: # NOTE: We currently set `PHASE_1_FORK_SLOT` to `GENESIS_SLOT` for test vectors. - if compute_previous_slot(state.slot) > PHASE_1_FORK_SLOT: + if compute_previous_slot(state.slot) > GENESIS_SLOT: # Process crosslinks process_crosslinks(state, shard_transitions, attestations) diff --git a/specs/phase1/validator.md b/specs/phase1/validator.md index 7e9cddd06..4eadec495 100644 --- a/specs/phase1/validator.md +++ b/specs/phase1/validator.md @@ -311,7 +311,7 @@ def get_shard_transition(beacon_state: BeaconState, shard: Shard, shard_blocks: Sequence[SignedShardBlock]) -> ShardTransition: # NOTE: We currently set `PHASE_1_FORK_SLOT` to `GENESIS_SLOT` for test vectors. - if beacon_state.slot == PHASE_1_FORK_SLOT: + if beacon_state.slot == GENESIS_SLOT: return ShardTransition() offset_slots = compute_offset_slots( From 302607eac56c94a7be1ca7a01f5419d93cdf4b79 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 21 Jul 2020 21:10:27 +0800 Subject: [PATCH 20/43] Rename `aggregate_na_pubkeys` to `aggregate_na_signatures` and add comments --- tests/generators/bls/main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/generators/bls/main.py b/tests/generators/bls/main.py index 9e10b4044..bfe0c8342 100644 --- a/tests/generators/bls/main.py +++ b/tests/generators/bls/main.py @@ -149,7 +149,9 @@ def case03_aggregate(): else: raise Exception("Should have been INVALID") - yield f'aggregate_na_pubkeys', { + # No signatures to aggregate. Follow IETF BLS spec, return `None` to represent INVALID. + # https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-02#section-2.8 + yield f'aggregate_na_signatures', { 'input': [], 'output': None, } From ec7be11c068aaa20f21eec80295516cea0a122ea Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 21 Jul 2020 16:45:25 -0600 Subject: [PATCH 21/43] mod gossip params and rename to reflect spec names --- specs/phase0/p2p-interface.md | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index b26bfeeb2..2d9962d6c 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -202,7 +202,7 @@ and will in most cases be out of sync with the ENR sequence number. ## The gossip domain: gossipsub -Clients MUST support the [gossipsub v1](https://github.com/libp2p/specs/tree/master/pubsub/gossipsub) libp2p Protocol +Clients MUST support the [gossipsub v1](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.0.md) libp2p Protocol including the [gossipsub v1.1](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md) extension. **Protocol ID:** `/meshsub/1.1.0` @@ -211,16 +211,17 @@ including the [gossipsub v1.1](https://github.com/libp2p/specs/blob/master/pubsu *Note*: Parameters listed here are subject to a large-scale network feasibility study. -The following gossipsub [parameters](https://github.com/libp2p/specs/tree/master/pubsub/gossipsub#meshsub-an-overlay-mesh-router) will be used: +The following gossipsub [parameters](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.0.md#parameters) will be used: - `D` (topic stable mesh target count): 6 - `D_low` (topic stable mesh low watermark): 5 - `D_high` (topic stable mesh high watermark): 12 - `D_lazy` (gossip target): 6 +- `heartbeat_interval` (frequency of heartbeat, seconds): 0.7 - `fanout_ttl` (ttl for fanout maps for topics we are not subscribed to but have published to, seconds): 60 -- `gossip_advertise` (number of windows to gossip about): 3 -- `gossip_history` (number of heartbeat intervals to retain message IDs): 385 -- `heartbeat_interval` (frequency of heartbeat, seconds): 1 +- `mcache_len` (number of windows to retain full messages in cache for `IWANT` responses): 6 +- `mcache_gossip` (number of windows to gossip about): 3 +- `seen_ttl` (number of heartbeat intervals to retain message IDs): 550 ### Topics and messages @@ -1150,12 +1151,21 @@ Some examples of where messages could be duplicated: ### Why are these specific gossip parameters chosen? - `D`, `D_low`, `D_high`, `D_lazy`: recommended defaults. -- `fanout_ttl`: 60 (TODO: increase to cover an epoch?) -- `gossip_advertise`: 3 (TODO: to increase to 6?) -- `gossip_history`: `SLOTS_PER_EPOCH * SECONDS_PER_SLOT / heartbeat_interval = approx. 385`. Attestation validity is bounded by an epoch, so this is the safe max bound. -- `heartbeat_interval`: 1 second, recommended default. - For Eth2 specifically, 0.6-0.7s was recommended in the [GossipSub evaluation report by Protocol Labs](https://gateway.ipfs.io/ipfs/QmRAFP5DBnvNjdYSbWhEhVRJJDFCLpPyvew5GwCCB4VxM4). - And this parameter will be experimented with in testnets. +- `heartbeat_interval`: 0.7 second, recommended for eth2 in the [GossipSub evaluation report by Protocol Labs](https://gateway.ipfs.io/ipfs/QmRAFP5DBnvNjdYSbWhEhVRJJDFCLpPyvew5GwCCB4VxM4). +- `fanout_ttl`: 60, recommended default. + Fanout is primarily used by committees publishing attestations to subnets. + This happens once per epoch per validator and the subnet changes each epoch + so there is little to gain in having a `fanout_ttl` be increased from the recommended default. +- `mcache_len`: 6, increase by one to ensure that mcache is around for long + enough for `IWANT`s to respond to `IHAVE`s in the context of the shorter + `heartbeat_interval`. If `mcache_gossip` is increased, this param should be + increased to be at least `3` (~2 seconds) more than `mcache_gossip`. + should be increased to be at least +- `mcache_gossip`: 3, recommended default. This can be increased to 5 or 6 + (~4 seconds) if gossip times are longer than expected and the current window + does not provide enough responsiveness during adverse conditions. +- `seen_ttl`: `SLOTS_PER_EPOCH * SECONDS_PER_SLOT / heartbeat_interval = approx. 550`. + Attestation gossip validity is bounded by an epoch, so this is the safe max bound. ### Why is there `MAXIMUM_GOSSIP_CLOCK_DISPARITY` when validating slot ranges of messages in gossip subnets? From edcce7bfefe13a1042aaa563dd03c485e5d8f1a4 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 21 Jul 2020 17:00:11 -0600 Subject: [PATCH 22/43] format --- specs/phase0/p2p-interface.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index 2d9962d6c..942d80243 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -1151,8 +1151,8 @@ Some examples of where messages could be duplicated: ### Why are these specific gossip parameters chosen? - `D`, `D_low`, `D_high`, `D_lazy`: recommended defaults. -- `heartbeat_interval`: 0.7 second, recommended for eth2 in the [GossipSub evaluation report by Protocol Labs](https://gateway.ipfs.io/ipfs/QmRAFP5DBnvNjdYSbWhEhVRJJDFCLpPyvew5GwCCB4VxM4). -- `fanout_ttl`: 60, recommended default. +- `heartbeat_interval`: 0.7 seconds, recommended for eth2 in the [GossipSub evaluation report by Protocol Labs](https://gateway.ipfs.io/ipfs/QmRAFP5DBnvNjdYSbWhEhVRJJDFCLpPyvew5GwCCB4VxM4). +- `fanout_ttl`: 60 seconds, recommended default. Fanout is primarily used by committees publishing attestations to subnets. This happens once per epoch per validator and the subnet changes each epoch so there is little to gain in having a `fanout_ttl` be increased from the recommended default. From 50cd0b2b31b544c368391eae25d7b395f007ea58 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 21 Jul 2020 17:49:39 -0600 Subject: [PATCH 23/43] Update specs/phase0/p2p-interface.md Co-authored-by: Diederik Loerakker --- specs/phase0/p2p-interface.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index 942d80243..2eac35c94 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -1160,7 +1160,6 @@ Some examples of where messages could be duplicated: enough for `IWANT`s to respond to `IHAVE`s in the context of the shorter `heartbeat_interval`. If `mcache_gossip` is increased, this param should be increased to be at least `3` (~2 seconds) more than `mcache_gossip`. - should be increased to be at least - `mcache_gossip`: 3, recommended default. This can be increased to 5 or 6 (~4 seconds) if gossip times are longer than expected and the current window does not provide enough responsiveness during adverse conditions. From cd8bb3d85a011225ea78043cf6f47d86ebff2358 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 22 Jul 2020 23:39:18 +0800 Subject: [PATCH 24/43] Update v-guide: Shard head root --- specs/phase1/validator.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/phase1/validator.md b/specs/phase1/validator.md index 4eadec495..7f38c6043 100644 --- a/specs/phase1/validator.md +++ b/specs/phase1/validator.md @@ -31,7 +31,7 @@ - [`FullAttestation`](#fullattestation) - [Timing](#timing) - [Attestation data](#attestation-data) - - [Head shard root](#head-shard-root) + - [Shard head root](#shard-head-root) - [Shard transition](#shard-transition) - [Construct attestation](#construct-attestation) - [Attestation Aggregation](#attestation-aggregation) @@ -267,9 +267,9 @@ A validator should create and broadcast the `attestation` to the associated atte *Note*: We assume that the fork choice only follows branches with valid `offset_slots` with respect to the most recent beacon state shard transition for the queried shard. -##### Head shard root +##### Shard head root -Set `attestation_data.shard_head_root = hash_tree_root(shard_head_block)`. +If `attestation_data.slot == GENESIS_SLOT`, set `attestation_data.shard_head_root = Root()`. Otherwise, set `attestation_data.shard_head_root = hash_tree_root(shard_head_block)`. ##### Shard transition From a64b8eba6e8b76db89c149d063dbd303a62d4656 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 23 Jul 2020 09:37:16 -0600 Subject: [PATCH 25/43] remove 'ssz' format from req/resp. now only ssz_snappy --- specs/phase0/p2p-interface.md | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index f792829ef..44cbb7982 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -41,7 +41,7 @@ It consists of four main sections: - [Requesting side](#requesting-side) - [Responding side](#responding-side) - [Encoding strategies](#encoding-strategies) - - [SSZ-encoding strategy (with or without Snappy)](#ssz-encoding-strategy-with-or-without-snappy) + - [SSZ-snappy encoding strategy](#ssz-snappy-encoding-strategy) - [Messages](#messages) - [Status](#status) - [Goodbye](#goodbye) @@ -550,20 +550,18 @@ Clients MUST treat as valid any byte sequences. ### Encoding strategies The token of the negotiated protocol ID specifies the type of encoding to be used for the req/resp interaction. -Two values are possible at this time: +Only one value is possible at this time: -- `ssz`: the contents are [SSZ-encoded](../../ssz/simple-serialize.md). - This encoding type MUST be supported by all clients. +- `ssz_snappy`: The contents are first [SSZ-encoded](../../ssz/simple-serialize.md) + and then compressed with [Snappy](https://github.com/google/snappy) frames compression. For objects containing a single field, only the field is SSZ-encoded not a container with a single field. - For example, the `BeaconBlocksByRoot` request is an SSZ-encoded list of `Root`'s. -- `ssz_snappy`: The contents are SSZ-encoded and then compressed with [Snappy](https://github.com/google/snappy) frames compression. This encoding type MUST be supported by all clients. -#### SSZ-encoding strategy (with or without Snappy) +#### SSZ-snappy encoding strategy The [SimpleSerialize (SSZ) specification](../../ssz/simple-serialize.md) outlines how objects are SSZ-encoded. -If the Snappy variant is selected, we feed the serialized form of the object to the Snappy compressor on encoding. +To achieve snappy encoding on top of SSZ, we feed the serialized form of the object to the Snappy compressor on encoding. The inverse happens on decoding. Snappy has two formats: "block" and "frames" (streaming). @@ -572,14 +570,14 @@ To support large requests and response chunks, snappy-framing is used. Since snappy frame contents [have a maximum size of `65536` bytes](https://github.com/google/snappy/blob/master/framing_format.txt#L104) and frame headers are just `identifier (1) + checksum (4)` bytes, the expected buffering of a single frame is acceptable. -**Encoding-dependent header:** Req/Resp protocols using the `ssz` or `ssz_snappy` encoding strategies MUST encode the length of the raw SSZ bytes, +**Encoding-dependent header:** Req/Resp protocols using the `ssz_snappy` encoding strategy MUST encode the length of the raw SSZ bytes, encoded as an unsigned [protobuf varint](https://developers.google.com/protocol-buffers/docs/encoding#varints). *Writing*: By first computing and writing the SSZ byte length, the SSZ encoder can then directly write the chunk contents to the stream. -If Snappy is applied, it can be passed through a buffered Snappy writer to compress frame by frame. +When Snappy is applied, it can be passed through a buffered Snappy writer to compress frame by frame. *Reading*: After reading the expected SSZ byte length, the SSZ decoder can directly read the contents from the stream. -If snappy is applied, it can be passed through a buffered Snappy reader to decompress frame by frame. +When snappy is applied, it can be passed through a buffered Snappy reader to decompress frame by frame. Before reading the payload, the header MUST be validated: - The unsigned protobuf varint used for the length-prefix MUST not be longer than 10 bytes, which is sufficient for any `uint64`. @@ -588,7 +586,6 @@ Before reading the payload, the header MUST be validated: After reading a valid header, the payload MAY be read, while maintaining the size constraints from the header. A reader SHOULD NOT read more than `max_encoded_len(n)` bytes after reading the SSZ length-prefix `n` from the header. -- For `ssz` this is: `n` - For `ssz_snappy` this is: `32 + n + n // 6`. This is considered the [worst-case compression result](https://github.com/google/snappy/blob/537f4ad6240e586970fe554614542e9717df7902/snappy.cc#L98) by Snappy. @@ -1299,7 +1296,7 @@ Thus, libp2p transparently handles message delimiting in the underlying stream. libp2p streams are full-duplex, and each party is responsible for closing their write side (like in TCP). We can therefore use stream closure to mark the end of the request and response independently. -Nevertheless, in the case of `ssz` and `ssz_snappy`, messages are still length-prefixed with the length of the underlying data: +Nevertheless, in the case of `ssz_snappy`, messages are still length-prefixed with the length of the underlying data: * A basic reader can prepare a correctly sized buffer before reading the message * A more advanced reader can stream-decode SSZ given the length of the SSZ data. * Alignment with protocols like gRPC over HTTP/2 that prefix with length From 289564aec03de5cb211ed4c5b57014349071a92e Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 23 Jul 2020 10:03:43 -0600 Subject: [PATCH 26/43] pr feedback --- specs/phase0/p2p-interface.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index 44cbb7982..aeddd8b51 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -555,6 +555,7 @@ Only one value is possible at this time: - `ssz_snappy`: The contents are first [SSZ-encoded](../../ssz/simple-serialize.md) and then compressed with [Snappy](https://github.com/google/snappy) frames compression. For objects containing a single field, only the field is SSZ-encoded not a container with a single field. + For example, the `BeaconBlocksByRoot` request is an SSZ-encoded list of `Root`'s. This encoding type MUST be supported by all clients. #### SSZ-snappy encoding strategy From fb13f67cca1ea8878f6d3a83f9252354b9aaab7c Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 23 Jul 2020 10:10:21 -0600 Subject: [PATCH 27/43] add that current block is in the same chain as finalized ancestor --- specs/phase0/p2p-interface.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index f792829ef..1d45ebae8 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -295,15 +295,18 @@ The following validations MUST pass before forwarding the `signed_beacon_block` (a client MAY choose to validate and store such blocks for additional purposes -- e.g. slashing detection, archive nodes, etc). - _[IGNORE]_ The block is the first block with valid signature received for the proposer for the slot, `signed_beacon_block.message.slot`. - _[REJECT]_ The proposer signature, `signed_beacon_block.signature`, is valid with respect to the `proposer_index` pubkey. +- _[IGNORE]_ The block's parent (defined by `block.parent_root`) has been seen + (via both gossip and non-gossip sources) + (a client MAY queue blocks for processing once the parent block is retrieved). +- _[REJECT]_ The block's parent (defined by `block.parent_root`) passes validation. +- _[REJECT]_ The current `finalized_checkpoint` is an ancestor of `block` -- i.e. + `get_ancestor(store, block.parent_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) + == store.finalized_checkpoint.root` - _[REJECT]_ The block is proposed by the expected `proposer_index` for the block's slot in the context of the current shuffling (defined by `parent_root`/`slot`). If the `proposer_index` cannot immediately be verified against the expected shuffling, the block MAY be queued for later processing while proposers for the block's branch are calculated -- in such a case _do not_ `REJECT`, instead `IGNORE` this message. -- _[IGNORE]_ The block's parent (defined by `block.parent_root`) has been seen - (via both gossip and non-gossip sources) - (a client MAY queue blocks for processing once the parent block is retrieved). -- _[REJECT]_ The block's parent (defined by `block.parent_root`) passes validation. ##### `beacon_aggregate_and_proof` From 0692ac017235c37f33dcc38455ef32273058501f Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 23 Jul 2020 10:28:02 -0600 Subject: [PATCH 28/43] bump VERSION.txt to 0.12.2 --- tests/core/pyspec/eth2spec/VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/VERSION.txt b/tests/core/pyspec/eth2spec/VERSION.txt index aac2dacab..e96a87111 100644 --- a/tests/core/pyspec/eth2spec/VERSION.txt +++ b/tests/core/pyspec/eth2spec/VERSION.txt @@ -1 +1 @@ -0.12.1 \ No newline at end of file +0.12.2 \ No newline at end of file From b3e49ff0d3f0667ceefebb8ec393694fa77db316 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 23 Jul 2020 10:39:04 -0600 Subject: [PATCH 29/43] add finalized ancestor checks to attestation gossip --- specs/phase0/p2p-interface.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index 1d45ebae8..0f70180e2 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -336,6 +336,10 @@ The following validations MUST pass before forwarding the `signed_aggregate_and_ (via both gossip and non-gossip sources) (a client MAY queue aggregates for processing once block is retrieved). - _[REJECT]_ The block being voted for (`aggregate.data.beacon_block_root`) passes validation. +- _[REJECT]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `aggregate.data.beacon_block_root` -- i.e. + `get_ancestor(store, hash_tree_root(block), compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) + == store.finalized_checkpoint.root` + ##### `voluntary_exit` @@ -396,6 +400,11 @@ The following validations MUST pass before forwarding the `attestation` on the s (via both gossip and non-gossip sources) (a client MAY queue aggregates for processing once block is retrieved). - _[REJECT]_ The block being voted for (`attestation.data.beacon_block_root`) passes validation. +- _[REJECT]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `attestation.data.beacon_block_root` -- i.e. + `get_ancestor(store, hash_tree_root(block), compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) + == store.finalized_checkpoint.root` + + #### Attestations and Aggregation From 3cf683219874e80d3ae5043c1ae200ddf0ece056 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 23 Jul 2020 10:46:31 -0600 Subject: [PATCH 30/43] Apply suggestions from code review Co-authored-by: Diederik Loerakker --- specs/phase0/p2p-interface.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index 0f70180e2..6acefb9ad 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -337,7 +337,7 @@ The following validations MUST pass before forwarding the `signed_aggregate_and_ (a client MAY queue aggregates for processing once block is retrieved). - _[REJECT]_ The block being voted for (`aggregate.data.beacon_block_root`) passes validation. - _[REJECT]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `aggregate.data.beacon_block_root` -- i.e. - `get_ancestor(store, hash_tree_root(block), compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) + `get_ancestor(store, aggregate.data.beacon_block_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) == store.finalized_checkpoint.root` @@ -401,7 +401,7 @@ The following validations MUST pass before forwarding the `attestation` on the s (a client MAY queue aggregates for processing once block is retrieved). - _[REJECT]_ The block being voted for (`attestation.data.beacon_block_root`) passes validation. - _[REJECT]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `attestation.data.beacon_block_root` -- i.e. - `get_ancestor(store, hash_tree_root(block), compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) + `get_ancestor(store, attestation.data.beacon_block_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) == store.finalized_checkpoint.root` From 9193a23f8eeb4bf83c2b9624f4a8d3d762f23191 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 23 Jul 2020 12:05:22 -0600 Subject: [PATCH 31/43] add chain id and netowrk id to config --- configs/mainnet/phase0.yaml | 3 +++ configs/minimal/phase0.yaml | 3 +++ specs/phase0/deposit-contract.md | 4 +++- specs/phase0/validator.md | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/configs/mainnet/phase0.yaml b/configs/mainnet/phase0.yaml index 39cfddf77..616baaf34 100644 --- a/configs/mainnet/phase0.yaml +++ b/configs/mainnet/phase0.yaml @@ -51,6 +51,9 @@ SECONDS_PER_ETH1_BLOCK: 14 # Deposit contract # --------------------------------------------------------------- +# Ethereum PoW Mainnet +DEPOSIT_CHAIN_ID: 1 +DEPOSIT_NETWORK_ID: 1 # **TBD** DEPOSIT_CONTRACT_ADDRESS: 0x1234567890123456789012345678901234567890 diff --git a/configs/minimal/phase0.yaml b/configs/minimal/phase0.yaml index 524d0d5f9..78cd60177 100644 --- a/configs/minimal/phase0.yaml +++ b/configs/minimal/phase0.yaml @@ -51,6 +51,9 @@ SECONDS_PER_ETH1_BLOCK: 14 # Deposit contract # --------------------------------------------------------------- +# Ethereum Goerli testnet +DEPOSIT_CHAIN_ID: 5 +DEPOSIT_NETWORK_ID: 5 # **TBD** DEPOSIT_CONTRACT_ADDRESS: 0x1234567890123456789012345678901234567890 diff --git a/specs/phase0/deposit-contract.md b/specs/phase0/deposit-contract.md index 9c5137a32..68d281614 100644 --- a/specs/phase0/deposit-contract.md +++ b/specs/phase0/deposit-contract.md @@ -31,12 +31,14 @@ This document represents the specification for the beacon chain deposit contract | Name | Value | | - | - | +| `DEPOSIT_CHAIN_ID` | `1` | +| `DEPOSIT_NETWORK_ID` | `1` | | `DEPOSIT_CONTRACT_ADDRESS` | **TBD** | | `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) | ## Ethereum 1.0 deposit contract -The initial deployment phases of Ethereum 2.0 are implemented without consensus changes to Ethereum 1.0. A deposit contract at address `DEPOSIT_CONTRACT_ADDRESS` is added to Ethereum 1.0 for deposits of ETH to the beacon chain. Validator balances will be withdrawable to the shards in Phase 2. +The initial deployment phases of Ethereum 2.0 are implemented without consensus changes to Ethereum 1.0. A deposit contract at address `DEPOSIT_CONTRACT_ADDRESS` is added to Ethereum 1.0 chain defined by `DEPOSIT_CHAIN_ID` and `DEPOSIT_NETWORK_ID` for deposits of ETH to the beacon chain. Validator balances will be withdrawable to the shards in Phase 2. ### `deposit` function diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index 6a66dc707..e379df03c 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -111,7 +111,7 @@ The validator constructs their `withdrawal_credentials` via the following: ### Submit deposit -In Phase 0, all incoming validator deposits originate from the Ethereum 1.0 proof-of-work chain. Deposits are made to the [deposit contract](./deposit-contract.md) located at `DEPOSIT_CONTRACT_ADDRESS`. +In Phase 0, all incoming validator deposits originate from the Ethereum 1.0 chain defined by `DEPOSIT_CHAIN_ID` and `DEPOSIT_NETWORK_ID`. Deposits are made to the [deposit contract](./deposit-contract.md) located at `DEPOSIT_CONTRACT_ADDRESS`. To submit a deposit: From 4c3f866a0ff012efa642db9fc09280fd0b90ba2a Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 23 Jul 2020 12:25:44 -0600 Subject: [PATCH 32/43] add links for network and chain id --- specs/phase0/deposit-contract.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specs/phase0/deposit-contract.md b/specs/phase0/deposit-contract.md index 68d281614..db9566189 100644 --- a/specs/phase0/deposit-contract.md +++ b/specs/phase0/deposit-contract.md @@ -38,7 +38,9 @@ This document represents the specification for the beacon chain deposit contract ## Ethereum 1.0 deposit contract -The initial deployment phases of Ethereum 2.0 are implemented without consensus changes to Ethereum 1.0. A deposit contract at address `DEPOSIT_CONTRACT_ADDRESS` is added to Ethereum 1.0 chain defined by `DEPOSIT_CHAIN_ID` and `DEPOSIT_NETWORK_ID` for deposits of ETH to the beacon chain. Validator balances will be withdrawable to the shards in Phase 2. +The initial deployment phases of Ethereum 2.0 are implemented without consensus changes to Ethereum 1.0. A deposit contract at address `DEPOSIT_CONTRACT_ADDRESS` is added to the Ethereum 1.0 chain defined by the [chain-id](https://eips.ethereum.org/EIPS/eip-155) -- `DEPOSIT_CHAIN_ID` -- and the network-id -- `DEPOSIT_NETWORK_ID` -- for deposits of ETH to the beacon chain. Validator balances will be withdrawable to the shards in Phase 2. + +_Note_: See [here](https://chainid.network/) for a comprehensive list of public Ethereum chain chain-id's and network-id's. ### `deposit` function From bdde2e588968e26a6ffe5748570693950c547bd2 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 24 Jul 2020 03:52:41 +0800 Subject: [PATCH 33/43] Reorg test dir --- .../pyspec/eth2spec/test/{ => phase0/finality}/test_finality.py | 0 .../core/pyspec/eth2spec/test/{ => phase0}/genesis/__init__.py | 0 .../eth2spec/test/{ => phase0}/genesis/test_initialization.py | 0 .../pyspec/eth2spec/test/{ => phase0}/genesis/test_validity.py | 0 .../test/{ => phase0/unittests}/fork_choice/test_get_head.py | 0 .../{ => phase0/unittests}/fork_choice/test_on_attestation.py | 0 .../test/{ => phase0/unittests}/fork_choice/test_on_block.py | 0 .../test/{ => phase0/unittests}/fork_choice/test_on_tick.py | 0 .../{ => phase0/unittests}/validator/test_validator_unittest.py | 0 .../{ => phase1/unittests}/fork_choice/test_on_shard_head.py | 0 tests/generators/genesis/main.py | 2 +- 11 files changed, 1 insertion(+), 1 deletion(-) rename tests/core/pyspec/eth2spec/test/{ => phase0/finality}/test_finality.py (100%) rename tests/core/pyspec/eth2spec/test/{ => phase0}/genesis/__init__.py (100%) rename tests/core/pyspec/eth2spec/test/{ => phase0}/genesis/test_initialization.py (100%) rename tests/core/pyspec/eth2spec/test/{ => phase0}/genesis/test_validity.py (100%) rename tests/core/pyspec/eth2spec/test/{ => phase0/unittests}/fork_choice/test_get_head.py (100%) rename tests/core/pyspec/eth2spec/test/{ => phase0/unittests}/fork_choice/test_on_attestation.py (100%) rename tests/core/pyspec/eth2spec/test/{ => phase0/unittests}/fork_choice/test_on_block.py (100%) rename tests/core/pyspec/eth2spec/test/{ => phase0/unittests}/fork_choice/test_on_tick.py (100%) rename tests/core/pyspec/eth2spec/test/{ => phase0/unittests}/validator/test_validator_unittest.py (100%) rename tests/core/pyspec/eth2spec/test/{ => phase1/unittests}/fork_choice/test_on_shard_head.py (100%) diff --git a/tests/core/pyspec/eth2spec/test/test_finality.py b/tests/core/pyspec/eth2spec/test/phase0/finality/test_finality.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/test_finality.py rename to tests/core/pyspec/eth2spec/test/phase0/finality/test_finality.py diff --git a/tests/core/pyspec/eth2spec/test/genesis/__init__.py b/tests/core/pyspec/eth2spec/test/phase0/genesis/__init__.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/genesis/__init__.py rename to tests/core/pyspec/eth2spec/test/phase0/genesis/__init__.py diff --git a/tests/core/pyspec/eth2spec/test/genesis/test_initialization.py b/tests/core/pyspec/eth2spec/test/phase0/genesis/test_initialization.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/genesis/test_initialization.py rename to tests/core/pyspec/eth2spec/test/phase0/genesis/test_initialization.py diff --git a/tests/core/pyspec/eth2spec/test/genesis/test_validity.py b/tests/core/pyspec/eth2spec/test/phase0/genesis/test_validity.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/genesis/test_validity.py rename to tests/core/pyspec/eth2spec/test/phase0/genesis/test_validity.py diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_get_head.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_get_head.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/fork_choice/test_get_head.py rename to tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_get_head.py diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_attestation.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/fork_choice/test_on_attestation.py rename to tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_block.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/fork_choice/test_on_block.py rename to tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_block.py diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_tick.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_tick.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/fork_choice/test_on_tick.py rename to tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_tick.py diff --git a/tests/core/pyspec/eth2spec/test/validator/test_validator_unittest.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/validator/test_validator_unittest.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/validator/test_validator_unittest.py rename to tests/core/pyspec/eth2spec/test/phase0/unittests/validator/test_validator_unittest.py diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_shard_head.py b/tests/core/pyspec/eth2spec/test/phase1/unittests/fork_choice/test_on_shard_head.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/fork_choice/test_on_shard_head.py rename to tests/core/pyspec/eth2spec/test/phase1/unittests/fork_choice/test_on_shard_head.py diff --git a/tests/generators/genesis/main.py b/tests/generators/genesis/main.py index 8548b12c1..a978ab684 100644 --- a/tests/generators/genesis/main.py +++ b/tests/generators/genesis/main.py @@ -1,7 +1,7 @@ from typing import Iterable from eth2spec.test.context import PHASE0 -from eth2spec.test.genesis import test_initialization, test_validity +from eth2spec.test.phase0.genesis import test_initialization, test_validity from gen_base import gen_runner, gen_typing from gen_from_tests.gen import generate_from_tests From de15960185804a82d41ef6f1df62a798cb7b8af5 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 24 Jul 2020 04:15:48 +0800 Subject: [PATCH 34/43] Add finality tests to test vectors --- .../eth2spec/test/phase0/finality/__init__.py | 0 tests/formats/finality/README.md | 43 +++++++++++++++++++ tests/generators/finality/README.md | 5 +++ tests/generators/finality/main.py | 37 ++++++++++++++++ tests/generators/finality/requirements.txt | 2 + 5 files changed, 87 insertions(+) create mode 100644 tests/core/pyspec/eth2spec/test/phase0/finality/__init__.py create mode 100644 tests/formats/finality/README.md create mode 100644 tests/generators/finality/README.md create mode 100644 tests/generators/finality/main.py create mode 100644 tests/generators/finality/requirements.txt diff --git a/tests/core/pyspec/eth2spec/test/phase0/finality/__init__.py b/tests/core/pyspec/eth2spec/test/phase0/finality/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/formats/finality/README.md b/tests/formats/finality/README.md new file mode 100644 index 000000000..3e2b40be3 --- /dev/null +++ b/tests/formats/finality/README.md @@ -0,0 +1,43 @@ +# Finality tests + +The aim of the tests for the finality rules. + +- [`finality`](./finality.md): transitions triggered by one or more blocks. + +## Test case format + +### `meta.yaml` + +```yaml +description: string -- Optional. Description of test case, purely for debugging purposes. +bls_setting: int -- see general test-format spec. +blocks_count: int -- the number of blocks processed in this test. +``` + +### `pre.yaml` + +A YAML-encoded `BeaconState`, the state before running the block transitions. + +Also available as `pre.ssz`. + + +### `blocks_.yaml` + +A series of files, with `` in range `[0, blocks_count)`. Blocks need to be processed in order, + following the main transition function (i.e. process slot and epoch transitions in between blocks as normal) + +Each file is a YAML-encoded `SignedBeaconBlock`. + +Each block is also available as `blocks_.ssz` + +### `post.yaml` + +A YAML-encoded `BeaconState`, the state after applying the block transitions. + +Also available as `post.ssz`. + + +## Condition + +The resulting state should match the expected `post` state, or if the `post` state is left blank, + the handler should reject the series of blocks as invalid. diff --git a/tests/generators/finality/README.md b/tests/generators/finality/README.md new file mode 100644 index 000000000..dec5819c6 --- /dev/null +++ b/tests/generators/finality/README.md @@ -0,0 +1,5 @@ +# Finality tests + +Finality tests cover regular state-transitions in a common block-list format to test finality rules. + +Information on the format of the tests can be found in the [finality test formats documentation](../../formats/finality/README.md). diff --git a/tests/generators/finality/main.py b/tests/generators/finality/main.py new file mode 100644 index 000000000..5ab5a98e1 --- /dev/null +++ b/tests/generators/finality/main.py @@ -0,0 +1,37 @@ +from typing import Iterable +from importlib import reload + +from gen_base import gen_runner, gen_typing +from gen_from_tests.gen import generate_from_tests + +from eth2spec.test.context import PHASE0 +from eth2spec.test.phase0.finality import test_finality +from eth2spec.config import config_util +from eth2spec.phase0 import spec as spec_phase0 +from eth2spec.phase1 import spec as spec_phase1 + + +def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider: + + def prepare_fn(configs_path: str) -> str: + config_util.prepare_config(configs_path, config_name) + reload(spec_phase0) + reload(spec_phase1) + return config_name + + def cases_fn() -> Iterable[gen_typing.TestCase]: + return generate_from_tests( + runner_name='finality', + handler_name=handler_name, + src=tests_src, + fork_name=PHASE0, + ) + + return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn) + + +if __name__ == "__main__": + gen_runner.run_generator("finality", [ + create_provider('finality', test_finality, 'minimal'), + create_provider('finality', test_finality, 'mainnet'), + ]) diff --git a/tests/generators/finality/requirements.txt b/tests/generators/finality/requirements.txt new file mode 100644 index 000000000..b82314298 --- /dev/null +++ b/tests/generators/finality/requirements.txt @@ -0,0 +1,2 @@ +../../core/gen_helpers +../../../ \ No newline at end of file From b4c91efdbc0ff3e4da9d49a0b1a973b927d2cd31 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 24 Jul 2020 04:25:53 +0800 Subject: [PATCH 35/43] Add __init__.py --- .../core/pyspec/eth2spec/test/phase1/epoch_processing/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/phase1/epoch_processing/__init__.py diff --git a/tests/core/pyspec/eth2spec/test/phase1/epoch_processing/__init__.py b/tests/core/pyspec/eth2spec/test/phase1/epoch_processing/__init__.py new file mode 100644 index 000000000..e69de29bb From 607041225f44fa76e5fb213d579a5c351e45ab2f Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 23 Jul 2020 15:05:25 -0600 Subject: [PATCH 36/43] split config vs constants in deposit-contract spec --- specs/phase0/deposit-contract.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/specs/phase0/deposit-contract.md b/specs/phase0/deposit-contract.md index db9566189..b4f8d3036 100644 --- a/specs/phase0/deposit-contract.md +++ b/specs/phase0/deposit-contract.md @@ -10,7 +10,7 @@ - [Introduction](#introduction) - [Constants](#constants) - - [Contract](#contract) +- [Configuration](#configuration) - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) - [`deposit` function](#deposit-function) - [Deposit amount](#deposit-amount) @@ -27,14 +27,23 @@ This document represents the specification for the beacon chain deposit contract ## Constants -### Contract +The following values are (non-configurable) constants used throughout the specification. + +| Name | Value | +| - | - | +| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) | + +## Configuration + +*Note*: The default mainnet configuration values are included here for spec-design purposes. +The different configurations for mainnet, testnets, and YAML-based testing can be found in the [`configs/constant_presets`](../../configs) directory. +These configurations are updated for releases and may be out of sync during `dev` changes. | Name | Value | | - | - | | `DEPOSIT_CHAIN_ID` | `1` | | `DEPOSIT_NETWORK_ID` | `1` | | `DEPOSIT_CONTRACT_ADDRESS` | **TBD** | -| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) | ## Ethereum 1.0 deposit contract From 64aed78d6aa4c66f5b3b5378b1db47828371f62f Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 24 Jul 2020 05:25:17 +0800 Subject: [PATCH 37/43] Only one test format for finality tests. No extra `finality.md` here --- tests/formats/finality/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/formats/finality/README.md b/tests/formats/finality/README.md index 3e2b40be3..da9108a6a 100644 --- a/tests/formats/finality/README.md +++ b/tests/formats/finality/README.md @@ -2,7 +2,7 @@ The aim of the tests for the finality rules. -- [`finality`](./finality.md): transitions triggered by one or more blocks. +- `finality`: transitions triggered by one or more blocks. ## Test case format From e9d887be4d23a9aec6e88d499007e5d547e7b01a Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 24 Jul 2020 01:19:14 +0200 Subject: [PATCH 38/43] Enable BLS in finality tests, use Milagro --- tests/core/pyspec/eth2spec/test/conftest.py | 4 ++-- .../test/phase0/finality/test_finality.py | 7 +------ tests/core/pyspec/eth2spec/utils/bls.py | 16 ++++++++++++++++ tests/generators/bls/main.py | 1 + tests/generators/epoch_processing/main.py | 2 ++ tests/generators/finality/main.py | 2 ++ tests/generators/genesis/main.py | 2 ++ tests/generators/operations/main.py | 2 ++ tests/generators/rewards/main.py | 2 ++ tests/generators/sanity/main.py | 2 ++ 10 files changed, 32 insertions(+), 8 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/conftest.py b/tests/core/pyspec/eth2spec/test/conftest.py index 01c974ae0..21f7c7abb 100644 --- a/tests/core/pyspec/eth2spec/test/conftest.py +++ b/tests/core/pyspec/eth2spec/test/conftest.py @@ -59,8 +59,8 @@ def bls_default(request): def bls_type(request): bls_type = request.config.getoption("--bls-type") if bls_type == "py_ecc": - bls_utils.bls = bls_utils.py_ecc_bls + bls_utils.use_py_ecc() elif bls_type == "milagro": - bls_utils.bls = bls_utils.milagro_bls + bls_utils.use_milagro() else: raise Exception(f"unrecognized bls type: {bls_type}") diff --git a/tests/core/pyspec/eth2spec/test/phase0/finality/test_finality.py b/tests/core/pyspec/eth2spec/test/phase0/finality/test_finality.py index adbadcdf2..d2d3d4405 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/finality/test_finality.py +++ b/tests/core/pyspec/eth2spec/test/phase0/finality/test_finality.py @@ -1,4 +1,4 @@ -from eth2spec.test.context import PHASE0, spec_state_test, never_bls, with_all_phases, with_phases +from eth2spec.test.context import PHASE0, spec_state_test, with_all_phases, with_phases from eth2spec.test.helpers.state import next_epoch_via_block from eth2spec.test.helpers.attestations import next_epoch_with_attestations @@ -30,7 +30,6 @@ def check_finality(spec, @with_phases([PHASE0]) @spec_state_test -@never_bls def test_finality_no_updates_at_genesis(spec, state): assert spec.get_current_epoch(state) == spec.GENESIS_EPOCH @@ -54,7 +53,6 @@ def test_finality_no_updates_at_genesis(spec, state): @with_all_phases @spec_state_test -@never_bls def test_finality_rule_4(spec, state): # get past first two epochs that finality does not run on next_epoch_via_block(spec, state) @@ -80,7 +78,6 @@ def test_finality_rule_4(spec, state): @with_all_phases @spec_state_test -@never_bls def test_finality_rule_1(spec, state): # get past first two epochs that finality does not run on next_epoch_via_block(spec, state) @@ -108,7 +105,6 @@ def test_finality_rule_1(spec, state): @with_all_phases @spec_state_test -@never_bls def test_finality_rule_2(spec, state): # get past first two epochs that finality does not run on next_epoch_via_block(spec, state) @@ -138,7 +134,6 @@ def test_finality_rule_2(spec, state): @with_all_phases @spec_state_test -@never_bls def test_finality_rule_3(spec, state): """ Test scenario described here diff --git a/tests/core/pyspec/eth2spec/utils/bls.py b/tests/core/pyspec/eth2spec/utils/bls.py index 778b23da7..8b91dd64e 100644 --- a/tests/core/pyspec/eth2spec/utils/bls.py +++ b/tests/core/pyspec/eth2spec/utils/bls.py @@ -14,6 +14,22 @@ Z2_SIGNATURE = b'\xc0' + b'\x00' * 95 STUB_COORDINATES = _signature_to_G2(Z2_SIGNATURE) +def use_milagro(): + """ + Shortcut to use Milagro as BLS library + """ + global bls + bls = milagro_bls + + +def use_py_ecc(): + """ + Shortcut to use Py-ecc as BLS library + """ + global bls + bls = py_ecc_bls + + def only_with_bls(alt_return=None): """ Decorator factory to make a function only run when BLS is active. Otherwise return the default. diff --git a/tests/generators/bls/main.py b/tests/generators/bls/main.py index bfe0c8342..6fec61de0 100644 --- a/tests/generators/bls/main.py +++ b/tests/generators/bls/main.py @@ -321,6 +321,7 @@ def create_provider(handler_name: str, if __name__ == "__main__": + bls.use_py_ecc() # Py-ecc is chosen instead of Milagro, since the code is better understood to be correct. gen_runner.run_generator("bls", [ create_provider('sign', case01_sign), create_provider('verify', case02_verify), diff --git a/tests/generators/epoch_processing/main.py b/tests/generators/epoch_processing/main.py index 13ac76f41..418d6c750 100644 --- a/tests/generators/epoch_processing/main.py +++ b/tests/generators/epoch_processing/main.py @@ -14,6 +14,7 @@ from gen_from_tests.gen import generate_from_tests from importlib import reload from eth2spec.config import config_util from eth2spec.test.context import PHASE0 +from eth2spec.utils import bls def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider: @@ -22,6 +23,7 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin config_util.prepare_config(configs_path, config_name) reload(spec_phase0) reload(spec_phase1) + bls.use_milagro() return config_name def cases_fn() -> Iterable[gen_typing.TestCase]: diff --git a/tests/generators/finality/main.py b/tests/generators/finality/main.py index 5ab5a98e1..dca0ecb8d 100644 --- a/tests/generators/finality/main.py +++ b/tests/generators/finality/main.py @@ -9,6 +9,7 @@ from eth2spec.test.phase0.finality import test_finality from eth2spec.config import config_util from eth2spec.phase0 import spec as spec_phase0 from eth2spec.phase1 import spec as spec_phase1 +from eth2spec.utils import bls def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider: @@ -17,6 +18,7 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin config_util.prepare_config(configs_path, config_name) reload(spec_phase0) reload(spec_phase1) + bls.use_milagro() return config_name def cases_fn() -> Iterable[gen_typing.TestCase]: diff --git a/tests/generators/genesis/main.py b/tests/generators/genesis/main.py index a978ab684..ce055b44a 100644 --- a/tests/generators/genesis/main.py +++ b/tests/generators/genesis/main.py @@ -8,6 +8,7 @@ from gen_from_tests.gen import generate_from_tests from eth2spec.phase0 import spec as spec from importlib import reload from eth2spec.config import config_util +from eth2spec.utils import bls def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider: @@ -15,6 +16,7 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin def prepare_fn(configs_path: str) -> str: config_util.prepare_config(configs_path, config_name) reload(spec) + bls.use_milagro() return config_name def cases_fn() -> Iterable[gen_typing.TestCase]: diff --git a/tests/generators/operations/main.py b/tests/generators/operations/main.py index 6d4f6d139..be490c5b2 100644 --- a/tests/generators/operations/main.py +++ b/tests/generators/operations/main.py @@ -16,6 +16,7 @@ from eth2spec.config import config_util from eth2spec.phase0 import spec as spec_phase0 from eth2spec.phase1 import spec as spec_phase1 from eth2spec.test.context import PHASE0 +from eth2spec.utils import bls def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider: @@ -24,6 +25,7 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin config_util.prepare_config(configs_path, config_name) reload(spec_phase0) reload(spec_phase1) + bls.use_milagro() return config_name def cases_fn() -> Iterable[gen_typing.TestCase]: diff --git a/tests/generators/rewards/main.py b/tests/generators/rewards/main.py index 5d063c434..dd82ee165 100644 --- a/tests/generators/rewards/main.py +++ b/tests/generators/rewards/main.py @@ -12,6 +12,7 @@ from gen_from_tests.gen import generate_from_tests from importlib import reload from eth2spec.config import config_util from eth2spec.test.context import PHASE0 +from eth2spec.utils import bls def create_provider(tests_src, config_name: str) -> gen_typing.TestProvider: @@ -20,6 +21,7 @@ def create_provider(tests_src, config_name: str) -> gen_typing.TestProvider: config_util.prepare_config(configs_path, config_name) reload(spec_phase0) reload(spec_phase1) + bls.use_milagro() return config_name def cases_fn() -> Iterable[gen_typing.TestCase]: diff --git a/tests/generators/sanity/main.py b/tests/generators/sanity/main.py index 45a1c8c4f..2feaaf09d 100644 --- a/tests/generators/sanity/main.py +++ b/tests/generators/sanity/main.py @@ -9,6 +9,7 @@ from eth2spec.test.phase0.sanity import test_blocks, test_slots from eth2spec.config import config_util from eth2spec.phase0 import spec as spec_phase0 from eth2spec.phase1 import spec as spec_phase1 +from eth2spec.utils import bls def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider: @@ -17,6 +18,7 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin config_util.prepare_config(configs_path, config_name) reload(spec_phase0) reload(spec_phase1) + bls.use_milagro() return config_name def cases_fn() -> Iterable[gen_typing.TestCase]: From 230e5ce34598cf0e86d23d34cd4f733ba92fbfae Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 24 Jul 2020 01:19:50 +0200 Subject: [PATCH 39/43] remove outdated hash cache, use less memory --- setup.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/setup.py b/setup.py index bac58e16a..d1f7b35ce 100644 --- a/setup.py +++ b/setup.py @@ -141,11 +141,6 @@ def ceillog2(x: uint64) -> int: return (x - 1).bit_length() ''' PHASE0_SUNDRY_FUNCTIONS = ''' -# Monkey patch hash cache -_hash = hash -hash_cache: Dict[bytes, Bytes32] = {} - - def get_eth1_data(block: Eth1Block) -> Eth1Data: """ A stub function return mocking Eth1Data. @@ -156,12 +151,6 @@ def get_eth1_data(block: Eth1Block) -> Eth1Data: block_hash=hash_tree_root(block)) -def hash(x: bytes) -> Bytes32: # type: ignore - if x not in hash_cache: - hash_cache[x] = Bytes32(_hash(x)) - return hash_cache[x] - - def cache_this(key_fn, value_fn, lru_size): # type: ignore cache_dict = LRU(size=lru_size) From 1d2a47566cd5372a7ae2233e6ae4f158b6264ddf Mon Sep 17 00:00:00 2001 From: terence tsao Date: Thu, 23 Jul 2020 16:35:21 -0700 Subject: [PATCH 40/43] Update sharding FAQs link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 13c644a77..78ce5f31f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Join the chat at https://discord.gg/hpFs23p](https://img.shields.io/badge/chat-on%20discord-blue.svg)](https://discord.gg/hpFs23p) [![Join the chat at https://gitter.im/ethereum/sharding](https://badges.gitter.im/ethereum/sharding.svg)](https://gitter.im/ethereum/sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -To learn more about sharding and Ethereum 2.0 (Serenity), see the [sharding FAQ](https://github.com/ethereum/wiki/wiki/Sharding-FAQ) and the [research compendium](https://notes.ethereum.org/s/H1PGqDhpm). +To learn more about sharding and Ethereum 2.0 (Serenity), see the [sharding FAQ](https://eth.wiki/sharding/Sharding-FAQs) and the [research compendium](https://notes.ethereum.org/s/H1PGqDhpm). This repository hosts the current Eth2 specifications. Discussions about design rationale and proposed changes can be brought up and discussed as issues. Solidified, agreed-upon changes to the spec can be made through pull requests. From 951c552ecccce805d09ebd50327b06accecbb97d Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 24 Jul 2020 01:53:14 +0200 Subject: [PATCH 41/43] remove second hash cache, type as Bytes32 --- .../core/pyspec/eth2spec/utils/hash_function.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/tests/core/pyspec/eth2spec/utils/hash_function.py b/tests/core/pyspec/eth2spec/utils/hash_function.py index 627f9b990..470cb1da9 100644 --- a/tests/core/pyspec/eth2spec/utils/hash_function.py +++ b/tests/core/pyspec/eth2spec/utils/hash_function.py @@ -1,17 +1,9 @@ from hashlib import sha256 -from typing import Dict, Union +from remerkleable.byte_arrays import Bytes32 +from typing import Union ZERO_BYTES32 = b'\x00' * 32 -def _hash(x: Union[bytes, bytearray, memoryview]) -> bytes: - return sha256(x).digest() - - -hash_cache: Dict[bytes, bytes] = {} - - -def hash(x: bytes) -> bytes: - if x in hash_cache: - return hash_cache[x] - return _hash(x) +def hash(x: Union[bytes, bytearray, memoryview]) -> Bytes32: + return Bytes32(sha256(x).digest()) From 6f7652d330f5c0db698deb994e5f242e35a43f68 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Fri, 24 Jul 2020 07:26:37 +0200 Subject: [PATCH 42/43] process_attestation: Validate epoch before using it `data.target.epoch` is used to count the active validator set. Because `get_committee_count_per_slot` is extremely inefficient the way the spec is written, clients cache it, or the underlying active validator set. Performing the checks in the given order leads to a (very unlikely) security issue where the a cold and above all, distant value may get used which may be costly - reordering the checks brings the value into a more reasonable range before using it. --- specs/phase0/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 4db18228c..15c8924a4 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -1718,10 +1718,10 @@ def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSla ```python def process_attestation(state: BeaconState, attestation: Attestation) -> None: data = attestation.data - assert data.index < get_committee_count_per_slot(state, data.target.epoch) assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state)) assert data.target.epoch == compute_epoch_at_slot(data.slot) assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH + assert data.index < get_committee_count_per_slot(state, data.target.epoch) committee = get_beacon_committee(state, data.slot, data.index) assert len(attestation.aggregation_bits) == len(committee) From fbb8401d03f0b61deb3acfa2aa802e2732355304 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 24 Jul 2020 15:09:20 +0200 Subject: [PATCH 43/43] sanity test: proposer slashes themselves --- .../test/phase0/sanity/test_blocks.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py index c3b1dceed..c183865a5 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py @@ -313,6 +313,28 @@ def test_empty_epoch_transition_not_finalizing(spec, state): assert state.balances[index] < pre_balances[index] +@with_all_phases +@spec_state_test +def test_proposer_self_slashing(spec, state): + yield 'pre', state + + block = build_empty_block_for_next_slot(spec, state) + assert not state.validators[block.proposer_index].slashed + + proposer_slashing = get_valid_proposer_slashing( + spec, state, slashed_index=block.proposer_index, signed_1=True, signed_2=True) + block.body.proposer_slashings.append(proposer_slashing) + + # The header is processed *before* the block body: + # the proposer was not slashed before the body, thus the block is valid. + signed_block = state_transition_and_sign_block(spec, state, block) + # The proposer slashed themselves. + assert state.validators[block.proposer_index].slashed + + yield 'blocks', [signed_block] + yield 'post', state + + @with_all_phases @spec_state_test def test_proposer_slashing(spec, state):