From df6e531d7401a9388c3a69d92bf542a18c7532d5 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Tue, 20 Aug 2019 11:37:27 +0200 Subject: [PATCH 1/5] Improve beacon proposer selection logic --- specs/core/0_beacon-chain.md | 14 +++++--------- .../eth2spec/test/helpers/proposer_slashings.py | 1 - 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7ed3226e1..f27d016cf 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -934,15 +934,12 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: Return the beacon proposer index at the current slot. """ epoch = get_current_epoch(state) - committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH - offset = committees_per_slot * (state.slot % SLOTS_PER_EPOCH) - shard = Shard((get_start_shard(state, epoch) + offset) % SHARD_COUNT) - first_committee = get_crosslink_committee(state, epoch, shard) + indices = get_active_validator_indices(state, epoch) + seed = hash(get_seed(state, epoch) + int_to_bytes(state.slot, length=8)) MAX_RANDOM_BYTE = 2**8 - 1 - seed = get_seed(state, epoch) i = 0 while True: - candidate_index = first_committee[(epoch + i) % len(first_committee)] + candidate_index = indices[compute_shuffled_index(ValidatorIndex(i % len(indices)), len(indices), 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: @@ -1608,9 +1605,8 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: ```python def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None: proposer = state.validators[proposer_slashing.proposer_index] - # Verify that the epoch is the same - assert (compute_epoch_of_slot(proposer_slashing.header_1.slot) - == compute_epoch_of_slot(proposer_slashing.header_2.slot)) + # Verify slots match + assert proposer_slashing.header_1.slot == proposer_slashing.header_2.slot # But the headers are different assert proposer_slashing.header_1 != proposer_slashing.header_2 # Check proposer is slashable diff --git a/test_libs/pyspec/eth2spec/test/helpers/proposer_slashings.py b/test_libs/pyspec/eth2spec/test/helpers/proposer_slashings.py index d5b7f7b7f..ce53c5931 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/proposer_slashings.py +++ b/test_libs/pyspec/eth2spec/test/helpers/proposer_slashings.py @@ -18,7 +18,6 @@ def get_valid_proposer_slashing(spec, state, signed_1=False, signed_2=False): ) header_2 = deepcopy(header_1) header_2.parent_root = b'\x99' * 32 - header_2.slot = slot + 1 if signed_1: sign_block_header(spec, state, header_1, privkey) From c8c47e39865567e4f9eb7a180bc32e99192f34e8 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Wed, 28 Aug 2019 10:22:34 +0100 Subject: [PATCH 2/5] Abstract away compute_proposer_index for phase 1 --- specs/core/0_beacon-chain.md | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index f27d016cf..7398da312 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -68,6 +68,7 @@ - [`is_valid_merkle_branch`](#is_valid_merkle_branch) - [Misc](#misc-1) - [`compute_shuffled_index`](#compute_shuffled_index) + - [`compute_proposer_index`](#compute_proposer_index) - [`compute_committee`](#compute_committee) - [`compute_epoch_of_slot`](#compute_epoch_of_slot) - [`compute_start_slot_of_epoch`](#compute_start_slot_of_epoch) @@ -729,6 +730,25 @@ def compute_shuffled_index(index: ValidatorIndex, index_count: uint64, seed: Has return ValidatorIndex(index) ``` +#### `compute_proposer_index` + +```python +def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Hash) -> ValidatorIndex: + """ + Return from ``indices`` a random index sampled by effective balance. + """ + assert len(indices) > 0 + MAX_RANDOM_BYTE = 2**8 - 1 + i = 0 + while True: + candidate_index = indices[compute_shuffled_index(ValidatorIndex(i % len(indices)), len(indices), 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 ValidatorIndex(candidate_index) + i += 1 +``` + #### `compute_committee` ```python @@ -934,17 +954,9 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: Return the beacon proposer index at the current slot. """ epoch = get_current_epoch(state) - indices = get_active_validator_indices(state, epoch) seed = hash(get_seed(state, epoch) + int_to_bytes(state.slot, length=8)) - MAX_RANDOM_BYTE = 2**8 - 1 - i = 0 - while True: - candidate_index = indices[compute_shuffled_index(ValidatorIndex(i % len(indices)), len(indices), 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 ValidatorIndex(candidate_index) - i += 1 + indices = get_active_validator_indices(state, epoch) + return compute_proposer_index(state, indices, seed) ``` #### `get_attestation_data_slot` From 970ae2fad1c82e250e8a1369126e6e4df310ed47 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 16 Sep 2019 09:16:44 -0500 Subject: [PATCH 3/5] update validator doc with modified beacon proposer selection --- specs/validator/0_beacon-chain-validator.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index ef5ad4415..ddf5b5f23 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -160,7 +160,7 @@ def get_committee_assignment(state: BeaconState, return None ``` -A validator can use the following function to see if they are supposed to propose during their assigned committee slot. This function can only be run with a `state` of the slot in question. Proposer selection is only stable within the context of the current epoch. +A validator can use the following function to see if they are supposed to propose during a slot. This function can only be run with a `state` of the slot in question. Proposer selection is only stable within the context of the current epoch. ```python def is_proposer(state: BeaconState, @@ -170,6 +170,8 @@ def is_proposer(state: BeaconState, *Note*: To see if a validator is assigned to propose during the slot, the beacon state must be in the epoch in question. At the epoch boundaries, the validator must run an epoch transition into the epoch to successfully check the proposal assignment of the first slot. +*Note*: `BeaconBlock` proposal is distinct from crosslink committee assignment, and in a given epoch each responsbility might occur at different a different slot. + ### Lookahead The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming committee assignments for attesting dictated by the shuffling and slot. Note that this lookahead does not apply to proposing, which must be checked during the epoch in question. From 2e5389978402736121819fd6dfe0d9031da47b6a Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 21 Sep 2019 02:59:41 +0300 Subject: [PATCH 4/5] typo --- specs/validator/0_beacon-chain-validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index ddf5b5f23..a9736de70 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -170,7 +170,7 @@ def is_proposer(state: BeaconState, *Note*: To see if a validator is assigned to propose during the slot, the beacon state must be in the epoch in question. At the epoch boundaries, the validator must run an epoch transition into the epoch to successfully check the proposal assignment of the first slot. -*Note*: `BeaconBlock` proposal is distinct from crosslink committee assignment, and in a given epoch each responsbility might occur at different a different slot. +*Note*: `BeaconBlock` proposal is distinct from crosslink committee assignment, and in a given epoch each responsability might occur at different a different slot. ### Lookahead From 2ec363d614ea0d597824c45e9bd28cad15663ae3 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 23 Sep 2019 12:42:29 -0600 Subject: [PATCH 5/5] typo --- specs/validator/0_beacon-chain-validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index a9736de70..a25dc969b 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -170,7 +170,7 @@ def is_proposer(state: BeaconState, *Note*: To see if a validator is assigned to propose during the slot, the beacon state must be in the epoch in question. At the epoch boundaries, the validator must run an epoch transition into the epoch to successfully check the proposal assignment of the first slot. -*Note*: `BeaconBlock` proposal is distinct from crosslink committee assignment, and in a given epoch each responsability might occur at different a different slot. +*Note*: `BeaconBlock` proposal is distinct from crosslink committee assignment, and in a given epoch each responsibility might occur at different a different slot. ### Lookahead