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/specs/altair/p2p-interface.md b/specs/altair/p2p-interface.md index 6d400c0a2..f76c3bce9 100644 --- a/specs/altair/p2p-interface.md +++ b/specs/altair/p2p-interface.md @@ -92,7 +92,7 @@ The following validations MUST pass before forwarding the `signed_contribution_a - _[REJECT]_ `contribution_and_proof.selection_proof` selects the validator as an aggregator for the slot -- i.e. `is_sync_committee_aggregator(state, contribution.slot, contribution_and_proof.selection_proof)` returns `True`. - _[REJECT]_ The aggregator's validator index is within the current sync committee -- i.e. `state.validators[contribution_and_proof.aggregator_index].pubkey in state.current_sync_committee.pubkeys`. -- _[REJECT]_ The `contribution_and_proof.selection_proof` is a valid signature of the `contribution.slot` by the validator with index `contribution_and_proof.aggregator_index`. +- _[REJECT]_ The `contribution_and_proof.selection_proof` is a valid signature of the `SyncCommitteeSigningData` derived from the `contribution` by the validator with index `contribution_and_proof.aggregator_index`. - _[REJECT]_ The aggregator signature, `signed_contribution_and_proof.signature`, is valid. - _[REJECT]_ The aggregate signature is valid for the message `beacon_block_root` and aggregate pubkey derived from the participation info in `aggregation_bits` for the subcommittee specified by the `subcommittee_index`. @@ -183,6 +183,7 @@ Request and Response remain unchanged. A `ForkDigest`-context is used to select Per `context = compute_fork_digest(fork_version, genesis_validators_root)`: | `fork_version` | Chunk SSZ type | +| ------------------------ | -------------------------- | | `GENESIS_FORK_VERSION` | `phase0.SignedBeaconBlock` | | `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` | @@ -195,6 +196,7 @@ Request and Response remain unchanged. A `ForkDigest`-context is used to select Per `context = compute_fork_digest(fork_version, genesis_validators_root)`: | `fork_version` | Chunk SSZ type | +| ------------------------ | -------------------------- | | `GENESIS_FORK_VERSION` | `phase0.SignedBeaconBlock` | | `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` | diff --git a/specs/altair/validator.md b/specs/altair/validator.md index c7b41dd8b..1649eabc8 100644 --- a/specs/altair/validator.md +++ b/specs/altair/validator.md @@ -220,8 +220,7 @@ Given a collection of the best seen `contributions` (with no repeating `subcommi the proposer processes them as follows: ```python -def process_sync_committee_contributions(state: BeaconState, - block: BeaconBlock, +def process_sync_committee_contributions(block: BeaconBlock, contributions: Set[SyncCommitteeContribution]) -> None: sync_aggregate = SyncAggregate() signatures = [] @@ -310,12 +309,12 @@ Each slot some sync committee members in each subcommittee are selected to aggre ##### Aggregation selection -A validator is selected to aggregate based on the computation in `is_sync_committee_aggregator` where `state` is a `BeaconState` as supplied to `get_sync_committee_slot_signature` and `signature` is the BLS signature returned by `get_sync_committee_slot_signature`. +A validator is selected to aggregate based on the computation in `is_sync_committee_aggregator` where `signature` is the BLS signature returned by `get_sync_committee_selection_proof`. The signature function takes a `BeaconState` with the relevant sync committees for the queried `slot` (i.e. `state.slot` is within the span covered by the current or next sync committee period), the `subcommittee_index` equal to the `subnet_id`, and the `privkey` is the BLS private key associated with the validator. ```python -def get_sync_committee_slot_signature(state: BeaconState, slot: Slot, - subcommittee_index: uint64, privkey: int) -> BLSSignature: +def get_sync_committee_selection_proof(state: BeaconState, slot: Slot, + subcommittee_index: uint64, privkey: int) -> BLSSignature: domain = get_domain(state, DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF, compute_epoch_at_slot(slot)) signing_data = SyncCommitteeSigningData( slot=slot, @@ -326,7 +325,7 @@ def get_sync_committee_slot_signature(state: BeaconState, slot: Slot, ``` ```python -def is_sync_committee_aggregator(state: BeaconState, signature: BLSSignature) -> bool: +def is_sync_committee_aggregator(signature: BLSSignature) -> bool: modulo = max(1, SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT // TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE) return bytes_to_uint64(hash(signature)[0:8]) % modulo == 0 ``` @@ -378,7 +377,7 @@ def get_contribution_and_proof(state: BeaconState, aggregator_index: ValidatorIndex, contribution: SyncCommitteeContribution, privkey: int) -> ContributionAndProof: - selection_proof = get_sync_committee_slot_signature( + selection_proof = get_sync_committee_selection_proof( state, contribution.slot, contribution.subcommittee_index, @@ -391,7 +390,7 @@ def get_contribution_and_proof(state: BeaconState, ) ``` -Then `signed_contribution_and_proof = SignedContributionAndProof(message=contribution_and_proof, signature=signature)` is constructed and broadast. Where `signature` is obtained from: +Then `signed_contribution_and_proof = SignedContributionAndProof(message=contribution_and_proof, signature=signature)` is constructed and broadcast. Where `signature` is obtained from: ```python def get_contribution_and_proof_signature(state: BeaconState, diff --git a/specs/das/das-core.md b/specs/das/das-core.md index ee73a6150..ce39565a1 100644 --- a/specs/das/das-core.md +++ b/specs/das/das-core.md @@ -12,7 +12,7 @@ - [Configuration](#configuration) - [Misc](#misc) - [New containers](#new-containers) - - [DASSample](#dassample) + - [`DASSample`](#dassample) - [Helper functions](#helper-functions) - [Reverse bit ordering](#reverse-bit-ordering) - [`reverse_bit_order`](#reverse_bit_order) @@ -45,7 +45,7 @@ We define the following Python custom types for type hinting and readability: ## New containers -### DASSample +### `DASSample` ```python class DASSample(Container): diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index e09d7ce88..a548003e1 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -589,7 +589,7 @@ def get_aggregate_and_proof(state: BeaconState, ) ``` -Then `signed_aggregate_and_proof = SignedAggregateAndProof(message=aggregate_and_proof, signature=signature)` is constructed and broadast. Where `signature` is obtained from: +Then `signed_aggregate_and_proof = SignedAggregateAndProof(message=aggregate_and_proof, signature=signature)` is constructed and broadcast. Where `signature` is obtained from: ```python def get_aggregate_and_proof_signature(state: BeaconState, diff --git a/specs/sharding/beacon-chain.md b/specs/sharding/beacon-chain.md index cc3b94e00..0c0764961 100644 --- a/specs/sharding/beacon-chain.md +++ b/specs/sharding/beacon-chain.md @@ -75,7 +75,7 @@ We define the following Python custom types for type hinting and readability: | Name | SSZ equivalent | Description | | - | - | - | | `Shard` | `uint64` | A shard number | -| `BLSCommitment` | `bytes48` | A G1 curve point | +| `BLSCommitment` | `Bytes48` | A G1 curve point | | `BLSPoint` | `uint256` | A number `x` in the range `0 <= x < MODULUS` | ## Constants @@ -114,7 +114,7 @@ The following values are (non-configurable) constants used throughout the specif | - | - | | `G1_SETUP` | Type `List[G1]`. The G1-side trusted setup `[G, G*s, G*s**2....]`; note that the first point is the generator. | | `G2_SETUP` | Type `List[G2]`. The G2-side trusted setup `[G, G*s, G*s**2....]` | -| `ROOT_OF_UNITY` | `pow(PRIMITIVE_ROOT_OF_UNITY, (MODULUS - 1) // (MAX_SAMPLES_PER_BLOCK * POINTS_PER_SAMPLE, MODULUS)` | +| `ROOT_OF_UNITY` | `pow(PRIMITIVE_ROOT_OF_UNITY, (MODULUS - 1) // (MAX_SAMPLES_PER_BLOCK * POINTS_PER_SAMPLE), MODULUS)` | ### Gwei values @@ -241,7 +241,7 @@ class PendingShardHeader(Container): # Who voted for the header votes: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] # Has this header been confirmed? - confirmed: bool + confirmed: boolean ``` ### `ShardBlobReference` @@ -572,7 +572,7 @@ def process_shard_header(state: BeaconState, assert body_summary.degree_proof == G1_SETUP[0] assert ( bls.Pairing(body_summary.degree_proof, G2_SETUP[0]) - == bls.Pairing(body_summary.commitment.point, G2_SETUP[-body_summary.commitment.length])) + == bls.Pairing(body_summary.commitment.point, G2_SETUP[-body_summary.commitment.length]) ) # Get the correct pending header list @@ -704,7 +704,7 @@ def process_pending_headers(state: BeaconState) -> None: for slot_index in range(SLOTS_PER_EPOCH): for shard in range(SHARD_COUNT): state.grandparent_epoch_confirmed_commitments[shard][slot_index] = DataCommitment() - confirmed_headers = [candidate in state.previous_epoch_pending_shard_headers if candidate.confirmed] + confirmed_headers = [candidate for candidate in state.previous_epoch_pending_shard_headers if candidate.confirmed] for header in confirmed_headers: state.grandparent_epoch_confirmed_commitments[c.shard][c.slot % SLOTS_PER_EPOCH] = c.commitment ``` @@ -752,7 +752,7 @@ def reset_pending_headers(state: BeaconState) -> None: next_epoch = get_current_epoch(state) + 1 next_epoch_start_slot = compute_start_slot_at_epoch(next_epoch) for slot in range(next_epoch_start_slot, next_epoch_start_slot + SLOTS_IN_EPOCH): - for index in range(get_committee_count_per_slot(next_epoch): + for index in range(get_committee_count_per_slot(next_epoch)): shard = compute_shard_from_committee_index(state, slot, index) committee_length = len(get_beacon_committee(state, slot, shard)) state.current_epoch_pending_shard_headers.append(PendingShardHeader( diff --git a/tests/core/pyspec/eth2spec/VERSION.txt b/tests/core/pyspec/eth2spec/VERSION.txt index 5655aea02..57b7d37da 100644 --- a/tests/core/pyspec/eth2spec/VERSION.txt +++ b/tests/core/pyspec/eth2spec/VERSION.txt @@ -1 +1 @@ -1.1.0-alpha.1 \ No newline at end of file +1.1.0-alpha.3 \ No newline at end of file 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