From 786e611c71d5c62bf30c988c692ab88d1a54d8f3 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Wed, 5 May 2021 12:05:32 +0100 Subject: [PATCH 01/22] Flags to BitVector --- specs/altair/beacon-chain.md | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 7bf0c2d99..66b069cc5 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -69,7 +69,7 @@ Altair is the first beacon chain hard fork. Its main features are: | Name | SSZ equivalent | Description | | - | - | - | -| `ParticipationFlags` | `uint8` | a succinct representation of 8 boolean participation flags | +| `ParticipationFlags` | `BitVector[PARTICIPATION_FLAGS_LENGTH]` | a succinct representation of up to 8 boolean participation flags | ## Constants @@ -99,6 +99,7 @@ Altair is the first beacon chain hard fork. Its main features are: | Name | Value | | - | - | | `G2_POINT_AT_INFINITY` | `BLSSignature(b'\xc0' + b'\x00' * 95)` | +| `PARTICIPATION_FLAGS_LENGTH` | `8` | ## Configuration @@ -247,28 +248,6 @@ def get_flag_indices_and_weights() -> Sequence[Tuple[int, uint64]]: ) ``` -#### `add_flag` - -```python -def add_flag(flags: ParticipationFlags, flag_index: int) -> ParticipationFlags: - """ - Return a new ``ParticipationFlags`` adding ``flag_index`` to ``flags``. - """ - flag = ParticipationFlags(2**flag_index) - return flags | flag -``` - -#### `has_flag` - -```python -def has_flag(flags: ParticipationFlags, flag_index: int) -> bool: - """ - Return whether ``flags`` has ``flag_index`` set. - """ - flag = ParticipationFlags(2**flag_index) - return flags & flag == flag -``` - ### Beacon state accessors #### `get_sync_committee_indices` @@ -348,7 +327,7 @@ def get_unslashed_participating_indices(state: BeaconState, flag_index: int, epo else: epoch_participation = state.previous_epoch_participation active_validator_indices = get_active_validator_indices(state, epoch) - participating_indices = [i for i in active_validator_indices if has_flag(epoch_participation[i], flag_index)] + participating_indices = [i for i in active_validator_indices if epoch_participation[i][flag_index]] return set(filter(lambda index: not state.validators[index].slashed, participating_indices)) ``` @@ -492,8 +471,8 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: proposer_reward_numerator = 0 for index in get_attesting_indices(state, data, attestation.aggregation_bits): for flag_index, weight in get_flag_indices_and_weights(): - if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index): - epoch_participation[index] = add_flag(epoch_participation[index], flag_index) + if flag_index in participation_flag_indices and not epoch_participation[index][flag_index]: + epoch_participation[index][flag_index] = True proposer_reward_numerator += get_base_reward(state, index) * weight # Reward proposer From df6bd1b6c33833c264561da361436453e5e55f4c Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Wed, 5 May 2021 12:26:37 +0100 Subject: [PATCH 02/22] BitVector -> Bitvector --- specs/altair/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 66b069cc5..e023db1f7 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -69,7 +69,7 @@ Altair is the first beacon chain hard fork. Its main features are: | Name | SSZ equivalent | Description | | - | - | - | -| `ParticipationFlags` | `BitVector[PARTICIPATION_FLAGS_LENGTH]` | a succinct representation of up to 8 boolean participation flags | +| `ParticipationFlags` | `Bitvector[PARTICIPATION_FLAGS_LENGTH]` | a succinct representation of up to 8 boolean participation flags | ## Constants From 238a9b03fc794bfe322ebec4b67290df4159891c Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Wed, 5 May 2021 18:09:36 +0100 Subject: [PATCH 03/22] Correct confusing comments in "get_sync_committee_indices" --- specs/altair/beacon-chain.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index e023db1f7..719f93033 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -205,6 +205,7 @@ class BeaconState(Container): ```python class SyncAggregate(Container): sync_committee_bits: Bitvector[SYNC_COMMITTEE_SIZE] + # TODO! Need multiple signatures as discussed between Justin and Vitalik May 3 2021 (see Telegram) sync_committee_signature: BLSSignature ``` @@ -255,8 +256,9 @@ def get_flag_indices_and_weights() -> Sequence[Tuple[int, uint64]]: ```python def get_sync_committee_indices(state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]: """ - Return the sequence of sync committee indices (which may include duplicate indices) + Return the sequence of sync committee indices for a given ``state`` and ``epoch``. + Can contain duplicate indices for small validator sets (< 2 * SYNC_COMMITTEE_SIZE) """ MAX_RANDOM_BYTE = 2**8 - 1 base_epoch = Epoch((max(epoch // EPOCHS_PER_SYNC_COMMITTEE_PERIOD, 1) - 1) * EPOCHS_PER_SYNC_COMMITTEE_PERIOD) @@ -270,7 +272,7 @@ def get_sync_committee_indices(state: BeaconState, epoch: Epoch) -> Sequence[Val candidate_index = active_validator_indices[shuffled_index] 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: # Sample with replacement + if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: sync_committee_indices.append(candidate_index) i += 1 return sync_committee_indices From a382cf6d5c97409868a3da558351522b8b710d95 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Wed, 5 May 2021 20:04:06 +0100 Subject: [PATCH 04/22] Refactor participation flag list --- specs/altair/beacon-chain.md | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 719f93033..e311734d9 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -100,6 +100,9 @@ Altair is the first beacon chain hard fork. Its main features are: | - | - | | `G2_POINT_AT_INFINITY` | `BLSSignature(b'\xc0' + b'\x00' * 95)` | | `PARTICIPATION_FLAGS_LENGTH` | `8` | +| `PARTICIPATION_FLAGS` | `3` | +| `TIMELY_HEAD_WEIGHT` | `uint64(12)` | +| `PARTICIPATION_FLAG_WEIGHTS` | `[TIMELY_HEAD_WEIGHT, TIMELY_SOURCE_WEIGHT, TIMELY_TARGET_WEIGHT]` | ## Configuration @@ -233,22 +236,6 @@ def eth2_fast_aggregate_verify(pubkeys: Sequence[BLSPubkey], message: Bytes32, s return bls.FastAggregateVerify(pubkeys, message, signature) ``` -### Misc - -#### `get_flag_indices_and_weights` - -```python -def get_flag_indices_and_weights() -> Sequence[Tuple[int, uint64]]: - """ - Return paired tuples of participation flag indices along with associated incentivization weights. - """ - return ( - (TIMELY_HEAD_FLAG_INDEX, TIMELY_HEAD_WEIGHT), - (TIMELY_SOURCE_FLAG_INDEX, TIMELY_SOURCE_WEIGHT), - (TIMELY_TARGET_FLAG_INDEX, TIMELY_TARGET_WEIGHT), - ) -``` - ### Beacon state accessors #### `get_sync_committee_indices` @@ -336,16 +323,16 @@ def get_unslashed_participating_indices(state: BeaconState, flag_index: int, epo #### `get_flag_index_deltas` ```python -def get_flag_index_deltas(state: BeaconState, flag_index: int, weight: uint64) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: +def get_flag_index_deltas(state: BeaconState, flag_index: int) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: """ Return the deltas for a given ``flag_index`` scaled by ``weight`` by scanning through the participation flags. """ rewards = [Gwei(0)] * len(state.validators) penalties = [Gwei(0)] * len(state.validators) unslashed_participating_indices = get_unslashed_participating_indices(state, flag_index, get_previous_epoch(state)) - increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from balances to avoid uint64 overflow - unslashed_participating_increments = get_total_balance(state, unslashed_participating_indices) // increment - active_increments = get_total_active_balance(state) // increment + weight = PARTICIPATION_FLAG_WEIGHTS[flag_index] + unslashed_participating_increments = get_total_balance(state, unslashed_participating_indices) // EFFECTIVE_BALANCE_INCREMENT + active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT for index in get_eligible_validator_indices(state): base_reward = get_base_reward(state, index) if index in unslashed_participating_indices: @@ -376,7 +363,7 @@ def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], S previous_epoch = get_previous_epoch(state) matching_target_indices = get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, previous_epoch) for index in get_eligible_validator_indices(state): - for (_, weight) in get_flag_indices_and_weights(): + for weight in PARTICIPATION_FLAG_WEIGHTS: # This inactivity penalty cancels the flag reward corresponding to the flag index penalties[index] += Gwei(get_base_reward(state, index) * weight // WEIGHT_DENOMINATOR) if index not in matching_target_indices: @@ -472,7 +459,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: # Update epoch participation flags proposer_reward_numerator = 0 for index in get_attesting_indices(state, data, attestation.aggregation_bits): - for flag_index, weight in get_flag_indices_and_weights(): + for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS): if flag_index in participation_flag_indices and not epoch_participation[index][flag_index]: epoch_participation[index][flag_index] = True proposer_reward_numerator += get_base_reward(state, index) * weight @@ -613,8 +600,7 @@ def process_rewards_and_penalties(state: BeaconState) -> None: if get_current_epoch(state) == GENESIS_EPOCH: return - flag_indices_and_numerators = get_flag_indices_and_weights() - flag_deltas = [get_flag_index_deltas(state, index, numerator) for (index, numerator) in flag_indices_and_numerators] + flag_deltas = [get_flag_index_deltas(state, flag_index) for flag_index in range(PARTICIPATION_FLAGS)] deltas = flag_deltas + [get_inactivity_penalty_deltas(state)] for (rewards, penalties) in deltas: for index in range(len(state.validators)): From 227d1007e67558e7b7a419723efe932470976441 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Wed, 5 May 2021 20:09:14 +0100 Subject: [PATCH 05/22] Update toc --- specs/altair/beacon-chain.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index e311734d9..bd9c23bf3 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -27,10 +27,6 @@ - [Helper functions](#helper-functions) - [`Predicates`](#predicates) - [`eth2_fast_aggregate_verify`](#eth2_fast_aggregate_verify) - - [Misc](#misc-2) - - [`get_flag_indices_and_weights`](#get_flag_indices_and_weights) - - [`add_flag`](#add_flag) - - [`has_flag`](#has_flag) - [Beacon state accessors](#beacon-state-accessors) - [`get_sync_committee_indices`](#get_sync_committee_indices) - [`get_sync_committee`](#get_sync_committee) From 687641a79bbe9db18de358f6c230e05a59cb905e Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Wed, 5 May 2021 21:55:36 +0100 Subject: [PATCH 06/22] Remove extra variable --- specs/altair/beacon-chain.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index bd9c23bf3..1a50e7337 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -97,7 +97,6 @@ Altair is the first beacon chain hard fork. Its main features are: | `G2_POINT_AT_INFINITY` | `BLSSignature(b'\xc0' + b'\x00' * 95)` | | `PARTICIPATION_FLAGS_LENGTH` | `8` | | `PARTICIPATION_FLAGS` | `3` | -| `TIMELY_HEAD_WEIGHT` | `uint64(12)` | | `PARTICIPATION_FLAG_WEIGHTS` | `[TIMELY_HEAD_WEIGHT, TIMELY_SOURCE_WEIGHT, TIMELY_TARGET_WEIGHT]` | ## Configuration From 55471bc5d4f69678baf6950e6197b5206b2cbb8a Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Wed, 5 May 2021 21:58:03 +0100 Subject: [PATCH 07/22] Revert "BitVector -> Bitvector" This reverts commit df6bd1b6c33833c264561da361436453e5e55f4c. --- specs/altair/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 1a50e7337..9e868fbaa 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -65,7 +65,7 @@ Altair is the first beacon chain hard fork. Its main features are: | Name | SSZ equivalent | Description | | - | - | - | -| `ParticipationFlags` | `Bitvector[PARTICIPATION_FLAGS_LENGTH]` | a succinct representation of up to 8 boolean participation flags | +| `ParticipationFlags` | `BitVector[PARTICIPATION_FLAGS_LENGTH]` | a succinct representation of up to 8 boolean participation flags | ## Constants From 77524036f56c71d4c8199c1393af524b7841290f Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Wed, 5 May 2021 22:02:37 +0100 Subject: [PATCH 08/22] Revert "Flags to BitVector" This reverts commit 786e611c71d5c62bf30c988c692ab88d1a54d8f3. # Conflicts: # specs/altair/beacon-chain.md --- specs/altair/beacon-chain.md | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 9e868fbaa..5bce9ba4e 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -27,6 +27,9 @@ - [Helper functions](#helper-functions) - [`Predicates`](#predicates) - [`eth2_fast_aggregate_verify`](#eth2_fast_aggregate_verify) + - [Misc](#misc-2) + - [`add_flag`](#add_flag) + - [`has_flag`](#has_flag) - [Beacon state accessors](#beacon-state-accessors) - [`get_sync_committee_indices`](#get_sync_committee_indices) - [`get_sync_committee`](#get_sync_committee) @@ -65,7 +68,7 @@ Altair is the first beacon chain hard fork. Its main features are: | Name | SSZ equivalent | Description | | - | - | - | -| `ParticipationFlags` | `BitVector[PARTICIPATION_FLAGS_LENGTH]` | a succinct representation of up to 8 boolean participation flags | +| `ParticipationFlags` | `uint8` | a succinct representation of 8 boolean participation flags | ## Constants @@ -95,7 +98,6 @@ Altair is the first beacon chain hard fork. Its main features are: | Name | Value | | - | - | | `G2_POINT_AT_INFINITY` | `BLSSignature(b'\xc0' + b'\x00' * 95)` | -| `PARTICIPATION_FLAGS_LENGTH` | `8` | | `PARTICIPATION_FLAGS` | `3` | | `PARTICIPATION_FLAG_WEIGHTS` | `[TIMELY_HEAD_WEIGHT, TIMELY_SOURCE_WEIGHT, TIMELY_TARGET_WEIGHT]` | @@ -231,6 +233,30 @@ def eth2_fast_aggregate_verify(pubkeys: Sequence[BLSPubkey], message: Bytes32, s return bls.FastAggregateVerify(pubkeys, message, signature) ``` +### Misc + +#### `add_flag` + +```python +def add_flag(flags: ParticipationFlags, flag_index: int) -> ParticipationFlags: + """ + Return a new ``ParticipationFlags`` adding ``flag_index`` to ``flags``. + """ + flag = ParticipationFlags(2**flag_index) + return flags | flag +``` + +#### `has_flag` + +```python +def has_flag(flags: ParticipationFlags, flag_index: int) -> bool: + """ + Return whether ``flags`` has ``flag_index`` set. + """ + flag = ParticipationFlags(2**flag_index) + return flags & flag == flag +``` + ### Beacon state accessors #### `get_sync_committee_indices` @@ -311,7 +337,7 @@ def get_unslashed_participating_indices(state: BeaconState, flag_index: int, epo else: epoch_participation = state.previous_epoch_participation active_validator_indices = get_active_validator_indices(state, epoch) - participating_indices = [i for i in active_validator_indices if epoch_participation[i][flag_index]] + participating_indices = [i for i in active_validator_indices if has_flag(epoch_participation[i], flag_index)] return set(filter(lambda index: not state.validators[index].slashed, participating_indices)) ``` From b041a9b0d68cdee2de0d0d36590f07b49ea12dff Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Wed, 5 May 2021 22:22:32 +0100 Subject: [PATCH 09/22] Further flag_index revert --- specs/altair/beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 5bce9ba4e..5a8dcce24 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -481,8 +481,8 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: proposer_reward_numerator = 0 for index in get_attesting_indices(state, data, attestation.aggregation_bits): for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS): - if flag_index in participation_flag_indices and not epoch_participation[index][flag_index]: - epoch_participation[index][flag_index] = True + if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index): + epoch_participation[index] = add_flag(epoch_participation[index], flag_index) proposer_reward_numerator += get_base_reward(state, index) * weight # Reward proposer From cbf9f85537c3c91c38ac51b1e3ac4efd2d5f240c Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Thu, 6 May 2021 12:28:18 +0100 Subject: [PATCH 10/22] Remove duplicate inactivity leak --- specs/altair/beacon-chain.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 5a8dcce24..9ad2c0089 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -357,10 +357,7 @@ def get_flag_index_deltas(state: BeaconState, flag_index: int) -> Tuple[Sequence for index in get_eligible_validator_indices(state): base_reward = get_base_reward(state, index) if index in unslashed_participating_indices: - if is_in_inactivity_leak(state): - # This flag reward cancels the inactivity penalty corresponding to the flag index - rewards[index] += Gwei(base_reward * weight // WEIGHT_DENOMINATOR) - else: + if not is_in_inactivity_leak(state): reward_numerator = base_reward * weight * unslashed_participating_increments rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR)) else: @@ -384,9 +381,6 @@ def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], S previous_epoch = get_previous_epoch(state) matching_target_indices = get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, previous_epoch) for index in get_eligible_validator_indices(state): - for weight in PARTICIPATION_FLAG_WEIGHTS: - # This inactivity penalty cancels the flag reward corresponding to the flag index - penalties[index] += Gwei(get_base_reward(state, index) * weight // WEIGHT_DENOMINATOR) if index not in matching_target_indices: penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index] penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR From cf724daa7a07092a02522879a0b1cb088db7c3b5 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Thu, 6 May 2021 12:42:05 +0100 Subject: [PATCH 11/22] No inactivity penalty for untimely head --- specs/altair/beacon-chain.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 9ad2c0089..e90c587be 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -354,13 +354,14 @@ def get_flag_index_deltas(state: BeaconState, flag_index: int) -> Tuple[Sequence weight = PARTICIPATION_FLAG_WEIGHTS[flag_index] unslashed_participating_increments = get_total_balance(state, unslashed_participating_indices) // EFFECTIVE_BALANCE_INCREMENT active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT + in_inactivity_leak = is_in_inactivity_leak(state) for index in get_eligible_validator_indices(state): base_reward = get_base_reward(state, index) if index in unslashed_participating_indices: - if not is_in_inactivity_leak(state): + if not in_inactivity_leak: reward_numerator = base_reward * weight * unslashed_participating_increments rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR)) - else: + elif not (in_inactivity_leak and flag_index == TIMELY_HEAD_FLAG_INDEX): penalties[index] += Gwei(base_reward * weight // WEIGHT_DENOMINATOR) return rewards, penalties ``` From 81a8c2748fed22b6b5753840f228f00b212c8072 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Thu, 6 May 2021 13:03:26 +0100 Subject: [PATCH 12/22] Integrate get_inactivity_penalty_deltas into reward computation for better readability --- specs/altair/beacon-chain.md | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index e90c587be..f464bd640 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -350,7 +350,8 @@ def get_flag_index_deltas(state: BeaconState, flag_index: int) -> Tuple[Sequence """ rewards = [Gwei(0)] * len(state.validators) penalties = [Gwei(0)] * len(state.validators) - unslashed_participating_indices = get_unslashed_participating_indices(state, flag_index, get_previous_epoch(state)) + previous_epoch = get_previous_epoch(state) + unslashed_participating_indices = get_unslashed_participating_indices(state, flag_index, previous_epoch) weight = PARTICIPATION_FLAG_WEIGHTS[flag_index] unslashed_participating_increments = get_total_balance(state, unslashed_participating_indices) // EFFECTIVE_BALANCE_INCREMENT active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT @@ -363,29 +364,11 @@ def get_flag_index_deltas(state: BeaconState, flag_index: int) -> Tuple[Sequence rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR)) elif not (in_inactivity_leak and flag_index == TIMELY_HEAD_FLAG_INDEX): penalties[index] += Gwei(base_reward * weight // WEIGHT_DENOMINATOR) - return rewards, penalties -``` - -#### Modified `get_inactivity_penalty_deltas` - -*Note*: The function `get_inactivity_penalty_deltas` is modified in the selection of matching target indices -and the removal of `BASE_REWARDS_PER_EPOCH`. - -```python -def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: - """ - Return the inactivity penalty deltas by considering timely target participation flags and inactivity scores. - """ - rewards = [Gwei(0) for _ in range(len(state.validators))] - penalties = [Gwei(0) for _ in range(len(state.validators))] - if is_in_inactivity_leak(state): - previous_epoch = get_previous_epoch(state) - matching_target_indices = get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, previous_epoch) - for index in get_eligible_validator_indices(state): - if index not in matching_target_indices: - penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index] - penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR - penalties[index] += Gwei(penalty_numerator // penalty_denominator) + # Quadratic inactivity leak + if flag_index == TIMELY_TARGET_FLAG_INDEX and in_inactivity_leak and index not in unslashed_participating_indices: + penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index] + penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR + penalties[index] += Gwei(penalty_numerator // penalty_denominator) return rewards, penalties ``` @@ -617,8 +600,7 @@ def process_rewards_and_penalties(state: BeaconState) -> None: return flag_deltas = [get_flag_index_deltas(state, flag_index) for flag_index in range(PARTICIPATION_FLAGS)] - deltas = flag_deltas + [get_inactivity_penalty_deltas(state)] - for (rewards, penalties) in deltas: + for (rewards, penalties) in flag_deltas: for index in range(len(state.validators)): increase_balance(state, ValidatorIndex(index), rewards[index]) decrease_balance(state, ValidatorIndex(index), penalties[index]) From ba2c717bf112bb496afc80cedbd1aeac7d59049a Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Thu, 6 May 2021 22:55:17 +0100 Subject: [PATCH 13/22] Remove PARTICIPATION_FLAGS --- specs/altair/beacon-chain.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index f464bd640..f559a55ef 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -98,7 +98,6 @@ Altair is the first beacon chain hard fork. Its main features are: | Name | Value | | - | - | | `G2_POINT_AT_INFINITY` | `BLSSignature(b'\xc0' + b'\x00' * 95)` | -| `PARTICIPATION_FLAGS` | `3` | | `PARTICIPATION_FLAG_WEIGHTS` | `[TIMELY_HEAD_WEIGHT, TIMELY_SOURCE_WEIGHT, TIMELY_TARGET_WEIGHT]` | ## Configuration @@ -599,7 +598,7 @@ def process_rewards_and_penalties(state: BeaconState) -> None: if get_current_epoch(state) == GENESIS_EPOCH: return - flag_deltas = [get_flag_index_deltas(state, flag_index) for flag_index in range(PARTICIPATION_FLAGS)] + flag_deltas = [get_flag_index_deltas(state, flag_index) for flag_index in range(len(PARTICIPATION_FLAG_WEIGHTS))] for (rewards, penalties) in flag_deltas: for index in range(len(state.validators)): increase_balance(state, ValidatorIndex(index), rewards[index]) From 2fc68c451e8168b2c2cc85182e77e41550c15302 Mon Sep 17 00:00:00 2001 From: dankrad Date: Thu, 6 May 2021 22:55:55 +0100 Subject: [PATCH 14/22] Update specs/altair/beacon-chain.md Co-authored-by: Alex Stokes --- specs/altair/beacon-chain.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index f559a55ef..f21584f30 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -263,8 +263,7 @@ def has_flag(flags: ParticipationFlags, flag_index: int) -> bool: ```python def get_sync_committee_indices(state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]: """ - Return the sequence of sync committee indices - for a given ``state`` and ``epoch``. + Return the sequence of sync committee indices for a given ``state`` and ``epoch``. Can contain duplicate indices for small validator sets (< 2 * SYNC_COMMITTEE_SIZE) """ MAX_RANDOM_BYTE = 2**8 - 1 From cd7842557054845462e2f209751c8f61d14c0111 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 10 May 2021 10:30:47 -0600 Subject: [PATCH 15/22] lint --- specs/altair/beacon-chain.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 5c21573fb..5a4b37a83 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -362,19 +362,22 @@ def get_flag_index_deltas(state: BeaconState, flag_index: int) -> Tuple[Sequence previous_epoch = get_previous_epoch(state) unslashed_participating_indices = get_unslashed_participating_indices(state, flag_index, previous_epoch) weight = PARTICIPATION_FLAG_WEIGHTS[flag_index] - unslashed_participating_increments = get_total_balance(state, unslashed_participating_indices) // EFFECTIVE_BALANCE_INCREMENT + unslashed_participating_balance = get_total_balance(state, unslashed_participating_indices) + unslashed_participating_increments = unslashed_participating_balance // EFFECTIVE_BALANCE_INCREMENT active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT in_inactivity_leak = is_in_inactivity_leak(state) for index in get_eligible_validator_indices(state): base_reward = get_base_reward(state, index) - if index in unslashed_participating_indices: + index_participated = index in unslashed_participating_indices + if index_participated: if not in_inactivity_leak: reward_numerator = base_reward * weight * unslashed_participating_increments rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR)) elif not (in_inactivity_leak and flag_index == TIMELY_HEAD_FLAG_INDEX): penalties[index] += Gwei(base_reward * weight // WEIGHT_DENOMINATOR) + # Quadratic inactivity leak - if flag_index == TIMELY_TARGET_FLAG_INDEX and in_inactivity_leak and index not in unslashed_participating_indices: + if flag_index == TIMELY_TARGET_FLAG_INDEX and in_inactivity_leak and index_participated: penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index] penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR penalties[index] += Gwei(penalty_numerator // penalty_denominator) From 1494fe6ace0c54231d8e7d080da7eb5134a829df Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 10 May 2021 10:59:33 -0600 Subject: [PATCH 16/22] add get_inactivity_penalty_deltas back in --- specs/altair/beacon-chain.md | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 5a4b37a83..265bbfa53 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -37,6 +37,7 @@ - [`get_base_reward`](#get_base_reward) - [`get_unslashed_participating_indices`](#get_unslashed_participating_indices) - [`get_flag_index_deltas`](#get_flag_index_deltas) + - [Modified `get_inactivity_penalty_deltas`](#modified-get_inactivity_penalty_deltas) - [Beacon state mutators](#beacon-state-mutators) - [Modified `slash_validator`](#modified-slash_validator) - [Block processing](#block-processing) @@ -368,19 +369,32 @@ def get_flag_index_deltas(state: BeaconState, flag_index: int) -> Tuple[Sequence in_inactivity_leak = is_in_inactivity_leak(state) for index in get_eligible_validator_indices(state): base_reward = get_base_reward(state, index) - index_participated = index in unslashed_participating_indices - if index_participated: + if index in unslashed_participating_indices: if not in_inactivity_leak: reward_numerator = base_reward * weight * unslashed_participating_increments rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR)) - elif not (in_inactivity_leak and flag_index == TIMELY_HEAD_FLAG_INDEX): + else: penalties[index] += Gwei(base_reward * weight // WEIGHT_DENOMINATOR) + return rewards, penalties +``` - # Quadratic inactivity leak - if flag_index == TIMELY_TARGET_FLAG_INDEX and in_inactivity_leak and index_participated: - penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index] - penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR - penalties[index] += Gwei(penalty_numerator // penalty_denominator) +#### Modified `get_inactivity_penalty_deltas` + +```python +def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: + """ + Return the inactivity penalty deltas by considering timely target participation flags and inactivity scores. + """ + rewards = [Gwei(0) for _ in range(len(state.validators))] + penalties = [Gwei(0) for _ in range(len(state.validators))] + if is_in_inactivity_leak(state): + previous_epoch = get_previous_epoch(state) + matching_target_indices = get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, previous_epoch) + for index in get_eligible_validator_indices(state): + if index not in matching_target_indices: + penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index] + penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR + penalties[index] += Gwei(penalty_numerator // penalty_denominator) return rewards, penalties ``` @@ -613,7 +627,8 @@ def process_rewards_and_penalties(state: BeaconState) -> None: return flag_deltas = [get_flag_index_deltas(state, flag_index) for flag_index in range(len(PARTICIPATION_FLAG_WEIGHTS))] - for (rewards, penalties) in flag_deltas: + deltas = flag_deltas + [get_inactivity_penalty_deltas(state)] + for (rewards, penalties) in deltas: for index in range(len(state.validators)): increase_balance(state, ValidatorIndex(index), rewards[index]) decrease_balance(state, ValidatorIndex(index), penalties[index]) From 77d607a760865d9dd621da5b1f3e39dfe0e7161e Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 10 May 2021 13:24:14 -0600 Subject: [PATCH 17/22] fix test --- tests/core/pyspec/eth2spec/test/helpers/rewards.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/rewards.py b/tests/core/pyspec/eth2spec/test/helpers/rewards.py index f81c1fc2a..7cf0206b3 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/rewards.py +++ b/tests/core/pyspec/eth2spec/test/helpers/rewards.py @@ -62,13 +62,13 @@ def run_deltas(spec, state): if is_post_altair(spec): def get_source_deltas(state): - return spec.get_flag_index_deltas(state, spec.TIMELY_SOURCE_FLAG_INDEX, spec.TIMELY_SOURCE_WEIGHT) + return spec.get_flag_index_deltas(state, spec.TIMELY_SOURCE_FLAG_INDEX) def get_head_deltas(state): - return spec.get_flag_index_deltas(state, spec.TIMELY_HEAD_FLAG_INDEX, spec.TIMELY_HEAD_WEIGHT) + return spec.get_flag_index_deltas(state, spec.TIMELY_HEAD_FLAG_INDEX) def get_target_deltas(state): - return spec.get_flag_index_deltas(state, spec.TIMELY_TARGET_FLAG_INDEX, spec.TIMELY_TARGET_WEIGHT) + return spec.get_flag_index_deltas(state, spec.TIMELY_TARGET_FLAG_INDEX) yield from run_attestation_component_deltas( spec, @@ -228,7 +228,7 @@ def run_get_inactivity_penalty_deltas(spec, state): else: base_penalty = sum( base_reward * numerator // spec.WEIGHT_DENOMINATOR - for (_, numerator) in spec.get_flag_indices_and_weights() + for (_, numerator) in spec.PARTICIPATION_FLAG_WEIGHTS ) if not has_enough_for_reward(spec, state, index): From a6b8574962dc03f6f36b56a744f3542e97ffeed8 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 10 May 2021 13:38:45 -0600 Subject: [PATCH 18/22] test --- specs/altair/beacon-chain.md | 3 +- .../pyspec/eth2spec/test/helpers/rewards.py | 28 ++++++++++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 265bbfa53..90b878cc1 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -366,11 +366,10 @@ def get_flag_index_deltas(state: BeaconState, flag_index: int) -> Tuple[Sequence unslashed_participating_balance = get_total_balance(state, unslashed_participating_indices) unslashed_participating_increments = unslashed_participating_balance // EFFECTIVE_BALANCE_INCREMENT active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT - in_inactivity_leak = is_in_inactivity_leak(state) for index in get_eligible_validator_indices(state): base_reward = get_base_reward(state, index) if index in unslashed_participating_indices: - if not in_inactivity_leak: + if not is_in_inactivity_leak(state): reward_numerator = base_reward * weight * unslashed_participating_increments rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR)) else: diff --git a/tests/core/pyspec/eth2spec/test/helpers/rewards.py b/tests/core/pyspec/eth2spec/test/helpers/rewards.py index 7cf0206b3..eb47dac98 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/rewards.py +++ b/tests/core/pyspec/eth2spec/test/helpers/rewards.py @@ -133,10 +133,17 @@ def run_attestation_component_deltas(spec, state, component_delta_fn, matching_a validator = state.validators[index] enough_for_reward = has_enough_for_reward(spec, state, index) if index in matching_indices and not validator.slashed: - if enough_for_reward: - assert rewards[index] > 0 + if is_post_altair(spec): + if not spec.is_in_inactivity_leak(state) and enough_for_reward: + assert rewards[index] > 0 + else: + assert rewards[index] == 0 else: - assert rewards[index] == 0 + if enough_for_reward: + assert rewards[index] > 0 + else: + assert rewards[index] == 0 + assert penalties[index] == 0 else: assert rewards[index] == 0 @@ -225,18 +232,19 @@ def run_get_inactivity_penalty_deltas(spec, state): if not is_post_altair(spec): cancel_base_rewards_per_epoch = spec.BASE_REWARDS_PER_EPOCH base_penalty = cancel_base_rewards_per_epoch * base_reward - spec.get_proposer_reward(state, index) - else: - base_penalty = sum( - base_reward * numerator // spec.WEIGHT_DENOMINATOR - for (_, numerator) in spec.PARTICIPATION_FLAG_WEIGHTS - ) if not has_enough_for_reward(spec, state, index): assert penalties[index] == 0 elif index in matching_attesting_indices or not has_enough_for_leak_penalty(spec, state, index): - assert penalties[index] == base_penalty + if is_post_altair(spec): + assert penalties[index] == 0 + else: + assert penalties[index] == base_penalty else: - assert penalties[index] > base_penalty + if is_post_altair(spec): + assert penalties[index] > 0 + else: + assert penalties[index] > base_penalty else: assert penalties[index] == 0 From a9cc036184dedde472e22a3ae9ba45db7a094695 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 11 May 2021 11:48:26 -0600 Subject: [PATCH 19/22] remove timely_head penalty --- specs/altair/beacon-chain.md | 2 +- tests/core/pyspec/eth2spec/test/helpers/rewards.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 90b878cc1..57fec33df 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -372,7 +372,7 @@ def get_flag_index_deltas(state: BeaconState, flag_index: int) -> Tuple[Sequence if not is_in_inactivity_leak(state): reward_numerator = base_reward * weight * unslashed_participating_increments rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR)) - else: + elif not flag_index == TIMELY_HEAD_FLAG_INDEX: penalties[index] += Gwei(base_reward * weight // WEIGHT_DENOMINATOR) return rewards, penalties ``` diff --git a/tests/core/pyspec/eth2spec/test/helpers/rewards.py b/tests/core/pyspec/eth2spec/test/helpers/rewards.py index eb47dac98..f563c70a2 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/rewards.py +++ b/tests/core/pyspec/eth2spec/test/helpers/rewards.py @@ -147,7 +147,9 @@ def run_attestation_component_deltas(spec, state, component_delta_fn, matching_a assert penalties[index] == 0 else: assert rewards[index] == 0 - if enough_for_reward: + if is_post_altair(spec) and 'head' in deltas_name: + assert penalties[index] == 0 + elif enough_for_reward: assert penalties[index] > 0 else: assert penalties[index] == 0 From 09cefa03f3895c2a319ee03bc06c66a6b8d5e11b Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 11 May 2021 16:07:24 -0600 Subject: [PATCH 20/22] remov sync signature todo --- specs/altair/beacon-chain.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 57fec33df..6d97c14b5 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -203,7 +203,6 @@ class BeaconState(Container): ```python class SyncAggregate(Container): sync_committee_bits: Bitvector[SYNC_COMMITTEE_SIZE] - # TODO! Need multiple signatures as discussed between Justin and Vitalik May 3 2021 (see Telegram) sync_committee_signature: BLSSignature ``` From 6371707779791a687886f6f7c3b2d1ebd45ae345 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 12 May 2021 08:40:34 -0600 Subject: [PATCH 21/22] Apply suggestions from code review Co-authored-by: Hsiao-Wei Wang --- specs/altair/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 6d97c14b5..9593d13a9 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -371,7 +371,7 @@ def get_flag_index_deltas(state: BeaconState, flag_index: int) -> Tuple[Sequence if not is_in_inactivity_leak(state): reward_numerator = base_reward * weight * unslashed_participating_increments rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR)) - elif not flag_index == TIMELY_HEAD_FLAG_INDEX: + elif flag_index != TIMELY_HEAD_FLAG_INDEX: penalties[index] += Gwei(base_reward * weight // WEIGHT_DENOMINATOR) return rewards, penalties ``` From 5188671816b3a846b1fa5ea781d3321f0e2c5f04 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 12 May 2021 12:18:35 -0600 Subject: [PATCH 22/22] Update specs/altair/beacon-chain.md Co-authored-by: dankrad --- specs/altair/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 9593d13a9..492a146ea 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -262,7 +262,7 @@ def has_flag(flags: ParticipationFlags, flag_index: int) -> bool: def get_sync_committee_indices(state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]: """ Return the sequence of sync committee indices for a given ``state`` and ``epoch``. - Note: Committee can contain duplicate indices for small validator sets (< 2 * SYNC_COMMITTEE_SIZE) + Note: Committee can contain duplicate indices for small validator sets (< SYNC_COMMITTEE_SIZE + 128) Note: This function is not stable during a sync committee period as a validator's effective balance may change enough to affect the sampling.