Merge pull request #2176 from ethereum/accounting-reform

Accounting reform [isolated]
This commit is contained in:
Danny Ryan 2021-02-04 11:16:40 -06:00 committed by GitHub
commit 6f2c69e79f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 848 additions and 160 deletions

View File

@ -1,4 +1,4 @@
# Ethereum 2.0 Light Client Support
# Ethereum 2.0 HF1
## Table of contents
@ -7,10 +7,13 @@
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Introduction](#introduction)
- [Custom types](#custom-types)
- [Constants](#constants)
- [Configuration](#configuration)
- [Constants](#constants-1)
- [Validator action flags](#validator-action-flags)
- [Participation rewards](#participation-rewards)
- [Misc](#misc)
- [Configuration](#configuration)
- [Misc](#misc-1)
- [Time parameters](#time-parameters)
- [Domain types](#domain-types)
- [Containers](#containers)
@ -22,36 +25,78 @@
- [Helper functions](#helper-functions)
- [`Predicates`](#predicates)
- [`eth2_fast_aggregate_verify`](#eth2_fast_aggregate_verify)
- [Misc](#misc-2)
- [`flags_and_numerators`](#flags_and_numerators)
- [Beacon state accessors](#beacon-state-accessors)
- [`get_sync_committee_indices`](#get_sync_committee_indices)
- [`get_sync_committee`](#get_sync_committee)
- [`get_base_reward`](#get_base_reward)
- [`get_unslashed_participating_indices`](#get_unslashed_participating_indices)
- [`get_flag_deltas`](#get_flag_deltas)
- [New `get_inactivity_penalty_deltas`](#new-get_inactivity_penalty_deltas)
- [Block processing](#block-processing)
- [New `process_attestation`](#new-process_attestation)
- [New `process_deposit`](#new-process_deposit)
- [Sync committee processing](#sync-committee-processing)
- [Epoch processing](#epoch-processing)
- [Components of attestation deltas](#components-of-attestation-deltas)
- [New `process_justification_and_finalization`](#new-process_justification_and_finalization)
- [New `process_rewards_and_penalties`](#new-process_rewards_and_penalties)
- [Sync committee updates](#sync-committee-updates)
- [Participation flags updates](#participation-flags-updates)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
This is a standalone beacon chain patch adding light client support via sync committees.
This is a patch implementing the first hard fork to the beacon chain, tentatively named HF1 pending a permanent name. It has three main features:
* Light client support via sync committees
* Incentive accounting reforms, reducing spec complexity
and [TODO] reducing the cost of processing chains that have very little or zero participation for a long span of epochs
* Fork choice rule changes to address weaknesses recently discovered in the existing fork choice
## Custom types
| Name | SSZ equivalent | Description |
| - | - | - |
| `ValidatorFlag` | `uint8` | Bitflags to track validator actions with |
## Constants
### Validator action flags
This is formatted as an enum, with values `2**i` that can be combined as bit-flags.
The `0` value is reserved as default. Remaining bits in `ValidatorFlag` may be used in future hardforks.
**Note**: Unlike Phase0, a `TIMELY_TARGET_FLAG` does not necessarily imply a `TIMELY_SOURCE_FLAG`
due to the varying slot delay requirements of each.
| Name | Value |
| - | - |
| `BASE_REWARDS_PER_EPOCH` | `uint64(5)` |
| - | - |
| `TIMELY_HEAD_FLAG` | `ValidatorFlag(2**0)` (= 1) |
| `TIMELY_SOURCE_FLAG` | `ValidatorFlag(2**1)` (= 2) |
| `TIMELY_TARGET_FLAG` | `ValidatorFlag(2**2)` (= 4) |
## Configuration
### Participation rewards
### Constants
| Name | Value |
| - | - |
| `TIMELY_HEAD_NUMERATOR` | `12` |
| `TIMELY_SOURCE_NUMERATOR` | `12` |
| `TIMELY_TARGET_NUMERATOR` | `32` |
| `REWARD_DENOMINATOR` | `64` |
The reward fractions add up to 7/8, leaving the remaining 1/8 for proposer rewards and other future micro-rewards.
### Misc
| Name | Value |
| - | - |
| `G2_POINT_AT_INFINITY` | `BLSSignature(b'\xc0' + b'\x00' * 95)` |
## Configuration
### Misc
| Name | Value |
@ -90,8 +135,37 @@ class BeaconBlockBody(phase0.BeaconBlockBody):
#### `BeaconState`
```python
class BeaconState(phase0.BeaconState):
# Sync committees
class BeaconState(Container):
# Versioning
genesis_time: uint64
genesis_validators_root: Root
slot: Slot
fork: Fork
# History
latest_block_header: BeaconBlockHeader
block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT]
state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT]
historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT]
# Eth1
eth1_data: Eth1Data
eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH]
eth1_deposit_index: uint64
# Registry
validators: List[Validator, VALIDATOR_REGISTRY_LIMIT]
balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT]
# Randomness
randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR]
# Slashings
slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances
# Participation
previous_epoch_participation: List[ValidatorFlag, VALIDATOR_REGISTRY_LIMIT]
current_epoch_participation: List[ValidatorFlag, VALIDATOR_REGISTRY_LIMIT]
# Finality
justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch
previous_justified_checkpoint: Checkpoint
current_justified_checkpoint: Checkpoint
finalized_checkpoint: Checkpoint
# Light client sync committees
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
```
@ -122,6 +196,29 @@ def eth2_fast_aggregate_verify(pubkeys: Sequence[BLSPubkey], message: Bytes32, s
return bls.FastAggregateVerify(pubkeys, message, signature)
```
### Misc
#### `flags_and_numerators`
```python
def get_flags_and_numerators() -> Sequence[Tuple[ValidatorFlag, int]]:
return (
(TIMELY_HEAD_FLAG, TIMELY_HEAD_NUMERATOR),
(TIMELY_SOURCE_FLAG, TIMELY_SOURCE_NUMERATOR),
(TIMELY_TARGET_FLAG, TIMELY_TARGET_NUMERATOR)
)
```
```python
def add_validator_flags(flags: ValidatorFlag, add: ValidatorFlag) -> ValidatorFlag:
return flags | add
```
```python
def has_validator_flags(flags: ValidatorFlag, has: ValidatorFlag) -> bool:
return flags & has == has
```
### Beacon state accessors
#### `get_sync_committee_indices`
@ -166,6 +263,97 @@ def get_sync_committee(state: BeaconState, epoch: Epoch) -> SyncCommittee:
return SyncCommittee(pubkeys=pubkeys, pubkey_aggregates=aggregates)
```
#### `get_base_reward`
*Note*: The function `get_base_reward` is modified with the removal of `BASE_REWARDS_PER_EPOCH`.
```python
def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
total_balance = get_total_active_balance(state)
effective_balance = state.validators[index].effective_balance
return Gwei(effective_balance * BASE_REWARD_FACTOR // integer_squareroot(total_balance))
```
#### `get_unslashed_participating_indices`
```python
def get_unslashed_participating_indices(state: BeaconState, flags: ValidatorFlag, epoch: Epoch) -> Set[ValidatorIndex]:
"""
Retrieve the active validator indices of the given epoch, which are not slashed, and have all of the given flags.
"""
assert epoch in (get_previous_epoch(state), get_current_epoch(state))
if epoch == get_current_epoch(state):
epoch_participation = state.current_epoch_participation
else:
epoch_participation = state.previous_epoch_participation
participating_indices = [
index for index in get_active_validator_indices(state, epoch)
if has_validator_flags(epoch_participation[index], flags)
]
return set(filter(lambda index: not state.validators[index].slashed, participating_indices))
```
#### `get_flag_deltas`
```python
def get_flag_deltas(state: BeaconState,
flag: ValidatorFlag,
numerator: uint64) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
"""
Compute the rewards and penalties associated with a particular duty, by scanning through the participation
flags to determine who participated and who did not and assigning them the appropriate rewards and penalties.
"""
rewards = [Gwei(0)] * len(state.validators)
penalties = [Gwei(0)] * len(state.validators)
unslashed_participating_indices = get_unslashed_participating_indices(state, flag, 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
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):
# Optimal participatition is fully rewarded to cancel the inactivity penalty
rewards[index] = base_reward * numerator // REWARD_DENOMINATOR
else:
rewards[index] = (
(base_reward * numerator * unslashed_participating_increments)
// (active_increments * REWARD_DENOMINATOR)
)
else:
penalties[index] = base_reward * numerator // REWARD_DENOMINATOR
return rewards, penalties
```
#### New `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]]:
"""
Compute the penalties associated with the inactivity leak, by scanning through the participation
flags to determine who participated and who did not, applying the leak penalty globally and applying
compensatory rewards to participants.
"""
penalties = [Gwei(0) for _ in range(len(state.validators))]
if is_in_inactivity_leak(state):
reward_numerator_sum = sum(numerator for (_, numerator) in get_flags_and_numerators())
matching_target_attesting_indices = get_unslashed_participating_indices(
state, TIMELY_TARGET_FLAG, get_previous_epoch(state)
)
for index in get_eligible_validator_indices(state):
# If validator is performing optimally this cancels all attestation rewards for a neutral balance
penalties[index] += Gwei(get_base_reward(state, index) * reward_numerator_sum // REWARD_DENOMINATOR)
if index not in matching_target_attesting_indices:
effective_balance = state.validators[index].effective_balance
penalties[index] += Gwei(effective_balance * get_finality_delay(state) // INACTIVITY_PENALTY_QUOTIENT)
rewards = [Gwei(0) for _ in range(len(state.validators))]
return rewards, penalties
```
### Block processing
```python
@ -178,6 +366,105 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_sync_committee(state, block.body)
```
#### New `process_attestation`
*Note*: The function `process_attestation` is modified to do incentive accounting with epoch participation flags.
```python
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
data = attestation.data
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)
if data.target.epoch == get_current_epoch(state):
epoch_participation = state.current_epoch_participation
justified_checkpoint = state.current_justified_checkpoint
else:
epoch_participation = state.previous_epoch_participation
justified_checkpoint = state.previous_justified_checkpoint
# Matching roots
is_matching_head = data.beacon_block_root == get_block_root_at_slot(state, data.slot)
is_matching_source = data.source == justified_checkpoint
is_matching_target = data.target.root == get_block_root(state, data.target.epoch)
assert is_matching_source
# Verify signature
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
# Participation flags
participation_flags = []
if is_matching_head and is_matching_target and state.slot <= data.slot + MIN_ATTESTATION_INCLUSION_DELAY:
participation_flags.append(TIMELY_HEAD_FLAG)
if is_matching_source and state.slot <= data.slot + integer_squareroot(SLOTS_PER_EPOCH):
participation_flags.append(TIMELY_SOURCE_FLAG)
if is_matching_target and state.slot <= data.slot + SLOTS_PER_EPOCH:
participation_flags.append(TIMELY_TARGET_FLAG)
# Update epoch participation flags
proposer_reward_numerator = 0
for index in get_attesting_indices(state, data, attestation.aggregation_bits):
for flag, numerator in get_flags_and_numerators():
if flag in participation_flags and not has_validator_flags(epoch_participation[index], flag):
epoch_participation[index] = add_validator_flags(epoch_participation[index], flag)
proposer_reward_numerator += get_base_reward(state, index) * numerator
# Reward proposer
proposer_reward = Gwei(proposer_reward_numerator // (REWARD_DENOMINATOR * PROPOSER_REWARD_QUOTIENT))
increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
```
#### New `process_deposit`
*Note*: The function `process_deposit` is modified to initialize `previous_epoch_participation` and `current_epoch_participation`.
```python
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
# Verify the Merkle branch
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
index=state.eth1_deposit_index,
root=state.eth1_data.deposit_root,
)
# Deposits must be processed in order
state.eth1_deposit_index += 1
pubkey = deposit.data.pubkey
amount = deposit.data.amount
validator_pubkeys = [v.pubkey for v in state.validators]
if pubkey not in validator_pubkeys:
# Verify the deposit signature (proof of possession) which is not checked by the deposit contract
deposit_message = DepositMessage(
pubkey=deposit.data.pubkey,
withdrawal_credentials=deposit.data.withdrawal_credentials,
amount=deposit.data.amount,
)
domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks
signing_root = compute_signing_root(deposit_message, domain)
if not bls.Verify(pubkey, signing_root, deposit.data.signature):
return
# Add validator and balance entries
state.validators.append(get_validator_from_deposit(state, deposit))
state.balances.append(amount)
# [Added in hf-1] Initialize empty participation flags for new validator
state.previous_epoch_participation.append(ValidatorFlag(0))
state.current_epoch_participation.append(ValidatorFlag(0))
else:
# Increase balance by deposit amount
index = ValidatorIndex(validator_pubkeys.index(pubkey))
increase_balance(state, index, amount)
```
#### Sync committee processing
```python
@ -211,8 +498,8 @@ def process_sync_committee(state: BeaconState, body: BeaconBlockBody) -> None:
```python
def process_epoch(state: BeaconState) -> None:
process_justification_and_finalization(state)
process_rewards_and_penalties(state)
process_justification_and_finalization(state) # [Updated in HF1]
process_rewards_and_penalties(state) # [Updated in HF1]
process_registry_updates(state)
process_slashings(state)
process_eth1_data_reset(state)
@ -220,43 +507,95 @@ def process_epoch(state: BeaconState) -> None:
process_slashings_reset(state)
process_randao_mixes_reset(state)
process_historical_roots_update(state)
process_participation_record_updates(state)
# Light client patch
# [Removed in HF1] -- process_participation_record_updates(state)
# [Added in HF1]
process_participation_flag_updates(state)
process_sync_committee_updates(state)
```
#### Components of attestation deltas
#### New `process_justification_and_finalization`
*Note*: The function `get_inactivity_penalty_deltas` is modified with `BASE_REWARDS_PER_EPOCH` replaced by `BASE_REWARDS_PER_EPOCH - 1`.
*Note*: The function `process_justification_and_finalization` is modified with `matching_target_attestations` replaced by `matching_target_indices`.
```python
def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
"""
Return inactivity reward/penalty deltas for each validator.
"""
penalties = [Gwei(0) for _ in range(len(state.validators))]
if is_in_inactivity_leak(state):
matching_target_attestations = get_matching_target_attestations(state, get_previous_epoch(state))
matching_target_attesting_indices = get_unslashed_attesting_indices(state, matching_target_attestations)
for index in get_eligible_validator_indices(state):
# Penalize validator so that optimal attestation performance is rewarded with one base reward per epoch
base_reward = get_base_reward(state, index)
penalties[index] += Gwei((BASE_REWARDS_PER_EPOCH - 1) * base_reward - get_proposer_reward(state, index))
if index not in matching_target_attesting_indices:
effective_balance = state.validators[index].effective_balance
penalties[index] += Gwei(effective_balance * get_finality_delay(state) // INACTIVITY_PENALTY_QUOTIENT)
def process_justification_and_finalization(state: BeaconState) -> None:
# Initial FFG checkpoint values have a `0x00` stub for `root`.
# Skip FFG updates in the first two epochs to avoid corner cases that might result in modifying this stub.
if get_current_epoch(state) <= GENESIS_EPOCH + 1:
return
previous_epoch = get_previous_epoch(state)
current_epoch = get_current_epoch(state)
old_previous_justified_checkpoint = state.previous_justified_checkpoint
old_current_justified_checkpoint = state.current_justified_checkpoint
# No rewards associated with inactivity penalties
rewards = [Gwei(0) for _ in range(len(state.validators))]
return rewards, penalties
# Process justifications
state.previous_justified_checkpoint = state.current_justified_checkpoint
state.justification_bits[1:] = state.justification_bits[:JUSTIFICATION_BITS_LENGTH - 1]
state.justification_bits[0] = 0b0
matching_target_indices = get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG, previous_epoch)
if get_total_balance(state, matching_target_indices) * 3 >= get_total_active_balance(state) * 2:
state.current_justified_checkpoint = Checkpoint(epoch=previous_epoch,
root=get_block_root(state, previous_epoch))
state.justification_bits[1] = 0b1
matching_target_indices = get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG, current_epoch)
if get_total_balance(state, matching_target_indices) * 3 >= get_total_active_balance(state) * 2:
state.current_justified_checkpoint = Checkpoint(epoch=current_epoch,
root=get_block_root(state, current_epoch))
state.justification_bits[0] = 0b1
# Process finalizations
bits = state.justification_bits
# The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source
if all(bits[1:4]) and old_previous_justified_checkpoint.epoch + 3 == current_epoch:
state.finalized_checkpoint = old_previous_justified_checkpoint
# The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source
if all(bits[1:3]) and old_previous_justified_checkpoint.epoch + 2 == current_epoch:
state.finalized_checkpoint = old_previous_justified_checkpoint
# The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source
if all(bits[0:3]) and old_current_justified_checkpoint.epoch + 2 == current_epoch:
state.finalized_checkpoint = old_current_justified_checkpoint
# The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source
if all(bits[0:2]) and old_current_justified_checkpoint.epoch + 1 == current_epoch:
state.finalized_checkpoint = old_current_justified_checkpoint
```
#### New `process_rewards_and_penalties`
*Note*: The function `process_rewards_and_penalties` is modified to use participation flag deltas.
```python
def process_rewards_and_penalties(state: BeaconState) -> None:
# No rewards are applied at the end of `GENESIS_EPOCH` because rewards are for work done in the previous epoch
if get_current_epoch(state) == GENESIS_EPOCH:
return
flag_deltas = [get_flag_deltas(state, flag, numerator) for (flag, numerator) in get_flags_and_numerators()]
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])
```
#### Sync committee updates
```python
def process_sync_committee_updates(state: BeaconState) -> None:
"""
Call to ``proces_sync_committee_updates`` added to ``process_epoch`` in HF1
"""
next_epoch = get_current_epoch(state) + Epoch(1)
if next_epoch % EPOCHS_PER_SYNC_COMMITTEE_PERIOD == 0:
state.current_sync_committee = state.next_sync_committee
state.next_sync_committee = get_sync_committee(state, next_epoch + EPOCHS_PER_SYNC_COMMITTEE_PERIOD)
```
#### Participation flags updates
```python
def process_participation_flag_updates(state: BeaconState) -> None:
"""
Call to ``process_participation_flag_updates`` added to ``process_epoch`` in HF1
"""
state.previous_epoch_participation = state.current_epoch_participation
state.current_epoch_participation = [ValidatorFlag(0) for _ in range(len(state.validators))]
```

View File

@ -43,6 +43,7 @@ def upgrade_to_lightclient_patch(pre: phase0.BeaconState) -> BeaconState:
epoch = get_current_epoch(pre)
post = BeaconState(
genesis_time=pre.genesis_time,
genesis_validators_root=pre.genesis_validators_root,
slot=pre.slot,
fork=Fork(
previous_version=pre.fork.current_version,
@ -66,10 +67,8 @@ def upgrade_to_lightclient_patch(pre: phase0.BeaconState) -> BeaconState:
# Slashings
slashings=pre.slashings,
# Attestations
# previous_epoch_attestations is cleared on upgrade.
previous_epoch_attestations=List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH](),
# empty in pre state, since the upgrade is performed just after an epoch boundary.
current_epoch_attestations=List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH](),
previous_epoch_participation=[ValidatorFlag(0) for _ in range(len(pre.validators))],
current_epoch_participation=[ValidatorFlag(0) for _ in range(len(pre.validators))],
# Finality
justification_bits=pre.justification_bits,
previous_justified_checkpoint=pre.previous_justified_checkpoint,

View File

@ -390,3 +390,11 @@ def only_full_crosslink(fn):
return None
return fn(*args, spec=spec, state=state, **kw)
return wrapper
def is_post_lightclient_patch(spec):
if spec.fork in [PHASE0, PHASE1]:
# TODO: PHASE1 fork is temporarily parallel to LIGHTCLIENT_PATCH.
# Will make PHASE1 fork inherit LIGHTCLIENT_PATCH later.
return False
return True

View File

@ -2,7 +2,7 @@ from lru import LRU
from typing import List
from eth2spec.test.context import expect_assertion_error, PHASE1
from eth2spec.test.context import expect_assertion_error, PHASE1, is_post_lightclient_patch
from eth2spec.test.helpers.state import state_transition_and_sign_block, next_epoch, next_slot
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
from eth2spec.test.helpers.shard_transitions import get_shard_transition_of_committee
@ -30,17 +30,22 @@ def run_attestation_processing(spec, state, attestation, valid=True):
yield 'post', None
return
current_epoch_count = len(state.current_epoch_attestations)
previous_epoch_count = len(state.previous_epoch_attestations)
if not is_post_lightclient_patch(spec):
current_epoch_count = len(state.current_epoch_attestations)
previous_epoch_count = len(state.previous_epoch_attestations)
# process attestation
spec.process_attestation(state, attestation)
# Make sure the attestation has been processed
if attestation.data.target.epoch == spec.get_current_epoch(state):
assert len(state.current_epoch_attestations) == current_epoch_count + 1
if not is_post_lightclient_patch(spec):
if attestation.data.target.epoch == spec.get_current_epoch(state):
assert len(state.current_epoch_attestations) == current_epoch_count + 1
else:
assert len(state.previous_epoch_attestations) == previous_epoch_count + 1
else:
assert len(state.previous_epoch_attestations) == previous_epoch_count + 1
# After accounting reform, there are cases when processing an attestation does not result in any flag updates
pass
# yield post-state
yield 'post', state
@ -315,7 +320,8 @@ def prepare_state_with_attestations(spec, state, participation_fn=None):
next_slot(spec, state)
assert state.slot == next_epoch_start_slot + spec.MIN_ATTESTATION_INCLUSION_DELAY
assert len(state.previous_epoch_attestations) == len(attestations)
if not is_post_lightclient_patch(spec):
assert len(state.previous_epoch_attestations) == len(attestations)
return attestations

View File

@ -1,4 +1,4 @@
from eth2spec.test.context import LIGHTCLIENT_PATCH
from eth2spec.test.context import is_post_lightclient_patch
from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils import bls
from eth2spec.utils.bls import only_with_bls
@ -91,7 +91,7 @@ def build_empty_block(spec, state, slot=None):
empty_block.body.eth1_data.deposit_count = state.eth1_deposit_index
empty_block.parent_root = parent_block_root
if spec.fork == LIGHTCLIENT_PATCH:
if is_post_lightclient_patch(spec):
empty_block.body.sync_committee_signature = spec.G2_POINT_AT_INFINITY
apply_randao_reveal(spec, state, empty_block)

View File

@ -1,23 +1,29 @@
process_calls = [
# PHASE0
'process_justification_and_finalization',
'process_rewards_and_penalties',
'process_registry_updates',
'process_reveal_deadlines',
'process_challenge_deadlines',
'process_slashings',
'process_eth1_data_reset',
'process_effective_balance_updates',
'process_slashings_reset',
'process_randao_mixes_reset',
'process_historical_roots_update',
'process_participation_record_updates',
# LIGHTCLIENT_PATCH
'process_sync_committee_updates',
# PHASE1
'process_phase_1_final_updates',
]
from eth2spec.test.context import is_post_lightclient_patch
def get_process_calls(spec):
return [
# PHASE0
'process_justification_and_finalization',
'process_rewards_and_penalties',
'process_registry_updates',
'process_reveal_deadlines',
'process_challenge_deadlines',
'process_slashings',
'process_eth1_data_reset',
'process_effective_balance_updates',
'process_slashings_reset',
'process_randao_mixes_reset',
'process_historical_roots_update',
# HF1 replaced `process_participation_record_updates` with `process_participation_flag_updates`
'process_participation_flag_updates' if is_post_lightclient_patch(spec) else (
'process_participation_record_updates'
),
'process_sync_committee_updates',
# PHASE1
'process_phase_1_final_updates',
]
def run_epoch_processing_to(spec, state, process_name: str):
@ -34,7 +40,7 @@ def run_epoch_processing_to(spec, state, process_name: str):
spec.process_slot(state)
# process components of epoch transition before final-updates
for name in process_calls:
for name in get_process_calls(spec):
if name == process_name:
break
# only run when present. Later phases introduce more to the epoch-processing.

View File

@ -2,7 +2,7 @@ from random import Random
from lru import LRU
from eth2spec.phase0 import spec as spec_phase0
from eth2spec.test.context import LIGHTCLIENT_PATCH
from eth2spec.test.context import is_post_lightclient_patch
from eth2spec.test.helpers.attestations import cached_prepare_state_with_attestations
from eth2spec.test.helpers.deposits import mock_deposit
from eth2spec.test.helpers.state import next_epoch
@ -38,24 +38,35 @@ def run_deltas(spec, state):
- inactivity penalty deltas ('inactivity_penalty_deltas')
"""
yield 'pre', state
if is_post_lightclient_patch(spec):
def get_source_deltas(state):
return spec.get_flag_deltas(state, spec.TIMELY_SOURCE_FLAG, spec.TIMELY_SOURCE_NUMERATOR)
def get_head_deltas(state):
return spec.get_flag_deltas(state, spec.TIMELY_HEAD_FLAG, spec.TIMELY_HEAD_NUMERATOR)
def get_target_deltas(state):
return spec.get_flag_deltas(state, spec.TIMELY_TARGET_FLAG, spec.TIMELY_TARGET_NUMERATOR)
yield from run_attestation_component_deltas(
spec,
state,
spec.get_source_deltas,
spec.get_source_deltas if not is_post_lightclient_patch(spec) else get_source_deltas,
spec.get_matching_source_attestations,
'source_deltas',
)
yield from run_attestation_component_deltas(
spec,
state,
spec.get_target_deltas,
spec.get_target_deltas if not is_post_lightclient_patch(spec) else get_target_deltas,
spec.get_matching_target_attestations,
'target_deltas',
)
yield from run_attestation_component_deltas(
spec,
state,
spec.get_head_deltas,
spec.get_head_deltas if not is_post_lightclient_patch(spec) else get_head_deltas,
spec.get_matching_head_attestations,
'head_deltas',
)
@ -63,6 +74,16 @@ def run_deltas(spec, state):
yield from run_get_inactivity_penalty_deltas(spec, state)
def deltas_name_to_flag(spec, deltas_name):
if 'source' in deltas_name:
return spec.TIMELY_SOURCE_FLAG
elif 'head' in deltas_name:
return spec.TIMELY_HEAD_FLAG
elif 'target' in deltas_name:
return spec.TIMELY_TARGET_FLAG
raise ValueError("Wrong deltas_name %s" % deltas_name)
def run_attestation_component_deltas(spec, state, component_delta_fn, matching_att_fn, deltas_name):
"""
Run ``component_delta_fn``, yielding:
@ -72,8 +93,14 @@ def run_attestation_component_deltas(spec, state, component_delta_fn, matching_a
yield deltas_name, Deltas(rewards=rewards, penalties=penalties)
matching_attestations = matching_att_fn(state, spec.get_previous_epoch(state))
matching_indices = spec.get_unslashed_attesting_indices(state, matching_attestations)
if not is_post_lightclient_patch(spec):
matching_attestations = matching_att_fn(state, spec.get_previous_epoch(state))
matching_indices = spec.get_unslashed_attesting_indices(state, matching_attestations)
else:
matching_indices = spec.get_unslashed_participating_indices(
state, deltas_name_to_flag(spec, deltas_name), spec.get_previous_epoch(state)
)
eligible_indices = spec.get_eligible_validator_indices(state)
for index in range(len(state.validators)):
if index not in eligible_indices:
@ -102,6 +129,12 @@ def run_get_inclusion_delay_deltas(spec, state):
Run ``get_inclusion_delay_deltas``, yielding:
- inclusion delay deltas ('inclusion_delay_deltas')
"""
if is_post_lightclient_patch(spec):
# No inclusion_delay_deltas
yield 'inclusion_delay_deltas', Deltas(rewards=[0] * len(state.validators),
penalties=[0] * len(state.validators))
return
rewards, penalties = spec.get_inclusion_delay_deltas(state)
yield 'inclusion_delay_deltas', Deltas(rewards=rewards, penalties=penalties)
@ -149,8 +182,14 @@ def run_get_inactivity_penalty_deltas(spec, state):
yield 'inactivity_penalty_deltas', Deltas(rewards=rewards, penalties=penalties)
matching_attestations = spec.get_matching_target_attestations(state, spec.get_previous_epoch(state))
matching_attesting_indices = spec.get_unslashed_attesting_indices(state, matching_attestations)
if not is_post_lightclient_patch(spec):
matching_attestations = spec.get_matching_target_attestations(state, spec.get_previous_epoch(state))
matching_attesting_indices = spec.get_unslashed_attesting_indices(state, matching_attestations)
else:
matching_attesting_indices = spec.get_unslashed_participating_indices(
state, spec.TIMELY_TARGET_FLAG, spec.get_previous_epoch(state)
)
reward_numerator_sum = sum(numerator for (_, numerator) in spec.get_flags_and_numerators())
eligible_indices = spec.get_eligible_validator_indices(state)
for index in range(len(state.validators)):
@ -160,12 +199,14 @@ def run_get_inactivity_penalty_deltas(spec, state):
continue
if spec.is_in_inactivity_leak(state):
if spec.fork == LIGHTCLIENT_PATCH:
cancel_base_rewards_per_epoch = spec.BASE_REWARDS_PER_EPOCH - 1
else:
# Compute base_penalty
if not is_post_lightclient_patch(spec):
cancel_base_rewards_per_epoch = spec.BASE_REWARDS_PER_EPOCH
base_reward = spec.get_base_reward(state, index)
base_penalty = cancel_base_rewards_per_epoch * base_reward - spec.get_proposer_reward(state, index)
base_reward = spec.get_base_reward(state, index)
base_penalty = cancel_base_rewards_per_epoch * base_reward - spec.get_proposer_reward(state, index)
else:
base_penalty = spec.get_base_reward(state, index) * reward_numerator_sum // spec.REWARD_DENOMINATOR
if not has_enough_for_reward(spec, state, index):
assert penalties[index] == 0
elif index in matching_attesting_indices:
@ -267,8 +308,13 @@ def run_test_full_all_correct(spec, state):
def run_test_full_but_partial_participation(spec, state, rng=Random(5522)):
cached_prepare_state_with_attestations(spec, state)
for a in state.previous_epoch_attestations:
a.aggregation_bits = [rng.choice([True, False]) for _ in a.aggregation_bits]
if not is_post_lightclient_patch(spec):
for a in state.previous_epoch_attestations:
a.aggregation_bits = [rng.choice([True, False]) for _ in a.aggregation_bits]
else:
for index in range(len(state.validators)):
if rng.choice([True, False]):
state.previous_epoch_participation[index] = spec.ValidatorFlag(0)
yield from run_deltas(spec, state)
@ -277,8 +323,12 @@ def run_test_partial(spec, state, fraction_filled):
cached_prepare_state_with_attestations(spec, state)
# Remove portion of attestations
num_attestations = int(len(state.previous_epoch_attestations) * fraction_filled)
state.previous_epoch_attestations = state.previous_epoch_attestations[:num_attestations]
if not is_post_lightclient_patch(spec):
num_attestations = int(len(state.previous_epoch_attestations) * fraction_filled)
state.previous_epoch_attestations = state.previous_epoch_attestations[:num_attestations]
else:
for index in range(int(len(state.validators) * fraction_filled)):
state.previous_epoch_participation[index] = spec.ValidatorFlag(0)
yield from run_deltas(spec, state)
@ -333,13 +383,18 @@ def run_test_some_very_low_effective_balances_that_attested(spec, state):
def run_test_some_very_low_effective_balances_that_did_not_attest(spec, state):
cached_prepare_state_with_attestations(spec, state)
# Remove attestation
attestation = state.previous_epoch_attestations[0]
state.previous_epoch_attestations = state.previous_epoch_attestations[1:]
# Set removed indices effective balance to very low amount
indices = spec.get_unslashed_attesting_indices(state, [attestation])
for i, index in enumerate(indices):
state.validators[index].effective_balance = i
if not is_post_lightclient_patch(spec):
# Remove attestation
attestation = state.previous_epoch_attestations[0]
state.previous_epoch_attestations = state.previous_epoch_attestations[1:]
# Set removed indices effective balance to very low amount
indices = spec.get_unslashed_attesting_indices(state, [attestation])
for i, index in enumerate(indices):
state.validators[index].effective_balance = i
else:
index = 0
state.validators[index].effective_balance = 1
state.previous_epoch_participation[index] = spec.ValidatorFlag(0)
yield from run_deltas(spec, state)
@ -447,16 +502,42 @@ def run_test_full_random(spec, state, rng=Random(8020)):
cached_prepare_state_with_attestations(spec, state)
for pending_attestation in state.previous_epoch_attestations:
# ~1/3 have bad target
if rng.randint(0, 2) == 0:
pending_attestation.data.target.root = b'\x55' * 32
# ~1/3 have bad head
if rng.randint(0, 2) == 0:
pending_attestation.data.beacon_block_root = b'\x66' * 32
# ~50% participation
pending_attestation.aggregation_bits = [rng.choice([True, False]) for _ in pending_attestation.aggregation_bits]
# Random inclusion delay
pending_attestation.inclusion_delay = rng.randint(1, spec.SLOTS_PER_EPOCH)
if not is_post_lightclient_patch(spec):
for pending_attestation in state.previous_epoch_attestations:
# ~1/3 have bad target
if rng.randint(0, 2) == 0:
pending_attestation.data.target.root = b'\x55' * 32
# ~1/3 have bad head
if rng.randint(0, 2) == 0:
pending_attestation.data.beacon_block_root = b'\x66' * 32
# ~50% participation
pending_attestation.aggregation_bits = [rng.choice([True, False])
for _ in pending_attestation.aggregation_bits]
# Random inclusion delay
pending_attestation.inclusion_delay = rng.randint(1, spec.SLOTS_PER_EPOCH)
else:
for index in range(len(state.validators)):
# ~1/3 have bad head or bad target or not timely enough
is_timely_correct_head = rng.randint(0, 2) != 0
flags = state.previous_epoch_participation[index]
def set_flag(f, v):
nonlocal flags
if v:
flags |= f
else:
flags &= 0xff ^ f
set_flag(spec.TIMELY_HEAD_FLAG, is_timely_correct_head)
if is_timely_correct_head:
# If timely head, then must be timely target
set_flag(spec.TIMELY_TARGET_FLAG, True)
# If timely head, then must be timely source
set_flag(spec.TIMELY_SOURCE_FLAG, True)
else:
# ~50% of remaining have bad target or not timely enough
set_flag(spec.TIMELY_TARGET_FLAG, rng.choice([True, False]))
# ~50% of remaining have bad source or not timely enough
set_flag(spec.TIMELY_SOURCE_FLAG, rng.choice([True, False]))
state.previous_epoch_participation[index] = flags
yield from run_deltas(spec, state)

View File

@ -18,6 +18,7 @@ from eth2spec.test.context import (
with_all_phases_except,
with_configs,
spec_state_test,
always_bls,
)
from eth2spec.utils.hash_function import hash
@ -196,6 +197,7 @@ def test_sync_committee_rewards_duplicate_committee(spec, state):
@with_all_phases_except([PHASE0, PHASE1])
@spec_state_test
@always_bls
def test_invalid_signature_past_block(spec, state):
committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
@ -237,6 +239,7 @@ def test_invalid_signature_past_block(spec, state):
@with_all_phases_except([PHASE0, PHASE1])
@with_configs([MINIMAL], reason="to produce different committee sets")
@spec_state_test
@always_bls
def test_invalid_signature_previous_committee(spec, state):
# NOTE: the `state` provided is at genesis and the process to select
# sync committees currently returns the same committee for the first and second

View File

@ -2,10 +2,13 @@ from eth2spec.test.context import (
spec_state_test,
always_bls, never_bls,
with_all_phases,
with_all_phases_except,
spec_test,
low_balances,
with_custom_state,
single_phase)
single_phase,
PHASE1,
)
from eth2spec.test.helpers.attestations import (
run_attestation_processing,
get_valid_attestation,
@ -329,3 +332,212 @@ def test_too_few_aggregation_bits(spec, state):
attestation.aggregation_bits = attestation.aggregation_bits[:-1]
yield from run_attestation_processing(spec, state, attestation, False)
#
# Full correct atttestation contents at different slot inclusions
#
@with_all_phases
@spec_state_test
def test_correct_min_inclusion_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=True)
next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
yield from run_attestation_processing(spec, state, attestation)
@with_all_phases
@spec_state_test
def test_correct_sqrt_epoch_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=True, on_time=False)
next_slots(spec, state, spec.integer_squareroot(spec.SLOTS_PER_EPOCH))
yield from run_attestation_processing(spec, state, attestation)
@with_all_phases
@spec_state_test
def test_correct_epoch_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=True, on_time=False)
next_slots(spec, state, spec.SLOTS_PER_EPOCH)
yield from run_attestation_processing(spec, state, attestation)
@with_all_phases
@spec_state_test
def test_correct_after_epoch_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=True, on_time=False)
# increment past latest inclusion slot
next_slots(spec, state, spec.SLOTS_PER_EPOCH + 1)
yield from run_attestation_processing(spec, state, attestation, False)
#
# Incorrect head but correct source/target at different slot inclusions
#
@with_all_phases_except([PHASE1])
@spec_state_test
def test_incorrect_head_min_inclusion_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=False)
next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
attestation.data.beacon_block_root = b'\x42' * 32
sign_attestation(spec, state, attestation)
yield from run_attestation_processing(spec, state, attestation)
@with_all_phases
@spec_state_test
def test_incorrect_head_sqrt_epoch_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=False, on_time=False)
next_slots(spec, state, spec.integer_squareroot(spec.SLOTS_PER_EPOCH))
attestation.data.beacon_block_root = b'\x42' * 32
sign_attestation(spec, state, attestation)
yield from run_attestation_processing(spec, state, attestation)
@with_all_phases
@spec_state_test
def test_incorrect_head_epoch_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=False, on_time=False)
next_slots(spec, state, spec.SLOTS_PER_EPOCH)
attestation.data.beacon_block_root = b'\x42' * 32
sign_attestation(spec, state, attestation)
yield from run_attestation_processing(spec, state, attestation)
@with_all_phases
@spec_state_test
def test_incorrect_head_after_epoch_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=False, on_time=False)
# increment past latest inclusion slot
next_slots(spec, state, spec.SLOTS_PER_EPOCH + 1)
attestation.data.beacon_block_root = b'\x42' * 32
sign_attestation(spec, state, attestation)
yield from run_attestation_processing(spec, state, attestation, False)
#
# Incorrect head and target but correct source at different slot inclusions
#
# Note: current phase 1 spec checks
# `assert data.beacon_block_root == get_block_root_at_slot(state, compute_previous_slot(state.slot))`
# so this test can't pass that until phase 1 refactor is merged
@with_all_phases_except([PHASE1])
@spec_state_test
def test_incorrect_head_and_target_min_inclusion_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=False)
next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
attestation.data.beacon_block_root = b'\x42' * 32
attestation.data.target.root = b'\x42' * 32
sign_attestation(spec, state, attestation)
yield from run_attestation_processing(spec, state, attestation)
@with_all_phases
@spec_state_test
def test_incorrect_head_and_target_sqrt_epoch_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=False, on_time=False)
next_slots(spec, state, spec.integer_squareroot(spec.SLOTS_PER_EPOCH))
attestation.data.beacon_block_root = b'\x42' * 32
attestation.data.target.root = b'\x42' * 32
sign_attestation(spec, state, attestation)
yield from run_attestation_processing(spec, state, attestation)
@with_all_phases
@spec_state_test
def test_incorrect_head_and_target_epoch_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=False, on_time=False)
next_slots(spec, state, spec.SLOTS_PER_EPOCH)
attestation.data.beacon_block_root = b'\x42' * 32
attestation.data.target.root = b'\x42' * 32
sign_attestation(spec, state, attestation)
yield from run_attestation_processing(spec, state, attestation)
@with_all_phases
@spec_state_test
def test_incorrect_head_and_target_after_epoch_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=False, on_time=False)
# increment past latest inclusion slot
next_slots(spec, state, spec.SLOTS_PER_EPOCH + 1)
attestation.data.beacon_block_root = b'\x42' * 32
attestation.data.target.root = b'\x42' * 32
sign_attestation(spec, state, attestation)
yield from run_attestation_processing(spec, state, attestation, False)
#
# Correct head and source but incorrect target at different slot inclusions
#
@with_all_phases_except([PHASE1])
@spec_state_test
def test_incorrect_target_min_inclusion_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=False)
next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
attestation.data.target.root = b'\x42' * 32
sign_attestation(spec, state, attestation)
yield from run_attestation_processing(spec, state, attestation)
@with_all_phases
@spec_state_test
def test_incorrect_target_sqrt_epoch_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=False, on_time=False)
next_slots(spec, state, spec.integer_squareroot(spec.SLOTS_PER_EPOCH))
attestation.data.target.root = b'\x42' * 32
sign_attestation(spec, state, attestation)
yield from run_attestation_processing(spec, state, attestation)
@with_all_phases
@spec_state_test
def test_incorrect_target_epoch_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=False, on_time=False)
next_slots(spec, state, spec.SLOTS_PER_EPOCH)
attestation.data.target.root = b'\x42' * 32
sign_attestation(spec, state, attestation)
yield from run_attestation_processing(spec, state, attestation)
@with_all_phases
@spec_state_test
def test_incorrect_target_after_epoch_delay(spec, state):
attestation = get_valid_attestation(spec, state, signed=False, on_time=False)
# increment past latest inclusion slot
next_slots(spec, state, spec.SLOTS_PER_EPOCH + 1)
attestation.data.target.root = b'\x42' * 32
sign_attestation(spec, state, attestation)
yield from run_attestation_processing(spec, state, attestation, False)

View File

@ -1,6 +1,6 @@
from eth2spec.test.context import spec_state_test, with_all_phases
from eth2spec.test.context import is_post_lightclient_patch, spec_state_test, with_all_phases
from eth2spec.test.helpers.epoch_processing import (
run_epoch_processing_with
run_epoch_processing_with,
)
from eth2spec.test.helpers.state import transition_to
@ -16,12 +16,20 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support
previous_epoch = spec.get_previous_epoch(state)
current_epoch = spec.get_current_epoch(state)
if current_epoch == epoch:
attestations = state.current_epoch_attestations
elif previous_epoch == epoch:
attestations = state.previous_epoch_attestations
if not is_post_lightclient_patch(spec):
if current_epoch == epoch:
attestations = state.current_epoch_attestations
elif previous_epoch == epoch:
attestations = state.previous_epoch_attestations
else:
raise Exception(f"cannot include attestations in epoch ${epoch} from epoch ${current_epoch}")
else:
raise Exception(f"cannot include attestations in epoch ${epoch} from epoch ${current_epoch}")
if current_epoch == epoch:
epoch_participation = state.current_epoch_participation
elif previous_epoch == epoch:
epoch_participation = state.previous_epoch_participation
else:
raise Exception(f"cannot include attestations in epoch ${epoch} from epoch ${current_epoch}")
total_balance = spec.get_total_active_balance(state)
remaining_balance = int(total_balance * 2 // 3) # can become negative
@ -52,19 +60,28 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support
for i in range(max(len(committee) // 5, 1)):
aggregation_bits[i] = 0
attestations.append(spec.PendingAttestation(
aggregation_bits=aggregation_bits,
data=spec.AttestationData(
slot=slot,
beacon_block_root=b'\xff' * 32, # irrelevant to testing
source=source,
target=target,
index=index,
),
inclusion_delay=1,
))
if messed_up_target:
attestations[len(attestations) - 1].data.target.root = b'\x99' * 32
# Update state
if not is_post_lightclient_patch(spec):
attestations.append(spec.PendingAttestation(
aggregation_bits=aggregation_bits,
data=spec.AttestationData(
slot=slot,
beacon_block_root=b'\xff' * 32, # irrelevant to testing
source=source,
target=target,
index=index,
),
inclusion_delay=1,
))
if messed_up_target:
attestations[len(attestations) - 1].data.target.root = b'\x99' * 32
else:
for i, index in enumerate(committee):
if aggregation_bits[i]:
epoch_participation[index] |= spec.TIMELY_HEAD_FLAG
epoch_participation[index] |= spec.TIMELY_SOURCE_FLAG
if not messed_up_target:
epoch_participation[index] |= spec.TIMELY_TARGET_FLAG
def get_checkpoints(spec, epoch):

View File

@ -1,4 +1,4 @@
from eth2spec.test.context import spec_state_test, with_all_phases
from eth2spec.test.context import PHASE0, spec_state_test, with_phases
from eth2spec.test.helpers.epoch_processing import (
run_epoch_processing_with
)
@ -8,7 +8,7 @@ def run_process_participation_record_updates(spec, state):
yield from run_epoch_processing_with(spec, state, 'process_participation_record_updates')
@with_all_phases
@with_phases([PHASE0])
@spec_state_test
def test_updated_participation_record(spec, state):
state.previous_epoch_attestations = [spec.PendingAttestation(proposer_index=100)]

View File

@ -1,11 +1,11 @@
from eth2spec.test.context import (
LIGHTCLIENT_PATCH,
spec_state_test, spec_test,
with_all_phases, single_phase,
with_phases, PHASE0,
with_phases, PHASE0, PHASE1,
with_custom_state,
zero_activation_threshold,
misc_balances, low_single_balance,
is_post_lightclient_patch,
)
from eth2spec.test.helpers.state import (
next_epoch,
@ -66,7 +66,7 @@ def test_genesis_epoch_full_attestations_no_rewards(spec, state):
assert state.balances[index] == pre_state.balances[index]
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
def test_full_attestations_random_incorrect_fields(spec, state):
attestations = prepare_state_with_attestations(spec, state)
@ -159,11 +159,11 @@ def run_with_participation(spec, state, participation_fn):
return att_participants
attestations = prepare_state_with_attestations(spec, state, participation_fn=participation_tracker)
proposer_indices = [a.proposer_index for a in state.previous_epoch_attestations]
pre_state = state.copy()
if spec.fork == LIGHTCLIENT_PATCH:
if not is_post_lightclient_patch(spec):
proposer_indices = [a.proposer_index for a in state.previous_epoch_attestations]
else:
sync_committee_indices = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
yield from run_process_rewards_and_penalties(spec, state)
@ -173,11 +173,11 @@ def run_with_participation(spec, state, participation_fn):
for index in range(len(pre_state.validators)):
if spec.is_in_inactivity_leak(state):
# Proposers can still make money during a leak
if index in proposer_indices and index in participated:
# Proposers can still make money during a leak before LIGHTCLIENT_PATCH
if not is_post_lightclient_patch(spec) and index in proposer_indices and index in participated:
assert state.balances[index] > pre_state.balances[index]
elif index in attesting_indices:
if spec.fork == LIGHTCLIENT_PATCH and index in sync_committee_indices:
if is_post_lightclient_patch(spec) and index in sync_committee_indices:
# The sync committee reward has not been canceled, so the sync committee participants still earn it
assert state.balances[index] >= pre_state.balances[index]
else:
@ -428,7 +428,8 @@ def test_attestations_some_slashed(spec, state):
for i in range(spec.MIN_PER_EPOCH_CHURN_LIMIT):
spec.slash_validator(state, attesting_indices_before_slashings[i])
assert len(state.previous_epoch_attestations) == len(attestations)
if not is_post_lightclient_patch(spec):
assert len(state.previous_epoch_attestations) == len(attestations)
pre_state = state.copy()

View File

@ -1,4 +1,4 @@
from eth2spec.test.context import with_all_phases, spec_state_test
from eth2spec.test.context import PHASE0, PHASE1, with_all_phases, with_phases, spec_state_test
import eth2spec.test.helpers.rewards as rewards_helpers
@ -32,7 +32,7 @@ def test_full_but_partial_participation(spec, state):
yield from rewards_helpers.run_test_full_but_partial_participation(spec, state)
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
def test_one_attestation_one_correct(spec, state):
yield from rewards_helpers.run_test_one_attestation_one_correct(spec, state)
@ -75,7 +75,7 @@ def test_some_very_low_effective_balances_that_did_not_attest(spec, state):
#
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
def test_full_half_correct_target_incorrect_head(spec, state):
yield from rewards_helpers.run_test_full_fraction_incorrect(
@ -86,7 +86,7 @@ def test_full_half_correct_target_incorrect_head(spec, state):
)
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
def test_full_correct_target_incorrect_head(spec, state):
yield from rewards_helpers.run_test_full_fraction_incorrect(
@ -97,7 +97,7 @@ def test_full_correct_target_incorrect_head(spec, state):
)
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
def test_full_half_incorrect_target_incorrect_head(spec, state):
yield from rewards_helpers.run_test_full_fraction_incorrect(
@ -108,7 +108,7 @@ def test_full_half_incorrect_target_incorrect_head(spec, state):
)
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
def test_full_half_incorrect_target_correct_head(spec, state):
yield from rewards_helpers.run_test_full_fraction_incorrect(
@ -119,31 +119,31 @@ def test_full_half_incorrect_target_correct_head(spec, state):
)
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
def test_full_delay_one_slot(spec, state):
yield from rewards_helpers.run_test_full_delay_one_slot(spec, state)
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
def test_full_delay_max_slots(spec, state):
yield from rewards_helpers.run_test_full_delay_max_slots(spec, state)
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
def test_full_mixed_delay(spec, state):
yield from rewards_helpers.run_test_full_mixed_delay(spec, state)
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
def test_proposer_not_in_attestations(spec, state):
yield from rewards_helpers.run_test_proposer_not_in_attestations(spec, state)
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
def test_duplicate_attestations_at_later_slots(spec, state):
yield from rewards_helpers.run_test_duplicate_attestations_at_later_slots(spec, state)

View File

@ -1,4 +1,4 @@
from eth2spec.test.context import with_all_phases, spec_state_test
from eth2spec.test.context import PHASE0, PHASE1, with_all_phases, with_phases, spec_state_test
from eth2spec.test.helpers.rewards import leaking
import eth2spec.test.helpers.rewards as rewards_helpers
@ -38,7 +38,7 @@ def test_full_but_partial_participation_leak(spec, state):
yield from rewards_helpers.run_test_full_but_partial_participation(spec, state)
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
@leaking()
def test_one_attestation_one_correct_leak(spec, state):
@ -87,7 +87,7 @@ def test_some_very_low_effective_balances_that_did_not_attest_leak(spec, state):
#
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
@leaking()
def test_full_half_correct_target_incorrect_head_leak(spec, state):
@ -99,7 +99,7 @@ def test_full_half_correct_target_incorrect_head_leak(spec, state):
)
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
@leaking()
def test_full_correct_target_incorrect_head_leak(spec, state):
@ -111,7 +111,7 @@ def test_full_correct_target_incorrect_head_leak(spec, state):
)
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
@leaking()
def test_full_half_incorrect_target_incorrect_head_leak(spec, state):
@ -123,7 +123,7 @@ def test_full_half_incorrect_target_incorrect_head_leak(spec, state):
)
@with_all_phases
@with_phases([PHASE0, PHASE1])
@spec_state_test
@leaking()
def test_full_half_incorrect_target_correct_head_leak(spec, state):

View File

@ -29,6 +29,12 @@ def test_full_random_2(spec, state):
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(3030))
@with_all_phases
@spec_state_test
def test_full_random_3(spec, state):
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(4040))
@with_all_phases
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
@spec_test

View File

@ -35,6 +35,7 @@ from eth2spec.test.context import (
with_configs,
with_custom_state,
large_validator_set,
is_post_lightclient_patch,
)
@ -780,15 +781,19 @@ def test_attestation(spec, state):
spec, state, shard_transition=shard_transition, index=index, signed=True, on_time=True
)
if not is_post_lightclient_patch(spec):
pre_current_attestations_len = len(state.current_epoch_attestations)
# Add to state via block transition
pre_current_attestations_len = len(state.current_epoch_attestations)
attestation_block.body.attestations.append(attestation)
signed_attestation_block = state_transition_and_sign_block(spec, state, attestation_block)
assert len(state.current_epoch_attestations) == pre_current_attestations_len + 1
# Epoch transition should move to previous_epoch_attestations
pre_current_attestations_root = spec.hash_tree_root(state.current_epoch_attestations)
if not is_post_lightclient_patch(spec):
assert len(state.current_epoch_attestations) == pre_current_attestations_len + 1
# Epoch transition should move to previous_epoch_attestations
pre_current_attestations_root = spec.hash_tree_root(state.current_epoch_attestations)
else:
pre_current_epoch_participation_root = spec.hash_tree_root(state.current_epoch_participation)
epoch_block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
signed_epoch_block = state_transition_and_sign_block(spec, state, epoch_block)
@ -796,8 +801,13 @@ def test_attestation(spec, state):
yield 'blocks', [signed_attestation_block, signed_epoch_block]
yield 'post', state
assert len(state.current_epoch_attestations) == 0
assert spec.hash_tree_root(state.previous_epoch_attestations) == pre_current_attestations_root
if not is_post_lightclient_patch(spec):
assert len(state.current_epoch_attestations) == 0
assert spec.hash_tree_root(state.previous_epoch_attestations) == pre_current_attestations_root
else:
for index in range(len(state.validators)):
assert state.current_epoch_participation[index] == 0
assert spec.hash_tree_root(state.previous_epoch_participation) == pre_current_epoch_participation_root
# In phase1 a committee is computed for SHARD_COMMITTEE_PERIOD slots ago,