diff --git a/setup.py b/setup.py index 91baf978f..6d9147661 100644 --- a/setup.py +++ b/setup.py @@ -277,6 +277,12 @@ ALTAIR_HARDCODED_SSZ_DEP_CONSTANTS = { } +ALTAIR_INVAIANT_CHECKS = ''' +assert ( + TIMELY_HEAD_WEIGHT + TIMELY_SOURCE_WEIGHT + TIMELY_TARGET_WEIGHT + SYNC_REWARD_WEIGHT + PROPOSER_WEIGHT +) == WEIGHT_DENOMINATOR''' + + def is_phase0(fork): return fork == PHASE0 @@ -331,6 +337,7 @@ def objects_to_spec(spec_object: SpecObject, imports: str, fork: str, ordered_cl if is_altair(fork): altair_ssz_dep_constants_verification = '\n'.join(map(lambda x: 'assert %s == %s' % (x, spec_object.ssz_dep_constants[x]), ALTAIR_HARDCODED_SSZ_DEP_CONSTANTS)) spec += '\n\n\n' + altair_ssz_dep_constants_verification + spec += '\n' + ALTAIR_INVAIANT_CHECKS spec += '\n' return spec diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 5b51be17a..72d511b04 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -88,9 +88,10 @@ Altair is the first beacon chain hard fork. Its main features are: | `TIMELY_SOURCE_WEIGHT` | `uint64(12)` | | `TIMELY_TARGET_WEIGHT` | `uint64(24)` | | `SYNC_REWARD_WEIGHT` | `uint64(8)` | +| `PROPOSER_WEIGHT` | `uint64(8)` | | `WEIGHT_DENOMINATOR` | `uint64(64)` | -*Note*: The sum of the weight fractions (7/8) plus the proposer inclusion fraction (1/8) equals 1. +*Note*: The sum of the weights equal `WEIGHT_DENOMINATOR`. ### Misc @@ -412,7 +413,7 @@ def slash_validator(state: BeaconState, if whistleblower_index is None: whistleblower_index = proposer_index whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT) - proposer_reward = Gwei(whistleblower_reward // PROPOSER_REWARD_QUOTIENT) + proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR) increase_balance(state, proposer_index, proposer_reward) increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward)) ``` @@ -477,7 +478,8 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: proposer_reward_numerator += get_base_reward(state, index) * weight # Reward proposer - proposer_reward = Gwei(proposer_reward_numerator // (WEIGHT_DENOMINATOR * PROPOSER_REWARD_QUOTIENT)) + proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT + proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator) increase_balance(state, get_beacon_proposer_index(state), proposer_reward) ``` @@ -529,29 +531,26 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: ```python def process_sync_committee(state: BeaconState, aggregate: SyncAggregate) -> None: # Verify sync committee aggregate signature signing over the previous slot block root - previous_slot = Slot(max(int(state.slot), 1) - 1) - committee_indices = get_sync_committee_indices(state, get_current_epoch(state)) - included_indices = [index for index, bit in zip(committee_indices, aggregate.sync_committee_bits) if bit] committee_pubkeys = state.current_sync_committee.pubkeys - included_pubkeys = [pubkey for pubkey, bit in zip(committee_pubkeys, aggregate.sync_committee_bits) if bit] + participant_pubkeys = [pubkey for pubkey, bit in zip(committee_pubkeys, aggregate.sync_committee_bits) if bit] + previous_slot = max(state.slot, Slot(1)) - Slot(1) domain = get_domain(state, DOMAIN_SYNC_COMMITTEE, compute_epoch_at_slot(previous_slot)) signing_root = compute_signing_root(get_block_root_at_slot(state, previous_slot), domain) - assert eth2_fast_aggregate_verify(included_pubkeys, signing_root, aggregate.sync_committee_signature) + assert eth2_fast_aggregate_verify(participant_pubkeys, signing_root, aggregate.sync_committee_signature) - # Compute the maximum sync rewards for the slot + # Compute participant and proposer rewards total_active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT total_base_rewards = Gwei(get_base_reward_per_increment(state) * total_active_increments) - max_epoch_rewards = Gwei(total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR) - max_slot_rewards = Gwei(max_epoch_rewards * len(included_indices) // len(committee_indices) // SLOTS_PER_EPOCH) + max_participant_rewards = Gwei(total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR // SLOTS_PER_EPOCH) + participant_reward = Gwei(max_participant_rewards // SYNC_COMMITTEE_SIZE) + proposer_reward = Gwei(participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT)) - # Compute the participant and proposer sync rewards - committee_effective_balance = get_total_balance(state, set(included_indices)) - for included_index in included_indices: - effective_balance = state.validators[included_index].effective_balance - inclusion_reward = Gwei(max_slot_rewards * effective_balance // committee_effective_balance) - proposer_reward = Gwei(inclusion_reward // PROPOSER_REWARD_QUOTIENT) + # Apply participant and proposer rewards + committee_indices = get_sync_committee_indices(state, get_current_epoch(state)) + participant_indices = [index for index, bit in zip(committee_indices, aggregate.sync_committee_bits) if bit] + for participant_index in participant_indices: + increase_balance(state, participant_index, participant_reward) increase_balance(state, get_beacon_proposer_index(state), proposer_reward) - increase_balance(state, included_index, inclusion_reward - proposer_reward) ``` ### Epoch processing diff --git a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_committee.py b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_committee.py index 7c9ba1358..ed9bcac9c 100644 --- a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_committee.py +++ b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_committee.py @@ -126,8 +126,7 @@ def compute_sync_committee_participant_reward(spec, state, participant_index, co inclusion_reward = compute_sync_committee_inclusion_reward( spec, state, participant_index, committee, committee_bits, ) - proposer_reward = spec.Gwei(inclusion_reward // spec.PROPOSER_REWARD_QUOTIENT) - return spec.Gwei((inclusion_reward - proposer_reward) * multiplicities[participant_index]) + return spec.Gwei(inclusion_reward * multiplicities[participant_index]) def compute_sync_committee_proposer_reward(spec, state, committee, committee_bits): @@ -138,7 +137,12 @@ def compute_sync_committee_proposer_reward(spec, state, committee, committee_bit inclusion_reward = compute_sync_committee_inclusion_reward( spec, state, index, committee, committee_bits, ) - proposer_reward += spec.Gwei(inclusion_reward // spec.PROPOSER_REWARD_QUOTIENT) + proposer_reward_denominator = ( + (spec.WEIGHT_DENOMINATOR - spec.PROPOSER_WEIGHT) + * spec.WEIGHT_DENOMINATOR + // spec.PROPOSER_WEIGHT + ) + proposer_reward += spec.Gwei((inclusion_reward * spec.WEIGHT_DENOMINATOR) // proposer_reward_denominator) return proposer_reward